mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-04 07:15:38 +08:00
Add v3 artifact test
This commit is contained in:
548
tests/StaticPHP/Artifact/ArtifactCacheTest.php
Normal file
548
tests/StaticPHP/Artifact/ArtifactCacheTest.php
Normal file
@@ -0,0 +1,548 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\StaticPHP\Artifact;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use StaticPHP\Artifact\Artifact;
|
||||
use StaticPHP\Artifact\ArtifactCache;
|
||||
use StaticPHP\Artifact\Downloader\DownloadResult;
|
||||
use StaticPHP\Exception\SPCInternalException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ArtifactCacheTest extends TestCase
|
||||
{
|
||||
private string $tempDir;
|
||||
|
||||
private string $cacheFile;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->tempDir = sys_get_temp_dir() . '/artifact_cache_test_' . uniqid();
|
||||
mkdir($this->tempDir, 0755, true);
|
||||
$this->cacheFile = $this->tempDir . '/.cache.json';
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
if (is_dir($this->tempDir)) {
|
||||
$this->removeDirectory($this->tempDir);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Constructor ====================
|
||||
|
||||
public function testConstructorCreatesFileWhenNotExists(): void
|
||||
{
|
||||
$this->assertFalse(file_exists($this->cacheFile));
|
||||
|
||||
new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertTrue(file_exists($this->cacheFile));
|
||||
$this->assertSame('[]', file_get_contents($this->cacheFile));
|
||||
}
|
||||
|
||||
public function testConstructorReadsExistingCacheFile(): void
|
||||
{
|
||||
$existing = ['openssl' => ['source' => null, 'binary' => []]];
|
||||
file_put_contents($this->cacheFile, json_encode($existing));
|
||||
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertSame([], $cache->getAllBinaryInfo('openssl'));
|
||||
}
|
||||
|
||||
public function testConstructorHandlesEmptyExistingFile(): void
|
||||
{
|
||||
file_put_contents($this->cacheFile, '');
|
||||
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertEmpty($cache->getCachedArtifactNames());
|
||||
}
|
||||
|
||||
// ==================== isSourceDownloaded ====================
|
||||
|
||||
public function testIsSourceDownloadedReturnsFalseWhenNotCached(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertFalse($cache->isSourceDownloaded('non-existent'));
|
||||
}
|
||||
|
||||
public function testIsSourceDownloadedReturnsFalseWhenCacheEntryHasNullSource(): void
|
||||
{
|
||||
file_put_contents($this->cacheFile, json_encode([
|
||||
'my-pkg' => ['source' => null, 'binary' => []],
|
||||
]));
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertFalse($cache->isSourceDownloaded('my-pkg'));
|
||||
}
|
||||
|
||||
public function testIsSourceDownloadedReturnsTrueForLocalTypeWhenDirExists(): void
|
||||
{
|
||||
$localDir = $this->tempDir . '/local-source';
|
||||
mkdir($localDir, 0755, true);
|
||||
|
||||
$this->writeCacheData([
|
||||
'my-pkg' => [
|
||||
'source' => [
|
||||
'lock_type' => 'source',
|
||||
'cache_type' => 'local',
|
||||
'dirname' => $localDir,
|
||||
'extract' => null,
|
||||
'hash' => null,
|
||||
'time' => time(),
|
||||
],
|
||||
'binary' => [],
|
||||
],
|
||||
]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertTrue($cache->isSourceDownloaded('my-pkg'));
|
||||
}
|
||||
|
||||
public function testIsSourceDownloadedReturnsFalseForLocalTypeWhenDirNotExists(): void
|
||||
{
|
||||
$this->writeCacheData([
|
||||
'my-pkg' => [
|
||||
'source' => [
|
||||
'lock_type' => 'source',
|
||||
'cache_type' => 'local',
|
||||
'dirname' => '/non/existent/path/xyz',
|
||||
'extract' => null,
|
||||
'hash' => null,
|
||||
'time' => time(),
|
||||
],
|
||||
'binary' => [],
|
||||
],
|
||||
]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertFalse($cache->isSourceDownloaded('my-pkg'));
|
||||
}
|
||||
|
||||
public function testIsSourceDownloadedReturnsFalseForArchiveTypeWhenFileNotExists(): void
|
||||
{
|
||||
$this->writeCacheData([
|
||||
'my-pkg' => [
|
||||
'source' => [
|
||||
'lock_type' => 'source',
|
||||
'cache_type' => 'archive',
|
||||
'filename' => 'non-existent-file.tar.gz',
|
||||
'extract' => null,
|
||||
'hash' => 'abc123',
|
||||
'time' => time(),
|
||||
],
|
||||
'binary' => [],
|
||||
],
|
||||
]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertFalse($cache->isSourceDownloaded('my-pkg'));
|
||||
}
|
||||
|
||||
// ==================== isBinaryDownloaded ====================
|
||||
|
||||
public function testIsBinaryDownloadedReturnsFalseWhenNotCached(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertFalse($cache->isBinaryDownloaded('non-existent', 'linux-x86_64'));
|
||||
}
|
||||
|
||||
public function testIsBinaryDownloadedReturnsFalseWhenPlatformNotCached(): void
|
||||
{
|
||||
$this->writeCacheData([
|
||||
'my-pkg' => ['source' => null, 'binary' => []],
|
||||
]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertFalse($cache->isBinaryDownloaded('my-pkg', 'linux-x86_64'));
|
||||
}
|
||||
|
||||
public function testIsBinaryDownloadedReturnsTrueForLocalTypeWhenDirExists(): void
|
||||
{
|
||||
$localDir = $this->tempDir . '/local-binary';
|
||||
mkdir($localDir, 0755, true);
|
||||
|
||||
$this->writeCacheData([
|
||||
'my-pkg' => [
|
||||
'source' => null,
|
||||
'binary' => [
|
||||
'linux-x86_64' => [
|
||||
'lock_type' => 'binary',
|
||||
'cache_type' => 'local',
|
||||
'dirname' => $localDir,
|
||||
'extract' => null,
|
||||
'hash' => null,
|
||||
'time' => time(),
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertTrue($cache->isBinaryDownloaded('my-pkg', 'linux-x86_64'));
|
||||
}
|
||||
|
||||
// ==================== lock ====================
|
||||
|
||||
public function testLockWithLocalSourceType(): void
|
||||
{
|
||||
$localDir = $this->tempDir . '/local-pkg';
|
||||
mkdir($localDir, 0755, true);
|
||||
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
$downloadResult = DownloadResult::local($localDir, [], null, '1.0.0');
|
||||
|
||||
$cache->lock('my-pkg', 'source', $downloadResult);
|
||||
|
||||
$info = $cache->getSourceInfo('my-pkg');
|
||||
$this->assertNotNull($info);
|
||||
$this->assertSame('source', $info['lock_type']);
|
||||
$this->assertSame('local', $info['cache_type']);
|
||||
$this->assertSame($localDir, $info['dirname']);
|
||||
}
|
||||
|
||||
public function testLockWithLocalBinaryTypePersistsCorrectPlatform(): void
|
||||
{
|
||||
$localDir = $this->tempDir . '/local-bin';
|
||||
mkdir($localDir, 0755, true);
|
||||
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
$downloadResult = DownloadResult::local($localDir, [], null, '1.0.0');
|
||||
|
||||
$cache->lock('my-pkg', 'binary', $downloadResult, 'linux-x86_64');
|
||||
|
||||
$info = $cache->getBinaryInfo('my-pkg', 'linux-x86_64');
|
||||
$this->assertNotNull($info);
|
||||
$this->assertSame('binary', $info['lock_type']);
|
||||
$this->assertSame('linux-x86_64', $info['platform']);
|
||||
}
|
||||
|
||||
public function testLockWithBinaryTypeThrowsWhenPlatformIsNull(): void
|
||||
{
|
||||
$localDir = $this->tempDir . '/local-bin2';
|
||||
mkdir($localDir, 0755, true);
|
||||
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
$downloadResult = DownloadResult::local($localDir, [], null);
|
||||
|
||||
$this->expectException(SPCInternalException::class);
|
||||
$cache->lock('my-pkg', 'binary', $downloadResult, null);
|
||||
}
|
||||
|
||||
public function testLockPersistsCacheToFile(): void
|
||||
{
|
||||
$localDir = $this->tempDir . '/persist-test';
|
||||
mkdir($localDir, 0755, true);
|
||||
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
$downloadResult = DownloadResult::local($localDir, []);
|
||||
|
||||
$cache->lock('my-pkg', 'source', $downloadResult);
|
||||
|
||||
// Read file contents to verify persisted
|
||||
$persisted = json_decode(file_get_contents($this->cacheFile), true);
|
||||
$this->assertArrayHasKey('my-pkg', $persisted);
|
||||
$this->assertNotNull($persisted['my-pkg']['source']);
|
||||
}
|
||||
|
||||
// ==================== getSourceInfo ====================
|
||||
|
||||
public function testGetSourceInfoReturnsNullWhenNotCached(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertNull($cache->getSourceInfo('non-existent'));
|
||||
}
|
||||
|
||||
public function testGetSourceInfoReturnsNullWhenSourceIsNull(): void
|
||||
{
|
||||
$this->writeCacheData(['my-pkg' => ['source' => null, 'binary' => []]]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertNull($cache->getSourceInfo('my-pkg'));
|
||||
}
|
||||
|
||||
public function testGetSourceInfoReturnsData(): void
|
||||
{
|
||||
$this->writeCacheData([
|
||||
'my-pkg' => [
|
||||
'source' => ['lock_type' => 'source', 'cache_type' => 'local', 'dirname' => '/tmp', 'extract' => null, 'hash' => null, 'time' => 0],
|
||||
'binary' => [],
|
||||
],
|
||||
]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$info = $cache->getSourceInfo('my-pkg');
|
||||
$this->assertIsArray($info);
|
||||
$this->assertSame('local', $info['cache_type']);
|
||||
}
|
||||
|
||||
// ==================== getBinaryInfo ====================
|
||||
|
||||
public function testGetBinaryInfoReturnsNullWhenNotCached(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertNull($cache->getBinaryInfo('non-existent', 'linux-x86_64'));
|
||||
}
|
||||
|
||||
public function testGetBinaryInfoReturnsDataForPlatform(): void
|
||||
{
|
||||
$this->writeCacheData([
|
||||
'my-pkg' => [
|
||||
'source' => null,
|
||||
'binary' => [
|
||||
'linux-x86_64' => ['lock_type' => 'binary', 'cache_type' => 'local', 'dirname' => '/tmp', 'extract' => null, 'hash' => null, 'time' => 0],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$info = $cache->getBinaryInfo('my-pkg', 'linux-x86_64');
|
||||
$this->assertIsArray($info);
|
||||
$this->assertSame('local', $info['cache_type']);
|
||||
}
|
||||
|
||||
public function testGetBinaryInfoReturnsNullForDifferentPlatform(): void
|
||||
{
|
||||
$this->writeCacheData([
|
||||
'my-pkg' => [
|
||||
'source' => null,
|
||||
'binary' => [
|
||||
'linux-x86_64' => ['lock_type' => 'binary', 'cache_type' => 'local', 'dirname' => '/tmp', 'extract' => null, 'hash' => null, 'time' => 0],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertNull($cache->getBinaryInfo('my-pkg', 'macos-aarch64'));
|
||||
}
|
||||
|
||||
// ==================== getAllBinaryInfo ====================
|
||||
|
||||
public function testGetAllBinaryInfoReturnsEmptyWhenNone(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertSame([], $cache->getAllBinaryInfo('non-existent'));
|
||||
}
|
||||
|
||||
public function testGetAllBinaryInfoReturnsAllPlatforms(): void
|
||||
{
|
||||
$this->writeCacheData([
|
||||
'my-pkg' => [
|
||||
'source' => null,
|
||||
'binary' => [
|
||||
'linux-x86_64' => ['lock_type' => 'binary', 'cache_type' => 'local', 'dirname' => '/tmp', 'extract' => null, 'hash' => null, 'time' => 0],
|
||||
'macos-aarch64' => ['lock_type' => 'binary', 'cache_type' => 'local', 'dirname' => '/tmp', 'extract' => null, 'hash' => null, 'time' => 0],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$all = $cache->getAllBinaryInfo('my-pkg');
|
||||
$this->assertCount(2, $all);
|
||||
$this->assertArrayHasKey('linux-x86_64', $all);
|
||||
$this->assertArrayHasKey('macos-aarch64', $all);
|
||||
}
|
||||
|
||||
// ==================== getCacheFullPath ====================
|
||||
|
||||
public function testGetCacheFullPathForArchiveType(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
$info = ['cache_type' => 'archive', 'filename' => 'openssl-3.0.tar.gz'];
|
||||
|
||||
$expected = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . 'openssl-3.0.tar.gz';
|
||||
$this->assertSame($expected, $cache->getCacheFullPath($info));
|
||||
}
|
||||
|
||||
public function testGetCacheFullPathForGitType(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
$info = ['cache_type' => 'git', 'dirname' => 'my-repo'];
|
||||
|
||||
$expected = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . 'my-repo';
|
||||
$this->assertSame($expected, $cache->getCacheFullPath($info));
|
||||
}
|
||||
|
||||
public function testGetCacheFullPathForLocalType(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
$info = ['cache_type' => 'local', 'dirname' => '/absolute/path/to/dir'];
|
||||
|
||||
$this->assertSame('/absolute/path/to/dir', $cache->getCacheFullPath($info));
|
||||
}
|
||||
|
||||
public function testGetCacheFullPathForFileType(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
$info = ['cache_type' => 'file', 'filename' => 'some-tool.exe'];
|
||||
|
||||
$expected = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . 'some-tool.exe';
|
||||
$this->assertSame($expected, $cache->getCacheFullPath($info));
|
||||
}
|
||||
|
||||
public function testGetCacheFullPathThrowsForUnknownType(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
$info = ['cache_type' => 'unknown-type'];
|
||||
|
||||
$this->expectException(SPCInternalException::class);
|
||||
$cache->getCacheFullPath($info);
|
||||
}
|
||||
|
||||
// ==================== removeSource ====================
|
||||
|
||||
public function testRemoveSourceIsNoOpWhenNotCached(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
// Should not throw
|
||||
$cache->removeSource('non-existent');
|
||||
$this->assertNull($cache->getSourceInfo('non-existent'));
|
||||
}
|
||||
|
||||
public function testRemoveSourceRemovesCacheEntry(): void
|
||||
{
|
||||
$this->writeCacheData([
|
||||
'my-pkg' => [
|
||||
'source' => ['lock_type' => 'source', 'cache_type' => 'local', 'dirname' => '/tmp', 'extract' => null, 'hash' => null, 'time' => 0],
|
||||
'binary' => [],
|
||||
],
|
||||
]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$cache->removeSource('my-pkg');
|
||||
|
||||
$this->assertNull($cache->getSourceInfo('my-pkg'));
|
||||
}
|
||||
|
||||
public function testRemoveSourcePersistsToFile(): void
|
||||
{
|
||||
$this->writeCacheData([
|
||||
'my-pkg' => [
|
||||
'source' => ['lock_type' => 'source', 'cache_type' => 'local', 'dirname' => '/tmp', 'extract' => null, 'hash' => null, 'time' => 0],
|
||||
'binary' => [],
|
||||
],
|
||||
]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$cache->removeSource('my-pkg');
|
||||
|
||||
$persisted = json_decode(file_get_contents($this->cacheFile), true);
|
||||
$this->assertNull($persisted['my-pkg']['source']);
|
||||
}
|
||||
|
||||
// ==================== removeBinary ====================
|
||||
|
||||
public function testRemoveBinaryIsNoOpWhenNotCached(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
// Should not throw
|
||||
$cache->removeBinary('non-existent', 'linux-x86_64');
|
||||
$this->assertNull($cache->getBinaryInfo('non-existent', 'linux-x86_64'));
|
||||
}
|
||||
|
||||
public function testRemoveBinaryRemovesPlatformEntry(): void
|
||||
{
|
||||
$this->writeCacheData([
|
||||
'my-pkg' => [
|
||||
'source' => null,
|
||||
'binary' => [
|
||||
'linux-x86_64' => ['lock_type' => 'binary', 'cache_type' => 'local', 'dirname' => '/tmp', 'extract' => null, 'hash' => null, 'time' => 0],
|
||||
'macos-aarch64' => ['lock_type' => 'binary', 'cache_type' => 'local', 'dirname' => '/tmp', 'extract' => null, 'hash' => null, 'time' => 0],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$cache->removeBinary('my-pkg', 'linux-x86_64');
|
||||
|
||||
$this->assertNull($cache->getBinaryInfo('my-pkg', 'linux-x86_64'));
|
||||
// Other platform should still be there
|
||||
$this->assertNotNull($cache->getBinaryInfo('my-pkg', 'macos-aarch64'));
|
||||
}
|
||||
|
||||
// ==================== getCachedArtifactNames ====================
|
||||
|
||||
public function testGetCachedArtifactNamesReturnsEmptyWhenNoCacheFile(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$this->assertSame([], $cache->getCachedArtifactNames());
|
||||
}
|
||||
|
||||
public function testGetCachedArtifactNamesReturnsAllNames(): void
|
||||
{
|
||||
$this->writeCacheData([
|
||||
'openssl' => ['source' => null, 'binary' => []],
|
||||
'zlib' => ['source' => null, 'binary' => []],
|
||||
'brotli' => ['source' => null, 'binary' => []],
|
||||
]);
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$names = $cache->getCachedArtifactNames();
|
||||
$this->assertCount(3, $names);
|
||||
$this->assertContains('openssl', $names);
|
||||
$this->assertContains('zlib', $names);
|
||||
$this->assertContains('brotli', $names);
|
||||
}
|
||||
|
||||
// ==================== save ====================
|
||||
|
||||
public function testSavePersistsInMemoryCacheToFile(): void
|
||||
{
|
||||
$localDir = $this->tempDir . '/save-test-dir';
|
||||
mkdir($localDir, 0755, true);
|
||||
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
// Lock an artifact so cache has data
|
||||
$downloadResult = DownloadResult::local($localDir, []);
|
||||
$cache->lock('my-pkg', 'source', $downloadResult);
|
||||
|
||||
// Overwrite cache file to simulate external change
|
||||
file_put_contents($this->cacheFile, json_encode([]));
|
||||
|
||||
// Save should re-write in-memory state
|
||||
$cache->save();
|
||||
|
||||
$persisted = json_decode(file_get_contents($this->cacheFile), true);
|
||||
$this->assertArrayHasKey('my-pkg', $persisted);
|
||||
}
|
||||
|
||||
// ==================== Helpers ====================
|
||||
|
||||
private function writeCacheData(array $data): void
|
||||
{
|
||||
file_put_contents($this->cacheFile, json_encode($data));
|
||||
}
|
||||
|
||||
private 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)) {
|
||||
$this->removeDirectory($path);
|
||||
} else {
|
||||
unlink($path);
|
||||
}
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
351
tests/StaticPHP/Artifact/ArtifactDownloaderTest.php
Normal file
351
tests/StaticPHP/Artifact/ArtifactDownloaderTest.php
Normal file
@@ -0,0 +1,351 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\StaticPHP\Artifact;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use StaticPHP\Artifact\Artifact;
|
||||
use StaticPHP\Artifact\ArtifactDownloader;
|
||||
use StaticPHP\Config\ArtifactConfig;
|
||||
use StaticPHP\Exception\WrongUsageException;
|
||||
use StaticPHP\Registry\ArtifactLoader;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ArtifactDownloaderTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Reset ArtifactConfig and ArtifactLoader static state
|
||||
$reflection = new \ReflectionClass(ArtifactConfig::class);
|
||||
$property = $reflection->getProperty('artifact_configs');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue(null, []);
|
||||
|
||||
$loaderReflection = new \ReflectionClass(ArtifactLoader::class);
|
||||
$loaderProperty = $loaderReflection->getProperty('artifacts');
|
||||
$loaderProperty->setAccessible(true);
|
||||
$loaderProperty->setValue(null, null);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
$reflection = new \ReflectionClass(ArtifactConfig::class);
|
||||
$property = $reflection->getProperty('artifact_configs');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue(null, []);
|
||||
|
||||
$loaderReflection = new \ReflectionClass(ArtifactLoader::class);
|
||||
$loaderProperty = $loaderReflection->getProperty('artifacts');
|
||||
$loaderProperty->setAccessible(true);
|
||||
$loaderProperty->setValue(null, null);
|
||||
}
|
||||
|
||||
// ==================== DOWNLOADERS constant ====================
|
||||
|
||||
public function testDownloadersConstantHasExpectedKeys(): void
|
||||
{
|
||||
$expectedKeys = ['bitbuckettag', 'filelist', 'git', 'ghrel', 'ghtar', 'ghtagtar', 'local', 'pie', 'pecl', 'url', 'php-release', 'hosted'];
|
||||
|
||||
foreach ($expectedKeys as $key) {
|
||||
$this->assertArrayHasKey($key, ArtifactDownloader::DOWNLOADERS, "Missing downloader key: {$key}");
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Constructor options ====================
|
||||
|
||||
public function testConstructWithDefaultOptions(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
|
||||
$this->assertSame(0, $downloader->getRetry());
|
||||
$this->assertFalse($downloader->interactive);
|
||||
$this->assertEmpty($downloader->getArtifacts());
|
||||
}
|
||||
|
||||
public function testConstructWithParallelOption(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(['parallel' => 4], false);
|
||||
|
||||
// parallel is internal but setParallel/getArtifacts reveals behavior; check via setParallel chainability
|
||||
// Indirect verification: setParallel with same value returns $this
|
||||
$this->assertSame($downloader, $downloader->setParallel(4));
|
||||
}
|
||||
|
||||
public function testConstructWithRetryOption(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(['retry' => 3], false);
|
||||
|
||||
$this->assertSame(3, $downloader->getRetry());
|
||||
}
|
||||
|
||||
public function testConstructWithNegativeRetryClampedToZero(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(['retry' => -5], false);
|
||||
|
||||
$this->assertSame(0, $downloader->getRetry());
|
||||
}
|
||||
|
||||
public function testConstructWithPreferSourceBoolOption(): void
|
||||
{
|
||||
// prefer-source=true sets default to FETCH_PREFER_SOURCE (0)
|
||||
$downloader = new ArtifactDownloader(['prefer-source' => true], false);
|
||||
|
||||
$this->assertSame(0, $downloader->getRetry()); // sanity check, object created fine
|
||||
}
|
||||
|
||||
public function testConstructWithPreferBinaryBoolOption(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(['prefer-binary' => true], false);
|
||||
|
||||
$this->assertNotNull($downloader);
|
||||
}
|
||||
|
||||
public function testConstructWithPreferPreBuiltBoolOption(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(['prefer-pre-built' => true], false);
|
||||
|
||||
$this->assertNotNull($downloader);
|
||||
}
|
||||
|
||||
public function testConstructWithSourceOnlyBoolOption(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(['source-only' => true], false);
|
||||
|
||||
$this->assertNotNull($downloader);
|
||||
}
|
||||
|
||||
public function testConstructWithBinaryOnlyBoolOption(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(['binary-only' => true], false);
|
||||
|
||||
$this->assertNotNull($downloader);
|
||||
}
|
||||
|
||||
public function testConstructWithIgnoreCacheBoolOption(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(['ignore-cache' => true], false);
|
||||
|
||||
$this->assertNotNull($downloader);
|
||||
}
|
||||
|
||||
public function testConstructWithIgnoreCacheStringOptionParsesNames(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(['ignore-cache' => 'openssl,zlib'], false);
|
||||
|
||||
$this->assertNotNull($downloader);
|
||||
}
|
||||
|
||||
public function testConstructWithIgnoreCacheSourcesBackwardCompat(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(['ignore-cache-sources' => true], false);
|
||||
|
||||
$this->assertNotNull($downloader);
|
||||
}
|
||||
|
||||
public function testConstructWithCustomUrlOptionAddsToIgnoreCache(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(
|
||||
['custom-url' => ['openssl:https://custom.example.com/openssl.tar.gz']],
|
||||
false
|
||||
);
|
||||
|
||||
$this->assertNotNull($downloader);
|
||||
}
|
||||
|
||||
public function testConstructWithCustomGitOption(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(
|
||||
['custom-git' => ['php-src:master:https://github.com/php/php-src.git']],
|
||||
false
|
||||
);
|
||||
|
||||
$this->assertNotNull($downloader);
|
||||
}
|
||||
|
||||
public function testConstructWithCustomLocalOption(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(
|
||||
['custom-local' => ['my-lib:/tmp/my-lib-source']],
|
||||
false
|
||||
);
|
||||
|
||||
$this->assertNotNull($downloader);
|
||||
}
|
||||
|
||||
public function testConstructWithNoAltOption(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(['no-alt' => true], false);
|
||||
|
||||
$this->assertNotNull($downloader);
|
||||
}
|
||||
|
||||
// ==================== getRetry ====================
|
||||
|
||||
public function testGetRetryDefaultsToZero(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
|
||||
$this->assertSame(0, $downloader->getRetry());
|
||||
}
|
||||
|
||||
public function testGetRetryReturnsConfiguredValue(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(['retry' => 5], false);
|
||||
|
||||
$this->assertSame(5, $downloader->getRetry());
|
||||
}
|
||||
|
||||
// ==================== getOption ====================
|
||||
|
||||
public function testGetOptionReturnsConfiguredValue(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader(['retry' => 2, 'parallel' => 3], false);
|
||||
|
||||
$this->assertSame(2, $downloader->getOption('retry'));
|
||||
$this->assertSame(3, $downloader->getOption('parallel'));
|
||||
}
|
||||
|
||||
public function testGetOptionReturnsDefaultWhenNotSet(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
|
||||
$this->assertNull($downloader->getOption('non-existent'));
|
||||
$this->assertSame('default-val', $downloader->getOption('non-existent', 'default-val'));
|
||||
}
|
||||
|
||||
// ==================== getArtifacts ====================
|
||||
|
||||
public function testGetArtifactsReturnsEmptyInitially(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
|
||||
$this->assertSame([], $downloader->getArtifacts());
|
||||
}
|
||||
|
||||
// ==================== add ====================
|
||||
|
||||
public function testAddWithArtifactObjectAddsToList(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$downloader->add($artifact);
|
||||
|
||||
$artifacts = $downloader->getArtifacts();
|
||||
$this->assertArrayHasKey('my-pkg', $artifacts);
|
||||
$this->assertSame($artifact, $artifacts['my-pkg']);
|
||||
}
|
||||
|
||||
public function testAddReturnsSelf(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$result = $downloader->add($artifact);
|
||||
|
||||
$this->assertSame($downloader, $result);
|
||||
}
|
||||
|
||||
public function testAddDoesNotAddDuplicateArtifact(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$downloader->add($artifact);
|
||||
$downloader->add($artifact);
|
||||
|
||||
$this->assertCount(1, $downloader->getArtifacts());
|
||||
}
|
||||
|
||||
public function testAddWithStringNameLooksUpFromArtifactLoader(): void
|
||||
{
|
||||
$this->injectArtifactConfig('my-lib', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
$downloader->add('my-lib');
|
||||
|
||||
$artifacts = $downloader->getArtifacts();
|
||||
$this->assertArrayHasKey('my-lib', $artifacts);
|
||||
}
|
||||
|
||||
public function testAddWithStringNameThrowsForNonExistentArtifact(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
|
||||
$this->expectException(WrongUsageException::class);
|
||||
$this->expectExceptionMessage("Artifact 'non-existent' not found");
|
||||
|
||||
$downloader->add('non-existent');
|
||||
}
|
||||
|
||||
// ==================== addArtifacts ====================
|
||||
|
||||
public function testAddArtifactsAddsMultipleAtOnce(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
$a1 = new Artifact('pkg-a', ['source' => ['type' => 'url', 'url' => 'https://example.com/a.tar.gz']]);
|
||||
$a2 = new Artifact('pkg-b', ['source' => ['type' => 'url', 'url' => 'https://example.com/b.tar.gz']]);
|
||||
|
||||
$downloader->addArtifacts([$a1, $a2]);
|
||||
|
||||
$this->assertCount(2, $downloader->getArtifacts());
|
||||
$this->assertArrayHasKey('pkg-a', $downloader->getArtifacts());
|
||||
$this->assertArrayHasKey('pkg-b', $downloader->getArtifacts());
|
||||
}
|
||||
|
||||
public function testAddArtifactsReturnsSelf(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
|
||||
$result = $downloader->addArtifacts([]);
|
||||
|
||||
$this->assertSame($downloader, $result);
|
||||
}
|
||||
|
||||
// ==================== setParallel ====================
|
||||
|
||||
public function testSetParallelReturnsSelf(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
|
||||
$result = $downloader->setParallel(3);
|
||||
|
||||
$this->assertSame($downloader, $result);
|
||||
}
|
||||
|
||||
public function testSetParallelEnforcesMinimumOfOne(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
|
||||
$downloader->setParallel(0);
|
||||
// No direct getter for parallel, but verifying it doesn't throw
|
||||
$this->assertSame($downloader, $downloader->setParallel(0));
|
||||
}
|
||||
|
||||
public function testSetParallelAcceptsNormalValue(): void
|
||||
{
|
||||
$downloader = new ArtifactDownloader([], false);
|
||||
|
||||
$result = $downloader->setParallel(5);
|
||||
$this->assertSame($downloader, $result);
|
||||
}
|
||||
|
||||
// ==================== Helpers ====================
|
||||
|
||||
private function injectArtifactConfig(string $name, array $config): void
|
||||
{
|
||||
$reflection = new \ReflectionClass(ArtifactConfig::class);
|
||||
$property = $reflection->getProperty('artifact_configs');
|
||||
$property->setAccessible(true);
|
||||
$configs = $property->getValue(null) ?? [];
|
||||
$configs[$name] = $config;
|
||||
$property->setValue(null, $configs);
|
||||
}
|
||||
}
|
||||
229
tests/StaticPHP/Artifact/ArtifactExtractorTest.php
Normal file
229
tests/StaticPHP/Artifact/ArtifactExtractorTest.php
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\StaticPHP\Artifact;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use StaticPHP\Artifact\Artifact;
|
||||
use StaticPHP\Artifact\ArtifactCache;
|
||||
use StaticPHP\Artifact\ArtifactExtractor;
|
||||
use StaticPHP\Config\ArtifactConfig;
|
||||
use StaticPHP\DI\ApplicationContext;
|
||||
use StaticPHP\Registry\ArtifactLoader;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ArtifactExtractorTest extends TestCase
|
||||
{
|
||||
private string $tempDir;
|
||||
|
||||
private string $cacheFile;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->tempDir = sys_get_temp_dir() . '/artifact_extractor_test_' . uniqid();
|
||||
mkdir($this->tempDir, 0755, true);
|
||||
$this->cacheFile = $this->tempDir . '/.cache.json';
|
||||
file_put_contents($this->cacheFile, json_encode([]));
|
||||
|
||||
$reflection = new \ReflectionClass(ArtifactConfig::class);
|
||||
$property = $reflection->getProperty('artifact_configs');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue(null, []);
|
||||
|
||||
$loaderReflection = new \ReflectionClass(ArtifactLoader::class);
|
||||
$loaderProperty = $loaderReflection->getProperty('artifacts');
|
||||
$loaderProperty->setAccessible(true);
|
||||
$loaderProperty->setValue(null, null);
|
||||
|
||||
ApplicationContext::reset();
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
if (is_dir($this->tempDir)) {
|
||||
$this->removeDirectory($this->tempDir);
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass(ArtifactConfig::class);
|
||||
$property = $reflection->getProperty('artifact_configs');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue(null, []);
|
||||
|
||||
$loaderReflection = new \ReflectionClass(ArtifactLoader::class);
|
||||
$loaderProperty = $loaderReflection->getProperty('artifacts');
|
||||
$loaderProperty->setAccessible(true);
|
||||
$loaderProperty->setValue(null, null);
|
||||
|
||||
ApplicationContext::reset();
|
||||
}
|
||||
|
||||
// ==================== Constructor ====================
|
||||
|
||||
public function testConstructorStoresProvidedCache(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
$extractor = new ArtifactExtractor($cache, false);
|
||||
|
||||
// Verify the extractor was created without error; it holds the cache internally
|
||||
$this->assertInstanceOf(ArtifactExtractor::class, $extractor);
|
||||
}
|
||||
|
||||
public function testConstructorDefaultsInteractiveToTrue(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
$extractor = new ArtifactExtractor($cache);
|
||||
|
||||
$this->assertInstanceOf(ArtifactExtractor::class, $extractor);
|
||||
}
|
||||
|
||||
// ==================== extractForPackages ====================
|
||||
|
||||
public function testExtractForPackagesWithEmptyArrayDoesNothing(): void
|
||||
{
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
$extractor = new ArtifactExtractor($cache, false);
|
||||
|
||||
// Should complete without exception
|
||||
$extractor->extractForPackages([]);
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testExtractForPackagesDeduplicatesArtifacts(): void
|
||||
{
|
||||
ApplicationContext::initialize();
|
||||
$artifactConfig = ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']];
|
||||
$this->injectArtifactConfig('shared-lib', $artifactConfig);
|
||||
|
||||
$artifact = new Artifact('shared-lib', $artifactConfig);
|
||||
|
||||
// Create two mock packages that share the same artifact
|
||||
$pkg1 = $this->createMockPackage($artifact);
|
||||
$pkg2 = $this->createMockPackage($artifact);
|
||||
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
// Partial mock to verify extract is called exactly once despite two packages
|
||||
$extractor = $this->getMockBuilder(ArtifactExtractor::class)
|
||||
->setConstructorArgs([$cache, false])
|
||||
->onlyMethods(['extract'])
|
||||
->getMock();
|
||||
|
||||
$extractor->expects($this->once())
|
||||
->method('extract')
|
||||
->with($artifact, false)
|
||||
->willReturn(SPC_STATUS_ALREADY_EXTRACTED);
|
||||
|
||||
$extractor->extractForPackages([$pkg1, $pkg2]);
|
||||
}
|
||||
|
||||
public function testExtractForPackagesSkipsPackagesWithNoArtifact(): void
|
||||
{
|
||||
$pkgWithoutArtifact = $this->createMockPackage(null);
|
||||
|
||||
$cache = new ArtifactCache($this->cacheFile);
|
||||
|
||||
$extractor = $this->getMockBuilder(ArtifactExtractor::class)
|
||||
->setConstructorArgs([$cache, false])
|
||||
->onlyMethods(['extract'])
|
||||
->getMock();
|
||||
|
||||
// extract should NOT be called when no artifact
|
||||
$extractor->expects($this->never())->method('extract');
|
||||
|
||||
$extractor->extractForPackages([$pkgWithoutArtifact]);
|
||||
}
|
||||
|
||||
// ==================== extract ====================
|
||||
|
||||
public function testExtractReturnsAlreadyExtractedForSecondCall(): void
|
||||
{
|
||||
ApplicationContext::initialize();
|
||||
$artifactConfig = ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']];
|
||||
$artifact = new Artifact('my-pkg', $artifactConfig);
|
||||
|
||||
$cache = $this->createMock(ArtifactCache::class);
|
||||
$cache->method('getSourceInfo')->willReturn(null);
|
||||
$cache->method('getBinaryInfo')->willReturn(null);
|
||||
$cache->method('isBinaryDownloaded')->willReturn(false);
|
||||
ApplicationContext::set(ArtifactCache::class, $cache);
|
||||
|
||||
$extractor = new ArtifactExtractor($cache, false);
|
||||
|
||||
// Pre-populate the extracted map for 'my-pkg' via reflection
|
||||
$reflection = new \ReflectionClass(ArtifactExtractor::class);
|
||||
$extractedProperty = $reflection->getProperty('extracted');
|
||||
$extractedProperty->setAccessible(true);
|
||||
$extractedProperty->setValue($extractor, ['my-pkg' => true]);
|
||||
|
||||
$result = $extractor->extract($artifact, false);
|
||||
$this->assertSame(SPC_STATUS_ALREADY_EXTRACTED, $result);
|
||||
}
|
||||
|
||||
public function testExtractWithStringNameLooksUpFromArtifactLoader(): void
|
||||
{
|
||||
ApplicationContext::initialize();
|
||||
$artifactConfig = ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']];
|
||||
$this->injectArtifactConfig('my-pkg', $artifactConfig);
|
||||
|
||||
$cache = $this->createMock(ArtifactCache::class);
|
||||
$cache->method('getSourceInfo')->willReturn(null);
|
||||
$cache->method('getBinaryInfo')->willReturn(null);
|
||||
$cache->method('isBinaryDownloaded')->willReturn(false);
|
||||
ApplicationContext::set(ArtifactCache::class, $cache);
|
||||
|
||||
$extractor = new ArtifactExtractor($cache, false);
|
||||
|
||||
// Pre-populate the extracted map so we don't need actual downloads
|
||||
$reflection = new \ReflectionClass(ArtifactExtractor::class);
|
||||
$extractedProperty = $reflection->getProperty('extracted');
|
||||
$extractedProperty->setAccessible(true);
|
||||
$extractedProperty->setValue($extractor, ['my-pkg' => true]);
|
||||
|
||||
$result = $extractor->extract('my-pkg', false);
|
||||
$this->assertSame(SPC_STATUS_ALREADY_EXTRACTED, $result);
|
||||
}
|
||||
|
||||
// ==================== Helpers ====================
|
||||
|
||||
/**
|
||||
* Create a mock Package object that returns the given artifact from getArtifact().
|
||||
*/
|
||||
private function createMockPackage(?Artifact $artifact): \StaticPHP\Package\Package
|
||||
{
|
||||
$mock = $this->createMock(\StaticPHP\Package\Package::class);
|
||||
$mock->method('getArtifact')->willReturn($artifact);
|
||||
return $mock;
|
||||
}
|
||||
|
||||
private function injectArtifactConfig(string $name, array $config): void
|
||||
{
|
||||
$reflection = new \ReflectionClass(ArtifactConfig::class);
|
||||
$property = $reflection->getProperty('artifact_configs');
|
||||
$property->setAccessible(true);
|
||||
$configs = $property->getValue(null) ?? [];
|
||||
$configs[$name] = $config;
|
||||
$property->setValue(null, $configs);
|
||||
}
|
||||
|
||||
private 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)) {
|
||||
$this->removeDirectory($path);
|
||||
} else {
|
||||
unlink($path);
|
||||
}
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
750
tests/StaticPHP/Artifact/ArtifactTest.php
Normal file
750
tests/StaticPHP/Artifact/ArtifactTest.php
Normal file
@@ -0,0 +1,750 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\StaticPHP\Artifact;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use StaticPHP\Artifact\Artifact;
|
||||
use StaticPHP\Artifact\ArtifactCache;
|
||||
use StaticPHP\Config\ArtifactConfig;
|
||||
use StaticPHP\DI\ApplicationContext;
|
||||
use StaticPHP\Exception\SPCInternalException;
|
||||
use StaticPHP\Exception\ValidationException;
|
||||
use StaticPHP\Exception\WrongUsageException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ArtifactTest extends TestCase
|
||||
{
|
||||
private string $tempDir;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->tempDir = sys_get_temp_dir() . '/artifact_test_' . uniqid();
|
||||
mkdir($this->tempDir, 0755, true);
|
||||
|
||||
// Reset ArtifactConfig static state
|
||||
$reflection = new \ReflectionClass(ArtifactConfig::class);
|
||||
$property = $reflection->getProperty('artifact_configs');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue(null, []);
|
||||
|
||||
// Reset DI container
|
||||
ApplicationContext::reset();
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
if (is_dir($this->tempDir)) {
|
||||
$this->removeDirectory($this->tempDir);
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass(ArtifactConfig::class);
|
||||
$property = $reflection->getProperty('artifact_configs');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue(null, []);
|
||||
|
||||
ApplicationContext::reset();
|
||||
}
|
||||
|
||||
// ==================== Constants ====================
|
||||
|
||||
public function testConstantValues(): void
|
||||
{
|
||||
$this->assertSame(0, Artifact::FETCH_PREFER_SOURCE);
|
||||
$this->assertSame(1, Artifact::FETCH_PREFER_BINARY);
|
||||
$this->assertSame(2, Artifact::FETCH_ONLY_SOURCE);
|
||||
$this->assertSame(3, Artifact::FETCH_ONLY_BINARY);
|
||||
}
|
||||
|
||||
// ==================== Constructor ====================
|
||||
|
||||
public function testConstructWithInlineConfig(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->assertSame('my-pkg', $artifact->getName());
|
||||
}
|
||||
|
||||
public function testConstructFallsBackToArtifactConfig(): void
|
||||
{
|
||||
$this->injectArtifactConfig('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$artifact = new Artifact('my-pkg');
|
||||
$this->assertSame('my-pkg', $artifact->getName());
|
||||
}
|
||||
|
||||
public function testConstructThrowsForNonExistentArtifact(): void
|
||||
{
|
||||
$this->expectException(WrongUsageException::class);
|
||||
$this->expectExceptionMessage("Artifact 'non-existent' not found.");
|
||||
|
||||
new Artifact('non-existent');
|
||||
}
|
||||
|
||||
// ==================== getName ====================
|
||||
|
||||
public function testGetName(): void
|
||||
{
|
||||
$artifact = new Artifact('openssl', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$this->assertSame('openssl', $artifact->getName());
|
||||
}
|
||||
|
||||
// ==================== getDownloadConfig ====================
|
||||
|
||||
public function testGetDownloadConfigReturnsSection(): void
|
||||
{
|
||||
$config = ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz'], 'binary' => []];
|
||||
$artifact = new Artifact('my-pkg', $config);
|
||||
|
||||
$this->assertSame(['type' => 'url', 'url' => 'https://example.com/file.tar.gz'], $artifact->getDownloadConfig('source'));
|
||||
}
|
||||
|
||||
public function testGetDownloadConfigReturnsNullForMissingSection(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->assertNull($artifact->getDownloadConfig('non-existent'));
|
||||
}
|
||||
|
||||
// ==================== hasSource ====================
|
||||
|
||||
public function testHasSourceReturnsTrueWhenConfigHasSource(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->assertTrue($artifact->hasSource());
|
||||
}
|
||||
|
||||
public function testHasSourceReturnsFalseWhenNoSource(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['binary' => []]);
|
||||
|
||||
$this->assertFalse($artifact->hasSource());
|
||||
}
|
||||
|
||||
public function testHasSourceReturnsTrueWithCustomCallback(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['binary' => []]);
|
||||
$artifact->setCustomSourceCallback(function () {});
|
||||
|
||||
$this->assertTrue($artifact->hasSource());
|
||||
}
|
||||
|
||||
// ==================== hasPlatformBinary ====================
|
||||
|
||||
public function testHasPlatformBinaryReturnsTrueWhenConfigHasBinaryForCurrentPlatform(): void
|
||||
{
|
||||
$platform = $this->getCurrentPlatform();
|
||||
$artifact = new Artifact('my-pkg', [
|
||||
'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz'],
|
||||
'binary' => [$platform => ['type' => 'url', 'url' => 'https://example.com/bin.tar.gz']],
|
||||
]);
|
||||
|
||||
$this->assertTrue($artifact->hasPlatformBinary());
|
||||
}
|
||||
|
||||
public function testHasPlatformBinaryReturnsFalseWhenNoBinaryConfig(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->assertFalse($artifact->hasPlatformBinary());
|
||||
}
|
||||
|
||||
public function testHasPlatformBinaryReturnsTrueWithCustomCallback(): void
|
||||
{
|
||||
$platform = $this->getCurrentPlatform();
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$artifact->setCustomBinaryCallback($platform, function () {});
|
||||
|
||||
$this->assertTrue($artifact->hasPlatformBinary());
|
||||
}
|
||||
|
||||
// ==================== getBinaryPlatforms ====================
|
||||
|
||||
public function testGetBinaryPlatformsReturnsConfiguredPlatforms(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', [
|
||||
'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz'],
|
||||
'binary' => [
|
||||
'linux-x86_64' => ['type' => 'url', 'url' => 'https://example.com/linux.tar.gz'],
|
||||
'macos-aarch64' => ['type' => 'url', 'url' => 'https://example.com/mac.tar.gz'],
|
||||
],
|
||||
]);
|
||||
|
||||
$platforms = $artifact->getBinaryPlatforms();
|
||||
$this->assertContains('linux-x86_64', $platforms);
|
||||
$this->assertContains('macos-aarch64', $platforms);
|
||||
}
|
||||
|
||||
public function testGetBinaryPlatformsExcludesCustomTypeWithoutCallback(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', [
|
||||
'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz'],
|
||||
'binary' => [
|
||||
'linux-x86_64' => ['type' => 'custom'],
|
||||
],
|
||||
]);
|
||||
|
||||
// No custom callback registered, so custom-type platform should NOT be included
|
||||
$platforms = $artifact->getBinaryPlatforms();
|
||||
$this->assertNotContains('linux-x86_64', $platforms);
|
||||
}
|
||||
|
||||
public function testGetBinaryPlatformsIncludesCustomTypeWhenCallbackRegistered(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', [
|
||||
'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz'],
|
||||
'binary' => [
|
||||
'linux-x86_64' => ['type' => 'custom'],
|
||||
],
|
||||
]);
|
||||
$artifact->setCustomBinaryCallback('linux-x86_64', function () {});
|
||||
|
||||
$platforms = $artifact->getBinaryPlatforms();
|
||||
$this->assertContains('linux-x86_64', $platforms);
|
||||
}
|
||||
|
||||
public function testGetBinaryPlatformsIncludesCustomCallbackPlatforms(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', [
|
||||
'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz'],
|
||||
]);
|
||||
$artifact->setCustomBinaryCallback('linux-x86_64', function () {});
|
||||
|
||||
$platforms = $artifact->getBinaryPlatforms();
|
||||
$this->assertContains('linux-x86_64', $platforms);
|
||||
}
|
||||
|
||||
// ==================== getSourceDir ====================
|
||||
|
||||
public function testGetSourceDirDefaultsToSourcePathWithName(): void
|
||||
{
|
||||
$cache = $this->makeStubbedArtifactCache([]);
|
||||
ApplicationContext::initialize();
|
||||
ApplicationContext::set(ArtifactCache::class, $cache);
|
||||
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$expected = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, SOURCE_PATH . '/my-pkg');
|
||||
$this->assertSame($expected, $artifact->getSourceDir());
|
||||
}
|
||||
|
||||
public function testGetSourceDirWithRelativeExtractInConfig(): void
|
||||
{
|
||||
$cache = $this->makeStubbedArtifactCache([]);
|
||||
ApplicationContext::initialize();
|
||||
ApplicationContext::set(ArtifactCache::class, $cache);
|
||||
|
||||
$artifact = new Artifact('my-pkg', [
|
||||
'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz', 'extract' => 'my-pkg-1.0'],
|
||||
]);
|
||||
|
||||
$expected = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, SOURCE_PATH . '/my-pkg-1.0');
|
||||
$this->assertSame($expected, $artifact->getSourceDir());
|
||||
}
|
||||
|
||||
public function testGetSourceDirWithAbsoluteExtractInConfig(): void
|
||||
{
|
||||
$cache = $this->makeStubbedArtifactCache([]);
|
||||
ApplicationContext::initialize();
|
||||
ApplicationContext::set(ArtifactCache::class, $cache);
|
||||
|
||||
$artifact = new Artifact('my-pkg', [
|
||||
'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz', 'extract' => '/tmp/my-pkg-extract'],
|
||||
]);
|
||||
|
||||
$expected = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, '/tmp/my-pkg-extract');
|
||||
$this->assertSame($expected, $artifact->getSourceDir());
|
||||
}
|
||||
|
||||
// ==================== getSourceRoot ====================
|
||||
|
||||
public function testGetSourceRootDefaultsToSourceDir(): void
|
||||
{
|
||||
$cache = $this->makeStubbedArtifactCache([]);
|
||||
ApplicationContext::initialize();
|
||||
ApplicationContext::set(ArtifactCache::class, $cache);
|
||||
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->assertSame($artifact->getSourceDir(), $artifact->getSourceRoot());
|
||||
}
|
||||
|
||||
public function testGetSourceRootUsesMetadataSourceRoot(): void
|
||||
{
|
||||
$cache = $this->makeStubbedArtifactCache([]);
|
||||
ApplicationContext::initialize();
|
||||
ApplicationContext::set(ArtifactCache::class, $cache);
|
||||
|
||||
$artifact = new Artifact('my-pkg', [
|
||||
'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz'],
|
||||
'metadata' => ['source-root' => 'src'],
|
||||
]);
|
||||
|
||||
$expected = $artifact->getSourceDir() . DIRECTORY_SEPARATOR . 'src';
|
||||
$this->assertSame($expected, $artifact->getSourceRoot());
|
||||
}
|
||||
|
||||
// ==================== getBinaryExtractConfig ====================
|
||||
|
||||
public function testGetBinaryExtractConfigDefaultsToStandard(): void
|
||||
{
|
||||
putenv('EMULATE_PLATFORM=linux-x86_64');
|
||||
$artifact = new Artifact('my-pkg', [
|
||||
'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz'],
|
||||
// no binary for linux-x86_64
|
||||
]);
|
||||
|
||||
$config = $artifact->getBinaryExtractConfig();
|
||||
$this->assertSame('standard', $config['mode']);
|
||||
$this->assertSame(PKG_ROOT_PATH, $config['path']);
|
||||
putenv('EMULATE_PLATFORM');
|
||||
}
|
||||
|
||||
public function testGetBinaryExtractConfigWithHostedExtractReturnsBuildRootPath(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$config = $artifact->getBinaryExtractConfig(['extract' => 'hosted']);
|
||||
$this->assertSame('standard', $config['mode']);
|
||||
$this->assertSame(BUILD_ROOT_PATH, $config['path']);
|
||||
}
|
||||
|
||||
public function testGetBinaryExtractConfigWithRelativeExtractInCacheInfo(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$config = $artifact->getBinaryExtractConfig(['extract' => 'subdir']);
|
||||
$this->assertSame('standard', $config['mode']);
|
||||
$this->assertStringContainsString('subdir', $config['path']);
|
||||
}
|
||||
|
||||
public function testGetBinaryExtractConfigWithAbsoluteExtractInCacheInfo(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$config = $artifact->getBinaryExtractConfig(['extract' => '/tmp/absolute-path']);
|
||||
$this->assertSame('standard', $config['mode']);
|
||||
$this->assertStringContainsString('absolute-path', $config['path']);
|
||||
}
|
||||
|
||||
public function testGetBinaryExtractConfigWithArrayReturnsSelective(): void
|
||||
{
|
||||
putenv('EMULATE_PLATFORM=linux-x86_64');
|
||||
$fileMap = ['lib/libfoo.a' => '/usr/local/lib/libfoo.a'];
|
||||
$artifact = new Artifact('my-pkg', [
|
||||
'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz'],
|
||||
'binary' => [
|
||||
'linux-x86_64' => ['type' => 'url', 'url' => 'https://example.com/bin.tar.gz', 'extract' => $fileMap],
|
||||
],
|
||||
]);
|
||||
|
||||
$config = $artifact->getBinaryExtractConfig();
|
||||
$this->assertSame('selective', $config['mode']);
|
||||
$this->assertNull($config['path']);
|
||||
$this->assertSame($fileMap, $config['files']);
|
||||
putenv('EMULATE_PLATFORM');
|
||||
}
|
||||
|
||||
// ==================== getBinaryDir ====================
|
||||
|
||||
public function testGetBinaryDirDelegatesToGetBinaryExtractConfig(): void
|
||||
{
|
||||
putenv('EMULATE_PLATFORM=linux-x86_64');
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->assertSame(PKG_ROOT_PATH, $artifact->getBinaryDir());
|
||||
putenv('EMULATE_PLATFORM');
|
||||
}
|
||||
|
||||
// ==================== Custom source callbacks ====================
|
||||
|
||||
public function testSetAndGetCustomSourceCallback(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$cb = function () {};
|
||||
$artifact->setCustomSourceCallback($cb);
|
||||
|
||||
$this->assertSame($cb, $artifact->getCustomSourceCallback());
|
||||
}
|
||||
|
||||
public function testGetCustomSourceCallbackReturnsNullWhenNotSet(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->assertNull($artifact->getCustomSourceCallback());
|
||||
}
|
||||
|
||||
public function testSetAndGetCustomSourceCheckUpdateCallback(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$cb = function () {};
|
||||
$artifact->setCustomSourceCheckUpdateCallback($cb);
|
||||
|
||||
$this->assertSame($cb, $artifact->getCustomSourceCheckUpdateCallback());
|
||||
}
|
||||
|
||||
public function testGetCustomSourceCheckUpdateCallbackReturnsNullWhenNotSet(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->assertNull($artifact->getCustomSourceCheckUpdateCallback());
|
||||
}
|
||||
|
||||
// ==================== Custom binary callbacks ====================
|
||||
|
||||
public function testSetAndGetCustomBinaryCallback(): void
|
||||
{
|
||||
$platform = $this->getCurrentPlatform();
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$cb = function () {};
|
||||
$artifact->setCustomBinaryCallback($platform, $cb);
|
||||
|
||||
$this->assertSame($cb, $artifact->getCustomBinaryCallback());
|
||||
}
|
||||
|
||||
public function testGetCustomBinaryCallbackReturnsNullWhenNotSet(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->assertNull($artifact->getCustomBinaryCallback());
|
||||
}
|
||||
|
||||
public function testSetCustomBinaryCallbackThrowsForInvalidPlatform(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->expectException(ValidationException::class);
|
||||
$artifact->setCustomBinaryCallback('invalid-platform-string', function () {});
|
||||
}
|
||||
|
||||
public function testSetAndGetCustomBinaryCheckUpdateCallback(): void
|
||||
{
|
||||
$platform = $this->getCurrentPlatform();
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$cb = function () {};
|
||||
$artifact->setCustomBinaryCheckUpdateCallback($platform, $cb);
|
||||
|
||||
$this->assertSame($cb, $artifact->getCustomBinaryCheckUpdateCallback());
|
||||
}
|
||||
|
||||
public function testSetCustomBinaryCheckUpdateCallbackThrowsForInvalidPlatform(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->expectException(ValidationException::class);
|
||||
$artifact->setCustomBinaryCheckUpdateCallback('bad-platform', function () {});
|
||||
}
|
||||
|
||||
// ==================== Source extract callbacks ====================
|
||||
|
||||
public function testSetAndGetSourceExtractCallback(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$cb = function () {};
|
||||
$artifact->setSourceExtractCallback($cb);
|
||||
|
||||
$this->assertSame($cb, $artifact->getSourceExtractCallback());
|
||||
}
|
||||
|
||||
public function testHasSourceExtractCallbackReturnsFalseWhenNotSet(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->assertFalse($artifact->hasSourceExtractCallback());
|
||||
}
|
||||
|
||||
public function testHasSourceExtractCallbackReturnsTrueWhenSet(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$artifact->setSourceExtractCallback(function () {});
|
||||
|
||||
$this->assertTrue($artifact->hasSourceExtractCallback());
|
||||
}
|
||||
|
||||
// ==================== Binary extract callbacks ====================
|
||||
|
||||
public function testSetAndGetBinaryExtractCallbackForCurrentPlatform(): void
|
||||
{
|
||||
$platform = $this->getCurrentPlatform();
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$cb = function () {};
|
||||
$artifact->setBinaryExtractCallback($cb, [$platform]);
|
||||
|
||||
$this->assertSame($cb, $artifact->getBinaryExtractCallback());
|
||||
}
|
||||
|
||||
public function testGetBinaryExtractCallbackReturnsNullForNonMatchingPlatform(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
// Register callback for a platform that is definitely NOT the current one
|
||||
$otherPlatforms = array_diff(['linux-x86_64', 'linux-aarch64', 'macos-x86_64', 'macos-aarch64', 'windows-x86_64'], [$this->getCurrentPlatform()]);
|
||||
$artifact->setBinaryExtractCallback(function () {}, array_values($otherPlatforms));
|
||||
|
||||
// Only returns null if none of the listed platforms match current
|
||||
// Since current platform is excluded, all remaining are "other"
|
||||
$this->assertNull($artifact->getBinaryExtractCallback());
|
||||
}
|
||||
|
||||
public function testSetBinaryExtractCallbackWithEmptyPlatformsMatchesAll(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$cb = function () {};
|
||||
$artifact->setBinaryExtractCallback($cb, []);
|
||||
|
||||
$this->assertSame($cb, $artifact->getBinaryExtractCallback());
|
||||
}
|
||||
|
||||
public function testHasBinaryExtractCallbackReturnsFalseWhenNotSet(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->assertFalse($artifact->hasBinaryExtractCallback());
|
||||
}
|
||||
|
||||
public function testHasBinaryExtractCallbackReturnsTrueWhenSet(): void
|
||||
{
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$artifact->setBinaryExtractCallback(function () {});
|
||||
|
||||
$this->assertTrue($artifact->hasBinaryExtractCallback());
|
||||
}
|
||||
|
||||
// ==================== After-extract callbacks ====================
|
||||
|
||||
public function testEmitAfterSourceExtractCallsAllCallbacks(): void
|
||||
{
|
||||
ApplicationContext::initialize();
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$called1 = false;
|
||||
$called2 = false;
|
||||
$artifact->addAfterSourceExtractCallback(function (string $target_path) use (&$called1) {
|
||||
$called1 = true;
|
||||
});
|
||||
$artifact->addAfterSourceExtractCallback(function (string $target_path) use (&$called2) {
|
||||
$called2 = true;
|
||||
});
|
||||
|
||||
$artifact->emitAfterSourceExtract('/tmp/test-path');
|
||||
|
||||
$this->assertTrue($called1);
|
||||
$this->assertTrue($called2);
|
||||
}
|
||||
|
||||
public function testEmitAfterBinaryExtractCallsCallbackMatchingPlatform(): void
|
||||
{
|
||||
ApplicationContext::initialize();
|
||||
$platform = $this->getCurrentPlatform();
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$called = false;
|
||||
$artifact->addAfterBinaryExtractCallback(function (string $target_path, string $platform) use (&$called) {
|
||||
$called = true;
|
||||
}, [$platform]);
|
||||
|
||||
$artifact->emitAfterBinaryExtract('/tmp/test-path', $platform);
|
||||
|
||||
$this->assertTrue($called);
|
||||
}
|
||||
|
||||
public function testEmitAfterBinaryExtractSkipsCallbackForNonMatchingPlatform(): void
|
||||
{
|
||||
ApplicationContext::initialize();
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$called = false;
|
||||
$artifact->addAfterBinaryExtractCallback(function () use (&$called) {
|
||||
$called = true;
|
||||
}, ['windows-x86_64']);
|
||||
|
||||
$artifact->emitAfterBinaryExtract('/tmp/test-path', 'linux-x86_64');
|
||||
|
||||
$this->assertFalse($called);
|
||||
}
|
||||
|
||||
public function testEmitAfterBinaryExtractWithEmptyPlatformsCallsForAnyPlatform(): void
|
||||
{
|
||||
ApplicationContext::initialize();
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$called = false;
|
||||
$artifact->addAfterBinaryExtractCallback(function () use (&$called) {
|
||||
$called = true;
|
||||
}, []);
|
||||
|
||||
$artifact->emitAfterBinaryExtract('/tmp/test-path', 'linux-x86_64');
|
||||
|
||||
$this->assertTrue($called);
|
||||
}
|
||||
|
||||
// ==================== isSourceDownloaded / isBinaryDownloaded delegation ====================
|
||||
|
||||
public function testIsSourceDownloadedDelegatesToArtifactCache(): void
|
||||
{
|
||||
$cache = $this->createMock(ArtifactCache::class);
|
||||
$cache->expects($this->once())
|
||||
->method('isSourceDownloaded')
|
||||
->with('my-pkg', false)
|
||||
->willReturn(true);
|
||||
|
||||
ApplicationContext::initialize();
|
||||
ApplicationContext::set(ArtifactCache::class, $cache);
|
||||
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$this->assertTrue($artifact->isSourceDownloaded());
|
||||
}
|
||||
|
||||
public function testIsBinaryDownloadedDelegatesToArtifactCache(): void
|
||||
{
|
||||
$platform = $this->getCurrentPlatform();
|
||||
$cache = $this->createMock(ArtifactCache::class);
|
||||
$cache->expects($this->once())
|
||||
->method('isBinaryDownloaded')
|
||||
->with('my-pkg', $platform, false)
|
||||
->willReturn(true);
|
||||
|
||||
ApplicationContext::initialize();
|
||||
ApplicationContext::set(ArtifactCache::class, $cache);
|
||||
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$this->assertTrue($artifact->isBinaryDownloaded($platform));
|
||||
}
|
||||
|
||||
// ==================== shouldUseBinary ====================
|
||||
|
||||
public function testShouldUseBinaryReturnsFalseWhenNotDownloaded(): void
|
||||
{
|
||||
$platform = $this->getCurrentPlatform();
|
||||
$cache = $this->createMock(ArtifactCache::class);
|
||||
$cache->method('isBinaryDownloaded')->willReturn(false);
|
||||
|
||||
ApplicationContext::initialize();
|
||||
ApplicationContext::set(ArtifactCache::class, $cache);
|
||||
|
||||
$artifact = new Artifact('my-pkg', [
|
||||
'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz'],
|
||||
'binary' => [$platform => ['type' => 'url', 'url' => 'https://example.com/bin.tar.gz']],
|
||||
]);
|
||||
$this->assertFalse($artifact->shouldUseBinary());
|
||||
}
|
||||
|
||||
public function testShouldUseBinaryReturnsFalseWhenNoBinaryConfig(): void
|
||||
{
|
||||
$cache = $this->createMock(ArtifactCache::class);
|
||||
$cache->method('isBinaryDownloaded')->willReturn(true);
|
||||
|
||||
ApplicationContext::initialize();
|
||||
ApplicationContext::set(ArtifactCache::class, $cache);
|
||||
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
$this->assertFalse($artifact->shouldUseBinary());
|
||||
}
|
||||
|
||||
public function testShouldUseBinaryReturnsTrueWhenDownloadedAndHasBinaryConfig(): void
|
||||
{
|
||||
$platform = $this->getCurrentPlatform();
|
||||
$cache = $this->createMock(ArtifactCache::class);
|
||||
$cache->method('isBinaryDownloaded')->willReturn(true);
|
||||
|
||||
ApplicationContext::initialize();
|
||||
ApplicationContext::set(ArtifactCache::class, $cache);
|
||||
|
||||
$artifact = new Artifact('my-pkg', [
|
||||
'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz'],
|
||||
'binary' => [$platform => ['type' => 'url', 'url' => 'https://example.com/bin.tar.gz']],
|
||||
]);
|
||||
$this->assertTrue($artifact->shouldUseBinary());
|
||||
}
|
||||
|
||||
// ==================== isSourceExtracted ====================
|
||||
|
||||
public function testIsSourceExtractedReturnsFalseWhenDirNotExists(): void
|
||||
{
|
||||
$cache = $this->createMock(ArtifactCache::class);
|
||||
$cache->method('getSourceInfo')->willReturn(null);
|
||||
|
||||
ApplicationContext::initialize();
|
||||
ApplicationContext::set(ArtifactCache::class, $cache);
|
||||
|
||||
// Use an artifact whose source dir doesn't exist on disk
|
||||
$artifact = new Artifact('this-pkg-does-not-exist-on-disk-2xyz', [
|
||||
'source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz'],
|
||||
]);
|
||||
$this->assertFalse($artifact->isSourceExtracted());
|
||||
}
|
||||
|
||||
// ==================== emitCustomBinary ====================
|
||||
|
||||
public function testEmitCustomBinaryThrowsWhenNoBinaryCallbackDefined(): void
|
||||
{
|
||||
ApplicationContext::initialize();
|
||||
$artifact = new Artifact('my-pkg', ['source' => ['type' => 'url', 'url' => 'https://example.com/file.tar.gz']]);
|
||||
|
||||
$this->expectException(SPCInternalException::class);
|
||||
$artifact->emitCustomBinary();
|
||||
}
|
||||
|
||||
// ==================== Helpers ====================
|
||||
|
||||
private function getCurrentPlatform(): string
|
||||
{
|
||||
$emulated = getenv('EMULATE_PLATFORM');
|
||||
if ($emulated !== false) {
|
||||
return $emulated;
|
||||
}
|
||||
$os = match (PHP_OS_FAMILY) {
|
||||
'Darwin' => 'macos',
|
||||
'Windows' => 'windows',
|
||||
default => 'linux',
|
||||
};
|
||||
$arch = php_uname('m');
|
||||
if ($arch === 'arm64') {
|
||||
$arch = 'aarch64';
|
||||
}
|
||||
return "{$os}-{$arch}";
|
||||
}
|
||||
|
||||
private function injectArtifactConfig(string $name, array $config): void
|
||||
{
|
||||
$reflection = new \ReflectionClass(ArtifactConfig::class);
|
||||
$property = $reflection->getProperty('artifact_configs');
|
||||
$property->setAccessible(true);
|
||||
$configs = $property->getValue(null) ?? [];
|
||||
$configs[$name] = $config;
|
||||
$property->setValue(null, $configs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a stub ArtifactCache that always returns null for source/binary info
|
||||
* and delegates isSourceDownloaded/isBinaryDownloaded to return false.
|
||||
*/
|
||||
private function makeStubbedArtifactCache(array $sourceInfoMap): ArtifactCache
|
||||
{
|
||||
$cacheFile = $this->tempDir . '/test-cache.json';
|
||||
file_put_contents($cacheFile, json_encode([]));
|
||||
return new ArtifactCache($cacheFile);
|
||||
}
|
||||
|
||||
private 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)) {
|
||||
$this->removeDirectory($path);
|
||||
} else {
|
||||
unlink($path);
|
||||
}
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user