cms/drupal/modules/search/search.test
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * Tests for search.module.
       
     6  */
       
     7 
       
     8 // The search index can contain different types of content. Typically the type is 'node'.
       
     9 // Here we test with _test_ and _test2_ as the type.
       
    10 define('SEARCH_TYPE', '_test_');
       
    11 define('SEARCH_TYPE_2', '_test2_');
       
    12 define('SEARCH_TYPE_JPN', '_test3_');
       
    13 
       
    14 /**
       
    15  * Indexes content and queries it.
       
    16  */
       
    17 class SearchMatchTestCase extends DrupalWebTestCase {
       
    18   public static function getInfo() {
       
    19     return array(
       
    20       'name' => 'Search engine queries',
       
    21       'description' => 'Indexes content and queries it.',
       
    22       'group' => 'Search',
       
    23     );
       
    24   }
       
    25 
       
    26   /**
       
    27    * Implementation setUp().
       
    28    */
       
    29   function setUp() {
       
    30     parent::setUp('search');
       
    31   }
       
    32 
       
    33   /**
       
    34    * Test search indexing.
       
    35    */
       
    36   function testMatching() {
       
    37     $this->_setup();
       
    38     $this->_testQueries();
       
    39   }
       
    40 
       
    41   /**
       
    42    * Set up a small index of items to test against.
       
    43    */
       
    44   function _setup() {
       
    45     variable_set('minimum_word_size', 3);
       
    46 
       
    47     for ($i = 1; $i <= 7; ++$i) {
       
    48       search_index($i, SEARCH_TYPE, $this->getText($i));
       
    49     }
       
    50     for ($i = 1; $i <= 5; ++$i) {
       
    51       search_index($i + 7, SEARCH_TYPE_2, $this->getText2($i));
       
    52     }
       
    53     // No getText builder function for Japanese text; just a simple array.
       
    54     foreach (array(
       
    55       13 => '以呂波耳・ほへとち。リヌルヲ。',
       
    56       14 => 'ドルーパルが大好きよ!',
       
    57       15 => 'コーヒーとケーキ',
       
    58     ) as $i => $jpn) {
       
    59       search_index($i, SEARCH_TYPE_JPN, $jpn);
       
    60     }
       
    61     search_update_totals();
       
    62   }
       
    63 
       
    64   /**
       
    65    * _test_: Helper method for generating snippets of content.
       
    66    *
       
    67    * Generated items to test against:
       
    68    *   1  ipsum
       
    69    *   2  dolore sit
       
    70    *   3  sit am ut
       
    71    *   4  am ut enim am
       
    72    *   5  ut enim am minim veniam
       
    73    *   6  enim am minim veniam es cillum
       
    74    *   7  am minim veniam es cillum dolore eu
       
    75    */
       
    76   function getText($n) {
       
    77     $words = explode(' ', "Ipsum dolore sit am. Ut enim am minim veniam. Es cillum dolore eu.");
       
    78     return implode(' ', array_slice($words, $n - 1, $n));
       
    79   }
       
    80 
       
    81   /**
       
    82    * _test2_: Helper method for generating snippets of content.
       
    83    *
       
    84    * Generated items to test against:
       
    85    *   8  dear
       
    86    *   9  king philip
       
    87    *   10 philip came over
       
    88    *   11 came over from germany
       
    89    *   12 over from germany swimming
       
    90    */
       
    91   function getText2($n) {
       
    92     $words = explode(' ', "Dear King Philip came over from Germany swimming.");
       
    93     return implode(' ', array_slice($words, $n - 1, $n));
       
    94   }
       
    95 
       
    96   /**
       
    97    * Run predefine queries looking for indexed terms.
       
    98    */
       
    99   function _testQueries() {
       
   100     /*
       
   101       Note: OR queries that include short words in OR groups are only accepted
       
   102       if the ORed terms are ANDed with at least one long word in the rest of the query.
       
   103 
       
   104       e.g. enim dolore OR ut = enim (dolore OR ut) = (enim dolor) OR (enim ut) -> good
       
   105       e.g. dolore OR ut = (dolore) OR (ut) -> bad
       
   106 
       
   107       This is a design limitation to avoid full table scans.
       
   108     */
       
   109     $queries = array(
       
   110       // Simple AND queries.
       
   111       'ipsum' => array(1),
       
   112       'enim' => array(4, 5, 6),
       
   113       'xxxxx' => array(),
       
   114       'enim minim' => array(5, 6),
       
   115       'enim xxxxx' => array(),
       
   116       'dolore eu' => array(7),
       
   117       'dolore xx' => array(),
       
   118       'ut minim' => array(5),
       
   119       'xx minim' => array(),
       
   120       'enim veniam am minim ut' => array(5),
       
   121       // Simple OR queries.
       
   122       'dolore OR ipsum' => array(1, 2, 7),
       
   123       'dolore OR xxxxx' => array(2, 7),
       
   124       'dolore OR ipsum OR enim' => array(1, 2, 4, 5, 6, 7),
       
   125       'ipsum OR dolore sit OR cillum' => array(2, 7),
       
   126       'minim dolore OR ipsum' => array(7),
       
   127       'dolore OR ipsum veniam' => array(7),
       
   128       'minim dolore OR ipsum OR enim' => array(5, 6, 7),
       
   129       'dolore xx OR yy' => array(),
       
   130       'xxxxx dolore OR ipsum' => array(),
       
   131       // Negative queries.
       
   132       'dolore -sit' => array(7),
       
   133       'dolore -eu' => array(2),
       
   134       'dolore -xxxxx' => array(2, 7),
       
   135       'dolore -xx' => array(2, 7),
       
   136       // Phrase queries.
       
   137       '"dolore sit"' => array(2),
       
   138       '"sit dolore"' => array(),
       
   139       '"am minim veniam es"' => array(6, 7),
       
   140       '"minim am veniam es"' => array(),
       
   141       // Mixed queries.
       
   142       '"am minim veniam es" OR dolore' => array(2, 6, 7),
       
   143       '"minim am veniam es" OR "dolore sit"' => array(2),
       
   144       '"minim am veniam es" OR "sit dolore"' => array(),
       
   145       '"am minim veniam es" -eu' => array(6),
       
   146       '"am minim veniam" -"cillum dolore"' => array(5, 6),
       
   147       '"am minim veniam" -"dolore cillum"' => array(5, 6, 7),
       
   148       'xxxxx "minim am veniam es" OR dolore' => array(),
       
   149       'xx "minim am veniam es" OR dolore' => array()
       
   150     );
       
   151     foreach ($queries as $query => $results) {
       
   152       $result = db_select('search_index', 'i')
       
   153         ->extend('SearchQuery')
       
   154         ->searchExpression($query, SEARCH_TYPE)
       
   155         ->execute();
       
   156 
       
   157       $set = $result ? $result->fetchAll() : array();
       
   158       $this->_testQueryMatching($query, $set, $results);
       
   159       $this->_testQueryScores($query, $set, $results);
       
   160     }
       
   161 
       
   162     // These queries are run against the second index type, SEARCH_TYPE_2.
       
   163     $queries = array(
       
   164       // Simple AND queries.
       
   165       'ipsum' => array(),
       
   166       'enim' => array(),
       
   167       'enim minim' => array(),
       
   168       'dear' => array(8),
       
   169       'germany' => array(11, 12),
       
   170     );
       
   171     foreach ($queries as $query => $results) {
       
   172       $result = db_select('search_index', 'i')
       
   173         ->extend('SearchQuery')
       
   174         ->searchExpression($query, SEARCH_TYPE_2)
       
   175         ->execute();
       
   176 
       
   177       $set = $result ? $result->fetchAll() : array();
       
   178       $this->_testQueryMatching($query, $set, $results);
       
   179       $this->_testQueryScores($query, $set, $results);
       
   180     }
       
   181 
       
   182     // These queries are run against the third index type, SEARCH_TYPE_JPN.
       
   183     $queries = array(
       
   184       // Simple AND queries.
       
   185       '呂波耳' => array(13),
       
   186       '以呂波耳' => array(13),
       
   187       'ほへと ヌルヲ' => array(13),
       
   188       'とちリ' => array(),
       
   189       'ドルーパル' => array(14),
       
   190       'パルが大' => array(14),
       
   191       'コーヒー' => array(15),
       
   192       'ヒーキ' => array(),
       
   193     );
       
   194     foreach ($queries as $query => $results) {
       
   195       $result = db_select('search_index', 'i')
       
   196         ->extend('SearchQuery')
       
   197         ->searchExpression($query, SEARCH_TYPE_JPN)
       
   198         ->execute();
       
   199 
       
   200       $set = $result ? $result->fetchAll() : array();
       
   201       $this->_testQueryMatching($query, $set, $results);
       
   202       $this->_testQueryScores($query, $set, $results);
       
   203     }
       
   204   }
       
   205 
       
   206   /**
       
   207    * Test the matching abilities of the engine.
       
   208    *
       
   209    * Verify if a query produces the correct results.
       
   210    */
       
   211   function _testQueryMatching($query, $set, $results) {
       
   212     // Get result IDs.
       
   213     $found = array();
       
   214     foreach ($set as $item) {
       
   215       $found[] = $item->sid;
       
   216     }
       
   217 
       
   218     // Compare $results and $found.
       
   219     sort($found);
       
   220     sort($results);
       
   221     $this->assertEqual($found, $results, "Query matching '$query'");
       
   222   }
       
   223 
       
   224   /**
       
   225    * Test the scoring abilities of the engine.
       
   226    *
       
   227    * Verify if a query produces normalized, monotonous scores.
       
   228    */
       
   229   function _testQueryScores($query, $set, $results) {
       
   230     // Get result scores.
       
   231     $scores = array();
       
   232     foreach ($set as $item) {
       
   233       $scores[] = $item->calculated_score;
       
   234     }
       
   235 
       
   236     // Check order.
       
   237     $sorted = $scores;
       
   238     sort($sorted);
       
   239     $this->assertEqual($scores, array_reverse($sorted), "Query order '$query'");
       
   240 
       
   241     // Check range.
       
   242     $this->assertEqual(!count($scores) || (min($scores) > 0.0 && max($scores) <= 1.0001), TRUE, "Query scoring '$query'");
       
   243   }
       
   244 }
       
   245 
       
   246 /**
       
   247  * Tests the bike shed text on no results page, and text on the search page.
       
   248  */
       
   249 class SearchPageText extends DrupalWebTestCase {
       
   250   protected $searching_user;
       
   251 
       
   252   public static function getInfo() {
       
   253     return array(
       
   254       'name' => 'Search page text',
       
   255       'description' => 'Tests the bike shed text on the no results page, and various other text on search pages.',
       
   256       'group' => 'Search'
       
   257     );
       
   258   }
       
   259 
       
   260   function setUp() {
       
   261     parent::setUp('search');
       
   262 
       
   263     // Create user.
       
   264     $this->searching_user = $this->drupalCreateUser(array('search content', 'access user profiles'));
       
   265   }
       
   266 
       
   267   /**
       
   268    * Tests the failed search text, and various other text on the search page.
       
   269    */
       
   270   function testSearchText() {
       
   271     $this->drupalLogin($this->searching_user);
       
   272     $this->drupalGet('search/node');
       
   273     $this->assertText(t('Enter your keywords'));
       
   274     $this->assertText(t('Search'));
       
   275     $title = t('Search') . ' | Drupal';
       
   276     $this->assertTitle($title, 'Search page title is correct');
       
   277 
       
   278     $edit = array();
       
   279     $edit['keys'] = 'bike shed ' . $this->randomName();
       
   280     $this->drupalPost('search/node', $edit, t('Search'));
       
   281     $this->assertText(t('Consider loosening your query with OR. bike OR shed will often show more results than bike shed.'), 'Help text is displayed when search returns no results.');
       
   282     $this->assertText(t('Search'));
       
   283     $this->assertTitle($title, 'Search page title is correct');
       
   284 
       
   285     $edit['keys'] = $this->searching_user->name;
       
   286     $this->drupalPost('search/user', $edit, t('Search'));
       
   287     $this->assertText(t('Search'));
       
   288     $this->assertTitle($title, 'Search page title is correct');
       
   289 
       
   290     // Test that search keywords containing slashes are correctly loaded
       
   291     // from the path and displayed in the search form.
       
   292     $arg = $this->randomName() . '/' . $this->randomName();
       
   293     $this->drupalGet('search/node/' . $arg);
       
   294     $input = $this->xpath("//input[@id='edit-keys' and @value='{$arg}']");
       
   295     $this->assertFalse(empty($input), 'Search keys with a / are correctly set as the default value in the search box.');
       
   296 
       
   297     // Test a search input exceeding the limit of AND/OR combinations to test
       
   298     // the Denial-of-Service protection.
       
   299     $limit = variable_get('search_and_or_limit', 7);
       
   300     $keys = array();
       
   301     for ($i = 0; $i < $limit + 1; $i++) {
       
   302       $keys[] = $this->randomName(3);
       
   303       if ($i % 2 == 0) {
       
   304         $keys[] = 'OR';
       
   305       }
       
   306     }
       
   307     $edit['keys'] = implode(' ', $keys);
       
   308     $this->drupalPost('search/node', $edit, t('Search'));
       
   309     $this->assertRaw(t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', array('@count' => $limit)));
       
   310   }
       
   311 }
       
   312 
       
   313 /**
       
   314  * Indexes content and tests the advanced search form.
       
   315  */
       
   316 class SearchAdvancedSearchForm extends DrupalWebTestCase {
       
   317   protected $node;
       
   318 
       
   319   public static function getInfo() {
       
   320     return array(
       
   321       'name' => 'Advanced search form',
       
   322       'description' => 'Indexes content and tests the advanced search form.',
       
   323       'group' => 'Search',
       
   324     );
       
   325   }
       
   326 
       
   327   function setUp() {
       
   328     parent::setUp('search');
       
   329     // Create and login user.
       
   330     $test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search', 'administer nodes'));
       
   331     $this->drupalLogin($test_user);
       
   332 
       
   333     // Create initial node.
       
   334     $node = $this->drupalCreateNode();
       
   335     $this->node = $this->drupalCreateNode();
       
   336 
       
   337     // First update the index. This does the initial processing.
       
   338     node_update_index();
       
   339 
       
   340     // Then, run the shutdown function. Testing is a unique case where indexing
       
   341     // and searching has to happen in the same request, so running the shutdown
       
   342     // function manually is needed to finish the indexing process.
       
   343     search_update_totals();
       
   344   }
       
   345 
       
   346   /**
       
   347    * Test using the search form with GET and POST queries.
       
   348    * Test using the advanced search form to limit search to nodes of type "Basic page".
       
   349    */
       
   350   function testNodeType() {
       
   351     $this->assertTrue($this->node->type == 'page', 'Node type is Basic page.');
       
   352 
       
   353     // Assert that the dummy title doesn't equal the real title.
       
   354     $dummy_title = 'Lorem ipsum';
       
   355     $this->assertNotEqual($dummy_title, $this->node->title, "Dummy title doesn't equal node title");
       
   356 
       
   357     // Search for the dummy title with a GET query.
       
   358     $this->drupalGet('search/node/' . $dummy_title);
       
   359     $this->assertNoText($this->node->title, 'Basic page node is not found with dummy title.');
       
   360 
       
   361     // Search for the title of the node with a GET query.
       
   362     $this->drupalGet('search/node/' . $this->node->title);
       
   363     $this->assertText($this->node->title, 'Basic page node is found with GET query.');
       
   364 
       
   365     // Search for the title of the node with a POST query.
       
   366     $edit = array('or' => $this->node->title);
       
   367     $this->drupalPost('search/node', $edit, t('Advanced search'));
       
   368     $this->assertText($this->node->title, 'Basic page node is found with POST query.');
       
   369 
       
   370     // Advanced search type option.
       
   371     $this->drupalPost('search/node', array_merge($edit, array('type[page]' => 'page')), t('Advanced search'));
       
   372     $this->assertText($this->node->title, 'Basic page node is found with POST query and type:page.');
       
   373 
       
   374     $this->drupalPost('search/node', array_merge($edit, array('type[article]' => 'article')), t('Advanced search'));
       
   375     $this->assertText('bike shed', 'Article node is not found with POST query and type:article.');
       
   376   }
       
   377 }
       
   378 
       
   379 /**
       
   380  * Indexes content and tests ranking factors.
       
   381  */
       
   382 class SearchRankingTestCase extends DrupalWebTestCase {
       
   383   public static function getInfo() {
       
   384     return array(
       
   385       'name' => 'Search engine ranking',
       
   386       'description' => 'Indexes content and tests ranking factors.',
       
   387       'group' => 'Search',
       
   388     );
       
   389   }
       
   390 
       
   391   /**
       
   392    * Implementation setUp().
       
   393    */
       
   394   function setUp() {
       
   395     parent::setUp('search', 'statistics', 'comment');
       
   396   }
       
   397 
       
   398   function testRankings() {
       
   399     // Login with sufficient privileges.
       
   400     $this->drupalLogin($this->drupalCreateUser(array('skip comment approval', 'create page content')));
       
   401 
       
   402     // Build a list of the rankings to test.
       
   403     $node_ranks = array('sticky', 'promote', 'relevance', 'recent', 'comments', 'views');
       
   404 
       
   405     // Create nodes for testing.
       
   406     foreach ($node_ranks as $node_rank) {
       
   407       $settings = array(
       
   408         'type' => 'page',
       
   409         'title' => 'Drupal rocks',
       
   410         'body' => array(LANGUAGE_NONE => array(array('value' => "Drupal's search rocks"))),
       
   411       );
       
   412       foreach (array(0, 1) as $num) {
       
   413         if ($num == 1) {
       
   414           switch ($node_rank) {
       
   415             case 'sticky':
       
   416             case 'promote':
       
   417               $settings[$node_rank] = 1;
       
   418               break;
       
   419             case 'relevance':
       
   420               $settings['body'][LANGUAGE_NONE][0]['value'] .= " really rocks";
       
   421               break;
       
   422             case 'recent':
       
   423               $settings['created'] = REQUEST_TIME + 3600;
       
   424               break;
       
   425             case 'comments':
       
   426               $settings['comment'] = 2;
       
   427               break;
       
   428           }
       
   429         }
       
   430         $nodes[$node_rank][$num] = $this->drupalCreateNode($settings);
       
   431       }
       
   432     }
       
   433 
       
   434     // Update the search index.
       
   435     module_invoke_all('update_index');
       
   436     search_update_totals();
       
   437 
       
   438     // Refresh variables after the treatment.
       
   439     $this->refreshVariables();
       
   440 
       
   441     // Add a comment to one of the nodes.
       
   442     $edit = array();
       
   443     $edit['subject'] = 'my comment title';
       
   444     $edit['comment_body[' . LANGUAGE_NONE . '][0][value]'] = 'some random comment';
       
   445     $this->drupalGet('comment/reply/' . $nodes['comments'][1]->nid);
       
   446     $this->drupalPost(NULL, $edit, t('Preview'));
       
   447     $this->drupalPost(NULL, $edit, t('Save'));
       
   448 
       
   449     // Enable counting of statistics.
       
   450     variable_set('statistics_count_content_views', 1);
       
   451 
       
   452     // Then View one of the nodes a bunch of times.
       
   453     for ($i = 0; $i < 5; $i ++) {
       
   454       $this->drupalGet('node/' . $nodes['views'][1]->nid);
       
   455     }
       
   456 
       
   457     // Test each of the possible rankings.
       
   458     foreach ($node_ranks as $node_rank) {
       
   459       // Disable all relevancy rankings except the one we are testing.
       
   460       foreach ($node_ranks as $var) {
       
   461         variable_set('node_rank_' . $var, $var == $node_rank ? 10 : 0);
       
   462       }
       
   463 
       
   464       // Do the search and assert the results.
       
   465       $set = node_search_execute('rocks');
       
   466       $this->assertEqual($set[0]['node']->nid, $nodes[$node_rank][1]->nid, 'Search ranking "' . $node_rank . '" order.');
       
   467     }
       
   468   }
       
   469 
       
   470   /**
       
   471    * Test rankings of HTML tags.
       
   472    */
       
   473   function testHTMLRankings() {
       
   474     // Login with sufficient privileges.
       
   475     $this->drupalLogin($this->drupalCreateUser(array('create page content')));
       
   476 
       
   477     // Test HTML tags with different weights.
       
   478     $sorted_tags = array('h1', 'h2', 'h3', 'h4', 'a', 'h5', 'h6', 'notag');
       
   479     $shuffled_tags = $sorted_tags;
       
   480 
       
   481     // Shuffle tags to ensure HTML tags are ranked properly.
       
   482     shuffle($shuffled_tags);
       
   483     $settings = array(
       
   484       'type' => 'page',
       
   485       'title' => 'Simple node',
       
   486     );
       
   487     foreach ($shuffled_tags as $tag) {
       
   488       switch ($tag) {
       
   489         case 'a':
       
   490           $settings['body'] = array(LANGUAGE_NONE => array(array('value' => l('Drupal Rocks', 'node'), 'format' => 'full_html')));
       
   491           break;
       
   492         case 'notag':
       
   493           $settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'Drupal Rocks')));
       
   494           break;
       
   495         default:
       
   496           $settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks</$tag>", 'format' => 'full_html')));
       
   497           break;
       
   498       }
       
   499       $nodes[$tag] = $this->drupalCreateNode($settings);
       
   500     }
       
   501 
       
   502     // Update the search index.
       
   503     module_invoke_all('update_index');
       
   504     search_update_totals();
       
   505 
       
   506     // Refresh variables after the treatment.
       
   507     $this->refreshVariables();
       
   508 
       
   509     // Disable all other rankings.
       
   510     $node_ranks = array('sticky', 'promote', 'recent', 'comments', 'views');
       
   511     foreach ($node_ranks as $node_rank) {
       
   512       variable_set('node_rank_' . $node_rank, 0);
       
   513     }
       
   514     $set = node_search_execute('rocks');
       
   515 
       
   516     // Test the ranking of each tag.
       
   517     foreach ($sorted_tags as $tag_rank => $tag) {
       
   518       // Assert the results.
       
   519       if ($tag == 'notag') {
       
   520         $this->assertEqual($set[$tag_rank]['node']->nid, $nodes[$tag]->nid, 'Search tag ranking for plain text order.');
       
   521       } else {
       
   522         $this->assertEqual($set[$tag_rank]['node']->nid, $nodes[$tag]->nid, 'Search tag ranking for "&lt;' . $sorted_tags[$tag_rank] . '&gt;" order.');
       
   523       }
       
   524     }
       
   525 
       
   526     // Test tags with the same weight against the sorted tags.
       
   527     $unsorted_tags = array('u', 'b', 'i', 'strong', 'em');
       
   528     foreach ($unsorted_tags as $tag) {
       
   529       $settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks</$tag>", 'format' => 'full_html')));
       
   530       $node = $this->drupalCreateNode($settings);
       
   531 
       
   532       // Update the search index.
       
   533       module_invoke_all('update_index');
       
   534       search_update_totals();
       
   535 
       
   536       // Refresh variables after the treatment.
       
   537       $this->refreshVariables();
       
   538 
       
   539       $set = node_search_execute('rocks');
       
   540 
       
   541       // Ranking should always be second to last.
       
   542       $set = array_slice($set, -2, 1);
       
   543 
       
   544       // Assert the results.
       
   545       $this->assertEqual($set[0]['node']->nid, $node->nid, 'Search tag ranking for "&lt;' . $tag . '&gt;" order.');
       
   546 
       
   547       // Delete node so it doesn't show up in subsequent search results.
       
   548       node_delete($node->nid);
       
   549     }
       
   550   }
       
   551 
       
   552   /**
       
   553    * Verifies that if we combine two rankings, search still works.
       
   554    *
       
   555    * See issue http://drupal.org/node/771596
       
   556    */
       
   557   function testDoubleRankings() {
       
   558     // Login with sufficient privileges.
       
   559     $this->drupalLogin($this->drupalCreateUser(array('skip comment approval', 'create page content')));
       
   560 
       
   561     // See testRankings() above - build a node that will rank high for sticky.
       
   562     $settings = array(
       
   563       'type' => 'page',
       
   564       'title' => 'Drupal rocks',
       
   565       'body' => array(LANGUAGE_NONE => array(array('value' => "Drupal's search rocks"))),
       
   566       'sticky' => 1,
       
   567     );
       
   568 
       
   569     $node = $this->drupalCreateNode($settings);
       
   570 
       
   571     // Update the search index.
       
   572     module_invoke_all('update_index');
       
   573     search_update_totals();
       
   574 
       
   575     // Refresh variables after the treatment.
       
   576     $this->refreshVariables();
       
   577 
       
   578     // Set up for ranking sticky and lots of comments; make sure others are
       
   579     // disabled.
       
   580     $node_ranks = array('sticky', 'promote', 'relevance', 'recent', 'comments', 'views');
       
   581     foreach ($node_ranks as $var) {
       
   582       $value = ($var == 'sticky' || $var == 'comments') ? 10 : 0;
       
   583       variable_set('node_rank_' . $var, $value);
       
   584     }
       
   585 
       
   586     // Do the search and assert the results.
       
   587     $set = node_search_execute('rocks');
       
   588     $this->assertEqual($set[0]['node']->nid, $node->nid, 'Search double ranking order.');
       
   589   }
       
   590 }
       
   591 
       
   592 /**
       
   593  * Tests the rendering of the search block.
       
   594  */
       
   595 class SearchBlockTestCase extends DrupalWebTestCase {
       
   596   public static function getInfo() {
       
   597     return array(
       
   598       'name' => 'Block availability',
       
   599       'description' => 'Check if the search form block is available.',
       
   600       'group' => 'Search',
       
   601     );
       
   602   }
       
   603 
       
   604   function setUp() {
       
   605     parent::setUp('search');
       
   606 
       
   607     // Create and login user
       
   608     $admin_user = $this->drupalCreateUser(array('administer blocks', 'search content'));
       
   609     $this->drupalLogin($admin_user);
       
   610   }
       
   611 
       
   612   function testSearchFormBlock() {
       
   613     // Set block title to confirm that the interface is available.
       
   614     $this->drupalPost('admin/structure/block/manage/search/form/configure', array('title' => $this->randomName(8)), t('Save block'));
       
   615     $this->assertText(t('The block configuration has been saved.'), 'Block configuration set.');
       
   616 
       
   617     // Set the block to a region to confirm block is available.
       
   618     $edit = array();
       
   619     $edit['blocks[search_form][region]'] = 'footer';
       
   620     $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
       
   621     $this->assertText(t('The block settings have been updated.'), 'Block successfully move to footer region.');
       
   622   }
       
   623 
       
   624   /**
       
   625    * Test that the search block form works correctly.
       
   626    */
       
   627   function testBlock() {
       
   628     // Enable the block, and place it in the 'content' region so that it isn't
       
   629     // hidden on 404 pages.
       
   630     $edit = array('blocks[search_form][region]' => 'content');
       
   631     $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
       
   632 
       
   633     // Test a normal search via the block form, from the front page.
       
   634     $terms = array('search_block_form' => 'test');
       
   635     $this->drupalPost('node', $terms, t('Search'));
       
   636     $this->assertText('Your search yielded no results');
       
   637 
       
   638     // Test a search from the block on a 404 page.
       
   639     $this->drupalGet('foo');
       
   640     $this->assertResponse(404);
       
   641     $this->drupalPost(NULL, $terms, t('Search'));
       
   642     $this->assertResponse(200);
       
   643     $this->assertText('Your search yielded no results');
       
   644 
       
   645     // Test a search from the block when it doesn't appear on the search page.
       
   646     $edit = array('pages' => 'search');
       
   647     $this->drupalPost('admin/structure/block/manage/search/form/configure', $edit, t('Save block'));
       
   648     $this->drupalPost('node', $terms, t('Search'));
       
   649     $this->assertText('Your search yielded no results');
       
   650 
       
   651     // Confirm that the user is redirected to the search page.
       
   652     $this->assertEqual(
       
   653       $this->getUrl(),
       
   654       url('search/node/' . $terms['search_block_form'], array('absolute' => TRUE)),
       
   655       'Redirected to correct url.'
       
   656     );
       
   657 
       
   658     // Test an empty search via the block form, from the front page.
       
   659     $terms = array('search_block_form' => '');
       
   660     $this->drupalPost('node', $terms, t('Search'));
       
   661     $this->assertText('Please enter some keywords');
       
   662 
       
   663     // Confirm that the user is redirected to the search page, when form is submitted empty.
       
   664     $this->assertEqual(
       
   665       $this->getUrl(),
       
   666       url('search/node/', array('absolute' => TRUE)),
       
   667       'Redirected to correct url.'
       
   668     );
       
   669 
       
   670     // Test that after entering a too-short keyword in the form, you can then
       
   671     // search again with a longer keyword. First test using the block form.
       
   672     $terms = array('search_block_form' => 'a');
       
   673     $this->drupalPost('node', $terms, t('Search'));
       
   674     $this->assertText('You must include at least one positive keyword with 3 characters or more');
       
   675     $terms = array('search_block_form' => 'foo');
       
   676     $this->drupalPost(NULL, $terms, t('Search'));
       
   677     $this->assertNoText('You must include at least one positive keyword with 3 characters or more');
       
   678     $this->assertText('Your search yielded no results');
       
   679 
       
   680     // Same test again, using the search page form for the second search this time.
       
   681     $terms = array('search_block_form' => 'a');
       
   682     $this->drupalPost('node', $terms, t('Search'));
       
   683     $terms = array('keys' => 'foo');
       
   684     $this->drupalPost(NULL, $terms, t('Search'));
       
   685     $this->assertNoText('You must include at least one positive keyword with 3 characters or more');
       
   686     $this->assertText('Your search yielded no results');
       
   687   }
       
   688 }
       
   689 
       
   690 /**
       
   691  * Tests that searching for a phrase gets the correct page count.
       
   692  */
       
   693 class SearchExactTestCase extends DrupalWebTestCase {
       
   694   public static function getInfo() {
       
   695     return array(
       
   696       'name' => 'Search engine phrase queries',
       
   697       'description' => 'Tests that searching for a phrase gets the correct page count.',
       
   698       'group' => 'Search',
       
   699     );
       
   700   }
       
   701 
       
   702   function setUp() {
       
   703     parent::setUp('search');
       
   704   }
       
   705 
       
   706   /**
       
   707    * Tests that the correct number of pager links are found for both keywords and phrases.
       
   708    */
       
   709   function testExactQuery() {
       
   710     // Login with sufficient privileges.
       
   711     $this->drupalLogin($this->drupalCreateUser(array('create page content', 'search content')));
       
   712 
       
   713     $settings = array(
       
   714       'type' => 'page',
       
   715       'title' => 'Simple Node',
       
   716     );
       
   717     // Create nodes with exact phrase.
       
   718     for ($i = 0; $i <= 17; $i++) {
       
   719       $settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'love pizza')));
       
   720       $this->drupalCreateNode($settings);
       
   721     }
       
   722     // Create nodes containing keywords.
       
   723     for ($i = 0; $i <= 17; $i++) {
       
   724       $settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'love cheesy pizza')));
       
   725       $this->drupalCreateNode($settings);
       
   726     }
       
   727 
       
   728     // Update the search index.
       
   729     module_invoke_all('update_index');
       
   730     search_update_totals();
       
   731 
       
   732     // Refresh variables after the treatment.
       
   733     $this->refreshVariables();
       
   734 
       
   735     // Test that the correct number of pager links are found for keyword search.
       
   736     $edit = array('keys' => 'love pizza');
       
   737     $this->drupalPost('search/node', $edit, t('Search'));
       
   738     $this->assertLinkByHref('page=1', 0, '2nd page link is found for keyword search.');
       
   739     $this->assertLinkByHref('page=2', 0, '3rd page link is found for keyword search.');
       
   740     $this->assertLinkByHref('page=3', 0, '4th page link is found for keyword search.');
       
   741     $this->assertNoLinkByHref('page=4', '5th page link is not found for keyword search.');
       
   742 
       
   743     // Test that the correct number of pager links are found for exact phrase search.
       
   744     $edit = array('keys' => '"love pizza"');
       
   745     $this->drupalPost('search/node', $edit, t('Search'));
       
   746     $this->assertLinkByHref('page=1', 0, '2nd page link is found for exact phrase search.');
       
   747     $this->assertNoLinkByHref('page=2', '3rd page link is not found for exact phrase search.');
       
   748   }
       
   749 }
       
   750 
       
   751 /**
       
   752  * Test integration searching comments.
       
   753  */
       
   754 class SearchCommentTestCase extends DrupalWebTestCase {
       
   755   protected $admin_user;
       
   756 
       
   757   public static function getInfo() {
       
   758     return array(
       
   759       'name' => 'Comment Search tests',
       
   760       'description' => 'Test integration searching comments.',
       
   761       'group' => 'Search',
       
   762     );
       
   763   }
       
   764 
       
   765   function setUp() {
       
   766     parent::setUp('comment', 'search');
       
   767 
       
   768     // Create and log in an administrative user having access to the Full HTML
       
   769     // text format.
       
   770     $full_html_format = filter_format_load('full_html');
       
   771     $permissions = array(
       
   772       'administer filters',
       
   773       filter_permission_name($full_html_format),
       
   774       'administer permissions',
       
   775       'create page content',
       
   776       'skip comment approval',
       
   777       'access comments',
       
   778     );
       
   779     $this->admin_user = $this->drupalCreateUser($permissions);
       
   780     $this->drupalLogin($this->admin_user);
       
   781   }
       
   782 
       
   783   /**
       
   784    * Verify that comments are rendered using proper format in search results.
       
   785    */
       
   786   function testSearchResultsComment() {
       
   787     $comment_body = 'Test comment body';
       
   788 
       
   789     variable_set('comment_preview_article', DRUPAL_OPTIONAL);
       
   790     // Enable check_plain() for 'Filtered HTML' text format.
       
   791     $filtered_html_format_id = 'filtered_html';
       
   792     $edit = array(
       
   793       'filters[filter_html_escape][status]' => TRUE,
       
   794     );
       
   795     $this->drupalPost('admin/config/content/formats/' . $filtered_html_format_id, $edit, t('Save configuration'));
       
   796     // Allow anonymous users to search content.
       
   797     $edit = array(
       
   798       DRUPAL_ANONYMOUS_RID . '[search content]' => 1,
       
   799       DRUPAL_ANONYMOUS_RID . '[access comments]' => 1,
       
   800       DRUPAL_ANONYMOUS_RID . '[post comments]' => 1,
       
   801     );
       
   802     $this->drupalPost('admin/people/permissions', $edit, t('Save permissions'));
       
   803 
       
   804     // Create a node.
       
   805     $node = $this->drupalCreateNode(array('type' => 'article'));
       
   806     // Post a comment using 'Full HTML' text format.
       
   807     $edit_comment = array();
       
   808     $edit_comment['subject'] = 'Test comment subject';
       
   809     $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = '<h1>' . $comment_body . '</h1>';
       
   810     $full_html_format_id = 'full_html';
       
   811     $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][format]'] = $full_html_format_id;
       
   812     $this->drupalPost('comment/reply/' . $node->nid, $edit_comment, t('Save'));
       
   813 
       
   814     // Invoke search index update.
       
   815     $this->drupalLogout();
       
   816     $this->cronRun();
       
   817 
       
   818     // Search for the comment subject.
       
   819     $edit = array(
       
   820       'search_block_form' => "'" . $edit_comment['subject'] . "'",
       
   821     );
       
   822     $this->drupalPost('', $edit, t('Search'));
       
   823     $this->assertText($node->title, 'Node found in search results.');
       
   824     $this->assertText($edit_comment['subject'], 'Comment subject found in search results.');
       
   825 
       
   826     // Search for the comment body.
       
   827     $edit = array(
       
   828       'search_block_form' => "'" . $comment_body . "'",
       
   829     );
       
   830     $this->drupalPost('', $edit, t('Search'));
       
   831     $this->assertText($node->title, 'Node found in search results.');
       
   832 
       
   833     // Verify that comment is rendered using proper format.
       
   834     $this->assertText($comment_body, 'Comment body text found in search results.');
       
   835     $this->assertNoRaw(t('n/a'), 'HTML in comment body is not hidden.');
       
   836     $this->assertNoRaw(check_plain($edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]']), 'HTML in comment body is not escaped.');
       
   837 
       
   838     // Hide comments.
       
   839     $this->drupalLogin($this->admin_user);
       
   840     $node->comment = 0;
       
   841     node_save($node);
       
   842 
       
   843     // Invoke search index update.
       
   844     $this->drupalLogout();
       
   845     $this->cronRun();
       
   846 
       
   847     // Search for $title.
       
   848     $this->drupalPost('', $edit, t('Search'));
       
   849     $this->assertNoText($comment_body, 'Comment body text not found in search results.');
       
   850   }
       
   851 
       
   852   /**
       
   853    * Verify access rules for comment indexing with different permissions.
       
   854    */
       
   855   function testSearchResultsCommentAccess() {
       
   856     $comment_body = 'Test comment body';
       
   857     $this->comment_subject = 'Test comment subject';
       
   858     $this->admin_role = $this->admin_user->roles;
       
   859     unset($this->admin_role[DRUPAL_AUTHENTICATED_RID]);
       
   860     $this->admin_role = key($this->admin_role);
       
   861 
       
   862     // Create a node.
       
   863     variable_set('comment_preview_article', DRUPAL_OPTIONAL);
       
   864     $this->node = $this->drupalCreateNode(array('type' => 'article'));
       
   865 
       
   866     // Post a comment using 'Full HTML' text format.
       
   867     $edit_comment = array();
       
   868     $edit_comment['subject'] = $this->comment_subject;
       
   869     $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = '<h1>' . $comment_body . '</h1>';
       
   870     $this->drupalPost('comment/reply/' . $this->node->nid, $edit_comment, t('Save'));
       
   871 
       
   872     $this->drupalLogout();
       
   873     $this->setRolePermissions(DRUPAL_ANONYMOUS_RID);
       
   874     $this->checkCommentAccess('Anon user has search permission but no access comments permission, comments should not be indexed');
       
   875 
       
   876     $this->setRolePermissions(DRUPAL_ANONYMOUS_RID, TRUE);
       
   877     $this->checkCommentAccess('Anon user has search permission and access comments permission, comments should be indexed', TRUE);
       
   878 
       
   879     $this->drupalLogin($this->admin_user);
       
   880     $this->drupalGet('admin/people/permissions');
       
   881 
       
   882     // Disable search access for authenticated user to test admin user.
       
   883     $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, FALSE, FALSE);
       
   884 
       
   885     $this->setRolePermissions($this->admin_role);
       
   886     $this->checkCommentAccess('Admin user has search permission but no access comments permission, comments should not be indexed');
       
   887 
       
   888     $this->setRolePermissions($this->admin_role, TRUE);
       
   889     $this->checkCommentAccess('Admin user has search permission and access comments permission, comments should be indexed', TRUE);
       
   890 
       
   891     $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID);
       
   892     $this->checkCommentAccess('Authenticated user has search permission but no access comments permission, comments should not be indexed');
       
   893 
       
   894     $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE);
       
   895     $this->checkCommentAccess('Authenticated user has search permission and access comments permission, comments should be indexed', TRUE);
       
   896 
       
   897     // Verify that access comments permission is inherited from the
       
   898     // authenticated role.
       
   899     $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE, FALSE);
       
   900     $this->setRolePermissions($this->admin_role);
       
   901     $this->checkCommentAccess('Admin user has search permission and no access comments permission, but comments should be indexed because admin user inherits authenticated user\'s permission to access comments', TRUE);
       
   902 
       
   903     // Verify that search content permission is inherited from the authenticated
       
   904     // role.
       
   905     $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE, TRUE);
       
   906     $this->setRolePermissions($this->admin_role, TRUE, FALSE);
       
   907     $this->checkCommentAccess('Admin user has access comments permission and no search permission, but comments should be indexed because admin user inherits authenticated user\'s permission to search', TRUE);
       
   908 
       
   909   }
       
   910 
       
   911   /**
       
   912    * Set permissions for role.
       
   913    */
       
   914   function setRolePermissions($rid, $access_comments = FALSE, $search_content = TRUE) {
       
   915     $permissions = array(
       
   916       'access comments' => $access_comments,
       
   917       'search content' => $search_content,
       
   918     );
       
   919     user_role_change_permissions($rid, $permissions);
       
   920   }
       
   921 
       
   922   /**
       
   923    * Update search index and search for comment.
       
   924    */
       
   925   function checkCommentAccess($message, $assume_access = FALSE) {
       
   926     // Invoke search index update.
       
   927     search_touch_node($this->node->nid);
       
   928     $this->cronRun();
       
   929 
       
   930     // Search for the comment subject.
       
   931     $edit = array(
       
   932       'search_block_form' => "'" . $this->comment_subject . "'",
       
   933     );
       
   934     $this->drupalPost('', $edit, t('Search'));
       
   935     $method = $assume_access ? 'assertText' : 'assertNoText';
       
   936     $verb = $assume_access ? 'found' : 'not found';
       
   937     $this->{$method}($this->node->title, "Node $verb in search results: " . $message);
       
   938     $this->{$method}($this->comment_subject, "Comment subject $verb in search results: " . $message);
       
   939   }
       
   940 
       
   941   /**
       
   942    * Verify that 'add new comment' does not appear in search results or index.
       
   943    */
       
   944   function testAddNewComment() {
       
   945     // Create a node with a short body.
       
   946     $settings = array(
       
   947       'type' => 'article',
       
   948       'title' => 'short title',
       
   949       'body' => array(LANGUAGE_NONE => array(array('value' => 'short body text'))),
       
   950     );
       
   951 
       
   952     $user = $this->drupalCreateUser(array('search content', 'create article content', 'access content'));
       
   953     $this->drupalLogin($user);
       
   954 
       
   955     $node = $this->drupalCreateNode($settings);
       
   956     // Verify that if you view the node on its own page, 'add new comment'
       
   957     // is there.
       
   958     $this->drupalGet('node/' . $node->nid);
       
   959     $this->assertText(t('Add new comment'), 'Add new comment appears on node page');
       
   960 
       
   961     // Run cron to index this page.
       
   962     $this->drupalLogout();
       
   963     $this->cronRun();
       
   964 
       
   965     // Search for 'comment'. Should be no results.
       
   966     $this->drupalLogin($user);
       
   967     $this->drupalPost('search/node', array('keys' => 'comment'), t('Search'));
       
   968     $this->assertText(t('Your search yielded no results'), 'No results searching for the word comment');
       
   969 
       
   970     // Search for the node title. Should be found, and 'Add new comment' should
       
   971     // not be part of the search snippet.
       
   972     $this->drupalPost('search/node', array('keys' => 'short'), t('Search'));
       
   973     $this->assertText($node->title, 'Search for keyword worked');
       
   974     $this->assertNoText(t('Add new comment'), 'Add new comment does not appear on search results page');
       
   975   }
       
   976 
       
   977 }
       
   978 
       
   979 /**
       
   980  * Tests search_expression_insert() and search_expression_extract().
       
   981  *
       
   982  * @see http://drupal.org/node/419388 (issue)
       
   983  */
       
   984 class SearchExpressionInsertExtractTestCase extends DrupalUnitTestCase {
       
   985   public static function getInfo() {
       
   986     return array(
       
   987       'name' => 'Search expression insert/extract',
       
   988       'description' => 'Tests the functions search_expression_insert() and search_expression_extract()',
       
   989       'group' => 'Search',
       
   990     );
       
   991   }
       
   992 
       
   993   function setUp() {
       
   994     drupal_load('module', 'search');
       
   995     parent::setUp();
       
   996   }
       
   997 
       
   998   /**
       
   999    * Tests search_expression_insert() and search_expression_extract().
       
  1000    */
       
  1001   function testInsertExtract() {
       
  1002     $base_expression = "mykeyword";
       
  1003     // Build an array of option, value, what should be in the expression, what
       
  1004     // should be retrieved from expression.
       
  1005     $cases = array(
       
  1006       array('foo', 'bar', 'foo:bar', 'bar'), // Normal case.
       
  1007       array('foo', NULL, '', NULL), // Empty value: shouldn't insert.
       
  1008       array('foo', ' ', 'foo:', ''), // Space as value: should insert but retrieve empty string.
       
  1009       array('foo', '', 'foo:', ''), // Empty string as value: should insert but retrieve empty string.
       
  1010       array('foo', '0', 'foo:0', '0'), // String zero as value: should insert.
       
  1011       array('foo', 0, 'foo:0', '0'), // Numeric zero as value: should insert.
       
  1012     );
       
  1013 
       
  1014     foreach ($cases as $index => $case) {
       
  1015       $after_insert = search_expression_insert($base_expression, $case[0], $case[1]);
       
  1016       if (empty($case[2])) {
       
  1017         $this->assertEqual($after_insert, $base_expression, "Empty insert does not change expression in case $index");
       
  1018       }
       
  1019       else {
       
  1020         $this->assertEqual($after_insert, $base_expression . ' ' . $case[2], "Insert added correct expression for case $index");
       
  1021       }
       
  1022 
       
  1023       $retrieved = search_expression_extract($after_insert, $case[0]);
       
  1024       if (!isset($case[3])) {
       
  1025         $this->assertFalse(isset($retrieved), "Empty retrieval results in unset value in case $index");
       
  1026       }
       
  1027       else {
       
  1028         $this->assertEqual($retrieved, $case[3], "Value is retrieved for case $index");
       
  1029       }
       
  1030 
       
  1031       $after_clear = search_expression_insert($after_insert, $case[0]);
       
  1032       $this->assertEqual(trim($after_clear), $base_expression, "After clearing, base expression is restored for case $index");
       
  1033 
       
  1034       $cleared = search_expression_extract($after_clear, $case[0]);
       
  1035       $this->assertFalse(isset($cleared), "After clearing, value could not be retrieved for case $index");
       
  1036     }
       
  1037   }
       
  1038 }
       
  1039 
       
  1040 /**
       
  1041  * Tests that comment count display toggles properly on comment status of node
       
  1042  *
       
  1043  * Issue 537278
       
  1044  *
       
  1045  * - Nodes with comment status set to Open should always how comment counts
       
  1046  * - Nodes with comment status set to Closed should show comment counts
       
  1047  *     only when there are comments
       
  1048  * - Nodes with comment status set to Hidden should never show comment counts
       
  1049  */
       
  1050 class SearchCommentCountToggleTestCase extends DrupalWebTestCase {
       
  1051   protected $searching_user;
       
  1052   protected $searchable_nodes;
       
  1053 
       
  1054   public static function getInfo() {
       
  1055     return array(
       
  1056       'name' => 'Comment count toggle',
       
  1057       'description' => 'Verify that comment count display toggles properly on comment status of node.',
       
  1058       'group' => 'Search',
       
  1059     );
       
  1060   }
       
  1061 
       
  1062   function setUp() {
       
  1063     parent::setUp('search');
       
  1064 
       
  1065     // Create searching user.
       
  1066     $this->searching_user = $this->drupalCreateUser(array('search content', 'access content', 'access comments', 'skip comment approval'));
       
  1067 
       
  1068     // Create initial nodes.
       
  1069     $node_params = array('type' => 'article', 'body' => array(LANGUAGE_NONE => array(array('value' => 'SearchCommentToggleTestCase'))));
       
  1070 
       
  1071     $this->searchable_nodes['1 comment'] = $this->drupalCreateNode($node_params);
       
  1072     $this->searchable_nodes['0 comments'] = $this->drupalCreateNode($node_params);
       
  1073 
       
  1074     // Login with sufficient privileges.
       
  1075     $this->drupalLogin($this->searching_user);
       
  1076 
       
  1077     // Create a comment array
       
  1078     $edit_comment = array();
       
  1079     $edit_comment['subject'] = $this->randomName();
       
  1080     $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = $this->randomName();
       
  1081     $filtered_html_format_id = 'filtered_html';
       
  1082     $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][format]'] = $filtered_html_format_id;
       
  1083 
       
  1084     // Post comment to the test node with comment
       
  1085     $this->drupalPost('comment/reply/' . $this->searchable_nodes['1 comment']->nid, $edit_comment, t('Save'));
       
  1086 
       
  1087     // First update the index. This does the initial processing.
       
  1088     node_update_index();
       
  1089 
       
  1090     // Then, run the shutdown function. Testing is a unique case where indexing
       
  1091     // and searching has to happen in the same request, so running the shutdown
       
  1092     // function manually is needed to finish the indexing process.
       
  1093     search_update_totals();
       
  1094   }
       
  1095 
       
  1096   /**
       
  1097    * Verify that comment count display toggles properly on comment status of node
       
  1098    */
       
  1099   function testSearchCommentCountToggle() {
       
  1100     // Search for the nodes by string in the node body.
       
  1101     $edit = array(
       
  1102       'search_block_form' => "'SearchCommentToggleTestCase'",
       
  1103     );
       
  1104 
       
  1105     // Test comment count display for nodes with comment status set to Open
       
  1106     $this->drupalPost('', $edit, t('Search'));
       
  1107     $this->assertText(t('0 comments'), 'Empty comment count displays for nodes with comment status set to Open');
       
  1108     $this->assertText(t('1 comment'), 'Non-empty comment count displays for nodes with comment status set to Open');
       
  1109 
       
  1110     // Test comment count display for nodes with comment status set to Closed
       
  1111     $this->searchable_nodes['0 comments']->comment = COMMENT_NODE_CLOSED;
       
  1112     node_save($this->searchable_nodes['0 comments']);
       
  1113     $this->searchable_nodes['1 comment']->comment = COMMENT_NODE_CLOSED;
       
  1114     node_save($this->searchable_nodes['1 comment']);
       
  1115 
       
  1116     $this->drupalPost('', $edit, t('Search'));
       
  1117     $this->assertNoText(t('0 comments'), 'Empty comment count does not display for nodes with comment status set to Closed');
       
  1118     $this->assertText(t('1 comment'), 'Non-empty comment count displays for nodes with comment status set to Closed');
       
  1119 
       
  1120     // Test comment count display for nodes with comment status set to Hidden
       
  1121     $this->searchable_nodes['0 comments']->comment = COMMENT_NODE_HIDDEN;
       
  1122     node_save($this->searchable_nodes['0 comments']);
       
  1123     $this->searchable_nodes['1 comment']->comment = COMMENT_NODE_HIDDEN;
       
  1124     node_save($this->searchable_nodes['1 comment']);
       
  1125 
       
  1126     $this->drupalPost('', $edit, t('Search'));
       
  1127     $this->assertNoText(t('0 comments'), 'Empty comment count does not display for nodes with comment status set to Hidden');
       
  1128     $this->assertNoText(t('1 comment'), 'Non-empty comment count does not display for nodes with comment status set to Hidden');
       
  1129   }
       
  1130 }
       
  1131 
       
  1132 /**
       
  1133  * Test search_simplify() on every Unicode character, and some other cases.
       
  1134  */
       
  1135 class SearchSimplifyTestCase extends DrupalWebTestCase {
       
  1136   public static function getInfo() {
       
  1137     return array(
       
  1138       'name' => 'Search simplify',
       
  1139       'description' => 'Check that the search_simply() function works as intended.',
       
  1140       'group' => 'Search',
       
  1141     );
       
  1142   }
       
  1143 
       
  1144   /**
       
  1145    * Tests that all Unicode characters simplify correctly.
       
  1146    */
       
  1147   function testSearchSimplifyUnicode() {
       
  1148     // This test uses a file that was constructed so that the even lines are
       
  1149     // boundary characters, and the odd lines are valid word characters. (It
       
  1150     // was generated as a sequence of all the Unicode characters, and then the
       
  1151     // boundary chararacters (punctuation, spaces, etc.) were split off into
       
  1152     // their own lines).  So the even-numbered lines should simplify to nothing,
       
  1153     // and the odd-numbered lines we need to split into shorter chunks and
       
  1154     // verify that simplification doesn't lose any characters.
       
  1155     $input = file_get_contents(DRUPAL_ROOT . '/modules/search/tests/UnicodeTest.txt');
       
  1156     $basestrings = explode(chr(10), $input);
       
  1157     $strings = array();
       
  1158     foreach ($basestrings as $key => $string) {
       
  1159       if ($key %2) {
       
  1160         // Even line - should simplify down to a space.
       
  1161         $simplified = search_simplify($string);
       
  1162         $this->assertIdentical($simplified, ' ', "Line $key is excluded from the index");
       
  1163       }
       
  1164       else {
       
  1165         // Odd line, should be word characters.
       
  1166         // Split this into 30-character chunks, so we don't run into limits
       
  1167         // of truncation in search_simplify().
       
  1168         $start = 0;
       
  1169         while ($start < drupal_strlen($string)) {
       
  1170           $newstr = drupal_substr($string, $start, 30);
       
  1171           // Special case: leading zeros are removed from numeric strings,
       
  1172           // and there's one string in this file that is numbers starting with
       
  1173           // zero, so prepend a 1 on that string.
       
  1174           if (preg_match('/^[0-9]+$/', $newstr)) {
       
  1175             $newstr = '1' . $newstr;
       
  1176           }
       
  1177           $strings[] = $newstr;
       
  1178           $start += 30;
       
  1179         }
       
  1180       }
       
  1181     }
       
  1182     foreach ($strings as $key => $string) {
       
  1183       $simplified = search_simplify($string);
       
  1184       $this->assertTrue(drupal_strlen($simplified) >= drupal_strlen($string), "Nothing is removed from string $key.");
       
  1185     }
       
  1186 
       
  1187     // Test the low-numbered ASCII control characters separately. They are not
       
  1188     // in the text file because they are problematic for diff, especially \0.
       
  1189     $string = '';
       
  1190     for ($i = 0; $i < 32; $i++) {
       
  1191       $string .= chr($i);
       
  1192     }
       
  1193     $this->assertIdentical(' ', search_simplify($string), 'Search simplify works for ASCII control characters.');
       
  1194   }
       
  1195 
       
  1196   /**
       
  1197    * Tests that search_simplify() does the right thing with punctuation.
       
  1198    */
       
  1199   function testSearchSimplifyPunctuation() {
       
  1200     $cases = array(
       
  1201       array('20.03/94-28,876', '20039428876', 'Punctuation removed from numbers'),
       
  1202       array('great...drupal--module', 'great drupal module', 'Multiple dot and dashes are word boundaries'),
       
  1203       array('very_great-drupal.module', 'verygreatdrupalmodule', 'Single dot, dash, underscore are removed'),
       
  1204       array('regular,punctuation;word', 'regular punctuation word', 'Punctuation is a word boundary'),
       
  1205     );
       
  1206 
       
  1207     foreach ($cases as $case) {
       
  1208       $out = trim(search_simplify($case[0]));
       
  1209       $this->assertEqual($out, $case[1], $case[2]);
       
  1210     }
       
  1211   }
       
  1212 }
       
  1213 
       
  1214 
       
  1215 /**
       
  1216  * Tests keywords and conditions.
       
  1217  */
       
  1218 class SearchKeywordsConditions extends DrupalWebTestCase {
       
  1219 
       
  1220   public static function getInfo() {
       
  1221     return array(
       
  1222       'name' => 'Keywords and conditions',
       
  1223       'description' => 'Verify the search pulls in keywords and extra conditions.',
       
  1224       'group' => 'Search',
       
  1225     );
       
  1226   }
       
  1227 
       
  1228   function setUp() {
       
  1229     parent::setUp('search', 'search_extra_type');
       
  1230     // Create searching user.
       
  1231     $this->searching_user = $this->drupalCreateUser(array('search content', 'access content', 'access comments', 'skip comment approval'));
       
  1232     // Login with sufficient privileges.
       
  1233     $this->drupalLogin($this->searching_user);
       
  1234     // Test with all search modules enabled.
       
  1235     variable_set('search_active_modules', array('node' => 'node', 'user' => 'user', 'search_extra_type' => 'search_extra_type'));
       
  1236     menu_rebuild();
       
  1237   }
       
  1238 
       
  1239   /**
       
  1240    * Verify the kewords are captured and conditions respected.
       
  1241    */
       
  1242   function testSearchKeyswordsConditions() {
       
  1243     // No keys, not conditions - no results.
       
  1244     $this->drupalGet('search/dummy_path');
       
  1245     $this->assertNoText('Dummy search snippet to display');
       
  1246     // With keys - get results.
       
  1247     $keys = 'bike shed ' . $this->randomName();
       
  1248     $this->drupalGet("search/dummy_path/{$keys}");
       
  1249     $this->assertText("Dummy search snippet to display. Keywords: {$keys}");
       
  1250     $keys = 'blue drop ' . $this->randomName();
       
  1251     $this->drupalGet("search/dummy_path", array('query' => array('keys' => $keys)));
       
  1252     $this->assertText("Dummy search snippet to display. Keywords: {$keys}");
       
  1253     // Add some conditions and keys.
       
  1254     $keys = 'moving drop ' . $this->randomName();
       
  1255     $this->drupalGet("search/dummy_path/bike", array('query' => array('search_conditions' => $keys)));
       
  1256     $this->assertText("Dummy search snippet to display.");
       
  1257     $this->assertRaw(print_r(array('search_conditions' => $keys), TRUE));
       
  1258     // Add some conditions and no keys.
       
  1259     $keys = 'drop kick ' . $this->randomName();
       
  1260     $this->drupalGet("search/dummy_path", array('query' => array('search_conditions' => $keys)));
       
  1261     $this->assertText("Dummy search snippet to display.");
       
  1262     $this->assertRaw(print_r(array('search_conditions' => $keys), TRUE));
       
  1263   }
       
  1264 }
       
  1265 
       
  1266 /**
       
  1267  * Tests that numbers can be searched.
       
  1268  */
       
  1269 class SearchNumbersTestCase extends DrupalWebTestCase {
       
  1270   protected $test_user;
       
  1271   protected $numbers;
       
  1272   protected $nodes;
       
  1273 
       
  1274   public static function getInfo() {
       
  1275     return array(
       
  1276       'name' => 'Search numbers',
       
  1277       'description' => 'Check that numbers can be searched',
       
  1278       'group' => 'Search',
       
  1279     );
       
  1280   }
       
  1281 
       
  1282   function setUp() {
       
  1283     parent::setUp('search');
       
  1284 
       
  1285     $this->test_user = $this->drupalCreateUser(array('search content', 'access content', 'administer nodes', 'access site reports'));
       
  1286     $this->drupalLogin($this->test_user);
       
  1287 
       
  1288     // Create content with various numbers in it.
       
  1289     // Note: 50 characters is the current limit of the search index's word
       
  1290     // field.
       
  1291     $this->numbers = array(
       
  1292       'ISBN' => '978-0446365383',
       
  1293       'UPC' => '036000 291452',
       
  1294       'EAN bar code' => '5901234123457',
       
  1295       'negative' => '-123456.7890',
       
  1296       'quoted negative' => '"-123456.7890"',
       
  1297       'leading zero' => '0777777777',
       
  1298       'tiny' => '111',
       
  1299       'small' => '22222222222222',
       
  1300       'medium' => '333333333333333333333333333',
       
  1301       'large' => '444444444444444444444444444444444444444',
       
  1302       'gigantic' => '5555555555555555555555555555555555555555555555555',
       
  1303       'over fifty characters' => '666666666666666666666666666666666666666666666666666666666666',
       
  1304       'date', '01/02/2009',
       
  1305       'commas', '987,654,321',
       
  1306     );
       
  1307 
       
  1308     foreach ($this->numbers as $doc => $num) {
       
  1309       $info = array(
       
  1310         'body' => array(LANGUAGE_NONE => array(array('value' => $num))),
       
  1311         'type' => 'page',
       
  1312         'language' => LANGUAGE_NONE,
       
  1313         'title' => $doc . ' number',
       
  1314       );
       
  1315       $this->nodes[$doc] = $this->drupalCreateNode($info);
       
  1316     }
       
  1317 
       
  1318     // Run cron to ensure the content is indexed.
       
  1319     $this->cronRun();
       
  1320     $this->drupalGet('admin/reports/dblog');
       
  1321     $this->assertText(t('Cron run completed'), 'Log shows cron run completed');
       
  1322   }
       
  1323 
       
  1324   /**
       
  1325    * Tests that all the numbers can be searched.
       
  1326    */
       
  1327   function testNumberSearching() {
       
  1328     $types = array_keys($this->numbers);
       
  1329 
       
  1330     foreach ($types as $type) {
       
  1331       $number = $this->numbers[$type];
       
  1332       // If the number is negative, remove the - sign, because - indicates
       
  1333       // "not keyword" when searching.
       
  1334       $number = ltrim($number, '-');
       
  1335       $node = $this->nodes[$type];
       
  1336 
       
  1337       // Verify that the node title does not appear on the search page
       
  1338       // with a dummy search.
       
  1339       $this->drupalPost('search/node',
       
  1340         array('keys' => 'foo'),
       
  1341         t('Search'));
       
  1342       $this->assertNoText($node->title, $type . ': node title not shown in dummy search');
       
  1343 
       
  1344       // Verify that the node title does appear as a link on the search page
       
  1345       // when searching for the number.
       
  1346       $this->drupalPost('search/node',
       
  1347         array('keys' => $number),
       
  1348         t('Search'));
       
  1349       $this->assertText($node->title, format_string('%type: node title shown (search found the node) in search for number %number.', array('%type' => $type, '%number' => $number)));
       
  1350     }
       
  1351   }
       
  1352 }
       
  1353 
       
  1354 /**
       
  1355  * Tests that numbers can be searched, with more complex matching.
       
  1356  */
       
  1357 class SearchNumberMatchingTestCase extends DrupalWebTestCase {
       
  1358   protected $test_user;
       
  1359   protected $numbers;
       
  1360   protected $nodes;
       
  1361 
       
  1362   public static function getInfo() {
       
  1363     return array(
       
  1364       'name' => 'Search number matching',
       
  1365       'description' => 'Check that numbers can be searched with more complex matching',
       
  1366       'group' => 'Search',
       
  1367     );
       
  1368   }
       
  1369 
       
  1370   function setUp() {
       
  1371     parent::setUp('search');
       
  1372 
       
  1373     $this->test_user = $this->drupalCreateUser(array('search content', 'access content', 'administer nodes', 'access site reports'));
       
  1374     $this->drupalLogin($this->test_user);
       
  1375 
       
  1376     // Define a group of numbers that should all match each other --
       
  1377     // numbers with internal punctuation should match each other, as well
       
  1378     // as numbers with and without leading zeros and leading/trailing
       
  1379     // . and -.
       
  1380     $this->numbers = array(
       
  1381       '123456789',
       
  1382       '12/34/56789',
       
  1383       '12.3456789',
       
  1384       '12-34-56789',
       
  1385       '123,456,789',
       
  1386       '-123456789',
       
  1387       '0123456789',
       
  1388     );
       
  1389 
       
  1390     foreach ($this->numbers as $num) {
       
  1391       $info = array(
       
  1392         'body' => array(LANGUAGE_NONE => array(array('value' => $num))),
       
  1393         'type' => 'page',
       
  1394         'language' => LANGUAGE_NONE,
       
  1395       );
       
  1396       $this->nodes[] = $this->drupalCreateNode($info);
       
  1397     }
       
  1398 
       
  1399     // Run cron to ensure the content is indexed.
       
  1400     $this->cronRun();
       
  1401     $this->drupalGet('admin/reports/dblog');
       
  1402     $this->assertText(t('Cron run completed'), 'Log shows cron run completed');
       
  1403   }
       
  1404 
       
  1405   /**
       
  1406    * Tests that all the numbers can be searched.
       
  1407    */
       
  1408   function testNumberSearching() {
       
  1409     for ($i = 0; $i < count($this->numbers); $i++) {
       
  1410       $node = $this->nodes[$i];
       
  1411 
       
  1412       // Verify that the node title does not appear on the search page
       
  1413       // with a dummy search.
       
  1414       $this->drupalPost('search/node',
       
  1415         array('keys' => 'foo'),
       
  1416         t('Search'));
       
  1417       $this->assertNoText($node->title, format_string('%number: node title not shown in dummy search', array('%number' => $i)));
       
  1418 
       
  1419       // Now verify that we can find node i by searching for any of the
       
  1420       // numbers.
       
  1421       for ($j = 0; $j < count($this->numbers); $j++) {
       
  1422         $number = $this->numbers[$j];
       
  1423         // If the number is negative, remove the - sign, because - indicates
       
  1424         // "not keyword" when searching.
       
  1425         $number = ltrim($number, '-');
       
  1426 
       
  1427         $this->drupalPost('search/node',
       
  1428           array('keys' => $number),
       
  1429           t('Search'));
       
  1430         $this->assertText($node->title, format_string('%i: node title shown (search found the node) in search for number %number', array('%i' => $i, '%number' => $number)));
       
  1431       }
       
  1432     }
       
  1433 
       
  1434   }
       
  1435 }
       
  1436 
       
  1437 /**
       
  1438  * Test config page.
       
  1439  */
       
  1440 class SearchConfigSettingsForm extends DrupalWebTestCase {
       
  1441   public $search_user;
       
  1442   public $search_node;
       
  1443 
       
  1444   public static function getInfo() {
       
  1445     return array(
       
  1446       'name' => 'Config settings form',
       
  1447       'description' => 'Verify the search config settings form.',
       
  1448       'group' => 'Search',
       
  1449     );
       
  1450   }
       
  1451 
       
  1452   function setUp() {
       
  1453     parent::setUp('search', 'search_extra_type');
       
  1454 
       
  1455     // Login as a user that can create and search content.
       
  1456     $this->search_user = $this->drupalCreateUser(array('search content', 'administer search', 'administer nodes', 'bypass node access', 'access user profiles', 'administer users', 'administer blocks', 'access site reports'));
       
  1457     $this->drupalLogin($this->search_user);
       
  1458 
       
  1459     // Add a single piece of content and index it.
       
  1460     $node = $this->drupalCreateNode();
       
  1461     $this->search_node = $node;
       
  1462     // Link the node to itself to test that it's only indexed once. The content
       
  1463     // also needs the word "pizza" so we can use it as the search keyword.
       
  1464     $langcode = LANGUAGE_NONE;
       
  1465     $body_key = "body[$langcode][0][value]";
       
  1466     $edit[$body_key] = l($node->title, 'node/' . $node->nid) . ' pizza sandwich';
       
  1467     $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
       
  1468 
       
  1469     node_update_index();
       
  1470     search_update_totals();
       
  1471 
       
  1472     // Enable the search block.
       
  1473     $edit = array();
       
  1474     $edit['blocks[search_form][region]'] = 'content';
       
  1475     $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
       
  1476   }
       
  1477 
       
  1478   /**
       
  1479    * Verify the search settings form.
       
  1480    */
       
  1481   function testSearchSettingsPage() {
       
  1482 
       
  1483     // Test that the settings form displays the correct count of items left to index.
       
  1484     $this->drupalGet('admin/config/search/settings');
       
  1485     $this->assertText(t('There are @count items left to index.', array('@count' => 0)));
       
  1486 
       
  1487     // Test the re-index button.
       
  1488     $this->drupalPost('admin/config/search/settings', array(), t('Re-index site'));
       
  1489     $this->assertText(t('Are you sure you want to re-index the site'));
       
  1490     $this->drupalPost('admin/config/search/settings/reindex', array(), t('Re-index site'));
       
  1491     $this->assertText(t('The index will be rebuilt'));
       
  1492     $this->drupalGet('admin/config/search/settings');
       
  1493     $this->assertText(t('There is 1 item left to index.'));
       
  1494 
       
  1495     // Test that the form saves with the default values.
       
  1496     $this->drupalPost('admin/config/search/settings', array(), t('Save configuration'));
       
  1497     $this->assertText(t('The configuration options have been saved.'), 'Form saves with the default values.');
       
  1498 
       
  1499     // Test that the form does not save with an invalid word length.
       
  1500     $edit = array(
       
  1501       'minimum_word_size' => $this->randomName(3),
       
  1502     );
       
  1503     $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration'));
       
  1504     $this->assertNoText(t('The configuration options have been saved.'), 'Form does not save with an invalid word length.');
       
  1505 
       
  1506     // Test logging setting. It should be on by default.
       
  1507     $text = $this->randomName(5);
       
  1508     $this->drupalPost('search/node', array('keys' => $text), t('Search'));
       
  1509     $this->drupalGet('admin/reports/dblog');
       
  1510     $this->assertLink('Searched Content for ' . $text . '.', 0, 'Search was logged');
       
  1511 
       
  1512     // Turn off logging.
       
  1513     variable_set('search_logging', FALSE);
       
  1514     $text = $this->randomName(5);
       
  1515     $this->drupalPost('search/node', array('keys' => $text), t('Search'));
       
  1516     $this->drupalGet('admin/reports/dblog');
       
  1517     $this->assertNoLink('Searched Content for ' . $text . '.', 'Search was not logged');
       
  1518   }
       
  1519 
       
  1520   /**
       
  1521    * Verify that you can disable individual search modules.
       
  1522    */
       
  1523   function testSearchModuleDisabling() {
       
  1524     // Array of search modules to test: 'path' is the search path, 'title' is
       
  1525     // the tab title, 'keys' are the keywords to search for, and 'text' is
       
  1526     // the text to assert is on the results page.
       
  1527     $module_info = array(
       
  1528       'node' => array(
       
  1529         'path' => 'node',
       
  1530         'title' => 'Content',
       
  1531         'keys' => 'pizza',
       
  1532         'text' => $this->search_node->title,
       
  1533       ),
       
  1534       'user' => array(
       
  1535         'path' => 'user',
       
  1536         'title' => 'User',
       
  1537         'keys' => $this->search_user->name,
       
  1538         'text' => $this->search_user->mail,
       
  1539       ),
       
  1540       'search_extra_type' => array(
       
  1541         'path' => 'dummy_path',
       
  1542         'title' => 'Dummy search type',
       
  1543         'keys' => 'foo',
       
  1544         'text' => 'Dummy search snippet to display',
       
  1545       ),
       
  1546     );
       
  1547     $modules = array_keys($module_info);
       
  1548 
       
  1549     // Test each module if it's enabled as the only search module.
       
  1550     foreach ($modules as $module) {
       
  1551       // Enable the one module and disable other ones.
       
  1552       $info = $module_info[$module];
       
  1553       $edit = array();
       
  1554       foreach ($modules as $other) {
       
  1555         $edit['search_active_modules[' . $other . ']'] = (($other == $module) ? $module : FALSE);
       
  1556       }
       
  1557       $edit['search_default_module'] = $module;
       
  1558       $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration'));
       
  1559 
       
  1560       // Run a search from the correct search URL.
       
  1561       $this->drupalGet('search/' . $info['path'] . '/' . $info['keys']);
       
  1562       $this->assertNoText('no results', $info['title'] . ' search found results');
       
  1563       $this->assertText($info['text'], 'Correct search text found');
       
  1564 
       
  1565       // Verify that other module search tab titles are not visible.
       
  1566       foreach ($modules as $other) {
       
  1567         if ($other != $module) {
       
  1568           $title = $module_info[$other]['title'];
       
  1569           $this->assertNoText($title, $title . ' search tab is not shown');
       
  1570         }
       
  1571       }
       
  1572 
       
  1573       // Run a search from the search block on the node page. Verify you get
       
  1574       // to this module's search results page.
       
  1575       $terms = array('search_block_form' => $info['keys']);
       
  1576       $this->drupalPost('node', $terms, t('Search'));
       
  1577       $this->assertEqual(
       
  1578         $this->getURL(),
       
  1579         url('search/' . $info['path'] . '/' . $info['keys'], array('absolute' => TRUE)),
       
  1580         'Block redirected to right search page');
       
  1581 
       
  1582       // Try an invalid search path. Should redirect to our active module.
       
  1583       $this->drupalGet('search/not_a_module_path');
       
  1584       $this->assertEqual(
       
  1585         $this->getURL(),
       
  1586         url('search/' . $info['path'], array('absolute' => TRUE)),
       
  1587         'Invalid search path redirected to default search page');
       
  1588     }
       
  1589 
       
  1590     // Test with all search modules enabled. When you go to the search
       
  1591     // page or run search, all modules should be shown.
       
  1592     $edit = array();
       
  1593     foreach ($modules as $module) {
       
  1594       $edit['search_active_modules[' . $module . ']'] = $module;
       
  1595     }
       
  1596     $edit['search_default_module'] = 'node';
       
  1597 
       
  1598     $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration'));
       
  1599 
       
  1600     foreach (array('search/node/pizza', 'search/node') as $path) {
       
  1601       $this->drupalGet($path);
       
  1602       foreach ($modules as $module) {
       
  1603         $title = $module_info[$module]['title'];
       
  1604         $this->assertText($title, format_string('%title search tab is shown', array('%title' => $title)));
       
  1605       }
       
  1606     }
       
  1607   }
       
  1608 }
       
  1609 
       
  1610 /**
       
  1611  * Tests the search_excerpt() function.
       
  1612  */
       
  1613 class SearchExcerptTestCase extends DrupalWebTestCase {
       
  1614   public static function getInfo() {
       
  1615     return array(
       
  1616       'name' => 'Search excerpt extraction',
       
  1617       'description' => 'Tests that the search_excerpt() function works.',
       
  1618       'group' => 'Search',
       
  1619     );
       
  1620   }
       
  1621 
       
  1622   function setUp() {
       
  1623     parent::setUp('search');
       
  1624   }
       
  1625 
       
  1626   /**
       
  1627    * Tests search_excerpt() with several simulated search keywords.
       
  1628    *
       
  1629    * Passes keywords and a sample marked up string, "The quick
       
  1630    * brown fox jumps over the lazy dog", and compares it to the
       
  1631    * correctly marked up string. The correctly marked up string
       
  1632    * contains either highlighted keywords or the original marked
       
  1633    * up string if no keywords matched the string.
       
  1634    */
       
  1635   function testSearchExcerpt() {
       
  1636     // Make some text with entities and tags.
       
  1637     $text = 'The <strong>quick</strong> <a href="#">brown</a> fox &amp; jumps <h2>over</h2> the lazy dog';
       
  1638     // Note: The search_excerpt() function adds some extra spaces -- not
       
  1639     // important for HTML formatting. Remove these for comparison.
       
  1640     $expected = 'The quick brown fox &amp; jumps over the lazy dog';
       
  1641     $result = preg_replace('| +|', ' ', search_excerpt('nothing', $text));
       
  1642     $this->assertEqual(preg_replace('| +|', ' ', $result), $expected, 'Entire string is returned when keyword is not found in short string');
       
  1643 
       
  1644     $result = preg_replace('| +|', ' ', search_excerpt('fox', $text));
       
  1645     $this->assertEqual($result, 'The quick brown <strong>fox</strong> &amp; jumps over the lazy dog ...', 'Found keyword is highlighted');
       
  1646 
       
  1647     $longtext = str_repeat($text . ' ', 10);
       
  1648     $result = preg_replace('| +|', ' ', search_excerpt('nothing', $longtext));
       
  1649     $this->assertTrue(strpos($result, $expected) === 0, 'When keyword is not found in long string, return value starts as expected');
       
  1650 
       
  1651     $entities = str_repeat('k&eacute;sz&iacute;t&eacute;se ', 20);
       
  1652     $result = preg_replace('| +|', ' ', search_excerpt('nothing', $entities));
       
  1653     $this->assertFalse(strpos($result, '&'), 'Entities are not present in excerpt');
       
  1654     $this->assertTrue(strpos($result, 'í') > 0, 'Entities are converted in excerpt');
       
  1655 
       
  1656     // The node body that will produce this rendered $text is:
       
  1657     // 123456789 HTMLTest +123456789+&lsquo;  +&lsquo;  +&lsquo;  +&lsquo;  +12345678  &nbsp;&nbsp;  +&lsquo;  +&lsquo;  +&lsquo;   &lsquo;
       
  1658     $text = "<div class=\"field field-name-body field-type-text-with-summary field-label-hidden\"><div class=\"field-items\"><div class=\"field-item even\" property=\"content:encoded\"><p>123456789 HTMLTest +123456789+‘  +‘  +‘  +‘  +12345678      +‘  +‘  +‘   ‘</p>\n</div></div></div> ";
       
  1659     $result = search_excerpt('HTMLTest', $text);
       
  1660     $this->assertFalse(empty($result),  'Rendered Multi-byte HTML encodings are not corrupted in search excerpts');
       
  1661   }
       
  1662 
       
  1663   /**
       
  1664    * Tests search_excerpt() with search keywords matching simplified words.
       
  1665    *
       
  1666    * Excerpting should handle keywords that are matched only after going through
       
  1667    * search_simplify(). This test passes keywords that match simplified words
       
  1668    * and compares them with strings that contain the original unsimplified word.
       
  1669    */
       
  1670   function testSearchExcerptSimplified() {
       
  1671     $lorem1 = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae arcu at leo cursus laoreet. Curabitur dui tortor, adipiscing malesuada tempor in, bibendum ac diam. Cras non tellus a libero pellentesque condimentum. What is a Drupalism? Suspendisse ac lacus libero. Ut non est vel nisl faucibus interdum nec sed leo. Pellentesque sem risus, vulputate eu semper eget, auctor in libero.';
       
  1672     $lorem2 = 'Ut fermentum est vitae metus convallis scelerisque. Phasellus pellentesque rhoncus tellus, eu dignissim purus posuere id. Quisque eu fringilla ligula. Morbi ullamcorper, lorem et mattis egestas, tortor neque pretium velit, eget eleifend odio turpis eu purus. Donec vitae metus quis leo pretium tincidunt a pulvinar sem. Morbi adipiscing laoreet mauris vel placerat. Nullam elementum, nisl sit amet scelerisque malesuada, dolor nunc hendrerit quam, eu ultrices erat est in orci.';
       
  1673 
       
  1674     // Make some text with some keywords that will get simplified.
       
  1675     $text = $lorem1 . ' Number: 123456.7890 Hyphenated: one-two abc,def ' . $lorem2;
       
  1676     // Note: The search_excerpt() function adds some extra spaces -- not
       
  1677     // important for HTML formatting. Remove these for comparison.
       
  1678     $result = preg_replace('| +|', ' ', search_excerpt('123456.7890', $text));
       
  1679     $this->assertTrue(strpos($result, 'Number: <strong>123456.7890</strong>') !== FALSE, 'Numeric keyword is highlighted with exact match');
       
  1680 
       
  1681     $result = preg_replace('| +|', ' ', search_excerpt('1234567890', $text));
       
  1682     $this->assertTrue(strpos($result, 'Number: <strong>123456.7890</strong>') !== FALSE, 'Numeric keyword is highlighted with simplified match');
       
  1683 
       
  1684     $result = preg_replace('| +|', ' ', search_excerpt('Number 1234567890', $text));
       
  1685     $this->assertTrue(strpos($result, '<strong>Number</strong>: <strong>123456.7890</strong>') !== FALSE, 'Punctuated and numeric keyword is highlighted with simplified match');
       
  1686 
       
  1687     $result = preg_replace('| +|', ' ', search_excerpt('"Number 1234567890"', $text));
       
  1688     $this->assertTrue(strpos($result, '<strong>Number: 123456.7890</strong>') !== FALSE, 'Phrase with punctuated and numeric keyword is highlighted with simplified match');
       
  1689 
       
  1690     $result = preg_replace('| +|', ' ', search_excerpt('"Hyphenated onetwo"', $text));
       
  1691     $this->assertTrue(strpos($result, '<strong>Hyphenated: one-two</strong>') !== FALSE, 'Phrase with punctuated and hyphenated keyword is highlighted with simplified match');
       
  1692 
       
  1693     $result = preg_replace('| +|', ' ', search_excerpt('"abc def"', $text));
       
  1694     $this->assertTrue(strpos($result, '<strong>abc,def</strong>') !== FALSE, 'Phrase with keyword simplified into two separate words is highlighted with simplified match');
       
  1695 
       
  1696     // Test phrases with characters which are being truncated.
       
  1697     $result = preg_replace('| +|', ' ', search_excerpt('"ipsum _"', $text));
       
  1698     $this->assertTrue(strpos($result, '<strong>ipsum </strong>') !== FALSE, 'Only valid part of the phrase is highlighted and invalid part containing "_" is ignored.');
       
  1699 
       
  1700     $result = preg_replace('| +|', ' ', search_excerpt('"ipsum 0000"', $text));
       
  1701     $this->assertTrue(strpos($result, '<strong>ipsum </strong>') !== FALSE, 'Only valid part of the phrase is highlighted and invalid part "0000" is ignored.');
       
  1702 
       
  1703     // Test combination of the valid keyword and keyword containing only
       
  1704     // characters which are being truncated during simplification.
       
  1705     $result = preg_replace('| +|', ' ', search_excerpt('ipsum _', $text));
       
  1706     $this->assertTrue(strpos($result, '<strong>ipsum</strong>') !== FALSE, 'Only valid keyword is highlighted and invalid keyword "_" is ignored.');
       
  1707 
       
  1708     $result = preg_replace('| +|', ' ', search_excerpt('ipsum 0000', $text));
       
  1709     $this->assertTrue(strpos($result, '<strong>ipsum</strong>') !== FALSE, 'Only valid keyword is highlighted and invalid keyword "0000" is ignored.');
       
  1710   }
       
  1711 }
       
  1712 
       
  1713 /**
       
  1714  * Test the CJK tokenizer.
       
  1715  */
       
  1716 class SearchTokenizerTestCase extends DrupalWebTestCase {
       
  1717   public static function getInfo() {
       
  1718     return array(
       
  1719       'name' => 'CJK tokenizer',
       
  1720       'description' => 'Check that CJK tokenizer works as intended.',
       
  1721       'group' => 'Search',
       
  1722     );
       
  1723   }
       
  1724 
       
  1725   function setUp() {
       
  1726     parent::setUp('search');
       
  1727   }
       
  1728 
       
  1729   /**
       
  1730    * Verifies that strings of CJK characters are tokenized.
       
  1731    *
       
  1732    * The search_simplify() function does special things with numbers, symbols,
       
  1733    * and punctuation. So we only test that CJK characters that are not in these
       
  1734    * character classes are tokenized properly. See PREG_CLASS_CKJ for more
       
  1735    * information.
       
  1736    */
       
  1737   function testTokenizer() {
       
  1738     // Set the minimum word size to 1 (to split all CJK characters) and make
       
  1739     // sure CJK tokenizing is turned on.
       
  1740     variable_set('minimum_word_size', 1);
       
  1741     variable_set('overlap_cjk', TRUE);
       
  1742     $this->refreshVariables();
       
  1743 
       
  1744     // Create a string of CJK characters from various character ranges in
       
  1745     // the Unicode tables.
       
  1746 
       
  1747     // Beginnings of the character ranges.
       
  1748     $starts = array(
       
  1749       'CJK unified' => 0x4e00,
       
  1750       'CJK Ext A' => 0x3400,
       
  1751       'CJK Compat' => 0xf900,
       
  1752       'Hangul Jamo' => 0x1100,
       
  1753       'Hangul Ext A' => 0xa960,
       
  1754       'Hangul Ext B' => 0xd7b0,
       
  1755       'Hangul Compat' => 0x3131,
       
  1756       'Half non-punct 1' => 0xff21,
       
  1757       'Half non-punct 2' => 0xff41,
       
  1758       'Half non-punct 3' => 0xff66,
       
  1759       'Hangul Syllables' => 0xac00,
       
  1760       'Hiragana' => 0x3040,
       
  1761       'Katakana' => 0x30a1,
       
  1762       'Katakana Ext' => 0x31f0,
       
  1763       'CJK Reserve 1' => 0x20000,
       
  1764       'CJK Reserve 2' => 0x30000,
       
  1765       'Bomofo' => 0x3100,
       
  1766       'Bomofo Ext' => 0x31a0,
       
  1767       'Lisu' => 0xa4d0,
       
  1768       'Yi' => 0xa000,
       
  1769     );
       
  1770 
       
  1771     // Ends of the character ranges.
       
  1772     $ends = array(
       
  1773       'CJK unified' => 0x9fcf,
       
  1774       'CJK Ext A' => 0x4dbf,
       
  1775       'CJK Compat' => 0xfaff,
       
  1776       'Hangul Jamo' => 0x11ff,
       
  1777       'Hangul Ext A' => 0xa97f,
       
  1778       'Hangul Ext B' => 0xd7ff,
       
  1779       'Hangul Compat' => 0x318e,
       
  1780       'Half non-punct 1' => 0xff3a,
       
  1781       'Half non-punct 2' => 0xff5a,
       
  1782       'Half non-punct 3' => 0xffdc,
       
  1783       'Hangul Syllables' => 0xd7af,
       
  1784       'Hiragana' => 0x309f,
       
  1785       'Katakana' => 0x30ff,
       
  1786       'Katakana Ext' => 0x31ff,
       
  1787       'CJK Reserve 1' => 0x2fffd,
       
  1788       'CJK Reserve 2' => 0x3fffd,
       
  1789       'Bomofo' => 0x312f,
       
  1790       'Bomofo Ext' => 0x31b7,
       
  1791       'Lisu' => 0xa4fd,
       
  1792       'Yi' => 0xa48f,
       
  1793     );
       
  1794 
       
  1795     // Generate characters consisting of starts, midpoints, and ends.
       
  1796     $chars = array();
       
  1797     $charcodes = array();
       
  1798     foreach ($starts as $key => $value) {
       
  1799       $charcodes[] = $starts[$key];
       
  1800       $chars[] = $this->code2utf($starts[$key]);
       
  1801       $mid = round(0.5 * ($starts[$key] + $ends[$key]));
       
  1802       $charcodes[] = $mid;
       
  1803       $chars[] = $this->code2utf($mid);
       
  1804       $charcodes[] = $ends[$key];
       
  1805       $chars[] = $this->code2utf($ends[$key]);
       
  1806     }
       
  1807 
       
  1808     // Merge into a string and tokenize.
       
  1809     $string = implode('', $chars);
       
  1810     $out = trim(search_simplify($string));
       
  1811     $expected = drupal_strtolower(implode(' ', $chars));
       
  1812 
       
  1813     // Verify that the output matches what we expect.
       
  1814     $this->assertEqual($out, $expected, 'CJK tokenizer worked on all supplied CJK characters');
       
  1815   }
       
  1816 
       
  1817   /**
       
  1818    * Verifies that strings of non-CJK characters are not tokenized.
       
  1819    *
       
  1820    * This is just a sanity check - it verifies that strings of letters are
       
  1821    * not tokenized.
       
  1822    */
       
  1823   function testNoTokenizer() {
       
  1824     // Set the minimum word size to 1 (to split all CJK characters) and make
       
  1825     // sure CJK tokenizing is turned on.
       
  1826     variable_set('minimum_word_size', 1);
       
  1827     variable_set('overlap_cjk', TRUE);
       
  1828     $this->refreshVariables();
       
  1829 
       
  1830     $letters = 'abcdefghijklmnopqrstuvwxyz';
       
  1831     $out = trim(search_simplify($letters));
       
  1832 
       
  1833     $this->assertEqual($letters, $out, 'Letters are not CJK tokenized');
       
  1834   }
       
  1835 
       
  1836   /**
       
  1837    * Like PHP chr() function, but for unicode characters.
       
  1838    *
       
  1839    * chr() only works for ASCII characters up to character 255. This function
       
  1840    * converts a number to the corresponding unicode character. Adapted from
       
  1841    * functions supplied in comments on several functions on php.net.
       
  1842    */
       
  1843   function code2utf($num) {
       
  1844     if ($num < 128) {
       
  1845       return chr($num);
       
  1846     }
       
  1847 
       
  1848     if ($num < 2048) {
       
  1849       return chr(($num >> 6) + 192) . chr(($num & 63) + 128);
       
  1850     }
       
  1851 
       
  1852     if ($num < 65536) {
       
  1853       return chr(($num >> 12) + 224) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128);
       
  1854     }
       
  1855 
       
  1856     if ($num < 2097152) {
       
  1857       return chr(($num >> 18) + 240) . chr((($num >> 12) & 63) + 128) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128);
       
  1858     }
       
  1859 
       
  1860     return '';
       
  1861   }
       
  1862 }
       
  1863 
       
  1864 /**
       
  1865  * Tests that we can embed a form in search results and submit it.
       
  1866  */
       
  1867 class SearchEmbedForm extends DrupalWebTestCase {
       
  1868   /**
       
  1869    * Node used for testing.
       
  1870    */
       
  1871   public $node;
       
  1872 
       
  1873   /**
       
  1874    * Count of how many times the form has been submitted.
       
  1875    */
       
  1876   public $submit_count = 0;
       
  1877 
       
  1878   public static function getInfo() {
       
  1879     return array(
       
  1880       'name' => 'Embedded forms',
       
  1881       'description' => 'Verifies that a form embedded in search results works',
       
  1882       'group' => 'Search',
       
  1883     );
       
  1884   }
       
  1885 
       
  1886   function setUp() {
       
  1887     parent::setUp('search', 'search_embedded_form');
       
  1888 
       
  1889     // Create a user and a node, and update the search index.
       
  1890     $test_user = $this->drupalCreateUser(array('access content', 'search content', 'administer nodes'));
       
  1891     $this->drupalLogin($test_user);
       
  1892 
       
  1893     $this->node = $this->drupalCreateNode();
       
  1894 
       
  1895     node_update_index();
       
  1896     search_update_totals();
       
  1897 
       
  1898     // Set up a dummy initial count of times the form has been submitted.
       
  1899     $this->submit_count = 12;
       
  1900     variable_set('search_embedded_form_submitted', $this->submit_count);
       
  1901     $this->refreshVariables();
       
  1902   }
       
  1903 
       
  1904   /**
       
  1905    * Tests that the embedded form appears and can be submitted.
       
  1906    */
       
  1907   function testEmbeddedForm() {
       
  1908     // First verify we can submit the form from the module's page.
       
  1909     $this->drupalPost('search_embedded_form',
       
  1910       array('name' => 'John'),
       
  1911       t('Send away'));
       
  1912     $this->assertText(t('Test form was submitted'), 'Form message appears');
       
  1913     $count = variable_get('search_embedded_form_submitted', 0);
       
  1914     $this->assertEqual($this->submit_count + 1, $count, 'Form submission count is correct');
       
  1915     $this->submit_count = $count;
       
  1916 
       
  1917     // Now verify that we can see and submit the form from the search results.
       
  1918     $this->drupalGet('search/node/' . $this->node->title);
       
  1919     $this->assertText(t('Your name'), 'Form is visible');
       
  1920     $this->drupalPost('search/node/' . $this->node->title,
       
  1921       array('name' => 'John'),
       
  1922       t('Send away'));
       
  1923     $this->assertText(t('Test form was submitted'), 'Form message appears');
       
  1924     $count = variable_get('search_embedded_form_submitted', 0);
       
  1925     $this->assertEqual($this->submit_count + 1, $count, 'Form submission count is correct');
       
  1926     $this->submit_count = $count;
       
  1927 
       
  1928     // Now verify that if we submit the search form, it doesn't count as
       
  1929     // our form being submitted.
       
  1930     $this->drupalPost('search',
       
  1931       array('keys' => 'foo'),
       
  1932       t('Search'));
       
  1933     $this->assertNoText(t('Test form was submitted'), 'Form message does not appear');
       
  1934     $count = variable_get('search_embedded_form_submitted', 0);
       
  1935     $this->assertEqual($this->submit_count, $count, 'Form submission count is correct');
       
  1936     $this->submit_count = $count;
       
  1937   }
       
  1938 }
       
  1939 
       
  1940 /**
       
  1941  * Tests that hook_search_page runs.
       
  1942  */
       
  1943 class SearchPageOverride extends DrupalWebTestCase {
       
  1944   public $search_user;
       
  1945 
       
  1946   public static function getInfo() {
       
  1947     return array(
       
  1948       'name' => 'Search page override',
       
  1949       'description' => 'Verify that hook_search_page can override search page display.',
       
  1950       'group' => 'Search',
       
  1951     );
       
  1952   }
       
  1953 
       
  1954   function setUp() {
       
  1955     parent::setUp('search', 'search_extra_type');
       
  1956 
       
  1957     // Login as a user that can create and search content.
       
  1958     $this->search_user = $this->drupalCreateUser(array('search content', 'administer search'));
       
  1959     $this->drupalLogin($this->search_user);
       
  1960 
       
  1961     // Enable the extra type module for searching.
       
  1962     variable_set('search_active_modules', array('node' => 'node', 'user' => 'user', 'search_extra_type' => 'search_extra_type'));
       
  1963     menu_rebuild();
       
  1964   }
       
  1965 
       
  1966   function testSearchPageHook() {
       
  1967     $keys = 'bike shed ' . $this->randomName();
       
  1968     $this->drupalGet("search/dummy_path/{$keys}");
       
  1969     $this->assertText('Dummy search snippet', 'Dummy search snippet is shown');
       
  1970     $this->assertText('Test page text is here', 'Page override is working');
       
  1971   }
       
  1972 }
       
  1973 
       
  1974 /**
       
  1975  * Test node search with multiple languages.
       
  1976  */
       
  1977 class SearchLanguageTestCase extends DrupalWebTestCase {
       
  1978   public static function getInfo() {
       
  1979     return array(
       
  1980       'name' => 'Search language selection',
       
  1981       'description' => 'Tests advanced search with different languages enabled.',
       
  1982       'group' => 'Search',
       
  1983     );
       
  1984   }
       
  1985 
       
  1986   /**
       
  1987    * Implementation setUp().
       
  1988    */
       
  1989   function setUp() {
       
  1990     parent::setUp('search', 'locale');
       
  1991 
       
  1992     // Create and login user.
       
  1993     $test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search', 'administer nodes', 'administer languages', 'access administration pages'));
       
  1994     $this->drupalLogin($test_user);
       
  1995   }
       
  1996 
       
  1997   function testLanguages() {
       
  1998     // Check that there are initially no languages displayed.
       
  1999     $this->drupalGet('search/node');
       
  2000     $this->assertNoText(t('Languages'), 'No languages to choose from.');
       
  2001 
       
  2002     // Add predefined language.
       
  2003     $edit = array('langcode' => 'fr');
       
  2004     $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
       
  2005     $this->assertText('fr', 'Language added successfully.');
       
  2006 
       
  2007     // Now we should have languages displayed.
       
  2008     $this->drupalGet('search/node');
       
  2009     $this->assertText(t('Languages'), 'Languages displayed to choose from.');
       
  2010     $this->assertText(t('English'), 'English is a possible choice.');
       
  2011     $this->assertText(t('French'), 'French is a possible choice.');
       
  2012 
       
  2013     // Ensure selecting no language does not make the query different.
       
  2014     $this->drupalPost('search/node', array(), t('Advanced search'));
       
  2015     $this->assertEqual($this->getUrl(), url('search/node/', array('absolute' => TRUE)), 'Correct page redirection, no language filtering.');
       
  2016 
       
  2017     // Pick French and ensure it is selected.
       
  2018     $edit = array('language[fr]' => TRUE);
       
  2019     $this->drupalPost('search/node', $edit, t('Advanced search'));
       
  2020     $this->assertFieldByXPath('//input[@name="keys"]', 'language:fr', 'Language filter added to query.');
       
  2021 
       
  2022     // Change the default language and disable English.
       
  2023     $path = 'admin/config/regional/language';
       
  2024     $this->drupalGet($path);
       
  2025     $this->assertFieldChecked('edit-site-default-en', 'English is the default language.');
       
  2026     $edit = array('site_default' => 'fr');
       
  2027     $this->drupalPost(NULL, $edit, t('Save configuration'));
       
  2028     $this->assertNoFieldChecked('edit-site-default-en', 'Default language updated.');
       
  2029     $edit = array('enabled[en]' => FALSE);
       
  2030     $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));
       
  2031     $this->assertNoFieldChecked('edit-enabled-en', 'Language disabled.');
       
  2032 
       
  2033     // Check that there are again no languages displayed.
       
  2034     $this->drupalGet('search/node');
       
  2035     $this->assertNoText(t('Languages'), 'No languages to choose from.');
       
  2036   }
       
  2037 }
       
  2038 
       
  2039 /**
       
  2040  * Tests node search with node access control.
       
  2041  */
       
  2042 class SearchNodeAccessTest extends DrupalWebTestCase {
       
  2043   public $test_user;
       
  2044 
       
  2045   public static function getInfo() {
       
  2046     return array(
       
  2047       'name' => 'Search and node access',
       
  2048       'description' => 'Tests search functionality with node access control.',
       
  2049       'group' => 'Search',
       
  2050     );
       
  2051   }
       
  2052 
       
  2053   function setUp() {
       
  2054     parent::setUp('search', 'node_access_test');
       
  2055     node_access_rebuild();
       
  2056 
       
  2057     // Create a test user and log in.
       
  2058     $this->test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search'));
       
  2059     $this->drupalLogin($this->test_user);
       
  2060   }
       
  2061 
       
  2062   /**
       
  2063    * Tests that search works with punctuation and HTML entities.
       
  2064    */
       
  2065   function testPhraseSearchPunctuation() {
       
  2066     $node = $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => "The bunny's ears were fuzzy.")))));
       
  2067     $node2 = $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => 'Dignissim Aliquam &amp; Quieligo meus natu quae quia te. Damnum&copy; erat&mdash; neo pneum. Facilisi feugiat ibidem ratis.')))));
       
  2068 
       
  2069     // Update the search index.
       
  2070     module_invoke_all('update_index');
       
  2071     search_update_totals();
       
  2072 
       
  2073     // Refresh variables after the treatment.
       
  2074     $this->refreshVariables();
       
  2075 
       
  2076     // Submit a phrase wrapped in double quotes to include the punctuation.
       
  2077     $edit = array('keys' => '"bunny\'s"');
       
  2078     $this->drupalPost('search/node', $edit, t('Search'));
       
  2079     $this->assertText($node->title);
       
  2080 
       
  2081     // Search for "&" and verify entities are not broken up in the output.
       
  2082     $edit = array('keys' => '&');
       
  2083     $this->drupalPost('search/node', $edit, t('Search'));
       
  2084     $this->assertNoRaw('<strong>&</strong>amp;');
       
  2085     $this->assertText('You must include at least one positive keyword');
       
  2086 
       
  2087     $edit = array('keys' => '&amp;');
       
  2088     $this->drupalPost('search/node', $edit, t('Search'));
       
  2089     $this->assertNoRaw('<strong>&</strong>amp;');
       
  2090     $this->assertText('You must include at least one positive keyword');
       
  2091   }
       
  2092 }
       
  2093 
       
  2094 /**
       
  2095  * Tests node search with query tags.
       
  2096  */
       
  2097 class SearchNodeTagTest extends DrupalWebTestCase {
       
  2098   public $test_user;
       
  2099 
       
  2100   public static function getInfo() {
       
  2101     return array(
       
  2102       'name' => 'Node search query tags',
       
  2103       'description' => 'Tests Node search tags functionality.',
       
  2104       'group' => 'Search',
       
  2105     );
       
  2106   }
       
  2107 
       
  2108   function setUp() {
       
  2109     parent::setUp('search', 'search_node_tags');
       
  2110     node_access_rebuild();
       
  2111 
       
  2112     // Create a test user and log in.
       
  2113     $this->test_user = $this->drupalCreateUser(array('search content'));
       
  2114     $this->drupalLogin($this->test_user);
       
  2115   }
       
  2116 
       
  2117   /**
       
  2118    * Tests that the correct tags are available and hooks invoked.
       
  2119    */
       
  2120   function testNodeSearchQueryTags() {
       
  2121     $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => 'testing testing testing.')))));
       
  2122 
       
  2123     // Update the search index.
       
  2124     module_invoke_all('update_index');
       
  2125     search_update_totals();
       
  2126 
       
  2127     $edit = array('keys' => 'testing');
       
  2128     $this->drupalPost('search/node', $edit, t('Search'));
       
  2129 
       
  2130     $this->assertTrue(variable_get('search_node_tags_test_query_tag', FALSE), 'hook_query_alter() was invoked and the query contained the "search_node" tag.');
       
  2131     $this->assertTrue(variable_get('search_node_tags_test_query_tag_hook', FALSE), 'hook_query_search_node_alter() was invoked.');
       
  2132   }
       
  2133 }
       
  2134 
       
  2135 /**
       
  2136  * Tests searching with locale values set.
       
  2137  */
       
  2138 class SearchSetLocaleTest extends DrupalWebTestCase {
       
  2139 
       
  2140   public static function getInfo() {
       
  2141     return array(
       
  2142       'name' => 'Search with numeric locale set',
       
  2143       'description' => 'Check that search works with numeric locale settings',
       
  2144       'group' => 'Search',
       
  2145     );
       
  2146   }
       
  2147 
       
  2148   function setUp() {
       
  2149     parent::setUp('search');
       
  2150 
       
  2151     // Create a simple node so something will be put in the index.
       
  2152     $info = array(
       
  2153       'body' => array(LANGUAGE_NONE => array(array('value' => 'Tapir'))),
       
  2154     );
       
  2155     $this->drupalCreateNode($info);
       
  2156 
       
  2157     // Run cron to index.
       
  2158     $this->cronRun();
       
  2159   }
       
  2160 
       
  2161   /**
       
  2162    * Verify that search works with a numeric locale set.
       
  2163    */
       
  2164   public function testSearchWithNumericLocale() {
       
  2165     // French decimal point is comma.
       
  2166     setlocale(LC_NUMERIC, 'fr_FR');
       
  2167 
       
  2168     // An exception will be thrown if a float in the wrong format occurs in the
       
  2169     // query to the database, so an assertion is not necessary here.
       
  2170     db_select('search_index', 'i')
       
  2171       ->extend('searchquery')
       
  2172       ->searchexpression('tapir', 'node')
       
  2173       ->execute();
       
  2174   }
       
  2175 }