Skip to content

Commit

Permalink
Merge 74499eb into 44c506b
Browse files Browse the repository at this point in the history
  • Loading branch information
yoanm committed Apr 2, 2023
2 parents 44c506b + 74499eb commit e5323bf
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 38 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/CI.yml
Expand Up @@ -155,7 +155,7 @@ jobs:
run: make build

- name: ComposerRequireChecker
uses: docker://webfactory/composer-require-checker:3.2.0
uses: docker://webfactory/composer-require-checker:4.5.0

- name: Dependencies check
if: ${{ github.event_name == 'pull_request' }}
Expand Down Expand Up @@ -232,7 +232,8 @@ jobs:

- name: Build
run: |
composer require -W ${{ env.COMPOSER_OPTIONS }} \
composer config minimum-stability dev \
&& composer require -W ${{ env.COMPOSER_OPTIONS }} \
phpunit/phpunit:^${{ matrix.phpunit-version }} \
&& composer update \
&& make build
Expand Down
11 changes: 10 additions & 1 deletion features/bootstrap/FeatureContext.php
Expand Up @@ -61,6 +61,11 @@ public function iRunPhpunitTestSuite()
$this->process = new Process($argList, null, $env);

$this->process->run();

//var_dump(['env' => $env]);
//var_dump(['argList' => $argList]);
//var_dump(['output' => $this->process->getOutput()]);
//var_dump(['error' => $this->process->getErrorOutput()]);
}

