Codeception::canExecuteOnStage()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
namespace Fabrica\Tools\Plugin;
4
5
use Fabrica\Tools\Builder;
6
use Fabrica\Models\Infra\Ci\Build;
7
use Fabrica\Tools\Plugin\Util\TestResultParsers\Codeception as Parser;
8
use Fabrica\Tools\Plugin;
9
use Symfony\Component\Yaml\Parser as YamlParser;
10
use Fabrica\Tools\ZeroConfigPluginInterface;
11
12
/**
13
 * Codeception Plugin - Enables full acceptance, unit, and functional testing.
14
 *
15
 * @author Don Gilbert <[email protected]>
16
 * @author Igor Timoshenko <[email protected]>
17
 * @author Adam Cooper <[email protected]>
18
 */
19
class Codeception extends Plugin implements ZeroConfigPluginInterface
20
{
21
    /**
22
     * @var string
23
     */
24
    protected $args = '';
25
26
    /**
27
     * @var string $ymlConfigFile The path of a yml config for Codeception
28
     */
29
    protected $ymlConfigFile;
30
31
    /**
32
     * default sub-path for report.xml file
33
     *
34
     * @var array $path The path to the report.xml file
35
     */
36
    protected $outputPath = [
37
        'tests/_output',
38
        'tests/_log',
39
    ];
40
41
    /**
42
     * @return string
43
     */
44
    public static function pluginName()
45
    {
46
        return 'codeception';
47
    }
48
49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function __construct(Builder $builder, Build $build, array $options = [])
53
    {
54
        parent::__construct($builder, $build, $options);
55
56
        if (empty($options['config'])) {
57
            $this->ymlConfigFile = self::findConfigFile($this->directory);
58
        } else {
59
            $this->ymlConfigFile = $this->directory . $options['config'];
60
        }
61
62
        if (isset($options['args'])) {
63
            $this->args = (string) $options['args'];
64
        }
65
66
        /**
67
 * @deprecated Option "path" is deprecated and will be deleted in version 2.0. Use the option "output_path" instead. 
68
*/
69
        if (isset($options['path']) && !isset($options['output_path'])) {
70
            $this->builder->logWarning(
71
                '[DEPRECATED] Option "path" is deprecated and will be deleted in version 2.0. Use the option "output_path" instead.'
72
            );
73
74
            $options['output_path'] = $options['path'];
75
        }
76
77
        if (isset($options['output_path'])) {
78
            array_unshift($this->outputPath, $options['output_path']);
79
        }
80
81
        if (isset($options['executable'])) {
82
            $this->executable = $options['executable'];
83
        } else {
84
            $this->executable = $this->findBinary('codecept');
85
        }
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91
    public static function canExecuteOnStage($stage, Build $build)
92
    {
93
        return (Build::STAGE_TEST === $stage && !is_null(self::findConfigFile($build->getBuildPath())));
94
    }
95
96
    /**
97
     * Try and find the codeception YML config file.
98
     *
99
     * @param  $buildPath
100
     * @return null|string
101
     */
102
    public static function findConfigFile($buildPath)
103
    {
104
        if (file_exists($buildPath . 'codeception.yml')) {
105
            return $buildPath . 'codeception.yml';
106
        }
107
108
        if (file_exists($buildPath . 'codeception.dist.yml')) {
109
            return $buildPath . 'codeception.dist.yml';
110
        }
111
112
        return null;
113
    }
114
115
    /**
116
     * Runs Codeception tests
117
     */
118
    public function execute()
119
    {
120
        if (empty($this->ymlConfigFile)) {
121
            throw new \Exception("No configuration file found");
122
        }
123
124
        // Run any config files first. This can be either a single value or an array.
125
        return $this->runConfigFile();
126
    }
127
128
    /**
129
     * Run tests from a Codeception config file.
130
     *
131
     * @return bool|mixed
132
     * @throws \Exception
133
     */
134
    protected function runConfigFile()
135
    {
136
        $codeception = $this->executable;
137
138
        if (!$codeception) {
139
            $this->builder->logFailure(sprintf('Could not find "%s" binary', 'codecept'));
140
141
            return false;
142
        }
143
144
        $cmd = 'cd "%s" && ' . $codeception . ' run -c "%s" ' . $this->args . ' --xml';
145
146
        $success = $this->builder->executeCommand($cmd, $this->directory, $this->ymlConfigFile);
147
        if (!$success) {
148
            return false;
149
        }
150
151
        $parser = new YamlParser();
152
        $yaml   = file_get_contents($this->ymlConfigFile);
153
        $config = (array)$parser->parse($yaml);
154
155
        $trueReportXmlPath = null;
156
        if ($config && isset($config['paths']['log'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $config of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
157
            $trueReportXmlPath = rtrim($config['paths']['log'], '/\\') . '/';
158
        }
159
160
        if (!file_exists($trueReportXmlPath . 'report.xml')) {
161
            foreach ($this->outputPath as $outputPath) {
162
                $trueReportXmlPath = rtrim($outputPath, '/\\') . '/';
163
                if (file_exists($trueReportXmlPath . 'report.xml')) {
164
                    break;
165
                }
166
            }
167
        }
168
169
        if (!file_exists($trueReportXmlPath . 'report.xml')) {
170
            $this->builder->logFailure('"report.xml" file can not be found in configured "output_path!"');
171
172
            return false;
173
        }
174
175
        $parser = new Parser($this->builder, ($trueReportXmlPath . 'report.xml'));
176
        $output = $parser->parse();
177
178
        $meta = [
179
            'tests'     => $parser->getTotalTests(),
180
            'timetaken' => $parser->getTotalTimeTaken(),
181
            'failures'  => $parser->getTotalFailures(),
182
        ];
183
184
        // NOTE: Codeception does not use stderr, so failure can only be detected
185
        // through tests
186
        $success = $success && (intval($meta['failures']) < 1);
187
188
        $this->build->storeMeta((self::pluginName() . '-meta'), $meta);
189
        $this->build->storeMeta((self::pluginName() . '-data'), $output);
190
        $this->build->storeMeta((self::pluginName() . '-errors'), $parser->getTotalFailures());
191
192
        return $success;
193
    }
194
}
195