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