cms/drupal/modules/statistics/statistics.test
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     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' => '1.2.3.3',
       
    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     // Test that Ajax logging doesn't occur when disabled.
       
   122     $post = http_build_query(array('nid' => $this->node->nid));
       
   123     $headers = array('Content-Type' => 'application/x-www-form-urlencoded');
       
   124     global $base_url;
       
   125     $stats_path = $base_url . '/' . drupal_get_path('module', 'statistics'). '/statistics.php';
       
   126     drupal_http_request($stats_path, array('method' => 'POST', 'data' => $post, 'headers' => $headers, 'timeout' => 10000));
       
   127     $node_counter = statistics_get($this->node->nid);
       
   128     $this->assertIdentical($node_counter['totalcount'], '3', 'Page request was not counted via Ajax.');
       
   129 
       
   130     // Test that Ajax logging occurs when enabled.
       
   131     variable_set('statistics_count_content_views_ajax', 1);
       
   132     drupal_http_request($stats_path, array('method' => 'POST', 'data' => $post, 'headers' => $headers, 'timeout' => 10000));
       
   133     $node_counter = statistics_get($this->node->nid);
       
   134     $this->assertIdentical($node_counter['totalcount'], '4', 'Page request was counted via Ajax.');
       
   135     variable_set('statistics_count_content_views_ajax', 0);
       
   136 
       
   137     // Visit edit page to generate a title greater than 255.
       
   138     $path = 'node/' . $this->node->nid . '/edit';
       
   139     $expected = array(
       
   140       'title' => truncate_utf8(t('Edit Basic page') . ' ' . $this->node->title, 255),
       
   141       'path' => $path,
       
   142     );
       
   143     $this->drupalGet($path);
       
   144     $log = db_query('SELECT * FROM {accesslog}')->fetchAll(PDO::FETCH_ASSOC);
       
   145     $this->assertTrue(is_array($log) && count($log) == 7, 'Page request was logged.');
       
   146     $this->assertEqual(array_intersect_key($log[6], $expected), $expected);
       
   147 
       
   148     // Create a path longer than 255 characters. Drupal's .htaccess file
       
   149     // instructs Apache to test paths against the file system before routing to
       
   150     // index.php. Many file systems restrict file names to 255 characters
       
   151     // (http://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits), and
       
   152     // Apache returns a 403 when testing longer file names, but the total path
       
   153     // length is not restricted.
       
   154     $long_path = $this->randomName(127) . '/' . $this->randomName(128);
       
   155 
       
   156     // Test that the long path is properly truncated when logged.
       
   157     $this->drupalGet($long_path);
       
   158     $log = db_query('SELECT * FROM {accesslog}')->fetchAll(PDO::FETCH_ASSOC);
       
   159     $this->assertTrue(is_array($log) && count($log) == 8, 'Page request was logged for a path over 255 characters.');
       
   160     $this->assertEqual($log[7]['path'], truncate_utf8($long_path, 255));
       
   161   }
       
   162 }
       
   163 
       
   164 /**
       
   165  * Tests that report pages render properly, and that access logging works.
       
   166  */
       
   167 class StatisticsReportsTestCase extends StatisticsTestCase {
       
   168   public static function getInfo() {
       
   169     return array(
       
   170       'name' => 'Statistics reports tests',
       
   171       'description' => 'Tests display of statistics report pages and access logging.',
       
   172       'group' => 'Statistics'
       
   173     );
       
   174   }
       
   175 
       
   176   /**
       
   177    * Verifies that 'Recent hits' renders properly and displays the added hit.
       
   178    */
       
   179   function testRecentHits() {
       
   180     $this->drupalGet('admin/reports/hits');
       
   181     $this->assertText('test', 'Hit title found.');
       
   182     $this->assertText('node/1', 'Hit URL found.');
       
   183     $this->assertText('Anonymous', 'Hit user found.');
       
   184   }
       
   185 
       
   186   /**
       
   187    * Verifies that 'Top pages' renders properly and displays the added hit.
       
   188    */
       
   189   function testTopPages() {
       
   190     $this->drupalGet('admin/reports/pages');
       
   191     $this->assertText('test', 'Hit title found.');
       
   192     $this->assertText('node/1', 'Hit URL found.');
       
   193   }
       
   194 
       
   195   /**
       
   196    * Verifies that 'Top referrers' renders properly and displays the added hit.
       
   197    */
       
   198   function testTopReferrers() {
       
   199     $this->drupalGet('admin/reports/referrers');
       
   200     $this->assertText('http://example.com', 'Hit referrer found.');
       
   201   }
       
   202 
       
   203   /**
       
   204    * Verifies that 'Details' page renders properly and displays the added hit.
       
   205    */
       
   206   function testDetails() {
       
   207     $this->drupalGet('admin/reports/access/1');
       
   208     $this->assertText('test', 'Hit title found.');
       
   209     $this->assertText('node/1', 'Hit URL found.');
       
   210     $this->assertText('Anonymous', 'Hit user found.');
       
   211   }
       
   212 
       
   213   /**
       
   214    * Verifies that access logging is working and is reported correctly.
       
   215    */
       
   216   function testAccessLogging() {
       
   217     $this->drupalGet('admin/reports/referrers');
       
   218     $this->drupalGet('admin/reports/hits');
       
   219     $this->assertText('Top referrers in the past 3 days', 'Hit title found.');
       
   220     $this->assertText('admin/reports/referrers', 'Hit URL found.');
       
   221   }
       
   222 
       
   223   /**
       
   224    * Tests the "popular content" block.
       
   225    */
       
   226   function testPopularContentBlock() {
       
   227     // Visit a node to have something show up in the block.
       
   228     $node = $this->drupalCreateNode(array('type' => 'page', 'uid' => $this->blocking_user->uid));
       
   229     $this->drupalGet('node/' . $node->nid);
       
   230 
       
   231     // Configure and save the block.
       
   232     $block = block_load('statistics', 'popular');
       
   233     $block->theme = variable_get('theme_default', 'bartik');
       
   234     $block->status = 1;
       
   235     $block->pages = '';
       
   236     $block->region = 'sidebar_first';
       
   237     $block->cache = -1;
       
   238     $block->visibility = 0;
       
   239     $edit = array('statistics_block_top_day_num' => 3, 'statistics_block_top_all_num' => 3, 'statistics_block_top_last_num' => 3);
       
   240     module_invoke('statistics', 'block_save', 'popular', $edit);
       
   241     drupal_write_record('block', $block);
       
   242 
       
   243     // Get some page and check if the block is displayed.
       
   244     $this->drupalGet('user');
       
   245     $this->assertText('Popular content', 'Found the popular content block.');
       
   246     $this->assertText("Today's", 'Found today\'s popular content.');
       
   247     $this->assertText('All time', 'Found the alll time popular content.');
       
   248     $this->assertText('Last viewed', 'Found the last viewed popular content.');
       
   249 
       
   250     $this->assertRaw(l($node->title, 'node/' . $node->nid), 'Found link to visited node.');
       
   251   }
       
   252 }
       
   253 
       
   254 /**
       
   255  * Tests that the visitor blocking functionality works.
       
   256  */
       
   257 class StatisticsBlockVisitorsTestCase extends StatisticsTestCase {
       
   258   public static function getInfo() {
       
   259     return array(
       
   260       'name' => 'Top visitor blocking',
       
   261       'description' => 'Tests blocking of IP addresses via the top visitors report.',
       
   262       'group' => 'Statistics'
       
   263     );
       
   264   }
       
   265 
       
   266   /**
       
   267    * Blocks an IP address via the top visitors report and then unblocks it.
       
   268    */
       
   269   function testIPAddressBlocking() {
       
   270     // IP address for testing.
       
   271     $test_ip_address = '1.2.3.3';
       
   272 
       
   273     // Verify the IP address from accesslog appears on the top visitors page
       
   274     // and that a 'block IP address' link is displayed.
       
   275     $this->drupalLogin($this->blocking_user);
       
   276     $this->drupalGet('admin/reports/visitors');
       
   277     $this->assertText($test_ip_address, 'IP address found.');
       
   278     $this->assertText(t('block IP address'), 'Block IP link displayed');
       
   279 
       
   280     // Block the IP address.
       
   281     $this->clickLink('block IP address');
       
   282     $this->assertText(t('IP address blocking'), 'IP blocking page displayed.');
       
   283     $edit = array();
       
   284     $edit['ip'] = $test_ip_address;
       
   285     $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add'));
       
   286     $ip = db_query("SELECT iid from {blocked_ips} WHERE ip = :ip", array(':ip' => $edit['ip']))->fetchField();
       
   287     $this->assertNotEqual($ip, FALSE, 'IP address found in database');
       
   288     $this->assertRaw(t('The IP address %ip has been blocked.', array('%ip' => $edit['ip'])), 'IP address was blocked.');
       
   289 
       
   290     // Verify that the block/unblock link on the top visitors page has been
       
   291     // altered.
       
   292     $this->drupalGet('admin/reports/visitors');
       
   293     $this->assertText(t('unblock IP address'), 'Unblock IP address link displayed');
       
   294 
       
   295     // Unblock the IP address.
       
   296     $this->clickLink('unblock IP address');
       
   297     $this->assertRaw(t('Are you sure you want to delete %ip?', array('%ip' => $test_ip_address)), 'IP address deletion confirmation found.');
       
   298     $edit = array();
       
   299     $this->drupalPost('admin/config/people/ip-blocking/delete/1', NULL, t('Delete'));
       
   300     $this->assertRaw(t('The IP address %ip was deleted.', array('%ip' => $test_ip_address)), 'IP address deleted.');
       
   301   }
       
   302 }
       
   303 
       
   304 /**
       
   305  * Tests the statistics administration screen.
       
   306  */
       
   307 class StatisticsAdminTestCase extends DrupalWebTestCase {
       
   308 
       
   309   /**
       
   310    * A user that has permission to administer and access statistics.
       
   311    *
       
   312    * @var object|FALSE
       
   313    *
       
   314    * A fully loaded user object, or FALSE if user creation failed.
       
   315    */
       
   316   protected $privileged_user;
       
   317 
       
   318   /**
       
   319    * A page node for which to check access statistics.
       
   320    *
       
   321    * @var object
       
   322    */
       
   323   protected $test_node;
       
   324 
       
   325   public static function getInfo() {
       
   326     return array(
       
   327       'name' => 'Test statistics admin.',
       
   328       'description' => 'Tests the statistics admin.',
       
   329       'group' => 'Statistics'
       
   330     );
       
   331   }
       
   332 
       
   333   function setUp() {
       
   334     parent::setUp('statistics');
       
   335     $this->privileged_user = $this->drupalCreateUser(array('access statistics', 'administer statistics', 'view post access counter', 'create page content'));
       
   336     $this->drupalLogin($this->privileged_user);
       
   337     $this->test_node = $this->drupalCreateNode(array('type' => 'page', 'uid' => $this->privileged_user->uid));
       
   338   }
       
   339 
       
   340   /**
       
   341    * Verifies that the statistics settings page works.
       
   342    */
       
   343   function testStatisticsSettings() {
       
   344     $this->assertFalse(variable_get('statistics_enable_access_log', 0), 'Access log is disabled by default.');
       
   345     $this->assertFalse(variable_get('statistics_count_content_views', 0), 'Count content view log is disabled by default.');
       
   346 
       
   347     $this->drupalGet('admin/reports/pages');
       
   348     $this->assertRaw(t('No statistics available.'), 'Verifying text shown when no statistics is available.');
       
   349 
       
   350     // Enable access log and counter on content view.
       
   351     $edit['statistics_enable_access_log'] = 1;
       
   352     $edit['statistics_count_content_views'] = 1;
       
   353     $this->drupalPost('admin/config/system/statistics', $edit, t('Save configuration'));
       
   354     $this->assertTrue(variable_get('statistics_enable_access_log'), 'Access log is enabled.');
       
   355     $this->assertTrue(variable_get('statistics_count_content_views'), 'Count content view log is enabled.');
       
   356 
       
   357     // Hit the node.
       
   358     $this->drupalGet('node/' . $this->test_node->nid);
       
   359 
       
   360     $this->drupalGet('admin/reports/pages');
       
   361     $this->assertText('node/1', 'Test node found.');
       
   362 
       
   363     // Hit the node again (the counter is incremented after the hit, so
       
   364     // "1 read" will actually be shown when the node is hit the second time).
       
   365     $this->drupalGet('node/' . $this->test_node->nid);
       
   366     $this->assertText('1 read', 'Node is read once.');
       
   367 
       
   368     $this->drupalGet('node/' . $this->test_node->nid);
       
   369     $this->assertText('2 reads', 'Node is read 2 times.');
       
   370   }
       
   371 
       
   372   /**
       
   373    * Tests that when a node is deleted, the node counter is deleted too.
       
   374    */
       
   375   function testDeleteNode() {
       
   376     variable_set('statistics_count_content_views', 1);
       
   377 
       
   378     $this->drupalGet('node/' . $this->test_node->nid);
       
   379 
       
   380     $result = db_select('node_counter', 'n')
       
   381       ->fields('n', array('nid'))
       
   382       ->condition('n.nid', $this->test_node->nid)
       
   383       ->execute()
       
   384       ->fetchAssoc();
       
   385     $this->assertEqual($result['nid'], $this->test_node->nid, 'Verifying that the node counter is incremented.');
       
   386 
       
   387     node_delete($this->test_node->nid);
       
   388 
       
   389     $result = db_select('node_counter', 'n')
       
   390       ->fields('n', array('nid'))
       
   391       ->condition('n.nid', $this->test_node->nid)
       
   392       ->execute()
       
   393       ->fetchAssoc();
       
   394     $this->assertFalse($result, 'Verifying that the node counter is deleted.');
       
   395   }
       
   396 
       
   397   /**
       
   398    * Tests that accesslog reflects when a user is deleted.
       
   399    */
       
   400   function testDeleteUser() {
       
   401     variable_set('statistics_enable_access_log', 1);
       
   402 
       
   403     variable_set('user_cancel_method', 'user_cancel_delete');
       
   404     $this->drupalLogout($this->privileged_user);
       
   405     $account = $this->drupalCreateUser(array('access content', 'cancel account'));
       
   406     $this->drupalLogin($account);
       
   407     $this->drupalGet('node/' . $this->test_node->nid);
       
   408 
       
   409     $account = user_load($account->uid, TRUE);
       
   410 
       
   411     $this->drupalGet('user/' . $account->uid . '/edit');
       
   412     $this->drupalPost(NULL, NULL, t('Cancel account'));
       
   413 
       
   414     $timestamp = time();
       
   415     $this->drupalPost(NULL, NULL, t('Cancel account'));
       
   416     // Confirm account cancellation request.
       
   417     $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid));
       
   418     $this->assertFalse(user_load($account->uid, TRUE), 'User is not found in the database.');
       
   419 
       
   420     $this->drupalGet('admin/reports/visitors');
       
   421     $this->assertNoText($account->name, 'Did not find user in visitor statistics.');
       
   422   }
       
   423 
       
   424   /**
       
   425    * Tests that cron clears day counts and expired access logs.
       
   426    */
       
   427   function testExpiredLogs() {
       
   428     variable_set('statistics_enable_access_log', 1);
       
   429     variable_set('statistics_count_content_views', 1);
       
   430     variable_set('statistics_day_timestamp', 8640000);
       
   431     variable_set('statistics_flush_accesslog_timer', 1);
       
   432 
       
   433     $this->drupalGet('node/' . $this->test_node->nid);
       
   434     $this->drupalGet('node/' . $this->test_node->nid);
       
   435     $this->assertText('1 read', 'Node is read once.');
       
   436 
       
   437     $this->drupalGet('admin/reports/pages');
       
   438     $this->assertText('node/' . $this->test_node->nid, 'Hit URL found.');
       
   439 
       
   440     // statistics_cron will subtract the statistics_flush_accesslog_timer
       
   441     // variable from REQUEST_TIME in the delete query, so wait two secs here to
       
   442     // make sure the access log will be flushed for the node just hit.
       
   443     sleep(2);
       
   444     $this->cronRun();
       
   445 
       
   446     $this->drupalGet('admin/reports/pages');
       
   447     $this->assertNoText('node/' . $this->test_node->nid, 'No hit URL found.');
       
   448 
       
   449     $result = db_select('node_counter', 'nc')
       
   450       ->fields('nc', array('daycount'))
       
   451       ->condition('nid', $this->test_node->nid, '=')
       
   452       ->execute()
       
   453       ->fetchField();
       
   454     $this->assertFalse($result, 'Daycounter is zero.');
       
   455   }
       
   456 }
       
   457 
       
   458 /**
       
   459  * Tests statistics token replacement in strings.
       
   460  */
       
   461 class StatisticsTokenReplaceTestCase extends StatisticsTestCase {
       
   462   public static function getInfo() {
       
   463     return array(
       
   464       'name' => 'Statistics token replacement',
       
   465       'description' => 'Generates text using placeholders for dummy content to check statistics token replacement.',
       
   466       'group' => 'Statistics',
       
   467     );
       
   468   }
       
   469 
       
   470   /**
       
   471    * Creates a node, then tests the statistics tokens generated from it.
       
   472    */
       
   473   function testStatisticsTokenReplacement() {
       
   474     global $language;
       
   475 
       
   476     // Create user and node.
       
   477     $user = $this->drupalCreateUser(array('create page content'));
       
   478     $this->drupalLogin($user);
       
   479     $node = $this->drupalCreateNode(array('type' => 'page', 'uid' => $user->uid));
       
   480 
       
   481     // Hit the node.
       
   482     $this->drupalGet('node/' . $node->nid);
       
   483     $statistics = statistics_get($node->nid);
       
   484 
       
   485     // Generate and test tokens.
       
   486     $tests = array();
       
   487     $tests['[node:total-count]'] = 1;
       
   488     $tests['[node:day-count]'] = 1;
       
   489     $tests['[node:last-view]'] = format_date($statistics['timestamp']);
       
   490     $tests['[node:last-view:short]'] = format_date($statistics['timestamp'], 'short');
       
   491 
       
   492     // Test to make sure that we generated something for each token.
       
   493     $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
       
   494 
       
   495     foreach ($tests as $input => $expected) {
       
   496       $output = token_replace($input, array('node' => $node), array('language' => $language));
       
   497       $this->assertEqual($output, $expected, format_string('Statistics token %token replaced.', array('%token' => $input)));
       
   498     }
       
   499   }
       
   500 }