+ * @author Nicolas Grekas
+ */
+class CurlDownloader
+{
+ private $multiHandle;
+ private $shareHandle;
+ private $jobs = array();
+ /** @var IOInterface */
+ private $io;
+ /** @var Config */
+ private $config;
+ /** @var AuthHelper */
+ private $authHelper;
+ private $selectTimeout = 5.0;
+ private $maxRedirects = 20;
+ protected $multiErrors = array(
+ CURLM_BAD_HANDLE => array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'),
+ CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."),
+ CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'),
+ CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!')
+ );
+
+ private static $options = array(
+ 'http' => array(
+ 'method' => CURLOPT_CUSTOMREQUEST,
+ 'content' => CURLOPT_POSTFIELDS,
+ 'header' => CURLOPT_HTTPHEADER,
+ ),
+ 'ssl' => array(
+ 'cafile' => CURLOPT_CAINFO,
+ 'capath' => CURLOPT_CAPATH,
+ ),
+ );
+
+ private static $timeInfo = array(
+ 'total_time' => true,
+ 'namelookup_time' => true,
+ 'connect_time' => true,
+ 'pretransfer_time' => true,
+ 'starttransfer_time' => true,
+ 'redirect_time' => true,
+ );
+
+ public function __construct(IOInterface $io, Config $config, array $options = array(), $disableTls = false)
+ {
+ $this->io = $io;
+ $this->config = $config;
+
+ $this->multiHandle = $mh = curl_multi_init();
+ if (function_exists('curl_multi_setopt')) {
+ curl_multi_setopt($mh, CURLMOPT_PIPELINING, PHP_VERSION_ID >= 70400 ? /* CURLPIPE_MULTIPLEX */ 2 : /*CURLPIPE_HTTP1 | CURLPIPE_MULTIPLEX*/ 3);
+ if (defined('CURLMOPT_MAX_HOST_CONNECTIONS')) {
+ curl_multi_setopt($mh, CURLMOPT_MAX_HOST_CONNECTIONS, 8);
+ }
+ }
+
+ if (function_exists('curl_share_init')) {
+ $this->shareHandle = $sh = curl_share_init();
+ curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
+ curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
+ curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
+ }
+
+ $this->authHelper = new AuthHelper($io, $config);
+ }
+
+ public function download($resolve, $reject, $origin, $url, $options, $copyTo = null)
+ {
+ $attributes = array();
+ if (isset($options['retry-auth-failure'])) {
+ $attributes['retryAuthFailure'] = $options['retry-auth-failure'];
+ unset($options['retry-auth-failure']);
+ }
+
+ return $this->initDownload($resolve, $reject, $origin, $url, $options, $copyTo, $attributes);
+ }
+
+ private function initDownload($resolve, $reject, $origin, $url, $options, $copyTo = null, array $attributes = array())
+ {
+ $attributes = array_merge(array(
+ 'retryAuthFailure' => true,
+ 'redirects' => 0,
+ 'storeAuth' => false,
+ ), $attributes);
+
+ $originalOptions = $options;
+
+ // check URL can be accessed (i.e. is not insecure), but allow insecure Packagist calls to $hashed providers as file integrity is verified with sha256
+ if (!preg_match('{^http://(repo\.)?packagist\.org/p/}', $url) || (false === strpos($url, '$') && false === strpos($url, '%24'))) {
+ $this->config->prohibitUrlByConfig($url, $this->io);
+ }
+
+ $curlHandle = curl_init();
+ $headerHandle = fopen('php://temp/maxmemory:32768', 'w+b');
+
+ if ($copyTo) {
+ $errorMessage = '';
+ set_error_handler(function ($code, $msg) use (&$errorMessage) {
+ if ($errorMessage) {
+ $errorMessage .= "\n";
+ }
+ $errorMessage .= preg_replace('{^fopen\(.*?\): }', '', $msg);
+ });
+ $bodyHandle = fopen($copyTo.'~', 'w+b');
+ restore_error_handler();
+ if (!$bodyHandle) {
+ throw new TransportException('The "'.$url.'" file could not be written to '.$copyTo.': '.$errorMessage);
+ }
+ } else {
+ $bodyHandle = @fopen('php://temp/maxmemory:524288', 'w+b');
+ }
+
+ curl_setopt($curlHandle, CURLOPT_URL, $url);
+ curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, false);
+ //curl_setopt($curlHandle, CURLOPT_DNS_USE_GLOBAL_CACHE, false);
+ curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 10);
+ curl_setopt($curlHandle, CURLOPT_TIMEOUT, 60);
+ curl_setopt($curlHandle, CURLOPT_WRITEHEADER, $headerHandle);
+ curl_setopt($curlHandle, CURLOPT_FILE, $bodyHandle);
+ curl_setopt($curlHandle, CURLOPT_ENCODING, "gzip");
+ curl_setopt($curlHandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
+ if (defined('CURLOPT_SSL_FALSESTART')) {
+ curl_setopt($curlHandle, CURLOPT_SSL_FALSESTART, true);
+ }
+ if (function_exists('curl_share_init')) {
+ curl_setopt($curlHandle, CURLOPT_SHARE, $this->shareHandle);
+ }
+
+ if (!isset($options['http']['header'])) {
+ $options['http']['header'] = array();
+ }
+
+ $options['http']['header'] = array_diff($options['http']['header'], array('Connection: close'));
+ $options['http']['header'][] = 'Connection: keep-alive';
+
+ $version = curl_version();
+ $features = $version['features'];
+ if (0 === strpos($url, 'https://') && \defined('CURL_VERSION_HTTP2') && \defined('CURL_HTTP_VERSION_2_0') && (CURL_VERSION_HTTP2 & $features)) {
+ curl_setopt($curlHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
+ }
+
+ $options['http']['header'] = $this->authHelper->addAuthenticationHeader($options['http']['header'], $origin, $url);
+ $options = StreamContextFactory::initOptions($url, $options);
+
+ foreach (self::$options as $type => $curlOptions) {
+ foreach ($curlOptions as $name => $curlOption) {
+ if (isset($options[$type][$name])) {
+ curl_setopt($curlHandle, $curlOption, $options[$type][$name]);
+ }
+ }
+ }
+
+ $progress = array_diff_key(curl_getinfo($curlHandle), self::$timeInfo);
+
+ $this->jobs[(int) $curlHandle] = array(
+ 'url' => $url,
+ 'origin' => $origin,
+ 'attributes' => $attributes,
+ 'options' => $originalOptions,
+ 'progress' => $progress,
+ 'curlHandle' => $curlHandle,
+ 'filename' => $copyTo,
+ 'headerHandle' => $headerHandle,
+ 'bodyHandle' => $bodyHandle,
+ 'resolve' => $resolve,
+ 'reject' => $reject,
+ );
+
+ $usingProxy = !empty($options['http']['proxy']) ? ' using proxy ' . $options['http']['proxy'] : '';
+ $ifModified = false !== strpos(strtolower(implode(',', $options['http']['header'])), 'if-modified-since:') ? ' if modified' : '';
+ if ($attributes['redirects'] === 0) {
+ $this->io->writeError('Downloading ' . Url::sanitize($url) . $usingProxy . $ifModified, true, IOInterface::DEBUG);
+ }
+
+ $this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $curlHandle));
+// TODO progress
+ //$params['notification'](STREAM_NOTIFY_RESOLVE, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0, false);
+ }
+
+ public function tick()
+ {
+ if (!$this->jobs) {
+ return;
+ }
+
+ $active = true;
+ $this->checkCurlResult(curl_multi_exec($this->multiHandle, $active));
+ if (-1 === curl_multi_select($this->multiHandle, $this->selectTimeout)) {
+ // sleep in case select returns -1 as it can happen on old php versions or some platforms where curl does not manage to do the select
+ usleep(150);
+ }
+
+ while ($progress = curl_multi_info_read($this->multiHandle)) {
+ $curlHandle = $progress['handle'];
+ $i = (int) $curlHandle;
+ if (!isset($this->jobs[$i])) {
+ continue;
+ }
+
+ $progress = array_diff_key(curl_getinfo($curlHandle), self::$timeInfo);
+ $job = $this->jobs[$i];
+ unset($this->jobs[$i]);
+ curl_multi_remove_handle($this->multiHandle, $curlHandle);
+ $error = curl_error($curlHandle);
+ $errno = curl_errno($curlHandle);
+ curl_close($curlHandle);
+
+ $headers = null;
+ $statusCode = null;
+ $response = null;
+ try {
+// TODO progress
+ //$this->onProgress($curlHandle, $job['callback'], $progress, $job['progress']);
+ if (CURLE_OK !== $errno || $error) {
+ throw new TransportException($error);
+ }
+
+ $statusCode = $progress['http_code'];
+ rewind($job['headerHandle']);
+ $headers = explode("\r\n", rtrim(stream_get_contents($job['headerHandle'])));
+ fclose($job['headerHandle']);
+
+ // prepare response object
+ if ($job['filename']) {
+ $contents = $job['filename'].'~';
+ if ($statusCode >= 300) {
+ rewind($job['bodyHandle']);
+ $contents = stream_get_contents($job['bodyHandle']);
+ }
+ $response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents);
+ $this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG);
+ } else {
+ rewind($job['bodyHandle']);
+ $contents = stream_get_contents($job['bodyHandle']);
+ $response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents);
+ $this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG);
+ }
+ fclose($job['bodyHandle']);
+
+ if ($response->getStatusCode() >= 400 && $response->getHeader('content-type') === 'application/json') {
+ HttpDownloader::outputWarnings($this->io, $job['origin'], json_decode($response->getBody(), true));
+ }
+
+ $result = $this->isAuthenticatedRetryNeeded($job, $response);
+ if ($result['retry']) {
+ $this->restartJob($job, $job['url'], array('storeAuth' => $result['storeAuth']));
+ continue;
+ }
+
+ // handle 3xx redirects, 304 Not Modified is excluded
+ if ($statusCode >= 300 && $statusCode <= 399 && $statusCode !== 304 && $job['attributes']['redirects'] < $this->maxRedirects) {
+ $location = $this->handleRedirect($job, $response);
+ if ($location) {
+ $this->restartJob($job, $location, array('redirects' => $job['attributes']['redirects'] + 1));
+ continue;
+ }
+ }
+
+ // fail 4xx and 5xx responses and capture the response
+ if ($statusCode >= 400 && $statusCode <= 599) {
+ throw $this->failResponse($job, $response, $response->getStatusMessage());
+// TODO progress
+// $this->io->overwriteError("Downloading (failed)", false);
+ }
+
+ if ($job['attributes']['storeAuth']) {
+ $this->authHelper->storeAuth($job['origin'], $job['attributes']['storeAuth']);
+ }
+
+ // resolve promise
+ if ($job['filename']) {
+ rename($job['filename'].'~', $job['filename']);
+ call_user_func($job['resolve'], $response);
+ } else {
+ call_user_func($job['resolve'], $response);
+ }
+ } catch (\Exception $e) {
+ if ($e instanceof TransportException && $headers) {
+ $e->setHeaders($headers);
+ $e->setStatusCode($statusCode);
+ }
+ if ($e instanceof TransportException && $response) {
+ $e->setResponse($response->getBody());
+ }
+
+ if (is_resource($job['headerHandle'])) {
+ fclose($job['headerHandle']);
+ }
+ if (is_resource($job['bodyHandle'])) {
+ fclose($job['bodyHandle']);
+ }
+ if ($job['filename']) {
+ @unlink($job['filename'].'~');
+ }
+ call_user_func($job['reject'], $e);
+ }
+ }
+
+ foreach ($this->jobs as $i => $curlHandle) {
+ if (!isset($this->jobs[$i])) {
+ continue;
+ }
+ $curlHandle = $this->jobs[$i]['curlHandle'];
+ $progress = array_diff_key(curl_getinfo($curlHandle), self::$timeInfo);
+
+ if ($this->jobs[$i]['progress'] !== $progress) {
+ $previousProgress = $this->jobs[$i]['progress'];
+ $this->jobs[$i]['progress'] = $progress;
+
+ // TODO
+ //$this->onProgress($curlHandle, $this->jobs[$i]['callback'], $progress, $previousProgress);
+ }
+ }
+ }
+
+ private function handleRedirect(array $job, Response $response)
+ {
+ if ($locationHeader = $response->getHeader('location')) {
+ if (parse_url($locationHeader, PHP_URL_SCHEME)) {
+ // Absolute URL; e.g. https://example.com/composer
+ $targetUrl = $locationHeader;
+ } elseif (parse_url($locationHeader, PHP_URL_HOST)) {
+ // Scheme relative; e.g. //example.com/foo
+ $targetUrl = parse_url($job['url'], PHP_URL_SCHEME).':'.$locationHeader;
+ } elseif ('/' === $locationHeader[0]) {
+ // Absolute path; e.g. /foo
+ $urlHost = parse_url($job['url'], PHP_URL_HOST);
+
+ // Replace path using hostname as an anchor.
+ $targetUrl = preg_replace('{^(.+(?://|@)'.preg_quote($urlHost).'(?::\d+)?)(?:[/\?].*)?$}', '\1'.$locationHeader, $job['url']);
+ } else {
+ // Relative path; e.g. foo
+ // This actually differs from PHP which seems to add duplicate slashes.
+ $targetUrl = preg_replace('{^(.+/)[^/?]*(?:\?.*)?$}', '\1'.$locationHeader, $job['url']);
+ }
+ }
+
+ if (!empty($targetUrl)) {
+ $this->io->writeError(sprintf('Following redirect (%u) %s', $job['attributes']['redirects'] + 1, Url::sanitize($targetUrl)), true, IOInterface::DEBUG);
+
+ return $targetUrl;
+ }
+
+ throw new TransportException('The "'.$job['url'].'" file could not be downloaded, got redirect without Location ('.$response->getStatusMessage().')');
+ }
+
+ private function isAuthenticatedRetryNeeded(array $job, Response $response)
+ {
+ if (in_array($response->getStatusCode(), array(401, 403)) && $job['attributes']['retryAuthFailure']) {
+ $result = $this->authHelper->promptAuthIfNeeded($job['url'], $job['origin'], $response->getStatusCode(), $response->getStatusMessage(), $response->getHeaders());
+
+ if ($result['retry']) {
+ return $result;
+ }
+ }
+
+ $locationHeader = $response->getHeader('location');
+ $needsAuthRetry = false;
+
+ // check for bitbucket login page asking to authenticate
+ if (
+ $job['origin'] === 'bitbucket.org'
+ && !$this->authHelper->isPublicBitBucketDownload($job['url'])
+ && substr($job['url'], -4) === '.zip'
+ && (!$locationHeader || substr($locationHeader, -4) !== '.zip')
+ && preg_match('{^text/html\b}i', $response->getHeader('content-type'))
+ ) {
+ $needsAuthRetry = 'Bitbucket requires authentication and it was not provided';
+ }
+
+ // check for gitlab 404 when downloading archives
+ if (
+ $response->getStatusCode() === 404
+ && $this->config && in_array($job['origin'], $this->config->get('gitlab-domains'), true)
+ && false !== strpos($job['url'], 'archive.zip')
+ ) {
+ $needsAuthRetry = 'GitLab requires authentication and it was not provided';
+ }
+
+ if ($needsAuthRetry) {
+ if ($job['attributes']['retryAuthFailure']) {
+ $result = $this->authHelper->promptAuthIfNeeded($job['url'], $job['origin'], 401);
+ if ($result['retry']) {
+ return $result;
+ }
+ }
+
+ throw $this->failResponse($job, $response, $needsAuthRetry);
+ }
+
+ return array('retry' => false, 'storeAuth' => false);
+ }
+
+ private function restartJob(array $job, $url, array $attributes = array())
+ {
+ if ($job['filename']) {
+ @unlink($job['filename'].'~');
+ }
+
+ $attributes = array_merge($job['attributes'], $attributes);
+ $origin = Url::getOrigin($this->config, $url);
+
+ $this->initDownload($job['resolve'], $job['reject'], $origin, $url, $job['options'], $job['filename'], $attributes);
+ }
+
+ private function failResponse(array $job, Response $response, $errorMessage)
+ {
+ if ($job['filename']) {
+ @unlink($job['filename'].'~');
+ }
+
+ return new TransportException('The "'.$job['url'].'" file could not be downloaded ('.$errorMessage.')', $response->getStatusCode());
+ }
+
+ private function onProgress($curlHandle, callable $notify, array $progress, array $previousProgress)
+ {
+ // TODO add support for progress
+ if (300 <= $progress['http_code'] && $progress['http_code'] < 400) {
+ return;
+ }
+ if ($previousProgress['download_content_length'] < $progress['download_content_length']) {
+ $notify(STREAM_NOTIFY_FILE_SIZE_IS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, (int) $progress['download_content_length'], false);
+ }
+ if ($previousProgress['size_download'] < $progress['size_download']) {
+ $notify(STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, (int) $progress['size_download'], (int) $progress['download_content_length'], false);
+ }
+ }
+
+ private function checkCurlResult($code)
+ {
+ if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) {
+ throw new \RuntimeException(isset($this->multiErrors[$code])
+ ? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}"
+ : 'Unexpected cURL error: ' . $code
+ );
+ }
+ }
+}
diff --git a/src/Composer/Util/Http/Response.php b/src/Composer/Util/Http/Response.php
new file mode 100644
index 000000000..1b4581331
--- /dev/null
+++ b/src/Composer/Util/Http/Response.php
@@ -0,0 +1,104 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Util\Http;
+
+use Composer\Json\JsonFile;
+
+class Response
+{
+ private $request;
+ private $code;
+ private $headers;
+ private $body;
+
+ public function __construct(array $request, $code, array $headers, $body)
+ {
+ if (!isset($request['url'])) {
+ throw new \LogicException('url key missing from request array');
+ }
+ $this->request = $request;
+ $this->code = (int) $code;
+ $this->headers = $headers;
+ $this->body = $body;
+ }
+
+ public function getStatusCode()
+ {
+ return $this->code;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getStatusMessage()
+ {
+ $value = null;
+ foreach ($this->headers as $header) {
+ if (preg_match('{^HTTP/\S+ \d+}i', $header)) {
+ // In case of redirects, headers contain the headers of all responses
+ // so we can not return directly and need to keep iterating
+ $value = $header;
+ }
+ }
+
+ return $value;
+ }
+
+ public function getHeaders()
+ {
+ return $this->headers;
+ }
+
+ public function getHeader($name)
+ {
+ return self::findHeaderValue($this->headers, $name);
+ }
+
+ public function getBody()
+ {
+ return $this->body;
+ }
+
+ public function decodeJson()
+ {
+ return JsonFile::parseJson($this->body, $this->request['url']);
+ }
+
+ public function collect()
+ {
+ $this->request = $this->code = $this->headers = $this->body = null;
+ }
+
+ /**
+ * @param array $headers array of returned headers like from getLastHeaders()
+ * @param string $name header name (case insensitive)
+ * @return string|null
+ */
+ public static function findHeaderValue(array $headers, $name)
+ {
+ $value = null;
+ foreach ($headers as $header) {
+ if (preg_match('{^'.preg_quote($name).':\s*(.+?)\s*$}i', $header, $match)) {
+ $value = $match[1];
+ } elseif (preg_match('{^HTTP/}i', $header)) {
+ // TODO ideally redirects would be handled in CurlDownloader/RemoteFilesystem and this becomes unnecessary
+ //
+ // In case of redirects, http_response_headers contains the headers of all responses
+ // so we reset the flag when a new response is being parsed as we are only interested in the last response
+ $value = null;
+ }
+ }
+
+ return $value;
+ }
+}
diff --git a/src/Composer/Util/HttpDownloader.php b/src/Composer/Util/HttpDownloader.php
new file mode 100644
index 000000000..8a117a232
--- /dev/null
+++ b/src/Composer/Util/HttpDownloader.php
@@ -0,0 +1,339 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Util;
+
+use Composer\Config;
+use Composer\IO\IOInterface;
+use Composer\Downloader\TransportException;
+use Composer\CaBundle\CaBundle;
+use Composer\Util\Http\Response;
+use Composer\Composer;
+use Composer\Package\Version\VersionParser;
+use Composer\Semver\Constraint\Constraint;
+use React\Promise\Promise;
+
+/**
+ * @author Jordi Boggiano
+ */
+class HttpDownloader
+{
+ const STATUS_QUEUED = 1;
+ const STATUS_STARTED = 2;
+ const STATUS_COMPLETED = 3;
+ const STATUS_FAILED = 4;
+
+ private $io;
+ private $config;
+ private $jobs = array();
+ private $options = array();
+ private $runningJobs = 0;
+ private $maxJobs = 10;
+ private $lastProgress;
+ private $disableTls = false;
+ private $curl;
+ private $rfs;
+ private $idGen = 0;
+ private $disabled;
+
+ /**
+ * @param IOInterface $io The IO instance
+ * @param Config $config The config
+ * @param array $options The options
+ * @param bool $disableTls
+ */
+ public function __construct(IOInterface $io, Config $config, array $options = array(), $disableTls = false)
+ {
+ $this->io = $io;
+
+ $this->disabled = (bool) getenv('COMPOSER_DISABLE_NETWORK');
+
+ // Setup TLS options
+ // The cafile option can be set via config.json
+ if ($disableTls === false) {
+ $this->options = StreamContextFactory::getTlsDefaults($options, $io);
+ } else {
+ $this->disableTls = true;
+ }
+
+ // handle the other externally set options normally.
+ $this->options = array_replace_recursive($this->options, $options);
+ $this->config = $config;
+
+ // TODO enable curl only on 5.6+ if older versions cause any problem
+ if (extension_loaded('curl')) {
+ $this->curl = new Http\CurlDownloader($io, $config, $options, $disableTls);
+ }
+
+ $this->rfs = new RemoteFilesystem($io, $config, $options, $disableTls);
+ }
+
+ public function get($url, $options = array())
+ {
+ list($job, $promise) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => false), true);
+ $this->wait($job['id']);
+
+ return $this->getResponse($job['id']);
+ }
+
+ public function add($url, $options = array())
+ {
+ list($job, $promise) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => false));
+
+ return $promise;
+ }
+
+ public function copy($url, $to, $options = array())
+ {
+ list($job, $promise) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => $to), true);
+ $this->wait($job['id']);
+
+ return $this->getResponse($job['id']);
+ }
+
+ public function addCopy($url, $to, $options = array())
+ {
+ list($job, $promise) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => $to));
+
+ return $promise;
+ }
+
+ /**
+ * Retrieve the options set in the constructor
+ *
+ * @return array Options
+ */
+ public function getOptions()
+ {
+ return $this->options;
+ }
+
+ /**
+ * Merges new options
+ *
+ * @return void
+ */
+ public function setOptions(array $options)
+ {
+ $this->options = array_replace_recursive($this->options, $options);
+ }
+
+ private function addJob($request, $sync = false)
+ {
+ $job = array(
+ 'id' => $this->idGen++,
+ 'status' => self::STATUS_QUEUED,
+ 'request' => $request,
+ 'sync' => $sync,
+ 'origin' => Url::getOrigin($this->config, $request['url']),
+ );
+
+ // capture username/password from URL if there is one
+ if (preg_match('{^https?://([^:/]+):([^@/]+)@([^/]+)}i', $request['url'], $match)) {
+ $this->io->setAuthentication($job['origin'], rawurldecode($match[1]), rawurldecode($match[2]));
+ }
+
+ $rfs = $this->rfs;
+
+ if ($this->curl && preg_match('{^https?://}i', $job['request']['url'])) {
+ $resolver = function ($resolve, $reject) use (&$job) {
+ $job['status'] = HttpDownloader::STATUS_QUEUED;
+ $job['resolve'] = $resolve;
+ $job['reject'] = $reject;
+ };
+ } else {
+ $resolver = function ($resolve, $reject) use (&$job, $rfs) {
+ // start job
+ $url = $job['request']['url'];
+ $options = $job['request']['options'];
+
+ $job['status'] = HttpDownloader::STATUS_STARTED;
+
+ if ($job['request']['copyTo']) {
+ $result = $rfs->copy($job['origin'], $url, $job['request']['copyTo'], false /* TODO progress */, $options);
+
+ $headers = $rfs->getLastHeaders();
+ $response = new Http\Response($job['request'], $rfs->findStatusCode($headers), $headers, $job['request']['copyTo'].'~');
+
+ $resolve($response);
+ } else {
+ $body = $rfs->getContents($job['origin'], $url, false /* TODO progress */, $options);
+ $headers = $rfs->getLastHeaders();
+ $response = new Http\Response($job['request'], $rfs->findStatusCode($headers), $headers, $body);
+
+ $resolve($response);
+ }
+ };
+ }
+
+ $downloader = $this;
+ $io = $this->io;
+
+ $canceler = function () {};
+
+ $promise = new Promise($resolver, $canceler);
+ $promise->then(function ($response) use (&$job, $downloader) {
+ $job['status'] = HttpDownloader::STATUS_COMPLETED;
+ $job['response'] = $response;
+
+ // TODO 3.0 this should be done directly on $this when PHP 5.3 is dropped
+ $downloader->markJobDone();
+ $downloader->scheduleNextJob();
+
+ return $response;
+ }, function ($e) use (&$job, $downloader) {
+ $job['status'] = HttpDownloader::STATUS_FAILED;
+ $job['exception'] = $e;
+
+ $downloader->markJobDone();
+ $downloader->scheduleNextJob();
+
+ throw $e;
+ });
+ $this->jobs[$job['id']] =& $job;
+
+ if ($this->runningJobs < $this->maxJobs) {
+ $this->startJob($job['id']);
+ }
+
+ return array($job, $promise);
+ }
+
+ private function startJob($id)
+ {
+ $job =& $this->jobs[$id];
+ if ($job['status'] !== self::STATUS_QUEUED) {
+ return;
+ }
+
+ // start job
+ $job['status'] = self::STATUS_STARTED;
+ $this->runningJobs++;
+
+ $resolve = $job['resolve'];
+ $reject = $job['reject'];
+ $url = $job['request']['url'];
+ $options = $job['request']['options'];
+ $origin = $job['origin'];
+
+ if ($this->disabled) {
+ if (isset($job['request']['options']['http']['header']) && false !== stripos(implode('', $job['request']['options']['http']['header']), 'if-modified-since')) {
+ $resolve(new Response(array('url' => $url), 304, array(), ''));
+ } else {
+ $e = new TransportException('Network disabled, request canceled: '.$url, 499);
+ $e->setStatusCode(499);
+ $reject($e);
+ }
+ return;
+ }
+
+ if ($job['request']['copyTo']) {
+ $this->curl->download($resolve, $reject, $origin, $url, $options, $job['request']['copyTo']);
+ } else {
+ $this->curl->download($resolve, $reject, $origin, $url, $options);
+ }
+ }
+
+ /**
+ * @private
+ */
+ public function markJobDone()
+ {
+ $this->runningJobs--;
+ }
+
+ /**
+ * @private
+ */
+ public function scheduleNextJob()
+ {
+ foreach ($this->jobs as $job) {
+ if ($job['status'] === self::STATUS_QUEUED) {
+ $this->startJob($job['id']);
+ if ($this->runningJobs >= $this->maxJobs) {
+ return;
+ }
+ }
+ }
+ }
+
+ public function wait($index = null, $progress = false)
+ {
+ while (true) {
+ if ($this->curl) {
+ $this->curl->tick();
+ }
+
+ if (null !== $index) {
+ if ($this->jobs[$index]['status'] === self::STATUS_COMPLETED || $this->jobs[$index]['status'] === self::STATUS_FAILED) {
+ return;
+ }
+ } else {
+ $done = true;
+ foreach ($this->jobs as $job) {
+ if (!in_array($job['status'], array(self::STATUS_COMPLETED, self::STATUS_FAILED), true)) {
+ $done = false;
+ break;
+ } elseif (!$job['sync']) {
+ unset($this->jobs[$job['id']]);
+ }
+ }
+ if ($done) {
+ return;
+ }
+ }
+
+ usleep(1000);
+ }
+ }
+
+ private function getResponse($index)
+ {
+ if (!isset($this->jobs[$index])) {
+ throw new \LogicException('Invalid request id');
+ }
+
+ if ($this->jobs[$index]['status'] === self::STATUS_FAILED) {
+ throw $this->jobs[$index]['exception'];
+ }
+
+ if (!isset($this->jobs[$index]['response'])) {
+ throw new \LogicException('Response not available yet, call wait() first');
+ }
+
+ $resp = $this->jobs[$index]['response'];
+
+ unset($this->jobs[$index]);
+
+ return $resp;
+ }
+
+ public static function outputWarnings(IOInterface $io, $url, $data)
+ {
+ foreach (array('warning', 'info') as $type) {
+ if (empty($data[$type])) {
+ continue;
+ }
+
+ if (!empty($data[$type . '-versions'])) {
+ $versionParser = new VersionParser();
+ $constraint = $versionParser->parseConstraints($data[$type . '-versions']);
+ $composer = new Constraint('==', $versionParser->normalize(Composer::getVersion()));
+ if (!$constraint->matches($composer)) {
+ continue;
+ }
+ }
+
+ $io->writeError('<'.$type.'>'.ucfirst($type).' from '.$url.': '.$data[$type].''.$type.'>');
+ }
+ }
+}
diff --git a/src/Composer/Util/Loop.php b/src/Composer/Util/Loop.php
new file mode 100644
index 000000000..dfaa2ac53
--- /dev/null
+++ b/src/Composer/Util/Loop.php
@@ -0,0 +1,48 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Util;
+
+use Composer\Util\HttpDownloader;
+use React\Promise\Promise;
+
+/**
+ * @author Jordi Boggiano
+ */
+class Loop
+{
+ private $httpDownloader;
+
+ public function __construct(HttpDownloader $httpDownloader)
+ {
+ $this->httpDownloader = $httpDownloader;
+ }
+
+ public function wait(array $promises)
+ {
+ /** @var \Exception|null */
+ $uncaught = null;
+
+ \React\Promise\all($promises)->then(
+ function () { },
+ function ($e) use (&$uncaught) {
+ $uncaught = $e;
+ }
+ );
+
+ $this->httpDownloader->wait();
+
+ if ($uncaught) {
+ throw $uncaught;
+ }
+ }
+}
diff --git a/src/Composer/Util/MetadataMinifier.php b/src/Composer/Util/MetadataMinifier.php
new file mode 100644
index 000000000..ba4cc0a93
--- /dev/null
+++ b/src/Composer/Util/MetadataMinifier.php
@@ -0,0 +1,78 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Util;
+
+class MetadataMinifier
+{
+ public static function expand(array $versions)
+ {
+ $expanded = array();
+ $expandedVersion = null;
+ foreach ($versions as $versionData) {
+ if (!$expandedVersion) {
+ $expandedVersion = $versionData;
+ $expanded[] = $expandedVersion;
+ continue;
+ }
+
+ // add any changes from the previous version to the expanded one
+ foreach ($versionData as $key => $val) {
+ if ($val === '__unset') {
+ unset($expandedVersion[$key]);
+ } else {
+ $expandedVersion[$key] = $val;
+ }
+ }
+
+ $expanded[] = $expandedVersion;
+ }
+
+ return $expanded;
+ }
+
+ public static function minify(array $versions)
+ {
+ $minifiedVersions = array();
+
+ $lastKnownVersionData = null;
+ foreach ($versions as $version) {
+ if (!$lastKnownVersionData) {
+ $lastKnownVersionData = $version;
+ $minifiedVersions[] = $version;
+ continue;
+ }
+
+ $minifiedVersion = array();
+
+ // add any changes from the previous version
+ foreach ($version as $key => $val) {
+ if (!isset($lastKnownVersionData[$key]) || $lastKnownVersionData[$key] !== $val) {
+ $minifiedVersion[$key] = $val;
+ $lastKnownVersionData[$key] = $val;
+ }
+ }
+
+ // store any deletions from the previous version for keys missing in current one
+ foreach ($lastKnownVersionData as $key => $val) {
+ if (!isset($version[$key])) {
+ $minifiedVersion[$key] = "__unset";
+ unset($lastKnownVersionData[$key]);
+ }
+ }
+
+ $minifiedVersions[] = $minifiedVersion;
+ }
+
+ return $minifiedVersions;
+ }
+}
diff --git a/src/Composer/Util/ProcessExecutor.php b/src/Composer/Util/ProcessExecutor.php
index 83f19cf2d..a30a04d15 100644
--- a/src/Composer/Util/ProcessExecutor.php
+++ b/src/Composer/Util/ProcessExecutor.php
@@ -15,6 +15,7 @@ namespace Composer\Util;
use Composer\IO\IOInterface;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\ProcessUtils;
+use Symfony\Component\Process\Exception\RuntimeException;
/**
* @author Robert Schönthal
@@ -42,6 +43,27 @@ class ProcessExecutor
* @return int statuscode
*/
public function execute($command, &$output = null, $cwd = null)
+ {
+ if (func_num_args() > 1) {
+ return $this->doExecute($command, $cwd, false, $output);
+ }
+
+ return $this->doExecute($command, $cwd, false);
+ }
+
+ /**
+ * runs a process on the commandline in TTY mode
+ *
+ * @param string $command the command to execute
+ * @param string $cwd the working directory
+ * @return int statuscode
+ */
+ public function executeTty($command, $cwd = null)
+ {
+ return $this->doExecute($command, $cwd, true);
+ }
+
+ private function doExecute($command, $cwd, $tty, &$output = null)
{
if ($this->io && $this->io->isDebug()) {
$safeCommand = preg_replace_callback('{://(?P[^:/\s]+):(?P[^@\s/]+)@}i', function ($m) {
@@ -61,7 +83,7 @@ class ProcessExecutor
$cwd = realpath(getcwd());
}
- $this->captureOutput = func_num_args() > 1;
+ $this->captureOutput = func_num_args() > 3;
$this->errorOutput = null;
// TODO in v3, commands should be passed in as arrays of cmd + args
@@ -70,6 +92,13 @@ class ProcessExecutor
} else {
$process = new Process($command, $cwd, null, null, static::getTimeout());
}
+ if (!Platform::isWindows() && $tty) {
+ try {
+ $process->setTty(true);
+ } catch (RuntimeException $e) {
+ // ignore TTY enabling errors
+ }
+ }
$callback = is_callable($output) ? $output : array($this, 'outputHandler');
$process->run($callback);
@@ -112,18 +141,10 @@ class ProcessExecutor
return;
}
- if (method_exists($this->io, 'writeRaw')) {
- if (Process::ERR === $type) {
- $this->io->writeErrorRaw($buffer, false);
- } else {
- $this->io->writeRaw($buffer, false);
- }
+ if (Process::ERR === $type) {
+ $this->io->writeErrorRaw($buffer, false);
} else {
- if (Process::ERR === $type) {
- $this->io->writeError($buffer, false);
- } else {
- $this->io->write($buffer, false);
- }
+ $this->io->writeRaw($buffer, false);
}
}
diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php
index 9328d5bdd..6312deabc 100644
--- a/src/Composer/Util/RemoteFilesystem.php
+++ b/src/Composer/Util/RemoteFilesystem.php
@@ -13,13 +13,11 @@
namespace Composer\Util;
use Composer\Config;
-use Composer\Composer;
-use Composer\Semver\Constraint\Constraint;
-use Composer\Package\Version\VersionParser;
use Composer\IO\IOInterface;
use Composer\Downloader\TransportException;
use Composer\CaBundle\CaBundle;
-use Psr\Log\LoggerInterface;
+use Composer\Util\HttpDownloader;
+use Composer\Util\Http\Response;
/**
* @author François Pluchino
@@ -44,10 +42,10 @@ class RemoteFilesystem
private $retryAuthFailure;
private $lastHeaders;
private $storeAuth;
+ private $authHelper;
private $degradedMode = false;
private $redirects;
private $maxRedirects = 20;
- private $displayedOriginAuthentications = array();
/**
* Constructor.
@@ -57,14 +55,14 @@ class RemoteFilesystem
* @param array $options The options
* @param bool $disableTls
*/
- public function __construct(IOInterface $io, Config $config = null, array $options = array(), $disableTls = false)
+ public function __construct(IOInterface $io, Config $config, array $options = array(), $disableTls = false)
{
$this->io = $io;
// Setup TLS options
// The cafile option can be set via config.json
if ($disableTls === false) {
- $this->options = $this->getTlsDefaults($options);
+ $this->options = StreamContextFactory::getTlsDefaults($options, $io);
} else {
$this->disableTls = true;
}
@@ -72,6 +70,7 @@ class RemoteFilesystem
// handle the other externally set options normally.
$this->options = array_replace_recursive($this->options, $options);
$this->config = $config;
+ $this->authHelper = new AuthHelper($io, $config);
}
/**
@@ -145,32 +144,11 @@ class RemoteFilesystem
return $this->lastHeaders;
}
- /**
- * @param array $headers array of returned headers like from getLastHeaders()
- * @param string $name header name (case insensitive)
- * @return string|null
- */
- public function findHeaderValue(array $headers, $name)
- {
- $value = null;
- foreach ($headers as $header) {
- if (preg_match('{^'.$name.':\s*(.+?)\s*$}i', $header, $match)) {
- $value = $match[1];
- } elseif (preg_match('{^HTTP/}i', $header)) {
- // In case of redirects, http_response_headers contains the headers of all responses
- // so we reset the flag when a new response is being parsed as we are only interested in the last response
- $value = null;
- }
- }
-
- return $value;
- }
-
/**
* @param array $headers array of returned headers like from getLastHeaders()
* @return int|null
*/
- public function findStatusCode(array $headers)
+ public static function findStatusCode(array $headers)
{
$value = null;
foreach ($headers as $header) {
@@ -218,27 +196,6 @@ class RemoteFilesystem
*/
protected function get($originUrl, $fileUrl, $additionalOptions = array(), $fileName = null, $progress = true)
{
- if (strpos($originUrl, '.github.com') === (strlen($originUrl) - 11)) {
- $originUrl = 'github.com';
- }
-
- // Gitlab can be installed in a non-root context (i.e. gitlab.com/foo). When downloading archives the originUrl
- // is the host without the path, so we look for the registered gitlab-domains matching the host here
- if (
- $this->config
- && is_array($this->config->get('gitlab-domains'))
- && false === strpos($originUrl, '/')
- && !in_array($originUrl, $this->config->get('gitlab-domains'))
- ) {
- foreach ($this->config->get('gitlab-domains') as $gitlabDomain) {
- if (0 === strpos($gitlabDomain, $originUrl)) {
- $originUrl = $gitlabDomain;
- break;
- }
- }
- unset($gitlabDomain);
- }
-
$this->scheme = parse_url($fileUrl, PHP_URL_SCHEME);
$this->bytesMax = 0;
$this->originUrl = $originUrl;
@@ -250,11 +207,6 @@ class RemoteFilesystem
$this->lastHeaders = array();
$this->redirects = 1; // The first request counts.
- // capture username/password from URL if there is one
- if (preg_match('{^https?://([^:/]+):([^@/]+)@([^/]+)}i', $fileUrl, $match)) {
- $this->io->setAuthentication($originUrl, rawurldecode($match[1]), rawurldecode($match[2]));
- }
-
$tempAdditionalOptions = $additionalOptions;
if (isset($tempAdditionalOptions['retry-auth-failure'])) {
$this->retryAuthFailure = (bool) $tempAdditionalOptions['retry-auth-failure'];
@@ -275,14 +227,6 @@ class RemoteFilesystem
$origFileUrl = $fileUrl;
- if (isset($options['github-token'])) {
- // only add the access_token if it is actually a github URL (in case we were redirected to S3)
- if (preg_match('{^https?://([a-z0-9-]+\.)*github\.com/}', $fileUrl)) {
- $options['http']['header'][] = 'Authorization: token '.$options['github-token'];
- }
- unset($options['github-token']);
- }
-
if (isset($options['gitlab-token'])) {
$fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['gitlab-token'];
unset($options['gitlab-token']);
@@ -302,7 +246,7 @@ class RemoteFilesystem
$actualContextOptions = stream_context_get_options($ctx);
$usingProxy = !empty($actualContextOptions['http']['proxy']) ? ' using proxy ' . $actualContextOptions['http']['proxy'] : '';
- $this->io->writeError((substr($origFileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $this->stripCredentialsFromUrl($origFileUrl) . $usingProxy, true, IOInterface::DEBUG);
+ $this->io->writeError((substr($origFileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . Url::sanitize($origFileUrl) . $usingProxy, true, IOInterface::DEBUG);
unset($origFileUrl, $actualContextOptions);
// Check for secure HTTP, but allow insecure Packagist calls to $hashed providers as file integrity is verified with sha256
@@ -330,16 +274,16 @@ class RemoteFilesystem
if (!empty($http_response_header[0])) {
$statusCode = $this->findStatusCode($http_response_header);
- if ($statusCode >= 400 && $this->findHeaderValue($http_response_header, 'content-type') === 'application/json') {
- self::outputWarnings($this->io, $originUrl, json_decode($result, true));
+ if ($statusCode >= 400 && Response::findHeaderValue($http_response_header, 'content-type') === 'application/json') {
+ HttpDownloader::outputWarnings($this->io, $originUrl, json_decode($result, true));
}
if (in_array($statusCode, array(401, 403)) && $this->retryAuthFailure) {
- $this->promptAuthAndRetry($statusCode, $this->findStatusMessage($http_response_header), null, $http_response_header);
+ $this->promptAuthAndRetry($statusCode, $this->findStatusMessage($http_response_header), $http_response_header);
}
}
- $contentLength = !empty($http_response_header[0]) ? $this->findHeaderValue($http_response_header, 'content-length') : null;
+ $contentLength = !empty($http_response_header[0]) ? Response::findHeaderValue($http_response_header, 'content-length') : null;
if ($contentLength && Platform::strlen($result) < $contentLength) {
// alas, this is not possible via the stream callback because STREAM_NOTIFY_COMPLETED is documented, but not implemented anywhere in PHP
$e = new TransportException('Content-Length mismatch, received '.Platform::strlen($result).' bytes out of the expected '.$contentLength);
@@ -396,13 +340,13 @@ class RemoteFilesystem
$locationHeader = null;
if (!empty($http_response_header[0])) {
$statusCode = $this->findStatusCode($http_response_header);
- $contentType = $this->findHeaderValue($http_response_header, 'content-type');
- $locationHeader = $this->findHeaderValue($http_response_header, 'location');
+ $contentType = Response::findHeaderValue($http_response_header, 'content-type');
+ $locationHeader = Response::findHeaderValue($http_response_header, 'location');
}
// check for bitbucket login page asking to authenticate
if ($originUrl === 'bitbucket.org'
- && !$this->isPublicBitBucketDownload($fileUrl)
+ && !$this->authHelper->isPublicBitBucketDownload($fileUrl)
&& substr($fileUrl, -4) === '.zip'
&& (!$locationHeader || substr($locationHeader, -4) !== '.zip')
&& $contentType && preg_match('{^text/html\b}i', $contentType)
@@ -434,7 +378,7 @@ class RemoteFilesystem
// fail 4xx and 5xx responses and capture the response
if ($statusCode && $statusCode >= 400 && $statusCode <= 599) {
if (!$this->retry) {
- if ($this->progress && !$this->retry && !$isRedirect) {
+ if ($this->progress && !$isRedirect) {
$this->io->overwriteError("Downloading (failed)", false);
}
@@ -453,7 +397,7 @@ class RemoteFilesystem
// decode gzip
if ($result && extension_loaded('zlib') && substr($fileUrl, 0, 4) === 'http' && !$hasFollowedRedirect) {
- $contentEncoding = $this->findHeaderValue($http_response_header, 'content-encoding');
+ $contentEncoding = Response::findHeaderValue($http_response_header, 'content-encoding');
$decode = $contentEncoding && 'gzip' === strtolower($contentEncoding);
if ($decode) {
@@ -548,8 +492,7 @@ class RemoteFilesystem
$result = $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
if ($this->storeAuth && $this->config) {
- $authHelper = new AuthHelper($this->io, $this->config);
- $authHelper->storeAuth($this->originUrl, $this->storeAuth);
+ $this->authHelper->storeAuth($this->originUrl, $this->storeAuth);
$this->storeAuth = false;
}
@@ -652,109 +595,16 @@ class RemoteFilesystem
}
}
- protected function promptAuthAndRetry($httpStatus, $reason = null, $warning = null, $headers = array())
+ protected function promptAuthAndRetry($httpStatus, $reason = null, $headers = array())
{
- if ($this->config && in_array($this->originUrl, $this->config->get('github-domains'), true)) {
- $gitHubUtil = new GitHub($this->io, $this->config, null);
- $message = "\n";
+ $result = $this->authHelper->promptAuthIfNeeded($this->fileUrl, $this->originUrl, $httpStatus, $reason, $headers);
- $rateLimited = $gitHubUtil->isRateLimited($headers);
- if ($rateLimited) {
- $rateLimit = $gitHubUtil->getRateLimit($headers);
- if ($this->io->hasAuthentication($this->originUrl)) {
- $message = 'Review your configured GitHub OAuth token or enter a new one to go over the API rate limit.';
- } else {
- $message = 'Create a GitHub OAuth token to go over the API rate limit.';
- }
+ $this->storeAuth = $result['storeAuth'];
+ $this->retry = $result['retry'];
- $message = sprintf(
- 'GitHub API limit (%d calls/hr) is exhausted, could not fetch '.$this->fileUrl.'. '.$message.' You can also wait until %s for the rate limit to reset.',
- $rateLimit['limit'],
- $rateLimit['reset']
- )."\n";
- } else {
- $message .= 'Could not fetch '.$this->fileUrl.', please ';
- if ($this->io->hasAuthentication($this->originUrl)) {
- $message .= 'review your configured GitHub OAuth token or enter a new one to access private repos';
- } else {
- $message .= 'create a GitHub OAuth token to access private repos';
- }
- }
-
- if (!$gitHubUtil->authorizeOAuth($this->originUrl)
- && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, $message))
- ) {
- throw new TransportException('Could not authenticate against '.$this->originUrl, 401);
- }
- } elseif ($this->config && in_array($this->originUrl, $this->config->get('gitlab-domains'), true)) {
- $message = "\n".'Could not fetch '.$this->fileUrl.', enter your ' . $this->originUrl . ' credentials ' .($httpStatus === 401 ? 'to access private repos' : 'to go over the API rate limit');
- $gitLabUtil = new GitLab($this->io, $this->config, null);
-
- if ($this->io->hasAuthentication($this->originUrl) && ($auth = $this->io->getAuthentication($this->originUrl)) && in_array($auth['password'], array('gitlab-ci-token', 'private-token', 'oauth2'), true)) {
- throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus);
- }
-
- if (!$gitLabUtil->authorizeOAuth($this->originUrl)
- && (!$this->io->isInteractive() || !$gitLabUtil->authorizeOAuthInteractively($this->scheme, $this->originUrl, $message))
- ) {
- throw new TransportException('Could not authenticate against '.$this->originUrl, 401);
- }
- } elseif ($this->config && $this->originUrl === 'bitbucket.org') {
- $askForOAuthToken = true;
- if ($this->io->hasAuthentication($this->originUrl)) {
- $auth = $this->io->getAuthentication($this->originUrl);
- if ($auth['username'] !== 'x-token-auth') {
- $bitbucketUtil = new Bitbucket($this->io, $this->config);
- $accessToken = $bitbucketUtil->requestToken($this->originUrl, $auth['username'], $auth['password']);
- if (!empty($accessToken)) {
- $this->io->setAuthentication($this->originUrl, 'x-token-auth', $accessToken);
- $askForOAuthToken = false;
- }
- } else {
- throw new TransportException('Could not authenticate against ' . $this->originUrl, 401);
- }
- }
-
- if ($askForOAuthToken) {
- $message = "\n".'Could not fetch ' . $this->fileUrl . ', please create a bitbucket OAuth token to ' . (($httpStatus === 401 || $httpStatus === 403) ? 'access private repos' : 'go over the API rate limit');
- $bitBucketUtil = new Bitbucket($this->io, $this->config);
- if (! $bitBucketUtil->authorizeOAuth($this->originUrl)
- && (! $this->io->isInteractive() || !$bitBucketUtil->authorizeOAuthInteractively($this->originUrl, $message))
- ) {
- throw new TransportException('Could not authenticate against ' . $this->originUrl, 401);
- }
- }
- } else {
- // 404s are only handled for github
- if ($httpStatus === 404) {
- return;
- }
-
- // fail if the console is not interactive
- if (!$this->io->isInteractive()) {
- if ($httpStatus === 401) {
- $message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console to authenticate";
- }
- if ($httpStatus === 403) {
- $message = "The '" . $this->fileUrl . "' URL could not be accessed: " . $reason;
- }
-
- throw new TransportException($message, $httpStatus);
- }
- // fail if we already have auth
- if ($this->io->hasAuthentication($this->originUrl)) {
- throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus);
- }
-
- $this->io->writeError(' Authentication required ('.$this->originUrl.'):');
- $username = $this->io->ask(' Username: ');
- $password = $this->io->askAndHideAnswer(' Password: ');
- $this->io->setAuthentication($this->originUrl, $username, $password);
- $this->storeAuth = $this->config->get('store-auths');
+ if ($this->retry) {
+ throw new TransportException('RETRY');
}
-
- $this->retry = true;
- throw new TransportException('RETRY');
}
protected function getOptionsForUrl($originUrl, $additionalOptions)
@@ -814,40 +664,7 @@ class RemoteFilesystem
$headers[] = 'Connection: close';
}
- if ($this->io->hasAuthentication($originUrl)) {
- $authenticationDisplayMessage = null;
- $auth = $this->io->getAuthentication($originUrl);
- if ($auth['password'] === 'bearer') {
- $headers[] = 'Authorization: Bearer '.$auth['username'];
- } elseif ('github.com' === $originUrl && 'x-oauth-basic' === $auth['password']) {
- $options['github-token'] = $auth['username'];
- $authenticationDisplayMessage = 'Using GitHub token authentication';
- } elseif ($this->config && in_array($originUrl, $this->config->get('gitlab-domains'), true)) {
- if ($auth['password'] === 'oauth2') {
- $headers[] = 'Authorization: Bearer '.$auth['username'];
- $authenticationDisplayMessage = 'Using GitLab OAuth token authentication';
- } elseif ($auth['password'] === 'private-token' || $auth['password'] === 'gitlab-ci-token') {
- $headers[] = 'PRIVATE-TOKEN: '.$auth['username'];
- $authenticationDisplayMessage = 'Using GitLab private token authentication';
- }
- } elseif ('bitbucket.org' === $originUrl
- && $this->fileUrl !== Bitbucket::OAUTH2_ACCESS_TOKEN_URL && 'x-token-auth' === $auth['username']
- ) {
- if (!$this->isPublicBitBucketDownload($this->fileUrl)) {
- $headers[] = 'Authorization: Bearer ' . $auth['password'];
- $authenticationDisplayMessage = 'Using Bitbucket OAuth token authentication';
- }
- } else {
- $authStr = base64_encode($auth['username'] . ':' . $auth['password']);
- $headers[] = 'Authorization: Basic '.$authStr;
- $authenticationDisplayMessage = 'Using HTTP basic authentication with username "' . $auth['username'] . '"';
- }
-
- if ($authenticationDisplayMessage && !in_array($originUrl, $this->displayedOriginAuthentications, true)) {
- $this->io->writeError($authenticationDisplayMessage, true, IOInterface::DEBUG);
- $this->displayedOriginAuthentications[] = $originUrl;
- }
- }
+ $headers = $this->authHelper->addAuthenticationHeader($headers, $originUrl, $this->fileUrl);
$options['http']['follow_location'] = 0;
@@ -863,7 +680,7 @@ class RemoteFilesystem
private function handleRedirect(array $http_response_header, array $additionalOptions, $result)
{
- if ($locationHeader = $this->findHeaderValue($http_response_header, 'location')) {
+ if ($locationHeader = Response::findHeaderValue($http_response_header, 'location')) {
if (parse_url($locationHeader, PHP_URL_SCHEME)) {
// Absolute URL; e.g. https://example.com/composer
$targetUrl = $locationHeader;
@@ -887,7 +704,7 @@ class RemoteFilesystem
$this->redirects++;
$this->io->writeError('', true, IOInterface::DEBUG);
- $this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, $this->stripCredentialsFromUrl($targetUrl)), true, IOInterface::DEBUG);
+ $this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, Url::sanitize($targetUrl)), true, IOInterface::DEBUG);
$additionalOptions['redirects'] = $this->redirects;
@@ -905,111 +722,6 @@ class RemoteFilesystem
return false;
}
- /**
- * @param array $options
- *
- * @return array
- */
- private function getTlsDefaults(array $options)
- {
- $ciphers = implode(':', array(
- 'ECDHE-RSA-AES128-GCM-SHA256',
- 'ECDHE-ECDSA-AES128-GCM-SHA256',
- 'ECDHE-RSA-AES256-GCM-SHA384',
- 'ECDHE-ECDSA-AES256-GCM-SHA384',
- 'DHE-RSA-AES128-GCM-SHA256',
- 'DHE-DSS-AES128-GCM-SHA256',
- 'kEDH+AESGCM',
- 'ECDHE-RSA-AES128-SHA256',
- 'ECDHE-ECDSA-AES128-SHA256',
- 'ECDHE-RSA-AES128-SHA',
- 'ECDHE-ECDSA-AES128-SHA',
- 'ECDHE-RSA-AES256-SHA384',
- 'ECDHE-ECDSA-AES256-SHA384',
- 'ECDHE-RSA-AES256-SHA',
- 'ECDHE-ECDSA-AES256-SHA',
- 'DHE-RSA-AES128-SHA256',
- 'DHE-RSA-AES128-SHA',
- 'DHE-DSS-AES128-SHA256',
- 'DHE-RSA-AES256-SHA256',
- 'DHE-DSS-AES256-SHA',
- 'DHE-RSA-AES256-SHA',
- 'AES128-GCM-SHA256',
- 'AES256-GCM-SHA384',
- 'AES128-SHA256',
- 'AES256-SHA256',
- 'AES128-SHA',
- 'AES256-SHA',
- 'AES',
- 'CAMELLIA',
- 'DES-CBC3-SHA',
- '!aNULL',
- '!eNULL',
- '!EXPORT',
- '!DES',
- '!RC4',
- '!MD5',
- '!PSK',
- '!aECDH',
- '!EDH-DSS-DES-CBC3-SHA',
- '!EDH-RSA-DES-CBC3-SHA',
- '!KRB5-DES-CBC3-SHA',
- ));
-
- /**
- * CN_match and SNI_server_name are only known once a URL is passed.
- * They will be set in the getOptionsForUrl() method which receives a URL.
- *
- * cafile or capath can be overridden by passing in those options to constructor.
- */
- $defaults = array(
- 'ssl' => array(
- 'ciphers' => $ciphers,
- 'verify_peer' => true,
- 'verify_depth' => 7,
- 'SNI_enabled' => true,
- 'capture_peer_cert' => true,
- ),
- );
-
- if (isset($options['ssl'])) {
- $defaults['ssl'] = array_replace_recursive($defaults['ssl'], $options['ssl']);
- }
-
- $caBundleLogger = $this->io instanceof LoggerInterface ? $this->io : null;
-
- /**
- * Attempt to find a local cafile or throw an exception if none pre-set
- * The user may go download one if this occurs.
- */
- if (!isset($defaults['ssl']['cafile']) && !isset($defaults['ssl']['capath'])) {
- $result = CaBundle::getSystemCaRootBundlePath($caBundleLogger);
-
- if (is_dir($result)) {
- $defaults['ssl']['capath'] = $result;
- } else {
- $defaults['ssl']['cafile'] = $result;
- }
- }
-
- if (isset($defaults['ssl']['cafile']) && (!is_readable($defaults['ssl']['cafile']) || !CaBundle::validateCaFile($defaults['ssl']['cafile'], $caBundleLogger))) {
- throw new TransportException('The configured cafile was not valid or could not be read.');
- }
-
- if (isset($defaults['ssl']['capath']) && (!is_dir($defaults['ssl']['capath']) || !is_readable($defaults['ssl']['capath']))) {
- throw new TransportException('The configured capath was not valid or could not be read.');
- }
-
- /**
- * Disable TLS compression to prevent CRIME attacks where supported.
- */
- if (PHP_VERSION_ID >= 50413) {
- $defaults['ssl']['disable_compression'] = true;
- }
-
- return $defaults;
- }
-
/**
* Fetch certificate common name and fingerprint for validation of SAN.
*
@@ -1079,69 +791,4 @@ class RemoteFilesystem
return parse_url($url, PHP_URL_HOST).':'.$port;
}
-
- /**
- * @link https://github.com/composer/composer/issues/5584
- *
- * @param string $urlToBitBucketFile URL to a file at bitbucket.org.
- *
- * @return bool Whether the given URL is a public BitBucket download which requires no authentication.
- */
- private function isPublicBitBucketDownload($urlToBitBucketFile)
- {
- $domain = parse_url($urlToBitBucketFile, PHP_URL_HOST);
- if (strpos($domain, 'bitbucket.org') === false) {
- // Bitbucket downloads are hosted on amazonaws.
- // We do not need to authenticate there at all
- return true;
- }
-
- $path = parse_url($urlToBitBucketFile, PHP_URL_PATH);
-
- // Path for a public download follows this pattern /{user}/{repo}/downloads/{whatever}
- // {@link https://blog.bitbucket.org/2009/04/12/new-feature-downloads/}
- $pathParts = explode('/', $path);
-
- return count($pathParts) >= 4 && $pathParts[3] == 'downloads';
- }
-
- public static function outputWarnings(IOInterface $io, $url, $data)
- {
- foreach (array('warning', 'info') as $type) {
- if (empty($data[$type])) {
- continue;
- }
-
- if (!empty($data[$type . '-versions'])) {
- $versionParser = new VersionParser();
- $constraint = $versionParser->parseConstraints($data[$type . '-versions']);
- $composer = new Constraint('==', $versionParser->normalize(Composer::getVersion()));
- if (!$constraint->matches($composer)) {
- continue;
- }
- }
-
- $io->writeError('<'.$type.'>'.ucfirst($type).' from '.$url.': '.$data[$type].''.$type.'>');
- }
- }
-
- public static function getOrigin($urlOrPath)
- {
- $hostPort = parse_url($urlOrPath, PHP_URL_HOST);
- if (!$hostPort) {
- return $urlOrPath;
- }
- if (parse_url($urlOrPath, PHP_URL_PORT)) {
- $hostPort .= ':'.parse_url($urlOrPath, PHP_URL_PORT);
- }
-
- return $hostPort;
- }
-
- private function stripCredentialsFromUrl($url)
- {
- // GitHub repository rename result in redirect locations containing the access_token as GET parameter
- // e.g. https://api.github.com/repositories/9999999999?access_token=github_token
- return preg_replace('{([&?]access_token=)[^&]+}', '$1***', $url);
- }
}
diff --git a/src/Composer/Util/SpdxLicense.php b/src/Composer/Util/SpdxLicense.php
deleted file mode 100644
index be4efdc54..000000000
--- a/src/Composer/Util/SpdxLicense.php
+++ /dev/null
@@ -1,24 +0,0 @@
-
- * Jordi Boggiano
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Composer\Util;
-
-use Composer\Spdx\SpdxLicenses;
-
-trigger_error('The ' . __NAMESPACE__ . '\SpdxLicense class is deprecated, use Composer\Spdx\SpdxLicenses instead.', E_USER_DEPRECATED);
-
-/**
- * @deprecated use Composer\Spdx\SpdxLicenses instead
- */
-class SpdxLicense extends SpdxLicenses
-{
-}
diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php
index da3e578bd..09291a908 100644
--- a/src/Composer/Util/StreamContextFactory.php
+++ b/src/Composer/Util/StreamContextFactory.php
@@ -13,6 +13,9 @@
namespace Composer\Util;
use Composer\Composer;
+use Composer\CaBundle\CaBundle;
+use Composer\Downloader\TransportException;
+use Psr\Log\LoggerInterface;
/**
* Allows the creation of a basic context supporting http proxy
@@ -39,6 +42,32 @@ final class StreamContextFactory
'max_redirects' => 20,
));
+ $options = array_replace_recursive($options, self::initOptions($url, $defaultOptions));
+ unset($defaultOptions['http']['header']);
+ $options = array_replace_recursive($options, $defaultOptions);
+
+ if (isset($options['http']['header'])) {
+ $options['http']['header'] = self::fixHttpHeaderField($options['http']['header']);
+ }
+
+ return stream_context_create($options, $defaultParams);
+ }
+
+ /**
+ * @param string $url
+ * @param array $options
+ * @return array ['http' => ['header' => [...], 'proxy' => '..', 'request_fulluri' => bool]] formatted as a stream context array
+ */
+ public static function initOptions($url, array $options)
+ {
+ // Make sure the headers are in an array form
+ if (!isset($options['http']['header'])) {
+ $options['http']['header'] = array();
+ }
+ if (is_string($options['http']['header'])) {
+ $options['http']['header'] = explode("\r\n", $options['http']['header']);
+ }
+
// Handle HTTP_PROXY/http_proxy on CLI only for security reasons
if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && (!empty($_SERVER['HTTP_PROXY']) || !empty($_SERVER['http_proxy']))) {
$proxy = parse_url(!empty($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']);
@@ -85,15 +114,15 @@ final class StreamContextFactory
// enabled request_fulluri unless it is explicitly disabled
switch (parse_url($url, PHP_URL_SCHEME)) {
- case 'http': // default request_fulluri to true
+ case 'http': // default request_fulluri to true for HTTP
$reqFullUriEnv = getenv('HTTP_PROXY_REQUEST_FULLURI');
if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) {
$options['http']['request_fulluri'] = true;
}
break;
- case 'https': // default request_fulluri to true
+ case 'https': // default request_fulluri to false for HTTPS
$reqFullUriEnv = getenv('HTTPS_PROXY_REQUEST_FULLURI');
- if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) {
+ if (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv) {
$options['http']['request_fulluri'] = true;
}
break;
@@ -115,42 +144,139 @@ final class StreamContextFactory
}
$auth = base64_encode($auth);
- // Preserve headers if already set in default options
- if (isset($defaultOptions['http']['header'])) {
- if (is_string($defaultOptions['http']['header'])) {
- $defaultOptions['http']['header'] = array($defaultOptions['http']['header']);
- }
- $defaultOptions['http']['header'][] = "Proxy-Authorization: Basic {$auth}";
- } else {
- $options['http']['header'] = array("Proxy-Authorization: Basic {$auth}");
- }
+ $options['http']['header'][] = "Proxy-Authorization: Basic {$auth}";
}
}
- $options = array_replace_recursive($options, $defaultOptions);
-
- if (isset($options['http']['header'])) {
- $options['http']['header'] = self::fixHttpHeaderField($options['http']['header']);
- }
-
if (defined('HHVM_VERSION')) {
$phpVersion = 'HHVM ' . HHVM_VERSION;
} else {
$phpVersion = 'PHP ' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION;
}
+ if (extension_loaded('curl')) {
+ $curl = curl_version();
+ $httpVersion = 'curl '.$curl['version'];
+ } else {
+ $httpVersion = 'streams';
+ }
+
if (!isset($options['http']['header']) || false === stripos(implode('', $options['http']['header']), 'user-agent')) {
$options['http']['header'][] = sprintf(
- 'User-Agent: Composer/%s (%s; %s; %s%s)',
+ 'User-Agent: Composer/%s (%s; %s; %s; %s%s)',
Composer::getVersion(),
function_exists('php_uname') ? php_uname('s') : 'Unknown',
function_exists('php_uname') ? php_uname('r') : 'Unknown',
$phpVersion,
+ $httpVersion,
getenv('CI') ? '; CI' : ''
);
}
- return stream_context_create($options, $defaultParams);
+ return $options;
+ }
+
+ /**
+ * @param array $options
+ *
+ * @return array
+ */
+ public static function getTlsDefaults(array $options, LoggerInterface $logger = null)
+ {
+ $ciphers = implode(':', array(
+ 'ECDHE-RSA-AES128-GCM-SHA256',
+ 'ECDHE-ECDSA-AES128-GCM-SHA256',
+ 'ECDHE-RSA-AES256-GCM-SHA384',
+ 'ECDHE-ECDSA-AES256-GCM-SHA384',
+ 'DHE-RSA-AES128-GCM-SHA256',
+ 'DHE-DSS-AES128-GCM-SHA256',
+ 'kEDH+AESGCM',
+ 'ECDHE-RSA-AES128-SHA256',
+ 'ECDHE-ECDSA-AES128-SHA256',
+ 'ECDHE-RSA-AES128-SHA',
+ 'ECDHE-ECDSA-AES128-SHA',
+ 'ECDHE-RSA-AES256-SHA384',
+ 'ECDHE-ECDSA-AES256-SHA384',
+ 'ECDHE-RSA-AES256-SHA',
+ 'ECDHE-ECDSA-AES256-SHA',
+ 'DHE-RSA-AES128-SHA256',
+ 'DHE-RSA-AES128-SHA',
+ 'DHE-DSS-AES128-SHA256',
+ 'DHE-RSA-AES256-SHA256',
+ 'DHE-DSS-AES256-SHA',
+ 'DHE-RSA-AES256-SHA',
+ 'AES128-GCM-SHA256',
+ 'AES256-GCM-SHA384',
+ 'AES128-SHA256',
+ 'AES256-SHA256',
+ 'AES128-SHA',
+ 'AES256-SHA',
+ 'AES',
+ 'CAMELLIA',
+ 'DES-CBC3-SHA',
+ '!aNULL',
+ '!eNULL',
+ '!EXPORT',
+ '!DES',
+ '!RC4',
+ '!MD5',
+ '!PSK',
+ '!aECDH',
+ '!EDH-DSS-DES-CBC3-SHA',
+ '!EDH-RSA-DES-CBC3-SHA',
+ '!KRB5-DES-CBC3-SHA',
+ ));
+
+ /**
+ * CN_match and SNI_server_name are only known once a URL is passed.
+ * They will be set in the getOptionsForUrl() method which receives a URL.
+ *
+ * cafile or capath can be overridden by passing in those options to constructor.
+ */
+ $defaults = array(
+ 'ssl' => array(
+ 'ciphers' => $ciphers,
+ 'verify_peer' => true,
+ 'verify_depth' => 7,
+ 'SNI_enabled' => true,
+ 'capture_peer_cert' => true,
+ ),
+ );
+
+ if (isset($options['ssl'])) {
+ $defaults['ssl'] = array_replace_recursive($defaults['ssl'], $options['ssl']);
+ }
+
+ /**
+ * Attempt to find a local cafile or throw an exception if none pre-set
+ * The user may go download one if this occurs.
+ */
+ if (!isset($defaults['ssl']['cafile']) && !isset($defaults['ssl']['capath'])) {
+ $result = CaBundle::getSystemCaRootBundlePath($logger);
+
+ if (is_dir($result)) {
+ $defaults['ssl']['capath'] = $result;
+ } else {
+ $defaults['ssl']['cafile'] = $result;
+ }
+ }
+
+ if (isset($defaults['ssl']['cafile']) && (!is_readable($defaults['ssl']['cafile']) || !CaBundle::validateCaFile($defaults['ssl']['cafile'], $logger))) {
+ throw new TransportException('The configured cafile was not valid or could not be read.');
+ }
+
+ if (isset($defaults['ssl']['capath']) && (!is_dir($defaults['ssl']['capath']) || !is_readable($defaults['ssl']['capath']))) {
+ throw new TransportException('The configured capath was not valid or could not be read.');
+ }
+
+ /**
+ * Disable TLS compression to prevent CRIME attacks where supported.
+ */
+ if (PHP_VERSION_ID >= 50413) {
+ $defaults['ssl']['disable_compression'] = true;
+ }
+
+ return $defaults;
}
/**
diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php
index 58114ac93..be1a81c91 100644
--- a/src/Composer/Util/Svn.php
+++ b/src/Composer/Util/Svn.php
@@ -304,7 +304,7 @@ class Svn
$this->createAuthFromUrl();
}
- return $this->hasAuth;
+ return (bool) $this->hasAuth;
}
/**
diff --git a/src/Composer/Util/Url.php b/src/Composer/Util/Url.php
index 4a5d5f90c..2da171556 100644
--- a/src/Composer/Util/Url.php
+++ b/src/Composer/Util/Url.php
@@ -19,6 +19,12 @@ use Composer\Config;
*/
class Url
{
+ /**
+ * @param Config $config
+ * @param string $url
+ * @param string $ref
+ * @return string the updated URL
+ */
public static function updateDistReference(Config $config, $url, $ref)
{
$host = parse_url($url, PHP_URL_HOST);
@@ -52,4 +58,65 @@ class Url
return $url;
}
+
+ /**
+ * @param string $url
+ * @return string
+ */
+ public static function getOrigin(Config $config, $url)
+ {
+ if (0 === strpos($url, 'file://')) {
+ return $url;
+ }
+
+ $origin = (string) parse_url($url, PHP_URL_HOST);
+ if ($port = parse_url($url, PHP_URL_PORT)) {
+ $origin .= ':'.$port;
+ }
+
+ if (strpos($origin, '.github.com') === (strlen($origin) - 11)) {
+ return 'github.com';
+ }
+
+ if ($origin === 'repo.packagist.org') {
+ return 'packagist.org';
+ }
+
+ if ($origin === '') {
+ $origin = $url;
+ }
+
+ // Gitlab can be installed in a non-root context (i.e. gitlab.com/foo). When downloading archives the originUrl
+ // is the host without the path, so we look for the registered gitlab-domains matching the host here
+ if (
+ is_array($config->get('gitlab-domains'))
+ && false === strpos($origin, '/')
+ && !in_array($origin, $config->get('gitlab-domains'))
+ ) {
+ foreach ($config->get('gitlab-domains') as $gitlabDomain) {
+ if (0 === strpos($gitlabDomain, $origin)) {
+ return $gitlabDomain;
+ }
+ }
+ }
+
+ return $origin;
+ }
+
+ public static function sanitize($url)
+ {
+ // GitHub repository rename result in redirect locations containing the access_token as GET parameter
+ // e.g. https://api.github.com/repositories/9999999999?access_token=github_token
+ $url = preg_replace('{([&?]access_token=)[^&]+}', '$1***', $url);
+
+ $url = preg_replace_callback('{://(?P[^:/\s@]+):(?P[^@\s/]+)@}i', function ($m) {
+ if (preg_match('{^[a-f0-9]{12,}$}', $m['user'])) {
+ return '://***:***@';
+ }
+
+ return '://'.$m['user'].':***@';
+ }, $url);
+
+ return $url;
+ }
}
diff --git a/src/Composer/XdebugHandler.php b/src/Composer/XdebugHandler.php
deleted file mode 100644
index eb94e93f4..000000000
--- a/src/Composer/XdebugHandler.php
+++ /dev/null
@@ -1,31 +0,0 @@
-
- * Jordi Boggiano
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Composer;
-
-use Symfony\Component\Console\Output\OutputInterface;
-
-trigger_error('The ' . __NAMESPACE__ . '\XdebugHandler class is deprecated, use Composer\XdebugHandler\XdebugHandler instead,', E_USER_DEPRECATED);
-
-/**
- * @deprecated use Composer\XdebugHandler\XdebugHandler instead
- */
-class XdebugHandler extends XdebugHandler\XdebugHandler
-{
- const ENV_ALLOW = 'COMPOSER_ALLOW_XDEBUG';
- const ENV_VERSION = 'COMPOSER_XDEBUG_VERSION';
-
- public function __construct(OutputInterface $output)
- {
- parent::__construct('composer', '--ansi');
- }
-}
diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php
index 3cd8f7ff2..6cb9b76dc 100644
--- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php
+++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php
@@ -604,8 +604,6 @@ class AutoloadGeneratorTest extends TestCase
{
$package = new Package('a', '1.0', '1.0');
- $this->markTestSkipped('Skipped until ClassMapGenerator ignoring of invalid PSR-x classes is enabled');
-
$package->setAutoload(array(
'psr-0' => array('psr0_' => 'psr0/'),
'psr-4' => array('psr4\\' => 'psr4/'),
diff --git a/tests/Composer/Test/CacheTest.php b/tests/Composer/Test/CacheTest.php
index 693753a3e..dffa8390a 100644
--- a/tests/Composer/Test/CacheTest.php
+++ b/tests/Composer/Test/CacheTest.php
@@ -13,6 +13,7 @@
namespace Composer\Test;
use Composer\Test\TestCase;
+use Composer\Cache;
use Composer\Util\Filesystem;
class CacheTest extends TestCase
@@ -20,6 +21,7 @@ class CacheTest extends TestCase
private $files;
private $root;
private $finder;
+ private $filesystem;
private $cache;
public function setUp()
@@ -34,6 +36,7 @@ class CacheTest extends TestCase
}
$this->finder = $this->getMockBuilder('Symfony\Component\Finder\Finder')->disableOriginalConstructor()->getMock();
+ $this->filesystem = $this->getMockBuilder('Composer\Util\Filesystem')->getMock();
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
$this->cache = $this->getMockBuilder('Composer\Cache')
@@ -105,10 +108,8 @@ class CacheTest extends TestCase
public function testClearCache()
{
+ $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
+ $this->cache = new Cache($io, $this->root, 'a-z0-9.', $this->filesystem);
$this->assertTrue($this->cache->clear());
-
- for ($i = 0; $i < 3; $i++) {
- $this->assertFileNotExists("{$this->root}/cached.file{$i}.zip");
- }
}
}
diff --git a/tests/Composer/Test/Command/ArchiveCommandTest.php b/tests/Composer/Test/Command/ArchiveCommandTest.php
index 4a777988d..41a5c0993 100644
--- a/tests/Composer/Test/Command/ArchiveCommandTest.php
+++ b/tests/Composer/Test/Command/ArchiveCommandTest.php
@@ -95,9 +95,9 @@ class ArchiveCommandTest extends TestCase
null,
false,
null
- );
+ )->willReturn(0);
$command->method('isInteractive')->willReturn(false);
- $command->run($input, $output);
+ $this->assertEquals(0, $command->run($input, $output));
}
}
diff --git a/tests/Composer/Test/Command/RunScriptCommandTest.php b/tests/Composer/Test/Command/RunScriptCommandTest.php
index 97acc6d26..6a2adbd2d 100644
--- a/tests/Composer/Test/Command/RunScriptCommandTest.php
+++ b/tests/Composer/Test/Command/RunScriptCommandTest.php
@@ -66,7 +66,8 @@ class RunScriptCommandTest extends TestCase
$ed->expects($this->once())
->method('dispatchScript')
- ->with($scriptName, $expectedDevMode, array());
+ ->with($scriptName, $expectedDevMode, array())
+ ->willReturn(0);
$composer = $this->createComposerInstance();
$composer->setEventDispatcher($ed);
diff --git a/tests/Composer/Test/ComposerTest.php b/tests/Composer/Test/ComposerTest.php
index c2c425e76..87270baae 100644
--- a/tests/Composer/Test/ComposerTest.php
+++ b/tests/Composer/Test/ComposerTest.php
@@ -57,7 +57,7 @@ class ComposerTest extends TestCase
public function testSetGetInstallationManager()
{
$composer = new Composer();
- $manager = $this->getMockBuilder('Composer\Installer\InstallationManager')->getMock();
+ $manager = $this->getMockBuilder('Composer\Installer\InstallationManager')->disableOriginalConstructor()->getMock();
$composer->setInstallationManager($manager);
$this->assertSame($manager, $composer->getInstallationManager());
diff --git a/tests/Composer/Test/Console/HtmlOutputFormatterTest.php b/tests/Composer/Test/Console/HtmlOutputFormatterTest.php
index d23bcddb2..a105eec0d 100644
--- a/tests/Composer/Test/Console/HtmlOutputFormatterTest.php
+++ b/tests/Composer/Test/Console/HtmlOutputFormatterTest.php
@@ -24,7 +24,7 @@ class HtmlOutputFormatterTest extends TestCase
'warning' => new OutputFormatterStyle('black', 'yellow'),
));
- return $this->assertEquals(
+ $this->assertEquals(
'text green yellow black w/ yello bg',
$formatter->format('text green yellow black w/ yello bg')
);
diff --git a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php
index 2611d772f..8a0e5a15a 100644
--- a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php
+++ b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php
@@ -13,30 +13,32 @@
namespace Composer\Test\DependencyResolver;
use Composer\Repository\ArrayRepository;
+use Composer\Repository\LockArrayRepository;
use Composer\Repository\RepositoryInterface;
use Composer\DependencyResolver\DefaultPolicy;
use Composer\DependencyResolver\Pool;
use Composer\Package\Link;
use Composer\Package\AliasPackage;
+use Composer\Repository\RepositorySet;
use Composer\Semver\Constraint\Constraint;
use Composer\Test\TestCase;
class DefaultPolicyTest extends TestCase
{
- /** @var Pool */
- protected $pool;
+ /** @var RepositorySet */
+ protected $repositorySet;
/** @var ArrayRepository */
protected $repo;
- /** @var ArrayRepository */
- protected $repoInstalled;
+ /** @var LockArrayRepository */
+ protected $repoLocked;
/** @var DefaultPolicy */
protected $policy;
public function setUp()
{
- $this->pool = new Pool('dev');
+ $this->repositorySet = new RepositorySet('dev');
$this->repo = new ArrayRepository;
- $this->repoInstalled = new ArrayRepository;
+ $this->repoLocked = new LockArrayRepository;
$this->policy = new DefaultPolicy;
}
@@ -44,12 +46,14 @@ class DefaultPolicyTest extends TestCase
public function testSelectSingle()
{
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
- $this->pool->addRepository($this->repo);
+ $this->repositorySet->addRepository($this->repo);
+
+ $pool = $this->repositorySet->createPoolForPackage('A', $this->repoLocked);
$literals = array($packageA->getId());
$expected = array($packageA->getId());
- $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+ $selected = $this->policy->selectPreferredPackages($pool, $literals);
$this->assertSame($expected, $selected);
}
@@ -58,12 +62,14 @@ class DefaultPolicyTest extends TestCase
{
$this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0'));
$this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0'));
- $this->pool->addRepository($this->repo);
+ $this->repositorySet->addRepository($this->repo);
+
+ $pool = $this->repositorySet->createPoolForPackage('A', $this->repoLocked);
$literals = array($packageA1->getId(), $packageA2->getId());
$expected = array($packageA2->getId());
- $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+ $selected = $this->policy->selectPreferredPackages($pool, $literals);
$this->assertSame($expected, $selected);
}
@@ -72,12 +78,14 @@ class DefaultPolicyTest extends TestCase
{
$this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0.0'));
$this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.1-alpha'));
- $this->pool->addRepository($this->repo);
+ $this->repositorySet->addRepository($this->repo);
+
+ $pool = $this->repositorySet->createPoolForPackage('A', $this->repoLocked);
$literals = array($packageA1->getId(), $packageA2->getId());
$expected = array($packageA2->getId());
- $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+ $selected = $this->policy->selectPreferredPackages($pool, $literals);
$this->assertSame($expected, $selected);
}
@@ -86,13 +94,15 @@ class DefaultPolicyTest extends TestCase
{
$this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0.0'));
$this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.1-alpha'));
- $this->pool->addRepository($this->repo);
+ $this->repositorySet->addRepository($this->repo);
+
+ $pool = $this->repositorySet->createPoolForPackage('A', $this->repoLocked);
$literals = array($packageA1->getId(), $packageA2->getId());
$expected = array($packageA1->getId());
$policy = new DefaultPolicy(true);
- $selected = $policy->selectPreferredPackages($this->pool, array(), $literals);
+ $selected = $policy->selectPreferredPackages($pool, $literals);
$this->assertSame($expected, $selected);
}
@@ -101,46 +111,14 @@ class DefaultPolicyTest extends TestCase
{
$this->repo->addPackage($packageA1 = $this->getPackage('A', 'dev-foo'));
$this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.0'));
- $this->pool->addRepository($this->repo);
+ $this->repositorySet->addRepository($this->repo);
+
+ $pool = $this->repositorySet->createPoolForPackage('A', $this->repoLocked);
$literals = array($packageA1->getId(), $packageA2->getId());
$expected = array($packageA2->getId());
- $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
-
- $this->assertSame($expected, $selected);
- }
-
- public function testSelectNewestOverInstalled()
- {
- $this->repo->addPackage($packageA = $this->getPackage('A', '2.0'));
- $this->repoInstalled->addPackage($packageAInstalled = $this->getPackage('A', '1.0'));
- $this->pool->addRepository($this->repoInstalled);
- $this->pool->addRepository($this->repo);
-
- $literals = array($packageA->getId(), $packageAInstalled->getId());
- $expected = array($packageA->getId());
-
- $selected = $this->policy->selectPreferredPackages($this->pool, $this->mapFromRepo($this->repoInstalled), $literals);
-
- $this->assertSame($expected, $selected);
- }
-
- public function testSelectFirstRepo()
- {
- $otherRepository = new ArrayRepository;
-
- $this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
- $otherRepository->addPackage($packageAImportant = $this->getPackage('A', '1.0'));
-
- $this->pool->addRepository($this->repoInstalled);
- $this->pool->addRepository($otherRepository);
- $this->pool->addRepository($this->repo);
-
- $literals = array($packageA->getId(), $packageAImportant->getId());
- $expected = array($packageAImportant->getId());
-
- $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+ $selected = $this->policy->selectPreferredPackages($pool, $literals);
$this->assertSame($expected, $selected);
}
@@ -155,21 +133,25 @@ class DefaultPolicyTest extends TestCase
$repo2->addPackage($package3 = $this->getPackage('A', '1.1'));
$repo2->addPackage($package4 = $this->getPackage('A', '1.2'));
- $this->pool->addRepository($repo1);
- $this->pool->addRepository($repo2);
+ $this->repositorySet->addRepository($repo1);
+ $this->repositorySet->addRepository($repo2);
+
+ $pool = $this->repositorySet->createPoolForPackage('A', $this->repoLocked);
$literals = array($package1->getId(), $package2->getId(), $package3->getId(), $package4->getId());
$expected = array($package2->getId());
- $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+ $selected = $this->policy->selectPreferredPackages($pool, $literals);
$this->assertSame($expected, $selected);
- $this->pool = new Pool('dev');
- $this->pool->addRepository($repo2);
- $this->pool->addRepository($repo1);
+ $this->repositorySet = new RepositorySet('dev');
+ $this->repositorySet->addRepository($repo2);
+ $this->repositorySet->addRepository($repo1);
+
+ $pool = $this->repositorySet->createPoolForPackage('A', $this->repoLocked);
$expected = array($package4->getId());
- $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+ $selected = $this->policy->selectPreferredPackages($pool, $literals);
$this->assertSame($expected, $selected);
}
@@ -186,11 +168,13 @@ class DefaultPolicyTest extends TestCase
$repoImportant->addPackage($packageA2AliasImportant = new AliasPackage($packageA2Important, '2.1.9999999.9999999-dev', '2.1.x-dev'));
$packageAAliasImportant->setRootPackageAlias(true);
- $this->pool->addRepository($this->repoInstalled);
- $this->pool->addRepository($repoImportant);
- $this->pool->addRepository($this->repo);
+ $this->repositorySet->addRepository($repoImportant);
+ $this->repositorySet->addRepository($this->repo);
+ $this->repositorySet->addRepository($this->repoLocked);
- $packages = $this->pool->whatProvides('a', new Constraint('=', '2.1.9999999.9999999-dev'));
+ $pool = $this->repositorySet->createPoolForPackage('A', $this->repoLocked);
+
+ $packages = $pool->whatProvides('a', new Constraint('=', '2.1.9999999.9999999-dev'));
$literals = array();
foreach ($packages as $package) {
$literals[] = $package->getId();
@@ -198,7 +182,7 @@ class DefaultPolicyTest extends TestCase
$expected = array($packageAAliasImportant->getId());
- $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+ $selected = $this->policy->selectPreferredPackages($pool, $literals);
$this->assertSame($expected, $selected);
}
@@ -211,12 +195,14 @@ class DefaultPolicyTest extends TestCase
$packageA->setProvides(array(new Link('A', 'X', new Constraint('==', '1.0'), 'provides')));
$packageB->setProvides(array(new Link('B', 'X', new Constraint('==', '1.0'), 'provides')));
- $this->pool->addRepository($this->repo);
+ $this->repositorySet->addRepository($this->repo);
+
+ $pool = $this->repositorySet->createPoolForPackages(array('A', 'B'), $this->repoLocked);
$literals = array($packageA->getId(), $packageB->getId());
$expected = $literals;
- $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+ $selected = $this->policy->selectPreferredPackages($pool, $literals);
$this->assertSame($expected, $selected);
}
@@ -228,12 +214,14 @@ class DefaultPolicyTest extends TestCase
$packageB->setReplaces(array(new Link('B', 'A', new Constraint('==', '1.0'), 'replaces')));
- $this->pool->addRepository($this->repo);
+ $this->repositorySet->addRepository($this->repo);
+
+ $pool = $this->repositorySet->createPoolForPackages(array('A', 'B'), $this->repoLocked);
$literals = array($packageA->getId(), $packageB->getId());
$expected = $literals;
- $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+ $selected = $this->policy->selectPreferredPackages($pool, $literals);
$this->assertSame($expected, $selected);
}
@@ -247,12 +235,14 @@ class DefaultPolicyTest extends TestCase
$packageA->setReplaces(array(new Link('vendor-a/replacer', 'vendor-a/package', new Constraint('==', '1.0'), 'replaces')));
$packageB->setReplaces(array(new Link('vendor-b/replacer', 'vendor-a/package', new Constraint('==', '1.0'), 'replaces')));
- $this->pool->addRepository($this->repo);
+ $this->repositorySet->addRepository($this->repo);
+
+ $pool = $this->repositorySet->createPoolForPackages(array('vendor-a/replacer', 'vendor-b/replacer'), $this->repoLocked);
$literals = array($packageA->getId(), $packageB->getId());
$expected = $literals;
- $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals, 'vendor-a/package');
+ $selected = $this->policy->selectPreferredPackages($pool, $literals, 'vendor-a/package');
$this->assertEquals($expected, $selected);
// test with reversed order in repo
@@ -260,38 +250,32 @@ class DefaultPolicyTest extends TestCase
$repo->addPackage($packageA = clone $packageA);
$repo->addPackage($packageB = clone $packageB);
- $pool = new Pool('dev');
- $pool->addRepository($this->repo);
+ $repositorySet = new RepositorySet('dev');
+ $repositorySet->addRepository($this->repo);
+
+ $pool = $this->repositorySet->createPoolForPackages(array('vendor-a/replacer', 'vendor-b/replacer'), $this->repoLocked);
$literals = array($packageA->getId(), $packageB->getId());
$expected = $literals;
- $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals, 'vendor-a/package');
+ $selected = $this->policy->selectPreferredPackages($pool, $literals, 'vendor-a/package');
$this->assertSame($expected, $selected);
}
- protected function mapFromRepo(RepositoryInterface $repo)
- {
- $map = array();
- foreach ($repo->getPackages() as $package) {
- $map[$package->getId()] = true;
- }
-
- return $map;
- }
-
public function testSelectLowest()
{
$policy = new DefaultPolicy(false, true);
$this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0'));
$this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0'));
- $this->pool->addRepository($this->repo);
+ $this->repositorySet->addRepository($this->repo);
+
+ $pool = $this->repositorySet->createPoolForPackage('A', $this->repoLocked);
$literals = array($packageA1->getId(), $packageA2->getId());
$expected = array($packageA1->getId());
- $selected = $policy->selectPreferredPackages($this->pool, array(), $literals);
+ $selected = $policy->selectPreferredPackages($pool, $literals);
$this->assertSame($expected, $selected);
}
diff --git a/tests/Composer/Test/DependencyResolver/Fixtures/poolbuilder/fixed-packages-do-not-load-from-repos.test b/tests/Composer/Test/DependencyResolver/Fixtures/poolbuilder/fixed-packages-do-not-load-from-repos.test
new file mode 100644
index 000000000..f1dbd69fe
--- /dev/null
+++ b/tests/Composer/Test/DependencyResolver/Fixtures/poolbuilder/fixed-packages-do-not-load-from-repos.test
@@ -0,0 +1,23 @@
+--TEST--
+Fixed packages do not get loaded from the repos
+
+--REQUEST--
+{
+ "some/pkg": "*"
+}
+
+--FIXED--
+[
+ {"name": "some/pkg", "version": "1.0.3", "id": 1}
+]
+
+--PACKAGES--
+[
+ {"name": "some/pkg", "version": "1.0.0"},
+ {"name": "some/pkg", "version": "1.1.0"}
+]
+
+--EXPECT--
+[
+ 1
+]
diff --git a/tests/Composer/Test/DependencyResolver/Fixtures/poolbuilder/stability-flags-take-over-minimum-stability-and-filter-packages.test b/tests/Composer/Test/DependencyResolver/Fixtures/poolbuilder/stability-flags-take-over-minimum-stability-and-filter-packages.test
new file mode 100644
index 000000000..5a6ca8f2f
--- /dev/null
+++ b/tests/Composer/Test/DependencyResolver/Fixtures/poolbuilder/stability-flags-take-over-minimum-stability-and-filter-packages.test
@@ -0,0 +1,44 @@
+--TEST--
+Stability flags apply
+
+--ROOT--
+{
+ "stability-flags": {
+ "flagged/pkg": "alpha"
+ },
+ "minimum-stability": "RC",
+ "aliases": [
+ {
+ "package": "default/pkg",
+ "version": "1.0.0-RC",
+ "alias": "1.2.0"
+ }
+ ]
+}
+
+--REQUEST--
+{
+ "flagged/pkg": "*",
+ "default/pkg": "*"
+}
+
+--PACKAGES--
+[
+ {"name": "flagged/pkg", "version": "1.0.0", "id": 1},
+ {"name": "flagged/pkg", "version": "1.0.0-beta", "id": 2},
+ {"name": "flagged/pkg", "version": "1.0.0-dev", "id": 3},
+ {"name": "flagged/pkg", "version": "1.0.0-RC", "id": 4},
+ {"name": "default/pkg", "version": "1.0.0", "id": 5},
+ {"name": "default/pkg", "version": "1.0.0-RC", "id": 6},
+ {"name": "default/pkg", "version": "1.0.0-alpha", "id": 7}
+]
+
+--EXPECT--
+[
+ 1,
+ 2,
+ 4,
+ 5,
+ 6,
+ "default/pkg-1.2.0.0 alias of 6"
+]
diff --git a/tests/Composer/Test/DependencyResolver/PoolBuilderTest.php b/tests/Composer/Test/DependencyResolver/PoolBuilderTest.php
new file mode 100644
index 000000000..f51661db6
--- /dev/null
+++ b/tests/Composer/Test/DependencyResolver/PoolBuilderTest.php
@@ -0,0 +1,194 @@
+
+ * Jordi Boggiano
+ *
+ * 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\IO\NullIO;
+use Composer\Repository\ArrayRepository;
+use Composer\Repository\LockArrayRepository;
+use Composer\DependencyResolver\DefaultPolicy;
+use Composer\DependencyResolver\Pool;
+use Composer\DependencyResolver\PoolBuilder;
+use Composer\DependencyResolver\Request;
+use Composer\DependencyResolver\Solver;
+use Composer\DependencyResolver\SolverProblemsException;
+use Composer\Package\BasePackage;
+use Composer\Package\AliasPackage;
+use Composer\Json\JsonFile;
+use Composer\Package\Loader\ArrayLoader;
+use Composer\Package\Version\VersionParser;
+use Composer\Repository\InstalledArrayRepository;
+use Composer\Repository\RepositorySet;
+use Composer\Test\TestCase;
+use Composer\Semver\Constraint\MultiConstraint;
+
+class PoolBuilderTest extends TestCase
+{
+ /**
+ * @dataProvider getIntegrationTests
+ */
+ public function testPoolBuilder($file, $message, $expect, $root, $requestData, $packages, $fixed)
+ {
+ $rootAliases = !empty($root['aliases']) ? $root['aliases'] : array();
+ $minimumStability = !empty($root['minimum-stability']) ? $root['minimum-stability'] : 'stable';
+ $stabilityFlags = !empty($root['stability-flags']) ? $root['stability-flags'] : array();
+ $stabilityFlags = array_map(function ($stability) {
+ return BasePackage::$stabilities[$stability];
+ }, $stabilityFlags);
+
+ $parser = new VersionParser();
+ $normalizedAliases = array();
+ foreach ($rootAliases as $alias) {
+ $normalizedAliases[$alias['package']][$parser->normalize($alias['version'])] = array(
+ 'alias' => $alias['alias'],
+ 'alias_normalized' => $parser->normalize($alias['alias']),
+ );
+ }
+
+ $loader = new ArrayLoader();
+ $packageIds = array();
+ $loadPackage = function ($data) use ($loader, &$packageIds) {
+ if (!empty($data['id'])) {
+ $id = $data['id'];
+ unset($data['id']);
+ }
+
+ $pkg = $loader->load($data);
+
+ if (!empty($id)) {
+ if (!empty($packageIds[$id])) {
+ throw new \LogicException('Duplicate package id '.$id.' defined');
+ }
+ $packageIds[$id] = $pkg;
+ }
+
+ return $pkg;
+ };
+
+ $repositorySet = new RepositorySet($minimumStability, $stabilityFlags, $normalizedAliases);
+ $repositorySet->addRepository($repo = new ArrayRepository());
+ foreach ($packages as $package) {
+ $repo->addPackage($loadPackage($package));
+ }
+
+ $request = new Request();
+ foreach ($requestData as $package => $constraint) {
+ $request->requireName($package, $parser->parseConstraints($constraint));
+ }
+
+ foreach ($fixed as $fixedPackage) {
+ $request->fixPackage($loadPackage($fixedPackage));
+ }
+
+ $pool = $repositorySet->createPool($request, new NullIO());
+ for ($i = 1, $count = count($pool); $i <= $count; $i++) {
+ $result[] = $pool->packageById($i);
+ }
+
+ $result = array_map(function ($package) use ($packageIds) {
+ if ($id = array_search($package, $packageIds, true)) {
+ return $id;
+ }
+
+ if ($package instanceof AliasPackage && $id = array_search($package->getAliasOf(), $packageIds, true)) {
+ return (string) $package->getName().'-'.$package->getVersion() .' alias of '.$id;
+ }
+
+ return (string) $package;
+ }, $result);
+
+ $this->assertSame($expect, $result);
+ }
+
+ public function getIntegrationTests()
+ {
+ $fixturesDir = realpath(__DIR__.'/Fixtures/poolbuilder/');
+ $tests = array();
+ foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($fixturesDir), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
+ if (!preg_match('/\.test$/', $file)) {
+ continue;
+ }
+
+ try {
+ $testData = $this->readTestFile($file, $fixturesDir);
+
+ $message = $testData['TEST'];
+
+ $request = JsonFile::parseJson($testData['REQUEST']);
+ $root = !empty($testData['ROOT']) ? JsonFile::parseJson($testData['ROOT']) : array();
+
+ $packages = JsonFile::parseJson($testData['PACKAGES']);
+ $fixed = array();
+ if (!empty($testData['FIXED'])) {
+ $fixed = JsonFile::parseJson($testData['FIXED']);
+ }
+ $expect = JsonFile::parseJson($testData['EXPECT']);
+ } catch (\Exception $e) {
+ die(sprintf('Test "%s" is not valid: '.$e->getMessage(), str_replace($fixturesDir.'/', '', $file)));
+ }
+
+ $tests[basename($file)] = array(str_replace($fixturesDir.'/', '', $file), $message, $expect, $root, $request, $packages, $fixed);
+ }
+
+ return $tests;
+ }
+
+ protected function readTestFile(\SplFileInfo $file, $fixturesDir)
+ {
+ $tokens = preg_split('#(?:^|\n*)--([A-Z-]+)--\n#', file_get_contents($file->getRealPath()), null, PREG_SPLIT_DELIM_CAPTURE);
+
+ $sectionInfo = array(
+ 'TEST' => true,
+ 'ROOT' => false,
+ 'REQUEST' => true,
+ 'FIXED' => false,
+ 'PACKAGES' => true,
+ 'EXPECT' => true,
+ );
+
+ $section = null;
+ foreach ($tokens as $i => $token) {
+ if (null === $section && empty($token)) {
+ continue; // skip leading blank
+ }
+
+ if (null === $section) {
+ if (!isset($sectionInfo[$token])) {
+ throw new \RuntimeException(sprintf(
+ 'The test file "%s" must not contain a section named "%s".',
+ str_replace($fixturesDir.'/', '', $file),
+ $token
+ ));
+ }
+ $section = $token;
+ continue;
+ }
+
+ $sectionData = $token;
+
+ $data[$section] = $sectionData;
+ $section = $sectionData = null;
+ }
+
+ foreach ($sectionInfo as $section => $required) {
+ if ($required && !isset($data[$section])) {
+ throw new \RuntimeException(sprintf(
+ 'The test file "%s" must have a section named "%s".',
+ str_replace($fixturesDir.'/', '', $file),
+ $section
+ ));
+ }
+ }
+
+ return $data;
+ }
+}
diff --git a/tests/Composer/Test/DependencyResolver/PoolTest.php b/tests/Composer/Test/DependencyResolver/PoolTest.php
index aa38fa31d..ceeb928ba 100644
--- a/tests/Composer/Test/DependencyResolver/PoolTest.php
+++ b/tests/Composer/Test/DependencyResolver/PoolTest.php
@@ -21,91 +21,24 @@ class PoolTest extends TestCase
{
public function testPool()
{
- $pool = new Pool;
- $repo = new ArrayRepository;
$package = $this->getPackage('foo', '1');
- $repo->addPackage($package);
- $pool->addRepository($repo);
+ $pool = $this->createPool(array($package));
$this->assertEquals(array($package), $pool->whatProvides('foo'));
$this->assertEquals(array($package), $pool->whatProvides('foo'));
}
- public function testPoolIgnoresIrrelevantPackages()
- {
- $pool = new Pool('stable', array('bar' => BasePackage::STABILITY_BETA));
- $repo = new ArrayRepository;
- $repo->addPackage($package = $this->getPackage('bar', '1'));
- $repo->addPackage($betaPackage = $this->getPackage('bar', '1-beta'));
- $repo->addPackage($alphaPackage = $this->getPackage('bar', '1-alpha'));
- $repo->addPackage($package2 = $this->getPackage('foo', '1'));
- $repo->addPackage($rcPackage2 = $this->getPackage('foo', '1rc'));
-
- $pool->addRepository($repo);
-
- $this->assertEquals(array($package, $betaPackage), $pool->whatProvides('bar'));
- $this->assertEquals(array($package2), $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 testWhatProvidesSamePackageForDifferentRepositories()
- {
- $pool = new Pool;
- $firstRepository = new ArrayRepository;
- $secondRepository = new ArrayRepository;
-
- $firstPackage = $this->getPackage('foo', '1');
- $secondPackage = $this->getPackage('foo', '1');
- $thirdPackage = $this->getPackage('foo', '2');
-
- $firstRepository->addPackage($firstPackage);
- $secondRepository->addPackage($secondPackage);
- $secondRepository->addPackage($thirdPackage);
-
- $pool->addRepository($firstRepository);
- $pool->addRepository($secondRepository);
-
- $this->assertEquals(array($firstPackage, $secondPackage, $thirdPackage), $pool->whatProvides('foo'));
- }
-
public function testWhatProvidesPackageWithConstraint()
{
- $pool = new Pool;
- $repository = new ArrayRepository;
$firstPackage = $this->getPackage('foo', '1');
$secondPackage = $this->getPackage('foo', '2');
- $repository->addPackage($firstPackage);
- $repository->addPackage($secondPackage);
-
- $pool->addRepository($repository);
+ $pool = $this->createPool(array(
+ $firstPackage,
+ $secondPackage,
+ ));
$this->assertEquals(array($firstPackage, $secondPackage), $pool->whatProvides('foo'));
$this->assertEquals(array($secondPackage), $pool->whatProvides('foo', $this->getVersionConstraint('==', '2')));
@@ -113,20 +46,22 @@ class PoolTest extends TestCase
public function testPackageById()
{
- $pool = new Pool;
- $repository = new ArrayRepository;
$package = $this->getPackage('foo', '1');
- $repository->addPackage($package);
- $pool->addRepository($repository);
+ $pool = $this->createPool(array($package));
$this->assertSame($package, $pool->packageById(1));
}
public function testWhatProvidesWhenPackageCannotBeFound()
{
- $pool = new Pool;
+ $pool = $this->createPool();
$this->assertEquals(array(), $pool->whatProvides('foo'));
}
+
+ protected function createPool(array $packages = array())
+ {
+ return new Pool($packages);
+ }
}
diff --git a/tests/Composer/Test/DependencyResolver/RequestTest.php b/tests/Composer/Test/DependencyResolver/RequestTest.php
index dfa411ed9..e2a3ce9e6 100644
--- a/tests/Composer/Test/DependencyResolver/RequestTest.php
+++ b/tests/Composer/Test/DependencyResolver/RequestTest.php
@@ -18,7 +18,7 @@ use Composer\Test\TestCase;
class RequestTest extends TestCase
{
- public function testRequestInstallAndRemove()
+ public function testRequestInstall()
{
$repo = new ArrayRepository;
$foo = $this->getPackage('foo', '1');
@@ -30,17 +30,13 @@ class RequestTest extends TestCase
$repo->addPackage($foobar);
$request = new Request();
- $request->install('foo');
- $request->fix('bar');
- $request->remove('foobar');
+ $request->requireName('foo');
$this->assertEquals(
array(
- array('cmd' => 'install', 'packageName' => 'foo', 'constraint' => null, 'fixed' => false),
- array('cmd' => 'install', 'packageName' => 'bar', 'constraint' => null, 'fixed' => true),
- array('cmd' => 'remove', 'packageName' => 'foobar', 'constraint' => null, 'fixed' => false),
+ 'foo' => null,
),
- $request->getJobs()
+ $request->getRequires()
);
}
@@ -56,25 +52,13 @@ class RequestTest extends TestCase
$repo2->addPackage($foo2);
$request = new Request();
- $request->install('foo', $constraint = $this->getVersionConstraint('=', '1'));
+ $request->requireName('foo', $constraint = $this->getVersionConstraint('=', '1'));
$this->assertEquals(
array(
- array('cmd' => 'install', 'packageName' => 'foo', 'constraint' => $constraint, 'fixed' => false),
+ 'foo' => $constraint,
),
- $request->getJobs()
- );
- }
-
- public function testUpdateAll()
- {
- $request = new Request();
-
- $request->updateAll();
-
- $this->assertEquals(
- array(array('cmd' => 'update-all')),
- $request->getJobs()
+ $request->getRequires()
);
}
}
diff --git a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php
index f38c48e10..4ebf37bf3 100644
--- a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php
+++ b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php
@@ -17,6 +17,7 @@ use Composer\DependencyResolver\Rule;
use Composer\DependencyResolver\RuleSet;
use Composer\DependencyResolver\RuleSetIterator;
use Composer\DependencyResolver\Pool;
+use Composer\Package\BasePackage;
use Composer\Test\TestCase;
class RuleSetIteratorTest extends TestCase
@@ -26,12 +27,12 @@ class RuleSetIteratorTest extends TestCase
protected function setUp()
{
- $this->pool = new Pool;
+ $this->pool = new Pool();
$this->rules = array(
- RuleSet::TYPE_JOB => array(
- new GenericRule(array(), Rule::RULE_JOB_INSTALL, null),
- new GenericRule(array(), Rule::RULE_JOB_INSTALL, null),
+ RuleSet::TYPE_REQUEST => array(
+ new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, null),
+ new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, null),
),
RuleSet::TYPE_LEARNED => array(
new GenericRule(array(), Rule::RULE_INTERNAL_ALLOW_UPDATE, null),
@@ -50,8 +51,8 @@ class RuleSetIteratorTest extends TestCase
}
$expected = array(
- $this->rules[RuleSet::TYPE_JOB][0],
- $this->rules[RuleSet::TYPE_JOB][1],
+ $this->rules[RuleSet::TYPE_REQUEST][0],
+ $this->rules[RuleSet::TYPE_REQUEST][1],
$this->rules[RuleSet::TYPE_LEARNED][0],
);
@@ -68,8 +69,8 @@ class RuleSetIteratorTest extends TestCase
}
$expected = array(
- RuleSet::TYPE_JOB,
- RuleSet::TYPE_JOB,
+ RuleSet::TYPE_REQUEST,
+ RuleSet::TYPE_REQUEST,
RuleSet::TYPE_LEARNED,
);
diff --git a/tests/Composer/Test/DependencyResolver/RuleSetTest.php b/tests/Composer/Test/DependencyResolver/RuleSetTest.php
index 22c1c15d0..5a89ddf79 100644
--- a/tests/Composer/Test/DependencyResolver/RuleSetTest.php
+++ b/tests/Composer/Test/DependencyResolver/RuleSetTest.php
@@ -16,25 +16,19 @@ use Composer\DependencyResolver\GenericRule;
use Composer\DependencyResolver\Rule;
use Composer\DependencyResolver\RuleSet;
use Composer\DependencyResolver\Pool;
+use Composer\Package\BasePackage;
use Composer\Repository\ArrayRepository;
use Composer\Test\TestCase;
class RuleSetTest extends TestCase
{
- protected $pool;
-
- public function setUp()
- {
- $this->pool = new Pool;
- }
-
public function testAdd()
{
$rules = array(
RuleSet::TYPE_PACKAGE => array(),
- RuleSet::TYPE_JOB => array(
- new GenericRule(array(1), Rule::RULE_JOB_INSTALL, null),
- new GenericRule(array(2), Rule::RULE_JOB_INSTALL, null),
+ RuleSet::TYPE_REQUEST => array(
+ new GenericRule(array(1), Rule::RULE_ROOT_REQUIRE, null),
+ new GenericRule(array(2), Rule::RULE_ROOT_REQUIRE, null),
),
RuleSet::TYPE_LEARNED => array(
new GenericRule(array(), Rule::RULE_INTERNAL_ALLOW_UPDATE, null),
@@ -43,9 +37,9 @@ class RuleSetTest extends TestCase
$ruleSet = new RuleSet;
- $ruleSet->add($rules[RuleSet::TYPE_JOB][0], RuleSet::TYPE_JOB);
+ $ruleSet->add($rules[RuleSet::TYPE_REQUEST][0], RuleSet::TYPE_REQUEST);
$ruleSet->add($rules[RuleSet::TYPE_LEARNED][0], RuleSet::TYPE_LEARNED);
- $ruleSet->add($rules[RuleSet::TYPE_JOB][1], RuleSet::TYPE_JOB);
+ $ruleSet->add($rules[RuleSet::TYPE_REQUEST][1], RuleSet::TYPE_REQUEST);
$this->assertEquals($rules, $ruleSet->getRules());
}
@@ -53,20 +47,20 @@ class RuleSetTest extends TestCase
public function testAddIgnoresDuplicates()
{
$rules = array(
- RuleSet::TYPE_JOB => array(
- new GenericRule(array(), Rule::RULE_JOB_INSTALL, null),
- new GenericRule(array(), Rule::RULE_JOB_INSTALL, null),
- new GenericRule(array(), Rule::RULE_JOB_INSTALL, null),
+ RuleSet::TYPE_REQUEST => array(
+ new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, null),
+ new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, null),
+ new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, null),
),
);
$ruleSet = new RuleSet;
- $ruleSet->add($rules[RuleSet::TYPE_JOB][0], RuleSet::TYPE_JOB);
- $ruleSet->add($rules[RuleSet::TYPE_JOB][1], RuleSet::TYPE_JOB);
- $ruleSet->add($rules[RuleSet::TYPE_JOB][2], RuleSet::TYPE_JOB);
+ $ruleSet->add($rules[RuleSet::TYPE_REQUEST][0], RuleSet::TYPE_REQUEST);
+ $ruleSet->add($rules[RuleSet::TYPE_REQUEST][1], RuleSet::TYPE_REQUEST);
+ $ruleSet->add($rules[RuleSet::TYPE_REQUEST][2], RuleSet::TYPE_REQUEST);
- $this->assertCount(1, $ruleSet->getIteratorFor(array(RuleSet::TYPE_JOB)));
+ $this->assertCount(1, $ruleSet->getIteratorFor(array(RuleSet::TYPE_REQUEST)));
}
/**
@@ -76,15 +70,15 @@ class RuleSetTest extends TestCase
{
$ruleSet = new RuleSet;
- $ruleSet->add(new GenericRule(array(), Rule::RULE_JOB_INSTALL, null), 7);
+ $ruleSet->add(new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, null), 7);
}
public function testCount()
{
$ruleSet = new RuleSet;
- $ruleSet->add(new GenericRule(array(1), Rule::RULE_JOB_INSTALL, null), RuleSet::TYPE_JOB);
- $ruleSet->add(new GenericRule(array(2), Rule::RULE_JOB_INSTALL, null), RuleSet::TYPE_JOB);
+ $ruleSet->add(new GenericRule(array(1), Rule::RULE_ROOT_REQUIRE, null), RuleSet::TYPE_REQUEST);
+ $ruleSet->add(new GenericRule(array(2), Rule::RULE_ROOT_REQUIRE, null), RuleSet::TYPE_REQUEST);
$this->assertEquals(2, $ruleSet->count());
}
@@ -93,8 +87,8 @@ class RuleSetTest extends TestCase
{
$ruleSet = new RuleSet;
- $rule = new GenericRule(array(), Rule::RULE_JOB_INSTALL, null);
- $ruleSet->add($rule, RuleSet::TYPE_JOB);
+ $rule = new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, null);
+ $ruleSet->add($rule, RuleSet::TYPE_REQUEST);
$this->assertSame($rule, $ruleSet->ruleById[0]);
}
@@ -103,9 +97,9 @@ class RuleSetTest extends TestCase
{
$ruleSet = new RuleSet;
- $rule1 = new GenericRule(array(1), Rule::RULE_JOB_INSTALL, null);
- $rule2 = new GenericRule(array(2), Rule::RULE_JOB_INSTALL, null);
- $ruleSet->add($rule1, RuleSet::TYPE_JOB);
+ $rule1 = new GenericRule(array(1), Rule::RULE_ROOT_REQUIRE, null);
+ $rule2 = new GenericRule(array(2), Rule::RULE_ROOT_REQUIRE, null);
+ $ruleSet->add($rule1, RuleSet::TYPE_REQUEST);
$ruleSet->add($rule2, RuleSet::TYPE_LEARNED);
$iterator = $ruleSet->getIterator();
@@ -118,10 +112,10 @@ class RuleSetTest extends TestCase
public function testGetIteratorFor()
{
$ruleSet = new RuleSet;
- $rule1 = new GenericRule(array(1), Rule::RULE_JOB_INSTALL, null);
- $rule2 = new GenericRule(array(2), Rule::RULE_JOB_INSTALL, null);
+ $rule1 = new GenericRule(array(1), Rule::RULE_ROOT_REQUIRE, null);
+ $rule2 = new GenericRule(array(2), Rule::RULE_ROOT_REQUIRE, null);
- $ruleSet->add($rule1, RuleSet::TYPE_JOB);
+ $ruleSet->add($rule1, RuleSet::TYPE_REQUEST);
$ruleSet->add($rule2, RuleSet::TYPE_LEARNED);
$iterator = $ruleSet->getIteratorFor(RuleSet::TYPE_LEARNED);
@@ -132,29 +126,32 @@ class RuleSetTest extends TestCase
public function testGetIteratorWithout()
{
$ruleSet = new RuleSet;
- $rule1 = new GenericRule(array(1), Rule::RULE_JOB_INSTALL, null);
- $rule2 = new GenericRule(array(2), Rule::RULE_JOB_INSTALL, null);
+ $rule1 = new GenericRule(array(1), Rule::RULE_ROOT_REQUIRE, null);
+ $rule2 = new GenericRule(array(2), Rule::RULE_ROOT_REQUIRE, null);
- $ruleSet->add($rule1, RuleSet::TYPE_JOB);
+ $ruleSet->add($rule1, RuleSet::TYPE_REQUEST);
$ruleSet->add($rule2, RuleSet::TYPE_LEARNED);
- $iterator = $ruleSet->getIteratorWithout(RuleSet::TYPE_JOB);
+ $iterator = $ruleSet->getIteratorWithout(RuleSet::TYPE_REQUEST);
$this->assertSame($rule2, $iterator->current());
}
public function testPrettyString()
{
- $repo = new ArrayRepository;
- $repo->addPackage($p = $this->getPackage('foo', '2.1'));
- $this->pool->addRepository($repo);
+ $pool = new Pool(array(
+ $p = $this->getPackage('foo', '2.1'),
+ ));
+
+ $repositorySetMock = $this->getMockBuilder('Composer\Repository\RepositorySet')->disableOriginalConstructor()->getMock();
+ $requestMock = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock();
$ruleSet = new RuleSet;
$literal = $p->getId();
- $rule = new GenericRule(array($literal), Rule::RULE_JOB_INSTALL, null);
+ $rule = new GenericRule(array($literal), Rule::RULE_ROOT_REQUIRE, array('packageName' => 'foo/bar', 'constraint' => null));
- $ruleSet->add($rule, RuleSet::TYPE_JOB);
+ $ruleSet->add($rule, RuleSet::TYPE_REQUEST);
- $this->assertContains('JOB : Install command rule (install foo 2.1)', $ruleSet->getPrettyString($this->pool));
+ $this->assertContains('REQUEST : No package found to satisfy root composer.json require foo/bar', $ruleSet->getPrettyString($repositorySetMock, $requestMock, $pool));
}
}
diff --git a/tests/Composer/Test/DependencyResolver/RuleTest.php b/tests/Composer/Test/DependencyResolver/RuleTest.php
index 19f5fddea..f819397fb 100644
--- a/tests/Composer/Test/DependencyResolver/RuleTest.php
+++ b/tests/Composer/Test/DependencyResolver/RuleTest.php
@@ -16,21 +16,16 @@ use Composer\DependencyResolver\GenericRule;
use Composer\DependencyResolver\Rule;
use Composer\DependencyResolver\RuleSet;
use Composer\DependencyResolver\Pool;
+use Composer\Package\BasePackage;
+use Composer\Package\Link;
use Composer\Repository\ArrayRepository;
use Composer\Test\TestCase;
class RuleTest extends TestCase
{
- protected $pool;
-
- public function setUp()
- {
- $this->pool = new Pool;
- }
-
public function testGetHash()
{
- $rule = new GenericRule(array(123), Rule::RULE_JOB_INSTALL, null);
+ $rule = new GenericRule(array(123), Rule::RULE_ROOT_REQUIRE, null);
$hash = unpack('ihash', md5('123', true));
$this->assertEquals($hash['hash'], $rule->getHash());
@@ -38,39 +33,39 @@ class RuleTest extends TestCase
public function testEqualsForRulesWithDifferentHashes()
{
- $rule = new GenericRule(array(1, 2), Rule::RULE_JOB_INSTALL, null);
- $rule2 = new GenericRule(array(1, 3), Rule::RULE_JOB_INSTALL, null);
+ $rule = new GenericRule(array(1, 2), Rule::RULE_ROOT_REQUIRE, null);
+ $rule2 = new GenericRule(array(1, 3), Rule::RULE_ROOT_REQUIRE, null);
$this->assertFalse($rule->equals($rule2));
}
public function testEqualsForRulesWithDifferLiteralsQuantity()
{
- $rule = new GenericRule(array(1, 12), Rule::RULE_JOB_INSTALL, null);
- $rule2 = new GenericRule(array(1), Rule::RULE_JOB_INSTALL, null);
+ $rule = new GenericRule(array(1, 12), Rule::RULE_ROOT_REQUIRE, null);
+ $rule2 = new GenericRule(array(1), Rule::RULE_ROOT_REQUIRE, null);
$this->assertFalse($rule->equals($rule2));
}
public function testEqualsForRulesWithSameLiterals()
{
- $rule = new GenericRule(array(1, 12), Rule::RULE_JOB_INSTALL, null);
- $rule2 = new GenericRule(array(1, 12), Rule::RULE_JOB_INSTALL, null);
+ $rule = new GenericRule(array(1, 12), Rule::RULE_ROOT_REQUIRE, null);
+ $rule2 = new GenericRule(array(1, 12), Rule::RULE_ROOT_REQUIRE, null);
$this->assertTrue($rule->equals($rule2));
}
public function testSetAndGetType()
{
- $rule = new GenericRule(array(), Rule::RULE_JOB_INSTALL, null);
- $rule->setType(RuleSet::TYPE_JOB);
+ $rule = new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, null);
+ $rule->setType(RuleSet::TYPE_REQUEST);
- $this->assertEquals(RuleSet::TYPE_JOB, $rule->getType());
+ $this->assertEquals(RuleSet::TYPE_REQUEST, $rule->getType());
}
public function testEnable()
{
- $rule = new GenericRule(array(), Rule::RULE_JOB_INSTALL, null);
+ $rule = new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, null);
$rule->disable();
$rule->enable();
@@ -80,7 +75,7 @@ class RuleTest extends TestCase
public function testDisable()
{
- $rule = new GenericRule(array(), Rule::RULE_JOB_INSTALL, null);
+ $rule = new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, null);
$rule->enable();
$rule->disable();
@@ -90,8 +85,8 @@ class RuleTest extends TestCase
public function testIsAssertions()
{
- $rule = new GenericRule(array(1, 12), Rule::RULE_JOB_INSTALL, null);
- $rule2 = new GenericRule(array(1), Rule::RULE_JOB_INSTALL, null);
+ $rule = new GenericRule(array(1, 12), Rule::RULE_ROOT_REQUIRE, null);
+ $rule2 = new GenericRule(array(1), Rule::RULE_ROOT_REQUIRE, null);
$this->assertFalse($rule->isAssertion());
$this->assertTrue($rule2->isAssertion());
@@ -99,13 +94,16 @@ class RuleTest extends TestCase
public function testPrettyString()
{
- $repo = new ArrayRepository;
- $repo->addPackage($p1 = $this->getPackage('foo', '2.1'));
- $repo->addPackage($p2 = $this->getPackage('baz', '1.1'));
- $this->pool->addRepository($repo);
+ $pool = new Pool(array(
+ $p1 = $this->getPackage('foo', '2.1'),
+ $p2 = $this->getPackage('baz', '1.1'),
+ ));
- $rule = new GenericRule(array($p1->getId(), -$p2->getId()), Rule::RULE_JOB_INSTALL, null);
+ $repositorySetMock = $this->getMockBuilder('Composer\Repository\RepositorySet')->disableOriginalConstructor()->getMock();
+ $requestMock = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock();
- $this->assertEquals('Install command rule (don\'t install baz 1.1|install foo 2.1)', $rule->getPrettyString($this->pool));
+ $rule = new GenericRule(array($p1->getId(), -$p2->getId()), Rule::RULE_PACKAGE_REQUIRES, new Link('baz', 'foo'));
+
+ $this->assertEquals('baz 1.1 relates to foo -> satisfiable by foo[2.1].', $rule->getPrettyString($repositorySetMock, $requestMock, $pool));
}
}
diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php
index 4dadee7d8..3090d9d83 100644
--- a/tests/Composer/Test/DependencyResolver/SolverTest.php
+++ b/tests/Composer/Test/DependencyResolver/SolverTest.php
@@ -14,33 +14,36 @@ namespace Composer\Test\DependencyResolver;
use Composer\IO\NullIO;
use Composer\Repository\ArrayRepository;
+use Composer\Repository\LockArrayRepository;
use Composer\DependencyResolver\DefaultPolicy;
use Composer\DependencyResolver\Pool;
use Composer\DependencyResolver\Request;
use Composer\DependencyResolver\Solver;
use Composer\DependencyResolver\SolverProblemsException;
use Composer\Package\Link;
+use Composer\Repository\InstalledArrayRepository;
+use Composer\Repository\RepositorySet;
use Composer\Test\TestCase;
use Composer\Semver\Constraint\MultiConstraint;
class SolverTest extends TestCase
{
- protected $pool;
+ protected $repoSet;
protected $repo;
- protected $repoInstalled;
+ protected $repoLocked;
protected $request;
protected $policy;
protected $solver;
+ protected $pool;
public function setUp()
{
- $this->pool = new Pool;
+ $this->repoSet = new RepositorySet();
$this->repo = new ArrayRepository;
- $this->repoInstalled = new ArrayRepository;
+ $this->repoLocked = new LockArrayRepository;
- $this->request = new Request();
+ $this->request = new Request($this->repoLocked);
$this->policy = new DefaultPolicy;
- $this->solver = new Solver($this->policy, $this->pool, $this->repoInstalled, new NullIO());
}
public function testSolverInstallSingle()
@@ -48,16 +51,16 @@ class SolverTest extends TestCase
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
$this->reposComplete();
- $this->request->install('A');
+ $this->request->requireName('A');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $packageA),
));
}
- public function testSolverRemoveIfNotInstalled()
+ public function testSolverRemoveIfNotRequested()
{
- $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
+ $this->repoLocked->addPackage($packageA = $this->getPackage('A', '1.0'));
$this->reposComplete();
$this->checkSolverResult(array(
@@ -70,8 +73,9 @@ class SolverTest extends TestCase
$this->repo->addPackage($this->getPackage('A', '1.0'));
$this->reposComplete();
- $this->request->install('B', $this->getVersionConstraint('==', '1'));
+ $this->request->requireName('B', $this->getVersionConstraint('==', '1'));
+ $this->createSolver();
try {
$transaction = $this->solver->solve($this->request);
$this->fail('Unsolvable conflict did not result in exception.');
@@ -79,7 +83,7 @@ class SolverTest extends TestCase
$problems = $e->getProblems();
$this->assertCount(1, $problems);
$this->assertEquals(2, $e->getCode());
- $this->assertEquals("\n - The requested package b could not be found in any version, there may be a typo in the package name.", $problems[0]->getPrettyString());
+ $this->assertEquals("\n - Root composer.json requires b, it could not be found in any version, there may be a typo in the package name.", $problems[0]->getPrettyString($this->repoSet, $this->request, $this->pool));
}
}
@@ -91,11 +95,10 @@ class SolverTest extends TestCase
$repo1->addPackage($foo1 = $this->getPackage('foo', '1'));
$repo2->addPackage($foo2 = $this->getPackage('foo', '1'));
- $this->pool->addRepository($this->repoInstalled);
- $this->pool->addRepository($repo1);
- $this->pool->addRepository($repo2);
+ $this->repoSet->addRepository($repo1);
+ $this->repoSet->addRepository($repo2);
- $this->request->install('foo');
+ $this->request->requireName('foo');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $foo1),
@@ -112,7 +115,7 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
+ $this->request->requireName('A');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $packageB),
@@ -138,7 +141,7 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
+ $this->request->requireName('A');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $newPackageB11),
@@ -162,9 +165,9 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
- $this->request->install('B');
- $this->request->install('C');
+ $this->request->requireName('A');
+ $this->request->requireName('B');
+ $this->request->requireName('C');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $packageA),
@@ -173,62 +176,38 @@ class SolverTest extends TestCase
));
}
- public function testSolverInstallInstalled()
+ public function testSolverFixLocked()
{
- $this->repoInstalled->addPackage($this->getPackage('A', '1.0'));
+ $this->repoLocked->addPackage($packageA = $this->getPackage('A', '1.0'));
$this->reposComplete();
- $this->request->install('A');
+ $this->request->fixPackage($packageA);
$this->checkSolverResult(array());
}
- public function testSolverInstallInstalledWithAlternative()
+ public function testSolverFixLockedWithAlternative()
{
$this->repo->addPackage($this->getPackage('A', '1.0'));
- $this->repoInstalled->addPackage($this->getPackage('A', '1.0'));
+ $this->repoLocked->addPackage($packageA = $this->getPackage('A', '1.0'));
$this->reposComplete();
- $this->request->install('A');
-
- $this->checkSolverResult(array());
- }
-
- public function testSolverRemoveSingle()
- {
- $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
- $this->reposComplete();
-
- $this->request->remove('A');
-
- $this->checkSolverResult(array(
- array('job' => 'remove', 'package' => $packageA),
- ));
- }
-
- public function testSolverRemoveUninstalled()
- {
- $this->repo->addPackage($this->getPackage('A', '1.0'));
- $this->reposComplete();
-
- $this->request->remove('A');
+ $this->request->fixPackage($packageA);
$this->checkSolverResult(array());
}
public function testSolverUpdateDoesOnlyUpdate()
{
- $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
- $this->repoInstalled->addPackage($packageB = $this->getPackage('B', '1.0'));
+ $this->repoLocked->addPackage($packageA = $this->getPackage('A', '1.0'));
+ $this->repoLocked->addPackage($packageB = $this->getPackage('B', '1.0'));
$this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1'));
$this->reposComplete();
$packageA->setRequires(array('b' => new Link('A', 'B', $this->getVersionConstraint('>=', '1.0.0.0'), 'requires')));
- $this->request->install('A', $this->getVersionConstraint('=', '1.0.0.0'));
- $this->request->install('B', $this->getVersionConstraint('=', '1.1.0.0'));
- $this->request->update('A', $this->getVersionConstraint('=', '1.0.0.0'));
- $this->request->update('B', $this->getVersionConstraint('=', '1.0.0.0'));
+ $this->request->fixPackage($packageA);
+ $this->request->requireName('B', $this->getVersionConstraint('=', '1.1.0.0'));
$this->checkSolverResult(array(
array('job' => 'update', 'from' => $packageB, 'to' => $newPackageB),
@@ -237,12 +216,11 @@ class SolverTest extends TestCase
public function testSolverUpdateSingle()
{
- $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
+ $this->repoLocked->addPackage($packageA = $this->getPackage('A', '1.0'));
$this->repo->addPackage($newPackageA = $this->getPackage('A', '1.1'));
$this->reposComplete();
- $this->request->install('A');
- $this->request->update('A');
+ $this->request->requireName('A');
$this->checkSolverResult(array(
array('job' => 'update', 'from' => $packageA, 'to' => $newPackageA),
@@ -251,8 +229,8 @@ class SolverTest extends TestCase
public function testSolverUpdateAll()
{
- $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
- $this->repoInstalled->addPackage($packageB = $this->getPackage('B', '1.0'));
+ $this->repoLocked->addPackage($packageA = $this->getPackage('A', '1.0'));
+ $this->repoLocked->addPackage($packageB = $this->getPackage('B', '1.0'));
$this->repo->addPackage($newPackageA = $this->getPackage('A', '1.1'));
$this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1'));
@@ -261,8 +239,7 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
- $this->request->updateAll();
+ $this->request->requireName('A');
$this->checkSolverResult(array(
array('job' => 'update', 'from' => $packageB, 'to' => $newPackageB),
@@ -272,28 +249,26 @@ class SolverTest extends TestCase
public function testSolverUpdateCurrent()
{
- $this->repoInstalled->addPackage($this->getPackage('A', '1.0'));
+ $this->repoLocked->addPackage($this->getPackage('A', '1.0'));
$this->repo->addPackage($this->getPackage('A', '1.0'));
$this->reposComplete();
- $this->request->install('A');
- $this->request->update('A');
+ $this->request->requireName('A');
$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->repoLocked->addPackage($packageA = $this->getPackage('A', '1.0'));
+ $this->repoLocked->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->install('A');
- $this->request->install('B');
- $this->request->update('A');
+ $this->request->requireName('A');
+ $this->request->fixPackage($packageB);
$this->checkSolverResult(array(
array('job' => 'update', 'from' => $packageA, 'to' => $packageAnewer),
@@ -302,13 +277,12 @@ class SolverTest extends TestCase
public function testSolverUpdateConstrained()
{
- $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
+ $this->repoLocked->addPackage($packageA = $this->getPackage('A', '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', $this->getVersionConstraint('<', '2.0.0.0'));
- $this->request->update('A');
+ $this->request->requireName('A', $this->getVersionConstraint('<', '2.0.0.0'));
$this->checkSolverResult(array(array(
'job' => 'update',
@@ -319,13 +293,12 @@ class SolverTest extends TestCase
public function testSolverUpdateFullyConstrained()
{
- $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
+ $this->repoLocked->addPackage($packageA = $this->getPackage('A', '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', $this->getVersionConstraint('<', '2.0.0.0'));
- $this->request->update('A', $this->getVersionConstraint('=', '1.0.0.0'));
+ $this->request->requireName('A', $this->getVersionConstraint('<', '2.0.0.0'));
$this->checkSolverResult(array(array(
'job' => 'update',
@@ -336,32 +309,31 @@ class SolverTest extends TestCase
public function testSolverUpdateFullyConstrainedPrunesInstalledPackages()
{
- $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
- $this->repoInstalled->addPackage($packageB = $this->getPackage('B', '1.0'));
+ $this->repoLocked->addPackage($packageA = $this->getPackage('A', '1.0'));
+ $this->repoLocked->addPackage($packageB = $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', $this->getVersionConstraint('<', '2.0.0.0'));
- $this->request->update('A', $this->getVersionConstraint('=', '1.0.0.0'));
+ $this->request->requireName('A', $this->getVersionConstraint('<', '2.0.0.0'));
$this->checkSolverResult(array(
+ array(
+ 'job' => 'remove',
+ 'package' => $packageB,
+ ),
array(
'job' => 'update',
'from' => $packageA,
'to' => $newPackageA,
),
- array(
- 'job' => 'remove',
- 'package' => $packageB,
- ),
));
}
public function testSolverAllJobs()
{
- $this->repoInstalled->addPackage($packageD = $this->getPackage('D', '1.0'));
- $this->repoInstalled->addPackage($oldPackageC = $this->getPackage('C', '1.0'));
+ $this->repoLocked->addPackage($packageD = $this->getPackage('D', '1.0'));
+ $this->repoLocked->addPackage($oldPackageC = $this->getPackage('C', '1.0'));
$this->repo->addPackage($packageA = $this->getPackage('A', '2.0'));
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
@@ -372,16 +344,14 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
- $this->request->install('C');
- $this->request->update('C');
- $this->request->remove('D');
+ $this->request->requireName('A');
+ $this->request->requireName('C');
$this->checkSolverResult(array(
- array('job' => 'update', 'from' => $oldPackageC, 'to' => $packageC),
+ array('job' => 'remove', 'package' => $packageD),
array('job' => 'install', 'package' => $packageB),
array('job' => 'install', 'package' => $packageA),
- array('job' => 'remove', 'package' => $packageD),
+ array('job' => 'update', 'from' => $oldPackageC, 'to' => $packageC),
));
}
@@ -396,7 +366,7 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
+ $this->request->requireName('A');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $middlePackageB),
@@ -406,16 +376,17 @@ class SolverTest extends TestCase
public function testSolverObsolete()
{
- $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
+ $this->repoLocked->addPackage($packageA = $this->getPackage('A', '1.0'));
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
$packageB->setReplaces(array('a' => new Link('B', 'A', new MultiConstraint(array()))));
$this->reposComplete();
- $this->request->install('B');
+ $this->request->requireName('B');
$this->checkSolverResult(array(
- array('job' => 'update', 'from' => $packageA, 'to' => $packageB),
+ array('job' => 'remove', 'package' => $packageA),
+ array('job' => 'install', 'package' => $packageB),
));
}
@@ -426,7 +397,7 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
+ $this->request->requireName('A');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $packageA),
@@ -442,10 +413,11 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
+ $this->request->requireName('A');
// must explicitly pick the provider, so error in this case
$this->setExpectedException('Composer\DependencyResolver\SolverProblemsException');
+ $this->createSolver();
$this->solver->solve($this->request);
}
@@ -459,7 +431,7 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
+ $this->request->requireName('A');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $packageB),
@@ -476,9 +448,10 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
+ $this->request->requireName('A');
$this->setExpectedException('Composer\DependencyResolver\SolverProblemsException');
+ $this->createSolver();
$this->solver->solve($this->request);
}
@@ -492,8 +465,8 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
- $this->request->install('Q');
+ $this->request->requireName('A');
+ $this->request->requireName('Q');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $packageQ),
@@ -530,7 +503,7 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('X');
+ $this->request->requireName('X');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $newPackageB),
@@ -549,7 +522,7 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
+ $this->request->requireName('A');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $packageB2),
@@ -573,13 +546,13 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
- $this->request->install('C');
+ $this->request->requireName('A');
+ $this->request->requireName('C');
$this->checkSolverResult(array(
+ array('job' => 'install', 'package' => $packageB),
array('job' => 'install', 'package' => $packageA),
array('job' => 'install', 'package' => $packageC),
- array('job' => 'install', 'package' => $packageB),
));
}
@@ -611,8 +584,8 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
- $this->request->install('D');
+ $this->request->requireName('A');
+ $this->request->requireName('D');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $packageD2),
@@ -647,10 +620,11 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('C', $this->getVersionConstraint('==', '2.0.0.0-dev'));
+ $this->request->requireName('C', $this->getVersionConstraint('==', '2.0.0.0-dev'));
$this->setExpectedException('Composer\DependencyResolver\SolverProblemsException');
+ $this->createSolver();
$this->solver->solve($this->request);
}
@@ -664,9 +638,10 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
- $this->request->install('B');
+ $this->request->requireName('A');
+ $this->request->requireName('B');
+ $this->createSolver();
try {
$transaction = $this->solver->solve($this->request);
$this->fail('Unsolvable conflict did not result in exception.');
@@ -676,10 +651,10 @@ class SolverTest extends TestCase
$msg = "\n";
$msg .= " Problem 1\n";
- $msg .= " - Installation request for a -> satisfiable by A[1.0].\n";
- $msg .= " - B 1.0 conflicts with A[1.0].\n";
- $msg .= " - Installation request for b -> satisfiable by B[1.0].\n";
- $this->assertEquals($msg, $e->getMessage());
+ $msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n";
+ $msg .= " - A 1.0 conflicts with B 1.0.\n";
+ $msg .= " - Root composer.json requires b -> satisfiable by B[1.0].\n";
+ $this->assertEquals($msg, $e->getPrettyString($this->repoSet, $this->request, $this->pool));
}
}
@@ -694,8 +669,9 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
+ $this->request->requireName('A');
+ $this->createSolver();
try {
$transaction = $this->solver->solve($this->request);
$this->fail('Unsolvable conflict did not result in exception.');
@@ -706,15 +682,9 @@ class SolverTest extends TestCase
$msg = "\n";
$msg .= " Problem 1\n";
- $msg .= " - Installation request for a -> satisfiable by A[1.0].\n";
- $msg .= " - A 1.0 requires b >= 2.0 -> no matching package found.\n\n";
- $msg .= "Potential causes:\n";
- $msg .= " - A typo in the package name\n";
- $msg .= " - The package is not available in a stable-enough version according to your minimum-stability setting\n";
- $msg .= " see for more details.\n";
- $msg .= " - It's a private package and you forgot to add a custom repository to find it\n\n";
- $msg .= "Read for further common problems.";
- $this->assertEquals($msg, $e->getMessage());
+ $msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n";
+ $msg .= " - A 1.0 requires b >= 2.0 -> found B[1.0] but it does not match your constraint.\n";
+ $this->assertEquals($msg, $e->getPrettyString($this->repoSet, $this->request, $this->pool));
}
}
@@ -741,8 +711,9 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
+ $this->request->requireName('A');
+ $this->createSolver();
try {
$transaction = $this->solver->solve($this->request);
$this->fail('Unsolvable conflict did not result in exception.');
@@ -755,10 +726,10 @@ class SolverTest extends TestCase
$msg .= " - C 1.0 requires d >= 1.0 -> satisfiable by D[1.0].\n";
$msg .= " - D 1.0 requires b < 1.0 -> satisfiable by B[0.9].\n";
$msg .= " - B 1.0 requires c >= 1.0 -> satisfiable by C[1.0].\n";
- $msg .= " - Can only install one of: B[0.9, 1.0].\n";
+ $msg .= " - You can only install one version of a package, so only one of these can be installed: B[0.9, 1.0].\n";
$msg .= " - A 1.0 requires b >= 1.0 -> satisfiable by B[1.0].\n";
- $msg .= " - Installation request for a -> satisfiable by A[1.0].\n";
- $this->assertEquals($msg, $e->getMessage());
+ $msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n";
+ $this->assertEquals($msg, $e->getPrettyString($this->repoSet, $this->request, $this->pool));
}
}
@@ -780,8 +751,8 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('symfony/twig-bridge');
- $this->request->install('twig/twig');
+ $this->request->requireName('symfony/twig-bridge');
+ $this->request->requireName('twig/twig');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $packageTwig16),
@@ -806,11 +777,11 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A', $this->getVersionConstraint('==', '1.1.0.0'));
+ $this->request->requireName('A', $this->getVersionConstraint('==', '1.1.0.0'));
$this->checkSolverResult(array(
- array('job' => 'install', 'package' => $packageA2),
array('job' => 'install', 'package' => $packageB),
+ array('job' => 'install', 'package' => $packageA2),
array('job' => 'install', 'package' => $packageA2Alias),
));
}
@@ -828,8 +799,8 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A', $this->getVersionConstraint('==', '2.0'));
- $this->request->install('B');
+ $this->request->requireName('A', $this->getVersionConstraint('==', '2.0'));
+ $this->request->requireName('B');
$this->checkSolverResult(array(
array('job' => 'install', 'package' => $packageA),
@@ -889,7 +860,9 @@ class SolverTest extends TestCase
$this->reposComplete();
- $this->request->install('A');
+ $this->request->requireName('A');
+
+ $this->createSolver();
// check correct setup for assertion later
$this->assertFalse($this->solver->testFlagLearnedPositiveLiteral);
@@ -911,24 +884,32 @@ class SolverTest extends TestCase
protected function reposComplete()
{
- $this->pool->addRepository($this->repoInstalled);
- $this->pool->addRepository($this->repo);
+ $this->repoSet->addRepository($this->repo);
+ $this->repoSet->addRepository($this->repoLocked);
+ }
+
+ protected function createSolver()
+ {
+ $io = new NullIO();
+ $this->pool = $this->repoSet->createPool($this->request, $io);
+ $this->solver = new Solver($this->policy, $this->pool, $io);
}
protected function checkSolverResult(array $expected)
{
+ $this->createSolver();
$transaction = $this->solver->solve($this->request);
$result = array();
- foreach ($transaction as $operation) {
- if ('update' === $operation->getJobType()) {
+ foreach ($transaction->getOperations() as $operation) {
+ if ('update' === $operation->getOperationType()) {
$result[] = array(
'job' => 'update',
'from' => $operation->getInitialPackage(),
'to' => $operation->getTargetPackage(),
);
} else {
- $job = ('uninstall' === $operation->getJobType() ? 'remove' : 'install');
+ $job = ('uninstall' === $operation->getOperationType() ? 'remove' : 'install');
$result[] = array(
'job' => $job,
'package' => $operation->getPackage(),
diff --git a/tests/Composer/Test/DependencyResolver/TransactionTest.php b/tests/Composer/Test/DependencyResolver/TransactionTest.php
new file mode 100644
index 000000000..8b3e66b68
--- /dev/null
+++ b/tests/Composer/Test/DependencyResolver/TransactionTest.php
@@ -0,0 +1,90 @@
+
+ * Jordi Boggiano
+ *
+ * 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\Transaction;
+use Composer\Package\Link;
+use Composer\Test\TestCase;
+
+class TransactionTest extends TestCase
+{
+ public function setUp()
+ {
+ }
+
+ public function testTransactionGenerationAndSorting()
+ {
+ $presentPackages = array(
+ $packageA = $this->getPackage('a/a', 'dev-master'),
+ $packageAalias = $this->getAliasPackage($packageA, '1.0.x-dev'),
+ $packageB = $this->getPackage('b/b', '1.0.0'),
+ $packageE = $this->getPackage('e/e', 'dev-foo'),
+ $packageEalias = $this->getAliasPackage($packageE, '1.0.x-dev'),
+ $packageC = $this->getPackage('c/c', '1.0.0'),
+ );
+ $resultPackages = array(
+ $packageA,
+ $packageAalias,
+ $packageBnew = $this->getPackage('b/b', '2.1.3'),
+ $packageD = $this->getPackage('d/d', '1.2.3'),
+ $packageF = $this->getPackage('f/f', '1.0.0'),
+ $packageFalias1 = $this->getAliasPackage($packageF, 'dev-foo'),
+ $packageG = $this->getPackage('g/g', '1.0.0'),
+ $packageA0first = $this->getPackage('a0/first', '1.2.3'),
+ $packageFalias2 = $this->getAliasPackage($packageF, 'dev-bar'),
+ );
+
+ $packageD->setRequires(array(
+ 'f/f' => new Link('d/d', 'f/f', $this->getVersionConstraint('>', '0.2'), 'requires'),
+ 'g/provider' => new Link('d/d', 'g/provider', $this->getVersionConstraint('>', '0.2'), 'requires'),
+ ));
+ $packageG->setProvides(array('g/provider' => new Link('g/g', 'g/provider', $this->getVersionConstraint('==', '1.0.0'), 'provides')));
+
+ $expectedOperations = array(
+ array('job' => 'uninstall', 'package' => $packageC),
+ array('job' => 'uninstall', 'package' => $packageE),
+ array('job' => 'install', 'package' => $packageA0first),
+ array('job' => 'update', 'from' => $packageB, 'to' => $packageBnew),
+ array('job' => 'install', 'package' => $packageG),
+ array('job' => 'install', 'package' => $packageF),
+ array('job' => 'markAliasInstalled', 'package' => $packageFalias1),
+ array('job' => 'markAliasInstalled', 'package' => $packageFalias2),
+ array('job' => 'install', 'package' => $packageD),
+ array('job' => 'markAliasUninstalled', 'package' => $packageEalias),
+ );
+
+ $transaction = new Transaction($presentPackages, $resultPackages);
+ $this->checkTransactionOperations($transaction, $expectedOperations);
+ }
+
+ protected function checkTransactionOperations(Transaction $transaction, array $expected)
+ {
+ $result = array();
+ foreach ($transaction->getOperations() as $operation) {
+ if ('update' === $operation->getOperationType()) {
+ $result[] = array(
+ 'job' => 'update',
+ 'from' => $operation->getInitialPackage(),
+ 'to' => $operation->getTargetPackage(),
+ );
+ } else {
+ $result[] = array(
+ 'job' => $operation->getOperationType(),
+ 'package' => $operation->getPackage(),
+ );
+ }
+ }
+
+ $this->assertEquals($expected, $result);
+ }
+}
diff --git a/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php b/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php
index a6a9b6b61..793c5eab5 100644
--- a/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php
@@ -16,6 +16,8 @@ use Composer\Test\TestCase;
class ArchiveDownloaderTest extends TestCase
{
+ protected $config;
+
public function testGetFileName()
{
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
@@ -28,8 +30,13 @@ class ArchiveDownloaderTest extends TestCase
$method = new \ReflectionMethod($downloader, 'getFileName');
$method->setAccessible(true);
+ $this->config->expects($this->any())
+ ->method('get')
+ ->with('vendor-dir')
+ ->will($this->returnValue('/vendor'));
+
$first = $method->invoke($downloader, $packageMock, '/path');
- $this->assertRegExp('#/path/[a-z0-9]+\.js#', $first);
+ $this->assertRegExp('#/vendor/composer/[a-z0-9]+\.js#', $first);
$this->assertSame($first, $method->invoke($downloader, $packageMock, '/path'));
}
@@ -156,7 +163,11 @@ class ArchiveDownloaderTest extends TestCase
{
return $this->getMockForAbstractClass(
'Composer\Downloader\ArchiveDownloader',
- array($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getMockBuilder('Composer\Config')->getMock())
+ array(
+ $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
+ $this->config = $this->getMockBuilder('Composer\Config')->getMock(),
+ new \Composer\Util\HttpDownloader($io, $this->config),
+ )
);
}
}
diff --git a/tests/Composer/Test/Downloader/DownloadManagerTest.php b/tests/Composer/Test/Downloader/DownloadManagerTest.php
index b5c63a8d9..4253882b6 100644
--- a/tests/Composer/Test/Downloader/DownloadManagerTest.php
+++ b/tests/Composer/Test/Downloader/DownloadManagerTest.php
@@ -50,7 +50,7 @@ class DownloadManagerTest extends TestCase
$this->setExpectedException('InvalidArgumentException');
- $manager->getDownloaderForInstalledPackage($package);
+ $manager->getDownloaderForPackage($package);
}
public function testGetDownloaderForCorrectlyInstalledDistPackage()
@@ -82,7 +82,7 @@ class DownloadManagerTest extends TestCase
->with('pear')
->will($this->returnValue($downloader));
- $this->assertSame($downloader, $manager->getDownloaderForInstalledPackage($package));
+ $this->assertSame($downloader, $manager->getDownloaderForPackage($package));
}
public function testGetDownloaderForIncorrectlyInstalledDistPackage()
@@ -116,7 +116,7 @@ class DownloadManagerTest extends TestCase
$this->setExpectedException('LogicException');
- $manager->getDownloaderForInstalledPackage($package);
+ $manager->getDownloaderForPackage($package);
}
public function testGetDownloaderForCorrectlyInstalledSourcePackage()
@@ -148,7 +148,7 @@ class DownloadManagerTest extends TestCase
->with('git')
->will($this->returnValue($downloader));
- $this->assertSame($downloader, $manager->getDownloaderForInstalledPackage($package));
+ $this->assertSame($downloader, $manager->getDownloaderForPackage($package));
}
public function testGetDownloaderForIncorrectlyInstalledSourcePackage()
@@ -182,7 +182,7 @@ class DownloadManagerTest extends TestCase
$this->setExpectedException('LogicException');
- $manager->getDownloaderForInstalledPackage($package);
+ $manager->getDownloaderForPackage($package);
}
public function testGetDownloaderForMetapackage()
@@ -195,7 +195,7 @@ class DownloadManagerTest extends TestCase
$manager = new DownloadManager($this->io, false, $this->filesystem);
- $this->assertNull($manager->getDownloaderForInstalledPackage($package));
+ $this->assertNull($manager->getDownloaderForPackage($package));
}
public function testFullPackageDownload()
@@ -223,11 +223,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -274,16 +274,16 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->at(0))
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloaderFail));
$manager
->expects($this->at(1))
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloaderSuccess));
@@ -333,11 +333,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -369,11 +369,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -399,11 +399,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue(null)); // There is no downloader for Metapackages.
@@ -435,11 +435,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -472,11 +472,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -509,11 +509,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -550,33 +550,30 @@ class DownloadManagerTest extends TestCase
$initial
->expects($this->once())
->method('getDistType')
- ->will($this->returnValue('pear'));
+ ->will($this->returnValue('zip'));
$target = $this->createPackageMock();
$target
->expects($this->once())
- ->method('getDistType')
- ->will($this->returnValue('pear'));
+ ->method('getInstallationSource')
+ ->will($this->returnValue('dist'));
$target
->expects($this->once())
- ->method('setInstallationSource')
- ->with('dist');
+ ->method('getDistType')
+ ->will($this->returnValue('zip'));
- $pearDownloader = $this->createDownloaderMock();
- $pearDownloader
+ $zipDownloader = $this->createDownloaderMock();
+ $zipDownloader
->expects($this->once())
->method('update')
->with($initial, $target, 'vendor/bundles/FOS/UserBundle');
+ $zipDownloader
+ ->expects($this->any())
+ ->method('getInstallationSource')
+ ->will($this->returnValue('dist'));
- $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
- ->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
- ->getMock();
- $manager
- ->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
- ->with($initial)
- ->will($this->returnValue($pearDownloader));
+ $manager = new DownloadManager($this->io, false, $this->filesystem);
+ $manager->setDownloader('zip', $zipDownloader);
$manager->update($initial, $target, 'vendor/bundles/FOS/UserBundle');
}
@@ -591,113 +588,89 @@ class DownloadManagerTest extends TestCase
$initial
->expects($this->once())
->method('getDistType')
- ->will($this->returnValue('pear'));
+ ->will($this->returnValue('xz'));
$target = $this->createPackageMock();
$target
- ->expects($this->once())
+ ->expects($this->any())
+ ->method('getInstallationSource')
+ ->will($this->returnValue('dist'));
+ $target
+ ->expects($this->any())
->method('getDistType')
- ->will($this->returnValue('composer'));
+ ->will($this->returnValue('zip'));
- $pearDownloader = $this->createDownloaderMock();
- $pearDownloader
+ $xzDownloader = $this->createDownloaderMock();
+ $xzDownloader
->expects($this->once())
->method('remove')
->with($initial, 'vendor/bundles/FOS/UserBundle');
+ $xzDownloader
+ ->expects($this->any())
+ ->method('getInstallationSource')
+ ->will($this->returnValue('dist'));
- $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
- ->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
- ->getMock();
- $manager
+ $zipDownloader = $this->createDownloaderMock();
+ $zipDownloader
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
- ->with($initial)
- ->will($this->returnValue($pearDownloader));
- $manager
- ->expects($this->once())
- ->method('download')
- ->with($target, 'vendor/bundles/FOS/UserBundle', false);
+ ->method('install')
+ ->with($target, 'vendor/bundles/FOS/UserBundle');
+ $zipDownloader
+ ->expects($this->any())
+ ->method('getInstallationSource')
+ ->will($this->returnValue('dist'));
+
+ $manager = new DownloadManager($this->io, false, $this->filesystem);
+ $manager->setDownloader('xz', $xzDownloader);
+ $manager->setDownloader('zip', $zipDownloader);
$manager->update($initial, $target, 'vendor/bundles/FOS/UserBundle');
}
- public function testUpdateSourceWithEqualTypes()
+ /**
+ * @dataProvider updatesProvider
+ */
+ public function testGetAvailableSourcesUpdateSticksToSameSource($prevPkgSource, $prevPkgIsDev, $targetAvailable, $targetIsDev, $expected)
{
- $initial = $this->createPackageMock();
- $initial
- ->expects($this->once())
- ->method('getInstallationSource')
- ->will($this->returnValue('source'));
- $initial
- ->expects($this->once())
- ->method('getSourceType')
- ->will($this->returnValue('svn'));
+ $initial = null;
+ if ($prevPkgSource) {
+ $initial = $this->prophesize('Composer\Package\PackageInterface');
+ $initial->getInstallationSource()->willReturn($prevPkgSource);
+ $initial->isDev()->willReturn($prevPkgIsDev);
+ }
- $target = $this->createPackageMock();
- $target
- ->expects($this->once())
- ->method('getSourceType')
- ->will($this->returnValue('svn'));
+ $target = $this->prophesize('Composer\Package\PackageInterface');
+ $target->getSourceType()->willReturn(in_array('source', $targetAvailable, true) ? 'git' : null);
+ $target->getDistType()->willReturn(in_array('dist', $targetAvailable, true) ? 'zip' : null);
+ $target->isDev()->willReturn($targetIsDev);
- $svnDownloader = $this->createDownloaderMock();
- $svnDownloader
- ->expects($this->once())
- ->method('update')
- ->with($initial, $target, 'vendor/pkg');
-
- $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
- ->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
- ->getMock();
- $manager
- ->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
- ->with($initial)
- ->will($this->returnValue($svnDownloader));
-
- $manager->update($initial, $target, 'vendor/pkg');
+ $manager = new DownloadManager($this->io, false, $this->filesystem);
+ $method = new \ReflectionMethod($manager, 'getAvailableSources');
+ $method->setAccessible(true);
+ $this->assertEquals($expected, $method->invoke($manager, $target->reveal(), $initial ? $initial->reveal() : null));
}
- public function testUpdateSourceWithNotEqualTypes()
+ public static function updatesProvider()
{
- $initial = $this->createPackageMock();
- $initial
- ->expects($this->once())
- ->method('getInstallationSource')
- ->will($this->returnValue('source'));
- $initial
- ->expects($this->once())
- ->method('getSourceType')
- ->will($this->returnValue('svn'));
-
- $target = $this->createPackageMock();
- $target
- ->expects($this->once())
- ->method('getSourceType')
- ->will($this->returnValue('git'));
-
- $svnDownloader = $this->createDownloaderMock();
- $svnDownloader
- ->expects($this->once())
- ->method('remove')
- ->with($initial, 'vendor/pkg');
-
- $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
- ->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
- ->getMock();
- $manager
- ->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
- ->with($initial)
- ->will($this->returnValue($svnDownloader));
- $manager
- ->expects($this->once())
- ->method('download')
- ->with($target, 'vendor/pkg', true);
-
- $manager->update($initial, $target, 'vendor/pkg');
+ return array(
+ // prevPkg source, prevPkg isDev, pkg available, pkg isDev, expected
+ // updates keep previous source as preference
+ array('source', false, array('source', 'dist'), false, array('source', 'dist')),
+ array('dist', false, array('source', 'dist'), false, array('dist', 'source')),
+ // updates do not keep previous source if target package does not have it
+ array('source', false, array('dist'), false, array('dist')),
+ array('dist', false, array('source'), false, array('source')),
+ // updates do not keep previous source if target is dev and prev wasn't dev and installed from dist
+ array('source', false, array('source', 'dist'), true, array('source', 'dist')),
+ array('dist', false, array('source', 'dist'), true, array('source', 'dist')),
+ // install picks the right default
+ array(null, null, array('source', 'dist'), true, array('source', 'dist')),
+ array(null, null, array('dist'), true, array('dist')),
+ array(null, null, array('source'), true, array('source')),
+ array(null, null, array('source', 'dist'), false, array('dist', 'source')),
+ array(null, null, array('dist'), false, array('dist')),
+ array(null, null, array('source'), false, array('source')),
+ );
}
public function testUpdateMetapackage()
@@ -707,11 +680,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
- ->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->expects($this->exactly(2))
+ ->method('getDownloaderForPackage')
->with($initial)
->will($this->returnValue(null)); // There is no downloader for metapackages.
@@ -730,11 +703,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($pearDownloader));
@@ -747,11 +720,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue(null)); // There is no downloader for metapackages.
@@ -790,11 +763,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -833,11 +806,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -879,11 +852,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
$manager->setPreferences(array('foo/*' => 'source'));
@@ -926,11 +899,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
$manager->setPreferences(array('foo/*' => 'source'));
@@ -973,11 +946,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
$manager->setPreferences(array('foo/*' => 'auto'));
@@ -1020,11 +993,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
$manager->setPreferences(array('foo/*' => 'auto'));
@@ -1063,11 +1036,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
$manager->setPreferences(array('foo/*' => 'source'));
@@ -1106,11 +1079,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
$manager->setPreferences(array('foo/*' => 'dist'));
diff --git a/tests/Composer/Test/Downloader/FileDownloaderTest.php b/tests/Composer/Test/Downloader/FileDownloaderTest.php
index 8a78c241b..af5a9da11 100644
--- a/tests/Composer/Test/Downloader/FileDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/FileDownloaderTest.php
@@ -15,16 +15,26 @@ namespace Composer\Test\Downloader;
use Composer\Downloader\FileDownloader;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
+use Composer\Util\Http\Response;
+use Composer\Util\Loop;
class FileDownloaderTest extends TestCase
{
- protected function getDownloader($io = null, $config = null, $eventDispatcher = null, $cache = null, $rfs = null, $filesystem = null)
+ private $httpDownloader;
+ private $config;
+
+ protected function getDownloader($io = null, $config = null, $eventDispatcher = null, $cache = null, $httpDownloader = null, $filesystem = null)
{
$io = $io ?: $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
- $config = $config ?: $this->getMockBuilder('Composer\Config')->getMock();
- $rfs = $rfs ?: $this->getMockBuilder('Composer\Util\RemoteFilesystem')->disableOriginalConstructor()->getMock();
+ $this->config = $config ?: $this->getMockBuilder('Composer\Config')->getMock();
+ $httpDownloader = $httpDownloader ?: $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock();
+ $httpDownloader
+ ->expects($this->any())
+ ->method('addCopy')
+ ->will($this->returnValue(\React\Promise\resolve(new Response(array('url' => 'http://example.org/'), 200, array(), 'file~'))));
+ $this->httpDownloader = $httpDownloader;
- return new FileDownloader($io, $config, $eventDispatcher, $cache, $rfs, $filesystem);
+ return new FileDownloader($io, $this->config, $httpDownloader, $eventDispatcher, $cache, $filesystem);
}
/**
@@ -45,11 +55,11 @@ class FileDownloaderTest extends TestCase
public function testDownloadToExistingFile()
{
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
- $packageMock->expects($this->once())
+ $packageMock->expects($this->any())
->method('getDistUrl')
->will($this->returnValue('url'))
;
- $packageMock->expects($this->once())
+ $packageMock->expects($this->any())
->method('getDistUrls')
->will($this->returnValue(array('url')))
;
@@ -84,7 +94,12 @@ class FileDownloaderTest extends TestCase
$method = new \ReflectionMethod($downloader, 'getFileName');
$method->setAccessible(true);
- $this->assertEquals('/path/script.js', $method->invoke($downloader, $packageMock, '/path'));
+ $this->config->expects($this->once())
+ ->method('get')
+ ->with('vendor-dir')
+ ->will($this->returnValue('/vendor'));
+
+ $this->assertRegExp('#/vendor/composer/[a-z0-9]+\.js#', $method->invoke($downloader, $packageMock, '/path'));
}
public function testDownloadButFileIsUnsaved()
@@ -117,9 +132,18 @@ class FileDownloaderTest extends TestCase
;
$downloader = $this->getDownloader($ioMock);
+
+ $this->config->expects($this->once())
+ ->method('get')
+ ->with('vendor-dir')
+ ->will($this->returnValue($path.'/vendor'));
+
try {
- $downloader->download($packageMock, $path);
- $this->fail();
+ $promise = $downloader->download($packageMock, $path);
+ $loop = new Loop($this->httpDownloader);
+ $loop->wait(array($promise));
+
+ $this->fail('Download was expected to throw');
} catch (\Exception $e) {
if (is_dir($path)) {
$fs = new Filesystem();
@@ -128,7 +152,7 @@ class FileDownloaderTest extends TestCase
unlink($path);
}
- $this->assertInstanceOf('UnexpectedValueException', $e);
+ $this->assertInstanceOf('UnexpectedValueException', $e, $e->getMessage());
$this->assertContains('could not be saved to', $e->getMessage());
}
}
@@ -187,12 +211,25 @@ class FileDownloaderTest extends TestCase
$path = $this->getUniqueTmpDirectory();
$downloader = $this->getDownloader(null, null, null, null, null, $filesystem);
+
// make sure the file expected to be downloaded is on disk already
- touch($path.'/script.js');
+ $this->config->expects($this->any())
+ ->method('get')
+ ->with('vendor-dir')
+ ->will($this->returnValue($path.'/vendor'));
+
+ $method = new \ReflectionMethod($downloader, 'getFileName');
+ $method->setAccessible(true);
+ $dlFile = $method->invoke($downloader, $packageMock, $path);
+ mkdir(dirname($dlFile), 0777, true);
+ touch($dlFile);
try {
- $downloader->download($packageMock, $path);
- $this->fail();
+ $promise = $downloader->download($packageMock, $path);
+ $loop = new Loop($this->httpDownloader);
+ $loop->wait(array($promise));
+
+ $this->fail('Download was expected to throw');
} catch (\Exception $e) {
if (is_dir($path)) {
$fs = new Filesystem();
@@ -201,7 +238,7 @@ class FileDownloaderTest extends TestCase
unlink($path);
}
- $this->assertInstanceOf('UnexpectedValueException', $e);
+ $this->assertInstanceOf('UnexpectedValueException', $e, $e->getMessage());
$this->assertContains('checksum verification', $e->getMessage());
}
}
@@ -217,7 +254,7 @@ class FileDownloaderTest extends TestCase
->will($this->returnValue('1.2.0.0'));
$newPackage = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
- $newPackage->expects($this->once())
+ $newPackage->expects($this->any())
->method('getFullPrettyVersion')
->will($this->returnValue('1.0.0'));
$newPackage->expects($this->once())
@@ -232,17 +269,37 @@ class FileDownloaderTest extends TestCase
$ioMock = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
$ioMock->expects($this->at(0))
+ ->method('writeError')
+ ->with($this->stringContains('Downloading'));
+
+ $ioMock->expects($this->at(1))
->method('writeError')
->with($this->stringContains('Downgrading'));
$path = $this->getUniqueTmpDirectory();
- touch($path.'/script.js');
$filesystem = $this->getMockBuilder('Composer\Util\Filesystem')->getMock();
$filesystem->expects($this->once())
->method('removeDirectory')
->will($this->returnValue(true));
$downloader = $this->getDownloader($ioMock, null, null, null, null, $filesystem);
+
+ // make sure the file expected to be downloaded is on disk already
+ $this->config->expects($this->any())
+ ->method('get')
+ ->with('vendor-dir')
+ ->will($this->returnValue($path.'/vendor'));
+
+ $method = new \ReflectionMethod($downloader, 'getFileName');
+ $method->setAccessible(true);
+ $dlFile = $method->invoke($downloader, $newPackage, $path);
+ mkdir(dirname($dlFile), 0777, true);
+ touch($dlFile);
+
+ $promise = $downloader->download($newPackage, $path, $oldPackage);
+ $loop = new Loop($this->httpDownloader);
+ $loop->wait(array($promise));
+
$downloader->update($oldPackage, $newPackage, $path);
}
}
diff --git a/tests/Composer/Test/Downloader/FossilDownloaderTest.php b/tests/Composer/Test/Downloader/FossilDownloaderTest.php
index 97706435d..40ab561db 100644
--- a/tests/Composer/Test/Downloader/FossilDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/FossilDownloaderTest.php
@@ -48,7 +48,7 @@ class FossilDownloaderTest extends TestCase
/**
* @expectedException \InvalidArgumentException
*/
- public function testDownloadForPackageWithoutSourceReference()
+ public function testInstallForPackageWithoutSourceReference()
{
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
$packageMock->expects($this->once())
@@ -56,10 +56,10 @@ class FossilDownloaderTest extends TestCase
->will($this->returnValue(null));
$downloader = $this->getDownloaderMock();
- $downloader->download($packageMock, '/path');
+ $downloader->install($packageMock, '/path');
}
- public function testDownload()
+ public function testInstall()
{
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
$packageMock->expects($this->any())
@@ -89,7 +89,7 @@ class FossilDownloaderTest extends TestCase
->will($this->returnValue(0));
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
- $downloader->download($packageMock, 'repo');
+ $downloader->install($packageMock, 'repo');
}
/**
@@ -104,7 +104,9 @@ class FossilDownloaderTest extends TestCase
->will($this->returnValue(null));
$downloader = $this->getDownloaderMock();
+ $downloader->prepare('update', $sourcePackageMock, '/path', $initialPackageMock);
$downloader->update($initialPackageMock, $sourcePackageMock, '/path');
+ $downloader->cleanup('update', $sourcePackageMock, '/path', $initialPackageMock);
}
public function testUpdate()
@@ -140,7 +142,9 @@ class FossilDownloaderTest extends TestCase
->will($this->returnValue(0));
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
+ $downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
$downloader->update($packageMock, $packageMock, $this->workingDir);
+ $downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
}
public function testRemove()
diff --git a/tests/Composer/Test/Downloader/GitDownloaderTest.php b/tests/Composer/Test/Downloader/GitDownloaderTest.php
index 393ecfc5f..4c1e37806 100644
--- a/tests/Composer/Test/Downloader/GitDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/GitDownloaderTest.php
@@ -17,6 +17,7 @@ use Composer\Config;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
use Composer\Util\Platform;
+use Prophecy\Argument;
class GitDownloaderTest extends TestCase
{
@@ -80,6 +81,9 @@ class GitDownloaderTest extends TestCase
$downloader = $this->getDownloaderMock();
$downloader->download($packageMock, '/path');
+ $downloader->prepare('install', $packageMock, '/path');
+ $downloader->install($packageMock, '/path');
+ $downloader->cleanup('install', $packageMock, '/path');
}
public function testDownload()
@@ -131,6 +135,9 @@ class GitDownloaderTest extends TestCase
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
$downloader->download($packageMock, 'composerPath');
+ $downloader->prepare('install', $packageMock, 'composerPath');
+ $downloader->install($packageMock, 'composerPath');
+ $downloader->cleanup('install', $packageMock, 'composerPath');
}
public function testDownloadWithCache()
@@ -211,6 +218,9 @@ class GitDownloaderTest extends TestCase
$downloader = $this->getDownloaderMock(null, $config, $processExecutor);
$downloader->download($packageMock, 'composerPath');
+ $downloader->prepare('install', $packageMock, 'composerPath');
+ $downloader->install($packageMock, 'composerPath');
+ $downloader->cleanup('install', $packageMock, 'composerPath');
@rmdir($cachePath);
}
@@ -281,6 +291,9 @@ class GitDownloaderTest extends TestCase
$downloader = $this->getDownloaderMock(null, new Config(), $processExecutor);
$downloader->download($packageMock, 'composerPath');
+ $downloader->prepare('install', $packageMock, 'composerPath');
+ $downloader->install($packageMock, 'composerPath');
+ $downloader->cleanup('install', $packageMock, 'composerPath');
}
public function pushUrlProvider()
@@ -345,11 +358,11 @@ class GitDownloaderTest extends TestCase
$downloader = $this->getDownloaderMock(null, $config, $processExecutor);
$downloader->download($packageMock, 'composerPath');
+ $downloader->prepare('install', $packageMock, 'composerPath');
+ $downloader->install($packageMock, 'composerPath');
+ $downloader->cleanup('install', $packageMock, 'composerPath');
}
- /**
- * @expectedException \RuntimeException
- */
public function testDownloadThrowsRuntimeExceptionIfGitCommandFails()
{
$expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://example.com/composer/composer' && git fetch composer && git remote set-url origin 'https://example.com/composer/composer' && git remote set-url composer 'https://example.com/composer/composer'");
@@ -374,8 +387,20 @@ class GitDownloaderTest extends TestCase
->with($this->equalTo($expectedGitCommand))
->will($this->returnValue(1));
- $downloader = $this->getDownloaderMock(null, null, $processExecutor);
- $downloader->download($packageMock, 'composerPath');
+ // not using PHPUnit's expected exception because Prophecy exceptions extend from RuntimeException too so it is not safe
+ try {
+ $downloader = $this->getDownloaderMock(null, null, $processExecutor);
+ $downloader->download($packageMock, 'composerPath');
+ $downloader->prepare('install', $packageMock, 'composerPath');
+ $downloader->install($packageMock, 'composerPath');
+ $downloader->cleanup('install', $packageMock, 'composerPath');
+ $this->fail('This test should throw');
+ } catch (\RuntimeException $e) {
+ if ('RuntimeException' !== get_class($e)) {
+ throw $e;
+ }
+ $this->assertEquals('RuntimeException', get_class($e));
+ }
}
/**
@@ -390,12 +415,15 @@ class GitDownloaderTest extends TestCase
->will($this->returnValue(null));
$downloader = $this->getDownloaderMock();
+ $downloader->download($sourcePackageMock, '/path', $initialPackageMock);
+ $downloader->prepare('update', $sourcePackageMock, '/path', $initialPackageMock);
$downloader->update($initialPackageMock, $sourcePackageMock, '/path');
+ $downloader->cleanup('update', $sourcePackageMock, '/path', $initialPackageMock);
}
public function testUpdate()
{
- $expectedGitUpdateCommand = $this->winCompat("(git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer 'https://github.com/composer/composer'");
+ $expectedGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'https://github.com/composer/composer'");
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
$packageMock->expects($this->any())
@@ -407,44 +435,27 @@ class GitDownloaderTest extends TestCase
$packageMock->expects($this->any())
->method('getVersion')
->will($this->returnValue('1.0.0.0'));
- $processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
- $processExecutor->expects($this->at(0))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git show-ref --head -d")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(1))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(2))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git remote -v")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(3))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git remote -v")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(4))
- ->method('execute')
- ->with($this->equalTo($this->winCompat($expectedGitUpdateCommand)), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(5))
- ->method('execute')
- ->with($this->equalTo('git branch -r'))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(6))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --")), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
- ->will($this->returnValue(0));
+
+ $process = $this->prophesize('Composer\Util\ProcessExecutor');
+ $process->execute($this->winCompat('git --version'), Argument::cetera())->willReturn(0);
+ $process->execute($this->winCompat('git show-ref --head -d'), Argument::cetera())->willReturn(0);
+ $process->execute($this->winCompat('git status --porcelain --untracked-files=no'), Argument::cetera())->willReturn(0);
+ $process->execute($this->winCompat('git remote -v'), Argument::cetera())->willReturn(0);
+ $process->execute($this->winCompat('git branch -r'), Argument::cetera())->willReturn(0);
+ $process->execute($expectedGitUpdateCommand, null, $this->winCompat($this->workingDir))->willReturn(0)->shouldBeCalled();
+ $process->execute($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --"), null, $this->winCompat($this->workingDir))->willReturn(0)->shouldBeCalled();
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
- $downloader = $this->getDownloaderMock(null, new Config(), $processExecutor);
+ $downloader = $this->getDownloaderMock(null, new Config(), $process->reveal());
+ $downloader->download($packageMock, $this->workingDir, $packageMock);
+ $downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
$downloader->update($packageMock, $packageMock, $this->workingDir);
+ $downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
}
public function testUpdateWithNewRepoUrl()
{
- $expectedGitUpdateCommand = $this->winCompat("(git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer 'https://github.com/composer/composer'");
+ $expectedGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'https://github.com/composer/composer'");
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
$packageMock->expects($this->any())
@@ -459,27 +470,20 @@ class GitDownloaderTest extends TestCase
$packageMock->expects($this->any())
->method('getVersion')
->will($this->returnValue('1.0.0.0'));
+
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
$processExecutor->expects($this->at(0))
->method('execute')
- ->with($this->equalTo($this->winCompat("git show-ref --head -d")))
+ ->with($this->equalTo($this->winCompat("git --version")))
->will($this->returnValue(0));
$processExecutor->expects($this->at(1))
->method('execute')
- ->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
+ ->with($this->equalTo($this->winCompat("git show-ref --head -d")))
->will($this->returnValue(0));
$processExecutor->expects($this->at(2))
->method('execute')
- ->with($this->equalTo($this->winCompat("git remote -v")))
- ->will($this->returnCallback(function ($cmd, &$output, $cwd) {
- $output = 'origin https://github.com/old/url (fetch)
-origin https://github.com/old/url (push)
-composer https://github.com/old/url (fetch)
-composer https://github.com/old/url (push)
-';
-
- return 0;
- }));
+ ->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
+ ->will($this->returnValue(0));
$processExecutor->expects($this->at(3))
->method('execute')
->with($this->equalTo($this->winCompat("git remote -v")))
@@ -497,26 +501,41 @@ composer https://github.com/old/url (push)
->with($this->equalTo($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --")), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
->will($this->returnValue(0));
$processExecutor->expects($this->at(7))
+ ->method('execute')
+ ->with($this->equalTo($this->winCompat("git remote -v")))
+ ->will($this->returnCallback(function ($cmd, &$output, $cwd) {
+ $output = 'origin https://github.com/old/url (fetch)
+origin https://github.com/old/url (push)
+composer https://github.com/old/url (fetch)
+composer https://github.com/old/url (push)
+';
+
+ return 0;
+ }));
+ $processExecutor->expects($this->at(8))
->method('execute')
->with($this->equalTo($this->winCompat("git remote set-url origin 'https://github.com/composer/composer'")), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
->will($this->returnValue(0));
- $processExecutor->expects($this->at(8))
+ $processExecutor->expects($this->at(9))
->method('execute')
->with($this->equalTo($this->winCompat("git remote set-url --push origin 'git@github.com:composer/composer.git'")), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
->will($this->returnValue(0));
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
$downloader = $this->getDownloaderMock(null, new Config(), $processExecutor);
+ $downloader->download($packageMock, $this->workingDir, $packageMock);
+ $downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
$downloader->update($packageMock, $packageMock, $this->workingDir);
+ $downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
}
/**
* @group failing
- * @expectedException \RuntimeException
*/
public function testUpdateThrowsRuntimeExceptionIfGitCommandFails()
{
- $expectedGitUpdateCommand = $this->winCompat("(git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer 'https://github.com/composer/composer'");
+ $expectedGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'https://github.com/composer/composer'");
+ $expectedGitUpdateCommand2 = $this->winCompat("git remote set-url composer 'git@github.com:composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'git@github.com:composer/composer'");
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
$packageMock->expects($this->any())
@@ -528,37 +547,39 @@ composer https://github.com/old/url (push)
$packageMock->expects($this->any())
->method('getVersion')
->will($this->returnValue('1.0.0.0'));
- $processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
- $processExecutor->expects($this->at(0))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git show-ref --head -d")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(1))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(2))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git remote -v")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(3))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git remote -v")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(4))
- ->method('execute')
- ->with($this->equalTo($expectedGitUpdateCommand))
- ->will($this->returnValue(1));
+
+ $process = $this->prophesize('Composer\Util\ProcessExecutor');
+ $process->execute($this->winCompat('git --version'), Argument::cetera())->willReturn(0);
+ $process->execute($this->winCompat('git show-ref --head -d'), Argument::cetera())->willReturn(0);
+ $process->execute($this->winCompat('git status --porcelain --untracked-files=no'), Argument::cetera())->willReturn(0);
+ $process->execute($this->winCompat('git remote -v'), Argument::cetera())->willReturn(0);
+ $process->execute($this->winCompat('git branch -r'), Argument::cetera())->willReturn(0);
+ $process->execute($expectedGitUpdateCommand, null, $this->winCompat($this->workingDir))->willReturn(1)->shouldBeCalled();
+ $process->execute($expectedGitUpdateCommand2, null, $this->winCompat($this->workingDir))->willReturn(1)->shouldBeCalled();
+ $process->getErrorOutput()->willReturn('');
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
- $downloader = $this->getDownloaderMock(null, new Config(), $processExecutor);
- $downloader->update($packageMock, $packageMock, $this->workingDir);
+
+ // not using PHPUnit's expected exception because Prophecy exceptions extend from RuntimeException too so it is not safe
+ try {
+ $downloader = $this->getDownloaderMock(null, new Config(), $process->reveal());
+ $downloader->download($packageMock, $this->workingDir, $packageMock);
+ $downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
+ $downloader->update($packageMock, $packageMock, $this->workingDir);
+ $downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
+ $this->fail('This test should throw');
+ } catch (\RuntimeException $e) {
+ if ('RuntimeException' !== get_class($e)) {
+ throw $e;
+ }
+ $this->assertEquals('RuntimeException', get_class($e));
+ }
}
public function testUpdateDoesntThrowsRuntimeExceptionIfGitCommandFailsAtFirstButIsAbleToRecover()
{
- $expectedFirstGitUpdateCommand = $this->winCompat("(git remote set-url composer '' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer ''");
- $expectedSecondGitUpdateCommand = $this->winCompat("(git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer 'https://github.com/composer/composer'");
+ $expectedFirstGitUpdateCommand = $this->winCompat("git remote set-url composer '".(Platform::isWindows() ? 'C:\\\\' : '/')."' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer '".(Platform::isWindows() ? 'C:\\\\' : '/')."'");
+ $expectedSecondGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer); git remote set-url composer 'https://github.com/composer/composer'");
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
$packageMock->expects($this->any())
@@ -569,52 +590,25 @@ composer https://github.com/old/url (push)
->will($this->returnValue('1.0.0.0'));
$packageMock->expects($this->any())
->method('getSourceUrls')
- ->will($this->returnValue(array('/foo/bar', 'https://github.com/composer/composer')));
- $processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
- $processExecutor->expects($this->at(0))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git show-ref --head -d")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(1))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(2))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git remote -v")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(3))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git remote -v")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(4))
- ->method('execute')
- ->with($this->equalTo($expectedFirstGitUpdateCommand))
- ->will($this->returnValue(1));
- $processExecutor->expects($this->at(6))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git --version")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(7))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git remote -v")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(8))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git remote -v")))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(9))
- ->method('execute')
- ->with($this->equalTo($expectedSecondGitUpdateCommand))
- ->will($this->returnValue(0));
- $processExecutor->expects($this->at(11))
- ->method('execute')
- ->with($this->equalTo($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --")), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
- ->will($this->returnValue(0));
+ ->will($this->returnValue(array(Platform::isWindows() ? 'C:\\' : '/', 'https://github.com/composer/composer')));
+
+ $process = $this->prophesize('Composer\Util\ProcessExecutor');
+ $process->execute($this->winCompat('git --version'), Argument::cetera())->willReturn(0);
+ $process->execute($this->winCompat('git show-ref --head -d'), Argument::cetera())->willReturn(0);
+ $process->execute($this->winCompat('git status --porcelain --untracked-files=no'), Argument::cetera())->willReturn(0);
+ $process->execute($this->winCompat('git remote -v'), Argument::cetera())->willReturn(0);
+ $process->execute($this->winCompat('git branch -r'), Argument::cetera())->willReturn(0);
+ $process->execute($expectedFirstGitUpdateCommand, Argument::cetera())->willReturn(1)->shouldBeCalled();
+ $process->execute($expectedSecondGitUpdateCommand, Argument::cetera())->willReturn(0)->shouldBeCalled();
+ $process->execute($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --"), null, $this->winCompat($this->workingDir))->willReturn(0)->shouldBeCalled();
+ $process->getErrorOutput()->willReturn('');
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
- $downloader = $this->getDownloaderMock(null, new Config(), $processExecutor);
+ $downloader = $this->getDownloaderMock(null, new Config(), $process->reveal());
+ $downloader->download($packageMock, $this->workingDir, $packageMock);
+ $downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
$downloader->update($packageMock, $packageMock, $this->workingDir);
+ $downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
}
public function testDowngradeShowsAppropriateMessage()
@@ -659,7 +653,10 @@ composer https://github.com/old/url (push)
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
$downloader = $this->getDownloaderMock($ioMock, null, $processExecutor);
+ $downloader->download($newPackage, $this->workingDir, $oldPackage);
+ $downloader->prepare('update', $newPackage, $this->workingDir, $oldPackage);
$downloader->update($oldPackage, $newPackage, $this->workingDir);
+ $downloader->cleanup('update', $newPackage, $this->workingDir, $oldPackage);
}
public function testNotUsingDowngradingWithReferences()
@@ -694,11 +691,14 @@ composer https://github.com/old/url (push)
$ioMock = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
$ioMock->expects($this->at(0))
->method('writeError')
- ->with($this->stringContains('updating'));
+ ->with($this->stringContains('Upgrading'));
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
$downloader = $this->getDownloaderMock($ioMock, null, $processExecutor);
+ $downloader->download($newPackage, $this->workingDir, $oldPackage);
+ $downloader->prepare('update', $newPackage, $this->workingDir, $oldPackage);
$downloader->update($oldPackage, $newPackage, $this->workingDir);
+ $downloader->cleanup('update', $newPackage, $this->workingDir, $oldPackage);
}
public function testRemove()
@@ -718,7 +718,9 @@ composer https://github.com/old/url (push)
->will($this->returnValue(true));
$downloader = $this->getDownloaderMock(null, null, $processExecutor, $filesystem);
+ $downloader->prepare('uninstall', $packageMock, 'composerPath');
$downloader->remove($packageMock, 'composerPath');
+ $downloader->cleanup('uninstall', $packageMock, 'composerPath');
}
public function testGetInstallationSource()
diff --git a/tests/Composer/Test/Downloader/HgDownloaderTest.php b/tests/Composer/Test/Downloader/HgDownloaderTest.php
index c074e18dd..0b00bd7d2 100644
--- a/tests/Composer/Test/Downloader/HgDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/HgDownloaderTest.php
@@ -56,7 +56,7 @@ class HgDownloaderTest extends TestCase
->will($this->returnValue(null));
$downloader = $this->getDownloaderMock();
- $downloader->download($packageMock, '/path');
+ $downloader->install($packageMock, '/path');
}
public function testDownload()
@@ -83,7 +83,7 @@ class HgDownloaderTest extends TestCase
->will($this->returnValue(0));
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
- $downloader->download($packageMock, 'composerPath');
+ $downloader->install($packageMock, 'composerPath');
}
/**
@@ -98,7 +98,9 @@ class HgDownloaderTest extends TestCase
->will($this->returnValue(null));
$downloader = $this->getDownloaderMock();
+ $downloader->prepare('update', $sourcePackageMock, '/path', $initialPackageMock);
$downloader->update($initialPackageMock, $sourcePackageMock, '/path');
+ $downloader->cleanup('update', $sourcePackageMock, '/path', $initialPackageMock);
}
public function testUpdate()
@@ -129,7 +131,9 @@ class HgDownloaderTest extends TestCase
->will($this->returnValue(0));
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
+ $downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
$downloader->update($packageMock, $packageMock, $this->workingDir);
+ $downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
}
public function testRemove()
@@ -148,7 +152,9 @@ class HgDownloaderTest extends TestCase
->will($this->returnValue(true));
$downloader = $this->getDownloaderMock(null, null, $processExecutor, $filesystem);
+ $downloader->prepare('uninstall', $packageMock, 'composerPath');
$downloader->remove($packageMock, 'composerPath');
+ $downloader->cleanup('uninstall', $packageMock, 'composerPath');
}
public function testGetInstallationSource()
diff --git a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php
index 3b5f4165c..ca9386332 100644
--- a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php
@@ -17,6 +17,7 @@ use Composer\Config;
use Composer\Repository\VcsRepository;
use Composer\IO\IOInterface;
use Composer\Test\TestCase;
+use Composer\Factory;
use Composer\Util\Filesystem;
/**
@@ -96,7 +97,7 @@ class PerforceDownloaderTest extends TestCase
{
$repository = $this->getMockBuilder('Composer\Repository\VcsRepository')
->setMethods(array('getRepoConfig'))
- ->setConstructorArgs(array($repoConfig, $io, $config))
+ ->setConstructorArgs(array($repoConfig, $io, $config, Factory::createHttpDownloader($io, $config)))
->getMock();
$repository->expects($this->any())->method('getRepoConfig')->will($this->returnValue($repoConfig));
@@ -123,7 +124,7 @@ class PerforceDownloaderTest extends TestCase
* @depends testInitPerforceInstantiatesANewPerforceObject
* @depends testInitPerforceDoesNothingIfPerforceAlreadySet
*/
- public function testDoDownloadWithTag()
+ public function testDoInstallWithTag()
{
//I really don't like this test but the logic of each Perforce method is tested in the Perforce class. Really I am just enforcing workflow.
$ref = 'SOURCE_REF@123';
@@ -131,7 +132,7 @@ class PerforceDownloaderTest extends TestCase
$this->package->expects($this->once())->method('getSourceReference')->will($this->returnValue($ref));
$this->io->expects($this->once())->method('writeError')->with($this->stringContains('Cloning '.$ref));
$perforceMethods = array('setStream', 'p4Login', 'writeP4ClientSpec', 'connectClient', 'syncCodeBase', 'cleanupClientSpec');
- $perforce = $this->getMockBuilder('Composer\Util\Perforce', $perforceMethods)->disableOriginalConstructor()->getMock();
+ $perforce = $this->getMockBuilder('Composer\Util\Perforce')->disableOriginalConstructor()->getMock();
$perforce->expects($this->at(0))->method('initializePath')->with($this->equalTo($this->testPath));
$perforce->expects($this->at(1))->method('setStream')->with($this->equalTo($ref));
$perforce->expects($this->at(2))->method('p4Login');
@@ -140,21 +141,21 @@ class PerforceDownloaderTest extends TestCase
$perforce->expects($this->at(5))->method('syncCodeBase')->with($label);
$perforce->expects($this->at(6))->method('cleanupClientSpec');
$this->downloader->setPerforce($perforce);
- $this->downloader->doDownload($this->package, $this->testPath, 'url');
+ $this->downloader->doInstall($this->package, $this->testPath, 'url');
}
/**
* @depends testInitPerforceInstantiatesANewPerforceObject
* @depends testInitPerforceDoesNothingIfPerforceAlreadySet
*/
- public function testDoDownloadWithNoTag()
+ public function testDoInstallWithNoTag()
{
$ref = 'SOURCE_REF';
$label = null;
$this->package->expects($this->once())->method('getSourceReference')->will($this->returnValue($ref));
$this->io->expects($this->once())->method('writeError')->with($this->stringContains('Cloning '.$ref));
$perforceMethods = array('setStream', 'p4Login', 'writeP4ClientSpec', 'connectClient', 'syncCodeBase', 'cleanupClientSpec');
- $perforce = $this->getMockBuilder('Composer\Util\Perforce', $perforceMethods)->disableOriginalConstructor()->getMock();
+ $perforce = $this->getMockBuilder('Composer\Util\Perforce')->disableOriginalConstructor()->getMock();
$perforce->expects($this->at(0))->method('initializePath')->with($this->equalTo($this->testPath));
$perforce->expects($this->at(1))->method('setStream')->with($this->equalTo($ref));
$perforce->expects($this->at(2))->method('p4Login');
@@ -163,6 +164,6 @@ class PerforceDownloaderTest extends TestCase
$perforce->expects($this->at(5))->method('syncCodeBase')->with($label);
$perforce->expects($this->at(6))->method('cleanupClientSpec');
$this->downloader->setPerforce($perforce);
- $this->downloader->doDownload($this->package, $this->testPath, 'url');
+ $this->downloader->doInstall($this->package, $this->testPath, 'url');
}
}
diff --git a/tests/Composer/Test/Downloader/XzDownloaderTest.php b/tests/Composer/Test/Downloader/XzDownloaderTest.php
index 6df782ddb..f770b0d35 100644
--- a/tests/Composer/Test/Downloader/XzDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/XzDownloaderTest.php
@@ -16,7 +16,8 @@ use Composer\Downloader\XzDownloader;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
use Composer\Util\Platform;
-use Composer\Util\RemoteFilesystem;
+use Composer\Util\Loop;
+use Composer\Util\HttpDownloader;
class XzDownloaderTest extends TestCase
{
@@ -66,10 +67,14 @@ class XzDownloaderTest extends TestCase
->method('get')
->with('vendor-dir')
->will($this->returnValue($this->testDir));
- $downloader = new XzDownloader($io, $config, null, null, null, new RemoteFilesystem($io));
+ $downloader = new XzDownloader($io, $config, $httpDownloader = new HttpDownloader($io, $this->getMockBuilder('Composer\Config')->getMock()), null, null, null);
try {
- $downloader->download($packageMock, $this->getUniqueTmpDirectory());
+ $promise = $downloader->download($packageMock, $this->testDir.'/install-path');
+ $loop = new Loop($httpDownloader);
+ $loop->wait(array($promise));
+ $downloader->install($packageMock, $this->testDir.'/install-path');
+
$this->fail('Download of invalid tarball should throw an exception');
} catch (\RuntimeException $e) {
$this->assertRegexp('/(File format not recognized|Unrecognized archive format)/i', $e->getMessage());
diff --git a/tests/Composer/Test/Downloader/ZipDownloaderTest.php b/tests/Composer/Test/Downloader/ZipDownloaderTest.php
index 4ff3f055c..e3bbe45a8 100644
--- a/tests/Composer/Test/Downloader/ZipDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/ZipDownloaderTest.php
@@ -16,6 +16,8 @@ use Composer\Downloader\ZipDownloader;
use Composer\Package\PackageInterface;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
+use Composer\Util\HttpDownloader;
+use Composer\Util\Loop;
class ZipDownloaderTest extends TestCase
{
@@ -23,14 +25,19 @@ class ZipDownloaderTest extends TestCase
* @var string
*/
private $testDir;
+ private $httpDownloader;
private $io;
private $config;
+ private $package;
public function setUp()
{
$this->testDir = $this->getUniqueTmpDirectory();
$this->io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
$this->config = $this->getMockBuilder('Composer\Config')->getMock();
+ $dlConfig = $this->getMockBuilder('Composer\Config')->getMock();
+ $this->httpDownloader = new HttpDownloader($this->io, $dlConfig);
+ $this->package = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
}
public function tearDown()
@@ -62,43 +69,34 @@ class ZipDownloaderTest extends TestCase
$this->markTestSkipped('zip extension missing');
}
- $this->config->expects($this->at(0))
- ->method('get')
- ->with('disable-tls')
- ->will($this->returnValue(false));
- $this->config->expects($this->at(1))
- ->method('get')
- ->with('cafile')
- ->will($this->returnValue(null));
- $this->config->expects($this->at(2))
- ->method('get')
- ->with('capath')
- ->will($this->returnValue(null));
- $this->config->expects($this->at(3))
+ $this->config->expects($this->any())
->method('get')
->with('vendor-dir')
->will($this->returnValue($this->testDir));
- $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
- $packageMock->expects($this->any())
+ $this->package->expects($this->any())
->method('getDistUrl')
->will($this->returnValue($distUrl = 'file://'.__FILE__))
;
- $packageMock->expects($this->any())
+ $this->package->expects($this->any())
->method('getDistUrls')
->will($this->returnValue(array($distUrl)))
;
- $packageMock->expects($this->atLeastOnce())
+ $this->package->expects($this->atLeastOnce())
->method('getTransportOptions')
->will($this->returnValue(array()))
;
- $downloader = new ZipDownloader($this->io, $this->config);
+ $downloader = new ZipDownloader($this->io, $this->config, $this->httpDownloader);
$this->setPrivateProperty('hasSystemUnzip', false);
try {
- $downloader->download($packageMock, sys_get_temp_dir().'/composer-zip-test');
+ $promise = $downloader->download($this->package, $path = sys_get_temp_dir().'/composer-zip-test');
+ $loop = new Loop($this->httpDownloader);
+ $loop->wait(array($promise));
+ $downloader->install($this->package, $path);
+
$this->fail('Download of invalid zip files should throw an exception');
} catch (\Exception $e) {
$this->assertContains('is not a zip archive', $e->getMessage());
@@ -117,8 +115,7 @@ class ZipDownloaderTest extends TestCase
$this->setPrivateProperty('hasSystemUnzip', false);
$this->setPrivateProperty('hasZipArchive', true);
- $downloader = new MockedZipDownloader($this->io, $this->config);
-
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader);
$zipArchive = $this->getMockBuilder('ZipArchive')->getMock();
$zipArchive->expects($this->at(0))
->method('open')
@@ -128,7 +125,7 @@ class ZipDownloaderTest extends TestCase
->will($this->returnValue(false));
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
/**
@@ -143,8 +140,7 @@ class ZipDownloaderTest extends TestCase
$this->setPrivateProperty('hasSystemUnzip', false);
$this->setPrivateProperty('hasZipArchive', true);
- $downloader = new MockedZipDownloader($this->io, $this->config);
-
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader);
$zipArchive = $this->getMockBuilder('ZipArchive')->getMock();
$zipArchive->expects($this->at(0))
->method('open')
@@ -154,7 +150,7 @@ class ZipDownloaderTest extends TestCase
->will($this->throwException(new \ErrorException('Not a directory')));
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
/**
@@ -168,8 +164,7 @@ class ZipDownloaderTest extends TestCase
$this->setPrivateProperty('hasSystemUnzip', false);
$this->setPrivateProperty('hasZipArchive', true);
- $downloader = new MockedZipDownloader($this->io, $this->config);
-
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader);
$zipArchive = $this->getMockBuilder('ZipArchive')->getMock();
$zipArchive->expects($this->at(0))
->method('open')
@@ -179,7 +174,7 @@ class ZipDownloaderTest extends TestCase
->will($this->returnValue(true));
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
/**
@@ -199,8 +194,8 @@ class ZipDownloaderTest extends TestCase
->method('execute')
->will($this->returnValue(1));
- $downloader = new MockedZipDownloader($this->io, $this->config, null, null, $processExecutor);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor);
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
public function testSystemUnzipOnlyGood()
@@ -216,8 +211,8 @@ class ZipDownloaderTest extends TestCase
->method('execute')
->will($this->returnValue(0));
- $downloader = new MockedZipDownloader($this->io, $this->config, null, null, $processExecutor);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor);
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
public function testNonWindowsFallbackGood()
@@ -243,9 +238,9 @@ class ZipDownloaderTest extends TestCase
->method('extractTo')
->will($this->returnValue(true));
- $downloader = new MockedZipDownloader($this->io, $this->config, null, null, $processExecutor);
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor);
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
/**
@@ -275,9 +270,9 @@ class ZipDownloaderTest extends TestCase
->method('extractTo')
->will($this->returnValue(false));
- $downloader = new MockedZipDownloader($this->io, $this->config, null, null, $processExecutor);
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor);
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
public function testWindowsFallbackGood()
@@ -303,9 +298,9 @@ class ZipDownloaderTest extends TestCase
->method('extractTo')
->will($this->returnValue(false));
- $downloader = new MockedZipDownloader($this->io, $this->config, null, null, $processExecutor);
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor);
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
/**
@@ -335,21 +330,26 @@ class ZipDownloaderTest extends TestCase
->method('extractTo')
->will($this->returnValue(false));
- $downloader = new MockedZipDownloader($this->io, $this->config, null, null, $processExecutor);
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor);
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
}
class MockedZipDownloader extends ZipDownloader
{
- public function download(PackageInterface $package, $path, $output = true)
+ public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null, $output = true)
{
return;
}
- public function extract($file, $path)
+ public function install(PackageInterface $package, $path, $output = true)
{
- parent::extract($file, $path);
+ return;
+ }
+
+ public function extract(PackageInterface $package, $file, $path)
+ {
+ parent::extract($package, $file, $path);
}
}
diff --git a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php
index f1a7bb1db..c038681da 100644
--- a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php
+++ b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php
@@ -21,7 +21,7 @@ use Composer\Composer;
use Composer\Test\TestCase;
use Composer\IO\BufferIO;
use Composer\Script\ScriptEvents;
-use Composer\Script\CommandEvent;
+use Composer\Script\Event as ScriptEvent;
use Composer\Util\ProcessExecutor;
use Symfony\Component\Console\Output\OutputInterface;
@@ -52,30 +52,6 @@ class EventDispatcherTest extends TestCase
$dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
}
- /**
- * @group legacy
- */
- public function testDispatcherCanConvertScriptEventToCommandEventForListener()
- {
- $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
- $dispatcher = $this->getDispatcherStubForListenersTest(array(
- 'Composer\Test\EventDispatcher\EventDispatcherTest::expectsCommandEvent',
- ), $io);
-
- $this->setExpectedException('PHPUnit\Framework\Error\Deprecated');
- $this->assertEquals(1, $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false));
- }
-
- public function testDispatcherDoesNotAttemptConversionForListenerWithoutTypehint()
- {
- $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
- $dispatcher = $this->getDispatcherStubForListenersTest(array(
- 'Composer\Test\EventDispatcher\EventDispatcherTest::expectsVariableEvent',
- ), $io);
-
- $this->assertEquals(1, $dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false));
- }
-
/**
* @dataProvider getValidCommands
* @param string $command
@@ -125,7 +101,7 @@ class EventDispatcherTest extends TestCase
$composer->setPackage($package);
$composer->setRepositoryManager($this->getRepositoryManagerMockForDevModePassingTest());
- $composer->setInstallationManager($this->getMockBuilder('Composer\Installer\InstallationManager')->getMock());
+ $composer->setInstallationManager($this->getMockBuilder('Composer\Installer\InstallationManager')->disableOriginalConstructor()->getMock());
$dispatcher = new EventDispatcher(
$composer,
@@ -195,6 +171,49 @@ class EventDispatcherTest extends TestCase
return $rm;
}
+ public function testDispatcherRemoveListener()
+ {
+ $composer = $this->createComposerInstance();
+
+ $composer->setRepositoryManager($this->getRepositoryManagerMockForDevModePassingTest());
+ $composer->setInstallationManager($this->getMockBuilder('Composer\Installer\InstallationManager')->disableOriginalConstructor()->getMock());
+
+ $dispatcher = new EventDispatcher(
+ $composer,
+ $io = new BufferIO('', OutputInterface::VERBOSITY_VERBOSE),
+ $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock()
+ );
+
+ $listener = array($this, 'someMethod');
+ $listener2 = array($this, 'someMethod2');
+ $listener3 = 'Composer\\Test\\EventDispatcher\\EventDispatcherTest::someMethod';
+
+ $dispatcher->addListener('ev1', $listener, 0);
+ $dispatcher->addListener('ev1', $listener, 1);
+ $dispatcher->addListener('ev1', $listener2, 1);
+ $dispatcher->addListener('ev1', $listener3);
+ $dispatcher->addListener('ev2', $listener3);
+ $dispatcher->addListener('ev2', $listener);
+ $dispatcher->dispatch('ev1');
+ $dispatcher->dispatch('ev2');
+
+ $expected = '> ev1: Composer\Test\EventDispatcher\EventDispatcherTest->someMethod'.PHP_EOL
+ .'> ev1: Composer\Test\EventDispatcher\EventDispatcherTest->someMethod2'.PHP_EOL
+ .'> ev1: Composer\Test\EventDispatcher\EventDispatcherTest->someMethod'.PHP_EOL
+ .'> ev1: Composer\Test\EventDispatcher\EventDispatcherTest::someMethod'.PHP_EOL
+ .'> ev2: Composer\Test\EventDispatcher\EventDispatcherTest::someMethod'.PHP_EOL
+ .'> ev2: Composer\Test\EventDispatcher\EventDispatcherTest->someMethod'.PHP_EOL;
+ $this->assertEquals($expected, $io->getOutput());
+
+ $dispatcher->removeListener($this);
+ $dispatcher->dispatch('ev1');
+ $dispatcher->dispatch('ev2');
+
+ $expected .= '> ev1: Composer\Test\EventDispatcher\EventDispatcherTest::someMethod'.PHP_EOL
+ .'> ev2: Composer\Test\EventDispatcher\EventDispatcherTest::someMethod'.PHP_EOL;
+ $this->assertEquals($expected, $io->getOutput());
+ }
+
public function testDispatcherCanExecuteCliAndPhpInSameEventScriptStack()
{
$process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
@@ -358,7 +377,7 @@ class EventDispatcherTest extends TestCase
return array();
}));
- $dispatcher->dispatch('root', new CommandEvent('root', $composer, $io));
+ $dispatcher->dispatch('root', new ScriptEvent('root', $composer, $io));
$expected = '> root: @group'.PHP_EOL.
'> group: echo -n foo'.PHP_EOL.
'> group: @subgroup'.PHP_EOL.
@@ -399,7 +418,7 @@ class EventDispatcherTest extends TestCase
return array();
}));
- $dispatcher->dispatch('helloWorld', new CommandEvent('helloWorld', $composer, $io));
+ $dispatcher->dispatch('helloWorld', new ScriptEvent('helloWorld', $composer, $io));
$expected = "> helloWorld: @hello World".PHP_EOL.
"> hello: echo Hello " .escapeshellarg('World').PHP_EOL;
@@ -437,7 +456,7 @@ class EventDispatcherTest extends TestCase
return array();
}));
- $dispatcher->dispatch('root', new CommandEvent('root', $composer, $io));
+ $dispatcher->dispatch('root', new ScriptEvent('root', $composer, $io));
}
private function getDispatcherStubForListenersTest($listeners, $io)
@@ -487,7 +506,7 @@ class EventDispatcherTest extends TestCase
->with($this->equalTo('> echo foo'));
$io->expects($this->once())
- ->method('write')
+ ->method('writeRaw')
->with($this->equalTo('foo'.PHP_EOL), false);
$dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
@@ -519,6 +538,10 @@ class EventDispatcherTest extends TestCase
->willReturn('> exit 1');
$io->expects($this->at(2))
+ ->method('isInteractive')
+ ->willReturn(1);
+
+ $io->expects($this->at(3))
->method('writeError')
->with($this->equalTo('Script '.$code.' handling the post-install-cmd event returned with error code 1'));
@@ -542,13 +565,9 @@ class EventDispatcherTest extends TestCase
->method('getListeners')
->will($this->returnValue(array()));
- $policy = $this->getMockBuilder('Composer\DependencyResolver\PolicyInterface')->getMock();
- $pool = $this->getMockBuilder('Composer\DependencyResolver\Pool')->disableOriginalConstructor()->getMock();
- $installedRepo = $this->getMockBuilder('Composer\Repository\CompositeRepository')->disableOriginalConstructor()->getMock();
- $request = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock();
+ $transaction = $this->getMockBuilder('Composer\DependencyResolver\LockTransaction')->disableOriginalConstructor()->getMock();
- $dispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, true, $policy, $pool, $installedRepo, $request);
- $dispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, true, $policy, $pool, $installedRepo, $request, array());
+ $dispatcher->dispatchInstallerEvent(InstallerEvents::PRE_OPERATIONS_EXEC, true, true, $transaction);
}
public static function call()
@@ -556,21 +575,16 @@ class EventDispatcherTest extends TestCase
throw new \RuntimeException();
}
- public static function expectsCommandEvent(CommandEvent $event)
- {
- return false;
- }
-
- public static function expectsVariableEvent($event)
- {
- return false;
- }
-
public static function someMethod()
{
return true;
}
+ public static function someMethod2()
+ {
+ return true;
+ }
+
private function createComposerInstance()
{
$composer = new Composer;
diff --git a/tests/Composer/Test/FactoryTest.php b/tests/Composer/Test/FactoryTest.php
index 6704e5b15..96b0e95d5 100644
--- a/tests/Composer/Test/FactoryTest.php
+++ b/tests/Composer/Test/FactoryTest.php
@@ -35,6 +35,6 @@ class FactoryTest extends TestCase
->with($this->equalTo('disable-tls'))
->will($this->returnValue(true));
- Factory::createRemoteFilesystem($ioMock, $config);
+ Factory::createHttpDownloader($ioMock, $config);
}
}
diff --git a/tests/Composer/Test/Fixtures/installer/abandoned-listed.test b/tests/Composer/Test/Fixtures/installer/abandoned-listed.test
index 7eba0a6f0..3ad5b0428 100644
--- a/tests/Composer/Test/Fixtures/installer/abandoned-listed.test
+++ b/tests/Composer/Test/Fixtures/installer/abandoned-listed.test
@@ -22,14 +22,18 @@ Abandoned packages are flagged
}
}
--RUN--
-install
+update
--EXPECT-OUTPUT--
Loading composer repositories with package information
-Updating dependencies (including require-dev)
+Updating dependencies
+Lock file operations: 2 installs, 0 updates, 0 removals
+ - Locking a/a (1.0.0)
+ - Locking c/c (1.0.0)
+Writing lock file
+Installing dependencies from lock file (including require-dev)
Package operations: 2 installs, 0 updates, 0 removals
Package a/a is abandoned, you should avoid using it. No replacement was suggested.
Package c/c is abandoned, you should avoid using it. Use b/b instead.
-Writing lock file
Generating autoload files
--EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/alias-with-reference.test b/tests/Composer/Test/Fixtures/installer/alias-with-reference.test
index d1609ed9a..df25f7478 100644
--- a/tests/Composer/Test/Fixtures/installer/alias-with-reference.test
+++ b/tests/Composer/Test/Fixtures/installer/alias-with-reference.test
@@ -28,4 +28,5 @@ install
--EXPECT--
Installing a/aliased (dev-master abcd)
Marking a/aliased (1.0.0) as installed, alias of a/aliased (dev-master abcd)
+Marking a/aliased (9999999-dev abcd) as installed, alias of a/aliased (dev-master abcd)
Installing b/requirer (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/aliased-priority-conflicting.test b/tests/Composer/Test/Fixtures/installer/aliased-priority-conflicting.test
index cda5d31d3..c13d5fc0e 100644
--- a/tests/Composer/Test/Fixtures/installer/aliased-priority-conflicting.test
+++ b/tests/Composer/Test/Fixtures/installer/aliased-priority-conflicting.test
@@ -14,12 +14,12 @@ Aliases take precedence over default package even if default is selected
"name": "a/req", "version": "dev-master",
"extra": { "branch-alias": { "dev-master": "1.0.x-dev" } },
"source": { "reference": "forked", "type": "git", "url": "" }
- }
- ]
- },
- {
- "type": "package",
- "package": [
+ },
+ {
+ "name": "a/req", "version": "dev-master",
+ "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } },
+ "source": { "reference": "master", "type": "git", "url": "" }
+ },
{
"name": "a/a", "version": "dev-master",
"require": { "a/req": "dev-master" }
@@ -27,11 +27,6 @@ Aliases take precedence over default package even if default is selected
{
"name": "a/b", "version": "dev-master",
"require": { "a/req": "dev-master" }
- },
- {
- "name": "a/req", "version": "dev-master",
- "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } },
- "source": { "reference": "master", "type": "git", "url": "" }
}
]
}
@@ -43,10 +38,51 @@ Aliases take precedence over default package even if default is selected
},
"minimum-stability": "dev"
}
+--EXPECT-LOCK--
+{
+ "packages": [
+ {
+ "name": "a/a", "version": "dev-master",
+ "require": { "a/req": "dev-master" },
+ "type": "library"
+ },
+ {
+ "name": "a/b", "version": "dev-master",
+ "require": { "a/req": "dev-master" },
+ "type": "library"
+ },
+ {
+ "name": "a/req", "version": "dev-feature-foo",
+ "source": { "reference": "feat.f", "type": "git", "url": "" },
+ "type": "library"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [
+ {
+ "alias": "dev-master",
+ "alias_normalized": "dev-master",
+ "version": "dev-feature-foo",
+ "package": "a/req"
+ }
+ ],
+ "minimum-stability": "dev",
+ "stability-flags": {
+ "a/a": 20,
+ "a/b": 20,
+ "a/req": 20
+ },
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
--RUN--
install
--EXPECT--
Installing a/req (dev-feature-foo feat.f)
Marking a/req (dev-master feat.f) as installed, alias of a/req (dev-feature-foo feat.f)
Installing a/a (dev-master)
+Marking a/a (9999999-dev) as installed, alias of a/a (dev-master)
Installing a/b (dev-master)
+Marking a/b (9999999-dev) as installed, alias of a/b (dev-master)
diff --git a/tests/Composer/Test/Fixtures/installer/aliased-priority.test b/tests/Composer/Test/Fixtures/installer/aliased-priority.test
index 97ffe5521..8dd0e8470 100644
--- a/tests/Composer/Test/Fixtures/installer/aliased-priority.test
+++ b/tests/Composer/Test/Fixtures/installer/aliased-priority.test
@@ -51,6 +51,6 @@ install
Installing a/c (dev-feature-foo feat.f)
Marking a/c (dev-master feat.f) as installed, alias of a/c (dev-feature-foo feat.f)
Installing a/b (dev-master forked)
+Marking a/b (1.0.x-dev forked) as installed, alias of a/b (dev-master forked)
Installing a/a (dev-master master)
Marking a/a (1.0.x-dev master) as installed, alias of a/a (dev-master master)
-Marking a/b (1.0.x-dev forked) as installed, alias of a/b (dev-master forked)
diff --git a/tests/Composer/Test/Fixtures/installer/broken-deps-do-not-replace.test b/tests/Composer/Test/Fixtures/installer/broken-deps-do-not-replace.test
index db4ef23c0..c9a9dba6e 100644
--- a/tests/Composer/Test/Fixtures/installer/broken-deps-do-not-replace.test
+++ b/tests/Composer/Test/Fixtures/installer/broken-deps-do-not-replace.test
@@ -19,16 +19,16 @@ Broken dependencies should not lead to a replacer being installed which is not m
}
}
--RUN--
-install
+update
--EXPECT-OUTPUT--
Loading composer repositories with package information
-Updating dependencies (including require-dev)
+Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- - c/c 1.0.0 requires x/x 1.0 -> no matching package found.
+ - c/c 1.0.0 requires x/x 1.0 -> could not be found in any version, there may be a typo in the package name.
- b/b 1.0.0 requires c/c 1.* -> satisfiable by c/c[1.0.0].
- - Installation request for b/b 1.* -> satisfiable by b/b[1.0.0].
+ - Root composer.json requires b/b 1.* -> satisfiable by b/b[1.0.0].
Potential causes:
- A typo in the package name
diff --git a/tests/Composer/Test/Fixtures/installer/circular-dependency2.test b/tests/Composer/Test/Fixtures/installer/circular-dependency2.test
index c89beef6b..2a2cc4b0d 100644
--- a/tests/Composer/Test/Fixtures/installer/circular-dependency2.test
+++ b/tests/Composer/Test/Fixtures/installer/circular-dependency2.test
@@ -2,7 +2,7 @@
Circular dependencies are possible between packages
--COMPOSER--
{
- "name": "root",
+ "name": "root/pkg",
"version": "dev-master",
"require": {
"require/itself": "1.0.0",
@@ -32,5 +32,5 @@ Circular dependencies are possible between packages
--RUN--
update -v
--EXPECT--
-Installing require/itself (1.0.0)
Installing regular/pkg (1.0.0)
+Installing require/itself (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/conflict-between-dependents.test b/tests/Composer/Test/Fixtures/installer/conflict-between-dependents.test
new file mode 100644
index 000000000..a59b2ef98
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/conflict-between-dependents.test
@@ -0,0 +1,38 @@
+--TEST--
+Test the error output of solver problems for conflicts between two dependents
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "conflicter/pkg", "version": "1.0.0", "conflict": { "victim/pkg": "1.0.0"} },
+ { "name": "victim/pkg", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "conflicter/pkg": "1.0.0",
+ "victim/pkg": "1.0.0"
+ }
+}
+
+
+--RUN--
+update
+
+--EXPECT-EXIT-CODE--
+2
+
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+Your requirements could not be resolved to an installable set of packages.
+
+ Problem 1
+ - Root composer.json requires conflicter/pkg 1.0.0 -> satisfiable by conflicter/pkg[1.0.0].
+ - conflicter/pkg 1.0.0 conflicts with victim/pkg 1.0.0.
+ - Root composer.json requires victim/pkg 1.0.0 -> satisfiable by victim/pkg[1.0.0].
+
+--EXPECT--
+
diff --git a/tests/Composer/Test/Fixtures/installer/conflict-between-root-and-dependent.test b/tests/Composer/Test/Fixtures/installer/conflict-between-root-and-dependent.test
new file mode 100644
index 000000000..8df58ef44
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/conflict-between-root-and-dependent.test
@@ -0,0 +1,40 @@
+--TEST--
+Test conflicts between a dependency's requirements and the root requirements
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "requirer/pkg", "version": "1.0.0", "require": {
+ "dependency/pkg": "1.0.0",
+ "dependency/unstable-pkg": "1.0.0-dev"
+ } },
+ { "name": "dependency/pkg", "version": "2.0.0" },
+ { "name": "dependency/pkg", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "requirer/pkg": "1.*",
+ "dependency/pkg": "2.*"
+ }
+}
+
+--RUN--
+update
+
+--EXPECT-EXIT-CODE--
+2
+
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+Your requirements could not be resolved to an installable set of packages.
+
+ Problem 1
+ - Root composer.json requires requirer/pkg 1.* -> satisfiable by requirer/pkg[1.0.0].
+ - requirer/pkg 1.0.0 requires dependency/pkg 1.0.0 -> found dependency/pkg[1.0.0] but it conflicts with your root composer.json require (2.*).
+
+--EXPECT--
+
diff --git a/tests/Composer/Test/Fixtures/installer/disjunctive-multi-constraints.test b/tests/Composer/Test/Fixtures/installer/disjunctive-multi-constraints.test
index b274c5de2..0e130e747 100644
--- a/tests/Composer/Test/Fixtures/installer/disjunctive-multi-constraints.test
+++ b/tests/Composer/Test/Fixtures/installer/disjunctive-multi-constraints.test
@@ -6,19 +6,19 @@ Disjunctive multi constraints work
{
"type": "package",
"package": [
- { "name": "foo", "version": "1.1.0" },
- { "name": "foo", "version": "1.0.0" },
- { "name": "bar", "version": "1.1.0", "require": { "foo": "1.0.*" } }
+ { "name": "foo/pkg", "version": "1.1.0" },
+ { "name": "foo/pkg", "version": "1.0.0" },
+ { "name": "bar/pkg", "version": "1.1.0", "require": { "foo/pkg": "1.0.*" } }
]
}
],
"require": {
- "bar": "1.*",
- "foo": "1.0.*|1.1.*"
+ "bar/pkg": "1.*",
+ "foo/pkg": "1.0.*|1.1.*"
}
}
--RUN--
install
--EXPECT--
-Installing foo (1.0.0)
-Installing bar (1.1.0)
+Installing foo/pkg (1.0.0)
+Installing bar/pkg (1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/github-issues-4319.test b/tests/Composer/Test/Fixtures/installer/github-issues-4319.test
index ee221dab0..dcc94dff2 100644
--- a/tests/Composer/Test/Fixtures/installer/github-issues-4319.test
+++ b/tests/Composer/Test/Fixtures/installer/github-issues-4319.test
@@ -28,16 +28,16 @@ Present a clear error message when config.platform.php version results in a conf
}
--RUN--
-install
+update
--EXPECT-OUTPUT--
Loading composer repositories with package information
-Updating dependencies (including require-dev)
+Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- - Installation request for a/a ~1.0 -> satisfiable by a/a[1.0.0].
- - a/a 1.0.0 requires php 5.5 -> your PHP version (%s) overridden by "config.platform.php" version (5.3) does not satisfy that requirement.
+ - Root composer.json requires a/a ~1.0 -> satisfiable by a/a[1.0.0].
+ - a/a 1.0.0 requires php 5.5 -> your php version (5.3; overridden via config.platform, actual: %s) does not satisfy that requirement.
--EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/github-issues-4795-2.test b/tests/Composer/Test/Fixtures/installer/github-issues-4795-2.test
index 877ac3653..b8968c35b 100644
--- a/tests/Composer/Test/Fixtures/installer/github-issues-4795-2.test
+++ b/tests/Composer/Test/Fixtures/installer/github-issues-4795-2.test
@@ -29,17 +29,39 @@ that are also a root package, when that root package is also explicitly whitelis
{ "name": "a/a", "version": "1.0.0" },
{ "name": "b/b", "version": "1.0.0", "require": { "a/a": "~1.0" } }
]
-
+--LOCK--
+{
+ "packages": [
+ {
+ "name": "a/a", "version": "1.0.0"
+ },
+ {
+ "name": "b/b", "version": "1.0.0", "require": { "a/a": "~1.0" }
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {},
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
--RUN--
update a/a b/b --with-dependencies
--EXPECT-OUTPUT--
Loading composer repositories with package information
-Updating dependencies (including require-dev)
-Package operations: 0 installs, 2 updates, 0 removals
+Updating dependencies
+Lock file operations: 0 installs, 2 updates, 0 removals
+ - Upgrading a/a (1.0.0 => 1.1.0)
+ - Upgrading b/b (1.0.0 => 1.1.0)
Writing lock file
+Installing dependencies from lock file (including require-dev)
+Package operations: 0 installs, 2 updates, 0 removals
Generating autoload files
--EXPECT--
-Updating a/a (1.0.0) to a/a (1.1.0)
-Updating b/b (1.0.0) to b/b (1.1.0)
+Upgrading a/a (1.0.0 => 1.1.0)
+Upgrading b/b (1.0.0 => 1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/github-issues-4795.test b/tests/Composer/Test/Fixtures/installer/github-issues-4795.test
index 1f4b1af27..dc722c379 100644
--- a/tests/Composer/Test/Fixtures/installer/github-issues-4795.test
+++ b/tests/Composer/Test/Fixtures/installer/github-issues-4795.test
@@ -14,7 +14,7 @@ dependency of one the requirements that is whitelisted for update.
{ "name": "a/a", "version": "1.0.0" },
{ "name": "a/a", "version": "1.1.0" },
{ "name": "b/b", "version": "1.0.0", "require": { "a/a": "~1.0" } },
- { "name": "b/b", "version": "1.1.0", "require": { "a/b": "~1.1" } }
+ { "name": "b/b", "version": "1.1.0", "require": { "a/a": "~1.1" } }
]
}
],
@@ -30,15 +30,32 @@ dependency of one the requirements that is whitelisted for update.
{ "name": "b/b", "version": "1.0.0", "require": { "a/a": "~1.0" } }
]
+--LOCK--
+{
+ "packages": [
+ { "name": "a/a", "version": "1.0.0" },
+ { "name": "b/b", "version": "1.0.0", "require": { "a/a": "~1.0" } }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
--RUN--
update b/b --with-dependencies
--EXPECT-OUTPUT--
-Dependency "a/a" is also a root requirement, but is not explicitly whitelisted. Ignoring.
Loading composer repositories with package information
-Updating dependencies (including require-dev)
-Nothing to install or update
+Updating dependencies
+Dependency "a/a" is also a root requirement. Package has not been listed as an update argument, so keeping locked at old version. Use --with-all-dependencies to include root dependencies.
+Nothing to modify in lock file
Writing lock file
+Installing dependencies from lock file (including require-dev)
+Nothing to install, update or remove
Generating autoload files
--EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/install-aliased-alias.test b/tests/Composer/Test/Fixtures/installer/install-aliased-alias.test
index f535caa7e..6fd1f7b53 100644
--- a/tests/Composer/Test/Fixtures/installer/install-aliased-alias.test
+++ b/tests/Composer/Test/Fixtures/installer/install-aliased-alias.test
@@ -32,5 +32,6 @@ install
--EXPECT--
Installing b/b (dev-foo)
Marking b/b (dev-master) as installed, alias of b/b (dev-foo)
-Installing a/a (dev-master)
Marking b/b (1.0.x-dev) as installed, alias of b/b (dev-foo)
+Installing a/a (dev-master)
+Marking a/a (9999999-dev) as installed, alias of a/a (dev-master)
diff --git a/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test b/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test
index 5846d13c0..400e932ed 100644
--- a/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test
+++ b/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test
@@ -53,3 +53,4 @@ install --prefer-dist
}
--EXPECT--
Installing a/a (dev-master)
+Marking a/a (9999999-dev) as installed, alias of a/a (dev-master)
diff --git a/tests/Composer/Test/Fixtures/installer/install-from-empty-lock.test b/tests/Composer/Test/Fixtures/installer/install-from-empty-lock.test
index 7bb69f131..0bba90cff 100644
--- a/tests/Composer/Test/Fixtures/installer/install-from-empty-lock.test
+++ b/tests/Composer/Test/Fixtures/installer/install-from-empty-lock.test
@@ -6,22 +6,22 @@ Requirements from the composer file are not installed if the lock file is presen
{
"type": "package",
"package": [
- { "name": "required", "version": "1.0.0" },
- { "name": "newly-required", "version": "1.0.0" }
+ { "name": "required/pkg", "version": "1.0.0" },
+ { "name": "newly-required/pkg", "version": "1.0.0" }
]
}
],
"require": {
- "required": "1.0.0",
- "newly-required": "1.0.0"
+ "required/pkg": "1.0.0",
+ "newly-required/pkg": "1.0.0"
}
}
--LOCK--
{
"packages": [
- { "name": "required", "version": "1.0.0" }
+ { "name": "required/pkg", "version": "1.0.0" }
],
- "packages-dev": null,
+ "packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
@@ -31,4 +31,4 @@ Requirements from the composer file are not installed if the lock file is presen
--RUN--
install
--EXPECT--
-Installing required (1.0.0)
\ No newline at end of file
+Installing required/pkg (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/install-from-lock-removes-package.test b/tests/Composer/Test/Fixtures/installer/install-from-lock-removes-package.test
index 6063abfee..b996ff65b 100644
--- a/tests/Composer/Test/Fixtures/installer/install-from-lock-removes-package.test
+++ b/tests/Composer/Test/Fixtures/installer/install-from-lock-removes-package.test
@@ -6,26 +6,26 @@ Install from a lock file that deleted a package
{
"type": "package",
"package": [
- { "name": "whitelisted", "version": "1.1.0" },
- { "name": "whitelisted", "version": "1.0.0", "require": { "fixed-dependency": "1.0.0", "old-dependency": "1.0.0" } },
- { "name": "fixed-dependency", "version": "1.1.0" },
- { "name": "fixed-dependency", "version": "1.0.0" },
- { "name": "old-dependency", "version": "1.0.0" }
+ { "name": "whitelisted/pkg", "version": "1.1.0" },
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "fixed/dependency": "1.0.0", "old/dependency": "1.0.0" } },
+ { "name": "fixed/dependency", "version": "1.1.0" },
+ { "name": "fixed/dependency", "version": "1.0.0" },
+ { "name": "old/dependency", "version": "1.0.0" }
]
}
],
"require": {
- "whitelisted": "1.*",
- "fixed-dependency": "1.*"
+ "whitelisted/pkg": "1.*",
+ "fixed/dependency": "1.*"
}
}
--LOCK--
{
"packages": [
- { "name": "whitelisted", "version": "1.1.0" },
- { "name": "fixed-dependency", "version": "1.0.0" }
+ { "name": "whitelisted/pkg", "version": "1.1.0" },
+ { "name": "fixed/dependency", "version": "1.0.0" }
],
- "packages-dev": null,
+ "packages-dev": [],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": [],
@@ -33,12 +33,12 @@ Install from a lock file that deleted a package
}
--INSTALLED--
[
- { "name": "whitelisted", "version": "1.0.0", "require": { "old-dependency": "1.0.0", "fixed-dependency": "1.0.0" } },
- { "name": "fixed-dependency", "version": "1.0.0" },
- { "name": "old-dependency", "version": "1.0.0" }
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "old/dependency": "1.0.0", "fixed/dependency": "1.0.0" } },
+ { "name": "fixed/dependency", "version": "1.0.0" },
+ { "name": "old/dependency", "version": "1.0.0" }
]
--RUN--
install
--EXPECT--
-Uninstalling old-dependency (1.0.0)
-Updating whitelisted (1.0.0) to whitelisted (1.1.0)
+Removing old/dependency (1.0.0)
+Upgrading whitelisted/pkg (1.0.0 => 1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/install-funding-notice.test b/tests/Composer/Test/Fixtures/installer/install-funding-notice.test
index a73badc77..cbdc206d8 100644
--- a/tests/Composer/Test/Fixtures/installer/install-funding-notice.test
+++ b/tests/Composer/Test/Fixtures/installer/install-funding-notice.test
@@ -41,10 +41,16 @@ Installs a simple package with exact match requirement
--RUN--
install
--EXPECT-OUTPUT--
+No lock file found. Updating dependencies instead of installing from lock file. Use composer update over composer install if you do not have a lock file.
Loading composer repositories with package information
-Updating dependencies (including require-dev)
-Package operations: 3 installs, 0 updates, 0 removals
+Updating dependencies
+Lock file operations: 3 installs, 0 updates, 0 removals
+ - Locking a/a (1.0.0)
+ - Locking b/b (1.0.0)
+ - Locking d/d (1.0.0)
Writing lock file
+Installing dependencies from lock file (including require-dev)
+Package operations: 3 installs, 0 updates, 0 removals
Generating autoload files
2 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
diff --git a/tests/Composer/Test/Fixtures/installer/install-missing-alias-from-lock.test b/tests/Composer/Test/Fixtures/installer/install-missing-alias-from-lock.test
index 5acb7a069..5f6459799 100644
--- a/tests/Composer/Test/Fixtures/installer/install-missing-alias-from-lock.test
+++ b/tests/Composer/Test/Fixtures/installer/install-missing-alias-from-lock.test
@@ -29,7 +29,7 @@ Installing an old alias that doesn't exist anymore from a lock is possible
"type": "library"
}
],
- "packages-dev": null,
+ "packages-dev": [],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": [],
diff --git a/tests/Composer/Test/Fixtures/installer/install-reference.test b/tests/Composer/Test/Fixtures/installer/install-reference.test
index f8e696f99..74bf6e40a 100644
--- a/tests/Composer/Test/Fixtures/installer/install-reference.test
+++ b/tests/Composer/Test/Fixtures/installer/install-reference.test
@@ -21,3 +21,4 @@ Installs a dev package forcing it's reference
install
--EXPECT--
Installing a/a (dev-master def000)
+Marking a/a (9999999-dev def000) as installed, alias of a/a (dev-master def000)
diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test b/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-allow-listed-unstable.test
similarity index 62%
rename from tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test
rename to tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-allow-listed-unstable.test
index 3a428c97c..25bd4a9c6 100644
--- a/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-whitelisted-unstable.test
+++ b/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-allow-listed-unstable.test
@@ -1,5 +1,5 @@
--TEST--
-Partial update from lock file should apply lock file and downgrade unstable packages even if not whitelisted
+Partial update from lock file should apply lock file and if an unstable package is not allowed anymore by latest composer.json it should fail
--COMPOSER--
{
"repositories": [
@@ -48,24 +48,15 @@ Partial update from lock file should apply lock file and downgrade unstable pack
]
--RUN--
update c/uptodate
---EXPECT-LOCK--
-{
- "packages": [
- { "name": "a/old", "version": "1.0.0", "type": "library" },
- { "name": "b/unstable", "version": "1.0.0", "type": "library" },
- { "name": "c/uptodate", "version": "2.0.0", "type": "library" },
- { "name": "d/removed", "version": "1.0.0", "type": "library" }
- ],
- "packages-dev": [],
- "aliases": [],
- "minimum-stability": "stable",
- "stability-flags": [],
- "prefer-stable": false,
- "prefer-lowest": false,
- "platform": [],
- "platform-dev": []
-}
--EXPECT--
-Downgrading b/unstable (1.1.0-alpha) to b/unstable (1.0.0)
-Updating a/old (0.9.0) to a/old (1.0.0)
-Installing d/removed (1.0.0)
+
+--EXPECT-EXIT-CODE--
+2
+
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+Your requirements could not be resolved to an installable set of packages.
+
+ Problem 1
+ - b/unstable is fixed to 1.1.0-alpha (lock file version) by a partial update but that version is rejected by your minimum-stability. Make sure you list it as an argument for the update command.
diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-forces-dev-reference-from-lock-for-non-updated-packages.test b/tests/Composer/Test/Fixtures/installer/partial-update-forces-dev-reference-from-lock-for-non-updated-packages.test
index 4533d5a94..f45f6d528 100644
--- a/tests/Composer/Test/Fixtures/installer/partial-update-forces-dev-reference-from-lock-for-non-updated-packages.test
+++ b/tests/Composer/Test/Fixtures/installer/partial-update-forces-dev-reference-from-lock-for-non-updated-packages.test
@@ -93,5 +93,5 @@ update b/b
"platform-dev": []
}
--EXPECT--
-Updating a/a (dev-master oldmaster-a) to a/a (dev-master newmaster-a)
-Updating b/b (dev-master oldmaster-b) to b/b (dev-master newmaster-b2)
+Upgrading a/a (dev-master oldmaster-a => dev-master newmaster-a)
+Upgrading b/b (dev-master oldmaster-b => dev-master newmaster-b2)
diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test b/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test
index a54d66ee9..bce29a21f 100644
--- a/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test
+++ b/tests/Composer/Test/Fixtures/installer/partial-update-from-lock.test
@@ -74,8 +74,8 @@ update b/unstable
"platform-dev": []
}
--EXPECT--
-Downgrading b/unstable (1.1.0-alpha) to b/unstable (1.0.0)
-Updating a/old (0.9.0) to a/old (1.0.0)
-Downgrading c/uptodate (2.0.0) to c/uptodate (1.0.0)
+Upgrading a/old (0.9.0 => 1.0.0)
+Downgrading b/unstable (1.1.0-alpha => 1.0.0)
+Downgrading c/uptodate (2.0.0 => 1.0.0)
Installing d/removed (1.0.0)
Installing e/newreq (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-installs-from-lock-even-missing.test b/tests/Composer/Test/Fixtures/installer/partial-update-installs-from-lock-even-missing.test
index 9a6f0ab9e..7530ca862 100644
--- a/tests/Composer/Test/Fixtures/installer/partial-update-installs-from-lock-even-missing.test
+++ b/tests/Composer/Test/Fixtures/installer/partial-update-installs-from-lock-even-missing.test
@@ -97,9 +97,9 @@ update b/b
"platform-dev": []
}
--EXPECT--
-Updating a/a (dev-master oldmaster-a) to a/a (dev-master newmaster-a)
-Updating b/b (dev-master oldmaster-b) to b/b (dev-master newmaster-b2)
+Upgrading a/a (dev-master oldmaster-a => dev-master newmaster-a)
Marking a/a (2.2.x-dev newmaster-a) as installed, alias of a/a (dev-master newmaster-a)
+Upgrading b/b (dev-master oldmaster-b => dev-master newmaster-b2)
Marking b/b (2.3.x-dev newmaster-b2) as installed, alias of b/b (dev-master newmaster-b2)
-Marking b/b (2.1.x-dev oldmaster-b) as uninstalled, alias of b/b (dev-master oldmaster-b)
Marking a/a (2.1.x-dev oldmaster-a) as uninstalled, alias of a/a (dev-master oldmaster-a)
+Marking b/b (2.1.x-dev oldmaster-b) as uninstalled, alias of b/b (dev-master oldmaster-b)
diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test b/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test
index 94be9176c..74007af7b 100644
--- a/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test
+++ b/tests/Composer/Test/Fixtures/installer/partial-update-without-lock.test
@@ -1,5 +1,5 @@
--TEST--
-Partial update without lock file should update everything whitelisted, remove overly unstable packages
+Partial update without lock file should error
--COMPOSER--
{
"repositories": [
@@ -30,22 +30,8 @@ Partial update without lock file should update everything whitelisted, remove ov
]
--RUN--
update b/unstable
---EXPECT-LOCK--
-{
- "packages": [
- { "name": "a/old", "version": "1.0.0", "type": "library" },
- { "name": "b/unstable", "version": "1.0.0", "type": "library" },
- { "name": "c/uptodate", "version": "1.0.0", "type": "library" },
- { "name": "d/removed", "version": "1.0.0", "type": "library" }
- ],
- "packages-dev": [],
- "aliases": [],
- "minimum-stability": "stable",
- "stability-flags": [],
- "prefer-stable": false,
- "prefer-lowest": false,
- "platform": [],
- "platform-dev": []
-}
+--EXPECT-OUTPUT--
+Cannot update only a partial set of packages without a lock file present.
+--EXPECT-EXIT-CODE--
+1
--EXPECT--
-Downgrading b/unstable (1.1.0-alpha) to b/unstable (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test b/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test
index 009eb576d..6a8b2030c 100644
--- a/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test
+++ b/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test
@@ -6,26 +6,26 @@ Composer installers and their requirements are installed first
{
"type": "package",
"package": [
- { "name": "pkg", "version": "1.0.0" },
- { "name": "pkg2", "version": "1.0.0" },
- { "name": "inst", "version": "1.0.0", "type": "composer-plugin" },
- { "name": "inst-with-req", "version": "1.0.0", "type": "composer-plugin", "require": { "php": ">=5", "ext-json": "*", "composer-plugin-api": "*" } },
- { "name": "inst-with-req2", "version": "1.0.0", "type": "composer-plugin", "require": { "pkg2": "*" } }
+ { "name": "pkg/1", "version": "1.0.0" },
+ { "name": "pkg/2", "version": "1.0.0" },
+ { "name": "inst/pkg", "version": "1.0.0", "type": "composer-plugin" },
+ { "name": "inst/with-req", "version": "1.0.0", "type": "composer-plugin", "require": { "php": ">=5", "ext-json": "*", "composer-plugin-api": "*" } },
+ { "name": "inst/with-req2", "version": "1.0.0", "type": "composer-plugin", "require": { "pkg/2": "*" } }
]
}
],
"require": {
- "pkg": "1.0.0",
- "inst": "1.0.0",
- "inst-with-req2": "1.0.0",
- "inst-with-req": "1.0.0"
+ "pkg/1": "1.0.0",
+ "inst/pkg": "1.0.0",
+ "inst/with-req2": "1.0.0",
+ "inst/with-req": "1.0.0"
}
}
--RUN--
install
--EXPECT--
-Installing inst (1.0.0)
-Installing inst-with-req (1.0.0)
-Installing pkg2 (1.0.0)
-Installing inst-with-req2 (1.0.0)
-Installing pkg (1.0.0)
+Installing inst/pkg (1.0.0)
+Installing inst/with-req (1.0.0)
+Installing pkg/2 (1.0.0)
+Installing inst/with-req2 (1.0.0)
+Installing pkg/1 (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/provider-conflicts.test b/tests/Composer/Test/Fixtures/installer/provider-conflicts.test
new file mode 100644
index 000000000..3a9700103
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/provider-conflicts.test
@@ -0,0 +1,49 @@
+--TEST--
+Test that names provided by a dependent and root package cause a conflict only for replace
+--COMPOSER--
+{
+ "version": "1.2.3",
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ {
+ "name": "provider/pkg",
+ "version": "1.0.0",
+ "provide": { "root-provided/transitive-provided": "2.*", "root-replaced/transitive-provided": "2.*" },
+ "replace": { "root-provided/transitive-replaced": "2.*", "root-replaced/transitive-replaced": "2.*" }
+ }
+ ]
+ }
+ ],
+ "require": {
+ "provider/pkg": "*"
+ },
+ "provide": {
+ "root-provided/transitive-replaced": "2.*",
+ "root-provided/transitive-provided": "2.*"
+ },
+ "replace": {
+ "root-replaced/transitive-replaced": "2.*",
+ "root-replaced/transitive-provided": "2.*"
+ }
+}
+
+--RUN--
+update
+
+--EXPECT-EXIT-CODE--
+2
+
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+Your requirements could not be resolved to an installable set of packages.
+
+ Problem 1
+ - __root__ is present at version 1.2.3 and cannot be modified by Composer
+ - provider/pkg 1.0.0 cannot be installed as that would require removing __root__ 1.2.3. They both replace root-replaced/transitive-replaced and thus cannot coexist.
+ - Root composer.json requires provider/pkg * -> satisfiable by provider/pkg[1.0.0].
+
+--EXPECT--
+
diff --git a/tests/Composer/Test/Fixtures/installer/provider-conflicts2.test b/tests/Composer/Test/Fixtures/installer/provider-conflicts2.test
new file mode 100644
index 000000000..343dab537
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/provider-conflicts2.test
@@ -0,0 +1,45 @@
+--TEST--
+Test that names provided by two dependents cause a conflict
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ {
+ "name": "provider/pkg",
+ "version": "1.0.0",
+ "provide": { "third/pkg": "2.*" }
+ },
+ {
+ "name": "replacer/pkg",
+ "version": "1.0.0",
+ "replace": { "third/pkg": "2.*" }
+ }
+ ]
+ }
+ ],
+ "require": {
+ "provider/pkg": "*",
+ "replacer/pkg": "*"
+ }
+}
+
+--RUN--
+update
+
+--EXPECT-EXIT-CODE--
+2
+
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+Your requirements could not be resolved to an installable set of packages.
+
+ Problem 1
+ - Root composer.json requires provider/pkg * -> satisfiable by provider/pkg[1.0.0].
+ - Only one of these can be installed: replacer/pkg 1.0.0, provider/pkg 1.0.0.
+ - Root composer.json requires replacer/pkg * -> satisfiable by replacer/pkg[1.0.0].
+
+--EXPECT--
+
diff --git a/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test b/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test
new file mode 100644
index 000000000..1c2ea0ceb
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test
@@ -0,0 +1,54 @@
+--TEST--
+Test that a replacer can not be installed together with another version of the package it replaces
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ {"name": "replacer/pkg", "version": "2.0.0", "replace": { "regular/pkg": "self.version" }},
+ {"name": "replacer/pkg", "version": "2.0.1", "replace": { "regular/pkg": "self.version" }},
+ {"name": "replacer/pkg", "version": "2.0.2", "replace": { "regular/pkg": "self.version" }},
+ {"name": "replacer/pkg", "version": "2.0.3", "replace": { "regular/pkg": "self.version" }},
+ {"name": "regular/pkg", "version": "1.0.0"},
+ {"name": "regular/pkg", "version": "1.0.1"},
+ {"name": "regular/pkg", "version": "1.0.2"},
+ {"name": "regular/pkg", "version": "1.0.3"},
+ {"name": "regular/pkg", "version": "2.0.0"},
+ {"name": "regular/pkg", "version": "2.0.1"}
+ ]
+ }
+ ],
+ "require": {
+ "regular/pkg": "1.*",
+ "replacer/pkg": "2.*"
+ }
+}
+
+--RUN--
+update
+
+--EXPECT-EXIT-CODE--
+2
+
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+Your requirements could not be resolved to an installable set of packages.
+
+ Problem 1
+ - Conclusion: don't install regular/pkg 1.0.3, learned rules:
+ - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3].
+ - Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and thus cannot coexist with it.
+ - Conclusion: don't install regular/pkg 1.0.2, learned rules:
+ - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3].
+ - Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and thus cannot coexist with it.
+ - Conclusion: don't install regular/pkg 1.0.1, learned rules:
+ - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3].
+ - Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and thus cannot coexist with it.
+ - Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and thus cannot coexist with it.
+ - Root composer.json requires regular/pkg 1.* -> satisfiable by regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3].
+ - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3].
+
+--EXPECT--
+
diff --git a/tests/Composer/Test/Fixtures/installer/provider-dev-require-can-satisfy-require.test b/tests/Composer/Test/Fixtures/installer/provider-dev-require-can-satisfy-require.test
new file mode 100644
index 000000000..b5d3fefe7
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/provider-dev-require-can-satisfy-require.test
@@ -0,0 +1,52 @@
+--TEST--
+Test that a requirement can be satisfied by a providing package required in require-dev.
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ {"name": "provider/requirer", "version": "1.0.0", "type": "metapackage", "require": {"b/b": "1.0.0"}},
+ {"name": "b/b", "version": "1.0.0", "type": "metapackage", "provide": {"provided/pkg": "1.0.0"}}
+ ]
+ }
+ ],
+ "require": {
+ "provided/pkg": "1.0.0"
+ },
+ "require-dev": {
+ "provider/requirer": "1.0.0"
+ }
+}
+
+--RUN--
+update --no-dev
+
+--EXPECT-LOCK--
+{
+ "packages": [
+ {
+ "name": "b/b",
+ "version": "1.0.0",
+ "type": "metapackage",
+ "provide": {"provided/pkg": "1.0.0"}
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "provider/requirer",
+ "version": "1.0.0",
+ "type": "metapackage",
+ "require": {"b/b": "1.0.0"}
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--EXPECT--
+Installing b/b (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/provider-packages-can-be-installed-if-selected.test b/tests/Composer/Test/Fixtures/installer/provider-packages-can-be-installed-if-selected.test
new file mode 100644
index 000000000..be425010c
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/provider-packages-can-be-installed-if-selected.test
@@ -0,0 +1,36 @@
+--TEST--
+Test that providers can be installed if they are selected and the package they provide is not installable
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ {
+ "name": "foo/polyfill",
+ "provide": {
+ "foo/standard": "1.0.0"
+ },
+ "version": "1.0.0"
+ },
+ {
+ "name": "foo/standard",
+ "require": {
+ "foo/does-not-exist": "1.0.0"
+ },
+ "version": "1.0.0"
+ }
+ ]
+ }
+ ],
+ "require": {
+ "foo/standard": "1.0.0",
+ "foo/polyfill": "1.0.0"
+ }
+}
+
+--RUN--
+update
+
+--EXPECT--
+Installing foo/polyfill (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/provider-packages-can-be-installed-together-with-provided-if-both-installable.json b/tests/Composer/Test/Fixtures/installer/provider-packages-can-be-installed-together-with-provided-if-both-installable.json
new file mode 100644
index 000000000..9abe90dd8
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/provider-packages-can-be-installed-together-with-provided-if-both-installable.json
@@ -0,0 +1,34 @@
+--TEST--
+Test that providers can be installed in conjunction with the package they provide if they are selected and the package they provide is also installable
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ {
+ "name": "foo/polyfill",
+ "provide": {
+ "foo/standard": "1.0.0"
+ },
+ "version": "1.0.0"
+ },
+ {
+ "name": "foo/standard",
+ "version": "1.0.0"
+ }
+ ]
+ }
+ ],
+ "require": {
+ "foo/standard": "1.0.0",
+ "foo/polyfill": "1.0.0"
+ }
+}
+
+--RUN--
+update
+
+--EXPECT--
+Installing foo/standard (1.0.0)
+Installing foo/polyfill (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/provider-packages-can-not-be-installed-unless-selected.test b/tests/Composer/Test/Fixtures/installer/provider-packages-can-not-be-installed-unless-selected.test
new file mode 100644
index 000000000..816b8efe9
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/provider-packages-can-not-be-installed-unless-selected.test
@@ -0,0 +1,55 @@
+--TEST--
+Test that providers can not be installed if they are not selected
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ {
+ "name": "foo/polyfill",
+ "provide": {
+ "foo/standard": "1.0.0"
+ },
+ "version": "1.0.0"
+ },
+ {
+ "name": "foo/standard",
+ "require": {
+ "foo/does-not-exist": "1.0.0"
+ },
+ "version": "1.0.0"
+ }
+ ]
+ }
+ ],
+ "require": {
+ "foo/standard": "1.0.0"
+ }
+}
+
+--RUN--
+update
+
+--EXPECT-EXIT-CODE--
+2
+
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+Your requirements could not be resolved to an installable set of packages.
+
+ Problem 1
+ - Root composer.json requires foo/standard 1.0.0 -> satisfiable by foo/standard[1.0.0].
+ - foo/standard 1.0.0 requires foo/does-not-exist 1.0.0 -> could not be found in any version, there may be a typo in the package name.
+
+Potential causes:
+ - A typo in the package name
+ - The package is not available in a stable-enough version according to your minimum-stability setting
+ see for more details.
+ - It's a private package and you forgot to add a custom repository to find it
+
+Read for further common problems.
+
+--EXPECT--
+
diff --git a/tests/Composer/Test/Fixtures/installer/replace-priorities.test b/tests/Composer/Test/Fixtures/installer/replace-priorities.test
index d69dd9a22..e561b548b 100644
--- a/tests/Composer/Test/Fixtures/installer/replace-priorities.test
+++ b/tests/Composer/Test/Fixtures/installer/replace-priorities.test
@@ -6,28 +6,28 @@ Replace takes precedence only in higher priority repositories and if explicitly
{
"type": "package",
"package": [
- { "name": "forked", "version": "1.1.0", "replace": { "package2": "1.1.0" } }
+ { "name": "forked/pkg", "version": "1.1.0", "replace": { "package/2": "1.1.0" } }
]
},
{
"type": "package",
"package": [
- { "name": "package", "version": "1.0.0" },
- { "name": "package2", "version": "1.0.0" },
- { "name": "package3", "version": "1.0.0", "require": { "forked": "*" } },
- { "name": "hijacker", "version": "1.1.0", "replace": { "package": "1.1.0" } }
+ { "name": "package/1", "version": "1.0.0" },
+ { "name": "package/2", "version": "1.0.0" },
+ { "name": "package/3", "version": "1.0.0", "require": { "forked/pkg": "*" } },
+ { "name": "hijacker/pkg", "version": "1.1.0", "replace": { "package/1": "1.1.0" } }
]
}
],
"require": {
- "package": "1.*",
- "package2": "1.*",
- "package3": "1.*"
+ "package/1": "1.*",
+ "package/2": "1.*",
+ "package/3": "1.*"
}
}
--RUN--
install
--EXPECT--
-Installing package (1.0.0)
-Installing forked (1.1.0)
-Installing package3 (1.0.0)
+Installing package/1 (1.0.0)
+Installing forked/pkg (1.1.0)
+Installing package/3 (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/repositories-priorities.test b/tests/Composer/Test/Fixtures/installer/repositories-priorities.test
new file mode 100644
index 000000000..bc06179e0
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/repositories-priorities.test
@@ -0,0 +1,35 @@
+--TEST--
+Packages found in a higher priority repository take precedence even if they are not found in the requested version
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "foo/a", "version": "1.0.0" }
+ ]
+ },
+ {
+ "type": "package",
+ "package": [
+ { "name": "foo/a", "version": "2.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "foo/a": "2.*"
+ }
+}
+--RUN--
+update
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+Your requirements could not be resolved to an installable set of packages.
+
+ Problem 1
+ - Root composer.json requires foo/a 2.*, it is satisfiable by foo/a[2.0.0] from package repo (defining 1 package) but foo/a[1.0.0] from package repo (defining 1 package) has higher repository priority. The packages with higher priority do not match your constraint and are therefore not installable.
+
+--EXPECT--
+--EXPECT-EXIT-CODE--
+2
diff --git a/tests/Composer/Test/Fixtures/installer/repositories-priorities2.test b/tests/Composer/Test/Fixtures/installer/repositories-priorities2.test
new file mode 100644
index 000000000..598079d80
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/repositories-priorities2.test
@@ -0,0 +1,26 @@
+--TEST--
+Packages found in a higher priority repository take precedence
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "foo/a", "version": "1.0.0" }
+ ]
+ },
+ {
+ "type": "package",
+ "package": [
+ { "name": "foo/a", "version": "1.1.0" }
+ ]
+ }
+ ],
+ "require": {
+ "foo/a": "1.*"
+ }
+}
+--RUN--
+update
+--EXPECT--
+Installing foo/a (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/root-requirements-do-not-affect-locked-versions.test b/tests/Composer/Test/Fixtures/installer/root-requirements-do-not-affect-locked-versions.test
index 15d1b4ef5..202767c1f 100644
--- a/tests/Composer/Test/Fixtures/installer/root-requirements-do-not-affect-locked-versions.test
+++ b/tests/Composer/Test/Fixtures/installer/root-requirements-do-not-affect-locked-versions.test
@@ -23,7 +23,7 @@ The locked version will not get overwritten by an install
{ "name": "foo/bar", "version": "1.0.0" },
{ "name": "foo/baz", "version": "2.0.0" }
],
- "packages-dev": null,
+ "packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
@@ -38,4 +38,4 @@ The locked version will not get overwritten by an install
--RUN--
install
--EXPECT--
-Updating foo/baz (1.0.0) to foo/baz (2.0.0)
+Upgrading foo/baz (1.0.0 => 2.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/solver-problems.test b/tests/Composer/Test/Fixtures/installer/solver-problems.test
index cab45f9dc..9c3d0e534 100644
--- a/tests/Composer/Test/Fixtures/installer/solver-problems.test
+++ b/tests/Composer/Test/Fixtures/installer/solver-problems.test
@@ -6,50 +6,151 @@ Test the error output of solver problems.
{
"type": "package",
"package": [
+ { "name": "package/found", "version": "2.0.0", "require": {
+ "unstable/package2": "2.*"
+ } },
+ { "name": "package/found2", "version": "2.0.0", "require": {
+ "invalid/💩package": "*"
+ } },
+ { "name": "package/found3", "version": "2.0.0", "require": {
+ "unstable/package2": "2.*"
+ } },
+ { "name": "package/found4", "version": "2.0.0", "require": {
+ "non-existent/pkg2": "1.*"
+ } },
+ { "name": "package/found5", "version": "2.0.0", "require": {
+ "requirer/pkg": "1.*"
+ } },
+ { "name": "package/found6", "version": "2.0.0", "require": {
+ "stable-requiree-excluded/pkg2": "1.0.1"
+ } },
+ { "name": "package/found7", "version": "2.0.0", "require": {
+ "php-64bit": "1.0.1"
+ } },
+ { "name": "conflict/requirer", "version": "2.0.0", "require": {
+ "conflict/dep": "1.0.0"
+ } },
+ { "name": "conflict/requirer2", "version": "2.0.0", "require": {
+ "conflict/dep": "2.0.0"
+ } },
+ { "name": "conflict/dep", "version": "1.0.0" },
+ { "name": "conflict/dep", "version": "2.0.0" },
{ "name": "unstable/package", "version": "2.0.0-alpha" },
{ "name": "unstable/package", "version": "1.0.0" },
- { "name": "requirer/pkg", "version": "1.0.0", "require": {"dependency/pkg": "1.0.0" } },
+ { "name": "unstable/package2", "version": "2.0.0-alpha" },
+ { "name": "unstable/package2", "version": "1.0.0" },
+ { "name": "requirer/pkg", "version": "1.0.0", "require": {
+ "dependency/pkg": "1.0.0",
+ "dependency/unstable-pkg": "1.0.0-dev"
+ } },
{ "name": "dependency/pkg", "version": "2.0.0" },
{ "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "dependency/unstable-pkg", "version": "1.0.0-dev" },
{ "name": "stable-requiree-excluded/pkg", "version": "1.0.1" },
- { "name": "stable-requiree-excluded/pkg", "version": "1.0.0" }
+ { "name": "stable-requiree-excluded/pkg", "version": "1.0.0" },
+ { "name": "api/provider", "description": "Provides the missing API", "version": "1.0.0", "provide": { "missing/provided-api": "1.*" } }
]
}
],
"require": {
+ "package/found": "2.*",
+ "package/found2": "2.*",
+ "package/found3": "2.*",
+ "package/found4": "2.*",
+ "package/found5": "2.*",
+ "package/found6": "2.*",
+ "package/found7": "2.*",
+ "missing/provided-api": "2.*",
+ "conflict/requirer": "2.*",
+ "conflict/requirer2": "2.*",
"unstable/package": "2.*",
- "bogus/pkg": "1.*",
+ "non-existent/pkg": "1.*",
"requirer/pkg": "1.*",
"dependency/pkg": "2.*",
- "stable-requiree-excluded/pkg": "1.0.1"
+ "stable-requiree-excluded/pkg": "1.0.1",
+ "lib-xml": "1002.*",
+ "lib-icu": "1001.*",
+ "ext-xml": "1002.*",
+ "php": "1"
}
}
--INSTALLED--
[
- { "name": "stable-requiree-excluded/pkg", "version": "1.0.0" }
+ { "name": "stable-requiree-excluded/pkg", "version": "1.0.0" },
+ { "name": "stable-requiree-excluded/pkg2", "version": "1.0.0" }
]
+--LOCK--
+{
+ "packages": [
+ { "name": "stable-requiree-excluded/pkg", "version": "1.0.0" },
+ { "name": "stable-requiree-excluded/pkg2", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+
--RUN--
-update unstable/package requirer/pkg dependency/pkg
+update unstable/package requirer/pkg dependency/pkg conflict/requirer
--EXPECT-EXIT-CODE--
2
--EXPECT-OUTPUT--
Loading composer repositories with package information
-Updating dependencies (including require-dev)
+Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- - The requested package unstable/package 2.* exists as unstable/package[1.0.0] but these are rejected by your constraint.
+ - Root composer.json requires missing/provided-api 2.*, it could not be found in any version, but the following packages provide it:
+ - api/provider Provides the missing API
+ Consider requiring one of these to satisfy the missing/provided-api requirement.
Problem 2
- - The requested package bogus/pkg could not be found in any version, there may be a typo in the package name.
+ - Root composer.json requires unstable/package 2.*, found unstable/package[2.0.0-alpha] but it does not match your minimum-stability.
Problem 3
- - The requested package stable-requiree-excluded/pkg (installed at 1.0.0, required as 1.0.1) is satisfiable by stable-requiree-excluded/pkg[1.0.0] but these conflict with your requirements or minimum-stability.
+ - Root composer.json requires non-existent/pkg, it could not be found in any version, there may be a typo in the package name.
Problem 4
- - Installation request for requirer/pkg 1.* -> satisfiable by requirer/pkg[1.0.0].
- - requirer/pkg 1.0.0 requires dependency/pkg 1.0.0 -> satisfiable by dependency/pkg[1.0.0] but these conflict with your requirements or minimum-stability.
+ - Root composer.json requires stable-requiree-excluded/pkg 1.0.1, found stable-requiree-excluded/pkg[1.0.1] but the package is fixed to 1.0.0 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
+ Problem 5
+ - Root composer.json requires linked library lib-xml 1002.* but it has the wrong version installed or is missing from your system, make sure to load the extension providing it.
+ Problem 6
+ - Root composer.json requires linked library lib-icu 1001.* but it has the wrong version installed, try upgrading the intl extension.
+ Problem 7
+ - Root composer.json requires PHP extension ext-xml 1002.* but it has the wrong version (%s) installed. Install or enable PHP's xml extension.
+ Problem 8
+ - Root composer.json requires php 1 but your php version (%s) does not satisfy that requirement.
+ Problem 9
+ - Root composer.json requires package/found 2.* -> satisfiable by package/found[2.0.0].
+ - package/found 2.0.0 requires unstable/package2 2.* -> found unstable/package2[2.0.0-alpha] but it does not match your minimum-stability.
+ Problem 10
+ - Root composer.json requires package/found2 2.* -> satisfiable by package/found2[2.0.0].
+ - package/found2 2.0.0 requires invalid/💩package * -> could not be found, it looks like its name is invalid, "💩" is not allowed in package names.
+ Problem 11
+ - Root composer.json requires package/found3 2.* -> satisfiable by package/found3[2.0.0].
+ - package/found3 2.0.0 requires unstable/package2 2.* -> found unstable/package2[2.0.0-alpha] but it does not match your minimum-stability.
+ Problem 12
+ - Root composer.json requires package/found4 2.* -> satisfiable by package/found4[2.0.0].
+ - package/found4 2.0.0 requires non-existent/pkg2 1.* -> could not be found in any version, there may be a typo in the package name.
+ Problem 13
+ - Root composer.json requires package/found6 2.* -> satisfiable by package/found6[2.0.0].
+ - package/found6 2.0.0 requires stable-requiree-excluded/pkg2 1.0.1 -> found stable-requiree-excluded/pkg2[1.0.0] but it does not match your constraint.
+ Problem 14
+ - Root composer.json requires package/found7 2.* -> satisfiable by package/found7[2.0.0].
+ - package/found7 2.0.0 requires php-64bit 1.0.1 -> your php-64bit version (%s) does not satisfy that requirement.
+ Problem 15
+ - Root composer.json requires requirer/pkg 1.* -> satisfiable by requirer/pkg[1.0.0].
+ - requirer/pkg 1.0.0 requires dependency/pkg 1.0.0 -> found dependency/pkg[1.0.0] but it conflicts with your root composer.json require (2.*).
+ Problem 16
+ - requirer/pkg 1.0.0 requires dependency/pkg 1.0.0 -> found dependency/pkg[1.0.0] but it conflicts with your root composer.json require (2.*).
+ - package/found5 2.0.0 requires requirer/pkg 1.* -> satisfiable by requirer/pkg[1.0.0].
+ - Root composer.json requires package/found5 2.* -> satisfiable by package/found5[2.0.0].
Potential causes:
- A typo in the package name
@@ -58,6 +159,9 @@ Potential causes:
- It's a private package and you forgot to add a custom repository to find it
Read for further common problems.
+ To enable extensions, verify that they are enabled in your .ini files:
+__inilist__
+ You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.
--EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/suggest-installed.test b/tests/Composer/Test/Fixtures/installer/suggest-installed.test
index 198203ce9..413ac8665 100644
--- a/tests/Composer/Test/Fixtures/installer/suggest-installed.test
+++ b/tests/Composer/Test/Fixtures/installer/suggest-installed.test
@@ -17,12 +17,16 @@ Suggestions are not displayed for installed packages
}
}
--RUN--
-install
+update
--EXPECT-OUTPUT--
Loading composer repositories with package information
-Updating dependencies (including require-dev)
-Package operations: 2 installs, 0 updates, 0 removals
+Updating dependencies
+Lock file operations: 2 installs, 0 updates, 0 removals
+ - Locking a/a (1.0.0)
+ - Locking b/b (1.0.0)
Writing lock file
+Installing dependencies from lock file (including require-dev)
+Package operations: 2 installs, 0 updates, 0 removals
Generating autoload files
--EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/suggest-prod-nolock.test b/tests/Composer/Test/Fixtures/installer/suggest-prod-nolock.test
new file mode 100644
index 000000000..b4d1fbb09
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/suggest-prod-nolock.test
@@ -0,0 +1,32 @@
+--TEST--
+Suggestions are displayed even in non-dev mode for new suggesters installed when updating the lock file
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "a/a", "version": "1.0.0", "suggest": { "b/b": "an obscure reason" } }
+ ]
+ }
+ ],
+ "require": {
+ "a/a": "1.0.0"
+ }
+}
+--RUN--
+install --no-dev
+--EXPECT-OUTPUT--
+No lock file found. Updating dependencies instead of installing from lock file. Use composer update over composer install if you do not have a lock file.
+Loading composer repositories with package information
+Updating dependencies
+Lock file operations: 1 install, 0 updates, 0 removals
+ - Locking a/a (1.0.0)
+Writing lock file
+Installing dependencies from lock file
+Package operations: 1 install, 0 updates, 0 removals
+1 package suggestions were added by new dependencies, use `composer suggest` to see details.
+Generating autoload files
+
+--EXPECT--
+Installing a/a (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/suggest-prod.test b/tests/Composer/Test/Fixtures/installer/suggest-prod.test
index 40546f8d0..ed2023504 100644
--- a/tests/Composer/Test/Fixtures/installer/suggest-prod.test
+++ b/tests/Composer/Test/Fixtures/installer/suggest-prod.test
@@ -1,5 +1,5 @@
--TEST--
-Suggestions are not displayed in non-dev mode
+Suggestions are not displayed for when not updating the lock file
--COMPOSER--
{
"repositories": [
@@ -14,13 +14,26 @@ Suggestions are not displayed in non-dev mode
"a/a": "1.0.0"
}
}
+--LOCK--
+{
+ "packages": [
+ { "name": "a/a", "version": "1.0.0", "suggest": { "b/b": "an obscure reason" } }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
--RUN--
-install --no-dev
+install
--EXPECT-OUTPUT--
-Loading composer repositories with package information
-Updating dependencies
+Installing dependencies from lock file (including require-dev)
+Verifying lock file contents can be installed on current platform.
Package operations: 1 install, 0 updates, 0 removals
-Writing lock file
Generating autoload files
--EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/suggest-replaced.test b/tests/Composer/Test/Fixtures/installer/suggest-replaced.test
index f18054d74..a0e90332e 100644
--- a/tests/Composer/Test/Fixtures/installer/suggest-replaced.test
+++ b/tests/Composer/Test/Fixtures/installer/suggest-replaced.test
@@ -17,12 +17,16 @@ Suggestions are not displayed for packages if they are replaced
}
}
--RUN--
-install
+update
--EXPECT-OUTPUT--
Loading composer repositories with package information
-Updating dependencies (including require-dev)
-Package operations: 2 installs, 0 updates, 0 removals
+Updating dependencies
+Lock file operations: 2 installs, 0 updates, 0 removals
+ - Locking a/a (1.0.0)
+ - Locking c/c (1.0.0)
Writing lock file
+Installing dependencies from lock file (including require-dev)
+Package operations: 2 installs, 0 updates, 0 removals
Generating autoload files
--EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test b/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test
index ae5ff36e3..fff3e19d6 100644
--- a/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test
+++ b/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test
@@ -17,11 +17,15 @@ Suggestions are displayed
--RUN--
install
--EXPECT-OUTPUT--
+No lock file found. Updating dependencies instead of installing from lock file. Use composer update over composer install if you do not have a lock file.
Loading composer repositories with package information
-Updating dependencies (including require-dev)
-Package operations: 1 install, 0 updates, 0 removals
-a/a suggests installing b/b (an obscure reason)
+Updating dependencies
+Lock file operations: 1 install, 0 updates, 0 removals
+ - Locking a/a (1.0.0)
Writing lock file
+Installing dependencies from lock file (including require-dev)
+Package operations: 1 install, 0 updates, 0 removals
+1 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating autoload files
--EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/unbounded-conflict-does-not-match-dev-master.test b/tests/Composer/Test/Fixtures/installer/unbounded-conflict-does-not-match-dev-master.test
new file mode 100644
index 000000000..6997e5a77
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/unbounded-conflict-does-not-match-dev-master.test
@@ -0,0 +1,31 @@
+--TEST--
+Test that a conflict against >=5 does not include dev-master or other dev-x
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "conflicter/pkg", "version": "1.0.0", "conflict": { "victim/pkg": ">=5", "victim/pkg2": ">=5" } },
+ { "name": "victim/pkg", "version": "dev-master" },
+ { "name": "victim/pkg2", "version": "dev-foo" }
+ ]
+ }
+ ],
+ "require": {
+ "conflicter/pkg": "1.0.0",
+ "victim/pkg": "*",
+ "victim/pkg2": "*"
+ },
+ "minimum-stability": "dev"
+}
+
+
+--RUN--
+update
+
+--EXPECT--
+Installing conflicter/pkg (1.0.0)
+Installing victim/pkg (dev-master)
+Marking victim/pkg (9999999-dev) as installed, alias of victim/pkg (dev-master)
+Installing victim/pkg2 (dev-foo)
diff --git a/tests/Composer/Test/Fixtures/installer/update-alias-lock.test b/tests/Composer/Test/Fixtures/installer/update-alias-lock.test
index f4f5e98eb..5aceda8ea 100644
--- a/tests/Composer/Test/Fixtures/installer/update-alias-lock.test
+++ b/tests/Composer/Test/Fixtures/installer/update-alias-lock.test
@@ -29,20 +29,6 @@ Update aliased package does not mess up the lock file
},
"minimum-stability": "dev"
}
---LOCK--
-{
- "_": "outdated lock file, should not have to be loaded in an update",
- "packages": [
- { "package": "a/a", "version": "dev-master", "source-reference": "1234" },
- { "package": "a/a", "version": "dev-master", "alias-pretty-version": "1.0.x-dev", "alias-version": "1.0.9999999.9999999-dev" }
- ],
- "packages-dev": null,
- "aliases": [],
- "minimum-stability": "dev",
- "stability-flags": [],
- "prefer-stable": false,
- "prefer-lowest": false
-}
--INSTALLED--
[
{
@@ -73,4 +59,4 @@ update
"platform-dev": []
}
--EXPECT--
-Updating a/a (dev-master 1234) to a/a (dev-master master)
+Upgrading a/a (dev-master 1234 => dev-master master)
diff --git a/tests/Composer/Test/Fixtures/installer/update-alias.test b/tests/Composer/Test/Fixtures/installer/update-alias.test
index c1020e33c..8da3d4d23 100644
--- a/tests/Composer/Test/Fixtures/installer/update-alias.test
+++ b/tests/Composer/Test/Fixtures/installer/update-alias.test
@@ -33,5 +33,5 @@ Update aliased package to non-aliased version
--RUN--
update
--EXPECT--
-Updating a/a (dev-master master) to a/a (dev-foo foo)
-Marking a/a (1.0.x-dev master) as uninstalled, alias of a/a (dev-master master)
\ No newline at end of file
+Upgrading a/a (dev-master master => dev-foo foo)
+Marking a/a (1.0.x-dev master) as uninstalled, alias of a/a (dev-master master)
diff --git a/tests/Composer/Test/Fixtures/installer/update-all-dry-run.test b/tests/Composer/Test/Fixtures/installer/update-all-dry-run.test
index cca859e9f..3d93d23bd 100644
--- a/tests/Composer/Test/Fixtures/installer/update-all-dry-run.test
+++ b/tests/Composer/Test/Fixtures/installer/update-all-dry-run.test
@@ -35,6 +35,15 @@ Updates updateable packages in dry-run mode
]
--RUN--
update --dry-run
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+Lock file operations: 3 installs, 0 updates, 0 removals
+ - Locking a/a (1.0.1)
+ - Locking a/b (2.0.0)
+ - Locking a/c (1.0.0)
+Installing dependencies from lock file (including require-dev)
+Package operations: 0 installs, 2 updates, 0 removals
+ - Upgrading a/a (1.0.0 => 1.0.1)
+ - Upgrading a/b (1.0.0 => 2.0.0)
--EXPECT--
-Updating a/a (1.0.0) to a/a (1.0.1)
-Updating a/b (1.0.0) to a/b (2.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-all.test b/tests/Composer/Test/Fixtures/installer/update-all.test
index a9bb435a1..893f9717c 100644
--- a/tests/Composer/Test/Fixtures/installer/update-all.test
+++ b/tests/Composer/Test/Fixtures/installer/update-all.test
@@ -36,5 +36,5 @@ Updates updateable packages
--RUN--
update
--EXPECT--
-Updating a/a (1.0.0) to a/a (1.0.1)
-Updating a/b (1.0.0) to a/b (2.0.0)
+Upgrading a/a (1.0.0 => 1.0.1)
+Upgrading a/b (1.0.0 => 2.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-locked-require.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-locked-require.test
new file mode 100644
index 000000000..0f009ae6f
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-locked-require.test
@@ -0,0 +1,53 @@
+--TEST--
+Update with a package whitelist only updates those packages if they are not present in composer.json
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "whitelisted/pkg", "version": "1.1.0", "require": { "dependency/pkg": "1.1.0", "fixed/dependency": "1.*" } },
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0", "fixed/dependency": "1.*" } },
+ { "name": "dependency/pkg", "version": "1.1.0" },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "fixed/dependency", "version": "1.1.0", "require": { "fixed/sub-dependency": "1.*" } },
+ { "name": "fixed/dependency", "version": "1.0.0", "require": { "fixed/sub-dependency": "1.*" } },
+ { "name": "fixed/sub-dependency", "version": "1.1.0" },
+ { "name": "fixed/sub-dependency", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "whitelisted/pkg": "1.*",
+ "fixed/dependency": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0", "fixed/dependency": "1.*" } },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "fixed/dependency", "version": "1.0.0", "require": { "fixed/sub-dependency": "1.*" } },
+ { "name": "fixed/sub-dependency", "version": "1.0.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0", "fixed/dependency": "1.*" } },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "fixed/dependency", "version": "1.0.0", "require": { "fixed/sub-dependency": "1.*" } },
+ { "name": "fixed/sub-dependency", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update whitelisted/pkg dependency/pkg
+--EXPECT--
+Upgrading dependency/pkg (1.0.0 => 1.1.0)
+Upgrading whitelisted/pkg (1.0.0 => 1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns-with-all-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns-with-all-dependencies.test
new file mode 100644
index 000000000..95fd639f2
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns-with-all-dependencies.test
@@ -0,0 +1,65 @@
+--TEST--
+Update with a package whitelist pattern and all-dependencies flag updates packages and their dependencies, even if defined as root dependency, matching the pattern
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "fixed/pkg", "version": "1.1.0" },
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.1.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component2", "version": "1.1.0", "require": { "dependency/pkg": "1.*" } },
+ { "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.*" } },
+ { "name": "dependency/pkg", "version": "1.1.0" },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.1.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.1.0" },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "fixed/pkg": "1.*",
+ "whitelisted/pkg-component1": "1.*",
+ "whitelisted/pkg-component2": "1.*",
+ "dependency/pkg": "1.*",
+ "unrelated/pkg": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update whitelisted/pkg-* --with-all-dependencies
+--EXPECT--
+Upgrading whitelisted/pkg-component1 (1.0.0 => 1.1.0)
+Upgrading dependency/pkg (1.0.0 => 1.1.0)
+Upgrading whitelisted/pkg-component2 (1.0.0 => 1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns-with-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns-with-dependencies.test
new file mode 100644
index 000000000..d40a924ab
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns-with-dependencies.test
@@ -0,0 +1,67 @@
+--TEST--
+Update with a package whitelist only updates those packages and their dependencies matching the pattern but no dependencies defined as roo package
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "fixed/pkg", "version": "1.1.0" },
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.1.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component2", "version": "1.1.0", "require": { "dependency/pkg": "1.*", "root/pkg-dependency": "1.*" } },
+ { "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.*", "root/pkg-dependency": "1.*" } },
+ { "name": "dependency/pkg", "version": "1.1.0" },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "root/pkg-dependency", "version": "1.1.0" },
+ { "name": "root/pkg-dependency", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.1.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.1.0" },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "fixed/pkg": "1.*",
+ "whitelisted/pkg-component1": "1.*",
+ "whitelisted/pkg-component2": "1.*",
+ "root/pkg-dependency": "1.*",
+ "unrelated/pkg": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "root/pkg-dependency", "version": "1.0.0" },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "root/pkg-dependency", "version": "1.0.0" },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": {"a/a":20},
+ "prefer-stable": false,
+ "prefer-lowest": false
+}
+--RUN--
+update whitelisted/pkg-* --with-dependencies
+--EXPECT--
+Upgrading whitelisted/pkg-component1 (1.0.0 => 1.1.0)
+Upgrading dependency/pkg (1.0.0 => 1.1.0)
+Upgrading whitelisted/pkg-component2 (1.0.0 => 1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns-with-root-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns-with-root-dependencies.test
new file mode 100644
index 000000000..55a07b118
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns-with-root-dependencies.test
@@ -0,0 +1,77 @@
+--TEST--
+Update with a package whitelist only updates those packages and their dependencies matching the pattern
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "fixed/pkg", "version": "1.1.0" },
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.1.0", "require": { "whitelisted/pkg-component2": "1.1.0" } },
+ { "name": "whitelisted/pkg-component1", "version": "1.0.0", "require": { "whitelisted/pkg-component2": "1.0.0" } },
+ { "name": "whitelisted/pkg-component2", "version": "1.1.0", "require": { "dependency/pkg": "1.1.0", "whitelisted/pkg-component5": "1.0.0" } },
+ { "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "whitelisted/pkg-component3", "version": "1.1.0", "require": { "whitelisted/pkg-component4": "1.1.0" } },
+ { "name": "whitelisted/pkg-component3", "version": "1.0.0", "require": { "whitelisted/pkg-component4": "1.0.0" } },
+ { "name": "whitelisted/pkg-component4", "version": "1.1.0" },
+ { "name": "whitelisted/pkg-component4", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component5", "version": "1.1.0" },
+ { "name": "whitelisted/pkg-component5", "version": "1.0.0" },
+ { "name": "dependency/pkg", "version": "1.1.0" },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.1.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.1.0" },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "fixed/pkg": "1.*",
+ "whitelisted/pkg-component1": "1.*",
+ "whitelisted/pkg-component2": "1.*",
+ "whitelisted/pkg-component3": "1.0.0",
+ "unrelated/pkg": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.0.0", "require": { "whitelisted/pkg-component2": "1.0.0" } },
+ { "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "whitelisted/pkg-component3", "version": "1.0.0", "require": { "whitelisted/pkg-component4": "1.0.0" } },
+ { "name": "whitelisted/pkg-component4", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component5", "version": "1.0.0" },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.0.0", "require": { "whitelisted/pkg-component2": "1.0.0" } },
+ { "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "whitelisted/pkg-component3", "version": "1.0.0", "require": { "whitelisted/pkg-component4": "1.0.0" } },
+ { "name": "whitelisted/pkg-component4", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component5", "version": "1.0.0" },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update whitelisted/pkg-* foobar --with-dependencies
+--EXPECT--
+Upgrading dependency/pkg (1.0.0 => 1.1.0)
+Upgrading whitelisted/pkg-component2 (1.0.0 => 1.1.0)
+Upgrading whitelisted/pkg-component1 (1.0.0 => 1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns-without-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns-without-dependencies.test
new file mode 100644
index 000000000..6cd1d7778
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns-without-dependencies.test
@@ -0,0 +1,61 @@
+--TEST--
+Update with a package whitelist only updates those packages matching the pattern
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "fixed/pkg", "version": "1.1.0" },
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.1.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component2", "version": "1.1.0", "require": { "dependency/pkg": "1.*" } },
+ { "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.*" } },
+ { "name": "dependency/pkg", "version": "1.1.0" },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.1.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.1.0" },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "fixed/pkg": "1.*",
+ "whitelisted/pkg-component1": "1.*",
+ "whitelisted/pkg-component2": "1.*",
+ "unrelated/pkg": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component1", "version": "1.0.0" },
+ { "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": {"a/a":20},
+ "prefer-stable": false,
+ "prefer-lowest": false
+}
+--RUN--
+update whitelisted/pkg-*
+--EXPECT--
+Upgrading whitelisted/pkg-component1 (1.0.0 => 1.1.0)
+Upgrading whitelisted/pkg-component2 (1.0.0 => 1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns.test
new file mode 100644
index 000000000..738f0af74
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-patterns.test
@@ -0,0 +1,69 @@
+--TEST--
+Update with a package whitelist only updates those corresponding to the pattern
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "vendor/test-package", "version": "2.0" },
+ { "name": "vendor/not-me", "version": "2.0" },
+ { "name": "exact/test-package", "version": "2.0" },
+ { "name": "notexact/testpackage", "version": "2.0" },
+ { "name": "all/package1", "version": "2.0" },
+ { "name": "all/package2", "version": "2.0" },
+ { "name": "another/another", "version": "2.0" },
+ { "name": "no/regexp", "version": "2.0" }
+ ]
+ }
+ ],
+ "require": {
+ "vendor/test-package": "*.*",
+ "vendor/not-me": "*.*",
+ "exact/test-package": "*.*",
+ "notexact/testpackage": "*.*",
+ "all/package1": "*.*",
+ "all/package2": "*.*",
+ "another/another": "*.*",
+ "no/regexp": "*.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "vendor/test-package", "version": "1.0" },
+ { "name": "vendor/not-me", "version": "1.0" },
+ { "name": "exact/test-package", "version": "1.0" },
+ { "name": "notexact/testpackage", "version": "1.0" },
+ { "name": "all/package1", "version": "1.0" },
+ { "name": "all/package2", "version": "1.0" },
+ { "name": "another/another", "version": "1.0" },
+ { "name": "no/regexp", "version": "1.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "vendor/test-package", "version": "1.0" },
+ { "name": "vendor/not-me", "version": "1.0" },
+ { "name": "exact/test-package", "version": "1.0" },
+ { "name": "notexact/testpackage", "version": "1.0" },
+ { "name": "all/package1", "version": "1.0" },
+ { "name": "all/package2", "version": "1.0" },
+ { "name": "another/another", "version": "1.0" },
+ { "name": "no/regexp", "version": "1.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update vendor/Test* exact/test-package notexact/Test all/* no/reg.?xp
+--EXPECT--
+Upgrading all/package1 (1.0 => 2.0)
+Upgrading all/package2 (1.0 => 2.0)
+Upgrading exact/test-package (1.0 => 2.0)
+Upgrading vendor/test-package (1.0 => 2.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-reads-lock.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-reads-lock.test
similarity index 90%
rename from tests/Composer/Test/Fixtures/installer/update-whitelist-reads-lock.test
rename to tests/Composer/Test/Fixtures/installer/update-allow-list-reads-lock.test
index c84f0e65d..ef7ca56c0 100644
--- a/tests/Composer/Test/Fixtures/installer/update-whitelist-reads-lock.test
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-reads-lock.test
@@ -28,7 +28,7 @@ Limited update takes rules from lock if available, and not from the installed re
{ "name": "toupdate/installed", "version": "1.0.0" },
{ "name": "toupdate/notinstalled", "version": "1.0.0" }
],
- "packages-dev": null,
+ "packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
@@ -43,6 +43,6 @@ Limited update takes rules from lock if available, and not from the installed re
--RUN--
update toupdate/installed
--EXPECT--
-Updating toupdate/installed (1.0.0) to toupdate/installed (1.1.0)
-Updating old/installed (0.9.0) to old/installed (1.0.0)
+Upgrading old/installed (0.9.0 => 1.0.0)
+Upgrading toupdate/installed (1.0.0 => 1.1.0)
Installing toupdate/notinstalled (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-removes-unused.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-removes-unused.test
new file mode 100644
index 000000000..9360bc2f6
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-removes-unused.test
@@ -0,0 +1,48 @@
+--TEST--
+Update with a package whitelist removes unused packages
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "whitelisted/pkg", "version": "1.1.0" },
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "fixed/dependency": "1.0.0", "old/dependency": "1.0.0" } },
+ { "name": "fixed/dependency", "version": "1.1.0" },
+ { "name": "fixed/dependency", "version": "1.0.0" },
+ { "name": "old/dependency", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "whitelisted/pkg": "1.*",
+ "fixed/dependency": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "old/dependency": "1.0.0", "fixed/dependency": "1.0.0" } },
+ { "name": "fixed/dependency", "version": "1.0.0" },
+ { "name": "old/dependency", "version": "1.0.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "old/dependency": "1.0.0", "fixed/dependency": "1.0.0" } },
+ { "name": "fixed/dependency", "version": "1.0.0" },
+ { "name": "old/dependency", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update --with-dependencies whitelisted/pkg
+--EXPECT--
+Removing old/dependency (1.0.0)
+Upgrading whitelisted/pkg (1.0.0 => 1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-require-new-replace.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-require-new-replace.test
new file mode 100644
index 000000000..dc6e9aa5f
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-require-new-replace.test
@@ -0,0 +1,55 @@
+--TEST--
+If a new requirement cannot be installed on a partial update due to replace, there should be a suggestion to use --with-all-dependencies
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "*" } },
+ { "name": "current/dep", "version": "1.0.0" },
+ { "name": "new/pkg", "version": "1.0.0", "replace": { "current/dep": "1.0.0" } }
+ ]
+ }
+ ],
+ "require": {
+ "current/pkg": "1.*",
+ "new/pkg": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "*" } },
+ { "name": "current/dep", "version": "1.0.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "*" } },
+ { "name": "current/dep", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update new/pkg
+--EXPECT-EXIT-CODE--
+2
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+Your requirements could not be resolved to an installable set of packages.
+
+ Problem 1
+ - current/dep is locked to version 1.0.0 and an update of this package was not requested.
+ - new/pkg 1.0.0 cannot be installed as that would require removing current/dep 1.0.0. new/pkg replaces current/dep and thus cannot coexist with it.
+ - Root composer.json requires new/pkg 1.* -> satisfiable by new/pkg[1.0.0].
+
+Use the option --with-all-dependencies to allow updates and removals for packages currently locked to specific versions.
+--EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-warns-non-existing-patterns.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-warns-non-existing-patterns.test
new file mode 100644
index 000000000..d4d258112
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-warns-non-existing-patterns.test
@@ -0,0 +1,58 @@
+--TEST--
+Verify that partial updates warn about using patterns in the argument which have no matches
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "a/a", "version": "1.0.0" },
+ { "name": "b/b", "version": "1.0.0" },
+ { "name": "b/b", "version": "1.1.0" }
+ ]
+ }
+ ],
+ "require": {
+ "a/a": "~1.0",
+ "b/b": "~1.0"
+ }
+}
+
+--INSTALLED--
+[
+ { "name": "a/a", "version": "1.0.0" },
+ { "name": "b/b", "version": "1.0.0" }
+]
+
+--LOCK--
+{
+ "packages": [
+ { "name": "a/a", "version": "1.0.0" },
+ { "name": "b/b", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update b/b foo/bar baz/* --with-dependencies
+
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+Package "foo/bar" listed for update is not locked.
+Pattern "baz/*" listed for update does not match any locked packages.
+Lock file operations: 0 installs, 1 update, 0 removals
+ - Upgrading b/b (1.0.0 => 1.1.0)
+Writing lock file
+Installing dependencies from lock file (including require-dev)
+Package operations: 0 installs, 1 update, 0 removals
+Generating autoload files
+
+--EXPECT--
+Upgrading b/b (1.0.0 => 1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-alias.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-alias.test
new file mode 100644
index 000000000..2b68c6c69
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-alias.test
@@ -0,0 +1,99 @@
+--TEST--
+Verify that a partial update with deps correctly keeps track of all aliases.
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" } },
+ { "name": "current/dep", "version": "dev-master", "extra": {"branch-alias": {"dev-master": "1.0.x-dev"}}},
+ { "name": "current/dep", "version": "1.0.0" },
+ { "name": "current/dep", "version": "1.1.0", "require": {"current/dep2": "*"} },
+ { "name": "current/dep", "version": "1.2.0" },
+ { "name": "current/dep2", "version": "dev-foo", "extra": {"branch-alias": {"dev-foo": "1.0.x-dev"}}},
+ { "name": "current/dep2", "version": "dev-master", "extra": {"branch-alias": {"dev-master": "2.x-dev"}}},
+ { "name": "new/pkg", "version": "1.0.0", "require": { "current/dep": "^1.1", "current/dep2": "^1.1"} },
+ { "name": "new/pkg", "version": "1.1.0", "require": { "current/dep": "^1.2" } }
+ ]
+ }
+ ],
+ "require": {
+ "current/dep": "dev-master as 1.1.0",
+ "current/dep2": "dev-master as 1.1.2",
+ "current/pkg": "1.0.0 as 2.0.0",
+ "new/pkg": "1.*"
+ },
+ "minimum-stability": "dev"
+}
+--INSTALLED--
+[
+ { "name": "current/dep", "version": "dev-master", "extra": {"branch-alias": {"dev-master": "1.0.x-dev"}}},
+ { "name": "current/dep2", "version": "dev-foo", "extra": {"branch-alias": {"dev-foo": "1.0.x-dev"}}},
+ { "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" } }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "current/dep", "version": "dev-master", "extra": {"branch-alias": {"dev-master": "1.0.x-dev"}}, "type": "library"},
+ { "name": "current/dep2", "version": "dev-foo", "extra": {"branch-alias": {"dev-foo": "1.0.x-dev"}}, "type": "library"},
+ { "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" } }
+ ],
+ "packages-dev": [],
+ "aliases": [
+ {
+ "alias": "1.1.0",
+ "alias_normalized": "1.1.0.0",
+ "version": "dev-master",
+ "package": "current/dep"
+ }
+ ],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update new/pkg --with-all-dependencies
+--EXPECT-LOCK--
+{
+ "packages": [
+ { "name": "current/dep", "version": "dev-master", "extra": {"branch-alias": {"dev-master": "1.0.x-dev"}}, "type": "library"},
+ { "name": "current/dep2", "version": "dev-master", "extra": {"branch-alias": {"dev-master": "2.x-dev"}}, "type": "library"},
+ { "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" }, "type": "library"},
+ { "name": "new/pkg", "version": "1.0.0", "require": { "current/dep": "^1.1", "current/dep2": "^1.1"}, "type": "library"}
+ ],
+ "packages-dev": [],
+ "aliases": [
+ {
+ "alias": "1.1.0",
+ "alias_normalized": "1.1.0.0",
+ "version": "dev-master",
+ "package": "current/dep"
+ },
+ {
+ "alias": "1.1.2",
+ "alias_normalized": "1.1.2.0",
+ "version": "dev-master",
+ "package": "current/dep2"
+ }
+ ],
+ "minimum-stability": "dev",
+ "stability-flags": {
+ "current/dep": 20,
+ "current/dep2": 20
+ },
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--EXPECT--
+Marking current/dep (1.1.0) as installed, alias of current/dep (dev-master)
+Upgrading current/dep2 (dev-foo => dev-master)
+Marking current/dep2 (1.1.2) as installed, alias of current/dep2 (dev-master)
+Marking current/dep2 (2.x-dev) as installed, alias of current/dep2 (dev-master)
+Installing new/pkg (1.0.0)
+Marking current/dep2 (1.0.x-dev) as uninstalled, alias of current/dep2 (dev-foo)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-new-requirement.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-new-requirement.test
new file mode 100644
index 000000000..ff1257498
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-new-requirement.test
@@ -0,0 +1,49 @@
+--TEST--
+When partially updating a package to a newer version and the new version has a new requirement for a package we already have installed, mark it for update
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "root/pkg1", "version": "1.0.0", "require": { "current/dep": "^1.0" } },
+ { "name": "root/pkg1", "version": "1.2.0", "require": { "current/dep": "^1.0" } },
+ { "name": "current/dep", "version": "1.0.0" },
+ { "name": "current/dep", "version": "1.2.0" },
+ { "name": "root/pkg2", "version": "1.0.0" },
+ { "name": "root/pkg2", "version": "1.2.0", "require": { "current/dep": "^1.2" } }
+ ]
+ }
+ ],
+ "require": {
+ "root/pkg1": "1.*",
+ "root/pkg2": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "root/pkg1", "version": "1.0.0", "require": { "current/dep": "^1.0" } },
+ { "name": "current/dep", "version": "1.0.0" },
+ { "name": "root/pkg2", "version": "1.0.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "root/pkg1", "version": "1.0.0", "require": { "current/dep": "^1.0" } },
+ { "name": "current/dep", "version": "1.0.0" },
+ { "name": "root/pkg2", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update root/pkg2 --with-dependencies
+--EXPECT--
+Upgrading current/dep (1.0.0 => 1.2.0)
+Upgrading root/pkg2 (1.0.0 => 1.2.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-require-new-replace-mutual.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-require-new-replace-mutual.test
new file mode 100644
index 000000000..0cb5ad97f
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-require-new-replace-mutual.test
@@ -0,0 +1,50 @@
+--TEST--
+Require a new package in the composer.json and updating with its name as an argument and with-dependencies should remove packages it replaces which are not root requirements
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "current/pkg", "version": "1.0.0", "require": { "mutual/target": "*", "mutual/target-provide": "*" } },
+ { "name": "current/dep", "version": "1.0.0", "replace": { "mutual/target": "1.0.0" } },
+ { "name": "new/pkg", "version": "1.0.0", "replace": { "mutual/target": "1.0.0" } },
+ { "name": "current/dep-provide", "version": "1.0.0", "provide": { "mutual/target-provide": "1.0.0" } },
+ { "name": "new/pkg-provide", "version": "1.0.0", "provide": { "mutual/target-provide": "1.0.0" } }
+ ]
+ }
+ ],
+ "require": {
+ "current/pkg": "1.*",
+ "new/pkg": "1.*",
+ "new/pkg-provide": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "current/pkg", "version": "1.0.0", "require": { "mutual/target": "*" } },
+ { "name": "current/dep", "version": "1.0.0", "replace": { "mutual/target": "1.0.0" } },
+ { "name": "current/dep-provide", "version": "1.0.0", "provide": { "mutual/target-provide": "1.0.0" } }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "current/pkg", "version": "1.0.0", "require": { "mutual/target": "*" } },
+ { "name": "current/dep", "version": "1.0.0", "replace": { "mutual/target": "1.0.0" } },
+ { "name": "current/dep-provide", "version": "1.0.0", "provide": { "mutual/target-provide": "1.0.0" } }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update new/pkg --with-dependencies
+--EXPECT--
+Removing current/dep (1.0.0)
+Installing new/pkg (1.0.0)
+Installing new/pkg-provide (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-require-new-replace.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-require-new-replace.test
new file mode 100644
index 000000000..8bb676e76
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-require-new-replace.test
@@ -0,0 +1,44 @@
+--TEST--
+Require a new package in the composer.json and updating with its name as an argument and with-dependencies should remove packages it replaces which are not root requirements
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "*" } },
+ { "name": "current/dep", "version": "1.0.0" },
+ { "name": "new/pkg", "version": "1.0.0", "replace": { "current/dep": "1.0.0" } }
+ ]
+ }
+ ],
+ "require": {
+ "current/pkg": "1.*",
+ "new/pkg": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "*" } },
+ { "name": "current/dep", "version": "1.0.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "*" } },
+ { "name": "current/dep", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update new/pkg --with-dependencies
+--EXPECT--
+Removing current/dep (1.0.0)
+Installing new/pkg (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-require-new.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-require-new.test
new file mode 100644
index 000000000..24eb95538
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-require-new.test
@@ -0,0 +1,48 @@
+--TEST--
+Require a new package in the composer.json and updating with its name as an argument and with-dependencies should update locked dependencies as far as possible
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" } },
+ { "name": "current/pkg", "version": "1.1.0", "require": { "current/dep": "^1.0" } },
+ { "name": "current/dep", "version": "1.0.0" },
+ { "name": "current/dep", "version": "1.1.0" },
+ { "name": "current/dep", "version": "1.2.0" },
+ { "name": "new/pkg", "version": "1.0.0", "require": { "current/dep": "^1.1" } },
+ { "name": "new/pkg", "version": "1.1.0", "require": { "current/dep": "^1.2" } }
+ ]
+ }
+ ],
+ "require": {
+ "current/pkg": "1.*",
+ "new/pkg": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" } },
+ { "name": "current/dep", "version": "1.0.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" } },
+ { "name": "current/dep", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update new/pkg --with-dependencies
+--EXPECT--
+Upgrading current/dep (1.0.0 => 1.1.0)
+Installing new/pkg (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies.test
new file mode 100644
index 000000000..079ad9d2b
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies.test
@@ -0,0 +1,56 @@
+--TEST--
+Update with a package whitelist only updates those packages and their dependencies listed as command arguments
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "fixed/pkg", "version": "1.1.0" },
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg", "version": "1.1.0", "require": { "dependency/pkg": "1.1.0" } },
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "dependency/pkg", "version": "1.1.0" },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.1.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.1.0" },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "fixed/pkg": "1.*",
+ "whitelisted/pkg": "1.*",
+ "unrelated/pkg": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": {"a/a":20},
+ "prefer-stable": false,
+ "prefer-lowest": false
+}
+--RUN--
+update whitelisted/pkg --with-dependencies
+--EXPECT--
+Upgrading dependency/pkg (1.0.0 => 1.1.0)
+Upgrading whitelisted/pkg (1.0.0 => 1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependency-conflict.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependency-conflict.test
new file mode 100644
index 000000000..299c505cb
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependency-conflict.test
@@ -0,0 +1,54 @@
+--TEST--
+Update with a package whitelist only updates whitelisted packages if no dependency conflicts
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "fixed/pkg", "version": "1.1.0" },
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg", "version": "1.1.0", "require": { "dependency/pkg": "1.1.0" } },
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "dependency/pkg", "version": "1.1.0" },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.1.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.1.0" },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "fixed/pkg": "1.*",
+ "whitelisted/pkg": "1.*",
+ "unrelated/pkg": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": {"a/a":20},
+ "prefer-stable": false,
+ "prefer-lowest": false
+}
+--RUN--
+update whitelisted/pkg
+--EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list.test b/tests/Composer/Test/Fixtures/installer/update-allow-list.test
new file mode 100644
index 000000000..a02e00c4b
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-allow-list.test
@@ -0,0 +1,57 @@
+--TEST--
+Update with a package whitelist only updates those packages listed as command arguments
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "fixed/pkg", "version": "1.1.0" },
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg", "version": "1.1.0", "require": { "dependency/pkg": "1.*" } },
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.*" } },
+ { "name": "dependency/pkg", "version": "1.1.0" },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.1.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.1.0" },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "fixed/pkg": "1.*",
+ "whitelisted/pkg": "1.*",
+ "unrelated/pkg": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency": "1.*" } },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+]
+--LOCK--
+{
+ "packages": [
+ { "name": "fixed/pkg", "version": "1.0.0" },
+ { "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.*" } },
+ { "name": "dependency/pkg", "version": "1.0.0" },
+ { "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
+ { "name": "unrelated/pkg-dependency", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update whitelisted/pkg
+--EXPECT--
+Upgrading whitelisted/pkg (1.0.0 => 1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-changes-url.test b/tests/Composer/Test/Fixtures/installer/update-changes-url.test
index 0a0d47507..4831c7705 100644
--- a/tests/Composer/Test/Fixtures/installer/update-changes-url.test
+++ b/tests/Composer/Test/Fixtures/installer/update-changes-url.test
@@ -3,10 +3,10 @@ Update updates URLs for updated packages if they have changed
a/a is dev and gets everything updated as it updates to a new ref
b/b is a tag and gets everything updated by updating the package URL directly
-c/c is a tag and not whitelisted and gets the new URL but keeps its old ref
+c/c is a tag and not whitelisted and remains unchanged
d/d is dev but with a #ref so it should get URL updated but not the reference
e/e is dev and newly installed with a #ref so it should get the correct URL but with the #111 ref
-e/e is dev but not whitelisted and gets the new URL but keeps its old ref
+f/f is dev but not whitelisted and remains unchanged
g/g is dev and installed in a different ref than the #ref, so it gets updated and gets the new URL but not the new ref
--COMPOSER--
{
@@ -98,6 +98,57 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
"transport-options": { "foo": "bar" }
}
]
+--LOCK--
+{
+ "packages": [
+ {
+ "name": "a/a", "version": "dev-master",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/a/a", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/a/a/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
+ "type": "library"
+ },
+ {
+ "name": "b/b", "version": "2.0.3",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/b/b", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/b/b/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
+ "type": "library"
+ },
+ {
+ "name": "c/c", "version": "1.0.0",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/c", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/c/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
+ "type": "library"
+ },
+ {
+ "name": "d/d", "version": "dev-master",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/d/d", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/d/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
+ "type": "library"
+ },
+ {
+ "name": "f/f", "version": "dev-master",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/f", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/f/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
+ "type": "library",
+ "transport-options": { "foo": "bar" }
+ },
+ {
+ "name": "g/g", "version": "dev-master",
+ "source": { "reference": "0000000000000000000000000000000000000000", "url": "https://github.com/g/g", "type": "git" },
+ "dist": { "reference": "0000000000000000000000000000000000000000", "url": "https://api.github.com/repos/g/g/zipball/0000000000000000000000000000000000000000", "type": "zip", "shasum": "oldsum" },
+ "type": "library",
+ "transport-options": { "foo": "bar" }
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {},
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
--EXPECT-LOCK--
{
"packages": [
@@ -115,8 +166,8 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
},
{
"name": "c/c", "version": "1.0.0",
- "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/newc", "type": "git" },
- "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/newc/tarball/1111111111111111111111111111111111111111", "type": "tar", "shasum": "newsum" },
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/c", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/c/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
"type": "library"
},
{
@@ -133,10 +184,10 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
},
{
"name": "f/f", "version": "dev-master",
- "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/newf", "type": "git" },
- "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/newf/tarball/1111111111111111111111111111111111111111", "type": "tar", "shasum": "newsum" },
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/f", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/f/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
"type": "library",
- "transport-options": { "foo": "bar2" }
+ "transport-options": { "foo": "bar" }
},
{
"name": "g/g", "version": "dev-master",
@@ -163,6 +214,8 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
--RUN--
update a/a b/b d/d g/g
--EXPECT--
+Upgrading a/a (dev-master 1111111 => dev-master 2222222)
+Upgrading b/b (2.0.3 1111111 => 2.0.3 2222222)
Installing e/e (dev-master 1111111)
-Updating a/a (dev-master 1111111) to a/a (dev-master 2222222)
-Updating g/g (dev-master 0000000) to g/g (dev-master 1111111)
+Marking e/e (9999999-dev 1111111) as installed, alias of e/e (dev-master 1111111)
+Upgrading g/g (dev-master 0000000 => dev-master 1111111)
diff --git a/tests/Composer/Test/Fixtures/installer/update-dev-ignores-providers.test b/tests/Composer/Test/Fixtures/installer/update-dev-ignores-providers.test
index e0858e054..68c02006d 100644
--- a/tests/Composer/Test/Fixtures/installer/update-dev-ignores-providers.test
+++ b/tests/Composer/Test/Fixtures/installer/update-dev-ignores-providers.test
@@ -35,4 +35,4 @@ Updating a dev package selects its newest version but no providers
--RUN--
update
--EXPECT--
-Updating a/installed (dev-master oldref) to a/installed (dev-master newref)
+Upgrading a/installed (dev-master oldref => dev-master newref)
diff --git a/tests/Composer/Test/Fixtures/installer/update-dev-packages-updates-repo-url.test b/tests/Composer/Test/Fixtures/installer/update-dev-packages-updates-repo-url.test
index f91a67cea..0a918b183 100644
--- a/tests/Composer/Test/Fixtures/installer/update-dev-packages-updates-repo-url.test
+++ b/tests/Composer/Test/Fixtures/installer/update-dev-packages-updates-repo-url.test
@@ -93,4 +93,4 @@ update
"platform-dev": []
}
--EXPECT--
-Updating a/a (dev-master oldmaster) to a/a (dev-master newmaster)
+Upgrading a/a (dev-master oldmaster => dev-master newmaster)
diff --git a/tests/Composer/Test/Fixtures/installer/update-dev-to-new-ref-picks-up-changes.test b/tests/Composer/Test/Fixtures/installer/update-dev-to-new-ref-picks-up-changes.test
index fa2146345..181e039ea 100644
--- a/tests/Composer/Test/Fixtures/installer/update-dev-to-new-ref-picks-up-changes.test
+++ b/tests/Composer/Test/Fixtures/installer/update-dev-to-new-ref-picks-up-changes.test
@@ -38,4 +38,5 @@ Updating a dev package to its latest ref should pick up new dependencies
update
--EXPECT--
Installing a/dependency (dev-master ref)
-Updating a/devpackage (dev-master oldref) to a/devpackage (dev-master newref)
+Marking a/dependency (9999999-dev ref) as installed, alias of a/dependency (dev-master ref)
+Upgrading a/devpackage (dev-master oldref => dev-master newref)
diff --git a/tests/Composer/Test/Fixtures/installer/update-downgrades-unstable-packages.test b/tests/Composer/Test/Fixtures/installer/update-downgrades-unstable-packages.test
index 0cb42e6fa..f0755d0d0 100644
--- a/tests/Composer/Test/Fixtures/installer/update-downgrades-unstable-packages.test
+++ b/tests/Composer/Test/Fixtures/installer/update-downgrades-unstable-packages.test
@@ -46,4 +46,5 @@ Downgrading from unstable to more stable package should work even if already ins
--RUN--
update
--EXPECT--
-Downgrading a/a (dev-master abcd) to a/a (1.0.0)
+Downgrading a/a (dev-master abcd => 1.0.0)
+Marking a/a (9999999-dev abcd) as uninstalled, alias of a/a (dev-master abcd)
diff --git a/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirements.test b/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirements.test
index 02f94cd6e..30e0e6112 100644
--- a/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirements.test
+++ b/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirements.test
@@ -23,4 +23,4 @@ Update in ignore-platform-reqs mode
--RUN--
update --ignore-platform-reqs
--EXPECT--
-Updating a/a (1.0.0) to a/a (1.0.1)
+Upgrading a/a (1.0.0 => 1.0.1)
diff --git a/tests/Composer/Test/Fixtures/installer/update-installed-reference.test b/tests/Composer/Test/Fixtures/installer/update-installed-reference.test
index e6814ccfe..ae0b2d537 100644
--- a/tests/Composer/Test/Fixtures/installer/update-installed-reference.test
+++ b/tests/Composer/Test/Fixtures/installer/update-installed-reference.test
@@ -28,3 +28,4 @@ Updating a dev package forcing it's reference should not do anything if the refe
--RUN--
update
--EXPECT--
+Upgrading a/a (dev-master def000 => dev-master )
diff --git a/tests/Composer/Test/Fixtures/installer/update-mirrors-changes-url.test b/tests/Composer/Test/Fixtures/installer/update-mirrors-changes-url.test
new file mode 100644
index 000000000..9bfca4c85
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-mirrors-changes-url.test
@@ -0,0 +1,204 @@
+--TEST--
+Update mirrors updates URLs for all packages if they have changed without updating versions
+
+a/a is dev and gets everything updated as it updates to a new ref
+b/b is a tag and gets everything updated by updating the package URL directly
+c/c is a tag and not whitelisted and gets the new URL but keeps its old ref
+d/d is dev but with a #ref so it should get URL updated but not the reference
+e/e is dev and newly installed with a #ref so it should get the correct URL but with the #111 ref
+e/e is dev but not whitelisted and gets the new URL but keeps its old ref
+g/g is dev and installed in a different ref than the #ref, so it gets updated and gets the new URL but not the new ref
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ {
+ "name": "a/a", "version": "dev-master",
+ "source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/a/newa", "type": "git" },
+ "dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/a/newa/zipball/2222222222222222222222222222222222222222", "type": "zip" }
+ },
+ {
+ "name": "b/b", "version": "2.0.3",
+ "source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/b/newb", "type": "git" },
+ "dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/b/newb/zipball/2222222222222222222222222222222222222222", "type": "zip" }
+ },
+ {
+ "name": "c/c", "version": "1.0.0",
+ "source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/c/newc", "type": "git" },
+ "dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/c/newc/zipball/2222222222222222222222222222222222222222", "type": "zip" }
+ },
+ {
+ "name": "d/d", "version": "dev-master",
+ "source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/d/newd", "type": "git" },
+ "dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/d/newd/zipball/2222222222222222222222222222222222222222", "type": "zip" }
+ },
+ {
+ "name": "e/e", "version": "dev-master",
+ "source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/e/newe", "type": "git" },
+ "dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/e/newe/zipball/2222222222222222222222222222222222222222", "type": "zip" }
+ },
+ {
+ "name": "f/f", "version": "dev-master",
+ "source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/f/newf", "type": "git" },
+ "dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/f/newf/zipball/2222222222222222222222222222222222222222", "type": "zip" }
+ },
+ {
+ "name": "g/g", "version": "dev-master",
+ "source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/g/newg", "type": "git" },
+ "dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/g/newg/zipball/2222222222222222222222222222222222222222", "type": "zip" }
+ }
+ ]
+ }
+ ],
+ "require": {
+ "a/a": "dev-master",
+ "b/b": "2.0.3",
+ "c/c": "1.0.0",
+ "d/d": "dev-master#1111111111111111111111111111111111111111",
+ "e/e": "dev-master#1111111111111111111111111111111111111111",
+ "f/f": "dev-master",
+ "g/g": "dev-master#1111111111111111111111111111111111111111"
+ }
+}
+--INSTALLED--
+[
+ {
+ "name": "a/a", "version": "dev-master",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/a/a", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/a/a/zipball/1111111111111111111111111111111111111111", "type": "zip" }
+ },
+ {
+ "name": "b/b", "version": "2.0.3",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/b/b", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/b/b/zipball/1111111111111111111111111111111111111111", "type": "zip" }
+ },
+ {
+ "name": "c/c", "version": "1.0.0",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/c", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/c/zipball/1111111111111111111111111111111111111111", "type": "zip" }
+ },
+ {
+ "name": "d/d", "version": "dev-master",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/d/d", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/d/zipball/1111111111111111111111111111111111111111", "type": "zip" }
+ },
+ {
+ "name": "f/f", "version": "dev-master",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/f", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/f/zipball/1111111111111111111111111111111111111111", "type": "zip" }
+ },
+ {
+ "name": "g/g", "version": "dev-master",
+ "source": { "reference": "0000000000000000000000000000000000000000", "url": "https://github.com/g/g", "type": "git" },
+ "dist": { "reference": "0000000000000000000000000000000000000000", "url": "https://api.github.com/repos/g/g/zipball/0000000000000000000000000000000000000000", "type": "zip" }
+ }
+]
+--LOCK--
+{
+ "packages": [
+ {
+ "name": "a/a", "version": "dev-master",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/a/a", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/a/a/zipball/1111111111111111111111111111111111111111", "type": "zip" },
+ "type": "library"
+ },
+ {
+ "name": "b/b", "version": "2.0.3",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/b/b", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/b/b/zipball/1111111111111111111111111111111111111111", "type": "zip" },
+ "type": "library"
+ },
+ {
+ "name": "c/c", "version": "1.0.0",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/c", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/c/zipball/1111111111111111111111111111111111111111", "type": "zip" },
+ "type": "library"
+ },
+ {
+ "name": "d/d", "version": "dev-master",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/d/d", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/d/zipball/1111111111111111111111111111111111111111", "type": "zip" },
+ "type": "library"
+ },
+ {
+ "name": "f/f", "version": "dev-master",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/f", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/f/zipball/1111111111111111111111111111111111111111", "type": "zip" },
+ "type": "library"
+ },
+ {
+ "name": "g/g", "version": "dev-master",
+ "source": { "reference": "0000000000000000000000000000000000000000", "url": "https://github.com/g/g", "type": "git" },
+ "dist": { "reference": "0000000000000000000000000000000000000000", "url": "https://api.github.com/repos/g/g/zipball/0000000000000000000000000000000000000000", "type": "zip" },
+ "type": "library"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {},
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--EXPECT-LOCK--
+{
+ "packages": [
+ {
+ "name": "a/a", "version": "dev-master",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/a/newa", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/a/newa/zipball/1111111111111111111111111111111111111111", "type": "zip" },
+ "type": "library"
+ },
+ {
+ "name": "b/b", "version": "2.0.3",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/b/newb", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/b/newb/zipball/1111111111111111111111111111111111111111", "type": "zip" },
+ "type": "library"
+ },
+ {
+ "name": "c/c", "version": "1.0.0",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/newc", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/newc/zipball/1111111111111111111111111111111111111111", "type": "zip" },
+ "type": "library"
+ },
+ {
+ "name": "d/d", "version": "dev-master",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/d/newd", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/newd/zipball/1111111111111111111111111111111111111111", "type": "zip" },
+ "type": "library"
+ },
+ {
+ "name": "f/f", "version": "dev-master",
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/newf", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/newf/zipball/1111111111111111111111111111111111111111", "type": "zip" },
+ "type": "library"
+ },
+ {
+ "name": "g/g", "version": "dev-master",
+ "source": { "reference": "0000000000000000000000000000000000000000", "url": "https://github.com/g/newg", "type": "git" },
+ "dist": { "reference": "0000000000000000000000000000000000000000", "url": "https://api.github.com/repos/g/newg/zipball/0000000000000000000000000000000000000000", "type": "zip" },
+ "type": "library"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {
+ "a/a": 20,
+ "d/d": 20,
+ "e/e": 20,
+ "f/f": 20,
+ "g/g": 20
+ },
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update mirrors
+--EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/update-no-dev-still-resolves-dev.test b/tests/Composer/Test/Fixtures/installer/update-no-dev-still-resolves-dev.test
index 3b90755e2..06cbc27c7 100644
--- a/tests/Composer/Test/Fixtures/installer/update-no-dev-still-resolves-dev.test
+++ b/tests/Composer/Test/Fixtures/installer/update-no-dev-still-resolves-dev.test
@@ -60,9 +60,9 @@ Updates with --no-dev but we still end up with a complete lock file including de
--RUN--
update --no-dev
--EXPECT--
-Uninstalling a/b (1.0.0)
-Updating a/a (1.0.0) to a/a (1.0.1)
-Updating dev/pkg (dev-master old) to dev/pkg (dev-master new)
+Removing a/b (1.0.0)
+Upgrading a/a (1.0.0 => 1.0.1)
Installing a/c (1.0.0)
+Upgrading dev/pkg (dev-master old => dev-master new)
Marking dev/pkg (1.1.x-dev new) as installed, alias of dev/pkg (dev-master new)
Marking dev/pkg (1.0.x-dev old) as uninstalled, alias of dev/pkg (dev-master old)
diff --git a/tests/Composer/Test/Fixtures/installer/update-picks-up-change-of-vcs-type.test b/tests/Composer/Test/Fixtures/installer/update-picks-up-change-of-vcs-type.test
index 1e528d047..c1e746356 100644
--- a/tests/Composer/Test/Fixtures/installer/update-picks-up-change-of-vcs-type.test
+++ b/tests/Composer/Test/Fixtures/installer/update-picks-up-change-of-vcs-type.test
@@ -31,10 +31,18 @@ Converting from one VCS type to another (including an URL change) should update
"name": "a/a", "version": "1.0.0",
"source": { "reference": "old-hg-ref", "type": "hg", "url": "old-hg-url" }
}
- ]
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
}
--RUN--
-update
+update mirrors
--EXPECT-LOCK--
{
"packages": [
@@ -54,4 +62,4 @@ update
"platform-dev": []
}
--EXPECT--
-
+Upgrading a/a (1.0.0 old-hg-ref => 1.0.0 new-git-ref)
diff --git a/tests/Composer/Test/Fixtures/installer/update-prefer-lowest-stable.test b/tests/Composer/Test/Fixtures/installer/update-prefer-lowest-stable.test
index 68e3effe2..58935c8d7 100644
--- a/tests/Composer/Test/Fixtures/installer/update-prefer-lowest-stable.test
+++ b/tests/Composer/Test/Fixtures/installer/update-prefer-lowest-stable.test
@@ -36,5 +36,5 @@ Updates packages to their lowest stable version
--RUN--
update --prefer-lowest --prefer-stable
--EXPECT--
-Updating a/a (1.0.0-rc1) to a/a (1.0.1)
-Downgrading a/b (1.0.1) to a/b (1.0.0)
+Upgrading a/a (1.0.0-rc1 => 1.0.1)
+Downgrading a/b (1.0.1 => 1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-reference-picks-latest.test b/tests/Composer/Test/Fixtures/installer/update-reference-picks-latest.test
new file mode 100644
index 000000000..734315eb5
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-reference-picks-latest.test
@@ -0,0 +1,31 @@
+--TEST--
+Updating a dev package should update to the latest available reference
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ {
+ "name": "a/a", "version": "dev-master",
+ "source": { "reference": "abc123", "url": "", "type": "git" }
+ }
+ ]
+ }
+ ],
+ "require": {
+ "a/a": "dev-master"
+ }
+}
+--INSTALLED--
+[
+ {
+ "name": "a/a", "version": "dev-master",
+ "source": { "reference": "def000", "url": "", "type": "git" },
+ "dist": { "reference": "def000", "url": "", "type": "zip", "shasum": "" }
+ }
+]
+--RUN--
+update
+--EXPECT--
+Upgrading a/a (dev-master def000 => dev-master abc123)
diff --git a/tests/Composer/Test/Fixtures/installer/update-reference.test b/tests/Composer/Test/Fixtures/installer/update-reference.test
index 9dca245ee..7836cbf5a 100644
--- a/tests/Composer/Test/Fixtures/installer/update-reference.test
+++ b/tests/Composer/Test/Fixtures/installer/update-reference.test
@@ -27,4 +27,4 @@ Updates a dev package forcing it's reference
--RUN--
install
--EXPECT--
-Updating a/a (dev-master abc123) to a/a (dev-master def000)
+Upgrading a/a (dev-master abc123 => dev-master def000)
diff --git a/tests/Composer/Test/Fixtures/installer/update-removes-unused-locked-dep.test b/tests/Composer/Test/Fixtures/installer/update-removes-unused-locked-dep.test
new file mode 100644
index 000000000..a47afb7ff
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-removes-unused-locked-dep.test
@@ -0,0 +1,67 @@
+--TEST--
+A composer update should remove unused locked dependencies from the lock file and remove unused installed deps from disk
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "a/a", "version": "1.0.0" },
+ { "name": "b/b", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "a/a": "*"
+ }
+}
+--LOCK--
+{
+ "packages": [
+ { "name": "a/a", "version": "1.0.0" },
+ { "name": "b/b", "version": "1.0.0" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--INSTALLED--
+[
+ { "name": "a/a", "version": "1.0.0" },
+ { "name": "b/b", "version": "1.0.0" },
+ { "name": "c/c", "version": "1.0.0" }
+]
+--RUN--
+update
+--EXPECT-LOCK--
+{
+ "packages": [
+ { "name": "a/a", "version": "1.0.0", "type": "library" }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+Lock file operations: 0 installs, 0 updates, 1 removal
+ - Removing b/b (1.0.0)
+Writing lock file
+Installing dependencies from lock file (including require-dev)
+Package operations: 0 installs, 0 updates, 2 removals
+Generating autoload files
+
+--EXPECT--
+Removing c/c (1.0.0)
+Removing b/b (1.0.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-to-empty-from-blank.test b/tests/Composer/Test/Fixtures/installer/update-to-empty-from-blank.test
new file mode 100644
index 000000000..e9892c3b6
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-to-empty-from-blank.test
@@ -0,0 +1,20 @@
+--TEST--
+Update to a state without dependency works well from a blank slate
+--COMPOSER--
+{
+}
+--RUN--
+update
+--EXPECT-LOCK--
+{
+ "packages": [],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/update-to-empty-from-locked.test b/tests/Composer/Test/Fixtures/installer/update-to-empty-from-locked.test
new file mode 100644
index 000000000..89e94d781
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-to-empty-from-locked.test
@@ -0,0 +1,48 @@
+--TEST--
+Update to a state without dependency works well from locked with dependency
+--COMPOSER--
+{
+ "minimum-stability": "dev"
+}
+--INSTALLED--
+[
+ {
+ "name": "a/a", "version": "dev-master",
+ "source": { "reference": "1234", "type": "git", "url": "" }
+ }
+]
+--LOCK--
+{
+ "packages": [
+ {
+ "name": "a/a", "version": "dev-master",
+ "source": { "reference": "1234", "type": "git", "url": "" },
+ "type": "library"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--RUN--
+update
+--EXPECT-LOCK--
+{
+ "packages": [],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
+--EXPECT--
+Removing a/a (dev-master 1234)
+Marking a/a (9999999-dev 1234) as uninstalled, alias of a/a (dev-master 1234)
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-locked-require.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-locked-require.test
deleted file mode 100644
index 381416af1..000000000
--- a/tests/Composer/Test/Fixtures/installer/update-whitelist-locked-require.test
+++ /dev/null
@@ -1,36 +0,0 @@
---TEST--
-Update with a package whitelist only updates those packages if they are not present in composer.json
---COMPOSER--
-{
- "repositories": [
- {
- "type": "package",
- "package": [
- { "name": "whitelisted", "version": "1.1.0", "require": { "dependency": "1.1.0", "fixed-dependency": "1.*" } },
- { "name": "whitelisted", "version": "1.0.0", "require": { "dependency": "1.0.0", "fixed-dependency": "1.*" } },
- { "name": "dependency", "version": "1.1.0" },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "fixed-dependency", "version": "1.1.0", "require": { "fixed-sub-dependency": "1.*" } },
- { "name": "fixed-dependency", "version": "1.0.0", "require": { "fixed-sub-dependency": "1.*" } },
- { "name": "fixed-sub-dependency", "version": "1.1.0" },
- { "name": "fixed-sub-dependency", "version": "1.0.0" }
- ]
- }
- ],
- "require": {
- "whitelisted": "1.*",
- "fixed-dependency": "1.*"
- }
-}
---INSTALLED--
-[
- { "name": "whitelisted", "version": "1.0.0", "require": { "dependency": "1.0.0", "fixed-dependency": "1.*" } },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "fixed-dependency", "version": "1.0.0", "require": { "fixed-sub-dependency": "1.*" } },
- { "name": "fixed-sub-dependency", "version": "1.0.0" }
-]
---RUN--
-update whitelisted dependency
---EXPECT--
-Updating dependency (1.0.0) to dependency (1.1.0)
-Updating whitelisted (1.0.0) to whitelisted (1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-all-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-all-dependencies.test
deleted file mode 100644
index 8ea177cad..000000000
--- a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-all-dependencies.test
+++ /dev/null
@@ -1,46 +0,0 @@
---TEST--
-Update with a package whitelist pattern and all-dependencies flag updates packages and their dependencies, even if defined as root dependency, matching the pattern
---COMPOSER--
-{
- "repositories": [
- {
- "type": "package",
- "package": [
- { "name": "fixed", "version": "1.1.0" },
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted-component1", "version": "1.1.0" },
- { "name": "whitelisted-component1", "version": "1.0.0" },
- { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.*" } },
- { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.*" } },
- { "name": "dependency", "version": "1.1.0" },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.1.0" },
- { "name": "unrelated-dependency", "version": "1.0.0" }
- ]
- }
- ],
- "require": {
- "fixed": "1.*",
- "whitelisted-component1": "1.*",
- "whitelisted-component2": "1.*",
- "dependency": "1.*",
- "unrelated": "1.*"
- }
-}
---INSTALLED--
-[
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted-component1", "version": "1.0.0" },
- { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.0.0" }
-]
---RUN--
-update whitelisted-* --with-all-dependencies
---EXPECT--
-Updating whitelisted-component1 (1.0.0) to whitelisted-component1 (1.1.0)
-Updating dependency (1.0.0) to dependency (1.1.0)
-Updating whitelisted-component2 (1.0.0) to whitelisted-component2 (1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-dependencies.test
deleted file mode 100644
index c685f14ce..000000000
--- a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-dependencies.test
+++ /dev/null
@@ -1,49 +0,0 @@
---TEST--
-Update with a package whitelist only updates those packages and their dependencies matching the pattern but no dependencies defined as roo package
---COMPOSER--
-{
- "repositories": [
- {
- "type": "package",
- "package": [
- { "name": "fixed", "version": "1.1.0" },
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted-component1", "version": "1.1.0" },
- { "name": "whitelisted-component1", "version": "1.0.0" },
- { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.*", "root-dependency": "1.*" } },
- { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.*", "root-dependency": "1.*" } },
- { "name": "dependency", "version": "1.1.0" },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "root-dependency", "version": "1.1.0" },
- { "name": "root-dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.1.0" },
- { "name": "unrelated-dependency", "version": "1.0.0" }
- ]
- }
- ],
- "require": {
- "fixed": "1.*",
- "whitelisted-component1": "1.*",
- "whitelisted-component2": "1.*",
- "root-dependency": "1.*",
- "unrelated": "1.*"
- }
-}
---INSTALLED--
-[
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted-component1", "version": "1.0.0" },
- { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
- { "name": "root-dependency", "version": "1.0.0" },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.0.0" }
-]
---RUN--
-update whitelisted-* --with-dependencies
---EXPECT--
-Updating whitelisted-component1 (1.0.0) to whitelisted-component1 (1.1.0)
-Updating dependency (1.0.0) to dependency (1.1.0)
-Updating whitelisted-component2 (1.0.0) to whitelisted-component2 (1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test
deleted file mode 100644
index a24bafb91..000000000
--- a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test
+++ /dev/null
@@ -1,55 +0,0 @@
---TEST--
-Update with a package whitelist only updates those packages and their dependencies matching the pattern
---COMPOSER--
-{
- "repositories": [
- {
- "type": "package",
- "package": [
- { "name": "fixed", "version": "1.1.0" },
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted-component1", "version": "1.1.0", "require": { "whitelisted-component2": "1.1.0" } },
- { "name": "whitelisted-component1", "version": "1.0.0", "require": { "whitelisted-component2": "1.0.0" } },
- { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.1.0", "whitelisted-component5": "1.0.0" } },
- { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
- { "name": "whitelisted-component3", "version": "1.1.0", "require": { "whitelisted-component4": "1.1.0" } },
- { "name": "whitelisted-component3", "version": "1.0.0", "require": { "whitelisted-component4": "1.0.0" } },
- { "name": "whitelisted-component4", "version": "1.1.0" },
- { "name": "whitelisted-component4", "version": "1.0.0" },
- { "name": "whitelisted-component5", "version": "1.1.0" },
- { "name": "whitelisted-component5", "version": "1.0.0" },
- { "name": "dependency", "version": "1.1.0" },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.1.0" },
- { "name": "unrelated-dependency", "version": "1.0.0" }
- ]
- }
- ],
- "require": {
- "fixed": "1.*",
- "whitelisted-component1": "1.*",
- "whitelisted-component2": "1.*",
- "whitelisted-component3": "1.0.0",
- "unrelated": "1.*"
- }
-}
---INSTALLED--
-[
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted-component1", "version": "1.0.0", "require": { "whitelisted-component2": "1.0.0" } },
- { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
- { "name": "whitelisted-component3", "version": "1.0.0", "require": { "whitelisted-component4": "1.0.0" } },
- { "name": "whitelisted-component4", "version": "1.0.0" },
- { "name": "whitelisted-component5", "version": "1.0.0" },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.0.0" }
-]
---RUN--
-update whitelisted-* --with-dependencies
---EXPECT--
-Updating dependency (1.0.0) to dependency (1.1.0)
-Updating whitelisted-component2 (1.0.0) to whitelisted-component2 (1.1.0)
-Updating whitelisted-component1 (1.0.0) to whitelisted-component1 (1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-without-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-without-dependencies.test
deleted file mode 100644
index e5551b43f..000000000
--- a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-without-dependencies.test
+++ /dev/null
@@ -1,44 +0,0 @@
---TEST--
-Update with a package whitelist only updates those packages matching the pattern
---COMPOSER--
-{
- "repositories": [
- {
- "type": "package",
- "package": [
- { "name": "fixed", "version": "1.1.0" },
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted-component1", "version": "1.1.0" },
- { "name": "whitelisted-component1", "version": "1.0.0" },
- { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.*" } },
- { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.*" } },
- { "name": "dependency", "version": "1.1.0" },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.1.0" },
- { "name": "unrelated-dependency", "version": "1.0.0" }
- ]
- }
- ],
- "require": {
- "fixed": "1.*",
- "whitelisted-component1": "1.*",
- "whitelisted-component2": "1.*",
- "unrelated": "1.*"
- }
-}
---INSTALLED--
-[
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted-component1", "version": "1.0.0" },
- { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.0.0" }
-]
---RUN--
-update whitelisted-*
---EXPECT--
-Updating whitelisted-component1 (1.0.0) to whitelisted-component1 (1.1.0)
-Updating whitelisted-component2 (1.0.0) to whitelisted-component2 (1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns.test
deleted file mode 100644
index de1fb1b73..000000000
--- a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns.test
+++ /dev/null
@@ -1,48 +0,0 @@
---TEST--
-Update with a package whitelist only updates those corresponding to the pattern
---COMPOSER--
-{
- "repositories": [
- {
- "type": "package",
- "package": [
- { "name": "vendor/Test-Package", "version": "2.0" },
- { "name": "vendor/NotMe", "version": "2.0" },
- { "name": "exact/Test-Package", "version": "2.0" },
- { "name": "notexact/TestPackage", "version": "2.0" },
- { "name": "all/Package1", "version": "2.0" },
- { "name": "all/Package2", "version": "2.0" },
- { "name": "another/another", "version": "2.0" },
- { "name": "no/regexp", "version": "2.0" }
- ]
- }
- ],
- "require": {
- "vendor/Test-Package": "*.*",
- "vendor/NotMe": "*.*",
- "exact/Test-Package": "*.*",
- "notexact/TestPackage": "*.*",
- "all/Package1": "*.*",
- "all/Package2": "*.*",
- "another/another": "*.*",
- "no/regexp": "*.*"
- }
-}
---INSTALLED--
-[
- { "name": "vendor/Test-Package", "version": "1.0" },
- { "name": "vendor/NotMe", "version": "1.0" },
- { "name": "exact/Test-Package", "version": "1.0" },
- { "name": "notexact/TestPackage", "version": "1.0" },
- { "name": "all/Package1", "version": "1.0" },
- { "name": "all/Package2", "version": "1.0" },
- { "name": "another/another", "version": "1.0" },
- { "name": "no/regexp", "version": "1.0" }
-]
---RUN--
-update vendor/Test* exact/Test-Package notexact/Test all/* no/reg?xp
---EXPECT--
-Updating vendor/Test-Package (1.0) to vendor/Test-Package (2.0)
-Updating exact/Test-Package (1.0) to exact/Test-Package (2.0)
-Updating all/Package1 (1.0) to all/Package1 (2.0)
-Updating all/Package2 (1.0) to all/Package2 (2.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-removes-unused.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-removes-unused.test
deleted file mode 100644
index e658e8c06..000000000
--- a/tests/Composer/Test/Fixtures/installer/update-whitelist-removes-unused.test
+++ /dev/null
@@ -1,32 +0,0 @@
---TEST--
-Update with a package whitelist removes unused packages
---COMPOSER--
-{
- "repositories": [
- {
- "type": "package",
- "package": [
- { "name": "whitelisted", "version": "1.1.0" },
- { "name": "whitelisted", "version": "1.0.0", "require": { "fixed-dependency": "1.0.0", "old-dependency": "1.0.0" } },
- { "name": "fixed-dependency", "version": "1.1.0" },
- { "name": "fixed-dependency", "version": "1.0.0" },
- { "name": "old-dependency", "version": "1.0.0" }
- ]
- }
- ],
- "require": {
- "whitelisted": "1.*",
- "fixed-dependency": "1.*"
- }
-}
---INSTALLED--
-[
- { "name": "whitelisted", "version": "1.0.0", "require": { "old-dependency": "1.0.0", "fixed-dependency": "1.0.0" } },
- { "name": "fixed-dependency", "version": "1.0.0" },
- { "name": "old-dependency", "version": "1.0.0" }
-]
---RUN--
-update --with-dependencies whitelisted
---EXPECT--
-Uninstalling old-dependency (1.0.0)
-Updating whitelisted (1.0.0) to whitelisted (1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-with-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-with-dependencies.test
deleted file mode 100644
index bb2e04193..000000000
--- a/tests/Composer/Test/Fixtures/installer/update-whitelist-with-dependencies.test
+++ /dev/null
@@ -1,40 +0,0 @@
---TEST--
-Update with a package whitelist only updates those packages and their dependencies listed as command arguments
---COMPOSER--
-{
- "repositories": [
- {
- "type": "package",
- "package": [
- { "name": "fixed", "version": "1.1.0" },
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted", "version": "1.1.0", "require": { "dependency": "1.1.0" } },
- { "name": "whitelisted", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
- { "name": "dependency", "version": "1.1.0" },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.1.0" },
- { "name": "unrelated-dependency", "version": "1.0.0" }
- ]
- }
- ],
- "require": {
- "fixed": "1.*",
- "whitelisted": "1.*",
- "unrelated": "1.*"
- }
-}
---INSTALLED--
-[
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.0.0" }
-]
---RUN--
-update whitelisted --with-dependencies
---EXPECT--
-Updating dependency (1.0.0) to dependency (1.1.0)
-Updating whitelisted (1.0.0) to whitelisted (1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-with-dependency-conflict.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-with-dependency-conflict.test
deleted file mode 100644
index f63229fbc..000000000
--- a/tests/Composer/Test/Fixtures/installer/update-whitelist-with-dependency-conflict.test
+++ /dev/null
@@ -1,38 +0,0 @@
---TEST--
-Update with a package whitelist only updates whitelisted packages if no dependency conflicts
---COMPOSER--
-{
- "repositories": [
- {
- "type": "package",
- "package": [
- { "name": "fixed", "version": "1.1.0" },
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted", "version": "1.1.0", "require": { "dependency": "1.1.0" } },
- { "name": "whitelisted", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
- { "name": "dependency", "version": "1.1.0" },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.1.0" },
- { "name": "unrelated-dependency", "version": "1.0.0" }
- ]
- }
- ],
- "require": {
- "fixed": "1.*",
- "whitelisted": "1.*",
- "unrelated": "1.*"
- }
-}
---INSTALLED--
-[
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.0.0" }
-]
---RUN--
-update whitelisted
---EXPECT--
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist.test b/tests/Composer/Test/Fixtures/installer/update-whitelist.test
deleted file mode 100644
index 751d79e70..000000000
--- a/tests/Composer/Test/Fixtures/installer/update-whitelist.test
+++ /dev/null
@@ -1,39 +0,0 @@
---TEST--
-Update with a package whitelist only updates those packages listed as command arguments
---COMPOSER--
-{
- "repositories": [
- {
- "type": "package",
- "package": [
- { "name": "fixed", "version": "1.1.0" },
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted", "version": "1.1.0", "require": { "dependency": "1.*" } },
- { "name": "whitelisted", "version": "1.0.0", "require": { "dependency": "1.*" } },
- { "name": "dependency", "version": "1.1.0" },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.1.0" },
- { "name": "unrelated-dependency", "version": "1.0.0" }
- ]
- }
- ],
- "require": {
- "fixed": "1.*",
- "whitelisted": "1.*",
- "unrelated": "1.*"
- }
-}
---INSTALLED--
-[
- { "name": "fixed", "version": "1.0.0" },
- { "name": "whitelisted", "version": "1.0.0", "require": { "dependency": "1.*" } },
- { "name": "dependency", "version": "1.0.0" },
- { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
- { "name": "unrelated-dependency", "version": "1.0.0" }
-]
---RUN--
-update whitelisted
---EXPECT--
-Updating whitelisted (1.0.0) to whitelisted (1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-with-all-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-with-all-dependencies.test
index c0019e6ca..a950b247a 100644
--- a/tests/Composer/Test/Fixtures/installer/update-with-all-dependencies.test
+++ b/tests/Composer/Test/Fixtures/installer/update-with-all-dependencies.test
@@ -28,17 +28,35 @@ When `--with-all-dependencies` is used, Composer\Installer::whitelistUpdateDepen
{ "name": "a/a", "version": "1.0.0" },
{ "name": "b/b", "version": "1.0.0", "require": { "a/a": "~1.0" } }
]
-
+--LOCK--
+{
+ "packages": [
+ { "name": "a/a", "version": "1.0.0" },
+ { "name": "b/b", "version": "1.0.0", "require": { "a/a": "~1.0" } }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
--RUN--
update b/b --with-all-dependencies
--EXPECT-OUTPUT--
Loading composer repositories with package information
-Updating dependencies (including require-dev)
-Package operations: 0 installs, 2 updates, 0 removals
+Updating dependencies
+Lock file operations: 0 installs, 2 updates, 0 removals
+ - Upgrading a/a (1.0.0 => 1.1.0)
+ - Upgrading b/b (1.0.0 => 1.1.0)
Writing lock file
+Installing dependencies from lock file (including require-dev)
+Package operations: 0 installs, 2 updates, 0 removals
Generating autoload files
--EXPECT--
-Updating a/a (1.0.0) to a/a (1.1.0)
-Updating b/b (1.0.0) to b/b (1.1.0)
+Upgrading a/a (1.0.0 => 1.1.0)
+Upgrading b/b (1.0.0 => 1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test b/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test
index 04624561d..3fb6654ab 100644
--- a/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test
+++ b/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test
@@ -16,7 +16,7 @@ Installing locked dev packages should remove old dependencies
"require": {}
}
],
- "packages-dev": null,
+ "packages-dev": [],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": [],
@@ -41,5 +41,6 @@ Installing locked dev packages should remove old dependencies
--RUN--
install
--EXPECT--
-Uninstalling a/dependency (dev-master ref)
-Updating a/devpackage (dev-master oldref) to a/devpackage (dev-master newref)
+Removing a/dependency (dev-master ref)
+Upgrading a/devpackage (dev-master oldref => dev-master newref)
+Marking a/dependency (9999999-dev ref) as uninstalled, alias of a/dependency (dev-master ref)
diff --git a/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test b/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test
index c5c838517..c84ab3525 100644
--- a/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test
+++ b/tests/Composer/Test/Fixtures/installer/updating-dev-updates-url-and-reference.test
@@ -28,7 +28,7 @@ Updating a dev package for new reference updates the url and reference
"dist": { "reference": "oldref", "url": "oldurl", "type": "zip", "shasum": "" }
}
],
- "packages-dev": null,
+ "packages-dev": [],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": {"a/a":20},
@@ -65,4 +65,4 @@ update
"platform-dev": []
}
--EXPECT--
-Updating a/a (dev-master oldref) to a/a (dev-master newref)
+Upgrading a/a (dev-master oldref => dev-master newref)
diff --git a/tests/Composer/Test/Installer/InstallationManagerTest.php b/tests/Composer/Test/Installer/InstallationManagerTest.php
index 477cbbe41..9f9bf5572 100644
--- a/tests/Composer/Test/Installer/InstallationManagerTest.php
+++ b/tests/Composer/Test/Installer/InstallationManagerTest.php
@@ -13,6 +13,7 @@
namespace Composer\Test\Installer;
use Composer\Installer\InstallationManager;
+use Composer\Installer\NoopInstaller;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\DependencyResolver\Operation\UninstallOperation;
@@ -21,10 +22,14 @@ use Composer\Test\TestCase;
class InstallationManagerTest extends TestCase
{
protected $repository;
+ protected $loop;
+ protected $io;
public function setUp()
{
+ $this->loop = $this->getMockBuilder('Composer\Util\Loop')->disableOriginalConstructor()->getMock();
$this->repository = $this->getMockBuilder('Composer\Repository\InstalledRepositoryInterface')->getMock();
+ $this->io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
}
public function testAddGetInstaller()
@@ -38,7 +43,7 @@ class InstallationManagerTest extends TestCase
return $arg === 'vendor';
}));
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop, $this->io);
$manager->addInstaller($installer);
$this->assertSame($installer, $manager->getInstaller('vendor'));
@@ -67,7 +72,7 @@ class InstallationManagerTest extends TestCase
return $arg === 'vendor';
}));
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop, $this->io);
$manager->addInstaller($installer);
$this->assertSame($installer, $manager->getInstaller('vendor'));
@@ -80,16 +85,21 @@ class InstallationManagerTest extends TestCase
public function testExecute()
{
$manager = $this->getMockBuilder('Composer\Installer\InstallationManager')
+ ->setConstructorArgs(array($this->loop, $this->io))
->setMethods(array('install', 'update', 'uninstall'))
->getMock();
- $installOperation = new InstallOperation($this->createPackageMock());
- $removeOperation = new UninstallOperation($this->createPackageMock());
+ $installOperation = new InstallOperation($package = $this->createPackageMock());
+ $removeOperation = new UninstallOperation($package);
$updateOperation = new UpdateOperation(
- $this->createPackageMock(),
- $this->createPackageMock()
+ $package,
+ $package
);
+ $package->expects($this->any())
+ ->method('getType')
+ ->will($this->returnValue('library'));
+
$manager
->expects($this->once())
->method('install')
@@ -103,15 +113,14 @@ class InstallationManagerTest extends TestCase
->method('update')
->with($this->repository, $updateOperation);
- $manager->execute($this->repository, $installOperation);
- $manager->execute($this->repository, $removeOperation);
- $manager->execute($this->repository, $updateOperation);
+ $manager->addInstaller(new NoopInstaller());
+ $manager->execute($this->repository, array($installOperation, $removeOperation, $updateOperation));
}
public function testInstall()
{
$installer = $this->createInstallerMock();
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop, $this->io);
$manager->addInstaller($installer);
$package = $this->createPackageMock();
@@ -139,7 +148,7 @@ class InstallationManagerTest extends TestCase
public function testUpdateWithEqualTypes()
{
$installer = $this->createInstallerMock();
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop, $this->io);
$manager->addInstaller($installer);
$initial = $this->createPackageMock();
@@ -173,18 +182,17 @@ class InstallationManagerTest extends TestCase
{
$libInstaller = $this->createInstallerMock();
$bundleInstaller = $this->createInstallerMock();
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop, $this->io);
$manager->addInstaller($libInstaller);
$manager->addInstaller($bundleInstaller);
$initial = $this->createPackageMock();
- $target = $this->createPackageMock();
- $operation = new UpdateOperation($initial, $target, 'test');
-
$initial
->expects($this->once())
->method('getType')
->will($this->returnValue('library'));
+
+ $target = $this->createPackageMock();
$target
->expects($this->once())
->method('getType')
@@ -213,13 +221,14 @@ class InstallationManagerTest extends TestCase
->method('install')
->with($this->repository, $target);
+ $operation = new UpdateOperation($initial, $target, 'test');
$manager->update($this->repository, $operation);
}
public function testUninstall()
{
$installer = $this->createInstallerMock();
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop, $this->io);
$manager->addInstaller($installer);
$package = $this->createPackageMock();
@@ -249,7 +258,7 @@ class InstallationManagerTest extends TestCase
$installer = $this->getMockBuilder('Composer\Installer\LibraryInstaller')
->disableOriginalConstructor()
->getMock();
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop, $this->io);
$manager->addInstaller($installer);
$package = $this->createPackageMock();
@@ -281,7 +290,9 @@ class InstallationManagerTest extends TestCase
private function createPackageMock()
{
- return $this->getMockBuilder('Composer\Package\PackageInterface')
+ $mock = $this->getMockBuilder('Composer\Package\PackageInterface')
->getMock();
+
+ return $mock;
}
}
diff --git a/tests/Composer/Test/Installer/InstallerEventTest.php b/tests/Composer/Test/Installer/InstallerEventTest.php
index 531697b62..6c0689844 100644
--- a/tests/Composer/Test/Installer/InstallerEventTest.php
+++ b/tests/Composer/Test/Installer/InstallerEventTest.php
@@ -21,21 +21,14 @@ class InstallerEventTest extends TestCase
{
$composer = $this->getMockBuilder('Composer\Composer')->getMock();
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
- $policy = $this->getMockBuilder('Composer\DependencyResolver\PolicyInterface')->getMock();
- $pool = $this->getMockBuilder('Composer\DependencyResolver\Pool')->disableOriginalConstructor()->getMock();
- $installedRepo = $this->getMockBuilder('Composer\Repository\CompositeRepository')->disableOriginalConstructor()->getMock();
- $request = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock();
- $operations = array($this->getMockBuilder('Composer\DependencyResolver\Operation\OperationInterface')->getMock());
- $event = new InstallerEvent('EVENT_NAME', $composer, $io, true, $policy, $pool, $installedRepo, $request, $operations);
+ $transaction = $this->getMockBuilder('Composer\DependencyResolver\LockTransaction')->disableOriginalConstructor()->getMock();
+ $event = new InstallerEvent('EVENT_NAME', $composer, $io, true, true, $transaction);
$this->assertSame('EVENT_NAME', $event->getName());
$this->assertInstanceOf('Composer\Composer', $event->getComposer());
$this->assertInstanceOf('Composer\IO\IOInterface', $event->getIO());
$this->assertTrue($event->isDevMode());
- $this->assertInstanceOf('Composer\DependencyResolver\PolicyInterface', $event->getPolicy());
- $this->assertInstanceOf('Composer\DependencyResolver\Pool', $event->getPool());
- $this->assertInstanceOf('Composer\Repository\CompositeRepository', $event->getInstalledRepo());
- $this->assertInstanceOf('Composer\DependencyResolver\Request', $event->getRequest());
- $this->assertCount(1, $event->getOperations());
+ $this->assertTrue($event->isExecutingOperations());
+ $this->assertInstanceOf('Composer\DependencyResolver\Transaction', $event->getTransaction());
}
}
diff --git a/tests/Composer/Test/Installer/LibraryInstallerTest.php b/tests/Composer/Test/Installer/LibraryInstallerTest.php
index d92ed3309..f2f2e89e6 100644
--- a/tests/Composer/Test/Installer/LibraryInstallerTest.php
+++ b/tests/Composer/Test/Installer/LibraryInstallerTest.php
@@ -113,7 +113,7 @@ class LibraryInstallerTest extends TestCase
$this->dm
->expects($this->once())
- ->method('download')
+ ->method('install')
->with($package, $this->vendorDir.'/some/package');
$this->repository
diff --git a/tests/Composer/Test/Installer/SuggestedPackagesReporterTest.php b/tests/Composer/Test/Installer/SuggestedPackagesReporterTest.php
index 03bb0b5fb..286b386d9 100644
--- a/tests/Composer/Test/Installer/SuggestedPackagesReporterTest.php
+++ b/tests/Composer/Test/Installer/SuggestedPackagesReporterTest.php
@@ -33,14 +33,13 @@ class SuggestedPackagesReporterTest extends TestCase
/**
* @covers ::__construct
*/
- public function testContrsuctor()
+ public function testConstructor()
{
$this->io->expects($this->once())
- ->method('writeError');
+ ->method('write');
- $suggestedPackagesReporter = new SuggestedPackagesReporter($this->io);
- $suggestedPackagesReporter->addPackage('a', 'b', 'c');
- $suggestedPackagesReporter->output();
+ $this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
+ $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_LIST);
}
/**
@@ -135,25 +134,33 @@ class SuggestedPackagesReporterTest extends TestCase
{
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
- $this->io->expects($this->once())
- ->method('writeError')
- ->with('a suggests installing b (c)');
+ $this->io->expects($this->at(0))
+ ->method('write')
+ ->with('a suggests:');
- $this->suggestedPackagesReporter->output();
+ $this->io->expects($this->at(1))
+ ->method('write')
+ ->with(' - b: c');
+
+ $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
}
/**
* @covers ::output
*/
- public function testOutputWithNoSuggestedPackage()
+ public function testOutputWithNoSuggestionReason()
{
$this->suggestedPackagesReporter->addPackage('a', 'b', '');
- $this->io->expects($this->once())
- ->method('writeError')
- ->with('a suggests installing b');
+ $this->io->expects($this->at(0))
+ ->method('write')
+ ->with('a suggests:');
- $this->suggestedPackagesReporter->output();
+ $this->io->expects($this->at(1))
+ ->method('write')
+ ->with(' - b');
+
+ $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
}
/**
@@ -165,14 +172,18 @@ class SuggestedPackagesReporterTest extends TestCase
$this->suggestedPackagesReporter->addPackage('source', 'target2', "Like us on Facebook>");
$this->io->expects($this->at(0))
- ->method('writeError')
- ->with("source suggests installing target1 ([1;37;42m Like us on Facebook [0m)");
+ ->method('write')
+ ->with('source suggests:');
$this->io->expects($this->at(1))
- ->method('writeError')
- ->with('source suggests installing target2 (\\Like us on Facebook\\>)');
+ ->method('write')
+ ->with(' - target1: [1;37;42m Like us on Facebook [0m');
- $this->suggestedPackagesReporter->output();
+ $this->io->expects($this->at(2))
+ ->method('write')
+ ->with(' - target2: \\Like us on Facebook\\>');
+
+ $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
}
/**
@@ -184,14 +195,26 @@ class SuggestedPackagesReporterTest extends TestCase
$this->suggestedPackagesReporter->addPackage('source package', 'target', 'because reasons');
$this->io->expects($this->at(0))
- ->method('writeError')
- ->with('a suggests installing b (c)');
+ ->method('write')
+ ->with('a suggests:');
$this->io->expects($this->at(1))
- ->method('writeError')
- ->with('source package suggests installing target (because reasons)');
+ ->method('write')
+ ->with(' - b: c');
- $this->suggestedPackagesReporter->output();
+ $this->io->expects($this->at(2))
+ ->method('write')
+ ->with('');
+
+ $this->io->expects($this->at(3))
+ ->method('write')
+ ->with('source package suggests:');
+
+ $this->io->expects($this->at(4))
+ ->method('write')
+ ->with(' - target: because reasons');
+
+ $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
}
/**
@@ -221,11 +244,15 @@ class SuggestedPackagesReporterTest extends TestCase
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
$this->suggestedPackagesReporter->addPackage('source package', 'target', 'because reasons');
- $this->io->expects($this->once())
- ->method('writeError')
- ->with('source package suggests installing target (because reasons)');
+ $this->io->expects($this->at(0))
+ ->method('write')
+ ->with('source package suggests:');
- $this->suggestedPackagesReporter->output($repository);
+ $this->io->expects($this->at(1))
+ ->method('write')
+ ->with(' - target: because reasons');
+
+ $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE, $repository);
}
/**
@@ -237,7 +264,7 @@ class SuggestedPackagesReporterTest extends TestCase
$repository->expects($this->exactly(0))
->method('getPackages');
- $this->suggestedPackagesReporter->output($repository);
+ $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE, $repository);
}
private function getSuggestedPackageArray()
diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php
index 067baf17a..3c6c35de6 100644
--- a/tests/Composer/Test/InstallerTest.php
+++ b/tests/Composer/Test/InstallerTest.php
@@ -12,9 +12,12 @@
namespace Composer\Test;
+use Composer\DependencyResolver\Request;
use Composer\Installer;
use Composer\Console\Application;
+use Composer\IO\BufferIO;
use Composer\Json\JsonFile;
+use Composer\Package\Dumper\ArrayDumper;
use Composer\Util\Filesystem;
use Composer\Repository\ArrayRepository;
use Composer\Repository\RepositoryManager;
@@ -29,8 +32,6 @@ use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatter;
-use Composer\Test\TestCase;
-use Composer\IO\BufferIO;
class InstallerTest extends TestCase
{
@@ -57,14 +58,16 @@ class InstallerTest extends TestCase
*/
public function testInstaller(RootPackageInterface $rootPackage, $repositories, array $options)
{
- $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
+ $io = new BufferIO('', OutputInterface::VERBOSITY_NORMAL, new OutputFormatter(false));
$downloadManager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($io))
->getMock();
$config = $this->getMockBuilder('Composer\Config')->getMock();
- $repositoryManager = new RepositoryManager($io, $config);
+ $eventDispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock();
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock();
+ $repositoryManager = new RepositoryManager($io, $config, $httpDownloader, $eventDispatcher);
$repositoryManager->setLocalRepository(new InstalledArrayRepository());
if (!is_array($repositories)) {
@@ -73,23 +76,44 @@ class InstallerTest extends TestCase
foreach ($repositories as $repository) {
$repositoryManager->addRepository($repository);
}
-
- $locker = $this->getMockBuilder('Composer\Package\Locker')->disableOriginalConstructor()->getMock();
$installationManager = new InstallationManagerMock();
- $eventDispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock();
+ // emulate a writable lock file
+ $lockData = null;
+ $lockJsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock();
+ $lockJsonMock->expects($this->any())
+ ->method('read')
+ ->will($this->returnCallback(function() use (&$lockData) {
+ return json_decode($lockData, true);
+ }));
+ $lockJsonMock->expects($this->any())
+ ->method('exists')
+ ->will($this->returnCallback(function () use (&$lockData) {
+ return $lockData !== null;
+ }));
+ $lockJsonMock->expects($this->any())
+ ->method('write')
+ ->will($this->returnCallback(function ($value, $options = 0) use (&$lockData) {
+ $lockData = json_encode($value, JsonFile::JSON_PRETTY_PRINT);
+ }));
+
+ $tempLockData = null;
+ $locker = new Locker($io, $lockJsonMock, $installationManager, '{}');
+
$autoloadGenerator = $this->getMockBuilder('Composer\Autoload\AutoloadGenerator')->disableOriginalConstructor()->getMock();
$installer = new Installer($io, $config, clone $rootPackage, $downloadManager, $repositoryManager, $locker, $installationManager, $eventDispatcher, $autoloadGenerator);
$result = $installer->run();
- $this->assertSame(0, $result);
+
+ $output = str_replace("\r", '', $io->getOutput());
+ $this->assertEquals(0, $result, $output);
$expectedInstalled = isset($options['install']) ? $options['install'] : array();
$expectedUpdated = isset($options['update']) ? $options['update'] : array();
$expectedUninstalled = isset($options['uninstall']) ? $options['uninstall'] : array();
$installed = $installationManager->getInstalledPackages();
- $this->assertSame($expectedInstalled, $installed);
+ $this->assertEquals($this->makePackagesComparable($expectedInstalled), $this->makePackagesComparable($installed));
$updated = $installationManager->getUpdatedPackages();
$this->assertSame($expectedUpdated, $updated);
@@ -98,6 +122,17 @@ class InstallerTest extends TestCase
$this->assertSame($expectedUninstalled, $uninstalled);
}
+ protected function makePackagesComparable($packages)
+ {
+ $dumper = new ArrayDumper();
+
+ $comparable = array();
+ foreach ($packages as $package) {
+ $comparable[] = $dumper->dump($package);
+ }
+ return $comparable;
+ }
+
public function provideInstaller()
{
$cases = array();
@@ -107,11 +142,11 @@ class InstallerTest extends TestCase
$a = $this->getPackage('A', '1.0.0', 'Composer\Package\RootPackage');
$a->setRequires(array(
- new Link('A', 'B', $this->getVersionConstraint('=', '1.0.0')),
+ 'b' => new Link('A', 'B', $v = $this->getVersionConstraint('=', '1.0.0'), 'requires', $v->getPrettyString()),
));
$b = $this->getPackage('B', '1.0.0');
$b->setRequires(array(
- new Link('B', 'A', $this->getVersionConstraint('=', '1.0.0')),
+ 'a' => new Link('B', 'A', $v = $this->getVersionConstraint('=', '1.0.0'), 'requires', $v->getPrettyString()),
));
$cases[] = array(
@@ -127,11 +162,11 @@ class InstallerTest extends TestCase
$a = $this->getPackage('A', '1.0.0', 'Composer\Package\RootPackage');
$a->setRequires(array(
- new Link('A', 'B', $this->getVersionConstraint('=', '1.0.0')),
+ 'b' => new Link('A', 'B', $v = $this->getVersionConstraint('=', '1.0.0'), 'requires', $v->getPrettyString()),
));
$b = $this->getPackage('B', '1.0.0');
$b->setRequires(array(
- new Link('B', 'A', $this->getVersionConstraint('=', '1.0.0')),
+ 'a' => new Link('B', 'A', $v = $this->getVersionConstraint('=', '1.0.0'), 'requires', $v->getPrettyString()),
));
$cases[] = array(
@@ -142,6 +177,7 @@ class InstallerTest extends TestCase
),
);
+ // TODO why are there not more cases with uninstall/update?
return $cases;
}
@@ -180,13 +216,24 @@ class InstallerTest extends TestCase
$repositoryManager = $composer->getRepositoryManager();
$repositoryManager->setLocalRepository(new InstalledFilesystemRepositoryMock($jsonMock));
+ // emulate a writable lock file
+ $lockData = $lock ? json_encode($lock, JsonFile::JSON_PRETTY_PRINT): null;
$lockJsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock();
$lockJsonMock->expects($this->any())
->method('read')
- ->will($this->returnValue($lock));
+ ->will($this->returnCallback(function() use (&$lockData) {
+ return json_decode($lockData, true);
+ }));
$lockJsonMock->expects($this->any())
->method('exists')
- ->will($this->returnValue(true));
+ ->will($this->returnCallback(function () use (&$lockData) {
+ return $lockData !== null;
+ }));
+ $lockJsonMock->expects($this->any())
+ ->method('write')
+ ->will($this->returnCallback(function ($value, $options = 0) use (&$lockData) {
+ $lockData = json_encode($value, JsonFile::JSON_PRETTY_PRINT);
+ }));
if ($expectLock) {
$actualLock = array();
@@ -203,7 +250,7 @@ class InstallerTest extends TestCase
}
$contents = json_encode($composerConfig);
- $locker = new Locker($io, $lockJsonMock, $repositoryManager, $composer->getInstallationManager(), $contents);
+ $locker = new Locker($io, $lockJsonMock, $composer->getInstallationManager(), $contents);
$composer->setLocker($locker);
$eventDispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock();
@@ -226,13 +273,27 @@ class InstallerTest extends TestCase
});
$application->get('update')->setCode(function ($input, $output) use ($installer) {
+ $packages = $input->getArgument('packages');
+ $filteredPackages = array_filter($packages, function ($package) {
+ return !in_array($package, array('lock', 'nothing', 'mirrors'), true);
+ });
+ $updateMirrors = $input->getOption('lock') || count($filteredPackages) != count($packages);
+ $packages = $filteredPackages;
+
+ $updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
+ if ($input->getOption('with-all-dependencies')) {
+ $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
+ } elseif ($input->getOption('with-dependencies')) {
+ $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
+ }
+
$installer
->setDevMode(!$input->getOption('no-dev'))
->setUpdate(true)
->setDryRun($input->getOption('dry-run'))
- ->setUpdateWhitelist($input->getArgument('packages'))
- ->setWhitelistTransitiveDependencies($input->getOption('with-dependencies'))
- ->setWhitelistAllDependencies($input->getOption('with-all-dependencies'))
+ ->setUpdateMirrors($updateMirrors)
+ ->setUpdateAllowList($packages)
+ ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
->setPreferStable($input->getOption('prefer-stable'))
->setPreferLowest($input->getOption('prefer-lowest'))
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'));
@@ -246,7 +307,7 @@ class InstallerTest extends TestCase
$application->setAutoExit(false);
$appOutput = fopen('php://memory', 'w+');
- $input = new StringInput($run);
+ $input = new StringInput($run.' -vvv');
$input->setInteractive(false);
$result = $application->run($input, new StreamOutput($appOutput));
fseek($appOutput, 0);
@@ -270,6 +331,9 @@ class InstallerTest extends TestCase
$this->assertSame(rtrim($expect), implode("\n", $installationManager->getTrace()));
if ($expectOutput) {
+ $output = preg_replace('{^ - .*?\.ini$}m', '__inilist__', $output);
+ $output = preg_replace('{(__inilist__\r?\n)+}', "__inilist__\n", $output);
+
$this->assertStringMatchesFormat(rtrim($expectOutput), rtrim($output));
}
}
diff --git a/tests/Composer/Test/Mock/FactoryMock.php b/tests/Composer/Test/Mock/FactoryMock.php
index 47683afcd..d4dc444a0 100644
--- a/tests/Composer/Test/Mock/FactoryMock.php
+++ b/tests/Composer/Test/Mock/FactoryMock.php
@@ -18,8 +18,10 @@ use Composer\Factory;
use Composer\Repository\RepositoryManager;
use Composer\Repository\WritableRepositoryInterface;
use Composer\Installer;
+use Composer\EventDispatcher\EventDispatcher;
use Composer\IO\IOInterface;
use Composer\Test\TestCase;
+use Composer\Util\Loop;
class FactoryMock extends Factory
{
@@ -39,9 +41,9 @@ class FactoryMock extends Factory
{
}
- protected function createInstallationManager()
+ public function createInstallationManager(Loop $loop, IOInterface $io, EventDispatcher $dispatcher = null)
{
- return new InstallationManagerMock;
+ return new InstallationManagerMock();
}
protected function createDefaultInstallers(Installer\InstallationManager $im, Composer $composer, IOInterface $io)
diff --git a/tests/Composer/Test/Mock/RemoteFilesystemMock.php b/tests/Composer/Test/Mock/HttpDownloaderMock.php
similarity index 73%
rename from tests/Composer/Test/Mock/RemoteFilesystemMock.php
rename to tests/Composer/Test/Mock/HttpDownloaderMock.php
index 5d4f52e54..1e2774af0 100644
--- a/tests/Composer/Test/Mock/RemoteFilesystemMock.php
+++ b/tests/Composer/Test/Mock/HttpDownloaderMock.php
@@ -12,13 +12,11 @@
namespace Composer\Test\Mock;
-use Composer\Util\RemoteFilesystem;
+use Composer\Util\HttpDownloader;
+use Composer\Util\Http\Response;
use Composer\Downloader\TransportException;
-/**
- * Remote filesystem mock
- */
-class RemoteFilesystemMock extends RemoteFilesystem
+class HttpDownloaderMock extends HttpDownloader
{
protected $contentMap;
@@ -30,10 +28,10 @@ class RemoteFilesystemMock extends RemoteFilesystem
$this->contentMap = $contentMap;
}
- public function getContents($originUrl, $fileUrl, $progress = true, $options = array())
+ public function get($fileUrl, $options = array())
{
if (!empty($this->contentMap[$fileUrl])) {
- return $this->contentMap[$fileUrl];
+ return new Response(array('url' => $fileUrl), 200, array(), $this->contentMap[$fileUrl]);
}
throw new TransportException('The "'.$fileUrl.'" file could not be downloaded (NOT FOUND)', 404);
diff --git a/tests/Composer/Test/Mock/InstallationManagerMock.php b/tests/Composer/Test/Mock/InstallationManagerMock.php
index de1de514b..220313daa 100644
--- a/tests/Composer/Test/Mock/InstallationManagerMock.php
+++ b/tests/Composer/Test/Mock/InstallationManagerMock.php
@@ -17,6 +17,7 @@ use Composer\Repository\RepositoryInterface;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Package\PackageInterface;
use Composer\DependencyResolver\Operation\InstallOperation;
+use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\DependencyResolver\Operation\UninstallOperation;
use Composer\DependencyResolver\Operation\MarkAliasInstalledOperation;
@@ -29,6 +30,20 @@ class InstallationManagerMock extends InstallationManager
private $uninstalled = array();
private $trace = array();
+ public function __construct()
+ {
+
+ }
+
+ public function execute(RepositoryInterface $repo, array $operations, $devMode = true, $runScripts = true)
+ {
+ foreach ($operations as $operation) {
+ $method = $operation->getOperationType();
+ // skipping download() step here for tests
+ $this->$method($repo, $operation);
+ }
+ }
+
public function getInstallPath(PackageInterface $package)
{
return '';
diff --git a/tests/Composer/Test/Mock/InstalledFilesystemRepositoryMock.php b/tests/Composer/Test/Mock/InstalledFilesystemRepositoryMock.php
index 9c11dc307..574cfbd83 100644
--- a/tests/Composer/Test/Mock/InstalledFilesystemRepositoryMock.php
+++ b/tests/Composer/Test/Mock/InstalledFilesystemRepositoryMock.php
@@ -13,6 +13,7 @@
namespace Composer\Test\Mock;
use Composer\Repository\InstalledFilesystemRepository;
+use Composer\Installer\InstallationManager;
class InstalledFilesystemRepositoryMock extends InstalledFilesystemRepository
{
@@ -20,7 +21,7 @@ class InstalledFilesystemRepositoryMock extends InstalledFilesystemRepository
{
}
- public function write()
+ public function write($devMode, InstallationManager $installationManager)
{
}
}
diff --git a/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php b/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php
index f6afe10f1..b8f110937 100644
--- a/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php
+++ b/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php
@@ -189,6 +189,7 @@ class ArchivableFilesFinderTest extends TestCase
'git init && '.
'git config user.email "you@example.com" && '.
'git config user.name "Your Name" && '.
+ 'git config commit.gpgsign false && '.
'git add .git* && '.
'git commit -m "ignore rules" && '.
'git add . && '.
@@ -308,7 +309,11 @@ class ArchivableFilesFinderTest extends TestCase
protected function getArchivedFiles($command)
{
- $process = new Process($command, $this->sources);
+ if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) {
+ $process = Process::fromShellCommandline($command, $this->sources);
+ } else {
+ $process = new Process($command, $this->sources);
+ }
$process->run();
$archive = new \PharData($this->sources.'/archive.zip');
diff --git a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php
index f9fe308fa..45a635437 100644
--- a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php
+++ b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php
@@ -12,9 +12,12 @@
namespace Composer\Test\Package\Archiver;
+use Composer\IO\NullIO;
use Composer\Factory;
use Composer\Package\Archiver\ArchiveManager;
use Composer\Package\PackageInterface;
+use Composer\Util\Loop;
+use Composer\Test\Mock\FactoryMock;
class ArchiveManagerTest extends ArchiverTest
{
@@ -30,7 +33,13 @@ class ArchiveManagerTest extends ArchiverTest
parent::setUp();
$factory = new Factory();
- $this->manager = $factory->createArchiveManager($factory->createConfig());
+ $dm = $factory->createDownloadManager(
+ $io = new NullIO,
+ $config = FactoryMock::createConfig(),
+ $httpDownloader = $factory->createHttpDownloader($io, $config)
+ );
+ $loop = new Loop($httpDownloader);
+ $this->manager = $factory->createArchiveManager($factory->createConfig(), $dm, $loop);
$this->targetDir = $this->testDir.'/composer_archiver_tests';
}
@@ -116,6 +125,12 @@ class ArchiveManagerTest extends ArchiverTest
throw new \RuntimeException('Could not config: '.$this->process->getErrorOutput());
}
+ $result = $this->process->execute('git config commit.gpgsign false', $output, $this->testDir);
+ if ($result > 0) {
+ chdir($currentWorkDir);
+ throw new \RuntimeException('Could not config: '.$this->process->getErrorOutput());
+ }
+
$result = $this->process->execute('git config user.name "Your Name"', $output, $this->testDir);
if ($result > 0) {
chdir($currentWorkDir);
diff --git a/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php b/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php
index a94539279..93cde83f5 100644
--- a/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php
+++ b/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php
@@ -46,8 +46,8 @@ class RootPackageLoaderTest extends TestCase
'zux/complex' => '~1.0,>=1.0.2@dev',
'or/op' => '^2.0@dev || ^2.0@dev',
'multi/lowest-wins' => '^2.0@rc || >=3.0@dev , ~3.5@alpha',
- 'or/op/without-flags' => 'dev-master || 2.0 , ~3.5-alpha',
- 'or/op/without-flags2' => '3.0-beta || 2.0 , ~3.5-alpha',
+ 'or/op-without-flags' => 'dev-master || 2.0 , ~3.5-alpha',
+ 'or/op-without-flags2' => '3.0-beta || 2.0 , ~3.5-alpha',
),
'minimum-stability' => 'alpha',
));
@@ -59,8 +59,8 @@ class RootPackageLoaderTest extends TestCase
'zux/complex' => BasePackage::STABILITY_DEV,
'or/op' => BasePackage::STABILITY_DEV,
'multi/lowest-wins' => BasePackage::STABILITY_DEV,
- 'or/op/without-flags' => BasePackage::STABILITY_DEV,
- 'or/op/without-flags2' => BasePackage::STABILITY_ALPHA,
+ 'or/op-without-flags' => BasePackage::STABILITY_DEV,
+ 'or/op-without-flags2' => BasePackage::STABILITY_ALPHA,
), $package->getStabilityFlags());
}
@@ -160,6 +160,8 @@ class RootPackageLoaderTest extends TestCase
$loader = new RootPackageLoader($manager, $config, null, new VersionGuesser($config, $executor, new VersionParser()));
$package = $loader->load(array('require' => array('foo/bar' => 'self.version')));
+ $this->assertEquals("9999999-dev", $package->getPrettyVersion());
+ $package = $package->getAliasOf();
$this->assertEquals("dev-master", $package->getPrettyVersion());
}
diff --git a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php
index 2cde001ac..1b7565992 100644
--- a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php
+++ b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php
@@ -356,7 +356,6 @@ class ValidatingArrayLoaderTest extends TestCase
'require' => array(
'foo/baz' => '*',
'bar/baz' => '>=1.0',
- 'bar/foo' => 'dev-master',
'bar/hacked' => '@stable',
'bar/woo' => '1.0.0',
),
@@ -364,7 +363,6 @@ class ValidatingArrayLoaderTest extends TestCase
array(
'require.foo/baz : unbound version constraints (*) should be avoided',
'require.bar/baz : unbound version constraints (>=1.0) should be avoided',
- 'require.bar/foo : unbound version constraints (dev-master) should be avoided',
'require.bar/hacked : unbound version constraints (@stable) should be avoided',
'require.bar/woo : exact version constraints (1.0.0) should be avoided if the package follows semantic versioning',
),
diff --git a/tests/Composer/Test/Package/LockerTest.php b/tests/Composer/Test/Package/LockerTest.php
index 04e8e6c84..29f3e7c2a 100644
--- a/tests/Composer/Test/Package/LockerTest.php
+++ b/tests/Composer/Test/Package/LockerTest.php
@@ -25,7 +25,6 @@ class LockerTest extends TestCase
$locker = new Locker(
new NullIO,
$json,
- $this->createRepositoryManagerMock(),
$this->createInstallationManagerMock(),
$this->getJsonContent()
);
@@ -45,10 +44,9 @@ class LockerTest extends TestCase
public function testGetNotLockedPackages()
{
$json = $this->createJsonFileMock();
- $repo = $this->createRepositoryManagerMock();
$inst = $this->createInstallationManagerMock();
- $locker = new Locker(new NullIO, $json, $repo, $inst, $this->getJsonContent());
+ $locker = new Locker(new NullIO, $json, $inst, $this->getJsonContent());
$json
->expects($this->once())
@@ -63,10 +61,9 @@ class LockerTest extends TestCase
public function testGetLockedPackages()
{
$json = $this->createJsonFileMock();
- $repo = $this->createRepositoryManagerMock();
$inst = $this->createInstallationManagerMock();
- $locker = new Locker(new NullIO, $json, $repo, $inst, $this->getJsonContent());
+ $locker = new Locker(new NullIO, $json, $inst, $this->getJsonContent());
$json
->expects($this->once())
@@ -90,11 +87,10 @@ class LockerTest extends TestCase
public function testSetLockData()
{
$json = $this->createJsonFileMock();
- $repo = $this->createRepositoryManagerMock();
$inst = $this->createInstallationManagerMock();
$jsonContent = $this->getJsonContent() . ' ';
- $locker = new Locker(new NullIO, $json, $repo, $inst, $jsonContent);
+ $locker = new Locker(new NullIO, $json, $inst, $jsonContent);
$package1 = $this->createPackageMock();
$package2 = $this->createPackageMock();
@@ -164,10 +160,9 @@ class LockerTest extends TestCase
public function testLockBadPackages()
{
$json = $this->createJsonFileMock();
- $repo = $this->createRepositoryManagerMock();
$inst = $this->createInstallationManagerMock();
- $locker = new Locker(new NullIO, $json, $repo, $inst, $this->getJsonContent());
+ $locker = new Locker(new NullIO, $json, $inst, $this->getJsonContent());
$package1 = $this->createPackageMock();
$package1
@@ -183,11 +178,10 @@ class LockerTest extends TestCase
public function testIsFresh()
{
$json = $this->createJsonFileMock();
- $repo = $this->createRepositoryManagerMock();
$inst = $this->createInstallationManagerMock();
$jsonContent = $this->getJsonContent();
- $locker = new Locker(new NullIO, $json, $repo, $inst, $jsonContent);
+ $locker = new Locker(new NullIO, $json, $inst, $jsonContent);
$json
->expects($this->once())
@@ -200,10 +194,9 @@ class LockerTest extends TestCase
public function testIsFreshFalse()
{
$json = $this->createJsonFileMock();
- $repo = $this->createRepositoryManagerMock();
$inst = $this->createInstallationManagerMock();
- $locker = new Locker(new NullIO, $json, $repo, $inst, $this->getJsonContent());
+ $locker = new Locker(new NullIO, $json, $inst, $this->getJsonContent());
$json
->expects($this->once())
@@ -216,11 +209,10 @@ class LockerTest extends TestCase
public function testIsFreshWithContentHash()
{
$json = $this->createJsonFileMock();
- $repo = $this->createRepositoryManagerMock();
$inst = $this->createInstallationManagerMock();
$jsonContent = $this->getJsonContent();
- $locker = new Locker(new NullIO, $json, $repo, $inst, $jsonContent);
+ $locker = new Locker(new NullIO, $json, $inst, $jsonContent);
$json
->expects($this->once())
@@ -233,11 +225,10 @@ class LockerTest extends TestCase
public function testIsFreshWithContentHashAndNoHash()
{
$json = $this->createJsonFileMock();
- $repo = $this->createRepositoryManagerMock();
$inst = $this->createInstallationManagerMock();
$jsonContent = $this->getJsonContent();
- $locker = new Locker(new NullIO, $json, $repo, $inst, $jsonContent);
+ $locker = new Locker(new NullIO, $json, $inst, $jsonContent);
$json
->expects($this->once())
@@ -250,10 +241,9 @@ class LockerTest extends TestCase
public function testIsFreshFalseWithContentHash()
{
$json = $this->createJsonFileMock();
- $repo = $this->createRepositoryManagerMock();
$inst = $this->createInstallationManagerMock();
- $locker = new Locker(new NullIO, $json, $repo, $inst, $this->getJsonContent());
+ $locker = new Locker(new NullIO, $json, $inst, $this->getJsonContent());
$differentHash = md5($this->getJsonContent(array('name' => 'test2')));
@@ -272,19 +262,6 @@ class LockerTest extends TestCase
->getMock();
}
- private function createRepositoryManagerMock()
- {
- $mock = $this->getMockBuilder('Composer\Repository\RepositoryManager')
- ->disableOriginalConstructor()
- ->getMock();
-
- $mock->expects($this->any())
- ->method('getLocalRepository')
- ->will($this->returnValue($this->getMockBuilder('Composer\Repository\ArrayRepository')->getMock()));
-
- return $mock;
- }
-
private function createInstallationManagerMock()
{
$mock = $this->getMockBuilder('Composer\Installer\InstallationManager')
diff --git a/tests/Composer/Test/Package/Version/VersionGuesserTest.php b/tests/Composer/Test/Package/Version/VersionGuesserTest.php
index 31c47d72b..9527b628f 100644
--- a/tests/Composer/Test/Package/Version/VersionGuesserTest.php
+++ b/tests/Composer/Test/Package/Version/VersionGuesserTest.php
@@ -89,7 +89,7 @@ class VersionGuesserTest extends TestCase
$guesser = new VersionGuesser($config, $executor, new VersionParser());
$versionArray = $guesser->guessVersion(array(), 'dummy/path');
- $this->assertEquals("9999999-dev", $versionArray['version']);
+ $this->assertEquals("dev-".$branch, $versionArray['version']);
$this->assertEquals("dev-".$branch, $versionArray['pretty_version']);
$this->assertEmpty($versionArray['commit']);
}
@@ -124,7 +124,7 @@ class VersionGuesserTest extends TestCase
$guesser = new VersionGuesser($config, $executor, new VersionParser());
$versionArray = $guesser->guessVersion(array(), 'dummy/path');
- $this->assertEquals("9999999-dev", $versionArray['version']);
+ $this->assertEquals("dev-master", $versionArray['version']);
$this->assertEquals("dev-master", $versionArray['pretty_version']);
$this->assertArrayNotHasKey('feature_version', $versionArray);
$this->assertArrayNotHasKey('feature_pretty_version', $versionArray);
diff --git a/tests/Composer/Test/Package/Version/VersionSelectorTest.php b/tests/Composer/Test/Package/Version/VersionSelectorTest.php
index 5a76720e8..66d07d267 100644
--- a/tests/Composer/Test/Package/Version/VersionSelectorTest.php
+++ b/tests/Composer/Test/Package/Version/VersionSelectorTest.php
@@ -21,7 +21,7 @@ use Composer\Test\TestCase;
class VersionSelectorTest extends TestCase
{
// A) multiple versions, get the latest one
- // B) targetPackageVersion will pass to pool
+ // B) targetPackageVersion will pass to repo set
// C) No results, throw exception
public function testLatestVersionIsReturned()
@@ -33,13 +33,13 @@ class VersionSelectorTest extends TestCase
$package3 = $this->createPackage('1.2.0');
$packages = array($package1, $package2, $package3);
- $pool = $this->createMockPool();
- $pool->expects($this->once())
- ->method('whatProvides')
- ->with($packageName, null, true)
+ $repositorySet = $this->createMockRepositorySet();
+ $repositorySet->expects($this->once())
+ ->method('findPackages')
+ ->with($packageName, null)
->will($this->returnValue($packages));
- $versionSelector = new VersionSelector($pool);
+ $versionSelector = new VersionSelector($repositorySet);
$best = $versionSelector->findBestCandidate($packageName);
// 1.2.2 should be returned because it's the latest of the returned versions
@@ -57,13 +57,13 @@ class VersionSelectorTest extends TestCase
$package2->setRequires(array('php' => new Link($packageName, 'php', $parser->parseConstraints('>=5.6'), 'requires', '>=5.6')));
$packages = array($package1, $package2);
- $pool = $this->createMockPool();
- $pool->expects($this->once())
- ->method('whatProvides')
- ->with($packageName, null, true)
+ $repositorySet = $this->createMockRepositorySet();
+ $repositorySet->expects($this->once())
+ ->method('findPackages')
+ ->with($packageName, null)
->will($this->returnValue($packages));
- $versionSelector = new VersionSelector($pool);
+ $versionSelector = new VersionSelector($repositorySet);
$best = $versionSelector->findBestCandidate($packageName, null, '5.5.0');
$this->assertSame($package1, $best, 'Latest version supporting php 5.5 should be returned (1.0.0)');
@@ -77,13 +77,13 @@ class VersionSelectorTest extends TestCase
$package2 = $this->createPackage('1.1.0-beta');
$packages = array($package1, $package2);
- $pool = $this->createMockPool();
- $pool->expects($this->once())
- ->method('whatProvides')
- ->with($packageName, null, true)
+ $repositorySet = $this->createMockRepositorySet();
+ $repositorySet->expects($this->once())
+ ->method('findPackages')
+ ->with($packageName, null)
->will($this->returnValue($packages));
- $versionSelector = new VersionSelector($pool);
+ $versionSelector = new VersionSelector($repositorySet);
$best = $versionSelector->findBestCandidate($packageName);
$this->assertSame($package1, $best, 'Latest most stable version should be returned (1.0.0)');
@@ -97,18 +97,18 @@ class VersionSelectorTest extends TestCase
$package2 = $this->createPackage('2.0.0-beta3');
$packages = array($package1, $package2);
- $pool = $this->createMockPool();
- $pool->expects($this->at(0))
- ->method('whatProvides')
- ->with($packageName, null, true)
+ $repositorySet = $this->createMockRepositorySet();
+ $repositorySet->expects($this->at(0))
+ ->method('findPackages')
+ ->with($packageName, null)
->will($this->returnValue($packages));
- $pool->expects($this->at(1))
- ->method('whatProvides')
- ->with($packageName, null, true)
+ $repositorySet->expects($this->at(1))
+ ->method('findPackages')
+ ->with($packageName, null)
->will($this->returnValue(array_reverse($packages)));
- $versionSelector = new VersionSelector($pool);
+ $versionSelector = new VersionSelector($repositorySet);
$best = $versionSelector->findBestCandidate($packageName, null, null);
$this->assertSame($package2, $best, 'Expecting 2.0.0-beta3, cause beta is more stable than dev');
@@ -124,13 +124,13 @@ class VersionSelectorTest extends TestCase
$package2 = $this->createPackage('1.1.0-beta');
$packages = array($package1, $package2);
- $pool = $this->createMockPool();
- $pool->expects($this->once())
- ->method('whatProvides')
- ->with($packageName, null, true)
+ $repositorySet = $this->createMockRepositorySet();
+ $repositorySet->expects($this->once())
+ ->method('findPackages')
+ ->with($packageName, null)
->will($this->returnValue($packages));
- $versionSelector = new VersionSelector($pool);
+ $versionSelector = new VersionSelector($repositorySet);
$best = $versionSelector->findBestCandidate($packageName, null, null, 'dev');
$this->assertSame($package2, $best, 'Latest version should be returned (1.1.0-beta)');
@@ -145,13 +145,13 @@ class VersionSelectorTest extends TestCase
$package3 = $this->createPackage('1.2.0-alpha');
$packages = array($package1, $package2, $package3);
- $pool = $this->createMockPool();
- $pool->expects($this->once())
- ->method('whatProvides')
- ->with($packageName, null, true)
+ $repositorySet = $this->createMockRepositorySet();
+ $repositorySet->expects($this->once())
+ ->method('findPackages')
+ ->with($packageName, null)
->will($this->returnValue($packages));
- $versionSelector = new VersionSelector($pool);
+ $versionSelector = new VersionSelector($repositorySet);
$best = $versionSelector->findBestCandidate($packageName, null, null, 'beta');
$this->assertSame($package2, $best, 'Latest version should be returned (1.1.0-beta)');
@@ -165,13 +165,13 @@ class VersionSelectorTest extends TestCase
$package3 = $this->createPackage('1.2.0-alpha');
$packages = array($package2, $package3);
- $pool = $this->createMockPool();
- $pool->expects($this->once())
- ->method('whatProvides')
- ->with($packageName, null, true)
+ $repositorySet = $this->createMockRepositorySet();
+ $repositorySet->expects($this->once())
+ ->method('findPackages')
+ ->with($packageName, null)
->will($this->returnValue($packages));
- $versionSelector = new VersionSelector($pool);
+ $versionSelector = new VersionSelector($repositorySet);
$best = $versionSelector->findBestCandidate($packageName, null, null, 'stable');
$this->assertSame($package2, $best, 'Latest version should be returned (1.1.0-beta)');
@@ -179,12 +179,12 @@ class VersionSelectorTest extends TestCase
public function testFalseReturnedOnNoPackages()
{
- $pool = $this->createMockPool();
- $pool->expects($this->once())
- ->method('whatProvides')
+ $repositorySet = $this->createMockRepositorySet();
+ $repositorySet->expects($this->once())
+ ->method('findPackages')
->will($this->returnValue(array()));
- $versionSelector = new VersionSelector($pool);
+ $versionSelector = new VersionSelector($repositorySet);
$best = $versionSelector->findBestCandidate('foobaz');
$this->assertFalse($best, 'No versions are available returns false');
}
@@ -194,8 +194,8 @@ class VersionSelectorTest extends TestCase
*/
public function testFindRecommendedRequireVersion($prettyVersion, $isDev, $stability, $expectedVersion, $branchAlias = null)
{
- $pool = $this->createMockPool();
- $versionSelector = new VersionSelector($pool);
+ $repositorySet = $this->createMockRepositorySet();
+ $versionSelector = new VersionSelector($repositorySet);
$versionParser = new VersionParser();
$package = $this->getMockBuilder('\Composer\Package\PackageInterface')->getMock();
@@ -273,8 +273,10 @@ class VersionSelectorTest extends TestCase
return new Package('foo', $parser->normalize($version), $version);
}
- private function createMockPool()
+ private function createMockRepositorySet()
{
- return $this->getMockBuilder('Composer\DependencyResolver\Pool')->getMock();
+ return $this->getMockBuilder('Composer\Repository\RepositorySet')
+ ->disableOriginalConstructor()
+ ->getMock();
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v1/Installer/Plugin.php b/tests/Composer/Test/Plugin/Fixtures/plugin-v1/Installer/Plugin.php
index f80acd325..c757d4b09 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v1/Installer/Plugin.php
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v1/Installer/Plugin.php
@@ -12,5 +12,16 @@ class Plugin implements PluginInterface
public function activate(Composer $composer, IOInterface $io)
{
+ $io->write('activate v1');
+ }
+
+ public function deactivate(Composer $composer, IOInterface $io)
+ {
+ $io->write('deactivate v1');
+ }
+
+ public function uninstall(Composer $composer, IOInterface $io)
+ {
+ $io->write('uninstall v1');
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v1/composer.json b/tests/Composer/Test/Plugin/Fixtures/plugin-v1/composer.json
index 574c4402f..335f772c9 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v1/composer.json
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v1/composer.json
@@ -7,6 +7,6 @@
"class": "Installer\\Plugin"
},
"require": {
- "composer-plugin-api": "^1.0"
+ "composer-plugin-api": "^2.0"
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v2/Installer/Plugin2.php b/tests/Composer/Test/Plugin/Fixtures/plugin-v2/Installer/Plugin2.php
index db5a4462e..32090b66d 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v2/Installer/Plugin2.php
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v2/Installer/Plugin2.php
@@ -12,5 +12,16 @@ class Plugin2 implements PluginInterface
public function activate(Composer $composer, IOInterface $io)
{
+ $io->write('activate v2');
+ }
+
+ public function deactivate(Composer $composer, IOInterface $io)
+ {
+ $io->write('deactivate v2');
+ }
+
+ public function uninstall(Composer $composer, IOInterface $io)
+ {
+ $io->write('uninstall v2');
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v2/composer.json b/tests/Composer/Test/Plugin/Fixtures/plugin-v2/composer.json
index 27432acfa..4104f4be6 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v2/composer.json
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v2/composer.json
@@ -7,6 +7,6 @@
"class": "Installer\\Plugin2"
},
"require": {
- "composer-plugin-api": "^1.0"
+ "composer-plugin-api": "^2.0"
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v3/Installer/Plugin2.php b/tests/Composer/Test/Plugin/Fixtures/plugin-v3/Installer/Plugin2.php
index 861c1679b..034388162 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v3/Installer/Plugin2.php
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v3/Installer/Plugin2.php
@@ -12,5 +12,16 @@ class Plugin2 implements PluginInterface
public function activate(Composer $composer, IOInterface $io)
{
+ $io->write('activate v3');
+ }
+
+ public function deactivate(Composer $composer, IOInterface $io)
+ {
+ $io->write('deactivate v3');
+ }
+
+ public function uninstall(Composer $composer, IOInterface $io)
+ {
+ $io->write('uninstall v3');
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v3/composer.json b/tests/Composer/Test/Plugin/Fixtures/plugin-v3/composer.json
index 881eb5cae..ee087e2d7 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v3/composer.json
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v3/composer.json
@@ -7,6 +7,6 @@
"class": "Installer\\Plugin2"
},
"require": {
- "composer-plugin-api": "^1.0"
+ "composer-plugin-api": "^2.0"
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v4/Installer/Plugin1.php b/tests/Composer/Test/Plugin/Fixtures/plugin-v4/Installer/Plugin1.php
index 93bcabc98..2eaee6a3f 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v4/Installer/Plugin1.php
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v4/Installer/Plugin1.php
@@ -13,5 +13,16 @@ class Plugin1 implements PluginInterface
public function activate(Composer $composer, IOInterface $io)
{
+ $io->write('activate v4-plugin1');
+ }
+
+ public function deactivate(Composer $composer, IOInterface $io)
+ {
+ $io->write('deactivate v4-plugin1');
+ }
+
+ public function uninstall(Composer $composer, IOInterface $io)
+ {
+ $io->write('uninstall v4-plugin1');
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v4/Installer/Plugin2.php b/tests/Composer/Test/Plugin/Fixtures/plugin-v4/Installer/Plugin2.php
index d946deb89..3c5311a82 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v4/Installer/Plugin2.php
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v4/Installer/Plugin2.php
@@ -13,5 +13,16 @@ class Plugin2 implements PluginInterface
public function activate(Composer $composer, IOInterface $io)
{
+ $io->write('activate v4-plugin2');
+ }
+
+ public function deactivate(Composer $composer, IOInterface $io)
+ {
+ $io->write('deactivate v4-plugin2');
+ }
+
+ public function uninstall(Composer $composer, IOInterface $io)
+ {
+ $io->write('uninstall v4-plugin2');
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v4/composer.json b/tests/Composer/Test/Plugin/Fixtures/plugin-v4/composer.json
index f61cb3fbd..a349ccc2c 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v4/composer.json
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v4/composer.json
@@ -10,6 +10,6 @@
]
},
"require": {
- "composer-plugin-api": "^1.0"
+ "composer-plugin-api": "^2.0"
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v5/Installer/Plugin5.php b/tests/Composer/Test/Plugin/Fixtures/plugin-v5/Installer/Plugin5.php
index a2ac37bc5..fb9f08a6d 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v5/Installer/Plugin5.php
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v5/Installer/Plugin5.php
@@ -10,5 +10,16 @@ class Plugin5 implements PluginInterface
{
public function activate(Composer $composer, IOInterface $io)
{
+ $io->write('activate v5');
+ }
+
+ public function deactivate(Composer $composer, IOInterface $io)
+ {
+ $io->write('deactivate v5');
+ }
+
+ public function uninstall(Composer $composer, IOInterface $io)
+ {
+ $io->write('uninstall v5');
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v6/Installer/Plugin6.php b/tests/Composer/Test/Plugin/Fixtures/plugin-v6/Installer/Plugin6.php
index e46c0fcb0..acce1f972 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v6/Installer/Plugin6.php
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v6/Installer/Plugin6.php
@@ -10,5 +10,16 @@ class Plugin6 implements PluginInterface
{
public function activate(Composer $composer, IOInterface $io)
{
+ $io->write('activate v6');
+ }
+
+ public function deactivate(Composer $composer, IOInterface $io)
+ {
+ $io->write('deactivate v6');
+ }
+
+ public function uninstall(Composer $composer, IOInterface $io)
+ {
+ $io->write('uninstall v6');
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v7/Installer/Plugin7.php b/tests/Composer/Test/Plugin/Fixtures/plugin-v7/Installer/Plugin7.php
index 5560a6047..84734ce3b 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v7/Installer/Plugin7.php
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v7/Installer/Plugin7.php
@@ -10,5 +10,16 @@ class Plugin7 implements PluginInterface
{
public function activate(Composer $composer, IOInterface $io)
{
+ $io->write('activate v7');
+ }
+
+ public function deactivate(Composer $composer, IOInterface $io)
+ {
+ $io->write('deactivate v7');
+ }
+
+ public function uninstall(Composer $composer, IOInterface $io)
+ {
+ $io->write('uninstall v7');
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v8/Installer/Plugin8.php b/tests/Composer/Test/Plugin/Fixtures/plugin-v8/Installer/Plugin8.php
index 7e9a0aab1..4534e13ef 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v8/Installer/Plugin8.php
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v8/Installer/Plugin8.php
@@ -13,6 +13,17 @@ class Plugin8 implements PluginInterface, Capable
public function activate(Composer $composer, IOInterface $io)
{
+ $io->write('activate v8');
+ }
+
+ public function deactivate(Composer $composer, IOInterface $io)
+ {
+ $io->write('deactivate v8');
+ }
+
+ public function uninstall(Composer $composer, IOInterface $io)
+ {
+ $io->write('uninstall v8');
}
public function getCapabilities()
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v8/composer.json b/tests/Composer/Test/Plugin/Fixtures/plugin-v8/composer.json
index 799df2e61..aa44b5a3d 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v8/composer.json
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v8/composer.json
@@ -9,6 +9,6 @@
]
},
"require": {
- "composer-plugin-api": "1.1.0"
+ "composer-plugin-api": "2.0.0"
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v9/Installer/Plugin.php b/tests/Composer/Test/Plugin/Fixtures/plugin-v9/Installer/Plugin.php
index 74e1beb8b..870f11cd1 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v9/Installer/Plugin.php
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v9/Installer/Plugin.php
@@ -14,5 +14,16 @@ class Plugin implements PluginInterface
public function activate(Composer $composer, IOInterface $io)
{
+ $io->write('activate v9');
+ }
+
+ public function deactivate(Composer $composer, IOInterface $io)
+ {
+ $io->write('deactivate v9');
+ }
+
+ public function uninstall(Composer $composer, IOInterface $io)
+ {
+ $io->write('uninstall v9');
}
}
diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v9/composer.json b/tests/Composer/Test/Plugin/Fixtures/plugin-v9/composer.json
index f3ccb9397..45d8d794b 100644
--- a/tests/Composer/Test/Plugin/Fixtures/plugin-v9/composer.json
+++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v9/composer.json
@@ -7,6 +7,6 @@
"class": "Installer\\Plugin"
},
"require": {
- "composer-plugin-api": "^1.0"
+ "composer-plugin-api": "^2.0"
}
}
diff --git a/tests/Composer/Test/Plugin/PluginInstallerTest.php b/tests/Composer/Test/Plugin/PluginInstallerTest.php
index d56e054fb..b73907b48 100644
--- a/tests/Composer/Test/Plugin/PluginInstallerTest.php
+++ b/tests/Composer/Test/Plugin/PluginInstallerTest.php
@@ -19,6 +19,9 @@ use Composer\Package\CompletePackage;
use Composer\Package\Loader\JsonLoader;
use Composer\Package\Loader\ArrayLoader;
use Composer\Plugin\PluginManager;
+use Symfony\Component\Console\Output\OutputInterface;
+use Composer\IO\BufferIO;
+use Composer\EventDispatcher\EventDispatcher;
use Composer\Autoload\AutoloadGenerator;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
@@ -89,14 +92,14 @@ class PluginInstallerTest extends TestCase
->method('getLocalRepository')
->will($this->returnValue($this->repository));
- $im = $this->getMockBuilder('Composer\Installer\InstallationManager')->getMock();
+ $im = $this->getMockBuilder('Composer\Installer\InstallationManager')->disableOriginalConstructor()->getMock();
$im->expects($this->any())
->method('getInstallPath')
->will($this->returnCallback(function ($package) {
return __DIR__.'/Fixtures/'.$package->getPrettyName();
}));
- $this->io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
+ $this->io = new BufferIO();
$dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock();
$this->autoloadGenerator = new AutoloadGenerator($dispatcher);
@@ -108,6 +111,7 @@ class PluginInstallerTest extends TestCase
$this->composer->setRepositoryManager($rm);
$this->composer->setInstallationManager($im);
$this->composer->setAutoloadGenerator($this->autoloadGenerator);
+ $this->composer->setEventDispatcher(new EventDispatcher($this->composer, $this->io));
$this->pm = new PluginManager($this->io, $this->composer);
$this->composer->setPluginManager($this->pm);
@@ -130,7 +134,7 @@ class PluginInstallerTest extends TestCase
public function testInstallNewPlugin()
{
$this->repository
- ->expects($this->exactly(2))
+ ->expects($this->any())
->method('getPackages')
->will($this->returnValue(array()));
$installer = new PluginInstaller($this->io, $this->composer);
@@ -140,12 +144,13 @@ class PluginInstallerTest extends TestCase
$plugins = $this->pm->getPlugins();
$this->assertEquals('installer-v1', $plugins[0]->version);
+ $this->assertEquals('activate v1'.PHP_EOL, $this->io->getOutput());
}
public function testInstallMultiplePlugins()
{
$this->repository
- ->expects($this->exactly(2))
+ ->expects($this->any())
->method('getPackages')
->will($this->returnValue(array($this->packages[3])));
$installer = new PluginInstaller($this->io, $this->composer);
@@ -158,12 +163,13 @@ class PluginInstallerTest extends TestCase
$this->assertEquals('installer-v4', $plugins[0]->version);
$this->assertEquals('plugin2', $plugins[1]->name);
$this->assertEquals('installer-v4', $plugins[1]->version);
+ $this->assertEquals('activate v4-plugin1'.PHP_EOL.'activate v4-plugin2'.PHP_EOL, $this->io->getOutput());
}
public function testUpgradeWithNewClassName()
{
$this->repository
- ->expects($this->exactly(3))
+ ->expects($this->any())
->method('getPackages')
->will($this->returnValue(array($this->packages[0])));
$this->repository
@@ -176,13 +182,35 @@ class PluginInstallerTest extends TestCase
$installer->update($this->repository, $this->packages[0], $this->packages[1]);
$plugins = $this->pm->getPlugins();
+ $this->assertCount(1, $plugins);
$this->assertEquals('installer-v2', $plugins[1]->version);
+ $this->assertEquals('activate v1'.PHP_EOL.'deactivate v1'.PHP_EOL.'activate v2'.PHP_EOL, $this->io->getOutput());
+ }
+
+ public function testUninstall()
+ {
+ $this->repository
+ ->expects($this->any())
+ ->method('getPackages')
+ ->will($this->returnValue(array($this->packages[0])));
+ $this->repository
+ ->expects($this->exactly(1))
+ ->method('hasPackage')
+ ->will($this->onConsecutiveCalls(true, false));
+ $installer = new PluginInstaller($this->io, $this->composer);
+ $this->pm->loadInstalledPlugins();
+
+ $installer->uninstall($this->repository, $this->packages[0]);
+
+ $plugins = $this->pm->getPlugins();
+ $this->assertCount(0, $plugins);
+ $this->assertEquals('activate v1'.PHP_EOL.'deactivate v1'.PHP_EOL.'uninstall v1'.PHP_EOL, $this->io->getOutput());
}
public function testUpgradeWithSameClassName()
{
$this->repository
- ->expects($this->exactly(3))
+ ->expects($this->any())
->method('getPackages')
->will($this->returnValue(array($this->packages[1])));
$this->repository
@@ -196,12 +224,13 @@ class PluginInstallerTest extends TestCase
$plugins = $this->pm->getPlugins();
$this->assertEquals('installer-v3', $plugins[1]->version);
+ $this->assertEquals('activate v2'.PHP_EOL.'deactivate v2'.PHP_EOL.'activate v3'.PHP_EOL, $this->io->getOutput());
}
public function testRegisterPluginOnlyOneTime()
{
$this->repository
- ->expects($this->exactly(2))
+ ->expects($this->any())
->method('getPackages')
->will($this->returnValue(array()));
$installer = new PluginInstaller($this->io, $this->composer);
@@ -213,6 +242,7 @@ class PluginInstallerTest extends TestCase
$plugins = $this->pm->getPlugins();
$this->assertCount(1, $plugins);
$this->assertEquals('installer-v1', $plugins[0]->version);
+ $this->assertEquals('activate v1'.PHP_EOL, $this->io->getOutput());
}
/**
@@ -240,11 +270,11 @@ class PluginInstallerTest extends TestCase
// Add the plugins to the repo along with the internal Plugin package on which they all rely.
$this->repository
- ->expects($this->any())
- ->method('getPackages')
- ->will($this->returnCallback(function () use ($plugApiInternalPackage, $plugins) {
- return array_merge(array($plugApiInternalPackage), $plugins);
- }));
+ ->expects($this->any())
+ ->method('getPackages')
+ ->will($this->returnCallback(function () use ($plugApiInternalPackage, $plugins) {
+ return array_merge(array($plugApiInternalPackage), $plugins);
+ }));
$this->pm->loadInstalledPlugins();
}
@@ -300,7 +330,7 @@ class PluginInstallerTest extends TestCase
public function testCommandProviderCapability()
{
$this->repository
- ->expects($this->exactly(2))
+ ->expects($this->any())
->method('getPackages')
->will($this->returnValue(array($this->packages[7])));
$installer = new PluginInstaller($this->io, $this->composer);
diff --git a/tests/Composer/Test/Repository/ComposerRepositoryTest.php b/tests/Composer/Test/Repository/ComposerRepositoryTest.php
index 4ec826334..1915389c4 100644
--- a/tests/Composer/Test/Repository/ComposerRepositoryTest.php
+++ b/tests/Composer/Test/Repository/ComposerRepositoryTest.php
@@ -18,7 +18,7 @@ use Composer\Repository\RepositoryInterface;
use Composer\Test\Mock\FactoryMock;
use Composer\Test\TestCase;
use Composer\Package\Loader\ArrayLoader;
-use Composer\Semver\VersionParser;
+use Composer\Package\Version\VersionParser;
class ComposerRepositoryTest extends TestCase
{
@@ -32,11 +32,13 @@ class ComposerRepositoryTest extends TestCase
);
$repository = $this->getMockBuilder('Composer\Repository\ComposerRepository')
- ->setMethods(array('loadRootServerFile', 'createPackage'))
+ ->setMethods(array('loadRootServerFile', 'createPackages'))
->setConstructorArgs(array(
$repoConfig,
new NullIO,
FactoryMock::createConfig(),
+ $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
+ $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock()
))
->getMock();
@@ -45,16 +47,17 @@ class ComposerRepositoryTest extends TestCase
->method('loadRootServerFile')
->will($this->returnValue($repoPackages));
+ $stubs = array();
foreach ($expected as $at => $arg) {
- $stubPackage = $this->getPackage('stub/stub', '1.0.0');
-
- $repository
- ->expects($this->at($at + 2))
- ->method('createPackage')
- ->with($this->identicalTo($arg), $this->equalTo('Composer\Package\CompletePackage'))
- ->will($this->returnValue($stubPackage));
+ $stubs[] = $this->getPackage('stub/stub', '1.0.0');
}
+ $repository
+ ->expects($this->at(2))
+ ->method('createPackages')
+ ->with($this->identicalTo($expected), $this->equalTo('Composer\Package\CompletePackage'))
+ ->will($this->returnValue($stubs));
+
// Triggers initialization
$packages = $repository->getPackages();
@@ -96,7 +99,13 @@ class ComposerRepositoryTest extends TestCase
public function testWhatProvides()
{
$repo = $this->getMockBuilder('Composer\Repository\ComposerRepository')
- ->disableOriginalConstructor()
+ ->setConstructorArgs(array(
+ array('url' => 'https://dummy.test.link'),
+ new NullIO,
+ FactoryMock::createConfig(),
+ $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
+ $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock()
+ ))
->setMethods(array('fetchFile'))
->getMock();
@@ -142,28 +151,21 @@ class ComposerRepositoryTest extends TestCase
),
)));
- $pool = $this->getMockBuilder('Composer\DependencyResolver\Pool')->getMock();
- $pool->expects($this->any())
- ->method('isPackageAcceptable')
- ->will($this->returnValue(true));
-
$versionParser = new VersionParser();
- $repo->setRootAliases(array(
- 'a' => array(
- $versionParser->normalize('0.6') => array('alias' => 'dev-feature', 'alias_normalized' => $versionParser->normalize('dev-feature')),
- $versionParser->normalize('1.1.x-dev') => array('alias' => '1.0', 'alias_normalized' => $versionParser->normalize('1.0')),
- ),
- ));
+ $reflMethod = new \ReflectionMethod($repo, 'whatProvides');
+ $reflMethod->setAccessible(true);
+ $packages = $reflMethod->invoke($repo, 'a', array($this, 'isPackageAcceptableReturnTrue'));
- $packages = $repo->whatProvides($pool, 'a');
-
- $this->assertCount(7, $packages);
- $this->assertEquals(array('1', '1-alias', '2', '2-alias', '2-root', '3', '3-root'), array_keys($packages));
- $this->assertInstanceOf('Composer\Package\AliasPackage', $packages['2-root']);
- $this->assertSame($packages['2'], $packages['2-root']->getAliasOf());
+ $this->assertCount(5, $packages);
+ $this->assertEquals(array('1', '1-alias', '2', '2-alias', '3'), array_keys($packages));
$this->assertSame($packages['2'], $packages['2-alias']->getAliasOf());
}
+ public function isPackageAcceptableReturnTrue()
+ {
+ return true;
+ }
+
public function testSearchWithType()
{
$repoConfig = array(
@@ -179,21 +181,29 @@ class ComposerRepositoryTest extends TestCase
),
);
- $rfs = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $eventDispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
->disableOriginalConstructor()
->getMock();
- $rfs->expects($this->at(0))
- ->method('getContents')
- ->with('example.org', 'http://example.org/packages.json', false)
- ->willReturn(json_encode(array('search' => '/search.json?q=%query%&type=%type%')));
+ $httpDownloader->expects($this->at(0))
+ ->method('get')
+ ->with($url = 'http://example.org/packages.json')
+ ->willReturn(new \Composer\Util\Http\Response(array('url' => $url), 200, array(), json_encode(array('search' => '/search.json?q=%query%&type=%type%'))));
- $rfs->expects($this->at(1))
- ->method('getContents')
- ->with('example.org', 'http://example.org/search.json?q=foo&type=composer-plugin', false)
- ->willReturn(json_encode($result));
+ $httpDownloader->expects($this->at(1))
+ ->method('get')
+ ->with($url = 'http://example.org/search.json?q=foo&type=composer-plugin')
+ ->willReturn(new \Composer\Util\Http\Response(array('url' => $url), 200, array(), json_encode($result)));
- $repository = new ComposerRepository($repoConfig, new NullIO, FactoryMock::createConfig(), null, $rfs);
+ $httpDownloader->expects($this->at(2))
+ ->method('get')
+ ->with($url = 'http://example.org/search.json?q=foo&type=library')
+ ->willReturn(new \Composer\Util\Http\Response(array('url' => $url), 200, array(), json_encode(array())));
+
+ $repository = new ComposerRepository($repoConfig, new NullIO, FactoryMock::createConfig(), $httpDownloader, $eventDispatcher);
$this->assertSame(
array(array('name' => 'foo', 'description' => null)),
@@ -217,7 +227,9 @@ class ComposerRepositoryTest extends TestCase
$repository = new ComposerRepository(
array('url' => $repositoryUrl),
new NullIO(),
- FactoryMock::createConfig()
+ FactoryMock::createConfig(),
+ $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
+ $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock()
);
$object = new \ReflectionObject($repository);
@@ -274,30 +286,28 @@ class ComposerRepositoryTest extends TestCase
public function testGetProviderNamesWillReturnPartialPackageNames()
{
- $rfs = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
->disableOriginalConstructor()
->getMock();
- $rfs->expects($this->at(0))
- ->method('getContents')
- ->with('example.org', 'http://example.org/packages.json', false)
- ->willReturn(json_encode(array(
+ $httpDownloader->expects($this->at(0))
+ ->method('get')
+ ->with($url = 'http://example.org/packages.json')
+ ->willReturn(new \Composer\Util\Http\Response(array('url' => $url), 200, array(), json_encode(array(
'providers-lazy-url' => '/foo/p/%package%.json',
'packages' => array('foo/bar' => array(
- 'dev-branch' => array(),
- 'v1.0.0' => array(),
+ 'dev-branch' => array('name' => 'foo/bar'),
+ 'v1.0.0' => array('name' => 'foo/bar'),
))
- )));
+ ))));
$repository = new ComposerRepository(
array('url' => 'http://example.org/packages.json'),
new NullIO(),
FactoryMock::createConfig(),
- null,
- $rfs
+ $httpDownloader
);
- $this->assertTrue($repository->hasProviders());
- $this->assertEquals(array('foo/bar'), $repository->getProviderNames());
+ $this->assertEquals(array('foo/bar'), $repository->getPackageNames());
}
}
diff --git a/tests/Composer/Test/Repository/FilesystemRepositoryTest.php b/tests/Composer/Test/Repository/FilesystemRepositoryTest.php
index be8b0d0a9..97747ebc5 100644
--- a/tests/Composer/Test/Repository/FilesystemRepositoryTest.php
+++ b/tests/Composer/Test/Repository/FilesystemRepositoryTest.php
@@ -82,11 +82,21 @@ class FilesystemRepositoryTest extends TestCase
$json = $this->createJsonFileMock();
$repository = new FilesystemRepository($json);
+ $im = $this->getMockBuilder('Composer\Installer\InstallationManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $im->expects($this->once())
+ ->method('getInstallPath')
+ ->will($this->returnValue('/foo/bar/vendor/woop/woop'));
$json
->expects($this->once())
->method('read')
->will($this->returnValue(array()));
+ $json
+ ->expects($this->once())
+ ->method('getPath')
+ ->will($this->returnValue('/foo/bar/vendor/composer/installed.json'));
$json
->expects($this->once())
->method('exists')
@@ -95,11 +105,12 @@ class FilesystemRepositoryTest extends TestCase
->expects($this->once())
->method('write')
->with(array(
- array('name' => 'mypkg', 'type' => 'library', 'version' => '0.1.10', 'version_normalized' => '0.1.10.0'),
+ 'packages' => array(array('name' => 'mypkg', 'type' => 'library', 'version' => '0.1.10', 'version_normalized' => '0.1.10.0', 'install-path' => '../woop/woop')),
+ 'dev' => true,
));
$repository->addPackage($this->getPackage('mypkg', '0.1.10'));
- $repository->write();
+ $repository->write(true, $im);
}
private function createJsonFileMock()
diff --git a/tests/Composer/Test/Repository/InstalledRepositoryTest.php b/tests/Composer/Test/Repository/InstalledRepositoryTest.php
new file mode 100644
index 000000000..a37adb058
--- /dev/null
+++ b/tests/Composer/Test/Repository/InstalledRepositoryTest.php
@@ -0,0 +1,51 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Test\Repository;
+
+use Composer\Repository\InstalledRepository;
+use Composer\Repository\ArrayRepository;
+use Composer\Repository\InstalledArrayRepository;
+use Composer\Package\Link;
+use Composer\Test\TestCase;
+
+class InstalledRepositoryTest extends TestCase
+{
+ public function testFindPackagesWithReplacersAndProviders()
+ {
+ $arrayRepoOne = new InstalledArrayRepository;
+ $arrayRepoOne->addPackage($foo = $this->getPackage('foo', '1'));
+ $arrayRepoOne->addPackage($foo2 = $this->getPackage('foo', '2'));
+
+ $arrayRepoTwo = new InstalledArrayRepository;
+ $arrayRepoTwo->addPackage($bar = $this->getPackage('bar', '1'));
+ $arrayRepoTwo->addPackage($bar2 = $this->getPackage('bar', '2'));
+
+ $foo->setReplaces(array(new Link('foo', 'provided')));
+ $bar2->setProvides(array(new Link('bar', 'provided')));
+
+ $repo = new InstalledRepository(array($arrayRepoOne, $arrayRepoTwo));
+
+ $this->assertEquals(array($foo2), $repo->findPackagesWithReplacersAndProviders('foo', '2'));
+ $this->assertEquals(array($bar), $repo->findPackagesWithReplacersAndProviders('bar', '1'));
+ $this->assertEquals(array($foo, $bar2), $repo->findPackagesWithReplacersAndProviders('provided'));
+ }
+
+ public function testAddRepository()
+ {
+ $arrayRepoOne = new ArrayRepository;
+
+ $this->setExpectedException('LogicException');
+
+ new InstalledRepository(array($arrayRepoOne));
+ }
+}
diff --git a/tests/Composer/Test/Repository/PathRepositoryTest.php b/tests/Composer/Test/Repository/PathRepositoryTest.php
index 159d8cc97..f1c394156 100644
--- a/tests/Composer/Test/Repository/PathRepositoryTest.php
+++ b/tests/Composer/Test/Repository/PathRepositoryTest.php
@@ -14,8 +14,8 @@ namespace Composer\Test\Repository;
use Composer\Package\Loader\ArrayLoader;
use Composer\Repository\PathRepository;
-use Composer\Semver\VersionParser;
use Composer\Test\TestCase;
+use Composer\Package\Version\VersionParser;
class PathRepositoryTest extends TestCase
{
@@ -47,7 +47,7 @@ class PathRepositoryTest extends TestCase
$repository = new PathRepository(array('url' => $repositoryUrl), $ioInterface, $config);
$repository->getPackages();
- $this->assertEquals(1, $repository->count());
+ $this->assertSame(1, $repository->count());
$this->assertTrue($repository->hasPackage($this->getPackage('test/path-versioned', '0.0.2')));
}
@@ -63,10 +63,10 @@ class PathRepositoryTest extends TestCase
$repository = new PathRepository(array('url' => $repositoryUrl), $ioInterface, $config);
$packages = $repository->getPackages();
- $this->assertEquals(1, $repository->count());
+ $this->assertGreaterThanOrEqual(1, $repository->count());
$package = $packages[0];
- $this->assertEquals('test/path-unversioned', $package->getName());
+ $this->assertSame('test/path-unversioned', $package->getName());
$packageVersion = $package->getVersion();
$this->assertNotEmpty($packageVersion);
@@ -85,16 +85,16 @@ class PathRepositoryTest extends TestCase
$packages = $repository->getPackages();
$names = array();
- $this->assertEquals(2, $repository->count());
+ $this->assertGreaterThanOrEqual(2, $repository->count());
$package = $packages[0];
$names[] = $package->getName();
- $package = $packages[1];
+ $package = $packages[count($packages) - 1];
$names[] = $package->getName();
sort($names);
- $this->assertEquals(array('test/path-unversioned', 'test/path-versioned'), $names);
+ $this->assertSame(array('test/path-unversioned', 'test/path-versioned'), $names);
}
/**
@@ -118,13 +118,13 @@ class PathRepositoryTest extends TestCase
$repository = new PathRepository(array('url' => $relativeUrl), $ioInterface, $config);
$packages = $repository->getPackages();
- $this->assertEquals(1, $repository->count());
+ $this->assertSame(1, $repository->count());
$package = $packages[0];
- $this->assertEquals('test/path-versioned', $package->getName());
+ $this->assertSame('test/path-versioned', $package->getName());
// Convert platform specific separators back to generic URL slashes
$relativeUrl = str_replace(DIRECTORY_SEPARATOR, '/', $relativeUrl);
- $this->assertEquals($relativeUrl, $package->getDistUrl());
+ $this->assertSame($relativeUrl, $package->getDistUrl());
}
}
diff --git a/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php b/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php
index 74e3c0c25..2e2fe6933 100644
--- a/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php
+++ b/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php
@@ -22,19 +22,19 @@ use Composer\Semver\VersionParser;
use Composer\Semver\Constraint\Constraint;
use Composer\Package\Link;
use Composer\Package\CompletePackage;
-use Composer\Test\Mock\RemoteFilesystemMock;
+use Composer\Test\Mock\HttpDownloaderMock;
class ChannelReaderTest extends TestCase
{
public function testShouldBuildPackagesFromPearSchema()
{
- $rfs = new RemoteFilesystemMock(array(
+ $httpDownloader = new HttpDownloaderMock(array(
'http://pear.net/channel.xml' => file_get_contents(__DIR__ . '/Fixtures/channel.1.1.xml'),
'http://test.loc/rest11/c/categories.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/categories.xml'),
'http://test.loc/rest11/c/Default/packagesinfo.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/packagesinfo.xml'),
));
- $reader = new \Composer\Repository\Pear\ChannelReader($rfs);
+ $reader = new \Composer\Repository\Pear\ChannelReader($httpDownloader);
$channelInfo = $reader->read('http://pear.net/');
$packages = $channelInfo->getPackages();
@@ -50,17 +50,21 @@ class ChannelReaderTest extends TestCase
public function testShouldSelectCorrectReader()
{
- $rfs = new RemoteFilesystemMock(array(
+ $httpDownloader = new HttpDownloaderMock(array(
'http://pear.1.0.net/channel.xml' => file_get_contents(__DIR__ . '/Fixtures/channel.1.0.xml'),
'http://test.loc/rest10/p/packages.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/packages.xml'),
'http://test.loc/rest10/p/http_client/info.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_info.xml'),
+ 'http://test.loc/rest10/r/http_client/allreleases.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_allreleases.xml'),
+ 'http://test.loc/rest10/r/http_client/deps.1.2.1.txt' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_deps.1.2.1.txt'),
'http://test.loc/rest10/p/http_request/info.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_request_info.xml'),
+ 'http://test.loc/rest10/r/http_request/allreleases.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_request_allreleases.xml'),
+ 'http://test.loc/rest10/r/http_request/deps.1.4.0.txt' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_request_deps.1.4.0.txt'),
'http://pear.1.1.net/channel.xml' => file_get_contents(__DIR__ . '/Fixtures/channel.1.1.xml'),
'http://test.loc/rest11/c/categories.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/categories.xml'),
'http://test.loc/rest11/c/Default/packagesinfo.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/packagesinfo.xml'),
));
- $reader = new \Composer\Repository\Pear\ChannelReader($rfs);
+ $reader = new \Composer\Repository\Pear\ChannelReader($httpDownloader);
$pear10 = $reader->read('http://pear.1.0.net/');
$this->assertCount(2, $pear10->getPackages());
diff --git a/tests/Composer/Test/Repository/Pear/ChannelRest10ReaderTest.php b/tests/Composer/Test/Repository/Pear/ChannelRest10ReaderTest.php
index 4aa7bbba2..3960c7858 100644
--- a/tests/Composer/Test/Repository/Pear/ChannelRest10ReaderTest.php
+++ b/tests/Composer/Test/Repository/Pear/ChannelRest10ReaderTest.php
@@ -13,13 +13,13 @@
namespace Composer\Test\Repository\Pear;
use Composer\Test\TestCase;
-use Composer\Test\Mock\RemoteFilesystemMock;
+use Composer\Test\Mock\HttpDownloaderMock;
class ChannelRest10ReaderTest extends TestCase
{
public function testShouldBuildPackagesFromPearSchema()
{
- $rfs = new RemoteFilesystemMock(array(
+ $httpDownloader = new HttpDownloaderMock(array(
'http://test.loc/rest10/p/packages.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/packages.xml'),
'http://test.loc/rest10/p/http_client/info.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_info.xml'),
'http://test.loc/rest10/r/http_client/allreleases.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_allreleases.xml'),
@@ -29,7 +29,7 @@ class ChannelRest10ReaderTest extends TestCase
'http://test.loc/rest10/r/http_request/deps.1.4.0.txt' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_request_deps.1.4.0.txt'),
));
- $reader = new \Composer\Repository\Pear\ChannelRest10Reader($rfs);
+ $reader = new \Composer\Repository\Pear\ChannelRest10Reader($httpDownloader);
/** @var \Composer\Package\PackageInterface[] $packages */
$packages = $reader->read('http://test.loc/rest10');
diff --git a/tests/Composer/Test/Repository/Pear/ChannelRest11ReaderTest.php b/tests/Composer/Test/Repository/Pear/ChannelRest11ReaderTest.php
index 04e48426e..684c59155 100644
--- a/tests/Composer/Test/Repository/Pear/ChannelRest11ReaderTest.php
+++ b/tests/Composer/Test/Repository/Pear/ChannelRest11ReaderTest.php
@@ -13,19 +13,19 @@
namespace Composer\Test\Repository\Pear;
use Composer\Test\TestCase;
-use Composer\Test\Mock\RemoteFilesystemMock;
+use Composer\Test\Mock\HttpDownloaderMock;
class ChannelRest11ReaderTest extends TestCase
{
public function testShouldBuildPackagesFromPearSchema()
{
- $rfs = new RemoteFilesystemMock(array(
+ $httpDownloader = new HttpDownloaderMock(array(
'http://pear.1.1.net/channel.xml' => file_get_contents(__DIR__ . '/Fixtures/channel.1.1.xml'),
'http://test.loc/rest11/c/categories.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/categories.xml'),
'http://test.loc/rest11/c/Default/packagesinfo.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/packagesinfo.xml'),
));
- $reader = new \Composer\Repository\Pear\ChannelRest11Reader($rfs);
+ $reader = new \Composer\Repository\Pear\ChannelRest11Reader($httpDownloader);
/** @var \Composer\Package\PackageInterface[] $packages */
$packages = $reader->read('http://test.loc/rest11');
diff --git a/tests/Composer/Test/Repository/Pear/PackageDependencyParserTest.php b/tests/Composer/Test/Repository/Pear/PackageDependencyParserTest.php
index 0ca9259d9..f8cf3efca 100644
--- a/tests/Composer/Test/Repository/Pear/PackageDependencyParserTest.php
+++ b/tests/Composer/Test/Repository/Pear/PackageDependencyParserTest.php
@@ -53,7 +53,7 @@ class PackageDependencyParserTest extends TestCase
{
$data = json_decode(file_get_contents(__DIR__.'/Fixtures/DependencyParserTestData.json'), true);
if (0 !== json_last_error()) {
- throw new \PHPUnit_Framework_Exception('Invalid json file.');
+ throw new \PHPUnit\Framework\Exception('Invalid json file.');
}
return $data;
diff --git a/tests/Composer/Test/Repository/PearRepositoryTest.php b/tests/Composer/Test/Repository/PearRepositoryTest.php
index b1a3c0b5e..867d4978d 100644
--- a/tests/Composer/Test/Repository/PearRepositoryTest.php
+++ b/tests/Composer/Test/Repository/PearRepositoryTest.php
@@ -28,7 +28,7 @@ class PearRepositoryTest extends TestCase
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
- private $remoteFilesystem;
+ private $httpDownloader;
public function testComposerShouldSetIncludePath()
{
@@ -133,7 +133,7 @@ class PearRepositoryTest extends TestCase
$config = new \Composer\Config();
- $this->remoteFilesystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
+ $this->httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
->disableOriginalConstructor()
->getMock();
@@ -143,6 +143,6 @@ class PearRepositoryTest extends TestCase
protected function tearDown()
{
$this->repository = null;
- $this->remoteFilesystem = null;
+ $this->httpDownloader = null;
}
}
diff --git a/tests/Composer/Test/Repository/RepositoryFactoryTest.php b/tests/Composer/Test/Repository/RepositoryFactoryTest.php
index acd666430..20b1fad28 100644
--- a/tests/Composer/Test/Repository/RepositoryFactoryTest.php
+++ b/tests/Composer/Test/Repository/RepositoryFactoryTest.php
@@ -21,7 +21,9 @@ class RepositoryFactoryTest extends TestCase
{
$manager = RepositoryFactory::manager(
$this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
- $this->getMockBuilder('Composer\Config')->getMock()
+ $this->getMockBuilder('Composer\Config')->getMock(),
+ $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
+ $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock()
);
$ref = new \ReflectionProperty($manager, 'repositoryClasses');
diff --git a/tests/Composer/Test/Repository/RepositoryManagerTest.php b/tests/Composer/Test/Repository/RepositoryManagerTest.php
index f09b55ad8..dad0bd346 100644
--- a/tests/Composer/Test/Repository/RepositoryManagerTest.php
+++ b/tests/Composer/Test/Repository/RepositoryManagerTest.php
@@ -38,6 +38,7 @@ class RepositoryManagerTest extends TestCase
$rm = new RepositoryManager(
$this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
$this->getMockBuilder('Composer\Config')->getMock(),
+ $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
$this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock()
);
@@ -61,6 +62,7 @@ class RepositoryManagerTest extends TestCase
$rm = new RepositoryManager(
$this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
$config = $this->getMockBuilder('Composer\Config')->setMethods(array('get'))->getMock(),
+ $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
$this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock()
);
diff --git a/tests/Composer/Test/Repository/Vcs/GitBitbucketDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitBitbucketDriverTest.php
index 8d711e8f0..f0139970b 100644
--- a/tests/Composer/Test/Repository/Vcs/GitBitbucketDriverTest.php
+++ b/tests/Composer/Test/Repository/Vcs/GitBitbucketDriverTest.php
@@ -16,6 +16,8 @@ use Composer\Config;
use Composer\Repository\Vcs\GitBitbucketDriver;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
+use Composer\Util\ProcessExecutor;
+use Composer\Util\Http\Response;
/**
* @group bitbucket
@@ -26,8 +28,8 @@ class GitBitbucketDriverTest extends TestCase
private $io;
/** @type \Composer\Config */
private $config;
- /** @type \Composer\Util\RemoteFilesystem|\PHPUnit_Framework_MockObject_MockObject */
- private $rfs;
+ /** @type \Composer\Util\HttpDownloader|\PHPUnit_Framework_MockObject_MockObject */
+ private $httpDownloader;
/** @type string */
private $home;
/** @type string */
@@ -46,7 +48,7 @@ class GitBitbucketDriverTest extends TestCase
),
));
- $this->rfs = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
+ $this->httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
->disableOriginalConstructor()
->getMock();
}
@@ -67,8 +69,8 @@ class GitBitbucketDriverTest extends TestCase
$repoConfig,
$this->io,
$this->config,
- null,
- $this->rfs
+ $this->httpDownloader,
+ new ProcessExecutor($this->io)
);
$driver->initialize();
@@ -83,15 +85,14 @@ class GitBitbucketDriverTest extends TestCase
'https://bitbucket.org/user/repo.git does not appear to be a git repository, use https://bitbucket.org/user/repo if this is a mercurial bitbucket repository'
);
- $this->rfs->expects($this->once())
- ->method('getContents')
+ $this->httpDownloader->expects($this->once())
+ ->method('get')
->with(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo?fields=-project%2C-owner',
- false
+ $url = 'https://api.bitbucket.org/2.0/repositories/user/repo?fields=-project%2C-owner',
+ array()
)
->willReturn(
- '{"scm":"hg","website":"","has_wiki":false,"name":"repo","links":{"branches":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/branches"},"tags":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/tags"},"clone":[{"href":"https:\/\/user@bitbucket.org\/user\/repo","name":"https"},{"href":"ssh:\/\/hg@bitbucket.org\/user\/repo","name":"ssh"}],"html":{"href":"https:\/\/bitbucket.org\/user\/repo"}},"language":"php","created_on":"2015-02-18T16:22:24.688+00:00","updated_on":"2016-05-17T13:20:21.993+00:00","is_private":true,"has_issues":false}'
+ new Response(array('url' => $url), 200, array(), '{"scm":"hg","website":"","has_wiki":false,"name":"repo","links":{"branches":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/branches"},"tags":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/tags"},"clone":[{"href":"https:\/\/user@bitbucket.org\/user\/repo","name":"https"},{"href":"ssh:\/\/hg@bitbucket.org\/user\/repo","name":"ssh"}],"html":{"href":"https:\/\/bitbucket.org\/user\/repo"}},"language":"php","created_on":"2015-02-18T16:22:24.688+00:00","updated_on":"2016-05-17T13:20:21.993+00:00","is_private":true,"has_issues":false}')
);
$driver = $this->getDriver(array('url' => 'https://bitbucket.org/user/repo.git'));
@@ -103,47 +104,43 @@ class GitBitbucketDriverTest extends TestCase
{
$driver = $this->getDriver(array('url' => 'https://bitbucket.org/user/repo.git'));
- $this->rfs->expects($this->any())
- ->method('getContents')
+ $urls = array(
+ 'https://api.bitbucket.org/2.0/repositories/user/repo?fields=-project%2C-owner',
+ 'https://api.bitbucket.org/2.0/repositories/user/repo?fields=mainbranch',
+ 'https://api.bitbucket.org/2.0/repositories/user/repo/refs/tags?pagelen=100&fields=values.name%2Cvalues.target.hash%2Cnext&sort=-target.date',
+ 'https://api.bitbucket.org/2.0/repositories/user/repo/refs/branches?pagelen=100&fields=values.name%2Cvalues.target.hash%2Cvalues.heads%2Cnext&sort=-target.date',
+ 'https://api.bitbucket.org/2.0/repositories/user/repo/src/master/composer.json',
+ 'https://api.bitbucket.org/2.0/repositories/user/repo/commit/master?fields=date',
+ );
+ $this->httpDownloader->expects($this->any())
+ ->method('get')
->withConsecutive(
array(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo?fields=-project%2C-owner',
- false,
+ $urls[0], array()
),
array(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo?fields=mainbranch',
- false,
+ $urls[1], array()
),
array(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo/refs/tags?pagelen=100&fields=values.name%2Cvalues.target.hash%2Cnext&sort=-target.date',
- false,
+ $urls[2], array()
),
array(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo/refs/branches?pagelen=100&fields=values.name%2Cvalues.target.hash%2Cvalues.heads%2Cnext&sort=-target.date',
- false,
+ $urls[3], array()
),
array(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo/src/master/composer.json',
- false,
+ $urls[4], array()
),
array(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo/commit/master?fields=date',
- false,
+ $urls[5], array()
)
)
->willReturnOnConsecutiveCalls(
- '{"scm":"git","website":"","has_wiki":false,"name":"repo","links":{"branches":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/branches"},"tags":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/tags"},"clone":[{"href":"https:\/\/user@bitbucket.org\/user\/repo.git","name":"https"},{"href":"ssh:\/\/git@bitbucket.org\/user\/repo.git","name":"ssh"}],"html":{"href":"https:\/\/bitbucket.org\/user\/repo"}},"language":"php","created_on":"2015-02-18T16:22:24.688+00:00","updated_on":"2016-05-17T13:20:21.993+00:00","is_private":true,"has_issues":false}',
- '{"mainbranch": {"name": "master"}}',
- '{"values":[{"name":"1.0.1","target":{"hash":"9b78a3932143497c519e49b8241083838c8ff8a1"}},{"name":"1.0.0","target":{"hash":"d3393d514318a9267d2f8ebbf463a9aaa389f8eb"}}]}',
- '{"values":[{"name":"master","target":{"hash":"937992d19d72b5116c3e8c4a04f960e5fa270b22"}}]}',
- '{"name": "user/repo","description": "test repo","license": "GPL","authors": [{"name": "Name","email": "local@domain.tld"}],"require": {"creator/package": "^1.0"},"require-dev": {"phpunit/phpunit": "~4.8"}}',
- '{"date": "2016-05-17T13:19:52+00:00"}'
+ new Response(array('url' => $urls[0]), 200, array(), '{"scm":"git","website":"","has_wiki":false,"name":"repo","links":{"branches":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/branches"},"tags":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/tags"},"clone":[{"href":"https:\/\/user@bitbucket.org\/user\/repo.git","name":"https"},{"href":"ssh:\/\/git@bitbucket.org\/user\/repo.git","name":"ssh"}],"html":{"href":"https:\/\/bitbucket.org\/user\/repo"}},"language":"php","created_on":"2015-02-18T16:22:24.688+00:00","updated_on":"2016-05-17T13:20:21.993+00:00","is_private":true,"has_issues":false}'),
+ new Response(array('url' => $urls[1]), 200, array(), '{"mainbranch": {"name": "master"}}'),
+ new Response(array('url' => $urls[2]), 200, array(), '{"values":[{"name":"1.0.1","target":{"hash":"9b78a3932143497c519e49b8241083838c8ff8a1"}},{"name":"1.0.0","target":{"hash":"d3393d514318a9267d2f8ebbf463a9aaa389f8eb"}}]}'),
+ new Response(array('url' => $urls[3]), 200, array(), '{"values":[{"name":"master","target":{"hash":"937992d19d72b5116c3e8c4a04f960e5fa270b22"}}]}'),
+ new Response(array('url' => $urls[4]), 200, array(), '{"name": "user/repo","description": "test repo","license": "GPL","authors": [{"name": "Name","email": "local@domain.tld"}],"require": {"creator/package": "^1.0"},"require-dev": {"phpunit/phpunit": "~4.8"}}'),
+ new Response(array('url' => $urls[5]), 200, array(), '{"date": "2016-05-17T13:19:52+00:00"}')
);
$this->assertEquals(
diff --git a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php
index 45df0ce7f..29c153d4b 100644
--- a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php
+++ b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php
@@ -16,6 +16,7 @@ use Composer\Downloader\TransportException;
use Composer\Repository\Vcs\GitHubDriver;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
+use Composer\Util\Http\Response;
use Composer\Config;
class GitHubDriverTest extends TestCase
@@ -53,8 +54,8 @@ class GitHubDriverTest extends TestCase
->method('isInteractive')
->will($this->returnValue(true));
- $remoteFilesystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
- ->setConstructorArgs(array($io))
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
+ ->setConstructorArgs(array($io, $this->config))
->getMock();
$process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
@@ -62,9 +63,9 @@ class GitHubDriverTest extends TestCase
->method('execute')
->will($this->returnValue(1));
- $remoteFilesystem->expects($this->at(0))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
+ $httpDownloader->expects($this->at(0))
+ ->method('get')
+ ->with($this->equalTo($repoApiUrl))
->will($this->throwException(new TransportException('HTTP/1.1 404 Not Found', 404)));
$io->expects($this->once())
@@ -76,15 +77,15 @@ class GitHubDriverTest extends TestCase
->method('setAuthentication')
->with($this->equalTo('github.com'), $this->matchesRegularExpression('{sometoken}'), $this->matchesRegularExpression('{x-oauth-basic}'));
- $remoteFilesystem->expects($this->at(1))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo('https://api.github.com/'), $this->equalTo(false))
- ->will($this->returnValue('{}'));
+ $httpDownloader->expects($this->at(1))
+ ->method('get')
+ ->with($this->equalTo($url = 'https://api.github.com/'))
+ ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{}')));
- $remoteFilesystem->expects($this->at(2))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
- ->will($this->returnValue('{"master_branch": "test_master", "private": true, "owner": {"login": "composer"}, "name": "packagist"}'));
+ $httpDownloader->expects($this->at(2))
+ ->method('get')
+ ->with($this->equalTo($url = $repoApiUrl))
+ ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"master_branch": "test_master", "private": true, "owner": {"login": "composer"}, "name": "packagist"}')));
$configSource = $this->getMockBuilder('Composer\Config\ConfigSourceInterface')->getMock();
$authConfigSource = $this->getMockBuilder('Composer\Config\ConfigSourceInterface')->getMock();
@@ -95,7 +96,7 @@ class GitHubDriverTest extends TestCase
'url' => $repoUrl,
);
- $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, $process, $remoteFilesystem);
+ $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, $httpDownloader, $process);
$gitHubDriver->initialize();
$this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha));
@@ -124,21 +125,25 @@ class GitHubDriverTest extends TestCase
->method('isInteractive')
->will($this->returnValue(true));
- $remoteFilesystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
- ->setConstructorArgs(array($io))
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
+ ->setConstructorArgs(array($io, $this->config))
->getMock();
- $remoteFilesystem->expects($this->at(0))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
- ->will($this->returnValue('{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}'));
+ $httpDownloader->expects($this->at(0))
+ ->method('get')
+ ->with($this->equalTo($repoApiUrl))
+ ->will($this->returnValue(new Response(array('url' => $repoApiUrl), 200, array(), '{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}')));
$repoConfig = array(
'url' => $repoUrl,
);
$repoUrl = 'https://github.com/composer/packagist.git';
- $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, null, $remoteFilesystem);
+ $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, $httpDownloader, $process);
$gitHubDriver->initialize();
$this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha));
@@ -167,31 +172,40 @@ class GitHubDriverTest extends TestCase
->method('isInteractive')
->will($this->returnValue(true));
- $remoteFilesystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
- ->setConstructorArgs(array($io))
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
+ ->setConstructorArgs(array($io, $this->config))
->getMock();
- $remoteFilesystem->expects($this->at(0))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
- ->will($this->returnValue('{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}'));
+ $httpDownloader->expects($this->at(0))
+ ->method('get')
+ ->with($this->equalTo($url = $repoApiUrl))
+ ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}')));
- $remoteFilesystem->expects($this->at(1))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo('https://api.github.com/repos/composer/packagist/contents/composer.json?ref=feature%2F3.2-foo'), $this->equalTo(false))
- ->will($this->returnValue('{"encoding":"base64","content":"'.base64_encode('{"support": {"source": "'.$repoUrl.'" }}').'"}'));
+ $httpDownloader->expects($this->at(1))
+ ->method('get')
+ ->with($this->equalTo($url = 'https://api.github.com/repos/composer/packagist/contents/composer.json?ref=feature%2F3.2-foo'))
+ ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"encoding":"base64","content":"'.base64_encode('{"support": {"source": "'.$repoUrl.'" }}').'"}')));
- $remoteFilesystem->expects($this->at(2))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo('https://api.github.com/repos/composer/packagist/commits/feature%2F3.2-foo'), $this->equalTo(false))
- ->will($this->returnValue('{"commit": {"committer":{ "date": "2012-09-10"}}}'));
+ $httpDownloader->expects($this->at(2))
+ ->method('get')
+ ->with($this->equalTo($url = 'https://api.github.com/repos/composer/packagist/commits/feature%2F3.2-foo'))
+ ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"commit": {"committer":{ "date": "2012-09-10"}}}')));
+
+ $httpDownloader->expects($this->at(3))
+ ->method('get')
+ ->with($this->equalTo($url = 'https://api.github.com/repos/composer/packagist/contents/.github/FUNDING.yml'))
+ ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"encoding": "base64", "content": "'.base64_encode("custom: https://example.com").'"}')));
$repoConfig = array(
'url' => $repoUrl,
);
$repoUrl = 'https://github.com/composer/packagist.git';
- $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, null, $remoteFilesystem);
+ $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config,$httpDownloader, $process);
$gitHubDriver->initialize();
$this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha));
@@ -225,30 +239,39 @@ class GitHubDriverTest extends TestCase
->method('isInteractive')
->will($this->returnValue(true));
- $remoteFilesystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
- ->setConstructorArgs(array($io))
+ $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')
+ ->disableOriginalConstructor()
->getMock();
- $remoteFilesystem->expects($this->at(0))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
- ->will($this->returnValue('{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist", "archived": true}'));
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
+ ->setConstructorArgs(array($io, $this->config))
+ ->getMock();
- $remoteFilesystem->expects($this->at(1))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo($composerJsonUrl), $this->equalTo(false))
- ->will($this->returnValue('{"encoding": "base64", "content": "' . base64_encode('{"name": "composer/packagist"}') . '"}'));
+ $httpDownloader->expects($this->at(0))
+ ->method('get')
+ ->with($this->equalTo($repoApiUrl))
+ ->will($this->returnValue(new Response(array('url' => $repoApiUrl), 200, array(), '{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist", "archived": true}')));
- $remoteFilesystem->expects($this->at(2))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo('https://api.github.com/repos/composer/packagist/commits/SOMESHA'), $this->equalTo(false))
- ->will($this->returnValue('{"commit": {"committer":{ "date": "2012-09-10"}}}'));
+ $httpDownloader->expects($this->at(1))
+ ->method('get')
+ ->with($this->equalTo($composerJsonUrl))
+ ->will($this->returnValue(new Response(array('url' => $composerJsonUrl), 200, array(), '{"encoding": "base64", "content": "' . base64_encode('{"name": "composer/packagist"}') . '"}')));
+
+ $httpDownloader->expects($this->at(2))
+ ->method('get')
+ ->with($this->equalTo($url = 'https://api.github.com/repos/composer/packagist/commits/'.$sha))
+ ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"commit": {"committer":{ "date": "2012-09-10"}}}')));
+
+ $httpDownloader->expects($this->at(3))
+ ->method('get')
+ ->with($this->equalTo($url = 'https://api.github.com/repos/composer/packagist/contents/.github/FUNDING.yml'))
+ ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"encoding": "base64", "content": "'.base64_encode("custom: https://example.com").'"}')));
$repoConfig = array(
'url' => $repoUrl,
);
- $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, null, $remoteFilesystem);
+ $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, $httpDownloader, $process);
$gitHubDriver->initialize();
$this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha));
@@ -274,13 +297,13 @@ class GitHubDriverTest extends TestCase
->method('isInteractive')
->will($this->returnValue(false));
- $remoteFilesystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
- ->setConstructorArgs(array($io))
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
+ ->setConstructorArgs(array($io, $this->config))
->getMock();
- $remoteFilesystem->expects($this->at(0))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
+ $httpDownloader->expects($this->at(0))
+ ->method('get')
+ ->with($this->equalTo($repoApiUrl))
->will($this->throwException(new TransportException('HTTP/1.1 404 Not Found', 404)));
// clean local clone if present
@@ -325,7 +348,7 @@ class GitHubDriverTest extends TestCase
'url' => $repoUrl,
);
- $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, $process, $remoteFilesystem);
+ $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, $httpDownloader, $process);
$gitHubDriver->initialize();
$this->assertEquals('test_master', $gitHubDriver->getRootIdentifier());
diff --git a/tests/Composer/Test/Repository/Vcs/GitLabDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitLabDriverTest.php
index 81e623182..0fd2fa956 100644
--- a/tests/Composer/Test/Repository/Vcs/GitLabDriverTest.php
+++ b/tests/Composer/Test/Repository/Vcs/GitLabDriverTest.php
@@ -17,6 +17,7 @@ use Composer\Config;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
use Prophecy\Argument;
+use Composer\Util\Http\Response;
/**
* @author Jérôme Tamarelle
@@ -27,7 +28,7 @@ class GitLabDriverTest extends TestCase
private $config;
private $io;
private $process;
- private $remoteFilesystem;
+ private $httpDownloader;
public function setUp()
{
@@ -47,7 +48,7 @@ class GitLabDriverTest extends TestCase
$this->io = $this->prophesize('Composer\IO\IOInterface');
$this->process = $this->prophesize('Composer\Util\ProcessExecutor');
- $this->remoteFilesystem = $this->prophesize('Composer\Util\RemoteFilesystem');
+ $this->httpDownloader = $this->prophesize('Composer\Util\HttpDownloader');
}
public function tearDown()
@@ -87,13 +88,11 @@ class GitLabDriverTest extends TestCase
}
JSON;
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($projectData)
+ $this->mockResponse($apiUrl, array(), $projectData)
->shouldBeCalledTimes(1)
;
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -126,13 +125,11 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($projectData)
+ $this->mockResponse($apiUrl, array(), $projectData)
->shouldBeCalledTimes(1)
;
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -164,13 +161,11 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($projectData)
+ $this->mockResponse($apiUrl, array(), $projectData)
->shouldBeCalledTimes(1)
;
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -206,12 +201,10 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents($domain.':'.$port, $apiUrl, false, array())
- ->willReturn(sprintf($projectData, $domain, $port, $namespace))
+ $this->mockResponse($apiUrl, array(), sprintf($projectData, $domain, $port, $namespace))
->shouldBeCalledTimes(1);
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -289,15 +282,11 @@ JSON;
]
JSON;
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($tagData)
+ $this->mockResponse($apiUrl, array(), $tagData)
->shouldBeCalledTimes(1)
;
- $this->remoteFilesystem->getLastHeaders()
- ->willReturn(array());
- $driver->setRemoteFilesystem($this->remoteFilesystem->reveal());
+ $driver->setHttpDownloader($this->httpDownloader->reveal());
$expected = array(
'v1.0.0' => '092ed2c762bbae331e3f51d4a17f67310bf99a81',
@@ -344,26 +333,20 @@ JSON;
$branchData = json_encode($branchData);
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($branchData)
- ->shouldBeCalledTimes(1)
- ;
+ $headers = array('Link: ; rel="next", ; rel="first", ; rel="last"');
+ $this->httpDownloader
+ ->get($apiUrl, array())
+ ->willReturn(new Response(array('url' => $apiUrl), 200, $headers, $branchData))
+ ->shouldBeCalledTimes(1);
- $this->remoteFilesystem
- ->getContents('gitlab.com', "http://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/tags?id=mygroup%2Fmyproject&page=2&per_page=20", false, array())
- ->willReturn($branchData)
- ->shouldBeCalledTimes(1)
- ;
+ $apiUrl = "http://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/tags?id=mygroup%2Fmyproject&page=2&per_page=20";
+ $headers = array('Link: ; rel="prev", ; rel="first", ; rel="last"');
+ $this->httpDownloader
+ ->get($apiUrl, array())
+ ->willReturn(new Response(array('url' => $apiUrl), 200, $headers, $branchData))
+ ->shouldBeCalledTimes(1);
- $this->remoteFilesystem->getLastHeaders()
- ->willReturn(
- array('Link: ; rel="next", ; rel="first", ; rel="last"'),
- array('Link: ; rel="prev", ; rel="first", ; rel="last"')
- )
- ->shouldBeCalledTimes(2);
-
- $driver->setRemoteFilesystem($this->remoteFilesystem->reveal());
+ $driver->setHttpDownloader($this->httpDownloader->reveal());
$expected = array(
'mymaster' => '97eda36b5c1dd953a3792865c222d4e85e5f302e',
@@ -401,15 +384,11 @@ JSON;
]
JSON;
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($branchData)
+ $this->mockResponse($apiUrl, array(), $branchData)
->shouldBeCalledTimes(1)
;
- $this->remoteFilesystem->getLastHeaders()
- ->willReturn(array());
- $driver->setRemoteFilesystem($this->remoteFilesystem->reveal());
+ $driver->setHttpDownloader($this->httpDownloader->reveal());
$expected = array(
'mymaster' => '97eda36b5c1dd953a3792865c222d4e85e5f302e',
@@ -474,13 +453,11 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents('mycompany.com/gitlab', $apiUrl, false, array())
- ->willReturn($projectData)
+ $this->mockResponse($apiUrl, array(), $projectData)
->shouldBeCalledTimes(1)
;
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -507,13 +484,11 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($projectData)
+ $this->mockResponse($apiUrl, array(), $projectData)
->shouldBeCalledTimes(1)
;
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -540,13 +515,11 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents('mycompany.com/gitlab', $apiUrl, false, array())
- ->willReturn($projectData)
+ $this->mockResponse($apiUrl, array(), $projectData)
->shouldBeCalledTimes(1)
;
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -575,18 +548,23 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents(Argument::cetera(), $options)
- ->willReturn($projectData)
+ $this->mockResponse(Argument::cetera(), $options, $projectData)
->shouldBeCalled();
$driver = new GitLabDriver(
array('url' => 'https://gitlab.mycompany.local/mygroup/myproject', 'options' => $options),
$this->io->reveal(),
$this->config,
- $this->process->reveal(),
- $this->remoteFilesystem->reveal()
+ $this->httpDownloader->reveal(),
+ $this->process->reveal()
);
$driver->initialize();
}
+
+ private function mockResponse($url, $options, $return)
+ {
+ return $this->httpDownloader
+ ->get($url, $options)
+ ->willReturn(new Response(array('url' => $url), 200, array(), $return));
+ }
}
diff --git a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php
index a5e5d4b4c..1c44e82dd 100644
--- a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php
+++ b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php
@@ -26,7 +26,7 @@ class PerforceDriverTest extends TestCase
protected $config;
protected $io;
protected $process;
- protected $remoteFileSystem;
+ protected $httpDownloader;
protected $testPath;
protected $driver;
protected $repoConfig;
@@ -43,9 +43,9 @@ class PerforceDriverTest extends TestCase
$this->repoConfig = $this->getTestRepoConfig();
$this->io = $this->getMockIOInterface();
$this->process = $this->getMockProcessExecutor();
- $this->remoteFileSystem = $this->getMockRemoteFilesystem();
+ $this->httpDownloader = $this->getMockHttpDownloader();
$this->perforce = $this->getMockPerforce();
- $this->driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem);
+ $this->driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->httpDownloader, $this->process);
$this->overrideDriverInternalPerforce($this->perforce);
}
@@ -56,7 +56,7 @@ class PerforceDriverTest extends TestCase
$fs->removeDirectory($this->testPath);
$this->driver = null;
$this->perforce = null;
- $this->remoteFileSystem = null;
+ $this->httpDownloader = null;
$this->process = null;
$this->io = null;
$this->repoConfig = null;
@@ -99,21 +99,21 @@ class PerforceDriverTest extends TestCase
return $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
}
- protected function getMockRemoteFilesystem()
+ protected function getMockHttpDownloader()
{
- return $this->getMockBuilder('Composer\Util\RemoteFilesystem')->disableOriginalConstructor()->getMock();
+ return $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock();
}
protected function getMockPerforce()
{
$methods = array('p4login', 'checkStream', 'writeP4ClientSpec', 'connectClient', 'getComposerInformation', 'cleanupClientSpec');
- return $this->getMockBuilder('Composer\Util\Perforce', $methods)->disableOriginalConstructor()->getMock();
+ return $this->getMockBuilder('Composer\Util\Perforce')->disableOriginalConstructor()->getMock();
}
public function testInitializeCapturesVariablesFromRepoConfig()
{
- $driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem);
+ $driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->httpDownloader, $this->process);
$driver->initialize();
$this->assertEquals(self::TEST_URL, $driver->getUrl());
$this->assertEquals(self::TEST_DEPOT, $driver->getDepot());
diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php
index 4ef0d9bcc..b43cbbc2a 100644
--- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php
+++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php
@@ -46,6 +46,7 @@ class SvnDriverTest extends TestCase
public function testWrongCredentialsInUrl()
{
$console = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock();
$output = "svn: OPTIONS of 'https://corp.svn.local/repo':";
$output .= " authorization failed: Could not authenticate to server:";
@@ -66,7 +67,7 @@ class SvnDriverTest extends TestCase
'url' => 'https://till:secret@corp.svn.local/repo',
);
- $svn = new SvnDriver($repoConfig, $console, $this->config, $process);
+ $svn = new SvnDriver($repoConfig, $console, $this->config, $httpDownloader, $process);
$svn->initialize();
}
diff --git a/tests/Composer/Test/Repository/VcsRepositoryTest.php b/tests/Composer/Test/Repository/VcsRepositoryTest.php
index 0cab2f8bb..65bf52409 100644
--- a/tests/Composer/Test/Repository/VcsRepositoryTest.php
+++ b/tests/Composer/Test/Repository/VcsRepositoryTest.php
@@ -32,17 +32,18 @@ class VcsRepositoryTest extends TestCase
protected function initialize()
{
- $oldCwd = getcwd();
- self::$composerHome = $this->getUniqueTmpDirectory();
- self::$gitRepo = $this->getUniqueTmpDirectory();
-
$locator = new ExecutableFinder();
if (!$locator->find('git')) {
$this->skipped = 'This test needs a git binary in the PATH to be able to run';
return;
}
- if (!@mkdir(self::$gitRepo) || !@chdir(self::$gitRepo)) {
+
+ $oldCwd = getcwd();
+ self::$composerHome = $this->getUniqueTmpDirectory();
+ self::$gitRepo = $this->getUniqueTmpDirectory();
+
+ if (!@chdir(self::$gitRepo)) {
$this->skipped = 'Could not create and move into the temp git repo '.self::$gitRepo;
return;
@@ -60,6 +61,7 @@ class VcsRepositoryTest extends TestCase
$exec('git init');
$exec('git config user.email composertest@example.org');
$exec('git config user.name ComposerTest');
+ $exec('git config commit.gpgsign false');
touch('foo');
$exec('git add foo');
$exec('git commit -m init');
@@ -150,7 +152,8 @@ class VcsRepositoryTest extends TestCase
'home' => self::$composerHome,
),
));
- $repo = new VcsRepository(array('url' => self::$gitRepo, 'type' => 'vcs'), new NullIO, $config);
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock();
+ $repo = new VcsRepository(array('url' => self::$gitRepo, 'type' => 'vcs'), new NullIO, $config, $httpDownloader);
$packages = $repo->getPackages();
$dumper = new ArrayDumper();
diff --git a/tests/Composer/Test/Util/BitbucketTest.php b/tests/Composer/Test/Util/BitbucketTest.php
index c89b393e1..5837d1996 100644
--- a/tests/Composer/Test/Util/BitbucketTest.php
+++ b/tests/Composer/Test/Util/BitbucketTest.php
@@ -13,6 +13,7 @@
namespace Composer\Test\Util;
use Composer\Util\Bitbucket;
+use Composer\Util\Http\Response;
use Composer\Test\TestCase;
/**
@@ -30,8 +31,8 @@ class BitbucketTest extends TestCase
/** @type \Composer\IO\ConsoleIO|\PHPUnit_Framework_MockObject_MockObject */
private $io;
- /** @type \Composer\Util\RemoteFilesystem|\PHPUnit_Framework_MockObject_MockObject */
- private $rfs;
+ /** @type \Composer\Util\HttpDownloader|\PHPUnit_Framework_MockObject_MockObject */
+ private $httpDownloader;
/** @type \Composer\Config|\PHPUnit_Framework_MockObject_MockObject */
private $config;
/** @type Bitbucket */
@@ -47,8 +48,8 @@ class BitbucketTest extends TestCase
->getMock()
;
- $this->rfs = $this
- ->getMockBuilder('Composer\Util\RemoteFilesystem')
+ $this->httpDownloader = $this
+ ->getMockBuilder('Composer\Util\HttpDownloader')
->disableOriginalConstructor()
->getMock()
;
@@ -57,7 +58,7 @@ class BitbucketTest extends TestCase
$this->time = time();
- $this->bitbucket = new Bitbucket($this->io, $this->config, null, $this->rfs, $this->time);
+ $this->bitbucket = new Bitbucket($this->io, $this->config, null, $this->httpDownloader, $this->time);
}
public function testRequestAccessTokenWithValidOAuthConsumer()
@@ -66,12 +67,10 @@ class BitbucketTest extends TestCase
->method('setAuthentication')
->with($this->origin, $this->consumer_key, $this->consumer_secret);
- $this->rfs->expects($this->once())
- ->method('getContents')
+ $this->httpDownloader->expects($this->once())
+ ->method('get')
->with(
- $this->origin,
Bitbucket::OAUTH2_ACCESS_TOKEN_URL,
- false,
array(
'retry-auth-failure' => false,
'http' => array(
@@ -81,9 +80,14 @@ class BitbucketTest extends TestCase
)
)
->willReturn(
- sprintf(
- '{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refreshtoken", "token_type": "bearer"}',
- $this->token
+ new Response(
+ array('url' => Bitbucket::OAUTH2_ACCESS_TOKEN_URL),
+ 200,
+ array(),
+ sprintf(
+ '{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refreshtoken", "token_type": "bearer"}',
+ $this->token
+ )
)
);
@@ -142,12 +146,10 @@ class BitbucketTest extends TestCase
->method('setAuthentication')
->with($this->origin, $this->consumer_key, $this->consumer_secret);
- $this->rfs->expects($this->once())
- ->method('getContents')
+ $this->httpDownloader->expects($this->once())
+ ->method('get')
->with(
- $this->origin,
Bitbucket::OAUTH2_ACCESS_TOKEN_URL,
- false,
array(
'retry-auth-failure' => false,
'http' => array(
@@ -157,9 +159,14 @@ class BitbucketTest extends TestCase
)
)
->willReturn(
- sprintf(
- '{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refreshtoken", "token_type": "bearer"}',
- $this->token
+ new Response(
+ array('url' => Bitbucket::OAUTH2_ACCESS_TOKEN_URL),
+ 200,
+ array(),
+ sprintf(
+ '{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refreshtoken", "token_type": "bearer"}',
+ $this->token
+ )
)
);
@@ -186,12 +193,10 @@ class BitbucketTest extends TestCase
array('2. You are using an OAuth consumer, but didn\'t configure a (dummy) callback url')
);
- $this->rfs->expects($this->once())
- ->method('getContents')
+ $this->httpDownloader->expects($this->once())
+ ->method('get')
->with(
- $this->origin,
Bitbucket::OAUTH2_ACCESS_TOKEN_URL,
- false,
array(
'retry-auth-failure' => false,
'http' => array(
@@ -234,21 +239,24 @@ class BitbucketTest extends TestCase
)
->willReturnOnConsecutiveCalls($this->consumer_key, $this->consumer_secret);
- $this->rfs
+ $this->httpDownloader
->expects($this->once())
- ->method('getContents')
+ ->method('get')
->with(
- $this->equalTo($this->origin),
- $this->equalTo(sprintf('https://%s/site/oauth2/access_token', $this->origin)),
- $this->isFalse(),
+ $this->equalTo($url = sprintf('https://%s/site/oauth2/access_token', $this->origin)),
$this->anything()
)
->willReturn(
- sprintf(
- '{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refresh_token", "token_type": "bearer"}',
- $this->token
+ new Response(
+ array('url' => $url),
+ 200,
+ array(),
+ sprintf(
+ '{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refresh_token", "token_type": "bearer"}',
+ $this->token
+ )
)
- )
+ );
;
$this->setExpectationsForStoringAccessToken(true);
diff --git a/tests/Composer/Test/Util/GitHubTest.php b/tests/Composer/Test/Util/GitHubTest.php
index 375279652..37683395f 100644
--- a/tests/Composer/Test/Util/GitHubTest.php
+++ b/tests/Composer/Test/Util/GitHubTest.php
@@ -14,6 +14,7 @@ namespace Composer\Test\Util;
use Composer\Downloader\TransportException;
use Composer\Util\GitHub;
+use Composer\Util\Http\Response;
use Composer\Test\TestCase;
use RecursiveArrayIterator;
use RecursiveIteratorIterator;
@@ -42,17 +43,15 @@ class GitHubTest extends TestCase
->willReturn($this->password)
;
- $rfs = $this->getRemoteFilesystemMock();
- $rfs
+ $httpDownloader = $this->getHttpDownloaderMock();
+ $httpDownloader
->expects($this->once())
- ->method('getContents')
+ ->method('get')
->with(
- $this->equalTo($this->origin),
- $this->equalTo(sprintf('https://api.%s/', $this->origin)),
- $this->isFalse(),
+ $this->equalTo($url = sprintf('https://api.%s/', $this->origin)),
$this->anything()
)
- ->willReturn('{}')
+ ->willReturn(new Response(array('url' => $url), 200, array(), '{}'));
;
$config = $this->getConfigMock();
@@ -67,7 +66,7 @@ class GitHubTest extends TestCase
->willReturn($this->getConfJsonMock())
;
- $github = new GitHub($io, $config, null, $rfs);
+ $github = new GitHub($io, $config, null, $httpDownloader);
$this->assertTrue($github->authorizeOAuthInteractively($this->origin, $this->message));
}
@@ -82,10 +81,10 @@ class GitHubTest extends TestCase
->willReturn($this->password)
;
- $rfs = $this->getRemoteFilesystemMock();
- $rfs
+ $httpDownloader = $this->getHttpDownloaderMock();
+ $httpDownloader
->expects($this->exactly(1))
- ->method('getContents')
+ ->method('get')
->will($this->throwException(new TransportException('', 401)))
;
@@ -96,7 +95,7 @@ class GitHubTest extends TestCase
->willReturn($this->getAuthJsonMock())
;
- $github = new GitHub($io, $config, null, $rfs);
+ $github = new GitHub($io, $config, null, $httpDownloader);
$this->assertFalse($github->authorizeOAuthInteractively($this->origin));
}
@@ -117,15 +116,15 @@ class GitHubTest extends TestCase
return $this->getMockBuilder('Composer\Config')->getMock();
}
- private function getRemoteFilesystemMock()
+ private function getHttpDownloaderMock()
{
- $rfs = $this
- ->getMockBuilder('Composer\Util\RemoteFilesystem')
+ $httpDownloader = $this
+ ->getMockBuilder('Composer\Util\HttpDownloader')
->disableOriginalConstructor()
->getMock()
;
- return $rfs;
+ return $httpDownloader;
}
private function getAuthJsonMock()
diff --git a/tests/Composer/Test/Util/GitLabTest.php b/tests/Composer/Test/Util/GitLabTest.php
index 541e94c61..c9ac9dbf6 100644
--- a/tests/Composer/Test/Util/GitLabTest.php
+++ b/tests/Composer/Test/Util/GitLabTest.php
@@ -14,6 +14,7 @@ namespace Composer\Test\Util;
use Composer\Downloader\TransportException;
use Composer\Util\GitLab;
+use Composer\Util\Http\Response;
use Composer\Test\TestCase;
/**
@@ -48,17 +49,15 @@ class GitLabTest extends TestCase
->willReturn($this->password)
;
- $rfs = $this->getRemoteFilesystemMock();
- $rfs
+ $httpDownloader = $this->getHttpDownloaderMock();
+ $httpDownloader
->expects($this->once())
- ->method('getContents')
+ ->method('get')
->with(
- $this->equalTo($this->origin),
- $this->equalTo(sprintf('http://%s/oauth/token', $this->origin)),
- $this->isFalse(),
+ $this->equalTo($url = sprintf('http://%s/oauth/token', $this->origin)),
$this->anything()
)
- ->willReturn(sprintf('{"access_token": "%s", "token_type": "bearer", "expires_in": 7200}', $this->token))
+ ->willReturn(new Response(array('url' => $url), 200, array(), sprintf('{"access_token": "%s", "token_type": "bearer", "expires_in": 7200}', $this->token)));
;
$config = $this->getConfigMock();
@@ -68,7 +67,7 @@ class GitLabTest extends TestCase
->willReturn($this->getAuthJsonMock())
;
- $gitLab = new GitLab($io, $config, null, $rfs);
+ $gitLab = new GitLab($io, $config, null, $httpDownloader);
$this->assertTrue($gitLab->authorizeOAuthInteractively('http', $this->origin, $this->message));
}
@@ -93,10 +92,10 @@ class GitLabTest extends TestCase
->willReturn($this->password)
;
- $rfs = $this->getRemoteFilesystemMock();
- $rfs
+ $httpDownloader = $this->getHttpDownloaderMock();
+ $httpDownloader
->expects($this->exactly(5))
- ->method('getContents')
+ ->method('get')
->will($this->throwException(new TransportException('', 401)))
;
@@ -107,7 +106,7 @@ class GitLabTest extends TestCase
->willReturn($this->getAuthJsonMock())
;
- $gitLab = new GitLab($io, $config, null, $rfs);
+ $gitLab = new GitLab($io, $config, null, $httpDownloader);
$gitLab->authorizeOAuthInteractively('https', $this->origin);
}
@@ -128,15 +127,15 @@ class GitLabTest extends TestCase
return $this->getMockBuilder('Composer\Config')->getMock();
}
- private function getRemoteFilesystemMock()
+ private function getHttpDownloaderMock()
{
- $rfs = $this
- ->getMockBuilder('Composer\Util\RemoteFilesystem')
+ $httpDownloader = $this
+ ->getMockBuilder('Composer\Util\HttpDownloader')
->disableOriginalConstructor()
->getMock()
;
- return $rfs;
+ return $httpDownloader;
}
private function getAuthJsonMock()
diff --git a/tests/Composer/Test/Util/HttpDownloaderTest.php b/tests/Composer/Test/Util/HttpDownloaderTest.php
new file mode 100644
index 000000000..b65aa760a
--- /dev/null
+++ b/tests/Composer/Test/Util/HttpDownloaderTest.php
@@ -0,0 +1,51 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Test\Util;
+
+use Composer\Util\HttpDownloader;
+use PHPUnit\Framework\TestCase;
+
+class HttpDownloaderTest extends TestCase
+{
+ private function getConfigMock()
+ {
+ $config = $this->getMockBuilder('Composer\Config')->getMock();
+ $config->expects($this->any())
+ ->method('get')
+ ->will($this->returnCallback(function ($key) {
+ if ($key === 'github-domains' || $key === 'gitlab-domains') {
+ return array();
+ }
+ }));
+
+ return $config;
+ }
+
+ /**
+ * @group slow
+ */
+ public function testCaptureAuthenticationParamsFromUrl()
+ {
+ $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
+ $io->expects($this->once())
+ ->method('setAuthentication')
+ ->with($this->equalTo('github.com'), $this->equalTo('user'), $this->equalTo('pass'));
+
+ $fs = new HttpDownloader($io, $this->getConfigMock());
+ try {
+ $fs->get('https://user:pass@github.com/composer/composer/404');
+ } catch (\Composer\Downloader\TransportException $e) {
+ $this->assertNotEquals(200, $e->getCode());
+ }
+ }
+}
diff --git a/tests/Composer/Test/Util/MetadataMinifierTest.php b/tests/Composer/Test/Util/MetadataMinifierTest.php
new file mode 100644
index 000000000..ad8d3abad
--- /dev/null
+++ b/tests/Composer/Test/Util/MetadataMinifierTest.php
@@ -0,0 +1,45 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Test\Util;
+
+use Composer\Util\MetadataMinifier;
+use Composer\Package\CompletePackage;
+use Composer\Package\Dumper\ArrayDumper;
+use PHPUnit\Framework\TestCase;
+
+class MetadataMinifierTest extends TestCase
+{
+ public function testMinifyExpand()
+ {
+ $package1 = new CompletePackage('foo/bar', '2.0.0.0', '2.0.0');
+ $package1->setScripts(array('foo' => 'bar'));
+ $package1->setLicense(array('MIT'));
+ $package2 = new CompletePackage('foo/bar', '1.2.0.0', '1.2.0');
+ $package2->setLicense(array('GPL'));
+ $package2->setHomepage('https://example.org');
+ $package3 = new CompletePackage('foo/bar', '1.0.0.0', '1.0.0');
+ $package3->setLicense(array('GPL'));
+ $dumper = new ArrayDumper();
+
+ $minified = array(
+ array('name' => 'foo/bar', 'version' => '2.0.0', 'version_normalized' => '2.0.0.0', 'type' => 'library', 'scripts' => array('foo' => 'bar'), 'license' => array('MIT')),
+ array('version' => '1.2.0', 'version_normalized' => '1.2.0.0', 'license' => array('GPL'), 'homepage' => 'https://example.org', 'scripts' => '__unset'),
+ array('version' => '1.0.0', 'version_normalized' => '1.0.0.0', 'homepage' => '__unset'),
+ );
+
+ $source = array($dumper->dump($package1), $dumper->dump($package2), $dumper->dump($package3));
+
+ $this->assertSame($minified, MetadataMinifier::minify($source));
+ $this->assertSame($source, MetadataMinifier::expand($minified));
+ }
+}
diff --git a/tests/Composer/Test/Util/ProcessExecutorTest.php b/tests/Composer/Test/Util/ProcessExecutorTest.php
index 4ac0d570f..87c2d07cc 100644
--- a/tests/Composer/Test/Util/ProcessExecutorTest.php
+++ b/tests/Composer/Test/Util/ProcessExecutorTest.php
@@ -44,7 +44,7 @@ class ProcessExecutorTest extends TestCase
{
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
$io->expects($this->once())
- ->method('write')
+ ->method('writeRaw')
->with($this->equalTo('foo'.PHP_EOL), false);
$process = new ProcessExecutor($io);
diff --git a/tests/Composer/Test/Util/RemoteFilesystemTest.php b/tests/Composer/Test/Util/RemoteFilesystemTest.php
index 486ad9a3b..fe4f213c6 100644
--- a/tests/Composer/Test/Util/RemoteFilesystemTest.php
+++ b/tests/Composer/Test/Util/RemoteFilesystemTest.php
@@ -17,6 +17,20 @@ use Composer\Test\TestCase;
class RemoteFilesystemTest extends TestCase
{
+ private function getConfigMock()
+ {
+ $config = $this->getMockBuilder('Composer\Config')->getMock();
+ $config->expects($this->any())
+ ->method('get')
+ ->will($this->returnCallback(function ($key) {
+ if ($key === 'github-domains' || $key === 'gitlab-domains') {
+ return array();
+ }
+ }));
+
+ return $config;
+ }
+
public function testGetOptionsForUrl()
{
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
@@ -113,7 +127,7 @@ class RemoteFilesystemTest extends TestCase
public function testCallbackGetFileSize()
{
- $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock());
+ $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getConfigMock());
$this->callCallbackGet($fs, STREAM_NOTIFY_FILE_SIZE_IS, 0, '', 0, 0, 20);
$this->assertAttributeEquals(20, 'bytesMax', $fs);
}
@@ -126,7 +140,7 @@ class RemoteFilesystemTest extends TestCase
->method('overwriteError')
;
- $fs = new RemoteFilesystem($io);
+ $fs = new RemoteFilesystem($io, $this->getConfigMock());
$this->setAttribute($fs, 'bytesMax', 20);
$this->setAttribute($fs, 'progress', true);
@@ -136,40 +150,21 @@ class RemoteFilesystemTest extends TestCase
public function testCallbackGetPassesThrough404()
{
- $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock());
+ $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getConfigMock());
$this->assertNull($this->callCallbackGet($fs, STREAM_NOTIFY_FAILURE, 0, 'HTTP/1.1 404 Not Found', 404, 0, 0));
}
- /**
- * @group slow
- */
- public function testCaptureAuthenticationParamsFromUrl()
- {
- $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
- $io->expects($this->once())
- ->method('setAuthentication')
- ->with($this->equalTo('github.com'), $this->equalTo('user'), $this->equalTo('pass'));
-
- $fs = new RemoteFilesystem($io);
- try {
- $fs->getContents('github.com', 'https://user:pass@github.com/composer/composer/404');
- } catch (\Exception $e) {
- $this->assertInstanceOf('Composer\Downloader\TransportException', $e);
- $this->assertNotEquals(200, $e->getCode());
- }
- }
-
public function testGetContents()
{
- $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock());
+ $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getConfigMock());
$this->assertContains('testGetContents', $fs->getContents('http://example.org', 'file://'.__FILE__));
}
public function testCopy()
{
- $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock());
+ $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getConfigMock());
$file = tempnam(sys_get_temp_dir(), 'c');
$this->assertTrue($fs->copy('http://example.org', 'file://'.__FILE__, $file));
@@ -230,7 +225,7 @@ class RemoteFilesystemTest extends TestCase
->disableOriginalConstructor()
->getMock();
- $rfs = new RemoteFilesystem($io);
+ $rfs = new RemoteFilesystem($io, $this->getConfigMock());
$hostname = parse_url($url, PHP_URL_HOST);
$result = $rfs->getContents($hostname, $url, false);
@@ -252,14 +247,6 @@ class RemoteFilesystemTest extends TestCase
->disableOriginalConstructor()
->getMock();
- $config = $this
- ->getMockBuilder('Composer\Config')
- ->getMock();
- $config
- ->method('get')
- ->withAnyParameters()
- ->willReturn(array());
-
$domains = array();
$io
->expects($this->any())
@@ -279,7 +266,7 @@ class RemoteFilesystemTest extends TestCase
'password' => '1A0yeK5Po3ZEeiiRiMWLivS0jirLdoGuaSGq9NvESFx1Fsdn493wUDXC8rz_1iKVRTl1GINHEUCsDxGh5lZ=',
));
- $rfs = new RemoteFilesystem($io, $config);
+ $rfs = new RemoteFilesystem($io, $this->getConfigMock());
$hostname = parse_url($url, PHP_URL_HOST);
$result = $rfs->getContents($hostname, $url, false);
@@ -290,7 +277,7 @@ class RemoteFilesystemTest extends TestCase
protected function callGetOptionsForUrl($io, array $args = array(), array $options = array(), $fileUrl = '')
{
- $fs = new RemoteFilesystem($io, null, $options);
+ $fs = new RemoteFilesystem($io, $this->getConfigMock(), $options);
$ref = new \ReflectionMethod($fs, 'getOptionsForUrl');
$prop = new \ReflectionProperty($fs, 'fileUrl');
$ref->setAccessible(true);
diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php
index 34de2fa33..bd4935cf2 100644
--- a/tests/Composer/Test/Util/StreamContextFactoryTest.php
+++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php
@@ -142,7 +142,6 @@ class StreamContextFactoryTest extends TestCase
$expected = array(
'http' => array(
'proxy' => 'tcp://proxyserver.net:80',
- 'request_fulluri' => true,
'method' => 'GET',
'header' => array('User-Agent: foo', "Proxy-Authorization: Basic " . base64_encode('username:password')),
'max_redirects' => 20,
@@ -173,7 +172,6 @@ class StreamContextFactoryTest extends TestCase
$expected = array(
'http' => array(
'proxy' => 'ssl://woopproxy.net:443',
- 'request_fulluri' => true,
'method' => 'GET',
'max_redirects' => 20,
'follow_location' => 1,
diff --git a/tests/Composer/Test/Util/UrlTest.php b/tests/Composer/Test/Util/UrlTest.php
index e57c0ba9f..8eb33f851 100644
--- a/tests/Composer/Test/Util/UrlTest.php
+++ b/tests/Composer/Test/Util/UrlTest.php
@@ -58,4 +58,25 @@ class UrlTest extends TestCase
array('https://mygitlab.com/api/v3/projects/foo%2Fbar/repository/archive.tar.bz2?sha=abcd', 'https://mygitlab.com/api/v3/projects/foo%2Fbar/repository/archive.tar.bz2?sha=65', array('gitlab-domains' => array('mygitlab.com')), '65'),
);
}
+
+ /**
+ * @dataProvider sanitizeProvider
+ */
+ public function testSanitize($expected, $url)
+ {
+ $this->assertSame($expected, Url::sanitize($url));
+ }
+
+ public static function sanitizeProvider()
+ {
+ return array(
+ array('https://foo:***@example.org/', 'https://foo:bar@example.org/'),
+ array('https://foo@example.org/', 'https://foo@example.org/'),
+ array('https://example.org/', 'https://example.org/'),
+ array('http://***:***@example.org', 'http://10a8f08e8d7b7b9:foo@example.org'),
+ array('https://foo:***@example.org:123/', 'https://foo:bar@example.org:123/'),
+ array('https://example.org/foo/bar?access_token=***', 'https://example.org/foo/bar?access_token=abcdef'),
+ array('https://example.org/foo/bar?foo=bar&access_token=***', 'https://example.org/foo/bar?foo=bar&access_token=abcdef'),
+ );
+ }
}