mirror of
https://github.com/php-fig/log-test.git
synced 2026-03-23 22:52:14 +01:00
Import test classes from psr/log 1.1.4
Fix compatibility with PHPUnit and psr/log 3.0 Modernize code for PHP 8.0 Materialize magical methods from TestLogger::__call
This commit is contained in:
6
.github/workflows/continuous-integration.yml
vendored
6
.github/workflows/continuous-integration.yml
vendored
@@ -5,9 +5,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
# There is no code, we don't have to test old PHP versions
|
||||
php-versions:
|
||||
- 7.4
|
||||
- 8.0
|
||||
- 8.1
|
||||
dependency-levels:
|
||||
@@ -15,7 +13,7 @@ jobs:
|
||||
experimental:
|
||||
- false
|
||||
include:
|
||||
- php-versions: 7.4
|
||||
- php-versions: 8.0
|
||||
dependency-levels: 'lowest'
|
||||
experimental: false
|
||||
fail-fast: false
|
||||
@@ -30,7 +28,7 @@ jobs:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
|
||||
- name: Validating PHP syntax
|
||||
run: find ./tests/ -type f -name '*.php' -print0 | xargs -0 -L 1 -P 4 -- php -l
|
||||
run: find ./{src,tests}/ -type f -name '*.php' -print0 | xargs -0 -L 1 -P 4 -- php -l
|
||||
|
||||
- name: Validate composer.json and composer.lock
|
||||
run: composer validate
|
||||
|
||||
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [1.1.0] - YYYY-MM-DD
|
||||
|
||||
- Import classes from [`psr/log`][] `v1.1.4`, for compatibility with `v2.0.0` and `v3.0.0`.
|
||||
|
||||
## [1.0.0] - YYYY-MM-DD
|
||||
|
||||
- Initial release. This ports the test for and from [`psr/log` v1.1][], according to
|
||||
|
||||
@@ -43,9 +43,9 @@ If the project supports older versions of PHP:
|
||||
The version of `fig/log-test` that is installed after composer dependencies resolution varies with the version of `psr/log`.
|
||||
|
||||
| psr/log | fig/log-test | |
|
||||
|---------------|---------------|-------------------------------------------------|
|
||||
| `^1.1.14` | `1.0.*` | Empty package, classes a provided by `psr/log`. |
|
||||
| `^2.0\|^3.0` | `^1.1` | Imports test classes removed from `psr/log`. |
|
||||
|---------------|--------------|-------------------------------------------------|
|
||||
| `^1.1.14` | `1.0.*` | Empty package, classes a provided by `psr/log`. |
|
||||
| `^2.0\|^3.0` | `^1.1` | Imports test classes removed from `psr/log`. |
|
||||
|
||||
[`psr/log`]: https://packagist.org/packages/psr/log
|
||||
[PSR-3]: https://www.php-fig.org/psr/psr-3/
|
||||
|
||||
@@ -10,13 +10,18 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^5.3 | ^7.0 | ^8.0",
|
||||
"psr/log": "^1.1.1"
|
||||
"php": "^8.0",
|
||||
"psr/log": "^2.0 | ^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.0 | ^9.0",
|
||||
"squizlabs/php_codesniffer": "^3.6"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Log\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Fig\\Log\\Tests\\": "tests"
|
||||
|
||||
18
src/Test/DummyTest.php
Normal file
18
src/Test/DummyTest.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log\Test;
|
||||
|
||||
/**
|
||||
* This class is internal and does not follow the BC promise.
|
||||
*
|
||||
* Do NOT use this class in any way.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class DummyTest
|
||||
{
|
||||
public function __toString()
|
||||
{
|
||||
return 'DummyTest';
|
||||
}
|
||||
}
|
||||
135
src/Test/LoggerInterfaceTest.php
Normal file
135
src/Test/LoggerInterfaceTest.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log\Test;
|
||||
|
||||
use Psr\Log\InvalidArgumentException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Provides a base test class for ensuring compliance with the LoggerInterface.
|
||||
*
|
||||
* Implementors can extend the class and implement abstract methods to run this
|
||||
* as part of their test suite.
|
||||
*/
|
||||
abstract class LoggerInterfaceTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Return an instance of the logger to be tested.
|
||||
*
|
||||
* @return LoggerInterface
|
||||
*/
|
||||
abstract public function getLogger();
|
||||
|
||||
/**
|
||||
* This must return the log messages in order.
|
||||
*
|
||||
* The simple formatting of the messages is: "<LOG LEVEL> <MESSAGE>".
|
||||
*
|
||||
* Example ->error('Foo') would yield "error Foo".
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
abstract public function getLogs();
|
||||
|
||||
public function testImplements()
|
||||
{
|
||||
$this->assertInstanceOf(LoggerInterface::class, $this->getLogger());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideLevelsAndMessages
|
||||
*/
|
||||
public function testLogsAtAllLevels($level, $message)
|
||||
{
|
||||
$logger = $this->getLogger();
|
||||
$logger->{$level}($message, ['user' => 'Bob']);
|
||||
$logger->log($level, $message, ['user' => 'Bob']);
|
||||
|
||||
$expected = [
|
||||
$level . ' message of level ' . $level . ' with context: Bob',
|
||||
$level . ' message of level ' . $level . ' with context: Bob',
|
||||
];
|
||||
$this->assertEquals($expected, $this->getLogs());
|
||||
}
|
||||
|
||||
public function provideLevelsAndMessages()
|
||||
{
|
||||
return [
|
||||
LogLevel::EMERGENCY => [LogLevel::EMERGENCY, 'message of level emergency with context: {user}'],
|
||||
LogLevel::ALERT => [LogLevel::ALERT, 'message of level alert with context: {user}'],
|
||||
LogLevel::CRITICAL => [LogLevel::CRITICAL, 'message of level critical with context: {user}'],
|
||||
LogLevel::ERROR => [LogLevel::ERROR, 'message of level error with context: {user}'],
|
||||
LogLevel::WARNING => [LogLevel::WARNING, 'message of level warning with context: {user}'],
|
||||
LogLevel::NOTICE => [LogLevel::NOTICE, 'message of level notice with context: {user}'],
|
||||
LogLevel::INFO => [LogLevel::INFO, 'message of level info with context: {user}'],
|
||||
LogLevel::DEBUG => [LogLevel::DEBUG, 'message of level debug with context: {user}'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testThrowsOnInvalidLevel()
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$logger = $this->getLogger();
|
||||
$logger->log('invalid level', 'Foo');
|
||||
}
|
||||
|
||||
public function testContextReplacement()
|
||||
{
|
||||
$logger = $this->getLogger();
|
||||
$logger->info('{Message {nothing} {user} {foo.bar} a}', ['user' => 'Bob', 'foo.bar' => 'Bar']);
|
||||
|
||||
$expected = ['info {Message {nothing} Bob Bar a}'];
|
||||
$this->assertEquals($expected, $this->getLogs());
|
||||
}
|
||||
|
||||
public function testObjectCastToString()
|
||||
{
|
||||
$dummy = $this->createPartialMock(DummyTest::class, ['__toString']);
|
||||
$dummy->expects($this->once())
|
||||
->method('__toString')
|
||||
->will($this->returnValue('DUMMY'));
|
||||
|
||||
$this->getLogger()->warning($dummy);
|
||||
|
||||
$expected = ['warning DUMMY'];
|
||||
$this->assertEquals($expected, $this->getLogs());
|
||||
}
|
||||
|
||||
public function testContextCanContainAnything()
|
||||
{
|
||||
$closed = fopen('php://memory', 'r');
|
||||
fclose($closed);
|
||||
|
||||
$context = [
|
||||
'bool' => true,
|
||||
'null' => null,
|
||||
'string' => 'Foo',
|
||||
'int' => 0,
|
||||
'float' => 0.5,
|
||||
'nested' => ['with object' => new DummyTest()],
|
||||
'object' => new \DateTime(),
|
||||
'resource' => fopen('php://memory', 'r'),
|
||||
'closed' => $closed,
|
||||
];
|
||||
|
||||
$this->getLogger()->warning('Crazy context data', $context);
|
||||
|
||||
$expected = ['warning Crazy context data'];
|
||||
$this->assertEquals($expected, $this->getLogs());
|
||||
}
|
||||
|
||||
public function testContextExceptionKeyCanBeExceptionOrOtherValues()
|
||||
{
|
||||
$logger = $this->getLogger();
|
||||
$logger->warning('Random message', ['exception' => 'oops']);
|
||||
$logger->critical('Uncaught Exception!', ['exception' => new \LogicException('Fail')]);
|
||||
|
||||
$expected = [
|
||||
'warning Random message',
|
||||
'critical Uncaught Exception!'
|
||||
];
|
||||
$this->assertEquals($expected, $this->getLogs());
|
||||
}
|
||||
}
|
||||
328
src/Test/TestLogger.php
Normal file
328
src/Test/TestLogger.php
Normal file
@@ -0,0 +1,328 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log\Test;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LoggerTrait;
|
||||
|
||||
/**
|
||||
* Used for testing purposes.
|
||||
*
|
||||
* It records all records and gives you access to them for verification.
|
||||
*/
|
||||
class TestLogger implements LoggerInterface
|
||||
{
|
||||
use LoggerTrait;
|
||||
|
||||
public array $records = [];
|
||||
public array $recordsByLevel = [];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function log($level, string|\Stringable $message, array $context = []): void
|
||||
{
|
||||
$record = [
|
||||
'level' => $level,
|
||||
'message' => $message,
|
||||
'context' => $context,
|
||||
];
|
||||
|
||||
$this->recordsByLevel[$record['level']][] = $record;
|
||||
$this->records[] = $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $level
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRecords($level)
|
||||
{
|
||||
return isset($this->recordsByLevel[$level]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $record
|
||||
* @param string $level
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRecord($record, $level)
|
||||
{
|
||||
if (is_string($record)) {
|
||||
$record = ['message' => $record];
|
||||
}
|
||||
|
||||
return $this->hasRecordThatPasses(function ($rec) use ($record) {
|
||||
if ($rec['message'] !== $record['message']) {
|
||||
return false;
|
||||
}
|
||||
if (isset($record['context']) && $rec['context'] !== $record['context']) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param string $level
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRecordThatContains($message, $level)
|
||||
{
|
||||
return $this->hasRecordThatPasses(fn ($rec) => str_contains($rec['message'], $message), $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $regex
|
||||
* @param string $level
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRecordThatMatches($regex, $level)
|
||||
{
|
||||
return $this->hasRecordThatPasses(fn ($rec) => preg_match($regex, $rec['message']) > 0, $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $predicate
|
||||
* @param string $level
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRecordThatPasses(callable $predicate, $level)
|
||||
{
|
||||
if (!isset($this->recordsByLevel[$level])) {
|
||||
return false;
|
||||
}
|
||||
foreach ($this->recordsByLevel[$level] as $i => $rec) {
|
||||
if ($predicate($rec, $i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Since psr/log-util 1.1
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
@trigger_error(sprintf('Since psr/log-util 1.1: Method "%s" is deprecated and should not be called. Use method "%s" instead.', __FUNCTION__, $method), \E_USER_DEPRECATED);
|
||||
|
||||
if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) {
|
||||
$genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3];
|
||||
$level = strtolower($matches[2]);
|
||||
if (method_exists($this, $genericMethod)) {
|
||||
$args[] = $level;
|
||||
return call_user_func_array([$this, $genericMethod], $args);
|
||||
}
|
||||
}
|
||||
throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()');
|
||||
}
|
||||
|
||||
public function hasEmergency($record): bool
|
||||
{
|
||||
return $this->hasRecord($record, 'emergency');
|
||||
}
|
||||
|
||||
public function hasAlert($record): bool
|
||||
{
|
||||
return $this->hasRecord($record, 'alert');
|
||||
}
|
||||
|
||||
public function hasCritical($record): bool
|
||||
{
|
||||
return $this->hasRecord($record, 'critical');
|
||||
}
|
||||
|
||||
public function hasError($record): bool
|
||||
{
|
||||
return $this->hasRecord($record, 'error');
|
||||
}
|
||||
|
||||
public function hasWarning($record): bool
|
||||
{
|
||||
return $this->hasRecord($record, 'warning');
|
||||
}
|
||||
|
||||
public function hasNotice($record): bool
|
||||
{
|
||||
return $this->hasRecord($record, 'notice');
|
||||
}
|
||||
|
||||
public function hasInfo($record): bool
|
||||
{
|
||||
return $this->hasRecord($record, 'info');
|
||||
}
|
||||
|
||||
public function hasDebug($record): bool
|
||||
{
|
||||
return $this->hasRecord($record, 'debug');
|
||||
}
|
||||
|
||||
public function hasEmergencyRecords(): bool
|
||||
{
|
||||
return $this->hasRecords('emergency');
|
||||
}
|
||||
|
||||
public function hasAlertRecords(): bool
|
||||
{
|
||||
return $this->hasRecords('alert');
|
||||
}
|
||||
|
||||
public function hasCriticalRecords(): bool
|
||||
{
|
||||
return $this->hasRecords('critical');
|
||||
}
|
||||
|
||||
public function hasErrorRecords(): bool
|
||||
{
|
||||
return $this->hasRecords('error');
|
||||
}
|
||||
|
||||
public function hasWarningRecords(): bool
|
||||
{
|
||||
return $this->hasRecords('warning');
|
||||
}
|
||||
|
||||
public function hasNoticeRecords(): bool
|
||||
{
|
||||
return $this->hasRecords('notice');
|
||||
}
|
||||
|
||||
public function hasInfoRecords(): bool
|
||||
{
|
||||
return $this->hasRecords('info');
|
||||
}
|
||||
|
||||
public function hasDebugRecords(): bool
|
||||
{
|
||||
return $this->hasRecords('debug');
|
||||
}
|
||||
|
||||
public function hasEmergencyThatContains($message): bool
|
||||
{
|
||||
return $this->hasRecordThatContains($message, 'emergency');
|
||||
}
|
||||
|
||||
public function hasAlertThatContains($message): bool
|
||||
{
|
||||
return $this->hasRecordThatContains($message, 'alert');
|
||||
}
|
||||
|
||||
public function hasCriticalThatContains($message): bool
|
||||
{
|
||||
return $this->hasRecordThatContains($message, 'critical');
|
||||
}
|
||||
|
||||
public function hasErrorThatContains($message): bool
|
||||
{
|
||||
return $this->hasRecordThatContains($message, 'error');
|
||||
}
|
||||
|
||||
public function hasWarningThatContains($message): bool
|
||||
{
|
||||
return $this->hasRecordThatContains($message, 'warning');
|
||||
}
|
||||
|
||||
public function hasNoticeThatContains($message): bool
|
||||
{
|
||||
return $this->hasRecordThatContains($message, 'notice');
|
||||
}
|
||||
|
||||
public function hasInfoThatContains($message): bool
|
||||
{
|
||||
return $this->hasRecordThatContains($message, 'info');
|
||||
}
|
||||
|
||||
public function hasDebugThatContains($message): bool
|
||||
{
|
||||
return $this->hasRecordThatContains($message, 'debug');
|
||||
}
|
||||
|
||||
public function hasEmergencyThatMatches(string $regex): bool
|
||||
{
|
||||
return $this->hasRecordThatMatches($regex, 'emergency');
|
||||
}
|
||||
|
||||
public function hasAlertThatMatches(string $regex): bool
|
||||
{
|
||||
return $this->hasRecordThatMatches($regex, 'alert');
|
||||
}
|
||||
|
||||
public function hasCriticalThatMatches(string $regex): bool
|
||||
{
|
||||
return $this->hasRecordThatMatches($regex, 'critical');
|
||||
}
|
||||
|
||||
public function hasErrorThatMatches(string $regex): bool
|
||||
{
|
||||
return $this->hasRecordThatMatches($regex, 'error');
|
||||
}
|
||||
|
||||
public function hasWarningThatMatches(string $regex): bool
|
||||
{
|
||||
return $this->hasRecordThatMatches($regex, 'warning');
|
||||
}
|
||||
|
||||
public function hasNoticeThatMatches(string $regex): bool
|
||||
{
|
||||
return $this->hasRecordThatMatches($regex, 'notice');
|
||||
}
|
||||
|
||||
public function hasInfoThatMatches(string $regex): bool
|
||||
{
|
||||
return $this->hasRecordThatMatches($regex, 'info');
|
||||
}
|
||||
|
||||
public function hasDebugThatMatches(string $regex): bool
|
||||
{
|
||||
return $this->hasRecordThatMatches($regex, 'debug');
|
||||
}
|
||||
|
||||
public function hasEmergencyThatPasses(callable $predicate): bool
|
||||
{
|
||||
return $this->hasRecordThatPasses($predicate, 'emergency');
|
||||
}
|
||||
|
||||
public function hasAlertThatPasses(callable $predicate): bool
|
||||
{
|
||||
return $this->hasRecordThatPasses($predicate, 'alert');
|
||||
}
|
||||
|
||||
public function hasCriticalThatPasses(callable $predicate): bool
|
||||
{
|
||||
return $this->hasRecordThatPasses($predicate, 'critical');
|
||||
}
|
||||
|
||||
public function hasErrorThatPasses(callable $predicate): bool
|
||||
{
|
||||
return $this->hasRecordThatPasses($predicate, 'error');
|
||||
}
|
||||
|
||||
public function hasWarningThatPasses(callable $predicate): bool
|
||||
{
|
||||
return $this->hasRecordThatPasses($predicate, 'warning');
|
||||
}
|
||||
|
||||
public function hasNoticeThatPasses(callable $predicate): bool
|
||||
{
|
||||
return $this->hasRecordThatPasses($predicate, 'notice');
|
||||
}
|
||||
|
||||
public function hasInfoThatPasses(callable $predicate): bool
|
||||
{
|
||||
return $this->hasRecordThatPasses($predicate, 'info');
|
||||
}
|
||||
|
||||
public function hasDebugThatPasses(callable $predicate): bool
|
||||
{
|
||||
return $this->hasRecordThatPasses($predicate, 'debug');
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->records = [];
|
||||
$this->recordsByLevel = [];
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ use Psr\Log\Test\TestLogger;
|
||||
/**
|
||||
* Test classes from psr/log 1.1.x
|
||||
*
|
||||
* @requires PHP 7.4
|
||||
* @covers \Psr\Log\Test\TestLogger
|
||||
* @covers \Psr\Log\Test\LoggerInterfaceTest
|
||||
*
|
||||
@@ -72,7 +71,7 @@ class TestLoggerTest extends LoggerInterfaceTest
|
||||
$this->assertTrue(call_user_func([$logger, $levelMethod], $record), $levelMethod.' without context');
|
||||
|
||||
$record = ['message' => $level.' Message', ['foo' => 'bar']];
|
||||
$this->assertTrue($logger->hasRecord($record, $level),'hasRecord with context');
|
||||
$this->assertTrue($logger->hasRecord($record, $level), 'hasRecord with context');
|
||||
$this->assertTrue(call_user_func([$logger, $levelMethod], $record), $levelMethod.' with context');
|
||||
|
||||
$this->assertTrue(call_user_func([$logger, $levelMethod.'ThatContains'], 'Message'), $levelMethod.'ThatContains');
|
||||
|
||||
Reference in New Issue
Block a user