GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — develop ( b6562d...080777 )
by Stuart
08:15
created

Story::getNameForConsole()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 23
rs 8.7972
c 1
b 0
f 0
cc 4
eloc 12
nc 4
nop 0
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/PlayerLib
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\PlayerLib;
45
46
use Exception;
47
48
/**
49
 * Object that represents a single story
50
 *
51
 * @category  Libraries
52
 * @package   Storyplayer/StoryLib
53
 * @author    Stuart Herbert <[email protected]>
54
 * @copyright 2011-present Mediasift Ltd www.datasift.com
55
 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
56
 * @link      http://datasift.github.io/storyplayer
57
 */
58
class Story
59
{
60
    /**
61
     * the category that this story belongs to
62
     * @var string
63
     */
64
    protected $category;
65
66
    /**
67
     * the group that this story belongs to
68
     * @var array<string>
69
     */
70
    protected $group;
71
72
    /**
73
     * the name of this story
74
     * @var string
75
     */
76
    protected $name;
77
78
    /**
79
     * the function that provides hints about how this story changes
80
     * the state of the system or user
81
     *
82
     * @var callable
83
     */
84
    protected $hintsCallback;
85
86
    /**
87
     * the function that checks to see if the test should run at all,
88
     * or should be skipped
89
     *
90
     * @var array
91
     */
92
    protected $testCanRunCheckCallback = array();
93
94
    /**
95
     * the function that provides any story-specific setup work
96
     *
97
     * @var array
98
     */
99
    protected $testSetupCallback = array();
100
101
    /**
102
     * the function that provides any story-specific teardown action
103
     * @var array
104
     */
105
    protected $testTeardownCallback = array();
106
107
    /**
108
     * the function that provides any story-specific setup work that
109
     * happens before each phase of the test
110
     * @var array
111
     */
112
    protected $perPhaseSetupCallback = array();
113
114
    /**
115
     * the function that provides any story-specific teardown work that
116
     * happens at the end of each phase of the test
117
     * @var array
118
     */
119
    protected $perPhaseTeardownCallback = array();
120
121
    /**
122
     * the function that provides any story-specific setup work that
123
     * happens when we start a device
124
     * @var array
125
     */
126
    protected $deviceSetupCallback = array();
127
128
    /**
129
     * the function that provides any story-specific teardown work that
130
     * happens just before we stop a device
131
     * @var array
132
     */
133
    protected $deviceTeardownCallback = array();
134
135
    /**
136
     * the function that provides information about how this story has
137
     * changed the state of the system or user
138
     *
139
     * @var array
140
     */
141
    protected $roleChangesCallback = array();
142
143
    /**
144
     * the callback that dynamically determines in advance whether the
145
     * story actions should succeed or fail
146
     *
147
     * @var array
148
     */
149
    protected $preTestPredictionCallback = array();
150
151
    /**
152
     * the callback that dynamically determines afterwards whether or not
153
     * the story actions actually did succeed
154
     *
155
     * @var array
156
     */
157
    protected $reportTestResultsCallback = array();
158
159
    /**
160
     * the callback used to remember the state of the system *before*
161
     * the action occurs
162
     *
163
     * @var array
164
     */
165
    protected $preTestInspectionCallback = array();
166
167
    /**
168
     * the callback used to see if the action *did* change the state of
169
     * the system under test
170
     *
171
     * @var array
172
     */
173
    protected $postTestInspectionCallback = array();
174
175
    /**
176
     * the actions that execute the story on behalf of the user
177
     *
178
     * this is an array of callbacks.  Each callback is a single set of
179
     * actions to execute the story.  Each callback is an alternative way
180
     * to execute the story.  Each callback is meant to be equivalent; ie
181
     * they achieve the same thing, just in different ways.
182
     *
183
     * If any of the callbacks has different outcomes, then they belong
184
     * in separate user stories. NO EXCEPTIONS. This is a fundamental
185
     * assumption of Storyplayer; ignore it, and Storyplayer's no use to
186
     * you!
187
     *
188
     * @var array
189
     */
190
    protected $actionsCallbacks = array();
191
192
    /**
193
     * a list of the StoryTemplates that this story is based on. can be
194
     * empty
195
     *
196
     * @var array
197
     */
198
    protected $storyTemplates = array();
199
200
    /**
201
     * the parameters that get passed into virtual machines et al, and
202
     * which can be overridden on the command-line
203
     *
204
     * @var array
205
     */
206
    protected $params = array();
207
208
    /**
209
     * the environments that a story states it runs on
210
     *
211
     * by default, a story is allowed to run on all environments, *but*
212
     * if an environment's config sets 'mustBeWhitelisted' to TRUE, then
213
     * the story is only allowed to run on that environment if the story
214
     * declares itself safe to run
215
     *
216
     * we believe that this is the safest approach to handling those
217
     * stories that simply aren't safe to run absolutely everywhere
218
     */
219
    protected $whitelistedEnvironments = array();
220
221
    /**
222
     * the raw parser tree of this story, and of any templates that we
223
     * use
224
     *
225
     * @var array
226
     */
227
    protected $parserTrees = array();
228
229
    /**
230
     * the file that contains the story
231
     *
232
     * @var string
233
     */
234
    protected $storyFilename = '';
235
236
    /**
237
     * a list of the roles that the test environment must have defined,
238
     * otherwise we cannot run
239
     *
240
     * @var array
241
     */
242
    protected $requiredTestEnvRoles = array();
243
244
    /**
245
     * does the story want the test device kept open between phases?
246
     *
247
     * @var boolean
248
     */
249
    protected $persistDevice = false;
250
251
    /**
252
     * which version of Storyplayer is this test written for?
253
     *
254
     * you HAVE to set this in your story, otherwise we will skip your
255
     * story
256
     *
257
     * @var integer
258
     */
259
    protected $compatibleVersion = 1;
260
261
    /**
262
     * what happened to this story?
263
     * @var \DataSift\Storyplayer\PlayerLib\Story_Result
264
     */
265
    protected $storyResult = null;
266
267
    // ====================================================================
268
    //
269
    // Metadata about the story itself
270
    //
271
    // --------------------------------------------------------------------
272
273
    /**
274
     * which group of tests does this story belong to?
275
     *
276
     * @param  array|string $groupName
277
     *         the group to use
278
     * @return Story
279
     *         $this for fluent interface
280
     */
281
    public function inGroup($groupName)
282
    {
283
        if (!is_array($groupName)) {
284
            $parts = explode(" > ", $groupName);
285
            $groupName = $parts;
286
        }
287
        $this->setGroup($groupName);
288
289
        return $this;
290
    }
291
292
    /**
293
     * @return Story
294
     */
295
    public function called($userStoryText)
296
    {
297
        $this->setName($userStoryText);
298
299
        return $this;
300
    }
301
302
    /**
303
     * Get the category that this story belongs to
304
     *
305
     * Systems under test can grow to encompass hundreds, if not thousands
306
     * of user stories.  To make this manageable at scale, we break down
307
     * each user story like this:
308
     *
309
     * Name    : Starts as a free user with 10 USD in credit
310
     * Category: Billing User Stories
311
     * Group   : User States
312
     *
313
     * The 'name' is the summary text of the user story itself, which
314
     * should be no longer than a single sentence, please.
315
     *
316
     * The 'category' is the general group that the user story belongs to.
317
     * These are the top-level groups, such as 'Registration', 'Billing'
318
     * and so forth.
319
     *
320
     * The 'group' is the specific group _inside_ the category that the
321
     * user story belongs to.  The groups are specific to the category.
322
     *
323
     * @return string the category that this story belongs to
324
     */
325
    public function getCategory()
326
    {
327
        return $this->category;
328
    }
329
330
    /**
331
     * Set the category that this story belongs to
332
     *
333
     * Systems under test can grow to encompass hundreds, if not thousands
334
     * of user stories.  To make this manageable at scale, we break down
335
     * each user story like this:
336
     *
337
     * Name    : Starts as a free user with 10 USD in credit
338
     * Category: Billing User Stories
339
     * Group   : User States
340
     *
341
     * The 'name' is the summary text of the user story itself, which
342
     * should be no longer than a single sentence, please.
343
     *
344
     * The 'category' is the general group that the user story belongs to.
345
     * These are the top-level groups, such as 'Registration', 'Billing'
346
     * and so forth.
347
     *
348
     * The 'group' is the specific group _inside_ the category that the
349
     * user story belongs to.  The groups are specific to the category.
350
     *
351
     * @param  string $newCategory the category that this story belongs to
352
     * @return Story  $this
353
     */
354
    public function setCategory($newCategory)
355
    {
356
        $this->category = $newCategory;
357
        return $this;
358
    }
359
360
    /**
361
     * Get the group that this story belongs to
362
     *
363
     * Systems under test can grow to encompass hundreds, if not thousands
364
     * of user stories.  To make this manageable at scale, we break down
365
     * each user story like this:
366
     *
367
     * Name    : Starts as a free user with 10 USD in credit
368
     * Category: Billing User Stories
369
     * Group   : User States
370
     *
371
     * The 'name' is the summary text of the user story itself, which
372
     * should be no longer than a single sentence, please.
373
     *
374
     * The 'category' is the general group that the user story belongs to.
375
     * These are the top-level groups, such as 'Registration', 'Billing'
376
     * and so forth.
377
     *
378
     * The 'group' is the specific group _inside_ the category that the
379
     * user story belongs to.  The groups are specific to the category.
380
     *
381
     * @return array<string> the group that this story belongs to
382
     */
383
    public function getGroup()
384
    {
385
        return $this->group;
386
    }
387
388
    /**
389
     * return the story's group as a printable string
390
     *
391
     * @return string
392
     */
393
    public function getGroupAsString()
394
    {
395
        return implode(" > ", $this->group);
396
    }
397
398
    /**
399
     * Set the group that this story belongs to
400
     *
401
     * Systems under test can grow to encompass hundreds, if not thousands
402
     * of user stories.  To make this manageable at scale, we break down
403
     * each user story like this:
404
     *
405
     * Name    : Starts as a free user with 10 USD in credit
406
     * Category: Billing User Stories
407
     * Group   : User States
408
     *
409
     * The 'name' is the summary text of the user story itself, which
410
     * should be no longer than a single sentence, please.
411
     *
412
     * The 'category' is the general group that the user story belongs to.
413
     * These are the top-level groups, such as 'Registration', 'Billing'
414
     * and so forth.
415
     *
416
     * The 'group' is the specific group _inside_ the category that the
417
     * user story belongs to.  The groups are specific to the category.
418
     *
419
     * @param  array<string> $newGroup
420
     * @return Story  $this
421
     */
422
    public function setGroup($newGroup)
423
    {
424
        $this->group = $newGroup;
425
        return $this;
426
    }
427
428
    /**
429
     * Get the name of this story
430
     *
431
     * Systems under test can grow to encompass hundreds, if not thousands
432
     * of user stories.  To make this manageable at scale, we break down
433
     * each user story like this:
434
     *
435
     * Name    : Starts as a free user with 10 USD in credit
436
     * Category: Billing User Stories
437
     * Group   : User States
438
     *
439
     * The 'name' is the summary text of the user story itself, which
440
     * should be no longer than a single sentence, please.
441
     *
442
     * The 'category' is the general group that the user story belongs to.
443
     * These are the top-level groups, such as 'Registration', 'Billing'
444
     * and so forth.
445
     *
446
     * The 'group' is the specific group _inside_ the category that the
447
     * user story belongs to.  The groups are specific to the category.
448
     *
449
     * @return string the name of this story
450
     */
451
    public function getName()
452
    {
453
        return $this->name;
454
    }
455
456
    /**
457
     * Set the name of this story
458
     *
459
     * Systems under test can grow to encompass hundreds, if not thousands
460
     * of user stories.  To make this manageable at scale, we break down
461
     * each user story like this:
462
     *
463
     * Name    : Starts as a free user with 10 USD in credit
464
     * Category: Billing User Stories
465
     * Group   : User States
466
     *
467
     * The 'name' is the summary text of the user story itself, which
468
     * should be no longer than a single sentence, please.
469
     *
470
     * The 'category' is the general group that the user story belongs to.
471
     * These are the top-level groups, such as 'Registration', 'Billing'
472
     * and so forth.
473
     *
474
     * The 'group' is the specific group _inside_ the category that the
475
     * user story belongs to.  The groups are specific to the category.
476
     *
477
     * @param  string $newName the name of this story
478
     * @return Story  $this
479
     */
480
    public function setName($newName)
481
    {
482
        $this->name = $newName;
483
        return $this;
484
    }
485
486
    /**
487
     * get the name of this story, as is suitable for displaying in
488
     * the console
489
     *
490
     * @return string
491
     */
492
    public function getNameForConsole()
493
    {
494
        // this is for stories that made the effort to tell us
495
        // about themselves
496
        if (isset($this->category)) {
497
            return $this->getCategory() . ' > ' . $this->getGroupAsString() . ' > ' . $this->getName();
498
        }
499
500
        // this is for stories that have not gone to the trouble
501
        $cwd = getcwd() . DIRECTORY_SEPARATOR;
502
        $filename = $this->getStoryFilename();
503
504
        $filename = str_replace($cwd, '', $filename);
505
        $parts = explode(DIRECTORY_SEPARATOR, $filename);
506
        while (!empty($parts)) {
507
            $part = array_shift($parts);
508
            if (strtolower($part) === 'stories') {
509
                return implode(DIRECTORY_SEPARATOR, $parts);
510
            }
511
        }
512
513
        return $filename;
514
    }
515
516
    /**
517
     * Set the parameters for this story
518
     *
519
     * Parameters are a way of passing settings between Stories and
520
     * StoryTemplates.
521
     *
522
     * Order of precedence:
523
     *
524
     * 1) Story
525
     * 2) StoryTemplates (in 'basedOn()' order)
526
     *    (templates cannot override each other)
527
     *
528
     * @param array $defaults
529
     *        a list of the parameters for this story
530
     */
531
    public function setParams($defaults)
532
    {
533
        $this->params = $defaults;
534
        return $this;
535
    }
536
537
    /**
538
     * @return array
539
     */
540
    public function getParams()
541
    {
542
        // our return value
543
        $return = array();
544
545
        // populate it with the parameters from the templates
546
        foreach ($this->storyTemplates as $template) {
547
            // get any params from the template
548
            $params = $template->getParams();
549
550
            // any params already set have precedence
551
            foreach ($params as $key => $value) {
552
                // do we have a clash?
553
                if (!isset($return[$key])) {
554
                    $return[$key] = $value;
555
                }
556
            }
557
        }
558
559
        // now, merge in our own params
560
        //
561
        // our params always take precedence over the params set
562
        // in the template(s)
563
        $return = array_merge($this->params, $return);
564
565
        // all done
566
        return $return;
567
    }
568
569
    /**
570
     * @return void
571
     */
572
    public function determineStoryFilename()
573
    {
574
        $trace = debug_backtrace();
575
        $this->storyFilename = $trace[1]['file'];
576
    }
577
578
    /**
579
     * @return string
580
     */
581
    public function getStoryFilename()
582
    {
583
        return $this->storyFilename;
584
    }
585
586
    // ====================================================================
587
    //
588
    // Metadata for which states the story is valid for
589
    //
590
    // --------------------------------------------------------------------
591
592
    public function addValidRole($role)
0 ignored issues
show
Unused Code introduced by
The parameter $role is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
593
    {
594
        throw new E4xx_DeprecatedFeature('user roles in stories have been removed from Storyplayer v2.');
595
    }
596
597
    /**
598
     * Synonym for addValidRole()
599
     *
600
     * @see Story::addValidRole
601
     */
602
    public function andValidRole($role)
0 ignored issues
show
Unused Code introduced by
The parameter $role is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
603
    {
604
        throw new E4xx_DeprecatedFeature('user roles in stories have been removed from Storyplayer v2.');
605
    }
606
607
    public function hasRole($roleName)
0 ignored issues
show
Unused Code introduced by
The parameter $roleName is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
608
    {
609
        throw new E4xx_DeprecatedFeature('user roles in stories have been removed from Storyplayer v2.');
610
    }
611
612
    // ====================================================================
613
    //
614
    // Support for changing roles after a test has succeeded
615
    //
616
    // --------------------------------------------------------------------
617
618
    /**
619
     * get the role changes callback
620
     */
621
    public function getRoleChanges()
622
    {
623
        throw new E4xx_DeprecatedFeature('user roles in stories have been removed from Storyplayer v2.');
624
    }
625
626
    /**
627
     * has the role changes callback been set?
628
     */
629
    public function hasRoleChanges()
630
    {
631
        throw new E4xx_DeprecatedFeature('user roles in stories have been removed from Storyplayer v2.');
632
    }
633
634
    public function setRoleChanges($newCallback)
0 ignored issues
show
Unused Code introduced by
The parameter $newCallback is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
635
    {
636
        throw new E4xx_DeprecatedFeature('user roles in stories have been removed from Storyplayer v2.');
637
    }
638
639
    // ====================================================================
640
    //
641
    // Information about story templates
642
    //
643
    // --------------------------------------------------------------------
644
645
    /**
646
     * set up any templated methods from a predefined class
647
     *
648
     * @return Story
649
     */
650
    public function basedOn(StoryTemplate $tmpl)
651
    {
652
        // tell the template which story it is being used with
653
        $tmpl->setStory($this);
654
655
        $tmpl->hasTestCanRunCheck()         && $this->addTestCanRunCheck($tmpl->getTestCanRunCheck());
656
        $tmpl->hasTestSetup()               && $this->addTestSetup($tmpl->getTestSetup());
657
        $tmpl->hasTestTeardown()            && $this->addTestTeardown($tmpl->getTestTeardown());
658
        $tmpl->hasPerPhaseSetup()           && $this->addPerPhaseSetup($tmpl->getPerPhaseSetup());
659
        $tmpl->hasPerPhaseTeardown()        && $this->addPerPhaseTeardown($tmpl->getPerPhaseTeardown());
660
        $tmpl->hasDeviceSetup()             && $this->addDeviceSetup($tmpl->getDeviceSetup());
661
        $tmpl->hasDeviceTeardown()          && $this->addDeviceTeardown($tmpl->getDeviceTeardown());
662
        $tmpl->hasHints()                   && $this->addHints($tmpl->getHints());
663
        $tmpl->hasPreTestPrediction()       && $this->addPreTestPrediction($tmpl->getPreTestPrediction());
664
        $tmpl->hasPreTestInspection()       && $this->addPreTestInspection($tmpl->getPreTestInspection());
665
        $tmpl->hasAction()                  && $this->addAction($tmpl->getAction());
666
        $tmpl->hasPostTestInspection()      && $this->addPostTestInspection($tmpl->getPostTestInspection());
667
668
        // remember this template for future use
669
        $this->storyTemplates[] = $tmpl;
670
671
        // Return $this for a fluent interface
672
        return $this;
673
    }
674
675
    /**
676
     * get a list of the templates that this story is based on, in order
677
     * or precedence
678
     *
679
     * can be empty
680
     *
681
     * @return array<StoryTemplate>
682
     */
683
    public function getStoryTemplates()
684
    {
685
        return $this->storyTemplates;
686
    }
687
688
    // ==================================================================
689
    //
690
    // Information about dependencies
691
    //
692
    // ------------------------------------------------------------------
693
694
    /**
695
     * @return int
696
     */
697
    public function getRequiredStoryplayerVersion()
698
    {
699
        return $this->compatibleVersion;
700
    }
701
702
    /**
703
     * @return Story
704
     */
705
    public function requiresStoryplayerVersion($version)
706
    {
707
        $this->compatibleVersion = $version;
708
        return $this;
709
    }
710
711
    // ====================================================================
712
    //
713
    // Information about environments
714
    //
715
    // --------------------------------------------------------------------
716
717
    /**
718
     * @return Story
719
     */
720
    public function runsOn($envName)
721
    {
722
        $this->whitelistedEnvironments[$envName] = true;
723
        return $this;
724
    }
725
726
    /**
727
     * @return Story
728
     */
729
    public function andOn($envName)
730
    {
731
        $this->whitelistedEnvironments[$envName] = true;
732
        return $this;
733
    }
734
735
    /**
736
     * @return array
737
     */
738
    public function getWhitelistedEnvironments()
739
    {
740
        return $this->whitelistedEnvironments;
741
    }
742
743
    /**
744
     * @return void
745
     */
746
    public function requiresTestEnvironmentWithRoles($roles)
747
    {
748
        $this->requiredTestEnvRoles = $roles;
749
    }
750
751
    /**
752
     * @return array
753
     */
754
    public function getRequiredTestEnvironmentRoles()
755
    {
756
        return $this->requiredTestEnvRoles;
757
    }
758
759
    // ==================================================================
760
    //
761
    // Device support
762
    //
763
    // ------------------------------------------------------------------
764
765
    /**
766
     * does this story want to keep the web browser open between phases?
767
     *
768
     * @return boolean
769
     */
770
    public function getPersistDevice()
771
    {
772
        return $this->persistDevice;
773
    }
774
775
    /**
776
     * tell Storyplayer to keep the web browser open between test phases
777
     *
778
     * by default, we close the browser after every phase, to make sure
779
     * that the next phase always starts with a browser in a known state
780
     */
781
    public function setPersistDevice()
782
    {
783
        $this->persistDevice = true;
784
        return $this;
785
    }
786
787
    // ====================================================================
788
    //
789
    // Information about how check if the test should run at all
790
    //
791
    // --------------------------------------------------------------------
792
793
    /**
794
     * get the callback which allows the story to be skipped
795
     *
796
     * @return array
797
     */
798
    public function getTestCanRunCheck()
799
    {
800
        return $this->testCanRunCheckCallback;
801
    }
802
803
    /**
804
     * do we have a 'check story can run' callback?
805
     *
806
     * @return boolean true if the callback exists
807
     */
808
    public function hasTestCanRunCheck()
809
    {
810
        return count($this->testCanRunCheckCallback) > 0;
811
    }
812
813
    /**
814
     * @return void
815
     */
816
    public function addTestCanRunCheck($newCallback)
817
    {
818
        $this->testCanRunCheckCallback[] = $newCallback;
819
    }
820
821
    // ====================================================================
822
    //
823
    // Information about how to setup and teardown the test environment
824
    //
825
    // This is a feature from Storyplayer v1 that we've dropped in v2
826
    //
827
    // --------------------------------------------------------------------
828
829
    public function setTestEnvironmentSetup($newCallback)
0 ignored issues
show
Unused Code introduced by
The parameter $newCallback is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
830
    {
831
        throw new E4xx_DeprecatedFeature('TestEnvironmentSetup phase was removed from Storyplayer v2.');
832
    }
833
834
    public function addTestEnvironmentSetup($newCallback)
0 ignored issues
show
Unused Code introduced by
The parameter $newCallback is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
835
    {
836
        throw new E4xx_DeprecatedFeature('TestEnvironmentSetup phase was removed from Storyplayer v2.');
837
    }
838
839
    public function setTestEnvironmentTeardown($newCallback)
0 ignored issues
show
Unused Code introduced by
The parameter $newCallback is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
840
    {
841
        throw new E4xx_DeprecatedFeature('TestEnvironmentSetup phase was removed from Storyplayer v2.');
842
    }
843
844
    public function addTestEnvironmentTeardown($newCallback)
0 ignored issues
show
Unused Code introduced by
The parameter $newCallback is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
845
    {
846
        throw new E4xx_DeprecatedFeature('TestEnvironmentSetup phase was removed from Storyplayer v2.');
847
    }
848
849
    // ====================================================================
850
    //
851
    // Information about how to setup and teardown the test
852
    //
853
    // --------------------------------------------------------------------
854
855
    /**
856
     * get the callback for per-story setup work
857
     *
858
     * @return array
859
     */
860
    public function getTestSetup()
861
    {
862
        return $this->testSetupCallback;
863
    }
864
865
    /**
866
     * do we have a pre-story setup callback?
867
     *
868
     * @return boolean true if there is a pre-story setup callback
869
     */
870
    public function hasTestSetup()
871
    {
872
        return count($this->testSetupCallback) > 0;
873
    }
874
875
    public function addTestSetup($newCallback)
876
    {
877
        $this->testSetupCallback[] = $newCallback;
878
    }
879
880
    /**
881
     * get the callback for post-story teardown work
882
     *
883
     * @return array
884
     */
885
    public function getTestTeardown()
886
    {
887
        return $this->testTeardownCallback;
888
    }
889
890
    /**
891
     * do we have a post-story teardown callback?
892
     *
893
     * @return boolean true if there is a post-story teardown callback
894
     */
895
    public function hasTestTeardown()
896
    {
897
        return count($this->testTeardownCallback) > 0;
898
    }
899
900
    /**
901
     * @return void
902
     */
903
    public function addTestTeardown($newCallback)
904
    {
905
        $this->testTeardownCallback[] = $newCallback;
906
    }
907
908
    // ====================================================================
909
    //
910
    // Actions to happen before and after every phase of the test
911
    //
912
    // --------------------------------------------------------------------
913
914
    /**
915
     * get the callback for per-phase setup work
916
     *
917
     * @return array
918
     */
919
    public function getPerPhaseSetup()
920
    {
921
        return $this->perPhaseSetupCallback;
922
    }
923
924
    /**
925
     * do we have a per-phase setup callback?
926
     *
927
     * @return boolean true if there is a per-phase setup callback
928
     */
929
    public function hasPerPhaseSetup()
930
    {
931
        return count($this->perPhaseSetupCallback) > 0;
932
    }
933
934
    /**
935
     * @return void
936
     */
937
    public function addPerPhaseSetup($newCallback)
938
    {
939
        $this->perPhaseSetupCallback[] = $newCallback;
940
    }
941
942
    /**
943
     * get the callback for per-phase teardown work
944
     *
945
     * @return array
946
     */
947
    public function getPerPhaseTeardown()
948
    {
949
        return $this->perPhaseTeardownCallback;
950
    }
951
952
    /**
953
     * do we have a per-phase teardown callback?
954
     *
955
     * @return boolean true if there is a per-phase teardown callback
956
     */
957
    public function hasPerPhaseTeardown()
958
    {
959
        return count($this->perPhaseTeardownCallback) > 0;
960
    }
961
962
    /**
963
     * @return void
964
     */
965
    public function addPerPhaseTeardown($newCallback)
966
    {
967
        $this->perPhaseTeardownCallback[] = $newCallback;
968
    }
969
970
    // ====================================================================
971
    //
972
    // Actions to happen when starting and stopping test devices
973
    //
974
    // --------------------------------------------------------------------
975
976
    /**
977
     * get the callback for device setup work
978
     *
979
     * @return array
980
     */
981
    public function getDeviceSetup()
982
    {
983
        return $this->deviceSetupCallback;
984
    }
985
986
    /**
987
     * do we have a device setup callback?
988
     *
989
     * @return boolean true if there is a device setup callback
990
     */
991
    public function hasDeviceSetup()
992
    {
993
        return count($this->deviceSetupCallback) > 0;
994
    }
995
996
    /**
997
     * @return void
998
     */
999
    public function addDeviceSetup($newCallback)
1000
    {
1001
        $this->deviceSetupCallback[] = $newCallback;
1002
    }
1003
1004
    /**
1005
     * get the callback for device teardown work
1006
     *
1007
     * @return array
1008
     */
1009
    public function getDeviceTeardown()
1010
    {
1011
        return $this->deviceTeardownCallback;
1012
    }
1013
1014
    /**
1015
     * do we have a device teardown callback?
1016
     *
1017
     * @return boolean true if there is a device teardown callback
1018
     */
1019
    public function hasDeviceTeardown()
1020
    {
1021
        return count($this->deviceTeardownCallback) > 0;
1022
    }
1023
1024
    /**
1025
     * @return void
1026
     */
1027
    public function addDeviceTeardown($newCallback)
1028
    {
1029
        $this->deviceTeardownCallback[] = $newCallback;
1030
    }
1031
1032
    // ====================================================================
1033
    //
1034
    // Information about how the story changes the system
1035
    //
1036
    // --------------------------------------------------------------------
1037
1038
    /**
1039
     * get the hints callback
1040
     *
1041
     * @return callable
1042
     */
1043
    public function getHints()
1044
    {
1045
        return $this->hintsCallback;
1046
    }
1047
1048
    /**
1049
     * have any hints been set?
1050
     *
1051
     * @return boolean true if the callback has been set
1052
     */
1053
    public function hasHints()
1054
    {
1055
        return count($this->hintsCallback) > 0;
1056
    }
1057
1058
    /**
1059
     * @return void
1060
     */
1061
    public function setHints($newCallback)
1062
    {
1063
        $this->hintsCallback = array($newCallback);
1064
    }
1065
1066
    /**
1067
     * @return void
1068
     */
1069
    public function addHints($newCallback)
1070
    {
1071
        $this->hintsCallback[] = $newCallback;
1072
    }
1073
1074
    // ====================================================================
1075
    //
1076
    // Before and after tests
1077
    //
1078
    // --------------------------------------------------------------------
1079
1080
    /**
1081
     * get the callback to use to perform the preflight checks
1082
     *
1083
     * @return array
1084
     */
1085
    public function getPreTestPrediction()
1086
    {
1087
        return $this->preTestPredictionCallback;
1088
    }
1089
1090
    /**
1091
     * do we have a callback
1092
     * @return boolean [description]
1093
     */
1094
    public function hasPreTestPrediction()
1095
    {
1096
        return count($this->preTestPredictionCallback) > 0;
1097
    }
1098
1099
    /**
1100
     * @return void
1101
     */
1102
    public function addPreTestPrediction($newCallback)
1103
    {
1104
        $this->preTestPredictionCallback[] = $newCallback;
1105
    }
1106
1107
    // ====================================================================
1108
    //
1109
    // Checkpoint the relevant system state before actions occur
1110
    //
1111
    // --------------------------------------------------------------------
1112
1113
    /**
1114
     * get the callback to use to perform the preflight checkpoint
1115
     *
1116
     * @return array
1117
     */
1118
    public function getPreTestInspection()
1119
    {
1120
        return $this->preTestInspectionCallback;
1121
    }
1122
1123
    /**
1124
     * do we have a callback
1125
     * @return boolean [description]
1126
     */
1127
    public function hasPreTestInspection()
1128
    {
1129
        return count($this->preTestInspectionCallback) > 0;
1130
    }
1131
1132
    /**
1133
     * @return void
1134
     */
1135
    public function addPreTestInspection($newCallback)
1136
    {
1137
        $this->preTestInspectionCallback[] = $newCallback;
1138
    }
1139
1140
    // ====================================================================
1141
    //
1142
    // Add in the actions that make the story come to life
1143
    //
1144
    // --------------------------------------------------------------------
1145
1146
    /**
1147
     * @return void
1148
     */
1149
    public function addAction($newCallback)
1150
    {
1151
        $this->actionsCallbacks[] = $newCallback;
1152
    }
1153
1154
    /**
1155
     * @return void
1156
     */
1157
    public function addActions($newCallback)
1158
    {
1159
        $this->actionsCallbacks[] = $newCallback;
1160
    }
1161
1162
    /**
1163
     * pick one action at random, and return it to the caller
1164
     *
1165
     * @return callable
1166
     */
1167
    public function getOneAction()
1168
    {
1169
        // do we have any callbacks to pick from?
1170
        if (count($this->actionsCallbacks) == 0)
1171
        {
1172
            throw new E5xx_NoStoryActions($this->getName());
1173
        }
1174
1175
        // pick one story
1176
        $i = rand(0, count($this->actionsCallbacks) - 1);
1177
1178
        // return it to the caller
1179
        return $this->actionsCallbacks[$i];
1180
    }
1181
1182
    /**
1183
     * does this story have any actions?
1184
     *
1185
     * @return boolean true if this story has any actions
1186
     */
1187
    public function hasActions()
1188
    {
1189
        return (count($this->actionsCallbacks) > 0);
1190
    }
1191
1192
    // ====================================================================
1193
    //
1194
    // Determine whether the test passed or failed
1195
    //
1196
    // --------------------------------------------------------------------
1197
1198
    /**
1199
     * get the callback to use to work out the test results
1200
     *
1201
     * @return array
1202
     */
1203
    public function getPostTestInspection()
1204
    {
1205
        return $this->postTestInspectionCallback;
1206
    }
1207
1208
    /**
1209
     * @return bool
1210
     */
1211
    public function hasPostTestInspection()
1212
    {
1213
        return count($this->postTestInspectionCallback) > 0;
1214
    }
1215
1216
    /**
1217
     * @return void
1218
     */
1219
    public function addPostTestInspection($newCallback)
1220
    {
1221
        $this->postTestInspectionCallback[] = $newCallback;
1222
    }
1223
1224
    // ====================================================================
1225
    //
1226
    // Our default behaviour when the story object is instantiated
1227
    //
1228
    // --------------------------------------------------------------------
1229
1230
    /**
1231
     * @return void
1232
     */
1233
    public function setDefaultCallbacks()
1234
    {
1235
        // 1: test setup
1236
        if (!$this->hasTestSetup()) {
1237
            $this->addTestSetup(function(StoryTeller $st) {
1238
                $st->usingReporting()->reportNotRequired();
1239
            });
1240
        }
1241
1242
        // 2: pre-test prediction
1243
        if (!$this->hasPreTestPrediction()) {
1244
            $this->addPreTestPrediction(function(StoryTeller $st) {
1245
                $st->usingReporting()->reportShouldAlwaysSucceed();
1246
            });
1247
        }
1248
1249
        // 3: pre-test inspection
1250
        if (!$this->hasPreTestInspection()) {
1251
            $this->addPreTestInspection(function(StoryTeller $st) {
1252
                $st->usingReporting()->reportNotRequired();
1253
            });
1254
        }
1255
1256
        // 4: test action
1257
        //
1258
        // we set no default for this, because we do not want the action
1259
        // to be chosen by StoryPlayer
1260
        //
1261
        // (StoryPlayer chooses one action at random from the set of
1262
        // supplied actions)
1263
1264
        // 5: post-test inspection
1265
        //
1266
        // we set no default for this, because each story must provide
1267
        // this
1268
1269
        // 6: test tear down
1270
        if (!$this->hasTestTeardown()) {
1271
            $this->addTestTeardown(function(StoryTeller $st) {
1272
                $st->usingReporting()->reportNotRequired();
1273
            });
1274
        }
1275
1276
        // all done
1277
    }
1278
1279
    // ====================================================================
1280
    //
1281
    // Serialisation and other format convertors
1282
    //
1283
    // --------------------------------------------------------------------
1284
1285
    /**
1286
     * return a string representation of the story, for things like logging
1287
     * @return string
1288
     */
1289
    public function __toString()
1290
    {
1291
        return $this->getNameForConsole();
1292
    }
1293
1294
    // ==================================================================
1295
    //
1296
    // Dealing with the story result
1297
    //
1298
    // ------------------------------------------------------------------
1299
1300
    /**
1301
     * @return Story_Result
1302
     */
1303
    public function getResult()
1304
    {
1305
        if (!isset($this->storyResult)) {
1306
            $this->storyResult = new Story_Result($this);
1307
        }
1308
1309
        return $this->storyResult;
1310
    }
1311
}
1312