Index: modules/aggregator/aggregator.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.test,v
retrieving revision 1.31
diff -u -r1.31 aggregator.test
--- modules/aggregator/aggregator.test	24 Aug 2009 17:11:42 -0000	1.31
+++ modules/aggregator/aggregator.test	30 Aug 2009 21:53:58 -0000
@@ -247,6 +247,22 @@
     return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/aggregator_test_rss091.xml';
   }
 
+  function getRSS092Sample() {
+    return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/rss092.xml';
+  }
+
+  function getRSS2Sample() {
+    return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/rss2.xml';
+  }
+
+  function getRDFSample() {
+    return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/rdf.xml';
+  }
+
+  function getAtomSample() {
+    return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/atom.xml';
+  }
+
   function createSampleNodes() {
     $langcode = FIELD_LANGUAGE_NONE;
     // Post 5 articles.
@@ -637,3 +653,193 @@
     $this->submitImportForm();
   }
 }
+
+class FeedParserTestCase extends AggregatorTestCase {
+  /**
+   * Implementation of getInfo().
+   */
+  function getInfo() {
+    return array(
+      'name' => 'Feed parser tests',
+      'description' => "Test Aggregator's built-in feed parser with valid feed samples.",
+      'group' => 'Aggregator',
+    );
+  }
+
+  function addFeed($edit) {
+    $this->drupalGet($edit['url']);
+    $this->assertResponse(array(200), t('URL !url is accessible.', array('!url' => $edit['url'])));
+
+    $this->drupalPost('admin/config/services/aggregator/add/feed', $edit, t('Save'));
+    $this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title'])));
+
+    $feed = db_query("SELECT * FROM {aggregator_feed} WHERE url = :url", array(':url' => $edit['url']))->fetch();
+    return $feed;
+  }
+
+  function updateFeed($feed) {
+    $this->drupalGet('aggregator/sources/' . $feed->fid);
+    $this->assertResponse(array(200), t('Feed %name exists.', array('%name' => $feed->title)));
+
+    $this->drupalGet('admin/config/services/aggregator/update/' . $feed->fid);
+    $this->assertRaw(t('There is new syndicated content from %name.', array('%name' => $feed->title)));
+  }
+
+  function deleteFeed($feed) {
+    $this->drupalGet('aggregator/sources/' . $feed->fid);
+    $this->assertResponse(array(200), t('Feed %name exists.', array('%name' => $feed->title)));
+
+    $this->drupalPost('admin/config/services/aggregator/edit/feed/' . $feed->fid, array(), t('Delete'));
+    $this->assertRaw(t('The feed %title has been deleted.', array('%title' => $feed->title)));
+  }
+
+  function testFeedParser() {
+    $this->_testRSS091Sample();
+    $this->_testRSS092Sample();
+    $this->_testRSS2Sample();
+    $this->_testAtomSample();
+
+    $this->_testAtomEntryLinkSample();
+  }
+
+  function _testRSS091Sample() {
+    $edit = array(
+      'title' => "Sample RSS 0.91 feed",
+      'url' => $this->getRSS091Sample(),
+    );
+    $feed = $this->addFeed($edit);
+    $this->updateFeed($feed);
+
+    $this->drupalGet('aggregator/sources/' . $feed->fid);
+    $this->assertResponse(array(200), t('Feed %name exists.', array('%name' => $feed->title)));
+
+    $this->assertRaw('http://example.com');
+    $this->assertRaw('Example updates');
+
+    $this->assertRaw('Example');
+    $this->assertRaw('http://example.com');
+    $this->assertRaw('http://example.com/images/druplicon.png');
+
+    $this->assertRaw('Example turns one');
+    $this->assertRaw('http://example.com/example-turns-one');
+    $this->assertRaw('Example turns one.');
+
+    $this->deleteFeed($feed);
+  }
+
+  function _testRSS092Sample() {
+    $edit = array(
+      'title' => "Sample RSS 0.92 feed",
+      'url' => $this->getRSS092Sample(),
+    );
+    $feed = $this->addFeed($edit);
+    $this->updateFeed($feed);
+
+    $this->drupalGet('aggregator/sources/' . $feed->fid);
+    $this->assertResponse(array(200), t('Feed %name exists.', array('%name' => $feed->title)));
+
+    $this->assertRaw('http://www.scripting.com/blog/categories/gratefulDead.html');
+    $this->assertRaw('A high-fidelity Grateful Dead song every day.');
+    $this->assertRaw("It's been a few days since I added a song to the");
+
+    $this->deleteFeed($feed);
+  }
+
+  function _testRSS2Sample() {
+    $edit = array(
+      'title' => "Sample RSS 2.0 feed",
+      'url' => $this->getRSS2Sample(),
+    );
+    $feed = $this->addFeed($edit);
+    $this->updateFeed($feed);
+
+    $this->drupalGet('aggregator/sources/' . $feed->fid);
+    $this->assertResponse(array(200), t('Feed %name exists.', array('%name' => $feed->title)));
+
+    $this->assertRaw('http://liftoff.msfc.nasa.gov/');
+    $this->assertRaw('Liftoff to Space Exploration.');
+    $this->assertRaw('Star City');
+    $this->assertRaw('http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp');
+    $this->assertRaw('How do Americans get ready to work with');
+
+    $this->deleteFeed($feed);
+  }
+
+  function _testAtomSample() {
+    $edit = array(
+      'title' => "Sample atom feed",
+      'url' => $this->getAtomSample(),
+    );
+    $feed = $this->addFeed($edit);
+    $this->updateFeed($feed);
+
+    $this->drupalGet('aggregator/sources/' . $feed->fid);
+    $this->assertResponse(array(200), t('Feed %name exists.', array('%name' => $feed->title)));
+
+    $this->assertRaw('http://example.org/');
+    $this->assertRaw('Atom-Powered Robots Run Amok');
+    $this->assertRaw('http://example.org/2003/12/13/atom03');
+    $this->assertRaw('Some text.');
+
+    $this->deleteFeed($feed);
+  }
+
+  function _testAtomEntryLinkSample() {
+    $edit = array(
+      'title' => "Sample feed to test parsing of atom entry's link",
+      'url' => $this->getAtomEntryLinkSample(),
+    );
+    $feed = $this->addFeed($edit);
+    $this->updateFeed($feed);
+
+    $this->drupalGet('aggregator/sources/' . $feed->fid);
+    $this->assertResponse(array(200), t('Feed %name exists.', array('%name' => $feed->title)));
+
+    $this->assertRaw('http://www.example.com/post-1');
+    $this->assertRaw('http://www.example.com/post-2');
+    $this->assertRaw('http://www.example.com/post-3');
+
+    $this->deleteFeed($feed);
+  }
+
+  /**
+   * Sample Atom formatted feed.
+   *
+   * @see http://drupal.org/node/130344
+   */
+  function getAtomEntryLinkSample() {
+    return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/atom_entry_link.xml';
+  }
+
+  /**
+   * $feed->items had actual + 1 number of items.
+   */
+  function testAtomResultHasOneMoreEntry() {
+    include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'aggregator') . '/aggregator.parser.inc';
+    global $channel, $image;
+
+    $feed = new stdClass();
+    $feed->link = $this->getAtomSample();
+    $feed->source_string = $this->drupalGet($this->getAtomSample());
+    if (aggregator_parse_feed($feed->source_string, $feed)) {
+      $this->assertTrue(count($feed->items) == 1);
+    }
+  }
+
+  /**
+   * Every resulting item from an atom feed had $item['entry'] as an offset.
+   */
+  function testAtomResultHasEntryOffset() {
+    include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'aggregator') . '/aggregator.parser.inc';
+    global $channel, $image;
+
+    $feed = new stdClass();
+    $feed->link = $this->getAtomSample();
+    $feed->source_string = $this->drupalGet($this->getAtomSample());
+    if (aggregator_parse_feed($feed->source_string, $feed)) {
+      foreach ($feed->items as $item) {
+        $this->assertFalse(isset($item['entry']));
+      }
+    }
+  }
+}
Index: modules/aggregator/aggregator.parser.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.parser.inc,v
retrieving revision 1.4
diff -u -r1.4 aggregator.parser.inc
--- modules/aggregator/aggregator.parser.inc	15 Jul 2009 21:32:43 -0000	1.4
+++ modules/aggregator/aggregator.parser.inc	30 Aug 2009 21:53:57 -0000
@@ -201,12 +201,12 @@
         $element = $name;
       }
     case 'link':
