add phpunit files

pull/1/head
韩天峰 3 months ago
parent 0c2a25ea52
commit d73d077a7a
  1. 114
      phpunit/src/ArgInfoTest.php
  2. 113
      phpunit/src/Context/FunctionContextTest.php
  3. 15
      phpunit/src/Context/ScopeContextTest.php
  4. 133
      phpunit/src/Entity/ClassDefTest.php
  5. 47
      phpunit/src/Entity/ClassLikeDefTest.php
  6. 68
      phpunit/src/Entity/ConstantDefTest.php
  7. 115
      phpunit/src/Entity/FunctionDefTest.php
  8. 42
      phpunit/src/Entity/InterfaceDefTest.php
  9. 51
      phpunit/src/Entity/MethodDefTest.php
  10. 82
      phpunit/src/Entity/PropertyDefTest.php
  11. 151
      phpunit/src/ExceptionTest.php
  12. 183
      phpunit/src/FileScannerTest.php
  13. 71
      phpunit/src/SymbolTest.php

@ -0,0 +1,114 @@
<?php
namespace PhpAot\Tests;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\ArgInfo;
class ArgInfoTest extends TestCase
{
public function testDefaultBooleanProperties(): void
{
$arg = new ArgInfo();
// Set required string properties first (they have no defaults)
$arg->name = '';
$arg->type = '';
// default values for implicitly-initialized fields
$this->assertEquals('', $arg->default);
$this->assertNull($arg->defaultValue);
$this->assertEquals('', $arg->class);
$this->assertFalse($arg->byRef);
$this->assertFalse($arg->variadic);
$this->assertFalse($arg->nullable);
$this->assertFalse($arg->property);
$this->assertFalse($arg->unsafePtr);
}
public function testNameAndType(): void
{
$arg = new ArgInfo();
$arg->name = 'value';
$arg->type = 'php::Int';
$this->assertEquals('value', $arg->name);
$this->assertEquals('php::Int', $arg->type);
}
public function testDefaultValues(): void
{
$arg = new ArgInfo();
$arg->default = '0';
$this->assertEquals('0', $arg->default);
}
public function testClass(): void
{
$arg = new ArgInfo();
$arg->class = 'DateTime';
$this->assertEquals('DateTime', $arg->class);
}
public function testByRef(): void
{
$arg = new ArgInfo();
$this->assertFalse($arg->byRef);
$arg->byRef = true;
$this->assertTrue($arg->byRef);
}
public function testVariadic(): void
{
$arg = new ArgInfo();
$this->assertFalse($arg->variadic);
$arg->variadic = true;
$this->assertTrue($arg->variadic);
}
public function testNullable(): void
{
$arg = new ArgInfo();
$this->assertFalse($arg->nullable);
$arg->nullable = true;
$this->assertTrue($arg->nullable);
}
public function testProperty(): void
{
$arg = new ArgInfo();
$this->assertFalse($arg->property);
$arg->property = true;
$this->assertTrue($arg->property);
}
public function testUnsafePtr(): void
{
$arg = new ArgInfo();
$this->assertFalse($arg->unsafePtr);
$arg->unsafePtr = true;
$this->assertTrue($arg->unsafePtr);
}
public function testAllFlagsCombined(): void
{
$arg = new ArgInfo();
$arg->name = 'args';
$arg->type = 'php::Var';
$arg->variadic = true;
$arg->byRef = true;
$arg->nullable = true;
$this->assertEquals('args', $arg->name);
$this->assertEquals('php::Var', $arg->type);
$this->assertTrue($arg->variadic);
$this->assertTrue($arg->byRef);
$this->assertTrue($arg->nullable);
}
}

