Security update for Core, with self-updated composer
[yaffs-website] / vendor / symfony / console / Tests / Command / CommandTest.php
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
12 namespace Symfony\Component\Console\Tests\Command;
13
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\Console\Command\Command;
16 use Symfony\Component\Console\Helper\FormatterHelper;
17 use Symfony\Component\Console\Application;
18 use Symfony\Component\Console\Input\InputDefinition;
19 use Symfony\Component\Console\Input\InputArgument;
20 use Symfony\Component\Console\Input\InputOption;
21 use Symfony\Component\Console\Input\InputInterface;
22 use Symfony\Component\Console\Input\StringInput;
23 use Symfony\Component\Console\Output\OutputInterface;
24 use Symfony\Component\Console\Output\NullOutput;
25 use Symfony\Component\Console\Tester\CommandTester;
26
27 class CommandTest extends TestCase
28 {
29     protected static $fixturesPath;
30
31     public static function setUpBeforeClass()
32     {
33         self::$fixturesPath = __DIR__.'/../Fixtures/';
34         require_once self::$fixturesPath.'/TestCommand.php';
35     }
36
37     public function testConstructor()
38     {
39         $command = new Command('foo:bar');
40         $this->assertEquals('foo:bar', $command->getName(), '__construct() takes the command name as its first argument');
41     }
42
43     /**
44      * @expectedException        \LogicException
45      * @expectedExceptionMessage The command defined in "Symfony\Component\Console\Command\Command" cannot have an empty name.
46      */
47     public function testCommandNameCannotBeEmpty()
48     {
49         new Command();
50     }
51
52     public function testSetApplication()
53     {
54         $application = new Application();
55         $command = new \TestCommand();
56         $command->setApplication($application);
57         $this->assertEquals($application, $command->getApplication(), '->setApplication() sets the current application');
58         $this->assertEquals($application->getHelperSet(), $command->getHelperSet());
59     }
60
61     public function testSetApplicationNull()
62     {
63         $command = new \TestCommand();
64         $command->setApplication(null);
65         $this->assertNull($command->getHelperSet());
66     }
67
68     public function testSetGetDefinition()
69     {
70         $command = new \TestCommand();
71         $ret = $command->setDefinition($definition = new InputDefinition());
72         $this->assertEquals($command, $ret, '->setDefinition() implements a fluent interface');
73         $this->assertEquals($definition, $command->getDefinition(), '->setDefinition() sets the current InputDefinition instance');
74         $command->setDefinition(array(new InputArgument('foo'), new InputOption('bar')));
75         $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument');
76         $this->assertTrue($command->getDefinition()->hasOption('bar'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument');
77         $command->setDefinition(new InputDefinition());
78     }
79
80     public function testAddArgument()
81     {
82         $command = new \TestCommand();
83         $ret = $command->addArgument('foo');
84         $this->assertEquals($command, $ret, '->addArgument() implements a fluent interface');
85         $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->addArgument() adds an argument to the command');
86     }
87
88     public function testAddOption()
89     {
90         $command = new \TestCommand();
91         $ret = $command->addOption('foo');
92         $this->assertEquals($command, $ret, '->addOption() implements a fluent interface');
93         $this->assertTrue($command->getDefinition()->hasOption('foo'), '->addOption() adds an option to the command');
94     }
95
96     public function testSetHidden()
97     {
98         $command = new \TestCommand();
99         $command->setHidden(true);
100         $this->assertTrue($command->isHidden());
101     }
102
103     public function testGetNamespaceGetNameSetName()
104     {
105         $command = new \TestCommand();
106         $this->assertEquals('namespace:name', $command->getName(), '->getName() returns the command name');
107         $command->setName('foo');
108         $this->assertEquals('foo', $command->getName(), '->setName() sets the command name');
109
110         $ret = $command->setName('foobar:bar');
111         $this->assertEquals($command, $ret, '->setName() implements a fluent interface');
112         $this->assertEquals('foobar:bar', $command->getName(), '->setName() sets the command name');
113     }
114
115     /**
116      * @dataProvider provideInvalidCommandNames
117      */
118     public function testInvalidCommandNames($name)
119     {
120         if (method_exists($this, 'expectException')) {
121             $this->expectException('InvalidArgumentException');
122             $this->expectExceptionMessage(sprintf('Command name "%s" is invalid.', $name));
123         } else {
124             $this->setExpectedException('InvalidArgumentException', sprintf('Command name "%s" is invalid.', $name));
125         }
126
127         $command = new \TestCommand();
128         $command->setName($name);
129     }
130
131     public function provideInvalidCommandNames()
132     {
133         return array(
134             array(''),
135             array('foo:'),
136         );
137     }
138
139     public function testGetSetDescription()
140     {
141         $command = new \TestCommand();
142         $this->assertEquals('description', $command->getDescription(), '->getDescription() returns the description');
143         $ret = $command->setDescription('description1');
144         $this->assertEquals($command, $ret, '->setDescription() implements a fluent interface');
145         $this->assertEquals('description1', $command->getDescription(), '->setDescription() sets the description');
146     }
147
148     public function testGetSetHelp()
149     {
150         $command = new \TestCommand();
151         $this->assertEquals('help', $command->getHelp(), '->getHelp() returns the help');
152         $ret = $command->setHelp('help1');
153         $this->assertEquals($command, $ret, '->setHelp() implements a fluent interface');
154         $this->assertEquals('help1', $command->getHelp(), '->setHelp() sets the help');
155         $command->setHelp('');
156         $this->assertEquals('', $command->getHelp(), '->getHelp() does not fall back to the description');
157     }
158
159     public function testGetProcessedHelp()
160     {
161         $command = new \TestCommand();
162         $command->setHelp('The %command.name% command does... Example: php %command.full_name%.');
163         $this->assertContains('The namespace:name command does...', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.name% correctly');
164         $this->assertNotContains('%command.full_name%', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.full_name%');
165
166         $command = new \TestCommand();
167         $command->setHelp('');
168         $this->assertContains('description', $command->getProcessedHelp(), '->getProcessedHelp() falls back to the description');
169     }
170
171     public function testGetSetAliases()
172     {
173         $command = new \TestCommand();
174         $this->assertEquals(array('name'), $command->getAliases(), '->getAliases() returns the aliases');
175         $ret = $command->setAliases(array('name1'));
176         $this->assertEquals($command, $ret, '->setAliases() implements a fluent interface');
177         $this->assertEquals(array('name1'), $command->getAliases(), '->setAliases() sets the aliases');
178     }
179
180     public function testSetAliasesNull()
181     {
182         $command = new \TestCommand();
183         $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
184         $command->setAliases(null);
185     }
186
187     public function testGetSynopsis()
188     {
189         $command = new \TestCommand();
190         $command->addOption('foo');
191         $command->addArgument('bar');
192         $this->assertEquals('namespace:name [--foo] [--] [<bar>]', $command->getSynopsis(), '->getSynopsis() returns the synopsis');
193     }
194
195     public function testAddGetUsages()
196     {
197         $command = new \TestCommand();
198         $command->addUsage('foo1');
199         $command->addUsage('foo2');
200         $this->assertContains('namespace:name foo1', $command->getUsages());
201         $this->assertContains('namespace:name foo2', $command->getUsages());
202     }
203
204     public function testGetHelper()
205     {
206         $application = new Application();
207         $command = new \TestCommand();
208         $command->setApplication($application);
209         $formatterHelper = new FormatterHelper();
210         $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper');
211     }
212
213     /**
214      * @expectedException        \LogicException
215      * @expectedExceptionMessage Cannot retrieve helper "formatter" because there is no HelperSet defined.
216      */
217     public function testGetHelperWithoutHelperSet()
218     {
219         $command = new \TestCommand();
220         $command->getHelper('formatter');
221     }
222
223     public function testMergeApplicationDefinition()
224     {
225         $application1 = new Application();
226         $application1->getDefinition()->addArguments(array(new InputArgument('foo')));
227         $application1->getDefinition()->addOptions(array(new InputOption('bar')));
228         $command = new \TestCommand();
229         $command->setApplication($application1);
230         $command->setDefinition($definition = new InputDefinition(array(new InputArgument('bar'), new InputOption('foo'))));
231
232         $r = new \ReflectionObject($command);
233         $m = $r->getMethod('mergeApplicationDefinition');
234         $m->setAccessible(true);
235         $m->invoke($command);
236         $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition() merges the application arguments and the command arguments');
237         $this->assertTrue($command->getDefinition()->hasArgument('bar'), '->mergeApplicationDefinition() merges the application arguments and the command arguments');
238         $this->assertTrue($command->getDefinition()->hasOption('foo'), '->mergeApplicationDefinition() merges the application options and the command options');
239         $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition() merges the application options and the command options');
240
241         $m->invoke($command);
242         $this->assertEquals(3, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments and options');
243     }
244
245     public function testMergeApplicationDefinitionWithoutArgsThenWithArgsAddsArgs()
246     {
247         $application1 = new Application();
248         $application1->getDefinition()->addArguments(array(new InputArgument('foo')));
249         $application1->getDefinition()->addOptions(array(new InputOption('bar')));
250         $command = new \TestCommand();
251         $command->setApplication($application1);
252         $command->setDefinition($definition = new InputDefinition(array()));
253
254         $r = new \ReflectionObject($command);
255         $m = $r->getMethod('mergeApplicationDefinition');
256         $m->setAccessible(true);
257         $m->invoke($command, false);
258         $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition(false) merges the application and the command options');
259         $this->assertFalse($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(false) does not merge the application arguments');
260
261         $m->invoke($command, true);
262         $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(true) merges the application arguments and the command arguments');
263
264         $m->invoke($command);
265         $this->assertEquals(2, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments');
266     }
267
268     public function testRunInteractive()
269     {
270         $tester = new CommandTester(new \TestCommand());
271
272         $tester->execute(array(), array('interactive' => true));
273
274         $this->assertEquals('interact called'.PHP_EOL.'execute called'.PHP_EOL, $tester->getDisplay(), '->run() calls the interact() method if the input is interactive');
275     }
276
277     public function testRunNonInteractive()
278     {
279         $tester = new CommandTester(new \TestCommand());
280
281         $tester->execute(array(), array('interactive' => false));
282
283         $this->assertEquals('execute called'.PHP_EOL, $tester->getDisplay(), '->run() does not call the interact() method if the input is not interactive');
284     }
285
286     /**
287      * @expectedException        \LogicException
288      * @expectedExceptionMessage You must override the execute() method in the concrete command class.
289      */
290     public function testExecuteMethodNeedsToBeOverridden()
291     {
292         $command = new Command('foo');
293         $command->run(new StringInput(''), new NullOutput());
294     }
295
296     /**
297      * @expectedException        \Symfony\Component\Console\Exception\InvalidOptionException
298      * @expectedExceptionMessage The "--bar" option does not exist.
299      */
300     public function testRunWithInvalidOption()
301     {
302         $command = new \TestCommand();
303         $tester = new CommandTester($command);
304         $tester->execute(array('--bar' => true));
305     }
306
307     public function testRunReturnsIntegerExitCode()
308     {
309         $command = new \TestCommand();
310         $exitCode = $command->run(new StringInput(''), new NullOutput());
311         $this->assertSame(0, $exitCode, '->run() returns integer exit code (treats null as 0)');
312
313         $command = $this->getMockBuilder('TestCommand')->setMethods(array('execute'))->getMock();
314         $command->expects($this->once())
315             ->method('execute')
316             ->will($this->returnValue('2.3'));
317         $exitCode = $command->run(new StringInput(''), new NullOutput());
318         $this->assertSame(2, $exitCode, '->run() returns integer exit code (casts numeric to int)');
319     }
320
321     public function testRunWithApplication()
322     {
323         $command = new \TestCommand();
324         $command->setApplication(new Application());
325         $exitCode = $command->run(new StringInput(''), new NullOutput());
326
327         $this->assertSame(0, $exitCode, '->run() returns an integer exit code');
328     }
329
330     public function testRunReturnsAlwaysInteger()
331     {
332         $command = new \TestCommand();
333
334         $this->assertSame(0, $command->run(new StringInput(''), new NullOutput()));
335     }
336
337     public function testRunWithProcessTitle()
338     {
339         $command = new \TestCommand();
340         $command->setApplication(new Application());
341         $command->setProcessTitle('foo');
342         $this->assertSame(0, $command->run(new StringInput(''), new NullOutput()));
343         if (function_exists('cli_set_process_title')) {
344             if (null === @cli_get_process_title() && 'Darwin' === PHP_OS) {
345                 $this->markTestSkipped('Running "cli_get_process_title" as an unprivileged user is not supported on MacOS.');
346             }
347             $this->assertEquals('foo', cli_get_process_title());
348         }
349     }
350
351     public function testSetCode()
352     {
353         $command = new \TestCommand();
354         $ret = $command->setCode(function (InputInterface $input, OutputInterface $output) {
355             $output->writeln('from the code...');
356         });
357         $this->assertEquals($command, $ret, '->setCode() implements a fluent interface');
358         $tester = new CommandTester($command);
359         $tester->execute(array());
360         $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay());
361     }
362
363     public function getSetCodeBindToClosureTests()
364     {
365         return array(
366             array(true, 'not bound to the command'),
367             array(false, 'bound to the command'),
368         );
369     }
370
371     /**
372      * @dataProvider getSetCodeBindToClosureTests
373      */
374     public function testSetCodeBindToClosure($previouslyBound, $expected)
375     {
376         $code = createClosure();
377         if ($previouslyBound) {
378             $code = $code->bindTo($this);
379         }
380
381         $command = new \TestCommand();
382         $command->setCode($code);
383         $tester = new CommandTester($command);
384         $tester->execute(array());
385         $this->assertEquals('interact called'.PHP_EOL.$expected.PHP_EOL, $tester->getDisplay());
386     }
387
388     public function testSetCodeWithStaticClosure()
389     {
390         $command = new \TestCommand();
391         $command->setCode(self::createClosure());
392         $tester = new CommandTester($command);
393         $tester->execute(array());
394
395         if (\PHP_VERSION_ID < 70000) {
396             // Cannot bind static closures in PHP 5
397             $this->assertEquals('interact called'.PHP_EOL.'not bound'.PHP_EOL, $tester->getDisplay());
398         } else {
399             // Can bind static closures in PHP 7
400             $this->assertEquals('interact called'.PHP_EOL.'bound'.PHP_EOL, $tester->getDisplay());
401         }
402     }
403
404     private static function createClosure()
405     {
406         return function (InputInterface $input, OutputInterface $output) {
407             $output->writeln(isset($this) ? 'bound' : 'not bound');
408         };
409     }
410
411     public function testSetCodeWithNonClosureCallable()
412     {
413         $command = new \TestCommand();
414         $ret = $command->setCode(array($this, 'callableMethodCommand'));
415         $this->assertEquals($command, $ret, '->setCode() implements a fluent interface');
416         $tester = new CommandTester($command);
417         $tester->execute(array());
418         $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay());
419     }
420
421     public function callableMethodCommand(InputInterface $input, OutputInterface $output)
422     {
423         $output->writeln('from the code...');
424     }
425 }
426
427 // In order to get an unbound closure, we should create it outside a class
428 // scope.
429 function createClosure()
430 {
431     return function (InputInterface $input, OutputInterface $output) {
432         $output->writeln($this instanceof Command ? 'bound to the command' : 'not bound to the command');
433     };
434 }