4 * This file is part of Psy Shell.
6 * (c) 2012-2017 Justin Hileman
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
14 use Psy\Configuration;
15 use Psy\Exception\ErrorException;
16 use Psy\Exception\ParseErrorException;
18 use Psy\TabCompletion\Matcher\ClassMethodsMatcher;
19 use Symfony\Component\Console\Output\StreamOutput;
21 class ShellTest extends \PHPUnit_Framework_TestCase
23 private $streams = array();
25 public function tearDown()
27 foreach ($this->streams as $stream) {
32 public function testScopeVariables()
36 $three = new \StdClass();
37 $__psysh__ = 'ignore this';
41 $shell = new Shell($this->getConfig());
42 $shell->setScopeVariables(compact('one', 'two', 'three', '__psysh__', '_', '_e', 'this'));
44 $this->assertNotContains('__psysh__', $shell->getScopeVariableNames());
45 $this->assertEquals(array('one', 'two', 'three', '_'), $shell->getScopeVariableNames());
46 $this->assertEquals('banana', $shell->getScopeVariable('one'));
47 $this->assertEquals(123, $shell->getScopeVariable('two'));
48 $this->assertSame($three, $shell->getScopeVariable('three'));
49 $this->assertNull($shell->getScopeVariable('_'));
51 $shell->setScopeVariables(array());
52 $this->assertEquals(array('_'), $shell->getScopeVariableNames());
54 $shell->setBoundObject($this);
55 $this->assertEquals(array('_', 'this'), $shell->getScopeVariableNames());
56 $this->assertSame($this, $shell->getScopeVariable('this'));
57 $this->assertEquals(array('_' => null), $shell->getScopeVariables(false));
58 $this->assertEquals(array('_' => null, 'this' => $this), $shell->getScopeVariables());
62 * @expectedException \InvalidArgumentException
64 public function testUnknownScopeVariablesThrowExceptions()
66 $shell = new Shell($this->getConfig());
67 $shell->setScopeVariables(array('foo' => 'FOO', 'bar' => 1));
68 $shell->getScopeVariable('baz');
71 public function testIncludes()
73 $config = $this->getConfig(array('configFile' => __DIR__ . '/../../fixtures/empty.php'));
75 $shell = new Shell($config);
76 $this->assertEmpty($shell->getIncludes());
77 $shell->setIncludes(array('foo', 'bar', 'baz'));
78 $this->assertEquals(array('foo', 'bar', 'baz'), $shell->getIncludes());
81 public function testIncludesConfig()
83 $config = $this->getConfig(array(
84 'defaultIncludes' => array('/file.php'),
85 'configFile' => __DIR__ . '/../../fixtures/empty.php',
88 $shell = new Shell($config);
90 $includes = $shell->getIncludes();
91 $this->assertEquals('/file.php', $includes[0]);
94 public function testAddMatchersViaConfig()
96 $config = $this->getConfig(array(
97 'tabCompletionMatchers' => array(
98 new ClassMethodsMatcher(),
102 $matchers = $config->getTabCompletionMatchers();
104 $this->assertTrue(array_pop($matchers) instanceof ClassMethodsMatcher);
107 public function testRenderingExceptions()
109 $shell = new Shell($this->getConfig());
110 $output = $this->getOutput();
111 $stream = $output->getStream();
112 $e = new ParseErrorException('message', 13);
114 $shell->setOutput($output);
115 $shell->addCode('code');
116 $this->assertTrue($shell->hasCode());
117 $this->assertNotEmpty($shell->getCodeBuffer());
119 $shell->writeException($e);
121 $this->assertSame($e, $shell->getScopeVariable('_e'));
122 $this->assertFalse($shell->hasCode());
123 $this->assertEmpty($shell->getCodeBuffer());
126 $streamContents = stream_get_contents($stream);
128 $this->assertContains('PHP Parse error', $streamContents);
129 $this->assertContains('message', $streamContents);
130 $this->assertContains('line 13', $streamContents);
133 public function testHandlingErrors()
135 $shell = new Shell($this->getConfig());
136 $output = $this->getOutput();
137 $stream = $output->getStream();
138 $shell->setOutput($output);
140 $oldLevel = error_reporting();
141 error_reporting($oldLevel & ~E_USER_NOTICE);
144 $shell->handleError(E_USER_NOTICE, 'wheee', null, 13);
145 } catch (ErrorException $e) {
146 error_reporting($oldLevel);
147 $this->fail('Unexpected error exception');
149 error_reporting($oldLevel);
152 $streamContents = stream_get_contents($stream);
154 $this->assertContains('PHP error:', $streamContents);
155 $this->assertContains('wheee', $streamContents);
156 $this->assertContains('line 13', $streamContents);
160 * @expectedException \Psy\Exception\ErrorException
162 public function testNotHandlingErrors()
164 $shell = new Shell($this->getConfig());
165 $oldLevel = error_reporting();
166 error_reporting($oldLevel | E_USER_NOTICE);
169 $shell->handleError(E_USER_NOTICE, 'wheee', null, 13);
170 } catch (ErrorException $e) {
171 error_reporting($oldLevel);
176 public function testVersion()
178 $shell = new Shell($this->getConfig());
180 $this->assertInstanceOf('Symfony\Component\Console\Application', $shell);
181 $this->assertContains(Shell::VERSION, $shell->getVersion());
182 $this->assertContains(phpversion(), $shell->getVersion());
183 $this->assertContains(php_sapi_name(), $shell->getVersion());
186 public function testCodeBuffer()
188 $shell = new Shell($this->getConfig());
190 $shell->addCode('class');
191 $this->assertNull($shell->flushCode());
192 $this->assertTrue($shell->hasCode());
194 $shell->addCode('a');
195 $this->assertNull($shell->flushCode());
196 $this->assertTrue($shell->hasCode());
198 $shell->addCode('{}');
199 $code = $shell->flushCode();
200 $this->assertFalse($shell->hasCode());
201 $code = preg_replace('/\s+/', ' ', $code);
202 $this->assertNotNull($code);
203 $this->assertEquals('class a { } return new \\Psy\\CodeCleaner\\NoReturnValue();', $code);
206 public function testKeepCodeBufferOpen()
208 $shell = new Shell($this->getConfig());
210 $shell->addCode('1 \\');
211 $this->assertNull($shell->flushCode());
212 $this->assertTrue($shell->hasCode());
214 $shell->addCode('+ 1 \\');
215 $this->assertNull($shell->flushCode());
216 $this->assertTrue($shell->hasCode());
218 $shell->addCode('+ 1');
219 $code = $shell->flushCode();
220 $this->assertFalse($shell->hasCode());
221 $code = preg_replace('/\s+/', ' ', $code);
222 $this->assertNotNull($code);
223 $this->assertEquals('return 1 + 1 + 1;', $code);
227 * @expectedException \Psy\Exception\ParseErrorException
229 public function testCodeBufferThrowsParseExceptions()
231 $shell = new Shell($this->getConfig());
232 $shell->addCode('this is not valid');
236 public function testClosuresSupport()
238 $shell = new Shell($this->getConfig());
239 $code = '$test = function () {}';
240 $shell->addCode($code);
243 $shell->addCode($code);
247 public function testWriteStdout()
249 $output = $this->getOutput();
250 $stream = $output->getStream();
251 $shell = new Shell($this->getConfig());
252 $shell->setOutput($output);
254 $shell->writeStdout("{{stdout}}\n");
257 $streamContents = stream_get_contents($stream);
259 $this->assertEquals('{{stdout}}' . PHP_EOL, $streamContents);
262 public function testWriteStdoutWithoutNewline()
264 $output = $this->getOutput();
265 $stream = $output->getStream();
266 $shell = new Shell($this->getConfig());
267 $shell->setOutput($output);
269 $shell->writeStdout('{{stdout}}');
272 $streamContents = stream_get_contents($stream);
274 $this->assertEquals('{{stdout}}<aside>⏎</aside>' . PHP_EOL, $streamContents);
278 * @dataProvider getReturnValues
280 public function testWriteReturnValue($input, $expected)
282 $output = $this->getOutput();
283 $stream = $output->getStream();
284 $shell = new Shell($this->getConfig());
285 $shell->setOutput($output);
287 $shell->writeReturnValue($input);
289 $this->assertEquals($expected, stream_get_contents($stream));
292 public function getReturnValues()
295 array('{{return value}}', "=> \"\033[32m{{return value}}\033[39m\"" . PHP_EOL),
296 array(1, "=> \033[35m1\033[39m" . PHP_EOL),
301 * @dataProvider getRenderedExceptions
303 public function testWriteException($exception, $expected)
305 $output = $this->getOutput();
306 $stream = $output->getStream();
307 $shell = new Shell($this->getConfig());
308 $shell->setOutput($output);
310 $shell->writeException($exception);
312 $this->assertEquals($expected, stream_get_contents($stream));
315 public function getRenderedExceptions()
318 array(new \Exception('{{message}}'), "Exception with message '{{message}}'" . PHP_EOL),
322 private function getOutput()
324 $stream = fopen('php://memory', 'w+');
325 $this->streams[] = $stream;
327 $output = new StreamOutput($stream, StreamOutput::VERBOSITY_NORMAL, false);
332 private function getConfig(array $config = array())
334 // Mebbe there's a better way than this?
335 $dir = tempnam(sys_get_temp_dir(), 'psysh_shell_test_');
341 'runtimeDir' => $dir,
344 return new Configuration(array_merge($defaults, $config));