digitalkaoz 13 years ago
commit 0b6bfe6f63

6
composer.lock generated

@ -2,15 +2,15 @@
"hash": "9c243b2c15fdc7c3e35c5200d704ba53",
"packages": [
{
"package": "symfony\/finder",
"package": "symfony\/process",
"version": "2.1.0-dev"
},
{
"package": "symfony\/console",
"package": "symfony\/finder",
"version": "2.1.0-dev"
},
{
"package": "symfony\/process",
"package": "symfony\/console",
"version": "2.1.0-dev"
}
]

@ -0,0 +1,37 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command\Helper;
use Symfony\Component\Console\Helper\DialogHelper as BaseDialogHelper;
use Symfony\Component\Console\Output\OutputInterface;
class DialogHelper extends BaseDialogHelper
{
/**
* Build text for asking a question. For example:
*
* "Do you want to continue [yes]:"
*
* @param string $question The question you want to ask
* @param mixed $default Default value to add to message, if false no default will be shown
* @param string $sep Separation char for between message and user input
*
* @return string
*/
public function getQuestion($question, $default = null, $sep = ':')
{
return $default !== null ?
sprintf('<info>%s</info> [<comment>%s</comment>]%s ', $question, $default, $sep) :
sprintf('<info>%s</info>%s ', $question, $sep);
}
}

@ -0,0 +1,386 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Json\JsonFile;
use Composer\Repository\CompositeRepository;
use Composer\Repository\PlatformRepository;
use Composer\Repository\ComposerRepository;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\ExecutableFinder;
/**
* @author Justin Rainbow <justin.rainbow@gmail.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class InitCommand extends Command
{
private $gitConfig;
private $repos;
public function parseAuthorString($author)
{
if (preg_match('/^(?P<name>[- \.,a-z0-9]+) <(?P<email>.+?)>$/i', $author, $match)) {
if ($match['email'] === filter_var($match['email'], FILTER_VALIDATE_EMAIL)) {
return array(
'name' => trim($match['name']),
'email' => $match['email']
);
}
}
throw new \InvalidArgumentException(
'Invalid author string. Must be in the format: '.
'John Smith <john@example.com>'
);
}
protected function configure()
{
$this
->setName('init')
->setDescription('Creates a basic composer.json file in current directory.')
->setDefinition(array(
new InputOption('name', null, InputOption::VALUE_NONE, 'Name of the package'),
new InputOption('description', null, InputOption::VALUE_NONE, 'Description of package'),
new InputOption('author', null, InputOption::VALUE_NONE, 'Author name of package'),
// new InputOption('version', null, InputOption::VALUE_NONE, 'Version of package'),
new InputOption('homepage', null, InputOption::VALUE_NONE, 'Homepage of package'),
new InputOption('require', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'An array required packages'),
))
->setHelp(<<<EOT
The <info>init</info> command creates a basic composer.json file
in the current directory.
<info>php composer.phar init</info>
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getHelperSet()->get('dialog');
$whitelist = array('name', 'description', 'author', 'require');
$options = array_filter(array_intersect_key($input->getOptions(), array_flip($whitelist)));
if (isset($options['author'])) {
$options['authors'] = $this->formatAuthors($options['author']);
unset($options['author']);
}
$options['require'] = isset($options['require']) ?
$this->formatRequirements($options['require']) :
new \stdClass;
$file = new JsonFile('composer.json');
$json = $file->encode($options);
if ($input->isInteractive()) {
$output->writeln(array(
'',
$json,
''
));
if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) {
$output->writeln('<error>Command aborted</error>');
return 1;
}
}
$file->write($options);
if ($input->isInteractive()) {
$ignoreFile = realpath('.gitignore');
if (false === $ignoreFile) {
$ignoreFile = realpath('.') . '/.gitignore';
}
if (!$this->hasVendorIgnore($ignoreFile)) {
$question = 'Would you like the <info>vendor</info> directory added to your <info>.gitignore</info> [<comment>yes</comment>]?';
if ($dialog->askConfirmation($output, $question, true)) {
$this->addVendorIgnore($ignoreFile);
}
}
}
}
protected function interact(InputInterface $input, OutputInterface $output)
{
$git = $this->getGitConfig();
$dialog = $this->getHelperSet()->get('dialog');
$formatter = $this->getHelperSet()->get('formatter');
$output->writeln(array(
'',
$formatter->formatBlock('Welcome to the Composer config generator', 'bg=blue;fg=white', true),
''
));
// namespace
$output->writeln(array(
'',
'This command will guide you through creating your composer.json config.',
'',
));
$cwd = realpath(".");
if (false === $name = $input->getOption('name')) {
$name = basename($cwd);
if (isset($git['github.user'])) {
$name = $git['github.user'] . '/' . $name;
} elseif (!empty($_SERVER['USERNAME'])) {
$name = $_SERVER['USERNAME'] . '/' . $name;
} elseif (get_current_user()) {
$name = get_current_user() . '/' . $name;
} else {
// package names must be in the format foo/bar
$name = $name . '/' . $name;
}
}
$name = $dialog->askAndValidate(
$output,
$dialog->getQuestion('Package name (<vendor>/<name>)', $name),
function ($value) use ($name) {
if (null === $value) {
return $name;
}
if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}i', $value)) {
throw new \InvalidArgumentException(
'The package name '.$value.' is invalid, it should have a vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+'
);
}
return $value;
}
);
$input->setOption('name', $name);
$description = $input->getOption('description') ?: false;
$description = $dialog->ask(
$output,
$dialog->getQuestion('Description', $description)
);
$input->setOption('description', $description);
if (false === $author = $input->getOption('author')) {
if (isset($git['user.name']) && isset($git['user.email'])) {
$author = sprintf('%s <%s>', $git['user.name'], $git['user.email']);
}
}
$self = $this;
$author = $dialog->askAndValidate(
$output,
$dialog->getQuestion('Author', $author),
function ($value) use ($self, $author) {
if (null === $value) {
return $author;
}
$author = $self->parseAuthorString($value);
return sprintf('%s <%s>', $author['name'], $author['email']);
}
);
$input->setOption('author', $author);
$output->writeln(array(
'',
'Define your dependencies.',
''
));
$requirements = array();
if ($dialog->askConfirmation($output, $dialog->getQuestion('Would you like to define your dependencies interactively', 'yes', '?'), true)) {
$requirements = $this->determineRequirements($input, $output);
}
$input->setOption('require', $requirements);
}
protected function findPackages($name)
{
$packages = array();
// init repos
if (!$this->repos) {
$this->repos = new CompositeRepository(array(
new PlatformRepository,
new ComposerRepository(array('url' => 'http://packagist.org'))
));
}
$token = strtolower($name);
foreach ($this->repos->getPackages() as $package) {
if (false === ($pos = strpos($package->getName(), $token))) {
continue;
}
$packages[] = $package;
}
return $packages;
}
protected function determineRequirements(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getHelperSet()->get('dialog');
$prompt = $dialog->getQuestion('Search for a package', false, ':');
$requires = $input->getOption('require') ?: array();
while (null !== $package = $dialog->ask($output, $prompt)) {
$matches = $this->findPackages($package);
if (count($matches)) {
$output->writeln(array(
'',
sprintf('Found <info>%s</info> packages matching <info>%s</info>', count($matches), $package),
''
));
foreach ($matches as $position => $package) {
$output->writeln(sprintf(' <info>%5s</info> %s <comment>%s</comment>', "[$position]", $package->getPrettyName(), $package->getPrettyVersion()));
}
$output->writeln('');
$validator = function ($selection) use ($matches) {
if ('' === $selection) {
return false;
}
if (!is_numeric($selection) && preg_match('{^\s*(\S+) +(\S.*)\s*}', $selection, $matches)) {
return $matches[1].' '.$matches[2];
}
if (!isset($matches[(int) $selection])) {
throw new \Exception('Not a valid selection');
}
$package = $matches[(int) $selection];
return sprintf('%s %s', $package->getName(), $package->getPrettyVersion());
};
$package = $dialog->askAndValidate($output, $dialog->getQuestion('Enter package # to add, or a <package> <version> couple if it is not listed', false, ':'), $validator, 3);
if (false !== $package) {
$requires[] = $package;
}
}
}
return $requires;
}
protected function formatAuthors($author)
{
return array($this->parseAuthorString($author));
}
protected function formatRequirements(array $requirements)
{
$requires = array();
foreach ($requirements as $requirement) {
list($packageName, $packageVersion) = explode(" ", $requirement, 2);
$requires[$packageName] = $packageVersion;
}
return empty($requires) ? new \stdClass : $requires;
}
protected function getGitConfig()
{
if (null !== $this->gitConfig) {
return $this->gitConfig;
}
$finder = new ExecutableFinder();
$gitBin = $finder->find('git');
$cmd = new Process(sprintf('%s config -l', $gitBin));
$cmd->run();
if ($cmd->isSuccessful()) {
return $this->gitConfig = parse_ini_string($cmd->getOutput(), false, INI_SCANNER_RAW);
}
return $this->gitConfig = array();
}
/**
* Checks the local .gitignore file for the Composer vendor directory.
*
* Tested patterns include:
* "/$vendor"
* "$vendor"
* "$vendor/"
* "/$vendor/"
* "/$vendor/*"
* "$vendor/*"
*
* @param string $ignoreFile
* @param string $vendor
*
* @return Boolean
*/
protected function hasVendorIgnore($ignoreFile, $vendor = 'vendor')
{
if (!file_exists($ignoreFile)) {
return false;
}
$pattern = sprintf(
'~^/?%s(/|/\*)?$~',
preg_quote($vendor, '~')
);
$lines = file($ignoreFile, FILE_IGNORE_NEW_LINES);
foreach ($lines as $line) {
if (preg_match($pattern, $line)) {
return true;
}
}
return false;
}
protected function addVendorIgnore($ignoreFile, $vendor = 'vendor')
{
$contents = "";
if (file_exists($ignoreFile)) {
$contents = file_get_contents($ignoreFile);
if ("\n" !== substr($contents, 0, -1)) {
$contents .= "\n";
}
}
file_put_contents($ignoreFile, $contents . $vendor. "\n");
}
}

