diff --git a/src/BinariesInstaller.php b/src/BinariesInstaller.php deleted file mode 100644 index 52396a6..0000000 --- a/src/BinariesInstaller.php +++ /dev/null @@ -1,30 +0,0 @@ -getParentPath(); - foreach ($subpackage->getExecutable() as $bin) { - $path = $baseDir.\DIRECTORY_SEPARATOR.$bin; - if (Platform::isWindows() || (method_exists(Platform::class, 'isWindowsSubsystemForLinux') ? Platform::isWindowsSubsystemForLinux() : false)) { - $proxy = $path.'.bat'; - if (file_exists($proxy)) { - $io->writeError(sprintf(' Skipped installation of bin %s.bat proxy for package %s: a .bat proxy was already installed', $bin, $subpackage->getName())); - } else { - $caller = BinaryInstaller::determineBinaryCaller($path); - file_put_contents($proxy, '@'.$caller.' "%~dp0'.ProcessExecutor::escape(basename($proxy, '.bat')).'" %*'); - } - } else { - chmod($path, 0777 ^ umask()); - } - } - } -} diff --git a/src/Exception/InvalidDownloadedFileException.php b/src/Exception/InvalidDownloadedFileException.php deleted file mode 100644 index 38f4979..0000000 --- a/src/Exception/InvalidDownloadedFileException.php +++ /dev/null @@ -1,7 +0,0 @@ -cache) { - return $this->cache; - } - - return $this->cache = $this->get($extraFile); - } - - abstract protected function get(array $extraFile): mixed; - - protected function throwException(string $attribute, string $reason): void - { - throw new UnexpectedValueException(sprintf('Attribute "%s" of extra file "%s" defined in package "%s" %s.', $attribute, $this->subpackageName, $this->parent->getName(), $reason)); - } -} diff --git a/src/Filter/ExecutableFilter.php b/src/Filter/ExecutableFilter.php deleted file mode 100644 index de3c546..0000000 --- a/src/Filter/ExecutableFilter.php +++ /dev/null @@ -1,68 +0,0 @@ -typeFilter->filter($extraFile); - $path = $this->pathFilter->filter($extraFile); - - if (Types::TYPE_PHAR === $type) { - if (isset($executable) && true !== $executable) { - $this->throwException('executable', sprintf('must be true, "%s" given', get_debug_type($executable))); - } else { - $executable = [$path]; - } - } - - if (\in_array($type, [Types::TYPE_FILE, Types::TYPE_GZIP])) { - if (isset($executable) && !\is_bool($executable)) { - $this->throwException('executable', sprintf('must be boolean, "%s" given', get_debug_type($executable))); - } else { - $executable = $executable ? [$path] : []; - } - } - - if (Types::isArchiveType($type)) { - $executable = $executable ?? []; - if (!\is_array($executable)) { - $this->throwException('executable', sprintf('must be array, "%s" given', get_debug_type($executable))); - } - if (!empty($executable) && !empty(array_filter($executable, fn (mixed $path) => !$this->isValidPath($path)))) { - $this->throwException('executable', 'are not valid paths'); - } - } - - return $executable; - } - - private function isValidPath(mixed $path): bool - { - if (!\is_string($path)) { - return false; - } - - if (Path::isAbsolute($path)) { - return false; - } - - return Path::isBasePath($this->parentPath, $this->parentPath.\DIRECTORY_SEPARATOR.$path); - } -} diff --git a/src/Filter/FilterInterface.php b/src/Filter/FilterInterface.php deleted file mode 100644 index 7db3c04..0000000 --- a/src/Filter/FilterInterface.php +++ /dev/null @@ -1,8 +0,0 @@ -filters = [ - self::VARIABLES => $variablesFilter = new VariablesFilter($subpackageName, $parent), - self::PATH => $pathFilter = new PathFilter($subpackageName, $parent, $parentPath, $variablesFilter), - self::URL => $urlFilter = new UrlFilter($subpackageName, $parent, $variablesFilter), - self::VERSION => new VersionFilter($subpackageName, $parent), - self::TYPE => $typeFilter = new TypeFilter($subpackageName, $parent, $urlFilter), - self::EXECUTABLE => new ExecutableFilter($subpackageName, $parent, $parentPath, $typeFilter, $pathFilter), - self::IGNORE => new IgnoreFilter($subpackageName, $parent, $typeFilter, $variablesFilter), - self::HASH => new HashFilter($subpackageName, $parent), - ]; - } - - public function get(string $name): FilterInterface - { - if (!isset($this->filters[$name])) { - throw new OutOfRangeException(sprintf('Filter "%s" not found.', $name)); - } - - return $this->filters[$name]; - } -} diff --git a/src/Filter/HashFilter.php b/src/Filter/HashFilter.php deleted file mode 100644 index 8fda13e..0000000 --- a/src/Filter/HashFilter.php +++ /dev/null @@ -1,44 +0,0 @@ -throwException('hash', sprintf('must be array, "%s" given', get_debug_type($hash))); - } - - $algo = $hash['algo'] ?? null; - if (!\is_string($algo)) { - $this->throwException('hash > algo', sprintf('must be string, "%s" given', get_debug_type($algo))); - } - - if (!\in_array($algo, hash_algos(), true)) { - $this->throwException('hash > algo', 'is not supported'); - } - - $value = $hash['value'] ?? null; - if (!\is_string($value)) { - $this->throwException('hash > value', sprintf('must be string, "%s" given', get_debug_type($value))); - } - - return new Hash($algo, $value); - } - - return null; - } -} diff --git a/src/Filter/IgnoreFilter.php b/src/Filter/IgnoreFilter.php deleted file mode 100644 index 7713db7..0000000 --- a/src/Filter/IgnoreFilter.php +++ /dev/null @@ -1,41 +0,0 @@ -typeFilter->filter($extraFile)) || !isset($extraFile['ignore'])) { - return []; - } - - $ignore = $extraFile['ignore']; - if (!\is_array($ignore)) { - $this->throwException('ignore', sprintf('must be array, "%s" given', get_debug_type($ignore))); - } - - $ignores = []; - $variables = $this->variablesFilter->filter($extraFile); - foreach ($ignore as $item) { - if (!\is_string($item)) { - $this->throwException('ignore', 'must be array of string'); - } - $ignores[] = strtr($item, $variables); - } - - return $ignores; - } -} diff --git a/src/Filter/PathFilter.php b/src/Filter/PathFilter.php deleted file mode 100644 index 6136298..0000000 --- a/src/Filter/PathFilter.php +++ /dev/null @@ -1,41 +0,0 @@ -throwException('path', 'is required'); - } - - $path = $extraFile['path']; - if (!\is_string($path)) { - $this->throwException('path', sprintf('must be string, "%s" given', get_debug_type($path))); - } - - $path = strtr($path, $this->variablesFilter->filter($extraFile)); - if (Path::isAbsolute($path)) { - $this->throwException('path', 'must be relative path'); - } - - if (!Path::isBasePath($this->parentPath, $this->parentPath.\DIRECTORY_SEPARATOR.$path)) { - $this->throwException('path', "must be inside relative to parent package's path"); - } - - return $path; - } -} diff --git a/src/Filter/TypeFilter.php b/src/Filter/TypeFilter.php deleted file mode 100644 index 5f502f6..0000000 --- a/src/Filter/TypeFilter.php +++ /dev/null @@ -1,52 +0,0 @@ -urlFilter->filter($extraFile); - - return $this->parseType($url); - } - - $type = $extraFile['type']; - if (!\is_string($type)) { - $this->throwException('type', sprintf('must be string, "%s" given', get_debug_type($type))); - } - - if (!\in_array($type, Types::ALL_TYPES)) { - $this->throwException('type', 'is not supported'); - } - - return $type; - } - - private function parseType(string $url): string - { - $parts = parse_url($url); - $filename = pathinfo($parts['path'], \PATHINFO_BASENAME); - if (preg_match('/\.(tar\.gz|tar\.bz2)$/', $filename)) { - return 'tar'; - } - if (preg_match('/\.tar\.xz$/', $filename)) { - return 'xz'; - } - $extension = pathinfo($parts['path'], \PATHINFO_EXTENSION); - - return Types::mapExtensionToType($extension); - } -} diff --git a/src/Filter/UrlFilter.php b/src/Filter/UrlFilter.php deleted file mode 100644 index 66f3924..0000000 --- a/src/Filter/UrlFilter.php +++ /dev/null @@ -1,35 +0,0 @@ -throwException('url', 'is required'); - } - - $url = $extraFile['url']; - if (!\is_string($url)) { - $this->throwException('url', sprintf('must be string, "%s" given', get_debug_type($url))); - } - - $url = strtr($url, $this->variablesFilter->filter($extraFile)); - if (false === filter_var($url, \FILTER_VALIDATE_URL)) { - $this->throwException('url', 'is invalid url'); - } - - return $url; - } -} diff --git a/src/Filter/VariablesFilter.php b/src/Filter/VariablesFilter.php deleted file mode 100644 index b611226..0000000 --- a/src/Filter/VariablesFilter.php +++ /dev/null @@ -1,61 +0,0 @@ - $this->subpackageName, - '{$version}' => $extraFile['version'] ?? '', - ]; - - if (!isset($extraFile['variables'])) { - return $variables; - } - - $values = $extraFile['variables']; - if (!\is_array($values)) { - $this->throwException('variables', sprintf('must be array, "%s" given', get_debug_type($values))); - } - - if (empty($values)) { - return $variables; - } - - $smpl = new SMPLang([ - 'range' => range(...), - 'strtolower' => strtolower(...), - 'php_uname' => php_uname(...), - 'in_array' => in_array(...), - 'str_contains' => str_contains(...), - 'str_starts_with' => str_starts_with(...), - 'str_ends_with' => str_ends_with(...), - 'matches' => fn (string $pattern, string $subject) => 1 === preg_match($pattern, $subject), - 'PHP_OS' => \PHP_OS, - 'PHP_OS_FAMILY' => \PHP_OS_FAMILY, - 'PHP_SHLIB_SUFFIX' => \PHP_SHLIB_SUFFIX, - 'DIRECTORY_SEPARATOR' => \DIRECTORY_SEPARATOR, - ]); - foreach ($values as $key => $value) { - if (!preg_match('/^{\$[^}]+}$/', $key)) { - $this->throwException('variables', sprintf('is invalid: Variable key "%s" should be this format "{$variable-name}"', $key)); - } - try { - $result = $smpl->evaluate($value); - } catch (Exception $exception) { - $this->throwException('variables', sprintf('is invalid. There is an error while evaluating expression "%s": %s', $value, $exception->getMessage())); - } - if (!\is_string($result)) { - $this->throwException('variables', sprintf('is invalid: Expression "%s" should be evaluated to string, "%s" given', $value, get_debug_type($result))); - } - $variables[$key] = $result; - } - - return $variables; - } -} diff --git a/src/Filter/VersionFilter.php b/src/Filter/VersionFilter.php deleted file mode 100644 index be3745b..0000000 --- a/src/Filter/VersionFilter.php +++ /dev/null @@ -1,46 +0,0 @@ -versionParser = $versionParser ?? new VersionParser(); - } - - protected function get(array $extraFile): Version - { - if (isset($extraFile['version'])) { - $value = $extraFile['version']; - if (!\is_string($value)) { - $this->throwException('version', sprintf('must be string, "%s" given', get_debug_type($value))); - } - - // $version = $this->versionParser->normalize($version); - $version = $this->versionParser->normalize(self::FAKE_VERSION); - $prettyVersion = $value; - } elseif ($this->parent instanceof RootPackageInterface) { - $version = $this->versionParser->normalize(self::FAKE_VERSION); - $prettyVersion = self::FAKE_VERSION; - } else { - $version = $this->parent->getVersion(); - $prettyVersion = $this->parent->getPrettyVersion(); - } - - return new Version($version, $prettyVersion); - } -} diff --git a/src/GlobCleaner.php b/src/GlobCleaner.php deleted file mode 100644 index 2c6cfc5..0000000 --- a/src/GlobCleaner.php +++ /dev/null @@ -1,31 +0,0 @@ -in($baseDir)->notName(BaseHandler::DOT_DIR)->path(Gitignore::toRegex(implode(\PHP_EOL, $ignores))); - foreach (iterator_to_array($finder) as $item) { - if ($item->isDir()) { - $dirs[] = $item->getPathname(); - } else { - unlink($item->getPathname()); - } - } - foreach ($dirs as $dir) { - @rmdir($dir); - } - } -} diff --git a/src/Handler/ArchiveHandler.php b/src/Handler/ArchiveHandler.php deleted file mode 100644 index 31d02bf..0000000 --- a/src/Handler/ArchiveHandler.php +++ /dev/null @@ -1,59 +0,0 @@ -cleaner = $cleaner ?? new GlobCleaner(); - } - - public function getTrackingFile(): string - { - $id = $this->subpackage->getSubpackageName(); - $file = basename($id).'-'.md5($id).'.json'; - - return - $this->subpackage->getTargetPath(). - \DIRECTORY_SEPARATOR.self::DOT_DIR. - \DIRECTORY_SEPARATOR.$file; - } - - public function getTrackingData(): array - { - return ['ignore' => $this->subpackage->getIgnore()] + parent::getTrackingData(); - } - - protected function getChecksumData(): array - { - $ignore = array_values($this->subpackage->getIgnore()); - sort($ignore); - - return ['ignore' => $ignore] + parent::getChecksumData(); - } - - protected function handleDownloadedFile(Composer $composer, string $file): void - { - $this->extract($composer, $this->subpackage->getTargetPath()); - $this->clean(); - } - - private function clean(): void - { - $this->cleaner->clean($this->subpackage->getTargetPath(), $this->subpackage->getIgnore()); - } -} diff --git a/src/Handler/BaseHandler.php b/src/Handler/BaseHandler.php deleted file mode 100644 index 85a0842..0000000 --- a/src/Handler/BaseHandler.php +++ /dev/null @@ -1,143 +0,0 @@ -binariesInstaller = $binariesInstaller ?? new BinariesInstaller(); - $this->filesystem = $filesystem ?? new Filesystem(); - } - - public function getSubpackage(): Subpackage - { - return $this->subpackage; - } - - public function getTrackingData(): array - { - return [ - 'name' => $this->subpackage->getName(), - 'url' => $this->subpackage->getDistUrl(), - 'checksum' => $this->getChecksum(), - ]; - } - - /** - * @return string A unique identifier for this configuration of this asset. - * If the identifier changes, that implies that the asset should be - * replaced/redownloaded. - */ - public function getChecksum(): string - { - return hash('sha256', serialize($this->getChecksumData())); - } - - protected function getChecksumData(): array - { - return [ - 'class' => static::class, - 'id' => $this->subpackage->getSubpackageName(), - 'url' => $this->subpackage->getDistUrl(), - 'path' => $this->subpackage->getTargetDir(), - ]; - } - - public function install(Composer $composer, IOInterface $io): void - { - $file = $this->download($composer); - if ($this->validateDownloadedFile($file)) { - $this->handleDownloadedFile($composer, $file); - $this->installBinaries($io); - } else { - $this->handleInvalidDownloadedFile($file); - } - } - - abstract protected function handleDownloadedFile(Composer $composer, string $file): void; - - private function installBinaries(IOInterface $io): void - { - $this->binariesInstaller->install($this->subpackage, $io); - } - - /** - * Download file to temporary place ("vendor/composer/tmp-[random-file-name]"), return downloaded file's path. - */ - private function download(Composer $composer): string - { - $downloadManager = $composer->getDownloadManager(); - - $file = ''; - $promise = $downloadManager->download($this->subpackage, \dirname($this->subpackage->getTargetPath())); - $promise->then(static function ($res) use (&$file) { - $file = $res; - - return \React\Promise\resolve($res); - }); - $composer->getLoop()->wait([$promise]); - - return $file; - } - - private function validateDownloadedFile(string $filePath): bool - { - $hash = $this->subpackage->getHash(); - - if (!$hash) { - return true; - } - - return $hash->verifyFile($filePath); - } - - protected function remove(string $file): void - { - $this->filesystem->remove($file); - } - - protected function move(string $file): void - { - $this->filesystem->rename($file, $this->subpackage->getTargetPath()); - } - - /** - * Extract downloaded file to new path. - */ - protected function extract(Composer $composer, string $targetPath): void - { - $downloadManager = $composer->getDownloadManager(); - - $promise = $downloadManager->install($this->subpackage, $targetPath); - $composer->getLoop()->wait([$promise]); - } - - private function handleInvalidDownloadedFile(string $file): void - { - $this->remove($file); - throw new InvalidDownloadedFileException(sprintf('Extra file "%s" does not match hash value defined in "%s".', $this->subpackage->getDistUrl(), $this->subpackage->getSubpackageName())); - } -} diff --git a/src/Handler/FileHandler.php b/src/Handler/FileHandler.php deleted file mode 100644 index c3575a3..0000000 --- a/src/Handler/FileHandler.php +++ /dev/null @@ -1,26 +0,0 @@ -subpackage->getSubpackageName(); - $file = $id.'-'.md5($id).'.json'; - - return - \dirname($this->subpackage->getTargetPath()). - \DIRECTORY_SEPARATOR.self::DOT_DIR. - \DIRECTORY_SEPARATOR.$file; - } - - protected function handleDownloadedFile(Composer $composer, string $file): void - { - $this->move($file); - } -} diff --git a/src/Handler/GzipHandler.php b/src/Handler/GzipHandler.php deleted file mode 100644 index ce11146..0000000 --- a/src/Handler/GzipHandler.php +++ /dev/null @@ -1,18 +0,0 @@ -subpackage->getTargetPath()).\DIRECTORY_SEPARATOR.uniqid(self::TMP_PREFIX, true); - $targetName = pathinfo($this->subpackage->getDistUrl(), \PATHINFO_FILENAME); - - $this->extract($composer, $tmpDir); - $this->move($tmpDir.\DIRECTORY_SEPARATOR.$targetName); - $this->remove($tmpDir); - } -} diff --git a/src/Handler/HandlerInterface.php b/src/Handler/HandlerInterface.php deleted file mode 100644 index c89c1e1..0000000 --- a/src/Handler/HandlerInterface.php +++ /dev/null @@ -1,20 +0,0 @@ -getExtra()['downloads'])) { - return; - } - - $this->downloadExtraFiles($package, $composer, $io); - if (!$package instanceof RootPackageInterface) { - $composer->getInstallationManager()->ensureBinariesPresence($package); - } - } - - private function downloadExtraFiles(PackageInterface $package, Composer $composer, IOInterface $io): void - { - $first = true; - foreach ($this->getSubpackages($package, $composer, $io) as $subpackage) { - $subpackageInstaller = $this->subpackageInstaller ?? new SubpackageInstaller($subpackage); - if ($subpackageInstaller->isInstalled($io)) { - continue; - } - - if ($first) { - $io->write(sprintf('Download extra files for %s', $package->getName())); - $first = false; - } - - $subpackageInstaller->install($composer, $io); - } - } - - private function getSubpackages(PackageInterface $package, Composer $composer, IOInterface $io): array - { - $basePath = $package instanceof RootPackageInterface ? getcwd() : $composer->getInstallationManager()->getInstallPath($package); - - try { - $factory = $this->factory ?? new SubpackageFactory(); - - return $factory->create($package, $basePath); - } catch (UnexpectedValueException $exception) { - $io->writeError(sprintf(' Skipped download extra files for package %s: %s', $package->getName(), $exception->getMessage())); - - return []; - } - } -} diff --git a/src/Plugin.php b/src/Plugin.php deleted file mode 100644 index a38d18d..0000000 --- a/src/Plugin.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace LastCall\DownloadsPlugin; - -use Composer\Composer; -use Composer\DependencyResolver\Operation\InstallOperation; -use Composer\DependencyResolver\Operation\UpdateOperation; -use Composer\EventDispatcher\EventSubscriberInterface; -use Composer\Installer\PackageEvent; -use Composer\Installer\PackageEvents; -use Composer\IO\IOInterface; -use Composer\Plugin\PluginInterface; -use Composer\Script\Event; -use Composer\Script\ScriptEvents; - -class Plugin implements PluginInterface, EventSubscriberInterface -{ - private const EVENT_PRIORITY = 10; - private PackageInstaller $installer; - - public function __construct(?PackageInstaller $installer = null) - { - $this->installer = $installer ?? new PackageInstaller(); - } - - public static function getSubscribedEvents(): array - { - return [ - PackageEvents::POST_PACKAGE_INSTALL => ['installDownloads', self::EVENT_PRIORITY], - PackageEvents::POST_PACKAGE_UPDATE => ['updateDownloads', self::EVENT_PRIORITY], - ScriptEvents::POST_INSTALL_CMD => ['installDownloadsRoot', self::EVENT_PRIORITY], - ScriptEvents::POST_UPDATE_CMD => ['installDownloadsRoot', self::EVENT_PRIORITY], - ]; - } - - public function installDownloadsRoot(Event $event): void - { - $rootPackage = $event->getComposer()->getPackage(); - $this->installer->install($rootPackage, $event->getComposer(), $event->getIO()); - - // Ensure that any other packages are properly reconciled. - $localRepo = $event->getComposer()->getRepositoryManager()->getLocalRepository(); - foreach ($localRepo->getCanonicalPackages() as $package) { - $this->installer->install($package, $event->getComposer(), $event->getIO()); - } - } - - public function installDownloads(PackageEvent $event): void - { - /** @var InstallOperation $operation */ - $operation = $event->getOperation(); - $package = $operation->getPackage(); - $this->installer->install($package, $event->getComposer(), $event->getIO()); - } - - public function updateDownloads(PackageEvent $event): void - { - /** @var UpdateOperation $operation */ - $operation = $event->getOperation(); - $package = $operation->getTargetPackage(); - $this->installer->install($package, $event->getComposer(), $event->getIO()); - } - - public function activate(Composer $composer, IOInterface $io): void - { - } - - public function deactivate(Composer $composer, IOInterface $io): void - { - } - - public function uninstall(Composer $composer, IOInterface $io): void - { - } -} diff --git a/src/Subpackage.php b/src/Subpackage.php deleted file mode 100644 index d9b1b85..0000000 --- a/src/Subpackage.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace LastCall\DownloadsPlugin; - -use Composer\Package\Package; -use Composer\Package\PackageInterface; -use LastCall\DownloadsPlugin\Model\Hash; -use LastCall\DownloadsPlugin\Model\Version; - -/** - * Class Subpackage. - */ -class Subpackage extends Package -{ - public function __construct( - PackageInterface $parent, - private string $parentPath, - private string $subpackageName, - private string $subpackageType, - private array $executable, - private array $ignore, - string $url, - string $path, - Version $version, - private ?Hash $hash = null, - ) { - parent::__construct( - sprintf('%s:%s', $parent->getName(), $subpackageName), - $version->version, - $version->prettyVersion - ); - $this->setDistUrl($url); - $this->setDistType(Types::mapTypeToDistType($subpackageType)); - $this->setTargetDir($path); - $this->setInstallationSource('dist'); - } - - public function getParentPath(): string - { - return $this->parentPath; - } - - public function getSubpackageName(): string - { - return $this->subpackageName; - } - - public function getExecutable(): array - { - return $this->executable; - } - - public function getIgnore(): array - { - return $this->ignore; - } - - public function getTargetPath(): string - { - return $this->parentPath.\DIRECTORY_SEPARATOR.$this->getTargetDir(); - } - - public function getSubpackageType(): string - { - return $this->subpackageType; - } - - public function setHash(?Hash $hash): void - { - $this->hash = $hash; - } - - public function getHash(): ?Hash - { - return $this->hash; - } -} diff --git a/src/SubpackageFactory.php b/src/SubpackageFactory.php deleted file mode 100644 index d214a08..0000000 --- a/src/SubpackageFactory.php +++ /dev/null @@ -1,65 +0,0 @@ -filterManager ?? new FilterManager($subpackageName, $parent, $parentPath); - - $executable = $filterManager->get(FilterManager::EXECUTABLE)->filter($extraFile); - $ignore = $filterManager->get(FilterManager::IGNORE)->filter($extraFile); - $url = $filterManager->get(FilterManager::URL)->filter($extraFile); - $path = $filterManager->get(FilterManager::PATH)->filter($extraFile); - $version = $filterManager->get(FilterManager::VERSION)->filter($extraFile); - $type = $filterManager->get(FilterManager::TYPE)->filter($extraFile); - $hash = $filterManager->get(FilterManager::HASH)->filter($extraFile); - - return new Subpackage( - $parent, - $parentPath, - $subpackageName, - $type, - $executable, - $ignore, - $url, - $path, - $version, - $hash - ); - } - - /** - * @return Subpackage[] - */ - public function create(PackageInterface $package, string $basePath): array - { - $subpackages = []; - $extra = $package->getExtra(); - $defaults = $extra['downloads']['*'] ?? []; - - if (!empty($extra['downloads'])) { - foreach ((array) $extra['downloads'] as $id => $extraFile) { - if ('*' === $id) { - continue; - } - - $subpackages[] = $this->createSingle($id, array_merge($defaults, $extraFile), $package, $basePath); - } - } - - return $subpackages; - } -} diff --git a/src/SubpackageInstaller.php b/src/SubpackageInstaller.php deleted file mode 100644 index 7db4c2d..0000000 --- a/src/SubpackageInstaller.php +++ /dev/null @@ -1,79 +0,0 @@ -handler = $handler ?? Types::createHandler($subpackage); - } - - public function isInstalled(IOInterface $io): bool - { - $targetPath = $this->subpackage->getTargetPath(); - $trackingFile = $this->handler->getTrackingFile(); - - if (file_exists($targetPath) && !file_exists($trackingFile)) { - $io->write( - sprintf( - 'Extra file %s has been locally overriden in %s. To reset it, delete and reinstall.', - $this->subpackage->getName(), - $this->subpackage->getTargetDir() - ), - true - ); - - return true; - } - - if (file_exists($targetPath) && file_exists($trackingFile)) { - $meta = @json_decode(file_get_contents($trackingFile), 1, 512, \JSON_THROW_ON_ERROR); - if (isset($meta['checksum']) && $meta['checksum'] === $this->handler->getChecksum()) { - $io->write( - sprintf('Skip extra file %s', $this->subpackage->getName()), - true, - IOInterface::VERY_VERBOSE - ); - - return true; - } - } - - return false; - } - - public function install(Composer $composer, IOInterface $io): void - { - $io->write( - sprintf('Download extra file %s', $this->subpackage->getName()), - true, - IOInterface::VERBOSE - ); - $this->handler->install($composer, $io); - $this->createTrackingFile($io); - } - - private function createTrackingFile(IOInterface $io): void - { - $io->write( - sprintf('Create tracking file for %s', $this->subpackage->getName()), - true, - IOInterface::VERY_VERBOSE - ); - $trackingFile = $this->handler->getTrackingFile(); - if (!file_exists(\dirname($trackingFile))) { - mkdir(\dirname($trackingFile), 0777, true); - } - file_put_contents($trackingFile, json_encode( - $this->handler->getTrackingData(), - \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES - )); - } -} diff --git a/src/Types.php b/src/Types.php deleted file mode 100644 index 7889ef5..0000000 --- a/src/Types.php +++ /dev/null @@ -1,88 +0,0 @@ - self::TYPE_ZIP, - 'rar' => self::TYPE_RAR, - 'tgz' => self::TYPE_TAR, - 'tar' => self::TYPE_TAR, - 'gz' => self::TYPE_GZIP, - 'phar' => self::TYPE_PHAR, - ]; - public const TYPE_TO_HANDLER_CLASS_MAP = [ - self::TYPE_ZIP => ZipHandler::class, - self::TYPE_RAR => RarHandler::class, - self::TYPE_TAR => TarHandler::class, - self::TYPE_XZ => XzHandler::class, - self::TYPE_FILE => FileHandler::class, - self::TYPE_PHAR => PharHandler::class, - self::TYPE_GZIP => GzipHandler::class, - ]; - - public static function isArchiveType(string $type): bool - { - return \in_array($type, self::ARCHIVE_TYPES); - } - - public static function isFileType(string $type): bool - { - return \in_array($type, self::FILE_TYPES); - } - - public static function mapExtensionToType(string $extension): string - { - return self::EXTENSION_TO_TYPE_MAP[$extension] ?? self::TYPE_FILE; - } - - public static function createHandler(Subpackage $subpackage): HandlerInterface - { - $class = self::TYPE_TO_HANDLER_CLASS_MAP[$subpackage->getSubpackageType()] ?? FileHandler::class; - - return new $class($subpackage); - } - - public static function mapTypeToDistType(string $type): string - { - return self::TYPE_PHAR === $type ? 'file' : $type; - } -} diff --git a/tests/Unit/BinariesInstallerTest.php b/tests/Unit/BinariesInstallerTest.php deleted file mode 100644 index 67c0913..0000000 --- a/tests/Unit/BinariesInstallerTest.php +++ /dev/null @@ -1,80 +0,0 @@ - 'file1', - true => 'file2', - ]; - - protected function setUp(): void - { - $this->fs = new FileSystem(); // Keep virtual file system alive during test - $this->io = $this->createMock(IOInterface::class); - $this->subpackage = $this->createMock(Subpackage::class); - $this->installer = new BinariesInstaller(); - } - - protected function tearDown(): void - { - $this->fs = null; - } - - public function testInstall(): void - { - $this->subpackage->expects($this->once())->method('getExecutable')->willReturn($this->binaries); - $this->subpackage->expects($this->once())->method('getParentPath')->willReturn($this->fs->path($this->baseDir)); - $this->fs->createDirectory($this->baseDir, true); - foreach ($this->binaries as $hasProxy => $binary) { - $path = $this->baseDir.\DIRECTORY_SEPARATOR.$binary; - $content = implode(\PHP_EOL, [ - '#!/usr/bin/env php', - 'fs->createFile($path, $content); - chmod($this->fs->path($path), 0600 ^ umask()); - if (\PHP_OS_FAMILY === 'Windows' && $hasProxy) { - $proxy = $path.'.bat'; - $this->fs->createFile($proxy, 'proxy content'); - } - } - if (\PHP_OS_FAMILY === 'Windows') { - $this->subpackage->expects($this->once())->method('getName')->willReturn($this->subpackageName); - $this->io - ->expects($this->once()) - ->method('writeError') - ->with(' Skipped installation of bin '.$binary.'.bat proxy for package '.$this->subpackageName.': a .bat proxy was already installed'); - } - $this->installer->install($this->subpackage, $this->io); - foreach ($this->binaries as $hasProxy => $binary) { - $path = $this->baseDir.\DIRECTORY_SEPARATOR.$binary; - if (\PHP_OS_FAMILY === 'Windows') { - $proxy = $path.'.bat'; - if (!$hasProxy) { - $this->assertStringEqualsFile( - $this->fs->path($proxy), - '@php "%~dp0file1" %*' - ); - } - } else { - $this->assertTrue(is_executable($this->fs->path($path))); - } - } - } -} diff --git a/tests/Unit/Filter/BaseFilterTestCase.php b/tests/Unit/Filter/BaseFilterTestCase.php deleted file mode 100644 index e9eca1c..0000000 --- a/tests/Unit/Filter/BaseFilterTestCase.php +++ /dev/null @@ -1,32 +0,0 @@ -parent = $this->createMock(PackageInterface::class); - $this->filter = $this->createFilter(); - } - - protected function expectUnexpectedValueException(string $attribute, string $reason): void - { - $this->expectException(UnexpectedValueException::class); - $this->expectExceptionMessage(sprintf('Attribute "%s" of extra file "%s" defined in package "%s" %s.', $attribute, $this->name, $this->parentName, $reason)); - } - - abstract protected function createFilter(): FilterInterface; -} diff --git a/tests/Unit/Filter/ExecutableFilterTest.php b/tests/Unit/Filter/ExecutableFilterTest.php deleted file mode 100644 index e60ceb6..0000000 --- a/tests/Unit/Filter/ExecutableFilterTest.php +++ /dev/null @@ -1,174 +0,0 @@ -pathFilter = $this->createMock(PathFilter::class); - $this->typeFilter = $this->createMock(TypeFilter::class); - parent::setUp(); - } - - public function getEmptyExecutableTests(): array - { - return [ - [[]], - [['executable' => []]], - ]; - } - - /** - * @dataProvider getEmptyExecutableTests - */ - public function testEmptyExecutable(array $extraFile): void - { - $this->pathFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn($this->path); - $this->typeFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn(Types::TYPE_ZIP); - $this->assertSame([], $this->filter->filter($extraFile)); - } - - public function getInvalidExecutableArchiveTypeTests(): array - { - return [ - [true, 'bool'], - [false, 'bool'], - [123, 'int'], - [12.3, 'float'], - ['test', 'string'], - [(object) ['key' => 'value'], 'stdClass'], - ]; - } - - /** - * @dataProvider getInvalidExecutableArchiveTypeTests - */ - public function testInvalidExecutableArchiveType(mixed $invalidExecutable, string $type): void - { - $extraFile = [ - 'executable' => $invalidExecutable, - ]; - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->pathFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn($this->path); - $this->typeFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn(Types::TYPE_ZIP); - $this->expectUnexpectedValueException('executable', sprintf('must be array, "%s" given', $type)); - $this->filter->filter($extraFile); - } - - public function testInvalidExecutableArchivePaths(): void - { - $extraFile = [ - 'executable' => [ - ['not a string'], - '/root/path/to/file', - '../../outside/path/to/file', - ], - ]; - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->pathFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn($this->path); - $this->typeFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn(Types::TYPE_ZIP); - $this->expectUnexpectedValueException('executable', 'are not valid paths'); - $this->filter->filter($extraFile); - } - - public function getInvalidExecutableFileTests(): array - { - return [ - [123, 'int'], - [12.3, 'float'], - ['test', 'string'], - [['key' => 'value'], 'array'], - [(object) ['key' => 'value'], 'stdClass'], - ]; - } - - /** - * @dataProvider getInvalidExecutableFileTests - */ - public function testInvalidExecutableFile(mixed $invalidExecutable, string $type): void - { - $extraFile = [ - 'executable' => $invalidExecutable, - ]; - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->pathFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn($this->path); - $this->typeFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn(Types::TYPE_FILE); - $this->expectUnexpectedValueException('executable', sprintf('must be boolean, "%s" given', $type)); - $this->filter->filter($extraFile); - } - - public function getInvalidExecutablePharTests(): array - { - return [ - [false, 'bool'], - [123, 'int'], - [12.3, 'float'], - ['test', 'string'], - [['key' => 'value'], 'array'], - [(object) ['key' => 'value'], 'stdClass'], - ]; - } - - /** - * @dataProvider getInvalidExecutablePharTests - */ - public function testInvalidExecutablePhar(mixed $invalidExecutable, string $type): void - { - $extraFile = [ - 'executable' => $invalidExecutable, - ]; - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->pathFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn($this->path); - $this->typeFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn(Types::TYPE_PHAR); - $this->expectUnexpectedValueException('executable', sprintf('must be true, "%s" given', $type)); - $this->filter->filter($extraFile); - } - - public function getFilterExecutableFileTests(): array - { - return [ - [Types::TYPE_FILE], - [Types::TYPE_PHAR], - ]; - } - - /** - * @dataProvider getFilterExecutableFileTests - */ - public function testFilterExecutableFile(string $type): void - { - $extraFile = ['executable' => true, 'path' => $this->path]; - $this->pathFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn($this->path); - $this->typeFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn($type); - $expectedValue = [$this->path]; - $this->assertSame($expectedValue, $this->filter->filter($extraFile)); - $this->assertSame($expectedValue, $this->filter->filter([])); - } - - public function testFilterExecutableArchive(): void - { - $executable = ['path/to/file', 'path/to/another/file']; - $extraFile = ['executable' => $executable]; - $this->pathFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn($this->path); - $this->typeFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn(Types::TYPE_ZIP); - $this->assertSame($executable, $this->filter->filter($extraFile)); - $this->assertSame($executable, $this->filter->filter([])); - } - - protected function createFilter(): FilterInterface - { - return new ExecutableFilter($this->name, $this->parent, $this->parentPath, $this->typeFilter, $this->pathFilter); - } -} diff --git a/tests/Unit/Filter/FilterManagerTest.php b/tests/Unit/Filter/FilterManagerTest.php deleted file mode 100644 index 7dc6798..0000000 --- a/tests/Unit/Filter/FilterManagerTest.php +++ /dev/null @@ -1,60 +0,0 @@ -parent = $this->createMock(PackageInterface::class); - $this->manager = new FilterManager($this->subpackageName, $this->parent, $this->parentPath); - } - - public function getValidFilterTests(): array - { - return [ - ['path', PathFilter::class], - ['url', UrlFilter::class], - ['variables', VariablesFilter::class], - ['version', VersionFilter::class], - ['executable', ExecutableFilter::class], - ['ignore', IgnoreFilter::class], - ['type', TypeFilter::class], - ['hash', HashFilter::class], - ]; - } - - /** - * @dataProvider getValidFilterTests - */ - public function testGetValidFilter(string $name, string $class): void - { - $this->assertInstanceOf($class, $this->manager->get($name)); - } - - public function testGetInvalidFilter(): void - { - $this->expectException(OutOfRangeException::class); - $this->expectExceptionMessage('Filter "invalid" not found.'); - $this->manager->get('invalid'); - } -} diff --git a/tests/Unit/Filter/HashFilterTest.php b/tests/Unit/Filter/HashFilterTest.php deleted file mode 100644 index 2afdf1d..0000000 --- a/tests/Unit/Filter/HashFilterTest.php +++ /dev/null @@ -1,113 +0,0 @@ - 'value'], 'stdClass'], - ]; - } - - /** - * @dataProvider getInvalidHashTests - */ - public function testInvalidHash(mixed $invalidHash, string $type): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->expectUnexpectedValueException('hash', sprintf('must be array, "%s" given', $type)); - $this->filter->filter([ - 'hash' => $invalidHash, - ]); - } - - public function getInvalidHashChildTests(): array - { - return [ - [true, 'bool'], - [false, 'bool'], - [123, 'int'], - [12.3, 'float'], - [['key' => 'value'], 'array'], - [(object) ['key' => 'value'], 'stdClass'], - ]; - } - - /** - * @dataProvider getInvalidHashChildTests - */ - public function testInvalidAlgo(mixed $invalidAlgo, string $type): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->expectUnexpectedValueException('hash > algo', sprintf('must be string, "%s" given', $type)); - $this->filter->filter([ - 'hash' => [ - 'algo' => $invalidAlgo, - 'value' => 'abc123', - ], - ]); - } - - public function testNotSupportedAlgo(): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->expectUnexpectedValueException('hash > algo', 'is not supported'); - $this->filter->filter([ - 'hash' => [ - 'algo' => 'not-supported', - 'value' => 'abc123', - ], - ]); - } - - /** - * @dataProvider getInvalidHashChildTests - */ - public function testInvalidValue(mixed $invalidValue, string $type): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->expectUnexpectedValueException('hash > value', sprintf('must be string, "%s" given', $type)); - $this->filter->filter([ - 'hash' => [ - 'algo' => 'md5', - 'value' => $invalidValue, - ], - ]); - } - - public function testValidHash(): void - { - $hash = $this->filter->filter([ - 'hash' => [ - 'algo' => $expectedAlgo = 'sha256', - 'value' => $expectedValue = '1d0f9d464f7330e866357380d76f5f68becf0ca84b205a2135fd392581ed0b1d', - ], - ]); - $reflection = new \ReflectionClass($hash); - $this->assertSame($expectedAlgo, $reflection->getProperty('algo')->getValue($hash)); - $this->assertSame($expectedValue, $reflection->getProperty('value')->getValue($hash)); - } - - protected function createFilter(): FilterInterface - { - return new HashFilter($this->name, $this->parent); - } -} diff --git a/tests/Unit/Filter/IgnoreFilterTest.php b/tests/Unit/Filter/IgnoreFilterTest.php deleted file mode 100644 index e16e320..0000000 --- a/tests/Unit/Filter/IgnoreFilterTest.php +++ /dev/null @@ -1,116 +0,0 @@ -typeFilter = $this->createMock(TypeFilter::class); - $this->variablesFilter = $this->createMock(VariablesFilter::class); - parent::setUp(); - } - - public function testNotArchiveType(): void - { - $extraFile = ['ignore' => ['file']]; - $this->typeFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn(Types::TYPE_FILE); - $this->assertSame([], $this->filter->filter($extraFile)); - } - - public function getEmptyIgnoreTests(): array - { - return [ - [[]], - [['ignore' => []]], - ]; - } - - /** - * @dataProvider getEmptyIgnoreTests - */ - public function testEmptyIgnore(array $extraFile): void - { - $this->typeFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn(Types::TYPE_ZIP); - $this->assertSame([], $this->filter->filter($extraFile)); - } - - public function getInvalidIgnoreTests(): array - { - return [ - [true, 'bool'], - [false, 'bool'], - [123, 'int'], - [12.3, 'float'], - ['test', 'string'], - [(object) ['key' => 'value'], 'stdClass'], - ]; - } - - /** - * @dataProvider getInvalidIgnoreTests - */ - public function testInvalidIgnore(mixed $invalidIgnore, string $type): void - { - $extraFile = [ - 'ignore' => $invalidIgnore, - ]; - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->typeFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn(Types::TYPE_ZIP); - $this->expectUnexpectedValueException('ignore', sprintf('must be array, "%s" given', $type)); - $this->filter->filter($extraFile); - } - - public function testInvalidIgnoreItem(): void - { - $extraFile = [ - 'ignore' => [ - ['not a string'], - 123, - null, - ], - ]; - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->typeFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn(Types::TYPE_ZIP); - $this->variablesFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn([]); - $this->expectUnexpectedValueException('ignore', 'must be array of string'); - $this->filter->filter($extraFile); - } - - public function testFilterIgnore(): void - { - $ignore = ['dir/*', '!dir/file']; - $extraFile = ['ignore' => $ignore]; - $this->typeFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn(Types::TYPE_ZIP); - $this->variablesFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn([]); - $this->assertSame($ignore, $this->filter->filter($extraFile)); - $this->assertSame($ignore, $this->filter->filter([])); - } - - public function testFilterIgnoreWithVariables(): void - { - $ignore = ['dir/*', '!dir/file{$extension}']; - $extraFile = ['ignore' => $ignore]; - $variables = ['{$extension}' => '.txt']; - $this->typeFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn(Types::TYPE_ZIP); - $this->variablesFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn($variables); - $ignores = ['dir/*', '!dir/file.txt']; - $this->assertSame($ignores, $this->filter->filter($extraFile)); - $this->assertSame($ignores, $this->filter->filter([])); - } - - protected function createFilter(): FilterInterface - { - return new IgnoreFilter($this->name, $this->parent, $this->typeFilter, $this->variablesFilter); - } -} diff --git a/tests/Unit/Filter/PathFilterTest.php b/tests/Unit/Filter/PathFilterTest.php deleted file mode 100644 index e087415..0000000 --- a/tests/Unit/Filter/PathFilterTest.php +++ /dev/null @@ -1,110 +0,0 @@ -variablesFilter = $this->createMock(VariablesFilter::class); - parent::setUp(); - } - - public function testNotSet(): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->variablesFilter->expects($this->never())->method('filter'); - $this->expectUnexpectedValueException('path', 'is required'); - $this->filter->filter([]); - } - - public function getInvalidPathTests(): array - { - return [ - [true, 'bool'], - [false, 'bool'], - [123, 'int'], - [12.3, 'float'], - [['key' => 'value'], 'array'], - [(object) ['key' => 'value'], 'stdClass'], - ]; - } - - /** - * @dataProvider getInvalidPathTests - */ - public function testInvalidPath(mixed $invalidPath, string $type): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->variablesFilter->expects($this->never())->method('filter'); - $this->expectUnexpectedValueException('path', sprintf('must be string, "%s" given', $type)); - $this->filter->filter([ - 'path' => $invalidPath, - ]); - } - - public function getAbsolutePathTests(): array - { - return [ - ['C:\Programs\PHP\php.ini'], - ['/var/www/project/uploads'], - ]; - } - - /** - * @dataProvider getAbsolutePathTests - */ - public function testAbsolutePathFile(string $absolutePath): void - { - $extraFile = [ - 'path' => $absolutePath, - ]; - $this->variablesFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn([]); - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->expectUnexpectedValueException('path', 'must be relative path'); - $this->filter->filter($extraFile); - } - - public function testOutsidePath(): void - { - $extraFile = [ - 'path' => '../../other/place', - ]; - $this->variablesFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn([]); - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->expectUnexpectedValueException('path', "must be inside relative to parent package's path"); - $this->filter->filter($extraFile); - } - - public function getFilterPathTests(): array - { - return [ - ['path/to/file', [], 'path/to/file'], - ['path/to/file/{$version}', ['{$version}' => '1.2.3'], 'path/to/file/1.2.3'], - ]; - } - - /** - * @dataProvider getFilterPathTests - */ - public function testFilterPath(string $path, array $variables, string $expectedPath): void - { - $extraFile = ['variables' => $variables, 'path' => $path]; - $this->variablesFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn($variables); - $this->parent->expects($this->never())->method('getName'); - $this->assertSame($expectedPath, $this->filter->filter($extraFile)); - $this->assertSame($expectedPath, $this->filter->filter([])); - } - - protected function createFilter(): FilterInterface - { - return new PathFilter($this->name, $this->parent, $this->parentPath, $this->variablesFilter); - } -} diff --git a/tests/Unit/Filter/TypeFilterTest.php b/tests/Unit/Filter/TypeFilterTest.php deleted file mode 100644 index 639fcfb..0000000 --- a/tests/Unit/Filter/TypeFilterTest.php +++ /dev/null @@ -1,97 +0,0 @@ -urlFilter = $this->createMock(UrlFilter::class); - parent::setUp(); - } - - public function getParseTypeFromUrlTests(): array - { - return [ - ['http://example.com/file.tar.gz', Types::TYPE_TAR], - ['http://example.com/file.tar.bz2', Types::TYPE_TAR], - ['http://example.com/file.tar.xz', Types::TYPE_XZ], - ['http://example.com/file.zip', Types::TYPE_ZIP], - ['http://example.com/file.rar', Types::TYPE_RAR], - ['http://example.com/file.tgz', Types::TYPE_TAR], - ['http://example.com/file.tar', Types::TYPE_TAR], - ['http://example.com/file.gz', Types::TYPE_GZIP], - ['http://example.com/file.phar', Types::TYPE_PHAR], - ['http://example.com/file', Types::TYPE_FILE], - ]; - } - - /** - * @dataProvider getParseTypeFromUrlTests - */ - public function testParseTypeFromUrl(string $url, string $expectedType): void - { - $extraFile = ['url' => $url]; - $this->urlFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn($url); - $this->assertSame($expectedType, $this->filter->filter($extraFile)); - $this->assertSame($expectedType, $this->filter->filter([])); - } - - public function getInvalidTypeTests(): array - { - return [ - [true, 'bool'], - [false, 'bool'], - [123, 'int'], - [12.3, 'float'], - [['key' => 'value'], 'array'], - [(object) ['key' => 'value'], 'stdClass'], - ]; - } - - /** - * @dataProvider getInvalidTypeTests - */ - public function testInvalidType(mixed $invalidType, string $type): void - { - $extraFile = [ - 'type' => $invalidType, - ]; - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->urlFilter->expects($this->never())->method('filter'); - $this->expectUnexpectedValueException('type', sprintf('must be string, "%s" given', $type)); - $this->filter->filter($extraFile); - } - - public function testNotSupportedType(): void - { - $extraFile = [ - 'type' => 'not supported', - ]; - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->urlFilter->expects($this->never())->method('filter'); - $this->expectUnexpectedValueException('type', 'is not supported'); - $this->filter->filter($extraFile); - } - - public function testFilterType(): void - { - $extraFile = ['type' => Types::TYPE_TAR]; - $this->urlFilter->expects($this->never())->method('filter'); - $this->assertSame(Types::TYPE_TAR, $this->filter->filter($extraFile)); - $this->assertSame(Types::TYPE_TAR, $this->filter->filter([])); - } - - protected function createFilter(): FilterInterface - { - return new TypeFilter($this->name, $this->parent, $this->urlFilter); - } -} diff --git a/tests/Unit/Filter/UrlFilterTest.php b/tests/Unit/Filter/UrlFilterTest.php deleted file mode 100644 index 3fcb6bc..0000000 --- a/tests/Unit/Filter/UrlFilterTest.php +++ /dev/null @@ -1,100 +0,0 @@ -variablesFilter = $this->createMock(VariablesFilter::class); - parent::setUp(); - } - - public function testNotSet(): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->variablesFilter->expects($this->never())->method('filter'); - $this->expectUnexpectedValueException('url', 'is required'); - $this->filter->filter([]); - } - - public function getInvalidUrlTypeTests(): array - { - return [ - [true, 'bool'], - [false, 'bool'], - [123, 'int'], - [12.3, 'float'], - [['key' => 'value'], 'array'], - [(object) ['key' => 'value'], 'stdClass'], - ]; - } - - /** - * @dataProvider getInvalidUrlTypeTests - */ - public function testInvalidUrlType(mixed $invalidUrlType, string $type): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->variablesFilter->expects($this->never())->method('filter'); - $this->expectUnexpectedValueException('url', sprintf('must be string, "%s" given', $type)); - $this->filter->filter([ - 'url' => $invalidUrlType, - ]); - } - - public function getInvalidUrlTests(): array - { - return [ - [''], - ['C:\Programs\PHP\php.ini'], - ['/var/www/project/uploads'], - ]; - } - - /** - * @dataProvider getInvalidUrlTests - */ - public function testInvalidUrl(string $invalidUrl): void - { - $extraFile = [ - 'url' => $invalidUrl, - ]; - $this->variablesFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn([]); - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->expectUnexpectedValueException('url', 'is invalid url'); - $this->filter->filter($extraFile); - } - - public function getFilterUrlTests(): array - { - return [ - ['http://example/file.rar', [], 'http://example/file.rar'], - ['http://example/file-{$version}.rar', ['{$version}' => '1.2.3'], 'http://example/file-1.2.3.rar'], - ]; - } - - /** - * @dataProvider getFilterUrlTests - */ - public function testFilterUrl(string $url, array $variables, string $expectedUrl): void - { - $extraFile = ['variables' => $variables, 'url' => $url]; - $this->variablesFilter->expects($this->once())->method('filter')->with($extraFile)->willReturn($variables); - $this->parent->expects($this->never())->method('getName'); - $this->assertSame($expectedUrl, $this->filter->filter($extraFile)); - $this->assertSame($expectedUrl, $this->filter->filter([])); - } - - protected function createFilter(): FilterInterface - { - return new UrlFilter($this->name, $this->parent, $this->variablesFilter); - } -} diff --git a/tests/Unit/Filter/VariablesFilterTest.php b/tests/Unit/Filter/VariablesFilterTest.php deleted file mode 100644 index db57141..0000000 --- a/tests/Unit/Filter/VariablesFilterTest.php +++ /dev/null @@ -1,153 +0,0 @@ - []]], - ]; - } - - /** - * @dataProvider getEmptyVariablesTests - */ - public function testEmptyVariables(array $extraFile): void - { - $this->assertSame([ - '{$id}' => $this->name, - '{$version}' => '', - ], $this->filter->filter($extraFile)); - } - - public function getInvalidVariablesTests(): array - { - return [ - [true, 'bool'], - [false, 'bool'], - [123, 'int'], - [12.3, 'float'], - ['test', 'string'], - [(object) ['key' => 'value'], 'stdClass'], - ]; - } - - /** - * @dataProvider getInvalidVariablesTests - */ - public function testInvalidVariables(mixed $invalidVariables, string $type): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->expectUnexpectedValueException('variables', sprintf('must be array, "%s" given', $type)); - $this->filter->filter([ - 'variables' => $invalidVariables, - ]); - } - - public function getInvalidVariableKeyTests(): array - { - return [ - ['baz'], - ['$baz'], - ['{baz}'], - ['${baz}'], - ['{$baz'], - ['$baz}'], - ]; - } - - /** - * @dataProvider getInvalidVariableKeyTests - */ - public function testInvalidVariableKey(string $invalidVariableKey): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->expectUnexpectedValueException('variables', sprintf('is invalid: Variable key "%s" should be this format "{$variable-name}"', $invalidVariableKey)); - $this->filter->filter([ - 'variables' => [ - $invalidVariableKey => '"baz"', - ], - ]); - } - - public function getInvalidVariableValueTests(): array - { - return [ - ["{ foo: 'bar' }", 'stdClass'], - ["['foo', 'baz']", 'array'], - ['true', 'bool'], - ['false', 'bool'], - ['null', 'null'], - ['123', 'int'], - ['1.92', 'float'], - ['1e-2', 'float'], - ]; - } - - /** - * @dataProvider getInvalidVariableValueTests - */ - public function testInvalidVariableValue(string $invalidVariableValue, string $type): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->expectUnexpectedValueException('variables', sprintf('is invalid: Expression "%s" should be evaluated to string, "%s" given', $invalidVariableValue, $type)); - $this->filter->filter([ - 'variables' => [ - '{$baz}' => $invalidVariableValue, - ], - ]); - } - - public function getInvalidVariableExpressionSyntaxTests(): array - { - return [ - ['in_array(1, range(1, 10)', 'expected closing `)`'], - ["'foo' in ['foo', 'baz']", 'unexpected end of string'], - ["invalid('test')", 'var `invalid` not defined'], - ["PHP_OS('test')", '`PHP_OS` is not callable'], - ]; - } - - /** - * @dataProvider getInvalidVariableExpressionSyntaxTests - */ - public function testInvalidVariableExpressionSyntax(string $expression, string $reason): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->expectUnexpectedValueException('variables', sprintf('is invalid. There is an error while evaluating expression "%s": %s', $expression, $reason)); - $this->filter->filter([ - 'variables' => [ - '{$baz}' => $expression, - ], - ]); - } - - public function testFilterVariables(): void - { - $expectedVariables = [ - '{$id}' => $this->name, - '{$version}' => '1.2.3', - '{$foo}' => 'foo', - '{$baz}' => 'baz3', - ]; - $this->assertEquals($expectedVariables, $this->filter->filter([ - 'variables' => [ - '{$foo}' => '"foo"', - '{$baz}' => '"baz"~1+2', - ], - 'version' => '1.2.3', - ])); - $this->assertSame($expectedVariables, $this->filter->filter([])); - } - - protected function createFilter(): FilterInterface - { - return new VariablesFilter($this->name, $this->parent); - } -} diff --git a/tests/Unit/Filter/VersionFilterTest.php b/tests/Unit/Filter/VersionFilterTest.php deleted file mode 100644 index f4b657c..0000000 --- a/tests/Unit/Filter/VersionFilterTest.php +++ /dev/null @@ -1,88 +0,0 @@ -versionParser = $this->createMock(VersionParser::class); - parent::setUp(); - } - - public function getInvalidVersionTests(): array - { - return [ - [true, 'bool'], - [false, 'bool'], - [123, 'int'], - [12.3, 'float'], - [['key' => 'value'], 'array'], - [(object) ['key' => 'value'], 'stdClass'], - ]; - } - - /** - * @dataProvider getInvalidVersionTests - */ - public function testInvalidVersion(mixed $invalidVersion, string $type): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $this->versionParser->expects($this->never())->method('normalize'); - $this->expectUnexpectedValueException('version', sprintf('must be string, "%s" given', $type)); - $this->filter->filter([ - 'version' => $invalidVersion, - ]); - } - - public function testVersionFromParentPackage(): void - { - $this->parent->expects($this->once())->method('getVersion')->willReturn($this->version); - $this->parent->expects($this->once())->method('getPrettyVersion')->willReturn($this->prettyVersion); - $this->versionParser->expects($this->never())->method('normalize'); - $this->assertVersion($this->filter->filter([]), $this->version, $this->prettyVersion); - $this->assertVersion($this->filter->filter(['cached']), $this->version, $this->prettyVersion); - } - - public function testVersionFromParentRootPackage(): void - { - $parent = new RootPackage('vendor/project-name', '1.0.0', 'v1.0.0'); - $filter = new VersionFilter($this->name, $parent, $this->versionParser); - $this->parent->expects($this->never())->method('getVersion'); - $this->parent->expects($this->never())->method('getPrettyVersion'); - $this->versionParser->expects($this->once())->method('normalize')->with('dev-master')->willReturn('9999999-dev'); - $this->assertVersion($filter->filter([]), '9999999-dev', 'dev-master'); - $this->assertVersion($filter->filter(['cached']), '9999999-dev', 'dev-master'); - } - - public function testCustomVersion(): void - { - $this->parent->expects($this->never())->method('getVersion'); - $this->parent->expects($this->never())->method('getPrettyVersion'); - $this->versionParser->expects($this->once())->method('normalize')->with('dev-master')->willReturn('9999999-dev'); - $this->assertVersion($this->filter->filter(['version' => '1.2.3']), '9999999-dev', '1.2.3'); - $this->assertVersion($this->filter->filter([]), '9999999-dev', '1.2.3'); - } - - protected function createFilter(): FilterInterface - { - return new VersionFilter($this->name, $this->parent, $this->versionParser); - } - - private function assertVersion(Version $version, string $expectedVersion, string $expectedPrettyVersion): void - { - $this->assertSame($expectedVersion, $version->version); - $this->assertSame($expectedPrettyVersion, $version->prettyVersion); - } -} diff --git a/tests/Unit/GlobCleanerTest.php b/tests/Unit/GlobCleanerTest.php deleted file mode 100644 index acc7a45..0000000 --- a/tests/Unit/GlobCleanerTest.php +++ /dev/null @@ -1,110 +0,0 @@ - self::DIR, - '/dir1/file1.txt' => self::FILE, - '/dir1/file2.csv' => self::FILE, - '/dir1/file3.json' => self::FILE, - '/dir1/dir11' => self::DIR, - '/dir1/dir11/file1.doc' => self::FILE, - '/dir1/dir11/file2.xls' => self::FILE, - '/dir1/dir11/file3.ppt' => self::FILE, - '/dir1/dir12' => self::DIR, - '/dir1/dir12/file1.php' => self::FILE, - '/dir2' => self::DIR, - '/dir2/dir21' => self::DIR, - '/dir2/dir21/file1.png' => self::FILE, - '/dir2/dir21/file2.jpg' => self::FILE, - '/dir2/dir22' => self::DIR, - '/dir2/dir22/.composer-downloads' => self::DIR, - '/dir2/dir22/.composer-downloads/file1.json' => self::FILE, - '/dir3' => self::DIR, - '/file1.exe' => self::FILE, - '/file2.bat' => self::FILE, - ]; - - private GlobCleaner $cleaner; - private ?FileSystem $fs = null; - - protected function setUp(): void - { - $this->cleaner = new GlobCleaner(); - $this->fs = new FileSystem(); // Keep virtual file system alive during test - $this->createFilesAndDirectories(); - } - - protected function tearDown(): void - { - $this->fs = null; - } - - public function testEmptyIgnore(): void - { - $this->cleaner->clean($this->fs->path('/dir1'), []); - $this->assertRemaining(array_keys(self::FILES_AND_DIRECTORIES)); - } - - public function testNotEmptyIgnores(): void - { - $this->cleaner->clean($this->fs->path('/dir1'), [ - 'file*', - '!/dir11/file2.xls', - '/dir12', - ]); - $this->cleaner->clean($this->fs->path('/dir2'), [ - '/dir21', - '/dir22', - '/dir22/.composer-downloads', - '/dir22/.composer-downloads/*', - '/dir22/.composer-downloads/file1.json', - ]); - $this->cleaner->clean($this->fs->path('/dir3'), [ - '*', - ]); - $this->assertRemaining([ - '/dir1', - '/dir1/dir11', - '/dir1/dir11/file2.xls', - '/dir2', - '/dir2/dir22', - '/dir2/dir22/.composer-downloads', - '/dir2/dir22/.composer-downloads/file1.json', - '/dir3', - '/file1.exe', - '/file2.bat', - ]); - } - - private function createFilesAndDirectories(): void - { - foreach (self::FILES_AND_DIRECTORIES as $path => $type) { - if (self::DIR === $type) { - mkdir($this->fs->path($path)); - } else { - file_put_contents($this->fs->path($path), md5(rand())); - } - } - } - - private function assertRemaining(array $remainingFilesAndDirs): void - { - foreach (self::FILES_AND_DIRECTORIES as $path => $type) { - $remain = \in_array($path, $remainingFilesAndDirs); - if (self::DIR === $type) { - $this->assertSame(is_dir($this->fs->path($path)), $remain, sprintf("Directory '%s' is expected to be %s", $path, $remain ? 'remaining' : 'removed')); - } else { - $this->assertSame(is_file($this->fs->path($path)), $remain, sprintf("File '%s' is expected to be %s", $path, $remain ? 'remaining' : 'removed')); - } - } - } -} diff --git a/tests/Unit/Handler/ArchiveHandlerTestCase.php b/tests/Unit/Handler/ArchiveHandlerTestCase.php deleted file mode 100644 index 05890c1..0000000 --- a/tests/Unit/Handler/ArchiveHandlerTestCase.php +++ /dev/null @@ -1,89 +0,0 @@ -cleaner = $this->createMock(GlobCleaner::class); - parent::setUp(); - $this->extraFile += [ - 'ignore' => $this->ignore, - ]; - } - - protected function getHandlerExtraArguments(): array - { - return [$this->cleaner]; - } - - protected function getTrackingFile(): string - { - return $this->targetPath.\DIRECTORY_SEPARATOR.'.composer-downloads'.\DIRECTORY_SEPARATOR.'sub-package-name-4fcb9a7a2ac376c89d1d147894dca87b.json'; - } - - protected function getExecutableType(): string - { - return 'array'; - } - - protected function getTrackingData(): array - { - return [ - 'ignore' => $this->ignore, - 'name' => "{$this->parentName}:{$this->id}", - 'url' => $this->url, - 'checksum' => $this->getChecksum(), - ]; - } - - /** - * @testWith [true, true] - * [true, false] - * [false, true] - */ - public function testInstall(bool $hasHash, bool $isValid): void - { - $this->assertDownload(); - $this->assertValidateDownloadedFile($hasHash, $isValid); - $this->assertExtract($isValid); - $this->assertBinariesInstaller($isValid); - $this->assertClean($isValid); - $this->assertRemoveDownloadedFile($isValid); - $this->expectInvalidDownloadedFileException($isValid); - $this->handler->install($this->composer, $this->io); - } - - private function assertExtract(bool $isValid): void - { - if ($isValid) { - $this->downloadManager - ->expects($this->once()) - ->method('install') - ->with($this->subpackage, $this->targetPath) - ->willReturn($this->installPromise); - $this->loop - ->expects($this->at(1)) - ->method('wait') - ->with([$this->installPromise]); - } else { - $this->downloadManager - ->expects($this->never()) - ->method('install'); - } - } - - private function assertClean(bool $isValid): void - { - $this->cleaner - ->expects($this->exactly($isValid)) - ->method('clean') - ->with($this->targetPath, $this->ignore); - } -} diff --git a/tests/Unit/Handler/BaseHandlerTestCase.php b/tests/Unit/Handler/BaseHandlerTestCase.php deleted file mode 100644 index 1470ba2..0000000 --- a/tests/Unit/Handler/BaseHandlerTestCase.php +++ /dev/null @@ -1,183 +0,0 @@ -fs = new VirtualFileSystem(); // Keep virtual file system alive during test - $this->composer = $this->createMock(Composer::class); - $this->io = $this->createMock(IOInterface::class); - $this->downloadManager = $this->createMock(DownloadManager::class); - $this->binariesInstaller = $this->createMock(BinariesInstaller::class); - $this->downloadPromise = $this->createMock(PromiseInterface::class); - $this->installPromise = $this->createMock(PromiseInterface::class); - $this->loop = $this->createMock(Loop::class); - $this->filesystem = $this->createMock(Filesystem::class); - $this->extraFile = [ - 'id' => $this->id, - 'url' => $this->url, - 'path' => $this->path, - ]; - $this->targetPath = $this->parentPath.\DIRECTORY_SEPARATOR.$this->path; - $this->subpackage = new Subpackage( - new Package($this->parentName, '1.0.0', 'v1.0.0'), - $this->parentPath, - $this->id, - $this->getSubpackageType(), - ['file1', 'dir/file2'], - $this->ignore, - $this->url, - $this->path, - new Version('1.2.3.0', 'v1.2.3'), - ); - $this->handler = $this->createHandler(); - } - - protected function tearDown(): void - { - $this->fs = null; - } - - public function testGetSubpackage(): void - { - $this->assertSame($this->subpackage, $this->handler->getSubpackage()); - } - - public function testGetTrackingData(): void - { - $this->assertSame($this->getTrackingData(), $this->handler->getTrackingData()); - } - - public function testGetChecksum(): void - { - $this->assertSame($this->getChecksum(), $this->handler->getChecksum()); - } - - public function testGetTrackingFile(): void - { - $this->assertSame($this->getTrackingFile(), $this->handler->getTrackingFile()); - } - - protected function assertBinariesInstaller(bool $isValid): void - { - $this->binariesInstaller - ->expects($this->exactly($isValid)) - ->method('install') - ->with($this->isInstanceOf(Subpackage::class), $this->io); - } - - protected function assertDownload(): void - { - $this->composer->expects($this->any())->method('getDownloadManager')->willReturn($this->downloadManager); - $this->downloadPromise - ->expects($this->once()) - ->method('then') - ->willReturnCallback(fn (callable $callback) => $callback($this->getTmpFilePath())); - $this->downloadManager - ->expects($this->once()) - ->method('download') - ->with($this->subpackage, \dirname($this->targetPath)) - ->willReturn($this->downloadPromise); - $this->loop - ->expects($this->at(0)) - ->method('wait') - ->with([$this->downloadPromise]); - $this->composer->expects($this->any())->method('getLoop')->willReturn($this->loop); - } - - protected function assertValidateDownloadedFile(bool $hasHash, bool $isValid): void - { - $this->fs->createDirectory(\dirname($this->tmpFile), true); - $this->fs->createFile($this->tmpFile, $content = 'hello world'); - if ($hasHash) { - $this->subpackage->setHash(new Hash('md5', $isValid ? md5($content) : 'not valid')); - } else { - $this->subpackage->setHash(null); - } - } - - protected function assertRemoveDownloadedFile(bool $isValid): void - { - $this->filesystem - ->expects($this->exactly(!$isValid)) - ->method('remove') - ->with($this->getTmpFilePath()); - } - - protected function expectInvalidDownloadedFileException(bool $isValid): void - { - if (!$isValid) { - $this->expectException(InvalidDownloadedFileException::class); - $this->expectExceptionMessage(sprintf('Extra file "%s" does not match hash value defined in "%s".', $this->url, $this->id)); - } - } - - abstract protected function getHandlerClass(): string; - - protected function getHandlerExtraArguments(): array - { - return []; - } - - abstract protected function getTrackingFile(): string; - - abstract protected function getSubpackageType(): string; - - abstract protected function getChecksum(): string; - - abstract protected function getExecutableType(): string; - - abstract protected function getTrackingData(): array; - - protected function createHandler(): HandlerInterface - { - $class = $this->getHandlerClass(); - - return new $class($this->subpackage, $this->binariesInstaller, $this->filesystem, ...$this->getHandlerExtraArguments()); - } - - protected function getTmpFilePath(): string - { - return $this->fs->path($this->tmpFile); - } -} diff --git a/tests/Unit/Handler/FileHandlerTest.php b/tests/Unit/Handler/FileHandlerTest.php deleted file mode 100644 index 7ee3cb6..0000000 --- a/tests/Unit/Handler/FileHandlerTest.php +++ /dev/null @@ -1,66 +0,0 @@ -targetPath).\DIRECTORY_SEPARATOR.'.composer-downloads'.\DIRECTORY_SEPARATOR.'sub-package-name-4fcb9a7a2ac376c89d1d147894dca87b.json'; - } - - protected function getHandlerClass(): string - { - return FileHandler::class; - } - - protected function getSubpackageType(): string - { - return 'file'; - } - - protected function getChecksum(): string - { - return 'eb2ddda74e9049129d10e5b75520a374820a3826ca86a99f72a300cfe57320cc'; - } - - protected function getExecutableType(): string - { - return 'boolean'; - } - - protected function getTrackingData(): array - { - return [ - 'name' => "{$this->parentName}:{$this->id}", - 'url' => $this->url, - 'checksum' => $this->getChecksum(), - ]; - } - - /** - * @testWith [true, true] - * [true, false] - * [false, true] - */ - public function testInstall(bool $hasHash, bool $isValid): void - { - $this->assertDownload(); - $this->assertValidateDownloadedFile($hasHash, $isValid); - $this->assertBinariesInstaller($isValid); - $this->assertMoveDownloadedFile($isValid); - $this->assertRemoveDownloadedFile($isValid); - $this->expectInvalidDownloadedFileException($isValid); - $this->handler->install($this->composer, $this->io); - } - - private function assertMoveDownloadedFile(bool $isValid): void - { - $this->filesystem - ->expects($this->exactly($isValid)) - ->method('rename') - ->with($this->getTmpFilePath(), $this->targetPath); - } -} diff --git a/tests/Unit/Handler/GzipHandlerTest.php b/tests/Unit/Handler/GzipHandlerTest.php deleted file mode 100644 index 5216ff5..0000000 --- a/tests/Unit/Handler/GzipHandlerTest.php +++ /dev/null @@ -1,90 +0,0 @@ -assertDownload(); - $this->assertValidateDownloadedFile($hasHash, $isValid); - $this->assertExtract($isValid); - $this->assertBinariesInstaller($isValid); - $this->assertMoveTempFile($isValid); - $this->assertRemoveFile($isValid); - $this->expectInvalidDownloadedFileException($isValid); - $this->handler->install($this->composer, $this->io); - } - - private function assertRemoveFile(bool $isValid): void - { - if ($isValid) { - $this->filesystem - ->expects($this->once()) - ->method('remove') - ->with($this->isTempDir()); - } else { - $this->assertRemoveDownloadedFile(false); - } - } - - private function assertExtract(bool $isValid): void - { - if ($isValid) { - $this->downloadManager - ->expects($this->once()) - ->method('install') - ->with($this->subpackage, $this->isTempDir()) - ->willReturn($this->installPromise); - $this->loop - ->expects($this->at(1)) - ->method('wait') - ->with([$this->installPromise]); - } else { - $this->downloadManager - ->expects($this->never()) - ->method('install'); - } - } - - private function assertMoveTempFile(bool $isValid): void - { - $this->filesystem - ->expects($this->exactly($isValid)) - ->method('rename') - ->with($this->isTempDir(), $this->targetPath); - } - - private function isTempDir(): Callback - { - return $this->callback(function (string $dir): bool { - $this->assertStringContainsString(FileHandler::TMP_PREFIX, $dir); - - return true; - }); - } -} diff --git a/tests/Unit/Handler/PharHandlerTest.php b/tests/Unit/Handler/PharHandlerTest.php deleted file mode 100644 index 5256cf0..0000000 --- a/tests/Unit/Handler/PharHandlerTest.php +++ /dev/null @@ -1,23 +0,0 @@ - ['file1', 'file2', 'file3']]; - private array $subpackages; - - protected function setUp(): void - { - $this->factory = $this->createMock(SubpackageFactory::class); - $this->subpackageInstaller = $this->createMock(SubpackageInstaller::class); - $this->installer = new PackageInstaller($this->factory, $this->subpackageInstaller); - $this->composer = $this->createMock(Composer::class); - $this->io = $this->createMock(IOInterface::class); - $this->package = $this->createMock(PackageInterface::class); - $this->rootPackage = $this->createMock(RootPackageInterface::class); - $this->installationManager = $this->createMock(InstallationManager::class); - $this->subpackages = [ - $this->createMock(Subpackage::class), - $this->createMock(Subpackage::class), - $this->createMock(Subpackage::class), - ]; - } - - /** - * @testWith [[]] - * [{"key": "value"}] - * [{"downloads": []}] - */ - public function testInstallPackageWithoutExtraFiles(array $extra): void - { - $this->composer->expects($this->never())->method('getInstallationManager'); - $this->package->expects($this->once())->method('getExtra')->willReturn($extra); - $this->installer->install($this->package, $this->composer, $this->io); - } - - public function testInstallRootPackage(): void - { - $this->rootPackage->expects($this->once())->method('getExtra')->willReturn($this->extra); - $this->rootPackage->expects($this->once())->method('getName')->willReturn('root/package-name'); - $this->composer->expects($this->never())->method('getInstallationManager'); - $this->installationManager->expects($this->never())->method('getInstallPath'); - $this->installationManager->expects($this->never())->method('ensureBinariesPresence'); - $this->factory - ->expects($this->once()) - ->method('create') - ->with($this->rootPackage, getcwd()) - ->willReturn($this->subpackages); - $this->subpackageInstaller - ->expects($this->exactly(\count($this->subpackages))) - ->method('isInstalled') - ->with($this->io) - ->willReturnOnConsecutiveCalls(true, false, false); - $this->subpackageInstaller - ->expects($this->exactly(2)) - ->method('install') - ->with($this->composer, $this->io); - $this->io->expects($this->once())->method('write')->with('Download extra files for root/package-name'); - $this->installer->install($this->rootPackage, $this->composer, $this->io); - } - - public function testInstallNormalPackage(): void - { - $basePath = '/path/to/install/path'; - $this->package->expects($this->once())->method('getExtra')->willReturn($this->extra); - $this->package->expects($this->once())->method('getName')->willReturn('normal/package-name'); - $this->composer->expects($this->exactly(2))->method('getInstallationManager')->willReturn($this->installationManager); - $this->installationManager->expects($this->once())->method('getInstallPath')->with($this->package)->willReturn($basePath); - $this->installationManager->expects($this->once())->method('ensureBinariesPresence')->with($this->package); - $this->factory - ->expects($this->once()) - ->method('create') - ->with($this->package, $basePath) - ->willReturn($this->subpackages); - $this->subpackageInstaller - ->expects($this->exactly(\count($this->subpackages))) - ->method('isInstalled') - ->with($this->io) - ->willReturnOnConsecutiveCalls(true, false, false); - $this->subpackageInstaller - ->expects($this->exactly(2)) - ->method('install') - ->with($this->composer, $this->io); - $this->io->expects($this->once())->method('write')->with('Download extra files for normal/package-name'); - $this->installer->install($this->package, $this->composer, $this->io); - } - - public function testInstallRootPackageWithInvalidExtraFiles(): void - { - $this->rootPackage->expects($this->once())->method('getExtra')->willReturn($this->extra); - $this->rootPackage->expects($this->once())->method('getName')->willReturn('root/package-name'); - $this->composer->expects($this->never())->method('getInstallationManager'); - $this->installationManager->expects($this->never())->method('getInstallPath'); - $this->installationManager->expects($this->never())->method('ensureBinariesPresence'); - $this->factory - ->expects($this->once()) - ->method('create') - ->with($this->rootPackage, getcwd()) - ->willThrowException(new UnexpectedValueException('Invalid extra files')); - $this->subpackageInstaller->expects($this->never())->method('isInstalled'); - $this->subpackageInstaller->expects($this->never())->method('install'); - $this->io->expects($this->never())->method('write'); - $this->installer->install($this->rootPackage, $this->composer, $this->io); - } - - public function testInstallNormalPackageWithInvalidExtraFiles(): void - { - $basePath = '/path/to/install/path'; - $this->package->expects($this->once())->method('getExtra')->willReturn($this->extra); - $this->package->expects($this->once())->method('getName')->willReturn('normal/package-name'); - $this->composer->expects($this->exactly(2))->method('getInstallationManager')->willReturn($this->installationManager); - $this->installationManager->expects($this->once())->method('getInstallPath')->with($this->package)->willReturn($basePath); - $this->installationManager->expects($this->once())->method('ensureBinariesPresence')->with($this->package); - $this->factory - ->expects($this->once()) - ->method('create') - ->with($this->package, $basePath) - ->willThrowException(new UnexpectedValueException('Invalid extra files')); - $this->subpackageInstaller->expects($this->never())->method('isInstalled'); - $this->subpackageInstaller->expects($this->never())->method('install'); - $this->io->expects($this->never())->method('write'); - $this->installer->install($this->package, $this->composer, $this->io); - } -} diff --git a/tests/Unit/PluginTest.php b/tests/Unit/PluginTest.php deleted file mode 100644 index 51be751..0000000 --- a/tests/Unit/PluginTest.php +++ /dev/null @@ -1,129 +0,0 @@ -installer = $this->createMock(PackageInstaller::class); - $this->plugin = new Plugin($this->installer); - $this->composer = $this->createMock(Composer::class); - $this->io = $this->createMock(IOInterface::class); - } - - public function testGetSubscribedEvents(): void - { - $this->assertSame([ - PackageEvents::POST_PACKAGE_INSTALL => ['installDownloads', 10], - PackageEvents::POST_PACKAGE_UPDATE => ['updateDownloads', 10], - ScriptEvents::POST_INSTALL_CMD => ['installDownloadsRoot', 10], - ScriptEvents::POST_UPDATE_CMD => ['installDownloadsRoot', 10], - ], Plugin::getSubscribedEvents()); - } - - public function testActivate(): void - { - $this->expectNotToPerformAssertions(); - $this->plugin->activate($this->composer, $this->io); - } - - public function testDeactivate(): void - { - $this->expectNotToPerformAssertions(); - $this->plugin->deactivate($this->composer, $this->io); - } - - public function testUninstall(): void - { - $this->expectNotToPerformAssertions(); - $this->plugin->uninstall($this->composer, $this->io); - } - - public function testInstallDownloadsRoot(): void - { - $rootPackage = $this->createMock(RootPackageInterface::class); - $this->composer->expects($this->once())->method('getPackage')->willReturn($rootPackage); - $repositoryManager = $this->createMock(RepositoryManager::class); - $this->composer->expects($this->once())->method('getRepositoryManager')->willReturn($repositoryManager); - $localRepository = $this->createMock(InstalledRepositoryInterface::class); - $repositoryManager->expects($this->once())->method('getLocalRepository')->willReturn($localRepository); - $packages = [ - $this->createMock(PackageInterface::class), - $this->createMock(PackageInterface::class), - $this->createMock(PackageInterface::class), - ]; - $localRepository->expects($this->once())->method('getCanonicalPackages')->willReturn($packages); - $this->installer - ->expects($this->exactly(\count($packages) + 1)) - ->method('install') - ->withConsecutive( - [$rootPackage, $this->composer, $this->io], - ...array_map(fn (PackageInterface $package) => [$package, $this->composer, $this->io], $packages), - ); - $event = new Event('name', $this->composer, $this->io); - $this->plugin->installDownloadsRoot($event); - } - - public function testInstallDownloads(): void - { - $package = $this->createMock(PackageInterface::class); - $this->installer - ->expects($this->once()) - ->method('install') - ->with($package, $this->composer, $this->io); - $event = new PackageEvent( - 'name', - $this->composer, - $this->io, - false, - $this->createMock(RepositoryInterface::class), - [], - new InstallOperation($package) - ); - $this->plugin->installDownloads($event); - } - - public function testUpdateDownloads(): void - { - $initial = $this->createMock(PackageInterface::class); - $target = $this->createMock(PackageInterface::class); - $this->installer - ->expects($this->once()) - ->method('install') - ->with($target, $this->composer, $this->io); - $event = new PackageEvent( - 'name', - $this->composer, - $this->io, - false, - $this->createMock(RepositoryInterface::class), - [], - new UpdateOperation($initial, $target) - ); - $this->plugin->updateDownloads($event); - } -} diff --git a/tests/Unit/SubpackageFactoryTest.php b/tests/Unit/SubpackageFactoryTest.php deleted file mode 100644 index 1dfc084..0000000 --- a/tests/Unit/SubpackageFactoryTest.php +++ /dev/null @@ -1,155 +0,0 @@ - [ - '*' => [ - 'path' => 'common/path/to/dir', - ], - 'file1' => [ - 'url' => 'http://example.com/file1.zip', - 'version' => '1.2.3', - ], - 'file2' => [ - 'url' => 'http://example.com/file2.tar.gz', - 'executable' => [ - 'file1', - 'path/to/file2', - ], - ], - 'file3' => [ - 'type' => 'xz', - 'url' => 'http://example.com/{$id}.xz', - 'ignore' => [ - 'dir/*', - '!dir/file1', - ], - 'path' => 'path/to/dir', - ], - 'file4' => [ - 'type' => 'file', - 'url' => 'http://example.com/file.ext', - 'hash' => [ - 'algo' => 'md5', - 'value' => 'text', - ], - ], - ], - ]; - private string $parentPath = '/path/to/vendor/package-name'; - - protected function setUp(): void - { - $this->parent = new Package('vendor/package-name', '1.0.0', 'v1.0.0'); - $this->parent->setExtra($this->extra); - $this->factory = new SubpackageFactory(); - } - - public function testCreate(): void - { - $subpackages = $this->factory->create( - $this->parent, - $this->parentPath - ); - $this->assertCount(4, $subpackages); - $this->assertSubpackage( - $subpackages[0], - 'file1', - 'zip', - 'dev-master', - '1.2.3', - 'http://example.com/file1.zip', - 'zip', - 'common/path/to/dir', - [], - [] - ); - $this->assertSubpackage( - $subpackages[1], - 'file2', - 'tar', - '1.0.0', - 'v1.0.0', - 'http://example.com/file2.tar.gz', - 'tar', - 'common/path/to/dir', - [ - 'file1', - 'path/to/file2', - ], - [] - ); - $this->assertSubpackage( - $subpackages[2], - 'file3', - 'xz', - '1.0.0', - 'v1.0.0', - 'http://example.com/file3.xz', - 'xz', - 'path/to/dir', - [], - [ - 'dir/*', - '!dir/file1', - ] - ); - $this->assertSubpackage( - $subpackages[3], - 'file4', - 'file', - '1.0.0', - 'v1.0.0', - 'http://example.com/file.ext', - 'file', - 'common/path/to/dir', - [], - [], - new Hash('md5', 'text') - ); - } - - private function assertSubpackage( - Subpackage $subpackage, - string $subpackageName, - string $subpackageType, - string $version, - string $prettyVersion, - string $url, - string $distType, - string $path, - array $executable, - array $ignore, - ?Hash $hash = null - ): void { - $this->assertSame(sprintf('vendor/package-name:%s', $subpackageName), $subpackage->getName()); - $this->assertSame($version, $subpackage->getVersion()); - $this->assertSame($prettyVersion, $subpackage->getPrettyVersion()); - $this->assertSame($url, $subpackage->getDistUrl()); - $this->assertSame($distType, $subpackage->getDistType()); - $this->assertSame($path, $subpackage->getTargetDir()); - $this->assertSame('dist', $subpackage->getInstallationSource()); - $this->assertSame($subpackageName, $subpackage->getSubpackageName()); - $this->assertSame($executable, $subpackage->getExecutable()); - $this->assertSame($ignore, $subpackage->getIgnore()); - $this->assertSame($subpackageType, $subpackage->getSubpackageType()); - $this->assertSame($this->parentPath.\DIRECTORY_SEPARATOR.$path, $subpackage->getTargetPath()); - if ($hash) { - $this->assertEquals($hash, $subpackage->getHash()); - } else { - $this->assertNull($subpackage->getHash()); - } - } -} diff --git a/tests/Unit/SubpackageInstallerTest.php b/tests/Unit/SubpackageInstallerTest.php deleted file mode 100644 index 102a1f2..0000000 --- a/tests/Unit/SubpackageInstallerTest.php +++ /dev/null @@ -1,132 +0,0 @@ -fs = new FileSystem(); // Keep virtual file system alive during test - $this->composer = $this->createMock(Composer::class); - $this->io = $this->createMock(IOInterface::class); - $this->subpackage = $this->createMock(Subpackage::class); - $this->handler = $this->createMock(HandlerInterface::class); - $this->installer = new SubpackageInstaller($this->subpackage, $this->handler); - } - - protected function tearDown(): void - { - $this->fs = null; - } - - public function testNotInstalled(): void - { - $this->subpackage->expects($this->once())->method('getTargetPath')->willReturn($this->targetPath); - $this->handler->expects($this->once())->method('getTrackingFile')->willReturn($this->trackingFile); - $this->subpackage->expects($this->never())->method('getName'); - $this->io->expects($this->never())->method('write'); - $this->assertFalse($this->installer->isInstalled($this->io)); - } - - public function testInstalledAndOverrode(): void - { - $this->subpackage->expects($this->once())->method('getTargetPath')->willReturn($this->fs->path($this->targetPath)); - $this->handler->expects($this->once())->method('getTrackingFile')->willReturn($this->fs->path($this->trackingFile)); - $this->fs->createDirectory(\dirname($this->targetPath), true); - $this->fs->createFile($this->targetPath, 'test'); - $this->subpackage->expects($this->once())->method('getName')->willReturn($this->subpackageName); - $this->subpackage->expects($this->once())->method('getTargetDir')->willReturn('files'); - $this->io->expects($this->once())->method('write')->with("Extra file {$this->subpackageName} has been locally overriden in files. To reset it, delete and reinstall.", true); - $this->assertTrue($this->installer->isInstalled($this->io)); - } - - /** - * @testWith [[]] - * [{"key": "value"}] - * [{"checksum": "not-match"}] - */ - public function testDifferentChecksum(array $meta): void - { - $this->subpackage->expects($this->once())->method('getTargetPath')->willReturn($this->fs->path($this->targetPath)); - $this->handler->expects($this->once())->method('getTrackingFile')->willReturn($this->fs->path($this->trackingFile)); - $this->fs->createDirectory(\dirname($this->targetPath), true); - $this->fs->createFile($this->targetPath, 'test'); - $this->fs->createDirectory(\dirname($this->trackingFile), true); - $this->fs->createFile($this->trackingFile, json_encode($meta)); - $this->subpackage->expects($this->never())->method('getName'); - $this->io->expects($this->never())->method('write'); - $this->assertFalse($this->installer->isInstalled($this->io)); - } - - public function testInstalled(): void - { - $this->subpackage->expects($this->once())->method('getTargetPath')->willReturn($this->fs->path($this->targetPath)); - $this->handler->expects($this->once())->method('getTrackingFile')->willReturn($this->fs->path($this->trackingFile)); - $this->handler->expects($this->once())->method('getChecksum')->willReturn('match'); - $this->fs->createDirectory(\dirname($this->targetPath), true); - $this->fs->createFile($this->targetPath, 'test'); - $this->fs->createDirectory(\dirname($this->trackingFile), true); - $this->fs->createFile($this->trackingFile, json_encode(['checksum' => 'match'])); - $this->subpackage->expects($this->once())->method('getName')->willReturn($this->subpackageName); - $this->io->expects($this->once())->method('write')->with("Skip extra file {$this->subpackageName}", true, IOInterface::VERY_VERBOSE); - $this->assertTrue($this->installer->isInstalled($this->io)); - } - - public function testInstall(): void - { - $this->handler->expects($this->once())->method('getTrackingFile')->willReturn($this->fs->path($this->trackingFile)); - $this->handler->expects($this->once())->method('getTrackingData')->willReturn([ - 'key' => 'value', - ]); - $this->handler - ->expects($this->once()) - ->method('install') - ->with($this->composer, $this->io) - ->willReturnCallback(function () { - $this->fs->createDirectory(\dirname($this->targetPath), true); - $this->fs->createFile($this->targetPath, 'test'); - }); - $this->subpackage->expects($this->exactly(2))->method('getName')->willReturn($this->subpackageName); - $this->io - ->expects($this->exactly(2)) - ->method('write') - ->withConsecutive( - ["Download extra file {$this->subpackageName}", true, IOInterface::VERBOSE], - ["Create tracking file for {$this->subpackageName}", true, IOInterface::VERY_VERBOSE], - ); - $this->installer->install($this->composer, $this->io); - $this->assertFileExists($this->fs->path($this->trackingFile)); - $this->assertStringEqualsFile( - $this->fs->path($this->trackingFile), - '{'.$this->eol(). - ' "key": "value"'.$this->eol(). - '}' - ); - $this->assertFileExists($this->fs->path($this->targetPath)); - $this->assertSame('test', file_get_contents($this->fs->path($this->targetPath))); - } - - private function eol(): string - { - return "\n"; - } -} diff --git a/tests/Unit/SubpackageTest.php b/tests/Unit/SubpackageTest.php deleted file mode 100644 index 4814930..0000000 --- a/tests/Unit/SubpackageTest.php +++ /dev/null @@ -1,78 +0,0 @@ -parent = $this->createMock(PackageInterface::class); - } - - public function hashDataProvider(): array - { - return [ - [null], - [new Hash('name', 'value')], - ]; - } - - /** - * @dataProvider hashDataProvider - */ - public function testInstance(?Hash $hash): void - { - $this->parent->expects($this->once())->method('getName')->willReturn($this->parentName); - $subpackage = new Subpackage( - $this->parent, - $this->parentPath, - $this->subpackageName, - $this->subpackageType, - $this->executable, - $this->ignore, - $this->url, - $this->path, - new Version($this->version, $this->prettyVersion), - $hash - ); - $this->assertSame(sprintf('%s:%s', $this->parentName, $this->subpackageName), $subpackage->getName()); - $this->assertSame($this->version, $subpackage->getVersion()); - $this->assertSame($this->prettyVersion, $subpackage->getPrettyVersion()); - $this->assertSame($this->url, $subpackage->getDistUrl()); - $this->assertSame('file', $subpackage->getDistType()); - $this->assertSame($this->path, $subpackage->getTargetDir()); - $this->assertSame('dist', $subpackage->getInstallationSource()); - $this->assertSame($this->subpackageName, $subpackage->getSubpackageName()); - $this->assertSame($this->subpackageType, $subpackage->getSubpackageType()); - $this->assertSame($this->executable, $subpackage->getExecutable()); - $this->assertSame($this->ignore, $subpackage->getIgnore()); - $this->assertSame($this->parentPath, $subpackage->getParentPath()); - $this->assertSame($this->parentPath.\DIRECTORY_SEPARATOR.$this->path, $subpackage->getTargetPath()); - $this->assertSame($hash, $subpackage->getHash()); - } -} diff --git a/tests/Unit/TypesTest.php b/tests/Unit/TypesTest.php deleted file mode 100644 index c0710d7..0000000 --- a/tests/Unit/TypesTest.php +++ /dev/null @@ -1,61 +0,0 @@ -assertTrue(Types::isArchiveType($type)); - } - foreach (Types::FILE_TYPES as $type) { - $this->assertFalse(Types::isArchiveType($type)); - } - } - - public function testIsFileType(): void - { - foreach (Types::FILE_TYPES as $type) { - $this->assertTrue(Types::isFileType($type)); - } - foreach (Types::ARCHIVE_TYPES as $type) { - $this->assertFalse(Types::isFileType($type)); - } - } - - public function testMapExtensionToType(): void - { - foreach (Types::EXTENSION_TO_TYPE_MAP as $extension => $type) { - $this->assertSame($type, Types::mapExtensionToType($extension)); - } - $this->assertSame('file', Types::mapExtensionToType('mp3')); - } - - public function testCreateHandler(): void - { - foreach (Types::TYPE_TO_HANDLER_CLASS_MAP as $type => $class) { - $this->assertCreateHandler($class, $type); - } - $this->assertCreateHandler(FileHandler::class, 'mp4'); - } - - private function assertCreateHandler(string $class, string $type): void - { - $subpackage = $this->createMock(Subpackage::class); - $subpackage->expects($this->once())->method('getSubpackageType')->willReturn($type); - $this->assertInstanceOf($class, Types::createHandler($subpackage)); - } - - public function testMapTypeToDistType(): void - { - foreach (Types::ALL_TYPES as $type) { - $this->assertSame(Types::TYPE_PHAR === $type ? 'file' : $type, Types::mapTypeToDistType($type)); - } - } -}