TestUserRegistry.php 3.4 KB

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