PHPUnitTask::execute()   F
last analyzed

Complexity

Conditions 42
Paths > 20000

Size

Total Lines 166
Code Lines 96

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 539.7501

Importance

Changes 0
Metric Value
eloc 96
dl 0
loc 166
ccs 32
cts 93
cp 0.3441
rs 0
c 0
b 0
f 0
cc 42
nc 14250152
nop 1
crap 539.7501

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
 *
16
 * This software consists of voluntary contributions made by many individuals
17
 * and is licensed under the LGPL. For more information please see
18
 * <http://phing.info>.
19
 */
20
21
namespace Phing\Task\Ext\PhpUnit;
22
23
use Exception;
24
use Phing\Exception\BuildException;
25
use Phing\Io\FileWriter;
26
use Phing\Io\LogWriter;
27
use Phing\Io\File;
28
use Phing\Task;
29
use PHPUnit\Framework\TestSuite;
30
use PHPUnit\TextUI\XmlConfiguration\Loader;
31
use ReflectionException;
32
use ReflectionClass;
33
34
/**
35
 * Runs PHPUnit tests.
36
 *
37
 * @author  Michiel Rook <[email protected]>
38
 * @package phing.tasks.ext.phpunit
39
 * @see     BatchTest
40
 * @since   2.1.0
41
 */
