mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-02 14:25:41 +08:00
Merge pull request #835 from crazywhalecc/chore/test-and-validate
Chore: PHPUnit test & docs & PHPDoc for vendor mode
This commit is contained in:
@@ -103,7 +103,7 @@ class BuilderTest extends TestCase
|
||||
{
|
||||
if (file_exists(SOURCE_PATH . '/php-src/main/php_version.h')) {
|
||||
$file = SOURCE_PATH . '/php-src/main/php_version.h';
|
||||
$cnt = preg_match('/PHP_VERSION "(\d+\.\d+\.\d+)"/', file_get_contents($file), $match);
|
||||
$cnt = preg_match('/PHP_VERSION "(\d+\.\d+\.\d+(?:-[^"]+)?)/', file_get_contents($file), $match);
|
||||
if ($cnt !== 0) {
|
||||
$this->assertEquals($match[1], $this->builder->getPHPVersion());
|
||||
} else {
|
||||
|
||||
@@ -44,6 +44,35 @@ class ConfigValidatorTest extends TestCase
|
||||
'type' => 'url',
|
||||
'url' => 'https://example.com',
|
||||
],
|
||||
'source7' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://example.com',
|
||||
'filename' => 'test.tar.gz',
|
||||
'path' => 'test/path',
|
||||
'provide-pre-built' => true,
|
||||
'prefer-stable' => false,
|
||||
'license' => [
|
||||
'type' => 'file',
|
||||
'path' => 'LICENSE',
|
||||
],
|
||||
],
|
||||
'source8' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://example.com',
|
||||
'alt' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://alt.example.com',
|
||||
],
|
||||
],
|
||||
'source9' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://example.com',
|
||||
'alt' => false,
|
||||
'license' => [
|
||||
'type' => 'text',
|
||||
'text' => 'MIT License',
|
||||
],
|
||||
],
|
||||
];
|
||||
try {
|
||||
ConfigValidator::validateSource($good_source);
|
||||
@@ -83,6 +112,47 @@ class ConfigValidatorTest extends TestCase
|
||||
'source6' => [
|
||||
'type' => 'url', // no url
|
||||
],
|
||||
'source7' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://example.com',
|
||||
'provide-pre-built' => 'not boolean', // not boolean
|
||||
],
|
||||
'source8' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://example.com',
|
||||
'prefer-stable' => 'not boolean', // not boolean
|
||||
],
|
||||
'source9' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://example.com',
|
||||
'license' => 'not object', // not object
|
||||
],
|
||||
'source10' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://example.com',
|
||||
'license' => [
|
||||
'type' => 'invalid', // invalid type
|
||||
],
|
||||
],
|
||||
'source11' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://example.com',
|
||||
'license' => [
|
||||
'type' => 'file', // missing path
|
||||
],
|
||||
],
|
||||
'source12' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://example.com',
|
||||
'license' => [
|
||||
'type' => 'text', // missing text
|
||||
],
|
||||
],
|
||||
'source13' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://example.com',
|
||||
'alt' => 'not object or boolean', // not object or boolean
|
||||
],
|
||||
];
|
||||
foreach ($bad_source as $name => $src) {
|
||||
try {
|
||||
@@ -112,9 +182,38 @@ class ConfigValidatorTest extends TestCase
|
||||
'lib1',
|
||||
],
|
||||
],
|
||||
'lib4' => [
|
||||
'source' => 'source4',
|
||||
'headers' => [
|
||||
'header1.h',
|
||||
'header2.h',
|
||||
],
|
||||
'headers-windows' => [
|
||||
'windows_header.h',
|
||||
],
|
||||
'bin-unix' => [
|
||||
'binary1',
|
||||
'binary2',
|
||||
],
|
||||
'frameworks' => [
|
||||
'CoreFoundation',
|
||||
'SystemConfiguration',
|
||||
],
|
||||
],
|
||||
'lib5' => [
|
||||
'type' => 'package',
|
||||
'source' => 'source5',
|
||||
'pkg-configs' => [
|
||||
'pkg1',
|
||||
'pkg2',
|
||||
],
|
||||
],
|
||||
'lib6' => [
|
||||
'type' => 'root',
|
||||
],
|
||||
];
|
||||
try {
|
||||
ConfigValidator::validateLibs($good_libs, ['source1' => [], 'source2' => [], 'source3' => []]);
|
||||
ConfigValidator::validateLibs($good_libs, ['source1' => [], 'source2' => [], 'source3' => [], 'source4' => [], 'source5' => []]);
|
||||
$this->assertTrue(true);
|
||||
} catch (ValidationException $e) {
|
||||
$this->fail($e->getMessage());
|
||||
@@ -193,6 +292,20 @@ class ConfigValidatorTest extends TestCase
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
// headers must be list
|
||||
try {
|
||||
ConfigValidator::validateLibs(['lib1' => ['source' => 'source1', 'headers' => 'not list']], ['source1' => [], 'source2' => []]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
// bin must be list
|
||||
try {
|
||||
ConfigValidator::validateLibs(['lib1' => ['source' => 'source1', 'bin-unix' => 'not list']], ['source1' => [], 'source2' => []]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,15 +313,459 @@ class ConfigValidatorTest extends TestCase
|
||||
*/
|
||||
public function testValidateExts(): void
|
||||
{
|
||||
ConfigValidator::validateExts([]);
|
||||
// Test valid extensions
|
||||
$valid_exts = [
|
||||
'ext1' => [
|
||||
'type' => 'builtin',
|
||||
],
|
||||
'ext2' => [
|
||||
'type' => 'external',
|
||||
'source' => 'source1',
|
||||
],
|
||||
'ext3' => [
|
||||
'type' => 'external',
|
||||
'source' => 'source2',
|
||||
'arg-type' => 'enable',
|
||||
'lib-depends' => ['lib1'],
|
||||
'lib-suggests' => ['lib2'],
|
||||
'ext-depends-windows' => ['ext1'],
|
||||
'support' => [
|
||||
'Windows' => 'wip',
|
||||
'BSD' => 'wip',
|
||||
],
|
||||
'notes' => true,
|
||||
],
|
||||
'ext4' => [
|
||||
'type' => 'external',
|
||||
'source' => 'source3',
|
||||
'arg-type-unix' => 'with-path',
|
||||
'arg-type-windows' => 'with',
|
||||
],
|
||||
];
|
||||
ConfigValidator::validateExts($valid_exts);
|
||||
|
||||
// Test invalid data
|
||||
$this->expectException(ValidationException::class);
|
||||
ConfigValidator::validateExts(null);
|
||||
}
|
||||
|
||||
public function testValidateExtsBad(): void
|
||||
{
|
||||
// Test invalid extension type
|
||||
try {
|
||||
ConfigValidator::validateExts(['ext1' => ['type' => 'invalid']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test external extension without source
|
||||
try {
|
||||
ConfigValidator::validateExts(['ext1' => ['type' => 'external']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test non-object extension
|
||||
try {
|
||||
ConfigValidator::validateExts(['ext1' => 'not object']);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test invalid source type
|
||||
try {
|
||||
ConfigValidator::validateExts(['ext1' => ['type' => 'external', 'source' => true]]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test invalid support
|
||||
try {
|
||||
ConfigValidator::validateExts(['ext1' => ['type' => 'builtin', 'support' => 'not object']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test invalid notes
|
||||
try {
|
||||
ConfigValidator::validateExts(['ext1' => ['type' => 'builtin', 'notes' => 'not boolean']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test invalid lib-depends
|
||||
try {
|
||||
ConfigValidator::validateExts(['ext1' => ['type' => 'builtin', 'lib-depends' => 'not list']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test invalid arg-type
|
||||
try {
|
||||
ConfigValidator::validateExts(['ext1' => ['type' => 'builtin', 'arg-type' => 'invalid']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test invalid arg-type with suffix
|
||||
try {
|
||||
ConfigValidator::validateExts(['ext1' => ['type' => 'builtin', 'arg-type-unix' => 'invalid']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function testValidatePkgs(): void
|
||||
{
|
||||
ConfigValidator::validatePkgs([]);
|
||||
// Test valid packages (all supported types)
|
||||
$valid_pkgs = [
|
||||
'pkg1' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://example.com/file.tar.gz',
|
||||
],
|
||||
'pkg2' => [
|
||||
'type' => 'ghrel',
|
||||
'repo' => 'owner/repo',
|
||||
'match' => 'file.+\.tar\.gz',
|
||||
],
|
||||
'pkg3' => [
|
||||
'type' => 'custom',
|
||||
],
|
||||
'pkg4' => [
|
||||
'type' => 'url',
|
||||
'url' => 'https://example.com/archive.zip',
|
||||
'filename' => 'archive.zip',
|
||||
'path' => 'extract/path',
|
||||
'extract-files' => [
|
||||
'source/file.exe' => '{pkg_root_path}/bin/file.exe',
|
||||
'source/lib.dll' => '{pkg_root_path}/lib/lib.dll',
|
||||
],
|
||||
],
|
||||
'pkg5' => [
|
||||
'type' => 'ghrel',
|
||||
'repo' => 'owner/repo',
|
||||
'match' => 'release.+\.zip',
|
||||
'extract-files' => [
|
||||
'binary' => '{pkg_root_path}/bin/binary',
|
||||
],
|
||||
],
|
||||
'pkg6' => [
|
||||
'type' => 'filelist',
|
||||
'url' => 'https://example.com/filelist',
|
||||
'regex' => '/href="(?<file>.*\.tar\.gz)"/',
|
||||
],
|
||||
'pkg7' => [
|
||||
'type' => 'git',
|
||||
'url' => 'https://github.com/owner/repo.git',
|
||||
'rev' => 'main',
|
||||
],
|
||||
'pkg8' => [
|
||||
'type' => 'git',
|
||||
'url' => 'https://github.com/owner/repo.git',
|
||||
'rev' => 'v1.0.0',
|
||||
'path' => 'subdir/path',
|
||||
],
|
||||
'pkg9' => [
|
||||
'type' => 'ghtagtar',
|
||||
'repo' => 'owner/repo',
|
||||
],
|
||||
'pkg10' => [
|
||||
'type' => 'ghtar',
|
||||
'repo' => 'owner/repo',
|
||||
'path' => 'subdir',
|
||||
],
|
||||
];
|
||||
ConfigValidator::validatePkgs($valid_pkgs);
|
||||
|
||||
// Test invalid data
|
||||
$this->expectException(ValidationException::class);
|
||||
ConfigValidator::validatePkgs(null);
|
||||
}
|
||||
|
||||
public function testValidatePkgsBad(): void
|
||||
{
|
||||
// Test invalid package type
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'invalid']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test non-object package
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => 'not object']);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test filelist type without url
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'filelist', 'regex' => '.*']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test filelist type without regex
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'filelist', 'url' => 'https://example.com']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test git type without url
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'git', 'rev' => 'main']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test git type without rev
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'git', 'url' => 'https://github.com/owner/repo.git']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test ghtagtar type without repo
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'ghtagtar']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test ghtar type without repo
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'ghtar']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test url type without url
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'url']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test url type with non-string url
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'url', 'url' => true]]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test ghrel type without repo
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'ghrel', 'match' => 'pattern']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test ghrel type without match
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'ghrel', 'repo' => 'owner/repo']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test ghrel type with non-string repo
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'ghrel', 'repo' => true, 'match' => 'pattern']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test ghrel type with non-string match
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'ghrel', 'repo' => 'owner/repo', 'match' => 123]]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test git type with non-string path
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'git', 'url' => 'https://github.com/owner/repo.git', 'rev' => 'main', 'path' => 123]]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test url type with non-string filename
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'url', 'url' => 'https://example.com', 'filename' => 123]]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test invalid extract-files (not object)
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'url', 'url' => 'https://example.com', 'extract-files' => 'not object']]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test invalid extract-files mapping (non-string key)
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'url', 'url' => 'https://example.com', 'extract-files' => [123 => 'target']]]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test invalid extract-files mapping (non-string value)
|
||||
try {
|
||||
ConfigValidator::validatePkgs(['pkg1' => ['type' => 'url', 'url' => 'https://example.com', 'extract-files' => ['source' => 123]]]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function testValidatePreBuilt(): void
|
||||
{
|
||||
// Test valid pre-built configurations
|
||||
$valid_prebuilt = [
|
||||
'basic' => [
|
||||
'repo' => 'static-php/static-php-cli-hosted',
|
||||
'match-pattern-linux' => '{name}-{arch}-{os}-{libc}-{libcver}.txz',
|
||||
],
|
||||
'full' => [
|
||||
'repo' => 'static-php/static-php-cli-hosted',
|
||||
'prefer-stable' => true,
|
||||
'match-pattern-linux' => '{name}-{arch}-{os}-{libc}-{libcver}.txz',
|
||||
'match-pattern-macos' => '{name}-{arch}-{os}.txz',
|
||||
'match-pattern-windows' => '{name}-{arch}-{os}.tgz',
|
||||
],
|
||||
'prefer-stable-false' => [
|
||||
'repo' => 'owner/repo',
|
||||
'prefer-stable' => false,
|
||||
'match-pattern-macos' => '{name}-{arch}-{os}.tar.gz',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($valid_prebuilt as $name => $config) {
|
||||
try {
|
||||
ConfigValidator::validatePreBuilt($config);
|
||||
$this->assertTrue(true, "Config {$name} should be valid");
|
||||
} catch (ValidationException $e) {
|
||||
$this->fail("Config {$name} should be valid but got: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testValidatePreBuiltBad(): void
|
||||
{
|
||||
// Test non-array data
|
||||
try {
|
||||
ConfigValidator::validatePreBuilt('invalid');
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test missing repo
|
||||
try {
|
||||
ConfigValidator::validatePreBuilt(['match-pattern-linux' => '{name}-{arch}-{os}-{libc}-{libcver}.txz']);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test invalid repo type
|
||||
try {
|
||||
ConfigValidator::validatePreBuilt(['repo' => 123, 'match-pattern-linux' => '{name}-{arch}-{os}-{libc}-{libcver}.txz']);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test invalid prefer-stable type
|
||||
try {
|
||||
ConfigValidator::validatePreBuilt(['repo' => 'owner/repo', 'prefer-stable' => 'true', 'match-pattern-linux' => '{name}-{arch}-{os}-{libc}-{libcver}.txz']);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test no match patterns
|
||||
try {
|
||||
ConfigValidator::validatePreBuilt(['repo' => 'owner/repo']);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test invalid match pattern type
|
||||
try {
|
||||
ConfigValidator::validatePreBuilt(['repo' => 'owner/repo', 'match-pattern-linux' => 123]);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test missing {name} placeholder
|
||||
try {
|
||||
ConfigValidator::validatePreBuilt(['repo' => 'owner/repo', 'match-pattern-linux' => '{arch}-{os}-{libc}-{libcver}.txz']);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test missing {arch} placeholder
|
||||
try {
|
||||
ConfigValidator::validatePreBuilt(['repo' => 'owner/repo', 'match-pattern-linux' => '{name}-{os}-{libc}-{libcver}.txz']);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test missing {os} placeholder
|
||||
try {
|
||||
ConfigValidator::validatePreBuilt(['repo' => 'owner/repo', 'match-pattern-linux' => '{name}-{arch}-{libc}-{libcver}.txz']);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test linux pattern missing {libc} placeholder
|
||||
try {
|
||||
ConfigValidator::validatePreBuilt(['repo' => 'owner/repo', 'match-pattern-linux' => '{name}-{arch}-{os}-{libcver}.txz']);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
// Test linux pattern missing {libcver} placeholder
|
||||
try {
|
||||
ConfigValidator::validatePreBuilt(['repo' => 'owner/repo', 'match-pattern-linux' => '{name}-{arch}-{os}-{libc}.txz']);
|
||||
$this->fail('should throw ValidationException');
|
||||
} catch (ValidationException) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,15 +14,29 @@ use SPC\util\DependencyUtil;
|
||||
*/
|
||||
final class DependencyUtilTest extends TestCase
|
||||
{
|
||||
public function testGetExtLibsByDeps(): void
|
||||
private array $originalConfig;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
// setup
|
||||
$bak = [
|
||||
// Save original configuration
|
||||
$this->originalConfig = [
|
||||
'source' => Config::$source,
|
||||
'lib' => Config::$lib,
|
||||
'ext' => Config::$ext,
|
||||
];
|
||||
// example
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
// Restore original configuration
|
||||
Config::$source = $this->originalConfig['source'];
|
||||
Config::$lib = $this->originalConfig['lib'];
|
||||
Config::$ext = $this->originalConfig['ext'];
|
||||
}
|
||||
|
||||
public function testGetExtLibsByDeps(): void
|
||||
{
|
||||
// Set up test data
|
||||
Config::$source = [
|
||||
'test1' => [
|
||||
'type' => 'url',
|
||||
@@ -73,14 +87,15 @@ final class DependencyUtilTest extends TestCase
|
||||
'lib-depends' => ['libeee'],
|
||||
],
|
||||
];
|
||||
// test getExtLibsByDeps (notmal test with ext-depends and lib-depends)
|
||||
|
||||
// Test dependency resolution
|
||||
[$exts, $libs, $not_included] = DependencyUtil::getExtsAndLibs(['ext-a'], include_suggested_exts: true);
|
||||
$this->assertContains('libbbb', $libs);
|
||||
$this->assertContains('libccc', $libs);
|
||||
$this->assertContains('ext-b', $exts);
|
||||
$this->assertContains('ext-b', $not_included);
|
||||
// test dep order
|
||||
|
||||
// Test dependency order
|
||||
$this->assertIsInt($b = array_search('libbbb', $libs));
|
||||
$this->assertIsInt($c = array_search('libccc', $libs));
|
||||
$this->assertIsInt($a = array_search('libaaa', $libs));
|
||||
@@ -88,10 +103,6 @@ final class DependencyUtilTest extends TestCase
|
||||
$this->assertTrue($b < $a);
|
||||
$this->assertTrue($c < $a);
|
||||
$this->assertTrue($c < $b);
|
||||
// restore
|
||||
Config::$source = $bak['source'];
|
||||
Config::$lib = $bak['lib'];
|
||||
Config::$ext = $bak['ext'];
|
||||
}
|
||||
|
||||
public function testNotExistExtException(): void
|
||||
|
||||
135
tests/SPC/util/GlobalEnvManagerTest.php
Normal file
135
tests/SPC/util/GlobalEnvManagerTest.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\Tests\util;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\util\GlobalEnvManager;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class GlobalEnvManagerTest extends TestCase
|
||||
{
|
||||
private array $originalEnv;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
// Save original environment variables
|
||||
$this->originalEnv = [
|
||||
'BUILD_ROOT_PATH' => getenv('BUILD_ROOT_PATH'),
|
||||
'SPC_TARGET' => getenv('SPC_TARGET'),
|
||||
'SPC_LIBC' => getenv('SPC_LIBC'),
|
||||
];
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
// Restore original environment variables
|
||||
foreach ($this->originalEnv as $key => $value) {
|
||||
if ($value === false) {
|
||||
putenv($key);
|
||||
} else {
|
||||
putenv("{$key}={$value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetInitializedEnv(): void
|
||||
{
|
||||
// Test that getInitializedEnv returns an array
|
||||
$result = GlobalEnvManager::getInitializedEnv();
|
||||
$this->assertIsArray($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider envVariableProvider
|
||||
*/
|
||||
public function testPutenv(string $envVar): void
|
||||
{
|
||||
// Test putenv functionality
|
||||
GlobalEnvManager::putenv($envVar);
|
||||
|
||||
$env = GlobalEnvManager::getInitializedEnv();
|
||||
$this->assertContains($envVar, $env);
|
||||
$this->assertEquals(explode('=', $envVar, 2)[1], getenv(explode('=', $envVar, 2)[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider pathProvider
|
||||
*/
|
||||
public function testAddPathIfNotExistsOnUnix(string $path): void
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$this->markTestSkipped('This test is for Unix systems only');
|
||||
}
|
||||
|
||||
$originalPath = getenv('PATH');
|
||||
GlobalEnvManager::addPathIfNotExists($path);
|
||||
|
||||
$newPath = getenv('PATH');
|
||||
$this->assertStringContainsString($path, $newPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider pathProvider
|
||||
*/
|
||||
public function testAddPathIfNotExistsWhenPathAlreadyExists(string $path): void
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$this->markTestSkipped('This test is for Unix systems only');
|
||||
}
|
||||
|
||||
GlobalEnvManager::addPathIfNotExists($path);
|
||||
$pathAfterFirstAdd = getenv('PATH');
|
||||
|
||||
GlobalEnvManager::addPathIfNotExists($path);
|
||||
$pathAfterSecondAdd = getenv('PATH');
|
||||
|
||||
// Should not add the same path twice
|
||||
$this->assertEquals($pathAfterFirstAdd, $pathAfterSecondAdd);
|
||||
}
|
||||
|
||||
public function testInitWithoutBuildRootPath(): void
|
||||
{
|
||||
// Temporarily unset BUILD_ROOT_PATH
|
||||
putenv('BUILD_ROOT_PATH');
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
GlobalEnvManager::init();
|
||||
}
|
||||
|
||||
public function testAfterInit(): void
|
||||
{
|
||||
// Set required environment variable
|
||||
putenv('BUILD_ROOT_PATH=/test/path');
|
||||
putenv('SPC_SKIP_TOOLCHAIN_CHECK=true');
|
||||
|
||||
// Should not throw exception when SPC_SKIP_TOOLCHAIN_CHECK is true
|
||||
GlobalEnvManager::afterInit();
|
||||
|
||||
$this->assertTrue(true); // Test passes if no exception is thrown
|
||||
}
|
||||
|
||||
public function envVariableProvider(): array
|
||||
{
|
||||
return [
|
||||
'simple-env' => ['TEST_VAR=test_value'],
|
||||
'complex-env' => ['COMPLEX_VAR=complex_value_with_spaces'],
|
||||
'numeric-env' => ['NUMERIC_VAR=123'],
|
||||
'special-chars-env' => ['SPECIAL_VAR=test@#$%'],
|
||||
];
|
||||
}
|
||||
|
||||
public function pathProvider(): array
|
||||
{
|
||||
return [
|
||||
'simple-path' => ['/test/path'],
|
||||
'complex-path' => ['/usr/local/bin'],
|
||||
'home-path' => ['/home/user/bin'],
|
||||
'root-path' => ['/root/bin'],
|
||||
];
|
||||
}
|
||||
}
|
||||
206
tests/SPC/util/PkgConfigUtilTest.php
Normal file
206
tests/SPC/util/PkgConfigUtilTest.php
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\Tests\util;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\util\PkgConfigUtil;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class PkgConfigUtilTest extends TestCase
|
||||
{
|
||||
private static string $originalPath;
|
||||
|
||||
private static string $fakePkgConfigPath;
|
||||
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
// Save original PATH
|
||||
self::$originalPath = getenv('PATH');
|
||||
|
||||
// Create fake pkg-config directory
|
||||
self::$fakePkgConfigPath = sys_get_temp_dir() . '/fake-pkg-config-' . uniqid();
|
||||
mkdir(self::$fakePkgConfigPath, 0755, true);
|
||||
|
||||
// Create fake pkg-config executable
|
||||
self::createFakePkgConfig();
|
||||
|
||||
// Add fake pkg-config to PATH
|
||||
putenv('PATH=' . self::$fakePkgConfigPath . ':' . self::$originalPath);
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass(): void
|
||||
{
|
||||
// Restore original PATH
|
||||
putenv('PATH=' . self::$originalPath);
|
||||
|
||||
// Clean up fake pkg-config
|
||||
if (is_dir(self::$fakePkgConfigPath)) {
|
||||
self::removeDirectory(self::$fakePkgConfigPath);
|
||||
}
|
||||
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider validPackageProvider
|
||||
*/
|
||||
public function testGetCflagsWithValidPackage(string $package, string $expectedCflags): void
|
||||
{
|
||||
$result = PkgConfigUtil::getCflags($package);
|
||||
$this->assertEquals($expectedCflags, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider validPackageProvider
|
||||
*/
|
||||
public function testGetLibsArrayWithValidPackage(string $package, string $expectedCflags, array $expectedLibs): void
|
||||
{
|
||||
$result = PkgConfigUtil::getLibsArray($package);
|
||||
$this->assertEquals($expectedLibs, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidPackageProvider
|
||||
*/
|
||||
public function testGetCflagsWithInvalidPackage(string $package): void
|
||||
{
|
||||
$this->expectException(RuntimeException::class);
|
||||
PkgConfigUtil::getCflags($package);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidPackageProvider
|
||||
*/
|
||||
public function testGetLibsArrayWithInvalidPackage(string $package): void
|
||||
{
|
||||
$this->expectException(RuntimeException::class);
|
||||
PkgConfigUtil::getLibsArray($package);
|
||||
}
|
||||
|
||||
public static function invalidPackageProvider(): array
|
||||
{
|
||||
return [
|
||||
'invalid-package' => ['invalid-package'],
|
||||
'empty-string' => [''],
|
||||
'non-existent-package' => ['non-existent-package'],
|
||||
];
|
||||
}
|
||||
|
||||
public static function validPackageProvider(): array
|
||||
{
|
||||
return [
|
||||
'libxml2' => ['libxml-2.0', '-I/usr/include/libxml2', ['-lxml2', '']],
|
||||
'zlib' => ['zlib', '-I/usr/include', ['-lz', '']],
|
||||
'openssl' => ['openssl', '-I/usr/include/openssl', ['-lssl', '-lcrypto', '']],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fake pkg-config executable
|
||||
*/
|
||||
private static function createFakePkgConfig(): void
|
||||
{
|
||||
$pkgConfigScript = self::$fakePkgConfigPath . '/pkg-config';
|
||||
|
||||
$script = <<<'SCRIPT'
|
||||
#!/bin/bash
|
||||
|
||||
# Fake pkg-config script for testing
|
||||
# Shift arguments to get the package name
|
||||
shift
|
||||
|
||||
case "$1" in
|
||||
--cflags-only-other)
|
||||
shift
|
||||
case "$1" in
|
||||
libxml-2.0)
|
||||
echo "-I/usr/include/libxml2"
|
||||
;;
|
||||
zlib)
|
||||
echo "-I/usr/include"
|
||||
;;
|
||||
openssl)
|
||||
echo "-I/usr/include/openssl"
|
||||
;;
|
||||
*)
|
||||
echo "Package '$1' was not found in the pkg-config search path." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
--libs-only-l)
|
||||
shift
|
||||
case "$1" in
|
||||
libxml-2.0)
|
||||
echo "-lxml2"
|
||||
;;
|
||||
zlib)
|
||||
echo "-lz"
|
||||
;;
|
||||
openssl)
|
||||
echo "-lssl -lcrypto"
|
||||
;;
|
||||
*)
|
||||
echo "Package '$1' was not found in the pkg-config search path." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
--libs-only-other)
|
||||
shift
|
||||
case "$1" in
|
||||
libxml-2.0)
|
||||
echo ""
|
||||
;;
|
||||
zlib)
|
||||
echo ""
|
||||
;;
|
||||
openssl)
|
||||
echo ""
|
||||
;;
|
||||
*)
|
||||
echo "Package '$1' was not found in the pkg-config search path." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo "Usage: pkg-config [OPTION] [PACKAGE]" >&2
|
||||
echo "Try 'pkg-config --help' for more information." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
SCRIPT;
|
||||
|
||||
file_put_contents($pkgConfigScript, $script);
|
||||
chmod($pkgConfigScript, 0755);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove directory recursively
|
||||
*/
|
||||
private static function removeDirectory(string $dir): void
|
||||
{
|
||||
if (!is_dir($dir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$files = array_diff(scandir($dir), ['.', '..']);
|
||||
foreach ($files as $file) {
|
||||
$path = $dir . '/' . $file;
|
||||
if (is_dir($path)) {
|
||||
self::removeDirectory($path);
|
||||
} else {
|
||||
unlink($path);
|
||||
}
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
140
tests/SPC/util/SPCTargetTest.php
Normal file
140
tests/SPC/util/SPCTargetTest.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\Tests\util;
|
||||
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\util\SPCTarget;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class SPCTargetTest extends TestBase
|
||||
{
|
||||
private array $originalEnv;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
// Save original environment variables
|
||||
$this->originalEnv = [
|
||||
'SPC_TARGET' => getenv('SPC_TARGET'),
|
||||
'SPC_LIBC' => getenv('SPC_LIBC'),
|
||||
];
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
// Restore original environment variables
|
||||
foreach ($this->originalEnv as $key => $value) {
|
||||
if ($value === false) {
|
||||
putenv($key);
|
||||
} else {
|
||||
putenv("{$key}={$value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider libcProvider
|
||||
*/
|
||||
public function testIsStatic(string $libc, bool $expected): void
|
||||
{
|
||||
putenv("SPC_LIBC={$libc}");
|
||||
|
||||
$result = SPCTarget::isStatic();
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider libcProvider
|
||||
*/
|
||||
public function testGetLibc(string $libc, bool $expected): void
|
||||
{
|
||||
putenv("SPC_LIBC={$libc}");
|
||||
|
||||
$result = SPCTarget::getLibc();
|
||||
if ($libc === '') {
|
||||
// When SPC_LIBC is set to empty string, getenv returns empty string, not false
|
||||
$this->assertEquals('', $result);
|
||||
} else {
|
||||
$this->assertEquals($libc, $result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider libcProvider
|
||||
*/
|
||||
public function testGetLibcVersion(string $libc): void
|
||||
{
|
||||
putenv("SPC_LIBC={$libc}");
|
||||
|
||||
$result = SPCTarget::getLibcVersion();
|
||||
// The actual result depends on the system, but it could be null if libc is not available
|
||||
$this->assertIsStringOrNull($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider targetOSProvider
|
||||
*/
|
||||
public function testGetTargetOS(string $target, string $expected): void
|
||||
{
|
||||
putenv("SPC_TARGET={$target}");
|
||||
|
||||
$result = SPCTarget::getTargetOS();
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidTargetProvider
|
||||
*/
|
||||
public function testGetTargetOSWithInvalidTarget(string $target): void
|
||||
{
|
||||
putenv("SPC_TARGET={$target}");
|
||||
|
||||
$this->expectException(WrongUsageException::class);
|
||||
$this->expectExceptionMessage('Cannot parse target.');
|
||||
|
||||
SPCTarget::getTargetOS();
|
||||
}
|
||||
|
||||
public function testLibcListConstant(): void
|
||||
{
|
||||
$this->assertIsArray(SPCTarget::LIBC_LIST);
|
||||
$this->assertContains('musl', SPCTarget::LIBC_LIST);
|
||||
$this->assertContains('glibc', SPCTarget::LIBC_LIST);
|
||||
}
|
||||
|
||||
public function libcProvider(): array
|
||||
{
|
||||
return [
|
||||
'musl' => ['musl', true],
|
||||
'glibc' => ['glibc', false],
|
||||
'empty' => ['', false],
|
||||
];
|
||||
}
|
||||
|
||||
public function targetOSProvider(): array
|
||||
{
|
||||
return [
|
||||
'linux-target' => ['linux-x86_64', 'Linux'],
|
||||
'macos-target' => ['macos-x86_64', 'Darwin'],
|
||||
'windows-target' => ['windows-x86_64', 'Windows'],
|
||||
'empty-target' => ['', PHP_OS_FAMILY],
|
||||
];
|
||||
}
|
||||
|
||||
public function invalidTargetProvider(): array
|
||||
{
|
||||
return [
|
||||
'invalid-target' => ['invalid-target'],
|
||||
'unknown-target' => ['unknown-target'],
|
||||
'mixed-target' => ['mixed-target'],
|
||||
];
|
||||
}
|
||||
|
||||
private function assertIsStringOrNull($value): void
|
||||
{
|
||||
$this->assertTrue(is_string($value) || is_null($value), 'Value must be string or null');
|
||||
}
|
||||
}
|
||||
100
tests/SPC/util/TestBase.php
Normal file
100
tests/SPC/util/TestBase.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\Tests\util;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Base test class for util tests with output suppression
|
||||
*/
|
||||
abstract class TestBase extends TestCase
|
||||
{
|
||||
protected $outputBuffer;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->suppressOutput();
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->restoreOutput();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppress output during tests
|
||||
*/
|
||||
protected function suppressOutput(): void
|
||||
{
|
||||
// Start output buffering to capture PHP output
|
||||
$this->outputBuffer = ob_start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore output after tests
|
||||
*/
|
||||
protected function restoreOutput(): void
|
||||
{
|
||||
// Clean output buffer
|
||||
if ($this->outputBuffer) {
|
||||
ob_end_clean();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a UnixShell instance with debug disabled to suppress logs
|
||||
*/
|
||||
protected function createUnixShell(): \SPC\util\UnixShell
|
||||
{
|
||||
return new \SPC\util\UnixShell(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WindowsCmd instance with debug disabled to suppress logs
|
||||
*/
|
||||
protected function createWindowsCmd(): \SPC\util\WindowsCmd
|
||||
{
|
||||
return new \SPC\util\WindowsCmd(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a test with output suppression
|
||||
*/
|
||||
protected function runWithOutputSuppression(callable $callback)
|
||||
{
|
||||
$this->suppressOutput();
|
||||
try {
|
||||
return $callback();
|
||||
} finally {
|
||||
$this->restoreOutput();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command with output suppression
|
||||
*/
|
||||
protected function execWithSuppression(string $command): array
|
||||
{
|
||||
$this->suppressOutput();
|
||||
try {
|
||||
exec($command, $output, $returnCode);
|
||||
return [$returnCode, $output];
|
||||
} finally {
|
||||
$this->restoreOutput();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command with output redirected to /dev/null
|
||||
*/
|
||||
protected function execSilently(string $command): array
|
||||
{
|
||||
$command .= ' 2>/dev/null 1>/dev/null';
|
||||
exec($command, $output, $returnCode);
|
||||
return [$returnCode, $output];
|
||||
}
|
||||
}
|
||||
184
tests/SPC/util/UnixShellTest.php
Normal file
184
tests/SPC/util/UnixShellTest.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\Tests\util;
|
||||
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\util\UnixShell;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class UnixShellTest extends TestBase
|
||||
{
|
||||
public function testConstructorOnWindows(): void
|
||||
{
|
||||
if (PHP_OS_FAMILY !== 'Windows') {
|
||||
$this->markTestSkipped('This test is for Windows systems only');
|
||||
}
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionMessage('Windows cannot use UnixShell');
|
||||
|
||||
new UnixShell();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider envProvider
|
||||
*/
|
||||
public function testSetEnv(array $env): void
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$this->markTestSkipped('This test is for Unix systems only');
|
||||
}
|
||||
|
||||
$shell = $this->createUnixShell();
|
||||
$result = $shell->setEnv($env);
|
||||
|
||||
$this->assertSame($shell, $result);
|
||||
foreach ($env as $item) {
|
||||
if (trim($item) !== '') {
|
||||
$this->assertStringContainsString($item, $shell->getEnvString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider envProvider
|
||||
*/
|
||||
public function testAppendEnv(array $env): void
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$this->markTestSkipped('This test is for Unix systems only');
|
||||
}
|
||||
|
||||
$shell = $this->createUnixShell();
|
||||
$shell->setEnv(['CFLAGS' => '-O2']);
|
||||
|
||||
$shell->appendEnv($env);
|
||||
|
||||
$this->assertStringContainsString('-O2', $shell->getEnvString());
|
||||
foreach ($env as $value) {
|
||||
if (trim($value) !== '') {
|
||||
$this->assertStringContainsString($value, $shell->getEnvString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider envProvider
|
||||
*/
|
||||
public function testGetEnvString(array $env): void
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$this->markTestSkipped('This test is for Unix systems only');
|
||||
}
|
||||
|
||||
$shell = $this->createUnixShell();
|
||||
$shell->setEnv($env);
|
||||
|
||||
$envString = $shell->getEnvString();
|
||||
|
||||
$hasNonEmptyValues = false;
|
||||
foreach ($env as $key => $value) {
|
||||
if (trim($value) !== '') {
|
||||
$this->assertStringContainsString("{$key}=\"{$value}\"", $envString);
|
||||
$hasNonEmptyValues = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If all values are empty, ensure we still have a test assertion
|
||||
if (!$hasNonEmptyValues) {
|
||||
$this->assertIsString($envString);
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetEnvStringWithEmptyEnv(): void
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$this->markTestSkipped('This test is for Unix systems only');
|
||||
}
|
||||
|
||||
$shell = $this->createUnixShell();
|
||||
$envString = $shell->getEnvString();
|
||||
|
||||
$this->assertEquals('', trim($envString));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider commandProvider
|
||||
*/
|
||||
public function testExecWithResult(string $command): void
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$this->markTestSkipped('This test is for Unix systems only');
|
||||
}
|
||||
|
||||
$shell = $this->createUnixShell();
|
||||
[$code, $output] = $shell->execWithResult($command);
|
||||
|
||||
$this->assertIsInt($code);
|
||||
$this->assertIsArray($output);
|
||||
}
|
||||
|
||||
public function testExecWithResultWithLog(): void
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$this->markTestSkipped('This test is for Unix systems only');
|
||||
}
|
||||
|
||||
$shell = $this->createUnixShell();
|
||||
[$code, $output] = $shell->execWithResult('echo "test"', false);
|
||||
|
||||
$this->assertIsInt($code);
|
||||
$this->assertIsArray($output);
|
||||
$this->assertEquals(0, $code);
|
||||
$this->assertEquals(['test'], $output);
|
||||
}
|
||||
|
||||
public function testExecWithResultWithCd(): void
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$this->markTestSkipped('This test is for Unix systems only');
|
||||
}
|
||||
|
||||
$shell = $this->createUnixShell();
|
||||
$shell->cd('/tmp');
|
||||
|
||||
[$code, $output] = $shell->execWithResult('pwd');
|
||||
|
||||
$this->assertIsInt($code);
|
||||
$this->assertEquals(0, $code);
|
||||
$this->assertIsArray($output);
|
||||
}
|
||||
|
||||
public static function directoryProvider(): array
|
||||
{
|
||||
return [
|
||||
'simple-directory' => ['/test/directory'],
|
||||
'home-directory' => ['/home/user'],
|
||||
'root-directory' => ['/root'],
|
||||
'tmp-directory' => ['/tmp'],
|
||||
];
|
||||
}
|
||||
|
||||
public static function envProvider(): array
|
||||
{
|
||||
return [
|
||||
'simple-env' => [['CFLAGS' => '-O2', 'LDFLAGS' => '-L/usr/lib']],
|
||||
'complex-env' => [['CXXFLAGS' => '-std=c++11', 'LIBS' => '-lz -lxml']],
|
||||
'empty-env' => [['CFLAGS' => '', 'LDFLAGS' => ' ']],
|
||||
'mixed-env' => [['CFLAGS' => '-O2', 'EMPTY_VAR' => '']],
|
||||
];
|
||||
}
|
||||
|
||||
public static function commandProvider(): array
|
||||
{
|
||||
return [
|
||||
'echo-command' => ['echo "test"'],
|
||||
'pwd-command' => ['pwd'],
|
||||
'ls-command' => ['ls -la'],
|
||||
];
|
||||
}
|
||||
}
|
||||
68
tests/SPC/util/WindowsCmdTest.php
Normal file
68
tests/SPC/util/WindowsCmdTest.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\Tests\util;
|
||||
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\util\WindowsCmd;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class WindowsCmdTest extends TestBase
|
||||
{
|
||||
public function testConstructorOnUnix(): void
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$this->markTestSkipped('This test is for Unix systems only');
|
||||
}
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionMessage('Only windows can use WindowsCmd');
|
||||
|
||||
new WindowsCmd();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider commandProvider
|
||||
*/
|
||||
public function testExecWithResult(string $command): void
|
||||
{
|
||||
if (PHP_OS_FAMILY !== 'Windows') {
|
||||
$this->markTestSkipped('This test is for Windows systems only');
|
||||
}
|
||||
|
||||
$cmd = $this->createWindowsCmd();
|
||||
[$code, $output] = $cmd->execWithResult($command);
|
||||
|
||||
$this->assertIsInt($code);
|
||||
$this->assertEquals(0, $code);
|
||||
$this->assertIsArray($output);
|
||||
$this->assertNotEmpty($output);
|
||||
}
|
||||
|
||||
public function testExecWithResultWithLog(): void
|
||||
{
|
||||
if (PHP_OS_FAMILY !== 'Windows') {
|
||||
$this->markTestSkipped('This test is for Windows systems only');
|
||||
}
|
||||
|
||||
$cmd = $this->createWindowsCmd();
|
||||
[$code, $output] = $cmd->execWithResult('echo test', false);
|
||||
|
||||
$this->assertIsInt($code);
|
||||
$this->assertIsArray($output);
|
||||
$this->assertEquals(0, $code);
|
||||
$this->assertEquals(['test'], $output);
|
||||
}
|
||||
|
||||
public static function commandProvider(): array
|
||||
{
|
||||
return [
|
||||
'echo-command' => ['echo test'],
|
||||
'dir-command' => ['dir'],
|
||||
'cd-command' => ['cd'],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user