@ -15,6 +15,9 @@ namespace Composer\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Repository\CompositeRepository;
use Composer\Repository\PlatformRepository;
use Composer\Repository\ComposerRepository;
/**
* @author Robert Schönthal <seroscho@googlemail.com>
@ -40,27 +43,32 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output)
{
$composer = $this->getComposer();
// create local repo, this contains all packages that are installed in the local project
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
// init repos
$platformRepo = new PlatformRepository;
if ($composer = $this->getComposer(false)) {
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
$installedRepo = new CompositeRepository(array($localRepo, $platformRepo));
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
} else {
$output->writeln('No composer.json found in the current directory, showing packages from packagist.org');
$installedRepo = $platformRepo;
$repos = new CompositeRepository(array($installedRepo, new ComposerRepository(array('url' => 'http://packagist.org'))));
}
$tokens = array_map('strtolower', $input->getArgument('tokens'));
foreach ($composer->getRepositoryManager()->getRepositories() as $repository) {
foreach ($repository->getPackages() as $package) {
foreach ($tokens as $token) {
if (false === ($pos = strpos($package->getName(), $token))) {
continue;
}
foreach ($repos->getPackages() as $package) {
foreach ($tokens as $token) {
if (false === ($pos = strpos($package->getName(), $token))) {
continue;
}
$state = $localRepo->hasPackage($package) ? '<info>installed</info>' : $state = '<comment>available</comment>';
$state = $localRepo->hasPackage($package) ? '<info>installed</info>' : $state = '<comment>available</comment>';
$name = substr($package->getPrettyName(), 0, $pos)
. '<highlight>' . substr($package->getPrettyName(), $pos, strlen($token)) . '</highlight>'
. substr($package->getPrettyName(), $pos + strlen($token));
$output->writeln($state . ': ' . $name . ' <comment>' . $package->getPrettyVersion() . '</comment>');
continue 2;
}
$name = substr($package->getPrettyName(), 0, $pos)
. '<highlight>' . substr($package->getPrettyName(), $pos, strlen($token)) . '</highlight>'
. substr($package->getPrettyName(), $pos + strlen($token));
$output->writeln($state . ': ' . $name . ' <comment>' . $package->getPrettyVersion() . '</comment>');
continue 2;
}
}
}

@ -42,7 +42,7 @@ EOT
{
$ctx = StreamContextFactory::getContext();
$latest = trim(file_get_contents('http://getcomposer.org/version'), false, $ctx);
$latest = trim(file_get_contents('http://getcomposer.org/version', false, $ctx));
if (Composer::VERSION !== $latest) {
$output->writeln(sprintf("Updating to version <info>%s</info>.", $latest));

@ -20,6 +20,7 @@ use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Finder\Finder;
use Composer\Command;
use Composer\Command\Helper\DialogHelper;
use Composer\Composer;
use Composer\Factory;
use Composer\IO\IOInterface;
@ -104,6 +105,7 @@ class Application extends BaseApplication
{
$this->add(new Command\AboutCommand());
$this->add(new Command\DependsCommand());
$this->add(new Command\InitCommand());
$this->add(new Command\InstallCommand());
$this->add(new Command\UpdateCommand());
$this->add(new Command\SearchCommand());
@ -114,4 +116,16 @@ class Application extends BaseApplication
$this->add(new Command\SelfUpdateCommand());
}
}
/**
* {@inheritDoc}
*/
protected function getDefaultHelperSet()
{
$helperSet = parent::getDefaultHelperSet();
$helperSet->set(new DialogHelper());
return $helperSet;
}
}

