Index: modules/aggregator/aggregator.parser.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.parser.inc,v
retrieving revision 1.9
diff -u -p -r1.9 aggregator.parser.inc
--- modules/aggregator/aggregator.parser.inc	20 May 2010 08:51:24 -0000	1.9
+++ modules/aggregator/aggregator.parser.inc	15 Jul 2010 18:27:29 -0000
@@ -190,12 +190,12 @@ function aggregator_element_start($parse
         $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.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.test,v
retrieving revision 1.39
diff -u -p -r1.39 aggregator.test
--- modules/aggregator/aggregator.test	24 Mar 2010 08:51:42 -0000	1.39
+++ modules/aggregator/aggregator.test	15 Jul 2010 18:27:30 -0000
@@ -248,6 +248,22 @@ EOF;
     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 = LANGUAGE_NONE;
     // Post 5 articles.
@@ -686,3 +702,193 @@ class AggregatorCronTestCase extends Agg
     $this->assertEqual(5, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(), 'Expected number of items in database.');
   }
 }
+
+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/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	15 Jul 2010 18:27:30 -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/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	15 Jul 2010 18:27:30 -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/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	15 Jul 2010 18:27:30 -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	15 Jul 2010 18:27:30 -0000
@@ -0,0 +1,34 @@
+<?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>
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	15 Jul 2010 18:27:30 -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>
