1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Magium\Util\Api\Clairvoyant; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use Magium\AbstractConfigurableElement; |
7
|
|
|
use Magium\AbstractTestCase; |
8
|
|
|
use Magium\Util\Api\ApiConfiguration; |
9
|
|
|
use Magium\Util\Api\Request; |
10
|
|
|
use Magium\Util\Configuration\ConfigurationCollector\DefaultPropertyCollector; |
11
|
|
|
use Magium\Util\Configuration\StandardConfigurationProvider; |
12
|
|
|
use Magium\Util\Log\Logger; |
13
|
|
|
use PHPUnit_Framework_AssertionFailedError; |
14
|
|
|
use PHPUnit_Framework_Test; |
15
|
|
|
use PHPUnit_Framework_TestSuite; |
16
|
|
|
use RandomLib\Factory; |
17
|
|
|
use SecurityLib\Strength; |
18
|
|
|
use Zend\Log\Writer\WriterInterface; |
19
|
|
|
|
20
|
|
|
class Clairvoyant extends AbstractConfigurableElement implements WriterInterface, \PHPUnit_Framework_TestListener |
21
|
|
|
{ |
22
|
|
|
const TYPE_TEST_RESULT = 'test-result'; |
23
|
|
|
const TYPE_TEST_STATUS = 'test-status'; |
24
|
|
|
const TYPE_TEST_CHECKPOINT = 'test-checkpoint'; |
25
|
|
|
|
26
|
|
|
const TEST_RESULT_PASSED = 'passed'; |
27
|
|
|
const TEST_RESULT_ERROR = 'error'; |
28
|
|
|
const TEST_RESULT_FAILED = 'failed'; |
29
|
|
|
const TEST_RESULT_SKIPPED = 'skipped'; |
30
|
|
|
const TEST_RESULT_RISKY = 'risky'; |
31
|
|
|
const TEST_RESULT_INCOMPLETE = 'incomplete'; |
32
|
|
|
|
33
|
|
|
const TEST_STATUS_STARTED = 'started'; |
34
|
|
|
const TEST_STATUS_COMPLETED = 'completed'; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* This provides the Clairvoyant-based project ID. It must be retrieved from the MagiumLib.com website. If |
38
|
|
|
* Clairvoyant is enabled and this project ID is missing an exception will be thrown. |
39
|
|
|
* |
40
|
|
|
* @var string |
41
|
|
|
*/ |
42
|
|
|
|
43
|
|
|
public $projectId; |
44
|
|
|
public $enabled = null; |
45
|
|
|
|
46
|
|
|
protected $testName; |
47
|
|
|
protected $logger; |
48
|
|
|
protected $testTitle; |
49
|
|
|
protected $testDescription; |
50
|
|
|
protected $capability; |
51
|
|
|
protected $sessionId; |
52
|
|
|
/** |
53
|
|
|
* @var Request |
54
|
|
|
*/ |
55
|
|
|
protected $request; |
56
|
|
|
protected $testId; |
57
|
|
|
protected static $testRunId; |
58
|
|
|
protected $events = []; |
59
|
|
|
protected $apiConfiguration; |
60
|
|
|
protected $testResult; |
61
|
|
|
protected $characteristics = []; |
62
|
|
|
|
63
|
|
|
public function __construct( |
64
|
|
|
StandardConfigurationProvider $configurationProvider, |
65
|
|
|
DefaultPropertyCollector $collector, |
66
|
|
|
ApiConfiguration $apiConfiguration, |
67
|
|
|
Logger $logger) |
68
|
|
|
{ |
69
|
|
|
parent::__construct($configurationProvider, $collector); |
70
|
|
|
$this->apiConfiguration = $apiConfiguration; |
71
|
|
|
$this->logger = $logger; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* @return bool |
77
|
|
|
*/ |
78
|
|
|
public function getEnabled() |
79
|
|
|
{ |
80
|
|
|
return $this->enabled; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @param bool $enabled |
85
|
|
|
*/ |
86
|
|
|
public function setEnabled($enabled) |
87
|
|
|
{ |
88
|
|
|
$this->enabled = $enabled; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* @return mixed |
93
|
|
|
*/ |
94
|
|
|
public function getProjectId() |
95
|
|
|
{ |
96
|
|
|
return $this->projectId; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* @param mixed $projectId |
101
|
|
|
*/ |
102
|
|
|
public function setProjectId($projectId) |
103
|
|
|
{ |
104
|
|
|
$this->projectId = $projectId; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
|
108
|
|
|
|
109
|
|
|
public function setApiRequest(Request $request) |
110
|
|
|
{ |
111
|
|
|
$this->request = $request; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Provide a test-run-wide ID that can be used to tie individual test runs together |
116
|
|
|
* |
117
|
|
|
* @param string $id |
118
|
|
|
*/ |
119
|
|
|
|
120
|
|
|
public static function setTestRunId($id = null) |
121
|
|
|
{ |
122
|
|
|
if ($id === null) { |
123
|
|
|
|
124
|
|
|
$factory = new Factory(); |
125
|
|
|
$generator = $factory->getGenerator(new Strength(Strength::MEDIUM)); |
126
|
|
|
$id = $generator->generateString(64); |
127
|
|
|
} |
128
|
|
|
self::$testRunId = $id; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
public function reset() |
132
|
|
|
{ |
133
|
|
|
$this->testDescription |
134
|
|
|
= $this->testName |
135
|
|
|
= $this->testTitle = null; |
136
|
|
|
$this->testResult = self::TEST_RESULT_PASSED; |
137
|
|
|
$this->characteristics = []; |
138
|
|
|
$this->events = []; |
139
|
|
|
|
140
|
|
|
$factory = new Factory(); |
141
|
|
|
$generator = $factory->getGenerator(new Strength(Strength::MEDIUM)); |
142
|
|
|
$this->testId = $generator->generateString(64); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
public function markKeyCheckpoint($checkpoint) |
146
|
|
|
{ |
147
|
|
|
$this->write([ |
148
|
|
|
'message' => $checkpoint, |
149
|
|
|
'extra' => [ |
150
|
|
|
'type' => self::TYPE_TEST_CHECKPOINT, |
151
|
|
|
'value' => $checkpoint |
152
|
|
|
] |
153
|
|
|
]); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
public function setSessionId($sessionId) |
157
|
|
|
{ |
158
|
|
|
$this->sessionId = $sessionId; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* @param mixed $capability |
163
|
|
|
*/ |
164
|
|
|
public function setCapability($capability) |
165
|
|
|
{ |
166
|
|
|
$this->capability = $capability; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* @param mixed $testDescription |
171
|
|
|
*/ |
172
|
|
|
public function setTestDescription($testDescription) |
173
|
|
|
{ |
174
|
|
|
$this->testDescription = $testDescription; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* @param mixed $testTitle |
179
|
|
|
*/ |
180
|
|
|
public function setTestTitle($testTitle) |
181
|
|
|
{ |
182
|
|
|
$this->testTitle = $testTitle; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
public function addFilter($filter) |
186
|
|
|
{ |
187
|
|
|
// Ignore - The filter is on the server side |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
public function setFormatter($formatter) |
191
|
|
|
{ |
192
|
|
|
// Ignore |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
public function write(array $event) |
196
|
|
|
{ |
197
|
|
|
if (isset($event['extra']['type']) && $event['extra']['type'] == 'characteristic') { |
198
|
|
|
$this->characteristics[$event['extra']['characteristic']] = $event['extra']['value']; |
199
|
|
|
return; |
200
|
|
|
} |
201
|
|
|
if (isset($event['extra'][self::TYPE_TEST_RESULT])) { |
202
|
|
|
$this->testResult = $event['extra'][self::TYPE_TEST_RESULT]; |
203
|
|
|
} |
204
|
|
|
$event['microtime'] = microtime(true); |
205
|
|
|
$this->events[] = $event; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
public function shutdown() |
209
|
|
|
{ |
210
|
|
|
// Ignored. Test data is pushed at the end of each test run. |
211
|
|
|
} |
212
|
|
|
|
213
|
|
View Code Duplication |
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) |
|
|
|
|
214
|
|
|
{ |
215
|
|
|
$this->testResult = self::TEST_RESULT_ERROR; |
216
|
|
|
$this->write([ |
217
|
|
|
'message' => $e->getMessage(), |
218
|
|
|
'extra' => [ |
219
|
|
|
'type' => self::TYPE_TEST_RESULT, |
220
|
|
|
'value' => self::TEST_RESULT_ERROR |
221
|
|
|
] |
222
|
|
|
]); |
223
|
|
|
} |
224
|
|
|
|
225
|
|
View Code Duplication |
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) |
|
|
|
|
226
|
|
|
{ |
227
|
|
|
$this->testResult = self::TEST_RESULT_FAILED; |
228
|
|
|
$this->write([ |
229
|
|
|
'message' => $e->getMessage(), |
230
|
|
|
'extra' => [ |
231
|
|
|
'type' => self::TYPE_TEST_RESULT, |
232
|
|
|
'value' => self::TEST_RESULT_FAILED |
233
|
|
|
] |
234
|
|
|
]); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
View Code Duplication |
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) |
|
|
|
|
238
|
|
|
{ |
239
|
|
|
$this->testResult = self::TEST_RESULT_INCOMPLETE; |
240
|
|
|
$this->write([ |
241
|
|
|
'message' => $e->getMessage(), |
242
|
|
|
'extra' => [ |
243
|
|
|
'type' => self::TYPE_TEST_RESULT, |
244
|
|
|
'value' => self::TEST_RESULT_INCOMPLETE |
245
|
|
|
] |
246
|
|
|
]); |
247
|
|
|
} |
248
|
|
|
|
249
|
|
View Code Duplication |
public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) |
|
|
|
|
250
|
|
|
{ |
251
|
|
|
$this->testResult = self::TEST_RESULT_RISKY; |
252
|
|
|
$this->write([ |
253
|
|
|
'message' => $e->getMessage(), |
254
|
|
|
'extra' => [ |
255
|
|
|
'type' => self::TYPE_TEST_RESULT, |
256
|
|
|
'value' => self::TEST_RESULT_RISKY |
257
|
|
|
] |
258
|
|
|
]); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
View Code Duplication |
public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) |
|
|
|
|
262
|
|
|
{ |
263
|
|
|
$this->testResult = self::TEST_RESULT_SKIPPED; |
264
|
|
|
$this->write([ |
265
|
|
|
'message' => $e->getMessage(), |
266
|
|
|
'extra' => [ |
267
|
|
|
'type' => self::TYPE_TEST_RESULT, |
268
|
|
|
'value' => self::TEST_RESULT_SKIPPED |
269
|
|
|
] |
270
|
|
|
]); |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
public function startTestSuite(PHPUnit_Framework_TestSuite $suite) |
274
|
|
|
{ |
275
|
|
|
$this->reset(); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
public function endTestSuite(PHPUnit_Framework_TestSuite $suite) |
279
|
|
|
{ |
280
|
|
|
$this->send(); // Just in case. |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
public function startTest(PHPUnit_Framework_Test $test) |
284
|
|
|
{ |
285
|
|
|
$this->reset(); |
286
|
|
|
if ($test instanceof AbstractTestCase) { |
287
|
|
|
$this->testName = $test->getName(); |
288
|
|
|
} |
289
|
|
|
$this->write([ |
290
|
|
|
'message' => 'Test started', |
291
|
|
|
'extra' => [ |
292
|
|
|
'type' => self::TYPE_TEST_STATUS, |
293
|
|
|
'value' => self::TEST_STATUS_STARTED, |
294
|
|
|
'class' => get_class($test), |
295
|
|
|
'name' => $test->getName() |
|
|
|
|
296
|
|
|
] |
297
|
|
|
]); |
298
|
|
|
} |
299
|
|
|
|
300
|
|
View Code Duplication |
public function endTest(PHPUnit_Framework_Test $test, $time) |
|
|
|
|
301
|
|
|
{ |
302
|
|
|
$this->write([ |
303
|
|
|
'message' => 'Test completed', |
304
|
|
|
'extra' => [ |
305
|
|
|
'type' => self::TYPE_TEST_STATUS, |
306
|
|
|
'value' => self::TEST_STATUS_COMPLETED, |
307
|
|
|
] |
308
|
|
|
]); |
309
|
|
|
$this->send(); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
public function send() |
313
|
|
|
{ |
314
|
|
|
|
315
|
|
|
if (!$this->enabled === null) { |
316
|
|
|
$this->enabled = $this->apiConfiguration->getEnabled(); |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
if (!empty($this->events) && $this->enabled) { |
320
|
|
|
if (!$this->getProjectId()) { |
321
|
|
|
throw new MissingProjectIdException('Missing the project ID. You either need to disable Clairvoyant or get a project ID from http://magiumlib.com/'); |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
$this->write([ |
325
|
|
|
'message' => 'Final test result', |
326
|
|
|
'extra' => [ |
327
|
|
|
'type' => self::TYPE_TEST_RESULT, |
328
|
|
|
'value' => $this->testResult, |
329
|
|
|
] |
330
|
|
|
]); |
331
|
|
|
$payload = [ |
332
|
|
|
'title' => $this->testTitle, |
333
|
|
|
'description' => $this->testDescription, |
334
|
|
|
'id' => $this->testId, |
335
|
|
|
'session_id' => $this->sessionId, |
336
|
|
|
'events' => $this->events, |
337
|
|
|
'version' => '1', |
338
|
|
|
'project_id' => $this->projectId, |
339
|
|
|
'invoked_test' => $this->logger->getInvokedTest(), |
340
|
|
|
'characteristics' => $this->characteristics |
341
|
|
|
]; |
342
|
|
|
|
343
|
|
|
if (self::$testRunId !== null) { |
344
|
|
|
$payload['test_run_id'] = self::$testRunId; |
345
|
|
|
} |
346
|
|
|
$this->request->push('/clairvoyant/api/ingest', $payload); |
347
|
|
|
} |
348
|
|
|
$this->events = []; |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.