Test Failed
Push — feature/html-code-handler ( 69c349...0406d3 )
by Arnaud
02:18
created

SmokeCommand::match()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 4
nop 1
dl 0
loc 16
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace LAG\SmokerBundle\Command;
4
5
use LAG\SmokerBundle\Exception\Exception;
6
use LAG\SmokerBundle\Message\MessageCollectorInterface;
7
use LAG\SmokerBundle\Response\Registry\ResponseHandlerRegistry;
8
use Goutte\Client;
9
use LAG\SmokerBundle\Url\Registry\UrlProviderRegistry;
10
use Symfony\Component\BrowserKit\Response;
11
use Symfony\Component\Console\Command\Command;
12
use Symfony\Component\Console\Helper\Helper;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Input\InputOption;
15
use Symfony\Component\Console\Output\Output;
16
use Symfony\Component\Console\Output\OutputInterface;
17
use Symfony\Component\Console\Style\SymfonyStyle;
18
use Symfony\Component\DomCrawler\Crawler;
19
use Symfony\Component\Filesystem\Filesystem;
20
21
class SmokeCommand extends Command
22
{
23
    protected static $defaultName = 'smoker:smoke';
24
25
    /**
26
     * @var string
27
     */
28
    protected $cacheDir;
29
30
    /**
31
     * @var ResponseHandlerRegistry
32
     */
33
    protected $responseHandlerRegistry;
34
35
    /**
36
     * @var string
37
     */
38
    protected $cacheFile;
39
40
    /**
41
     * @var \Twig_Environment
42
     */
43
    protected $twig;
44
45
    /**
46
     * @var Filesystem
47
     */
48
    protected $fileSystem;
49
50
    /**
51
     * @var SymfonyStyle
52
     */
53
    protected $io;
54
55
    /**
56
     * @var UrlProviderRegistry
57
     */
58
    protected $urlProviderRegistry;
59
60
    /**
61
     * @var MessageCollectorInterface
62
     */
63
    protected $messageCollector;
64
65
    /**
66
     * @var Client
67
     */
68
    protected $client;
69
70
    /**
71
     * @var string
72
     */
73
    protected $routing;
74
75
    /**
76
     * @var bool
77
     */
78
    protected $stopOnFailure = false;
79
80
    /**
81
     * @var array
82
     */
83
    protected $routes;
84
85
    /**
86
     * SmokeCommand constructor.
87
     *
88
     * @param string                    $cacheDir
89
     * @param array                     $routing
90
     * @param array                     $routes
91
     * @param ResponseHandlerRegistry   $responseHandlerRegistry
92
     * @param UrlProviderRegistry       $urlProviderRegistry
93
     * @param MessageCollectorInterface $messageCollector
94
     * @param \Twig_Environment         $twig
95
     */
96
    public function __construct(
97
        string $cacheDir,
98
        array $routing,
99
        array $routes,
100
        ResponseHandlerRegistry $responseHandlerRegistry,
101
        UrlProviderRegistry $urlProviderRegistry,
102
        MessageCollectorInterface $messageCollector,
103
        \Twig_Environment $twig
104
    ) {
105
        parent::__construct();
106
107
        $this->cacheDir = $cacheDir;
108
        $this->responseHandlerRegistry = $responseHandlerRegistry;
109
        $this->twig = $twig;
110
        $this->fileSystem = new Filesystem();
111
        $this->urlProviderRegistry = $urlProviderRegistry;
112
        $this->messageCollector = $messageCollector;
113
        $this->routing = $routing;
0 ignored issues
show
Documentation Bug introduced by
It seems like $routing of type array is incompatible with the declared type string of property $routing.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
114
        $this->routes = $routes;
115
    }
116
117
    protected function configure()
118
    {
119
        $this
120
            ->addOption('stop-on-failure', null, InputOption::VALUE_NONE, 'Stop all tests if an error is detected')
121
            ->addOption('host', null, InputOption::VALUE_REQUIRED, 'The host called by smoker')
122
        ;
123
    }
124
125
    protected function execute(InputInterface $input, OutputInterface $output)
126
    {
127
        $this->io = new SymfonyStyle($input, $output);
128
        $this->io->title('Smoker Tests');
129
130
        $this->initializeCommand($input);
131
        $this->smoke();
132
        $this->generateResults();
133
    }
134
135
    protected function initializeCommand(InputInterface $input)
136
    {
137
        $this->io->note('Initialize results cache...');
138
        $this->cacheFile = $this->cacheDir.'/smoker/smoker.cache';
139
        $this->messageCollector->initialize();
140
141
        if ($input->getOption('host')) {
142
            $this->routing = $input->getOption('host');
143
        }
144
145
        if ($input->getOption('stop-on-failure')) {
146
            $this->stopOnFailure = true;
147
        }
148
149
        // Create a new client. Cookies are by default enabled
150
        $this->client = new Client();
151
        $this->client->followRedirects(false);
152
    }
153
154
    protected function smoke()
155
    {
156
        if (!$this->fileSystem->exists($this->cacheFile)) {
157
            $this->io->warning('The cache file is not generated. Nothing will be done.');
158
            $this->io->note('The cache can be generated with the command bin/console smoker:generate-cache');
159
160
            return;
161
        }
162
        $handle = fopen($this->cacheFile, 'r');
163
164
        if ($handle) {
0 ignored issues
show
introduced by
$handle is of type false|resource, thus it always evaluated to false.
Loading history...
165
            $this->io->text('Start reading urls in cache...');
166
167
            while (false !== ($row = fgets($handle, 4096))) {
168
                $data = unserialize($row);
169
                $this->processRow($data['location']);
170
171
                if (Output::VERBOSITY_DEBUG === $this->io->getVerbosity()) {
172
                    $this->io->write('  '.Helper::formatMemory(memory_get_usage(true)));
173
                }
174
                $this->io->newLine();
175
                gc_collect_cycles();
176
            }
177
178
            if (!feof($handle)) {
179
                $this->io->error('An error has occurred when reading the cache');
180
            }
181
            fclose($handle);
182
        }
183
    }
184
185
    protected function generateResults()
186
    {
187
        $this->io->note('Generating results report...');
188
        $this->messageCollector->flush();
189
        $messages = $this->messageCollector->read();
190
191
        $content = $this->twig->render('@LAGSmoker/Results/results.html.twig', [
192
            'messages' => $messages,
193
        ]);
194
        $this->fileSystem->dumpFile($this->cacheDir.'/smoker/results.html', $content);
195
        $this->io->text('The results report has been generated here file://'.$this->cacheDir.'/smoker/results.html');
196
    }
197
198
    /**
199
     * @param string $location
200
     */
201
    protected function processRow(string $location): void
202
    {
203
        $this->io->write('Processing '.$location.'...');
204
205
        // Create a new empty client and fetch the request data
206
        $crawler = $this->client->request('get', $location);
207
        /** @var Response $response */
208
        $response = $this->client->getResponse();
209
210
        try {
211
            $urlInfo = $this->urlProviderRegistry->match($location);
212
        } catch (Exception $exception) {
213
            $this
214
                ->messageCollector
215
                ->addError(
216
                    $location,
217
                    $exception->getMessage(),
218
                    500,
219
                    $exception
220
                );
221
            $this->io->write('...[<error>KO</error>]');
222
            $this->messageCollector->flush();
223
224
            return;
225
        }
226
        $responseHandled = $this->handleResponse($urlInfo->routeName, $location, $crawler, $response);
227
228
        if (!$responseHandled) {
229
            $this->io->write('...[<comment>WARN</comment>]');
230
            $this
231
                ->messageCollector
232
                ->addWarning($location, 'The response of the url is not handle by any response handler')
233
            ;
234
        }
235
236
        $this->messageCollector->flush();
237
    }
238
239
    /**
240
     * @param string   $routeName
241
     * @param string   $location
242
     * @param Crawler  $crawler
243
     * @param Response $response
244
     *
245
     * @return bool
246
     *
247
     * @throws \Exception
248
     */
249
    protected function handleResponse(string $routeName, string $location, Crawler $crawler, Response $response): bool
250
    {
251
        $responseHandled = false;
252
253
        foreach ($this->responseHandlerRegistry->all() as $responseHandler) {
254
            if (!$responseHandler->supports($routeName)) {
255
                continue;
256
            }
257
            $responseHandled = true;
258
259
            try {
260
                $routeOptions = $this->routes[$routeName];
261
                $providerOptions = [];
262
263
                if (key_exists($responseHandler->getName(), $routeOptions['handlers'])) {
264
                    $providerOptions = $routeOptions['handlers'][$responseHandler->getName()];
265
266
                    if (!is_array($providerOptions)) {
267
                        $providerOptions = [
268
                            $providerOptions,
269
                        ];
270
                    }
271
                }
272
                $responseHandler->handle($routeName, $crawler, $this->client, $providerOptions);
273
274
                $this->io->write('...[<info>OK</info>]');
275
                $this
276
                    ->messageCollector
277
                    ->addSuccess($location, 'Success for handler', $response->getStatus())
278
                ;
279
            } catch (\Exception $exception) {
280
                $url = $this->routing.$location;
281
                $message = sprintf(
282
                    'An error has occurred when processing the url %s',
283
                    $url
284
                );
285
                $this
286
                    ->messageCollector
287
                    ->addError($location, $message, $response->getStatus(), $exception)
288
                ;
289
                if ($this->stopOnFailure) {
290
                    $this->generateResults();
291
292
                    throw $exception;
293
                }
294
                $this->io->write('...[<error>KO</error>]');
295
            }
296
        }
297
298
        return $responseHandled;
299
    }
300
}
301