@ -0,0 +1,113 @@
<?php
namespace PhpAot\Tests\Context;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\Context\FunctionContext;
use PhpAot\Php\Context\ScopeContext;
class FunctionContextTest extends TestCase
{
public function testConstructInitializesAllProperties(): void
{
$ctx = new FunctionContext();
$this->assertEmpty($ctx->objects);
$this->assertEmpty($ctx->stdArrays);
$this->assertEmpty($ctx->stdContainers);
$this->assertEmpty($ctx->localVars);
$this->assertEmpty($ctx->staticVars);
$this->assertEmpty($ctx->globalVars);
$this->assertEmpty($ctx->ceWrappers);
$this->assertEmpty($ctx->arguments);
$this->assertEmpty($ctx->beforeStmtLines);
$this->assertEmpty($ctx->afterStmtLines);
$this->assertEmpty($ctx->objectProps);
$this->assertEmpty($ctx->scopeLayouts);
$this->assertSame(0, $ctx->tmpVarIndex);
$this->assertSame(0, $ctx->scopeLevel);
$this->assertFalse($ctx->inLoop);
$this->assertFalse($ctx->inClosure);
$this->assertFalse($ctx->inAssignExpr);
}
public function testEnterScopeIncrementsLevel(): void
{
$ctx = new FunctionContext();
$this->assertSame(0, $ctx->scopeLevel);
$ctx->enterScope();
$this->assertSame(1, $ctx->scopeLevel);
$this->assertArrayHasKey(0, $ctx->scopeLayouts);
$this->assertInstanceOf(ScopeContext::class, $ctx->scopeLayouts[0]);
$ctx->enterScope();
$this->assertSame(2, $ctx->scopeLevel);
$this->assertArrayHasKey(0, $ctx->scopeLayouts);
$this->assertArrayHasKey(1, $ctx->scopeLayouts);
}
public function testLeaveScopeDecrementsLevel(): void
{
$ctx = new FunctionContext();
$ctx->enterScope();
$ctx->enterScope();
$this->assertSame(2, $ctx->scopeLevel);
$ctx->leaveScope();
$this->assertSame(1, $ctx->scopeLevel);
$ctx->leaveScope();
$this->assertSame(0, $ctx->scopeLevel);
}
public function testMutableProperties(): void
{
$ctx = new FunctionContext();
$ctx->inLoop = true;
$this->assertTrue($ctx->inLoop);
$ctx->inClosure = true;
$this->assertTrue($ctx->inClosure);
$ctx->inAssignExpr = true;
$this->assertTrue($ctx->inAssignExpr);
$ctx->tmpVarIndex = 5;
$this->assertSame(5, $ctx->tmpVarIndex);
}
public function testLocalVarsManipulation(): void
{
$ctx = new FunctionContext();
$ctx->localVars['x'] = 'php::Int';
$ctx->localVars['name'] = 'php::Str';
$this->assertArrayHasKey('x', $ctx->localVars);
$this->assertArrayHasKey('name', $ctx->localVars);
$this->assertEquals('php::Int', $ctx->localVars['x']);
$this->assertEquals('php::Str', $ctx->localVars['name']);
}
public function testObjectsMap(): void
{
$ctx = new FunctionContext();
$ctx->objects['obj1'] = 'stdClass';
$ctx->objects['obj2'] = 'DateTime';
$this->assertCount(2, $ctx->objects);
$this->assertEquals('stdClass', $ctx->objects['obj1']);
}
public function testBeforeAfterStmtLines(): void
{
$ctx = new FunctionContext();
$ctx->beforeStmtLines[] = 'int x = 1;';
$ctx->afterStmtLines[] = 'x = 0;';
$this->assertCount(1, $ctx->beforeStmtLines);
$this->assertCount(1, $ctx->afterStmtLines);
}
}

@ -0,0 +1,15 @@
<?php
namespace PhpAot\Tests\Context;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\Context\ScopeContext;
class ScopeContextTest extends TestCase
{
public function testCanInstantiate(): void
{
$scope = new ScopeContext();
$this->assertInstanceOf(ScopeContext::class, $scope);
}
}

