 modules/aggregator/aggregator.test |  313 +++++++++++++++++++++++++++++-------
 1 files changed, 257 insertions(+), 56 deletions(-)

diff --git modules/aggregator/aggregator.test modules/aggregator/aggregator.test
index a8de3a7..3920861 100644
--- modules/aggregator/aggregator.test
+++ modules/aggregator/aggregator.test
@@ -9,12 +9,13 @@
 class AggregatorTestCase extends DrupalWebTestCase {
   function setUp() {
     parent::setUp('aggregator', 'aggregator_test');
-    $web_user = $this->drupalCreateUser(array('administer news feeds', 'access news feeds', 'create article content'));
+    $web_user = $this->drupalCreateUser(array('administer news feeds', 'access news feeds', 'create article content', 'administer blocks', 'access administration pages'));
     $this->drupalLogin($web_user);
   }
 
   /**
-   * Create an aggregator feed (simulate form submission on admin/config/services/aggregator/add/feed).
+   * Create an aggregator feed (simulate form submission
+   * on admin/config/services/aggregator/add/feed).
    *
    * @param $feed_url
    *   If given, feed will be created with this URL, otherwise /rss.xml will be used.
@@ -34,6 +35,25 @@ class AggregatorTestCase extends DrupalWebTestCase {
   }
 
   /**
+   * Create an aggretor category.
+   *
+   * @param $title
+   *   Title for the category, defaults to randomName().
+   *
+   * @return
+   *   The aggregator category object.
+   */
+  function createCategory($title = NULL) {
+    $edit = array('title' => empty($title) ? $this->randomName(10) : $title);
+    $this->drupalPost('admin/config/services/aggregator/add/category', $edit, t('Save'));
+    $this->assertRaw(t('The category %title has been added.', array('%title' => $edit['title'])), t('The category %title has been added.', array('%title' => $edit['title'])));
+
+    $category = db_query("SELECT * FROM {aggregator_category} WHERE title = :title", array(':title' => $edit['title']))->fetch();
+    $this->assertTrue(!empty($category), t('The category found in database.'));
+    return $category;
+  }
+
+  /**
    * Delete an aggregator feed.
    *
    * @param $feed
@@ -81,7 +101,8 @@ class AggregatorTestCase extends DrupalWebTestCase {
   }
 
   /**
-   * Update feed items (simulate click to admin/config/services/aggregator/update/$fid).
+   * Update feed items (simulate click
+   * to admin/config/services/aggregator/update/$fid).
    *
    * @param $feed
    *   Feed object representing the feed.
@@ -190,6 +211,7 @@ class AggregatorTestCase extends DrupalWebTestCase {
     foreach ($feeds as &$feed) {
       $feed['url'] = htmlspecialchars($feed['url']);
     }
+
     /**
      * Does not have an XML declaration, must pass the parser.
      */
@@ -289,7 +311,8 @@ class AddFeedTestCase extends AggregatorTestCase {
   }
 
   /**
-   * Create a feed, ensure that it is unique, check the source, and delete the feed.
+   * Create a feed, ensure that it is unique, check the source,
+   * and delete the feed.
    */
   function testAddFeed() {
     $feed = $this->createFeed();
@@ -308,48 +331,35 @@ class AddFeedTestCase extends AggregatorTestCase {
     // Delete feed.
     $this->deleteFeed($feed);
   }
-}
-
-class CategorizeFeedTestCase extends AggregatorTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Categorize feed functionality',
-      'description' => 'Categorize feed test.',
-      'group' => 'Aggregator'
-    );
-  }
 
   /**
-   * Create a feed and make sure you can add more than one category to it.
+   * Test adding an aggregator feed block.
    */
