From c1d81f0fb01f77af16b007b149fa8cf368bb4d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pluchino?= Date: Wed, 1 Oct 2014 21:54:16 +0200 Subject: [PATCH] Optimize import of package definitions with dependencies of root package --- FxpAssetPlugin.php | 9 + Repository/AbstractAssetVcsRepository.php | 8 + Repository/AbstractAssetsRepository.php | 9 + Repository/AssetVcsRepository.php | 52 +-- Repository/VcsPackageFilter.php | 174 ++++++++++ Tests/Repository/AssetVcsRepositoryTest.php | 58 +++- Tests/Repository/VcsPackageFilterTest.php | 337 ++++++++++++++++++++ 7 files changed, 620 insertions(+), 27 deletions(-) create mode 100644 Repository/VcsPackageFilter.php create mode 100644 Tests/Repository/VcsPackageFilterTest.php diff --git a/FxpAssetPlugin.php b/FxpAssetPlugin.php index dfe14c39..ee2399d7 100644 --- a/FxpAssetPlugin.php +++ b/FxpAssetPlugin.php @@ -23,6 +23,7 @@ use Fxp\Composer\AssetPlugin\Event\VcsRepositoryEvent; use Fxp\Composer\AssetPlugin\Installer\AssetInstaller; use Fxp\Composer\AssetPlugin\Installer\BowerInstaller; +use Fxp\Composer\AssetPlugin\Repository\VcsPackageFilter; use Fxp\Composer\AssetPlugin\Repository\Util; /** @@ -47,6 +48,11 @@ class FxpAssetPlugin implements PluginInterface, EventSubscriberInterface */ protected $pool; + /** + * @var VcsPackageFilter + */ + protected $packageFilter; + /** * {@inheritdoc} */ @@ -68,6 +74,7 @@ public static function getSubscribedEvents() public function activate(Composer $composer, IOInterface $io) { $this->composer = $composer; + $this->packageFilter = new VcsPackageFilter($composer->getPackage()); $extra = $composer->getPackage()->getExtra(); $rm = $composer->getRepositoryManager(); @@ -119,6 +126,7 @@ protected function addRegistryRepositories(RepositoryManager $rm, array $extra) foreach (Assets::getRegistries() as $assetType => $registryClass) { $config = array( 'repository-manager' => $rm, + 'vcs-package-filter' => $this->packageFilter, 'asset-options' => $this->crateAssetOptions($opts, $assetType), ); @@ -158,6 +166,7 @@ protected function addRepositories(RepositoryManager $rm, array $repositories, P $this->validateRepositories($index, $repo); $name = is_int($index) ? preg_replace('{^https?://}i', '', $repo['url']) : $index; $name = isset($repo['name']) ? $repo['name'] : $name; + $repo['vcs-package-filter'] = $this->packageFilter; Util::addRepository($rm, $this->repos, $name, $repo, $pool); } diff --git a/Repository/AbstractAssetVcsRepository.php b/Repository/AbstractAssetVcsRepository.php index a7ef42ed..d73e98d2 100644 --- a/Repository/AbstractAssetVcsRepository.php +++ b/Repository/AbstractAssetVcsRepository.php @@ -61,6 +61,11 @@ abstract class AbstractAssetVcsRepository extends VcsRepository */ protected $rootData; + /** + * @var VcsPackageFilter + */ + protected $filter; + /** * Constructor. * @@ -82,6 +87,9 @@ public function __construct(array $repoConfig, IOInterface $io, Config $config, $repoConfig['filename'] = $assetType->getFilename(); $this->assetType = $assetType; $this->dispatcher = $dispatcher; + $this->filter = isset($repoConfig['vcs-package-filter']) + ? $repoConfig['vcs-package-filter'] + : null; parent::__construct($repoConfig, $io, $config, $dispatcher, $drivers); } diff --git a/Repository/AbstractAssetsRepository.php b/Repository/AbstractAssetsRepository.php index 5c18df4c..b13946fe 100644 --- a/Repository/AbstractAssetsRepository.php +++ b/Repository/AbstractAssetsRepository.php @@ -59,6 +59,11 @@ abstract class AbstractAssetsRepository extends ComposerRepository */ protected $repositoryManager; + /** + * @var VcsPackageFilter + */ + protected $packageFilter; + /** * Constructor. * @@ -82,6 +87,9 @@ public function __construct(array $repoConfig, IOInterface $io, Config $config, $this->searchUrl = $this->getSearchUrl(); $this->hasProviders = true; $this->rm = $repoConfig['repository-manager']; + $this->packageFilter = isset($repoConfig['vcs-package-filter']) + ? $repoConfig['vcs-package-filter'] + : null; $this->repos = array(); $this->searchable = (bool) $this->getOption($repoConfig['asset-options'], 'searchable', true); $this->fallbackProviders = false; @@ -126,6 +134,7 @@ public function whatProvides(Pool $pool, $name) $cacheName = $packageName . '-' . sha1($packageName) . '-package.json'; $data = $this->fetchFile($packageUrl, $cacheName); $repo = $this->createVcsRepositoryConfig($data, Util::cleanPackageName($name)); + $repo['vcs-package-filter'] = $this->packageFilter; Util::addRepository($this->rm, $this->repos, $name, $repo, $pool); diff --git a/Repository/AssetVcsRepository.php b/Repository/AssetVcsRepository.php index bf8d4f2e..4d8dc246 100644 --- a/Repository/AssetVcsRepository.php +++ b/Repository/AssetVcsRepository.php @@ -55,36 +55,24 @@ protected function initialize() */ protected function initTags(VcsDriverInterface $driver) { - $verbose = $this->verbose; - $packageClass = 'Fxp\Composer\AssetPlugin\Package\LazyCompletePackage'; - foreach ($driver->getTags() as $tag => $identifier) { $packageName = $this->createPackageName(); // strip the release- prefix from tags if present $tag = str_replace('release-', '', $tag); + if (null !== $this->filter && $this->filter->skip($this->assetType, $packageName, $tag)) { + continue; + } + if (!$parsedTag = Validator::validateTag($tag, $this->assetType, $this->versionParser)) { - if ($verbose) { + if ($this->verbose) { $this->io->write('Skipped tag '.$tag.', invalid tag name'); } continue; } - $data = $this->createMockOfPackageConfig($packageName, $tag); - $data['version'] = $this->assetType->getVersionConverter()->convertVersion($tag); - $data['version_normalized'] = $parsedTag; - - // make sure tag packages have no -dev flag - $data['version'] = preg_replace('{[.-]?dev$}i', '', (string) $data['version']); - $data['version_normalized'] = preg_replace('{(^dev-|[.-]?dev$)}i', '', (string) $data['version_normalized']); - - $packageData = $this->preProcessAsset($data); - $package = $this->loader->load($packageData, $packageClass); - $lazyLoader = $this->createLazyLoader('tag', $identifier, $packageData, $driver); - /* @var LazyCompletePackage $package */ - $package->setLoader($lazyLoader); - $this->addPackage($package); + $this->initTag($driver, $packageName, $tag, $identifier, $parsedTag); } if (!$this->verbose) { @@ -92,6 +80,34 @@ protected function initTags(VcsDriverInterface $driver) } } + /** + * Initializes the tag. + * + * @param VcsDriverInterface $driver + * @param string $packageName + * @param string $tag + * @param string $identifier + * @param string $parsedTag + */ + protected function initTag(VcsDriverInterface $driver, $packageName, $tag, $identifier, $parsedTag) + { + $packageClass = 'Fxp\Composer\AssetPlugin\Package\LazyCompletePackage'; + $data = $this->createMockOfPackageConfig($packageName, $tag); + $data['version'] = $this->assetType->getVersionConverter()->convertVersion($tag); + $data['version_normalized'] = $parsedTag; + + // make sure tag packages have no -dev flag + $data['version'] = preg_replace('{[.-]?dev$}i', '', (string) $data['version']); + $data['version_normalized'] = preg_replace('{(^dev-|[.-]?dev$)}i', '', (string) $data['version_normalized']); + + $packageData = $this->preProcessAsset($data); + $package = $this->loader->load($packageData, $packageClass); + $lazyLoader = $this->createLazyLoader('tag', $identifier, $packageData, $driver); + /* @var LazyCompletePackage $package */ + $package->setLoader($lazyLoader); + $this->addPackage($package); + } + /** * Initializes all branches. * diff --git a/Repository/VcsPackageFilter.php b/Repository/VcsPackageFilter.php new file mode 100644 index 00000000..de1e1c89 --- /dev/null +++ b/Repository/VcsPackageFilter.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fxp\Composer\AssetPlugin\Repository; + +use Composer\Package\Link; +use Composer\Package\Package; +use Composer\Package\RootPackageInterface; +use Composer\Package\Version\VersionParser; +use Composer\Package\LinkConstraint\LinkConstraintInterface; +use Fxp\Composer\AssetPlugin\Type\AssetTypeInterface; + +/** + * Filters the asset packages imported into VCS repository to optimize + * performance when getting the informations of packages. + * + * @author François Pluchino + */ +class VcsPackageFilter +{ + /** + * @var RootPackageInterface + */ + protected $package; + + /** + * @var VersionParser + */ + protected $versionParser; + + /** + * @var array + */ + protected $requires; + + /** + * Constructor. + * + * @param RootPackageInterface $package The root package + */ + public function __construct(RootPackageInterface $package) + { + $this->package = $package; + $this->versionParser = new VersionParser(); + + $this->initialize(); + } + + /** + * Check if the version must be skipped. + * + * @param AssetTypeInterface $assetType The asset type + * @param string $name The composer package name + * @param string $version The version + * + * @return bool + */ + public function skip(AssetTypeInterface $assetType, $name, $version) + { + if (!isset($this->requires[$name])) { + return true; + } + + /* @var Link $require */ + $require = $this->requires[$name]; + $cVersion = $assetType->getVersionConverter()->convertVersion($version); + $normalizedVersion = $this->versionParser->normalize($cVersion); + + return !$this->satisfy($require, $normalizedVersion); + } + + /** + * Check if the require dependency has a satisfactory version and stability. + * + * @param Link $require The require link defined in root package. + * @param string $normalizedVersion The normalized version + * + * @return bool + */ + protected function satisfy(Link $require, $normalizedVersion) + { + return $this->satisfyVersion($require, $normalizedVersion) + && $this->satisfyStability($require, $normalizedVersion); + } + + /** + * Check if the require dependency has a satisfactory version. + * + * @param Link $require The require link defined in root package. + * @param string $normalizedVersion The normalized version + * + * @return bool + */ + protected function satisfyVersion(Link $require, $normalizedVersion) + { + $constraintSame = $this->versionParser->parseConstraints($normalizedVersion); + $sameVersion = (bool) $require->getConstraint()->matches($constraintSame); + + $normalizedVersion = $this->getVersionConstraint($normalizedVersion); + $constraint = $this->getVersionConstraint($normalizedVersion); + + return (bool) $require->getConstraint()->matches($constraint) || $sameVersion; + } + + /** + * Check if the require dependency has a satisfactory stability. + * + * @param Link $require The require link defined in root package. + * @param string $normalizedVersion The normalized version + * + * @return bool + */ + protected function satisfyStability(Link $require, $normalizedVersion) + { + $requireStability = $this->getRequireStability($require); + $stability = $this->versionParser->parseStability($normalizedVersion); + $stability = false !== strpos($normalizedVersion, '-patch') ? 'dev' : $stability; + + return Package::$stabilities[$stability] <= Package::$stabilities[$requireStability]; + } + + /** + * Get the minimum stability for the require dependency defined in root package. + * + * @param Link $require The require link defined in root package. + * + * @return string The minimum stability + */ + protected function getRequireStability(Link $require) + { + $prettyConstraint = $require->getPrettyConstraint(); + + if (false !== strpos($prettyConstraint, '@')) { + return $this->versionParser->parseStability($prettyConstraint); + } + + return $this->package->getMinimumStability(); + } + + /** + * Get the link constraint of normalized version. + * + * @param string $normalizedVersion The normalized version + * + * @return LinkConstraintInterface The constraint + */ + protected function getVersionConstraint($normalizedVersion) + { + if (preg_match('/^\d+(\.\d+)(\.\d+)(\.\d+)\-[A-Za-z0-9]+$/', $normalizedVersion)) { + $normalizedVersion = substr($normalizedVersion, 0, strpos($normalizedVersion, '-')); + } + + return $this->versionParser->parseConstraints($normalizedVersion); + } + + /** + * Initialize. + */ + protected function initialize() + { + $this->requires = array_merge( + $this->package->getRequires(), + $this->package->getDevRequires() + ); + } +} diff --git a/Tests/Repository/AssetVcsRepositoryTest.php b/Tests/Repository/AssetVcsRepositoryTest.php index 9b612e22..85ac5648 100644 --- a/Tests/Repository/AssetVcsRepositoryTest.php +++ b/Tests/Repository/AssetVcsRepositoryTest.php @@ -17,6 +17,7 @@ use Composer\Package\PackageInterface; use Composer\Repository\InvalidRepositoryException; use Fxp\Composer\AssetPlugin\Repository\AssetVcsRepository; +use Fxp\Composer\AssetPlugin\Repository\VcsPackageFilter; use Fxp\Composer\AssetPlugin\Tests\Fixtures\IO\MockIO; use Fxp\Composer\AssetPlugin\Tests\Fixtures\Repository\Vcs\MockVcsDriver; @@ -301,22 +302,61 @@ public function testWithTagsAndBranchsWithRegistryPackageName($type, $url, $clas $this->assertSame($validTraces, $this->io->getTraces()); } + /** + * @dataProvider getMockDriversWithVersions + */ + public function testWithFilterTags($type, $url, $class, $verbose) + { + $validPackageName = substr($type, 0, strpos($type, '-')) . '-asset/registry-foobar'; + $validTraces = array(''); + if ($verbose) { + $validTraces = array(); + } + + $filter = $this->getMockBuilder('Fxp\Composer\AssetPlugin\Repository\VcsPackageFilter') + ->disableOriginalConstructor() + ->getMock(); + + $filter->expects($this->any()) + ->method('skip') + ->will($this->returnValue(true)); + + /* @var VcsPackageFilter $filter */ + $this->init(true, $type, $url, $class, $verbose, null, 'registry-foobar', $filter); + + /* @var PackageInterface[] $packages */ + $packages = $this->repository->getPackages(); + $this->assertCount(3, $packages); + + foreach ($packages as $package) { + if ($package instanceof AliasPackage) { + $package = $package->getAliasOf(); + } + + $this->assertInstanceOf('Composer\Package\CompletePackage', $package); + $this->assertSame($validPackageName, $package->getName()); + } + + $this->assertSame($validTraces, $this->io->getTraces()); + } + /** * Init the test. * - * @param bool $supported - * @param string $type - * @param string $url - * @param string $class - * @param bool $verbose - * @param array|null $drivers - * @param string|null $registryName + * @param bool $supported + * @param string $type + * @param string $url + * @param string $class + * @param bool $verbose + * @param array|null $drivers + * @param string|null $registryName + * @param VcsPackageFilter|null $vcsPackageFilter */ - protected function init($supported, $type, $url, $class, $verbose = false, $drivers = null, $registryName = null) + protected function init($supported, $type, $url, $class, $verbose = false, $drivers = null, $registryName = null, VcsPackageFilter $vcsPackageFilter = null) { MockVcsDriver::$supported = $supported; $driverType = substr($type, strpos($type, '-') + 1); - $repoConfig = array('type' => $type, 'url' => $url, 'name' => $registryName); + $repoConfig = array('type' => $type, 'url' => $url, 'name' => $registryName, 'vcs-package-filter' => $vcsPackageFilter); if (null === $drivers) { $drivers = array( diff --git a/Tests/Repository/VcsPackageFilterTest.php b/Tests/Repository/VcsPackageFilterTest.php new file mode 100644 index 00000000..e916574f --- /dev/null +++ b/Tests/Repository/VcsPackageFilterTest.php @@ -0,0 +1,337 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fxp\Composer\AssetPlugin\Tests\Repository; + +use Composer\Package\RootPackageInterface; +use Composer\Package\Version\VersionParser; +use Fxp\Composer\AssetPlugin\Repository\VcsPackageFilter; +use Fxp\Composer\AssetPlugin\Type\AssetTypeInterface; + +/** + * Tests of VCS Package Filter. + * + * @author François Pluchino + */ +class VcsPackageFilterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $package; + + /** + * @var AssetTypeInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $assetType; + + /** + * @var VcsPackageFilter + */ + protected $filter; + + protected function setUp() + { + $this->package = $this->getMock('Composer\Package\RootPackageInterface'); + $this->assetType = $this->getMock('Fxp\Composer\AssetPlugin\Type\AssetTypeInterface'); + + $versionConverter = $this->getMock('Fxp\Composer\AssetPlugin\Converter\VersionConverterInterface'); + $versionConverter->expects($this->any()) + ->method('convertVersion') + ->will($this->returnCallback(function ($value) { + return $value; + })); + $this->assetType->expects($this->any()) + ->method('getVersionConverter') + ->will($this->returnValue($versionConverter)); + } + + protected function tearDown() + { + $this->package = null; + $this->assetType = null; + $this->filter = null; + } + + public function getDataProvider() + { + return array( + array('acme/foobar', 'v1.0.0', 'stable', array(), true), + + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '>=1.0'), true), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '>=1.0'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '>=1.0'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '>=1.0'), true), + + array('acme/foobar', 'v1.0.0', 'RC', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-RC1', 'RC', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-beta1', 'RC', array('acme/foobar' => '>=1.0'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'RC', array('acme/foobar' => '>=1.0'), true), + array('acme/foobar', 'v1.0.0-patch1', 'RC', array('acme/foobar' => '>=1.0'), true), + + array('acme/foobar', 'v1.0.0', 'beta', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-RC1', 'beta', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-beta1', 'beta', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'beta', array('acme/foobar' => '>=1.0'), true), + array('acme/foobar', 'v1.0.0-patch1', 'beta', array('acme/foobar' => '>=1.0'), true), + + array('acme/foobar', 'v1.0.0', 'alpha', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-RC1', 'alpha', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-beta1', 'alpha', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'alpha', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-patch1', 'alpha', array('acme/foobar' => '>=1.0'), true), + + array('acme/foobar', 'v1.0.0', 'dev', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-RC1', 'dev', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-beta1', 'dev', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'dev', array('acme/foobar' => '>=1.0'), false), + array('acme/foobar', 'v1.0.0-patch1', 'dev', array('acme/foobar' => '>=1.0'), false), + + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '>=1.0@stable'), false), + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '>=1.0@RC'), false), + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '>=1.0@beta'), false), + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0', 'RC', array('acme/foobar' => '>=1.0@stable'), false), + array('acme/foobar', 'v1.0.0', 'RC', array('acme/foobar' => '>=1.0@RC'), false), + array('acme/foobar', 'v1.0.0', 'RC', array('acme/foobar' => '>=1.0@beta'), false), + array('acme/foobar', 'v1.0.0', 'RC', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0', 'RC', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0', 'beta', array('acme/foobar' => '>=1.0@stable'), false), + array('acme/foobar', 'v1.0.0', 'beta', array('acme/foobar' => '>=1.0@RC'), false), + array('acme/foobar', 'v1.0.0', 'beta', array('acme/foobar' => '>=1.0@beta'), false), + array('acme/foobar', 'v1.0.0', 'beta', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0', 'beta', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0', 'alpha', array('acme/foobar' => '>=1.0@stable'), false), + array('acme/foobar', 'v1.0.0', 'alpha', array('acme/foobar' => '>=1.0@RC'), false), + array('acme/foobar', 'v1.0.0', 'alpha', array('acme/foobar' => '>=1.0@beta'), false), + array('acme/foobar', 'v1.0.0', 'alpha', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0', 'alpha', array('acme/foobar' => '>=1.0@dev'), false), + + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '>=1.0@RC'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '>=1.0@beta'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0-RC1', 'RC', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-RC1', 'RC', array('acme/foobar' => '>=1.0@RC'), false), + array('acme/foobar', 'v1.0.0-RC1', 'RC', array('acme/foobar' => '>=1.0@beta'), false), + array('acme/foobar', 'v1.0.0-RC1', 'RC', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0-RC1', 'RC', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0-RC1', 'beta', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-RC1', 'beta', array('acme/foobar' => '>=1.0@RC'), false), + array('acme/foobar', 'v1.0.0-RC1', 'beta', array('acme/foobar' => '>=1.0@beta'), false), + array('acme/foobar', 'v1.0.0-RC1', 'beta', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0-RC1', 'beta', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0-RC1', 'alpha', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-RC1', 'alpha', array('acme/foobar' => '>=1.0@RC'), false), + array('acme/foobar', 'v1.0.0-RC1', 'alpha', array('acme/foobar' => '>=1.0@beta'), false), + array('acme/foobar', 'v1.0.0-RC1', 'alpha', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0-RC1', 'alpha', array('acme/foobar' => '>=1.0@dev'), false), + + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '>=1.0@RC'), true), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '>=1.0@beta'), false), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0-beta1', 'RC', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-beta1', 'RC', array('acme/foobar' => '>=1.0@RC'), true), + array('acme/foobar', 'v1.0.0-beta1', 'RC', array('acme/foobar' => '>=1.0@beta'), false), + array('acme/foobar', 'v1.0.0-beta1', 'RC', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0-beta1', 'RC', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0-beta1', 'beta', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-beta1', 'beta', array('acme/foobar' => '>=1.0@RC'), true), + array('acme/foobar', 'v1.0.0-beta1', 'beta', array('acme/foobar' => '>=1.0@beta'), false), + array('acme/foobar', 'v1.0.0-beta1', 'beta', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0-beta1', 'beta', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0-beta1', 'alpha', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-beta1', 'alpha', array('acme/foobar' => '>=1.0@RC'), true), + array('acme/foobar', 'v1.0.0-beta1', 'alpha', array('acme/foobar' => '>=1.0@beta'), false), + array('acme/foobar', 'v1.0.0-beta1', 'alpha', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0-beta1', 'alpha', array('acme/foobar' => '>=1.0@dev'), false), + + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '>=1.0@RC'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '>=1.0@beta'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'RC', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'RC', array('acme/foobar' => '>=1.0@RC'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'RC', array('acme/foobar' => '>=1.0@beta'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'RC', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'RC', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'beta', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'beta', array('acme/foobar' => '>=1.0@RC'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'beta', array('acme/foobar' => '>=1.0@beta'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'beta', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'beta', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'alpha', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'alpha', array('acme/foobar' => '>=1.0@RC'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'alpha', array('acme/foobar' => '>=1.0@beta'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'alpha', array('acme/foobar' => '>=1.0@alpha'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'alpha', array('acme/foobar' => '>=1.0@dev'), false), + + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '>=1.0@RC'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '>=1.0@beta'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '>=1.0@alpha'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0-patch1', 'RC', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-patch1', 'RC', array('acme/foobar' => '>=1.0@RC'), true), + array('acme/foobar', 'v1.0.0-patch1', 'RC', array('acme/foobar' => '>=1.0@beta'), true), + array('acme/foobar', 'v1.0.0-patch1', 'RC', array('acme/foobar' => '>=1.0@alpha'), true), + array('acme/foobar', 'v1.0.0-patch1', 'RC', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0-patch1', 'beta', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-patch1', 'beta', array('acme/foobar' => '>=1.0@RC'), true), + array('acme/foobar', 'v1.0.0-patch1', 'beta', array('acme/foobar' => '>=1.0@beta'), true), + array('acme/foobar', 'v1.0.0-patch1', 'beta', array('acme/foobar' => '>=1.0@alpha'), true), + array('acme/foobar', 'v1.0.0-patch1', 'beta', array('acme/foobar' => '>=1.0@dev'), false), + array('acme/foobar', 'v1.0.0-patch1', 'alpha', array('acme/foobar' => '>=1.0@stable'), true), + array('acme/foobar', 'v1.0.0-patch1', 'alpha', array('acme/foobar' => '>=1.0@RC'), true), + array('acme/foobar', 'v1.0.0-patch1', 'alpha', array('acme/foobar' => '>=1.0@beta'), true), + array('acme/foobar', 'v1.0.0-patch1', 'alpha', array('acme/foobar' => '>=1.0@alpha'), true), + array('acme/foobar', 'v1.0.0-patch1', 'alpha', array('acme/foobar' => '>=1.0@dev'), false), + + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '~1.0'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '~1.0'), true), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '~1.0'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '~1.0'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '~1.0'), true), + + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '1.0'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '1.0'), true), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '1.0'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '1.0'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '1.0'), true), + + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '@stable'), false), + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '@RC'), false), + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '@beta'), false), + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '@alpha'), false), + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '@dev'), false), + + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '@stable'), true), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '@RC'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '@beta'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '@alpha'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '@dev'), false), + + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '@stable'), true), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '@RC'), true), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '@beta'), false), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '@alpha'), false), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '@dev'), false), + + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '@stable'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '@RC'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '@beta'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '@alpha'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '@dev'), false), + + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '@stable'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '@RC'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '@beta'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '@alpha'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '@dev'), false), + + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '1.0'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '1.0-RC1'), true), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '1.0-beta1'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '1.0-alpha1'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '1.0-patch1'), true), + + array('acme/foobar', 'v1.0.0', 'RC', array('acme/foobar' => '1.0'), false), + array('acme/foobar', 'v1.0.0-RC1', 'RC', array('acme/foobar' => '1.0-RC1'), false), + array('acme/foobar', 'v1.0.0-beta1', 'RC', array('acme/foobar' => '1.0-beta1'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'RC', array('acme/foobar' => '1.0-alpha1'), true), + array('acme/foobar', 'v1.0.0-patch1', 'RC', array('acme/foobar' => '1.0-patch1'), true), + + array('acme/foobar', 'v1.0.0', 'beta', array('acme/foobar' => '1.0'), false), + array('acme/foobar', 'v1.0.0-RC1', 'beta', array('acme/foobar' => '1.0-RC1'), false), + array('acme/foobar', 'v1.0.0-beta1', 'beta', array('acme/foobar' => '1.0-beta1'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'beta', array('acme/foobar' => '1.0-alpha1'), true), + array('acme/foobar', 'v1.0.0-patch1', 'beta', array('acme/foobar' => '1.0-patch1'), true), + + array('acme/foobar', 'v1.0.0', 'alpha', array('acme/foobar' => '1.0'), false), + array('acme/foobar', 'v1.0.0-RC1', 'alpha', array('acme/foobar' => '1.0-RC1'), false), + array('acme/foobar', 'v1.0.0-beta1', 'alpha', array('acme/foobar' => '1.0-beta1'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'alpha', array('acme/foobar' => '1.0-alpha1'), false), + array('acme/foobar', 'v1.0.0-patch1', 'alpha', array('acme/foobar' => '1.0-patch1'), true), + + array('acme/foobar', 'v1.0.0', 'dev', array('acme/foobar' => '1.0'), false), + array('acme/foobar', 'v1.0.0-RC1', 'dev', array('acme/foobar' => '1.0-RC1'), false), + array('acme/foobar', 'v1.0.0-beta1', 'dev', array('acme/foobar' => '1.0-beta1'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'dev', array('acme/foobar' => '1.0-alpha1'), false), + array('acme/foobar', 'v1.0.0-patch1', 'dev', array('acme/foobar' => '1.0-patch1'), false), + + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '1.0@stable'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '1.0-RC1@stable'), true), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '1.0-beta1@stable'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '1.0-alpha1@stable'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '1.0-patch1@stable'), true), + + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '1.0@RC'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '1.0-RC1@RC'), false), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '1.0-beta1@RC'), true), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '1.0-alpha1@RC'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '1.0-patch1@RC'), true), + + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '1.0@beta'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '1.0-RC1@beta'), false), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '1.0-beta1@beta'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '1.0-alpha1@beta'), true), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '1.0-patch1@beta'), true), + + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '1.0@alpha'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '1.0-RC1@alpha'), false), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '1.0-beta1@alpha'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '1.0-alpha1@alpha'), false), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '1.0-patch1@alpha'), true), + + array('acme/foobar', 'v1.0.0', 'stable', array('acme/foobar' => '1.0@dev'), false), + array('acme/foobar', 'v1.0.0-RC1', 'stable', array('acme/foobar' => '1.0-RC1@dev'), false), + array('acme/foobar', 'v1.0.0-beta1', 'stable', array('acme/foobar' => '1.0-beta1@dev'), false), + array('acme/foobar', 'v1.0.0-alpha1', 'stable', array('acme/foobar' => '1.0-alpha1@dev'), false), + array('acme/foobar', 'v1.0.0-patch1', 'stable', array('acme/foobar' => '1.0-patch1@dev'), false), + ); + } + + /** + * @dataProvider getDataProvider + */ + public function testSkipVersion($packageName, $version, $minimumStability, array $rootRequires, $validSkip) + { + $this->init($rootRequires, $minimumStability); + + $this->assertSame($validSkip, $this->filter->skip($this->assetType, $packageName, $version)); + } + + protected function init(array $requires = array(), $minimumStability = 'stable') + { + $parser = new VersionParser(); + $linkRequires = $parser->parseLinks('__ROOT__', '1.0.0', 'requires', $requires); + + $this->package->expects($this->any()) + ->method('getRequires') + ->will($this->returnValue($linkRequires)); + $this->package->expects($this->any()) + ->method('getDevRequires') + ->will($this->returnValue(array())); + $this->package->expects($this->any()) + ->method('getMinimumStability') + ->will($this->returnValue($minimumStability)); + + /* @var RootPackageInterface $package */ + $package = $this->package; + $this->filter = new VcsPackageFilter($package); + } +}