@ -0,0 +1,133 @@
<?php
namespace PhpAot\Tests\Entity;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\Entity\ClassDef;
use PhpAot\Php\Entity\MethodDef;
use PhpAot\Php\Entity\PropertyDef;
use PhpAot\Php\Entity\ConstantDef;
use PhpAot\Php\Entity\FunctionDef;
use PhpParser\Modifiers;
class ClassDefTest extends TestCase
{
public function testConstructDefaults(): void
{
$class = new ClassDef('Foo', Modifiers::PUBLIC);
$this->assertEquals('Foo', $class->name);
$this->assertEquals('', $class->namespace);
$this->assertSame(Modifiers::PUBLIC, $class->flags);
$this->assertEquals('', $class->extends);
$this->assertEmpty($class->implements);
$this->assertEmpty($class->methods);
$this->assertEmpty($class->properties);
$this->assertEmpty($class->constants);
$this->assertFalse($class->enum);
$this->assertFalse($class->requireCtor);
$this->assertFalse($class->inheritedFromInternalClass);
$this->assertNull($class->trait);
}
public function testConstructWithNamespace(): void
{
$class = new ClassDef('Bar', Modifiers::PUBLIC, 'App\\Lib');
$this->assertEquals('Bar', $class->name);
$this->assertEquals('App\\Lib', $class->namespace);
}
public function testPropertyContextIsInitialized(): void
{
$class = new ClassDef('Foo', Modifiers::PUBLIC);
$this->assertNotNull($class->propertyContext);
$this->assertInstanceOf(\PhpAot\Php\Context\FunctionContext::class, $class->propertyContext);
}
public function testGetNamespacedNameInheritedFromClassLikeDef(): void
{
$class = new ClassDef('Foo', Modifiers::PUBLIC, 'A\\B');
$this->assertEquals('A_B_Foo', $class->getNamespacedName(true));
$this->assertEquals('A\\B\\Foo', $class->getNamespacedName(false));
}
public function testAddAndHasMethod(): void
{
$class = new ClassDef('Foo', Modifiers::PUBLIC);
$method = new MethodDef(Modifiers::PUBLIC, 'bar');
$this->assertFalse($class->hasMethod('bar'));
$class->addMethod($method);
$this->assertTrue($class->hasMethod('bar'));
$this->assertTrue($class->hasMethod('BAR')); // case-insensitive
$this->assertSame($method, $class->getMethod('bar'));
}
public function testGetMethodCaseInsensitive(): void
{
$class = new ClassDef('Foo', Modifiers::PUBLIC);
$method = new MethodDef(Modifiers::PUBLIC, 'MyMethod');
$class->addMethod($method);
$this->assertSame($method, $class->getMethod('mymethod'));
$this->assertSame($method, $class->getMethod('MYMETHOD'));
}
public function testHasMethodNotFound(): void
{
$class = new ClassDef('Foo', Modifiers::PUBLIC);
$this->assertFalse($class->hasMethod('nonexistent'));
}
public function testHasProperty(): void
{
$class = new ClassDef('Foo', Modifiers::PUBLIC);
$prop = new PropertyDef('name', Modifiers::PUBLIC, 'php::Str');
$class->properties['name'] = $prop;
$this->assertTrue($class->hasProperty('name'));
$this->assertFalse($class->hasProperty('missing'));
$this->assertSame($prop, $class->getProperty('name'));
}
public function testHasConstant(): void
{
$class = new ClassDef('Foo', Modifiers::PUBLIC);
$const = new ConstantDef('VERSION', Modifiers::PUBLIC, 'php::Str', '"1.0"');
$class->constants['VERSION'] = $const;
$this->assertTrue($class->hasConstant('VERSION'));
$this->assertFalse($class->hasConstant('MISSING'));
$this->assertSame($const, $class->getConstant('VERSION'));
}
public function testIsAbstract(): void
{
$abstractClass = new ClassDef('AbstractFoo', Modifiers::ABSTRACT);
$this->assertTrue($abstractClass->isAbstract());
$concreteClass = new ClassDef('ConcreteFoo', Modifiers::PUBLIC);
$this->assertFalse($concreteClass->isAbstract());
}
public function testRequireCtorAndEnumDefaults(): void
{
$class = new ClassDef('Foo', Modifiers::PUBLIC);
$this->assertFalse($class->requireCtor);
$this->assertFalse($class->enum);
}
public function testTraitAliasesAndIgnoredDefaults(): void
{
$class = new ClassDef('Foo', Modifiers::PUBLIC);
$this->assertEmpty($class->traitAliases);
$this->assertEmpty($class->traitIgnored);
}
public function testImplementsAndExtendsDefaults(): void
{
$class = new ClassDef('Foo', Modifiers::PUBLIC);
$this->assertEmpty($class->implements);
$this->assertEquals('', $class->extends);
}
}

@ -0,0 +1,47 @@
<?php
namespace PhpAot\Tests\Entity;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\Entity\ClassLikeDef;
use PhpAot\Php\Entity\ClassDef;
use PhpAot\Php\Entity\InterfaceDef;
use PhpParser\Modifiers;
class ClassLikeDefTest extends TestCase
{
public function testConstructWithoutNamespace(): void
{
$def = new ClassLikeDef('Foo');
$this->assertEquals('Foo', $def->name);
$this->assertEquals('', $def->namespace);
$this->assertEquals('', $def->extends);
}
public function testConstructWithNamespace(): void
{
$def = new ClassLikeDef('Foo', 'App\\Bar');
$this->assertEquals('Foo', $def->name);
$this->assertEquals('App\\Bar', $def->namespace);
}
public function testGetNamespacedNameWithoutNamespace(): void
{
$def = new ClassLikeDef('Foo');
$this->assertEquals('Foo', $def->getNamespacedName());
$this->assertEquals('Foo', $def->getNamespacedName(false));
}
public function testGetNamespacedNameSymbolic(): void
{
$def = new ClassLikeDef('Foo', 'App\\Bar');
// symbolic: backslashes → underscores → App_Bar_Foo
$this->assertEquals('App_Bar_Foo', $def->getNamespacedName(true));
}
public function testGetNamespacedNameNonSymbolic(): void
{
$def = new ClassLikeDef('Foo', 'App\\Bar');
$this->assertEquals('App\\Bar\\Foo', $def->getNamespacedName(false));
}
}