-  function testCategorizeFeed() {
+  function testBlocks() {
+    $this->createSampleNodes();
 
-    // Create 2 categories.
-    $category_1 = array('title' => $this->randomName(10), 'description' => '');
-    $this->drupalPost('admin/config/services/aggregator/add/category', $category_1, t('Save'));
-    $this->assertRaw(t('The category %title has been added.', array('%title' => $category_1['title'])), t('The category %title has been added.', array('%title' => $category_1['title'])));
+    $feed = $this->createFeed();
+    $this->updateFeedItems($feed, $this->getDefaultFeedItemCount());
 
-    $category_2 = array('title' => $this->randomName(10), 'description' => '');
-    $this->drupalPost('admin/config/services/aggregator/add/category', $category_2, t('Save'));
-    $this->assertRaw(t('The category %title has been added.', array('%title' => $category_2['title'])), t('The category %title has been added.', array('%title' => $category_2['title'])));
+    $this->drupalGet('admin/structure/block');
+    $this->assertText(t('!title feed latest items', array('!title' => $feed->title)), t('New feed is displayed as possible block'));
 
-    // Get categories from database.
-    $categories = $this->getCategories();
+    // Set the created block to a specific region.
+    $edit = array();
+    $edit['blocks[aggregator_feed-' . $feed->fid . '][region]'] = 'sidebar_first';
+    $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
 
-    // Create a feed and assign 2 categories to it.
-    $feed = $this->getFeedEditArray();
-    $feed['block'] = 5;
-    foreach ($categories as $cid => $category) {
-      $feed['category'][$cid] = $cid;
-    }
+    $this->assertText(t('The block settings have been updated.'), t('Block successfully moved to %region_name region.', array('%region_name' => 'sidebar_first')));
 
-    // Use aggregator_save_feed() function to save the feed.
-    aggregator_save_feed($feed);
-    $db_feed = db_query("SELECT *  FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $feed['title'], ':url' => $feed['url']))->fetch();
+    $this->drupalGet('/');
+    // Confirm that the block is being displayed.
+    $this->assertText($feed->title, t('Block successfully being displayed on the page.'));
 
-    // Assert the feed has two categories.
-    $this->getFeedCategories($db_feed);
-    $this->assertEqual(count($db_feed->categories), 2, t('Feed has 2 categories'));
+    // Confirm that the feed items are displayed.
+    $result = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, $feed->block, array(':fid' => $feed->fid));
+    foreach ($result as $feed_item) {
+      $this->assertLink($feed_item->title, 0, t('Feed item %title found in block.', array('%title' => $feed_item->title)));
+    }
   }
 }
 
