diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a9875b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/vendor/ +composer.lock diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a5e6507 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 LinkuNijmegen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..8ccd910 --- /dev/null +++ b/composer.json @@ -0,0 +1,23 @@ +{ + "name": "linku/feedback", + "description": "Abstract layer between services and output in PHP", + "type": "library", + "license": "MIT", + "require": { + "php": "^7.2.0", + "psr/log": "^1.1" + }, + "autoload": { + "psr-4": { + "Linku\\Feedback\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Linku\\Feedback\\Tests\\": "tests/" + } + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + } +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..f634440 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,24 @@ + + + + + + + + + + + tests + + + + + + src + + + diff --git a/src/ChainedFeedback.php b/src/ChainedFeedback.php new file mode 100644 index 0000000..0f6ef5d --- /dev/null +++ b/src/ChainedFeedback.php @@ -0,0 +1,75 @@ +chain = $chain; + } + + public function exception(Throwable $exception): void + { + foreach ($this->chain as $feedback) { + $feedback->exception($exception); + } + } + + public function error(string $message): void + { + foreach ($this->chain as $feedback) { + $feedback->error($message); + } + } + + public function warning(string $message): void + { + foreach ($this->chain as $feedback) { + $feedback->warning($message); + } + } + + public function info(string $message): void + { + foreach ($this->chain as $feedback) { + $feedback->info($message); + } + } + + public function success(string $message): void + { + foreach ($this->chain as $feedback) { + $feedback->success($message); + } + } + + public function startProcess(int $total = 0): void + { + foreach ($this->chain as $feedback) { + $feedback->startProcess($total); + } + } + + public function finishProcess(): void + { + foreach ($this->chain as $feedback) { + $feedback->finishProcess(); + } + } + + public function advanceProcess(int $steps = 1): void + { + foreach ($this->chain as $feedback) { + $feedback->advanceProcess($steps); + } + } +} diff --git a/src/ClosureFeedback.php b/src/ClosureFeedback.php new file mode 100644 index 0000000..2bdef0b --- /dev/null +++ b/src/ClosureFeedback.php @@ -0,0 +1,111 @@ +errorClosure = $error ?? static function (string $message) {}; + $this->warningClosure = $warning ?? static function (string $message) {}; + $this->infoClosure = $info ?? static function (string $message) {}; + $this->successClosure = $success ?? static function (string $message) {}; + $this->exceptionClosure = $exception ?? function (Throwable $exception) { $this->error($exception->getMessage()); }; + $this->startProcessClosure = $startProcess ?? static function (int $total) {}; + $this->finishProcessClosure = $finishProcess ?? static function () {}; + $this->advanceProcessClosure = $advanceProcess ?? static function (int $steps) {}; + } + + public function exception(Throwable $exception): void + { + ($this->exceptionClosure)($exception); + } + + public function error(string $message): void + { + ($this->errorClosure)($message); + } + + public function warning(string $message): void + { + ($this->warningClosure)($message); + } + + public function info(string $message): void + { + ($this->infoClosure)($message); + } + + public function success(string $message): void + { + ($this->successClosure)($message); + } + + public function startProcess(int $total = 0): void + { + ($this->startProcessClosure)($total); + } + + public function finishProcess(): void + { + ($this->finishProcessClosure)(); + } + + public function advanceProcess(int $steps = 1): void + { + ($this->advanceProcessClosure)($steps); + } +} diff --git a/src/Feedback.php b/src/Feedback.php new file mode 100644 index 0000000..6c8af9c --- /dev/null +++ b/src/Feedback.php @@ -0,0 +1,25 @@ +logger = $logger; + } + + public function exception(Throwable $exception): void + { + $this->logger->error($exception->getMessage()); + } + + public function error(string $message): void + { + $this->logger->error($message); + } + + public function warning(string $message): void + { + $this->logger->warning($message); + } + + public function info(string $message): void + { + $this->logger->info($message); + } + + public function success(string $message): void + { + $this->logger->info($message); + } + + public function startProcess(int $total = 0): void + { + $this->logger->info('Process '.($total?'of '.$total.' ':'').'started'); + } + + public function finishProcess(): void + { + $this->logger->info('Process stopped'); + } + + public function advanceProcess(int $steps = 1): void + { + $this->logger->debug('Process advanced with '.$steps); + } +} diff --git a/src/NoFeedback.php b/src/NoFeedback.php new file mode 100644 index 0000000..4b30bc7 --- /dev/null +++ b/src/NoFeedback.php @@ -0,0 +1,41 @@ +firstFeedback = $this->prophesize(Feedback::class); + $this->secondFeedback = $this->prophesize(Feedback::class); + + $this->testSubject = new ChainedFeedback( + $this->firstFeedback->reveal(), + $this->secondFeedback->reveal() + ); + } + + public function testException(): void + { + $exceptionMessage = 'Something went wrong!'; + $exception = new RuntimeException($exceptionMessage); + + $this->firstFeedback->exception($exception) + ->shouldBeCalledOnce(); + $this->secondFeedback->exception($exception) + ->shouldBeCalledOnce(); + + $this->testSubject->exception($exception); + } + + public function testError(): void + { + $message = 'Something went wrong!'; + + $this->firstFeedback->error($message) + ->shouldBeCalledOnce(); + $this->secondFeedback->error($message) + ->shouldBeCalledOnce(); + + $this->testSubject->error($message); + } + + public function testWarning(): void + { + $message = 'Something went wrong!'; + + $this->firstFeedback->warning($message) + ->shouldBeCalledOnce(); + $this->secondFeedback->warning($message) + ->shouldBeCalledOnce(); + + $this->testSubject->warning($message); + } + + public function testInfo(): void + { + $message = 'Something happened.'; + + $this->firstFeedback->info($message) + ->shouldBeCalledOnce(); + $this->secondFeedback->info($message) + ->shouldBeCalledOnce(); + + $this->testSubject->info($message); + } + + public function testSuccess(): void + { + $message = 'Something went right!'; + + $this->firstFeedback->success($message) + ->shouldBeCalledOnce(); + $this->secondFeedback->success($message) + ->shouldBeCalledOnce(); + + $this->testSubject->success($message); + } + + public function testStartProcess(): void + { + $defaultTotal = 0; + + $this->firstFeedback->startProcess($defaultTotal) + ->shouldBeCalledOnce(); + $this->secondFeedback->startProcess($defaultTotal) + ->shouldBeCalledOnce(); + + $this->testSubject->startProcess(); + } + + public function testStartProcessWithTotal(): void + { + $total = 25; + + $this->firstFeedback->startProcess($total) + ->shouldBeCalledOnce(); + $this->secondFeedback->startProcess($total) + ->shouldBeCalledOnce(); + + $this->testSubject->startProcess($total); + } + + public function testFinishProcess(): void + { + $this->firstFeedback->finishProcess() + ->shouldBeCalledOnce(); + $this->secondFeedback->finishProcess() + ->shouldBeCalledOnce(); + + $this->testSubject->finishProcess(); + } + + public function testAdvanceProcess(): void + { + $defaultSteps = 1; + + $this->firstFeedback->advanceProcess($defaultSteps) + ->shouldBeCalledOnce(); + $this->secondFeedback->advanceProcess($defaultSteps) + ->shouldBeCalledOnce(); + + $this->testSubject->advanceProcess(); + } + + public function testAdvanceProcessWithSteps(): void + { + $steps = 5; + + $this->firstFeedback->advanceProcess($steps) + ->shouldBeCalledOnce(); + $this->secondFeedback->advanceProcess($steps) + ->shouldBeCalledOnce(); + + $this->testSubject->advanceProcess($steps); + } +} diff --git a/tests/ClosureFeedbackTest.php b/tests/ClosureFeedbackTest.php new file mode 100644 index 0000000..1ab3599 --- /dev/null +++ b/tests/ClosureFeedbackTest.php @@ -0,0 +1,255 @@ +expectOutputString(''); + + $testSubject = new ClosureFeedback(); + + $testSubject->exception(new RuntimeException('Something went wrong!')); + $testSubject->error('Something went wrong!'); + $testSubject->warning('Something went wrong!'); + $testSubject->info('Something happened.'); + $testSubject->success('Something went right!'); + $testSubject->startProcess(); + $testSubject->advanceProcess(); + $testSubject->finishProcess(); + } + + public function testException(): void + { + $timesCalled = 0; + $exceptionMessage = 'Something went wrong!'; + $exception = new RuntimeException($exceptionMessage); + + $testSubject = new ClosureFeedback( + null, + null, + null, + null, + static function (\Throwable $actualException) use (&$timesCalled, $exception) { + $timesCalled++; + static::assertEquals($exception, $actualException); + } + ); + + $testSubject->exception($exception); + + static::assertEquals(1, $timesCalled); + } + + public function testExceptionThroughError(): void + { + $timesCalled = 0; + $exceptionMessage = 'Something went wrong!'; + $exception = new RuntimeException($exceptionMessage); + + $testSubject = new ClosureFeedback( + static function (string $actualMessage) use (&$timesCalled, $exceptionMessage) { + $timesCalled++; + static::assertEquals($exceptionMessage, $actualMessage); + } + ); + + $testSubject->exception($exception); + + static::assertEquals(1, $timesCalled); + } + + public function testError(): void + { + $timesCalled = 0; + $message = 'Something went wrong!'; + + $testSubject = new ClosureFeedback( + static function (string $actualMessage) use (&$timesCalled, $message) { + $timesCalled++; + static::assertEquals($message, $actualMessage); + } + ); + + $testSubject->error($message); + + static::assertEquals(1, $timesCalled); + } + + public function testWarning(): void + { + $timesCalled = 0; + $message = 'Something went wrong!'; + + $testSubject = new ClosureFeedback( + null, + static function (string $actualMessage) use (&$timesCalled, $message) { + $timesCalled++; + static::assertEquals($message, $actualMessage); + } + ); + + $testSubject->warning($message); + + static::assertEquals(1, $timesCalled); + } + + public function testInfo(): void + { + $timesCalled = 0; + $message = 'Something happened.'; + + $testSubject = new ClosureFeedback( + null, + null, + static function (string $actualMessage) use (&$timesCalled, $message) { + $timesCalled++; + static::assertEquals($message, $actualMessage); + } + ); + + $testSubject->info($message); + + static::assertEquals(1, $timesCalled); + } + + public function testSuccess(): void + { + $timesCalled = 0; + $message = 'Something went right!'; + + $testSubject = new ClosureFeedback( + null, + null, + null, + static function (string $actualMessage) use (&$timesCalled, $message) { + $timesCalled++; + static::assertEquals($message, $actualMessage); + } + ); + + $testSubject->success($message); + + static::assertEquals(1, $timesCalled); + } + + public function testStartProcess(): void + { + $timesCalled = 0; + $defaultTotal = 0; + + $testSubject = new ClosureFeedback( + null, + null, + null, + null, + null, + static function (int $actualTotal) use (&$timesCalled, $defaultTotal) { + $timesCalled++; + static::assertEquals($defaultTotal, $actualTotal); + } + ); + + $testSubject->startProcess(); + + static::assertEquals(1, $timesCalled); + } + + public function testStartProcessWithTotal(): void + { + $timesCalled = 0; + $total = 25; + + $testSubject = new ClosureFeedback( + null, + null, + null, + null, + null, + static function (int $actualTotal) use (&$timesCalled, $total) { + $timesCalled++; + static::assertEquals($total, $actualTotal); + } + ); + + $testSubject->startProcess($total); + + static::assertEquals(1, $timesCalled); + } + + public function testFinishProcess(): void + { + $timesCalled = 0; + + $testSubject = new ClosureFeedback( + null, + null, + null, + null, + null, + null, + static function () use (&$timesCalled) { + $timesCalled++; + } + ); + + $testSubject->finishProcess(); + + static::assertEquals(1, $timesCalled); + } + + public function testAdvanceProcess(): void + { + $timesCalled = 0; + $defaultSteps = 1; + + $testSubject = new ClosureFeedback( + null, + null, + null, + null, + null, + null, + null, + static function (int $actualSteps) use (&$timesCalled, $defaultSteps) { + $timesCalled++; + static::assertEquals($defaultSteps, $actualSteps); + } + ); + + $testSubject->advanceProcess(); + + static::assertEquals(1, $timesCalled); + } + + public function testAdvanceProcessWithSteps(): void + { + $timesCalled = 0; + $steps = 5; + + $testSubject = new ClosureFeedback( + null, + null, + null, + null, + null, + null, + null, + static function (int $actualSteps) use (&$timesCalled, $steps) { + $timesCalled++; + static::assertEquals($steps, $actualSteps); + } + ); + + $testSubject->advanceProcess($steps); + + static::assertEquals(1, $timesCalled); + } +} diff --git a/tests/LoggerFeedbackTest.php b/tests/LoggerFeedbackTest.php new file mode 100644 index 0000000..053fcdc --- /dev/null +++ b/tests/LoggerFeedbackTest.php @@ -0,0 +1,135 @@ +logger = $this->prophesize(LoggerInterface::class); + + $this->testSubject = new LoggerFeedback( + $this->logger->reveal() + ); + } + + public function testException(): void + { + $exceptionMessage = 'Something went wrong!'; + $exception = new RuntimeException($exceptionMessage); + + $this->logger->error($exceptionMessage) + ->shouldBeCalledOnce(); + + $this->testSubject->exception($exception); + } + + public function testError(): void + { + $message = 'Something went wrong!'; + + $this->logger->error($message) + ->shouldBeCalledOnce(); + + $this->testSubject->error($message); + } + + public function testWarning(): void + { + $message = 'Something went wrong!'; + + $this->logger->warning($message) + ->shouldBeCalledOnce(); + + $this->testSubject->warning($message); + } + + public function testInfo(): void + { + $message = 'Something happened.'; + + $this->logger->info($message) + ->shouldBeCalledOnce(); + + $this->testSubject->info($message); + } + + public function testSuccess(): void + { + $message = 'Something went right!'; + + $this->logger->info($message) + ->shouldBeCalledOnce(); + + $this->testSubject->success($message); + } + + public function testStartProcess(): void + { + $message = 'Process started'; + + $this->logger->info($message) + ->shouldBeCalledOnce(); + + $this->testSubject->startProcess(); + } + + public function testStartProcessWithTotal(): void + { + $total = 25; + $message = 'Process of 25 started'; + + $this->logger->info($message) + ->shouldBeCalledOnce(); + + $this->testSubject->startProcess($total); + } + + public function testFinishProcess(): void + { + $message = 'Process stopped'; + + $this->logger->info($message) + ->shouldBeCalledOnce(); + + $this->testSubject->finishProcess(); + } + + public function testAdvanceProcess(): void + { + $message = 'Process advanced with 1'; + + $this->logger->debug($message) + ->shouldBeCalledOnce(); + + $this->testSubject->advanceProcess(); + } + + public function testAdvanceProcessWithSteps(): void + { + $steps = 5; + $message = 'Process advanced with 5'; + + $this->logger->debug($message) + ->shouldBeCalledOnce(); + + $this->testSubject->advanceProcess($steps); + } +} diff --git a/tests/NoFeedbackTest.php b/tests/NoFeedbackTest.php new file mode 100644 index 0000000..938b0c6 --- /dev/null +++ b/tests/NoFeedbackTest.php @@ -0,0 +1,28 @@ +expectOutputString(''); + + $testSubject = new NoFeedback(); + + $testSubject->exception(new RuntimeException('Something went wrong!')); + $testSubject->error('Something went wrong!'); + $testSubject->warning('Something went wrong!'); + $testSubject->info('Something happened.'); + $testSubject->success('Something went right!'); + $testSubject->startProcess(); + $testSubject->advanceProcess(); + $testSubject->finishProcess(); + } +}