@ -0,0 +1,68 @@
<?php
namespace PhpAot\Tests\Entity;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\Entity\ConstantDef;
use PhpParser\Modifiers;
class ConstantDefTest extends TestCase
{
public function testConstruct(): void
{
$const = new ConstantDef('MAX_SIZE', Modifiers::PUBLIC, 'php::Int', '1024L');
$this->assertEquals('MAX_SIZE', $const->name);
$this->assertSame(Modifiers::PUBLIC, $const->flags);
$this->assertEquals('php::Int', $const->type);
$this->assertEquals('1024L', $const->value);
$this->assertEquals('', $const->arrayExpr);
$this->assertEquals('', $const->class);
$this->assertNull($const->valueExpr);
}
public function testConstructWithStringValue(): void
{
$const = new ConstantDef('APP_NAME', Modifiers::PUBLIC, 'php::Str', 'php::Str{"MyApp"}');
$this->assertEquals('APP_NAME', $const->name);
$this->assertEquals('php::Str', $const->type);
$this->assertEquals('php::Str{"MyApp"}', $const->value);
}
public function testClassProperty(): void
{
$const = new ConstantDef('PI', Modifiers::PUBLIC, 'php::Float', '3.14');
$this->assertEquals('', $const->class);
$const->class = 'Math';
$this->assertEquals('Math', $const->class);
}
public function testArrayExprProperty(): void
{
$const = new ConstantDef('ITEMS', Modifiers::PUBLIC, 'php::Array', '[]');
$this->assertEquals('', $const->arrayExpr);
$const->arrayExpr = 'zval arr; array_init(&arr);';
$this->assertEquals('zval arr; array_init(&arr);', $const->arrayExpr);
}
public function testValueExprProperty(): void
{
$const = new ConstantDef('VALUE', Modifiers::PUBLIC, 'php::Int', '1L');
$this->assertNull($const->valueExpr);
}
public function testDifferentVisibilityFlags(): void
{
$publicConst = new ConstantDef('A', Modifiers::PUBLIC, 'php::Int', '1');
$this->assertSame(Modifiers::PUBLIC, $publicConst->flags);
$protectedConst = new ConstantDef('B', Modifiers::PROTECTED, 'php::Int', '2');
$this->assertSame(Modifiers::PROTECTED, $protectedConst->flags);
$privateConst = new ConstantDef('C', Modifiers::PRIVATE, 'php::Int', '3');
$this->assertSame(Modifiers::PRIVATE, $privateConst->flags);
}
}

@ -0,0 +1,115 @@
<?php
namespace PhpAot\Tests\Entity;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\Entity\FunctionDef;
use PhpAot\Php\ArgInfo;
class FunctionDefTest extends TestCase
{
public function testConstruct(): void
{
$fn = new FunctionDef('test', 'void', '');
$this->assertEquals('test', $fn->name);
$this->assertEquals('void', $fn->returnType);
$this->assertEquals('', $fn->namespace);
$this->assertFalse($fn->method);
$this->assertFalse($fn->stub);
$this->assertEmpty($fn->argInfoList);
$this->assertEquals(0, $fn->argCountRequired);
$this->assertEquals('', $fn->params);
$this->assertEquals('', $fn->returnClass);
}
public function testConstructWithNamespace(): void
{
$fn = new FunctionDef('run', 'php::Int', 'App\\Lib');
$this->assertEquals('run', $fn->name);
$this->assertEquals('php::Int', $fn->returnType);
$this->assertEquals('App\\Lib', $fn->namespace);
}
public function testGetNamespacedNameWithoutNamespace(): void
{
$fn = new FunctionDef('main', 'void', '');
$this->assertEquals('main', $fn->getNamespacedName());
}
public function testGetNamespacedNameWithNamespace(): void
{
$fn = new FunctionDef('run', 'void', 'App\\Lib');
$this->assertEquals('App\\Lib\\run', $fn->getNamespacedName());
}
public function testHasVariadicArgEmptyList(): void
{
$fn = new FunctionDef('test', 'void', '');
$this->assertFalse($fn->hasVariadicArg());
}
public function testHasVariadicArgFalse(): void
{
$fn = new FunctionDef('test', 'void', '');
$arg1 = new ArgInfo();
$arg1->name = 'x';
$arg1->type = 'php::Int';
$arg1->variadic = false;
$fn->argInfoList = [$arg1];
$this->assertFalse($fn->hasVariadicArg());
}
public function testHasVariadicArgTrue(): void
{
$fn = new FunctionDef('test', 'void', '');
$arg1 = new ArgInfo();
$arg1->name = 'args';
$arg1->type = 'php::Var';
$arg1->variadic = true;
$fn->argInfoList = [$arg1];
$this->assertTrue($fn->hasVariadicArg());
}
public function testHasVariadicArgLastOnly(): void
{
$fn = new FunctionDef('test', 'void', '');
$arg1 = new ArgInfo();
$arg1->name = 'x';
$arg1->type = 'php::Int';
$arg1->variadic = false;
$arg2 = new ArgInfo();
$arg2->name = 'rest';
$arg2->type = 'php::Var';
$arg2->variadic = true;
$fn->argInfoList = [$arg1, $arg2];
$this->assertTrue($fn->hasVariadicArg());
}
public function testMethodFlag(): void
{
$fn = new FunctionDef('handle', 'void', '');
$this->assertFalse($fn->method);
$fn->method = true;
$this->assertTrue($fn->method);
}
public function testParamsAndReturnClassSettable(): void
{
$fn = new FunctionDef('test', 'php::Object', '');
$fn->params = 'php::Int x, php::Str y';
$fn->returnClass = 'DateTime';
$this->assertEquals('php::Int x, php::Str y', $fn->params);
$this->assertEquals('DateTime', $fn->returnClass);
}
}

