1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\Console\Tests\EventListener;
13
14use PHPUnit\Framework\TestCase;
15use Psr\Log\LoggerInterface;
16use Symfony\Component\Console\Command\Command;
17use Symfony\Component\Console\Event\ConsoleErrorEvent;
18use Symfony\Component\Console\Event\ConsoleTerminateEvent;
19use Symfony\Component\Console\EventListener\ErrorListener;
20use Symfony\Component\Console\Input\ArgvInput;
21use Symfony\Component\Console\Input\ArrayInput;
22use Symfony\Component\Console\Input\Input;
23use Symfony\Component\Console\Input\InputInterface;
24use Symfony\Component\Console\Input\StringInput;
25use Symfony\Component\Console\Output\OutputInterface;
26
27class ErrorListenerTest extends TestCase
28{
29    public function testOnConsoleError()
30    {
31        $error = new \TypeError('An error occurred');
32
33        $logger = $this->getLogger();
34        $logger
35            ->expects($this->once())
36            ->method('error')
37            ->with('Error thrown while running command "{command}". Message: "{message}"', ['exception' => $error, 'command' => 'test:run --foo=baz buzz', 'message' => 'An error occurred'])
38        ;
39
40        $listener = new ErrorListener($logger);
41        $listener->onConsoleError(new ConsoleErrorEvent(new ArgvInput(['console.php', 'test:run', '--foo=baz', 'buzz']), $this->getOutput(), $error, new Command('test:run')));
42    }
43
44    public function testOnConsoleErrorWithNoCommandAndNoInputString()
45    {
46        $error = new \RuntimeException('An error occurred');
47
48        $logger = $this->getLogger();
49        $logger
50            ->expects($this->once())
51            ->method('error')
52            ->with('An error occurred while using the console. Message: "{message}"', ['exception' => $error, 'message' => 'An error occurred'])
53        ;
54
55        $listener = new ErrorListener($logger);
56        $listener->onConsoleError(new ConsoleErrorEvent(new NonStringInput(), $this->getOutput(), $error));
57    }
58
59    public function testOnConsoleTerminateForNonZeroExitCodeWritesToLog()
60    {
61        $logger = $this->getLogger();
62        $logger
63            ->expects($this->once())
64            ->method('debug')
65            ->with('Command "{command}" exited with code "{code}"', ['command' => 'test:run', 'code' => 255])
66        ;
67
68        $listener = new ErrorListener($logger);
69        $listener->onConsoleTerminate($this->getConsoleTerminateEvent(new ArgvInput(['console.php', 'test:run']), 255));
70    }
71
72    public function testOnConsoleTerminateForZeroExitCodeDoesNotWriteToLog()
73    {
74        $logger = $this->getLogger();
75        $logger
76            ->expects($this->never())
77            ->method('debug')
78        ;
79
80        $listener = new ErrorListener($logger);
81        $listener->onConsoleTerminate($this->getConsoleTerminateEvent(new ArgvInput(['console.php', 'test:run']), 0));
82    }
83
84    public function testGetSubscribedEvents()
85    {
86        $this->assertEquals(
87            [
88                'console.error' => ['onConsoleError', -128],
89                'console.terminate' => ['onConsoleTerminate', -128],
90            ],
91            ErrorListener::getSubscribedEvents()
92        );
93    }
94
95    public function testAllKindsOfInputCanBeLogged()
96    {
97        $logger = $this->getLogger();
98        $logger
99            ->expects($this->exactly(3))
100            ->method('debug')
101            ->with('Command "{command}" exited with code "{code}"', ['command' => 'test:run --foo=bar', 'code' => 255])
102        ;
103
104        $listener = new ErrorListener($logger);
105        $listener->onConsoleTerminate($this->getConsoleTerminateEvent(new ArgvInput(['console.php', 'test:run', '--foo=bar']), 255));
106        $listener->onConsoleTerminate($this->getConsoleTerminateEvent(new ArrayInput(['name' => 'test:run', '--foo' => 'bar']), 255));
107        $listener->onConsoleTerminate($this->getConsoleTerminateEvent(new StringInput('test:run --foo=bar'), 255));
108    }
109
110    public function testCommandNameIsDisplayedForNonStringableInput()
111    {
112        $logger = $this->getLogger();
113        $logger
114            ->expects($this->once())
115            ->method('debug')
116            ->with('Command "{command}" exited with code "{code}"', ['command' => 'test:run', 'code' => 255])
117        ;
118
119        $listener = new ErrorListener($logger);
120        $listener->onConsoleTerminate($this->getConsoleTerminateEvent($this->getMockBuilder(InputInterface::class)->getMock(), 255));
121    }
122
123    private function getLogger()
124    {
125        return $this->getMockForAbstractClass(LoggerInterface::class);
126    }
127
128    private function getConsoleTerminateEvent(InputInterface $input, $exitCode)
129    {
130        return new ConsoleTerminateEvent(new Command('test:run'), $input, $this->getOutput(), $exitCode);
131    }
132
133    private function getOutput()
134    {
135        return $this->getMockBuilder(OutputInterface::class)->getMock();
136    }
137}
138
139class NonStringInput extends Input
140{
141    public function getFirstArgument()
142    {
143    }
144
145    public function hasParameterOption($values, $onlyParams = false)
146    {
147    }
148
149    public function getParameterOption($values, $default = false, $onlyParams = false)
150    {
151    }
152
153    public function parse()
154    {
155    }
156}
157