@ -26,11 +26,6 @@ class DefaultPolicy implements PolicyInterface
return true;
}
public function allowDowngrade()
{
return true;
}
public function versionCompare(PackageInterface $a, PackageInterface $b, $operator)
{
$constraint = new VersionConstraint($operator, $b->getVersion());
@ -39,16 +34,11 @@ class DefaultPolicy implements PolicyInterface
return $constraint->matchSpecific($version);
}
public function findUpdatePackages(Solver $solver, Pool $pool, array $installedMap, PackageInterface $package, $allowAll = false)
public function findUpdatePackages(Solver $solver, Pool $pool, array $installedMap, PackageInterface $package)
{
$packages = array();
foreach ($pool->whatProvides($package->getName()) as $candidate) {
// skip old packages unless downgrades are an option
if (!$allowAll && !$this->allowDowngrade() && $this->versionCompare($package, $candidate, '>')) {
continue;
}
if ($candidate !== $package) {
$packages[] = $candidate;
}

@ -21,9 +21,8 @@ use Composer\Package\PackageInterface;
interface PolicyInterface
{
function allowUninstall();
function allowDowngrade();
function versionCompare(PackageInterface $a, PackageInterface $b, $operator);
function findUpdatePackages(Solver $solver, Pool $pool, array $installedMap, PackageInterface $package, $allowAll);
function findUpdatePackages(Solver $solver, Pool $pool, array $installedMap, PackageInterface $package);
function installable(Solver $solver, Pool $pool, array $installedMap, PackageInterface $package);
function selectPreferedPackages(Pool $pool, array $installedMap, array $literals);
}

@ -29,6 +29,8 @@ class Rule
public $next1;
public $next2;
public $ruleHash;
public function __construct(array $literals, $reason, $reasonData)
{
// sort all packages ascending by id
@ -85,7 +87,7 @@ class Rule
}
for ($i = 0, $n = count($this->literals); $i < $n; $i++) {
if (!$this->literals[$i]->getId() === $rule->literals[$i]->getId()) {
if ($this->literals[$i]->getId() !== $rule->literals[$i]->getId()) {
return false;
}
}

@ -77,7 +77,7 @@ class Solver
* that goes with the reason
* @return Rule The generated rule or null if tautological
*/
public function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null)
protected function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null)
{
$literals = array(new Literal($package, false));
@ -128,7 +128,7 @@ class Solver
* goes with the reason
* @return Rule The generated rule
*/
public function createInstallRule(PackageInterface $package, $reason, $reasonData = null)
protected function createInstallRule(PackageInterface $package, $reason, $reasonData = null)
{
return new Rule(new Literal($package, true));
}
@ -146,7 +146,7 @@ class Solver
* the reason
* @return Rule The generated rule
*/
public function createInstallOneOfRule(array $packages, $reason, $reasonData = null)
protected function createInstallOneOfRule(array $packages, $reason, $reasonData = null)
{
if (empty($packages)) {
return $this->createImpossibleRule($reason, $reasonData);
@ -172,7 +172,7 @@ class Solver
* goes with the reason
* @return Rule The generated rule
*/
public function createRemoveRule(PackageInterface $package, $reason, $reasonData = null)
protected function createRemoveRule(PackageInterface $package, $reason, $reasonData = null)
{
return new Rule(array(new Literal($package, false)), $reason, $reasonData);
}
@ -191,7 +191,7 @@ class Solver
* goes with the reason
* @return Rule The generated rule
*/
public function createConflictRule(PackageInterface $issuer, PackageInterface $provider, $reason, $reasonData = null)
protected function createConflictRule(PackageInterface $issuer, PackageInterface $provider, $reason, $reasonData = null)
{
// ignore self conflict
if ($issuer === $provider) {
@ -212,7 +212,7 @@ class Solver
* the reason
* @return Rule An empty rule
*/
public function createImpossibleRule($reason, $reasonData = null)
protected function createImpossibleRule($reason, $reasonData = null)
{
return new Rule(array(), $reason, $reasonData);
}
@ -237,7 +237,7 @@ class Solver
}
}
public function addRulesForPackage(PackageInterface $package)
protected function addRulesForPackage(PackageInterface $package)
{
$workQueue = new \SplQueue;
$workQueue->enqueue($package);
@ -375,9 +375,9 @@ class Solver
* be added
* @param bool $allowAll Whether downgrades are allowed
*/
private function addRulesForUpdatePackages(PackageInterface $package, $allowAll)
private function addRulesForUpdatePackages(PackageInterface $package)
{
$updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package, $allowAll);
$updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package);
$this->addRulesForPackage($package);
@ -571,7 +571,7 @@ class Solver
}
}
public function addChoiceRules()
protected function addChoiceRules()
{
// void
@ -944,20 +944,6 @@ class Solver
}
foreach ($this->jobs as $job) {
switch ($job['cmd']) {
case 'update-all':
foreach ($installedPackages as $package) {
$this->updateMap[$package->getId()] = true;
}
break;
case 'fix-all':
foreach ($installedPackages as $package) {
$this->fixMap[$package->getId()] = true;
}
break;
}
foreach ($job['packages'] as $package) {
switch ($job['cmd']) {
case 'fix':
@ -979,7 +965,7 @@ class Solver
}
foreach ($installedPackages as $package) {
$this->addRulesForUpdatePackages($package, true);
$this->addRulesForUpdatePackages($package);
}
@ -997,31 +983,15 @@ class Solver
// solver_addrpmrulesforweak(solv, &addedmap);
foreach ($installedPackages as $package) {
// create a feature rule which allows downgrades
$updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package, true);
$featureRule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
// create an update rule which does not allow downgrades
$updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package, false);
$updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package);
$rule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
if ($rule->equals($featureRule)) {
if ($this->policy->allowUninstall()) {
$featureRule->setWeak(true);
$this->addRule(RuleSet::TYPE_FEATURE, $featureRule);
$this->packageToFeatureRule[$package->getId()] = $rule;
} else {
$this->addRule(RuleSet::TYPE_UPDATE, $rule);
$this->packageToUpdateRule[$package->getId()] = $rule;
}
} else if ($this->policy->allowUninstall()) {
$featureRule->setWeak(true);
if ($this->policy->allowUninstall()) {
$rule->setWeak(true);
$this->addRule(RuleSet::TYPE_FEATURE, $featureRule);
$this->addRule(RuleSet::TYPE_UPDATE, $rule);
$this->packageToFeatureRule[$package->getId()] = $rule;
} else {
$this->addRule(RuleSet::TYPE_UPDATE, $rule);
$this->packageToUpdateRule[$package->getId()] = $rule;
}
}
@ -1477,7 +1447,7 @@ class Solver
$l1retry = false;
if (!$num && !$l1num) {
if (!$num && !--$l1num) {
// all level 1 literals done
break 2;
}

@ -1,4 +1,5 @@
<?php
/*
* This file is part of Composer.
*
@ -14,7 +15,7 @@ namespace Composer\Downloader;
use Composer\IO\IOInterface;
use Composer\Package\PackageInterface;
use Composer\Util\Filesystem;
use Composer\Util\StreamContextFactory;
use Composer\Util\RemoteFilesystem;
/**
* Base downloader for file packages
@ -26,7 +27,6 @@ use Composer\Util\StreamContextFactory;
abstract class FileDownloader implements DownloaderInterface
{
protected $io;
private $bytesMax;
/**
* Constructor.
@ -51,9 +51,6 @@ abstract class FileDownloader implements DownloaderInterface
*/
public function download(PackageInterface $package, $path)
{
// init the progress bar
$this->bytesMax = 0;
$url = $package->getDistUrl();
$checksum = $package->getDistSha1Checksum();
@ -79,18 +76,9 @@ abstract class FileDownloader implements DownloaderInterface
}
}
$options = array();
if ($this->io->hasAuthorization($package->getSourceUrl())) {
$auth = $this->io->getAuthorization($package->getSourceUrl());
$authStr = base64_encode($auth['username'] . ':' . $auth['password']);
$options['http']['header'] = "Authorization: Basic $authStr\r\n";
}
$ctx = StreamContextFactory::getContext($options, array('notification' => array($this, 'callbackGet')));
$this->io->overwrite(" Downloading: <comment>connection...</comment>", false);
@copy($url, $fileName, $ctx);
$this->io->overwrite(" Downloading");
$rfs = new RemoteFilesystem($this->io);
$rfs->copy($package->getSourceUrl(), $url, $fileName);
$this->io->write('');
if (!file_exists($fileName)) {
throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the'
@ -111,6 +99,13 @@ abstract class FileDownloader implements DownloaderInterface
$contentDir = glob($path . '/*');
if (1 === count($contentDir)) {
$contentDir = $contentDir[0];
// Rename the content directory to avoid error when moving up
// a child folder with the same name
$temporaryName = md5(time().rand());
rename($contentDir, $temporaryName);
$contentDir = $temporaryName;
foreach (array_merge(glob($contentDir . '/.*'), glob($contentDir . '/*')) as $file) {
if (trim(basename($file), '.')) {
rename($file, $path . '/' . basename($file));
@ -141,52 +136,6 @@ abstract class FileDownloader implements DownloaderInterface
$fs->removeDirectory($path);
}
/**
* Get notification action.
*
* @param integer $notificationCode The notification code
* @param integer $severity The severity level
* @param string $message The message
* @param integer $messageCode The message code
* @param integer $bytesTransferred The loaded size
* @param integer $bytesMax The total size
*/
protected function callbackGet($notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax)
{
switch ($notificationCode) {
case STREAM_NOTIFY_AUTH_REQUIRED:
throw new \LogicException("Authorization is required");
break;
case STREAM_NOTIFY_FAILURE:
throw new \LogicException("File not found");
break;
case STREAM_NOTIFY_FILE_SIZE_IS:
if ($this->bytesMax < $bytesMax) {
$this->bytesMax = $bytesMax;
}
break;
case STREAM_NOTIFY_PROGRESS:
if ($this->bytesMax > 0) {
$progression = 0;
if ($this->bytesMax > 0) {
$progression = round($bytesTransferred / $this->bytesMax * 100);
}
if (0 === $progression % 5) {
$this->io->overwrite(" Downloading: <comment>$progression%</comment>", false);
}
}
break;
default:
break;
}
}
/**
* Extract file to directory
*

@ -24,11 +24,13 @@ abstract class VcsDownloader implements DownloaderInterface
{
protected $io;
protected $process;
protected $filesystem;
public function __construct(IOInterface $io, ProcessExecutor $process = null)
public function __construct(IOInterface $io, ProcessExecutor $process = null, Filesystem $fs = null)
{
$this->io = $io;
$this->process = $process ?: new ProcessExecutor;
$this->filesystem = $fs ?: new Filesystem;
}
/**
@ -74,8 +76,7 @@ abstract class VcsDownloader implements DownloaderInterface
public function remove(PackageInterface $package, $path)
{
$this->enforceCleanDirectory($path);
$fs = new Filesystem();
$fs->removeDirectory($path);
$this->filesystem->removeDirectory($path);
}
/**
@ -101,4 +102,4 @@ abstract class VcsDownloader implements DownloaderInterface
* @throws \RuntimeException if the directory is not clean
*/
abstract protected function enforceCleanDirectory($path);
}
}

@ -14,6 +14,7 @@ namespace Composer;
use Composer\Json\JsonFile;
use Composer\IO\IOInterface;
use Composer\Repository\RepositoryManager;
/**
* Creates an configured instance of composer.
@ -67,22 +68,25 @@ class Factory
$binDir = getenv('COMPOSER_BIN_DIR') ?: $packageConfig['config']['bin-dir'];
// initialize repository manager
$rm = $this->createRepositoryManager($io, $vendorDir);
$rm = $this->createRepositoryManager($io);
// initialize download manager
$dm = $this->createDownloadManager($io);
// load default repository unless it's explicitly disabled
if (!isset($packageConfig['repositories']['packagist']) || $packageConfig['repositories']['packagist'] !== false) {
$this->addPackagistRepository($rm);
}
// initialize installation manager
$im = $this->createInstallationManager($rm, $dm, $vendorDir, $binDir, $io);
// load local repository
$this->addLocalRepository($rm, $vendorDir);
// load package
$loader = new Package\Loader\RootPackageLoader($rm);
$package = $loader->load($packageConfig);
// load default repository unless it's explicitly disabled
if (!isset($packageConfig['repositories']['packagist']) || $packageConfig['repositories']['packagist'] !== false) {
$rm->addRepository(new Repository\ComposerRepository(array('url' => 'http://packagist.org')));
}
// initialize download manager
$dm = $this->createDownloadManager($io);
// initialize installation manager
$im = $this->createInstallationManager($rm, $dm, $vendorDir, $binDir, $io);
// init locker
$lockFile = substr($composerFile, -5) === '.json' ? substr($composerFile, 0, -4).'lock' : $composerFile . '.lock';
@ -99,10 +103,9 @@ class Factory
return $composer;
}
protected function createRepositoryManager(IOInterface $io, $vendorDir)
protected function createRepositoryManager(IOInterface $io)
{
$rm = new Repository\RepositoryManager($io);
$rm->setLocalRepository(new Repository\FilesystemRepository(new JsonFile($vendorDir.'/.composer/installed.json')));
$rm = new RepositoryManager($io);
$rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository');
$rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository');
$rm->setRepositoryClass('pear', 'Composer\Repository\PearRepository');
@ -111,6 +114,16 @@ class Factory
return $rm;
}
protected function addLocalRepository(RepositoryManager $rm, $vendorDir)
{
$rm->setLocalRepository(new Repository\FilesystemRepository(new JsonFile($vendorDir.'/.composer/installed.json')));
}
protected function addPackagistRepository(RepositoryManager $rm)
{
$rm->addRepository(new Repository\ComposerRepository(array('url' => 'http://packagist.org')));
}
protected function createDownloadManager(IOInterface $io)
{
$dm = new Downloader\DownloadManager();

@ -129,11 +129,7 @@ class ConsoleIO implements IOInterface
$value = rtrim(shell_exec($command));
unlink($vbscript);
for ($i = 0; $i < strlen($value); ++$i) {
$this->write('*', false);
}
$this->write('');
$this->write('***');
return $value;
}

@ -0,0 +1,122 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\IO;
/**
* IOInterface that is not interactive and never writes the output
*
* @author Christophe Coevoet <stof@notk.org>
*/
class NullIO implements IOInterface
{
/**
* {@inheritDoc}
*/
public function isInteractive()
{
return false;
}
/**
* {@inheritDoc}
*/
public function write($messages, $newline = true)
{
}
/**
* {@inheritDoc}
*/
public function overwrite($messages, $newline = true, $size = 80)
{
}
/**
* {@inheritDoc}
*/
public function ask($question, $default = null)
{
return $default;
}
/**
* {@inheritDoc}
*/
public function askConfirmation($question, $default = true)
{
return $default;
}
/**
* {@inheritDoc}
*/
public function askAndValidate($question, $validator, $attempts = false, $default = null)
{
return $default;
}
/**
* {@inheritDoc}
*/
public function askAndHideAnswer($question)
{
return null;
}
/**
* {@inheritDoc}
*/
public function getLastUsername()
{
return null;
}
/**
* {@inheritDoc}
*/
public function getLastPassword()
{
return null;
}
/**
* {@inheritDoc}
*/
public function getAuthorizations()
{
return array();
}
/**
* {@inheritDoc}
*/
public function hasAuthorization($repositoryName)
{
return false;
}
/**
* {@inheritDoc}
*/
public function getAuthorization($repositoryName)
{
return array('username' => null, 'password' => null);
}
/**
* {@inheritDoc}
*/
public function setAuthorization($repositoryName, $username, $password = null)
{
}
}

@ -53,10 +53,8 @@ class LibraryInstaller implements InstallerInterface
$this->type = $type;
$this->filesystem = new Filesystem();
$this->filesystem->ensureDirectoryExists($vendorDir);
$this->filesystem->ensureDirectoryExists($binDir);
$this->vendorDir = realpath($vendorDir);
$this->binDir = realpath($binDir);
$this->vendorDir = rtrim($vendorDir, '/');
$this->binDir = rtrim($binDir, '/');
}
/**
@ -82,6 +80,9 @@ class LibraryInstaller implements InstallerInterface
{
$downloadPath = $this->getInstallPath($package);
$this->filesystem->ensureDirectoryExists($this->vendorDir);
$this->filesystem->ensureDirectoryExists($this->binDir);
// remove the binaries if it appears the package files are missing
if (!is_readable($downloadPath) && $this->repository->hasPackage($package)) {
$this->removeBinaries($package);
@ -105,6 +106,9 @@ class LibraryInstaller implements InstallerInterface
$downloadPath = $this->getInstallPath($initial);
$this->filesystem->ensureDirectoryExists($this->vendorDir);
$this->filesystem->ensureDirectoryExists($this->binDir);
$this->removeBinaries($initial);
$this->downloadManager->update($initial, $target, $downloadPath);
$this->installBinaries($target);

@ -16,6 +16,16 @@ use Composer\Repository\RepositoryManager;
use Composer\Composer;
use Composer\Util\StreamContextFactory;
if (!defined('JSON_UNESCAPED_SLASHES')) {
define('JSON_UNESCAPED_SLASHES', 64);
}
if (!defined('JSON_PRETTY_PRINT')) {
define('JSON_PRETTY_PRINT', 128);
}
if (!defined('JSON_UNESCAPED_UNICODE')) {
define('JSON_UNESCAPED_UNICODE', 256);
}
/**
* Reads/writes json files.
*
@ -77,9 +87,9 @@ class JsonFile
* Writes json file.
*
* @param array $hash writes hash into json file
* @param Boolean $prettyPrint If true, output is pretty-printed
* @param int $options json_encode options
*/
public function write(array $hash, $prettyPrint = true)
public function write(array $hash, $options = 448)
{
$dir = dirname($this->path);
if (!is_dir($dir)) {
@ -94,7 +104,7 @@ class JsonFile
);
}
}
file_put_contents($this->path, static::encode($hash, $prettyPrint));
file_put_contents($this->path, static::encode($hash, $options). ($options & JSON_PRETTY_PRINT ? "\n" : ''));
}
/**
@ -103,19 +113,23 @@ class JsonFile
* Original code for this function can be found at:
* http://recursive-design.com/blog/2008/03/11/format-json-with-php/
*
* @param array $hash Data to encode into a formatted JSON string
* @param Boolean $prettyPrint If true, output is pretty-printed
* @return string Indented version of the original JSON string
* @param mixed $data Data to encode into a formatted JSON string
* @param int $options json_encode options
* @return string Encoded json
*/
static public function encode(array $hash, $prettyPrint = true)
static public function encode($data, $options = 448)
{
if ($prettyPrint && defined('JSON_PRETTY_PRINT')) {
return json_encode($hash, JSON_PRETTY_PRINT);
if (version_compare(PHP_VERSION, '5.4', '>=')) {
return json_encode($data, $options);
}
$json = json_encode($hash);
$json = json_encode($data);
if (!$prettyPrint) {
$prettyPrint = (Boolean) ($options & JSON_PRETTY_PRINT);
$unescapeUnicode = (Boolean) ($options & JSON_UNESCAPED_UNICODE);
$unescapeSlashes = (Boolean) ($options & JSON_UNESCAPED_SLASHES);
if (!$prettyPrint && !$unescapeUnicode && !$unescapeSlashes) {
return $json;
}
@ -124,21 +138,46 @@ class JsonFile
$strLen = strlen($json);
$indentStr = ' ';
$newLine = "\n";
$prevChar = '';
$outOfQuotes = true;
$buffer = '';
$noescape = true;
for ($i = 0; $i <= $strLen; $i++) {
// Grab the next character in the string
$char = substr($json, $i, 1);
// Are we inside a quoted string?
if ('"' === $char && ('\\' !== $prevChar || '\\\\' === substr($json, $i-2, 2))) {
if ('"' === $char && $noescape) {
$outOfQuotes = !$outOfQuotes;
} elseif (':' === $char && $outOfQuotes) {
}
if (!$outOfQuotes) {
$buffer .= $char;
$noescape = '\\' === $char ? !$noescape : true;
continue;
} elseif ('' !== $buffer) {
if ($unescapeSlashes) {
$buffer = str_replace('\\/', '/', $buffer);
}
if ($unescapeUnicode && function_exists('mb_convert_encoding')) {
// http://stackoverflow.com/questions/2934563/how-to-decode-unicode-escape-sequences-like-u00ed-to-proper-utf-8-encoded-cha
$buffer = preg_replace_callback('/\\\\u([0-9a-f]{4})/i', function($match) {
return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UCS-2BE');
}, $buffer);
}
$result .= $buffer.$char;
$buffer = '';
continue;
}
if (':' === $char) {
// Add a space after the : character
$char .= ' ';
} elseif (('}' === $char || ']' === $char) && $outOfQuotes) {
} elseif (('}' === $char || ']' === $char)) {
$pos--;
$prevChar = substr($json, $i - 1, 1);
if ('{' !== $prevChar && '[' !== $prevChar) {
// If this character is the end of an element,
@ -153,12 +192,11 @@ class JsonFile
}
}
// Add the character to the result string
$result .= $char;
// If the last character was the beginning of an element,
// output a new line and indent the next line
if ((',' === $char || '{' === $char || '[' === $char) && $outOfQuotes) {
if (',' === $char || '{' === $char || '[' === $char) {
$result .= $newLine;
if ('{' === $char || '[' === $char) {
@ -169,8 +207,6 @@ class JsonFile
$result .= $indentStr;
}
}
$prevChar = $char;
}
return $result;
@ -181,7 +217,7 @@ class JsonFile
*
* @param string $json json string
*
* @return array
* @return mixed
*/
static public function parseJson($json)
{

@ -101,7 +101,7 @@ class GitDriver extends VcsDriver implements VcsDriverInterface
if (!isset($composer['time'])) {
$this->process->execute(sprintf('cd %s && git log -1 --format=%%at %s', escapeshellarg($this->tmpDir), escapeshellarg($identifier)), $output);
$date = new \DateTime('@'.$output[0]);
$date = new \DateTime('@'.trim($output));
$composer['time'] = $date->format('Y-m-d H:i:s');
}
$this->infoCache[$identifier] = $composer;
@ -169,6 +169,15 @@ class GitDriver extends VcsDriver implements VcsDriverInterface
return true;
}
// local filesystem
if (preg_match('{^(file://|/|[a-z]:[\\\\/])}i', $url)) {
$process = new ProcessExecutor();
// check whether there is a git repo in that path
if ($process->execute(sprintf('cd %s && git show', escapeshellarg($url)), $output) === 0) {
return true;
}
}
if (!$deep) {
return false;
}

@ -107,7 +107,7 @@ class HgDriver extends VcsDriver implements VcsDriverInterface
if (!isset($composer['time'])) {
$this->process->execute(sprintf('cd %s && hg log --template "{date|rfc822date}" -r %s', escapeshellarg($this->tmpDir), escapeshellarg($identifier)), $output);
$date = new \DateTime($output[0]);
$date = new \DateTime(trim($output));
$composer['time'] = $date->format('Y-m-d H:i:s');
}
$this->infoCache[$identifier] = $composer;

@ -14,6 +14,7 @@ namespace Composer\Repository\Vcs;
use Composer\IO\IOInterface;
use Composer\Util\ProcessExecutor;
use Composer\Util\RemoteFilesystem;
/**
* A driver implementation for driver with authorization interaction.
@ -25,9 +26,6 @@ abstract class VcsDriver
protected $url;
protected $io;
protected $process;
private $firstCall;
private $contentUrl;
private $content;
/**
* Constructor.
@ -41,7 +39,6 @@ abstract class VcsDriver
$this->url = $url;
$this->io = $io;
$this->process = $process ?: new ProcessExecutor;
$this->firstCall = true;
}
/**
@ -68,85 +65,7 @@ abstract class VcsDriver
*/
protected function getContents($url)
{
$this->contentUrl = $url;
$auth = $this->io->getAuthorization($this->url);
$params = array();
// add authorization to curl options
if ($this->io->hasAuthorization($this->url)) {
$authStr = base64_encode($auth['username'] . ':' . $auth['password']);
$params['http'] = array('header' => "Authorization: Basic $authStr\r\n");
} else if (null !== $this->io->getLastUsername()) {
$authStr = base64_encode($this->io->getLastUsername() . ':' . $this->io->getLastPassword());
$params['http'] = array('header' => "Authorization: Basic $authStr\r\n");
$this->io->setAuthorization($this->url, $this->io->getLastUsername(), $this->io->getLastPassword());
}
$ctx = stream_context_create($params);
stream_context_set_params($ctx, array("notification" => array($this, 'callbackGet')));
$content = @file_get_contents($url, false, $ctx);
// content get after authorization
if (false === $content) {
$content = $this->content;
$this->content = null;
$this->contentUrl = null;
}
return $content;
}
/**
* Get notification action.
*
* @param integer $notificationCode The notification code
* @param integer $severity The severity level
* @param string $message The message
* @param integer $messageCode The message code
* @param integer $bytesTransferred The loaded size
* @param integer $bytesMax The total size
*/
protected function callbackGet($notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax)
{
switch ($notificationCode) {
case STREAM_NOTIFY_AUTH_REQUIRED:
case STREAM_NOTIFY_FAILURE:
// for private repository returning 404 error when the authorization is incorrect
$auth = $this->io->getAuthorization($this->url);
$ps = $this->firstCall && 404 === $messageCode
&& null === $this->io->getLastUsername()
&& null === $auth['username'];
if (404 === $messageCode && !$this->firstCall) {
throw new \RuntimeException("The '" . $this->contentUrl . "' URL not found");
}
$this->firstCall = false;
// get authorization informations
if (401 === $messageCode || $ps) {
if (!$this->io->isInteractive()) {
$mess = "The '" . $this->contentUrl . "' URL not found";
if (401 === $code || $ps) {
$mess = "The '" . $this->contentUrl . "' URL required the authorization.\nYou must be used the interactive console";
}
throw new \RuntimeException($mess);
}
$this->io->write("Authorization for <info>" . $this->contentUrl . "</info>:");
$username = $this->io->ask(' Username: ');
$password = $this->io->askAndHideAnswer(' Password: ');
$this->io->setAuthorization($this->url, $username, $password);
$this->content = $this->getContents($this->contentUrl);
}
break;
default:
break;
}
$rfs = new RemoteFilesystem($this->io);
return $rfs->getContents($this->url, $url, false);
}
}

@ -19,10 +19,6 @@ class VcsRepository extends ArrayRepository
public function __construct(array $config, IOInterface $io, array $drivers = null)
{
if (!filter_var($config['url'], FILTER_VALIDATE_URL)) {
throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$config['url']);
}
$this->drivers = $drivers ?: array(
'Composer\Repository\Vcs\GitHubDriver',
'Composer\Repository\Vcs\GitBitbucketDriver',

@ -0,0 +1,206 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Util;
use Composer\IO\IOInterface;
/**
* @author François Pluchino <francois.pluchino@opendisplay.com>
*/
class RemoteFilesystem
{
private $io;
private $firstCall;
private $bytesMax;
private $originUrl;
private $fileUrl;
private $fileName;
private $result;
private $progess;
private $lastProgress;
/**
* Constructor.
*
* @param IOInterface $io The IO instance
*/
public function __construct(IOInterface $io)
{
$this->io = $io;
}
/**
* Copy the remote file in local.
*
* @param string $originUrl The orgin URL
* @param string $fileUrl The file URL
* @param string $fileName the local filename
* @param boolean $progess Display the progression
*
* @return Boolean true
*/
public function copy($originUrl, $fileUrl, $fileName, $progess = true)
{
$this->get($originUrl, $fileUrl, $fileName, $progess);
return $this->result;
}
/**
* Get the content.
*
* @param string $originUrl The orgin URL
* @param string $fileUrl The file URL
* @param boolean $progess Display the progression
*
* @return string The content
*/
public function getContents($originUrl, $fileUrl, $progess = true)
{
$this->get($originUrl, $fileUrl, null, $progess);
return $this->result;
}
/**
* Get file content or copy action.
*
* @param string $originUrl The orgin URL
* @param string $fileUrl The file URL
* @param string $fileName the local filename
* @param boolean $progess Display the progression
* @param boolean $firstCall Whether this is the first attempt at fetching this resource
*
* @throws \RuntimeException When the file could not be downloaded
*/
protected function get($originUrl, $fileUrl, $fileName = null, $progess = true, $firstCall = true)
{
$this->firstCall = $firstCall;
$this->bytesMax = 0;
$this->result = null;
$this->originUrl = $originUrl;
$this->fileUrl = $fileUrl;
$this->fileName = $fileName;
$this->progress = $progess;
$this->lastProgress = null;
// add authorization in context
$options = array();
if ($this->io->hasAuthorization($originUrl)) {
$auth = $this->io->getAuthorization($originUrl);
$authStr = base64_encode($auth['username'] . ':' . $auth['password']);
$options['http']['header'] = "Authorization: Basic $authStr\r\n";
} elseif (null !== $this->io->getLastUsername()) {
$authStr = base64_encode($this->io->getLastUsername() . ':' . $this->io->getLastPassword());
$options['http'] = array('header' => "Authorization: Basic $authStr\r\n");
$this->io->setAuthorization($originUrl, $this->io->getLastUsername(), $this->io->getLastPassword());
}
$ctx = StreamContextFactory::getContext($options, array('notification' => array($this, 'callbackGet')));
if ($this->progress) {
$this->io->overwrite(" Downloading: <comment>connection...</comment>", false);
}
if (null !== $fileName) {
$result = @copy($fileUrl, $fileName, $ctx);
} else {
$result = @file_get_contents($fileUrl, false, $ctx);
}
// avoid overriding if content was loaded by a sub-call to get()
if (null === $this->result) {
$this->result = $result;
}
if ($this->progress) {
$this->io->overwrite(" Downloading", false);
}
if (false === $this->result) {
throw new \RuntimeException("The '$fileUrl' file could not be downloaded");
}
}
/**
* Get notification action.
*
* @param integer $notificationCode The notification code
* @param integer $severity The severity level
* @param string $message The message
* @param integer $messageCode The message code
* @param integer $bytesTransferred The loaded size
* @param integer $bytesMax The total size
*/
protected function callbackGet($notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax)
{
switch ($notificationCode) {
case STREAM_NOTIFY_AUTH_REQUIRED:
case STREAM_NOTIFY_FAILURE:
// for private repository returning 404 error when the authorization is incorrect
$auth = $this->io->getAuthorization($this->originUrl);
$attemptAuthentication = $this->firstCall && 404 === $messageCode && null === $auth['username'];
if (404 === $messageCode && !$this->firstCall) {
throw new \RuntimeException("The '" . $this->fileUrl . "' URL not found");
}
$this->firstCall = false;
// get authorization informations
if (401 === $messageCode || $attemptAuthentication) {
if (!$this->io->isInteractive()) {
$mess = "The '" . $this->fileUrl . "' URL was not found";
if (401 === $code || $attemptAuthentication) {
$mess = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console";
}
throw new \RuntimeException($mess);
}
$this->io->overwrite(' Authentication required (<info>'.parse_url($this->fileUrl, PHP_URL_HOST).'</info>):');
$username = $this->io->ask(' Username: ');
$password = $this->io->askAndHideAnswer(' Password: ');
$this->io->setAuthorization($this->originUrl, $username, $password);
$this->get($this->originUrl, $this->fileUrl, $this->fileName, $this->progress, false);
}
break;
case STREAM_NOTIFY_FILE_SIZE_IS:
if ($this->bytesMax < $bytesMax) {
$this->bytesMax = $bytesMax;
}
break;
case STREAM_NOTIFY_PROGRESS:
if ($this->bytesMax > 0 && $this->progress) {
$progression = 0;
if ($this->bytesMax > 0) {
$progression = round($bytesTransferred / $this->bytesMax * 100);
}
if ((0 === $progression % 5) && $progression !== $this->lastProgress) {
$this->lastProgress = $progression;
$this->io->overwrite(" Downloading: <comment>$progression%</comment>", false);
}
}
break;
default:
break;
}
}
}

@ -30,4 +30,65 @@ class PoolTest extends TestCase
$this->assertEquals(array($package), $pool->whatProvides('foo'));
$this->assertEquals(array($package), $pool->whatProvides('foo'));
}
/**
* @expectedException \RuntimeException
*/
public function testGetPriorityForNotRegisteredRepository()
{
$pool = new Pool;
$repository = new ArrayRepository;
$pool->getPriority($repository);
}
public function testGetPriorityWhenRepositoryIsRegistered()
{
$pool = new Pool;
$firstRepository = new ArrayRepository;
$pool->addRepository($firstRepository);
$secondRepository = new ArrayRepository;
$pool->addRepository($secondRepository);
$firstPriority = $pool->getPriority($firstRepository);
$secondPriority = $pool->getPriority($secondRepository);
$this->assertEquals(0, $firstPriority);
$this->assertEquals(1, $secondPriority);
}
public function testPackageById()
{
$pool = new Pool;
$repository = new ArrayRepository;
$package = $this->getPackage('foo', '1');
$repository->addPackage($package);
$pool->addRepository($repository);
$this->assertSame($package, $pool->packageById(1));
}
public function testWhatProvidesWhenPackageCannotBeFound()
{
$pool = new Pool;
$this->assertEquals(array(), $pool->whatProvides('foo'));
}
public function testGetMaxId()
{
$pool = new Pool;
$repository = new ArrayRepository;
$firstPackage = $this->getPackage('foo', '1');
$secondPackage = $this->getPackage('foo1', '1');
$this->assertEquals(0, $pool->getMaxId());
$repository->addPackage($firstPackage);
$repository->addPackage($secondPackage);
$pool->addRepository($repository);
$this->assertEquals(2, $pool->getMaxId());
}
}

@ -39,8 +39,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase
$ruleSetIterator = new RuleSetIterator($this->rules);
$result = array();
foreach ($ruleSetIterator as $rule)
{
foreach ($ruleSetIterator as $rule) {
$result[] = $rule;
}
@ -52,4 +51,22 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, $result);
}
public function testKeys()
{
$ruleSetIterator = new RuleSetIterator($this->rules);
$result = array();
foreach ($ruleSetIterator as $key => $rule) {
$result[] = $key;
}
$expected = array(
RuleSet::TYPE_JOB,
RuleSet::TYPE_JOB,
RuleSet::TYPE_UPDATE,
);
$this->assertEquals($expected, $result);
}
}

@ -14,8 +14,10 @@ namespace Composer\Test\DependencyResolver;
use Composer\DependencyResolver\Rule;
use Composer\DependencyResolver\RuleSet;
use Composer\DependencyResolver\Literal;
use Composer\Test\TestCase;
class RuleSetTest extends \PHPUnit_Framework_TestCase
class RuleSetTest extends TestCase
{
public function testAdd()
{
@ -41,4 +43,128 @@ class RuleSetTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($rules, $ruleSet->getRules());
}
/**
* @expectedException \OutOfBoundsException
*/
public function testAddWhenTypeIsNotRecognized()
{
$ruleSet = new RuleSet;
$ruleSet->add(new Rule(array(), 'job1', null), 7);
}
public function testCount()
{
$ruleSet = new RuleSet;
$ruleSet->add(new Rule(array(), 'job1', null), RuleSet::TYPE_JOB);
$ruleSet->add(new Rule(array(), 'job2', null), RuleSet::TYPE_JOB);
$this->assertEquals(2, $ruleSet->count());
}
public function testRuleById()
{
$ruleSet = new RuleSet;
$rule = new Rule(array(), 'job1', null);
$ruleSet->add($rule, RuleSet::TYPE_JOB);
$this->assertSame($rule, $ruleSet->ruleById(0));
}
public function testGetIterator()
{
$ruleSet = new RuleSet;
$rule1 = new Rule(array(), 'job1', null);
$rule2 = new Rule(array(), 'job1', null);
$ruleSet->add($rule1, RuleSet::TYPE_JOB);
$ruleSet->add($rule2, RuleSet::TYPE_UPDATE);
$iterator = $ruleSet->getIterator();
$this->assertSame($rule1, $iterator->current());
$iterator->next();
$this->assertSame($rule2, $iterator->current());
}
public function testGetIteratorFor()
{
$ruleSet = new RuleSet;
$rule1 = new Rule(array(), 'job1', null);
$rule2 = new Rule(array(), 'job1', null);
$ruleSet->add($rule1, RuleSet::TYPE_JOB);
$ruleSet->add($rule2, RuleSet::TYPE_UPDATE);
$iterator = $ruleSet->getIteratorFor(RuleSet::TYPE_UPDATE);
$this->assertSame($rule2, $iterator->current());
}
public function testGetIteratorWithout()
{
$ruleSet = new RuleSet;
$rule1 = new Rule(array(), 'job1', null);
$rule2 = new Rule(array(), 'job1', null);
$ruleSet->add($rule1, RuleSet::TYPE_JOB);
$ruleSet->add($rule2, RuleSet::TYPE_UPDATE);
$iterator = $ruleSet->getIteratorWithout(RuleSet::TYPE_JOB);
$this->assertSame($rule2, $iterator->current());
}
public function testContainsEqual()
{
$ruleSet = new RuleSet;
$rule = $this->getRuleMock();
$rule->expects($this->any())
->method('getHash')
->will($this->returnValue('rule_1_hash'));
$rule->expects($this->any())
->method('equals')
->will($this->returnValue(true));
$rule2 = $this->getRuleMock();
$rule2->expects($this->any())
->method('getHash')
->will($this->returnValue('rule_2_hash'));
$rule3 = $this->getRuleMock();
$rule3->expects($this->any())
->method('getHash')
->will($this->returnValue('rule_1_hash'));
$rule3->expects($this->any())
->method('equal')
->will($this->returnValue(false));
$ruleSet->add($rule, RuleSet::TYPE_UPDATE);
$this->assertTrue($ruleSet->containsEqual($rule));
$this->assertFalse($ruleSet->containsEqual($rule2));
$this->assertFalse($ruleSet->containsEqual($rule3));
}
public function testToString()
{
$ruleSet = new RuleSet;
$literal = new Literal($this->getPackage('foo', '2.1'), true);
$rule = new Rule(array($literal), 'job1', null);
$ruleSet->add($rule, RuleSet::TYPE_UPDATE);
$this->assertContains('UPDATE : (+foo-2.1.0.0)', $ruleSet->__toString());
}
private function getRuleMock()
{
return $this->getMockBuilder('Composer\DependencyResolver\Rule')
->disableOriginalConstructor()
->getMock();
}
}

@ -0,0 +1,170 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test\DependencyResolver;
use Composer\DependencyResolver\Rule;
use Composer\DependencyResolver\Literal;
use Composer\Test\TestCase;
class RuleTest extends TestCase
{
public function testGetHash()
{
$rule = new Rule(array(), 'job1', null);
$rule->ruleHash = '123';
$this->assertEquals('123', $rule->getHash());
}
public function testSetAndGetId()
{
$rule = new Rule(array(), 'job1', null);
$rule->setId(666);
$this->assertEquals(666, $rule->getId());
}
public function testEqualsForRulesWithDifferentHashes()
{
$rule = new Rule(array(), 'job1', null);
$rule->ruleHash = '123';
$rule2 = new Rule(array(), 'job1', null);
$rule2->ruleHash = '321';
$this->assertFalse($rule->equals($rule2));
}
public function testEqualsForRulesWithDifferentLiterals()
{
$literal = $this->getLiteralMock();
$literal->expects($this->any())
->method('getId')
->will($this->returnValue(1));
$rule = new Rule(array($literal), 'job1', null);
$rule->ruleHash = '123';
$literal = $this->getLiteralMock();
$literal->expects($this->any())
->method('getId')
->will($this->returnValue(12));
$rule2 = new Rule(array($literal), 'job1', null);
$rule2->ruleHash = '123';
$this->assertFalse($rule->equals($rule2));
}
public function testEqualsForRulesWithDifferLiteralsQuantity()
{
$literal = $this->getLiteralMock();
$literal->expects($this->any())
->method('getId')
->will($this->returnValue(1));
$literal2 = $this->getLiteralMock();
$literal2->expects($this->any())
->method('getId')
->will($this->returnValue(12));
$rule = new Rule(array($literal, $literal2), 'job1', null);
$rule->ruleHash = '123';
$rule2 = new Rule(array($literal), 'job1', null);
$rule2->ruleHash = '123';
$this->assertFalse($rule->equals($rule2));
}
public function testEqualsForRulesWithThisSameLiterals()
{
$literal = $this->getLiteralMock();
$literal->expects($this->any())
->method('getId')
->will($this->returnValue(1));
$literal2 = $this->getLiteralMock();
$literal2->expects($this->any())
->method('getId')
->will($this->returnValue(12));
$rule = new Rule(array($literal, $literal2), 'job1', null);
$rule2 = new Rule(array($literal, $literal2), 'job1', null);
$this->assertTrue($rule->equals($rule2));
}
public function testSetAndGetType()
{
$rule = new Rule(array(), 'job1', null);
$rule->setType('someType');
$this->assertEquals('someType', $rule->getType());
}
public function testEnable()
{
$rule = new Rule(array(), 'job1', null);
$rule->disable();
$rule->enable();
$this->assertTrue($rule->isEnabled());
$this->assertFalse($rule->isDisabled());
}
public function testDisable()
{
$rule = new Rule(array(), 'job1', null);
$rule->enable();
$rule->disable();
$this->assertTrue($rule->isDisabled());
$this->assertFalse($rule->isEnabled());
}
public function testSetWeak()
{
$rule = new Rule(array(), 'job1', null);
$rule->setWeak(true);
$rule2 = new Rule(array(), 'job1', null);
$rule2->setWeak(false);
$this->assertTrue($rule->isWeak());
$this->assertFalse($rule2->isWeak());
}
public function testIsAssertions()
{
$literal = $this->getLiteralMock();
$literal2 = $this->getLiteralMock();
$rule = new Rule(array($literal, $literal2), 'job1', null);
$rule2 = new Rule(array($literal), 'job1', null);
$this->assertFalse($rule->isAssertion());
$this->assertTrue($rule2->isAssertion());
}
public function testToString()
{
$literal = new Literal($this->getPackage('foo', '2.1'), true);
$literal2 = new Literal($this->getPackage('baz', '1.1'), false);
$rule = new Rule(array($literal, $literal2), 'job1', null);
$this->assertEquals('(-baz-1.1.0.0|+foo-2.1.0.0)', $rule->__toString());
}
private function getLiteralMock()
{
return $this->getMockBuilder('Composer\DependencyResolver\Literal')
->disableOriginalConstructor()
->getMock();
}
}

@ -158,6 +158,22 @@ class SolverTest extends TestCase
$this->checkSolverResult(array());
}
public function testSolverUpdateOnlyUpdatesSelectedPackage()
{
$this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
$this->repoInstalled->addPackage($packageB = $this->getPackage('B', '1.0'));
$this->repo->addPackage($packageAnewer = $this->getPackage('A', '1.1'));
$this->repo->addPackage($packageBnewer = $this->getPackage('B', '1.1'));
$this->reposComplete();
$this->request->update('A');
$this->checkSolverResult(array(
array('job' => 'update', 'from' => $packageA, 'to' => $packageAnewer),
));
}
public function testSolverUpdateConstrained()
{
$this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
@ -192,6 +208,24 @@ class SolverTest extends TestCase
)));
}
public function testSolverUpdateFullyConstrainedPrunesInstalledPackages()
{
$this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
$this->repoInstalled->addPackage($this->getPackage('B', '1.0'));
$this->repo->addPackage($newPackageA = $this->getPackage('A', '1.2'));
$this->repo->addPackage($this->getPackage('A', '2.0'));
$this->reposComplete();
$this->request->install('A', new VersionConstraint('<', '2.0.0.0'));
$this->request->update('A', new VersionConstraint('=', '1.0.0.0'));
$this->checkSolverResult(array(array(
'job' => 'update',
'from' => $packageA,
'to' => $newPackageA,
)));
}
public function testSolverAllJobs()
{
$this->repoInstalled->addPackage($packageD = $this->getPackage('D', '1.0'));
@ -414,6 +448,42 @@ class SolverTest extends TestCase
));
}
/**
* If a replacer D replaces B and C with C not otherwise available,
* D must be installed instead of the original B.
*/
public function testUseReplacerIfNecessary()
{
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
$this->repo->addPackage($packageD = $this->getPackage('D', '1.0'));
$this->repo->addPackage($packageD2 = $this->getPackage('D', '1.1'));
$packageA->setRequires(array(
new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'requires'),
new Link('A', 'C', new VersionConstraint('>=', '1.0'), 'requires'),
));
$packageD->setReplaces(array(
new Link('D', 'B', new VersionConstraint('>=', '1.0'), 'replaces'),
new Link('D', 'C', new VersionConstraint('>=', '1.0'), 'replaces'),
));
$packageD2->setReplaces(array(
new Link('D', 'B', new VersionConstraint('>=', '1.0'), 'replaces'),
new Link('D', 'C', new VersionConstraint('>=', '1.0'), 'replaces'),
));
$this->reposComplete();
$this->request->install('A');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $packageD2),
array('job' => 'install', 'package' => $packageA),
));
}
protected function reposComplete()
{
$this->pool->addRepository($this->repoInstalled);

@ -0,0 +1,124 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test\Downloader;
use Composer\Downloader\GitDownloader;
class GitDownloaderTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \InvalidArgumentException
*/
public function testDownloadForPackageWithoutSourceReference()
{
$packageMock = $this->getMock('Composer\Package\PackageInterface');
$packageMock->expects($this->once())
->method('getSourceReference')
->will($this->returnValue(null));
$downloader = new GitDownloader($this->getMock('Composer\IO\IOInterface'));
$downloader->download($packageMock, '/path');
}
public function testDownload()
{
$expectedGitCommand = $this->getCmd('git clone \'https://github.com/l3l0/composer\' \'composerPath\' && cd \'composerPath\' && git checkout \'ref\' && git reset --hard \'ref\'');
$packageMock = $this->getMock('Composer\Package\PackageInterface');
$packageMock->expects($this->any())
->method('getSourceReference')
->will($this->returnValue('ref'));
$packageMock->expects($this->once())
->method('getSourceUrl')
->will($this->returnValue('https://github.com/l3l0/composer'));
$processExecutor = $this->getMock('Composer\Util\ProcessExecutor');
$processExecutor->expects($this->once())
->method('execute')
->with($this->equalTo($expectedGitCommand));
$downloader = new GitDownloader($this->getMock('Composer\IO\IOInterface'), $processExecutor);
$downloader->download($packageMock, 'composerPath');
}
/**
* @expectedException \InvalidArgumentException
*/
public function testUpdateforPackageWithoutSourceReference()
{
$initialPackageMock = $this->getMock('Composer\Package\PackageInterface');
$sourcePackageMock = $this->getMock('Composer\Package\PackageInterface');
$sourcePackageMock->expects($this->once())
->method('getSourceReference')
->will($this->returnValue(null));
$downloader = new GitDownloader($this->getMock('Composer\IO\IOInterface'));
$downloader->update($initialPackageMock, $sourcePackageMock, '/path');
}
public function testUpdate()
{
$expectedGitUpdateCommand = $this->getCmd('cd \'composerPath\' && git fetch && git checkout \'ref\' && git reset --hard \'ref\'');
$expectedGitResetCommand = $this->getCmd('cd \'composerPath\' && git status --porcelain');
$packageMock = $this->getMock('Composer\Package\PackageInterface');
$packageMock->expects($this->any())
->method('getSourceReference')
->will($this->returnValue('ref'));
$packageMock->expects($this->any())
->method('getSourceUrl')
->will($this->returnValue('https://github.com/l3l0/composer'));
$processExecutor = $this->getMock('Composer\Util\ProcessExecutor');
$processExecutor->expects($this->at(0))
->method('execute')
->with($this->equalTo($expectedGitResetCommand));
$processExecutor->expects($this->at(1))
->method('execute')
->with($this->equalTo($expectedGitUpdateCommand));
$downloader = new GitDownloader($this->getMock('Composer\IO\IOInterface'), $processExecutor);
$downloader->update($packageMock, $packageMock, 'composerPath');
}
public function testRemove()
{
$expectedGitResetCommand = $this->getCmd('cd \'composerPath\' && git status --porcelain');
$packageMock = $this->getMock('Composer\Package\PackageInterface');
$processExecutor = $this->getMock('Composer\Util\ProcessExecutor');
$processExecutor->expects($this->any())
->method('execute')
->with($this->equalTo($expectedGitResetCommand));
$filesystem = $this->getMock('Composer\Util\Filesystem');
$filesystem->expects($this->any())
->method('removeDirectory')
->with($this->equalTo('composerPath'));
$downloader = new GitDownloader($this->getMock('Composer\IO\IOInterface'), $processExecutor, $filesystem);
$downloader->remove($packageMock, 'composerPath');
}
public function testGetInstallationSource()
{
$downloader = new GitDownloader($this->getMock('Composer\IO\IOInterface'));
$this->assertEquals('source', $downloader->getInstallationSource());
}
private function getCmd($cmd)
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
return strtr($cmd, "'", '"');
}
return $cmd;
}
}