@ -0,0 +1,42 @@
<?php
namespace PhpAot\Tests\Entity;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\Entity\InterfaceDef;
class InterfaceDefTest extends TestCase
{
public function testConstructWithoutNamespace(): void
{
$iface = new InterfaceDef('JsonSerializable');
$this->assertEquals('JsonSerializable', $iface->name);
$this->assertEquals('', $iface->namespace);
$this->assertEquals('', $iface->extends);
}
public function testConstructWithNamespace(): void
{
$iface = new InterfaceDef('Renderable', 'App\\Contracts');
$this->assertEquals('Renderable', $iface->name);
$this->assertEquals('App\\Contracts', $iface->namespace);
}
public function testGetNamespacedName(): void
{
$iface = new InterfaceDef('Logger', 'App\\Contracts');
$this->assertEquals('App_Contracts_Logger', $iface->getNamespacedName(true));
$this->assertEquals('App\\Contracts\\Logger', $iface->getNamespacedName(false));
}
public function testExtendsCanBeSet(): void
{
$iface = new InterfaceDef('Child');
$iface->extends = 'Parent';
$this->assertEquals('Parent', $iface->extends);
}
}

@ -0,0 +1,51 @@
<?php
namespace PhpAot\Tests\Entity;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\Entity\MethodDef;
use PhpAot\Php\Entity\FunctionDef;
use PhpParser\Modifiers;
class MethodDefTest extends TestCase
{
public function testConstruct(): void
{
$method = new MethodDef(Modifiers::PUBLIC, 'handle');
$this->assertEquals('handle', $method->name);
$this->assertSame(Modifiers::PUBLIC, $method->flags);
$this->assertNull($method->functionDef);
$this->assertFalse($method->hasDynamicCall);
}
public function testGetReturnType(): void
{
$method = new MethodDef(Modifiers::PUBLIC, 'getValue');
$fn = new FunctionDef('getValue', 'php::Int', '');
$method->functionDef = $fn;
$this->assertEquals('php::Int', $method->getReturnType());
}
public function testGetReturnTypeVoid(): void
{
$method = new MethodDef(Modifiers::PROTECTED, 'execute');
$fn = new FunctionDef('execute', 'void', '');
$method->functionDef = $fn;
$this->assertEquals('void', $method->getReturnType());
}
public function testFlags(): void
{
$publicMethod = new MethodDef(Modifiers::PUBLIC, 'pub');
$this->assertSame(Modifiers::PUBLIC, $publicMethod->flags);
$protectedMethod = new MethodDef(Modifiers::PROTECTED, 'prot');
$this->assertSame(Modifiers::PROTECTED, $protectedMethod->flags);
$privateMethod = new MethodDef(Modifiers::PRIVATE, 'priv');
$this->assertSame(Modifiers::PRIVATE, $privateMethod->flags);
}
}

@ -0,0 +1,82 @@
<?php
namespace PhpAot\Tests\Entity;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\Entity\PropertyDef;
use PhpParser\Modifiers;
class PropertyDefTest extends TestCase
{
public function testConstructWithDefaults(): void
{
$prop = new PropertyDef('count', Modifiers::PUBLIC, 'php::Int');
$this->assertEquals('count', $prop->name);
$this->assertSame(Modifiers::PUBLIC, $prop->flags);
$this->assertEquals('php::Int', $prop->type);
$this->assertNull($prop->default);
$this->assertFalse($prop->nullable);
$this->assertEquals('', $prop->class);
}
public function testConstructWithDefaultValue(): void
{
$prop = new PropertyDef('count', Modifiers::PUBLIC, 'php::Int', '0');
$this->assertEquals('0', $prop->default);
}
public function testConstructWithNullable(): void
{
$prop = new PropertyDef('name', Modifiers::PUBLIC, 'php::Str', null, true);
$this->assertTrue($prop->nullable);
}
public function testIsPrivate(): void
{
$privateProp = new PropertyDef('secret', Modifiers::PRIVATE, 'php::Str');
$this->assertTrue($privateProp->isPrivate());
$this->assertFalse($privateProp->isProtected());
$this->assertFalse($privateProp->isPublic());
}
public function testIsProtected(): void
{
$protectedProp = new PropertyDef('value', Modifiers::PROTECTED, 'php::Int');
$this->assertFalse($protectedProp->isPrivate());
$this->assertTrue($protectedProp->isProtected());
$this->assertFalse($protectedProp->isPublic());
}
public function testIsPublic(): void
{
$publicProp = new PropertyDef('name', Modifiers::PUBLIC, 'php::Str');
$this->assertFalse($publicProp->isPrivate());
$this->assertFalse($publicProp->isProtected());
$this->assertTrue($publicProp->isPublic());
// PUBLIC flag is 0, so no-flag properties are also public
$impliedPublic = new PropertyDef('name', 0, 'php::Str');
$this->assertTrue($impliedPublic->isPublic());
}
public function testIsStatic(): void
{
$staticProp = new PropertyDef('counter', Modifiers::PUBLIC | Modifiers::STATIC, 'php::Int');
$this->assertTrue($staticProp->isStatic());
$instanceProp = new PropertyDef('counter', Modifiers::PUBLIC, 'php::Int');
$this->assertFalse($instanceProp->isStatic());
}
public function testClassProperty(): void
{
$prop = new PropertyDef('data', Modifiers::PUBLIC, 'php::Var');
$this->assertEquals('', $prop->class);
$prop->class = 'App\\Entity\\User';
$this->assertEquals('App\\Entity\\User', $prop->class);
}
}

