1fb78b230908407147b141e43d78f807b1764945
[yaffs-website] / vendor / psy / psysh / test / ShellTest.php
1 <?php
2
3 /*
4  * This file is part of Psy Shell.
5  *
6  * (c) 2012-2018 Justin Hileman
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 Psy\Test;
13
14 use Psy\Configuration;
15 use Psy\Exception\ErrorException;
16 use Psy\Exception\ParseErrorException;
17 use Psy\Shell;
18 use Psy\TabCompletion\Matcher\ClassMethodsMatcher;
19 use Symfony\Component\Console\Output\StreamOutput;
20
21 class ShellTest extends \PHPUnit\Framework\TestCase
22 {
23     private $streams = [];
24
25     public function tearDown()
26     {
27         foreach ($this->streams as $stream) {
28             fclose($stream);
29         }
30     }
31
32     public function testScopeVariables()
33     {
34         $one       = 'banana';
35         $two       = 123;
36         $three     = new \StdClass();
37         $__psysh__ = 'ignore this';
38         $_         = 'ignore this';
39         $_e        = 'ignore this';
40
41         $shell = new Shell($this->getConfig());
42         $shell->setScopeVariables(compact('one', 'two', 'three', '__psysh__', '_', '_e', 'this'));
43
44         $this->assertNotContains('__psysh__', $shell->getScopeVariableNames());
45         $this->assertSame(['one', 'two', 'three', '_'], $shell->getScopeVariableNames());
46         $this->assertSame('banana', $shell->getScopeVariable('one'));
47         $this->assertSame(123, $shell->getScopeVariable('two'));
48         $this->assertSame($three, $shell->getScopeVariable('three'));
49         $this->assertNull($shell->getScopeVariable('_'));
50
51         $shell->setScopeVariables([]);
52         $this->assertSame(['_'], $shell->getScopeVariableNames());
53
54         $shell->setBoundObject($this);
55         $this->assertSame(['_', 'this'], $shell->getScopeVariableNames());
56         $this->assertSame($this, $shell->getScopeVariable('this'));
57         $this->assertSame(['_' => null], $shell->getScopeVariables(false));
58         $this->assertSame(['_' => null, 'this' => $this], $shell->getScopeVariables());
59     }
60
61     /**
62      * @expectedException \InvalidArgumentException
63      */
64     public function testUnknownScopeVariablesThrowExceptions()
65     {
66         $shell = new Shell($this->getConfig());
67         $shell->setScopeVariables(['foo' => 'FOO', 'bar' => 1]);
68         $shell->getScopeVariable('baz');
69     }
70
71     public function testIncludesWithScopeVariables()
72     {
73         $one       = 'banana';
74         $two       = 123;
75         $three     = new \StdClass();
76         $__psysh__ = 'ignore this';
77         $_         = 'ignore this';
78         $_e        = 'ignore this';
79
80         $config = $this->getConfig(['usePcntl' => false]);
81
82         $shell = new Shell($config);
83         $shell->setScopeVariables(compact('one', 'two', 'three', '__psysh__', '_', '_e', 'this'));
84         $shell->addInput('exit', true);
85
86         // This is super slow and we shouldn't do this :(
87         $shell->run(null, $this->getOutput());
88
89         $this->assertNotContains('__psysh__', $shell->getScopeVariableNames());
90         $this->assertSame(['one', 'two', 'three', '_', '_e'], $shell->getScopeVariableNames());
91         $this->assertSame('banana', $shell->getScopeVariable('one'));
92         $this->assertSame(123, $shell->getScopeVariable('two'));
93         $this->assertSame($three, $shell->getScopeVariable('three'));
94         $this->assertNull($shell->getScopeVariable('_'));
95     }
96
97     public function testIncludes()
98     {
99         $config = $this->getConfig(['configFile' => __DIR__ . '/fixtures/empty.php']);
100
101         $shell = new Shell($config);
102         $this->assertEmpty($shell->getIncludes());
103         $shell->setIncludes(['foo', 'bar', 'baz']);
104         $this->assertSame(['foo', 'bar', 'baz'], $shell->getIncludes());
105     }
106
107     public function testIncludesConfig()
108     {
109         $config = $this->getConfig([
110             'defaultIncludes' => ['/file.php'],
111             'configFile'      => __DIR__ . '/fixtures/empty.php',
112         ]);
113
114         $shell = new Shell($config);
115
116         $includes = $shell->getIncludes();
117         $this->assertSame('/file.php', $includes[0]);
118     }
119
120     public function testAddMatchersViaConfig()
121     {
122         $shell = new FakeShell();
123         $matcher = new ClassMethodsMatcher();
124
125         $config = $this->getConfig([
126             'matchers' => [$matcher],
127         ]);
128         $config->setShell($shell);
129
130         $this->assertSame([$matcher], $shell->matchers);
131     }
132
133     public function testAddMatchersViaConfigAfterShell()
134     {
135         $shell = new FakeShell();
136         $matcher = new ClassMethodsMatcher();
137
138         $config = $this->getConfig([]);
139         $config->setShell($shell);
140         $config->addMatchers([$matcher]);
141
142         $this->assertSame([$matcher], $shell->matchers);
143     }
144
145     public function testRenderingExceptions()
146     {
147         $shell  = new Shell($this->getConfig());
148         $output = $this->getOutput();
149         $stream = $output->getStream();
150         $e      = new ParseErrorException('message', 13);
151
152         $shell->setOutput($output);
153         $shell->addCode('code');
154         $this->assertTrue($shell->hasCode());
155         $this->assertNotEmpty($shell->getCodeBuffer());
156
157         $shell->writeException($e);
158
159         $this->assertSame($e, $shell->getScopeVariable('_e'));
160         $this->assertFalse($shell->hasCode());
161         $this->assertEmpty($shell->getCodeBuffer());
162
163         rewind($stream);
164         $streamContents = stream_get_contents($stream);
165
166         $this->assertContains('PHP Parse error', $streamContents);
167         $this->assertContains('message', $streamContents);
168         $this->assertContains('line 13', $streamContents);
169     }
170
171     public function testHandlingErrors()
172     {
173         $shell  = new Shell($this->getConfig());
174         $output = $this->getOutput();
175         $stream = $output->getStream();
176         $shell->setOutput($output);
177
178         $oldLevel = error_reporting();
179         error_reporting($oldLevel & ~E_USER_NOTICE);
180
181         try {
182             $shell->handleError(E_USER_NOTICE, 'wheee', null, 13);
183         } catch (ErrorException $e) {
184             error_reporting($oldLevel);
185             $this->fail('Unexpected error exception');
186         }
187         error_reporting($oldLevel);
188
189         rewind($stream);
190         $streamContents = stream_get_contents($stream);
191
192         $this->assertContains('PHP Notice:', $streamContents);
193         $this->assertContains('wheee',       $streamContents);
194         $this->assertContains('line 13',     $streamContents);
195     }
196
197     /**
198      * @expectedException \Psy\Exception\ErrorException
199      */
200     public function testNotHandlingErrors()
201     {
202         $shell    = new Shell($this->getConfig());
203         $oldLevel = error_reporting();
204         error_reporting($oldLevel | E_USER_NOTICE);
205
206         try {
207             $shell->handleError(E_USER_NOTICE, 'wheee', null, 13);
208         } catch (ErrorException $e) {
209             error_reporting($oldLevel);
210             throw $e;
211         }
212     }
213
214     public function testVersion()
215     {
216         $shell = new Shell($this->getConfig());
217
218         $this->assertInstanceOf('Symfony\Component\Console\Application', $shell);
219         $this->assertContains(Shell::VERSION, $shell->getVersion());
220         $this->assertContains(phpversion(), $shell->getVersion());
221         $this->assertContains(php_sapi_name(), $shell->getVersion());
222     }
223
224     public function testCodeBuffer()
225     {
226         $shell = new Shell($this->getConfig());
227
228         $shell->addCode('class');
229         $this->assertNull($shell->flushCode());
230         $this->assertTrue($shell->hasCode());
231
232         $shell->addCode('a');
233         $this->assertNull($shell->flushCode());
234         $this->assertTrue($shell->hasCode());
235
236         $shell->addCode('{}');
237         $code = $shell->flushCode();
238         $this->assertFalse($shell->hasCode());
239         $code = preg_replace('/\s+/', ' ', $code);
240         $this->assertNotNull($code);
241         $this->assertSame('class a { } return new \\Psy\\CodeCleaner\\NoReturnValue();', $code);
242     }
243
244     public function testKeepCodeBufferOpen()
245     {
246         $shell = new Shell($this->getConfig());
247
248         $shell->addCode('1 \\');
249         $this->assertNull($shell->flushCode());
250         $this->assertTrue($shell->hasCode());
251
252         $shell->addCode('+ 1 \\');
253         $this->assertNull($shell->flushCode());
254         $this->assertTrue($shell->hasCode());
255
256         $shell->addCode('+ 1');
257         $code = $shell->flushCode();
258         $this->assertFalse($shell->hasCode());
259         $code = preg_replace('/\s+/', ' ', $code);
260         $this->assertNotNull($code);
261         $this->assertSame('return 1 + 1 + 1;', $code);
262     }
263
264     /**
265      * @expectedException \Psy\Exception\ParseErrorException
266      */
267     public function testCodeBufferThrowsParseExceptions()
268     {
269         $shell = new Shell($this->getConfig());
270         $shell->addCode('this is not valid');
271         $shell->flushCode();
272     }
273
274     public function testClosuresSupport()
275     {
276         $shell = new Shell($this->getConfig());
277         $code = '$test = function () {}';
278         $shell->addCode($code);
279         $shell->flushCode();
280         $code = '$test()';
281         $shell->addCode($code);
282         $this->assertSame($shell->flushCode(), 'return $test();');
283     }
284
285     public function testWriteStdout()
286     {
287         $output = $this->getOutput();
288         $stream = $output->getStream();
289         $shell  = new Shell($this->getConfig());
290         $shell->setOutput($output);
291
292         $shell->writeStdout("{{stdout}}\n");
293
294         rewind($stream);
295         $streamContents = stream_get_contents($stream);
296
297         $this->assertSame('{{stdout}}' . PHP_EOL, $streamContents);
298     }
299
300     public function testWriteStdoutWithoutNewline()
301     {
302         $output = $this->getOutput();
303         $stream = $output->getStream();
304         $shell  = new Shell($this->getConfig());
305         $shell->setOutput($output);
306
307         $shell->writeStdout('{{stdout}}');
308
309         rewind($stream);
310         $streamContents = stream_get_contents($stream);
311
312         $this->assertSame('{{stdout}}<aside>⏎</aside>' . PHP_EOL, $streamContents);
313     }
314
315     /**
316      * @dataProvider getReturnValues
317      */
318     public function testWriteReturnValue($input, $expected)
319     {
320         $output = $this->getOutput();
321         $stream = $output->getStream();
322         $shell  = new Shell($this->getConfig());
323         $shell->setOutput($output);
324
325         $shell->writeReturnValue($input);
326         rewind($stream);
327         $this->assertEquals($expected, stream_get_contents($stream));
328     }
329
330     public function getReturnValues()
331     {
332         return [
333             ['{{return value}}', "=> \"\033[32m{{return value}}\033[39m\"" . PHP_EOL],
334             [1, "=> \033[35m1\033[39m" . PHP_EOL],
335         ];
336     }
337
338     /**
339      * @dataProvider getRenderedExceptions
340      */
341     public function testWriteException($exception, $expected)
342     {
343         $output = $this->getOutput();
344         $stream = $output->getStream();
345         $shell  = new Shell($this->getConfig());
346         $shell->setOutput($output);
347
348         $shell->writeException($exception);
349         rewind($stream);
350         $this->assertSame($expected, stream_get_contents($stream));
351     }
352
353     public function getRenderedExceptions()
354     {
355         return [
356             [new \Exception('{{message}}'), "Exception with message '{{message}}'" . PHP_EOL],
357         ];
358     }
359
360     /**
361      * @dataProvider getExecuteValues
362      */
363     public function testShellExecute($input, $expected)
364     {
365         $output = $this->getOutput();
366         $stream = $output->getStream();
367         $shell  = new Shell($this->getConfig());
368         $shell->setOutput($output);
369         $this->assertEquals($expected, $shell->execute($input));
370         rewind($stream);
371         $this->assertSame('', stream_get_contents($stream));
372     }
373
374     public function getExecuteValues()
375     {
376         return [
377             ['return 12', 12],
378             ['"{{return value}}"', '{{return value}}'],
379             ['1', '1'],
380         ];
381     }
382
383     private function getOutput()
384     {
385         $stream = fopen('php://memory', 'w+');
386         $this->streams[] = $stream;
387
388         $output = new StreamOutput($stream, StreamOutput::VERBOSITY_NORMAL, false);
389
390         return $output;
391     }
392
393     private function getConfig(array $config = [])
394     {
395         // Mebbe there's a better way than this?
396         $dir = tempnam(sys_get_temp_dir(), 'psysh_shell_test_');
397         unlink($dir);
398
399         $defaults = [
400             'configDir'  => $dir,
401             'dataDir'    => $dir,
402             'runtimeDir' => $dir,
403         ];
404
405         return new Configuration(array_merge($defaults, $config));
406     }
407 }