@@ -409,6 +419,7 @@ class RemoveFeedTestCase extends AggregatorTestCase {
    */
   function testRemoveFeed() {
     $feed = $this->createFeed();
+    $this->updateFeedItems($feed, $this->getDefaultFeedItemCount());
 
     // Delete feed.
     $this->deleteFeed($feed);
@@ -417,9 +428,11 @@ class RemoveFeedTestCase extends AggregatorTestCase {
     $this->drupalGet('aggregator/sources/' . $feed->fid);
     $this->assertResponse(404, t('Deleted feed source does not exists.'));
 
-    // Check database for feed.
+    // Check database for feed and feed items.
     $result = db_query("SELECT COUNT(*) FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $feed->title, ':url' => $feed->url))->fetchField();
     $this->assertFalse($result, t('Feed not found in database'));
+    $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField();
+    $this->assertFalse($result, t('Feed items not found in database'));
   }
 }
 
@@ -482,6 +495,48 @@ class UpdateFeedItemTestCase extends AggregatorTestCase {
 
     $this->assertTrue($before === $after, t('Publish timestamp of feed item was not updated (!before === !after)', array('!before' => $before, '!after' => $after)));
   }
+
+  /**
+   * Test fetching and purging a feed by invoking cron.
+   */
+  function testCron() {
+    $feed = $this->createFeed();
+    $this->createSampleNodes();
+    
+    $updated_count = db_query("SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid", array(':fid' => $feed->fid))->fetchField();
+    $this->assertTrue($updated_count == 0, t('The feed currently contains no items.'));
+
+    // Create an old item that should be purged.
+    $iid = db_insert('aggregator_item')
+      ->fields(array(
+        'fid' => $feed->fid,
+        'title' => $this->randomName(10),
+        'link' => $this->randomName(10),
+        'author' => $this->randomName(10),
+        'description' => $this->randomName(10),
+        'timestamp' => REQUEST_TIME - variable_get('aggregator_clear', 9676800) - 100,
+      ))
+      ->execute();
+    db_insert('aggregator_category_item')
+      ->fields(array(
+        'iid' => $iid,
+        'cid' => 19,
+      ))
+      ->execute();
+
+    // Execute cron, refresh.
+    drupal_cron_run();
+
+    $updated_feed = db_query("SELECT * FROM {aggregator_feed} WHERE fid = :fid", array(':fid' => $feed->fid))->fetchObject();
+    $updated_count = db_query("SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid", array(':fid' => $feed->fid))->fetchField();
+    $old_feed = db_query_range('SELECT iid FROM {aggregator_item} WHERE iid = :iid', 0, 1, array(':iid' => $iid))->fetchField();
+    $old_feed_category = db_query_range('SELECT iid FROM {aggregator_category_item} WHERE iid = :iid', 0, 1, array(':iid' => $iid))->fetchField();
+
+    $this->assertTrue($updated_feed->checked >= REQUEST_TIME, t('Feed has been updated.'));
+    $this->assertTrue($updated_count == 5, t('5 new feed items have been added.'));
+    $this->assertFalse($old_feed, t('Old feed has been purged.'));
+    $this->assertFalse($old_feed_category, t('Old feed category has been purged.'));
+  }
 }
 
 class RemoveFeedItemTestCase extends AggregatorTestCase {
@@ -538,18 +593,72 @@ class CategorizeFeedItemTestCase extends AggregatorTestCase {
   function testCategorizeFeedItem() {
     $this->createSampleNodes();
 
+    // Add 3 categories
+    $category1 = $this->createCategory();
+    $category2 = $this->createCategory();
+    $category3 = $this->createCategory();
+
+    $feed = $this->createFeed();
+
+    $edit = array('category[' . $category1->cid . ']' => 1, 'category[' . $category2->cid . ']' => 1);
+    $this->drupalPost('/admin/config/services/aggregator/edit/feed/' . $feed->fid, $edit, t('Save'));
+    $this->updateFeedItems($feed, $this->getDefaultFeedItemCount());
+    $this->getFeedCategories($feed);
+    $this->assertTrue(in_array($category1->cid, $feed->categories), t('First category found in the feed.'));
+    $this->assertTrue(in_array($category2->cid, $feed->categories), t('Second category found in the feed.'));
+    $this->assertFalse(in_array($category3->cid, $feed->categories), t('Third category not found in the feed.'));
+ 
+    $this->drupalGet('aggregator/categories/' . $category1->cid);
+    $this->assertTitle(t("@title | Drupal", array('@title' => $category1->title)), 'Category title is set.');
+
+    // Confirm that the feed items are displayed for category 1.
+    $result = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, $feed->block, array(':fid' => $feed->fid));
+    foreach ($result as $feed_item) {
+      $this->assertText($feed_item->title, t('Feed item %title found in category 1.', array('%title' => $feed_item->title)));
+    }
+
+    $this->drupalGet('aggregator/categories/' . $category2->cid);
+    $this->assertTitle(t("@title | Drupal", array('@title' => $category2->title)), 'Category title is set.');
+
+    // Confirm that the feed items are displayed for category 2.
+    $result = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, $feed->block, array(':fid' => $feed->fid));
+    foreach ($result as $feed_item) {
+      $this->assertText($feed_item->title, t('Feed item %title found in category 2.', array('%title' => $feed_item->title)));
+    }
+
+    // Verify that nothing shows up in category 3.
+    $this->drupalGet('aggregator/categories/' . $category3->cid);
+    $item = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, 1, array(':fid' => $feed->fid))->fetchObject();
+    $this->assertNoText($item->title, t('Feed item not found in category 3.'));
+
+    // For each category of a feed, ensure feed items have that category, too.
+    foreach ($feed->categories as $cid) {
+      $categorized_count = db_select('aggregator_category_item')
+        ->condition('iid', $feed->items, 'IN')
+        ->condition('cid', $cid)
+        ->countQuery()
+        ->execute()
+        ->fetchField();
+
+      $this->assertEqual($feed->item_count, $categorized_count, t('Total items in feed equal to the total categorized feed items in database'));
+    }
+  }
+
+  /**
+   * Aggregator category block tests.
+   */
+  function testBlocks() {
+    $this->createSampleNodes();
+
     // Simulate form submission on "admin/config/services/aggregator/add/category".
     $edit = array('title' => $this->randomName(10), 'description' => '');
     $this->drupalPost('admin/config/services/aggregator/add/category', $edit, t('Save'));
     $this->assertRaw(t('The category %title has been added.', array('%title' => $edit['title'])), t('The category %title has been added.', array('%title' => $edit['title'])));
 
     $category = db_query("SELECT * FROM {aggregator_category} WHERE title = :title", array(':title' => $edit['title']))->fetch();
-    $this->assertTrue(!empty($category), t('The category found in database.'));
-
-    $link_path = 'aggregator/categories/' . $category->cid;
-    $menu_link = db_query("SELECT * FROM {menu_links} WHERE link_path = :link_path", array(':link_path' => $link_path))->fetch();
-    $this->assertTrue(!empty($menu_link), t('The menu link associated with the category found in database.'));
+    $this->assertTrue(!empty($category), t('Found the category in database.'));
 
+    // Create a feed and assign a category.
     $feed = $this->createFeed();
     db_insert('aggregator_category_feed')
       ->fields(array(
@@ -559,23 +668,64 @@ class CategorizeFeedItemTestCase extends AggregatorTestCase {
       ->execute();
     $this->updateFeedItems($feed, $this->getDefaultFeedItemCount());
     $this->getFeedCategories($feed);
-    $this->assertTrue(!empty($feed->categories), t('The category found in the feed.'));
+    $this->assertTrue(!empty($feed->categories), t('Found the category in the feed.'));
 
-    // For each category of a feed, ensure feed items have that category, too.
-    if (!empty($feed->categories) && !empty($feed->items)) {
-      foreach ($feed->categories as $category) {
-        $categorized_count = db_select('aggregator_category_item')
-          ->condition('iid', $feed->items, 'IN')
-          ->countQuery()
-          ->execute()
-          ->fetchField();
-
-        $this->assertEqual($feed->item_count, $categorized_count, t('Total items in feed equal to the total categorized feed items in database'));
+    // Set number of items to display in block.
+    $edit = array('block' => 4);
+    $this->drupalPost('admin/structure/block/manage/aggregator/category-' . $category->cid . '/configure', $edit, t('Save block'));
+    $this->assertText(t('The block configuration has been saved.'), t('The block configuration has been saved.'));
+
+    $this->drupalGet('admin/structure/block');
+    $this->assertText(t('!title category latest items', array('!title' => $category->title)), t('New category is displayed as possible block'));
+
+    // Set the created block to a specific region.
+    $edit = array();
+    $edit['blocks[aggregator_category-' . $category->cid . '][region]'] = 'sidebar_first';
+    $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
+
+    $this->assertText(t('The block settings have been updated.'), t('Block successfully moved to %region_name region.', array('%region_name' => 'sidebar_first')));
+
+    $this->drupalGet('/user');
+    // Confirm that the block is being displayed.
+    $this->assertText($category->title, t('Block successfully being displayed on the page.'));
+
+    // Confirm that the feed items are displayed.
+    $result = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, 5, array(':fid' => $feed->fid));
+    $count = 0;
+    foreach ($result as $feed_item) {
+      $count++;
+      if ($count == 5) {
+        break;
       }
+      $this->assertLink($feed_item->title, 0, t('Feed item %title in block found.', array('%title' => $feed_item->title)));
     }
 
-    // Delete feed.
-    $this->deleteFeed($feed);
+    // Make sure that the block configuration works.
+    $this->assertNoLink($feed_item->title, t('Feed item %title not found.', array('%title' => $feed_item->title)));
+  }
+
+  /**
+   * Create and rename a aggregator category.
+   */
+  function testRenameCategory() {
+    $this->createSampleNodes();
+
+    $category = $this->createCategory();
+
+    $edit = array('title' => $this->randomName(10), 'description' => '');
+    $this->drupalPost('admin/config/services/aggregator/edit/category/1', $edit, t('Save'));
+    $this->assertRaw(t('The category %title has been updated.', array('%title' => $edit['title'])), t('The category %title has been updated.', array('%title' => $edit['title'])));
+  }
+
+  /**
+   * Add and delete a category.
+   */
+  function testDeleteCategory() {
+    $this->createSampleNodes();
+
+    $category = $this->createCategory();
+    $this->drupalPost('admin/config/services/aggregator/edit/category/1', array(), t('Delete'));
+    $this->assertRaw(t('The category %title has been deleted.', array('%title' => $category->title)), t('The category %title has been deleted.', array('%title' => $category->title)));
   }
 }
 
@@ -614,12 +764,15 @@ class ImportOPMLTestCase extends AggregatorTestCase {
    * Submit form filled with invalid fields.
    */
   function validateImportFormFields() {
+    // Get original list of feeds before testing.
     $before = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
 
+    // Check leaving form blank.
     $edit = array();
     $this->drupalPost('admin/config/services/aggregator/add/opml', $edit, t('Import'));
     $this->assertRaw(t('You must <em>either</em> upload a file or enter a URL.'), t('Error if no fields are filled.'));
 
+    // Check filling both file upload and remote.
     $path = $this->getEmptyOpml();
     $edit = array(
       'files[upload]' => $path,
@@ -628,10 +781,12 @@ class ImportOPMLTestCase extends AggregatorTestCase {
     $this->drupalPost('admin/config/services/aggregator/add/opml', $edit, t('Import'));
     $this->assertRaw(t('You must <em>either</em> upload a file or enter a URL.'), t('Error if both fields are filled.'));
 
+    // Check supplying an invalid url for remote.
     $edit = array('remote' => 'invalidUrl://empty');
     $this->drupalPost('admin/config/services/aggregator/add/opml', $edit, t('Import'));
     $this->assertText(t('This URL is not valid.'), t('Error if the URL is invalid.'));
 
+    // Check to make sure no feeds were added during this test.
     $after = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
     $this->assertEqual($before, $after, t('No feeds were added during the three last form submissions.'));
   }
@@ -640,23 +795,29 @@ class ImportOPMLTestCase extends AggregatorTestCase {
    * Submit form with invalid, empty and valid OPML files.
    */
   function submitImportForm() {
+    // Get original list of feeds before testing.
     $before = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
 
+    // Attempt to upload an invalid OPML file.
     $form['files[upload]'] = $this->getInvalidOpml();
     $this->drupalPost('admin/config/services/aggregator/add/opml', $form, t('Import'));
     $this->assertText(t('No new feed has been added.'), t('Attempting to upload invalid XML.'));
 
+    // Attempt to link to an empty OPML file.
     $edit = array('remote' => file_create_url($this->getEmptyOpml()));
     $this->drupalPost('admin/config/services/aggregator/add/opml', $edit, t('Import'));
     $this->assertText(t('No new feed has been added.'), t('Attempting to load empty OPML from remote URL.'));
 
+    // Check to make sure no feeds were added during this test.
     $after = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
     $this->assertEqual($before, $after, t('No feeds were added during the two last form submissions.'));
 
+    // Remove aggregator feeds and categories.
     db_delete('aggregator_feed')->execute();
     db_delete('aggregator_category')->execute();
     db_delete('aggregator_category_feed')->execute();
 
+    // Create a new aggregator category.
     $category = $this->randomName(10);
     db_insert('aggregator_category')
       ->fields(array(
@@ -666,6 +827,8 @@ class ImportOPMLTestCase extends AggregatorTestCase {
       ))
       ->execute();
 
+    // Test that a valid OPML file that contains duplicate items and invalid 
+    // items are handled correctly when adding and OPML file.
     $feeds[0] = $this->getFeedEditArray();
     $feeds[1] = $this->getFeedEditArray();
     $feeds[2] = $this->getFeedEditArray();
@@ -675,12 +838,16 @@ class ImportOPMLTestCase extends AggregatorTestCase {
       'category[1]'   => $category,
     );
     $this->drupalPost('admin/config/services/aggregator/add/opml', $edit, t('Import'));
+
+    // Check to make sure that duplicate items are detected.
     $this->assertRaw(t('A feed with the URL %url already exists.', array('%url' => $feeds[0]['url'])), t('Verifying that a duplicate URL was identified'));
     $this->assertRaw(t('A feed named %title already exists.', array('%title' => $feeds[1]['title'])), t('Verifying that a duplicate title was identified'));
 
+    // Check to make sure that only the valid feeds were added.
     $after = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
     $this->assertEqual($after, 2, t('Verifying that two distinct feeds were added.'));
 
+    // Check to make sure settings are correct for added feeds.
     $feeds_from_db = db_query("SELECT f.title, f.url, f.refresh, cf.cid FROM {aggregator_feed} f LEFT JOIN {aggregator_category_feed} cf ON f.fid = cf.fid");
     $refresh = $category = TRUE;
     foreach ($feeds_from_db as $feed) {
@@ -858,3 +1025,37 @@ class FeedParserTestCase extends AggregatorTestCase {
   }
 }
 
+
+/**
+ * Test the pluggable architecture.
+ */
+class PluggableTestCase extends AggregatorTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Fetcher, parser and processor',
+      'description' => 'Test the default fetcher, parser and processor and related functions.',
+      'group' => 'Aggregator',
+    );
+  }
+
+  /**
+   * Test the sanitize function.
+   */
+  function testSanitize() {
+    // Verify that the configuration is correct by default.
+    $this->assertFalse(aggregator_sanitize_configuration(), t('Pluggable configuration is correctly set up.'));
+
+    // Specify invalid implementations and verify that they are being corrected.
+    variable_set('aggregator_fetcher', 'broken');
+    $this->assertTrue(aggregator_sanitize_configuration(), t('Pluggable configuration has been sanitized.'));
+    $this->assertEqual(variable_get('aggregator_fetcher', 'aggregator'), 'aggregator', t('Configuration has been corrected.'));
+
+    variable_set('aggregator_parser', 'broken');
+    $this->assertTrue(aggregator_sanitize_configuration(), t('Pluggable configuration has been sanitized.'));
+    $this->assertEqual(variable_get('aggregator_parser', 'aggregator'), 'aggregator', t('Configuration has been corrected.'));
+
+    variable_set('aggregator_processors', array('aggregator', 'broken'));
+    $this->assertTrue(aggregator_sanitize_configuration(), t('Pluggable configuration has been sanitized.'));
+    $this->assertEqual(variable_get('aggregator_processors', array('aggregator')), array('aggregator'), t('Configuration has been corrected.'));
+  }
+}