@ -0,0 +1,124 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test\Downloader;
use Composer\Downloader\HgDownloader;
class HgDownloaderTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \InvalidArgumentException
*/
public function testDownloadForPackageWithoutSourceReference()
{
$packageMock = $this->getMock('Composer\Package\PackageInterface');
$packageMock->expects($this->once())
->method('getSourceReference')
->will($this->returnValue(null));
$downloader = new HgDownloader($this->getMock('Composer\IO\IOInterface'));
$downloader->download($packageMock, '/path');
}
public function testDownload()
{
$expectedGitCommand = $this->getCmd('hg clone \'https://mercurial.dev/l3l0/composer\' \'composerPath\' && cd \'composerPath\' && hg up \'ref\'');
$packageMock = $this->getMock('Composer\Package\PackageInterface');
$packageMock->expects($this->any())
->method('getSourceReference')
->will($this->returnValue('ref'));
$packageMock->expects($this->once())
->method('getSourceUrl')
->will($this->returnValue('https://mercurial.dev/l3l0/composer'));
$processExecutor = $this->getMock('Composer\Util\ProcessExecutor');
$processExecutor->expects($this->once())
->method('execute')
->with($this->equalTo($expectedGitCommand));
$downloader = new HgDownloader($this->getMock('Composer\IO\IOInterface'), $processExecutor);
$downloader->download($packageMock, 'composerPath');
}
/**
* @expectedException \InvalidArgumentException
*/
public function testUpdateforPackageWithoutSourceReference()
{
$initialPackageMock = $this->getMock('Composer\Package\PackageInterface');
$sourcePackageMock = $this->getMock('Composer\Package\PackageInterface');
$sourcePackageMock->expects($this->once())
->method('getSourceReference')
->will($this->returnValue(null));
$downloader = new HgDownloader($this->getMock('Composer\IO\IOInterface'));
$downloader->update($initialPackageMock, $sourcePackageMock, '/path');
}
public function testUpdate()
{
$expectedUpdateCommand = $this->getCmd('cd \'composerPath\' && hg pull && hg up \'ref\'');
$expectedResetCommand = $this->getCmd('cd \'composerPath\' && hg st');
$packageMock = $this->getMock('Composer\Package\PackageInterface');
$packageMock->expects($this->any())
->method('getSourceReference')
->will($this->returnValue('ref'));
$packageMock->expects($this->any())
->method('getSourceUrl')
->will($this->returnValue('https://github.com/l3l0/composer'));
$processExecutor = $this->getMock('Composer\Util\ProcessExecutor');
$processExecutor->expects($this->at(0))
->method('execute')
->with($this->equalTo($expectedResetCommand));
$processExecutor->expects($this->at(1))
->method('execute')
->with($this->equalTo($expectedUpdateCommand));
$downloader = new HgDownloader($this->getMock('Composer\IO\IOInterface'), $processExecutor);
$downloader->update($packageMock, $packageMock, 'composerPath');
}
public function testRemove()
{
$expectedResetCommand = $this->getCmd('cd \'composerPath\' && hg st');
$packageMock = $this->getMock('Composer\Package\PackageInterface');
$processExecutor = $this->getMock('Composer\Util\ProcessExecutor');
$processExecutor->expects($this->any())
->method('execute')
->with($this->equalTo($expectedResetCommand));
$filesystem = $this->getMock('Composer\Util\Filesystem');
$filesystem->expects($this->any())
->method('removeDirectory')
->with($this->equalTo('composerPath'));
$downloader = new HgDownloader($this->getMock('Composer\IO\IOInterface'), $processExecutor, $filesystem);
$downloader->remove($packageMock, 'composerPath');
}
public function testGetInstallationSource()
{
$downloader = new HgDownloader($this->getMock('Composer\IO\IOInterface'));
$this->assertEquals('source', $downloader->getInstallationSource());
}
private function getCmd($cmd)
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
return strtr($cmd, "'", '"');
}
return $cmd;
}
}

