TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
213 lines
7.1 KiB
213 lines
7.1 KiB
<?php
|
|
|
|
namespace PhpAot\Tests;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use PhpAot\Php\CompilerTest;
|
|
use PhpAot\Php\ArgInfo;
|
|
use PhpParser\Node;
|
|
|
|
class PreprocessorTest extends TestCase
|
|
{
|
|
private string $testDir;
|
|
private CompilerTest $compiler;
|
|
private \ReflectionClass $ref;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->testDir = sys_get_temp_dir() . '/preprocessor_test_' . uniqid();
|
|
mkdir($this->testDir, 0777, true);
|
|
$this->compiler = CompilerTest::create($this->testDir);
|
|
$this->ref = new \ReflectionClass($this->compiler);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
parent::tearDown();
|
|
if (is_dir($this->testDir)) {
|
|
$this->removeDirectory($this->testDir);
|
|
}
|
|
}
|
|
|
|
private function removeDirectory(string $dir): void
|
|
{
|
|
if (!is_dir($dir)) {
|
|
return;
|
|
}
|
|
$files = array_diff(scandir($dir), ['.', '..']);
|
|
foreach ($files as $file) {
|
|
$path = $dir . DIRECTORY_SEPARATOR . $file;
|
|
is_dir($path) ? $this->removeDirectory($path) : unlink($path);
|
|
}
|
|
rmdir($dir);
|
|
}
|
|
|
|
private function invokeMethod(string $method, ...$args): mixed
|
|
{
|
|
$m = $this->ref->getMethod($method);
|
|
$m->setAccessible(true);
|
|
return $m->invoke($this->compiler, ...$args);
|
|
}
|
|
|
|
private function setProperty(string $name, mixed $value): void
|
|
{
|
|
$prop = $this->ref->getProperty($name);
|
|
$prop->setAccessible(true);
|
|
$prop->setValue($this->compiler, $value);
|
|
}
|
|
|
|
// ========================================================================
|
|
// genArgumentDeclaration
|
|
// ========================================================================
|
|
|
|
public function testGenArgumentDeclarationSimple(): void
|
|
{
|
|
$arg = new ArgInfo();
|
|
$arg->name = 'count';
|
|
$arg->type = 'php::Int';
|
|
$result = $this->invokeMethod('genArgumentDeclaration', $arg);
|
|
$this->assertEquals('php::Int count', $result);
|
|
}
|
|
|
|
public function testGenArgumentDeclarationString(): void
|
|
{
|
|
$arg = new ArgInfo();
|
|
$arg->name = 'name';
|
|
$arg->type = 'php::Str';
|
|
$result = $this->invokeMethod('genArgumentDeclaration', $arg);
|
|
$this->assertEquals('php::Str name', $result);
|
|
}
|
|
|
|
public function testGenArgumentDeclarationObject(): void
|
|
{
|
|
$arg = new ArgInfo();
|
|
$arg->name = 'obj';
|
|
$arg->type = 'php::Object';
|
|
$result = $this->invokeMethod('genArgumentDeclaration', $arg);
|
|
$this->assertEquals('php::Object obj', $result);
|
|
}
|
|
|
|
public function testGenArgumentDeclarationVar(): void
|
|
{
|
|
$arg = new ArgInfo();
|
|
$arg->name = 'container';
|
|
$arg->type = 'php::Var';
|
|
$result = $this->invokeMethod('genArgumentDeclaration', $arg);
|
|
$this->assertEquals('php::Var container', $result);
|
|
}
|
|
|
|
// ========================================================================
|
|
// getCppFile
|
|
// ========================================================================
|
|
|
|
public function testGetCppFile(): void
|
|
{
|
|
$phpFile = '/home/user/project/src/app.php';
|
|
$result = $this->compiler->getCppFile($phpFile);
|
|
|
|
$this->assertStringEndsWith('.cc', $result);
|
|
$this->assertStringStartsWith($this->compiler->getBuildDir(), $result);
|
|
$this->assertStringContainsString('app', $result);
|
|
}
|
|
|
|
public function testGetCppFilePreservesRelativePath(): void
|
|
{
|
|
$phpFile = '/var/www/myapp/controllers/UserController.php';
|
|
$result = $this->compiler->getCppFile($phpFile);
|
|
|
|
$this->assertStringEndsWith('UserController.cc', $result);
|
|
$this->assertStringStartsWith($this->compiler->getBuildDir(), $result);
|
|
}
|
|
|
|
public function testGetCppFileDotPhpReplaced(): void
|
|
{
|
|
$phpFile = '/tmp/test_file.php';
|
|
$result = $this->compiler->getCppFile($phpFile);
|
|
|
|
$this->assertStringEndsWith('test_file.cc', $result);
|
|
$this->assertStringNotContainsString('.php', basename($result));
|
|
}
|
|
|
|
// ========================================================================
|
|
// getObjectFile
|
|
// ========================================================================
|
|
|
|
public function testGetObjectFile(): void
|
|
{
|
|
$cppFile = $this->compiler->getBuildDir() . '/include/test.cc';
|
|
$result = $this->compiler->getObjectFile($cppFile);
|
|
|
|
$this->assertStringEndsWith('.o', $result);
|
|
$this->assertStringContainsString('test', $result);
|
|
}
|
|
|
|
public function testGetObjectFileDifferentObjectExtension(): void
|
|
{
|
|
// On Linux the object extension is .o
|
|
$path = '/some/path/file.cc';
|
|
$result = $this->compiler->getObjectFile($path);
|
|
$this->assertStringEndsWith('file.o', $result);
|
|
}
|
|
|
|
// ========================================================================
|
|
// getMethodName
|
|
// ========================================================================
|
|
|
|
public function testGetMethodName(): void
|
|
{
|
|
$method = new Node\Stmt\ClassMethod('handle');
|
|
$result = $this->invokeMethod('getMethodName', $method);
|
|
$this->assertEquals('handle', $result);
|
|
}
|
|
|
|
public function testGetMethodNameConstructor(): void
|
|
{
|
|
$method = new Node\Stmt\ClassMethod('__construct');
|
|
$result = $this->invokeMethod('getMethodName', $method);
|
|
$this->assertEquals('__construct', $result);
|
|
}
|
|
|
|
// ========================================================================
|
|
// getParentClass
|
|
// ========================================================================
|
|
|
|
public function testGetParentClassWithNamespace(): void
|
|
{
|
|
$extends = new Node\Name('BaseController');
|
|
$this->setProperty('namespace', 'App\\Controllers');
|
|
$result = $this->invokeMethod('getParentClass', $extends);
|
|
$this->assertEquals('App\\Controllers\\BaseController', $result);
|
|
}
|
|
|
|
public function testGetParentClassFullyQualified(): void
|
|
{
|
|
$extends = new Node\Name\FullyQualified('App\\Entity\\Base');
|
|
$result = $this->invokeMethod('getParentClass', $extends);
|
|
$this->assertEquals('App\\Entity\\Base', $result);
|
|
}
|
|
|
|
// ========================================================================
|
|
// sortFiles
|
|
// ========================================================================
|
|
|
|
public function testSortFilesPreservesOrderForUnrelatedFiles(): void
|
|
{
|
|
$files = ['/a/file1.php', '/a/file2.php', '/a/file3.php'];
|
|
$this->compiler->sortFiles($files);
|
|
// All original files must still be present
|
|
$this->assertContains('/a/file1.php', $files);
|
|
$this->assertContains('/a/file2.php', $files);
|
|
$this->assertContains('/a/file3.php', $files);
|
|
// Original files are preserved (sortFiles may append, not remove)
|
|
$this->assertGreaterThanOrEqual(3, count($files));
|
|
}
|
|
|
|
public function testSortFilesEmpty(): void
|
|
{
|
|
$files = [];
|
|
$this->compiler->sortFiles($files);
|
|
// Empty array stays empty or nearly empty
|
|
$this->assertIsArray($files);
|
|
}
|
|
}
|
|
|