/[drupal]/drupal/modules/search/search.test
ViewVC logotype

Contents of /drupal/modules/search/search.test

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.42 - (show annotations) (download) (as text)
Mon Oct 19 23:28:40 2009 UTC (5 weeks, 5 days ago) by dries
Branch: MAIN
CVS Tags: DRUPAL-7-0-UNSTABLE-10, HEAD
Changes since 1.41: +1 -6 lines
File MIME type: text/x-php
- Patch #550742 by sun: removed some left-over code.
1 <?php
2 // $Id: search.test,v 1.41 2009/10/16 23:48:38 webchick Exp $
3
4 // The search index can contain different types of content. Typically the type is 'node'.
5 // Here we test with _test_ and _test2_ as the type.
6 define('SEARCH_TYPE', '_test_');
7 define('SEARCH_TYPE_2', '_test2_');
8
9 class SearchMatchTestCase extends DrupalWebTestCase {
10 public static function getInfo() {
11 return array(
12 'name' => 'Search engine queries',
13 'description' => 'Indexes content and queries it.',
14 'group' => 'Search',
15 );
16 }
17
18 /**
19 * Implementation setUp().
20 */
21 function setUp() {
22 parent::setUp('search');
23 }
24
25 /**
26 * Test search indexing.
27 */
28 function testMatching() {
29 $this->_setup();
30 $this->_testQueries();
31 }
32
33 /**
34 * Set up a small index of items to test against.
35 */
36 function _setup() {
37 variable_set('minimum_word_size', 3);
38
39 for ($i = 1; $i <= 7; ++$i) {
40 search_index($i, SEARCH_TYPE, $this->getText($i));
41 }
42 for ($i = 1; $i <= 5; ++$i) {
43 search_index($i + 7, SEARCH_TYPE_2, $this->getText2($i));
44 }
45 search_update_totals();
46 }
47
48 /**
49 * _test_: Helper method for generating snippets of content.
50 *
51 * Generated items to test against:
52 * 1 ipsum
53 * 2 dolore sit
54 * 3 sit am ut
55 * 4 am ut enim am
56 * 5 ut enim am minim veniam
57 * 6 enim am minim veniam es cillum
58 * 7 am minim veniam es cillum dolore eu
59 */
60 function getText($n) {
61 $words = explode(' ', "Ipsum dolore sit am. Ut enim am minim veniam. Es cillum dolore eu.");
62 return implode(' ', array_slice($words, $n - 1, $n));
63 }
64
65 /**
66 * _test2_: Helper method for generating snippets of content.
67 *
68 * Generated items to test against:
69 * 8 dear
70 * 9 king philip
71 * 10 philip came over
72 * 11 came over from germany
73 * 12 over from germany swimming
74 */
75 function getText2($n) {
76 $words = explode(' ', "Dear King Philip came over from Germany swimming.");
77 return implode(' ', array_slice($words, $n - 1, $n));
78 }
79
80 /**
81 * Run predefine queries looking for indexed terms.
82 */
83 function _testQueries() {
84 /*
85 Note: OR queries that include short words in OR groups are only accepted
86 if the ORed terms are ANDed with at least one long word in the rest of the query.
87
88 e.g. enim dolore OR ut = enim (dolore OR ut) = (enim dolor) OR (enim ut) -> good
89 e.g. dolore OR ut = (dolore) OR (ut) -> bad
90
91 This is a design limitation to avoid full table scans.
92 */
93 $queries = array(
94 // Simple AND queries.
95 'ipsum' => array(1),
96 'enim' => array(4, 5, 6),
97 'xxxxx' => array(),
98 'enim minim' => array(5, 6),
99 'enim xxxxx' => array(),
100 'dolore eu' => array(7),
101 'dolore xx' => array(),
102 'ut minim' => array(5),
103 'xx minim' => array(),
104 'enim veniam am minim ut' => array(5),
105 // Simple OR queries.
106 'dolore OR ipsum' => array(1, 2, 7),
107 'dolore OR xxxxx' => array(2, 7),
108 'dolore OR ipsum OR enim' => array(1, 2, 4, 5, 6, 7),
109 'ipsum OR dolore sit OR cillum' => array(2, 7),
110 'minim dolore OR ipsum' => array(7),
111 'dolore OR ipsum veniam' => array(7),
112 'minim dolore OR ipsum OR enim' => array(5, 6, 7),
113 'dolore xx OR yy' => array(),
114 'xxxxx dolore OR ipsum' => array(),
115 // Negative queries.
116 'dolore -sit' => array(7),
117 'dolore -eu' => array(2),
118 'dolore -xxxxx' => array(2, 7),
119 'dolore -xx' => array(2, 7),
120 // Phrase queries.
121 '"dolore sit"' => array(2),
122 '"sit dolore"' => array(),
123 '"am minim veniam es"' => array(6, 7),
124 '"minim am veniam es"' => array(),
125 // Mixed queries.
126 '"am minim veniam es" OR dolore' => array(2, 6, 7),
127 '"minim am veniam es" OR "dolore sit"' => array(2),
128 '"minim am veniam es" OR "sit dolore"' => array(),
129 '"am minim veniam es" -eu' => array(6),
130 '"am minim veniam" -"cillum dolore"' => array(5, 6),
131 '"am minim veniam" -"dolore cillum"' => array(5, 6, 7),
132 'xxxxx "minim am veniam es" OR dolore' => array(),
133 'xx "minim am veniam es" OR dolore' => array()
134 );
135 foreach ($queries as $query => $results) {
136 $result = db_select('search_index', 'i')
137 ->extend('SearchQuery')
138 ->searchExpression($query, SEARCH_TYPE)
139 ->execute();
140
141 $set = $result ? $result->fetchAll() : array();
142 $this->_testQueryMatching($query, $set, $results);
143 $this->_testQueryScores($query, $set, $results);
144 }
145
146 // These queries are run against the second index type, SEARCH_TYPE_2.
147 $queries = array(
148 // Simple AND queries.
149 'ipsum' => array(),
150 'enim' => array(),
151 'enim minim' => array(),
152 'dear' => array(8),
153 'germany' => array(11, 12),
154 );
155 foreach ($queries as $query => $results) {
156 $result = db_select('search_index', 'i')
157 ->extend('SearchQuery')
158 ->searchExpression($query, SEARCH_TYPE_2)
159 ->execute();
160
161 $set = $result ? $result->fetchAll() : array();
162 $this->_testQueryMatching($query, $set, $results);
163 $this->_testQueryScores($query, $set, $results);
164 }
165 }
166
167 /**
168 * Test the matching abilities of the engine.
169 *
170 * Verify if a query produces the correct results.
171 */
172 function _testQueryMatching($query, $set, $results) {
173 // Get result IDs.
174 $found = array();
175 foreach ($set as $item) {
176 $found[] = $item->sid;
177 }
178
179 // Compare $results and $found.
180 sort($found);
181 sort($results);
182 $this->assertEqual($found, $results, "Query matching '$query'");
183 }
184
185 /**
186 * Test the scoring abilities of the engine.
187 *
188 * Verify if a query produces normalized, monotonous scores.
189 */
190 function _testQueryScores($query, $set, $results) {
191 // Get result scores.
192 $scores = array();
193 foreach ($set as $item) {
194 $scores[] = $item->calculated_score;
195 }
196
197 // Check order.
198 $sorted = $scores;
199 sort($sorted);
200 $this->assertEqual($scores, array_reverse($sorted), "Query order '$query'");
201
202 // Check range.
203 $this->assertEqual(!count($scores) || (min($scores) > 0.0 && max($scores) <= 1.0001), TRUE, "Query scoring '$query'");
204 }
205 }
206
207 class SearchBikeShed extends DrupalWebTestCase {
208 protected $searching_user;
209
210 public static function getInfo() {
211 return array(
212 'name' => 'Bike shed',
213 'description' => 'Tests the bike shed text on the no results page.',
214 'group' => 'Search'
215 );
216 }
217
218 function setUp() {
219 parent::setUp('search');
220
221 // Create user.
222 $this->searching_user = $this->drupalCreateUser(array('search content'));
223 }
224
225 function testFailedSearch() {
226 $this->drupalLogin($this->searching_user);
227 $this->drupalGet('search/node');
228 $this->assertText(t('Enter your keywords'));
229
230 $edit = array();
231 $edit['keys'] = 'bike shed ' . $this->randomName();
232 $this->drupalPost('search/node', $edit, t('Search'));
233 $this->assertText(t('Consider loosening your query with OR. bike OR shed will often show more results than bike shed.'), t('Help text is displayed when search returns no results.'));
234 }
235 }
236
237 class SearchAdvancedSearchForm extends DrupalWebTestCase {
238 protected $node;
239
240 public static function getInfo() {
241 return array(
242 'name' => 'Advanced search form',
243 'description' => 'Indexes content and tests the advanced search form.',
244 'group' => 'Search',
245 );
246 }
247
248 function setUp() {
249 parent::setUp('search');
250 // Create and login user.
251 $test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search', 'administer nodes'));
252 $this->drupalLogin($test_user);
253
254 // Create initial node.
255 $node = $this->drupalCreateNode();
256 $this->node = $this->drupalCreateNode();
257
258 // First update the index. This does the initial processing.
259 node_update_index();
260
261 // Then, run the shutdown function. Testing is a unique case where indexing
262 // and searching has to happen in the same request, so running the shutdown
263 // function manually is needed to finish the indexing process.
264 search_update_totals();
265 }
266
267 /**
268 * Test using the search form with GET and POST queries.
269 * Test using the advanced search form to limit search to pages.
270 */
271 function testNodeType() {
272 $this->assertTrue($this->node->type == 'page', t('Node type is page.'));
273
274 // Assert that the dummy title doesn't equal the real title.
275 $dummy_title = 'Lorem ipsum';
276 $this->assertNotEqual($dummy_title, $this->node->title, t("Dummy title doens't equal node title"));
277
278 // Search for the dummy title with a GET query.
279 $this->drupalGet('search/node/' . $dummy_title);
280 $this->assertNoText($this->node->title, t('Page node is not found with dummy title.'));
281
282 // Search for the title of the node with a GET query.
283 $this->drupalGet('search/node/' . $this->node->title[FIELD_LANGUAGE_NONE][0]['value']);
284 $this->assertText($this->node->title[FIELD_LANGUAGE_NONE][0]['value'], t('Page node is found with GET query.'));
285
286 // Search for the title of the node with a POST query.
287 $edit = array('or' => $this->node->title[FIELD_LANGUAGE_NONE][0]['value']);
288 $this->drupalPost('search/node', $edit, t('Advanced search'));
289 $this->assertText($this->node->title[FIELD_LANGUAGE_NONE][0]['value'], t('Page node is found with POST query.'));
290
291 // Advanced search type option.
292 $this->drupalPost('search/node', array_merge($edit, array('type[page]' => 'page')), t('Advanced search'));
293 $this->assertText($this->node->title[FIELD_LANGUAGE_NONE][0]['value'], t('Page node is found with POST query and type:page.'));
294
295 $this->drupalPost('search/node', array_merge($edit, array('type[article]' => 'article')), t('Advanced search'));
296 $this->assertText('bike shed', t('Article node is not found with POST query and type:article.'));
297 }
298 }
299
300 class SearchRankingTestCase extends DrupalWebTestCase {
301 public static function getInfo() {
302 return array(
303 'name' => 'Search engine ranking',
304 'description' => 'Indexes content and tests ranking factors.',
305 'group' => 'Search',
306 );
307 }
308
309 /**
310 * Implementation setUp().
311 */
312 function setUp() {
313 parent::setUp('search', 'statistics', 'comment');
314 }
315
316 function testRankings() {
317 // Login with sufficient privileges.
318 $this->drupalLogin($this->drupalCreateUser(array('post comments without approval', 'create page content')));
319
320 // Build a list of the rankings to test.
321 $node_ranks = array('sticky', 'promote', 'relevance', 'recent', 'comments', 'views');
322
323 // Create nodes for testing.
324 foreach ($node_ranks as $node_rank) {
325 $settings = array('type' => 'page', 'title' => array(FIELD_LANGUAGE_NONE => array(array('value' => 'Drupal rocks'))), 'body' => array(FIELD_LANGUAGE_NONE => array(array('value' => "Drupal's search rocks"))));
326 foreach (array(0, 1) as $num) {
327 if ($num == 1) {
328 switch ($node_rank) {
329 case 'sticky':
330 case 'promote':
331 $settings[$node_rank] = 1;
332 break;
333 case 'relevance':
334 $settings['body'][FIELD_LANGUAGE_NONE][0]['value'] .= " really rocks";
335 break;
336 case 'recent':
337 $settings['created'] = REQUEST_TIME + 3600;
338 break;
339 case 'comments':
340 $settings['comment'] = 2;
341 break;
342 }
343 }
344 $nodes[$node_rank][$num] = $this->drupalCreateNode($settings);
345 }
346 }
347
348 // Update the search index.
349 module_invoke_all('update_index');
350 search_update_totals();
351
352 // Refresh variables after the treatment.
353 $this->refreshVariables();
354
355 // Add a comment to one of the nodes.
356 $edit = array('subject' => 'my comment title', 'comment' => 'some random comment');
357 $this->drupalGet('comment/reply/' . $nodes['comments'][1]->nid);
358 $this->drupalPost(NULL, $edit, t('Preview'));
359 $this->drupalPost(NULL, $edit, t('Save'));
360
361 // Enable counting of statistics.
362 variable_set('statistics_count_content_views', 1);
363
364 // Then View one of the nodes a bunch of times.
365 for ($i = 0; $i < 5; $i ++) {
366 $this->drupalGet('node/' . $nodes['views'][1]->nid);
367 }
368
369 // Test each of the possible rankings.
370 foreach ($node_ranks as $node_rank) {
371 // Disable all relevancy rankings except the one we are testing.
372 foreach ($node_ranks as $var) {
373 variable_set('node_rank_' . $var, $var == $node_rank ? 10 : 0);
374 }
375
376 // Do the search and assert the results.
377 $set = node_search_execute('rocks');
378 $this->assertEqual($set[0]['node']->nid, $nodes[$node_rank][1]->nid, 'Search ranking "' . $node_rank . '" order.');
379 }
380 }
381 }
382
383 class SearchBlockTestCase extends DrupalWebTestCase {
384 public static function getInfo() {
385 return array(
386 'name' => 'Block availability',
387 'description' => 'Check if the search form block is available.',
388 'group' => 'Search',
389 );
390 }
391
392 function setUp() {
393 parent::setUp('search');
394
395 // Create and login user
396 $admin_user = $this->drupalCreateUser(array('administer blocks', 'search content'));
397 $this->drupalLogin($admin_user);
398 }
399
400 function testSearchFormBlock() {
401 // Set block title to confirm that the interface is availble.
402 $this->drupalPost('admin/structure/block/manage/search/form/configure', array('title' => $this->randomName(8)), t('Save block'));
403 $this->assertText(t('The block configuration has been saved.'), t('Block configuration set.'));
404
405 // Set the block to a region to confirm block is availble.
406 $edit = array();
407 $edit['search_form[region]'] = 'footer';
408 $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
409 $this->assertText(t('The block settings have been updated.'), t('Block successfully move to footer region.'));
410 }
411
412 /**
413 * Test that the search block form works correctly.
414 */
415 function testBlock() {
416 // Enable the block, and place it in the 'content' region so that it isn't
417 // hidden on 404 pages.
418 $edit = array('search_form[region]' => 'content');
419 $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
420
421 // Test a normal search via the block form, from the front page.
422 $terms = array('search_block_form' => 'test');
423 $this->drupalPost('node', $terms, t('Search'));
424 $this->assertText('Your search yielded no results');
425
426 // Test a search from the block on a 404 page.
427 $this->drupalPost('foo', $terms, t('Search'));
428 $this->assertText('Your search yielded no results');
429
430 // Test a search from the block when it doesn't appear on the search page.
431 $edit = array('pages' => 'search');
432 $this->drupalPost('admin/structure/block/manage/search/form/configure', $edit, t('Save block'));
433 $this->drupalPost('node', $terms, t('Search'));
434 $this->assertText('Your search yielded no results');
435 }
436 }
437
438
439 /**
440 * Test integration searching comments.
441 */
442 class SearchCommentTestCase extends DrupalWebTestCase {
443 protected $admin_user;
444
445 public static function getInfo() {
446 return array(
447 'name' => 'Comment Search tests',
448 'description' => 'Verify text formats and filters used elsewhere.',
449 'group' => 'Search',
450 );
451 }
452
453 function setUp() {
454 parent::setUp('comment', 'search');
455
456 $this->admin_user = $this->drupalCreateUser(array('administer filters', 'administer permissions', 'create page content', 'post comments without approval'));
457 $this->drupalLogin($this->admin_user);
458 }
459
460 /**
461 * Verify that comments are rendered using proper format in search results.
462 */
463 function testSearchResultsComment() {
464 $comment_body = $this->randomName(5);
465
466 variable_set('comment_preview_article', DRUPAL_OPTIONAL);
467 // Enable check_plain() for 'Filtered HTML' text format.
468 $edit = array(
469 'filters[filter_html_escape][status]' => 1,
470 );
471 $this->drupalPost('admin/config/content/formats/1', $edit, t('Save configuration'));
472 // Allow anonymous users to search content.
473 $edit = array(
474 DRUPAL_ANONYMOUS_RID . '[search content]' => 1,
475 // @todo Comments are added to search index without checking first whether
476 // anonymous users are allowed to access comments.
477 DRUPAL_ANONYMOUS_RID . '[access comments]' => 1,
478 // @todo Without this permission, "Login or register to post comments" is
479 // added to the search index. Comment.module is not guilty; that text
480 // seems to be added via node links.
481 DRUPAL_ANONYMOUS_RID . '[post comments]' => 1,
482 );
483 $this->drupalPost('admin/config/people/permissions', $edit, t('Save permissions'));
484
485 // Create a node.
486 $node = $this->drupalCreateNode(array('type' => 'article'));
487 // Post a comment using 'Full HTML' text format.
488 $edit_comment = array(
489 'subject' => $this->randomName(2),
490 'comment' => '<h1>' . $comment_body . '</h1>',
491 'comment_format' => 2,
492 );
493 $this->drupalPost('comment/reply/' . $node->nid, $edit_comment, t('Save'));
494
495 // Invoke search index update.
496 $this->drupalLogout();
497 $this->cronRun();
498
499 // Search for $title.
500 $edit = array(
501 'search_block_form' => $comment_body,
502 );
503 $this->drupalPost('', $edit, t('Search'));
504 $this->assertText($node->title[FIELD_LANGUAGE_NONE][0]['value'], t('Node found in search results.'));
505
506 // Verify that comment is rendered using proper format.
507 $this->assertText($edit_comment['subject'], t('Comment subject found in search results.'));
508 $this->assertText($comment_body, t('Comment body text found in search results.'));
509 $this->assertNoRaw(t('n/a'), t('HTML in comment body is not hidden.'));
510 $this->assertNoRaw(check_plain($edit_comment['comment']), t('HTML in comment body is not escaped.'));
511
512 // Hide comments.
513 $this->drupalLogin($this->admin_user);
514 $node->comment = 0;
515 node_save($node);
516
517 // Invoke search index update.
518 $this->drupalLogout();
519 $this->cronRun();
520
521 // Search for $title.
522 $this->drupalPost('', $edit, t('Search'));
523 $this->assertNoText($comment_body, t('Comment body text not found in search results.'));
524 }
525 }

  ViewVC Help
Powered by ViewVC 1.1.2