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