assertSame($expected, self::trimLines($formattedWithoutColors));
$this->assertNotEquals($expected, self::trimLines($formatted));
}
public function reflectors()
{
$expectClass = <<<'EOS'
14: class SomeClass
15: {
16: const SOME_CONST = 'some const';
17: private $someProp = 'some prop';
18:
19: public function someMethod($someParam)
20: {
21: return 'some method';
22: }
23:
24: public static function someClosure()
25: {
26: return function () {
27: return 'some closure';
28: };
29: }
30: }
EOS;
$expectMethod = <<<'EOS'
19: public function someMethod($someParam)
20: {
21: return 'some method';
22: }
EOS;
$expectClosure = <<<'EOS'
26: return function () {
27: return 'some closure';
28: };
EOS;
return [
[new \ReflectionClass(SomeClass::class), $expectClass],
[new \ReflectionObject(new SomeClass()), $expectClass],
[new \ReflectionMethod(SomeClass::class, 'someMethod'), $expectMethod],
[new \ReflectionFunction(SomeClass::someClosure()), $expectClosure],
];
}
/**
* @dataProvider invalidReflectors
*/
public function testCodeFormatterThrowsExceptionForReflectorsItDoesntUnderstand($reflector)
{
$this->expectException(\Psy\Exception\RuntimeException::class);
CodeFormatter::format($reflector);
$this->fail();
}
public function invalidReflectors()
{
$reflectors = [
[new \ReflectionExtension('json')],
[new \ReflectionParameter([SomeClass::class, 'someMethod'], 'someParam')],
[new \ReflectionProperty(SomeClass::class, 'someProp')],
];
if (\version_compare(\PHP_VERSION, '7.1.0', '>=')) {
$reflectors[] = [new \ReflectionClassConstant(SomeClass::class, 'SOME_CONST')];
}
return $reflectors;
}
/**
* @dataProvider filenames
*/
public function testCodeFormatterThrowsExceptionForMissingFile($filename)
{
$this->expectException(\Psy\Exception\RuntimeException::class);
$reflector = $this->getMockBuilder(\ReflectionClass::class)
->disableOriginalConstructor()
->getMock();
$reflector
->expects($this->once())
->method('getFileName')
->willReturn($filename);
CodeFormatter::format($reflector);
$this->fail();
}
public function filenames()
{
return [[false], ['not a file']];
}
/**
* @dataProvider validCode
*/
public function testFormatCode($code, $startLine, $endLine, $markLine, $expected)
{
$formatted = CodeFormatter::formatCode($code, $startLine, $endLine, $markLine);
$formattedWithoutColors = self::stripTags($formatted);
$this->assertSame($expected, self::trimLines($formattedWithoutColors));
$this->assertNotEquals($expected, self::trimLines($formatted));
}
public function validCode()
{
$someCode = <<<'EOS'
20: {
21: return 'some method';
22: }
EOS;
return [
[$someCode, 1, null, null, $someCodeExpected],
[$someCode, 19, 22, null, $someCodeSnippet],
[$someCode, 19, 22, 20, $someCodeSnippetWithMarker],
];
}
/**
* Test some smaller ones with spans... we don't want the test to be tooo flaky so we don't
* explicitly test the exact formatting above. Just to be safe, let's add a couple of tests
* that *do* expect specific formatting.
*
* @dataProvider smallCodeLines
*/
public function testFormatSmallCodeLines($code, $startLine, $endLine, $markLine, $expected)
{
$formatted = CodeFormatter::formatCode($code, $startLine, $endLine, $markLine);
$this->assertSame($expected, self::trimLines($formatted));
}
public function smallCodeLines()
{
return [
['1: \\= 42;'],
['1: \\echo "yay $foo!";'],
// Start and end lines
["1: \\echo \'wat\';'],
["2: $foo = 42;'],
["2: $foo = 42;'],
// With a line marker
["> : $foo = 42;'],
// Line marker before or after our line range
["2: $foo = 42;'],
["1: \echo \'wat\';'],
];
}
/**
* Remove tags from formatted output. This is kind of ugly o_O.
*/
private static function stripTags($code)
{
$tagRegex = '[a-z][^<>]*+';
$output = \preg_replace("#<(($tagRegex) | /($tagRegex)?)>#ix", '', $code);
return \str_replace('\\<', '<', $output);
}
private static function trimLines($code)
{
return \rtrim(\implode("\n", \array_map('rtrim', \explode("\n", $code))));
}
}