mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-02 14:25:41 +08:00
292 lines
11 KiB
PHP
292 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\StaticPHP\Command\Dev;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use Psr\Log\LogLevel;
|
|
use StaticPHP\Command\Dev\GenExtTestMatrixCommand;
|
|
use StaticPHP\Config\PackageConfig;
|
|
use Symfony\Component\Console\Application;
|
|
use Symfony\Component\Console\Tester\CommandTester;
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
class GenExtTestMatrixCommandTest extends TestCase
|
|
{
|
|
private Application $app;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
// Reset PackageConfig static state
|
|
$ref = new \ReflectionClass(PackageConfig::class);
|
|
$prop = $ref->getProperty('package_configs');
|
|
$prop->setValue(null, []);
|
|
|
|
// Register fixture packages
|
|
PackageConfig::loadFromArray(self::buildFixture(), 'test');
|
|
|
|
// Set up Symfony Application with the command under test
|
|
$this->app = new Application();
|
|
$this->app->add(new GenExtTestMatrixCommand());
|
|
$this->app->setAutoExit(false);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
parent::tearDown();
|
|
|
|
// Reset PackageConfig static state
|
|
$ref = new \ReflectionClass(PackageConfig::class);
|
|
$prop = $ref->getProperty('package_configs');
|
|
$prop->setValue(null, []);
|
|
|
|
// Restore logger level (BaseCommand::execute() may have changed it)
|
|
logger()->setLevel(LogLevel::ERROR);
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────────────────
|
|
// Tests
|
|
// ──────────────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* swoole entry must contain all swoole-hook-* virtuals and nothing else.
|
|
*/
|
|
public function testSwooleBundlesHookVirtuals(): void
|
|
{
|
|
$matrix = $this->runMatrix(['--os' => 'Linux']);
|
|
|
|
$swooleEntries = $this->findEntriesContaining($matrix, 'swoole');
|
|
$this->assertCount(1, $swooleEntries, 'Expected exactly one entry containing swoole');
|
|
|
|
$parts = explode(',', $swooleEntries[0]['extension']);
|
|
sort($parts);
|
|
|
|
$this->assertContains('swoole', $parts);
|
|
$this->assertContains('swoole-hook-mysql', $parts);
|
|
$this->assertContains('swoole-hook-pgsql', $parts);
|
|
}
|
|
|
|
/**
|
|
* curl must NOT appear in the same entry as swoole, even though swoole depends on it.
|
|
*/
|
|
public function testCurlIsNotPulledIntoSwooleEntry(): void
|
|
{
|
|
$matrix = $this->runMatrix(['--os' => 'Linux']);
|
|
|
|
// The swoole entry must not contain 'curl'
|
|
$swooleEntries = $this->findEntriesContaining($matrix, 'swoole');
|
|
$this->assertCount(1, $swooleEntries);
|
|
$parts = explode(',', $swooleEntries[0]['extension']);
|
|
$this->assertNotContains('curl', $parts, 'curl must not appear inside the swoole matrix entry');
|
|
|
|
// curl must appear in a separate entry
|
|
$curlEntries = $this->findEntriesContaining($matrix, 'curl');
|
|
$this->assertNotEmpty($curlEntries, 'curl must have its own matrix entry');
|
|
}
|
|
|
|
/**
|
|
* swow must be fully isolated — its entry should only contain 'swow'.
|
|
*/
|
|
public function testSwowIsIsolated(): void
|
|
{
|
|
$matrix = $this->runMatrix(['--os' => 'Linux']);
|
|
|
|
$swowEntries = $this->findEntriesContaining($matrix, 'swow');
|
|
$this->assertCount(1, $swowEntries, 'Expected exactly one entry containing swow');
|
|
$this->assertSame('swow', $swowEntries[0]['extension'], 'swow entry must contain only swow');
|
|
}
|
|
|
|
/**
|
|
* dom and xml must appear in the same matrix entry (DFS chain).
|
|
*/
|
|
public function testDomXmlChain(): void
|
|
{
|
|
$matrix = $this->runMatrix(['--os' => 'Linux']);
|
|
|
|
$chainEntries = $this->findEntriesContaining($matrix, 'dom', 'xml');
|
|
$this->assertNotEmpty($chainEntries, 'dom and xml must appear in the same matrix entry');
|
|
}
|
|
|
|
/**
|
|
* --os=Windows must exclude ext-linux-only.
|
|
*/
|
|
public function testOsFilterExcludesLinuxOnlyFromWindows(): void
|
|
{
|
|
$matrix = $this->runMatrix(['--os' => 'Windows']);
|
|
|
|
$linuxOnlyEntries = $this->findEntriesContaining($matrix, 'linux-only');
|
|
$this->assertEmpty($linuxOnlyEntries, 'ext-linux-only must not appear in the Windows matrix');
|
|
}
|
|
|
|
/**
|
|
* --os=Linux must include ext-linux-only.
|
|
*/
|
|
public function testOsFilterIncludesLinuxOnly(): void
|
|
{
|
|
$matrix = $this->runMatrix(['--os' => 'Linux']);
|
|
|
|
$linuxOnlyEntries = $this->findEntriesContaining($matrix, 'linux-only');
|
|
$this->assertNotEmpty($linuxOnlyEntries, 'ext-linux-only must appear in the Linux matrix');
|
|
}
|
|
|
|
/**
|
|
* All returned entries must reference the requested OS runner when --os is specified.
|
|
*/
|
|
public function testOsFilterRestrictsRunners(): void
|
|
{
|
|
$matrix = $this->runMatrix(['--os' => 'Linux']);
|
|
|
|
foreach ($matrix as $entry) {
|
|
$this->assertSame('linux', $entry['os'], "Entry {$entry['extension']} must only target Linux");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* --for-extensions=redis must return only entries that contain 'redis'.
|
|
*/
|
|
public function testForExtensionsFilter(): void
|
|
{
|
|
$matrix = $this->runMatrix(['--os' => 'Linux', '--for-extensions' => 'redis']);
|
|
|
|
$this->assertNotEmpty($matrix, '--for-extensions=redis must yield at least one entry');
|
|
foreach ($matrix as $entry) {
|
|
$parts = explode(',', $entry['extension']);
|
|
$this->assertContains('redis', $parts, "Entry {$entry['extension']} does not contain redis");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* --for-libs=libxml2 must return only entries whose extension(s) depend on libxml2.
|
|
*/
|
|
public function testForLibsFilter(): void
|
|
{
|
|
$matrix = $this->runMatrix(['--os' => 'Linux', '--for-libs' => 'libxml2']);
|
|
|
|
$this->assertNotEmpty($matrix, '--for-libs=libxml2 must yield at least one entry');
|
|
foreach ($matrix as $entry) {
|
|
$parts = explode(',', $entry['extension']);
|
|
// xml depends on libxml2 directly; dom depends on xml (which depends on libxml2)
|
|
$match = count(array_intersect($parts, ['xml', 'dom'])) > 0;
|
|
$this->assertTrue($match, "Entry {$entry['extension']} should not appear in --for-libs=libxml2 results");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* --tier2 must produce only Tier2 runners and no Windows entries.
|
|
*/
|
|
public function testTier2Flag(): void
|
|
{
|
|
$matrix = $this->runMatrix(['--tier2' => true]);
|
|
|
|
$this->assertNotEmpty($matrix);
|
|
foreach ($matrix as $entry) {
|
|
$this->assertNotSame('windows', $entry['os'], '--tier2 must not include Windows entries');
|
|
$this->assertContains(
|
|
$entry['runner'],
|
|
['ubuntu-24.04-arm', 'macos-15-intel'],
|
|
"Runner {$entry['runner']} is not a valid Tier2 runner"
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Each entry must have the mandatory keys and correct types.
|
|
*/
|
|
public function testEntryShape(): void
|
|
{
|
|
$matrix = $this->runMatrix(['--os' => 'Linux']);
|
|
|
|
$this->assertNotEmpty($matrix);
|
|
foreach ($matrix as $entry) {
|
|
$this->assertArrayHasKey('runner', $entry);
|
|
$this->assertArrayHasKey('os', $entry);
|
|
$this->assertArrayHasKey('arch', $entry);
|
|
$this->assertArrayHasKey('extension', $entry);
|
|
$this->assertArrayHasKey('build-args', $entry);
|
|
$this->assertIsString($entry['extension']);
|
|
$this->assertIsString($entry['build-args']);
|
|
$this->assertStringContainsString($entry['extension'], $entry['build-args']);
|
|
}
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────────────────
|
|
// Helpers
|
|
// ──────────────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Run the command with the given options and return the parsed JSON matrix.
|
|
*/
|
|
private function runMatrix(array $options = []): array
|
|
{
|
|
$tester = new CommandTester($this->app->find('dev:gen-ext-test-matrix'));
|
|
$tester->execute($options, ['decorated' => false]);
|
|
$output = $tester->getDisplay();
|
|
$matrix = json_decode($output, true);
|
|
$this->assertIsArray($matrix, "Command output is not valid JSON. Output:\n{$output}");
|
|
return $matrix;
|
|
}
|
|
|
|
/**
|
|
* Find matrix entries whose 'extension' field contains all of the given names.
|
|
*
|
|
* @return array[] matching entries
|
|
*/
|
|
private function findEntriesContaining(array $matrix, string ...$names): array
|
|
{
|
|
return array_values(array_filter($matrix, static function (array $entry) use ($names): bool {
|
|
$parts = explode(',', $entry['extension']);
|
|
foreach ($names as $name) {
|
|
if (!in_array($name, $parts, true)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Minimal valid php-extension fixture.
|
|
*
|
|
* Layout:
|
|
* - ext-swow standalone isolated, no ext deps
|
|
* - ext-swoole standalone isolated, depends on ext-curl
|
|
* - ext-swoole-hook-* virtual (arg-type: none) — must be bundled with swoole
|
|
* - ext-curl simple orphan, depended on by swoole but must NOT be pulled into swoole entry
|
|
* - ext-redis simple orphan
|
|
* - ext-xml depends on lib 'libxml2'
|
|
* - ext-dom depends on ext-xml (DFS chain)
|
|
* - ext-linux-only restricted to Linux via os: [Linux]
|
|
*/
|
|
private static function buildFixture(): array
|
|
{
|
|
// php-extension must be a non-empty assoc array ([] fails is_assoc_array() check).
|
|
$ext = static fn (array $phpExt = ['arg-type' => 'standard'], array $topLevel = []): array => array_merge(['type' => 'php-extension', 'php-extension' => $phpExt], $topLevel);
|
|
|
|
return [
|
|
// Isolated standalones
|
|
'ext-swow' => $ext(),
|
|
'ext-swoole' => $ext(['arg-type' => 'standard'], ['depends' => ['ext-curl']]),
|
|
|
|
// Swoole hook virtuals (arg-type: none → virtual)
|
|
'ext-swoole-hook-mysql' => $ext(['arg-type' => 'none']),
|
|
'ext-swoole-hook-pgsql' => $ext(['arg-type' => 'none']),
|
|
|
|
// Simple orphans
|
|
'ext-curl' => $ext(),
|
|
'ext-redis' => $ext(),
|
|
|
|
// DFS chain: dom depends on xml; xml depends on lib 'libxml2'
|
|
'ext-xml' => $ext(['arg-type' => 'standard'], ['depends' => ['libxml2']]),
|
|
'ext-dom' => $ext(['arg-type' => 'standard'], ['depends' => ['ext-xml']]),
|
|
|
|
// OS-restricted to Linux only
|
|
'ext-linux-only' => $ext(['os' => ['Linux']]),
|
|
];
|
|
}
|
|
}
|