Add IOMock and get rid of all withConsecutive calls in tests (#11497)
parent
ebd0a60411
commit
9f3e2105da
|
@ -5301,6 +5301,16 @@ parameters:
|
|||
count: 1
|
||||
path: ../tests/Composer/Test/Json/JsonManipulatorTest.php
|
||||
|
||||
-
|
||||
message: "#^Offset 'ask' might not exist on array\\{ask\\: string, reply\\?\\: string\\}\\|array\\{auth\\: array\\{string, string, string\\|null\\}\\}\\|array\\{text\\: string, verbosity\\?\\: 1\\|2\\|4\\|8\\|16\\}\\.$#"
|
||||
count: 2
|
||||
path: ../tests/Composer/Test/Mock/IOMock.php
|
||||
|
||||
-
|
||||
message: "#^Offset 'text' might not exist on array\\{ask\\: string, reply\\?\\: string\\}\\|array\\{auth\\: array\\{string, string, string\\|null\\}\\}\\|array\\{text\\: string, verbosity\\?\\: 1\\|2\\|4\\|8\\|16\\}\\.$#"
|
||||
count: 1
|
||||
path: ../tests/Composer/Test/Mock/IOMock.php
|
||||
|
||||
-
|
||||
message: "#^Composer\\\\Test\\\\Mock\\\\InstallationManagerMock\\:\\:__construct\\(\\) does not call parent constructor from Composer\\\\Installer\\\\InstallationManager\\.$#"
|
||||
count: 1
|
||||
|
|
|
@ -1328,10 +1328,16 @@ EOF;
|
|||
$this->eventDispatcher
|
||||
->expects($this->exactly(2))
|
||||
->method('dispatchScript')
|
||||
->withConsecutive(
|
||||
->willReturnCallback(function ($type, $dev) {
|
||||
static $series = [
|
||||
[ScriptEvents::PRE_AUTOLOAD_DUMP, false],
|
||||
[ScriptEvents::POST_AUTOLOAD_DUMP, false]
|
||||
);
|
||||
];
|
||||
|
||||
$this->assertSame(array_shift($series), [$type, $dev]);
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
$package = new RootPackage('root/a', '1.0', '1.0');
|
||||
$package->setAutoload(['psr-0' => ['Prefix' => 'foo/bar/non/existing/']]);
|
||||
|
|
|
@ -307,12 +307,9 @@ class ConfigTest extends TestCase
|
|||
|
||||
public function testProhibitedUrlsWarningVerifyPeer(): void
|
||||
{
|
||||
$io = $this->getMockBuilder(IOInterface::class)->disableOriginalConstructor()->getMock();
|
||||
$io = $this->getIOMock();
|
||||
|
||||
$io
|
||||
->expects($this->once())
|
||||
->method('writeError')
|
||||
->with($this->equalTo('<warning>Warning: Accessing example.org with verify_peer and verify_peer_name disabled.</warning>'));
|
||||
$io->expects([['text' => '<warning>Warning: Accessing example.org with verify_peer and verify_peer_name disabled.</warning>']], true);
|
||||
|
||||
$config = new Config(false);
|
||||
$config->prohibitUrlByConfig('https://example.org', $io, [
|
||||
|
|
|
@ -258,10 +258,14 @@ class DownloadManagerTest extends TestCase
|
|||
$package
|
||||
->expects($this->exactly(2))
|
||||
->method('setInstallationSource')
|
||||
->withConsecutive(
|
||||
['dist'],
|
||||
['source']
|
||||
);
|
||||
->willReturnCallback(function ($type) {
|
||||
static $series = [
|
||||
'dist',
|
||||
'source',
|
||||
];
|
||||
|
||||
$this->assertSame(array_shift($series), $type);
|
||||
});
|
||||
|
||||
$downloaderFail = $this->createDownloaderMock();
|
||||
$downloaderFail
|
||||
|
|
|
@ -375,13 +375,11 @@ class FileDownloaderTest extends TestCase
|
|||
$newPackage = self::getPackage('dummy/pkg', '1.0.0');
|
||||
$newPackage->setDistUrl($distUrl = 'http://example.com/script.js');
|
||||
|
||||
$ioMock = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$ioMock->expects($this->atLeast(2))
|
||||
->method('writeError')
|
||||
->withConsecutive(
|
||||
[$this->stringContains('Downloading')],
|
||||
[$this->stringContains('Downgrading')]
|
||||
);
|
||||
$ioMock = $this->getIOMock();
|
||||
$ioMock->expects([
|
||||
['text' => '{Downloading .*}', 'regex' => true],
|
||||
['text' => '{Downgrading .*}', 'regex' => true],
|
||||
]);
|
||||
|
||||
$path = self::getUniqueTmpDirectory();
|
||||
$config = $this->getConfig(['vendor-dir' => $path.'/vendor']);
|
||||
|
|
|
@ -547,12 +547,10 @@ composer https://github.com/old/url (push)
|
|||
|
||||
$process = $this->getProcessExecutorMock();
|
||||
|
||||
$ioMock = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$ioMock->expects($this->atLeastOnce())
|
||||
->method('writeError')
|
||||
->withConsecutive(
|
||||
[$this->stringContains('Downgrading')]
|
||||
);
|
||||
$ioMock = $this->getIOMock();
|
||||
$ioMock->expects([
|
||||
['text' => '{Downgrading .*}', 'regex' => true],
|
||||
]);
|
||||
|
||||
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
|
||||
$downloader = $this->getDownloaderMock($ioMock, null, $process);
|
||||
|
@ -591,12 +589,10 @@ composer https://github.com/old/url (push)
|
|||
|
||||
$process = $this->getProcessExecutorMock();
|
||||
|
||||
$ioMock = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$ioMock->expects($this->atLeastOnce())
|
||||
->method('writeError')
|
||||
->withConsecutive(
|
||||
[$this->stringContains('Upgrading')]
|
||||
);
|
||||
$ioMock = $this->getIOMock();
|
||||
$ioMock->expects([
|
||||
['text' => '{Upgrading .*}', 'regex' => true],
|
||||
]);
|
||||
|
||||
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
|
||||
$downloader = $this->getDownloaderMock($ioMock, null, $process);
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\Test\EventDispatcher;
|
|||
|
||||
use Composer\EventDispatcher\Event;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\EventDispatcher\ScriptExecutionException;
|
||||
use Composer\Installer\InstallerEvents;
|
||||
use Composer\Config;
|
||||
use Composer\Composer;
|
||||
|
@ -32,21 +33,15 @@ class EventDispatcherTest extends TestCase
|
|||
{
|
||||
self::expectException('RuntimeException');
|
||||
|
||||
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$io = $this->getIOMock(IOInterface::NORMAL);
|
||||
$dispatcher = $this->getDispatcherStubForListenersTest([
|
||||
'Composer\Test\EventDispatcher\EventDispatcherTest::call',
|
||||
], $io);
|
||||
|
||||
$io->expects($this->once())
|
||||
->method('isVerbose')
|
||||
->willReturn(0);
|
||||
|
||||
$io->expects($this->atLeast(2))
|
||||
->method('writeError')
|
||||
->withConsecutive(
|
||||
['> Composer\Test\EventDispatcher\EventDispatcherTest::call'],
|
||||
['<error>Script Composer\Test\EventDispatcher\EventDispatcherTest::call handling the post-install-cmd event terminated with an exception</error>']
|
||||
);
|
||||
$io->expects([
|
||||
['text' => '> Composer\Test\EventDispatcher\EventDispatcherTest::call'],
|
||||
['text' => 'Script Composer\Test\EventDispatcher\EventDispatcherTest::call handling the post-install-cmd event terminated with an exception'],
|
||||
], true);
|
||||
|
||||
$dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
|
||||
}
|
||||
|
@ -528,7 +523,7 @@ class EventDispatcherTest extends TestCase
|
|||
$dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
|
||||
->setConstructorArgs([
|
||||
$this->createComposerInstance(),
|
||||
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
|
||||
$io = $this->getIOMock(IOInterface::NORMAL),
|
||||
new ProcessExecutor,
|
||||
])
|
||||
->onlyMethods(['getListeners'])
|
||||
|
@ -540,22 +535,13 @@ class EventDispatcherTest extends TestCase
|
|||
->method('getListeners')
|
||||
->will($this->returnValue($listener));
|
||||
|
||||
$io->expects($this->once())
|
||||
->method('isVerbose')
|
||||
->willReturn(0);
|
||||
$io->expects([
|
||||
['text' => '> exit 1'],
|
||||
['text' => 'Script '.$code.' handling the post-install-cmd event returned with error code 1'],
|
||||
], true);
|
||||
|
||||
$io->expects($this->atLeast(2))
|
||||
->method('writeError')
|
||||
->withConsecutive(
|
||||
['> exit 1'],
|
||||
['<error>Script '.$code.' handling the post-install-cmd event returned with error code 1</error>']
|
||||
);
|
||||
|
||||
$io->expects($this->once())
|
||||
->method('isInteractive')
|
||||
->willReturn(1);
|
||||
|
||||
self::expectException('RuntimeException');
|
||||
self::expectException(ScriptExecutionException::class);
|
||||
self::expectExceptionMessage('Error Output: ');
|
||||
$dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
|
||||
}
|
||||
|
||||
|
|
|
@ -111,15 +111,25 @@ class ConsoleIOTest extends TestCase
|
|||
->willReturn(OutputInterface::VERBOSITY_NORMAL);
|
||||
$outputMock->expects($this->atLeast(7))
|
||||
->method('write')
|
||||
->withConsecutive(
|
||||
[$this->equalTo('something (<question>strlen = 23</question>)')],
|
||||
[$this->equalTo(str_repeat("\x08", 23)), $this->equalTo(false)],
|
||||
[$this->equalTo('shorter (<comment>12</comment>)'), $this->equalTo(false)],
|
||||
[$this->equalTo(str_repeat(' ', 11)), $this->equalTo(false)],
|
||||
[$this->equalTo(str_repeat("\x08", 11)), $this->equalTo(false)],
|
||||
[$this->equalTo(str_repeat("\x08", 12)), $this->equalTo(false)],
|
||||
[$this->equalTo('something longer than initial (<info>34</info>)')]
|
||||
);
|
||||
->willReturnCallback(function (...$args) {
|
||||
static $series = null;
|
||||
|
||||
if ($series === null) {
|
||||
$series = [
|
||||
['something (<question>strlen = 23</question>)', true],
|
||||
[str_repeat("\x08", 23), false],
|
||||
['shorter (<comment>12</comment>)', false],
|
||||
[str_repeat(' ', 11), false],
|
||||
[str_repeat("\x08", 11), false],
|
||||
[str_repeat("\x08", 12), false],
|
||||
['something longer than initial (<info>34</info>)', false],
|
||||
];
|
||||
}
|
||||
|
||||
if (count($series) > 0) {
|
||||
$this->assertSame(array_shift($series), [$args[0], $args[1]]);
|
||||
}
|
||||
});
|
||||
|
||||
$helperMock = $this->getMockBuilder('Symfony\Component\Console\Helper\HelperSet')->getMock();
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace Composer\Test\Installer;
|
|||
use Composer\InstalledVersions;
|
||||
use Composer\Installer\SuggestedPackagesReporter;
|
||||
use Composer\Semver\VersionParser;
|
||||
use Composer\Test\Mock\IOMock;
|
||||
use Composer\Test\TestCase;
|
||||
|
||||
/**
|
||||
|
@ -23,7 +24,7 @@ use Composer\Test\TestCase;
|
|||
class SuggestedPackagesReporterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var \PHPUnit\Framework\MockObject\MockObject
|
||||
* @var IOMock
|
||||
*/
|
||||
private $io;
|
||||
|
||||
|
@ -34,7 +35,7 @@ class SuggestedPackagesReporterTest extends TestCase
|
|||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$this->io = $this->getIOMock();
|
||||
|
||||
$this->suggestedPackagesReporter = new SuggestedPackagesReporter($this->io);
|
||||
}
|
||||
|
@ -44,8 +45,7 @@ class SuggestedPackagesReporterTest extends TestCase
|
|||
*/
|
||||
public function testConstructor(): void
|
||||
{
|
||||
$this->io->expects($this->once())
|
||||
->method('write');
|
||||
$this->io->expects([['text' => 'b']], true);
|
||||
|
||||
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
|
||||
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_LIST);
|
||||
|
@ -143,13 +143,11 @@ class SuggestedPackagesReporterTest extends TestCase
|
|||
{
|
||||
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
|
||||
|
||||
$this->io->expects($this->exactly(3))
|
||||
->method('write')
|
||||
->withConsecutive(
|
||||
['<comment>a</comment> suggests:'],
|
||||
[' - <info>b</info>: c'],
|
||||
['']
|
||||
);
|
||||
$this->io->expects([
|
||||
['text' => 'a suggests:'],
|
||||
['text' => ' - b: c'],
|
||||
['text' => ''],
|
||||
], true);
|
||||
|
||||
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
|
||||
}
|
||||
|
@ -161,13 +159,11 @@ class SuggestedPackagesReporterTest extends TestCase
|
|||
{
|
||||
$this->suggestedPackagesReporter->addPackage('a', 'b', '');
|
||||
|
||||
$this->io->expects($this->exactly(3))
|
||||
->method('write')
|
||||
->withConsecutive(
|
||||
['<comment>a</comment> suggests:'],
|
||||
[' - <info>b</info>'],
|
||||
['']
|
||||
);
|
||||
$this->io->expects([
|
||||
['text' => 'a suggests:'],
|
||||
['text' => ' - b'],
|
||||
['text' => ''],
|
||||
], true);
|
||||
|
||||
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
|
||||
}
|
||||
|
@ -180,18 +176,12 @@ class SuggestedPackagesReporterTest extends TestCase
|
|||
$this->suggestedPackagesReporter->addPackage('source', 'target1', "\x1b[1;37;42m Like us\r\non Facebook \x1b[0m");
|
||||
$this->suggestedPackagesReporter->addPackage('source', 'target2', "<bg=green>Like us on Facebook</>");
|
||||
|
||||
$expectedWrite = InstalledVersions::satisfies(new VersionParser(), 'symfony/console', '^4.4.37 || ~5.3.14 || ^5.4.3 || ^6.0.3')
|
||||
? ' - <info>target2</info>: \\<bg=green\\>Like us on Facebook\\</\\>'
|
||||
: ' - <info>target2</info>: \\<bg=green>Like us on Facebook\\</>';
|
||||
|
||||
$this->io->expects($this->exactly(4))
|
||||
->method('write')
|
||||
->withConsecutive(
|
||||
['<comment>source</comment> suggests:'],
|
||||
[' - <info>target1</info>: [1;37;42m Like us on Facebook [0m'],
|
||||
[$expectedWrite],
|
||||
['']
|
||||
);
|
||||
$this->io->expects([
|
||||
['text' => 'source suggests:'],
|
||||
['text' => ' - target1: [1;37;42m Like us on Facebook [0m'],
|
||||
['text' => ' - target2: <bg=green>Like us on Facebook</>'],
|
||||
['text' => ''],
|
||||
], true);
|
||||
|
||||
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
|
||||
}
|
||||
|
@ -204,16 +194,14 @@ class SuggestedPackagesReporterTest extends TestCase
|
|||
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
|
||||
$this->suggestedPackagesReporter->addPackage('source package', 'target', 'because reasons');
|
||||
|
||||
$this->io->expects($this->exactly(6))
|
||||
->method('write')
|
||||
->withConsecutive(
|
||||
['<comment>a</comment> suggests:'],
|
||||
[' - <info>b</info>: c'],
|
||||
[''],
|
||||
['<comment>source package</comment> suggests:'],
|
||||
[' - <info>target</info>: because reasons'],
|
||||
['']
|
||||
);
|
||||
$this->io->expects([
|
||||
['text' => 'a suggests:'],
|
||||
['text' => ' - b: c'],
|
||||
['text' => ''],
|
||||
['text' => 'source package suggests:'],
|
||||
['text' => ' - target: because reasons'],
|
||||
['text' => ''],
|
||||
], true);
|
||||
|
||||
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
|
||||
}
|
||||
|
@ -245,13 +233,11 @@ class SuggestedPackagesReporterTest extends TestCase
|
|||
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
|
||||
$this->suggestedPackagesReporter->addPackage('source package', 'target', 'because reasons');
|
||||
|
||||
$this->io->expects($this->exactly(3))
|
||||
->method('write')
|
||||
->withConsecutive(
|
||||
['<comment>source package</comment> suggests:'],
|
||||
[' - <info>target</info>: because reasons'],
|
||||
['']
|
||||
);
|
||||
$this->io->expects([
|
||||
['text' => 'source package suggests:'],
|
||||
['text' => ' - target: because reasons'],
|
||||
['text' => ''],
|
||||
], true);
|
||||
|
||||
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE, $repository);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Test\Mock;
|
||||
|
||||
use Composer\Config;
|
||||
use Composer\IO\BufferIO;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Pcre\PcreException;
|
||||
use Composer\Pcre\Preg;
|
||||
use Composer\Util\HttpDownloader;
|
||||
use Composer\Util\Http\Response;
|
||||
use Composer\Downloader\TransportException;
|
||||
use Composer\Util\Platform;
|
||||
use LogicException;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use PHPUnit\Framework\AssertionFailedError;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class IOMock extends BufferIO
|
||||
{
|
||||
/**
|
||||
* @var list<array{text: string, verbosity?: IOInterface::*}|array{ask: string, reply?: string}|array{auth: array{string, string, string|null}}>|null
|
||||
*/
|
||||
private $expectations = null;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $strict = false;
|
||||
/**
|
||||
* @var list<array{string, string, string|null}>
|
||||
*/
|
||||
private $authLog = [];
|
||||
|
||||
/**
|
||||
* @param IOInterface::* $verbosity
|
||||
*/
|
||||
public function __construct(int $verbosity)
|
||||
{
|
||||
$sfVerbosity = [
|
||||
self::QUIET => OutputInterface::VERBOSITY_QUIET,
|
||||
self::NORMAL => OutputInterface::VERBOSITY_NORMAL,
|
||||
self::VERBOSE => OutputInterface::VERBOSITY_VERBOSE,
|
||||
self::VERY_VERBOSE => OutputInterface::VERBOSITY_VERY_VERBOSE,
|
||||
self::DEBUG => OutputInterface::VERBOSITY_DEBUG,
|
||||
][$verbosity];
|
||||
parent::__construct('', $sfVerbosity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array{text: string, verbosity?: IOInterface::*, regex?: true}|array{ask: string, reply: string}|array{auth: array{string, string, string|null}}> $expectations
|
||||
* @param bool $strict set to true if you want to provide *all* expected messages, and not just a subset you are interested in testing
|
||||
*/
|
||||
public function expects(array $expectations, bool $strict = false): void
|
||||
{
|
||||
$this->expectations = $expectations;
|
||||
$inputs = [];
|
||||
foreach ($expectations as $expect) {
|
||||
if (isset($expect['ask'], $expect['reply'])) {
|
||||
if (!is_string($expect['reply'])) {
|
||||
throw new \LogicException('A question\'s reply must be a string, use empty string for null replies');
|
||||
}
|
||||
$inputs[] = $expect['reply'];
|
||||
}
|
||||
}
|
||||
|
||||
if (count($inputs) > 0) {
|
||||
$this->setUserInputs($inputs);
|
||||
}
|
||||
|
||||
$this->strict = $strict;
|
||||
}
|
||||
|
||||
public function assertComplete(): void
|
||||
{
|
||||
$output = $this->getOutput();
|
||||
|
||||
if (Platform::getEnv('DEBUG_OUTPUT') === '1') {
|
||||
echo PHP_EOL.'Collected output: '.$output.PHP_EOL;
|
||||
}
|
||||
|
||||
// this was not configured to expect anything, so no need to react here
|
||||
if (!is_array($this->expectations)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (count($this->expectations) > 0) {
|
||||
$lines = Preg::split("{\r?\n}", $output);
|
||||
|
||||
foreach ($this->expectations as $expect) {
|
||||
if (isset($expect['auth'])) {
|
||||
while (count($this->authLog) > 0) {
|
||||
$auth = array_shift($this->authLog);
|
||||
if ($auth === $expect['auth']) {
|
||||
continue 2;
|
||||
}
|
||||
|
||||
if ($this->strict) {
|
||||
throw new AssertionFailedError('IO authentication mismatch. Expected:'.PHP_EOL.json_encode($expect['auth']).PHP_EOL.'Got:'.PHP_EOL.json_encode($auth));
|
||||
}
|
||||
}
|
||||
|
||||
throw new AssertionFailedError('Expected "'.json_encode($expect['auth']).'" auth to be set but there are no setAuthentication calls left to consume.');
|
||||
}
|
||||
|
||||
if (isset($expect['ask'], $expect['reply'])) {
|
||||
$pattern = '{^'.preg_quote($expect['ask']).'$}';
|
||||
} elseif (isset($expect['regex']) && $expect['regex']) {
|
||||
$pattern = $expect['text'];
|
||||
} else {
|
||||
$pattern = '{^'.preg_quote($expect['text']).'$}';
|
||||
}
|
||||
|
||||
while (count($lines) > 0) {
|
||||
$line = array_shift($lines);
|
||||
try {
|
||||
if (Preg::isMatch($pattern, $line)) {
|
||||
continue 2;
|
||||
}
|
||||
} catch (PcreException $e) {
|
||||
throw new LogicException('Invalid regex pattern in IO expectation "'.$pattern.'": '.$e->getMessage());
|
||||
}
|
||||
|
||||
if ($this->strict) {
|
||||
throw new AssertionFailedError('IO output mismatch. Expected:'.PHP_EOL.($expect['text'] ?? $expect['ask']).PHP_EOL.'Got:'.PHP_EOL.$line);
|
||||
}
|
||||
}
|
||||
|
||||
throw new AssertionFailedError('Expected "'.($expect['text'] ?? $expect['ask']).'" to be output still but there is no output left to consume. Complete output:'.PHP_EOL.$output);
|
||||
}
|
||||
} elseif ($output !== '' && $this->strict) {
|
||||
throw new AssertionFailedError('There was strictly no output expected but some output occurred: '.$output);
|
||||
}
|
||||
|
||||
// dummy assertion to ensure the test is not marked as having no assertions
|
||||
Assert::assertTrue(true); // @phpstan-ignore-line
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function ask($question, $default = null)
|
||||
{
|
||||
return parent::ask(rtrim($question, "\r\n").PHP_EOL, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function askConfirmation($question, $default = true)
|
||||
{
|
||||
return parent::askConfirmation(rtrim($question, "\r\n").PHP_EOL, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function askAndValidate($question, $validator, $attempts = null, $default = null)
|
||||
{
|
||||
return parent::askAndValidate(rtrim($question, "\r\n").PHP_EOL, $validator, $attempts, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function askAndHideAnswer($question)
|
||||
{
|
||||
// do not hide answer in tests because that blocks on windows with hiddeninput.exe
|
||||
return parent::ask(rtrim($question, "\r\n").PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function select($question, $choices, $default, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false)
|
||||
{
|
||||
return parent::select(rtrim($question, "\r\n").PHP_EOL, $choices, $default, $attempts, $errorMessage, $multiselect);
|
||||
}
|
||||
|
||||
public function setAuthentication($repositoryName, $username, $password = null)
|
||||
{
|
||||
$this->authentications[$repositoryName] = ['username' => $username, 'password' => $password];
|
||||
$this->authLog[] = [$repositoryName, $username, $password];
|
||||
|
||||
parent::setAuthentication($repositoryName, $username, $password);
|
||||
}
|
||||
}
|
|
@ -55,7 +55,7 @@ class GitDriverTest extends TestCase
|
|||
public function testGetRootIdentifierFromRemoteLocalRepository(): void
|
||||
{
|
||||
$process = $this->getProcessExecutorMock();
|
||||
$io = $this->getMockBuilder(IOInterface::class)->getMock();
|
||||
$io = $this->getIOMock();
|
||||
|
||||
$driver = new GitDriver(['url' => $this->home], $io, $this->config, $this->getHttpDownloaderMock(), $process);
|
||||
$this->setRepoDir($driver, $this->home);
|
||||
|
@ -82,11 +82,9 @@ GIT;
|
|||
public function testGetRootIdentifierFromRemote(): void
|
||||
{
|
||||
$process = $this->getProcessExecutorMock();
|
||||
$io = $this->getMockBuilder(IOInterface::class)->getMock();
|
||||
$io = $this->getIOMock();
|
||||
|
||||
$io
|
||||
->expects($this->never())
|
||||
->method('writeError');
|
||||
$io->expects([], true);
|
||||
|
||||
$driver = new GitDriver(['url' => 'https://example.org/acme.git'], $io, $this->config, $this->getHttpDownloaderMock(), $process);
|
||||
$this->setRepoDir($driver, $this->home);
|
||||
|
@ -119,7 +117,7 @@ GIT;
|
|||
Platform::putEnv('COMPOSER_DISABLE_NETWORK', '1');
|
||||
|
||||
$process = $this->getProcessExecutorMock();
|
||||
$io = $this->getMockBuilder(IOInterface::class)->getMock();
|
||||
$io = $this->getIOMock();
|
||||
|
||||
$driver = new GitDriver(['url' => 'https://example.org/acme.git'], $io, $this->config, $this->getHttpDownloaderMock(), $process);
|
||||
$this->setRepoDir($driver, $this->home);
|
||||
|
|
|
@ -24,6 +24,7 @@ use Composer\Package\PackageInterface;
|
|||
use Composer\Semver\Constraint\Constraint;
|
||||
use Composer\Test\Mock\FactoryMock;
|
||||
use Composer\Test\Mock\HttpDownloaderMock;
|
||||
use Composer\Test\Mock\IOMock;
|
||||
use Composer\Test\Mock\ProcessExecutorMock;
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\Platform;
|
||||
|
@ -59,6 +60,10 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
|
|||
* @var list<ProcessExecutorMock>
|
||||
*/
|
||||
private $processExecutorMocks = [];
|
||||
/**
|
||||
* @var list<IOMock>
|
||||
*/
|
||||
private $ioMocks = [];
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
|
@ -75,6 +80,9 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
|
|||
foreach ($this->processExecutorMocks as $mock) {
|
||||
$mock->assertComplete();
|
||||
}
|
||||
foreach ($this->ioMocks as $mock) {
|
||||
$mock->assertComplete();
|
||||
}
|
||||
|
||||
if (null !== $this->prevCwd) {
|
||||
chdir($this->prevCwd);
|
||||
|
@ -181,7 +189,7 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
|
|||
{
|
||||
$factory = new FactoryMock();
|
||||
|
||||
$locker = new Locker($this->getMockBuilder(IOInterface::class)->getMock(), new JsonFile('./composer.lock'), $factory->createInstallationManager(), (string) file_get_contents('./composer.json'));
|
||||
$locker = new Locker($this->getIOMock(), new JsonFile('./composer.lock'), $factory->createInstallationManager(), (string) file_get_contents('./composer.json'));
|
||||
$locker->setLockData($packages, $devPackages, [], [], [], 'dev', [], false, false, []);
|
||||
}
|
||||
|
||||
|
@ -356,6 +364,16 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
|
|||
return $mock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IOInterface::* $verbosity
|
||||
*/
|
||||
protected function getIOMock(int $verbosity = IOInterface::DEBUG): IOMock
|
||||
{
|
||||
$this->ioMocks[] = $mock = new IOMock($verbosity);
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
protected function createTempFile(?string $dir = null): string
|
||||
{
|
||||
$dir = $dir ?? sys_get_temp_dir();
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
namespace Composer\Test\Util;
|
||||
|
||||
use Composer\Test\Mock\IOMock;
|
||||
use Composer\Util\Bitbucket;
|
||||
use Composer\Util\Http\Response;
|
||||
use Composer\Test\TestCase;
|
||||
|
@ -36,7 +37,7 @@ class BitbucketTest extends TestCase
|
|||
/** @var string */
|
||||
private $token = 'bitbuckettoken';
|
||||
|
||||
/** @var \Composer\IO\ConsoleIO&\PHPUnit\Framework\MockObject\MockObject */
|
||||
/** @var IOMock */
|
||||
private $io;
|
||||
/** @var \Composer\Util\HttpDownloader&\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $httpDownloader;
|
||||
|
@ -49,11 +50,7 @@ class BitbucketTest extends TestCase
|
|||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->io = $this
|
||||
->getMockBuilder('Composer\IO\ConsoleIO')
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
;
|
||||
$this->io = $this->getIOMock();
|
||||
|
||||
$this->httpDownloader = $this
|
||||
->getMockBuilder('Composer\Util\HttpDownloader')
|
||||
|
@ -70,9 +67,9 @@ class BitbucketTest extends TestCase
|
|||
|
||||
public function testRequestAccessTokenWithValidOAuthConsumer(): void
|
||||
{
|
||||
$this->io->expects($this->once())
|
||||
->method('setAuthentication')
|
||||
->with($this->origin, $this->consumer_key, $this->consumer_secret);
|
||||
$this->io->expects([
|
||||
['auth' => [$this->origin, $this->consumer_key, $this->consumer_secret]],
|
||||
]);
|
||||
|
||||
$this->httpDownloader->expects($this->once())
|
||||
->method('get')
|
||||
|
@ -151,9 +148,9 @@ class BitbucketTest extends TestCase
|
|||
]
|
||||
);
|
||||
|
||||
$this->io->expects($this->once())
|
||||
->method('setAuthentication')
|
||||
->with($this->origin, $this->consumer_key, $this->consumer_secret);
|
||||
$this->io->expects([
|
||||
['auth' => [$this->origin, $this->consumer_key, $this->consumer_secret]],
|
||||
]);
|
||||
|
||||
$this->httpDownloader->expects($this->once())
|
||||
->method('get')
|
||||
|
@ -189,19 +186,14 @@ class BitbucketTest extends TestCase
|
|||
|
||||
public function testRequestAccessTokenWithUsernameAndPassword(): void
|
||||
{
|
||||
$this->io->expects($this->once())
|
||||
->method('setAuthentication')
|
||||
->with($this->origin, $this->username, $this->password);
|
||||
|
||||
$this->io->expects($this->any())
|
||||
->method('writeError')
|
||||
->withConsecutive(
|
||||
['<error>Invalid OAuth consumer provided.</error>'],
|
||||
['This can have three reasons:'],
|
||||
['1. You are authenticating with a bitbucket username/password combination'],
|
||||
['2. You are using an OAuth consumer, but didn\'t configure a (dummy) callback url'],
|
||||
['3. You are using an OAuth consumer, but didn\'t configure it as private consumer']
|
||||
);
|
||||
$this->io->expects([
|
||||
['auth' => [$this->origin, $this->username, $this->password]],
|
||||
['text' => 'Invalid OAuth consumer provided.'],
|
||||
['text' => 'This can have three reasons:'],
|
||||
['text' => '1. You are authenticating with a bitbucket username/password combination'],
|
||||
['text' => '2. You are using an OAuth consumer, but didn\'t configure a (dummy) callback url'],
|
||||
['text' => '3. You are using an OAuth consumer, but didn\'t configure it as private consumer'],
|
||||
], true);
|
||||
|
||||
$this->httpDownloader->expects($this->once())
|
||||
->method('get')
|
||||
|
@ -240,16 +232,11 @@ class BitbucketTest extends TestCase
|
|||
->with('bitbucket-oauth')
|
||||
->willReturn(null);
|
||||
|
||||
$this->io->expects($this->once())
|
||||
->method('setAuthentication')
|
||||
->with($this->origin, $this->username, $this->password);
|
||||
|
||||
$this->io->expects($this->any())
|
||||
->method('writeError')
|
||||
->withConsecutive(
|
||||
['<error>Invalid OAuth consumer provided.</error>'],
|
||||
['You can also add it manually later by using "composer config --global --auth bitbucket-oauth.bitbucket.org <consumer-key> <consumer-secret>"']
|
||||
);
|
||||
$this->io->expects([
|
||||
['auth' => [$this->origin, $this->username, $this->password]],
|
||||
['text' => 'Invalid OAuth consumer provided.'],
|
||||
['text' => 'You can also add it manually later by using "composer config --global --auth bitbucket-oauth.bitbucket.org <consumer-key> <consumer-secret>"'],
|
||||
], true);
|
||||
|
||||
$this->httpDownloader->expects($this->once())
|
||||
->method('get')
|
||||
|
@ -276,9 +263,9 @@ class BitbucketTest extends TestCase
|
|||
->with('bitbucket-oauth')
|
||||
->willReturn(null);
|
||||
|
||||
$this->io->expects($this->once())
|
||||
->method('setAuthentication')
|
||||
->with($this->origin, $this->username, $this->password);
|
||||
$this->io->expects([
|
||||
['auth' => [$this->origin, $this->username, $this->password]],
|
||||
]);
|
||||
|
||||
$exception = new \Composer\Downloader\TransportException('HTTP/1.1 404 NOT FOUND', 404);
|
||||
$this->httpDownloader->expects($this->once())
|
||||
|
@ -300,19 +287,11 @@ class BitbucketTest extends TestCase
|
|||
|
||||
public function testUsernamePasswordAuthenticationFlow(): void
|
||||
{
|
||||
$this->io
|
||||
->expects($this->atLeastOnce())
|
||||
->method('writeError')
|
||||
->withConsecutive([$this->message])
|
||||
;
|
||||
|
||||
$this->io->expects($this->exactly(2))
|
||||
->method('askAndHideAnswer')
|
||||
->withConsecutive(
|
||||
['Consumer Key (hidden): '],
|
||||
['Consumer Secret (hidden): ']
|
||||
)
|
||||
->willReturnOnConsecutiveCalls($this->consumer_key, $this->consumer_secret);
|
||||
$this->io->expects([
|
||||
['text' => $this->message],
|
||||
['ask' => 'Consumer Key (hidden): ', 'reply' => $this->consumer_key],
|
||||
['ask' => 'Consumer Secret (hidden): ', 'reply' => $this->consumer_secret],
|
||||
]);
|
||||
|
||||
$this->httpDownloader
|
||||
->expects($this->once())
|
||||
|
@ -346,10 +325,9 @@ class BitbucketTest extends TestCase
|
|||
->method('getAuthConfigSource')
|
||||
->willReturn($authConfigSourceMock);
|
||||
|
||||
$this->io->expects($this->once())
|
||||
->method('askAndHideAnswer')
|
||||
->with('Consumer Key (hidden): ')
|
||||
->willReturnOnConsecutiveCalls(null);
|
||||
$this->io->expects([
|
||||
['ask' => 'Consumer Key (hidden): ', 'reply' => ''],
|
||||
]);
|
||||
|
||||
$this->assertFalse($this->bitbucket->authorizeOAuthInteractively($this->origin, $this->message));
|
||||
}
|
||||
|
@ -361,13 +339,11 @@ class BitbucketTest extends TestCase
|
|||
->method('getAuthConfigSource')
|
||||
->willReturn($authConfigSourceMock);
|
||||
|
||||
$this->io->expects($this->exactly(2))
|
||||
->method('askAndHideAnswer')
|
||||
->withConsecutive(
|
||||
['Consumer Key (hidden): '],
|
||||
['Consumer Secret (hidden): ']
|
||||
)
|
||||
->willReturnOnConsecutiveCalls($this->consumer_key, null);
|
||||
$this->io->expects([
|
||||
['text' => $this->message],
|
||||
['ask' => 'Consumer Key (hidden): ', 'reply' => $this->consumer_key],
|
||||
['ask' => 'Consumer Secret (hidden): ', 'reply' => ''],
|
||||
]);
|
||||
|
||||
$this->assertFalse($this->bitbucket->authorizeOAuthInteractively($this->origin, $this->message));
|
||||
}
|
||||
|
@ -379,13 +355,11 @@ class BitbucketTest extends TestCase
|
|||
->method('getAuthConfigSource')
|
||||
->willReturn($authConfigSourceMock);
|
||||
|
||||
$this->io->expects($this->exactly(2))
|
||||
->method('askAndHideAnswer')
|
||||
->withConsecutive(
|
||||
['Consumer Key (hidden): '],
|
||||
['Consumer Secret (hidden): ']
|
||||
)
|
||||
->willReturnOnConsecutiveCalls($this->consumer_key, $this->consumer_secret);
|
||||
$this->io->expects([
|
||||
['text' => $this->message],
|
||||
['ask' => 'Consumer Key (hidden): ', 'reply' => $this->consumer_key],
|
||||
['ask' => 'Consumer Secret (hidden): ', 'reply' => $this->consumer_secret],
|
||||
]);
|
||||
|
||||
$this->httpDownloader
|
||||
->expects($this->once())
|
||||
|
|
|
@ -30,17 +30,10 @@ class GitHubTest extends TestCase
|
|||
public function testUsernamePasswordAuthenticationFlow(): void
|
||||
{
|
||||
$io = $this->getIOMock();
|
||||
$io
|
||||
->expects($this->atLeastOnce())
|
||||
->method('writeError')
|
||||
->withConsecutive([$this->message])
|
||||
;
|
||||
$io
|
||||
->expects($this->once())
|
||||
->method('askAndHideAnswer')
|
||||
->with('Token (hidden): ')
|
||||
->willReturn($this->password)
|
||||
;
|
||||
$io->expects([
|
||||
['text' => $this->message],
|
||||
['ask' => 'Token (hidden): ', 'reply' => $this->password],
|
||||
]);
|
||||
|
||||
$httpDownloader = $this->getHttpDownloaderMock();
|
||||
$httpDownloader->expects(
|
||||
|
@ -68,12 +61,9 @@ class GitHubTest extends TestCase
|
|||
public function testUsernamePasswordFailure(): void
|
||||
{
|
||||
$io = $this->getIOMock();
|
||||
$io
|
||||
->expects($this->exactly(1))
|
||||
->method('askAndHideAnswer')
|
||||
->with('Token (hidden): ')
|
||||
->willReturn($this->password)
|
||||
;
|
||||
$io->expects([
|
||||
['ask' => 'Token (hidden): ', 'reply' => $this->password],
|
||||
]);
|
||||
|
||||
$httpDownloader = $this->getHttpDownloaderMock();
|
||||
$httpDownloader->expects(
|
||||
|
@ -93,20 +83,6 @@ class GitHubTest extends TestCase
|
|||
$this->assertFalse($github->authorizeOAuthInteractively($this->origin));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PHPUnit\Framework\MockObject\MockObject&\Composer\IO\ConsoleIO
|
||||
*/
|
||||
private function getIOMock()
|
||||
{
|
||||
$io = $this
|
||||
->getMockBuilder('Composer\IO\ConsoleIO')
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
;
|
||||
|
||||
return $io;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PHPUnit\Framework\MockObject\MockObject&\Composer\Config
|
||||
*/
|
||||
|
|
|
@ -36,23 +36,11 @@ class GitLabTest extends TestCase
|
|||
public function testUsernamePasswordAuthenticationFlow(): void
|
||||
{
|
||||
$io = $this->getIOMock();
|
||||
$io
|
||||
->expects($this->atLeastOnce())
|
||||
->method('writeError')
|
||||
->withConsecutive([$this->message])
|
||||
;
|
||||
$io
|
||||
->expects($this->once())
|
||||
->method('ask')
|
||||
->with('Username: ')
|
||||
->willReturn($this->username)
|
||||
;
|
||||
$io
|
||||
->expects($this->once())
|
||||
->method('askAndHideAnswer')
|
||||
->with('Password: ')
|
||||
->willReturn($this->password)
|
||||
;
|
||||
$io->expects([
|
||||
['text' => $this->message],
|
||||
['ask' => 'Username: ', 'reply' => $this->username],
|
||||
['ask' => 'Password: ', 'reply' => $this->password],
|
||||
]);
|
||||
|
||||
$httpDownloader = $this->getHttpDownloaderMock();
|
||||
$httpDownloader->expects(
|
||||
|
@ -77,18 +65,18 @@ class GitLabTest extends TestCase
|
|||
self::expectException('RuntimeException');
|
||||
self::expectExceptionMessage('Invalid GitLab credentials 5 times in a row, aborting.');
|
||||
$io = $this->getIOMock();
|
||||
$io
|
||||
->expects($this->exactly(5))
|
||||
->method('ask')
|
||||
->with('Username: ')
|
||||
->willReturn($this->username)
|
||||
;
|
||||
$io
|
||||
->expects($this->exactly(5))
|
||||
->method('askAndHideAnswer')
|
||||
->with('Password: ')
|
||||
->willReturn($this->password)
|
||||
;
|
||||
$io->expects([
|
||||
['ask' => 'Username: ', 'reply' => $this->username],
|
||||
['ask' => 'Password: ', 'reply' => $this->password],
|
||||
['ask' => 'Username: ', 'reply' => $this->username],
|
||||
['ask' => 'Password: ', 'reply' => $this->password],
|
||||
['ask' => 'Username: ', 'reply' => $this->username],
|
||||
['ask' => 'Password: ', 'reply' => $this->password],
|
||||
['ask' => 'Username: ', 'reply' => $this->username],
|
||||
['ask' => 'Password: ', 'reply' => $this->password],
|
||||
['ask' => 'Username: ', 'reply' => $this->username],
|
||||
['ask' => 'Password: ', 'reply' => $this->password],
|
||||
]);
|
||||
|
||||
$httpDownloader = $this->getHttpDownloaderMock();
|
||||
$httpDownloader->expects(
|
||||
|
@ -114,20 +102,6 @@ class GitLabTest extends TestCase
|
|||
$gitLab->authorizeOAuthInteractively('https', $this->origin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PHPUnit\Framework\MockObject\MockObject&\Composer\IO\ConsoleIO
|
||||
*/
|
||||
private function getIOMock()
|
||||
{
|
||||
$io = $this
|
||||
->getMockBuilder('Composer\IO\ConsoleIO')
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
;
|
||||
|
||||
return $io;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PHPUnit\Framework\MockObject\MockObject&\Composer\Config
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue