3 * Test class for SpecialRecentchanges class
5 * Copyright © 2011, Antoine Musso
7 * @author Antoine Musso
10 * @covers SpecialRecentChanges
12 class SpecialRecentchangesTest
extends MediaWikiTestCase
{
14 protected function setUp() {
16 $this->setMwGlobals( 'wgRCWatchCategoryMembership', true );
20 * @var SpecialRecentChanges
24 /** helper to test SpecialRecentchanges::buildMainQueryConds() */
25 private function assertConditions(
27 $requestOptions = null,
31 $context = new RequestContext
;
32 $context->setRequest( new FauxRequest( $requestOptions ) );
34 $context->setUser( $user );
38 $this->rc
= new SpecialRecentChanges();
39 $this->rc
->setContext( $context );
40 $formOptions = $this->rc
->setup( null );
42 # Filter out rc_timestamp conditions which depends on the test runtime
43 # This condition is not needed as of march 2, 2011 -- hashar
44 # @todo FIXME: Find a way to generate the correct rc_timestamp
45 $queryConditions = array_filter(
46 $this->rc
->buildMainQueryConds( $formOptions ),
47 'SpecialRecentchangesTest::filterOutRcTimestampCondition'
51 self
::normalizeCondition( $expected ),
52 self
::normalizeCondition( $queryConditions ),
57 private static function normalizeCondition( $conds ) {
58 $normalized = array_map(
60 return is_numeric( $k ) ?
$v : "$k = $v";
69 /** return false if condition begin with 'rc_timestamp ' */
70 private static function filterOutRcTimestampCondition( $var ) {
71 return ( false === strpos( $var, 'rc_timestamp ' ) );
74 public function testRcNsFilter() {
75 $this->assertConditions(
82 'namespace' => NS_MAIN
,
84 "rc conditions with no options (aka default setting)"
88 public function testRcNsFilterInversion() {
89 $this->assertConditions(
93 "rc_namespace != '0'",
96 'namespace' => NS_MAIN
,
99 "rc conditions with namespace inverted"
105 * @dataProvider provideNamespacesAssociations
107 public function testRcNsFilterAssociation( $ns1, $ns2 ) {
108 $this->assertConditions(
112 "(rc_namespace = '$ns1' OR rc_namespace = '$ns2')",
118 "rc conditions with namespace inverted"
124 * @dataProvider provideNamespacesAssociations
126 public function testRcNsFilterAssociationWithInversion( $ns1, $ns2 ) {
127 $this->assertConditions(
131 "(rc_namespace != '$ns1' AND rc_namespace != '$ns2')",
138 "rc conditions with namespace inverted"
143 * Provides associated namespaces to test recent changes
144 * namespaces association filtering.
146 public static function provideNamespacesAssociations() {
147 return [ # (NS => Associated_NS)
148 [ NS_MAIN
, NS_TALK
],
149 [ NS_TALK
, NS_MAIN
],
153 public function testRcHidemyselfFilter() {
154 $user = $this->getTestUser()->getUser();
155 $this->assertConditions(
158 "rc_user != '{$user->getId()}'",
164 "rc conditions: hidemyself=1 (logged in)",
168 $user = User
::newFromName( '10.11.12.13', false );
169 $this->assertConditions(
172 "rc_user_text != '10.11.12.13'",
178 "rc conditions: hidemyself=1 (anon)",
183 public function testRcHidebyothersFilter() {
184 $user = $this->getTestUser()->getUser();
185 $this->assertConditions(
188 "rc_user = '{$user->getId()}'",
194 "rc conditions: hidebyothers=1 (logged in)",
198 $user = User
::newFromName( '10.11.12.13', false );
199 $this->assertConditions(
202 "rc_user_text = '10.11.12.13'",
208 "rc conditions: hidebyothers=1 (anon)",
213 public function testRcHidemyselfHidebyothersFilter() {
214 $user = $this->getTestUser()->getUser();
215 $this->assertConditions(
218 "rc_user != '{$user->getId()}'",
219 "rc_user = '{$user->getId()}'",
226 "rc conditions: hidemyself=1 hidebyothers=1 (logged in)",
231 public function testRcHidepageedits() {
232 $this->assertConditions(
239 'hidepageedits' => 1,
241 "rc conditions: hidepageedits=1"
245 public function testRcHidenewpages() {
246 $this->assertConditions(
255 "rc conditions: hidenewpages=1"
259 public function testRcHidelog() {
260 $this->assertConditions(
269 "rc conditions: hidelog=1"
273 public function testRcHidehumans() {
274 $this->assertConditions(
283 "rc conditions: hidebots=0 hidehumans=1"
287 public function testRcHidepatrolledDisabledFilter() {
288 $user = $this->getTestUser()->getUser();
289 $this->assertConditions(
295 'hidepatrolled' => 1,
297 "rc conditions: hidepatrolled=1 (user not allowed)",
302 public function testRcHideunpatrolledDisabledFilter() {
303 $user = $this->getTestUser()->getUser();
304 $this->assertConditions(
310 'hideunpatrolled' => 1,
312 "rc conditions: hideunpatrolled=1 (user not allowed)",
316 public function testRcHidepatrolledFilter() {
317 $user = $this->getTestSysop()->getUser();
318 $this->assertConditions(
325 'hidepatrolled' => 1,
327 "rc conditions: hidepatrolled=1",
332 public function testRcHideunpatrolledFilter() {
333 $user = $this->getTestSysop()->getUser();
334 $this->assertConditions(
341 'hideunpatrolled' => 1,
343 "rc conditions: hideunpatrolled=1",
348 public function testRcHideminorFilter() {
349 $this->assertConditions(
358 "rc conditions: hideminor=1"
362 public function testRcHidemajorFilter() {
363 $this->assertConditions(
372 "rc conditions: hidemajor=1"
376 // This is probably going to change when we do auto-fix of
377 // filters combinations that don't make sense but for now
378 // it's the behavior therefore it's the test.
379 public function testRcHidepatrolledHideunpatrolledFilter() {
380 $user = $this->getTestSysop()->getUser();
381 $this->assertConditions(
389 'hidepatrolled' => 1,
390 'hideunpatrolled' => 1,
392 "rc conditions: hidepatrolled=1 hideunpatrolled=1",
397 public function testFilterUserExpLevel() {
398 $this->setMwGlobals( [
399 'wgLearnerEdits' => 10,
400 'wgLearnerMemberSince' => 4,
401 'wgExperiencedUserEdits' => 500,
402 'wgExperiencedUserMemberSince' => 30,
405 $this->createUsers( [
406 'Newcomer1' => [ 'edits' => 2, 'days' => 2 ],
407 'Newcomer2' => [ 'edits' => 12, 'days' => 3 ],
408 'Newcomer3' => [ 'edits' => 8, 'days' => 5 ],
409 'Learner1' => [ 'edits' => 15, 'days' => 10 ],
410 'Learner2' => [ 'edits' => 450, 'days' => 20 ],
411 'Learner3' => [ 'edits' => 460, 'days' => 33 ],
412 'Learner4' => [ 'edits' => 525, 'days' => 28 ],
413 'Experienced1' => [ 'edits' => 538, 'days' => 33 ],
417 $this->assertArrayEquals(
418 [ 'Newcomer1', 'Newcomer2', 'Newcomer3' ],
419 $this->fetchUsers( [ 'userExpLevel' => 'newcomer' ] )
422 // newcomers and learner
423 $this->assertArrayEquals(
425 'Newcomer1', 'Newcomer2', 'Newcomer3',
426 'Learner1', 'Learner2', 'Learner3', 'Learner4',
428 $this->fetchUsers( [ 'userExpLevel' => 'newcomer,learner' ] )
431 // newcomers and more learner
432 $this->assertArrayEquals(
434 'Newcomer1', 'Newcomer2', 'Newcomer3',
437 $this->fetchUsers( [ 'userExpLevel' => 'newcomer,experienced' ] )
441 $this->assertArrayEquals(
442 [ 'Learner1', 'Learner2', 'Learner3', 'Learner4' ],
443 $this->fetchUsers( [ 'userExpLevel' => 'learner' ] )
446 // more experienced only
447 $this->assertArrayEquals(
449 $this->fetchUsers( [ 'userExpLevel' => 'experienced' ] )
452 // learner and more experienced
453 $this->assertArrayEquals(
455 'Learner1', 'Learner2', 'Learner3', 'Learner4',
458 $this->fetchUsers( [ 'userExpLevel' => 'learner,experienced' ] )
461 // newcomers, learner, and more experienced
462 $this->assertArrayEquals(
464 'Newcomer1', 'Newcomer2', 'Newcomer3',
465 'Learner1', 'Learner2', 'Learner3', 'Learner4',
468 $this->fetchUsers( [ 'userExpLevel' => 'newcomer,learner,experienced' ] )
472 $this->assertArrayEquals(
474 'Newcomer1', 'Newcomer2', 'Newcomer3',
475 'Learner1', 'Learner2', 'Learner3', 'Learner4',
478 $this->fetchUsers( [ 'userExpLevel' => 'all' ] )
482 private function createUsers( $specs ) {
483 $dbw = wfGetDB( DB_MASTER
);
484 foreach ( $specs as $name => $spec ) {
488 'editcount' => $spec['edits'],
489 'registration' => $dbw->timestamp( $this->daysAgo( $spec['days'] ) ),
496 private function fetchUsers( $filters ) {
497 $specialRC = new SpecialRecentChanges();
503 $specialRC->filterOnUserExperienceLevel(
510 $result = wfGetDB( DB_MASTER
)->select(
513 array_filter( $conds ) +
[ 'user_email' => 'ut' ]
517 foreach ( $result as $row ) {
518 $usernames[] = $row->user_name
;
524 private function daysAgo( $days ) {
525 $secondsPerDay = 86400;
526 return time() - $days * $secondsPerDay;