This reduces the runtime of database-bound tests by about 40% (on my system, from 4:55 to 2:47; results from Jenkins are inconclusive). The basic idea is to call addCoreDBData() only once, and have a addDBDataOnce() that is called once per test class, not for every test method lie addDBData() is. Most tests could be trivially be changed to implement addDBDataOnce() instead of addDBData(). The ones for which this did not work immediately were left out for now. A closer look at the tests that still implement addDBData() may reveal additional potential for improvement. TODO: Once this is merged, try to change addDBData() to addDBDataOnce() where possible in extensions. Change-Id: Iec4ed4c8419fb4ad87e6710de808863ede9998b7
311 lines
7 KiB
PHP
311 lines
7 KiB
PHP
<?php
|
|
/**
|
|
* @group Search
|
|
* @group Database
|
|
*/
|
|
class PrefixSearchTest extends MediaWikiLangTestCase {
|
|
|
|
public function addDBDataOnce() {
|
|
if ( !$this->isWikitextNS( NS_MAIN ) ) {
|
|
// tests are skipped if NS_MAIN is not wikitext
|
|
return;
|
|
}
|
|
|
|
$this->insertPage( 'Sandbox' );
|
|
$this->insertPage( 'Bar' );
|
|
$this->insertPage( 'Example' );
|
|
$this->insertPage( 'Example Bar' );
|
|
$this->insertPage( 'Example Foo' );
|
|
$this->insertPage( 'Example Foo/Bar' );
|
|
$this->insertPage( 'Example/Baz' );
|
|
$this->insertPage( 'Redirect test', '#REDIRECT [[Redirect Test]]' );
|
|
$this->insertPage( 'Redirect Test' );
|
|
$this->insertPage( 'Redirect Test Worse Result' );
|
|
$this->insertPage( 'Redirect test2', '#REDIRECT [[Redirect Test2]]' );
|
|
$this->insertPage( 'Redirect TEST2', '#REDIRECT [[Redirect Test2]]' );
|
|
$this->insertPage( 'Redirect Test2' );
|
|
$this->insertPage( 'Redirect Test2 Worse Result' );
|
|
|
|
$this->insertPage( 'Talk:Sandbox' );
|
|
$this->insertPage( 'Talk:Example' );
|
|
|
|
$this->insertPage( 'User:Example' );
|
|
}
|
|
|
|
protected function setUp() {
|
|
parent::setUp();
|
|
|
|
if ( !$this->isWikitextNS( NS_MAIN ) ) {
|
|
$this->markTestSkipped( 'Main namespace does not support wikitext.' );
|
|
}
|
|
|
|
// Avoid special pages from extensions interferring with the tests
|
|
$this->setMwGlobals( 'wgSpecialPages', [] );
|
|
}
|
|
|
|
protected function searchProvision( array $results = null ) {
|
|
if ( $results === null ) {
|
|
$this->setMwGlobals( 'wgHooks', [] );
|
|
} else {
|
|
$this->setMwGlobals( 'wgHooks', [
|
|
'PrefixSearchBackend' => [
|
|
function ( $namespaces, $search, $limit, &$srchres ) use ( $results ) {
|
|
$srchres = $results;
|
|
return false;
|
|
}
|
|
],
|
|
] );
|
|
}
|
|
}
|
|
|
|
public static function provideSearch() {
|
|
return [
|
|
[ [
|
|
'Empty string',
|
|
'query' => '',
|
|
'results' => [],
|
|
] ],
|
|
[ [
|
|
'Main namespace with title prefix',
|
|
'query' => 'Ex',
|
|
'results' => [
|
|
'Example',
|
|
'Example/Baz',
|
|
'Example Bar',
|
|
],
|
|
// Third result when testing offset
|
|
'offsetresult' => [
|
|
'Example Foo',
|
|
],
|
|
] ],
|
|
[ [
|
|
'Talk namespace prefix',
|
|
'query' => 'Talk:',
|
|
'results' => [
|
|
'Talk:Example',
|
|
'Talk:Sandbox',
|
|
],
|
|
] ],
|
|
[ [
|
|
'User namespace prefix',
|
|
'query' => 'User:',
|
|
'results' => [
|
|
'User:Example',
|
|
],
|
|
] ],
|
|
[ [
|
|
'Special namespace prefix',
|
|
'query' => 'Special:',
|
|
'results' => [
|
|
'Special:ActiveUsers',
|
|
'Special:AllMessages',
|
|
'Special:AllMyFiles',
|
|
],
|
|
// Third result when testing offset
|
|
'offsetresult' => [
|
|
'Special:AllMyUploads',
|
|
],
|
|
] ],
|
|
[ [
|
|
'Special namespace with prefix',
|
|
'query' => 'Special:Un',
|
|
'results' => [
|
|
'Special:Unblock',
|
|
'Special:UncategorizedCategories',
|
|
'Special:UncategorizedFiles',
|
|
],
|
|
// Third result when testing offset
|
|
'offsetresult' => [
|
|
'Special:UncategorizedImages',
|
|
],
|
|
] ],
|
|
[ [
|
|
'Special page name',
|
|
'query' => 'Special:EditWatchlist',
|
|
'results' => [
|
|
'Special:EditWatchlist',
|
|
],
|
|
] ],
|
|
[ [
|
|
'Special page subpages',
|
|
'query' => 'Special:EditWatchlist/',
|
|
'results' => [
|
|
'Special:EditWatchlist/clear',
|
|
'Special:EditWatchlist/raw',
|
|
],
|
|
] ],
|
|
[ [
|
|
'Special page subpages with prefix',
|
|
'query' => 'Special:EditWatchlist/cl',
|
|
'results' => [
|
|
'Special:EditWatchlist/clear',
|
|
],
|
|
] ],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideSearch
|
|
* @covers PrefixSearch::search
|
|
* @covers PrefixSearch::searchBackend
|
|
*/
|
|
public function testSearch( array $case ) {
|
|
$this->searchProvision( null );
|
|
$searcher = new StringPrefixSearch;
|
|
$results = $searcher->search( $case['query'], 3 );
|
|
$this->assertEquals(
|
|
$case['results'],
|
|
$results,
|
|
$case[0]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideSearch
|
|
* @covers PrefixSearch::search
|
|
* @covers PrefixSearch::searchBackend
|
|
*/
|
|
public function testSearchWithOffset( array $case ) {
|
|
$this->searchProvision( null );
|
|
$searcher = new StringPrefixSearch;
|
|
$results = $searcher->search( $case['query'], 3, [], 1 );
|
|
|
|
// We don't expect the first result when offsetting
|
|
array_shift( $case['results'] );
|
|
// And sometimes we expect a different last result
|
|
$expected = isset( $case['offsetresult'] ) ?
|
|
array_merge( $case['results'], $case['offsetresult'] ) :
|
|
$case['results'];
|
|
|
|
$this->assertEquals(
|
|
$expected,
|
|
$results,
|
|
$case[0]
|
|
);
|
|
}
|
|
|
|
public static function provideSearchBackend() {
|
|
return [
|
|
[ [
|
|
'Simple case',
|
|
'provision' => [
|
|
'Bar',
|
|
'Barcelona',
|
|
'Barbara',
|
|
],
|
|
'query' => 'Bar',
|
|
'results' => [
|
|
'Bar',
|
|
'Barcelona',
|
|
'Barbara',
|
|
],
|
|
] ],
|
|
[ [
|
|
'Exact match not on top (bug 70958)',
|
|
'provision' => [
|
|
'Barcelona',
|
|
'Bar',
|
|
'Barbara',
|
|
],
|
|
'query' => 'Bar',
|
|
'results' => [
|
|
'Bar',
|
|
'Barcelona',
|
|
'Barbara',
|
|
],
|
|
] ],
|
|
[ [
|
|
'Exact match missing (bug 70958)',
|
|
'provision' => [
|
|
'Barcelona',
|
|
'Barbara',
|
|
'Bart',
|
|
],
|
|
'query' => 'Bar',
|
|
'results' => [
|
|
'Bar',
|
|
'Barcelona',
|
|
'Barbara',
|
|
],
|
|
] ],
|
|
[ [
|
|
'Exact match missing and not existing',
|
|
'provision' => [
|
|
'Exile',
|
|
'Exist',
|
|
'External',
|
|
],
|
|
'query' => 'Ex',
|
|
'results' => [
|
|
'Exile',
|
|
'Exist',
|
|
'External',
|
|
],
|
|
] ],
|
|
[ [
|
|
"Exact match shouldn't override already found match if " .
|
|
"exact is redirect and found isn't",
|
|
'provision' => [
|
|
// Target of the exact match is low in the list
|
|
'Redirect Test Worse Result',
|
|
'Redirect Test',
|
|
],
|
|
'query' => 'redirect test',
|
|
'results' => [
|
|
// Redirect target is pulled up and exact match isn't added
|
|
'Redirect Test',
|
|
'Redirect Test Worse Result',
|
|
],
|
|
] ],
|
|
[ [
|
|
"Exact match shouldn't override already found match if " .
|
|
"both exact match and found match are redirect",
|
|
'provision' => [
|
|
// Another redirect to the same target as the exact match
|
|
// is low in the list
|
|
'Redirect Test2 Worse Result',
|
|
'Redirect test2',
|
|
],
|
|
'query' => 'redirect TEST2',
|
|
'results' => [
|
|
// Found redirect is pulled to the top and exact match isn't
|
|
// added
|
|
'Redirect test2',
|
|
'Redirect Test2 Worse Result',
|
|
],
|
|
] ],
|
|
[ [
|
|
"Exact match should override any already found matches that " .
|
|
"are redirects to it",
|
|
'provision' => [
|
|
// Another redirect to the same target as the exact match
|
|
// is low in the list
|
|
'Redirect Test Worse Result',
|
|
'Redirect test',
|
|
],
|
|
'query' => 'Redirect Test',
|
|
'results' => [
|
|
// Found redirect is pulled to the top and exact match isn't
|
|
// added
|
|
'Redirect Test',
|
|
'Redirect Test Worse Result',
|
|
],
|
|
] ],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideSearchBackend
|
|
* @covers PrefixSearch::searchBackend
|
|
*/
|
|
public function testSearchBackend( array $case ) {
|
|
$this->searchProvision( $case['provision'] );
|
|
$searcher = new StringPrefixSearch;
|
|
$results = $searcher->search( $case['query'], 3 );
|
|
$this->assertEquals(
|
|
$case['results'],
|
|
$results,
|
|
$case[0]
|
|
);
|
|
}
|
|
}
|