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
Pull Request — master (#186)
by
unknown
11:15 queued 05:03
created

Story::generateName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 6
rs 9.4285
c 0
b 0
f 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
     * Set the group name as the file system path of the directory where the test is located
294
     *
295
     * @return Story
296
     *         $this for fluent interface
297
     */
298
    public function generateGroup()
299
    {
300
        // Get the directory path
301
        $path = ltrim(
302
            str_replace(getcwd(), '', $this->getStoryFilename()),
303
            DIRECTORY_SEPARATOR
304
        );
305
        $directories = explode(DIRECTORY_SEPARATOR, $path);
306
307
        // Remove the base directory at the beginning and story file at the end of the path
308
        $groupName = array_splice($directories, 1, count($directories) - 2);
309
        $this->setGroup($groupName);
310
311
        return $this;
312
    }
313
314
    /**
315
     * @return Story
316
     */
317
    public function called($userStoryText)
318
    {
319
        $this->setName($userStoryText);
320
321
        return $this;
322
    }
323
324
    /**
325
     * Set the name as the filename of the test
326
     *
327
     * @return Story
328
     *         $this for fluent interface
329
     */
330
    public function generateName()
331
    {
332
        $this->setName(basename($this->getStoryFilename()));
333
334
        return $this;
335
    }
336
337
    /**
338
     * Get the category that this story belongs to
339
     *
340
     * Systems under test can grow to encompass hundreds, if not thousands
341
     * of user stories.  To make this manageable at scale, we break down
342
     * each user story like this:
343
     *
344
     * Name    : Starts as a free user with 10 USD in credit
345
     * Category: Billing User Stories
346
     * Group   : User States
347
     *
348
     * The 'name' is the summary text of the user story itself, which
349
     * should be no longer than a single sentence, please.
350
     *
351
     * The 'category' is the general group that the user story belongs to.
352
     * These are the top-level groups, such as 'Registration', 'Billing'
353
     * and so forth.
354
     *
355
     * The 'group' is the specific group _inside_ the category that the
356
     * user story belongs to.  The groups are specific to the category.
357
     *
358
     * @return string the category that this story belongs to
359
     */
360
    public function getCategory()
361
    {
362
        return $this->category;
363
    }
364
365
    /**
366
     * Set the category that this story belongs to
367
     *
368
     * Systems under test can grow to encompass hundreds, if not thousands
369
     * of user stories.  To make this manageable at scale, we break down
370
     * each user story like this:
371
     *
372
     * Name    : Starts as a free user with 10 USD in credit
373
     * Category: Billing User Stories
374
     * Group   : User States
375
     *
376
     * The 'name' is the summary text of the user story itself, which
377
     * should be no longer than a single sentence, please.
378
     *
379
     * The 'category' is the general group that the user story belongs to.
380
     * These are the top-level groups, such as 'Registration', 'Billing'
381
     * and so forth.
382
     *
383
     * The 'group' is the specific group _inside_ the category that the
384
     * user story belongs to.  The groups are specific to the category.
385
     *
386
     * @param  string $newCategory the category that this story belongs to
387
     * @return Story  $this
388
     */
389
    public function setCategory($newCategory)
390
    {
391
        $this->category = $newCategory;
392
        return $this;
393
    }
394
395
    /**
396
     * Get the group that this story belongs to
397
     *
398
     * Systems under test can grow to encompass hundreds, if not thousands
399
     * of user stories.  To make this manageable at scale, we break down
400
     * each user story like this:
401
     *
402
     * Name    : Starts as a free user with 10 USD in credit
403
     * Category: Billing User Stories
404
     * Group   : User States
405
     *
406
     * The 'name' is the summary text of the user story itself, which
407
     * should be no longer than a single sentence, please.
408
     *
409
     * The 'category' is the general group that the user story belongs to.
410
     * These are the top-level groups, such as 'Registration', 'Billing'
411
     * and so forth.
412
     *
413
     * The 'group' is the specific group _inside_ the category that the
414
     * user story belongs to.  The groups are specific to the category.
415
     *
416
     * @return array<string> the group that this story belongs to
417
     */
418
    public function getGroup()
419
    {
420
        return $this->group;
421
    }
422
423
    /**
424
     * return the story's group as a printable string
425
     *
426
     * @return string
427
     */
428
    public function getGroupAsString()
429
    {
430
        return implode(" > ", $this->group);
431
    }
432
433
    /**
434
     * Set the group that this story belongs to
435
     *
436
     * Systems under test can grow to encompass hundreds, if not thousands
437
     * of user stories.  To make this manageable at scale, we break down
438
     * each user story like this:
439
     *
440
     * Name    : Starts as a free user with 10 USD in credit
441
     * Category: Billing User Stories
442
     * Group   : User States
443
     *
444
     * The 'name' is the summary text of the user story itself, which
445
     * should be no longer than a single sentence, please.
446
     *
447
     * The 'category' is the general group that the user story belongs to.
448
     * These are the top-level groups, such as 'Registration', 'Billing'
449
     * and so forth.
450
     *
451
     * The 'group' is the specific group _inside_ the category that the
452
     * user story belongs to.  The groups are specific to the category.
453
     *
454
     * @param  array<string> $newGroup
455
     * @return Story  $this
456
     */
457
    public function setGroup($newGroup)
458
    {
459
        $this->group = $newGroup;
460
        return $this;
461
    }
462
463
    /**
464
     * Get the name of this story
465
     *
466
     * Systems under test can grow to encompass hundreds, if not thousands
467
     * of user stories.  To make this manageable at scale, we break down
468
     * each user story like this:
469
     *
470
     * Name    : Starts as a free user with 10 USD in credit
471
     * Category: Billing User Stories
472
     * Group   : User States
473
     *
474
     * The 'name' is the summary text of the user story itself, which
475
     * should be no longer than a single sentence, please.
476
     *
477
     * The 'category' is the general group that the user story belongs to.
478
     * These are the top-level groups, such as 'Registration', 'Billing'
479
     * and so forth.
480
     *
481
     * The 'group' is the specific group _inside_ the category that the
482
     * user story belongs to.  The groups are specific to the category.
483
     *
484
     * @return string the name of this story
485
     */
486
    public function getName()
487
    {
488
        return $this->name;
489
    }
490
491
    /**
492
     * Set the name of this story
493
     *
494
     * Systems under test can grow to encompass hundreds, if not thousands
495
     * of user stories.  To make this manageable at scale, we break down
496
     * each user story like this:
497
     *
498
     * Name    : Starts as a free user with 10 USD in credit
499
     * Category: Billing User Stories
500
     * Group   : User States
501
     *
502
     * The 'name' is the summary text of the user story itself, which
503
     * should be no longer than a single sentence, please.
504
     *
505
     * The 'category' is the general group that the user story belongs to.
506
     * These are the top-level groups, such as 'Registration', 'Billing'
507
     * and so forth.
508
     *
509
     * The 'group' is the specific group _inside_ the category that the
510
     * user story belongs to.  The groups are specific to the category.
511
     *
512
     * @param  string $newName the name of this story
513
     * @return Story  $this
514
     */
515
    public function setName($newName)
516
    {
517
        $this->name = $newName;
518
        return $this;
519
    }
520
521
    /**
522
     * Set the parameters for this story
523
     *
524
     * Parameters are a way of passing settings between Stories and
525
     * StoryTemplates.
526
     *
527
     * Order of precedence:
528
     *
529
     * 1) Story
530
     * 2) StoryTemplates (in 'basedOn()' order)
531
     *    (templates cannot override each other)
532
     *
533
     * @param array $defaults
534
     *        a list of the parameters for this story
535
     */
536
    public function setParams($defaults)
537
    {
538
        $this->params = $defaults;
539
        return $this;
540
    }
541
542
    /**
543
     * @return array
544
     */
545
    public function getParams()
546
    {
547
        // our return value
548
        $return = array();
549
550
        // populate it with the parameters from the templates
551
        foreach ($this->storyTemplates as $template) {
552
            // get any params from the template
553
            $params = $template->getParams();
554
555
            // any params already set have precedence
556
            foreach ($params as $key => $value) {
557
                // do we have a clash?
558
                if (!isset($return[$key])) {
559
                    $return[$key] = $value;
560
                }
561
            }
562
        }
563
564
        // now, merge in our own params
565
        //
566
        // our params always take precedence over the params set
567
        // in the template(s)
568
        $return = array_merge($this->params, $return);
569
570
        // all done
571
        return $return;
572
    }
573
574
    /**
575
     * @return void
576
     */
577
    public function determineStoryFilename()
578
    {
579
        $trace = debug_backtrace();
580
        $this->storyFilename = $trace[1]['file'];
581
    }
582
583
    /**
584
     * @return string
585
     */
586
    public function getStoryFilename()
587
    {
588
        return $this->storyFilename;
589
    }
590
591
    // ====================================================================
592
    //
593
    // Metadata for which states the story is valid for
594
    //
595
    // --------------------------------------------------------------------
596
597
    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...
598
    {
599
        throw new E4xx_DeprecatedFeature('user roles in stories have been removed from Storyplayer v2.');
600
    }
601
602
    /**
603
     * Synonym for addValidRole()
604
     *
605
     * @see Story::addValidRole
606
     */
607
    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...
608
    {
609
        throw new E4xx_DeprecatedFeature('user roles in stories have been removed from Storyplayer v2.');
610
    }
611
612
    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...
613
    {
614
        throw new E4xx_DeprecatedFeature('user roles in stories have been removed from Storyplayer v2.');
615
    }
616
617
    // ====================================================================
618
    //
619
    // Support for changing roles after a test has succeeded
620
    //
621
    // --------------------------------------------------------------------
622
623
    /**
624
     * get the role changes callback
625
     */
626
    public function getRoleChanges()
627
    {
628
        throw new E4xx_DeprecatedFeature('user roles in stories have been removed from Storyplayer v2.');
629
    }
630
631
    /**
632
     * has the role changes callback been set?
633
     */
634
    public function hasRoleChanges()
635
    {
636
        throw new E4xx_DeprecatedFeature('user roles in stories have been removed from Storyplayer v2.');
637
    }
638
639
    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...
640
    {
641
        throw new E4xx_DeprecatedFeature('user roles in stories have been removed from Storyplayer v2.');
642
    }
643
644
    // ====================================================================
645
    //
646
    // Information about story templates
647
    //
648
    // --------------------------------------------------------------------
649
650
    /**
651
     * set up any templated methods from a predefined class
652
     *
653
     * @return Story
654
     */
655
    public function basedOn(StoryTemplate $tmpl)
656
    {
657
        // tell the template which story it is being used with
658
        $tmpl->setStory($this);
659
660
        $tmpl->hasTestCanRunCheck()         && $this->addTestCanRunCheck($tmpl->getTestCanRunCheck());
661
        $tmpl->hasTestSetup()               && $this->addTestSetup($tmpl->getTestSetup());
662
        $tmpl->hasTestTeardown()            && $this->addTestTeardown($tmpl->getTestTeardown());
663
        $tmpl->hasPerPhaseSetup()           && $this->addPerPhaseSetup($tmpl->getPerPhaseSetup());
664
        $tmpl->hasPerPhaseTeardown()        && $this->addPerPhaseTeardown($tmpl->getPerPhaseTeardown());
665
        $tmpl->hasDeviceSetup()             && $this->addDeviceSetup($tmpl->getDeviceSetup());
666
        $tmpl->hasDeviceTeardown()          && $this->addDeviceTeardown($tmpl->getDeviceTeardown());
667
        $tmpl->hasHints()                   && $this->addHints($tmpl->getHints());
668
        $tmpl->hasPreTestPrediction()       && $this->addPreTestPrediction($tmpl->getPreTestPrediction());
669
        $tmpl->hasPreTestInspection()       && $this->addPreTestInspection($tmpl->getPreTestInspection());
670
        $tmpl->hasAction()                  && $this->addAction($tmpl->getAction());
671
        $tmpl->hasPostTestInspection()      && $this->addPostTestInspection($tmpl->getPostTestInspection());
672
673
        // remember this template for future use
674
        $this->storyTemplates[] = $tmpl;
675
676
        // Return $this for a fluent interface
677
        return $this;
678
    }
679
680
    /**
681
     * get a list of the templates that this story is based on, in order
682
     * or precedence
683
     *
684
     * can be empty
685
     *
686
     * @return array<StoryTemplate>
687
     */
688
    public function getStoryTemplates()
689
    {
690
        return $this->storyTemplates;
691
    }
692
693
    // ==================================================================
694
    //
695
    // Information about dependencies
696
    //
697
    // ------------------------------------------------------------------
698
699
    /**
700
     * @return int
701
     */
702
    public function getRequiredStoryplayerVersion()
703
    {
704
        return $this->compatibleVersion;
705
    }
706
707
    /**
708
     * @return Story
709
     */
710
    public function requiresStoryplayerVersion($version)
711
    {
712
        $this->compatibleVersion = $version;
713
        return $this;
714
    }
715
716
    // ====================================================================
717
    //
718
    // Information about environments
719
    //
720
    // --------------------------------------------------------------------
721
722
    /**
723
     * @return Story
724
     */
725
    public function runsOn($envName)
726
    {
727
        $this->whitelistedEnvironments[$envName] = true;
728
        return $this;
729
    }
730
731
    /**
732
     * @return Story
733
     */
734
    public function andOn($envName)
735
    {
736
        $this->whitelistedEnvironments[$envName] = true;
737
        return $this;
738
    }
739
740
    /**
741
     * @return array
742
     */
743
    public function getWhitelistedEnvironments()
744
    {
745
        return $this->whitelistedEnvironments;
746
    }
747
748
    /**
749
     * @return void
750
     */
751
    public function requiresTestEnvironmentWithRoles($roles)
752
    {
753
        $this->requiredTestEnvRoles = $roles;
754
    }
755
756
    /**
757
     * @return array
758
     */
759
    public function getRequiredTestEnvironmentRoles()
760
    {
761
        return $this->requiredTestEnvRoles;
762
    }
763
764
    // ==================================================================
765
    //
766
    // Device support
767
    //
768
    // ------------------------------------------------------------------
769
770
    /**
771
     * does this story want to keep the web browser open between phases?
772
     *
773
     * @return boolean
774
     */
775
    public function getPersistDevice()
776
    {
777
        return $this->persistDevice;
778
    }
779
780
    /**
781
     * tell Storyplayer to keep the web browser open between test phases
782
     *
783
     * by default, we close the browser after every phase, to make sure
784
     * that the next phase always starts with a browser in a known state
785
     */
786
    public function setPersistDevice()
787
    {
788
        $this->persistDevice = true;
789
        return $this;
790
    }
791
792
    // ====================================================================
793
    //
794
    // Information about how check if the test should run at all
795
    //
796
    // --------------------------------------------------------------------
797
798
    /**
799
     * get the callback which allows the story to be skipped
800
     *
801
     * @return array
802
     */
803
    public function getTestCanRunCheck()
804
    {
805
        return $this->testCanRunCheckCallback;
806
    }
807
808
    /**
809
     * do we have a 'check story can run' callback?
810
     *
811
     * @return boolean true if the callback exists
812
     */
813
    public function hasTestCanRunCheck()
814
    {
815
        return count($this->testCanRunCheckCallback) > 0;
816
    }
817
818
    /**
819
     * @return void
820
     */
821
    public function addTestCanRunCheck($newCallback)
822
    {
823
        $this->testCanRunCheckCallback[] = $newCallback;
824
    }
825
826
    // ====================================================================
827
    //
828
    // Information about how to setup and teardown the test environment
829
    //
830
    // This is a feature from Storyplayer v1 that we've dropped in v2
831
    //
832
    // --------------------------------------------------------------------
833
834
    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...
835
    {
836
        throw new E4xx_DeprecatedFeature('TestEnvironmentSetup phase was removed from Storyplayer v2.');
837
    }
838
839
    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...
840
    {
841
        throw new E4xx_DeprecatedFeature('TestEnvironmentSetup phase was removed from Storyplayer v2.');
842
    }
843
844
    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...
845
    {
846
        throw new E4xx_DeprecatedFeature('TestEnvironmentSetup phase was removed from Storyplayer v2.');
847
    }
848
849
    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...
850
    {
851
        throw new E4xx_DeprecatedFeature('TestEnvironmentSetup phase was removed from Storyplayer v2.');
852
    }
853
854
    // ====================================================================
855
    //
856
    // Information about how to setup and teardown the test
857
    //
858
    // --------------------------------------------------------------------
859
860
    /**
861
     * get the callback for per-story setup work
862
     *
863
     * @return array
864
     */
865
    public function getTestSetup()
866
    {
867
        return $this->testSetupCallback;
868
    }
869
870
    /**
871
     * do we have a pre-story setup callback?
872
     *
873
     * @return boolean true if there is a pre-story setup callback
874
     */
875
    public function hasTestSetup()
876
    {
877
        return count($this->testSetupCallback) > 0;
878
    }
879
880
    public function addTestSetup($newCallback)
881
    {
882
        $this->testSetupCallback[] = $newCallback;
883
    }
884
885
    /**
886
     * get the callback for post-story teardown work
887
     *
888
     * @return array
889
     */
890
    public function getTestTeardown()
891
    {
892
        return $this->testTeardownCallback;
893
    }
894
895
    /**
896
     * do we have a post-story teardown callback?
897
     *
898
     * @return boolean true if there is a post-story teardown callback
899
     */
900
    public function hasTestTeardown()
901
    {
902
        return count($this->testTeardownCallback) > 0;
903
    }
904
905
    /**
906
     * @return void
907
     */
908
    public function addTestTeardown($newCallback)
909
    {
910
        $this->testTeardownCallback[] = $newCallback;
911
    }
912
913
    // ====================================================================
914
    //
915
    // Actions to happen before and after every phase of the test
916
    //
917
    // --------------------------------------------------------------------
918
919
    /**
920
     * get the callback for per-phase setup work
921
     *
922
     * @return array
923
     */
924
    public function getPerPhaseSetup()
925
    {
926
        return $this->perPhaseSetupCallback;
927
    }
928
929
    /**
930
     * do we have a per-phase setup callback?
931
     *
932
     * @return boolean true if there is a per-phase setup callback
933
     */
934
    public function hasPerPhaseSetup()
935
    {
936
        return count($this->perPhaseSetupCallback) > 0;
937
    }
938
939
    /**
940
     * @return void
941
     */
942
    public function addPerPhaseSetup($newCallback)
943
    {
944
        $this->perPhaseSetupCallback[] = $newCallback;
945
    }
946
947
    /**
948
     * get the callback for per-phase teardown work
949
     *
950
     * @return array
951
     */
952
    public function getPerPhaseTeardown()
953
    {
954
        return $this->perPhaseTeardownCallback;
955
    }
956
957
    /**
958
     * do we have a per-phase teardown callback?
959
     *
960
     * @return boolean true if there is a per-phase teardown callback
961
     */
962
    public function hasPerPhaseTeardown()
963
    {
964
        return count($this->perPhaseTeardownCallback) > 0;
965
    }
966
967
    /**
968
     * @return void
969
     */
970
    public function addPerPhaseTeardown($newCallback)
971
    {
972
        $this->perPhaseTeardownCallback[] = $newCallback;
973
    }
974
975
    // ====================================================================
976
    //
977
    // Actions to happen when starting and stopping test devices
978
    //
979
    // --------------------------------------------------------------------
980
981
    /**
982
     * get the callback for device setup work
983
     *
984
     * @return array
985
     */
986
    public function getDeviceSetup()
987
    {
988
        return $this->deviceSetupCallback;
989
    }
990
991
    /**
992
     * do we have a device setup callback?
993
     *
994
     * @return boolean true if there is a device setup callback
995
     */
996
    public function hasDeviceSetup()
997
    {
998
        return count($this->deviceSetupCallback) > 0;
999
    }
1000
1001
    /**
1002
     * @return void
1003
     */
1004
    public function addDeviceSetup($newCallback)
1005
    {
1006
        $this->deviceSetupCallback[] = $newCallback;
1007
    }
1008
1009
    /**
1010
     * get the callback for device teardown work
1011
     *
1012
     * @return array
1013
     */
1014
    public function getDeviceTeardown()
1015
    {
1016
        return $this->deviceTeardownCallback;
1017
    }
1018
1019
    /**
1020
     * do we have a device teardown callback?
1021
     *
1022
     * @return boolean true if there is a device teardown callback
1023
     */
1024
    public function hasDeviceTeardown()
1025
    {
1026
        return count($this->deviceTeardownCallback) > 0;
1027
    }
1028
1029
    /**
1030
     * @return void
1031
     */
1032
    public function addDeviceTeardown($newCallback)
1033
    {
1034
        $this->deviceTeardownCallback[] = $newCallback;
1035
    }
1036
1037
    // ====================================================================
1038
    //
1039
    // Information about how the story changes the system
1040
    //
1041
    // --------------------------------------------------------------------
1042
1043
    /**
1044
     * get the hints callback
1045
     *
1046
     * @return callable
1047
     */
1048
    public function getHints()
1049
    {
1050
        return $this->hintsCallback;
1051
    }
1052
1053
    /**
1054
     * have any hints been set?
1055
     *
1056
     * @return boolean true if the callback has been set
1057
     */
1058
    public function hasHints()
1059
    {
1060
        return count($this->hintsCallback) > 0;
1061
    }
1062
1063
    /**
1064
     * @return void
1065
     */
1066
    public function setHints($newCallback)
1067
    {
1068
        $this->hintsCallback = array($newCallback);
1069
    }
1070
1071
    /**
1072
     * @return void
1073
     */
1074
    public function addHints($newCallback)
1075
    {
1076
        $this->hintsCallback[] = $newCallback;
1077
    }
1078
1079
    // ====================================================================
1080
    //
1081
    // Before and after tests
1082
    //
1083
    // --------------------------------------------------------------------
1084
1085
    /**
1086
     * get the callback to use to perform the preflight checks
1087
     *
1088
     * @return array
1089
     */
1090
    public function getPreTestPrediction()
1091
    {
1092
        return $this->preTestPredictionCallback;
1093
    }
1094
1095
    /**
1096
     * do we have a callback
1097
     * @return boolean [description]
1098
     */
1099
    public function hasPreTestPrediction()
1100
    {
1101
        return count($this->preTestPredictionCallback) > 0;
1102
    }
1103
1104
    /**
1105
     * @return void
1106
     */
1107
    public function addPreTestPrediction($newCallback)
1108
    {
1109
        $this->preTestPredictionCallback[] = $newCallback;
1110
    }
1111
1112
    // ====================================================================
1113
    //
1114
    // Checkpoint the relevant system state before actions occur
1115
    //
1116
    // --------------------------------------------------------------------
1117
1118
    /**
1119
     * get the callback to use to perform the preflight checkpoint
1120
     *
1121
     * @return array
1122
     */
1123
    public function getPreTestInspection()
1124
    {
1125
        return $this->preTestInspectionCallback;
1126
    }
1127
1128
    /**
1129
     * do we have a callback
1130
     * @return boolean [description]
1131
     */
1132
    public function hasPreTestInspection()
1133
    {
1134
        return count($this->preTestInspectionCallback) > 0;
1135
    }
1136
1137
    /**
1138
     * @return void
1139
     */
1140
    public function addPreTestInspection($newCallback)
1141
    {
1142
        $this->preTestInspectionCallback[] = $newCallback;
1143
    }
1144
1145
    // ====================================================================
1146
    //
1147
    // Add in the actions that make the story come to life
1148
    //
1149
    // --------------------------------------------------------------------
1150
1151
    /**
1152
     * @return void
1153
     */
1154
    public function addAction($newCallback)
1155
    {
1156
        $this->actionsCallbacks[] = $newCallback;
1157
    }
1158
1159
    /**
1160
     * @return void
1161
     */
1162
    public function addActions($newCallback)
1163
    {
1164
        $this->actionsCallbacks[] = $newCallback;
1165
    }
1166
1167
    /**
1168
     * pick one action at random, and return it to the caller
1169
     *
1170
     * @return callable
1171
     */
1172
    public function getOneAction()
1173
    {
1174
        // do we have any callbacks to pick from?
1175
        if (count($this->actionsCallbacks) == 0)
1176
        {
1177
            throw new E5xx_NoStoryActions($this->getName());
1178
        }
1179
1180
        // pick one story
1181
        $i = rand(0, count($this->actionsCallbacks) - 1);
1182
1183
        // return it to the caller
1184
        return $this->actionsCallbacks[$i];
1185
    }
1186
1187
    /**
1188
     * does this story have any actions?
1189
     *
1190
     * @return boolean true if this story has any actions
1191
     */
1192
    public function hasActions()
1193
    {
1194
        return (count($this->actionsCallbacks) > 0);
1195
    }
1196
1197
    // ====================================================================
1198
    //
1199
    // Determine whether the test passed or failed
1200
    //
1201
    // --------------------------------------------------------------------
1202
1203
    /**
1204
     * get the callback to use to work out the test results
1205
     *
1206
     * @return array
1207
     */
1208
    public function getPostTestInspection()
1209
    {
1210
        return $this->postTestInspectionCallback;
1211
    }
1212
1213
    /**
1214
     * @return bool
1215
     */
1216
    public function hasPostTestInspection()
1217
    {
1218
        return count($this->postTestInspectionCallback) > 0;
1219
    }
1220
1221
    /**
1222
     * @return void
1223
     */
1224
    public function addPostTestInspection($newCallback)
1225
    {
1226
        $this->postTestInspectionCallback[] = $newCallback;
1227
    }
1228
1229
    // ====================================================================
1230
    //
1231
    // Our default behaviour when the story object is instantiated
1232
    //
1233
    // --------------------------------------------------------------------
1234
1235
    /**
1236
     * @return void
1237
     */
1238
    public function setDefaultCallbacks()
1239
    {
1240
        // 1: test setup
1241
        if (!$this->hasTestSetup()) {
1242
            $this->addTestSetup(function(StoryTeller $st) {
1243
                $st->usingReporting()->reportNotRequired();
1244
            });
1245
        }
1246
1247
        // 2: pre-test prediction
1248
        if (!$this->hasPreTestPrediction()) {
1249
            $this->addPreTestPrediction(function(StoryTeller $st) {
1250
                $st->usingReporting()->reportShouldAlwaysSucceed();
1251
            });
1252
        }
1253
1254
        // 3: pre-test inspection
1255
        if (!$this->hasPreTestInspection()) {
1256
            $this->addPreTestInspection(function(StoryTeller $st) {
1257
                $st->usingReporting()->reportNotRequired();
1258
            });
1259
        }
1260
1261
        // 4: test action
1262
        //
1263
        // we set no default for this, because we do not want the action
1264
        // to be chosen by StoryPlayer
1265
        //
1266
        // (StoryPlayer chooses one action at random from the set of
1267
        // supplied actions)
1268
1269
        // 5: post-test inspection
1270
        //
1271
        // we set no default for this, because each story must provide
1272
        // this
1273
1274
        // 6: test tear down
1275
        if (!$this->hasTestTeardown()) {
1276
            $this->addTestTeardown(function(StoryTeller $st) {
1277
                $st->usingReporting()->reportNotRequired();
1278
            });
1279
        }
1280
1281
        // all done
1282
    }
1283
1284
    // ====================================================================
1285
    //
1286
    // Serialisation and other format convertors
1287
    //
1288
    // --------------------------------------------------------------------
1289
1290
    /**
1291
     * return a string representation of the story, for things like logging
1292
     * @return string
1293
     */
1294
    public function __toString()
1295
    {
1296
        return $this->getCategory() . ' :: ' . $this->getGroupAsString() . ' :: ' . $this->getName();
1297
    }
1298
1299
    // ==================================================================
1300
    //
1301
    // Dealing with the story result
1302
    //
1303
    // ------------------------------------------------------------------
1304
1305
    /**
1306
     * @return Story_Result
1307
     */
1308
    public function getResult()
1309
    {
1310
        if (!isset($this->storyResult)) {
1311
            $this->storyResult = new Story_Result($this);
1312
        }
1313
1314
        return $this->storyResult;
1315
    }
1316
}
1317