Dustin Wilson
1 year ago
23 changed files with 806 additions and 2494 deletions
File diff suppressed because it is too large
@ -0,0 +1,11 @@ |
|||||
|
<?php |
||||
|
/** |
||||
|
* @license MIT |
||||
|
* Copyright 2022 Dustin Wilson, et al. |
||||
|
* See LICENSE and AUTHORS files for details |
||||
|
*/ |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
namespace MensBeam\Catcher; |
||||
|
|
||||
|
class ArgumentCountError extends \ArgumentCountError {} |
@ -1,196 +0,0 @@ |
|||||
<?php |
|
||||
/** |
|
||||
* @license MIT |
|
||||
* Copyright 2022 Dustin Wilson, et al. |
|
||||
* See LICENSE and AUTHORS files for details |
|
||||
*/ |
|
||||
|
|
||||
declare(strict_types=1); |
|
||||
namespace MensBeam\Catcher; |
|
||||
|
|
||||
|
|
||||
class HTMLHandler extends Handler { |
|
||||
public const CONTENT_TYPE = 'text/html'; |
|
||||
|
|
||||
|
|
||||
/** The DOMDocument errors should be inserted into */ |
|
||||
protected ?\DOMDocument $_document = null; |
|
||||
/** The XPath path to the element where the errors should be inserted */ |
|
||||
protected string $_errorPath = '/html/body'; |
|
||||
/** The PHP-standard date format which to use for times printed to output */ |
|
||||
protected string $_timeFormat = 'H:i:s'; |
|
||||
|
|
||||
protected \DOMXPath $xpath; |
|
||||
protected \DOMElement $errorLocation; |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
public function __construct(array $options = []) { |
|
||||
parent::__construct($options); |
|
||||
|
|
||||
if ($this->_document === null) { |
|
||||
$this->_document = new \DOMDocument(); |
|
||||
$this->_document->loadHTML(<<<HTML |
|
||||
<!DOCTYPE html> |
|
||||
<html> |
|
||||
<head><title>HTTP {$this->_httpCode}</title></head> |
|
||||
<body></body> |
|
||||
</html> |
|
||||
HTML); |
|
||||
} |
|
||||
|
|
||||
$this->xpath = new \DOMXPath($this->_document); |
|
||||
$location = $this->xpath->query($this->_errorPath); |
|
||||
if (count($location) === 0 || (!$location->item(0) instanceof \DOMElement && !$location->item(0) instanceof \DOMDocumentFragment)) { |
|
||||
throw new \InvalidArgumentException('Option "errorPath" must correspond to a location that is an instance of \DOMElement or \DOMDocumentFragment'); |
|
||||
} |
|
||||
$this->errorLocation = $location->item(0); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
protected function buildOutputThrowable(array $outputThrowable, bool $previous = false): \DOMDocumentFragment { |
|
||||
$frag = $this->_document->createDocumentFragment(); |
|
||||
$tFrag = $this->_document->createDocumentFragment(); |
|
||||
$ip = $frag; |
|
||||
$hasSiblings = false; |
|
||||
|
|
||||
if ($previous === false) { |
|
||||
if (isset($outputThrowable['time'])) { |
|
||||
$p = $this->_document->createElement('p'); |
|
||||
$time = $this->_document->createElement('time'); |
|
||||
$time->setAttribute('datetime', $outputThrowable['time']->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d\TH:i:s.vO')); |
|
||||
$time->appendChild($this->_document->createTextNode($outputThrowable['time']->format($this->_timeFormat))); |
|
||||
$p->appendChild($time); |
|
||||
$frag->appendChild($p); |
|
||||
|
|
||||
$div = $this->_document->createElement('div'); |
|
||||
$frag->appendChild($div); |
|
||||
$ip = $div; |
|
||||
} |
|
||||
} else { |
|
||||
$span = $this->_document->createElement('span'); |
|
||||
$span->appendChild($this->_document->createTextNode('Caused by:')); |
|
||||
$tFrag->appendChild($span); |
|
||||
$tFrag->appendChild($this->_document->createTextNode(' ')); |
|
||||
} |
|
||||
|
|
||||
$b = $this->_document->createElement('b'); |
|
||||
$code = $this->_document->createElement('code'); |
|
||||
$code->appendChild($this->_document->createTextNode($outputThrowable['class'])); |
|
||||
$b->appendChild($code); |
|
||||
if (isset($outputThrowable['errorType'])) { |
|
||||
$b->insertBefore($this->_document->createTextNode("{$outputThrowable['errorType']} ("), $code); |
|
||||
$b->appendChild($this->_document->createTextNode(')')); |
|
||||
} |
|
||||
$tFrag->appendChild($b); |
|
||||
$tFrag->appendChild($this->_document->createTextNode(': ')); |
|
||||
$i = $this->_document->createElement('i'); |
|
||||
$i->appendChild($this->_document->createTextNode($outputThrowable['message'])); |
|
||||
$tFrag->appendChild($i); |
|
||||
$tFrag->appendChild($this->_document->createTextNode(' in file ')); |
|
||||
$code = $this->_document->createElement('code'); |
|
||||
$code->appendChild($this->_document->createTextNode($outputThrowable['file'])); |
|
||||
$tFrag->appendChild($code); |
|
||||
$tFrag->appendChild($this->_document->createTextNode(" on line {$outputThrowable['line']}")); |
|
||||
|
|
||||
if (isset($outputThrowable['previous'])) { |
|
||||
$ul = $this->_document->createElement('ul'); |
|
||||
$li = $this->_document->createElement('li'); |
|
||||
$li->appendChild($this->buildOutputThrowable($outputThrowable['previous'], true)); |
|
||||
$ul->appendChild($li); |
|
||||
$ip->appendChild($ul); |
|
||||
$hasSiblings = true; |
|
||||
} |
|
||||
|
|
||||
if ($previous === false && isset($outputThrowable['frames'])) { |
|
||||
$p = $this->_document->createElement('p'); |
|
||||
$p->appendChild($this->_document->createTextNode('Stack trace:')); |
|
||||
$ip->appendChild($p); |
|
||||
|
|
||||
$ol = $this->_document->createElement('ol'); |
|
||||
$ip->appendChild($ol); |
|
||||
foreach ($outputThrowable['frames'] as $frame) { |
|
||||
$li = $this->_document->createElement('li'); |
|
||||
$ol->appendChild($li); |
|
||||
if (isset($frame['args'])) { |
|
||||
$t = $this->_document->createElement('p'); |
|
||||
$li->appendChild($t); |
|
||||
} else { |
|
||||
$t = $li; |
|
||||
} |
|
||||
$b = $this->_document->createElement('b'); |
|
||||
$code = $this->_document->createElement('code'); |
|
||||
$b->appendChild($code); |
|
||||
$t->appendChild($b); |
|
||||
|
|
||||
if (isset($frame['class'])) { |
|
||||
$code->appendChild($this->_document->createTextNode($frame['class'])); |
|
||||
|
|
||||
if (isset($frame['errorType'])) { |
|
||||
$b->insertBefore($this->_document->createTextNode("{$frame['errorType']} ("), $code); |
|
||||
$b->appendChild($this->_document->createTextNode(')')); |
|
||||
} elseif (isset($frame['function'])) { |
|
||||
$code->firstChild->appendData("::{$frame['function']}"); |
|
||||
} |
|
||||
} elseif (!empty($frame['function'])) { |
|
||||
$code->appendChild($this->_document->createTextNode($frame['function'])); |
|
||||
} |
|
||||
|
|
||||
$t->appendChild($this->_document->createTextNode("\u{00a0}\u{00a0}")); |
|
||||
$code = $this->_document->createElement('code'); |
|
||||
$code->appendChild($this->_document->createTextNode($frame['file'])); |
|
||||
$t->appendChild($code); |
|
||||
$t->appendChild($this->_document->createTextNode(":{$frame['line']}")); |
|
||||
|
|
||||
if (isset($frame['args'])) { |
|
||||
$varExporter = $this->_varExporter; |
|
||||
$pre = $this->_document->createElement('pre'); |
|
||||
$pre->appendChild($this->_document->createTextNode(trim($varExporter($frame['args'])))); |
|
||||
$li->appendChild($pre); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
$hasSiblings = true; |
|
||||
} |
|
||||
|
|
||||
if ($hasSiblings) { |
|
||||
$p = $this->_document->createElement('p'); |
|
||||
$p->appendChild($tFrag); |
|
||||
$ip->insertBefore($p, $ip->firstChild); |
|
||||
} else { |
|
||||
$ip->appendChild($tFrag); |
|
||||
} |
|
||||
|
|
||||
return $frag; |
|
||||
} |
|
||||
|
|
||||
protected function dispatchCallback(): void { |
|
||||
$frag = $this->_document->createDocumentFragment(); |
|
||||
$allSilent = true; |
|
||||
foreach ($this->outputBuffer as $o) { |
|
||||
if ($o['outputCode'] & self::SILENT) { |
|
||||
continue; |
|
||||
} |
|
||||
|
|
||||
$li = $this->_document->createElement('li'); |
|
||||
$li->appendChild($this->buildOutputThrowable($o)); |
|
||||
$frag->appendChild($li); |
|
||||
|
|
||||
$allSilent = false; |
|
||||
} |
|
||||
|
|
||||
if (!$allSilent) { |
|
||||
$ul = $this->_document->createElement('ul'); |
|
||||
$ul->appendChild($frag); |
|
||||
$this->errorLocation->appendChild($ul); |
|
||||
$this->print($this->serializeDocument()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
protected function serializeDocument() { |
|
||||
return $this->_document->saveHTML(); |
|
||||
} |
|
||||
} |
|
@ -1,32 +0,0 @@ |
|||||
<?php |
|
||||
/** |
|
||||
* @license MIT |
|
||||
* Copyright 2022 Dustin Wilson, et al. |
|
||||
* See LICENSE and AUTHORS files for details |
|
||||
*/ |
|
||||
|
|
||||
declare(strict_types=1); |
|
||||
namespace MensBeam\Catcher; |
|
||||
|
|
||||
|
|
||||
class JSONHandler extends Handler { |
|
||||
public const CONTENT_TYPE = 'application/json'; |
|
||||
|
|
||||
|
|
||||
protected function dispatchCallback(): void { |
|
||||
foreach ($this->outputBuffer as $key => $value) { |
|
||||
if ($value['outputCode'] & self::SILENT) { |
|
||||
unset($this->outputBuffer[$key]); |
|
||||
continue; |
|
||||
} |
|
||||
|
|
||||
$this->outputBuffer[$key] = $this->cleanOutputThrowable($this->outputBuffer[$key]); |
|
||||
} |
|
||||
|
|
||||
if (count($this->outputBuffer) > 0) { |
|
||||
$this->print(json_encode([ |
|
||||
'errors' => $this->outputBuffer |
|
||||
], \JSON_INVALID_UTF8_SUBSTITUTE | \JSON_PARTIAL_OUTPUT_ON_ERROR | \JSON_UNESCAPED_SLASHES)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,11 @@ |
|||||
|
<?php |
||||
|
/** |
||||
|
* @license MIT |
||||
|
* Copyright 2022 Dustin Wilson, et al. |
||||
|
* See LICENSE and AUTHORS files for details |
||||
|
*/ |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
namespace MensBeam\Catcher; |
||||
|
|
||||
|
class UnderflowException extends \UnderflowException {} |
@ -1,100 +0,0 @@ |
|||||
#!/usr/bin/env php |
|
||||
<?php |
|
||||
|
|
||||
$cwd = __DIR__; |
|
||||
$codeDir = "$cwd/lib"; |
|
||||
$testDir = "$cwd/tests"; |
|
||||
|
|
||||
function help($error = true): void { |
|
||||
$help = <<<USAGE |
|
||||
Usage: |
|
||||
run test [additional_phpunit_options] |
|
||||
run --help |
|
||||
|
|
||||
USAGE; |
|
||||
|
|
||||
if ($error) { |
|
||||
fprintf(\STDERR, $help); |
|
||||
} else { |
|
||||
echo $help; |
|
||||
} |
|
||||
|
|
||||
exit((int)$error); |
|
||||
} |
|
||||
|
|
||||
function error(string $message): void { |
|
||||
fprintf(\STDERR, "ERROR: $message\n"); |
|
||||
exit(1); |
|
||||
} |
|
||||
|
|
||||
if (count($argv) === 1) { |
|
||||
help(); |
|
||||
} |
|
||||
|
|
||||
switch ($argv[1]) { |
|
||||
case 'test': |
|
||||
$opts = [ |
|
||||
'--colors', |
|
||||
'--coverage-html='.escapeshellarg("$testDir/coverage") |
|
||||
]; |
|
||||
|
|
||||
if (isset($argv[2])) { |
|
||||
$opts = [ ...$opts, array_slice($argv, 2) ]; |
|
||||
} |
|
||||
|
|
||||
$opts = implode(' ', $opts); |
|
||||
break; |
|
||||
case '-h': |
|
||||
case '--help': |
|
||||
help(false); |
|
||||
break; |
|
||||
default: |
|
||||
help(); |
|
||||
} |
|
||||
|
|
||||
$phpunitPath = escapeshellarg("$cwd/vendor/bin/phpunit"); |
|
||||
$confPath = "$testDir/phpunit.dist.xml"; |
|
||||
if (!file_exists($confPath)) { |
|
||||
$confPath = "$testDir/phpunit.xml"; |
|
||||
if (!file_exists($confPath)) { |
|
||||
error('A phpunit configuration must be present at "tests/phpunit.dist.xml" or "tests/phpunit.xml"; aborting'); |
|
||||
} |
|
||||
} |
|
||||
$confPath = escapeshellarg($confPath); |
|
||||
|
|
||||
$cmd = [ |
|
||||
escapeshellarg(\PHP_BINARY), |
|
||||
'-d opcache.enable_cli=0', |
|
||||
'-d zend.assertions=1' |
|
||||
]; |
|
||||
if (!extension_loaded('xdebug')) { |
|
||||
$extDir = rtrim(ini_get("extension_dir"), "/"); |
|
||||
if (file_exists("$extDir/xdebug.so")) { |
|
||||
$cmd[] = '-d zend_extension=xdebug.so'; |
|
||||
} else { |
|
||||
error('Xdebug is not installed on your system; aborting'); |
|
||||
} |
|
||||
} |
|
||||
$cmd[] = '-d xdebug.mode=coverage,develop,trace'; |
|
||||
$cmd = implode(' ', $cmd); |
|
||||
|
|
||||
$process = proc_open("$cmd $phpunitPath -c $confPath $opts", [ |
|
||||
1 => ['pipe', 'w'], |
|
||||
2 => ['pipe', 'w'] |
|
||||
], $pipes); |
|
||||
|
|
||||
if ($process === false) { |
|
||||
error('Failed to execute phpunit'); |
|
||||
} |
|
||||
|
|
||||
$stderr = trim(stream_get_contents($pipes[2])); |
|
||||
$output = trim(stream_get_contents($pipes[1])); |
|
||||
|
|
||||
fclose($pipes[1]); |
|
||||
fclose($pipes[2]); |
|
||||
proc_close($process); |
|
||||
|
|
||||
echo "$output\n"; |
|
||||
if ($stderr !== '') { |
|
||||
error($stderr); |
|
||||
} |
|
@ -0,0 +1,37 @@ |
|||||
|
#!/usr/bin/env php |
||||
|
<?php |
||||
|
/** |
||||
|
* @license MIT |
||||
|
* Copyright 2022 Dustin Wilson, et al. |
||||
|
* See LICENSE and AUTHORS files for details |
||||
|
*/ |
||||
|
|
||||
|
$dir = ini_get('extension_dir'); |
||||
|
$php = escapeshellarg(\PHP_BINARY); |
||||
|
$code = escapeshellarg(__DIR__ . '/lib'); |
||||
|
|
||||
|
|
||||
|
array_shift($argv); |
||||
|
foreach ($argv as $k => $v) { |
||||
|
if (in_array($v, ['--coverage', '--coverage-html'])) { |
||||
|
$argv[$k] = '--coverage-html tests/coverage'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$cmd = [ |
||||
|
$php, |
||||
|
'-d opcache.enable_cli=0', |
||||
|
]; |
||||
|
|
||||
|
if (!extension_loaded('xdebug')) { |
||||
|
$cmd[] = '-d zend_extension=xdebug.so'; |
||||
|
} |
||||
|
|
||||
|
$cmd = implode(' ', [ |
||||
|
...$cmd, |
||||
|
'-d xdebug.mode=coverage,develop,trace', |
||||
|
escapeshellarg(__DIR__ . '/vendor/bin/phpunit'), |
||||
|
'--configuration tests/phpunit.xml', |
||||
|
...$argv |
||||
|
]); |
||||
|
passthru($cmd); |
@ -1,23 +1,18 @@ |
|||||
<?php |
<?php |
||||
/** @license MIT |
|
||||
* Copyright 2017 , Dustin Wilson, J. King et al. |
|
||||
* See LICENSE and AUTHORS files for details */ |
|
||||
|
|
||||
declare(strict_types=1); |
declare(strict_types=1); |
||||
namespace MensBeam; |
namespace MensBeam\Logger\Test; |
||||
|
|
||||
ini_set('memory_limit', '-1'); |
ini_set('memory_limit', '2G'); |
||||
ini_set('zend.assertions', '1'); |
ini_set('zend.assertions', '1'); |
||||
ini_set('assert.exception', 'true'); |
ini_set('assert.exception', 'true'); |
||||
error_reporting(\E_ALL); |
error_reporting(\E_ALL); |
||||
|
define('CWD', dirname(__DIR__)); |
||||
$cwd = dirname(__DIR__); |
require_once CWD . '/vendor/autoload.php'; |
||||
require_once "$cwd/vendor/autoload.php"; |
|
||||
|
|
||||
if (function_exists('xdebug_set_filter')) { |
if (function_exists('xdebug_set_filter')) { |
||||
if (defined('XDEBUG_PATH_INCLUDE')) { |
if (defined('XDEBUG_PATH_INCLUDE')) { |
||||
xdebug_set_filter(\XDEBUG_FILTER_CODE_COVERAGE, \XDEBUG_PATH_INCLUDE, [ "$cwd/lib/" ]); |
xdebug_set_filter(\XDEBUG_FILTER_CODE_COVERAGE, \XDEBUG_PATH_INCLUDE, [ CWD . '/lib/' ]); |
||||
} else { |
} else { |
||||
xdebug_set_filter(\XDEBUG_FILTER_CODE_COVERAGE, \XDEBUG_PATH_WHITELIST, [ "$cwd/lib/" ]); |
xdebug_set_filter(\XDEBUG_FILTER_CODE_COVERAGE, \XDEBUG_PATH_WHITELIST, [ CWD . '/lib/' ]); |
||||
} |
} |
||||
} |
} |
@ -1,89 +0,0 @@ |
|||||
<?php |
|
||||
/** |
|
||||
* @license MIT |
|
||||
* Copyright 2022 Dustin Wilson, et al. |
|
||||
* See LICENSE and AUTHORS files for details |
|
||||
*/ |
|
||||
|
|
||||
declare(strict_types=1); |
|
||||
namespace MensBeam\Catcher\Test; |
|
||||
use MensBeam\Catcher; |
|
||||
use MensBeam\Catcher\{ |
|
||||
Error, |
|
||||
Handler, |
|
||||
HTMLHandler, |
|
||||
ThrowableController |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
class TestHTMLHandler extends \PHPUnit\Framework\TestCase { |
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\HTMLHandler::__construct |
|
||||
* |
|
||||
* @covers \MensBeam\Catcher\Handler::__construct |
|
||||
*/ |
|
||||
public function testMethod___construct__exception(): void { |
|
||||
$this->expectException(\InvalidArgumentException::class); |
|
||||
new HTMLHandler([ 'errorPath' => '/html/body/fail' ]); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\HTMLHandler::buildOutputThrowable |
|
||||
* |
|
||||
* @covers \MensBeam\Catcher\Error::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::buildOutputArray |
|
||||
* @covers \MensBeam\Catcher\Handler::handle |
|
||||
* @covers \MensBeam\Catcher\HTMLHandler::__construct |
|
||||
* @covers \MensBeam\Catcher\HTMLHandler::dispatchCallback |
|
||||
* @covers \MensBeam\Catcher\HTMLHandler::handleCallback |
|
||||
* @covers \MensBeam\Catcher\HTMLHandler::serializeDocument |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::__construct |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getErrorType |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getFrames |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getPrevious |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getThrowable |
|
||||
*/ |
|
||||
public function testMethod_buildOutputThrowable(): void { |
|
||||
$c = new ThrowableController(new \Exception(message: 'Ook!', previous: new Error(message: 'Eek!', code: \E_USER_ERROR, previous: new Error(message: 'Ack!')))); |
|
||||
$h = new HTMLHandler([ |
|
||||
'backtraceArgFrameLimit' => 1, |
|
||||
'outputBacktrace' => true, |
|
||||
'outputToStderr' => false |
|
||||
]); |
|
||||
$o = $h->handle($c); |
|
||||
$this->assertSame(Handler::CONTINUE, $o['controlCode']); |
|
||||
|
|
||||
ob_start(); |
|
||||
$h->dispatch(); |
|
||||
ob_end_clean(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\HTMLHandler::dispatchCallback |
|
||||
* |
|
||||
* @covers \MensBeam\Catcher\Handler::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::buildOutputArray |
|
||||
* @covers \MensBeam\Catcher\Handler::dispatch |
|
||||
* @covers \MensBeam\Catcher\Handler::handle |
|
||||
* @covers \MensBeam\Catcher\Handler::handleCallback |
|
||||
* @covers \MensBeam\Catcher\HTMLHandler::__construct |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::__construct |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getPrevious |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getThrowable |
|
||||
*/ |
|
||||
public function testMethod_dispatchCallback(): void { |
|
||||
$c = new ThrowableController(new \Exception(message: 'Ook!')); |
|
||||
$h = new HTMLHandler([ |
|
||||
'backtraceArgFrameLimit' => 1, |
|
||||
'outputToStderr' => false, |
|
||||
'silent' => true |
|
||||
]); |
|
||||
$h->handle($c); |
|
||||
|
|
||||
ob_start(); |
|
||||
$h->dispatch(); |
|
||||
$o = ob_get_clean(); |
|
||||
$this->assertEmpty($o); |
|
||||
} |
|
||||
} |
|
@ -1,173 +0,0 @@ |
|||||
<?php |
|
||||
/** |
|
||||
* @license MIT |
|
||||
* Copyright 2022 Dustin Wilson, et al. |
|
||||
* See LICENSE and AUTHORS files for details |
|
||||
*/ |
|
||||
|
|
||||
declare(strict_types=1); |
|
||||
namespace MensBeam\Catcher\Test; |
|
||||
use MensBeam\Catcher; |
|
||||
use MensBeam\Catcher\{ |
|
||||
Error, |
|
||||
HTMLHandler, |
|
||||
PlainTextHandler, |
|
||||
JSONHandler, |
|
||||
ThrowableController |
|
||||
}; |
|
||||
use Eloquent\Phony\Phpunit\Phony; |
|
||||
|
|
||||
|
|
||||
class TestHandler extends \PHPUnit\Framework\TestCase { |
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\Handler::__construct |
|
||||
*/ |
|
||||
public function testMethod___construct__exception(): void { |
|
||||
$this->expectException(\RangeException::class); |
|
||||
new PlainTextHandler([ 'httpCode' => 42 ]); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\Handler::cleanOutputThrowable |
|
||||
* |
|
||||
* @covers \MensBeam\Catcher\Handler::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::buildOutputArray |
|
||||
* @covers \MensBeam\Catcher\Handler::dispatch |
|
||||
* @covers \MensBeam\Catcher\Handler::handle |
|
||||
* @covers \MensBeam\Catcher\Handler::handleCallback |
|
||||
* @covers \MensBeam\Catcher\Handler::print |
|
||||
* @covers \MensBeam\Catcher\JSONHandler::dispatchCallback |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::__construct |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getErrorType |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getFrames |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getPrevious |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getThrowable |
|
||||
*/ |
|
||||
public function testMethod__cleanOutputThrowable(): void { |
|
||||
// Just need to test coverage here; TestJSONHandler covers this one thoroughly. |
|
||||
$c = new ThrowableController(new \Exception(message: 'Ook!', previous: new \Error('Eek!'))); |
|
||||
$h = new JSONHandler([ |
|
||||
'outputBacktrace' => true, |
|
||||
'outputToStderr' => false |
|
||||
]); |
|
||||
$o = $h->handle($c); |
|
||||
|
|
||||
ob_start(); |
|
||||
$h->dispatch(); |
|
||||
ob_end_clean(); |
|
||||
|
|
||||
$this->assertTrue(isset($o['frames'])); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\Handler::handle |
|
||||
* |
|
||||
* @covers \MensBeam\Catcher\Handler::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::buildOutputArray |
|
||||
* @covers \MensBeam\Catcher\Handler::handleCallback |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::__construct |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getErrorType |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getFrames |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getPrevious |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getThrowable |
|
||||
*/ |
|
||||
public function testMethod__handle(): void { |
|
||||
// Just need to test backtrace handling. The rest has already been covered by prior tests. |
|
||||
$c = new ThrowableController(new \Exception(message: 'Ook!', previous: new \Error(message: 'Eek!', previous: new Error(message: 'Eek!', code: \E_USER_ERROR)))); |
|
||||
$h = new HTMLHandler([ |
|
||||
'outputBacktrace' => true, |
|
||||
'outputToStderr' => true |
|
||||
]); |
|
||||
$this->assertTrue(isset($h->handle($c)['frames'])); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\Handler::getOption |
|
||||
* |
|
||||
* @covers \MensBeam\Catcher::__construct |
|
||||
* @covers \MensBeam\Catcher::handleError |
|
||||
* @covers \MensBeam\Catcher::handleThrowable |
|
||||
* @covers \MensBeam\Catcher::isErrorFatal |
|
||||
* @covers \MensBeam\Catcher::pushHandler |
|
||||
* @covers \MensBeam\Catcher::register |
|
||||
* @covers \MensBeam\Catcher::unregister |
|
||||
* @covers \MensBeam\Catcher\Error::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::dispatch |
|
||||
* @covers \MensBeam\Catcher\Handler::handle |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::handleCallback |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::__construct |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getErrorType |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getPrevious |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getThrowable |
|
||||
*/ |
|
||||
public function testMethod__getOption(): void { |
|
||||
$h = new PlainTextHandler([ 'forceExit' => true, 'silent' => true ]); |
|
||||
$this->assertTrue($h->getOption('forceExit')); |
|
||||
|
|
||||
$c = new Catcher($h); |
|
||||
$c->preventExit = true; |
|
||||
$c->throwErrors = false; |
|
||||
$this->assertNull($h->getOption('ook')); |
|
||||
$c->unregister(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\Handler::setOption |
|
||||
* |
|
||||
* @covers \MensBeam\Catcher\Handler::__construct |
|
||||
*/ |
|
||||
public function testMethod__setOption(): void { |
|
||||
$h = new PlainTextHandler([ 'forceExit' => true, 'silent' => true ]); |
|
||||
$h->setOption('forceExit', false); |
|
||||
$r = new \ReflectionProperty($h, '_forceExit'); |
|
||||
$r->setAccessible(true); |
|
||||
$this->assertFalse($r->getValue($h)); |
|
||||
|
|
||||
$m = Phony::partialMock(Catcher::class, [ |
|
||||
$h |
|
||||
]); |
|
||||
$c = $m->get(); |
|
||||
$c->preventExit = true; |
|
||||
$c->throwErrors = false; |
|
||||
|
|
||||
$h->setOption('ook', 'FAIL'); |
|
||||
$m->handleError->called(); |
|
||||
|
|
||||
$c->unregister(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\Handler::print |
|
||||
* |
|
||||
* @covers \MensBeam\Catcher::__construct |
|
||||
* @covers \MensBeam\Catcher::handleError |
|
||||
* @covers \MensBeam\Catcher::isErrorFatal |
|
||||
* @covers \MensBeam\Catcher::handleThrowable |
|
||||
* @covers \MensBeam\Catcher::pushHandler |
|
||||
* @covers \MensBeam\Catcher::register |
|
||||
* @covers \MensBeam\Catcher::unregister |
|
||||
* @covers \MensBeam\Catcher\Error::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::dispatch |
|
||||
* @covers \MensBeam\Catcher\Handler::handle |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::dispatchCallback |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::handleCallback |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::serializeOutputThrowable |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::__construct |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getErrorType |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getPrevious |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getThrowable |
|
||||
*/ |
|
||||
public function testMethod__print(): void { |
|
||||
// Just need to test forceOutputNow for coverage purposes |
|
||||
$c = new Catcher(new PlainTextHandler([ 'forceOutputNow' => true, 'outputToStderr' => false ])); |
|
||||
$c->preventExit = true; |
|
||||
$c->throwErrors = false; |
|
||||
ob_start(); |
|
||||
trigger_error('Ook!', \E_USER_NOTICE); |
|
||||
ob_end_clean(); |
|
||||
$this->assertSame(\E_USER_NOTICE, $c->getLastThrowable()->getCode()); |
|
||||
$c->unregister(); |
|
||||
} |
|
||||
} |
|
@ -1,48 +0,0 @@ |
|||||
<?php |
|
||||
/** |
|
||||
* @license MIT |
|
||||
* Copyright 2022 Dustin Wilson, et al. |
|
||||
* See LICENSE and AUTHORS files for details |
|
||||
*/ |
|
||||
|
|
||||
declare(strict_types=1); |
|
||||
namespace MensBeam\Catcher\Test; |
|
||||
use MensBeam\Catcher; |
|
||||
use MensBeam\Catcher\{ |
|
||||
Error, |
|
||||
Handler, |
|
||||
JSONHandler, |
|
||||
ThrowableController |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
class TestJSONHandler extends \PHPUnit\Framework\TestCase { |
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\JSONHandler::dispatchCallback |
|
||||
* |
|
||||
* @covers \MensBeam\Catcher\Error::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::buildOutputArray |
|
||||
* @covers \MensBeam\Catcher\Handler::dispatch |
|
||||
* @covers \MensBeam\Catcher\Handler::handle |
|
||||
* @covers \MensBeam\Catcher\Handler::handleCallback |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::__construct |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getErrorType |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getPrevious |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getThrowable |
|
||||
*/ |
|
||||
public function testMethod_dispatchCallback(): void { |
|
||||
// Not much left to cover; just need to test silent output |
|
||||
$c = new ThrowableController(new \Exception(message: 'Ook!', previous: new Error(message: 'Eek!', code: \E_USER_ERROR, previous: new Error(message: 'Ack!')))); |
|
||||
$h = new JSONHandler([ |
|
||||
'silent' => true, |
|
||||
'outputToStderr' => false |
|
||||
]); |
|
||||
$o = $h->handle($c); |
|
||||
|
|
||||
ob_start(); |
|
||||
$h->dispatch(); |
|
||||
$o = ob_get_clean(); |
|
||||
$this->assertEmpty($o); |
|
||||
} |
|
||||
} |
|
@ -1,145 +0,0 @@ |
|||||
<?php |
|
||||
/** |
|
||||
* @license MIT |
|
||||
* Copyright 2022 Dustin Wilson, et al. |
|
||||
* See LICENSE and AUTHORS files for details |
|
||||
*/ |
|
||||
|
|
||||
declare(strict_types=1); |
|
||||
namespace MensBeam\Catcher\Test; |
|
||||
use MensBeam\Catcher; |
|
||||
use MensBeam\Catcher\{ |
|
||||
Error, |
|
||||
Handler, |
|
||||
PlainTextHandler, |
|
||||
ThrowableController |
|
||||
}; |
|
||||
use Eloquent\Phony\Phpunit\Phony, |
|
||||
Psr\Log\LoggerInterface; |
|
||||
|
|
||||
|
|
||||
class TestPlainTextHandler extends \PHPUnit\Framework\TestCase { |
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::handleCallback |
|
||||
* |
|
||||
* @covers \MensBeam\Catcher\Handler::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::buildOutputArray |
|
||||
* @covers \MensBeam\Catcher\Handler::handle |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::dispatchCallback |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::handleCallback |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::log |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::serializeOutputThrowable |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::__construct |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getErrorType |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getFrames |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getPrevious |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getThrowable |
|
||||
*/ |
|
||||
public function testMethod_handleCallback(): void { |
|
||||
$c = new ThrowableController(new \Exception(message: 'Ook!', previous: new \Error(message: 'Eek!', previous: new Error(message: 'Ack!', code: \E_USER_ERROR)))); |
|
||||
$l = Phony::mock(LoggerInterface::class); |
|
||||
$h = new PlainTextHandler([ |
|
||||
'logger' => $l->get(), |
|
||||
'outputBacktrace' => true, |
|
||||
'outputToStderr' => false |
|
||||
]); |
|
||||
$o = $h->handle($c); |
|
||||
$this->assertSame(Handler::CONTINUE, $o['controlCode']); |
|
||||
$this->assertSame(Handler::OUTPUT | Handler::NOW, $o['outputCode']); |
|
||||
$this->assertTrue(isset($o['previous'])); |
|
||||
|
|
||||
ob_start(); |
|
||||
$h->dispatch(); |
|
||||
ob_end_clean(); |
|
||||
|
|
||||
$l->critical->called(); |
|
||||
|
|
||||
$c = new ThrowableController(new \Exception(message: 'Ook!', previous: new \Error(message: 'Eek!', previous: new Error(message: 'Ack!', code: \E_USER_ERROR)))); |
|
||||
$l = Phony::mock(LoggerInterface::class); |
|
||||
$h = new PlainTextHandler([ |
|
||||
'logger' => $l->get(), |
|
||||
'silent' => true |
|
||||
]); |
|
||||
$o = $h->handle($c); |
|
||||
$this->assertSame(Handler::CONTINUE, $o['controlCode']); |
|
||||
$this->assertSame(Handler::SILENT | Handler::NOW, $o['outputCode']); |
|
||||
$this->assertTrue(isset($o['previous'])); |
|
||||
|
|
||||
ob_start(); |
|
||||
$h->dispatch(); |
|
||||
ob_end_clean(); |
|
||||
|
|
||||
$l->critical->called(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::log |
|
||||
* |
|
||||
* @covers \MensBeam\Catcher\Error::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::buildOutputArray |
|
||||
* @covers \MensBeam\Catcher\Handler::handle |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::dispatchCallback |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::handleCallback |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::dispatchCallback |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::__construct |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getErrorType |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getPrevious |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getThrowable |
|
||||
*/ |
|
||||
public function testMethod_log(): void { |
|
||||
$l = Phony::mock(LoggerInterface::class); |
|
||||
$h = new PlainTextHandler([ |
|
||||
'logger' => $l->get(), |
|
||||
'outputToStderr' => false |
|
||||
]); |
|
||||
|
|
||||
$e = [ |
|
||||
'notice' => [ |
|
||||
\E_NOTICE, |
|
||||
\E_USER_NOTICE, |
|
||||
\E_STRICT |
|
||||
], |
|
||||
'warning' => [ |
|
||||
\E_WARNING, |
|
||||
\E_COMPILE_WARNING, |
|
||||
\E_USER_WARNING, |
|
||||
\E_DEPRECATED, |
|
||||
\E_USER_DEPRECATED |
|
||||
], |
|
||||
'error' => [ |
|
||||
\E_RECOVERABLE_ERROR |
|
||||
], |
|
||||
'alert' => [ |
|
||||
\E_PARSE, |
|
||||
\E_CORE_ERROR, |
|
||||
\E_COMPILE_ERROR |
|
||||
] |
|
||||
]; |
|
||||
|
|
||||
foreach ($e as $k => $v) { |
|
||||
foreach ($v as $vv) { |
|
||||
$h->handle(new ThrowableController(new Error('Ook!', $vv))); |
|
||||
|
|
||||
ob_start(); |
|
||||
$h->dispatch(); |
|
||||
ob_end_clean(); |
|
||||
|
|
||||
$l->$k->called(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
$h->handle(new ThrowableController(new \PharException('Ook!'))); |
|
||||
ob_start(); |
|
||||
$h->dispatch(); |
|
||||
ob_end_clean(); |
|
||||
$l->alert->called(); |
|
||||
|
|
||||
$h->handle(new ThrowableController(new \RuntimeException('Ook!'))); |
|
||||
ob_start(); |
|
||||
$h->dispatch(); |
|
||||
ob_end_clean(); |
|
||||
$l->alert->called(); |
|
||||
} |
|
||||
} |
|
@ -1,189 +0,0 @@ |
|||||
<?php |
|
||||
/** |
|
||||
* @license MIT |
|
||||
* Copyright 2022 Dustin Wilson, et al. |
|
||||
* See LICENSE and AUTHORS files for details |
|
||||
*/ |
|
||||
|
|
||||
declare(strict_types=1); |
|
||||
namespace MensBeam\Catcher\Test; |
|
||||
use MensBeam\Catcher; |
|
||||
use MensBeam\Catcher\{ |
|
||||
Error, |
|
||||
PlainTextHandler, |
|
||||
ThrowableController |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
class TestThrowableController extends \PHPUnit\Framework\TestCase { |
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getErrorType |
|
||||
* |
|
||||
* @covers \MensBeam\Catcher::__construct |
|
||||
* @covers \MensBeam\Catcher::getLastThrowable |
|
||||
* @covers \MensBeam\Catcher::handleError |
|
||||
* @covers \MensBeam\Catcher::isErrorFatal |
|
||||
* @covers \MensBeam\Catcher::handleThrowable |
|
||||
* @covers \MensBeam\Catcher::pushHandler |
|
||||
* @covers \MensBeam\Catcher::register |
|
||||
* @covers \MensBeam\Catcher::unregister |
|
||||
* @covers \MensBeam\Catcher\Error::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::__construct |
|
||||
* @covers \MensBeam\Catcher\Handler::dispatch |
|
||||
* @covers \MensBeam\Catcher\Handler::handle |
|
||||
* @covers \MensBeam\Catcher\Handler::print |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::dispatchCallback |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::handleCallback |
|
||||
* @covers \MensBeam\Catcher\PlainTextHandler::serializeOutputThrowable |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::__construct |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getPrevious |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getThrowable |
|
||||
*/ |
|
||||
public function testMethod_getErrorType(): void { |
|
||||
$c = new Catcher(new PlainTextHandler([ 'outputToStderr' => false ])); |
|
||||
$c->preventExit = true; |
|
||||
$c->throwErrors = false; |
|
||||
ob_start(); |
|
||||
trigger_error('Ook!', \E_USER_DEPRECATED); |
|
||||
ob_end_clean(); |
|
||||
$this->assertSame(\E_USER_DEPRECATED, $c->getLastThrowable()->getCode()); |
|
||||
$c->unregister(); |
|
||||
|
|
||||
$c = new Catcher(new PlainTextHandler([ 'outputToStderr' => false ])); |
|
||||
$c->preventExit = true; |
|
||||
$c->throwErrors = false; |
|
||||
ob_start(); |
|
||||
trigger_error('Ook!', \E_USER_WARNING); |
|
||||
ob_end_clean(); |
|
||||
$this->assertSame(\E_USER_WARNING, $c->getLastThrowable()->getCode()); |
|
||||
$c->unregister(); |
|
||||
|
|
||||
$c = new Catcher(new PlainTextHandler([ 'outputToStderr' => false ])); |
|
||||
$c->preventExit = true; |
|
||||
$c->throwErrors = false; |
|
||||
ob_start(); |
|
||||
trigger_error('Ook!', \E_USER_NOTICE); |
|
||||
ob_end_clean(); |
|
||||
$this->assertSame(\E_USER_NOTICE, $c->getLastThrowable()->getCode()); |
|
||||
$c->unregister(); |
|
||||
|
|
||||
$c = new Catcher(new PlainTextHandler([ 'outputToStderr' => false ])); |
|
||||
$c->preventExit = true; |
|
||||
$c->throwErrors = false; |
|
||||
ob_start(); |
|
||||
trigger_error('Ook!', \E_USER_ERROR); |
|
||||
ob_end_clean(); |
|
||||
$this->assertSame(\E_USER_ERROR, $c->getLastThrowable()->getCode()); |
|
||||
$c->unregister(); |
|
||||
|
|
||||
// These others will be tested by invoking the method directly |
|
||||
$c = new ThrowableController(new Error('Ook!', \E_ERROR)); |
|
||||
$this->assertSame('PHP Fatal Error', $c->getErrorType()); |
|
||||
$c = new ThrowableController(new Error('Ook!', \E_WARNING)); |
|
||||
$this->assertSame('PHP Warning', $c->getErrorType()); |
|
||||
$c = new ThrowableController(new Error('Ook!', \E_PARSE)); |
|
||||
$this->assertSame('PHP Parsing Error', $c->getErrorType()); |
|
||||
$c = new ThrowableController(new Error('Ook!', \E_NOTICE)); |
|
||||
$this->assertSame('PHP Notice', $c->getErrorType()); |
|
||||
$c = new ThrowableController(new Error('Ook!', \E_DEPRECATED)); |
|
||||
$this->assertSame('Deprecated', $c->getErrorType()); |
|
||||
$c = new ThrowableController(new Error('Ook!', \E_CORE_ERROR)); |
|
||||
$this->assertSame('PHP Core Error', $c->getErrorType()); |
|
||||
$c = new ThrowableController(new Error('Ook!', \E_CORE_WARNING)); |
|
||||
$this->assertSame('PHP Core Warning', $c->getErrorType()); |
|
||||
$c = new ThrowableController(new Error('Ook!', \E_COMPILE_ERROR)); |
|
||||
$this->assertSame('Compile Error', $c->getErrorType()); |
|
||||
$c = new ThrowableController(new Error('Ook!', \E_COMPILE_WARNING)); |
|
||||
$this->assertSame('Compile Warning', $c->getErrorType()); |
|
||||
$c = new ThrowableController(new Error('Ook!', \E_STRICT)); |
|
||||
$this->assertSame('Runtime Notice', $c->getErrorType()); |
|
||||
$c = new ThrowableController(new Error('Ook!', \E_RECOVERABLE_ERROR)); |
|
||||
$this->assertSame('Recoverable Error', $c->getErrorType()); |
|
||||
$c = new ThrowableController(new Error('Ook!')); |
|
||||
$this->assertNull($c->getErrorType()); |
|
||||
$c = new ThrowableController(new \Exception('Ook!')); |
|
||||
$this->assertNull($c->getErrorType()); |
|
||||
|
|
||||
// For code coverage purposes. |
|
||||
$this->assertNull($c->getErrorType()); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getFrames |
|
||||
* |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::__construct |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getErrorType |
|
||||
* @covers \MensBeam\Catcher\ThrowableController::getPrevious |
|
||||
*/ |
|
||||
public function testMethod_getFrames(): void { |
|
||||
$f = false; |
|
||||
try { |
|
||||
throw new \Exception('Ook!'); |
|
||||
} catch (\Throwable $t) { |
|
||||
$c = new ThrowableController($t); |
|
||||
$f = $c->getFrames(); |
|
||||
} finally { |
|
||||
$this->assertSame(\Exception::class, $f[0]['class']); |
|
||||
} |
|
||||
|
|
||||
$f = false; |
|
||||
try { |
|
||||
throw new Error('Ook!', \E_ERROR); |
|
||||
} catch (\Throwable $t) { |
|
||||
$c = new ThrowableController($t); |
|
||||
$f = $c->getFrames(); |
|
||||
} finally { |
|
||||
$this->assertSame(Error::class, $f[0]['class']); |
|
||||
} |
|
||||
|
|
||||
$f = false; |
|
||||
try { |
|
||||
throw new \Exception(message: 'Ook!', previous: new Error(message: 'Ook!', code: \E_ERROR, previous: new \Exception('Ook!'))); |
|
||||
} catch (\Throwable $t) { |
|
||||
$c = new ThrowableController($t); |
|
||||
$f = $c->getFrames(); |
|
||||
} finally { |
|
||||
$this->assertSame(\Exception::class, $f[0]['class']); |
|
||||
$this->assertSame(Error::class, $f[count($f) - 2]['class']); |
|
||||
} |
|
||||
|
|
||||
$f = false; |
|
||||
try { |
|
||||
call_user_func_array(function () { |
|
||||
throw new \Exception('Ook!'); |
|
||||
}, []); |
|
||||
} catch (\Throwable $t) { |
|
||||
$c = new ThrowableController($t); |
|
||||
$f = $c->getFrames(); |
|
||||
} finally { |
|
||||
$this->assertSame(\Exception::class, $f[0]['class']); |
|
||||
$this->assertArrayHasKey('file', $f[2]); |
|
||||
$this->assertMatchesRegularExpression('/TestThrowableController\.php$/', $f[2]['file']); |
|
||||
$this->assertSame('call_user_func_array', $f[2]['function']); |
|
||||
$this->assertArrayHasKey('line', $f[2]); |
|
||||
$this->assertNotSame(0, $f[2]['line']); |
|
||||
} |
|
||||
|
|
||||
// This is mostly here for code coverage: to delete userland error handling from |
|
||||
// the backtrace |
|
||||
$f = false; |
|
||||
try { |
|
||||
function ook() {} |
|
||||
call_user_func('ook', []); |
|
||||
} catch (\Throwable $t) { |
|
||||
$c = new ThrowableController($t); |
|
||||
$f = $c->getFrames(); |
|
||||
} finally { |
|
||||
$this->assertSame(\TypeError::class, $f[0]['class']); |
|
||||
} |
|
||||
|
|
||||
// For code coverage purposes; should use the cached value instead of calculating |
|
||||
// the frames over again. |
|
||||
$f = $c->getFrames(); |
|
||||
|
|
||||
// Lastly test for a RangeException |
|
||||
$this->expectException(\RangeException::class); |
|
||||
$c = new ThrowableController(new \Exception('Ook!')); |
|
||||
$c->getFrames(-1); |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,41 @@ |
|||||
|
<?php |
||||
|
/** |
||||
|
* @license MIT |
||||
|
* Copyright 2022 Dustin Wilson, et al. |
||||
|
* See LICENSE and AUTHORS files for details |
||||
|
*/ |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
namespace MensBeam\Catcher\Test; |
||||
|
use MensBeam\Catcher\Handler; |
||||
|
|
||||
|
|
||||
|
class TestingHandler extends Handler { |
||||
|
protected ?string $_name = null; |
||||
|
|
||||
|
|
||||
|
protected function handleCallback(array $output): array { |
||||
|
$output['code'] = (\PHP_SAPI === 'cli') ? $output['code'] | self::NOW : $output['code']; |
||||
|
return $output; |
||||
|
} |
||||
|
|
||||
|
protected function invokeCallback(): void { |
||||
|
foreach ($this->outputBuffer as $o) { |
||||
|
if (($o['code'] & self::OUTPUT) === 0) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if ($o['code'] & self::LOG) { |
||||
|
$this->log($o['controller']->getThrowable(), json_encode([ |
||||
|
'class' => $o['class'], |
||||
|
'code' => $o['code'], |
||||
|
'file' => $o['file'], |
||||
|
'line' => $o['line'], |
||||
|
'message' => $o['message'] |
||||
|
])); |
||||
|
} |
||||
|
|
||||
|
//$this->print($this->serializeOutputThrowable($o)); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -1,28 +0,0 @@ |
|||||
<?xml version="1.0"?> |
|
||||
<phpunit |
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" |
|
||||
colors="true" |
|
||||
bootstrap="bootstrap.php" |
|
||||
convertErrorsToExceptions="false" |
|
||||
convertNoticesToExceptions="false" |
|
||||
convertWarningsToExceptions="false" |
|
||||
beStrictAboutTestsThatDoNotTestAnything="true" |
|
||||
forceCoversAnnotation="true" |
|
||||
stopOnError="false"> |
|
||||
<coverage processUncoveredFiles="true"> |
|
||||
<include> |
|
||||
<directory suffix=".php">../lib</directory> |
|
||||
</include> |
|
||||
</coverage> |
|
||||
<testsuites> |
|
||||
<testsuite name="Main"> |
|
||||
<file>cases/TestCatcher.php</file> |
|
||||
<file>cases/TestHandler.php</file> |
|
||||
<file>cases/TestHTMLHandler.php</file> |
|
||||
<file>cases/TestJSONHandler.php</file> |
|
||||
<file>cases/TestPlainTextHandler.php</file> |
|
||||
<file>cases/TestThrowableController.php</file> |
|
||||
</testsuite> |
|
||||
</testsuites> |
|
||||
</phpunit> |
|
@ -0,0 +1,21 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd" |
||||
|
beStrictAboutOutputDuringTests="true" |
||||
|
beStrictAboutTestsThatDoNotTestAnything="true" |
||||
|
bootstrap="bootstrap.php" |
||||
|
cacheDirectory=".phpunit.cache" |
||||
|
colors="true" |
||||
|
executionOrder="defects" |
||||
|
requireCoverageMetadata="true" |
||||
|
> |
||||
|
<testsuites> |
||||
|
<testsuite name="Main"> |
||||
|
<directory prefix="Test" suffix=".php">./cases</directory> |
||||
|
</testsuite> |
||||
|
</testsuites> |
||||
|
<coverage> |
||||
|
<include> |
||||
|
<directory suffix=".php">../lib</directory> |
||||
|
</include> |
||||
|
</coverage> |
||||
|
</phpunit> |
Loading…
Reference in new issue