Browse Source

100% coverage

main
Dustin Wilson 1 year ago
parent
commit
a7d0e6a260
  1. 3
      composer.json
  2. 53
      composer.lock
  3. 13
      lib/Logger.php
  4. 8
      lib/Logger/Handler.php
  5. 14
      lib/Logger/StreamHandler.php
  6. 2
      tests/Bootstrap.php
  7. 11
      tests/cases/TestHandler.php
  8. 17
      tests/cases/TestLogger.php
  9. 95
      tests/cases/TestStreamHandler.php

3
composer.json

@ -28,6 +28,7 @@
"require-dev": {
"ext-pcov": "*",
"docopt/docopt": "^1.0",
"phpunit/phpunit": "^10.0"
"phpunit/phpunit": "^10.0",
"mikey179/vfsstream": "^1.6"
}
}

53
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": "72b6aa958e055d50217e8d98aa9f070a",
"content-hash": "3d02f5002c6b62190718aa8c70a8acd1",
"packages": [
{
"name": "mensbeam/filesystem",
@ -374,6 +374,57 @@
},
"time": "2023-03-22T12:31:48+00:00"
},
{
"name": "mikey179/vfsstream",
"version": "v1.6.11",
"source": {
"type": "git",
"url": "https://github.com/bovigo/vfsStream.git",
"reference": "17d16a85e6c26ce1f3e2fa9ceeacdc2855db1e9f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bovigo/vfsStream/zipball/17d16a85e6c26ce1f3e2fa9ceeacdc2855db1e9f",
"reference": "17d16a85e6c26ce1f3e2fa9ceeacdc2855db1e9f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.5|^5.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.6.x-dev"
}
},
"autoload": {
"psr-0": {
"org\\bovigo\\vfs\\": "src/main/php"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Frank Kleine",
"homepage": "http://frankkleine.de/",
"role": "Developer"
}
],
"description": "Virtual file system to mock the real file system in unit tests.",
"homepage": "http://vfs.bovigo.org/",
"support": {
"issues": "https://github.com/bovigo/vfsStream/issues",
"source": "https://github.com/bovigo/vfsStream/tree/master",
"wiki": "https://github.com/bovigo/vfsStream/wiki"
},
"time": "2022-02-23T02:02:42+00:00"
},
{
"name": "myclabs/deep-copy",
"version": "1.11.1",

13
lib/Logger.php

@ -40,8 +40,13 @@ class Logger implements LoggerInterface {
public function __construct(?string $channel = null, Handler ...$handlers) {
$this->setChannel($channel);
// Set some handlers if no handlers are set, printing lower levels to stderr,
// higher to stdout.
if (count($handlers) === 0) {
$handlers[] = new StreamHandler('php://stdout');
$handlers = [
new StreamHandler(stream: 'php://stderr', levels: [ 0, 1, 2, 3 ]),
new StreamHandler(stream: 'php://stdout', levels: [ 4, 5, 6, 7 ])
];
}
$this->handlers = $handlers;
@ -116,12 +121,16 @@ class Logger implements LoggerInterface {
* @throws \MensBeam\Logger\InvalidArgumentException
*/
public function log($level, string|\Stringable $message, array $context = []): void {
if ($level instanceof Level) {
$level = $level->value;
}
// Because the interface won't allow limiting $level to just int|string this is
// necessary.
if (!is_int($level) && !is_string($level)) {
$type = gettype($level);
$type = ($type === 'object') ? $level::class : $type;
throw new InvalidArgumentException(sprintf('Argument #1 ($level) must be of type int|string, %s given', $type));
throw new InvalidArgumentException(sprintf('Argument #1 ($level) must be of type int|%s|string, %s given', Level::class, $type));
}
// If the level is a string convert it to a RFC5424 level integer.

8
lib/Logger/Handler.php

@ -64,7 +64,11 @@ abstract class Handler {
$this->$name = $value;
}
public function __invoke(int $level, string $channel, string $message, array $context = []): void {
public function __invoke(int $level, ?string $channel, string $message, array $context = []): void {
if (!in_array($level, $this->levels)) {
return;
}
$datetime = \DateTimeImmutable::createFromFormat('U.u', (string)microtime(true))->format($this->_datetimeFormat);
$message = trim($message);
@ -73,7 +77,7 @@ abstract class Handler {
$message = $t($message, $context);
}
$this->invokeCallback($datetime, $level, $channel, $message, $context);
$this->invokeCallback($datetime, $level, $channel ?? '', $message, $context);
}

14
lib/Logger/StreamHandler.php

@ -28,7 +28,7 @@ class StreamHandler extends Handler {
// The memory limit is in a shorthand format
// (https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes), so we
// need it as a integer representation in bytes.
if (preg_match('/^\s*(?<num>\d+)(?:\.\d+)?\s*(?<unit>[gkm])\s*$/i', strtolower(ini_get('memory_limit')), $matches) === 1) {
if (preg_match('/^\s*(?<num>\d+)(?:\.\d+)?\s*(?<unit>[gkm])\s*$/i', ini_get('memory_limit'), $matches) === 1) {
$num = (int)$matches['num'];
switch ($matches['unit'] ?? '') {
case 'g': $num *= 1024;
@ -61,6 +61,12 @@ class StreamHandler extends Handler {
throw new InvalidArgumentException(sprintf('Argument #1 ($stream) must be of type resource|string, %s given', $type));
}
// Bad dog, no biscuit!
if (($options['entryFormat'] ?? null) === '') {
$options['entryFormat'] = $this->_entryFormat;
}
parent::__construct($levels, $options);
}
@ -73,11 +79,7 @@ class StreamHandler extends Handler {
protected function invokeCallback(string $datetime, int $level, string $channel, string $message, array $context = []): void {
if (!in_array($level, $this->levels)) {
return;
}
// Do output formatting here.
// Do entry formatting here.
$output = trim(preg_replace_callback('/%([a-z_]+)%/', function($m) use ($datetime, $level, $channel, $message) {
switch ($m[1]) {
case 'channel': return $channel;

2
tests/Bootstrap.php

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace MensBeam\Logger\Test;
ini_set('memory_limit', '-1');
ini_set('memory_limit', '2G');
ini_set('zend.assertions', '1');
ini_set('assert.exception', 'true');
error_reporting(\E_ALL);

11
tests/cases/TestHandler.php

@ -83,6 +83,17 @@ class TestHandler extends ErrorHandlingTestCase {
rewind($s);
$o = stream_get_contents($s);
$this->assertEquals(1, preg_match('/^' . (new \DateTimeImmutable())->format('Y-m-d') . ' ook ERROR Ook! Eek!\n/', $o));
fclose($s);
}
public function testInvocationWithUnsupportedLevel(): void {
$s = fopen('php://memory', 'r+');
$l = new Logger('ook', new StreamHandler(stream: $s, levels: [ 0 ]));
$l->error('ook');
rewind($s);
$o = stream_get_contents($s);
$this->assertSame('', $o);
fclose($s);
}

17
tests/cases/TestLogger.php

@ -10,6 +10,7 @@ namespace MensBeam\Logger\Test;
use MensBeam\Logger;
use MensBeam\Logger\{
InvalidArgumentException,
Level,
StreamHandler
};
@ -24,10 +25,14 @@ class TestLogger extends \PHPUnit\Framework\TestCase {
public function testDefaultHandler(): void {
$l = new Logger();
$h = $l->getHandlers();
$this->assertEquals(1, count($h));
$this->assertEquals(2, count($h));
$this->assertInstanceOf(StreamHandler::class, $h[0]);
$this->assertInstanceOf(StreamHandler::class, $h[1]);
$s = $h[0]->getStream();
$this->assertIsString($s);
$this->assertSame('php://stderr', $s);
$s = $h[1]->getStream();
$this->assertIsString($s);
$this->assertSame('php://stdout', $s);
}
@ -49,7 +54,15 @@ class TestLogger extends \PHPUnit\Framework\TestCase {
$l->$levelName('Ook!');
rewind($s);
$o = stream_get_contents($s);
$this->assertEquals(1, preg_match('/^' . (new \DateTimeImmutable())->format('M d') . ' \d{2}:\d{2}:\d{2} ook ' . strtoupper($levelName) . ' Ook!\n/', $o));
$regex = '/^' . (new \DateTimeImmutable())->format('M d') . ' \d{2}:\d{2}:\d{2} ook ' . strtoupper($levelName) . ' Ook!\n/';
$this->assertEquals(1, preg_match($regex, $o));
// Try it again with Level enum
$l->log(constant(sprintf('%s::%s', Level::class, ucfirst($levelName))), 'Ook!');
rewind($s);
$o = stream_get_contents($s);
$this->assertEquals(1, preg_match($regex, $o));
fclose($s);
}
/** @dataProvider provideFatalErrorTests */

95
tests/cases/TestStreamHandler.php

@ -0,0 +1,95 @@
<?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,
org\bovigo\vfs\vfsStream;
use MensBeam\Logger\{
InvalidArgumentException,
Level,
StreamHandler
};
/** @covers \MensBeam\Logger\StreamHandler */
class TestStreamHandler extends ErrorHandlingTestCase {
/** @dataProvider provideResourceTypesTests */
public function testResourceTypes(\Closure $closure): void {
$regex = '/^' . (new \DateTimeImmutable())->format('M d') . ' \d{2}:\d{2}:\d{2} ook ERROR Ook!\nEek!\n/';
$this->assertEquals(1, preg_match($regex, $closure()));
}
public function testFatalErrors(): void {
$this->expectException(InvalidArgumentException::class);
new StreamHandler(42);
}
public function testGetStream(): void {
$h = new StreamHandler('ook');
$this->assertSame(CWD . '/ook', $h->getStream());
}
/** @dataProvider provideEntryFormattingTests */
public function testEntryFormatting(string $entryFormat, string $regex): void {
$s = fopen('php://memory', 'r+');
$h = new StreamHandler(stream: $s, options: [
'entryFormat' => $entryFormat
]);
$h(Level::Error->value, 'ook', 'ook');
rewind($s);
$o = stream_get_contents($s);
$this->assertEquals(1, preg_match($regex, $o));
fclose($s);
}
public static function provideResourceTypesTests(): iterable {
$iterable = [
function (): string {
$s = fopen('php://memory', 'r+');
$h = new StreamHandler($s);
$h(Level::Error->value, 'ook', "Ook!\nEek!");
rewind($s);
$o = stream_get_contents($s);
fclose($s);
return $o;
},
function (): string {
$f = tempnam(sys_get_temp_dir(), 'logger');
$h = new StreamHandler("file://$f");
$h(Level::Error->value, 'ook', "Ook!\nEek!");
$o = file_get_contents($f);
unlink($f);
return $o;
},
function (): string {
$v = vfsStream::setup('ook', 0777, [ 'ook.log' => '' ]);
$f = $v->url() . '/ook.log';
$h = new StreamHandler($f);
$h(Level::Error->value, 'ook', "Ook!\nEek!");
return file_get_contents($f);
}
];
foreach ($iterable as $i) {
yield [ $i ];
}
}
public static function provideEntryFormattingTests(): iterable {
$iterable = [
[ '%ook%', '/\n/' ],
[ '%channel% %channel% %channel% %channel% %level% %level_name%', '/ook ook ook ook 3 ERROR\n/' ],
[ '', '/^' . (new \DateTimeImmutable())->format('M d') . ' \d{2}:\d{2}:\d{2} ook ERROR ook\n/' ]
];
foreach ($iterable as $i) {
yield $i;
}
}
}
Loading…
Cancel
Save