@ -0,0 +1,151 @@
<?php
namespace PhpAot\Tests;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\Exception\DynamicCall;
use PhpAot\Php\Exception\PlaceHolder;
use PhpAot\Php\Exception\Redo;
use PhpAot\Php\Exception\Skip;
use PhpAot\Php\Exception\SyntaxError;
use PhpAot\Php\Exception\TestError;
use PhpAot\Php\Exception\Unsupported;
use PhpAot\Php\Constants;
class ExceptionTest extends TestCase
{
public function testDynamicCall(): void
{
$e = new DynamicCall('Dynamic call error');
$this->assertInstanceOf(\RuntimeException::class, $e);
$this->assertEquals('Dynamic call error', $e->getMessage());
}
public function testPlaceHolder(): void
{
$e = new PlaceHolder('Placeholder error');
$this->assertInstanceOf(\RuntimeException::class, $e);
$this->assertEquals('Placeholder error', $e->getMessage());
}
public function testRedo(): void
{
$e = new Redo('Redo compilation');
$this->assertInstanceOf(\RuntimeException::class, $e);
$this->assertStringContainsString('Redo', $e->getMessage());
}
public function testSkip(): void
{
$e = new Skip('Skip this file');
$this->assertInstanceOf(\RuntimeException::class, $e);
$this->assertStringContainsString('Skip', $e->getMessage());
}
public function testSyntaxError(): void
{
$e = new SyntaxError('Unexpected token');
$this->assertInstanceOf(\RuntimeException::class, $e);
$this->assertStringContainsString('Unexpected', $e->getMessage());
}
public function testTestError(): void
{
$e = new TestError('Test assertion failed');
$this->assertInstanceOf(\RuntimeException::class, $e);
$this->assertStringContainsString('Test assertion', $e->getMessage());
}
public function testUnsupported(): void
{
$e = new Unsupported('Unsupported syntax');
$this->assertInstanceOf(\RuntimeException::class, $e);
$this->assertStringContainsString('Unsupported', $e->getMessage());
}
public function testCustomErrorCodes(): void
{
$e = new TestError('Error 42', 42);
$this->assertEquals(42, $e->getCode());
}
public function testCatchByBaseRuntimeException(): void
{
// All exceptions can be caught by their parent RuntimeException
$errors = [
new DynamicCall('test'),
new PlaceHolder('test'),
new Redo('test'),
new Skip('test'),
new SyntaxError('test'),
new TestError('test'),
new Unsupported('test'),
];
foreach ($errors as $e) {
try {
throw $e;
} catch (\RuntimeException $caught) {
$this->assertSame($e, $caught);
}
}
}
public function testConstantsReservedNames(): void
{
$this->assertIsArray(Constants::CPP_RESERVED_NAMES);
$this->assertContains('class', Constants::CPP_RESERVED_NAMES);
$this->assertContains('namespace', Constants::CPP_RESERVED_NAMES);
$this->assertContains('int', Constants::CPP_RESERVED_NAMES);
$this->assertContains('void', Constants::CPP_RESERVED_NAMES);
}
public function testConstantsUnsupportedFunctions(): void
{
$this->assertIsArray(Constants::UNSUPPORTED_FUNCTIONS);
$this->assertContains('extract', Constants::UNSUPPORTED_FUNCTIONS);
}
public function testConstantsCompilerOptions(): void
{
$options = Constants::COMPILER_OPTIONS;
$this->assertIsArray($options);
$this->assertArrayHasKey('optimize', $options);
$this->assertArrayHasKey('output', $options);
$this->assertArrayHasKey('help', $options);
$this->assertArrayHasKey('debug', $options);
$this->assertArrayHasKey('job', $options);
$this->assertArrayHasKey('mode', $options);
}
public function testCompilerOptionOptimizeDefaults(): void
{
$opt = Constants::COMPILER_OPTIONS['optimize'];
$this->assertEquals('O', $opt['prefix']);
$this->assertIsInt($opt['defaultValue']);
$this->assertEquals(0, $opt['defaultValue']);
}
public function testCompilerOptionModeDefaults(): void
{
$opt = Constants::COMPILER_OPTIONS['mode'];
$this->assertEquals('bin', $opt['defaultValue']);
}
public function testCompilerOptionNoValueFlags(): void
{
$noValueFlags = ['help', 'version', 'no-literal-strings', 'force', 'debug', 'no-console'];
foreach ($noValueFlags as $flag) {
$this->assertArrayHasKey($flag, Constants::COMPILER_OPTIONS);
$this->assertTrue(Constants::COMPILER_OPTIONS[$flag]['noValue'] ?? false);
}
}
public function testMsvcSuppressedWarnings(): void
{
$warnings = Constants::MSVC_SUPPRESSED_WARNINGS;
$this->assertIsArray($warnings);
$this->assertArrayHasKey('4244', $warnings);
$this->assertArrayHasKey('4127', $warnings);
}
}

