| Drupal | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /** 4 * @file 5 * Tests for aggregator.module. 6 */ 7 8 class AggregatorTestCase extends DrupalWebTestCase { 9 function setUp() { 10 parent::setUp('aggregator', 'aggregator_test'); 11 $web_user = $this->drupalCreateUser(array('administer news feeds', 'access news feeds', 'create article content')); 12 $this->drupalLogin($web_user); 13 } 14 15 /** 16 * Create an aggregator feed (simulate form submission on admin/config/services/aggregator/add/feed). 17 * 18 * @param $feed_url 19 * If given, feed will be created with this URL, otherwise /rss.xml will be used. 20 * @return $feed 21 * Full feed object if possible. 22 * 23 * @see getFeedEditArray() 24 */ 25 function createFeed($feed_url = NULL) { 26 $edit = $this->getFeedEditArray($feed_url); 27 $this->drupalPost('admin/config/services/aggregator/add/feed', $edit, t('Save')); 28 $this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title'])), format_string('The feed !name has been added.', array('!name' => $edit['title']))); 29 30 $feed = db_query("SELECT * FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $edit['title'], ':url' => $edit['url']))->fetch(); 31 $this->assertTrue(!empty($feed), 'The feed found in database.'); 32 return $feed; 33 } 34 35 /** 36 * Delete an aggregator feed. 37 * 38 * @param $feed 39 * Feed object representing the feed. 40 */ 41 function deleteFeed($feed) { 42 $this->drupalPost('admin/config/services/aggregator/edit/feed/' . $feed->fid, array(), t('Delete')); 43 $this->assertRaw(t('The feed %title has been deleted.', array('%title' => $feed->title)), 'Feed deleted successfully.'); 44 } 45 46 /** 47 * Return a randomly generated feed edit array. 48 * 49 * @param $feed_url 50 * If given, feed will be created with this URL, otherwise /rss.xml will be used. 51 * @return 52 * A feed array. 53 */ 54 function getFeedEditArray($feed_url = NULL) { 55 $feed_name = $this->randomName(10); 56 if (!$feed_url) { 57 $feed_url = url('rss.xml', array( 58 'query' => array('feed' => $feed_name), 59 'absolute' => TRUE, 60 )); 61 } 62 $edit = array( 63 'title' => $feed_name, 64 'url' => $feed_url, 65 'refresh' => '900', 66 ); 67 return $edit; 68 } 69 70 /** 71 * Return the count of the randomly created feed array. 72 * 73 * @return 74 * Number of feed items on default feed created by createFeed(). 75 */ 76 function getDefaultFeedItemCount() { 77 // Our tests are based off of rss.xml, so let's find out how many elements should be related. 78 $feed_count = db_query_range('SELECT COUNT(*) FROM {node} n WHERE n.promote = 1 AND n.status = 1', 0, variable_get('feed_default_items', 10))->fetchField(); 79 return $feed_count > 10 ? 10 : $feed_count; 80 } 81 82 /** 83 * Update feed items (simulate click to admin/config/services/aggregator/update/$fid). 84 * 85 * @param $feed 86 * Feed object representing the feed. 87 * @param $expected_count 88 * Expected number of feed items. 89 */ 90 function updateFeedItems(&$feed, $expected_count) { 91 // First, let's ensure we can get to the rss xml. 92 $this->drupalGet($feed->url); 93 $this->assertResponse(200, format_string('!url is reachable.', array('!url' => $feed->url))); 94 95 // Attempt to access the update link directly without an access token. 96 $this->drupalGet('admin/config/services/aggregator/update/' . $feed->fid); 97 $this->assertResponse(403); 98 99 // Refresh the feed (simulated link click). 100 $this->drupalGet('admin/config/services/aggregator'); 101 $this->clickLink('update items'); 102 103 // Ensure we have the right number of items. 104 $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid)); 105 $items = array(); 106 $feed->items = array(); 107 foreach ($result as $item) { 108 $feed->items[] = $item->iid; 109 } 110 $feed->item_count = count($feed->items); 111 $this->assertEqual($expected_count, $feed->item_count, format_string('Total items in feed equal to the total items in database (!val1 != !val2)', array('!val1' => $expected_count, '!val2' => $feed->item_count))); 112 } 113 114 /** 115 * Confirm item removal from a feed. 116 * 117 * @param $feed 118 * Feed object representing the feed. 119 */ 120 function removeFeedItems($feed) { 121 $this->drupalPost('admin/config/services/aggregator/remove/' . $feed->fid, array(), t('Remove items')); 122 $this->assertRaw(t('The news items from %title have been removed.', array('%title' => $feed->title)), 'Feed items removed.'); 123 } 124 125 /** 126 * Add and remove feed items and ensure that the count is zero. 127 * 128 * @param $feed 129 * Feed object representing the feed. 130 * @param $expected_count 131 * Expected number of feed items. 132 */ 133 function updateAndRemove($feed, $expected_count) { 134 $this->updateFeedItems($feed, $expected_count); 135 $count = db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(); 136 $this->assertTrue($count); 137 $this->removeFeedItems($feed); 138 $count = db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(); 139 $this->assertTrue($count == 0); 140 } 141 142 /** 143 * Pull feed categories from aggregator_category_feed table. 144 * 145 * @param $feed 146 * Feed object representing the feed. 147 */ 148 function getFeedCategories($feed) { 149 // add the categories to the feed so we can use them 150 $result = db_query('SELECT cid FROM {aggregator_category_feed} WHERE fid = :fid', array(':fid' => $feed->fid)); 151 foreach ($result as $category) { 152 $feed->categories[] = $category->cid; 153 } 154 } 155 156 /** 157 * Pull categories from aggregator_category table. 158 */ 159 function getCategories() { 160 $categories = array(); 161 $result = db_query('SELECT * FROM {aggregator_category}'); 162 foreach ($result as $category) { 163 $categories[$category->cid] = $category; 164 } 165 return $categories; 166 } 167 168 169 /** 170 * Check if the feed name and URL is unique. 171 * 172 * @param $feed_name 173 * String containing the feed name to check. 174 * @param $feed_url 175 * String containing the feed URL to check. 176 * @return 177 * TRUE if feed is unique. 178 */ 179 function uniqueFeed($feed_name, $feed_url) { 180 $result = db_query("SELECT COUNT(*) FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $feed_name, ':url' => $feed_url))->fetchField(); 181 return (1 == $result); 182 } 183 184 /** 185 * Create a valid OPML file from an array of feeds. 186 * 187 * @param $feeds 188 * An array of feeds. 189 * @return 190 * Path to valid OPML file. 191 */ 192 function getValidOpml($feeds) { 193 // Properly escape URLs so that XML parsers don't choke on them. 194 foreach ($feeds as &$feed) { 195 $feed['url'] = htmlspecialchars($feed['url']); 196 } 197 /** 198 * Does not have an XML declaration, must pass the parser. 199 */ 200 $opml = <<<EOF 201 <opml version="1.0"> 202 <head></head> 203 <body> 204 <!-- First feed to be imported. --> 205 <outline text="{$feeds[0]['title']}" xmlurl="{$feeds[0]['url']}" /> 206 207 <!-- Second feed. Test string delimitation and attribute order. --> 208 <outline xmlurl='{$feeds[1]['url']}' text='{$feeds[1]['title']}'/> 209 210 <!-- Test for duplicate URL and title. --> 211 <outline xmlurl="{$feeds[0]['url']}" text="Duplicate URL"/> 212 <outline xmlurl="http://duplicate.title" text="{$feeds[1]['title']}"/> 213 214 <!-- Test that feeds are only added with required attributes. --> 215 <outline text="{$feeds[2]['title']}" /> 216 <outline xmlurl="{$feeds[2]['url']}" /> 217 </body> 218 </opml> 219 EOF; 220 221 $path = 'public://valid-opml.xml'; 222 return file_unmanaged_save_data($opml, $path); 223 } 224 225 /** 226 * Create an invalid OPML file. 227 * 228 * @return 229 * Path to invalid OPML file. 230 */ 231 function getInvalidOpml() { 232 $opml = <<<EOF 233 <opml> 234 <invalid> 235 </opml> 236 EOF; 237 238 $path = 'public://invalid-opml.xml'; 239 return file_unmanaged_save_data($opml, $path); 240 } 241 242 /** 243 * Create a valid but empty OPML file. 244 * 245 * @return 246 * Path to empty OPML file. 247 */ 248 function getEmptyOpml() { 249 $opml = <<<EOF 250 <?xml version="1.0" encoding="utf-8"?> 251 <opml version="1.0"> 252 <head></head> 253 <body> 254 <outline text="Sample text" /> 255 <outline text="Sample text" url="Sample URL" /> 256 </body> 257 </opml> 258 EOF; 259 260 $path = 'public://empty-opml.xml'; 261 return file_unmanaged_save_data($opml, $path); 262 } 263 264 function getRSS091Sample() { 265 return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/aggregator_test_rss091.xml'; 266 } 267 268 function getAtomSample() { 269 // The content of this sample ATOM feed is based directly off of the 270 // example provided in RFC 4287. 271 return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/aggregator_test_atom.xml'; 272 } 273 274 /** 275 * Creates sample article nodes. 276 * 277 * @param $count 278 * (optional) The number of nodes to generate. 279 */ 280 function createSampleNodes($count = 5) { 281 $langcode = LANGUAGE_NONE; 282 // Post $count article nodes. 283 for ($i = 0; $i < $count; $i++) { 284 $edit = array(); 285 $edit['title'] = $this->randomName(); 286 $edit["body[$langcode][0][value]"] = $this->randomName(); 287 $this->drupalPost('node/add/article', $edit, t('Save')); 288 } 289 } 290 } 291 292 /** 293 * Tests aggregator configuration settings. 294 */ 295 class AggregatorConfigurationTestCase extends AggregatorTestCase { 296 public static function getInfo() { 297 return array( 298 'name' => 'Aggregator configuration', 299 'description' => 'Test aggregator settings page.', 300 'group' => 'Aggregator', 301 ); 302 } 303 304 /** 305 * Tests the settings form to ensure the correct default values are used. 306 */ 307 function testSettingsPage() { 308 $edit = array( 309 'aggregator_allowed_html_tags' => '<a>', 310 'aggregator_summary_items' => 10, 311 'aggregator_clear' => 3600, 312 'aggregator_category_selector' => 'select', 313 'aggregator_teaser_length' => 200, 314 ); 315 $this->drupalPost('admin/config/services/aggregator/settings', $edit, t('Save configuration')); 316 $this->assertText(t('The configuration options have been saved.')); 317 318 foreach ($edit as $name => $value) { 319 $this->assertFieldByName($name, $value, format_string('"@name" has correct default value.', array('@name' => $name))); 320 } 321 } 322 } 323 324 class AddFeedTestCase extends AggregatorTestCase { 325 public static function getInfo() { 326 return array( 327 'name' => 'Add feed functionality', 328 'description' => 'Add feed test.', 329 'group' => 'Aggregator' 330 ); 331 } 332 333 /** 334 * Create a feed, ensure that it is unique, check the source, and delete the feed. 335 */ 336 function testAddFeed() { 337 $feed = $this->createFeed(); 338 339 // Check feed data. 340 $this->assertEqual($this->getUrl(), url('admin/config/services/aggregator/add/feed', array('absolute' => TRUE)), 'Directed to correct url.'); 341 $this->assertTrue($this->uniqueFeed($feed->title, $feed->url), 'The feed is unique.'); 342 343 // Check feed source. 344 $this->drupalGet('aggregator/sources/' . $feed->fid); 345 $this->assertResponse(200, 'Feed source exists.'); 346 $this->assertText($feed->title, 'Page title'); 347 $this->drupalGet('aggregator/sources/' . $feed->fid . '/categorize'); 348 $this->assertResponse(200, 'Feed categorization page exists.'); 349 350 // Delete feed. 351 $this->deleteFeed($feed); 352 } 353 354 /** 355 * Tests feeds with very long URLs. 356 */ 357 function testAddLongFeed() { 358 // Create a feed with a URL of > 255 characters. 359 $long_url = "https://www.google.com/search?ix=heb&sourceid=chrome&ie=UTF-8&q=angie+byron#sclient=psy-ab&hl=en&safe=off&source=hp&q=angie+byron&pbx=1&oq=angie+byron&aq=f&aqi=&aql=&gs_sm=3&gs_upl=0l0l0l10534l0l0l0l0l0l0l0l0ll0l0&bav=on.2,or.r_gc.r_pw.r_cp.,cf.osb&fp=a70b6b1f0abe28d8&biw=1629&bih=889&ix=heb"; 360 $feed = $this->createFeed($long_url); 361 362 // Create a second feed of > 255 characters, where the only difference is 363 // after the 255th character. 364 $long_url_2 = "https://www.google.com/search?ix=heb&sourceid=chrome&ie=UTF-8&q=angie+byron#sclient=psy-ab&hl=en&safe=off&source=hp&q=angie+byron&pbx=1&oq=angie+byron&aq=f&aqi=&aql=&gs_sm=3&gs_upl=0l0l0l10534l0l0l0l0l0l0l0l0ll0l0&bav=on.2,or.r_gc.r_pw.r_cp.,cf.osb&fp=a70b6b1f0abe28d8&biw=1629&bih=889"; 365 $feed_2 = $this->createFeed($long_url_2); 366 367 // Check feed data. 368 $this->assertTrue($this->uniqueFeed($feed->title, $feed->url), 'The first long URL feed is unique.'); 369 $this->assertTrue($this->uniqueFeed($feed_2->title, $feed_2->url), 'The second long URL feed is unique.'); 370 371 // Check feed source. 372 $this->drupalGet('aggregator/sources/' . $feed->fid); 373 $this->assertResponse(200, 'Long URL feed source exists.'); 374 $this->assertText($feed->title, 'Page title'); 375 $this->drupalGet('aggregator/sources/' . $feed->fid . '/categorize'); 376 $this->assertResponse(200, 'Long URL feed categorization page exists.'); 377 378 // Delete feeds. 379 $this->deleteFeed($feed); 380 $this->deleteFeed($feed_2); 381 } 382 } 383 384 class CategorizeFeedTestCase extends AggregatorTestCase { 385 public static function getInfo() { 386 return array( 387 'name' => 'Categorize feed functionality', 388 'description' => 'Categorize feed test.', 389 'group' => 'Aggregator' 390 ); 391 } 392 393 /** 394 * Create a feed and make sure you can add more than one category to it. 395 */ 396 function testCategorizeFeed() { 397 398 // Create 2 categories. 399 $category_1 = array('title' => $this->randomName(10), 'description' => ''); 400 $this->drupalPost('admin/config/services/aggregator/add/category', $category_1, t('Save')); 401 $this->assertRaw(t('The category %title has been added.', array('%title' => $category_1['title'])), format_string('The category %title has been added.', array('%title' => $category_1['title']))); 402 403 $category_2 = array('title' => $this->randomName(10), 'description' => ''); 404 $this->drupalPost('admin/config/services/aggregator/add/category', $category_2, t('Save')); 405 $this->assertRaw(t('The category %title has been added.', array('%title' => $category_2['title'])), format_string('The category %title has been added.', array('%title' => $category_2['title']))); 406 407 // Get categories from database. 408 $categories = $this->getCategories(); 409 410 // Create a feed and assign 2 categories to it. 411 $feed = $this->getFeedEditArray(); 412 $feed['block'] = 5; 413 foreach ($categories as $cid => $category) { 414 $feed['category'][$cid] = $cid; 415 } 416 417 // Use aggregator_save_feed() function to save the feed. 418 aggregator_save_feed($feed); 419 $db_feed = db_query("SELECT * FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $feed['title'], ':url' => $feed['url']))->fetch(); 420 421 // Assert the feed has two categories. 422 $this->getFeedCategories($db_feed); 423 $this->assertEqual(count($db_feed->categories), 2, 'Feed has 2 categories'); 424 } 425 } 426 427 class UpdateFeedTestCase extends AggregatorTestCase { 428 public static function getInfo() { 429 return array( 430 'name' => 'Update feed functionality', 431 'description' => 'Update feed test.', 432 'group' => 'Aggregator' 433 ); 434 } 435 436 /** 437 * Create a feed and attempt to update it. 438 */ 439 function testUpdateFeed() { 440 $remamining_fields = array('title', 'url', ''); 441 foreach ($remamining_fields as $same_field) { 442 $feed = $this->createFeed(); 443 444 // Get new feed data array and modify newly created feed. 445 $edit = $this->getFeedEditArray(); 446 $edit['refresh'] = 1800; // Change refresh value. 447 if (isset($feed->{$same_field})) { 448 $edit[$same_field] = $feed->{$same_field}; 449 } 450 $this->drupalPost('admin/config/services/aggregator/edit/feed/' . $feed->fid, $edit, t('Save')); 451 $this->assertRaw(t('The feed %name has been updated.', array('%name' => $edit['title'])), format_string('The feed %name has been updated.', array('%name' => $edit['title']))); 452 453 // Check feed data. 454 $this->assertEqual($this->getUrl(), url('admin/config/services/aggregator/', array('absolute' => TRUE))); 455 $this->assertTrue($this->uniqueFeed($edit['title'], $edit['url']), 'The feed is unique.'); 456 457 // Check feed source. 458 $this->drupalGet('aggregator/sources/' . $feed->fid); 459 $this->assertResponse(200, 'Feed source exists.'); 460 $this->assertText($edit['title'], 'Page title'); 461 462 // Delete feed. 463 $feed->title = $edit['title']; // Set correct title so deleteFeed() will work. 464 $this->deleteFeed($feed); 465 } 466 } 467 } 468 469 class RemoveFeedTestCase extends AggregatorTestCase { 470 public static function getInfo() { 471 return array( 472 'name' => 'Remove feed functionality', 473 'description' => 'Remove feed test.', 474 'group' => 'Aggregator' 475 ); 476 } 477 478 /** 479 * Remove a feed and ensure that all it services are removed. 480 */ 481 function testRemoveFeed() { 482 $feed = $this->createFeed(); 483 484 // Delete feed. 485 $this->deleteFeed($feed); 486 487 // Check feed source. 488 $this->drupalGet('aggregator/sources/' . $feed->fid); 489 $this->assertResponse(404, 'Deleted feed source does not exists.'); 490 491 // Check database for feed. 492 $result = db_query("SELECT COUNT(*) FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $feed->title, ':url' => $feed->url))->fetchField(); 493 $this->assertFalse($result, 'Feed not found in database'); 494 } 495 } 496 497 class UpdateFeedItemTestCase extends AggregatorTestCase { 498 public static function getInfo() { 499 return array( 500 'name' => 'Update feed item functionality', 501 'description' => 'Update feed items from a feed.', 502 'group' => 'Aggregator' 503 ); 504 } 505 506 /** 507 * Test running "update items" from the 'admin/config/services/aggregator' page. 508 */ 509 function testUpdateFeedItem() { 510 $this->createSampleNodes(); 511 512 // Create a feed and test updating feed items if possible. 513 $feed = $this->createFeed(); 514 if (!empty($feed)) { 515 $this->updateFeedItems($feed, $this->getDefaultFeedItemCount()); 516 $this->removeFeedItems($feed); 517 } 518 519 // Delete feed. 520 $this->deleteFeed($feed); 521 522 // Test updating feed items without valid timestamp information. 523 $edit = array( 524 'title' => "Feed without publish timestamp", 525 'url' => $this->getRSS091Sample(), 526 ); 527 528 $this->drupalGet($edit['url']); 529 $this->assertResponse(array(200), format_string('URL !url is accessible', array('!url' => $edit['url']))); 530 531 $this->drupalPost('admin/config/services/aggregator/add/feed', $edit, t('Save')); 532 $this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title'])), format_string('The feed !name has been added.', array('!name' => $edit['title']))); 533 534 $feed = db_query("SELECT * FROM {aggregator_feed} WHERE url = :url", array(':url' => $edit['url']))->fetchObject(); 535 536 aggregator_refresh($feed); 537 $before = db_query('SELECT timestamp FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(); 538 539 // Sleep for 3 second. 540 sleep(3); 541 db_update('aggregator_feed') 542 ->condition('fid', $feed->fid) 543 ->fields(array( 544 'checked' => 0, 545 'hash' => '', 546 'etag' => '', 547 'modified' => 0, 548 )) 549 ->execute(); 550 aggregator_refresh($feed); 551 552 $after = db_query('SELECT timestamp FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(); 553 $this->assertTrue($before === $after, format_string('Publish timestamp of feed item was not updated (!before === !after)', array('!before' => $before, '!after' => $after))); 554 } 555 } 556 557 class RemoveFeedItemTestCase extends AggregatorTestCase { 558 public static function getInfo() { 559 return array( 560 'name' => 'Remove feed item functionality', 561 'description' => 'Remove feed items from a feed.', 562 'group' => 'Aggregator' 563 ); 564 } 565 566 /** 567 * Test running "remove items" from the 'admin/config/services/aggregator' page. 568 */ 569 function testRemoveFeedItem() { 570 // Create a bunch of test feeds. 571 $feed_urls = array(); 572 // No last-modified, no etag. 573 $feed_urls[] = url('aggregator/test-feed', array('absolute' => TRUE)); 574 // Last-modified, but no etag. 575 $feed_urls[] = url('aggregator/test-feed/1', array('absolute' => TRUE)); 576 // No Last-modified, but etag. 577 $feed_urls[] = url('aggregator/test-feed/0/1', array('absolute' => TRUE)); 578 // Last-modified and etag. 579 $feed_urls[] = url('aggregator/test-feed/1/1', array('absolute' => TRUE)); 580 581 foreach ($feed_urls as $feed_url) { 582 $feed = $this->createFeed($feed_url); 583 // Update and remove items two times in a row to make sure that removal 584 // resets all 'modified' information (modified, etag, hash) and allows for 585 // immediate update. 586 $this->updateAndRemove($feed, 4); 587 $this->updateAndRemove($feed, 4); 588 $this->updateAndRemove($feed, 4); 589 // Delete feed. 590 $this->deleteFeed($feed); 591 } 592 } 593 } 594 595 class CategorizeFeedItemTestCase extends AggregatorTestCase { 596 public static function getInfo() { 597 return array( 598 'name' => 'Categorize feed item functionality', 599 'description' => 'Test feed item categorization.', 600 'group' => 'Aggregator' 601 ); 602 } 603 604 /** 605 * If a feed has a category, make sure that the children inherit that 606 * categorization. 607 */ 608 function testCategorizeFeedItem() { 609 $this->createSampleNodes(); 610 611 // Simulate form submission on "admin/config/services/aggregator/add/category". 612 $edit = array('title' => $this->randomName(10), 'description' => ''); 613 $this->drupalPost('admin/config/services/aggregator/add/category', $edit, t('Save')); 614 $this->assertRaw(t('The category %title has been added.', array('%title' => $edit['title'])), format_string('The category %title has been added.', array('%title' => $edit['title']))); 615 616 $category = db_query("SELECT * FROM {aggregator_category} WHERE title = :title", array(':title' => $edit['title']))->fetch(); 617 $this->assertTrue(!empty($category), 'The category found in database.'); 618 619 $link_path = 'aggregator/categories/' . $category->cid; 620 $menu_link = db_query("SELECT * FROM {menu_links} WHERE link_path = :link_path", array(':link_path' => $link_path))->fetch(); 621 $this->assertTrue(!empty($menu_link), 'The menu link associated with the category found in database.'); 622 623 $feed = $this->createFeed(); 624 db_insert('aggregator_category_feed') 625 ->fields(array( 626 'cid' => $category->cid, 627 'fid' => $feed->fid, 628 )) 629 ->execute(); 630 $this->updateFeedItems($feed, $this->getDefaultFeedItemCount()); 631 $this->getFeedCategories($feed); 632 $this->assertTrue(!empty($feed->categories), 'The category found in the feed.'); 633 634 // For each category of a feed, ensure feed items have that category, too. 635 if (!empty($feed->categories) && !empty($feed->items)) { 636 foreach ($feed->categories as $category) { 637 $categorized_count = db_select('aggregator_category_item') 638 ->condition('iid', $feed->items, 'IN') 639 ->countQuery() 640 ->execute() 641 ->fetchField(); 642 643 $this->assertEqual($feed->item_count, $categorized_count, 'Total items in feed equal to the total categorized feed items in database'); 644 } 645 } 646 647 // Delete feed. 648 $this->deleteFeed($feed); 649 } 650 } 651 652 class ImportOPMLTestCase extends AggregatorTestCase { 653 public static function getInfo() { 654 return array( 655 'name' => 'Import feeds from OPML functionality', 656 'description' => 'Test OPML import.', 657 'group' => 'Aggregator', 658 ); 659 } 660 661 /** 662 * Open OPML import form. 663 */ 664 function openImportForm() { 665 db_delete('aggregator_category')->execute(); 666 667 $category = $this->randomName(10); 668 $cid = db_insert('aggregator_category') 669 ->fields(array( 670 'title' => $category, 671 'description' => '', 672 )) 673 ->execute(); 674 675 $this->drupalGet('admin/config/services/aggregator/add/opml'); 676 $this->assertText('A single OPML document may contain a collection of many feeds.', 'Found OPML help text.'); 677 $this->assertField('files[upload]', 'Found file upload field.'); 678 $this->assertField('remote', 'Found Remote URL field.'); 679 $this->assertField('refresh', 'Found Refresh field.'); 680 $this->assertFieldByName("category[$cid]", $cid, 'Found category field.'); 681 } 682 683 /** 684 * Submit form filled with invalid fields. 685 */ 686 function validateImportFormFields() { 687 $before = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField(); 688 689 $edit = array(); 690 $this->drupalPost('admin/config/services/aggregator/add/opml', $edit, t('Import')); 691 $this->assertRaw(t('You must <em>either</em> upload a file or enter a URL.'), 'Error if no fields are filled.'); 692 693 $path = $this->getEmptyOpml(); 694 $edit = array( 695 'files[upload]' => $path, 696 'remote' => file_create_url($path), 697 ); 698 $this->drupalPost('admin/config/services/aggregator/add/opml', $edit, t('Import')); 699 $this->assertRaw(t('You must <em>either</em> upload a file or enter a URL.'), 'Error if both fields are filled.'); 700 701 $edit = array('remote' => 'invalidUrl://empty'); 702 $this->drupalPost('admin/config/services/aggregator/add/opml', $edit, t('Import')); 703 $this->assertText(t('This URL is not valid.'), 'Error if the URL is invalid.'); 704 705 $after = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField(); 706 $this->assertEqual($before, $after, 'No feeds were added during the three last form submissions.'); 707 } 708 709 /** 710 * Submit form with invalid, empty and valid OPML files. 711 */ 712 function submitImportForm() { 713 $before = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField(); 714 715 $form['files[upload]'] = $this->getInvalidOpml(); 716 $this->drupalPost('admin/config/services/aggregator/add/opml', $form, t('Import')); 717 $this->assertText(t('No new feed has been added.'), 'Attempting to upload invalid XML.'); 718 719 $edit = array('remote' => file_create_url($this->getEmptyOpml())); 720 $this->drupalPost('admin/config/services/aggregator/add/opml', $edit, t('Import')); 721 $this->assertText(t('No new feed has been added.'), 'Attempting to load empty OPML from remote URL.'); 722 723 $after = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField(); 724 $this->assertEqual($before, $after, 'No feeds were added during the two last form submissions.'); 725 726 db_delete('aggregator_feed')->execute(); 727 db_delete('aggregator_category')->execute(); 728 db_delete('aggregator_category_feed')->execute(); 729 730 $category = $this->randomName(10); 731 db_insert('aggregator_category') 732 ->fields(array( 733 'cid' => 1, 734 'title' => $category, 735 'description' => '', 736 )) 737 ->execute(); 738 739 $feeds[0] = $this->getFeedEditArray(); 740 $feeds[1] = $this->getFeedEditArray(); 741 $feeds[2] = $this->getFeedEditArray(); 742 $edit = array( 743 'files[upload]' => $this->getValidOpml($feeds), 744 'refresh' => '900', 745 'category[1]' => $category, 746 ); 747 $this->drupalPost('admin/config/services/aggregator/add/opml', $edit, t('Import')); 748 $this->assertRaw(t('A feed with the URL %url already exists.', array('%url' => $feeds[0]['url'])), 'Verifying that a duplicate URL was identified'); 749 $this->assertRaw(t('A feed named %title already exists.', array('%title' => $feeds[1]['title'])), 'Verifying that a duplicate title was identified'); 750 751 $after = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField(); 752 $this->assertEqual($after, 2, 'Verifying that two distinct feeds were added.'); 753 754 $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"); 755 $refresh = $category = TRUE; 756 foreach ($feeds_from_db as $feed) { 757 $title[$feed->url] = $feed->title; 758 $url[$feed->title] = $feed->url; 759 $category = $category && $feed->cid == 1; 760 $refresh = $refresh && $feed->refresh == 900; 761 } 762 763 $this->assertEqual($title[$feeds[0]['url']], $feeds[0]['title'], 'First feed was added correctly.'); 764 $this->assertEqual($url[$feeds[1]['title']], $feeds[1]['url'], 'Second feed was added correctly.'); 765 $this->assertTrue($refresh, 'Refresh times are correct.'); 766 $this->assertTrue($category, 'Categories are correct.'); 767 } 768 769 function testOPMLImport() { 770 $this->openImportForm(); 771 $this->validateImportFormFields(); 772 $this->submitImportForm(); 773 } 774 } 775 776 class AggregatorCronTestCase extends AggregatorTestCase { 777 public static function getInfo() { 778 return array( 779 'name' => 'Update on cron functionality', 780 'description' => 'Update feeds on cron.', 781 'group' => 'Aggregator' 782 ); 783 } 784 785 /** 786 * Add feeds update them on cron. 787 */ 788 public function testCron() { 789 // Create feed and test basic updating on cron. 790 global $base_url; 791 $key = variable_get('cron_key', 'drupal'); 792 $this->createSampleNodes(); 793 $feed = $this->createFeed(); 794 $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key))); 795 $this->assertEqual(5, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(), 'Expected number of items in database.'); 796 $this->removeFeedItems($feed); 797 $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(), 'Expected number of items in database.'); 798 $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key))); 799 $this->assertEqual(5, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(), 'Expected number of items in database.'); 800 801 // Test feed locking when queued for update. 802 $this->removeFeedItems($feed); 803 db_update('aggregator_feed') 804 ->condition('fid', $feed->fid) 805 ->fields(array( 806 'queued' => REQUEST_TIME, 807 )) 808 ->execute(); 809 $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key))); 810 $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(), 'Expected number of items in database.'); 811 db_update('aggregator_feed') 812 ->condition('fid', $feed->fid) 813 ->fields(array( 814 'queued' => 0, 815 )) 816 ->execute(); 817 $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key))); 818 $this->assertEqual(5, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(), 'Expected number of items in database.'); 819 } 820 } 821 822 class AggregatorRenderingTestCase extends AggregatorTestCase { 823 public static function getInfo() { 824 return array( 825 'name' => 'Checks display of aggregator items', 826 'description' => 'Checks display of aggregator items on the page.', 827 'group' => 'Aggregator' 828 ); 829 } 830 831 /** 832 * Add a feed block to the page and checks its links. 833 * 834 * TODO: Test the category block as well. 835 */ 836 public function testBlockLinks() { 837 // Create feed. 838 $this->createSampleNodes(); 839 $feed = $this->createFeed(); 840 $this->updateFeedItems($feed, $this->getDefaultFeedItemCount()); 841 842 // Place block on page (@see block.test:moveBlockToRegion()) 843 // Need admin user to be able to access block admin. 844 $this->admin_user = $this->drupalCreateUser(array( 845 'administer blocks', 846 'access administration pages', 847 'administer news feeds', 848 'access news feeds', 849 )); 850 $this->drupalLogin($this->admin_user); 851 852 // Prepare to use the block admin form. 853 $block = array( 854 'module' => 'aggregator', 855 'delta' => 'feed-' . $feed->fid, 856 'title' => $feed->title, 857 ); 858 $region = 'footer'; 859 $edit = array(); 860 $edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = $region; 861 // Check the feed block is available in the block list form. 862 $this->drupalGet('admin/structure/block'); 863 $this->assertFieldByName('blocks[' . $block['module'] . '_' . $block['delta'] . '][region]', '', 'Aggregator feed block is available for positioning.'); 864 // Position it. 865 $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); 866 $this->assertText(t('The block settings have been updated.'), format_string('Block successfully moved to %region_name region.', array( '%region_name' => $region))); 867 // Confirm that the block is now being displayed on pages. 868 $this->drupalGet('node'); 869 $this->assertText(t($block['title']), 'Feed block is displayed on the page.'); 870 871 // Find the expected read_more link. 872 $href = 'aggregator/sources/' . $feed->fid; 873 $links = $this->xpath('//a[@href = :href]', array(':href' => url($href))); 874 $this->assert(isset($links[0]), format_string('Link to href %href found.', array('%href' => $href))); 875 876 // Visit that page. 877 $this->drupalGet($href); 878 $correct_titles = $this->xpath('//h1[normalize-space(text())=:title]', array(':title' => $feed->title)); 879 $this->assertFalse(empty($correct_titles), 'Aggregator feed page is available and has the correct title.'); 880 881 // Set the number of news items to 0 to test that the block does not show 882 // up. 883 $feed->block = 0; 884 aggregator_save_feed((array) $feed); 885 // It is nescessary to flush the cache after saving the number of items. 886 drupal_flush_all_caches(); 887 // Check that the block is no longer displayed. 888 $this->drupalGet('node'); 889 $this->assertNoText(t($block['title']), 'Feed block is not displayed on the page when number of items is set to 0.'); 890 } 891 892 /** 893 * Create a feed and check that feed's page. 894 */ 895 public function testFeedPage() { 896 // Increase the number of items published in the rss.xml feed so we have 897 // enough articles to test paging. 898 variable_set('feed_default_items', 30); 899 900 // Create a feed with 30 items. 901 $this->createSampleNodes(30); 902 $feed = $this->createFeed(); 903 $this->updateFeedItems($feed, 30); 904 905 // Check for the presence of a pager. 906 $this->drupalGet('aggregator/sources/' . $feed->fid); 907 $elements = $this->xpath("//ul[@class=:class]", array(':class' => 'pager')); 908 $this->assertTrue(!empty($elements), 'Individual source page contains a pager.'); 909 910 // Reset the number of items in rss.xml to the default value. 911 variable_set('feed_default_items', 10); 912 } 913 } 914 915 /** 916 * Tests for feed parsing. 917 */ 918 class FeedParserTestCase extends AggregatorTestCase { 919 public static function getInfo() { 920 return array( 921 'name' => 'Feed parser functionality', 922 'description' => 'Test the built-in feed parser with valid feed samples.', 923 'group' => 'Aggregator', 924 ); 925 } 926 927 function setUp() { 928 parent::setUp(); 929 // Do not remove old aggregator items during these tests, since our sample 930 // feeds have hardcoded dates in them (which may be expired when this test 931 // is run). 932 variable_set('aggregator_clear', AGGREGATOR_CLEAR_NEVER); 933 } 934 935 /** 936 * Test a feed that uses the RSS 0.91 format. 937 */ 938 function testRSS091Sample() { 939 $feed = $this->createFeed($this->getRSS091Sample()); 940 aggregator_refresh($feed); 941 $this->drupalGet('aggregator/sources/' . $feed->fid); 942 $this->assertResponse(200, format_string('Feed %name exists.', array('%name' => $feed->title))); 943 $this->assertText('First example feed item title'); 944 $this->assertLinkByHref('http://example.com/example-turns-one'); 945 $this->assertText('First example feed item description.'); 946 947 // Several additional items that include elements over 255 characters. 948 $this->assertRaw("Second example feed item title."); 949 $this->assertText('Long link feed item title'); 950 $this->assertText('Long link feed item description'); 951 $this->assertLinkByHref('http://example.com/tomorrow/and/tomorrow/and/tomorrow/creeps/in/this/petty/pace/from/day/to/day/to/the/last/syllable/of/recorded/time/and/all/our/yesterdays/have/lighted/fools/the/way/to/dusty/death/out/out/brief/candle/life/is/but/a/walking/shadow/a/poor/player/that/struts/and/frets/his/hour/upon/the/stage/and/is/heard/no/more/it/is/a/tale/told/by/an/idiot/full/of/sound/and/fury/signifying/nothing'); 952 $this->assertText('Long author feed item title'); 953 $this->assertText('Long author feed item description'); 954 $this->assertLinkByHref('http://example.com/long/author'); 955 } 956 957 /** 958 * Test a feed that uses the Atom format. 959 */ 960 function testAtomSample() { 961 $feed = $this->createFeed($this->getAtomSample()); 962 aggregator_refresh($feed); 963 $this->drupalGet('aggregator/sources/' . $feed->fid); 964 $this->assertResponse(200, format_string('Feed %name exists.', array('%name' => $feed->title))); 965 $this->assertText('Atom-Powered Robots Run Amok'); 966 $this->assertLinkByHref('http://example.org/2003/12/13/atom03'); 967 $this->assertText('Some text.'); 968 $this->assertEqual('urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a', db_query('SELECT guid FROM {aggregator_item} WHERE link = :link', array(':link' => 'http://example.org/2003/12/13/atom03'))->fetchField(), 'Atom entry id element is parsed correctly.'); 969 } 970 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title