Skip to content

Commit

Permalink
Merge pull request #22 from xp-framework/feature/warnings-fail-tests
Browse files Browse the repository at this point in the history
Make warnings raised during test execution fail these tests
  • Loading branch information
thekid authored May 18, 2023
2 parents 4777ff2 + 60b0579 commit 16a83d5
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 5 deletions.
40 changes: 40 additions & 0 deletions src/main/php/test/Warnings.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php namespace test;

use lang\{Throwable, StackTraceElement};

class Warnings extends Throwable {
private static $LEVELS= [
E_ERROR => 'E_ERROR',
E_WARNING => 'E_WARNING',
E_PARSE => 'E_PARSE',
E_NOTICE => 'E_NOTICE',
E_CORE_ERROR => 'E_CORE_ERROR',
E_CORE_WARNING => 'E_CORE_WARNING',
E_COMPILE_ERROR => 'E_COMPILE_ERROR',
E_COMPILE_WARNING => 'E_COMPILE_WARNING',
E_USER_ERROR => 'E_USER_ERROR',
E_USER_WARNING => 'E_USER_WARNING',
E_USER_NOTICE => 'E_USER_NOTICE',
E_STRICT => 'E_STRICT',
E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
E_DEPRECATED => 'E_DEPRECATED',
E_USER_DEPRECATED => 'E_USER_DEPRECATED',
];

/** Creates a new warnings instance */
public function __construct(array $warnings) {
$message= '';
foreach ($warnings as $warning) {
$message.= ', '.$warning[1];
$this->trace[]= new StackTraceElement(
$warning[2],
null,
'@error',
$warning[3],
[],
(self::$LEVELS[$warning[0]] ?? 'E_UNKNOWN('.$warning[0].')').': '.$warning[1]
);
}
parent::__construct(substr($message, 2));
}
}
19 changes: 14 additions & 5 deletions src/main/php/test/execution/TestCase.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use lang\reflection\Type;
use lang\{Throwable, Runnable};
use test\outcome\{Succeeded, Skipped, Failed};
use test\{AssertionFailed, Expect, Outcome, Prerequisite};
use test\{AssertionFailed, Expect, Outcome, Prerequisite, Warnings};
use util\Objects;

class TestCase {
Expand Down Expand Up @@ -55,7 +55,11 @@ public function expecting(Expect $expectation) {
* @return Outcome
*/
public function run($arguments= []) {
\xp::gc();
$warnings= [];
set_error_handler(function($kind, $message, $file, $line) use(&$warnings) {
$warnings[]= [$kind, $message, $file, $line];
__error($kind, $message, $file, $line);
});
try {
if ($arguments) {
$name= $this->name.Objects::stringOf($arguments);
Expand All @@ -65,10 +69,13 @@ public function run($arguments= []) {
($this->run)();
}

if (null === $this->expectation) {
return new Succeeded($name);
} else {
if ($this->expectation) {
return new Failed($name, 'Did not catch expected '.$this->expectation->pattern(), null);
} else if ($warnings) {
\xp::gc();
return new Failed($name, 'Succeeded but raised '.sizeof($warnings).' warning(s)', new Warnings($warnings));
} else {
return new Succeeded($name);
}
} catch (Any $e) {
$t= Throwable::wrap($e);
Expand All @@ -86,6 +93,8 @@ public function run($arguments= []) {
} else {
return new Succeeded($name);
}
} finally {
restore_error_handler();
}
}
}
11 changes: 11 additions & 0 deletions src/test/php/test/unittest/ExecutionTest.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,17 @@ public function fixture() {
}));
}

#[Test]
public function tests_raising_warnings_fail() {
Assert::equals(['fixture' => Failed::class], $this->execute(new class() {

#[Test]
public function fixture() {
trigger_error('Test');
}
}));
}

#[Test]
public function skipped_test() {
Assert::equals(['fixture' => Skipped::class], $this->execute(new class() {
Expand Down
48 changes: 48 additions & 0 deletions src/test/php/test/unittest/WarningsTest.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php namespace test\unittest;

use test\assert\Matches;
use test\execution\TestCase;
use test\outcome\{Succeeded, Failed};
use test\{Assert, Test};

class WarningsTest {

/** Executes test function */
private function execute($function) {
return (new TestCase('test', $function))->run();
}

#[Test]
public function without_warnings() {
Assert::instance(Succeeded::class, $this->execute(function() { }));
}

#[Test]
public function trigger_error() {
$r= $this->execute(function() { trigger_error('Test'); });
Assert::equals('E_USER_NOTICE: Test', $r->cause->getStackTrace()[0]->message);
}

#[Test]
public function trigger_deprecation_error() {
$r= $this->execute(function() { trigger_error('Test', E_USER_DEPRECATED); });
Assert::equals('E_USER_DEPRECATED: Test', $r->cause->getStackTrace()[0]->message);
}

#[Test]
public function fopen_nonexistant_file() {
$r= $this->execute(function() { fopen('$', 'r'); });

Assert::matches(
'/E_WARNING: fopen.+: No such file or directory/i',
$r->cause->getStackTrace()[0]->message
);
}

#[Test]
public function multiple_warnings() {
$r= $this->execute(function() { trigger_error('One'); trigger_error('Two'); });
Assert::equals('E_USER_NOTICE: One', $r->cause->getStackTrace()[0]->message);
Assert::equals('E_USER_NOTICE: Two', $r->cause->getStackTrace()[1]->message);
}
}

0 comments on commit 16a83d5

Please sign in to comment.