Yaffs site version 1.1
[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 testGetNamespaceGetNameSetName()
97     {
98         $command = new \TestCommand();
99         $this->assertEquals('namespace:name', $command->getName(), '->getName() returns the command name');
100         $command->setName('foo');
101         $this->assertEquals('foo', $command->getName(), '->setName() sets the command name');
102
103         $ret = $command->setName('foobar:bar');
104         $this->assertEquals($command, $ret, '->setName() implements a fluent interface');
105         $this->assertEquals('foobar:bar', $command->getName(), '->setName() sets the command name');
106     }
107
108     /**
109      * @dataProvider provideInvalidCommandNames
110      */
111     public function testInvalidCommandNames($name)
112     {
113         if (method_exists($this, 'expectException')) {
114             $this->expectException('InvalidArgumentException');
115             $this->expectExceptionMessage(sprintf('Command name "%s" is invalid.', $name));
116         } else {
117             $this->setExpectedException('InvalidArgumentException', sprintf('Command name "%s" is invalid.', $name));
118         }
119
120         $command = new \TestCommand();
121         $command->setName($name);
122     }
123
124     public function provideInvalidCommandNames()
125     {
126         return array(
127             array(''),
128             array('foo:'),
129         );
130     }
131
132     public function testGetSetDescription()
133     {
134         $command = new \TestCommand();
135         $this->assertEquals('description', $command->getDescription(), '->getDescription() returns the description');
136         $ret = $command->setDescription('description1');
137         $this->assertEquals($command, $ret, '->setDescription() implements a fluent interface');
138         $this->assertEquals('description1', $command->getDescription(), '->setDescription() sets the description');
139     }
140
141     public function testGetSetHelp()
142     {
143         $command = new \TestCommand();
144         $this->assertEquals('help', $command->getHelp(), '->getHelp() returns the help');
145         $ret = $command->setHelp('help1');
146         $this->assertEquals($command, $ret, '->setHelp() implements a fluent interface');
147         $this->assertEquals('help1', $command->getHelp(), '->setHelp() sets the help');
148         $command->setHelp('');
149         $this->assertEquals('', $command->getHelp(), '->getHelp() does not fall back to the description');
150     }
151
152     public function testGetProcessedHelp()
153     {
154         $command = new \TestCommand();
155         $command->setHelp('The %command.name% command does... Example: php %command.full_name%.');
156         $this->assertContains('The namespace:name command does...', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.name% correctly');
157         $this->assertNotContains('%command.full_name%', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.full_name%');
158
159         $command = new \TestCommand();
160         $command->setHelp('');
161         $this->assertContains('description', $command->getProcessedHelp(), '->getProcessedHelp() falls back to the description');
162     }
163
164     public function testGetSetAliases()
165     {
166         $command = new \TestCommand();
167         $this->assertEquals(array('name'), $command->getAliases(), '->getAliases() returns the aliases');
168         $ret = $command->setAliases(array('name1'));
169         $this->assertEquals($command, $ret, '->setAliases() implements a fluent interface');
170         $this->assertEquals(array('name1'), $command->getAliases(), '->setAliases() sets the aliases');
171     }
172
173     public function testSetAliasesNull()
174     {
175         $command = new \TestCommand();
176         $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
177         $command->setAliases(null);
178     }
179
180     public function testGetSynopsis()
181     {
182         $command = new \TestCommand();
183         $command->addOption('foo');
184         $command->addArgument('bar');
185         $this->assertEquals('namespace:name [--foo] [--] [<bar>]', $command->getSynopsis(), '->getSynopsis() returns the synopsis');
186     }
187
188     public function testAddGetUsages()
189     {
190         $command = new \TestCommand();
191         $command->addUsage('foo1');
192         $command->addUsage('foo2');
193         $this->assertContains('namespace:name foo1', $command->getUsages());
194         $this->assertContains('namespace:name foo2', $command->getUsages());
195     }
196
197     public function testGetHelper()
198     {
199         $application = new Application();
200         $command = new \TestCommand();
201         $command->setApplication($application);
202         $formatterHelper = new FormatterHelper();
203         $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper');
204     }
205
206     /**
207      * @expectedException        \LogicException
208      * @expectedExceptionMessage Cannot retrieve helper "formatter" because there is no HelperSet defined.
209      */
210     public function testGetHelperWithoutHelperSet()
211     {
212         $command = new \TestCommand();
213         $command->getHelper('formatter');
214     }
215
216     public function testMergeApplicationDefinition()
217     {
218         $application1 = new Application();
219         $application1->getDefinition()->addArguments(array(new InputArgument('foo')));
220         $application1->getDefinition()->addOptions(array(new InputOption('bar')));
221         $command = new \TestCommand();
222         $command->setApplication($application1);
223         $command->setDefinition($definition = new InputDefinition(array(new InputArgument('bar'), new InputOption('foo'))));
224
225         $r = new \ReflectionObject($command);
226         $m = $r->getMethod('mergeApplicationDefinition');
227         $m->setAccessible(true);
228         $m->invoke($command);
229         $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition() merges the application arguments and the command arguments');
230         $this->assertTrue($command->getDefinition()->hasArgument('bar'), '->mergeApplicationDefinition() merges the application arguments and the command arguments');
231         $this->assertTrue($command->getDefinition()->hasOption('foo'), '->mergeApplicationDefinition() merges the application options and the command options');
232         $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition() merges the application options and the command options');
233
234         $m->invoke($command);
235         $this->assertEquals(3, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments and options');
236     }
237
238     public function testMergeApplicationDefinitionWithoutArgsThenWithArgsAddsArgs()
239     {
240         $application1 = new Application();
241         $application1->getDefinition()->addArguments(array(new InputArgument('foo')));
242         $application1->getDefinition()->addOptions(array(new InputOption('bar')));
243         $command = new \TestCommand();
244         $command->setApplication($application1);
245         $command->setDefinition($definition = new InputDefinition(array()));
246
247         $r = new \ReflectionObject($command);
248         $m = $r->getMethod('mergeApplicationDefinition');
249         $m->setAccessible(true);
250         $m->invoke($command, false);
251         $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition(false) merges the application and the command options');
252         $this->assertFalse($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(false) does not merge the application arguments');
253
254         $m->invoke($command, true);
255         $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(true) merges the application arguments and the command arguments');
256
257         $m->invoke($command);
258         $this->assertEquals(2, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments');
259     }
260
261     public function testRunInteractive()
262     {
263         $tester = new CommandTester(new \TestCommand());
264
265         $tester->execute(array(), array('interactive' => true));
266
267         $this->assertEquals('interact called'.PHP_EOL.'execute called'.PHP_EOL, $tester->getDisplay(), '->run() calls the interact() method if the input is interactive');
268     }
269
270     public function testRunNonInteractive()
271     {
272         $tester = new CommandTester(new \TestCommand());
273
274         $tester->execute(array(), array('interactive' => false));
275
276         $this->assertEquals('execute called'.PHP_EOL, $tester->getDisplay(), '->run() does not call the interact() method if the input is not interactive');
277     }
278
279     /**
280      * @expectedException        \LogicException
281      * @expectedExceptionMessage You must override the execute() method in the concrete command class.
282      */
283     public function testExecuteMethodNeedsToBeOverridden()
284     {
285         $command = new Command('foo');
286         $command->run(new StringInput(''), new NullOutput());
287     }
288
289     /**
290      * @expectedException        \Symfony\Component\Console\Exception\InvalidOptionException
291      * @expectedExceptionMessage The "--bar" option does not exist.
292      */
293     public function testRunWithInvalidOption()
294     {
295         $command = new \TestCommand();
296         $tester = new CommandTester($command);
297         $tester->execute(array('--bar' => true));
298     }
299
300     public function testRunReturnsIntegerExitCode()
301     {
302         $command = new \TestCommand();
303         $exitCode = $command->run(new StringInput(''), new NullOutput());
304         $this->assertSame(0, $exitCode, '->run() returns integer exit code (treats null as 0)');
305
306         $command = $this->getMockBuilder('TestCommand')->setMethods(array('execute'))->getMock();
307         $command->expects($this->once())
308             ->method('execute')
309             ->will($this->returnValue('2.3'));
310         $exitCode = $command->run(new StringInput(''), new NullOutput());
311         $this->assertSame(2, $exitCode, '->run() returns integer exit code (casts numeric to int)');
312     }
313
314     public function testRunWithApplication()
315     {
316         $command = new \TestCommand();
317         $command->setApplication(new Application());
318         $exitCode = $command->run(new StringInput(''), new NullOutput());
319
320         $this->assertSame(0, $exitCode, '->run() returns an integer exit code');
321     }
322
323     public function testRunReturnsAlwaysInteger()
324     {
325         $command = new \TestCommand();
326
327         $this->assertSame(0, $command->run(new StringInput(''), new NullOutput()));
328     }
329
330     public function testRunWithProcessTitle()
331     {
332         $command = new \TestCommand();
333         $command->setApplication(new Application());
334         $command->setProcessTitle('foo');
335         $this->assertSame(0, $command->run(new StringInput(''), new NullOutput()));
336         if (function_exists('cli_set_process_title')) {
337             if (null === @cli_get_process_title() && 'Darwin' === PHP_OS) {
338                 $this->markTestSkipped('Running "cli_get_process_title" as an unprivileged user is not supported on MacOS.');
339             }
340             $this->assertEquals('foo', cli_get_process_title());
341         }
342     }
343
344     public function testSetCode()
345     {
346         $command = new \TestCommand();
347         $ret = $command->setCode(function (InputInterface $input, OutputInterface $output) {
348             $output->writeln('from the code...');
349         });
350         $this->assertEquals($command, $ret, '->setCode() implements a fluent interface');
351         $tester = new CommandTester($command);
352         $tester->execute(array());
353         $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay());
354     }
355
356     public function getSetCodeBindToClosureTests()
357     {
358         return array(
359             array(true, 'not bound to the command'),
360             array(false, 'bound to the command'),
361         );
362     }
363
364     /**
365      * @dataProvider getSetCodeBindToClosureTests
366      * @requires PHP 5.4
367      */
368     public function testSetCodeBindToClosure($previouslyBound, $expected)
369     {
370         $code = createClosure();
371         if ($previouslyBound) {
372             $code = $code->bindTo($this);
373         }
374
375         $command = new \TestCommand();
376         $command->setCode($code);
377         $tester = new CommandTester($command);
378         $tester->execute(array());
379         $this->assertEquals('interact called'.PHP_EOL.$expected.PHP_EOL, $tester->getDisplay());
380     }
381
382     public function testSetCodeWithStaticClosure()
383     {
384         $command = new \TestCommand();
385         $command->setCode(self::createClosure());
386         $tester = new CommandTester($command);
387         $tester->execute(array());
388
389         if (\PHP_VERSION_ID < 70000) {
390             // Cannot bind static closures in PHP 5
391             $this->assertEquals('interact called'.PHP_EOL.'not bound'.PHP_EOL, $tester->getDisplay());
392         } else {
393             // Can bind static closures in PHP 7
394             $this->assertEquals('interact called'.PHP_EOL.'bound'.PHP_EOL, $tester->getDisplay());
395         }
396     }
397
398     private static function createClosure()
399     {
400         return function (InputInterface $input, OutputInterface $output) {
401             $output->writeln(isset($this) ? 'bound' : 'not bound');
402         };
403     }
404
405     public function testSetCodeWithNonClosureCallable()
406     {
407         $command = new \TestCommand();
408         $ret = $command->setCode(array($this, 'callableMethodCommand'));
409         $this->assertEquals($command, $ret, '->setCode() implements a fluent interface');
410         $tester = new CommandTester($command);
411         $tester->execute(array());
412         $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay());
413     }
414
415     /**
416      * @expectedException        \InvalidArgumentException
417      * @expectedExceptionMessage Invalid callable provided to Command::setCode.
418      */
419     public function testSetCodeWithNonCallable()
420     {
421         $command = new \TestCommand();
422         $command->setCode(array($this, 'nonExistentMethod'));
423     }
424
425     public function callableMethodCommand(InputInterface $input, OutputInterface $output)
426     {
427         $output->writeln('from the code...');
428     }
429
430     /**
431      * @group legacy
432      */
433     public function testLegacyAsText()
434     {
435         $command = new \TestCommand();
436         $command->setApplication(new Application());
437         $tester = new CommandTester($command);
438         $tester->execute(array('command' => $command->getName()));
439         $this->assertStringEqualsFile(self::$fixturesPath.'/command_astext.txt', $command->asText(), '->asText() returns a text representation of the command');
440     }
441
442     /**
443      * @group legacy
444      */
445     public function testLegacyAsXml()
446     {
447         $command = new \TestCommand();
448         $command->setApplication(new Application());
449         $tester = new CommandTester($command);
450         $tester->execute(array('command' => $command->getName()));
451         $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/command_asxml.txt', $command->asXml(), '->asXml() returns an XML representation of the command');
452     }
453 }
454
455 // In order to get an unbound closure, we should create it outside a class
456 // scope.
457 function createClosure()
458 {
459     return function (InputInterface $input, OutputInterface $output) {
460         $output->writeln($this instanceof Command ? 'bound to the command' : 'not bound to the command');
461     };
462 }