Skip to content

Commit

Permalink
Add more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
veewee committed Dec 2, 2020
1 parent 244eccb commit 4367040
Show file tree
Hide file tree
Showing 13 changed files with 284 additions and 5 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ jobs:
- name: Install dependencies
run: composer update --prefer-dist --no-progress --no-suggest
- name: Run the tests
run: composer run tests
run: |
composer run tests
composer parallel coverage infection
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
.phpunit.cache
.php_cs.cache
composer.lock
infection.log
7 changes: 6 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@
"fix-cs": "./tools/php-cs-fixer.phar fix",
"psalm": "./tools/psalm.phar",
"tests": "./tools/phpunit.phar",
"ci": "@parallel psalm cs tests",
"coverage": "@php ./tools/full-coverage-check.php .phpunit.cache/clover.xml",
"infection": "./tools/infection.phar",
"ci": [
"@parallel psalm cs tests",
"@parallel coverage infection"
],
"functional": "@parallel fun1 fun2",
"fun1": "@php -r \"sleep(3); echo '1';\"",
"fun2": "@php -r \"echo '2';\""
Expand Down
16 changes: 16 additions & 0 deletions infection.json.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"source": {
"directories": [
"src"
]
},
"phpUnit": {
"customPath": ".\/tools\/phpunit.phar"
},
"logs": {
"text": "infection.log"
},
"mutators": {
"@default": true
}
}
2 changes: 1 addition & 1 deletion src/ParallelPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,6 @@ public function getCapabilities(): array
*/
public function runParallelScript(Event $event): int
{
return (new ParallelScript())($event);
return ParallelScript::initializeAndRun($event);
}
}
11 changes: 9 additions & 2 deletions src/Scripts/ParallelScript.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,20 @@

class ParallelScript
{
private PhpExecutableFinder $executableFinder;

public function __construct(PhpExecutableFinder $executableFinder)
{
$this->executableFinder = $executableFinder;
}

/**
* @throws ParallelException
* @throws ScriptExecutionException
*/
public static function initializeAndRun(Event $event): int
{
$instance = new self();
$instance = new self(PhpExecutableFinder::default());

return $instance($event);
}
Expand All @@ -42,7 +49,7 @@ public function __invoke(Event $event): int

$loop = $event->getComposer()->getLoop();
$io = $event->getIO();
$executor = new AsyncTaskExecutor($loop, PhpExecutableFinder::default());
$executor = new AsyncTaskExecutor($loop, $this->executableFinder);
$resultMap = ResultMap::empty();

$io->write(['<warning>Running tasks in parallel:', ...$tasks, '</warning>']);
Expand Down
27 changes: 27 additions & 0 deletions tests/Unit/Command/ParallelCommandsProviderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace ComposerRunParallel\Test\Unit\Command;

use Composer\Plugin\Capability\CommandProvider;
use ComposerRunParallel\Command\ParallelCommandsProvider;
use PHPUnit\Framework\TestCase;

/**
* @covers \ComposerRunParallel\Command\ParallelCommandsProvider
*/
final class ParallelCommandsProviderTest extends TestCase
{
/** @test */
public function it_can_provide_commands(): void
{
$provider = new ParallelCommandsProvider();

self::assertInstanceOf(CommandProvider::class, $provider);

$commands = $provider->getCommands();
self::assertCount(1, $commands);
self::assertSame('parallel', $commands[0]->getName());
}
}
45 changes: 45 additions & 0 deletions tests/Unit/Exception/ParallelExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,53 @@

namespace ComposerRunParallel\Test\Unit\Exception;

use ComposerRunParallel\Exception\ParallelException;
use PHPUnit\Framework\TestCase;

/**
* @covers \ComposerRunParallel\Exception\ParallelException
*/
final class ParallelExceptionTest extends TestCase
{
/** @test */
public function it_can_throw_exception_on_invalid_amount_of_tasks(): void
{
$this->expectException(ParallelException::class);

throw ParallelException::atLeastOneTask();
}

/** @test */
public function it_can_throw_exception_on_unkown_task(): void
{
$this->expectException(ParallelException::class);
$this->expectExceptionMessage('taskName');

throw ParallelException::invalidTask('taskName');
}

/** @test */
public function it_can_throw_exception_on_no_result_yet_for_task(): void
{
$this->expectException(ParallelException::class);
$this->expectExceptionMessage('taskName');

throw ParallelException::noResultForTaskYet('taskName');
}

/** @test */
public function it_can_throw_exception_if_composer_does_not_contain_executor(): void
{
$this->expectException(ParallelException::class);

throw ParallelException::noProcessExecutorDetected();
}

/** @test */
public function it_can_throw_exception_if_php_binary_cannot_be_found(): void
{
$this->expectException(ParallelException::class);

throw ParallelException::phpBinaryNotFound();
}
}
77 changes: 77 additions & 0 deletions tests/Unit/Executor/AsyncTaskExecutorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

declare(strict_types=1);

namespace ComposerRunParallel\Test\Unit\Executor;

use Composer\Util\Loop;
use Composer\Util\ProcessExecutor;
use ComposerRunParallel\Exception\ParallelException;
use ComposerRunParallel\Executor\AsyncTaskExecutor;
use ComposerRunParallel\Finder\PhpExecutableFinder;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use React\Promise\FulfilledPromise;
use Symfony\Component\Process\PhpExecutableFinder as SymfonyPhpExecutableFinder;

/**
* @covers \ComposerRunParallel\Executor\AsyncTaskExecutor
* @covers \ComposerRunParallel\Finder\PhpExecutableFinder
*/
final class AsyncTaskExecutorTest extends TestCase
{
private AsyncTaskExecutor $asyncTaskExecutor;

/** @var MockObject & Loop */
private MockObject $loop;

/** @var MockObject & ProcessExecutor */
private MockObject $processExecutor;

protected function setUp(): void
{
$this->loop = $this->createMock(Loop::class);
$this->processExecutor = $this->createMock(ProcessExecutor::class);

$executableFinderMock = $this->createMock(SymfonyPhpExecutableFinder::class);
$executableFinderMock->method('find')->willReturn('php');
$executableFinderMock->method('findArguments')->willReturn(['phparg']);

$this->asyncTaskExecutor = new AsyncTaskExecutor($this->loop, new PhpExecutableFinder($executableFinderMock));
}

/** @test */
public function it_throws_exception_without_executor(): void
{
$this->expectException(ParallelException::class);
$this->expectExceptionMessage(ParallelException::noProcessExecutorDetected()->getMessage());

($this->asyncTaskExecutor)('task', []);
}

/** @test */
public function it_can_execute_a_task_async(): void
{
$this->loop->method('getProcessExecutor')->willReturn($this->processExecutor);
$this->processExecutor
->method('executeAsync')
->with($this->buildExecutableString()." 'task' 'arg1' 'arg2'")
->willReturn($expected = new FulfilledPromise('yes'));

$result = ($this->asyncTaskExecutor)('task', ['arg1', 'arg2']);
self::assertSame($expected, $result);
}

private function buildExecutableString()
{
return implode(' ', [
ProcessExecutor::escape('php'),
'phparg',
'-d allow_url_fopen='.ProcessExecutor::escape(ini_get('allow_url_fopen')),
'-d disable_functions='.ProcessExecutor::escape(ini_get('disable_functions')),
'-d memory_limit='.ProcessExecutor::escape(ini_get('memory_limit')),
ProcessExecutor::escape(getenv('COMPOSER_BINARY') ?: 'composer'),
ProcessExecutor::escape('run'),
]);
}
}
29 changes: 29 additions & 0 deletions tests/Unit/Finder/PhpExecutableFinderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace ComposerRunParallel\Test\Unit\Executor;

use ComposerRunParallel\Exception\ParallelException;
use ComposerRunParallel\Finder\PhpExecutableFinder;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Process\PhpExecutableFinder as SymfonyPhpExecutableFinder;

/**
* @covers \ComposerRunParallel\Finder\PhpExecutableFinder
*/
final class PhpExecutableFinderTest extends TestCase
{
/** @test */
public function it_throws_exception_on_php_executable_not_found(): void
{
$finder = $this->createMock(SymfonyPhpExecutableFinder::class);
$finder->method('find')->willReturn(false);

$this->expectException(ParallelException::class);
$this->expectExceptionMessage(ParallelException::phpBinaryNotFound()->getMessage());

$phpExecutableFinder = new PhpExecutableFinder($finder);
$phpExecutableFinder();
}
}
17 changes: 17 additions & 0 deletions tests/Unit/ParallelPluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
namespace ComposerRunParallel\Test\Unit;

use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\Plugin\Capability\CommandProvider;
use Composer\Plugin\Capable;
use Composer\Plugin\PluginInterface;
use ComposerRunParallel\Command\ParallelCommandsProvider;
use ComposerRunParallel\ParallelPlugin;
use PHPUnit\Framework\TestCase;

Expand All @@ -29,6 +32,20 @@ public function it_is_a_composer_event_subscriber(): void
self::assertSame(['parallel' => 'runParallelScript'], ParallelPlugin::getSubscribedEvents());
}