@ -0,0 +1,219 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test\IO;
use Composer\IO\ConsoleIO;
use Composer\Test\TestCase;
class ConsoleIOTest extends TestCase
{
public function testIsInteractive()
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$inputMock->expects($this->at(0))
->method('isInteractive')
->will($this->returnValue(true));
$inputMock->expects($this->at(1))
->method('isInteractive')
->will($this->returnValue(false));
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
$consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
$this->assertTrue($consoleIO->isInteractive());
$this->assertFalse($consoleIO->isInteractive());
}
public function testWrite()
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$outputMock->expects($this->once())
->method('write')
->with($this->equalTo('some information about something'), $this->equalTo(false));
$helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
$consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
$consoleIO->write('some information about something', false);
}
public function testOverwrite()
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$outputMock->expects($this->at(0))
->method('write')
->with($this->equalTo("\x08"), $this->equalTo(false));
$outputMock->expects($this->at(19))
->method('write')
->with($this->equalTo("\x08"), $this->equalTo(false));
$outputMock->expects($this->at(20))
->method('write')
->with($this->equalTo('some information'), $this->equalTo(false));
$outputMock->expects($this->at(21))
->method('write')
->with($this->equalTo(' '), $this->equalTo(false));
$outputMock->expects($this->at(24))
->method('write')
->with($this->equalTo(' '), $this->equalTo(false));
$outputMock->expects($this->at(25))
->method('write')
->with($this->equalTo("\x08"), $this->equalTo(false));
$outputMock->expects($this->at(28))
->method('write')
->with($this->equalTo("\x08"), $this->equalTo(false));
$outputMock->expects($this->at(29))
->method('write')
->with($this->equalTo(''));
$helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
$consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
$consoleIO->overwrite('some information', true, 20);
}
public function testAsk()
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$dialogMock = $this->getMock('Symfony\Component\Console\Helper\DialogHelper');
$helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
$dialogMock->expects($this->once())
->method('ask')
->with($this->isInstanceOf('Symfony\Component\Console\Output\OutputInterface'),
$this->equalTo('Why?'),
$this->equalTo('default'));
$helperMock->expects($this->once())
->method('get')
->with($this->equalTo('dialog'))
->will($this->returnValue($dialogMock));
$consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
$consoleIO->ask('Why?', 'default');
}
public function testAskConfirmation()
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$dialogMock = $this->getMock('Symfony\Component\Console\Helper\DialogHelper');
$helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
$dialogMock->expects($this->once())
->method('askConfirmation')
->with($this->isInstanceOf('Symfony\Component\Console\Output\OutputInterface'),
$this->equalTo('Why?'),
$this->equalTo('default'));
$helperMock->expects($this->once())
->method('get')
->with($this->equalTo('dialog'))
->will($this->returnValue($dialogMock));
$consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
$consoleIO->askConfirmation('Why?', 'default');
}
public function testAskAndValidate()
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$dialogMock = $this->getMock('Symfony\Component\Console\Helper\DialogHelper');
$helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
$dialogMock->expects($this->once())
->method('askAndValidate')
->with($this->isInstanceOf('Symfony\Component\Console\Output\OutputInterface'),
$this->equalTo('Why?'),
$this->equalTo('validator'),
$this->equalTo(10),
$this->equalTo('default'));
$helperMock->expects($this->once())
->method('get')
->with($this->equalTo('dialog'))
->will($this->returnValue($dialogMock));
$consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
$consoleIO->askAndValidate('Why?', 'validator', 10, 'default');
}
public function testSetAndGetAuthorization()
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
$consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
$consoleIO->setAuthorization('repoName', 'l3l0', 'passwd');
$this->assertEquals(
array('username' => 'l3l0', 'password' => 'passwd'),
$consoleIO->getAuthorization('repoName')
);
}
public function testGetAuthorizationWhenDidNotSet()
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
$consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
$this->assertEquals(
array('username' => null, 'password' => null),
$consoleIO->getAuthorization('repoName')
);
}
public function testHasAuthorization()
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
$consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
$consoleIO->setAuthorization('repoName', 'l3l0', 'passwd');
$this->assertTrue($consoleIO->hasAuthorization('repoName'));
$this->assertFalse($consoleIO->hasAuthorization('repoName2'));
}
public function testGetLastUsername()
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
$consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
$consoleIO->setAuthorization('repoName', 'l3l0', 'passwd');
$consoleIO->setAuthorization('repoName2', 'l3l02', 'passwd2');
$this->assertEquals('l3l02', $consoleIO->getLastUsername());
}
public function testGetLastPassword()
{
$inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
$helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
$consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
$consoleIO->setAuthorization('repoName', 'l3l0', 'passwd');
$consoleIO->setAuthorization('repoName2', 'l3l02', 'passwd2');
$this->assertEquals('passwd2', $consoleIO->getLastPassword());
}
}