@ -0,0 +1,183 @@
<?php
namespace PhpAot\Tests;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\FileScanner;
class FileScannerTest extends TestCase
{
// ========================================================================
// Extension type detection
// ========================================================================
public function testIsPhpFile(): void
{
$this->assertTrue(FileScanner::isPhpFile('/path/to/file.php'));
$this->assertFalse(FileScanner::isPhpFile('/path/to/file.cc'));
$this->assertFalse(FileScanner::isPhpFile('/path/to/file.c'));
$this->assertFalse(FileScanner::isPhpFile('/path/to/file.py'));
}
public function testIsCppFile(): void
{
$this->assertTrue(FileScanner::isCppFile('/path/to/file.cc'));
$this->assertTrue(FileScanner::isCppFile('/path/to/file.cpp'));
$this->assertTrue(FileScanner::isCppFile('/path/to/file.cxx'));
$this->assertFalse(FileScanner::isCppFile('/path/to/file.c'));
$this->assertFalse(FileScanner::isCppFile('/path/to/file.php'));
}
public function testIsNativeSourceFile(): void
{
// C++ files
$this->assertTrue(FileScanner::isNativeSourceFile('/path/to/file.cc'));
$this->assertTrue(FileScanner::isNativeSourceFile('/path/to/file.cpp'));
$this->assertTrue(FileScanner::isNativeSourceFile('/path/to/file.cxx'));
// C files
$this->assertTrue(FileScanner::isNativeSourceFile('/path/to/file.c'));
// Assembly files
$this->assertTrue(FileScanner::isNativeSourceFile('/path/to/file.s'));
$this->assertTrue(FileScanner::isNativeSourceFile('/path/to/file.S'));
// Objective-C files
$this->assertTrue(FileScanner::isNativeSourceFile('/path/to/file.m'));
$this->assertTrue(FileScanner::isNativeSourceFile('/path/to/file.mm'));
// Not native source
$this->assertFalse(FileScanner::isNativeSourceFile('/path/to/file.php'));
$this->assertFalse(FileScanner::isNativeSourceFile('/path/to/file.py'));
$this->assertFalse(FileScanner::isNativeSourceFile('/path/to/file.h'));
$this->assertFalse(FileScanner::isNativeSourceFile('/path/to/file.hpp'));
}
// ========================================================================
// getFileName / getFileExt
// ========================================================================
public function testGetFileName(): void
{
$this->assertEquals('hello', FileScanner::getFileName('/path/to/hello.php'));
$this->assertEquals('helper', FileScanner::getFileName('/path/to/helper.c'));
$this->assertEquals('main', FileScanner::getFileName('/path/to/main.cc'));
}
public function testGetFileNameWithMultipleDots(): void
{
$this->assertEquals('my.test', FileScanner::getFileName('/path/to/my.test.php'));
}
public function testGetFileExt(): void
{
$this->assertEquals('php', FileScanner::getFileExt('/path/to/file.php'));
$this->assertEquals('c', FileScanner::getFileExt('/path/to/file.c'));
$this->assertEquals('cc', FileScanner::getFileExt('/path/to/file.cc'));
$this->assertEquals('S', FileScanner::getFileExt('/path/to/file.S'));
}
// ========================================================================
// Constants
// ========================================================================
public function testPhpExtConstant(): void
{
$this->assertEquals(['php'], FileScanner::PHP_EXT);
}
public function testCppExtConstant(): void
{
$this->assertContains('cc', FileScanner::CPP_EXT);
$this->assertContains('cpp', FileScanner::CPP_EXT);
$this->assertContains('cxx', FileScanner::CPP_EXT);
}
public function testCextConstant(): void
{
$this->assertEquals(['c'], FileScanner::C_EXT);
}
public function testAsmExtConstant(): void
{
$this->assertContains('s', FileScanner::ASM_EXT);
$this->assertContains('S', FileScanner::ASM_EXT);
}
public function testObjcExtConstant(): void
{
$this->assertEquals(['m'], FileScanner::OBJC_EXT);
}
public function testObjcxxExtConstant(): void
{
$this->assertEquals(['mm'], FileScanner::OBJCXX_EXT);
}
public function testNativeSrcExtConstant(): void
{
$expected = ['cpp', 'cxx', 'cc', 'c', 's', 'S', 'm', 'mm'];
sort($expected);
$actual = FileScanner::NATIVE_SRC_EXT;
sort($actual);
$this->assertEquals($expected, $actual);
}
// ========================================================================
// Scan method
// ========================================================================
public function testScanDirectory(): void
{
$tmpDir = sys_get_temp_dir() . '/file_scanner_test_' . uniqid();
mkdir($tmpDir, 0777, true);
mkdir($tmpDir . '/sub', 0777, true);
touch($tmpDir . '/main.php');
touch($tmpDir . '/helper.c');
touch($tmpDir . '/math.S');
touch($tmpDir . '/module.cc');
touch($tmpDir . '/lib.m');
touch($tmpDir . '/ignored.py');
touch($tmpDir . '/README.md');
touch($tmpDir . '/sub/nested.cpp');
$scanner = new FileScanner($tmpDir);
$files = $scanner->scan();
// Python and Markdown should not be included
$this->assertContains($tmpDir . '/main.php', $files);
$this->assertContains($tmpDir . '/helper.c', $files);
$this->assertContains($tmpDir . '/math.S', $files);
$this->assertContains($tmpDir . '/module.cc', $files);
$this->assertContains($tmpDir . '/lib.m', $files);
$this->assertContains($tmpDir . '/sub/nested.cpp', $files);
$this->assertNotContains($tmpDir . '/ignored.py', $files);
$this->assertNotContains($tmpDir . '/README.md', $files);
// Clean up
unlink($tmpDir . '/main.php');
unlink($tmpDir . '/helper.c');
unlink($tmpDir . '/math.S');
unlink($tmpDir . '/module.cc');
unlink($tmpDir . '/lib.m');
unlink($tmpDir . '/ignored.py');
unlink($tmpDir . '/README.md');
unlink($tmpDir . '/sub/nested.cpp');
rmdir($tmpDir . '/sub');
rmdir($tmpDir);
}
public function testConstructorRejectsNonexistentDir(): void
{
$this->expectException(\InvalidArgumentException::class);
new FileScanner('/nonexistent/path');
}
public function testGetDirectory(): void
{
$tmpDir = sys_get_temp_dir() . '/file_scanner_getdir_' . uniqid();
mkdir($tmpDir, 0777, true);
$scanner = new FileScanner($tmpDir);
$this->assertEquals($tmpDir, $scanner->getDirectory());
rmdir($tmpDir);
}
}

