Merge "Special:BlockList: Update remove/change block links"
[mediawiki.git] / tests / phpunit / includes / TestUserRegistry.php
blob161bcbb444bcb891a1dcd558e29d4a81d9239f5d
1 <?php
3 use MediaWiki\User\User;
5 /**
6 * @since 1.28
7 */
8 class TestUserRegistry {
10 /** @var TestUser[] (group key => TestUser) */
11 private static $testUsers = [];
13 /** @var int Count of users that have been generated */
14 private static $counter = 0;
16 /** @var int Random int, included in IDs */
17 private static $randInt;
19 public static function getNextId() {
20 if ( !self::$randInt ) {
21 self::$randInt = mt_rand( 1, 0xFFFFFF );
23 return sprintf( '%06x.%03x', self::$randInt, ++self::$counter );
26 /**
27 * Get a TestUser object that the caller may modify.
29 * @since 1.28
31 * @param string $testName Caller's __CLASS__ or arbitrary string. Used to generate the
32 * user's username.
33 * @param string|string[] $groups Groups the test user should be added to.
34 * @param string|null $userPrefix if non-null, the user prefix will be as specified instead of "TestUser"
35 * @return TestUser
37 public static function getMutableTestUser( $testName, $groups = [], $userPrefix = null ) {
38 $id = self::getNextId();
39 $testUserName = "$testName $id";
40 $userPrefix ??= "TestUser";
41 $testUser = new TestUser(
42 "$userPrefix $testName $id",
43 "Name $id",
44 "$id@mediawiki.test",
45 (array)$groups
47 $testUser->getUser()->clearInstanceCache();
48 return $testUser;
51 /**
52 * Get a TestUser object that the caller may not modify.
54 * Whenever possible, unit tests should use immutable users, because
55 * immutable users can be reused in multiple tests, which helps keep
56 * the unit tests fast.
58 * @since 1.28
60 * @param string|string[] $groups Groups the test user should be added to.
61 * @return TestUser
63 public static function getImmutableTestUser( $groups = [] ) {
64 $groups = array_unique( (array)$groups );
65 sort( $groups );
66 $key = implode( ',', $groups );
68 $testUser = self::$testUsers[$key] ?? false;
70 if ( !$testUser || !$testUser->getUser()->isRegistered() ) {
71 $id = self::getNextId();
72 // Hack! If this is the primary sysop account, make the username
73 // be 'UTSysop', for back-compat, and for the sake of PHPUnit data
74 // provider methods, which are executed before the test database
75 // is set up. See T136348.
76 if ( $groups === [ 'bureaucrat', 'sysop' ] ) {
77 $username = 'UTSysop';
78 } else {
79 $username = "TestUser $id";
81 self::$testUsers[$key] = $testUser = new TestUser(
82 $username,
83 "Name $id",
84 "$id@mediawiki.test",
85 $groups
89 $testUser->getUser()->clearInstanceCache();
90 return self::$testUsers[$key];
93 /**
94 * TestUsers created by this class will not be deleted, but any handles
95 * to existing immutable TestUsers will be deleted, ensuring these users
96 * are not reused. We don't reset the counter or random string by design.
98 * @since 1.28
100 public static function clear() {
101 self::$testUsers = [];
105 * Call clearInstanceCache() on all User objects known to the registry.
106 * This ensures that the User objects do not retain stale references
107 * to service objects.
109 * @since 1.39
111 public static function clearInstanceCaches() {
112 foreach ( self::$testUsers as $user ) {
113 $user->getUser()->clearInstanceCache();
118 * @todo It would be nice if this were a non-static method of TestUser
119 * instead, but that doesn't seem possible without friends?
121 * @param User $user
122 * @return bool True if it's safe to modify the user
124 public static function isMutable( User $user ) {
125 foreach ( self::$testUsers as $key => $testUser ) {
126 if ( $user === $testUser->getUser() ) {
127 return false;
130 return true;