checkFilesDirectory(); } function tearDown() { // Capture any (remaining) watchdog errors. $this->assertNoWatchdogErrors(); // Reset the watchdog seen IDs for the next test run. $this->seen_ids = array(); parent::tearDown(); } /** * Check the files directory is created (massive fails if not done). * * @todo This can be removed when http://drupal.org/node/654752 is fixed. */ protected function checkFilesDirectory() { if (!xmlsitemap_check_directory()) { $this->fail(t('Sitemap directory was found and writable for testing.')); } } protected function drupalGetSitemap($language = LANGUAGE_NONE, $regenerate = FALSE) { if ($regenerate) { $this->regenerateSitemap(); } $this->drupalGet('sitemap.xml', array('language' => xmlsitemap_language_load($language))); $this->assertResponse(200); } /** * Regenerate the sitemap by setting the regenerate flag and running cron. */ protected function regenerateSitemap() { variable_set('xmlsitemap_regenerate_needed', TRUE); variable_set('xmlsitemap_generated_last', 0); module_load_include('inc', 'xmlsitemap'); xmlsitemap_regenerate(); $this->assertTrue(variable_get('xmlsitemap_generated_last', 0) && !variable_get('xmlsitemap_regenerate_needed', FALSE), t('XML sitemaps regenerated and flag cleared.')); } protected function assertSitemapLink($entity_type, $entity_id = NULL) { if (is_array($entity_type)) { $links = xmlsitemap_link_load_multiple($entity_type); $link = $links ? reset($links) : FALSE; } else { $link = xmlsitemap_link_load($entity_type, $entity_id); } $this->assertTrue(is_array($link), 'Link loaded.'); return $link; } protected function assertNoSitemapLink($entity_type, $entity_id = NULL) { if (is_array($entity_type)) { $links = xmlsitemap_link_load_multiple($entity_type); $link = $links ? reset($links) : FALSE; } else { $link = xmlsitemap_link_load($entity_type, $entity_id); } $this->assertFalse($link, 'Link not loaded.'); return $link; } protected function assertSitemapLinkVisible() { $links = func_get_args(); foreach ($links as $link) { $this->assertTrue($link && $link['access'] && $link['status'], 'Sitemap link is visible.'); } } protected function assertSitemapLinkNotVisible() { $links = func_get_args(); foreach ($links as $link) { $this->assertTrue($link && !($link['access'] && $link['status']), 'Sitemap link is not visible.'); } } protected function assertSitemapLinkValues(array $link, array $conditions) { foreach ($conditions as $key => $value) { if ($value === NULL || $link[$key] === NULL) { // For nullable fields, always check for identical values (===). $this->assertIdentical($link[$key], $value, t('Identical values for link field @key.', array('@key' => $key))); } else { // Otherwise check simple equality (==). $this->assertEqual($link[$key], $value, t('Equal values for link field @key.', array('@key' => $key))); } } } protected function assertNotSitemapLinkValues(array $link, array $conditions) { foreach ($conditions as $key => $value) { if ($value === NULL || $link[$key] === NULL) { // For nullable fields, always check for identical values (===). $this->assertNotIdentical($link[$key], $value, t('Not identical values for link field @key.', array('@key' => $key))); } else { // Otherwise check simple equality (==). $this->assertNotEqual($link[$key], $value, t('Not equal values for link field @key.', array('@key' => $key))); } } } protected function assertRawSitemapLinks() { $links = func_get_args(); foreach ($links as $link) { $path = url($link['loc'], array('language' => xmlsitemap_language_load($link['language']), 'absolute' => TRUE)); $this->assertRaw($link['loc'], t('Link %path found in the sitemap.', array('%path' => $path))); } } protected function assertNoRawSitemapLinks() { $links = func_get_args(); foreach ($links as $link) { $path = url($link['loc'], array('language' => xmlsitemap_language_load($link['language']), 'absolute' => TRUE)); $this->assertNoRaw($link['loc'], t('Link %path not found in the sitemap.', array('%path' => $path))); } } protected function addSitemapLink(array $link = array()) { $last_id = &drupal_static(__FUNCTION__, 1); $link += array( 'type' => 'testing', 'id' => $last_id, ); // Make the default path easier to read than a random string. $link += array('loc' => $link['type'] . '-' . $link['id']); $last_id = $link['id'] + 1; xmlsitemap_save_link($link); return $link; } protected function assertFlag($variable, $assert_value = TRUE, $reset_if_true = TRUE) { $value = xmlsitemap_var($variable); if ($reset_if_true && $value) { variable_set('xmlsitemap_' . $variable, FALSE); } return $this->assertEqual($value, $assert_value, "xmlsitemap_$variable is " . ($assert_value ? 'TRUE' : 'FALSE')); } protected function assertXMLSitemapProblems($problem_text = FALSE) { $this->drupalGet('admin/config/search/xmlsitemap'); $this->assertText(t('One or more problems were detected with your XML sitemap configuration')); if ($problem_text) { $this->assertText($problem_text); } } protected function assertNoXMLSitemapProblems() { $this->drupalGet('admin/config/search/xmlsitemap'); $this->assertNoText(t('One or more problems were detected with your XML sitemap configuration')); } /** * Fetch all seen watchdog messages. * * @todo Add unit tests for this function. */ protected function getWatchdogMessages(array $conditions = array(), $reset = FALSE) { static $levels; if (!module_exists('dblog') || $reset) { $this->seen_ids = array(); return; } if (!isset($levels)) { $levels = watchdog_severity_levels(); } $query = db_select('watchdog'); $query->fields('watchdog', array('wid', 'type', 'severity', 'message', 'variables', 'timestamp')); foreach ($conditions as $field => $value) { if ($field == 'variables' && is_array($value)) { $value = serialize($value); } $query->condition($field, $value); } if ($this->seen_ids) { $query->condition('wid', $this->seen_ids, 'NOT IN'); } $query->orderBy('timestamp'); $messages = $query->execute()->fetchAllAssoc('wid'); foreach ($messages as &$message) { $message->variables = unserialize($message->variables); if (!is_array($message->variables)) { $message->variables = array(); } $message->text = $message->timestamp . ' - ' . $levels[$message->severity] . ' - ' . $message->type . ' - ' . t($message->message, $message->variables); } $this->seen_ids = array_merge($this->seen_ids, array_keys($messages)); return $messages; } protected function assertWatchdogMessage(array $conditions, $message = 'Watchdog message found.') { $this->assertTrue($this->getWatchdogMessages($conditions), $message); } protected function assertNoWatchdogMessage(array $conditions, $message = 'Watchdog message not found.') { $this->assertFalse($this->getWatchdogMessages($conditions), $message); } /** * Check that there were no watchdog errors or worse. */ protected function assertNoWatchdogErrors() { $messages = $this->getWatchdogMessages(); $verbose = array(); foreach ($messages as $message) { if (in_array($message->severity, array(WATCHDOG_EMERG, WATCHDOG_ALERT, WATCHDOG_CRITICAL, WATCHDOG_ERROR, WATCHDOG_WARNING))) { $this->fail($message->text); } $verbose[] = $message->text; } if ($verbose) { array_unshift($verbose, '

Watchdog messages

'); $this->verbose(implode("
", $verbose)); } } } class XMLSitemapUnitTest extends XMLSitemapTestHelper { public static function getInfo() { return array( 'name' => 'XML sitemap unit tests', 'description' => 'Unit tests for the XML sitemap module.', 'group' => 'XML sitemap', ); } function setUp() { parent::setUp('xmlsitemap'); } function testAssertFlag() { variable_set('xmlsitemap_rebuild_needed', TRUE); $this->assertTrue(xmlsitemap_var('rebuild_needed')); $this->assertTrue($this->assertFlag('rebuild_needed', TRUE, FALSE)); $this->assertTrue(xmlsitemap_var('rebuild_needed')); $this->assertTrue($this->assertFlag('rebuild_needed', TRUE, TRUE)); $this->assertFalse(xmlsitemap_var('rebuild_needed')); $this->assertTrue($this->assertFlag('rebuild_needed', FALSE, FALSE)); $this->assertFalse(xmlsitemap_var('rebuild_needed')); } /** * Tests for xmlsitemap_get_changefreq(). */ function testGetChangefreq() { // The test values. $values = array( 0, mt_rand(1, XMLSITEMAP_FREQUENCY_ALWAYS), mt_rand(XMLSITEMAP_FREQUENCY_ALWAYS + 1, XMLSITEMAP_FREQUENCY_HOURLY), mt_rand(XMLSITEMAP_FREQUENCY_HOURLY + 1, XMLSITEMAP_FREQUENCY_DAILY), mt_rand(XMLSITEMAP_FREQUENCY_DAILY + 1, XMLSITEMAP_FREQUENCY_WEEKLY), mt_rand(XMLSITEMAP_FREQUENCY_WEEKLY + 1, XMLSITEMAP_FREQUENCY_MONTHLY), mt_rand(XMLSITEMAP_FREQUENCY_MONTHLY + 1, XMLSITEMAP_FREQUENCY_YEARLY), mt_rand(XMLSITEMAP_FREQUENCY_YEARLY + 1, mt_getrandmax()), ); // The expected values. $expected = array( FALSE, 'always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never', ); foreach ($values as $i => $value) { $actual = xmlsitemap_get_changefreq($value); $this->assertIdentical($actual, $expected[$i]); } } /** * Tests for xmlsitemap_get_chunk_count(). */ function testGetChunkCount() { // Set a low chunk size for testing. variable_set('xmlsitemap_chunk_size', 4); // Make the total number of links just equal to the chunk size. $count = db_query("SELECT COUNT(id) FROM {xmlsitemap}")->fetchField(); for ($i = $count; $i < 4; $i++) { $this->addSitemapLink(); $this->assertEqual(xmlsitemap_get_chunk_count(TRUE), 1); } $this->assertEqual(db_query("SELECT COUNT(id) FROM {xmlsitemap}")->fetchField(), 4); // Add a disabled link, should not change the chunk count. $this->addSitemapLink(array('status' => FALSE)); $this->assertEqual(xmlsitemap_get_chunk_count(TRUE), 1); // Add a visible link, should finally bump up the chunk count. $this->addSitemapLink(); $this->assertEqual(xmlsitemap_get_chunk_count(TRUE), 2); // Change all links to disabled. The chunk count should be 1 not 0. db_query("UPDATE {xmlsitemap} SET status = 0"); $this->assertEqual(xmlsitemap_get_chunk_count(TRUE), 1); $this->assertEqual(xmlsitemap_get_link_count(), 0); // Delete all links. The chunk count should be 1 not 0. db_query("DELETE FROM {xmlsitemap}"); $this->assertEqual(db_query("SELECT COUNT(id) FROM {xmlsitemap}")->fetchField(), 0); $this->assertEqual(xmlsitemap_get_chunk_count(TRUE), 1); } //function testGetChunkFile() { //} // //function testGetChunkSize() { //} // //function testGetLinkCount() { //} /** * Tests for xmlsitemap_calculate_changereq(). */ function testCalculateChangefreq() { // The test values. $values = array( array(), array(REQUEST_TIME), array(REQUEST_TIME, REQUEST_TIME - 200), array(REQUEST_TIME - 200, REQUEST_TIME, REQUEST_TIME - 600), ); // Expected values. $expected = array(0, 0, 200, 300); foreach ($values as $i => $value) { $actual = xmlsitemap_calculate_changefreq($value); $this->assertEqual($actual, $expected[$i]); } } /** * Test for xmlsitemap_recalculate_changefreq(). */ function testRecalculateChangefreq() { // The starting test value. $value = array('lastmod' => REQUEST_TIME - 1000, 'changefreq' => 0, 'changecount' => 0); // Expected values. $expecteds = array( array('lastmod' => REQUEST_TIME, 'changefreq' => 1000, 'changecount' => 1), array('lastmod' => REQUEST_TIME, 'changefreq' => 500, 'changecount' => 2), array('lastmod' => REQUEST_TIME, 'changefreq' => 333, 'changecount' => 3), ); foreach ($expecteds as $expected) { xmlsitemap_recalculate_changefreq($value); $this->assertEqual($value, $expected); } } /** * Tests for xmlsitemap_switch_user and xmlsitemap_restore_user(). */ function testSwitchUser() { global $user; $original_user = $user; $new_user = $this->drupalCreateUser(); // Switch to a new valid user. $this->assertEqual(xmlsitemap_switch_user($new_user), TRUE); $this->assertEqual($user->uid, $new_user->uid); // Switch again to the anonymous user. $this->assertEqual(xmlsitemap_switch_user(0), TRUE); $this->assertEqual($user->uid, 0); // Switch again to the new user. $this->assertEqual(xmlsitemap_switch_user($new_user->uid), TRUE); $this->assertEqual($user->uid, $new_user->uid); // Test that after two switches the original user was restored. $this->assertEqual(xmlsitemap_restore_user(), TRUE); $this->assertEqual($user->uid, $original_user->uid); // Attempt to switch to the same user. $this->assertEqual(xmlsitemap_switch_user($original_user->uid), FALSE); $this->assertEqual($user->uid, $original_user->uid); $this->assertEqual(xmlsitemap_restore_user(), FALSE); $this->assertEqual($user->uid, $original_user->uid); // Attempt to switch to an invalid user ID. $invalid_uid = db_query("SELECT MAX(uid) FROM {users}")->fetchField() + 100; $this->assertEqual(xmlsitemap_switch_user($invalid_uid), FALSE); $this->assertEqual($user->uid, $original_user->uid); $this->assertEqual(xmlsitemap_restore_user(), FALSE); $this->assertEqual($user->uid, $original_user->uid); // Attempt user switching when the original user is anonymous. $user = drupal_anonymous_user(); $this->assertEqual(xmlsitemap_switch_user(0), FALSE); $this->assertEqual($user->uid, 0); $this->assertEqual(xmlsitemap_restore_user(), FALSE); $this->assertEqual($user->uid, 0); } //function testLoadLink() { //} /** * Tests for xmlsitemap_save_link(). */ function testSaveLink() { $link = array('type' => 'testing', 'id' => 1, 'loc' => 'testing', 'status' => 1); xmlsitemap_save_link($link); $this->assertFlag('regenerate_needed', TRUE); $link['status'] = 0; xmlsitemap_save_link($link); $this->assertFlag('regenerate_needed', TRUE); $link['priority'] = 0.5; $link['loc'] = 'new_location'; $link['status'] = 1; xmlsitemap_save_link($link); $this->assertFlag('regenerate_needed', TRUE); $link['priority'] = 0.0; xmlsitemap_save_link($link); $this->assertFlag('regenerate_needed', TRUE); $link['priority'] = 0.1; xmlsitemap_save_link($link); $this->assertFlag('regenerate_needed', TRUE); $link['priority'] = 1.0; xmlsitemap_save_link($link); $this->assertFlag('regenerate_needed', TRUE); $link['priority'] = 1; xmlsitemap_save_link($link); $this->assertFlag('regenerate_needed', FALSE); $link['priority'] = 0; xmlsitemap_save_link($link); $this->assertFlag('regenerate_needed', TRUE); $link['priority'] = 0.5; xmlsitemap_save_link($link); $this->assertFlag('regenerate_needed', TRUE); $link['priority'] = 0.5; $link['priority_override'] = 0; $link['status'] = 1; xmlsitemap_save_link($link); $this->assertFlag('regenerate_needed', FALSE); } /** * Tests for xmlsitemap_link_delete(). */ function testLinkDelete() { // Add our testing data. $link1 = $this->addSitemapLink(array('loc' => 'testing1', 'status' => 0)); $link2 = $this->addSitemapLink(array('loc' => 'testing1', 'status' => 1)); $link3 = $this->addSitemapLink(array('status' => 0)); variable_set('xmlsitemap_regenerate_needed', FALSE); // Test delete multiple links. // Test that the regenerate flag is set when visible links are deleted. $deleted = xmlsitemap_link_delete_multiple(array('loc' => 'testing1')); $this->assertEqual($deleted, 2); $this->assertFalse(xmlsitemap_link_load($link1['type'], $link1['id'])); $this->assertFalse(xmlsitemap_link_load($link2['type'], $link2['id'])); $this->assertTrue(xmlsitemap_link_load($link3['type'], $link3['id'])); $this->assertFlag('regenerate_needed', TRUE); $deleted = xmlsitemap_link_delete($link3['type'], $link3['id']); $this->assertEqual($deleted, 1); $this->assertFalse(xmlsitemap_link_load($link3['type'], $link3['id'])); $this->assertFlag('regenerate_needed', FALSE); } /** * Tests for xmlsitemap_update_links(). */ function testUpdateLinks() { // Add our testing data. $links = array(); $links[1] = $this->addSitemapLink(array('subtype' => 'group1')); $links[2] = $this->addSitemapLink(array('subtype' => 'group1')); $links[3] = $this->addSitemapLink(array('subtype' => 'group2')); variable_set('xmlsitemap_regenerate_needed', FALSE); // id | type | subtype | language | access | status | priority // 1 | testing | group1 | '' | 1 | 1 | 0.5 // 2 | testing | group1 | '' | 1 | 1 | 0.5 // 3 | testing | group2 | '' | 1 | 1 | 0.5 $updated = xmlsitemap_update_links(array('status' => 0), array('type' => 'testing', 'subtype' => 'group1', 'status_override' => 0)); $this->assertEqual($updated, 2); $this->assertFlag('regenerate_needed', TRUE); // id | type | subtype | language | status | priority // 1 | testing | group1 | '' | 0 | 0.5 // 2 | testing | group1 | '' | 0 | 0.5 // 3 | testing | group2 | '' | 1 | 0.5 $updated = xmlsitemap_update_links(array('priority' => 0.0), array('type' => 'testing', 'subtype' => 'group1', 'priority_override' => 0)); $this->assertEqual($updated, 2); $this->assertFlag('regenerate_needed', FALSE); // id | type | subtype | language | status | priority // 1 | testing | group1 | '' | 0 | 0.0 // 2 | testing | group1 | '' | 0 | 0.0 // 3 | testing | group2 | '' | 1 | 0.5 $updated = xmlsitemap_update_links(array('subtype' => 'group2'), array('type' => 'testing', 'subtype' => 'group1')); $this->assertEqual($updated, 2); $this->assertFlag('regenerate_needed', FALSE); // id | type | subtype | language | status | priority // 1 | testing | group2 | '' | 0 | 0.0 // 2 | testing | group2 | '' | 0 | 0.0 // 3 | testing | group2 | '' | 1 | 0.5 $updated = xmlsitemap_update_links(array('status' => 1), array('type' => 'testing', 'subtype' => 'group2', 'status_override' => 0, 'status' => 0)); $this->assertEqual($updated, 2); $this->assertFlag('regenerate_needed', TRUE); // id | type | subtype | language | status | priority // 1 | testing | group2 | '' | 1 | 0.0 // 2 | testing | group2 | '' | 1 | 0.0 // 3 | testing | group2 | '' | 1 | 0.5 } } class XMLSitemapFunctionalTest extends XMLSitemapTestHelper { public static function getInfo() { return array( 'name' => 'XML sitemap interface tests', 'description' => 'Functional tests for the XML sitemap module.', 'group' => 'XML sitemap', ); } function setUp() { parent::setUp('path'); $this->admin_user = $this->drupalCreateUser(array('access content', 'administer site configuration', 'administer xmlsitemap')); $this->drupalLogin($this->admin_user); } /** * Test the sitemap file caching. */ function testSitemapCaching() { $this->drupalGetSitemap(LANGUAGE_NONE, TRUE); $this->assertResponse(200); $etag = $this->drupalGetHeader('etag'); $last_modified = $this->drupalGetHeader('last-modified'); $this->assertTrue($etag, t('Etag header found.')); $this->assertTrue($last_modified, t('Last-modified header found.')); $this->drupalGet('sitemap.xml', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag)); $this->assertResponse(304); } /** * Test that the sitemap will not be genereated before the lifetime expires. */ function testMinimumLifetime() { $this->regenerateSitemap(); $edit = array('xmlsitemap_minimum_lifetime' => 300); $this->drupalPost('admin/config/search/xmlsitemap', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.')); $link = $this->addSitemapLink(array('loc' => 'lifetime-test')); drupal_cron_run(); $this->drupalGetSitemap(); $this->assertNoRaw('lifetime-test'); variable_set('xmlsitemap_generated_last', REQUEST_TIME - 300); drupal_cron_run(); $this->drupalGetSitemap(); $this->assertRaw('lifetime-test'); xmlsitemap_link_delete($link['type'], $link['id']); drupal_cron_run(); $this->drupalGetSitemap(); $this->assertRaw('lifetime-test'); $this->drupalGetSitemap(LANGUAGE_NONE, TRUE); $this->assertNoRaw('lifetime-test'); } /** * Test base URL functionality. */ function testBaseURL() { $edit = array('xmlsitemap_base_url' => ''); $this->drupalPost('admin/config/search/xmlsitemap', $edit, t('Save configuration')); $this->assertText(t('Base URL field is required.')); $edit = array('xmlsitemap_base_url' => 'invalid'); $this->drupalPost('admin/config/search/xmlsitemap', $edit, t('Save configuration')); $this->assertText(t('Invalid base URL.')); $edit = array('xmlsitemap_base_url' => 'http://example.com/ '); $this->drupalPost('admin/config/search/xmlsitemap', $edit, t('Save configuration')); $this->assertText(t('Invalid base URL.')); $edit = array('xmlsitemap_base_url' => 'http://example.com/'); $this->drupalPost('admin/config/search/xmlsitemap', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.')); $this->drupalGetSitemap(LANGUAGE_NONE, TRUE); $this->assertRaw('http://example.com/'); } /** * Test that configuration problems are reported properly in the status report. */ function testStatusReport() { // Test the rebuild flag. variable_set('xmlsitemap_generated_last', REQUEST_TIME); variable_set('xmlsitemap_rebuild_needed', TRUE); $this->assertXMLSitemapProblems(t('The XML sitemap data is out of sync and needs to be completely rebuilt.')); $this->clickLink(t('completely rebuilt')); $this->assertResponse(200); variable_set('xmlsitemap_rebuild_needed', FALSE); $this->assertNoXMLSitemapProblems(); // Test the regenerate flag (and cron hasn't run in a while). variable_set('xmlsitemap_regenerate_needed', TRUE); variable_set('xmlsitemap_generated_last', REQUEST_TIME - variable_get('cron_threshold_warning', 172800) - 100); $this->assertXMLSitemapProblems(t('The XML cached files are out of date and need to be regenerated. You can run cron manually to regenerate the sitemap files.')); $this->clickLink(t('run cron manually')); $this->assertResponse(200); $this->assertNoXMLSitemapProblems(); // Test anonymous users access to sitemap.xml. user_role_revoke_permissions(DRUPAL_ANONYMOUS_RID, array('access content')); $this->assertXMLSitemapProblems(t('In order to allow search engines to view the XML sitemap and content on your site, the anonymous user role must have the access content permission.')); user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access content')); $this->assertNoXMLSitemapProblems(); // Test chunk count > 1000. // Test directory not writable. } /** * Test that duplicate paths are skipped during generation. */ function testDuplicatePaths() { $link1 = $this->addSitemapLink(array('loc' => 'duplicate')); $link2 = $this->addSitemapLink(array('loc' => 'duplicate')); $this->drupalGetSitemap(LANGUAGE_NONE, TRUE); $this->assertUniqueText('duplicate'); } } class XMLSitemapRobotsTxtIntegrationTest extends XMLSitemapTestHelper { public static function getInfo() { return array( 'name' => 'XML sitemap robots.txt', 'description' => 'Integration tests for the XML sitemap and robots.txt module.', 'group' => 'XML sitemap', 'dependencies' => array('robotstxt'), ); } function setUp() { parent::setUp('robotstxt'); } function testRobotsTxt() { // Request the un-clean robots.txt path so this will work in case there is // still the robots.txt file in the root directory. $this->drupalGet('', array('query' => array('q' => 'robots.txt'))); $this->assertRaw('Sitemap: ' . url('sitemap.xml', array('absolute' => TRUE))); } }