| Drupal | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /** 4 * @file 5 * Tests for the Statistics module. 6 */ 7 8 /** 9 * Defines a base class for testing the Statistics module. 10 */ 11 class StatisticsTestCase extends DrupalWebTestCase { 12 13 function setUp() { 14 parent::setUp('statistics'); 15 16 // Create user. 17 $this->blocking_user = $this->drupalCreateUser(array( 18 'access administration pages', 19 'access site reports', 20 'access statistics', 21 'block IP addresses', 22 'administer blocks', 23 'administer statistics', 24 'administer users', 25 )); 26 $this->drupalLogin($this->blocking_user); 27 28 // Enable access logging. 29 variable_set('statistics_enable_access_log', 1); 30 variable_set('statistics_count_content_views', 1); 31 32 // Insert dummy access by anonymous user into access log. 33 db_insert('accesslog') 34 ->fields(array( 35 'title' => 'test', 36 'path' => 'node/1', 37 'url' => 'http://example.com', 38 'hostname' => '192.168.1.1', 39 'uid' => 0, 40 'sid' => 10, 41 'timer' => 10, 42 'timestamp' => REQUEST_TIME, 43 )) 44 ->execute(); 45 } 46 } 47 48 /** 49 * Tests that logging via statistics_exit() works for all pages. 50 * 51 * We subclass DrupalWebTestCase rather than StatisticsTestCase, because we 52 * want to test requests from an anonymous user. 53 */ 54 class StatisticsLoggingTestCase extends DrupalWebTestCase { 55 public static function getInfo() { 56 return array( 57 'name' => 'Statistics logging tests', 58 'description' => 'Tests request logging for cached and uncached pages.', 59 'group' => 'Statistics' 60 ); 61 } 62 63 function setUp() { 64 parent::setUp('statistics'); 65 66 $this->auth_user = $this->drupalCreateUser(array('access content', 'create page content', 'edit own page content')); 67 68 // Ensure we have a node page to access. 69 $this->node = $this->drupalCreateNode(array('title' => $this->randomName(255), 'uid' => $this->auth_user->uid)); 70 71 // Enable page caching. 72 variable_set('cache', TRUE); 73 74 // Enable access logging. 75 variable_set('statistics_enable_access_log', 1); 76 variable_set('statistics_count_content_views', 1); 77 78 // Clear the logs. 79 db_truncate('accesslog'); 80 db_truncate('node_counter'); 81 } 82 83 /** 84 * Verifies request logging for cached and uncached pages. 85 */ 86 function testLogging() { 87 $path = 'node/' . $this->node->nid; 88 $expected = array( 89 'title' => $this->node->title, 90 'path' => $path, 91 ); 92 93 // Verify logging of an uncached page. 94 $this->drupalGet($path); 95 $this->assertIdentical($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Testing an uncached page.'); 96 $log = db_query('SELECT * FROM {accesslog}')->fetchAll(PDO::FETCH_ASSOC); 97 $this->assertTrue(is_array($log) && count($log) == 1, 'Page request was logged.'); 98 $this->assertEqual(array_intersect_key($log[0], $expected), $expected); 99 $node_counter = statistics_get($this->node->nid); 100 $this->assertIdentical($node_counter['totalcount'], '1'); 101 102 // Verify logging of a cached page. 103 $this->drupalGet($path); 104 $this->assertIdentical($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Testing a cached page.'); 105 $log = db_query('SELECT * FROM {accesslog}')->fetchAll(PDO::FETCH_ASSOC); 106 $this->assertTrue(is_array($log) && count($log) == 2, 'Page request was logged.'); 107 $this->assertEqual(array_intersect_key($log[1], $expected), $expected); 108 $node_counter = statistics_get($this->node->nid); 109 $this->assertIdentical($node_counter['totalcount'], '2'); 110 111 // Test logging from authenticated users 112 $this->drupalLogin($this->auth_user); 113 $this->drupalGet($path); 114 $log = db_query('SELECT * FROM {accesslog}')->fetchAll(PDO::FETCH_ASSOC); 115 // Check the 6th item since login and account pages are also logged 116 $this->assertTrue(is_array($log) && count($log) == 6, 'Page request was logged.'); 117 $this->assertEqual(array_intersect_key($log[5], $expected), $expected); 118 $node_counter = statistics_get($this->node->nid); 119 $this->assertIdentical($node_counter['totalcount'], '3'); 120 121 // Visit edit page to generate a title greater than 255. 122 $path = 'node/' . $this->node->nid . '/edit'; 123 $expected = array( 124 'title' => truncate_utf8(t('Edit Basic page') . ' ' . $this->node->title, 255), 125 'path' => $path, 126 ); 127 $this->drupalGet($path); 128 $log = db_query('SELECT * FROM {accesslog}')->fetchAll(PDO::FETCH_ASSOC); 129 $this->assertTrue(is_array($log) && count($log) == 7, 'Page request was logged.'); 130 $this->assertEqual(array_intersect_key($log[6], $expected), $expected); 131 132 // Create a path longer than 255 characters. Drupal's .htaccess file 133 // instructs Apache to test paths against the file system before routing to 134 // index.php. Many file systems restrict file names to 255 characters 135 // (http://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits), and 136 // Apache returns a 403 when testing longer file names, but the total path 137 // length is not restricted. 138 $long_path = $this->randomName(127) . '/' . $this->randomName(128); 139 140 // Test that the long path is properly truncated when logged. 141 $this->drupalGet($long_path); 142 $log = db_query('SELECT * FROM {accesslog}')->fetchAll(PDO::FETCH_ASSOC); 143 $this->assertTrue(is_array($log) && count($log) == 8, 'Page request was logged for a path over 255 characters.'); 144 $this->assertEqual($log[7]['path'], truncate_utf8($long_path, 255)); 145 146 } 147 } 148 149 /** 150 * Tests that report pages render properly, and that access logging works. 151 */ 152 class StatisticsReportsTestCase extends StatisticsTestCase { 153 public static function getInfo() { 154 return array( 155 'name' => 'Statistics reports tests', 156 'description' => 'Tests display of statistics report pages and access logging.', 157 'group' => 'Statistics' 158 ); 159 } 160 161 /** 162 * Verifies that 'Recent hits' renders properly and displays the added hit. 163 */ 164 function testRecentHits() { 165 $this->drupalGet('admin/reports/hits'); 166 $this->assertText('test', 'Hit title found.'); 167 $this->assertText('node/1', 'Hit URL found.'); 168 $this->assertText('Anonymous', 'Hit user found.'); 169 } 170 171 /** 172 * Verifies that 'Top pages' renders properly and displays the added hit. 173 */ 174 function testTopPages() { 175 $this->drupalGet('admin/reports/pages'); 176 $this->assertText('test', 'Hit title found.'); 177 $this->assertText('node/1', 'Hit URL found.'); 178 } 179 180 /** 181 * Verifies that 'Top referrers' renders properly and displays the added hit. 182 */ 183 function testTopReferrers() { 184 $this->drupalGet('admin/reports/referrers'); 185 $this->assertText('http://example.com', 'Hit referrer found.'); 186 } 187 188 /** 189 * Verifies that 'Details' page renders properly and displays the added hit. 190 */ 191 function testDetails() { 192 $this->drupalGet('admin/reports/access/1'); 193 $this->assertText('test', 'Hit title found.'); 194 $this->assertText('node/1', 'Hit URL found.'); 195 $this->assertText('Anonymous', 'Hit user found.'); 196 } 197 198 /** 199 * Verifies that access logging is working and is reported correctly. 200 */ 201 function testAccessLogging() { 202 $this->drupalGet('admin/reports/referrers'); 203 $this->drupalGet('admin/reports/hits'); 204 $this->assertText('Top referrers in the past 3 days', 'Hit title found.'); 205 $this->assertText('admin/reports/referrers', 'Hit URL found.'); 206 } 207 208 /** 209 * Tests the "popular content" block. 210 */ 211 function testPopularContentBlock() { 212 // Visit a node to have something show up in the block. 213 $node = $this->drupalCreateNode(array('type' => 'page', 'uid' => $this->blocking_user->uid)); 214 $this->drupalGet('node/' . $node->nid); 215 216 // Configure and save the block. 217 $block = block_load('statistics', 'popular'); 218 $block->theme = variable_get('theme_default', 'bartik'); 219 $block->status = 1; 220 $block->pages = ''; 221 $block->region = 'sidebar_first'; 222 $block->cache = -1; 223 $block->visibility = 0; 224 $edit = array('statistics_block_top_day_num' => 3, 'statistics_block_top_all_num' => 3, 'statistics_block_top_last_num' => 3); 225 module_invoke('statistics', 'block_save', 'popular', $edit); 226 drupal_write_record('block', $block); 227 228 // Get some page and check if the block is displayed. 229 $this->drupalGet('user'); 230 $this->assertText('Popular content', 'Found the popular content block.'); 231 $this->assertText("Today's", 'Found today\'s popular content.'); 232 $this->assertText('All time', 'Found the alll time popular content.'); 233 $this->assertText('Last viewed', 'Found the last viewed popular content.'); 234 235 $this->assertRaw(l($node->title, 'node/' . $node->nid), 'Found link to visited node.'); 236 } 237 } 238 239 /** 240 * Tests that the visitor blocking functionality works. 241 */ 242 class StatisticsBlockVisitorsTestCase extends StatisticsTestCase { 243 public static function getInfo() { 244 return array( 245 'name' => 'Top visitor blocking', 246 'description' => 'Tests blocking of IP addresses via the top visitors report.', 247 'group' => 'Statistics' 248 ); 249 } 250 251 /** 252 * Blocks an IP address via the top visitors report and then unblocks it. 253 */ 254 function testIPAddressBlocking() { 255 // IP address for testing. 256 $test_ip_address = '192.168.1.1'; 257 258 // Verify the IP address from accesslog appears on the top visitors page 259 // and that a 'block IP address' link is displayed. 260 $this->drupalLogin($this->blocking_user); 261 $this->drupalGet('admin/reports/visitors'); 262 $this->assertText($test_ip_address, 'IP address found.'); 263 $this->assertText(t('block IP address'), 'Block IP link displayed'); 264 265 // Block the IP address. 266 $this->clickLink('block IP address'); 267 $this->assertText(t('IP address blocking'), 'IP blocking page displayed.'); 268 $edit = array(); 269 $edit['ip'] = $test_ip_address; 270 $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add')); 271 $ip = db_query("SELECT iid from {blocked_ips} WHERE ip = :ip", array(':ip' => $edit['ip']))->fetchField(); 272 $this->assertNotEqual($ip, FALSE, 'IP address found in database'); 273 $this->assertRaw(t('The IP address %ip has been blocked.', array('%ip' => $edit['ip'])), 'IP address was blocked.'); 274 275 // Verify that the block/unblock link on the top visitors page has been 276 // altered. 277 $this->drupalGet('admin/reports/visitors'); 278 $this->assertText(t('unblock IP address'), 'Unblock IP address link displayed'); 279 280 // Unblock the IP address. 281 $this->clickLink('unblock IP address'); 282 $this->assertRaw(t('Are you sure you want to delete %ip?', array('%ip' => $test_ip_address)), 'IP address deletion confirmation found.'); 283 $edit = array(); 284 $this->drupalPost('admin/config/people/ip-blocking/delete/1', NULL, t('Delete')); 285 $this->assertRaw(t('The IP address %ip was deleted.', array('%ip' => $test_ip_address)), 'IP address deleted.'); 286 } 287 } 288 289 /** 290 * Tests the statistics administration screen. 291 */ 292 class StatisticsAdminTestCase extends DrupalWebTestCase { 293 294 /** 295 * A user that has permission to administer and access statistics. 296 * 297 * @var object|FALSE 298 * 299 * A fully loaded user object, or FALSE if user creation failed. 300 */ 301 protected $privileged_user; 302 303 /** 304 * A page node for which to check access statistics. 305 * 306 * @var object 307 */ 308 protected $test_node; 309 310 public static function getInfo() { 311 return array( 312 'name' => 'Test statistics admin.', 313 'description' => 'Tests the statistics admin.', 314 'group' => 'Statistics' 315 ); 316 } 317 318 function setUp() { 319 parent::setUp('statistics'); 320 $this->privileged_user = $this->drupalCreateUser(array('access statistics', 'administer statistics', 'view post access counter', 'create page content')); 321 $this->drupalLogin($this->privileged_user); 322 $this->test_node = $this->drupalCreateNode(array('type' => 'page', 'uid' => $this->privileged_user->uid)); 323 } 324 325 /** 326 * Verifies that the statistics settings page works. 327 */ 328 function testStatisticsSettings() { 329 $this->assertFalse(variable_get('statistics_enable_access_log', 0), 'Access log is disabled by default.'); 330 $this->assertFalse(variable_get('statistics_count_content_views', 0), 'Count content view log is disabled by default.'); 331 332 $this->drupalGet('admin/reports/pages'); 333 $this->assertRaw(t('No statistics available.'), 'Verifying text shown when no statistics is available.'); 334 335 // Enable access log and counter on content view. 336 $edit['statistics_enable_access_log'] = 1; 337 $edit['statistics_count_content_views'] = 1; 338 $this->drupalPost('admin/config/system/statistics', $edit, t('Save configuration')); 339 $this->assertTrue(variable_get('statistics_enable_access_log'), 'Access log is enabled.'); 340 $this->assertTrue(variable_get('statistics_count_content_views'), 'Count content view log is enabled.'); 341 342 // Hit the node. 343 $this->drupalGet('node/' . $this->test_node->nid); 344 345 $this->drupalGet('admin/reports/pages'); 346 $this->assertText('node/1', 'Test node found.'); 347 348 // Hit the node again (the counter is incremented after the hit, so 349 // "1 read" will actually be shown when the node is hit the second time). 350 $this->drupalGet('node/' . $this->test_node->nid); 351 $this->assertText('1 read', 'Node is read once.'); 352 353 $this->drupalGet('node/' . $this->test_node->nid); 354 $this->assertText('2 reads', 'Node is read 2 times.'); 355 } 356 357 /** 358 * Tests that when a node is deleted, the node counter is deleted too. 359 */ 360 function testDeleteNode() { 361 variable_set('statistics_count_content_views', 1); 362 363 $this->drupalGet('node/' . $this->test_node->nid); 364 365 $result = db_select('node_counter', 'n') 366 ->fields('n', array('nid')) 367 ->condition('n.nid', $this->test_node->nid) 368 ->execute() 369 ->fetchAssoc(); 370 $this->assertEqual($result['nid'], $this->test_node->nid, 'Verifying that the node counter is incremented.'); 371 372 node_delete($this->test_node->nid); 373 374 $result = db_select('node_counter', 'n') 375 ->fields('n', array('nid')) 376 ->condition('n.nid', $this->test_node->nid) 377 ->execute() 378 ->fetchAssoc(); 379 $this->assertFalse($result, 'Verifying that the node counter is deleted.'); 380 } 381 382 /** 383 * Tests that accesslog reflects when a user is deleted. 384 */ 385 function testDeleteUser() { 386 variable_set('statistics_enable_access_log', 1); 387 388 variable_set('user_cancel_method', 'user_cancel_delete'); 389 $this->drupalLogout($this->privileged_user); 390 $account = $this->drupalCreateUser(array('access content', 'cancel account')); 391 $this->drupalLogin($account); 392 $this->drupalGet('node/' . $this->test_node->nid); 393 394 $account = user_load($account->uid, TRUE); 395 396 $this->drupalGet('user/' . $account->uid . '/edit'); 397 $this->drupalPost(NULL, NULL, t('Cancel account')); 398 399 $timestamp = time(); 400 $this->drupalPost(NULL, NULL, t('Cancel account')); 401 // Confirm account cancellation request. 402 $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login)); 403 $this->assertFalse(user_load($account->uid, TRUE), 'User is not found in the database.'); 404 405 $this->drupalGet('admin/reports/visitors'); 406 $this->assertNoText($account->name, 'Did not find user in visitor statistics.'); 407 } 408 409 /** 410 * Tests that cron clears day counts and expired access logs. 411 */ 412 function testExpiredLogs() { 413 variable_set('statistics_enable_access_log', 1); 414 variable_set('statistics_count_content_views', 1); 415 variable_set('statistics_day_timestamp', 8640000); 416 variable_set('statistics_flush_accesslog_timer', 1); 417 418 $this->drupalGet('node/' . $this->test_node->nid); 419 $this->drupalGet('node/' . $this->test_node->nid); 420 $this->assertText('1 read', 'Node is read once.'); 421 422 $this->drupalGet('admin/reports/pages'); 423 $this->assertText('node/' . $this->test_node->nid, 'Hit URL found.'); 424 425 // statistics_cron will subtract the statistics_flush_accesslog_timer 426 // variable from REQUEST_TIME in the delete query, so wait two secs here to 427 // make sure the access log will be flushed for the node just hit. 428 sleep(2); 429 $this->cronRun(); 430 431 $this->drupalGet('admin/reports/pages'); 432 $this->assertNoText('node/' . $this->test_node->nid, 'No hit URL found.'); 433 434 $result = db_select('node_counter', 'nc') 435 ->fields('nc', array('daycount')) 436 ->condition('nid', $this->test_node->nid, '=') 437 ->execute() 438 ->fetchField(); 439 $this->assertFalse($result, 'Daycounter is zero.'); 440 } 441 } 442 443 /** 444 * Tests statistics token replacement in strings. 445 */ 446 class StatisticsTokenReplaceTestCase extends StatisticsTestCase { 447 public static function getInfo() { 448 return array( 449 'name' => 'Statistics token replacement', 450 'description' => 'Generates text using placeholders for dummy content to check statistics token replacement.', 451 'group' => 'Statistics', 452 ); 453 } 454 455 /** 456 * Creates a node, then tests the statistics tokens generated from it. 457 */ 458 function testStatisticsTokenReplacement() { 459 global $language; 460 461 // Create user and node. 462 $user = $this->drupalCreateUser(array('create page content')); 463 $this->drupalLogin($user); 464 $node = $this->drupalCreateNode(array('type' => 'page', 'uid' => $user->uid)); 465 466 // Hit the node. 467 $this->drupalGet('node/' . $node->nid); 468 $statistics = statistics_get($node->nid); 469 470 // Generate and test tokens. 471 $tests = array(); 472 $tests['[node:total-count]'] = 1; 473 $tests['[node:day-count]'] = 1; 474 $tests['[node:last-view]'] = format_date($statistics['timestamp']); 475 $tests['[node:last-view:short]'] = format_date($statistics['timestamp'], 'short'); 476 477 // Test to make sure that we generated something for each token. 478 $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.'); 479 480 foreach ($tests as $input => $expected) { 481 $output = token_replace($input, array('node' => $node), array('language' => $language)); 482 $this->assertEqual($output, $expected, format_string('Statistics token %token replaced.', array('%token' => $input))); 483 } 484 } 485 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title