Mercurial > hg > MPIWG-drupal-modules
comparison sites/all/modules/custom/solrconnect/tests/solr_index_and_search.test @ 0:015d06b10d37 default tip
initial
author | dwinter |
---|---|
date | Wed, 31 Jul 2013 13:49:13 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:015d06b10d37 |
---|---|
1 <?php | |
2 | |
3 abstract class AbstractDrupalSolrOnlineWebTestCase extends DrupalWebTestCase { | |
4 | |
5 protected $solr; | |
6 | |
7 protected $solr_available = FALSE; // workaround for drupal.org test bot | |
8 | |
9 /** | |
10 * Implementation of setUp(). | |
11 */ | |
12 function setUp() { | |
13 // Install modules needed for this test. This could have been passed in as | |
14 // either a single array argument or a variable number of string arguments. | |
15 $modules = func_get_args(); | |
16 if (isset($modules[0]) && is_array($modules[0])) { | |
17 $modules = $modules[0]; | |
18 } | |
19 $modules[] = 'apachesolr'; | |
20 $modules[] = 'apachesolr_search'; | |
21 $modules[] = 'search'; | |
22 | |
23 parent::setUp($modules); | |
24 } | |
25 | |
26 function setUpSolr() { | |
27 // Load the default server. | |
28 $env_id = apachesolr_default_environment(); | |
29 $environment = apachesolr_environment_load($env_id); | |
30 $this->base_solr_url = $environment['url']; | |
31 | |
32 // Because we are in a clean environment, this will always be the default | |
33 // http://localhost:8983/solr | |
34 $this->core_admin_url = "{$this->base_solr_url}/admin/cores"; | |
35 | |
36 // The core admin url will give a valid response if the | |
37 // Solr server is running locally. | |
38 if ($this->coreAdminAvailable()) { | |
39 | |
40 // We will use a core named after the simpletest prefix. | |
41 $environment['url'] .= '/' . $this->databasePrefix; | |
42 | |
43 $filesdir = file_directory_temp(); | |
44 // Create our Solr core directory. | |
45 drupal_mkdir("{$filesdir}/solr", 0777, TRUE); | |
46 // Our temporary core is located here. | |
47 $instancedir = realpath($filesdir . "/solr"); | |
48 | |
49 // use the Solr version confs where appropriate. | |
50 $version = $this->getSolrVersion(); | |
51 if (isset($version) && $version == 3) { | |
52 $conf_path = dirname(__FILE__) . '/../solr-conf/solr-3.x/*'; | |
53 } | |
54 elseif (isset($version) && $version == 4) { | |
55 $conf_path = dirname(__FILE__) . '/../solr-conf/solr-4.x/*'; | |
56 } | |
57 else { | |
58 $conf_path = dirname(__FILE__) . '/../solr-conf/solr-1.4/*'; | |
59 } | |
60 | |
61 $patterns = array( | |
62 $conf_path, | |
63 dirname(__FILE__) . '/conf/*', | |
64 ); | |
65 | |
66 // Copy all files in solr-conf dir to our temporary solr core. | |
67 drupal_mkdir("{$instancedir}/conf", 0777, TRUE); | |
68 foreach ($patterns as $pattern) { | |
69 foreach (glob($pattern) as $conf_file) { | |
70 copy($conf_file, "$instancedir/conf/" . basename($conf_file)); | |
71 } | |
72 } | |
73 | |
74 $contents = file_get_contents("$instancedir/conf/solrconfig.xml"); | |
75 | |
76 // Change the autoCommit time down to 1 second. | |
77 // @todo - use solrcore.properties file for 3.x. | |
78 file_put_contents("$instancedir/conf/solrconfig.xml", preg_replace('@<maxTime>[0-9]+</maxTime>@', '<maxTime>1000</maxTime>', $contents)); | |
79 | |
80 // hard chmod -R because it seems drupal dirs are too restricted in a | |
81 // testing environment | |
82 system("chmod -R 777 {$instancedir}"); | |
83 | |
84 $query['name'] = $this->databasePrefix; | |
85 $query['instanceDir'] = $instancedir; | |
86 $created = $this->coreAdmin('CREATE', $query); | |
87 | |
88 if ($created && apachesolr_server_status($environment['url'])) { | |
89 $this->instancedir = $instancedir; | |
90 $this->solr_url = $environment['url']; | |
91 apachesolr_environment_save($environment); | |
92 $this->solr = apachesolr_get_solr($env_id); | |
93 $this->solr_available = TRUE; | |
94 $this->checkCoreStatus($this->databasePrefix); | |
95 } | |
96 } | |
97 // Workaround for drupal.org test bot. | |
98 // The tests succeed but further tests will not run because $this->solr_available is FALSE. | |
99 if (!$this->solr_available) { | |
100 $this->pass(t('Warning : The solr instance could not be found. Please enable a multicore one on http://localhost:8983/solr')); | |
101 } | |
102 } | |
103 | |
104 protected function coreAdminAvailable() { | |
105 $url = url($this->core_admin_url, array('query' => array('action' => 'STATUS'))); | |
106 $options['timeout'] = 2; | |
107 $result = drupal_http_request($url, $options); | |
108 return ($result->code == 200 && empty($result->error)); | |
109 } | |
110 | |
111 protected function getSolrVersion() { | |
112 $status = $this->coreAdmin('STATUS'); | |
113 foreach($status['status'] as $core_id => $core) { | |
114 $solr = new DrupalApacheSolrService($this->base_solr_url . '/' . $core_id); | |
115 $version = $solr->getSolrVersion(); | |
116 if (!empty($version)) { | |
117 return $version; | |
118 } | |
119 else { | |
120 return "1"; | |
121 } | |
122 } | |
123 } | |
124 | |
125 /** | |
126 * Helper function to invoke core admin actions. | |
127 */ | |
128 protected function coreAdmin($action, $query = array()) { | |
129 $query['action'] = $action; | |
130 $query['wt'] = 'json'; | |
131 $url = url($this->core_admin_url, array('query' => $query)); | |
132 $options['timeout'] = 2; | |
133 $result = drupal_http_request($url, $options); | |
134 | |
135 if ($result->code == 200) { | |
136 return json_decode($result->data, TRUE); | |
137 } | |
138 else { | |
139 return FALSE; | |
140 } | |
141 } | |
142 | |
143 /** | |
144 * Helper function to verify that the expected core exists. | |
145 */ | |
146 protected function checkCoreStatus($core_name) { | |
147 $response = $this->coreAdmin('STATUS', array('core' => $core_name)); | |
148 $this->assertTrue(isset($response['status'][$core_name]['index']), 'Found Solr test core index status'); | |
149 } | |
150 | |
151 function tearDown() { | |
152 // Workaround for drupal.org test bot | |
153 if ($this->solr_available) { | |
154 // Unload the Solr core & delete all files | |
155 $query = array( | |
156 'core' => $this->databasePrefix, | |
157 'deleteIndex' => 'true', | |
158 'deleteDataDir' => 'true', | |
159 'deleteInstanceDir' => 'true' | |
160 ); | |
161 // This is currently broken due to | |
162 // https://issues.apache.org/jira/browse/SOLR-3586 | |
163 $this->coreAdmin('UNLOAD', $query); | |
164 | |
165 } | |
166 parent::tearDown(); | |
167 } | |
168 } | |
169 | |
170 | |
171 class DrupalSolrOnlineWebTestCase extends AbstractDrupalSolrOnlineWebTestCase { | |
172 /** | |
173 * Implementation of setUp(). | |
174 */ | |
175 function setUp() { | |
176 parent::setUp(); | |
177 parent::setUpSolr(); | |
178 } | |
179 } | |
180 | |
181 | |
182 class DrupalSolrMatchTestCase extends DrupalSolrOnlineWebTestCase { | |
183 public static function getInfo() { | |
184 return array( | |
185 'name' => 'Solr Index Data and test live queries', | |
186 'description' => 'Indexes content and queries it.', | |
187 'group' => 'ApacheSolr', | |
188 ); | |
189 } | |
190 | |
191 /** | |
192 * Test search indexing. | |
193 */ | |
194 function testMatching() { | |
195 if ($this->solr_available) { // workaround for drupal.org test bot | |
196 $this->assertTrue($this->solr->ping(), "The Server could be Pinged"); | |
197 $response = $this->solr->search("*:*", array()); | |
198 $response = $response->response; | |
199 $this->assertEqual($response->numFound, 0, "There should not be any documents in the index"); | |
200 $this->populateIndex(7); | |
201 $response = $this->solr->search("*:*", array()); | |
202 $response = $response->response; | |
203 $this->assertEqual($response->numFound, 7, "There should be 7 documents in the index"); | |
204 $this->_testQueries(); | |
205 } | |
206 } | |
207 | |
208 /** | |
209 * Set up a small index of items to test against. | |
210 */ | |
211 protected function populateIndex($count) { | |
212 | |
213 variable_set('minimum_word_size', 3); | |
214 for ($i = 1; $i <= $count; ++$i) { | |
215 $documents[] = $this->buildDocument(array('entity_id' => $i, 'content' => $this->getText($i))); | |
216 } | |
217 $this->solr->addDocuments($documents); | |
218 $this->solr->commit(); | |
219 } | |
220 | |
221 protected function buildDocument($values = array()) { | |
222 $document = new ApacheSolrDocument(); | |
223 if (!isset($values['entity_type'])) { | |
224 $values['entity_type'] = 'fake.'; | |
225 } | |
226 $document->id = apachesolr_document_id($values['entity_id'], $values['entity_type']); | |
227 foreach ($values as $key => $value) { | |
228 $document->$key = $value; | |
229 } | |
230 return $document; | |
231 } | |
232 | |
233 /** | |
234 * Helper method for generating snippets of content. | |
235 * | |
236 * Generated items to test against: | |
237 * 1 ipsum | |
238 * 2 dolore sit | |
239 * 3 sit am ut | |
240 * 4 am ut enim am | |
241 * 5 ut enim am minim veniam | |
242 * 6 enim am minim veniam es cillum | |
243 * 7 am minim veniam es cillum dolore eu | |
244 */ | |
245 function getText($n) { | |
246 // Start over after 7. | |
247 $n = $n % 7; | |
248 $words = explode(' ', "Ipsum dolore sit am. Ut enim am minim veniam. Es cillum dolore eu."); | |
249 return implode(' ', array_slice($words, $n - 1, $n)); | |
250 } | |
251 | |
252 /** | |
253 * Run predefine queries looking for indexed terms. | |
254 */ | |
255 function _testQueries() { | |
256 /* | |
257 Note: OR queries that include short words in OR groups are only accepted | |
258 if the ORed terms are ANDed with at least one long word in the rest of the query. | |
259 | |
260 e.g. enim dolore OR ut = enim (dolore OR ut) = (enim dolor) OR (enim ut) -> good | |
261 e.g. dolore OR ut = (dolore) OR (ut) -> bad | |
262 | |
263 This is a design limitation to avoid full table scans. | |
264 | |
265 APACHESOLR NOTE: These are not all in lucene syntax... @TODO. Still works for text searching | |
266 */ | |
267 $queries = array( | |
268 // Simple AND queries. | |
269 'ipsum' => array(1), | |
270 'enim' => array(4, 5, 6), | |
271 'xxxxx' => array(), | |
272 // Mixed queries. | |
273 '"minim am veniam es" OR "dolore sit"' => array(2), | |
274 '"minim am veniam es" OR "sit dolore"' => array(), | |
275 '"am minim veniam es" -eu' => array(6), | |
276 '"am minim veniam" -"cillum dolore"' => array(5, 6), | |
277 ); | |
278 $broken = array( | |
279 'enim minim' => array(5, 6), | |
280 'enim xxxxx' => array(), | |
281 'dolore eu' => array(7), | |
282 'dolore xx' => array(), | |
283 'ut minim' => array(5), | |
284 'xx minim' => array(), | |
285 'enim veniam am minim ut' => array(5), | |
286 // Simple OR queries. | |
287 'dolore OR ipsum' => array(1, 2, 7), | |
288 'dolore OR xxxxx' => array(2, 7), | |
289 'dolore OR ipsum OR enim' => array(1, 2, 4, 5, 6, 7), | |
290 'minim dolore OR ipsum OR enim' => array(5, 6, 7), | |
291 'xxxxx dolore OR ipsum' => array(), | |
292 // Negative queries. | |
293 'dolore -sit' => array(7), | |
294 'dolore -eu' => array(2), | |
295 'dolore -xxxxx' => array(2, 7), | |
296 'dolore -xx' => array(2, 7), | |
297 // Phrase queries. | |
298 '"dolore sit"' => array(2), | |
299 '"sit dolore"' => array(), | |
300 '"am minim veniam es"' => array(6, 7), | |
301 '"minim am veniam es"' => array(), | |
302 'xxxxx "minim am veniam es" OR dolore' => array(), | |
303 'xx "minim am veniam es" OR dolore' => array(), | |
304 // Mixed queries. | |
305 '"am minim veniam es" OR dolore' => array(2, 6, 7), | |
306 '"am minim veniam" -"dolore cillum"' => array(5, 6, 7), | |
307 ); | |
308 foreach ($queries as $query => $results) { | |
309 $response = $this->solr->search($query, array()); | |
310 $this->_testQueryMatching($query, $response->response->docs, $results); | |
311 //@TODO: We might get to this later | |
312 #$this->_testQueryScores($query, $response->responses->docs, $results); | |
313 } | |
314 } | |
315 | |
316 /** | |
317 * Test the matching abilities of the engine. | |
318 * | |
319 * Verify if a query produces the correct results. | |
320 */ | |
321 function _testQueryMatching($query, $set, $results) { | |
322 // Get result IDs. | |
323 $found = array(); | |
324 foreach ($set as $item) { | |
325 $found[] = $item->entity_id; | |
326 } | |
327 // Compare $results and $found. | |
328 sort($found); | |
329 sort($results); | |
330 $this->assertEqual($found, $results, strtr("Query matching '$query' found: @found expected: @expected", array('@found' => implode(',', $found), '@expected' => implode(',', $results)))); | |
331 } | |
332 | |
333 /** | |
334 * Test the scoring abilities of the engine. | |
335 * | |
336 * Verify if a query produces normalized, monotonous scores. | |
337 */ | |
338 function _testQueryScores($query, $set, $results) { | |
339 // Get result scores. | |
340 $scores = array(); | |
341 foreach ($set as $item) { | |
342 $scores[] = $item->score; | |
343 } | |
344 | |
345 // Check order. | |
346 $sorted = $scores; | |
347 sort($sorted); | |
348 $this->assertEqual($scores, array_reverse($sorted), "Query order '$query'"); | |
349 | |
350 // Check range. | |
351 $this->assertEqual(!count($scores) || (min($scores) > 0.0 && max($scores) <= 1.0001), TRUE, "Query scoring '$query'"); | |
352 } | |
353 | |
354 } |