Completed
Push — master ( df923f...c7bb10 )
by Benjamin
02:32
created

DockerChrome::stopServer()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 6
cts 6
cp 1
rs 9.6666
c 0
b 0
f 0
cc 3
eloc 5
nc 2
nop 0
crap 3
1
<?php
2
/**
3
 * This file is part of the teamneusta/codeception-docker-chrome package.
4
 *
5
 * Copyright (c) 2017 neusta GmbH | Ein team neusta Unternehmen
6
 *
7
 * For the full copyright and license information, please view the LICENSE file that was distributed with this source code.
8
 *
9
 * @license http://www.opensource.org/licenses/mit-license.html  MIT License
10
 */
11
12
namespace Codeception\Extension;
13
14
use Codeception\Exception\ExtensionException;
15
use Codeception\Platform\Extension;
16
use Symfony\Component\Process\Process;
17
use Symfony\Component\Yaml\Yaml;
18
19
class DockerChrome extends Extension
20
{
21
    /**
22
     * events
23
     *
24
     * @var array
25
     */
26
    static public $events = [
27
        'module.init' => 'moduleInit',
28
    ];
29
30
    /**
31
     * process
32
     *
33
     * @var \Symfony\Component\Process\Process
34
     */
35
    private $process;
36
37
    /**
38
     * dockerStarted
39
     *
40
     * @var bool
41
     */
42
    private $dockerStarted = false;
43
44
    /**
45
     * dockerComposePath
46
     *
47
     * @var string
48
     */
49
    private $dockerComposePath;
50
51
    /**
52
     * DockerChrome constructor.
53
     *
54
     * @param array $config
55
     * @param array $options
56
     * @param \Symfony\Component\Process\Process|null $process
57
     * @param string $defaultDockerComposePath
58
     */
59 14
    public function __construct(
60
        array $config,
61
        array $options,
62
        Process $process = null,
63
        string $defaultDockerComposePath = '/usr/local/bin/docker-compose'
64
    ) {
65
66
        // Set default https proxy
67 14
        if (!isset($options['silent'])) {
68 14
            $options['silent'] = false;
69
        }
70
71 14
        $this->dockerComposePath = $defaultDockerComposePath;
72
73 14
        parent::__construct($config, $options);
74
75 14
        $this->initDefaultConfig();
76 14
        $command = $this->getCommand();
77 14
        $this->process = $process ?: new Process($command, realpath(__DIR__));
78 14
    }
79
80
    /**
81
     * initDefaultConfig
82
     *
83
     * @return void
84
     * @throws \Codeception\Exception\ExtensionException
85
     */
86 14
    protected function initDefaultConfig()
87
    {
88 14
        $this->config['path'] = $this->config['path'] ?? $this->dockerComposePath;
89
90
        // Set default WebDriver port
91 14
        $this->config['port'] = $this->config['port'] ?? 4444;
92
93
        // Set default debug mode
94 14
        $this->config['debug'] = $this->config['debug'] ?? false;
95
96
        // Set default http proxy
97 14
        $this->config['http_proxy'] = $this->config['http_proxy'] ?? '';
98
99
        // Set default https proxy
100 14
        $this->config['https_proxy'] = $this->config['https_proxy'] ?? '';
101
102 14
        if (!file_exists($this->config['path'])) {
103 2
            throw new ExtensionException($this, "File not found: {$this->config['path']}.");
104
        }
105 14
    }
106
107
    /**
108
     * getCommand
109
     *
110
     * @return string
111
     */
112 14
    private function getCommand(): string
113
    {
114 14
        return 'exec ' . escapeshellarg(realpath($this->config['path'])) . ' up';
115
    }
116
117
    /**
118
     * getConfig
119
     *
120
     * @return array
121
     */
122 7
    public function getConfig(): array
123
    {
124 7
        return $this->config;
125
    }
126
127
    /**
128
     *
129
     */
130 9
    public function __destruct()
131
    {
132 9
        $this->stopServer();
133 9
    }
134
135
    /**
136
     * stopServer
137
     *
138
     * @return void
139
     */
140 9
    private function stopServer()
141
    {
142 9
        if ($this->process && $this->process->isRunning()) {
143 1
            $this->write('Stopping Docker Server');
144
145 1
            $this->process->signal(2);
146 1
            $this->process->wait();
147
        }
148 9
    }
149
150
    /**
151
     * moduleInit
152
     *
153
     * @param \Codeception\Event\SuiteEvent $e
154
     * @return void
155
     */
156 4
    public function moduleInit(\Codeception\Event\SuiteEvent $e)
157
    {
158 4
        if (!$this->suiteAllowed($e)) {
159 1
            return;
160
        }
161
162 3
        $this->overrideWithModuleConfig($e);
163 3
        $this->generateYaml();
164 3
        $this->startServer();
165 2
    }
166
167
    /**
168
     * suiteAllowed
169
     *
170
     * @param \Codeception\Event\SuiteEvent $e
171
     * @return bool
172
     */
173 4
    protected function suiteAllowed(\Codeception\Event\SuiteEvent $e): bool
174
    {
175 4
        $allowed = true;
176 4
        if (isset($this->config['suites'])) {
177 1
            $suites = (array)$this->config['suites'];
178
179 1
            $e->getSuite()->getBaseName();
180
181 1
            if (!in_array($e->getSuite()->getBaseName(), $suites)
182 1
                && !in_array($e->getSuite()->getName(), $suites)
183
            ) {
184 1
                $allowed = false;
185
            }
186
        }
187
188 4
        return $allowed;
189
    }
190
191
    /**
192
     * overrideWithModuleConfig
193
     *
194
     * @param \Codeception\Event\SuiteEvent $e
195
     * @return void
196
     */
197 3
    protected function overrideWithModuleConfig(\Codeception\Event\SuiteEvent $e)
198
    {
199 3
        $modules = array_filter($e->getSettings()['modules']['enabled']);
200 3
        foreach ($modules as $module) {
201 1
            if (is_array($module)) {
202 1
                $moduleSettings = current($module);
203 1
                $this->config['port'] = $moduleSettings['port'] ?? $this->config['port'];
204 1
                $this->config['http_proxy'] = $moduleSettings['capabilities']['httpProxy'] ?? $this->config['http_proxy'];
205 1
                $this->config['https_proxy'] = $moduleSettings['capabilities']['sslProxy'] ?? $this->config['https_proxy'];
206
            }
207
        }
208 3
    }
209
210
    /**
211
     * generateYaml
212
     *
213
     * @return void
214
     */
215 3
    protected function generateYaml()
216
    {
217 3
        $environment = [];
218
219 3
        if (!empty($this->config['http_proxy'])) {
220 1
            $environment[] = 'http_proxy=' . $this->config['http_proxy'];
221
        }
222 3
        if (!empty($this->config['https_proxy'])) {
223 1
            $environment[] = 'https_proxy=' . $this->config['https_proxy'];
224
        }
225
226
        $dockerYaml = [
227
            'hub'    => [
228 3
                'image'       => 'selenium/hub',
229 3
                'ports'       => [$this->config['port'] . ':4444'],
230 3
                'environment' => $environment
231 3
            ],
232
            'chrome' => [
233
                'volumes'     => ['/dev/shm:/dev/shm'],
234 3
                'image'       => 'selenium/node-chrome',
235
                'links'       => ['hub'],
236 3
                'environment' => $environment
237
            ]
238
        ];
239
240 3
        file_put_contents(__DIR__ . '/docker-compose.yml', Yaml::dump($dockerYaml));
241 3
    }
242
243
    /**
244
     * startServer
245
     *
246
     * @return void
247
     * @throws \Codeception\Exception\ExtensionException
248
     */
249 3
    private function startServer()
250
    {
251 3
        if ($this->config['debug']) {
252 3
            $this->writeln(['Generated Docker Command:', $this->process->getCommandLine()]);
253
        }
254 3
        $this->writeln('Starting Docker Server');
255 3
        $this->process->start(function($type, $buffer) {
256 2
            if (strpos($buffer, 'Registered a node') !== false) {
257 2
                $this->dockerStarted = true;
258
            }
259 3
        });
260
261
        // wait until docker is finished to start
262 3
        while ($this->process->isRunning() && !$this->dockerStarted) {
263
        }
264
265 3
        if (!$this->process->isRunning()) {
266 1
            throw new ExtensionException($this, 'Failed to start Docker server.');
267
        }
268 2
        $this->writeln(['', 'Docker server now accessible']);
269 2
    }
270
}
271