/** @test */
public function it_registers_commands(): void
{
$plugin = new ParallelPlugin();
self::assertInstanceOf(Capable::class, $plugin);

self::assertSame(
[
CommandProvider::class => ParallelCommandsProvider::class,
],
$plugin->getCapabilities()
);
}

/** @test */
public function it_can_run_the_parallel_script(): void
{
Expand Down
33 changes: 33 additions & 0 deletions tests/Unit/Scripts/ParallelScriptTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace ComposerRunParallel\Test\Unit\Script;

use PHPUnit\Framework\TestCase;

/**
* @covers \ComposerRunParallel\Scripts\ParallelScript
*/
class ParallelScriptTest extends TestCase
{
/** @test */
public function it_fails_if_there_are_not_tasks_specified(): void
{
}

/** @test */
public function it_fails_if_a_task_is_not_known(): void
{
}

/** @test */
public function it_can_successfully_run_scripts_in_parallel(): void
{
}

/** @test */
public function it_can_insuccessfully_run_scripts_in_parallel(): void
{
}
}
20 changes: 20 additions & 0 deletions tools/full-coverage-check.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

(static function (array $argv) {
$file = $argv[1] ?? null;
if (!$file || !file_exists($file)) {
throw new RuntimeException('Expected clover.xml as first argument. Invalid clover.xml file provided.');
}

$xml = simplexml_load_file($file);
$totalElements = (int) current($xml->xpath('/coverage/project/metrics/@elements'));
$checkedElements = (int) current($xml->xpath('/coverage/project/metrics/@coveredelements'));
$coverage = round(($checkedElements / $totalElements) * 100, 2);

if ($coverage !== 100) {
echo('Expected coverage of 100%, only got '.$coverage.'%.'.PHP_EOL);
exit(1);
}

echo 'Got 100% Coverage!'.PHP_EOL;
})($argv);

0 comments on commit 4367040

Please sign in to comment.