42
class PHPUnitTask extends Task
43
{
44
    private $batchtests = [];
45
    /**
46
     * @var FormatterElement[] $formatters
47
     */
48
    private $formatters = [];
49
    private $bootstrap = "";
50
    private $haltonerror = false;
51
    private $haltonfailure = false;
52
    private $haltonincomplete = false;
53
    private $haltonskipped = false;
54
    private $haltondefect = false;
55
    private $haltonwarning = false;
56
    private $haltonrisky = false;
57
    private $errorproperty;
58
    private $failureproperty;
59
    private $incompleteproperty;
60
    private $skippedproperty;
61
    private $warningproperty;
62
    private $riskyproperty;
63
    private $printsummary = false;
64
    private $testfailed = false;
65
    private $testfailuremessage = "";
66
    private $codecoverage = null;
67
    private $groups = [];
68
    private $excludeGroups = [];
69
    private $processIsolation = false;
70
    private $usecustomerrorhandler = true;
71
    private $listeners = [];
72
73
    /**
74
     * @var string
75
     */
76
    private $pharLocation = "";
77
78
    /**
79
     * @var File
80
     */
81
    private $configuration = null;
82
83
    /**
84
     * @var \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage
85
     */
86
    private $codeCoverageConfig = null;
87
88
    /**
89
     * Initialize Task.
90
     * This method includes any necessary PHPUnit libraries and triggers
91
     * appropriate error if they cannot be found.  This is not done in header
92
     * because we may want this class to be loaded w/o triggering an error.
93
     */
94 5
    public function init()
95
    {
96 5
    }
97
98 5
    private function loadPHPUnit()
99
    {
100 5
        if (!empty($this->pharLocation)) {
101
            // nasty but necessary: reorder the autoloaders so the one in the PHAR gets priority
102
            $autoloadFunctions = spl_autoload_functions();
103
            $composerAutoloader = null;
104
            if (get_class($autoloadFunctions[0][0]) === 'Composer\Autoload\ClassLoader') {
105
                $composerAutoloader = $autoloadFunctions[0];
106
                spl_autoload_unregister($composerAutoloader);
107
            }
108
109
            $GLOBALS['_SERVER']['SCRIPT_NAME'] = '-';
110
            ob_start();
111
            @include $this->pharLocation;
112
            ob_end_clean();
113
114
            if ($composerAutoloader !== null) {
115
                spl_autoload_register($composerAutoloader);
116
            }
117
        }
118
119 5
        if (!class_exists('PHPUnit\Runner\Version')) {
120
            throw new BuildException("PHPUnitTask requires PHPUnit to be installed", $this->getLocation());
121
        }
122
123
        if (
124 5
            class_exists('\PHPUnit\Runner\Version', false) &&
125 5
            version_compare(\PHPUnit\Runner\Version::id(), '9.0.0', '<')
126
        ) {
127
            throw new BuildException("Phing only supports PHPUnit 9+");
128
        }
129
    }
130
131
    /**
132
     * Sets the name of a bootstrap file that is run before
133
     * executing the tests
134
     *
135
     * @param string $bootstrap the name of the bootstrap file
136
     */
137 1
    public function setBootstrap($bootstrap)
138
    {
139 1
        $this->bootstrap = $bootstrap;
140
    }
141
142
    /**
143
     * @param $value
144
     */
145
    public function setErrorproperty($value)
146
    {
147
        $this->errorproperty = $value;
148
    }
149
150
    /**
151
     * @param $value
152
     */
153
    public function setFailureproperty($value)
154
    {
155
        $this->failureproperty = $value;
156
    }
157
158
    /**
159
     * @param $value
160
     */
161
    public function setIncompleteproperty($value)
162
    {
163
        $this->incompleteproperty = $value;
164
    }
165
166
    /**
167
     * @param $value
168
     */
169
    public function setSkippedproperty($value)
170
    {
171
        $this->skippedproperty = $value;
172
    }
173
174
    /**
175
     * @param $value
176
     */
177
    public function setRiskyproperty($value)
178
    {
179
        $this->riskyproperty = $value;
180
    }
181
182
    /**
183
     * @param $value
184
     */
185
    public function setWarningproperty($value)
186
    {
187
        $this->riskyproperty = $value;
188
    }
189
190
    /**
191
     * @return bool
192
     */
193
    public function getHaltondefect(): bool
194
    {
195
        return $this->haltondefect;
196
    }
197
198
    /**
199
     * @param bool $haltondefect
200
     */
201
    public function setHaltondefect(bool $haltondefect): void
202
    {
203
        $this->haltondefect = $haltondefect;
204
    }
205
206
    /**
207
     * @return bool
208
     */
209
    public function getHaltonwarning(): bool
210
    {
211
        return $this->haltonwarning;
212
    }
213
214
    /**
215
     * @param bool $haltonwarning
216
     */
217
    public function setHaltonwarning(bool $haltonwarning): void
218
    {
219
        $this->haltonwarning = $haltonwarning;
220
    }
221
222
    /**
223
     * @return bool
224
     */
225
    public function getHaltonrisky(): bool
226
    {
227
        return $this->haltonrisky;
228
    }
229
230
    /**
231
     * @param bool $haltonrisky
232
     */
233
    public function setHaltonrisky(bool $haltonrisky): void
234
    {
235
        $this->haltonrisky = $haltonrisky;
236
    }
237
238
    /**
239
     * @param $value
240
     */
241 3
    public function setHaltonerror($value)
242
    {
243 3
        $this->haltonerror = $value;
244
    }
245
246
    /**
247
     * @param $value
248
     */
249 4
    public function setHaltonfailure($value)
250
    {
251 4
        $this->haltonfailure = $value;
252
    }
253
254
    /**
255
     * @return bool
256
     */
257
    public function getHaltonfailure()
258
    {
259
        return $this->haltonfailure;
260
    }
261
262
    /**
263
     * @param $value
264
     */
265
    public function setHaltonincomplete($value)
266
    {
267
        $this->haltonincomplete = $value;
268
    }
269
270
    /**
271
     * @return bool
272
     */
273 1
    public function getHaltonincomplete()
274
    {
275 1
        return $this->haltonincomplete;
276
    }
277
278
    /**
279
     * @param $value
280
     */
281
    public function setHaltonskipped($value)
282
    {
283
        $this->haltonskipped = $value;
284
    }
285
286
    /**
287
     * @return bool
288
     */
289 1
    public function getHaltonskipped()
290
    {
291 1
        return $this->haltonskipped;
292
    }
293
294
    /**
295
     * @param $printsummary
296
     */
297 1
    public function setPrintsummary($printsummary)
298
    {
299 1
        $this->printsummary = $printsummary;
300
    }
301
302
    /**
303
     * @param $codecoverage
304
     */
305 1
    public function setCodecoverage($codecoverage)
306
    {
307 1
        $this->codecoverage = $codecoverage;
308
    }
309
310
    /**
311
     * @param $processIsolation
312
     */
313
    public function setProcessIsolation($processIsolation)
314
    {
315
        $this->processIsolation = $processIsolation;
316
    }
317
318
    /**
319
     * @param $usecustomerrorhandler
320
     */
321
    public function setUseCustomErrorHandler($usecustomerrorhandler)
322
    {
323
        $this->usecustomerrorhandler = $usecustomerrorhandler;
324
    }
325
326
    /**
327
     * @param $groups
328
     */
329
    public function setGroups($groups)
330
    {
331
        $token = ' ,;';
332
        $this->groups = [];
333
        $tok = strtok($groups, $token);
334
        while ($tok !== false) {
335
            $this->groups[] = $tok;
336
            $tok = strtok($token);
337
        }
338
    }
339
340
    /**
341
     * @param $excludeGroups
342
     */
343 1
    public function setExcludeGroups($excludeGroups)
344
    {
345 1
        $token = ' ,;';
346 1
        $this->excludeGroups = [];
347 1
        $tok = strtok($excludeGroups, $token);
348 1
        while ($tok !== false) {
349 1
            $this->excludeGroups[] = $tok;
350 1
            $tok = strtok($token);
351
        }
352
    }
353
354
    /**
355
     * Add a new formatter to all tests of this task.
356
     *
357
     * @param FormatterElement $fe formatter element
358
     */
359 2
    public function addFormatter(FormatterElement $fe)
360
    {
361 2
        $fe->setParent($this);
362 2
        $this->formatters[] = $fe;
363
    }
364
365
    /**
366
     * Add a new listener to all tests of this taks
367
     *
368
     * @param $listener
369
     */
370
    private function addListener($listener)
371
    {
372
        $this->listeners[] = $listener;
373
    }
374
375
    /**
376
     * @param File $configuration
377
     */
378
    public function setConfiguration(File $configuration)
379
    {
380
        $this->configuration = $configuration;
381
    }
382
383
    /**
384
     * @param string $pharLocation
385
     */
386
    public function setPharLocation($pharLocation)
387
    {
388
        $this->pharLocation = $pharLocation;
389
    }
390
391
    /**
392
     * Load and processes the PHPUnit configuration
393
     *
394
     * @param  $configuration
395
     * @return mixed
396
     * @throws ReflectionException
397
     * @throws BuildException
398
     */
399
    protected function handlePHPUnitConfiguration(File $configuration)
400
    {
401
        if (!$configuration->exists()) {
402
            throw new BuildException("Unable to find PHPUnit configuration file '" . (string) $configuration . "'");
403
        }
404
405
        $config = (new Loader())->load($configuration->getAbsolutePath());
406
        $phpunit = $config->phpunit();
407
408
        if ($phpunit->hasBootstrap()) {
409
            $this->setBootstrap($phpunit->bootstrap());
410
        }
411
        $this->setHaltonfailure($phpunit->stopOnFailure());
412
        $this->setHaltonerror($phpunit->stopOnError());
413
        $this->setHaltonskipped($phpunit->stopOnSkipped());
414
        $this->setHaltonincomplete($phpunit->stopOnIncomplete());
415
        $this->setHaltondefect($phpunit->stopOnDefect());
416
        $this->setHaltonwarning($phpunit->stopOnWarning());
417
        $this->setHaltonrisky($phpunit->stopOnRisky());
418
        $this->setProcessIsolation($phpunit->processIsolation());
419
420
        foreach ($config->listeners() as $listener) {
421
            if (
422
                !class_exists($listener->className(), false)
423
                && $listener->hasSourceFile()
424
            ) {
425
                include_once $listener->sourceFile();
426
            }
427
428
            if (class_exists($listener->className())) {
429
                if ($listener->hasArguments()) {
430
                    $listener = (new $listener->className())();
431
                } else {
432
                    $listenerClass = new ReflectionClass(
433
                        $listener->className()
434
                    );
435
                    $listener = $listenerClass->newInstanceArgs(
436
                        $listener->arguments()
437
                    );
438
                }
439
440
                if ($listener instanceof \PHPUnit\Framework\TestListener) {
441
                    $this->addListener($listener);
442
                }
443
            }
444
        }
445
446
        $this->codeCoverageConfig = $config->codeCoverage();
447
        return $phpunit;
448
    }
449
450
    /**
451
     * The main entry point
452
     *
453
     * @throws BuildException
454
     */
455 5
    public function main()
456
    {
457 5
        if ($this->codecoverage && !extension_loaded('xdebug')) {
458
            throw new BuildException("PHPUnitTask depends on Xdebug being installed to gather code coverage information.");
459
        }
460
461 5
        $this->loadPHPUnit();
462 5
        $suite = new \PHPUnit\Framework\TestSuite('AllTests');
463 5
        $autoloadSave = spl_autoload_functions();
464
465 5
        if ($this->bootstrap) {
466 1
            include $this->bootstrap;
467
        }
468
469 5
        if ($this->configuration) {
470
            $phpunit = $this->handlePHPUnitConfiguration($this->configuration);
471
472
            if ($phpunit->backupGlobals() === false) {
473
                $suite->setBackupGlobals(false);
474
            }
475
476
            if ($phpunit->backupStaticAttributes() === true) {
477
                $suite->setBackupStaticAttributes(true);
478
            }
479
        }
480
481 5
        if ($this->printsummary) {
482 1
            $fe = new FormatterElement();
483 1
            $fe->setParent($this);
484 1
            $fe->setType("summary");
485 1
            $fe->setUseFile(false);
486 1
            $this->formatters[] = $fe;
487
        }
488
489 5
        foreach ($this->batchtests as $batchTest) {
490 5
            $this->appendBatchTestToTestSuite($batchTest, $suite);
491
        }
492
493 5
        $this->execute($suite);
494
495 5
        if ($this->testfailed) {
496 1
            throw new BuildException("Test(s) failed: " . $this->testfailuremessage);
497
        }
498
499 4
        $autoloadNew = spl_autoload_functions();
500 4
        if (is_array($autoloadNew)) {
0 ignored issues
show
introduced by
The condition is_array($autoloadNew) is always true.
Loading history...
501 4
            foreach ($autoloadNew as $autoload) {
502 4
                spl_autoload_unregister($autoload);
503
            }
504
        }
505
506 4
        if (is_array($autoloadSave)) {
507 4
            foreach ($autoloadSave as $autoload) {
508 4
                spl_autoload_register($autoload);
509
            }
510
        }
511
    }
512
513
    /**
514
     * @param $suite
515
     * @throws BuildException
516
     * @throws ReflectionException
517
     */
518 5
    protected function execute($suite)
519
    {
520 5
        $runner = new PHPUnitTestRunner9(
521 5
            $this->project,
522 5
            $this->groups,
523 5
            $this->excludeGroups,
524 5
            $this->processIsolation
525 5
        );
526
527 5
        if ($this->codecoverage) {
528
            /**
529
             * Add some defaults to the PHPUnit filter
530
             */
531
            $pwd = __DIR__;
532
            $path = realpath($pwd . '/../../../');
533
534
            if (class_exists('\SebastianBergmann\CodeCoverage\Filter')) {
535
                $filter = new \SebastianBergmann\CodeCoverage\Filter();
536
                if (method_exists($filter, 'addDirectoryToBlacklist')) {
537
                    $filter->addDirectoryToBlacklist($path);
538
                }
539
                if (class_exists('\SebastianBergmann\CodeCoverage\CodeCoverage')) {
540
                    if (null !== $this->codeCoverageConfig) {
541
                        // Update filters
542
                        foreach ($this->codeCoverageConfig->files()->asArray() as $file) {
543
                            $filter->includeFile($file->path());
544
                        }
545
                        foreach ($this->codeCoverageConfig->directories()->asArray() as $dir) {
546
                            $filter->includeDirectory($dir->path(), $dir->suffix(), $dir->prefix());
547
                        }
548
                        foreach ($this->codeCoverageConfig->excludeFiles()->asArray() as $file) {
549
                            $filter->excludeFile($file->path());
550
                        }
551
                        foreach ($this->codeCoverageConfig->excludeDirectories()->asArray() as $dir) {
552
                            $filter->excludeDirectory($dir->path(), $dir->suffix(), $dir->prefix());
553
                        }
554
                    }
555
556
                    if (null !== $this->codeCoverageConfig && $this->codeCoverageConfig->pathCoverage()) {
557
                        $driver = (new \SebastianBergmann\CodeCoverage\Driver\Selector())->forLineAndPathCoverage($filter);
0 ignored issues
show
Unused Code introduced by
The assignment to $driver is dead and can be removed.
Loading history...
558
                    } else {
559
                        $driver = (new \SebastianBergmann\CodeCoverage\Driver\Selector())->forLineCoverage($filter);
560
                    }
561
562
                    $driver = (new \SebastianBergmann\CodeCoverage\Driver\Selector())->forLineCoverage($filter);
563
                    $codeCoverage = new \SebastianBergmann\CodeCoverage\CodeCoverage($driver, $filter);
564
565
                    if (null !== $this->codeCoverageConfig) {
566
                        // Set code coverage configuration
567
                        if ($this->codeCoverageConfig->hasCacheDirectory()) {
568
                            $codeCoverage->cacheStaticAnalysis($this->codeCoverageConfig->cacheDirectory()->path());
569
                        }
570
                        if ($this->codeCoverageConfig->ignoreDeprecatedCodeUnits()) {
571
                            $codeCoverage->ignoreDeprecatedCode();
572
                        } else {
573
                            $codeCoverage->doNotIgnoreDeprecatedCode();
574
                        }
575
                        if ($this->codeCoverageConfig->includeUncoveredFiles()) {
576
                            $codeCoverage->includeUncoveredFiles();
577
                        } else {
578
                            $codeCoverage->doNotProcessUncoveredFiles();
579
                        }
580
                    }
581
582
                    $runner->setCodecoverage($codeCoverage);
583
                }
584
            }
585
        }
586
587 5
        $runner->setUseCustomErrorHandler($this->usecustomerrorhandler);
588
589 5
        foreach ($this->listeners as $listener) {
590
            $runner->addListener($listener);
0 ignored issues
show
Bug introduced by
The method addListener() does not exist on Phing\Task\Ext\PhpUnit\PHPUnitTestRunner9. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

590
            $runner->/** @scrutinizer ignore-call */ 
591
                     addListener($listener);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
591
        }
592
593 5
        foreach ($this->formatters as $fe) {
594 2
            $formatter = $fe->getFormatter();
595
596 2
            if ($fe->getUseFile()) {
597
                try {
598
                    $destFile = new File($fe->getToDir(), $fe->getOutfile());
599
                } catch (Exception $e) {
600
                    throw new BuildException('Unable to create destination.', $e);
601
                }
602
603
                $writer = new FileWriter($destFile->getAbsolutePath());
604
605
                $formatter->setOutput($writer);
606
            } else {
607 2
                $formatter->setOutput($this->getDefaultOutput());
608
            }
609
610 2
            $runner->addFormatter($formatter);
611
612 2
            $formatter->startTestRun();
613
        }
614
615 5
        $runner->run($suite);
616
617 5
        foreach ($this->formatters as $fe) {
618 2
            $formatter = $fe->getFormatter();
619 2
            $formatter->endTestRun();
620
        }
621
622 5
        if ($runner->hasErrors()) {
623 2
            if ($this->errorproperty) {
624
                $this->project->setNewProperty($this->errorproperty, true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $value of Phing\Project::setNewProperty(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

624
                $this->project->setNewProperty($this->errorproperty, /** @scrutinizer ignore-type */ true);
Loading history...
625
            }
626 2
            if ($this->haltonerror || $this->haltondefect) {
627
                $this->testfailed = true;
628
                $this->testfailuremessage = $runner->getLastErrorMessage();
629
            }
630
        }
631
632 5
        if ($runner->hasFailures()) {
633 3
            if ($this->failureproperty) {
634
                $this->project->setNewProperty($this->failureproperty, true);
635
            }
636
637 3
            if ($this->haltonfailure || $this->haltondefect) {
638 1
                $this->testfailed = true;
639 1
                $this->testfailuremessage = $runner->getLastFailureMessage();
640
            }
641
        }
642
643 5
        if ($runner->hasIncomplete()) {
644
            if ($this->incompleteproperty) {
645
                $this->project->setNewProperty($this->incompleteproperty, true);
646
            }
647
648
            if ($this->haltonincomplete) {
649
                $this->testfailed = true;
650
                $this->testfailuremessage = $runner->getLastIncompleteMessage();
651
            }
652
        }
653
654 5
        if ($runner->hasSkipped()) {
655
            if ($this->skippedproperty) {
656
                $this->project->setNewProperty($this->skippedproperty, true);
657
            }
658
659
            if ($this->haltonskipped) {
660
                $this->testfailed = true;
661
                $this->testfailuremessage = $runner->getLastSkippedMessage();
662
            }
663
        }
664
665 5
        if ($runner->hasWarnings()) {
666
            if ($this->warningproperty) {
667
                $this->project->setNewProperty($this->warningproperty, true);
668
            }
669
670
            if ($this->haltonwarning || $this->haltondefect) {
671
                $this->testfailed = true;
672
                $this->testfailuremessage = $runner->getLastWarningMessage();
673
            }
674
        }
675
676 5
        if ($runner->hasRisky()) {
677
            if ($this->riskyproperty) {
678
                $this->project->setNewProperty($this->riskyproperty, true);
679
            }
680
681
            if ($this->haltonrisky) {
682
                $this->testfailed = true;
683
                $this->testfailuremessage = $runner->getLastRiskyMessage();
684
            }
685
        }
686
    }
687
688
    /**
689
     * Add the tests in this batchtest to a test suite
690
     *
691
     * @param BatchTest $batchTest
692
     * @param TestSuite $suite
693
     * @throws BuildException
694
     * @throws ReflectionException
695
     */
696 5
    protected function appendBatchTestToTestSuite(BatchTest $batchTest, $suite)
697
    {
698 5
        foreach ($batchTest->elements() as $element) {
699 5
            $testClass = new $element();
700 5
            if (!($testClass instanceof TestSuite)) {
701 5
                $testClass = new ReflectionClass($element);
702
            }
703
            try {
704 5
                $suite->addTestSuite($testClass);
705
            } catch (\PHPUnit\Framework\Exception $e) {
706
                throw new BuildException('Unable to add TestSuite ' . get_class($testClass), $e);
707
            }
708
        }
709
    }
710
711
    /**
712
     * @return LogWriter
713
     */
714 2
    protected function getDefaultOutput()
715
    {
716 2
        return new LogWriter($this);
717
    }
718
719
    /**
720
     * Adds a set of tests based on pattern matching.
721
     *
722
     * @return BatchTest a new instance of a batch test.
723
     */
724 5
    public function createBatchTest()
725
    {
726 5
        $batchtest = new BatchTest($this->getProject());
727
728 5
        $this->batchtests[] = $batchtest;
729
730 5
        return $batchtest;
731
    }
732
}
733