Browse Source

Added tests for Logger\Handler, Logger\Level

main
Dustin Wilson 1 year ago
parent
commit
3af91342e1
  1. 5
      composer.json
  2. 18
      composer.lock
  3. 71
      lib/Logger/Handler.php
  4. 151
      tests/cases/TestHandler.php
  5. 37
      tests/cases/TestLevel.php
  6. 6
      tests/cases/TestLogger.php
  7. 11
      tests/lib/Error.php
  8. 31
      tests/lib/ErrorHandlingTestCase.php

5
composer.json

@ -14,6 +14,11 @@
"MensBeam\\": "lib/"
}
},
"autoload-dev": {
"psr-4": {
"MensBeam\\Logger\\Test\\": "tests/lib/"
}
},
"authors": [
{
"name": "Dustin Wilson",

18
composer.lock

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "2bcb4ee6bff85959554f876637b58d0e",
"content-hash": "72b6aa958e055d50217e8d98aa9f070a",
"packages": [
{
"name": "mensbeam/filesystem",
@ -864,16 +864,16 @@
},
{
"name": "phpunit/phpunit",
"version": "10.0.18",
"version": "10.0.19",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "582563ed2edc62d1455cdbe00ea49fe09428eef3"
"reference": "20c23e85c86e5c06d63538ba464e8054f4744e62"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/582563ed2edc62d1455cdbe00ea49fe09428eef3",
"reference": "582563ed2edc62d1455cdbe00ea49fe09428eef3",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/20c23e85c86e5c06d63538ba464e8054f4744e62",
"reference": "20c23e85c86e5c06d63538ba464e8054f4744e62",
"shasum": ""
},
"require": {
@ -945,7 +945,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.0.18"
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.0.19"
},
"funding": [
{
@ -961,7 +961,7 @@
"type": "tidelift"
}
],
"time": "2023-03-22T06:15:31+00:00"
"time": "2023-03-27T11:46:33+00:00"
},
{
"name": "sebastian/cli-parser",
@ -1931,6 +1931,8 @@
"platform": {
"php": ">=8.1"
},
"platform-dev": [],
"platform-dev": {
"ext-pcov": "*"
},
"plugin-api-version": "2.3.0"
}

71
lib/Logger/Handler.php

@ -19,37 +19,24 @@ abstract class Handler {
public function __construct(array $levels = [ 0, 1, 2, 3, 4, 5, 6, 7 ], array $options = []) {
$levelsCount = count($levels);
if ($levelsCount > 8) {
throw new InvalidArgumentException(sprintf('Argument #%s ($levels) cannot have more than 8 values', $this->getParamPosition()));
}
if (count($levels) === 0) {
throw new InvalidArgumentException(sprintf('Argument #%s ($levels) must not be empty', $this->getParamPosition()));
}
$levels = array_unique($levels, \SORT_NUMERIC);
foreach ($levels as $k => $v) {
if (!is_int($v)) {
$type = gettype($v);
$type = ($type === 'object') ? $v::class : $type;
throw new InvalidArgumentException(sprintf('Value #%s of argument #%s ($levels) must be of type int, %s given', $k, $this->getParamPosition(), $type));
}
if ($v < 0 || $v > 7) {
throw new RangeException(sprintf('Argument #%s ($levels) cannot be %s; it is not in the range 0 - 7', $this->getParamPosition(), $v));
}
}
$this->levels = array_values($levels);
$this->levels = $this->verifyLevels($levels);
$class = get_class($this);
foreach ($options as $key => $value) {
$key = "_$key";
$this->$key = $value;
$name = "_$key";
if (!property_exists($class, $name)) {
trigger_error(sprintf('Undefined option in %s: %s', $class, $key), \E_USER_WARNING);
continue;
}
$this->$name = $value;
}
}
public function getLevels(): array {
return $this->levels;
}
public function getOption(string $name): mixed {
$class = get_class($this);
@ -62,10 +49,15 @@ abstract class Handler {
return $this->$name;
}
public function setLevels(int ...$levels): void {
$this->levels = $this->verifyLevels($levels, false);
}
public function setOption(string $name, mixed $value): void {
$class = get_class($this);
if (!property_exists($class, "_$name")) {
trigger_error(sprintf('Undefined option in %s: %s', $class, $name), \E_USER_WARNING);
return;
}
$name = "_$name";
@ -88,6 +80,35 @@ abstract class Handler {
abstract protected function invokeCallback(string $datetime, int $level, string $channel, string $message, array $context = []): void;
protected function verifyLevels(array $levels, bool $constructor = true): array {
$levelsCount = count($levels);
if (count($levels) === 0) {
throw new InvalidArgumentException(sprintf('Argument #%s ($levels) must not be empty', ($constructor) ? $this->getParamPosition() : 1));
}
foreach ($levels as $k => $v) {
if ($v instanceof Level) {
$levels[$k] = $v = $v->value;
}
if (!is_int($v)) {
$type = gettype($v);
$type = ($type === 'object') ? $v::class : $type;
$levelClassName = Level::class;
throw new InvalidArgumentException(sprintf('Value #%s of argument #%s ($levels) must be of type int|%s, %s given', $k + 1, ($constructor) ? $this->getParamPosition() : 1, $levelClassName, $type));
}
if ($v < 0 || $v > 7) {
throw new RangeException(sprintf('Value #%s of argument #%s ($levels) cannot be %s; it is not in the range 0 - 7', $k + 1, ($constructor) ? $this->getParamPosition() : 1, $v));
}
}
$levels = array_unique($levels, \SORT_NUMERIC);
sort($levels, \SORT_NUMERIC);
return array_values($levels);
}
private function getParamPosition(): int {
$params = (new \ReflectionClass(get_called_class()))->getConstructor()->getParameters();
foreach ($params as $k => $p) {
@ -96,6 +117,6 @@ abstract class Handler {
}
}
return -1;
return -1; // @codeCoverageIgnore
}
}

151
tests/cases/TestHandler.php

@ -0,0 +1,151 @@
<?php
/**
* @license MIT
* Copyright 2022 Dustin Wilson, et al.
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
namespace MensBeam\Logger\Test;
use MensBeam\Logger;
use MensBeam\Logger\{
Handler,
InvalidArgumentException,
Level,
RangeException,
StreamHandler
};
/** @covers \MensBeam\Logger\Handler */
class TestHandler extends ErrorHandlingTestCase {
public function testConstructor(): void {
// Test Level enums and integers, duplicated
$h = new StreamHandler(levels: [
Level::Notice,
6,
Level::Debug,
Level::Info,
Level::Critical,
0,
Level::Emergency,
Level::Error,
Level::Alert,
3,
Level::Warning
]);
$this->assertSame([ 0, 1, 2, 3, 4, 5, 6, 7 ], $h->getLevels());
}
public function testOptions(): void {
$h = new StreamHandler(options: [
'bubbles' => false,
'datetimeFormat' => 'Y-m-d\TH:i:sP'
]);
$this->assertFalse($h->getOption('bubbles'));
$this->assertSame('Y-m-d\TH:i:sP', $h->getOption('datetimeFormat'));
$h->setOption('bubbles', true);
$h->setOption('datetimeFormat', 'Y-m-d');
$this->assertTrue($h->getOption('bubbles'));
$this->assertSame('Y-m-d', $h->getOption('datetimeFormat'));
}
/** @dataProvider provideFatalErrorTests */
public function testFatalErrors(string $throwableClassName, int $code, string $message, \Closure $closure): void {
$this->expectException($throwableClassName);
$this->expectExceptionMessage($message);
if ($throwableClassName === Error::class) {
$this->expectExceptionCode($code);
}
$closure(new StreamHandler());
}
/** @dataProvider provideNonFatalErrorTests */
public function testNonFatalErrors(int $code, string $message, \Closure $closure): void {
$closure(new StreamHandler());
$this->assertEquals($code, $this->lastError?->getCode());
$this->assertSame($message, $this->lastError?->getMessage());
}
public function testInvocation(): void {
$s = fopen('php://memory', 'r+');
// Test setting the datetimeFormat and messageTransform options, showing
// a very simple example of using sprintf for interpolation.
$l = new Logger('ook', new StreamHandler(stream: $s, options: [
'datetimeFormat' => 'Y-m-d',
'messageTransform' => function (string $message, array $context): string {
return vsprintf($message, $context);
}
]));
$l->error('Ook! %s', [ 'Eek!' ]);
rewind($s);
$o = stream_get_contents($s);
$this->assertEquals(1, preg_match('/^' . (new \DateTimeImmutable())->format('Y-m-d') . ' ook ERROR Ook! Eek!\n/', $o));
}
public static function provideFatalErrorTests(): iterable {
$iterable = [
[
InvalidArgumentException::class,
0,
'Argument #1 ($levels) must not be empty',
function (Handler $h): void {
$h->setLevels();
}
],
[
InvalidArgumentException::class,
0,
'Value #5 of argument #2 ($levels) must be of type int|MensBeam\Logger\Level, string given',
function (Handler $h): void {
new StreamHandler(levels: [ 0, 1, 2, 3, '4', 5, 6, 7 ]);
}
],
[
RangeException::class,
0,
'Value #2 of argument #1 ($levels) cannot be 42; it is not in the range 0 - 7',
function (Handler $h): void {
$h->setLevels(0, 42);
}
]
];
foreach ($iterable as $i) {
yield $i;
}
}
public static function provideNonFatalErrorTests(): iterable {
$iterable = [
[
\E_USER_WARNING,
'Undefined option in ' . StreamHandler::class . ': ook',
function (Handler $h): void {
$h = new StreamHandler(options: [ 'ook' => 'eek' ]);
}
],
[
\E_USER_WARNING,
'Undefined option in ' . StreamHandler::class . ': ook',
function (Handler $h): void {
$ook = $h->getOption('ook');
}
],
[
\E_USER_WARNING,
'Undefined option in ' . StreamHandler::class . ': ook',
function (Handler $h): void {
$ook = $h->setOption('ook', 'eek');
}
]
];
foreach ($iterable as $i) {
yield $i;
}
}
}

37
tests/cases/TestLevel.php

@ -0,0 +1,37 @@
<?php
/**
* @license MIT
* Copyright 2022 Dustin Wilson, et al.
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
namespace MensBeam\Logger\Test;
use MensBeam\Logger;
use MensBeam\Logger\Level,
Psr\Log\LogLevel;
/** @covers \MensBeam\Logger\Level */
class TestLevel extends \PHPUnit\Framework\TestCase {
/** @dataProvider provideConversionsTests */
public function testConversions(string $PSR3Level, Level $level): void {
$this->assertSame($level, Level::fromPSR3($PSR3Level));
$this->assertSame($PSR3Level, $level->toPSR3());
}
public static function provideConversionsTests(): iterable {
foreach ([
[ LogLevel::EMERGENCY, Level::Emergency ],
[ LogLevel::ALERT, Level::Alert ],
[ LogLevel::CRITICAL, Level::Critical ],
[ LogLevel::ERROR, Level::Error ],
[ LogLevel::WARNING, Level::Warning ],
[ LogLevel::NOTICE, Level::Notice ],
[ LogLevel::INFO, Level::Info ],
[ LogLevel::DEBUG, Level::Debug ]
] as $l) {
yield $l;
}
}
}

6
tests/cases/TestLogger.php

@ -52,8 +52,8 @@ class TestLogger extends \PHPUnit\Framework\TestCase {
$this->assertEquals(1, preg_match('/^' . (new \DateTimeImmutable())->format('M d') . ' \d{2}:\d{2}:\d{2} ook ' . strtoupper($levelName) . ' Ook!\n/', $o));
}
/** @dataProvider provideErrorTests */
public function testErrors(string $throwableClassName, \Closure $closure): void {
/** @dataProvider provideFatalErrorTests */
public function testFatalErrors(string $throwableClassName, \Closure $closure): void {
$this->expectException($throwableClassName);
$closure(new Logger());
}
@ -65,7 +65,7 @@ class TestLogger extends \PHPUnit\Framework\TestCase {
}
}
public static function provideErrorTests(): iterable {
public static function provideFatalErrorTests(): iterable {
$iterable = [
[
InvalidArgumentException::class,

11
tests/lib/Error.php

@ -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\Logger\Test;
class Error extends \Error {}

31
tests/lib/ErrorHandlingTestCase.php

@ -0,0 +1,31 @@
<?php
/**
* @license MIT
* Copyright 2022 Dustin Wilson, et al.
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
namespace MensBeam\Logger\Test;
class ErrorHandlingTestCase extends \PHPUnit\Framework\TestCase {
protected ?Error $lastError = null;
public function setUp(): void {
set_error_handler([ $this, 'handleError' ]);
}
public function tearDown(): void {
restore_error_handler();
}
public function handleError(int $code, string $message, string $file, int $line): void {
$e = new Error($message, $code);
$this->lastError = $e;
if (in_array($code, [ \E_ERROR, \E_PARSE, \E_CORE_ERROR, \E_COMPILE_ERROR, \E_USER_ERROR, \E_RECOVERABLE_ERROR ])) {
throw $e;
}
}
}
Loading…
Cancel
Save