@ -22,22 +22,22 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase
private $binDir;
private $dm;
private $repository;
private $library;
private $io;
private $fs;
protected function setUp()
{
$fs = new Filesystem;
$this->fs = new Filesystem;
$this->vendorDir = realpath(sys_get_temp_dir()).DIRECTORY_SEPARATOR.'composer-test-vendor';
if (is_dir($this->vendorDir)) {
$fs->removeDirectory($this->vendorDir);
$this->fs->removeDirectory($this->vendorDir);
}
mkdir($this->vendorDir);
$this->binDir = realpath(sys_get_temp_dir()).DIRECTORY_SEPARATOR.'composer-test-bin';
if (is_dir($this->binDir)) {
$fs->removeDirectory($this->binDir);
$this->fs->removeDirectory($this->binDir);
}
mkdir($this->binDir);
@ -52,16 +52,20 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase
->getMock();
}
public function testInstallerCreation()
public function testInstallerCreationShouldNotCreateVendorDirectory()
{
$library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->repository, $this->io);
$this->assertTrue(is_dir($this->vendorDir));
$this->fs->removeDirectory($this->vendorDir);
new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->repository, $this->io);
$this->assertFileNotExists($this->vendorDir);
}
$file = sys_get_temp_dir().'/file';
touch($file);
public function testInstallerCreationShouldNotCreateBinDirectory()
{
$this->fs->removeDirectory($this->binDir);
$this->setExpectedException('RuntimeException');
$library = new LibraryInstaller($file, $this->binDir, $this->dm, $this->repository, $this->io);
new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->repository, $this->io);
$this->assertFileNotExists($this->binDir);
}
public function testIsInstalled()
@ -79,6 +83,10 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($library->isInstalled($package));
}
/**
* @depends testInstallerCreationShouldNotCreateVendorDirectory
* @depends testInstallerCreationShouldNotCreateBinDirectory
*/
public function testInstall()
{
$library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->repository, $this->io);
@ -100,8 +108,14 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase
->with($package);
$library->install($package);
$this->assertFileExists($this->vendorDir, 'Vendor dir should be created');
$this->assertFileExists($this->binDir, 'Bin dir should be created');
}
/**
* @depends testInstallerCreationShouldNotCreateVendorDirectory
* @depends testInstallerCreationShouldNotCreateBinDirectory
*/
public function testUpdate()
{
$library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->repository, $this->io);
@ -135,6 +149,8 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase
->with($target);
$library->update($initial, $target);
$this->assertFileExists($this->vendorDir, 'Vendor dir should be created');
$this->assertFileExists($this->binDir, 'Bin dir should be created');
$this->setExpectedException('InvalidArgumentException');