-      if (!empty($attributes['rel']) && $attributes['rel'] == 'alternate') {
+      if (!empty($attributes['REL']) && $attributes['REL'] == 'alternate') {
         if ($element == 'item') {
-          $items[$item]['link'] = $attributes['href'];
+          $items[$item]['link'] = $attributes['HREF'];
         }
         else {
-          $channel['link'] = $attributes['href'];
+          $channel['link'] = $attributes['HREF'];
         }
       }
       break;
Index: modules/aggregator/aggregator.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.admin.inc,v
retrieving revision 1.42
diff -u -r1.42 aggregator.admin.inc
--- modules/aggregator/aggregator.admin.inc	24 Aug 2009 17:11:41 -0000	1.42
+++ modules/aggregator/aggregator.admin.inc	30 Aug 2009 21:53:57 -0000
@@ -30,7 +30,7 @@
     $rows[] = array(l($feed->title, "aggregator/sources/$feed->fid"), format_plural($feed->items, '1 item', '@count items'), ($feed->checked ? t('@time ago', array('@time' => format_interval(REQUEST_TIME - $feed->checked))) : t('never')), ($feed->checked && $feed->refresh ? t('%time left', array('%time' => format_interval($feed->checked + $feed->refresh - REQUEST_TIME))) : t('never')), l(t('edit'), "admin/config/services/aggregator/edit/feed/$feed->fid"), l(t('remove items'), "admin/config/services/aggregator/remove/$feed->fid"), l(t('update items'), "admin/config/services/aggregator/update/$feed->fid"));
   }
   if (empty($rows)) {
-    $rows[] = array(array('data' => t('No feeds available. <a href="@link">Add feed</a>.', array('@link' => url('admin/content/aggregator/add/feed'))), 'colspan' => '5', 'class' => array('message')));
+    $rows[] = array(array('data' => t('No feeds available. <a href="@link">Add feed</a>.', array('@link' => url('admin/config/services/aggregator/add/feed'))), 'colspan' => '5', 'class' => array('message')));
   }
   $output .= theme('table', $header, $rows);
 