/**
Expand All @@ -70,7 +75,11 @@ public function iShouldHaveXFailures($expectedFailureCount)
{
$output = $this->process->getOutput();

$failureCount = preg_match_all(sprintf('#There was %s failure#', $expectedFailureCount), $output);
$matched = preg_match_all('#There (?:was|were) (\d+) failure#', $output, $matches);
if ($matched === false) {
throw new \Exception('Error when executing preg_match_all');
}
$failureCount = $matches[1][0];
if ($failureCount != $expectedFailureCount) {
throw new \Exception(sprintf('Found %d failure, but %d expected', $failureCount, $expectedFailureCount));
}
Expand Down
3 changes: 3 additions & 0 deletions features/demo_app/tests/RiskyOutputTest.php
@@ -1,5 +1,8 @@
<?php

/**
* @coversNothing
*/
class RiskyOutputTest extends \PHPUnit\Framework\TestCase
{
/**
Expand Down
45 changes: 30 additions & 15 deletions src/Listener/RiskyToFailedListener.php
Expand Up @@ -2,11 +2,15 @@
namespace Yoanm\PhpUnitExtended\Listener;

use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\CoveredCodeNotExecutedException;
use PHPUnit\Framework\InvalidCoversTargetException;
use PHPUnit\Framework\MissingCoversAnnotationException;
use PHPUnit\Framework\OutputError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestListenerDefaultImplementation;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\UnintentionallyCoveredCodeError;
use PHPUnit\Framework\Warning;

Expand Down Expand Up @@ -41,9 +45,12 @@ public function addRiskyTest(Test $test, \Throwable $e, float $time) : void
protected function addErrorIfNeeded(Test $test, \Throwable $e, $time)
{
/* Must be TestCase instance to have access to "getTestResultObject" method */
if ($test instanceof TestCase && $test->getTestResultObject() !== null) {
if ($test instanceof TestCase) {
$reason = $this->getErrorReason($e);
if (null !== $reason) {
if (!$test->getTestResultObject()) {
$test->setTestResultObject(new TestResult());
}
$test->getTestResultObject()->addFailure(
$test,
new AssertionFailedError(
Expand All @@ -66,32 +73,40 @@ protected function addErrorIfNeeded(Test $test, \Throwable $e, $time)
*/
protected function getErrorReason(\Throwable $e)
{
$reason = null;
$reason = $e->getMessage();
switch (true) {
/* beStrictAboutOutputDuringTests="true" */
case $e instanceof OutputError:
$reason = 'No output during test';
break;
return 'No output during test';
/* checkForUnintentionallyCoveredCode="true" */
case $e instanceof UnintentionallyCoveredCodeError:
$reason = 'Executed code must be defined with @covers and @uses annotations';
break;
case $e instanceof InvalidCoversTargetException:
return 'Executed code must be defined with @covers and @uses annotations';
default:
if (preg_match('#\-\-\- Global variables before the test#', $e->getMessage())) {
if (str_contains($e->getMessage(), '--- Global variables before the test')) {
/* beStrictAboutChangesToGlobalState="true" (no specific exception) for globals */
$reason = 'No global variable manipulation during test';
} elseif (preg_match('#\-\-\- Static attributes before the test#', $e->getMessage())) {
return 'No global variable manipulation during test';
} elseif (str_contains($e->getMessage(), '--- Static attributes before the test')) {
/* beStrictAboutChangesToGlobalState="true" (no specific exception) for static var */
/* Only when beStrictAboutChangesToGlobalState="true" */
$reason = 'No static attribute manipulation during test';
} elseif (preg_match('#This test did not perform any assertions#', $e->getMessage())) {
return 'No static attribute manipulation during test';
} elseif (str_contains($e->getMessage(), 'This test did not perform any assertions')) {
/* beStrictAboutTestsThatDoNotTestAnything="true" (no specific exception) */
$reason = 'No test that do not test anything';
} elseif (preg_match('#"@covers [^"]+" is invalid#', $e->getMessage())) {
return 'No test that do not test anything';
} elseif ($e instanceof CoveredCodeNotExecutedException
|| preg_match('#"@covers [^"]+" is invalid#', $e->getMessage())
) {
/* forceCoversAnnotation="true" (no specific exception) */
return 'Only executed code must be defined with @covers and @uses annotations';
} elseif ($e instanceof MissingCoversAnnotationException
|| str_contains(
$e->getMessage(),
'This test does not have a @covers annotation but is expected to have one'
)
) {
/* forceCoversAnnotation="true" (no specific exception) */
$reason = 'Only executed code must be defined with @covers and @uses annotations';
return 'Missing @covers or @coversNothing annotation';
}
break;
}

return $reason;
Expand Down
52 changes: 32 additions & 20 deletions tests/Functional/Listener/RiskyToFailedListenerTest.php
@@ -1,6 +1,9 @@
<?php
namespace Tests\Functional\Listener;

use PHPUnit\Framework\CoveredCodeNotExecutedException;
use PHPUnit\Framework\InvalidCoversTargetException;
use PHPUnit\Framework\MissingCoversAnnotationException;
use PHPUnit\Framework\OutputError;
use PHPUnit\Framework\RiskyTestError;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -50,32 +53,24 @@ public function testShouldHandleBadCoverageTagWarning()
*
* @param $exceptionClass
* @param $expectedReason
* @param bool|true $called
* @param null $exceptionMessage
*/
public function testShouldHandleRiskyTestWith(
$exceptionClass,
$expectedReason,
$called,
$exceptionMessage = 'my default exception message'
) {
$time = 0.3;
$test = new TestCaseMock();
$exception = new $exceptionClass($exceptionMessage);

$test->setTestResultObject(new TestResult());

$this->listener->addRiskyTest($test, $exception, $time);

$failures = $test->getTestResultObject()->failures();
if ($called) {
$this->assertCount(1, $failures);
$failure = array_shift($failures);
$this->assertInstanceOf(TestFailure::class, $failure);
$this->assertStringContainsString($exceptionMessage, $failure->getExceptionAsString());
} else {
$this->assertCount(0, $failures);
}
$this->assertCount(1, $failures);
$failure = array_shift($failures);
$this->assertInstanceOf(TestFailure::class, $failure);
$this->assertStringContainsString($exceptionMessage, $failure->getExceptionAsString());
}

/**
Expand All @@ -87,35 +82,52 @@ public function getExceptionMessageProvider()
'Output exception' => [
'exceptionClass' => OutputError::class,
'expectedMessage' => 'No output during test',
'called' => true,
],
'Coverage exception' => [
'Coverage overflow - UnintentionallyCoveredCodeError' => [
'exceptionClass' => UnintentionallyCoveredCodeError::class,
'expectedMessage' => 'Executed code must be defined with @covers and @uses annotations',
'called' => true,
],
'Coverage overflow - InvalidCoversTargetException instance' => [
'exceptionClass' => InvalidCoversTargetException::class,
'expectedMessage' => 'Executed code must be defined with @covers and @uses annotations',
],
'Globals manipulation - globals' => [
'exceptionClass' => RiskyTestError::class,
'expectedMessage' => 'No global variable manipulation during test',
'called' => true,
'exceptionMessage' => '--- Global variables before the test',
],
'Globals manipulation - static' => [
'exceptionClass' => RiskyTestError::class,
'expectedMessage' => 'No static attribute manipulation during test',
'called' => true,
'exceptionMessage' => '--- Static attributes before the test',
],
'Test nothing' => [
'exceptionClass' => RiskyTestError::class,
'expectedMessage' => 'No test that do not test anything',
'called' => true,
'exceptionMessage' => 'This test did not perform any assertions',
],
'Coverage exception - CoveredCodeNotExecutedException instance' => [
'exceptionClass' => CoveredCodeNotExecutedException::class,
'expectedMessage' => 'Only executed code must be defined with @covers and @uses annotations',
],
'Coverage exception - by message' => [
'exceptionClass' => RiskyTestError::class,
'expectedMessage' => 'Only executed code must be defined with @covers and @uses annotations',
'exceptionMessage' => '"@covers A\Class" is invalid'
],
'Missing @covers - MissingCoversAnnotationException instance' => [
'exceptionClass' => MissingCoversAnnotationException::class,
'expectedMessage' => 'Missing @covers or @coversNothing annotation',
],
'Missing @covers - by message' => [
'exceptionClass' => RiskyTestError::class,
'expectedMessage' => 'Missing @covers or @coversNothing annotation',
'exceptionMessage' => '"@covers A\Class" is invalid'
],
'other exceptions' => [
'exceptionClass' => \Exception::class,
'expectedMessage' => 'Risky test',
'called' => false,
'expectedMessage' => 'Arghh !',
'exceptionMessage' => 'Arghh !',
],
];
}
Expand Down

0 comments on commit e5323bf

Please sign in to comment.