@ -107,7 +107,7 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase
{
$data = array('name' => 'composer/composer');
$json = '{
"name": "composer\/composer"
"name": "composer/composer"
}';
$this->assertJsonFormat($json, $data);
}
@ -116,11 +116,51 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase
{
$data = array('Metadata\\' => 'src/');
$json = '{
"Metadata\\\\": "src\/"
"Metadata\\\\": "src/"
}';
$this->assertJsonFormat($json, $data);
}
public function testEscape()
{
$data = array("Metadata\\\"" => 'src/');
$json = '{
"Metadata\\\\\\"": "src/"
}';
$this->assertJsonFormat($json, $data);
}
public function testUnicode()
{
if (!function_exists('mb_convert_encoding')) {
$this->markTestSkipped('Test requires the mbstring extension');
}
$data = array("Žluťoučký \" kůň" => "úpěl ďábelské ódy za €");
$json = '{
"Žluťoučký \" kůň": "úpěl ďábelské ódy za €"
}';
$this->assertJsonFormat($json, $data);
}
public function testEscapedSlashes()
{
if (!function_exists('mb_convert_encoding')) {
$this->markTestSkipped('Test requires the mbstring extension');
}
$data = "\\/fooƌ";
$this->assertJsonFormat('"\\\\\\/fooƌ"', $data, JSON_UNESCAPED_UNICODE);
}
public function testEscapedUnicode()
{
$data = "ƌ";
$this->assertJsonFormat('"\\u018c"', $data, 0);
}
private function expectParseException($text, $json)
{
try {
@ -131,11 +171,15 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase
}
}
private function assertJsonFormat($json, $data)
private function assertJsonFormat($json, $data, $options = null)
{
$file = new JsonFile('composer.json');
$this->assertEquals($json, $file->encode($data));
if (null === $options) {
$this->assertEquals($json, $file->encode($data));
} else {
$this->assertEquals($json, $file->encode($data, $options));
}
}
}

Loading…
Cancel
Save