Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/infrastructure/testing/PhabricatorTestCase.php
12241 views
1
<?php
2
3
abstract 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
public function willRunTestCases(array $test_cases) {
61
$root = dirname(phutil_get_library_root('phabricator'));
62
require_once $root.'/scripts/__init_script__.php';
63
64
$config = $this->getComputedConfiguration();
65
66
if ($config[self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES]) {
67
++self::$storageFixtureReferences;
68
if (!self::$storageFixture) {
69
self::$storageFixture = $this->newStorageFixture();
70
}
71
}
72
73
++self::$testsAreRunning;
74
}
75
76
public function didRunTestCases(array $test_cases) {
77
if (self::$storageFixture) {
78
self::$storageFixtureReferences--;
79
if (!self::$storageFixtureReferences) {
80
self::$storageFixture = null;
81
}
82
}
83
84
--self::$testsAreRunning;
85
}
86
87
protected function willRunTests() {
88
$config = $this->getComputedConfiguration();
89
90
if ($config[self::PHABRICATOR_TESTCONFIG_ISOLATE_LISK]) {
91
LiskDAO::beginIsolateAllLiskEffectsToCurrentProcess();
92
}
93
94
$this->env = PhabricatorEnv::beginScopedEnv();
95
96
// NOTE: While running unit tests, we act as though all applications are
97
// installed, regardless of the install's configuration. Tests which need
98
// to uninstall applications are responsible for adjusting state themselves
99
// (such tests are exceedingly rare).
100
101
$this->env->overrideEnvConfig(
102
'phabricator.uninstalled-applications',
103
array());
104
$this->env->overrideEnvConfig(
105
'phabricator.show-prototypes',
106
true);
107
108
// Reset application settings to defaults, particularly policies.
109
$this->env->overrideEnvConfig(
110
'phabricator.application-settings',
111
array());
112
113
// We can't stub this service right now, and it's not generally useful
114
// to publish notifications about test execution.
115
$this->env->overrideEnvConfig(
116
'notification.servers',
117
array());
118
119
$this->env->overrideEnvConfig(
120
'phabricator.base-uri',
121
'http://phabricator.example.com');
122
123
$this->env->overrideEnvConfig(
124
'auth.email-domains',
125
array());
126
127
// Tests do their own stubbing/voiding for events.
128
$this->env->overrideEnvConfig('phabricator.silent', false);
129
130
$this->env->overrideEnvConfig('cluster.read-only', false);
131
132
$this->env->overrideEnvConfig(
133
'maniphest.custom-field-definitions',
134
array());
135
}
136
137
protected function didRunTests() {
138
$config = $this->getComputedConfiguration();
139
140
if ($config[self::PHABRICATOR_TESTCONFIG_ISOLATE_LISK]) {
141
LiskDAO::endIsolateAllLiskEffectsToCurrentProcess();
142
}
143
144
try {
145
if (phutil_is_hiphop_runtime()) {
146
$this->env->__destruct();
147
}
148
unset($this->env);
149
} catch (Exception $ex) {
150
throw new Exception(
151
pht(
152
'Some test called %s, but is still holding '.
153
'a reference to the scoped environment!',
154
'PhabricatorEnv::beginScopedEnv()'));
155
}
156
}
157
158
protected function willRunOneTest($test) {
159
$config = $this->getComputedConfiguration();
160
161
if ($config[self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES]) {
162
LiskDAO::beginIsolateAllLiskEffectsToTransactions();
163
}
164
}
165
166
protected function didRunOneTest($test) {
167
$config = $this->getComputedConfiguration();
168
169
if ($config[self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES]) {
170
LiskDAO::endIsolateAllLiskEffectsToTransactions();
171
}
172
}
173
174
protected function newStorageFixture() {
175
$bytes = Filesystem::readRandomCharacters(24);
176
$name = self::NAMESPACE_PREFIX.$bytes;
177
178
return new PhabricatorStorageFixtureScopeGuard($name);
179
}
180
181
/**
182
* Returns an integer seed to use when building unique identifiers (e.g.,
183
* non-colliding usernames). The seed is unstable and its value will change
184
* between test runs, so your tests must not rely on it.
185
*
186
* @return int A unique integer.
187
*/
188
protected function getNextObjectSeed() {
189
self::$storageFixtureObjectSeed += mt_rand(1, 100);
190
return self::$storageFixtureObjectSeed;
191
}
192
193
protected function generateNewTestUser() {
194
$seed = $this->getNextObjectSeed();
195
196
$user = id(new PhabricatorUser())
197
->setRealName(pht('Test User %s', $seed))
198
->setUserName("test{$seed}")
199
->setIsApproved(1);
200
201
$email = id(new PhabricatorUserEmail())
202
->setAddress("testuser{$seed}@example.com")
203
->setIsVerified(1);
204
205
$editor = new PhabricatorUserEditor();
206
$editor->setActor($user);
207
$editor->createNewUser($user, $email);
208
209
// When creating a new test user, we prefill their setting cache as empty.
210
// This is a little more efficient than doing a query to load the empty
211
// settings.
212
$user->attachRawCacheData(
213
array(
214
PhabricatorUserPreferencesCacheType::KEY_PREFERENCES => '[]',
215
));
216
217
return $user;
218
}
219
220
221
/**
222
* Throws unless tests are currently executing. This method can be used to
223
* guard code which is specific to unit tests and should not normally be
224
* reachable.
225
*
226
* If tests aren't currently being executed, throws an exception.
227
*/
228
public static function assertExecutingUnitTests() {
229
if (!self::$testsAreRunning) {
230
throw new Exception(
231
pht(
232
'Executing test code outside of test execution! '.
233
'This code path can only be run during unit tests.'));
234
}
235
}
236
237
protected function requireBinaryForTest($binary) {
238
if (!Filesystem::binaryExists($binary)) {
239
$this->assertSkipped(
240
pht(
241
'No binary "%s" found on this system, skipping test.',
242
$binary));
243
}
244
}
245
246
protected function newContentSource() {
247
return PhabricatorContentSource::newForSource(
248
PhabricatorUnitTestContentSource::SOURCECONST);
249
}
250
251
}
252
253