@ -0,0 +1,71 @@
<?php
namespace PhpAot\Tests;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\Symbol;
use PhpAot\Php\CompilerBase;
class SymbolTest extends TestCase
{
public function testGetStaticProperty(): void
{
$this->assertEquals('php::getStaticProperty', Symbol::getStaticProperty());
}
public function testSetStaticProperty(): void
{
$this->assertEquals('php::setStaticProperty', Symbol::setStaticProperty());
}
public function testInstanceOf(): void
{
$this->assertEquals('php::instanceOf', Symbol::instanceOf());
}
public function testConcat(): void
{
$this->assertEquals('php::concat', Symbol::concat());
}
public function testConstant(): void
{
$this->assertEquals('php::constant', Symbol::constant());
}
public function testGetClassEntrySafe(): void
{
$this->assertEquals('php::getClassEntrySafe', Symbol::getClassEntrySafe());
}
public function testArgList(): void
{
$this->assertEquals('php::ArgList', Symbol::argList());
}
public function testGetCalledCe(): void
{
$result = Symbol::getCalledCe();
$this->assertStringContainsString(CompilerBase::PREFIX, $result);
$this->assertStringContainsString('get_called_ce', $result);
}
public function testGetCalledClass(): void
{
$result = Symbol::getCalledClass();
$this->assertStringContainsString(CompilerBase::PREFIX, $result);
$this->assertStringContainsString('get_called_class', $result);
}
public function testSafeIndex(): void
{
$result = Symbol::safeIndex('0', '10');
$this->assertEquals('php::safeIndex(0, 10)', $result);
}
public function testSafeIndexWithVariables(): void
{
$result = Symbol::safeIndex('i', 'count');
$this->assertEquals('php::safeIndex(i, count)', $result);
}
}
Loading…
Cancel
Save