From 43f50f0292e17fc860f89d0677fc423749ff4903 Mon Sep 17 00:00:00 2001 From: Dustin Wilson Date: Thu, 8 Dec 2022 09:21:08 -0600 Subject: [PATCH] ThrowableController 100% Coverage --- lib/Catcher/ThrowableController.php | 38 ++++++++++--------- tests/cases/TestThrowableController.php | 50 ++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 19 deletions(-) diff --git a/lib/Catcher/ThrowableController.php b/lib/Catcher/ThrowableController.php index 8822b3f..688775c 100644 --- a/lib/Catcher/ThrowableController.php +++ b/lib/Catcher/ThrowableController.php @@ -116,10 +116,10 @@ class ThrowableController { $next = $frames[$key + 1] ?? []; if ( - !empty($frame['file']) && - !empty($frame['function']) && - !empty($frame['line']) && - str_contains($frame['function'], 'call_user_func') + !empty($next['file']) && + !empty($next['function']) && + !empty($next['line']) && + str_contains($next['function'], 'call_user_func') ) { $file = $next['file']; $line = $next['line']; @@ -157,11 +157,12 @@ class ThrowableController { ] ]; - // Add the error name if it is an Error. + // Add the error code and type if it is an Error. if ($this->throwable instanceof \Error) { $error = $this->getErrorType(); if ($error !== null) { - $f['error'] = $error; + $f['code'] = $this->throwable->getCode(); + $f['type'] = $error; } } @@ -169,20 +170,21 @@ class ThrowableController { // Go through previous throwables and merge in their frames if ($prev = $this->getPrevious()) { - $a = $frames; - $b = $prev->getFrames(); - $prevThrowable = $prev->getThrowable(); - - $diff = $a; - for ($i = count($a) - 1, $j = count($b) - 1; $i >= 0 && $j >= 0; $i--, $j--) { - $af = $diff[$i]['file']; - $bf = $b[$j]['file']; - if ($af && $bf && $af === $bf && $diff[$i]['line'] === $b[$j]['line']) { - unset($diff[$i]); + $frames = [ ...$frames, ...$prev->getFrames() ]; + + $temp = []; + foreach ($frames as $f) { + if (isset($f['file']) && isset($f['line'])) { + foreach ($temp as $t) { + if (isset($t['file']) && isset($t['line']) && $f['file'] === $t['file'] && $f['line'] === $t['line']) { + continue 2; + } + } } - } - $frames = [ ...$diff, ...$b ]; + $temp[] = $f; + } + $frames = $temp; } $this->frames = $frames; diff --git a/tests/cases/TestThrowableController.php b/tests/cases/TestThrowableController.php index 2180c76..b910030 100644 --- a/tests/cases/TestThrowableController.php +++ b/tests/cases/TestThrowableController.php @@ -88,6 +88,9 @@ class TestThrowableController extends \PHPUnit\Framework\TestCase { /** * @covers \MensBeam\Foundation\Catcher\ThrowableController::getFrames * + * @covers \MensBeam\Foundation\Catcher\ThrowableController::__construct + * @covers \MensBeam\Foundation\Catcher\ThrowableController::getErrorType + * @covers \MensBeam\Foundation\Catcher\ThrowableController::getPrevious */ public function testMethod_getFrames(): void { $f = false; @@ -97,7 +100,7 @@ class TestThrowableController extends \PHPUnit\Framework\TestCase { $c = new ThrowableController($t); $f = $c->getFrames(); } finally { - $this->assertEquals('Exception', $f[0]['class']); + $this->assertEquals(\Exception::class, $f[0]['class']); } $f = false; @@ -109,5 +112,50 @@ class TestThrowableController extends \PHPUnit\Framework\TestCase { } finally { $this->assertEquals(Error::class, $f[0]['class']); } + + $f = false; + try { + throw new \Exception(message: 'Ook!', previous: new Error('Ook!', \E_ERROR)); + } catch (\Throwable $t) { + $c = new ThrowableController($t); + $f = $c->getFrames(); + } finally { + $this->assertEquals(\Exception::class, $f[0]['class']); + $this->assertEquals(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->assertEquals(\Exception::class, $f[0]['class']); + $this->assertArrayHasKey('file', $f[2]); + $this->assertMatchesRegularExpression('/TestThrowableController\.php$/', $f[2]['file']); + $this->assertEquals('call_user_func_array', $f[2]['function']); + $this->assertArrayHasKey('line', $f[2]); + $this->assertNotEquals(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->assertEquals(\TypeError::class, $f[0]['class']); + } + + // For code coverage purposes; should use the cached value instead of calculating + // the frames over again. + $f = $c->getFrames(); } } \ No newline at end of file