1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Copyright (c) 2011-present Mediasift Ltd |
5
|
|
|
* All rights reserved. |
6
|
|
|
* |
7
|
|
|
* Redistribution and use in source and binary forms, with or without |
8
|
|
|
* modification, are permitted provided that the following conditions |
9
|
|
|
* are met: |
10
|
|
|
* |
11
|
|
|
* * Redistributions of source code must retain the above copyright |
12
|
|
|
* notice, this list of conditions and the following disclaimer. |
13
|
|
|
* |
14
|
|
|
* * Redistributions in binary form must reproduce the above copyright |
15
|
|
|
* notice, this list of conditions and the following disclaimer in |
16
|
|
|
* the documentation and/or other materials provided with the |
17
|
|
|
* distribution. |
18
|
|
|
* |
19
|
|
|
* * Neither the names of the copyright holders nor the names of his |
20
|
|
|
* contributors may be used to endorse or promote products derived |
21
|
|
|
* from this software without specific prior written permission. |
22
|
|
|
* |
23
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
24
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
25
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
26
|
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
27
|
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
28
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
29
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
30
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
31
|
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
32
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
33
|
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
34
|
|
|
* POSSIBILITY OF SUCH DAMAGE. |
35
|
|
|
* |
36
|
|
|
* @category Libraries |
37
|
|
|
* @package Storyplayer/Cli |
38
|
|
|
* @author Stuart Herbert <[email protected]> |
39
|
|
|
* @copyright 2011-present Mediasift Ltd www.datasift.com |
40
|
|
|
* @license http://www.opensource.org/licenses/bsd-license.php BSD License |
41
|
|
|
* @link http://datasift.github.io/storyplayer |
42
|
|
|
*/ |
43
|
|
|
|
44
|
|
|
namespace DataSift\Storyplayer\Cli; |
45
|
|
|
|
46
|
|
|
use Exception; |
47
|
|
|
use RecursiveDirectoryIterator; |
48
|
|
|
use RecursiveIteratorIterator; |
49
|
|
|
use RecursiveRegexIterator; |
50
|
|
|
use RegexIterator; |
51
|
|
|
use stdClass; |
52
|
|
|
use Phix_Project\CliEngine; |
53
|
|
|
use Phix_Project\CliEngine\CliCommand; |
54
|
|
|
use Phix_Project\ExceptionsLib1\Legacy_ErrorHandler; |
55
|
|
|
use Phix_Project\ExceptionsLib1\Legacy_ErrorException; |
56
|
|
|
use DataSift\Stone\ConfigLib\E5xx_ConfigFileNotFound; |
57
|
|
|
use DataSift\Stone\ConfigLib\E5xx_InvalidConfigFile; |
58
|
|
|
use DataSift\Storyplayer\PlayerLib\E4xx_NoSuchReport; |
59
|
|
|
use DataSift\Storyplayer\PlayerLib\PhaseGroup_Player; |
60
|
|
|
use DataSift\Storyplayer\PlayerLib\StoryTeller; |
61
|
|
|
use DataSift\Storyplayer\PlayerLib\Story_Player; |
62
|
|
|
use DataSift\Storyplayer\PlayerLib\TestEnvironment_Player; |
63
|
|
|
use DataSift\Storyplayer\Console\DevModeConsole; |
64
|
|
|
use DataSift\Storyplayer\Injectables; |
65
|
|
|
|
66
|
|
|
use DataSift\Storyplayer\Cli\Feature\ActiveConfigSupport; |
67
|
|
|
use DataSift\Storyplayer\Cli\Feature\ColorSupport; |
68
|
|
|
use DataSift\Storyplayer\Cli\Feature\ConsoleSupport; |
69
|
|
|
use DataSift\Storyplayer\Cli\Feature\DefinesSupport; |
70
|
|
|
use DataSift\Storyplayer\Cli\Feature\DeviceSupport; |
71
|
|
|
use DataSift\Storyplayer\Cli\Feature\LocalhostSupport; |
72
|
|
|
use DataSift\Storyplayer\Cli\Feature\PersistDeviceSupport; |
73
|
|
|
use DataSift\Storyplayer\Cli\Feature\PersistProcessesSupport; |
74
|
|
|
use DataSift\Storyplayer\Cli\Feature\PersistReuseTargetSupport; |
75
|
|
|
use DataSift\Storyplayer\Cli\Feature\PhaseLoaderSupport; |
76
|
|
|
use DataSift\Storyplayer\Cli\Feature\ProseLoaderSupport; |
77
|
|
|
use DataSift\Storyplayer\Cli\Feature\SystemUnderTestSupport; |
78
|
|
|
use DataSift\Storyplayer\Cli\Feature\TestEnvironmentSupport; |
79
|
|
|
use DataSift\Storyplayer\Cli\Feature\VerboseSupport; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* A command to build a test environment |
83
|
|
|
* |
84
|
|
|
* @category Libraries |
85
|
|
|
* @package Storyplayer/Cli |
86
|
|
|
* @author Stuart Herbert <[email protected]> |
87
|
|
|
* @copyright 2011-present Mediasift Ltd www.datasift.com |
88
|
|
|
* @license http://www.opensource.org/licenses/bsd-license.php BSD License |
89
|
|
|
* @link http://datasift.github.io/storyplayer |
90
|
|
|
*/ |
91
|
|
|
class BuildTestEnvironment_Command extends BaseCommand implements CliSignalHandler |
92
|
|
|
{ |
93
|
|
|
// we need to track this for handling CTRL-C |
94
|
|
|
protected $st; |
95
|
|
|
|
96
|
|
|
// we track this for convenience |
97
|
|
|
protected $output; |
98
|
|
|
|
99
|
|
|
// our list of players to execute |
100
|
|
|
protected $playerList; |
101
|
|
|
|
102
|
|
|
// our injected data / services |
103
|
|
|
// needed for when user presses CTRL+C |
104
|
|
|
protected $injectables; |
105
|
|
|
|
106
|
|
View Code Duplication |
public function __construct($injectables) |
107
|
|
|
{ |
108
|
|
|
parent::__construct($injectables); |
109
|
|
|
|
110
|
|
|
// define the command |
111
|
|
|
$this->setName('build-test-environment'); |
112
|
|
|
$this->setShortDescription('build a test environment'); |
113
|
|
|
$this->setLongDescription( |
114
|
|
|
"Use this command to build a test environment. Handy for testing that your test environment " |
115
|
|
|
."provisions correctly." |
116
|
|
|
.PHP_EOL |
117
|
|
|
); |
118
|
|
|
|
119
|
|
|
// add in the features that this command relies on |
120
|
|
|
$this->addFeature(new Feature_VerboseSupport); |
121
|
|
|
$this->addFeature(new Feature_ConsoleSupport); |
122
|
|
|
$this->addFeature(new Feature_ColorSupport); |
123
|
|
|
$this->addFeature(new Feature_TestEnvironmentConfigSupport); |
124
|
|
|
$this->addFeature(new Feature_LocalhostSupport); |
125
|
|
|
$this->addFeature(new Feature_ActiveConfigSupport); |
126
|
|
|
$this->addFeature(new Feature_DefinesSupport); |
127
|
|
|
$this->addFeature(new Feature_PhaseLoaderSupport); |
128
|
|
|
$this->addFeature(new Feature_ProseLoaderSupport); |
129
|
|
|
$this->addFeature(new Feature_PersistReuseTargetSupport); |
130
|
|
|
|
131
|
|
|
// now setup all of the switches that we support |
132
|
|
|
$this->addFeatureSwitches(); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* |
137
|
|
|
* @param CliEngine $engine |
138
|
|
|
* @param array $params |
139
|
|
|
* @param Injectables|null $injectables |
140
|
|
|
* @return integer |
141
|
|
|
*/ |
142
|
|
View Code Duplication |
public function processCommand(CliEngine $engine, $params = array(), $injectables = null) |
143
|
|
|
{ |
144
|
|
|
// we need to wrap our code to catch old-style PHP errors |
145
|
|
|
$legacyHandler = new Legacy_ErrorHandler(); |
146
|
|
|
|
147
|
|
|
// run our code |
148
|
|
|
try { |
149
|
|
|
$returnCode = $legacyHandler->run([$this, 'processInsideLegacyHandler'], [$engine, $params, $injectables]); |
150
|
|
|
return $returnCode; |
151
|
|
|
} |
152
|
|
|
catch (Exception $e) { |
153
|
|
|
$injectables->output->logCliError($e->getMessage()); |
154
|
|
|
$engine->options->dev = true; |
155
|
|
|
if (isset($engine->options->dev) && $engine->options->dev) { |
156
|
|
|
$injectables->output->logCliError("Stack trace is:\n\n" . $e->getTraceAsString()); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
// stop the browser if available |
160
|
|
|
if (isset($this->st)) { |
161
|
|
|
$this->st->stopDevice(); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
// tell the calling process that things did not end well |
165
|
|
|
exit(1); |
|
|
|
|
166
|
|
|
} |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
public function processInsideLegacyHandler(CliEngine $engine, $params = array(), $injectables = null) |
170
|
|
|
{ |
171
|
|
|
// process the common functionality |
172
|
|
|
$this->initFeaturesBeforeModulesAvailable($engine); |
173
|
|
|
|
174
|
|
|
// now it is safe to create our shorthand |
175
|
|
|
$runtimeConfig = $injectables->getRuntimeConfig(); |
176
|
|
|
$runtimeConfigManager = $injectables->getRuntimeConfigManager(); |
177
|
|
|
$output = $injectables->output; |
178
|
|
|
|
179
|
|
|
// save the output for use in other methods |
180
|
|
|
$this->output = $output; |
181
|
|
|
|
182
|
|
|
// at this point, all of the services / data held in $injectables |
183
|
|
|
// has been initialised and is ready for use |
184
|
|
|
// |
185
|
|
|
// what's left is the stuff that needs initialising in phases |
186
|
|
|
// or $st |
187
|
|
|
|
188
|
|
|
// create a new StoryTeller object |
189
|
|
|
$st = new StoryTeller($injectables); |
190
|
|
|
|
191
|
|
|
// remember our $st object, as we'll need it for our |
192
|
|
|
// shutdown function |
193
|
|
|
$this->st = $st; |
194
|
|
|
|
195
|
|
|
// now that we have $st, we can initialise any feature that |
196
|
|
|
// wants to use our modules |
197
|
|
|
$this->initFeaturesAfterModulesAvailable($st, $engine, $injectables); |
198
|
|
|
|
199
|
|
|
// install signal handling, now that $this->st is defined |
200
|
|
|
// |
201
|
|
|
// we wouldn't want signal handling called out of order :) |
202
|
|
|
$this->initSignalHandling($injectables); |
203
|
|
|
|
204
|
|
|
// build our list of players to run |
205
|
|
|
$this->initPlayerList($injectables); |
206
|
|
|
|
207
|
|
|
// let's keep score :) |
208
|
|
|
$startTime = microtime(true); |
209
|
|
|
|
210
|
|
|
// and we're ready to tell the world that we're here |
211
|
|
|
$output->startStoryplayer( |
212
|
|
|
$engine->getAppVersion(), |
213
|
|
|
$engine->getAppUrl(), |
214
|
|
|
$engine->getAppCopyright(), |
215
|
|
|
$engine->getAppLicense() |
216
|
|
|
); |
217
|
|
|
|
218
|
|
|
// $this->playerList contains one or more things to play |
219
|
|
|
// |
220
|
|
|
// let's play each of them in order |
221
|
|
|
foreach ($this->playerList as $player) |
222
|
|
|
{ |
223
|
|
|
// execute each player in turn |
224
|
|
|
// |
225
|
|
|
// they may also have their own list of nested players |
226
|
|
|
$player->play($st, $injectables); |
227
|
|
|
|
228
|
|
|
// make sure the test device has been stopped |
229
|
|
|
// (it may have been persisted by the story) |
230
|
|
|
// |
231
|
|
|
// we do not allow the test device to persist between |
232
|
|
|
// top-level players |
233
|
|
|
$st->stopDevice(); |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
// write out any changed runtime config to disk |
237
|
|
|
$runtimeConfigManager->saveRuntimeConfig($runtimeConfig, $output); |
238
|
|
|
|
239
|
|
|
// how long did that take? |
240
|
|
|
$duration = microtime(true) - $startTime; |
241
|
|
|
|
242
|
|
|
// tell the output plugins that we're all done |
243
|
|
|
$output->endStoryplayer($duration); |
244
|
|
|
|
245
|
|
|
// all done |
246
|
|
|
return 0; |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
// ================================================================== |
250
|
|
|
// |
251
|
|
|
// the individual initX() methods |
252
|
|
|
// |
253
|
|
|
// these are processed *after* the objects defined in the |
254
|
|
|
// CommonFunctionalitySupport trait have been initialised |
255
|
|
|
// |
256
|
|
|
// ------------------------------------------------------------------ |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* |
260
|
|
|
* @param Injectables $injectables |
261
|
|
|
* @return void |
262
|
|
|
*/ |
263
|
|
View Code Duplication |
protected function initSignalHandling(Injectables $injectables) |
264
|
|
|
{ |
265
|
|
|
// we need to remember the injectables, for when we handle CTRL+C |
266
|
|
|
$this->injectables = $injectables; |
267
|
|
|
|
268
|
|
|
// setup signal handling |
269
|
|
|
pcntl_signal(SIGTERM, array($this, 'sigtermHandler')); |
270
|
|
|
pcntl_signal(SIGINT , array($this, 'sigtermHandler')); |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* |
275
|
|
|
* @param Injectables $injectables |
276
|
|
|
* @return void |
277
|
|
|
*/ |
278
|
|
|
protected function initPlayerList(Injectables $injectables) |
279
|
|
|
{ |
280
|
|
|
// we just want a TestEnvironment |
281
|
|
|
$this->playerList[] = new TestEnvironment_Player([], $injectables); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
// ================================================================== |
285
|
|
|
// |
286
|
|
|
// SIGNAL handling |
287
|
|
|
// |
288
|
|
|
// ------------------------------------------------------------------ |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* |
292
|
|
|
* @param integer $signo |
293
|
|
|
* @return void |
294
|
|
|
*/ |
295
|
|
View Code Duplication |
public function sigtermHandler($signo) |
296
|
|
|
{ |
297
|
|
|
// tell the user what is happening |
298
|
|
|
echo PHP_EOL; |
299
|
|
|
echo "============================================================" . PHP_EOL; |
300
|
|
|
echo "USER ABORT!!" . PHP_EOL; |
301
|
|
|
|
302
|
|
|
// do we skip destroying the test environment? |
303
|
|
|
if ($this->st->getPersistTestEnvironment()) { |
304
|
|
|
echo PHP_EOL . "* Warning: NOT destroying test environment" . PHP_EOL |
305
|
|
|
. " --reuse-target flag is set" . PHP_EOL; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
// cleanup |
309
|
|
|
echo PHP_EOL . "Cleaning up: "; |
310
|
|
|
$phasesPlayer = new PhaseGroup_Player(); |
311
|
|
|
$phasesPlayer->playPhases( |
312
|
|
|
"user abort", |
313
|
|
|
$this->st, |
314
|
|
|
$this->injectables, |
315
|
|
|
$this->injectables->activeConfig->getData('storyplayer.phases.userAbort'), |
316
|
|
|
null |
317
|
|
|
); |
318
|
|
|
|
319
|
|
|
echo " done" . PHP_EOL . "============================================================" . PHP_EOL . PHP_EOL; |
320
|
|
|
|
321
|
|
|
// force a clean shutdown |
322
|
|
|
exit(1); |
|
|
|
|
323
|
|
|
} |
324
|
|
|
} |
325
|
|
|
|
An exit expression should only be used in rare cases. For example, if you write a short command line script.
In most cases however, using an
exit
expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.