@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at recaptime-dev/main 260 lines 8.2 kB view raw
1<?php 2 3abstract class PhabricatorTestCase extends PhutilTestCase { 4 5 const NAMESPACE_PREFIX = 'phabricator_unittest_'; 6 7 /** 8 * If true, put Lisk in process-isolated mode for the duration of the tests so 9 * that it will establish only isolated, side-effect-free database 10 * connections. Defaults to true. 11 * 12 * NOTE: You should disable this only in rare circumstances. Unit tests should 13 * not rely on external resources like databases, and should not produce 14 * side effects. 15 */ 16 const PHABRICATOR_TESTCONFIG_ISOLATE_LISK = 'isolate-lisk'; 17 18 /** 19 * If true, build storage fixtures before running tests, and connect to them 20 * during test execution. This will impose a performance penalty on test 21 * execution (currently, it takes roughly one second to build the fixture) 22 * but allows you to perform tests which require data to be read from storage 23 * after writes. The fixture is shared across all test cases in this process. 24 * Defaults to false. 25 * 26 * NOTE: All connections to fixture storage open transactions when established 27 * and roll them back when tests complete. Each test must independently 28 * write data it relies on; data will not persist across tests. 29 * 30 * NOTE: Enabling this implies disabling process isolation. 31 */ 32 const PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES = 'storage-fixtures'; 33 34 private $configuration; 35 private $env; 36 37 private static $storageFixtureReferences = 0; 38 private static $storageFixture; 39 private static $storageFixtureObjectSeed = 0; 40 private static $testsAreRunning = 0; 41 42 protected function getPhabricatorTestCaseConfiguration() { 43 return array(); 44 } 45 46 private function getComputedConfiguration() { 47 $config = $this->getPhabricatorTestCaseConfiguration() + array( 48 self::PHABRICATOR_TESTCONFIG_ISOLATE_LISK => true, 49 self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => false, 50 ); 51 52 if ($config[self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES]) { 53 // Fixtures don't make sense with process isolation. 54 $config[self::PHABRICATOR_TESTCONFIG_ISOLATE_LISK] = false; 55 } 56 57 return $config; 58 } 59 60 /** @phutil-external-symbol function init_phabricator_script */ 61 public function willRunTestCases(array $test_cases) { 62 $root = dirname(phutil_get_library_root('phabricator')); 63 if (!function_exists('init_phabricator_script')) { 64 // Run the initialization routines only if nothing else already did 65 require_once $root.'/scripts/init/lib.php'; 66 init_phabricator_script( 67 array( 68 'config.optional' => false, 69 'no-extensions' => true, 70 )); 71 } 72 73 $config = $this->getComputedConfiguration(); 74 75 if ($config[self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES]) { 76 ++self::$storageFixtureReferences; 77 if (!self::$storageFixture) { 78 self::$storageFixture = $this->newStorageFixture(); 79 } 80 } 81 82 ++self::$testsAreRunning; 83 } 84 85 public function didRunTestCases(array $test_cases) { 86 if (self::$storageFixture) { 87 self::$storageFixtureReferences--; 88 if (!self::$storageFixtureReferences) { 89 self::$storageFixture = null; 90 } 91 } 92 93 --self::$testsAreRunning; 94 } 95 96 protected function willRunTests() { 97 $config = $this->getComputedConfiguration(); 98 99 if ($config[self::PHABRICATOR_TESTCONFIG_ISOLATE_LISK]) { 100 LiskDAO::beginIsolateAllLiskEffectsToCurrentProcess(); 101 } 102 103 $this->env = PhabricatorEnv::beginScopedEnv(); 104 105 // NOTE: While running unit tests, we act as though all applications are 106 // enabled, regardless of the install's configuration. Tests which need 107 // to disable applications are responsible for adjusting state themselves 108 // (such tests are exceedingly rare). 109 110 $this->env->overrideEnvConfig( 111 'phabricator.uninstalled-applications', 112 array()); 113 $this->env->overrideEnvConfig( 114 'phabricator.show-prototypes', 115 true); 116 117 // Reset application settings to defaults, particularly policies. 118 $this->env->overrideEnvConfig( 119 'phabricator.application-settings', 120 array()); 121 122 // We can't stub this service right now, and it's not generally useful 123 // to publish notifications about test execution. 124 $this->env->overrideEnvConfig( 125 'notification.servers', 126 array()); 127 128 $this->env->overrideEnvConfig( 129 'phabricator.base-uri', 130 'http://phabricator.example.com'); 131 132 $this->env->overrideEnvConfig( 133 'auth.email-domains', 134 array()); 135 136 // Tests do their own stubbing/voiding for events. 137 $this->env->overrideEnvConfig('phabricator.silent', false); 138 139 $this->env->overrideEnvConfig('cluster.read-only', false); 140 141 $this->env->overrideEnvConfig( 142 'maniphest.custom-field-definitions', 143 array()); 144 } 145 146 protected function didRunTests() { 147 $config = $this->getComputedConfiguration(); 148 149 if ($config[self::PHABRICATOR_TESTCONFIG_ISOLATE_LISK]) { 150 LiskDAO::endIsolateAllLiskEffectsToCurrentProcess(); 151 } 152 153 try { 154 if (phutil_is_hiphop_runtime()) { 155 $this->env->__destruct(); 156 } 157 unset($this->env); 158 } catch (Exception $ex) { 159 throw new Exception( 160 pht( 161 'Some test called %s, but is still holding '. 162 'a reference to the scoped environment!', 163 'PhabricatorEnv::beginScopedEnv()')); 164 } 165 } 166 167 protected function willRunOneTest($test) { 168 $config = $this->getComputedConfiguration(); 169 170 if ($config[self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES]) { 171 LiskDAO::beginIsolateAllLiskEffectsToTransactions(); 172 } 173 } 174 175 protected function didRunOneTest($test) { 176 $config = $this->getComputedConfiguration(); 177 178 if ($config[self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES]) { 179 LiskDAO::endIsolateAllLiskEffectsToTransactions(); 180 } 181 } 182 183 protected function newStorageFixture() { 184 $bytes = Filesystem::readRandomCharacters(24); 185 $name = self::NAMESPACE_PREFIX.$bytes; 186 187 return new PhabricatorStorageFixtureScopeGuard($name); 188 } 189 190 /** 191 * Returns an integer seed to use when building unique identifiers (e.g., 192 * non-colliding usernames). The seed is unstable and its value will change 193 * between test runs, so your tests must not rely on it. 194 * 195 * @return int A unique integer. 196 */ 197 protected function getNextObjectSeed() { 198 self::$storageFixtureObjectSeed += mt_rand(1, 100); 199 return self::$storageFixtureObjectSeed; 200 } 201 202 protected function generateNewTestUser() { 203 $seed = $this->getNextObjectSeed(); 204 205 $user = id(new PhabricatorUser()) 206 ->setRealName(pht('Test User %s', $seed)) 207 ->setUserName("test{$seed}") 208 ->setIsApproved(1); 209 210 $email = id(new PhabricatorUserEmail()) 211 ->setAddress("testuser{$seed}@example.com") 212 ->setIsVerified(1); 213 214 $editor = new PhabricatorUserEditor(); 215 $editor->setActor($user); 216 $editor->createNewUser($user, $email); 217 218 // When creating a new test user, we prefill their setting cache as empty. 219 // This is a little more efficient than doing a query to load the empty 220 // settings. 221 $user->attachRawCacheData( 222 array( 223 PhabricatorUserPreferencesCacheType::KEY_PREFERENCES => '[]', 224 )); 225 226 return $user; 227 } 228 229 230 /** 231 * Throws unless tests are currently executing. This method can be used to 232 * guard code which is specific to unit tests and should not normally be 233 * reachable. 234 * 235 * If tests aren't currently being executed, throws an exception. 236 */ 237 public static function assertExecutingUnitTests() { 238 if (!self::$testsAreRunning) { 239 throw new Exception( 240 pht( 241 'Executing test code outside of test execution! '. 242 'This code path can only be run during unit tests.')); 243 } 244 } 245 246 protected function requireBinaryForTest($binary) { 247 if (!Filesystem::binaryExists($binary)) { 248 $this->assertSkipped( 249 pht( 250 'No binary "%s" found on this system, skipping test.', 251 $binary)); 252 } 253 } 254 255 protected function newContentSource() { 256 return PhabricatorContentSource::newForSource( 257 PhabricatorUnitTestContentSource::SOURCECONST); 258 } 259 260}