Index: modules/aggregator/tests/aggregator_test_rss091.xml
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/tests/aggregator_test_rss091.xml,v
retrieving revision 1.1
diff -u -r1.1 aggregator_test_rss091.xml
--- modules/aggregator/tests/aggregator_test_rss091.xml	2 Apr 2009 20:50:37 -0000	1.1
+++ modules/aggregator/tests/aggregator_test_rss091.xml	30 Aug 2009 21:53:58 -0000
@@ -27,4 +27,4 @@
       <description>Example turns two.</description>
     </item>
   </channel>
-</rss>
\ No newline at end of file
+</rss>
Index: modules/aggregator/tests/atom.xml
===================================================================
RCS file: modules/aggregator/tests/atom.xml
diff -N modules/aggregator/tests/atom.xml
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/aggregator/tests/atom.xml	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+  <title>Example Feed</title>
+  <link href="http://example.org/" />
+  <updated>2003-12-13T18:30:02Z</updated>
+  <author>
+    <name>John Doe</name>
+  </author>
+  <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
+  <entry>
+    <title>Atom-Powered Robots Run Amok</title>
+    <link href="http://example.org/2003/12/13/atom03" />
+    <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
+    <updated>2003-12-13T18:30:02Z</updated>
+    <summary>Some text.</summary>
+  </entry>
+</feed>
Index: modules/aggregator/tests/rdf.xml
===================================================================
RCS file: modules/aggregator/tests/rdf.xml
diff -N modules/aggregator/tests/rdf.xml
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/aggregator/tests/rdf.xml	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+  xmlns:dc="http://purl.org/dc/elements/1.1/">
+  <rdf:Description rdf:about="http://www.w3.org/">
+    <dc:title>World Wide Web Consortium</dc:title>
+  </rdf:Description>
+</rdf:RDF>
Index: modules/aggregator/tests/rss092.xml
===================================================================
RCS file: modules/aggregator/tests/rss092.xml
diff -N modules/aggregator/tests/rss092.xml
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/aggregator/tests/rss092.xml	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rss version="0.92">
+  <channel>
+    <title>Dave Winer: Grateful Dead</title>
+    <link>http://www.scripting.com/blog/categories/gratefulDead.html
+    </link>
+    <description>A high-fidelity Grateful Dead song every day. This is
+      where we're experimenting with enclosures on RSS news items that
+      download when you're not using your computer. If it works (it
+      will) it will be the end of the Click-And-Wait multimedia
+      experience on the Internet. </description>
+    <lastBuildDate>Fri, 13 Apr 2001 19:23:02 GMT</lastBuildDate>
+    <docs>http://backend.userland.com/rss092</docs>
+    <managingEditor>dave@userland.com (Dave Winer)</managingEditor>
+    <webMaster>dave@userland.com (Dave Winer)</webMaster>
+    <cloud domain="data.ourfavoritesongs.com" port="80" path="/RPC2"
+      registerProcedure="ourFavoriteSongs.rssPleaseNotify" protocol="xml-rpc" />
+    <item>
+      <description>It's been a few days since I added a song to the
+        Grateful Dead channel. Now that there are all these new Radio
+        users, many of whom are tuned into this channel (it's #16 on the
+        hotlist of upstreaming Radio users, there's no way of knowing
+        how many non-upstreaming users are subscribing, have to do
+        something about this..). Anyway, tonight's song is a live
+        version of Weather Report Suite from Dick's Picks Volume 7. It's
+        wistful music. Of course a beautiful song, oft-quoted here on
+        Scripting News. &lt;i&gt;A little change, the wind and
+        rain.&lt;/i&gt; </description>
+      <enclosure
+        url="http://www.scripting.com/mp3s/weatherReportDicksPicsVol7.mp3"
+        length="6182912" type="audio/mpeg" />
+    </item>
+  </channel>
+</rss>
+  
\ No newline at end of file
Index: modules/aggregator/tests/atom_entry_link.xml
===================================================================
RCS file: modules/aggregator/tests/atom_entry_link.xml
diff -N modules/aggregator/tests/atom_entry_link.xml
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/aggregator/tests/atom_entry_link.xml	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,53 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<feed xmlns='http://www.w3.org/2005/Atom'>
+  <id>tag:example.com,1999:blog</id>
+  <updated>2008-08-15T13:18:44.515-04:00</updated>
+  <title type='text'>Example blog</title>
+  <link rel='alternate' type='text/html' href='http://www.example.com/' />
+  <link rel='self' type='application/atom+xml' href='http://www.example.com/rss.xml' />
+  <entry>
+    <id>tag:example.com,1999:blog.post-1</id>
+    <published>2008-08-13T04:55:00.003-04:00</published>
+    <updated>2008-08-13T05:13:11.626-04:00</updated>
+    <title type='text'>Post one</title>
+    <content type='html'>Example blog post one</content>
+    <link rel='alternate' type='text/html' href='http://www.example.com/post-1'
+      title='Post one' />
+    <link rel='self' type='application/atom+xml' href='http://www.example.com/post-1/edit' />
+    <author>
+      <name>Tim</name>
+      <uri>http://www.example.com/profile</uri>
+      <email>noreply@example.com</email>
+    </author>
+  </entry>
+  <entry>
+    <id>tag:example.com,1999:blog.post-2</id>
+    <published>2008-08-12T12:51:00.003-04:00</published>
+    <updated>2008-08-13T06:04:43.803-04:00</updated>
+    <title type='text'>Post two</title>
+    <content type='html'>Example blog post two</content>
+    <link rel='alternate' type='text/html' href='http://www.example.com/post-2'
+      title='Post two' />
+    <link rel='self' type='application/atom+xml' href='http://www.example.com/post-2/edit' />
+    <author>
+      <name>Tim</name>
+      <uri>http://www.example.com/profile</uri>
+      <email>noreply@example.com</email>
+    </author>
+  </entry>
+  <entry>
+    <id>tag:example.com,1999:blog.post-3</id>
+    <published>2008-08-12T10:49:00.002-04:00</published>
+    <updated>2008-08-12T11:22:09.742-04:00</updated>
+    <title type='text'>Post three</title>
+    <content type='html'>Example blog post three</content>
+    <link rel='alternate' type='text/html' href='http://www.example.com/post-3'
+      title='Post three' />
+    <link rel='self' type='application/atom+xml' href='http://www.example.com/post-3/edit' />
+    <author>
+      <name>Tim</name>
+      <uri>http://www.example.com/profile</uri>
+      <email>noreply@example.com</email>
+    </author>
+  </entry>
+</feed>
Index: modules/aggregator/tests/rss2.xml
===================================================================
RCS file: modules/aggregator/tests/rss2.xml
diff -N modules/aggregator/tests/rss2.xml
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/aggregator/tests/rss2.xml	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rss version="2.0">
+  <channel>
+    <title>Liftoff News</title>
+    <link>http://liftoff.msfc.nasa.gov/</link>
+    <description>Liftoff to Space Exploration.</description>
+    <language>en-us</language>
+    <pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate>
+    <lastBuildDate>Tue, 10 Jun 2003 09:41:01 GMT</lastBuildDate>
+    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
+    <generator>Weblog Editor 2.0</generator>
+    <managingEditor>editor@example.com</managingEditor>
+    <webMaster>webmaster@example.com</webMaster>
+    <item>
+      <title>Star City</title>
+      <link>http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp
+      </link>
+      <description>How do Americans get ready to work with Russians
+        aboard the International Space Station? They take a crash course
+        in culture, language and protocol at Russia's &lt;a
+        href="http://howe.iki.rssi.ru/GCTC/gctc_e.htm"&gt;Star
+        City&lt;/a&gt;.</description>
+      <pubDate>Tue, 03 Jun 2003 09:39:21 GMT</pubDate>
+      <guid>http://liftoff.msfc.nasa.gov/2003/06/03.html#item573</guid>
+    </item>
+  </channel>
+</rss>
