diff --git a/.travis.yml b/.travis.yml index a06eef16..f0f1aee9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,8 +24,7 @@ before_script: script: - ./vendor/bin/phpunit --coverage-clover ./build/logs/clover.xml - - ./vendor/bin/phpcs --standard=PSR2 ./src/ - - ./vendor/bin/phpcs --standard=PSR2 ./test/ + - composer cs after_script: - bash <(curl -s https://codecov.io/bash) diff --git a/app/config.php b/app/config.php index ca78d61a..2e55c50b 100644 --- a/app/config.php +++ b/app/config.php @@ -37,7 +37,23 @@ use PhpSchool\PhpWorkshop\Output\OutputInterface; use PhpSchool\PhpWorkshop\Output\StdOutput; use PhpSchool\PhpWorkshop\Patch; +use PhpSchool\PhpWorkshop\Result\Cgi\GenericFailure as CgiGenericFailure; +use PhpSchool\PhpWorkshop\Result\Cgi\RequestFailure as CgiRequestFailure; +use PhpSchool\PhpWorkshop\Result\Cgi\CgiResult; +use PhpSchool\PhpWorkshop\Result\Cli\CliResult; +use PhpSchool\PhpWorkshop\Result\Cli\GenericFailure as CliGenericFailure; +use PhpSchool\PhpWorkshop\Result\Cli\RequestFailure as CliRequestFailure; +use PhpSchool\PhpWorkshop\Result\Failure; +use PhpSchool\PhpWorkshop\Result\FunctionRequirementsFailure; +use PhpSchool\PhpWorkshop\Result\StdOutFailure; use PhpSchool\PhpWorkshop\ResultAggregator; +use PhpSchool\PhpWorkshop\ResultRenderer\CgiResultRenderer; +use PhpSchool\PhpWorkshop\ResultRenderer\CliResultRenderer; +use PhpSchool\PhpWorkshop\ResultRenderer\FailureRenderer; +use PhpSchool\PhpWorkshop\ResultRenderer\FunctionRequirementsFailureRenderer; +use PhpSchool\PhpWorkshop\ResultRenderer\Cli\RequestFailureRenderer as CliRequestFailureRenderer; +use PhpSchool\PhpWorkshop\ResultRenderer\Cgi\RequestFailureRenderer as CgiRequestFailureRenderer; +use PhpSchool\PhpWorkshop\Utils\RequestRenderer; use PhpSchool\PSX\Factory as PsxFactory; use PhpSchool\PhpWorkshop\WorkshopType; use PhpSchool\PSX\SyntaxHighlighter; @@ -125,7 +141,7 @@ RunnerManager::class => function (ContainerInterface $c) { $manager = new RunnerManager; $manager->addFactory(new CliRunnerFactory($c->get(EventDispatcher::class))); - $manager->addFactory(new CgiRunnerFactory($c->get(EventDispatcher::class))); + $manager->addFactory(new CgiRunnerFactory($c->get(EventDispatcher::class), $c->get(RequestRenderer::class))); $manager->addFactory(new CustomVerifyingRunnerFactory); return $manager; }, @@ -231,6 +247,7 @@ FakerGenerator::class => function () { return FakerFactory::create(); }, + RequestRenderer::class => object(), TerminalInterface::class => factory([TerminalFactory::class, 'fromSystem']), 'menu' => factory(MenuFactory::class), @@ -266,7 +283,26 @@ ResetProgress::class => function (ContainerInterface $c) { return new ResetProgress($c->get(UserStateSerializer::class)); }, - ResultRendererFactory::class => object(), + ResultRendererFactory::class => function (ContainerInterface $c) { + $factory = new ResultRendererFactory; + $factory->registerRenderer(FunctionRequirementsFailure::class, FunctionRequirementsFailureRenderer::class); + $factory->registerRenderer(Failure::class, FailureRenderer::class); + $factory->registerRenderer( + CgiResult::class, + CgiResultRenderer::class, + function (CgiResult $result) use ($c) { + return new CgiResultRenderer($result, $c->get(RequestRenderer::class)); + } + ); + $factory->registerRenderer(CgiGenericFailure::class, FailureRenderer::class); + $factory->registerRenderer(CgiRequestFailure::class, CgiRequestFailureRenderer::class); + + $factory->registerRenderer(CliResult::class, CliResultRenderer::class); + $factory->registerRenderer(CliGenericFailure::class, FailureRenderer::class); + $factory->registerRenderer(CliRequestFailure::class, CliRequestFailureRenderer::class); + + return $factory; + }, ResultsRenderer::class => function (ContainerInterface $c) { return new ResultsRenderer( $c->get('appName'), @@ -277,6 +313,7 @@ $c->get(ResultRendererFactory::class) ); }, + 'coreContributors' => [ '@AydinHassan' => 'Aydin Hassan', '@mikeymike' => 'Michael Woodward', diff --git a/composer.json b/composer.json index ccd52484..a8cd2975 100644 --- a/composer.json +++ b/composer.json @@ -28,13 +28,13 @@ "php-school/cli-menu": "^2.0", "beberlei/assert": "^2.4", "psr/http-message": "^1.0", - "zendframework/zend-diactoros": "^1.1.3", + "zendframework/zend-diactoros": "^1.3.6", "myclabs/php-enum": "^1.4", "nikic/php-parser": "^2.1" }, "require-dev": { "composer/composer": "^1.2", - "phpunit/phpunit": "^5.6", + "phpunit/phpunit": "^5.7.2", "phpunit/phpunit-mock-objects": "^3.3", "squizlabs/php_codesniffer": "^2.4" }, @@ -43,7 +43,8 @@ "PhpSchool\\PhpWorkshop\\": "src" }, "files": [ - "src/Event/functions.php" + "src/Event/functions.php", + "src/functions.php" ] }, "autoload-dev": { @@ -58,8 +59,8 @@ }, "scripts" : { "cs" : [ - "phpcs src --standard=PSR2", - "phpcs test --standard=PSR2" + "phpcs src --standard=PSR2 --encoding=UTF-8", + "phpcs test --standard=PSR2 --encoding=UTF-8" ] } } diff --git a/composer.lock b/composer.lock index f71d5548..b334ba93 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "846264cf45eb6d3d35b0cd9c524ae776", + "content-hash": "bea3db6062cfb2b8d5bbcac5c9cfc1e9", "packages": [ { "name": "aydin-hassan/cli-md-renderer", @@ -51,16 +51,16 @@ }, { "name": "beberlei/assert", - "version": "v2.4", + "version": "v2.6.7", "source": { "type": "git", "url": "https://github.com/beberlei/assert.git", - "reference": "7281b1fd8118b31cb9162c2fb5a4cc6f01d62ed6" + "reference": "2b4c9249a7ceef51402d39bd797aae16cfd8f36f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/beberlei/assert/zipball/7281b1fd8118b31cb9162c2fb5a4cc6f01d62ed6", - "reference": "7281b1fd8118b31cb9162c2fb5a4cc6f01d62ed6", + "url": "https://api.github.com/repos/beberlei/assert/zipball/2b4c9249a7ceef51402d39bd797aae16cfd8f36f", + "reference": "2b4c9249a7ceef51402d39bd797aae16cfd8f36f", "shasum": "" }, "require": { @@ -68,14 +68,10 @@ "php": ">=5.3" }, "require-dev": { + "friendsofphp/php-cs-fixer": "2.0.0-alpha", "phpunit/phpunit": "@stable" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, "autoload": { "psr-0": { "Assert": "lib/" @@ -91,7 +87,13 @@ "authors": [ { "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" + "email": "kontakt@beberlei.de", + "role": "Lead Developer" + }, + { + "name": "Richard Quadling", + "email": "rquadling@gmail.com", + "role": "Collaborator" } ], "description": "Thin assertion library for input validation in business models.", @@ -100,7 +102,7 @@ "assertion", "validation" ], - "time": "2015-08-21T16:50:17+00:00" + "time": "2016-11-14T16:46:13+00:00" }, { "name": "container-interop/container-interop", @@ -131,33 +133,29 @@ }, { "name": "fzaninotto/faker", - "version": "v1.5.0", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/fzaninotto/Faker.git", - "reference": "d0190b156bcca848d401fb80f31f504f37141c8d" + "reference": "44f9a286a04b80c76a4e5fb7aad8bb539b920123" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/d0190b156bcca848d401fb80f31f504f37141c8d", - "reference": "d0190b156bcca848d401fb80f31f504f37141c8d", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/44f9a286a04b80c76a4e5fb7aad8bb539b920123", + "reference": "44f9a286a04b80c76a4e5fb7aad8bb539b920123", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3|^7.0" }, "require-dev": { + "ext-intl": "*", "phpunit/phpunit": "~4.0", "squizlabs/php_codesniffer": "~1.5" }, - "suggest": { - "ext-intl": "*" - }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.5.x-dev" - } + "branch-alias": [] }, "autoload": { "psr-4": { @@ -179,7 +177,7 @@ "faker", "fixtures" ], - "time": "2015-05-29T06:29:14+00:00" + "time": "2016-04-29T12:21:54+00:00" }, { "name": "kevinlebrun/colors.php", @@ -234,16 +232,16 @@ }, { "name": "league/commonmark", - "version": "0.13.0", + "version": "0.13.4", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "a4e93bc4fd1a8ff8f534040c4a07371ea5f4b484" + "reference": "83f8210427fb01f671e272bb8d44b4ed3a94d459" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/a4e93bc4fd1a8ff8f534040c4a07371ea5f4b484", - "reference": "a4e93bc4fd1a8ff8f534040c4a07371ea5f4b484", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/83f8210427fb01f671e272bb8d44b4ed3a94d459", + "reference": "83f8210427fb01f671e272bb8d44b4ed3a94d459", "shasum": "" }, "require": { @@ -254,13 +252,14 @@ "colinodell/commonmark-php": "*" }, "require-dev": { + "cebe/markdown": "~1.0", "erusev/parsedown": "~1.0", - "jgm/commonmark": "0.24", + "jgm/commonmark": "0.25", "michelf/php-markdown": "~1.4", - "mikehaertl/php-shellcommand": "~1.1.0", + "mikehaertl/php-shellcommand": "~1.2.0", "phpunit/phpunit": "~4.3|~5.0", - "scrutinizer/ocular": "^1.1", - "symfony/finder": "~2.3" + "scrutinizer/ocular": "~1.1", + "symfony/finder": "~2.3|~3.0" }, "suggest": { "league/commonmark-extras": "Library of useful extensions including smart punctuation" @@ -287,7 +286,7 @@ { "name": "Colin O'Dell", "email": "colinodell@gmail.com", - "homepage": "http://www.colinodell.com", + "homepage": "https://www.colinodell.com", "role": "Lead Developer" } ], @@ -298,20 +297,20 @@ "markdown", "parser" ], - "time": "2016-01-14T04:29:54+00:00" + "time": "2016-06-14T14:49:29+00:00" }, { "name": "myclabs/php-enum", - "version": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/myclabs/php-enum.git", - "reference": "7c4cd65efc984e80c70522b0aa50545ea51dfff4" + "reference": "e42fa9d2ae5dd660dbd0fb573d94c61e5a0dbb02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/php-enum/zipball/7c4cd65efc984e80c70522b0aa50545ea51dfff4", - "reference": "7c4cd65efc984e80c70522b0aa50545ea51dfff4", + "url": "https://api.github.com/repos/myclabs/php-enum/zipball/e42fa9d2ae5dd660dbd0fb573d94c61e5a0dbb02", + "reference": "e42fa9d2ae5dd660dbd0fb573d94c61e5a0dbb02", "shasum": "" }, "require": { @@ -342,20 +341,20 @@ "keywords": [ "enum" ], - "time": "2015-05-19T05:28:18+00:00" + "time": "2016-10-09T21:43:05+00:00" }, { "name": "nikic/php-parser", - "version": "v2.1.0", + "version": "v2.1.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "47b254ea51f1d6d5dc04b9b299e88346bf2369e3" + "reference": "4dd659edadffdc2143e4753df655d866dbfeedf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/47b254ea51f1d6d5dc04b9b299e88346bf2369e3", - "reference": "47b254ea51f1d6d5dc04b9b299e88346bf2369e3", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4dd659edadffdc2143e4753df655d866dbfeedf0", + "reference": "4dd659edadffdc2143e4753df655d866dbfeedf0", "shasum": "" }, "require": { @@ -393,20 +392,20 @@ "parser", "php" ], - "time": "2016-04-19T13:41:41+00:00" + "time": "2016-09-16T12:04:44+00:00" }, { "name": "php-di/invoker", - "version": "1.0.0", + "version": "1.3.3", "source": { "type": "git", "url": "https://github.com/PHP-DI/Invoker.git", - "reference": "7ea703c62dbb29d64763fa85258826034ce3c97d" + "reference": "1f4ca63b9abc66109e53b255e465d0ddb5c2e3f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/7ea703c62dbb29d64763fa85258826034ce3c97d", - "reference": "7ea703c62dbb29d64763fa85258826034ce3c97d", + "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/1f4ca63b9abc66109e53b255e465d0ddb5c2e3f7", + "reference": "1f4ca63b9abc66109e53b255e465d0ddb5c2e3f7", "shasum": "" }, "require": { @@ -436,39 +435,45 @@ "invoke", "invoker" ], - "time": "2015-04-24T10:18:34+00:00" + "time": "2016-07-14T13:09:58+00:00" }, { "name": "php-di/php-di", - "version": "5.0.0", + "version": "5.4.0", "source": { "type": "git", "url": "https://github.com/PHP-DI/PHP-DI.git", - "reference": "c3bbc0d334b888ec8baa75f95b4900ee17b18fa7" + "reference": "e348393488fa909e4bc0707ba5c9c44cd602a1cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/c3bbc0d334b888ec8baa75f95b4900ee17b18fa7", - "reference": "c3bbc0d334b888ec8baa75f95b4900ee17b18fa7", + "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/e348393488fa909e4bc0707ba5c9c44cd602a1cb", + "reference": "e348393488fa909e4bc0707ba5c9c44cd602a1cb", "shasum": "" }, "require": { "container-interop/container-interop": "~1.0", - "php": ">=5.4.0", - "php-di/invoker": "~1.0", - "php-di/phpdoc-reader": "~2.0" + "php": ">=5.5.0", + "php-di/invoker": "^1.3.2", + "php-di/phpdoc-reader": "^2.0.1" + }, + "provide": { + "container-interop/container-interop-implementation": "^1.0" + }, + "replace": { + "mnapoli/php-di": "*" }, "require-dev": { "doctrine/annotations": "~1.2", - "doctrine/cache": "~1.0", - "mnapoli/phpunit-easymock": "~0.1.4", - "ocramius/proxy-manager": "~1.0", + "doctrine/cache": "~1.4", + "mnapoli/phpunit-easymock": "~0.2.0", + "ocramius/proxy-manager": "~1.0|~2.0", "phpunit/phpunit": "~4.5" }, "suggest": { "doctrine/annotations": "Install it if you want to use annotations (version ~1.2)", - "doctrine/cache": "Install it if you want to use the cache (version ~1.0)", - "ocramius/proxy-manager": "Install it if you want to use lazy injection (version ~1.0)" + "doctrine/cache": "Install it if you want to use the cache (version ~1.4)", + "ocramius/proxy-manager": "Install it if you want to use lazy injection (version ~1.0 or ~2.0)" }, "type": "library", "autoload": { @@ -490,20 +495,20 @@ "dependency injection", "di" ], - "time": "2015-06-10T06:16:52+00:00" + "time": "2016-08-23T20:18:00+00:00" }, { "name": "php-di/phpdoc-reader", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/PHP-DI/PhpDocReader.git", - "reference": "21dce5e29f640d655e7b4583ecfb7d166127a5da" + "reference": "83f5ead159defccfa8e7092e5b6c1c533b326d68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-DI/PhpDocReader/zipball/21dce5e29f640d655e7b4583ecfb7d166127a5da", - "reference": "21dce5e29f640d655e7b4583ecfb7d166127a5da", + "url": "https://api.github.com/repos/PHP-DI/PhpDocReader/zipball/83f5ead159defccfa8e7092e5b6c1c533b326d68", + "reference": "83f5ead159defccfa8e7092e5b6c1c533b326d68", "shasum": "" }, "require": { @@ -527,7 +532,7 @@ "phpdoc", "reflection" ], - "time": "2015-06-01T14:23:20+00:00" + "time": "2015-11-29T10:34:25+00:00" }, { "name": "php-school/cli-menu", @@ -585,16 +590,16 @@ }, { "name": "php-school/psx", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/php-school/psx.git", - "reference": "762074f23541b20577bb01862988238a4256f4d8" + "reference": "4063d9ac62e057897a41be11ec979424ed93035c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-school/psx/zipball/762074f23541b20577bb01862988238a4256f4d8", - "reference": "762074f23541b20577bb01862988238a4256f4d8", + "url": "https://api.github.com/repos/php-school/psx/zipball/4063d9ac62e057897a41be11ec979424ed93035c", + "reference": "4063d9ac62e057897a41be11ec979424ed93035c", "shasum": "" }, "require": { @@ -623,20 +628,20 @@ } ], "description": "PHP CLI Syntax Highlighter", - "time": "2015-12-04T17:38:50+00:00" + "time": "2016-03-30T22:07:50+00:00" }, { "name": "psr/http-message", - "version": "1.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298" + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", - "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", "shasum": "" }, "require": { @@ -664,6 +669,7 @@ } ], "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ "http", "http-message", @@ -672,132 +678,136 @@ "request", "response" ], - "time": "2015-05-04T20:22:00+00:00" + "time": "2016-08-06T14:39:51+00:00" }, { "name": "symfony/filesystem", - "version": "v2.5.0", - "target-dir": "Symfony/Component/Filesystem", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "98e831eac836a0a5911626ce82684155f21d0e4d" + "reference": "8d4cf7561a5b17e5eb7a02b80d0b8f014a3796d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/98e831eac836a0a5911626ce82684155f21d0e4d", - "reference": "98e831eac836a0a5911626ce82684155f21d0e4d", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/8d4cf7561a5b17e5eb7a02b80d0b8f014a3796d4", + "reference": "8d4cf7561a5b17e5eb7a02b80d0b8f014a3796d4", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.5.9" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "3.2-dev" } }, "autoload": { - "psr-0": { + "psr-4": { "Symfony\\Component\\Filesystem\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Filesystem Component", - "homepage": "http://symfony.com", - "time": "2014-04-16T10:36:21+00:00" + "homepage": "https://symfony.com", + "time": "2016-11-24T00:46:43+00:00" }, { "name": "symfony/process", - "version": "v2.3.0", - "target-dir": "Symfony/Component/Process", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "bb721b29e033594512f8b08386e13593b0faaf0f" + "reference": "02ea84847aad71be7e32056408bb19f3a616cdd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/bb721b29e033594512f8b08386e13593b0faaf0f", - "reference": "bb721b29e033594512f8b08386e13593b0faaf0f", + "url": "https://api.github.com/repos/symfony/process/zipball/02ea84847aad71be7e32056408bb19f3a616cdd3", + "reference": "02ea84847aad71be7e32056408bb19f3a616cdd3", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.5.9" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "3.2-dev" } }, "autoload": { - "psr-0": { + "psr-4": { "Symfony\\Component\\Process\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Process Component", - "homepage": "http://symfony.com", - "time": "2013-05-06T20:03:44+00:00" + "homepage": "https://symfony.com", + "time": "2016-11-24T10:40:28+00:00" }, { "name": "zendframework/zend-diactoros", - "version": "1.1.3", + "version": "1.3.7", "source": { "type": "git", "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "e2f5c12916c74da384058d0dfbc7fbc0b03d1181" + "reference": "969ff423d3f201da3ff718a5831bb999bb0669b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/e2f5c12916c74da384058d0dfbc7fbc0b03d1181", - "reference": "e2f5c12916c74da384058d0dfbc7fbc0b03d1181", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/969ff423d3f201da3ff718a5831bb999bb0669b0", + "reference": "969ff423d3f201da3ff718a5831bb999bb0669b0", "shasum": "" }, "require": { - "php": ">=5.4", + "php": "^5.4 || ^7.0", "psr/http-message": "~1.0" }, "provide": { "psr/http-message-implementation": "~1.0.0" }, "require-dev": { - "phpunit/phpunit": "~4.6", + "phpunit/phpunit": "^4.6 || ^5.5", "squizlabs/php_codesniffer": "^2.3.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev", - "dev-develop": "1.1-dev" + "dev-master": "1.3-dev", + "dev-develop": "1.4-dev" } }, "autoload": { @@ -816,28 +826,31 @@ "psr", "psr-7" ], - "time": "2015-08-10T20:04:20+00:00" + "time": "2016-10-11T13:25:21+00:00" } ], "packages-dev": [ { "name": "composer/ca-bundle", - "version": "1.0.0", + "version": "1.0.6", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "4059c02b474668bde9d115731615eaf073691ee1" + "reference": "a795611394b3c05164fd0eb291b492b39339cba4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/4059c02b474668bde9d115731615eaf073691ee1", - "reference": "4059c02b474668bde9d115731615eaf073691ee1", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/a795611394b3c05164fd0eb291b492b39339cba4", + "reference": "a795611394b3c05164fd0eb291b492b39339cba4", "shasum": "" }, "require": { + "ext-openssl": "*", + "ext-pcre": "*", "php": "^5.3.2 || ^7.0" }, "require-dev": { + "psr/log": "^1.0", "symfony/process": "^2.5 || ^3.0" }, "suggest": { @@ -873,20 +886,20 @@ "ssl", "tls" ], - "time": "2016-04-11T15:47:46+00:00" + "time": "2016-11-02T18:11:27+00:00" }, { "name": "composer/composer", - "version": "1.2.0", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "b49a006748a460f8dae6500ec80ed021501ce969" + "reference": "e7f19286a7e7f940950c9069a0f549d483c30ba7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/b49a006748a460f8dae6500ec80ed021501ce969", - "reference": "b49a006748a460f8dae6500ec80ed021501ce969", + "url": "https://api.github.com/repos/composer/composer/zipball/e7f19286a7e7f940950c9069a0f549d483c30ba7", + "reference": "e7f19286a7e7f940950c9069a0f549d483c30ba7", "shasum": "" }, "require": { @@ -950,33 +963,33 @@ "dependency", "package" ], - "time": "2016-07-18T23:28:52+00:00" + "time": "2016-12-01T13:33:53+00:00" }, { "name": "composer/semver", - "version": "1.0.0", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "d0e1ccc6d44ab318b758d709e19176037da6b1ba" + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/d0e1ccc6d44ab318b758d709e19176037da6b1ba", - "reference": "d0e1ccc6d44ab318b758d709e19176037da6b1ba", + "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573", + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^5.3.2 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.5", - "phpunit/phpunit-mock-objects": "~2.3" + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.1-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -989,10 +1002,6 @@ "MIT" ], "authors": [ - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com" - }, { "name": "Nils Adermann", "email": "naderman@naderman.de", @@ -1002,6 +1011,11 @@ "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], "description": "Semver library that offers utilities, version constraint parsing and validation.", @@ -1011,34 +1025,33 @@ "validation", "versioning" ], - "time": "2015-09-21T09:42:36+00:00" + "time": "2016-08-30T16:08:34+00:00" }, { "name": "composer/spdx-licenses", - "version": "1.0.0", + "version": "1.1.5", "source": { "type": "git", "url": "https://github.com/composer/spdx-licenses.git", - "reference": "abf7dfc7da7d7dc66c147a91b6e927099512292a" + "reference": "96c6a07b05b716e89a44529d060bc7f5c263cb13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/abf7dfc7da7d7dc66c147a91b6e927099512292a", - "reference": "abf7dfc7da7d7dc66c147a91b6e927099512292a", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/96c6a07b05b716e89a44529d060bc7f5c263cb13", + "reference": "96c6a07b05b716e89a44529d060bc7f5c263cb13", "shasum": "" }, "require": { - "justinrainbow/json-schema": "~1.4", - "php": ">=5.3.2" + "php": "^5.3.2 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.5", - "phpunit/phpunit-mock-objects": "~2.3" + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -1051,10 +1064,6 @@ "MIT" ], "authors": [ - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com" - }, { "name": "Nils Adermann", "email": "naderman@naderman.de", @@ -1064,6 +1073,11 @@ "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], "description": "SPDX licenses list and validation library.", @@ -1072,75 +1086,20 @@ "spdx", "validator" ], - "time": "2015-07-15T17:38:14+00:00" - }, - { - "name": "dflydev/markdown", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/dflydev/dflydev-markdown.git", - "reference": "76501a808522dbe40a5a71d272bd08d54cbae03d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-markdown/zipball/76501a808522dbe40a5a71d272bd08d54cbae03d", - "reference": "76501a808522dbe40a5a71d272bd08d54cbae03d", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "type": "library", - "autoload": { - "psr-0": { - "dflydev\\markdown": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "New BSD License" - ], - "authors": [ - { - "name": "Dragonfly Development Inc.", - "email": "info@dflydev.com", - "homepage": "http://dflydev.com" - }, - { - "name": "Beau Simensen", - "email": "beau@dflydev.com", - "homepage": "http://beausimensen.com" - }, - { - "name": "Michel Fortin", - "homepage": "http://michelf.com" - }, - { - "name": "John Gruber", - "homepage": "http://daringfireball.net" - } - ], - "description": "PHP Markdown & Extra", - "homepage": "http://github.com/dflydev/dflydev-markdown", - "keywords": [ - "markdown" - ], - "abandoned": "michelf/php-markdown", - "time": "2012-01-02T23:11:32+00:00" + "time": "2016-09-28T07:17:45+00:00" }, { "name": "doctrine/instantiator", - "version": "1.0.4", + "version": "1.0.5", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119" + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119", - "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", "shasum": "" }, "require": { @@ -1151,7 +1110,7 @@ "ext-pdo": "*", "ext-phar": "*", "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "2.0.*@ALPHA" + "squizlabs/php_codesniffer": "~2.0" }, "type": "library", "extra": { @@ -1160,8 +1119,8 @@ } }, "autoload": { - "psr-0": { - "Doctrine\\Instantiator\\": "src" + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1181,29 +1140,29 @@ "constructor", "instantiate" ], - "time": "2014-10-13T12:58:55+00:00" + "time": "2015-06-14T21:17:01+00:00" }, { "name": "justinrainbow/json-schema", - "version": "v1.6.0", + "version": "2.0.5", "source": { "type": "git", "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "f9e27c3e202faf14fd581ef41355d83bb4b7eb7d" + "reference": "6b2a33e6a768f96bdc2ead5600af0822eed17d67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/f9e27c3e202faf14fd581ef41355d83bb4b7eb7d", - "reference": "f9e27c3e202faf14fd581ef41355d83bb4b7eb7d", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/6b2a33e6a768f96bdc2ead5600af0822eed17d67", + "reference": "6b2a33e6a768f96bdc2ead5600af0822eed17d67", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": ">=5.3.3" }, "require-dev": { - "json-schema/json-schema-test-suite": "1.1.0", + "json-schema/json-schema-test-suite": "1.2.0", "phpdocumentor/phpdocumentor": "~2", - "phpunit/phpunit": "~3.7" + "phpunit/phpunit": "^4.8.22" }, "bin": [ "bin/validate-json" @@ -1211,7 +1170,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -1221,7 +1180,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { @@ -1247,20 +1206,20 @@ "json", "schema" ], - "time": "2016-01-06T14:37:04+00:00" + "time": "2016-06-02T10:59:52+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.3.0", + "version": "1.5.5", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "96fbdc07635989c35c5a1912379f4c4b2ab15fd5" + "reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/96fbdc07635989c35c5a1912379f4c4b2ab15fd5", - "reference": "96fbdc07635989c35c5a1912379f4c4b2ab15fd5", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/399c1f9781e222f6eb6cc238796f5200d1b7f108", + "reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108", "shasum": "" }, "require": { @@ -1289,38 +1248,138 @@ "object", "object graph" ], - "time": "2015-03-21T22:40:23+00:00" + "time": "2016-10-31T17:19:45+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2015-12-27T11:43:31+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "2.0.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "66ae84e9d7c8ea85c979cb65977bd8e608baf0c5" + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/66ae84e9d7c8ea85c979cb65977bd8e608baf0c5", - "reference": "66ae84e9d7c8ea85c979cb65977bd8e608baf0c5", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", "shasum": "" }, "require": { - "dflydev/markdown": "1.0.*", - "php": ">=5.3.3" + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/type-resolver": "^0.2.0", + "webmozart/assert": "^1.0" }, "require-dev": { - "phpunit/phpunit": "3.7.*@stable" + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2016-09-30T07:12:33+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.2.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "psr-0": { - "phpDocumentor": [ + "psr-4": { + "phpDocumentor\\Reflection\\": [ "src/" ] } @@ -1332,36 +1391,40 @@ "authors": [ { "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" + "email": "me@mikevanriel.com" } ], - "time": "2013-08-07T11:04:22+00:00" + "time": "2016-11-25T06:54:22+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.3.1", + "version": "v1.6.2", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "9ca52329bcdd1500de24427542577ebf3fc2f1c9" + "reference": "6c52c2722f8460122f96f86346600e1077ce22cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/9ca52329bcdd1500de24427542577ebf3fc2f1c9", - "reference": "9ca52329bcdd1500de24427542577ebf3fc2f1c9", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/6c52c2722f8460122f96f86346600e1077ce22cb", + "reference": "6c52c2722f8460122f96f86346600e1077ce22cb", "shasum": "" }, "require": { - "doctrine/instantiator": "~1.0,>=1.0.2", - "phpdocumentor/reflection-docblock": "~2.0" + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "sebastian/comparator": "^1.1", + "sebastian/recursion-context": "^1.0|^2.0" }, "require-dev": { - "phpspec/phpspec": "~2.0" + "phpspec/phpspec": "^2.0", + "phpunit/phpunit": "^4.8 || ^5.6.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.6.x-dev" } }, "autoload": { @@ -1385,7 +1448,7 @@ } ], "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "http://phpspec.org", + "homepage": "https://github.com/phpspec/prophecy", "keywords": [ "Double", "Dummy", @@ -1394,20 +1457,20 @@ "spy", "stub" ], - "time": "2014-11-17T16:23:49+00:00" + "time": "2016-11-21T14:58:47+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "4.0.1", + "version": "4.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3" + "reference": "903fd6318d0a90b4770a009ff73e4a4e9c437929" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5f3f7e736d6319d5f1fc402aff8b026da26709a3", - "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/903fd6318d0a90b4770a009ff73e4a4e9c437929", + "reference": "903fd6318d0a90b4770a009ff73e4a4e9c437929", "shasum": "" }, "require": { @@ -1457,20 +1520,20 @@ "testing", "xunit" ], - "time": "2016-07-26T14:39:29+00:00" + "time": "2016-11-28T16:00:31+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.0", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb" + "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a923bb15680d0089e2316f7a4af8f437046e96bb", - "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", "shasum": "" }, "require": { @@ -1504,20 +1567,20 @@ "filesystem", "iterator" ], - "time": "2015-04-02T05:19:05+00:00" + "time": "2016-10-03T07:40:28+00:00" }, { "name": "phpunit/php-text-template", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", - "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", "shasum": "" }, "require": { @@ -1526,20 +1589,17 @@ "type": "library", "autoload": { "classmap": [ - "Text/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -1548,25 +1608,28 @@ "keywords": [ "template" ], - "time": "2014-01-30T17:20:04+00:00" + "time": "2015-06-21T13:50:34+00:00" }, { "name": "phpunit/php-timer", - "version": "1.0.6", + "version": "1.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "83fe1bdc5d47658b727595c14da140da92b3d66d" + "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/83fe1bdc5d47658b727595c14da140da92b3d66d", - "reference": "83fe1bdc5d47658b727595c14da140da92b3d66d", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", + "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", "shasum": "" }, "require": { "php": ">=5.3.3" }, + "require-dev": { + "phpunit/phpunit": "~4|~5" + }, "type": "library", "autoload": { "classmap": [ @@ -1589,20 +1652,20 @@ "keywords": [ "timer" ], - "time": "2015-06-13T07:35:30+00:00" + "time": "2016-05-12T18:03:57+00:00" }, { "name": "phpunit/php-token-stream", - "version": "1.4.2", + "version": "1.4.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "db63be1159c81df649cd0260e30249a586d4129e" + "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/db63be1159c81df649cd0260e30249a586d4129e", - "reference": "db63be1159c81df649cd0260e30249a586d4129e", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b", + "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b", "shasum": "" }, "require": { @@ -1638,20 +1701,20 @@ "keywords": [ "tokenizer" ], - "time": "2015-06-12T07:34:24+00:00" + "time": "2016-11-15T14:06:22+00:00" }, { "name": "phpunit/phpunit", - "version": "5.6.0", + "version": "5.7.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a7f2db56518e50ab92f28f739810dfad2f223b6b" + "reference": "336aff0ac52e306c98e7455bc3e8d7b0bf777a5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a7f2db56518e50ab92f28f739810dfad2f223b6b", - "reference": "a7f2db56518e50ab92f28f739810dfad2f223b6b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/336aff0ac52e306c98e7455bc3e8d7b0bf777a5e", + "reference": "336aff0ac52e306c98e7455bc3e8d7b0bf777a5e", "shasum": "" }, "require": { @@ -1663,17 +1726,17 @@ "myclabs/deep-copy": "~1.3", "php": "^5.6 || ^7.0", "phpspec/prophecy": "^1.3.1", - "phpunit/php-code-coverage": "^4.0.1", + "phpunit/php-code-coverage": "^4.0.3", "phpunit/php-file-iterator": "~1.4", "phpunit/php-text-template": "~1.2", "phpunit/php-timer": "^1.0.6", "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "~1.1", + "sebastian/comparator": "~1.2.2", "sebastian/diff": "~1.2", - "sebastian/environment": "^1.3 || ^2.0", - "sebastian/exporter": "~1.2", + "sebastian/environment": "^1.3.4 || ^2.0", + "sebastian/exporter": "~2.0", "sebastian/global-state": "~1.0", - "sebastian/object-enumerator": "~1.0", + "sebastian/object-enumerator": "~2.0", "sebastian/resource-operations": "~1.0", "sebastian/version": "~1.0|~2.0", "symfony/yaml": "~2.1|~3.0" @@ -1694,7 +1757,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.6.x-dev" + "dev-master": "5.7.x-dev" } }, "autoload": { @@ -1720,27 +1783,27 @@ "testing", "xunit" ], - "time": "2016-10-06T15:20:39+00:00" + "time": "2016-12-03T08:33:00+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "3.3.0", + "version": "3.4.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "7462c19bdb9814f6e6bdeb5cad3eb3ce72c6e0da" + "reference": "90a08f5deed5f7ac35463c161f2e8fa0e5652faf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/7462c19bdb9814f6e6bdeb5cad3eb3ce72c6e0da", - "reference": "7462c19bdb9814f6e6bdeb5cad3eb3ce72c6e0da", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/90a08f5deed5f7ac35463c161f2e8fa0e5652faf", + "reference": "90a08f5deed5f7ac35463c161f2e8fa0e5652faf", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.6 || ^7.0", "phpunit/php-text-template": "^1.2", - "sebastian/exporter": "^1.2" + "sebastian/exporter": "^1.2 || ^2.0" }, "conflict": { "phpunit/phpunit": "<5.4.0" @@ -1779,26 +1842,34 @@ "mock", "xunit" ], - "time": "2016-09-27T03:17:40+00:00" + "time": "2016-11-27T07:52:03+00:00" }, { "name": "psr/log", - "version": "1.0.0", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", "shasum": "" }, + "require": { + "php": ">=5.3.0" + }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { - "psr-0": { - "Psr\\Log\\": "" + "psr-4": { + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1812,12 +1883,13 @@ } ], "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ "log", "psr", "psr-3" ], - "time": "2012-12-21T11:40:51+00:00" + "time": "2016-10-10T12:19:37+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1866,30 +1938,30 @@ }, { "name": "sebastian/comparator", - "version": "1.1.0", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "c484a80f97573ab934e37826dba0135a3301b26a" + "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c484a80f97573ab934e37826dba0135a3301b26a", - "reference": "c484a80f97573ab934e37826dba0135a3301b26a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a1ed12e8b2409076ab22e3897126211ff8b1f7f", + "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f", "shasum": "" }, "require": { "php": ">=5.3.3", - "sebastian/diff": "~1.1", - "sebastian/exporter": "~1.0" + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2 || ~2.0" }, "require-dev": { - "phpunit/phpunit": "~4.1" + "phpunit/phpunit": "~4.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -1926,32 +1998,32 @@ "compare", "equality" ], - "time": "2014-11-16T21:32:38+00:00" + "time": "2016-11-19T09:18:40+00:00" }, { "name": "sebastian/diff", - "version": "1.2.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "5843509fed39dee4b356a306401e9dd1a931fec7" + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/5843509fed39dee4b356a306401e9dd1a931fec7", - "reference": "5843509fed39dee4b356a306401e9dd1a931fec7", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "phpunit/phpunit": "~4.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.4-dev" } }, "autoload": { @@ -1974,36 +2046,36 @@ } ], "description": "Diff implementation", - "homepage": "http://www.github.com/sebastianbergmann/diff", + "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ "diff" ], - "time": "2014-08-15T10:29:00+00:00" + "time": "2015-12-08T07:14:41+00:00" }, { "name": "sebastian/environment", - "version": "1.3.2", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44" + "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6324c907ce7a52478eeeaede764f48733ef5ae44", - "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -2028,33 +2100,34 @@ "environment", "hhvm" ], - "time": "2015-08-03T06:14:51+00:00" + "time": "2016-11-26T07:53:53+00:00" }, { "name": "sebastian/exporter", - "version": "1.2.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "84839970d05254c73cde183a721c7af13aede943" + "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943", - "reference": "84839970d05254c73cde183a721c7af13aede943", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", + "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", "shasum": "" }, "require": { "php": ">=5.3.3", - "sebastian/recursion-context": "~1.0" + "sebastian/recursion-context": "~2.0" }, "require-dev": { + "ext-mbstring": "*", "phpunit/phpunit": "~4.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -2094,20 +2167,20 @@ "export", "exporter" ], - "time": "2015-01-27T07:23:06+00:00" + "time": "2016-11-19T08:54:04+00:00" }, { "name": "sebastian/global-state", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01" + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01", - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", "shasum": "" }, "require": { @@ -2145,25 +2218,25 @@ "keywords": [ "global state" ], - "time": "2014-10-06T09:23:50+00:00" + "time": "2015-10-12T03:26:01+00:00" }, { "name": "sebastian/object-enumerator", - "version": "1.0.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "d4ca2fb70344987502567bc50081c03e6192fb26" + "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/d4ca2fb70344987502567bc50081c03e6192fb26", - "reference": "d4ca2fb70344987502567bc50081c03e6192fb26", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", + "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", "shasum": "" }, "require": { "php": ">=5.6", - "sebastian/recursion-context": "~1.0" + "sebastian/recursion-context": "~2.0" }, "require-dev": { "phpunit/phpunit": "~5" @@ -2171,7 +2244,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -2191,20 +2264,20 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2016-01-28T13:25:10+00:00" + "time": "2016-11-19T07:35:10+00:00" }, { "name": "sebastian/recursion-context", - "version": "1.0.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "3989662bbb30a29d20d9faa04a846af79b276252" + "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/3989662bbb30a29d20d9faa04a846af79b276252", - "reference": "3989662bbb30a29d20d9faa04a846af79b276252", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", + "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", "shasum": "" }, "require": { @@ -2216,7 +2289,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -2244,7 +2317,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2015-01-24T09:48:32+00:00" + "time": "2016-11-19T07:33:16+00:00" }, { "name": "sebastian/resource-operations", @@ -2290,19 +2363,27 @@ }, { "name": "sebastian/version", - "version": "1.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "16b021aed448b654ae05846e394e057e9a6f04cb" + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/16b021aed448b654ae05846e394e057e9a6f04cb", - "reference": "16b021aed448b654ae05846e394e057e9a6f04cb", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", "shasum": "" }, + "require": { + "php": ">=5.6" + }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -2321,20 +2402,20 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2013-01-05T14:27:32+00:00" + "time": "2016-10-03T07:35:21+00:00" }, { "name": "seld/cli-prompt", - "version": "1.0.0", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/Seldaek/cli-prompt.git", - "reference": "fe114c7a6ac5cb0ce76932ae4017024d9842a49c" + "reference": "8cbe10923cae5bcd7c5a713f6703fc4727c8c1b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/cli-prompt/zipball/fe114c7a6ac5cb0ce76932ae4017024d9842a49c", - "reference": "fe114c7a6ac5cb0ce76932ae4017024d9842a49c", + "url": "https://api.github.com/repos/Seldaek/cli-prompt/zipball/8cbe10923cae5bcd7c5a713f6703fc4727c8c1b4", + "reference": "8cbe10923cae5bcd7c5a713f6703fc4727c8c1b4", "shasum": "" }, "require": { @@ -2369,20 +2450,20 @@ "input", "prompt" ], - "time": "2015-04-30T20:24:49+00:00" + "time": "2016-04-18T09:31:41+00:00" }, { "name": "seld/jsonlint", - "version": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "66834d3e3566bb5798db7294619388786ae99394" + "reference": "19495c181d6d53a0a13414154e52817e3b504189" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/66834d3e3566bb5798db7294619388786ae99394", - "reference": "66834d3e3566bb5798db7294619388786ae99394", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/19495c181d6d53a0a13414154e52817e3b504189", + "reference": "19495c181d6d53a0a13414154e52817e3b504189", "shasum": "" }, "require": { @@ -2415,20 +2496,20 @@ "parser", "validator" ], - "time": "2015-11-21T02:21:41+00:00" + "time": "2016-11-14T17:59:58+00:00" }, { "name": "seld/phar-utils", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "336bb5ee20de511f3c1a164222fcfd194afcab3a" + "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/336bb5ee20de511f3c1a164222fcfd194afcab3a", - "reference": "336bb5ee20de511f3c1a164222fcfd194afcab3a", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/7009b5139491975ef6486545a39f3e6dad5ac30a", + "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a", "shasum": "" }, "require": { @@ -2459,27 +2540,31 @@ "keywords": [ "phra" ], - "time": "2015-05-01T12:45:48+00:00" + "time": "2015-10-13T18:44:15+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "2.4.0", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "32a879f4f35019d78d568db2885d7779ca084a33" + "reference": "9b324f3a1132459a7274a0ace2e1b766ba80930f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/32a879f4f35019d78d568db2885d7779ca084a33", - "reference": "32a879f4f35019d78d568db2885d7779ca084a33", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9b324f3a1132459a7274a0ace2e1b766ba80930f", + "reference": "9b324f3a1132459a7274a0ace2e1b766ba80930f", "shasum": "" }, "require": { + "ext-simplexml": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", "php": ">=5.1.2" }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, "bin": [ "scripts/phpcs", "scripts/phpcbf" @@ -2487,7 +2572,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -2533,163 +2618,347 @@ "phpcs", "standards" ], - "time": "2015-11-23T21:30:59+00:00" + "time": "2016-11-30T04:02:31+00:00" }, { "name": "symfony/console", - "version": "v2.5.0", - "target-dir": "Symfony/Component/Console", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "ef4ca73b0b3a10cbac653d3ca482d0cdd4502b2c" + "reference": "09d0fd33560e3573185a2ea17614e37ba38716c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/ef4ca73b0b3a10cbac653d3ca482d0cdd4502b2c", - "reference": "ef4ca73b0b3a10cbac653d3ca482d0cdd4502b2c", + "url": "https://api.github.com/repos/symfony/console/zipball/09d0fd33560e3573185a2ea17614e37ba38716c5", + "reference": "09d0fd33560e3573185a2ea17614e37ba38716c5", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.5.9", + "symfony/debug": "~2.8|~3.0", + "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.1" + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/filesystem": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0" }, "suggest": { "psr/log": "For using the console logger", - "symfony/event-dispatcher": "" + "symfony/event-dispatcher": "", + "symfony/filesystem": "", + "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "3.2-dev" } }, "autoload": { - "psr-0": { + "psr-4": { "Symfony\\Component\\Console\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, { "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2016-11-16T22:18:16+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "9f923e68d524a3095c5a2ae5fc7220c7cbc12231" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/9f923e68d524a3095c5a2ae5fc7220c7cbc12231", + "reference": "9f923e68d524a3095c5a2ae5fc7220c7cbc12231", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/class-loader": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Console Component", - "homepage": "http://symfony.com", - "time": "2014-05-22T08:54:24+00:00" + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2016-11-16T22:18:16+00:00" }, { "name": "symfony/finder", - "version": "v2.2.0", - "target-dir": "Symfony/Component/Finder", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "2feb8d33876f64801bd1b14bdce829b314e2c418" + "reference": "4263e35a1e342a0f195c9349c0dee38148f8a14f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/2feb8d33876f64801bd1b14bdce829b314e2c418", - "reference": "2feb8d33876f64801bd1b14bdce829b314e2c418", + "url": "https://api.github.com/repos/symfony/finder/zipball/4263e35a1e342a0f195c9349c0dee38148f8a14f", + "reference": "4263e35a1e342a0f195c9349c0dee38148f8a14f", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.5.9" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "3.2-dev" } }, "autoload": { - "psr-0": { + "psr-4": { "Symfony\\Component\\Finder\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Finder Component", - "homepage": "http://symfony.com", - "time": "2013-02-28T14:06:36+00:00" + "homepage": "https://symfony.com", + "time": "2016-11-03T08:11:03+00:00" }, { - "name": "symfony/yaml", - "version": "v2.1.0", - "target-dir": "Symfony/Component/Yaml", + "name": "symfony/polyfill-mbstring", + "version": "v1.3.0", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "f18e004fc975707bb4695df1dbbe9b0d8c8b7715" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/f18e004fc975707bb4695df1dbbe9b0d8c8b7715", - "reference": "f18e004fc975707bb4695df1dbbe9b0d8c8b7715", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", "shasum": "" }, "require": { "php": ">=5.3.3" }, + "suggest": { + "ext-mbstring": "For best performance" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "1.3-dev" } }, "autoload": { - "psr-0": { - "Symfony\\Component\\Yaml": "" - } + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, { "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14T01:06:16+00:00" + }, + { + "name": "symfony/yaml", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "f2300ba8fbb002c028710b92e1906e7457410693" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/f2300ba8fbb002c028710b92e1906e7457410693", + "reference": "f2300ba8fbb002c028710b92e1906e7457410693", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/console": "~2.8|~3.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Yaml Component", - "homepage": "http://symfony.com", - "time": "2012-08-22T13:48:41+00:00" + "homepage": "https://symfony.com", + "time": "2016-11-18T21:17:59+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2016-11-23T20:04:58+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, - "prefer-lowest": true, + "prefer-lowest": false, "platform": { "php": ">=5.6", "ext-pdo_sqlite": "*" diff --git a/src/Check/ComposerCheck.php b/src/Check/ComposerCheck.php index d6b016ae..e958f6a3 100644 --- a/src/Check/ComposerCheck.php +++ b/src/Check/ComposerCheck.php @@ -52,6 +52,10 @@ public function check(ExerciseInterface $exercise, Input $input) if (!file_exists(sprintf('%s/composer.lock', dirname($input->getArgument('program'))))) { return new Failure($this->getName(), 'No composer.lock file found'); } + + if (!file_exists(sprintf('%s/vendor', dirname($input->getArgument('program'))))) { + return new Failure($this->getName(), 'No vendor folder found'); + } $lockFile = new LockFileParser(sprintf('%s/composer.lock', dirname($input->getArgument('program')))); $missingPackages = array_filter($exercise->getRequiredPackages(), function ($package) use ($lockFile) { diff --git a/src/Exercise/CliExercise.php b/src/Exercise/CliExercise.php index c7586f54..4757f36c 100644 --- a/src/Exercise/CliExercise.php +++ b/src/Exercise/CliExercise.php @@ -10,10 +10,10 @@ interface CliExercise extends ProvidesSolution { /** - * This method should return an array of strings which will be passed to the student's solution - * as command line arguments. + * This method should return an array of an array of strings. + * Each set of arguments will be passed to the students solution as command line arguments. * - * @return string[] An array of string arguments. + * @return string[][] An array of string arguments. */ public function getArgs(); } diff --git a/src/ExerciseRunner/CgiRunner.php b/src/ExerciseRunner/CgiRunner.php index cae02576..4a3cbea6 100644 --- a/src/ExerciseRunner/CgiRunner.php +++ b/src/ExerciseRunner/CgiRunner.php @@ -14,11 +14,12 @@ use PhpSchool\PhpWorkshop\Exercise\CgiExercise; use PhpSchool\PhpWorkshop\Input\Input; use PhpSchool\PhpWorkshop\Output\OutputInterface; -use PhpSchool\PhpWorkshop\Result\CgiOutRequestFailure; -use PhpSchool\PhpWorkshop\Result\CgiOutResult; -use PhpSchool\PhpWorkshop\Result\Failure; +use PhpSchool\PhpWorkshop\Result\Cgi\CgiResult; +use PhpSchool\PhpWorkshop\Result\Cgi\RequestFailure; +use PhpSchool\PhpWorkshop\Result\Cgi\GenericFailure; +use PhpSchool\PhpWorkshop\Result\Cgi\Success; use PhpSchool\PhpWorkshop\Result\ResultInterface; -use PhpSchool\PhpWorkshop\Result\Success; +use PhpSchool\PhpWorkshop\Utils\RequestRenderer; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use RuntimeException; @@ -44,6 +45,11 @@ class CgiRunner implements ExerciseRunnerInterface */ private $eventDispatcher; + /** + * @var RequestRenderer + */ + private $requestRenderer; + /** * Requires the exercise instance and an event dispatcher. This runner requires the `php-cgi` binary to * be available. It will check for it's existence in the system's $PATH variable or the same @@ -51,10 +57,13 @@ class CgiRunner implements ExerciseRunnerInterface * * @param CgiExercise $exercise The exercise to be invoked. * @param EventDispatcher $eventDispatcher The event dispatcher. - * @throws RuntimeException If the `php-cgi` binary cannot be found. + * @param RequestRenderer $requestRenderer */ - public function __construct(CgiExercise $exercise, EventDispatcher $eventDispatcher) - { + public function __construct( + CgiExercise $exercise, + EventDispatcher $eventDispatcher, + RequestRenderer $requestRenderer + ) { if (strpos(PHP_OS, 'WIN') !== false) { // Check if in path. 2> nul > nul equivalent to 2>&1 /dev/null $silence = (PHP_OS == 'CYGWIN' ? '> /dev/null 2>&1' : '2> nul > nul'); @@ -79,6 +88,7 @@ public function __construct(CgiExercise $exercise, EventDispatcher $eventDispatc } $this->eventDispatcher = $eventDispatcher; $this->exercise = $exercise; + $this->requestRenderer = $requestRenderer; } /** @@ -129,7 +139,7 @@ private function checkRequest(RequestInterface $request, $fileName) $userResponse = $this->executePhpFile($fileName, $event->getRequest(), 'student'); } catch (CodeExecutionException $e) { $this->eventDispatcher->dispatch(new Event('cgi.verify.student-execute.fail', ['exception' => $e])); - return Failure::fromNameAndCodeExecutionFailure($this->getName(), $e); + return GenericFailure::fromRequestAndCodeExecutionFailure($request, $e); } $solutionBody = (string) $solutionResponse->getBody(); @@ -138,10 +148,10 @@ private function checkRequest(RequestInterface $request, $fileName) $userHeaders = $this->getHeaders($userResponse); if ($solutionBody !== $userBody || $solutionHeaders !== $userHeaders) { - return new CgiOutRequestFailure($request, $solutionBody, $userBody, $solutionHeaders, $userHeaders); + return new RequestFailure($request, $solutionBody, $userBody, $solutionHeaders, $userHeaders); } - return new Success($this->getName()); + return new Success($request); } /** @@ -232,13 +242,12 @@ private function getProcess($fileName, RequestInterface $request) * * cgi.verify.student-execute.fail (if the student's solution fails to execute) * * @param Input $input The command line arguments passed to the command. - * @return ResultInterface The result of the check. + * @return CgiResult The result of the check. */ public function verify(Input $input) { $this->eventDispatcher->dispatch(new ExerciseRunnerEvent('cgi.verify.start', $this->exercise, $input)); - $result = new CgiOutResult( - $this->getName(), + $result = new CgiResult( array_map( function (RequestInterface $request) use ($input) { return $this->checkRequest($request, $input->getArgument('program')); @@ -279,7 +288,7 @@ public function run(Input $input, OutputInterface $output) $output->writeTitle("Request"); $output->emptyLine(); - $output->writeRequest($request); + $output->write($this->requestRenderer->renderRequest($request)); $output->writeTitle("Output"); $output->emptyLine(); diff --git a/src/ExerciseRunner/CliRunner.php b/src/ExerciseRunner/CliRunner.php index 3bb760e9..54ff4356 100644 --- a/src/ExerciseRunner/CliRunner.php +++ b/src/ExerciseRunner/CliRunner.php @@ -14,10 +14,12 @@ use PhpSchool\PhpWorkshop\Exercise\CliExercise; use PhpSchool\PhpWorkshop\Input\Input; use PhpSchool\PhpWorkshop\Output\OutputInterface; -use PhpSchool\PhpWorkshop\Result\Failure; +use PhpSchool\PhpWorkshop\Result\Cli\RequestFailure; +use PhpSchool\PhpWorkshop\Result\Cli\CliResult; +use PhpSchool\PhpWorkshop\Result\Cli\GenericFailure; +use PhpSchool\PhpWorkshop\Result\Cli\Success; use PhpSchool\PhpWorkshop\Result\ResultInterface; use PhpSchool\PhpWorkshop\Result\StdOutFailure; -use PhpSchool\PhpWorkshop\Result\Success; use PhpSchool\PhpWorkshop\Utils\ArrayObject; use Symfony\Component\Process\Process; @@ -124,24 +126,49 @@ private function getPhpProcess($fileName, ArrayObject $args) * * cli.verify.student-execute.fail (if the student's solution fails to execute) * * @param Input $input The command line arguments passed to the command. - * @return ResultInterface The result of the check. + * @return CliResult The result of the check. */ public function verify(Input $input) { $this->eventDispatcher->dispatch(new ExerciseRunnerEvent('cli.verify.start', $this->exercise, $input)); - $result = $this->doVerify($input); + $result = new CliResult( + array_map( + function (array $args) use ($input) { + return $this->doVerify($args, $input); + }, + $this->preserveOldArgFormat($this->exercise->getArgs()) + ) + ); $this->eventDispatcher->dispatch(new ExerciseRunnerEvent('cli.verify.finish', $this->exercise, $input)); return $result; } /** + * BC - getArgs only returned 1 set of args in v1 instead of multiple sets of args in v2 + * + * @param array $args + * @return array + */ + private function preserveOldArgFormat(array $args) + { + if (isset($args[0]) && !is_array($args[0])) { + $args = [$args]; + } elseif (empty($args)) { + $args = [[]]; + } + + return $args; + } + + /** + * @param array $args * @param Input $input * @return ResultInterface */ - private function doVerify(Input $input) + private function doVerify(array $args, Input $input) { //arrays are not pass-by-ref - $args = new ArrayObject($this->exercise->getArgs()); + $args = new ArrayObject($args); try { $event = $this->eventDispatcher->dispatch(new CliExecuteEvent('cli.verify.reference-execute.pre', $args)); @@ -160,13 +187,13 @@ private function doVerify(Input $input) $userOutput = $this->executePhpFile($input->getArgument('program'), $event->getArgs(), 'student'); } catch (CodeExecutionException $e) { $this->eventDispatcher->dispatch(new Event('cli.verify.student-execute.fail', ['exception' => $e])); - return Failure::fromNameAndCodeExecutionFailure($this->getName(), $e); + return GenericFailure::fromArgsAndCodeExecutionFailure($args, $e); } if ($solutionOutput === $userOutput) { - return new Success($this->getName()); + return new Success($args); } - return StdOutFailure::fromNameAndOutput($this->getName(), $solutionOutput, $userOutput); + return RequestFailure::fromArgsAndOutput($args, $solutionOutput, $userOutput); } /** @@ -188,32 +215,42 @@ private function doVerify(Input $input) public function run(Input $input, OutputInterface $output) { $this->eventDispatcher->dispatch(new ExerciseRunnerEvent('cli.run.start', $this->exercise, $input)); - /** @var CliExecuteEvent $event */ - $event = $this->eventDispatcher->dispatch( - new CliExecuteEvent('cli.run.student-execute.pre', new ArrayObject($this->exercise->getArgs())) - ); + $success = true; + foreach ($this->preserveOldArgFormat($this->exercise->getArgs()) as $i => $args) { + /** @var CliExecuteEvent $event */ + $event = $this->eventDispatcher->dispatch( + new CliExecuteEvent('cli.run.student-execute.pre', new ArrayObject($args)) + ); + + $args = $event->getArgs(); - $args = $event->getArgs(); + if (count($args)) { + $glue = max(array_map('strlen', $args->getArrayCopy())) > 30 ? "\n" : ', '; - if (count($args)) { - $glue = max(array_map('strlen', $args->getArrayCopy())) > 30 ? "\n" : ', '; + $output->writeTitle('Arguments'); + $output->write(implode($glue, $args->getArrayCopy())); + $output->emptyLine(); + } - $output->writeTitle('Arguments'); - $output->write(implode($glue, $args->getArrayCopy())); + $output->writeTitle("Output"); + $process = $this->getPhpProcess($input->getArgument('program'), $args); + $process->start(); + $this->eventDispatcher->dispatch( + new CliExecuteEvent('cli.run.student.executing', $args, ['output' => $output]) + ); + $process->wait(function ($outputType, $outputBuffer) use ($output) { + $output->write($outputBuffer); + }); $output->emptyLine(); - } - $output->writeTitle("Output"); - $process = $this->getPhpProcess($input->getArgument('program'), $args); - $process->start(); - $this->eventDispatcher->dispatch( - new CliExecuteEvent('cli.run.student.executing', $args, ['output' => $output]) - ); - $process->wait(function ($outputType, $outputBuffer) use ($output) { - $output->writeLine($outputBuffer); - }); + if (!$process->isSuccessful()) { + $success = false; + } + + $output->lineBreak(); + } $this->eventDispatcher->dispatch(new ExerciseRunnerEvent('cli.run.finish', $this->exercise, $input)); - return $process->isSuccessful(); + return $success; } } diff --git a/src/ExerciseRunner/Factory/CgiRunnerFactory.php b/src/ExerciseRunner/Factory/CgiRunnerFactory.php index b9f02040..93cc7a33 100644 --- a/src/ExerciseRunner/Factory/CgiRunnerFactory.php +++ b/src/ExerciseRunner/Factory/CgiRunnerFactory.php @@ -9,6 +9,7 @@ use PhpSchool\PhpWorkshop\Exercise\ExerciseType; use PhpSchool\PhpWorkshop\ExerciseRunner\CgiRunner; use PhpSchool\PhpWorkshop\ExerciseRunner\ExerciseRunnerInterface; +use PhpSchool\PhpWorkshop\Utils\RequestRenderer; /** * @author Aydin Hassan @@ -25,12 +26,18 @@ class CgiRunnerFactory implements ExerciseRunnerFactoryInterface */ private $eventDispatcher; + /** + * @var RequestRenderer + */ + private $requestRenderer; + /** * @param EventDispatcher $eventDispatcher */ - public function __construct(EventDispatcher $eventDispatcher) + public function __construct(EventDispatcher $eventDispatcher, RequestRenderer $requestRenderer) { $this->eventDispatcher = $eventDispatcher; + $this->requestRenderer = $requestRenderer; } /** @@ -62,6 +69,6 @@ public function configureInput(CommandDefinition $commandDefinition) */ public function create(ExerciseInterface $exercise) { - return new CgiRunner($exercise, $this->eventDispatcher); + return new CgiRunner($exercise, $this->eventDispatcher, $this->requestRenderer); } } diff --git a/src/Factory/ResultRendererFactory.php b/src/Factory/ResultRendererFactory.php index 22ec4798..d1592e48 100644 --- a/src/Factory/ResultRendererFactory.php +++ b/src/Factory/ResultRendererFactory.php @@ -3,15 +3,7 @@ namespace PhpSchool\PhpWorkshop\Factory; use PhpSchool\PhpWorkshop\Exception\InvalidArgumentException; -use PhpSchool\PhpWorkshop\Result\CgiOutResult; -use PhpSchool\PhpWorkshop\Result\Failure; -use PhpSchool\PhpWorkshop\Result\FunctionRequirementsFailure; use PhpSchool\PhpWorkshop\Result\ResultInterface; -use PhpSchool\PhpWorkshop\Result\StdOutFailure; -use PhpSchool\PhpWorkshop\ResultRenderer\CgiOutResultRenderer; -use PhpSchool\PhpWorkshop\ResultRenderer\FailureRenderer; -use PhpSchool\PhpWorkshop\ResultRenderer\FunctionRequirementsFailureRenderer; -use PhpSchool\PhpWorkshop\ResultRenderer\OutputFailureRenderer; use PhpSchool\PhpWorkshop\ResultRenderer\ResultRendererInterface; /** @@ -24,18 +16,19 @@ class ResultRendererFactory /** * @var array */ - private $mappings = [ - StdOutFailure::class => OutputFailureRenderer::class, - CgiOutResult::class => CgiOutResultRenderer::class, - FunctionRequirementsFailure::class => FunctionRequirementsFailureRenderer::class, - Failure::class => FailureRenderer::class, - ]; + private $mappings = []; + + /** + * @var array + */ + private $factories = []; /** * @param string $resultClass * @param string $rendererClass + * @param callable $factory */ - public function registerRenderer($resultClass, $rendererClass) + public function registerRenderer($resultClass, $rendererClass, callable $factory = null) { if (!$this->isImplementationNameOfClass($resultClass, ResultInterface::class)) { throw new InvalidArgumentException; @@ -46,6 +39,10 @@ public function registerRenderer($resultClass, $rendererClass) } $this->mappings[$resultClass] = $rendererClass; + + $this->factories[$rendererClass] = $factory ?: function (ResultInterface $result) use ($rendererClass) { + return new $rendererClass($result); + }; } /** @@ -59,7 +56,23 @@ public function create(ResultInterface $result) throw new \RuntimeException(sprintf('No renderer found for "%s"', $class)); } - return new $this->mappings[$class]($result); + $class = $this->mappings[$class]; + $factory = $this->factories[$class]; + + $renderer = $factory($result); + + if (!$renderer instanceof $class) { + throw new \RuntimeException( + sprintf( + 'Renderer Factory for "%s" produced "%s" instead of expected "%s"', + $class, + is_object($renderer) ? get_class($renderer) : gettype($renderer), + $class + ) + ); + } + + return $renderer; } protected function isImplementationNameOfClass($implementationName, $className) diff --git a/src/Output/OutputInterface.php b/src/Output/OutputInterface.php index 95381fe0..f0074692 100644 --- a/src/Output/OutputInterface.php +++ b/src/Output/OutputInterface.php @@ -3,7 +3,6 @@ namespace PhpSchool\PhpWorkshop\Output; use Psr\Http\Message\RequestInterface; -use Zend\Diactoros\Request; /** * Interface StdOutput @@ -60,11 +59,4 @@ public function lineBreak(); * @param string $title */ public function writeTitle($title); - - /** - * Write a PSR-7 request. - * - * @param RequestInterface $request - */ - public function writeRequest(RequestInterface $request); } diff --git a/src/Output/StdOutput.php b/src/Output/StdOutput.php index 1846d871..9bf67e71 100644 --- a/src/Output/StdOutput.php +++ b/src/Output/StdOutput.php @@ -106,44 +106,4 @@ public function lineBreak() { echo $this->color->__invoke(str_repeat('─', $this->terminal->getWidth()))->yellow(); } - - /** - * Write a PSR-7 request. - * - * @param RequestInterface $request - */ - public function writeRequest(RequestInterface $request) - { - echo sprintf("URL: %s\n", $request->getUri()); - echo sprintf("METHOD: %s\n", $request->getMethod()); - - if ($request->getHeaders()) { - echo 'HEADERS:'; - } - - $indent = false; - foreach ($request->getHeaders() as $name => $values) { - if ($indent) { - echo str_repeat(' ', 9); - } - - echo sprintf(" %s: %s\n", $name, implode(', ', $values)); - $indent = true; - } - - if ($body = (string) $request->getBody()) { - echo "\nBODY:"; - - switch ($request->getHeaderLine('Content-Type')) { - case 'application/json': - echo json_encode(json_decode($body, true), JSON_PRETTY_PRINT); - break; - default: - echo $body; - break; - } - - $this->emptyLine(); - } - } } diff --git a/src/Result/Cgi/CgiResult.php b/src/Result/Cgi/CgiResult.php new file mode 100644 index 00000000..6aad4dc6 --- /dev/null +++ b/src/Result/Cgi/CgiResult.php @@ -0,0 +1,86 @@ + + */ +class CgiResult implements ResultGroupInterface, IteratorAggregate +{ + /** + * @var string + */ + private $name = 'CGI Program Runner'; + + /** + * @var array + */ + private $results = []; + + /** + * @param array $requestResults An array of results representing each request. + */ + public function __construct(array $requestResults = []) + { + foreach ($requestResults as $request) { + $this->add($request); + } + } + + /** + * Add a new result. + * + * @param ResultInterface $result + */ + public function add(ResultInterface $result) + { + $this->results[] = $result; + } + + /** + * Get the name of the check that this result was produced from, most likely the CGI Runner. + * + * @return string + */ + public function getCheckName() + { + return $this->name; + } + + /** + * @return bool + */ + public function isSuccessful() + { + return count( + array_filter($this->results, function ($result) { + return $result instanceof FailureInterface; + }) + ) === 0; + } + + /** + * @return ResultInterface + */ + public function getResults() + { + return $this->results; + } + + /** + * Get an iterator in order to `foreach` the results. + * + * @return ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->results); + } +} diff --git a/src/Result/Cgi/FailureInterface.php b/src/Result/Cgi/FailureInterface.php new file mode 100644 index 00000000..b30a9487 --- /dev/null +++ b/src/Result/Cgi/FailureInterface.php @@ -0,0 +1,14 @@ + + */ +interface FailureInterface extends ResultInterface +{ + +} diff --git a/src/Result/Cgi/GenericFailure.php b/src/Result/Cgi/GenericFailure.php new file mode 100644 index 00000000..6fd87400 --- /dev/null +++ b/src/Result/Cgi/GenericFailure.php @@ -0,0 +1,68 @@ + + */ +class GenericFailure extends Failure implements FailureInterface +{ + + /** + * @var RequestInterface + */ + private $request; + + /** + * @var string + */ + private static $name = 'CGI Program Runner'; + + /** + * @param RequestInterface $request The request that caused the failure. + * @param null $reason + */ + public function __construct(RequestInterface $request, $reason = null) + { + $this->request = $request; + parent::__construct(static::$name, $reason); + } + + /** + * Named constructor, for added code legibility. + * + * @param RequestInterface $request The request that caused the failure. + * @param string|null $reason The reason (if any) of the failure. + * @return static The result. + */ + public static function fromRequestAndReason(RequestInterface $request, $reason) + { + return new static($request, $reason); + } + + /** + * Static constructor to create from a `PhpSchool\PhpWorkshop\Exception\CodeExecutionException` exception. + * + * @param RequestInterface $request The request that caused the failure. + * @param CodeExecutionException $e The exception. + * @return static The result. + */ + public static function fromRequestAndCodeExecutionFailure(RequestInterface $request, CodeExecutionException $e) + { + return new static($request, $e->getMessage()); + } + + /** + * Get the request that caused the failure. + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } +} diff --git a/src/Result/CgiOutRequestFailure.php b/src/Result/Cgi/RequestFailure.php similarity index 90% rename from src/Result/CgiOutRequestFailure.php rename to src/Result/Cgi/RequestFailure.php index a067eb8b..25993c99 100644 --- a/src/Result/CgiOutRequestFailure.php +++ b/src/Result/Cgi/RequestFailure.php @@ -1,6 +1,6 @@ */ -class CgiOutRequestFailure implements FailureInterface +class RequestFailure implements FailureInterface { /** * @var RequestInterface @@ -39,7 +39,7 @@ class CgiOutRequestFailure implements FailureInterface private $actualHeaders; /** - * @param RequestInterface $request + * @param RequestInterface $request The request that caused the failure. * @param string $expectedOutput * @param string $actualOutput * @param array $expectedHeaders @@ -60,7 +60,7 @@ public function __construct( } /** - * Get the request object associated with this failure. + * Get the request that caused the failure. * * @return RequestInterface */ @@ -140,6 +140,8 @@ public function getActualHeaders() } /** + * Get the name of the check that this result was produced from. + * * @return string */ public function getCheckName() diff --git a/src/Result/Cgi/ResultInterface.php b/src/Result/Cgi/ResultInterface.php new file mode 100644 index 00000000..f6b65634 --- /dev/null +++ b/src/Result/Cgi/ResultInterface.php @@ -0,0 +1,18 @@ + + */ +interface ResultInterface extends \PhpSchool\PhpWorkshop\Result\ResultInterface +{ + /** + * Get the request associated with this result. + * + * @return RequestInterface + */ + public function getRequest(); +} diff --git a/src/Result/Cgi/Success.php b/src/Result/Cgi/Success.php new file mode 100644 index 00000000..d59ec645 --- /dev/null +++ b/src/Result/Cgi/Success.php @@ -0,0 +1,52 @@ + + */ +class Success implements SuccessInterface +{ + /** + * @var RequestInterface + */ + private $request; + + /** + * @var string + */ + private $name = 'CGI Program Runner'; + + /** + * @param RequestInterface $request The request for this success. + */ + public function __construct(RequestInterface $request) + { + $this->request = $request; + } + + /** + * Get the name of the check that this result was produced from. + * + * @return string + */ + public function getCheckName() + { + return $this->name; + } + + /** + * Get the request for this success. + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } +} diff --git a/src/Result/Cgi/SuccessInterface.php b/src/Result/Cgi/SuccessInterface.php new file mode 100644 index 00000000..e5e56f33 --- /dev/null +++ b/src/Result/Cgi/SuccessInterface.php @@ -0,0 +1,14 @@ + + */ +interface SuccessInterface extends ResultInterface +{ + +} diff --git a/src/Result/CgiOutResult.php b/src/Result/CgiOutResult.php deleted file mode 100644 index 0ae86e60..00000000 --- a/src/Result/CgiOutResult.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ -class CgiOutResult extends ResultAggregator implements ResultInterface -{ - /** - * @var string - */ - private $name; - - /** - * @param string $name The name of the check that produced this result. - * @param array $requestResults An array of results representing each request. - */ - public function __construct($name, array $requestResults) - { - $this->name = $name; - foreach ($requestResults as $request) { - $this->add($request); - } - } - - - /** - * Get the name of the check that this result was produced from, most likely the CGI Runner. - * - * @return string - */ - public function getCheckName() - { - return $this->name; - } -} diff --git a/src/Result/Cli/CliResult.php b/src/Result/Cli/CliResult.php new file mode 100644 index 00000000..950c6851 --- /dev/null +++ b/src/Result/Cli/CliResult.php @@ -0,0 +1,86 @@ + + */ +class CliResult implements ResultGroupInterface, IteratorAggregate +{ + /** + * @var string + */ + private $name = 'CLI Program Runner'; + + /** + * @var array + */ + private $results = []; + + /** + * @param array $requestResults An array of results representing each request. + */ + public function __construct(array $requestResults = []) + { + foreach ($requestResults as $request) { + $this->add($request); + } + } + + /** + * Add a new result. + * + * @param ResultInterface $result + */ + public function add(ResultInterface $result) + { + $this->results[] = $result; + } + + /** + * Get the name of the check that this result was produced from, most likely the CGI Runner. + * + * @return string + */ + public function getCheckName() + { + return $this->name; + } + + /** + * @return bool + */ + public function isSuccessful() + { + return count( + array_filter($this->results, function ($result) { + return $result instanceof FailureInterface; + }) + ) === 0; + } + + /** + * @return ResultInterface + */ + public function getResults() + { + return $this->results; + } + + /** + * Get an iterator in order to `foreach` the results. + * + * @return ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->results); + } +} diff --git a/src/Result/Cli/FailureInterface.php b/src/Result/Cli/FailureInterface.php new file mode 100644 index 00000000..ba052901 --- /dev/null +++ b/src/Result/Cli/FailureInterface.php @@ -0,0 +1,14 @@ + + */ +interface FailureInterface extends ResultInterface +{ + +} diff --git a/src/Result/Cli/GenericFailure.php b/src/Result/Cli/GenericFailure.php new file mode 100644 index 00000000..f2f96652 --- /dev/null +++ b/src/Result/Cli/GenericFailure.php @@ -0,0 +1,66 @@ + + */ +class GenericFailure extends Failure implements FailureInterface +{ + + /** + * @var ArrayObject + */ + private $args; + + /** + * @var string + */ + private static $name = 'CLI Program Runner'; + + /** + * @param ArrayObject $args The arguments that caused the failure. + * @param null $reason The reason (if any) of the failure. + */ + public function __construct(ArrayObject $args, $reason = null) + { + $this->args = $args; + parent::__construct(static::$name, $reason); + } + + /** + * Named constructor, for added code legibility. + * + * @param ArrayObject $args The arguments that caused the failure. + * @param string|null $reason The reason (if any) of the failure. + * @return static The result. + */ + public static function fromArgsAndReason(ArrayObject $args, $reason) + { + return new static($args, $reason); + } + + /** + * Static constructor to create from a `PhpSchool\PhpWorkshop\Exception\CodeExecutionException` exception. + * + * @param ArrayObject $args The arguments that caused the failure. + * @param CodeExecutionException $e The exception. + * @return static The result. + */ + public static function fromArgsAndCodeExecutionFailure(ArrayObject $args, CodeExecutionException $e) + { + return new static($args, $e->getMessage()); + } + + /** + * @return ArrayObject + */ + public function getArgs() + { + return $this->args; + } +} diff --git a/src/Result/StdOutFailure.php b/src/Result/Cli/RequestFailure.php similarity index 52% rename from src/Result/StdOutFailure.php rename to src/Result/Cli/RequestFailure.php index e54b1b01..3aa78045 100644 --- a/src/Result/StdOutFailure.php +++ b/src/Result/Cli/RequestFailure.php @@ -1,22 +1,22 @@ */ -class StdOutFailure implements FailureInterface +class RequestFailure implements FailureInterface { /** - * @var string + * @var ArrayObject */ - private $name; + private $args; /** * @var string @@ -29,13 +29,13 @@ class StdOutFailure implements FailureInterface private $actualOutput; /** - * @param string $name The name of the check that produced this result. + * @param ArrayObject $args The arguments that caused the failure. * @param string $expectedOutput The expected output. * @param string $actualOutput The actual output. */ - public function __construct($name, $expectedOutput, $actualOutput) + public function __construct(ArrayObject $args, $expectedOutput, $actualOutput) { - $this->name = $name; + $this->args = $args; $this->expectedOutput = $expectedOutput; $this->actualOutput = $actualOutput; } @@ -43,37 +43,24 @@ public function __construct($name, $expectedOutput, $actualOutput) /** * Named constructor, for added code legibility. * - * @param string $name The name of the check that produced this result. - * @param string $expectedOutput The expected output. + * @param ArrayObject $args The arguments that caused the failure. + * @param string $expectedOutput The expected result. * @param string $actualOutput The actual output. * @return static The result. */ - public static function fromNameAndOutput($name, $expectedOutput, $actualOutput) + public static function fromArgsAndOutput(ArrayObject $args, $expectedOutput, $actualOutput) { - return new static($name, $expectedOutput, $actualOutput); + return new static($args, $expectedOutput, $actualOutput); } /** - * Static constructor to create from an instance of `PhpSchool\PhpWorkshop\Check\CheckInterface`. + * Get the arguments that caused the failure. * - * @param CheckInterface $check The check instance. - * @param string $expectedOutput The expected output. - * @param string $actualOutput The actual output. - * @return static The result. + * @return ArrayObject */ - public static function fromCheckAndOutput(CheckInterface $check, $expectedOutput, $actualOutput) + public function getArgs() { - return new static($check->getName(), $expectedOutput, $actualOutput); - } - - /** - * Get the name of the check that this result was produced from. - * - * @return string - */ - public function getCheckName() - { - return $this->name; + return $this->args; } /** @@ -95,4 +82,14 @@ public function getActualOutput() { return $this->actualOutput; } + + /** + * Get the name of the check that this result was produced from. + * + * @return string + */ + public function getCheckName() + { + return 'Request Failure'; + } } diff --git a/src/Result/Cli/ResultInterface.php b/src/Result/Cli/ResultInterface.php new file mode 100644 index 00000000..e0d7f247 --- /dev/null +++ b/src/Result/Cli/ResultInterface.php @@ -0,0 +1,18 @@ + + */ +interface ResultInterface extends \PhpSchool\PhpWorkshop\Result\ResultInterface +{ + /** + * Get the arguments associated with this result. + * + * @return ArrayObject + */ + public function getArgs(); +} diff --git a/src/Result/Cli/Success.php b/src/Result/Cli/Success.php new file mode 100644 index 00000000..f4d9bd50 --- /dev/null +++ b/src/Result/Cli/Success.php @@ -0,0 +1,51 @@ + + */ +class Success implements SuccessInterface +{ + /** + * @var ArrayObject + */ + private $args; + + /** + * @var string + */ + private $name = 'CLI Program Runner'; + + /** + * @param ArrayObject $args The arguments for this success. + */ + public function __construct(ArrayObject $args) + { + $this->args = $args; + } + + /** + * Get the name of the check that this result was produced from. + * + * @return string + */ + public function getCheckName() + { + return $this->name; + } + + /** + * Get the arguments for this success. + * + * @return ArrayObject + */ + public function getArgs() + { + return $this->args; + } +} diff --git a/src/Result/Cli/SuccessInterface.php b/src/Result/Cli/SuccessInterface.php new file mode 100644 index 00000000..f4c80d5d --- /dev/null +++ b/src/Result/Cli/SuccessInterface.php @@ -0,0 +1,14 @@ + + */ +interface SuccessInterface extends ResultInterface +{ + +} diff --git a/src/Result/ResultGroupInterface.php b/src/Result/ResultGroupInterface.php new file mode 100644 index 00000000..7211ab5d --- /dev/null +++ b/src/Result/ResultGroupInterface.php @@ -0,0 +1,19 @@ + + */ +interface ResultGroupInterface extends ResultInterface +{ + /** + * @return bool + */ + public function isSuccessful(); + + /** + * @return ResultInterface[] + */ + public function getResults(); +} diff --git a/src/ResultAggregator.php b/src/ResultAggregator.php index a85fa568..b310dae6 100644 --- a/src/ResultAggregator.php +++ b/src/ResultAggregator.php @@ -6,6 +6,7 @@ use Countable; use IteratorAggregate; use PhpSchool\PhpWorkshop\Result\FailureInterface; +use PhpSchool\PhpWorkshop\Result\ResultGroupInterface; use PhpSchool\PhpWorkshop\Result\ResultInterface; /** @@ -42,7 +43,7 @@ public function isSuccessful() { return count( array_filter($this->results, function ($result) { - if ($result instanceof self) { + if ($result instanceof ResultGroupInterface) { return !$result->isSuccessful(); } return $result instanceof FailureInterface; diff --git a/src/ResultRenderer/Cgi/RequestFailureRenderer.php b/src/ResultRenderer/Cgi/RequestFailureRenderer.php new file mode 100644 index 00000000..4d61ac56 --- /dev/null +++ b/src/ResultRenderer/Cgi/RequestFailureRenderer.php @@ -0,0 +1,87 @@ +result = $result; + } + + /** + * Print the actual and expected output. + * + * @param ResultsRenderer $renderer + * @return string + */ + public function render(ResultsRenderer $renderer) + { + $output = ''; + if ($this->result->headersDifferent()) { + $output .= sprintf( + "%s %s\n%s %s", + $renderer->style('YOUR HEADERS:', ['bold', 'yellow']), + $this->headers($this->result->getActualHeaders(), $renderer), + $renderer->style('EXPECTED HEADERS:', ['bold', 'yellow']), + $this->headers($this->result->getExpectedHeaders(), $renderer, false) + ); + } + + if ($this->result->bodyDifferent()) { + if ($this->result->headersAndBodyDifferent()) { + $output .= "\n" . $renderer->style('- - - - - - - - -', ['default', 'bold']) . "\n\n"; + } + + $output .= sprintf( + "%s %s\n\n%s %s\n", + $renderer->style('YOUR OUTPUT:', ['bold', 'yellow']), + $renderer->style(sprintf('"%s"', $this->result->getActualOutput()), 'red'), + $renderer->style('EXPECTED OUTPUT:', ['bold', 'yellow']), + $renderer->style(sprintf('"%s"', $this->result->getExpectedOutput()), 'green') + ); + } + + return $output; + } + + /** + * @param array $headers + * @param ResultsRenderer $renderer + * @param bool $actual + * @return string + */ + private function headers(array $headers, ResultsRenderer $renderer, $actual = true) + { + $indent = false; + $output = ''; + foreach ($headers as $name => $value) { + if ($indent) { + $output .= str_repeat(' ', 19); + } + + $output .= $renderer->style(sprintf('%s: %s', $name, $value), $actual ? 'red' : 'green') . "\n"; + $indent = true; + } + + return $output; + } +} diff --git a/src/ResultRenderer/CgiOutResultRenderer.php b/src/ResultRenderer/CgiOutResultRenderer.php deleted file mode 100644 index 178a3793..00000000 --- a/src/ResultRenderer/CgiOutResultRenderer.php +++ /dev/null @@ -1,105 +0,0 @@ -result = $result; - } - - /** - * Render the details of each failed request including the mismatching headers and body. - * - * @param ResultsRenderer $renderer - * @return string - */ - public function render(ResultsRenderer $renderer) - { - $results = array_filter($this->result->getIterator()->getArrayCopy(), function (ResultInterface $result) { - return $result instanceof FailureInterface; - }); - - $output = ''; - foreach ($results as $key => $request) { - $output .= "\n"; - $output .= $renderer->style(sprintf("Request %02d\n\n", $key + 1), ['bold', 'underline', 'green']); - - if (!$request instanceof CgiOutRequestFailure) { - $output .= $renderer->renderResult($request); - $output .= $renderer->lineBreak(); - continue; - } - - if ($request->headersDifferent()) { - $output .= sprintf( - " %s %s\n %s %s\n", - $renderer->style("ACTUAL HEADERS:", ['bold', 'yellow']), - $this->headers($request->getActualHeaders(), $renderer), - $renderer->style("EXPECTED HEADERS:", ['bold', 'yellow']), - $this->headers($request->getExpectedHeaders(), $renderer, false) - ); - } - - if ($request->bodyDifferent()) { - if ($request->headersAndBodyDifferent()) { - $output .= $renderer->style(" * * * * * * * * *\n\n", ['green', 'bold']); - } - - $output .= sprintf( - " %s %s\n\n %s %s\n", - $renderer->style("ACTUAL CONTENT:", ['bold', 'yellow']), - $renderer->style(sprintf('"%s"', $request->getActualOutput()), 'red'), - $renderer->style("EXPECTED CONTENT:", ['bold', 'yellow']), - $renderer->style(sprintf('"%s"', $request->getExpectedOutput()), 'default') - ); - } - - $output .= $renderer->lineBreak(); - } - - return $output . "\n"; - } - - /** - * @param array $headers - * @param ResultsRenderer $renderer - * @param bool $actual - * @return string - */ - private function headers(array $headers, ResultsRenderer $renderer, $actual = true) - { - $indent = false; - $output = ''; - foreach ($headers as $name => $value) { - if ($indent) { - $output .= str_repeat(' ', 21); - } - - $output .= $renderer->style(sprintf("%s: %s", $name, $value), $actual ? 'red' : 'default') . "\n"; - $indent = true; - } - - return $output; - } -} diff --git a/src/ResultRenderer/CgiResultRenderer.php b/src/ResultRenderer/CgiResultRenderer.php new file mode 100644 index 00000000..af21a555 --- /dev/null +++ b/src/ResultRenderer/CgiResultRenderer.php @@ -0,0 +1,69 @@ +result = $result; + $this->requestRenderer = $requestRenderer; + } + + /** + * Render the details of each failed request including the mismatching headers and body. + * + * @param ResultsRenderer $renderer + * @return string + */ + public function render(ResultsRenderer $renderer) + { + $results = array_filter($this->result->getResults(), function (ResultInterface $result) { + return $result instanceof FailureInterface; + }); + + $output = ''; + if (count($results)) { + $output .= $renderer->center("Some requests to your solution produced incorrect output!\n"); + } + + foreach ($results as $key => $request) { + $output .= $renderer->lineBreak(); + $output .= "\n"; + $output .= $renderer->style(sprintf('Request %d', $key + 1), ['bold', 'underline', 'blue']); + $output .= ' ' . $renderer->style(' FAILED ', ['bg_red', 'bold']) . "\n\n"; + $output .= "Request Details:\n\n"; + $output .= $this->requestRenderer->renderRequest($request->getRequest()) . "\n"; + + $output .= $renderer->renderResult($request) . "\n"; + } + + return $output; + } +} diff --git a/src/ResultRenderer/OutputFailureRenderer.php b/src/ResultRenderer/Cli/RequestFailureRenderer.php similarity index 58% rename from src/ResultRenderer/OutputFailureRenderer.php rename to src/ResultRenderer/Cli/RequestFailureRenderer.php index d0b295fb..3cf84fa9 100644 --- a/src/ResultRenderer/OutputFailureRenderer.php +++ b/src/ResultRenderer/Cli/RequestFailureRenderer.php @@ -1,27 +1,28 @@ result = $result; } @@ -36,10 +37,10 @@ public function render(ResultsRenderer $renderer) { return sprintf( " %s\n%s\n\n %s\n%s\n", - $renderer->style("ACTUAL", ['bold', 'underline', 'yellow']), + $renderer->style('YOUR OUTPUT:', ['bold', 'yellow']), $this->indent($renderer->style(sprintf('"%s"', $this->result->getActualOutput()), 'red')), - $renderer->style("EXPECTED", ['yellow', 'bold', 'underline']), - $this->indent($renderer->style(sprintf('"%s"', $this->result->getExpectedOutput()), 'red')) + $renderer->style('EXPECTED OUTPUT:', ['bold', 'yellow']), + $this->indent($renderer->style(sprintf('"%s"', $this->result->getExpectedOutput()), 'green')) ); } diff --git a/src/ResultRenderer/CliResultRenderer.php b/src/ResultRenderer/CliResultRenderer.php new file mode 100644 index 00000000..576cbee5 --- /dev/null +++ b/src/ResultRenderer/CliResultRenderer.php @@ -0,0 +1,66 @@ +result = $result; + } + + /** + * Render the details of each failed request including the mismatching headers and body. + * + * @param ResultsRenderer $renderer + * @return string + */ + public function render(ResultsRenderer $renderer) + { + $results = array_filter($this->result->getResults(), function (ResultInterface $result) { + return $result instanceof FailureInterface; + }); + + $output = ''; + if (count($results)) { + $output .= $renderer->center("Some executions of your solution produced incorrect output!\n"); + } + + /** @var FailureInterface $request **/ + foreach ($results as $key => $request) { + $output .= $renderer->lineBreak(); + $output .= "\n"; + $output .= $renderer->style(sprintf('Execution %d', $key + 1), ['bold', 'underline', 'blue']); + $output .= ' ' . $renderer->style(' FAILED ', ['bg_red', 'bold']) . "\n\n"; + + $output .= $request->getArgs()->isEmpty() + ? "Arguments: None\n" + : sprintf("Arguments: \"%s\"\n", $request->getArgs()->implode('", "')); + + $output .= "\n" . $renderer->renderResult($request) . "\n"; + } + + return $output; + } +} diff --git a/src/ResultRenderer/FailureRenderer.php b/src/ResultRenderer/FailureRenderer.php index 716e5a11..1604d076 100644 --- a/src/ResultRenderer/FailureRenderer.php +++ b/src/ResultRenderer/FailureRenderer.php @@ -33,6 +33,6 @@ public function __construct(Failure $result) */ public function render(ResultsRenderer $renderer) { - return ' ' . $this->result->getReason() . "\n"; + return $renderer->center($this->result->getReason()) . "\n"; } } diff --git a/src/ResultRenderer/ResultsRenderer.php b/src/ResultRenderer/ResultsRenderer.php index 342c1d56..1a51eff6 100644 --- a/src/ResultRenderer/ResultsRenderer.php +++ b/src/ResultRenderer/ResultsRenderer.php @@ -4,6 +4,7 @@ use Colors\Color; use PhpSchool\CliMenu\Terminal\TerminalInterface; +use PhpSchool\CliMenu\Util\StringUtil; use PhpSchool\PhpWorkshop\Exercise\ProvidesSolution; use PhpSchool\PhpWorkshop\Factory\ResultRendererFactory; use PhpSchool\PhpWorkshop\Output\OutputInterface; @@ -41,7 +42,7 @@ class ResultsRenderer * @var TerminalInterface */ private $terminal; - + /** * @var SyntaxHighlighter */ @@ -58,6 +59,7 @@ class ResultsRenderer * @param TerminalInterface $terminal A helper to get information regarding the current terminal. * @param ExerciseRepository $exerciseRepository The exercise repository. * @param SyntaxHighlighter $syntaxHighlighter A PHP syntax highlighter for the terminal, uses ANSI escape codes. + * @param ResultRendererFactory $resultRendererFactory */ public function __construct( $appName, @@ -101,11 +103,22 @@ public function render( $failures[] = [$result, sprintf(' ✗ Check: %s', $result->getCheckName())]; } } - - $longest = max(array_map('strlen', array_merge($successes, array_column($failures, 1)))) + 2; - $output->writeLines( - $this->padArray($this->styleArray($successes, ['green', 'bg_black', 'bold']), $longest) - ); + + $output->emptyLine(); + $output->writeLine($this->center($this->style('*** RESULTS ***', ['magenta', 'bold']))); + $output->emptyLine(); + + $messages = array_merge($successes, array_column($failures, 1)); + $longest = max(array_map('mb_strlen', $messages)) + 4; + + foreach ($successes as $success) { + $output->writeLine($this->center($this->style(str_repeat(' ', $longest), ['bg_green']))); + $output->writeLine( + $this->center($this->style(mb_str_pad($success, $longest), ['bg_green', 'white', 'bold'])) + ); + $output->writeLine($this->center($this->style(str_repeat(' ', $longest), ['bg_green']))); + $output->emptyLine(); + } if ($results->isSuccessful()) { return $this->renderSuccessInformation($exercise, $userState, $output); @@ -125,17 +138,24 @@ private function renderErrorInformation( ExerciseInterface $exercise, OutputInterface $output ) { - foreach ($failures as $result) { - list ($failure, $message) = $result; - $output->writeLine(str_pad($this->style($message, ['red', 'bg_black', 'bold']), $padLength)); - $output->write($this->renderResult($failure)); + foreach ($failures as list ($failure, $message)) { + $output->writeLine($this->center($this->style(str_repeat(' ', $padLength), ['bg_red']))); + $output->writeLine($this->center($this->style(\mb_str_pad($message, $padLength), ['bg_red']))); + $output->writeLine($this->center($this->style(str_repeat(' ', $padLength), ['bg_red']))); + $output->emptyLine(); + $output->write($this->renderResult($failure)); } - $output->writeLine($this->style(' Your solution was unsuccessful!', ['red', 'bg_default', 'bold'])); + $output->lineBreak(); + $output->emptyLine(); + $output->emptyLine(); + $this->fullWidthBlock($output, 'Your solution was unsuccessful!', ['white', 'bg_red', 'bold']); $output->emptyLine(); - $output->writeLine(sprintf(" Your solution to %s didn't pass. Try again!", $exercise->getName())); + $output->writeLine( + $this->center(sprintf(" Your solution to %s didn't pass. Try again!", $exercise->getName())) + ); $output->emptyLine(); $output->emptyLine(); } @@ -150,12 +170,15 @@ private function renderSuccessInformation( UserState $userState, OutputInterface $output ) { + $output->lineBreak(); + $output->emptyLine(); $output->emptyLine(); - $output->writeLine($this->style(" PASS!", ['green', 'bg_default', 'bold'])); + $this->fullWidthBlock($output, 'PASS!', ['white', 'bg_green', 'bold']); $output->emptyLine(); if ($exercise instanceof ProvidesSolution) { - $output->writeLine("Here's the official solution in case you want to compare notes:"); + $output->writeLine($this->center("Here's the official solution in case you want to compare notes:")); + $output->emptyLine(); $output->writeLine($this->lineBreak()); foreach ($exercise->getSolution()->getFiles() as $file) { @@ -178,33 +201,54 @@ private function renderSuccessInformation( $completedCount = count($userState->getCompletedExercises()); $numExercises = $this->exerciseRepository->count(); - $output->writeLine(sprintf('You have %d challenges left.', $numExercises - $completedCount)); - $output->writeLine(sprintf('Type "%s" to show the menu.', $this->appName)); + $output->emptyLine(); + $output->writeLine($this->center(sprintf('You have %d challenges left.', $numExercises - $completedCount))); + $output->writeLine($this->center(sprintf('Type "%s" and hit enter to show the menu.', $this->appName))); $output->emptyLine(); } /** - * @param array $lines - * @param int $length - * @return array + * @param string $string + * @return string string */ - private function padArray(array $lines, $length) + public function center($string) { - return array_map(function ($line) use ($length) { - return str_pad($line, $length); - }, $lines); + $stringHalfLength = mb_strlen(StringUtil::stripAnsiEscapeSequence($string)) / 2; + $widthHalfLength = ceil($this->terminal->getWidth() / 2); + $start = $widthHalfLength - $stringHalfLength; + + if ($start < 0) { + $start = 0; + } + + return str_repeat(' ', $start) . $string; } /** - * @param array $lines - * @param array $styles - * @return array + * @param OutputInterface $output + * @param $string + * @param array $style */ - private function styleArray(array $lines, array $styles) + private function fullWidthBlock(OutputInterface $output, $string, array $style) { - return array_map(function ($line) use ($styles) { - return $this->style($line, $styles); - }, $lines); + $stringLength = mb_strlen(StringUtil::stripAnsiEscapeSequence($string)); + $stringHalfLength = $stringLength / 2; + $widthHalfLength = ceil($this->terminal->getWidth() / 2); + $start = ceil($widthHalfLength - $stringHalfLength); + + $output->writeLine($this->style(str_repeat(' ', $this->terminal->getWidth()), $style)); + $output->writeLine( + $this->style( + sprintf( + '%s%s%s', + str_repeat(' ', $start), + $string, + str_repeat(' ', $this->terminal->getWidth() - $stringLength - $start) + ), + $style + ) + ); + $output->writeLine($this->style(str_repeat(' ', $this->terminal->getWidth()), $style)); } /** @@ -249,6 +293,6 @@ public function renderResult(ResultInterface $result) */ public function lineBreak() { - return $this->style(str_repeat("─", $this->terminal->getWidth()), 'yellow'); + return $this->style(str_repeat('─', $this->terminal->getWidth()), 'yellow'); } } diff --git a/src/Utils/ArrayObject.php b/src/Utils/ArrayObject.php index 638cb373..5a4b0e43 100644 --- a/src/Utils/ArrayObject.php +++ b/src/Utils/ArrayObject.php @@ -187,4 +187,12 @@ public function count() { return count($this->array); } + + /** + * @return bool + */ + public function isEmpty() + { + return $this->array === []; + } } diff --git a/src/Utils/RequestRenderer.php b/src/Utils/RequestRenderer.php new file mode 100644 index 00000000..2cd6bb88 --- /dev/null +++ b/src/Utils/RequestRenderer.php @@ -0,0 +1,55 @@ + + */ +class RequestRenderer +{ + /** + * Render a PSR-7 request. + * + * @param RequestInterface $request + * @return string + */ + public function renderRequest(RequestInterface $request) + { + $return = ''; + $return .= sprintf("URL: %s\n", $request->getUri()); + $return .= sprintf("METHOD: %s\n", $request->getMethod()); + + if ($request->getHeaders()) { + $return .= 'HEADERS:'; + } + + $indent = false; + foreach ($request->getHeaders() as $name => $values) { + if ($indent) { + $return .= str_repeat(' ', 8); + } + + $return .= sprintf(" %s: %s\n", $name, implode(', ', $values)); + $indent = true; + } + + if ($body = (string) $request->getBody()) { + $return .= 'BODY: '; + + switch ($request->getHeaderLine('Content-Type')) { + case 'application/json': + $return .= json_encode(json_decode($body, true), JSON_PRETTY_PRINT); + break; + default: + $return .= $body; + break; + } + + $return .= "\n"; + } + + return $return; + } +} diff --git a/src/functions.php b/src/functions.php new file mode 100644 index 00000000..2496b3c4 --- /dev/null +++ b/src/functions.php @@ -0,0 +1,31 @@ +assertSame('No composer.lock file found', $result->getReason()); } + public function testCheckReturnsFailureIfNoVendorFolder() + { + $result = $this->check->check( + $this->exercise, + new Input('app', ['program' => __DIR__ . '/../res/composer/no-vendor/solution.php']) + ); + + $this->assertInstanceOf(Failure::class, $result); + $this->assertSame('Composer Dependency Check', $result->getCheckName()); + $this->assertSame('No vendor folder found', $result->getReason()); + } + /** * @dataProvider dependencyProvider * diff --git a/test/Check/DatabaseCheckTest.php b/test/Check/DatabaseCheckTest.php index 4fc28053..622e028d 100644 --- a/test/Check/DatabaseCheckTest.php +++ b/test/Check/DatabaseCheckTest.php @@ -180,7 +180,7 @@ public function testSuccessIsReturnedIfDatabaseVerificationPassed() $this->exercise ->expects($this->once()) ->method('getArgs') - ->will($this->returnValue([1, 2, 3])); + ->will($this->returnValue([[1, 2, 3]])); $this->exercise ->expects($this->once()) diff --git a/test/ExerciseRunner/CgiRunnerTest.php b/test/ExerciseRunner/CgiRunnerTest.php index 882fafbf..33c577f2 100644 --- a/test/ExerciseRunner/CgiRunnerTest.php +++ b/test/ExerciseRunner/CgiRunnerTest.php @@ -13,11 +13,13 @@ use PhpSchool\PhpWorkshop\ExerciseRunner\CgiRunner; use PhpSchool\PhpWorkshop\Input\Input; use PhpSchool\PhpWorkshop\Output\StdOutput; +use PhpSchool\PhpWorkshop\Result\Cgi\RequestFailure; use PhpSchool\PhpWorkshop\Result\CgiOutRequestFailure; -use PhpSchool\PhpWorkshop\Result\CgiOutResult; +use PhpSchool\PhpWorkshop\Result\Cgi\CgiResult; use PhpSchool\PhpWorkshop\Result\Failure; use PhpSchool\PhpWorkshop\ResultAggregator; use PhpSchool\PhpWorkshop\Solution\SingleFileSolution; +use PhpSchool\PhpWorkshop\Utils\RequestRenderer; use PhpSchool\PhpWorkshopTest\Asset\CgiExerciseInterface; use PHPUnit_Framework_TestCase; use Zend\Diactoros\Request; @@ -40,7 +42,7 @@ class CgiRunnerTest extends PHPUnit_Framework_TestCase public function setUp() { $this->exercise = $this->createMock(CgiExerciseInterface::class); - $this->runner = new CgiRunner($this->exercise, new EventDispatcher(new ResultAggregator)); + $this->runner = new CgiRunner($this->exercise, new EventDispatcher(new ResultAggregator), new RequestRenderer); $this->exercise ->expects($this->any()) @@ -102,7 +104,7 @@ public function testVerifyReturnsSuccessIfGetSolutionOutputMatchesUserOutput() ->will($this->returnValue([$request])); $this->assertInstanceOf( - CgiOutResult::class, + CgiResult::class, $this->runner->verify(new Input('app', ['program' => realpath(__DIR__ . '/../res/cgi/get-solution.php')])) ); } @@ -128,9 +130,13 @@ public function testVerifyReturnsSuccessIfPostSolutionOutputMatchesUserOutput() ->will($this->returnValue([$request])); $this->assertInstanceOf( - CgiOutResult::class, - $this->runner->verify(new Input('app', ['program' => realpath(__DIR__ . '/../res/cgi/post-solution.php')])) + CgiResult::class, + $res = $this->runner->verify( + new Input('app', ['program' => realpath(__DIR__ . '/../res/cgi/post-solution.php')]) + ) ); + + $this->assertTrue($res->isSuccessful()); } public function testVerifyReturnsSuccessIfPostSolutionOutputMatchesUserOutputWithMultipleParams() @@ -157,7 +163,7 @@ public function testVerifyReturnsSuccessIfPostSolutionOutputMatchesUserOutputWit new Input('app', ['program' => realpath(__DIR__ . '/../res/cgi/post-multiple-solution.php')]) ); - $this->assertInstanceOf(CgiOutResult::class, $result); + $this->assertInstanceOf(CgiResult::class, $result); } public function testVerifyReturnsFailureIfUserSolutionFailsToExecute() @@ -181,7 +187,7 @@ public function testVerifyReturnsFailureIfUserSolutionFailsToExecute() new Input('app', ['program' => realpath(__DIR__ . '/../res/cgi/user-error.php')]) ); - $this->assertInstanceOf(CgiOutResult::class, $failure); + $this->assertInstanceOf(CgiResult::class, $failure); $this->assertCount(1, $failure); $result = iterator_to_array($failure)[0]; @@ -213,11 +219,11 @@ public function testVerifyReturnsFailureIfSolutionOutputDoesNotMatchUserOutput() new Input('app', ['program' => realpath(__DIR__ . '/../res/cgi/get-user-wrong.php')]) ); - $this->assertInstanceOf(CgiOutResult::class, $failure); + $this->assertInstanceOf(CgiResult::class, $failure); $this->assertCount(1, $failure); $result = iterator_to_array($failure)[0]; - $this->assertInstanceOf(CgiOutRequestFailure::class, $result); + $this->assertInstanceOf(RequestFailure::class, $result); $this->assertEquals('10', $result->getExpectedOutput()); $this->assertEquals('15', $result->getActualOutput()); $this->assertEquals(['Content-type' => 'text/html; charset=UTF-8'], $result->getExpectedHeaders()); @@ -245,11 +251,11 @@ public function testVerifyReturnsFailureIfSolutionOutputHeadersDoesNotMatchUserO new Input('app', ['program' => realpath(__DIR__ . '/../res/cgi/get-user-header-wrong.php')]) ); - $this->assertInstanceOf(CgiOutResult::class, $failure); + $this->assertInstanceOf(CgiResult::class, $failure); $this->assertCount(1, $failure); $result = iterator_to_array($failure)[0]; - $this->assertInstanceOf(CgiOutRequestFailure::class, $result); + $this->assertInstanceOf(RequestFailure::class, $result); $this->assertSame($result->getExpectedOutput(), $result->getActualOutput()); $this->assertEquals( diff --git a/test/ExerciseRunner/CliRunnerTest.php b/test/ExerciseRunner/CliRunnerTest.php index 289c45da..34bb2afe 100644 --- a/test/ExerciseRunner/CliRunnerTest.php +++ b/test/ExerciseRunner/CliRunnerTest.php @@ -3,7 +3,6 @@ namespace PhpSchool\PhpWorkshopTest\ExerciseRunner; use Colors\Color; -use InvalidArgumentException; use PhpSchool\CliMenu\Terminal\TerminalInterface; use PhpSchool\PhpWorkshop\Check\CodeParseCheck; use PhpSchool\PhpWorkshop\Check\FileExistsCheck; @@ -14,7 +13,9 @@ use PhpSchool\PhpWorkshop\ExerciseRunner\CliRunner; use PhpSchool\PhpWorkshop\Input\Input; use PhpSchool\PhpWorkshop\Output\StdOutput; -use PhpSchool\PhpWorkshop\Result\Failure; +use PhpSchool\PhpWorkshop\Result\Cli\CliResult; +use PhpSchool\PhpWorkshop\Result\Cli\GenericFailure; +use PhpSchool\PhpWorkshop\Result\Cli\RequestFailure; use PhpSchool\PhpWorkshop\Result\StdOutFailure; use PhpSchool\PhpWorkshop\Result\Success; use PhpSchool\PhpWorkshop\ResultAggregator; @@ -71,7 +72,7 @@ public function testVerifyThrowsExceptionIfSolutionFailsExecution() $this->exercise ->expects($this->once()) ->method('getArgs') - ->will($this->returnValue([])); + ->will($this->returnValue([[]])); $regex = "/^PHP Code failed to execute\\. Error: \"PHP Parse error: syntax error, unexpected end of file"; $regex .= ", expecting ',' or ';'/"; @@ -81,6 +82,27 @@ public function testVerifyThrowsExceptionIfSolutionFailsExecution() } public function testVerifyReturnsSuccessIfSolutionOutputMatchesUserOutput() + { + $solution = SingleFileSolution::fromFile(realpath(__DIR__ . '/../res/cli/solution.php')); + $this->exercise + ->expects($this->once()) + ->method('getSolution') + ->will($this->returnValue($solution)); + + $this->exercise + ->expects($this->once()) + ->method('getArgs') + ->will($this->returnValue([[1, 2, 3]])); + + $this->assertInstanceOf( + CliResult::class, + $res = $this->runner->verify(new Input('app', ['program' => __DIR__ . '/../res/cli/user.php'])) + ); + + $this->assertTrue($res->isSuccessful()); + } + + public function testSuccessWithSingleSetOfArgsForBC() { $solution = SingleFileSolution::fromFile(realpath(__DIR__ . '/../res/cli/solution.php')); $this->exercise @@ -94,9 +116,11 @@ public function testVerifyReturnsSuccessIfSolutionOutputMatchesUserOutput() ->will($this->returnValue([1, 2, 3])); $this->assertInstanceOf( - Success::class, - $this->runner->verify(new Input('app', ['program' => __DIR__ . '/../res/cli/user.php'])) + CliResult::class, + $res = $this->runner->verify(new Input('app', ['program' => __DIR__ . '/../res/cli/user.php'])) ); + + $this->assertTrue($res->isSuccessful()); } public function testVerifyReturnsFailureIfUserSolutionFailsToExecute() @@ -110,15 +134,19 @@ public function testVerifyReturnsFailureIfUserSolutionFailsToExecute() $this->exercise ->expects($this->once()) ->method('getArgs') - ->will($this->returnValue([1, 2, 3])); + ->will($this->returnValue([[1, 2, 3]])); $failure = $this->runner->verify(new Input('app', ['program' => __DIR__ . '/../res/cli/user-error.php'])); $failureMsg = "/^PHP Code failed to execute. Error: \"PHP Parse error: syntax error, "; $failureMsg .= "unexpected end of file, expecting ',' or ';'/"; - $this->assertInstanceOf(Failure::class, $failure); - $this->assertRegExp($failureMsg, $failure->getReason()); + $this->assertInstanceOf(CLiResult::class, $failure); + $this->assertCount(1, $failure); + + $result = iterator_to_array($failure)[0]; + $this->assertInstanceOf(GenericFailure::class, $result); + $this->assertRegExp($failureMsg, $result->getReason()); } public function testVerifyReturnsFailureIfSolutionOutputDoesNotMatchUserOutput() @@ -132,29 +160,41 @@ public function testVerifyReturnsFailureIfSolutionOutputDoesNotMatchUserOutput() $this->exercise ->expects($this->once()) ->method('getArgs') - ->will($this->returnValue([1, 2, 3])); + ->will($this->returnValue([[1, 2, 3]])); $failure = $this->runner->verify(new Input('app', ['program' => __DIR__ . '/../res/cli/user-wrong.php'])); - $this->assertInstanceOf(StdOutFailure::class, $failure); - $this->assertEquals('6', $failure->getExpectedOutput()); - $this->assertEquals('10', $failure->getActualOutput()); + $this->assertInstanceOf(CLiResult::class, $failure); + $this->assertCount(1, $failure); + + $result = iterator_to_array($failure)[0]; + $this->assertInstanceOf(RequestFailure::class, $result); + + $this->assertEquals('6', $result->getExpectedOutput()); + $this->assertEquals('10', $result->getActualOutput()); } public function testRunPassesOutputAndReturnsSuccessIfScriptIsSuccessful() { - $output = new StdOutput(new Color, $this->createMock(TerminalInterface::class)); + $color = new Color; + $color->setForceStyle(true); + $output = new StdOutput($color, $this->createMock(TerminalInterface::class)); $this->exercise ->expects($this->once()) ->method('getArgs') - ->will($this->returnValue([1, 2, 3])); + ->will($this->returnValue([[1, 2, 3], [4, 5, 6]])); $exp = "\n\e[1m\e[4mArguments\e[0m\e[0m\n"; - $exp .= "1, 2, 3\n\n"; - $exp .= "\e[1m\e[4m"; - $exp .= "Output\e[0m\e[0m\n"; + $exp .= "1, 2, 3\n"; + $exp .= "\n\e[1m\e[4mOutput\e[0m\e[0m\n"; $exp .= "6\n"; + $exp .= "\e[33m\e[0m\n"; + $exp .= "\e[1m\e[4mArguments\e[0m\e[0m\n"; + $exp .= "4, 5, 6\n\n"; + $exp .= "\e[1m\e[4mOutput\e[0m\e[0m\n"; + $exp .= "15\n"; + $exp .= "\e[33m\e[0m"; $this->expectOutputString($exp); @@ -169,7 +209,7 @@ public function testRunPassesOutputAndReturnsFailureIfScriptFails() $this->exercise ->expects($this->once()) ->method('getArgs') - ->will($this->returnValue([1, 2, 3])); + ->will($this->returnValue([[1, 2, 3]])); $this->expectOutputRegex('/PHP Parse error: syntax error, unexpected end of file, expecting \',\' or \';\' /'); diff --git a/test/ExerciseRunner/Factory/CgiRunnerFactoryTest.php b/test/ExerciseRunner/Factory/CgiRunnerFactoryTest.php index 69b59c3b..dc6d0211 100644 --- a/test/ExerciseRunner/Factory/CgiRunnerFactoryTest.php +++ b/test/ExerciseRunner/Factory/CgiRunnerFactoryTest.php @@ -8,6 +8,7 @@ use PhpSchool\PhpWorkshop\Exercise\ExerciseType; use PhpSchool\PhpWorkshop\ExerciseRunner\CgiRunner; use PhpSchool\PhpWorkshop\ExerciseRunner\Factory\CgiRunnerFactory; +use PhpSchool\PhpWorkshop\Utils\RequestRenderer; use PhpSchool\PhpWorkshopTest\Asset\CgiExerciseImpl; use PHPUnit_Framework_TestCase; @@ -29,7 +30,7 @@ class CgiRunnerFactoryTest extends PHPUnit_Framework_TestCase public function setUp() { $this->eventDispatcher = $this->createMock(EventDispatcher::class); - $this->factory = new CgiRunnerFactory($this->eventDispatcher); + $this->factory = new CgiRunnerFactory($this->eventDispatcher, new RequestRenderer); } public function testSupports() diff --git a/test/Factory/ResultRendererFactoryTest.php b/test/Factory/ResultRendererFactoryTest.php index 51cb9c47..35a489f6 100644 --- a/test/Factory/ResultRendererFactoryTest.php +++ b/test/Factory/ResultRendererFactoryTest.php @@ -87,4 +87,19 @@ public function testCreateReturnsMappedRendererInterface() $this->assertInstanceOf($rendererClassName, $returnedRenderer); } + + public function testExceptionIsThrownIfFactoryReturnsInCorrectRenderer() + { + $resultClass = $this->createMock(ResultInterface::class); + $resultClassName = get_class($resultClass); + $rendererClassName = get_class($this->createMock(ResultRendererInterface::class)); + $factory = new ResultRendererFactory(); + $factory->registerRenderer($resultClassName, $rendererClassName, function () { + return new \stdClass; + }); + + $this->expectException(RuntimeException::class); + + $factory->create($resultClass); + } } diff --git a/test/FunctionsTest.php b/test/FunctionsTest.php new file mode 100644 index 00000000..69dd5695 --- /dev/null +++ b/test/FunctionsTest.php @@ -0,0 +1,59 @@ + + */ +class FunctionsTest extends PHPUnit_Framework_TestCase +{ + /** + * @dataProvider mbStrPadProvider + * + * @param string $string + * @param string $pad + * @param string $expected + */ + public function testMbStrPad($string, $pad, $expected) + { + self::assertSame(mb_str_pad($string, $pad), $expected); + } + + /** + * @return array + */ + public function mbStrPadProvider() + { + return [ + ['hello', 10, 'hello '], + ['hello😂', 10, 'hello😂 '], + ]; + } + + /** + * @dataProvider camelCaseToKebabCaseProvider + * + * @param string $string + * @param string $expected + */ + public function testCamelCaseToKebabCase($string, $expected) + { + self::assertSame(camel_case_to_kebab_case($string), $expected); + } + + /** + * @return array + */ + public function camelCaseToKebabCaseProvider() + { + return [ + ['camelCase', 'camel-case'], + [ + 'educationIsThePassportToTheFutureForTomorrowBelongsToThoseWhoPrepareForItToday', + 'education-is-the-passport-to-the-future-for-tomorrow-belongs-to-those-who-prepare-for-it-today' + ] + ]; + } +} diff --git a/test/Output/StdOutputTest.php b/test/Output/StdOutputTest.php index a50a578e..42f606df 100644 --- a/test/Output/StdOutputTest.php +++ b/test/Output/StdOutputTest.php @@ -1,13 +1,11 @@ expectOutputString($message); @@ -57,7 +55,7 @@ public function testWrite() public function testWriteLine() { - $message = "Talk is cheap. Show me the code."; + $message = 'Talk is cheap. Show me the code.'; $this->expectOutputString($message . "\n"); $this->output->writeLine($message); } @@ -74,52 +72,4 @@ public function testEmptyLine() $this->expectOutputString("\n"); $this->output->emptyLine(); } - - public function testWriteRequestWithHeaders() - { - $request = (new Request('http://www.time.com/api/pt?iso=2016-01-21T18:14:33+0000')) - ->withMethod('GET'); - - $expected = "URL: http://www.time.com/api/pt?iso=2016-01-21T18:14:33+0000\n"; - $expected .= "METHOD: GET\n"; - $expected .= "HEADERS: Host: www.time.com\n"; - - $this->expectOutputString($expected); - $this->output->writeRequest($request); - } - - public function testWriteRequestWithNoHeaders() - { - $request = (new Request('/endpoint')) - ->withMethod('GET'); - - $expected = "URL: /endpoint\n"; - $expected .= "METHOD: GET\n"; - - $this->expectOutputString($expected); - $this->output->writeRequest($request); - } - - public function testWriteRequestWithPostBody() - { - $request = (new Request('/endpoint')) - ->withMethod('POST') - ->withHeader('Content-Type', 'application/json'); - - $request->getBody()->write( - json_encode(['data' => 'test', 'other_data' => 'test2']) - ); - - $expected = "URL: /endpoint\n"; - $expected .= "METHOD: POST\n"; - $expected .= "HEADERS: Content-Type: application/json\n\n"; - $expected .= "BODY:{\n"; - $expected .= " \"data\": \"test\",\n"; - $expected .= " \"other_data\": \"test2\"\n"; - $expected .= "}\n"; - - - $this->expectOutputString($expected); - $this->output->writeRequest($request); - } } diff --git a/test/Result/Cgi/CgiResultTest.php b/test/Result/Cgi/CgiResultTest.php new file mode 100644 index 00000000..667cefc0 --- /dev/null +++ b/test/Result/Cgi/CgiResultTest.php @@ -0,0 +1,36 @@ + + */ +class CgiResultTest extends TestCase +{ + public function testName() + { + $request = new RequestFailure($this->createMock(RequestInterface::class), '', '', [], []); + $cgiResult = new CgiResult([$request]); + $this->assertSame('CGI Program Runner', $cgiResult->getCheckName()); + } + + public function testIsSuccessful() + { + $request = new RequestFailure($this->createMock(RequestInterface::class), '', '', [], []); + $cgiResult = new CgiResult([$request]); + + $this->assertFalse($cgiResult->isSuccessful()); + + $cgiResult = new CgiResult([new Success($this->createMock(RequestInterface::class), 'Successful Check')]); + $this->assertTrue($cgiResult->isSuccessful()); + + $cgiResult->add($request); + $this->assertFalse($cgiResult->isSuccessful()); + } +} diff --git a/test/Result/Cgi/GenericFailureTest.php b/test/Result/Cgi/GenericFailureTest.php new file mode 100644 index 00000000..a28fbfcd --- /dev/null +++ b/test/Result/Cgi/GenericFailureTest.php @@ -0,0 +1,45 @@ + + */ +class GenericFailureTest extends PHPUnit_Framework_TestCase +{ + public function testFailure() + { + $request = $this->createMock(RequestInterface::class); + $failure = new GenericFailure($request, 'Oops'); + $this->assertInstanceOf(GenericFailure::class, $failure); + $this->assertSame($request, $failure->getRequest()); + $this->assertEquals('Oops', $failure->getReason()); + $this->assertEquals('CGI Program Runner', $failure->getCheckName()); + } + + public function testFailureWithRequestAndReason() + { + $request = $this->createMock(RequestInterface::class); + $failure = GenericFailure::fromRequestAndReason($request, 'Oops'); + $this->assertInstanceOf(GenericFailure::class, $failure); + $this->assertSame($request, $failure->getRequest()); + $this->assertEquals('Oops', $failure->getReason()); + $this->assertEquals('CGI Program Runner', $failure->getCheckName()); + } + + public function testFailureFromCodeExecutionException() + { + $e = new CodeExecutionException('Something went wrong yo'); + $request = $this->createMock(RequestInterface::class); + $failure = GenericFailure::fromRequestAndCodeExecutionFailure($request, $e); + $this->assertInstanceOf(GenericFailure::class, $failure); + $this->assertSame($request, $failure->getRequest()); + $this->assertEquals('Something went wrong yo', $failure->getReason()); + $this->assertEquals('CGI Program Runner', $failure->getCheckName()); + } +} diff --git a/test/Result/Cgi/RequestFailureTest.php b/test/Result/Cgi/RequestFailureTest.php new file mode 100644 index 00000000..0ba97faa --- /dev/null +++ b/test/Result/Cgi/RequestFailureTest.php @@ -0,0 +1,84 @@ + + */ +class RequestFailureTest extends PHPUnit_Framework_TestCase +{ + public function setUp() + { + $request = $this->createMock(RequestInterface::class); + $requestFailure = new RequestFailure($request, '', '', [], []); + $this->assertSame('Request Failure', $requestFailure->getCheckName()); + $this->assertSame($request, $requestFailure->getRequest()); + } + + public function testWhenOnlyOutputDifferent() + { + $requestFailure = new RequestFailure( + $this->createMock(RequestInterface::class), + 'Expected Output', + 'Actual Output', + [], + [] + ); + + $this->assertEquals('Expected Output', $requestFailure->getExpectedOutput()); + $this->assertEquals('Actual Output', $requestFailure->getActualOutput()); + $this->assertTrue($requestFailure->bodyDifferent()); + $this->assertFalse($requestFailure->headersDifferent()); + $this->assertFalse($requestFailure->headersAndBodyDifferent()); + $this->assertSame($requestFailure->getExpectedHeaders(), $requestFailure->getActualHeaders()); + } + + public function testWhenOnlyHeadersDifferent() + { + $requestFailure = new RequestFailure( + $this->createMock(RequestInterface::class), + 'Output', + 'Output', + ['header1' => 'some-value'], + ['header2' => 'some-value'] + ); + + $this->assertEquals(['header1' => 'some-value'], $requestFailure->getExpectedHeaders()); + $this->assertEquals(['header2' => 'some-value'], $requestFailure->getActualHeaders()); + $this->assertTrue($requestFailure->headersDifferent()); + $this->assertFalse($requestFailure->bodyDifferent()); + $this->assertFalse($requestFailure->headersAndBodyDifferent()); + $this->assertSame($requestFailure->getExpectedOutput(), $requestFailure->getActualOutput()); + } + + public function testWhenOutputAndHeadersDifferent() + { + $requestFailure = new RequestFailure( + $this->createMock(RequestInterface::class), + 'Expected Output', + 'Actual Output', + ['header1' => 'some-value'], + ['header2' => 'some-value'] + ); + + $this->assertTrue($requestFailure->headersDifferent()); + $this->assertTrue($requestFailure->bodyDifferent()); + $this->assertTrue($requestFailure->headersAndBodyDifferent()); + + $this->assertEquals(['header1' => 'some-value'], $requestFailure->getExpectedHeaders()); + $this->assertEquals(['header2' => 'some-value'], $requestFailure->getActualHeaders()); + $this->assertNotEquals($requestFailure->getExpectedHeaders(), $requestFailure->getActualHeaders()); + + $this->assertEquals('Expected Output', $requestFailure->getExpectedOutput()); + $this->assertEquals('Actual Output', $requestFailure->getActualOutput()); + $this->assertNotEquals($requestFailure->getExpectedOutput(), $requestFailure->getActualOutput()); + } +} diff --git a/test/Result/Cgi/SuccessTest.php b/test/Result/Cgi/SuccessTest.php new file mode 100644 index 00000000..d55f4a39 --- /dev/null +++ b/test/Result/Cgi/SuccessTest.php @@ -0,0 +1,22 @@ + + */ +class SuccessTest extends PHPUnit_Framework_TestCase +{ + public function testSuccess() + { + $request = $this->createMock(RequestInterface::class); + $success = new Success($request); + $this->assertInstanceOf(Success::class, $success); + $this->assertSame($request, $success->getRequest()); + $this->assertEquals('CGI Program Runner', $success->getCheckName()); + } +} diff --git a/test/Result/CgiOutRequestFailureTest.php b/test/Result/CgiOutRequestFailureTest.php deleted file mode 100644 index 7568c4ec..00000000 --- a/test/Result/CgiOutRequestFailureTest.php +++ /dev/null @@ -1,87 +0,0 @@ - - */ -class CgiOutRequestFailureTest extends PHPUnit_Framework_TestCase -{ - public function setUp() - { - $request = $this->createMock(RequestInterface::class); - $cgiOutResult = new CgiOutRequestFailure($request, '', '', [], []); - $this->assertSame('Request Failure', $cgiOutResult->getCheckName()); - $this->assertSame($request, $cgiOutResult->getRequest()); - } - - public function testWhenOnlyOutputDifferent() - { - $failure = new CgiOutRequestFailure( - $this->createMock(RequestInterface::class), - 'Expected Output', - 'Actual Output', - [], - [] - ); - - $this->assertEquals('Expected Output', $failure->getExpectedOutput()); - $this->assertEquals('Actual Output', $failure->getActualOutput()); - $this->assertTrue($failure->bodyDifferent()); - $this->assertFalse($failure->headersDifferent()); - $this->assertFalse($failure->headersAndBodyDifferent()); - $this->assertSame($failure->getExpectedHeaders(), $failure->getActualHeaders()); - } - - public function testWhenOnlyHeadersDifferent() - { - $failure = new CgiOutRequestFailure( - $this->createMock(RequestInterface::class), - 'Output', - 'Output', - ['header1' => 'some-value'], - ['header2' => 'some-value'] - ); - - $this->assertEquals(['header1' => 'some-value'], $failure->getExpectedHeaders()); - $this->assertEquals(['header2' => 'some-value'], $failure->getActualHeaders()); - $this->assertTrue($failure->headersDifferent()); - $this->assertFalse($failure->bodyDifferent()); - $this->assertFalse($failure->headersAndBodyDifferent()); - $this->assertSame($failure->getExpectedOutput(), $failure->getActualOutput()); - } - - public function testWhenOutputAndHeadersDifferent() - { - $failure = new CgiOutRequestFailure( - $this->createMock(RequestInterface::class), - 'Expected Output', - 'Actual Output', - ['header1' => 'some-value'], - ['header2' => 'some-value'] - ); - - $this->assertTrue($failure->headersDifferent()); - $this->assertTrue($failure->bodyDifferent()); - $this->assertTrue($failure->headersAndBodyDifferent()); - - $this->assertEquals(['header1' => 'some-value'], $failure->getExpectedHeaders()); - $this->assertEquals(['header2' => 'some-value'], $failure->getActualHeaders()); - $this->assertNotEquals($failure->getExpectedHeaders(), $failure->getActualHeaders()); - - $this->assertEquals('Expected Output', $failure->getExpectedOutput()); - $this->assertEquals('Actual Output', $failure->getActualOutput()); - $this->assertNotEquals($failure->getExpectedOutput(), $failure->getActualOutput()); - } -} diff --git a/test/Result/CgiOutResultTest.php b/test/Result/CgiOutResultTest.php deleted file mode 100644 index 77309011..00000000 --- a/test/Result/CgiOutResultTest.php +++ /dev/null @@ -1,40 +0,0 @@ - - */ -class CgiOutResultTest extends PHPUnit_Framework_TestCase -{ - public function testName() - { - $request = new CgiOutRequestFailure($this->createMock(RequestInterface::class), '', '', [], []); - $cgiOutResult = new CgiOutResult('Some Check', [$request]); - $this->assertSame('Some Check', $cgiOutResult->getCheckName()); - } - - public function testIsSuccessful() - { - $request = new CgiOutRequestFailure($this->createMock(RequestInterface::class), '', '', [], []); - $cgiOutResult = new CgiOutResult('Some Check', [$request]); - - $this->assertFalse($cgiOutResult->isSuccessful()); - - $cgiOutResult = new CgiOutResult('Some Check', [new Success('Successful Check')]); - $this->assertTrue($cgiOutResult->isSuccessful()); - - $cgiOutResult->add($request); - $this->assertFalse($cgiOutResult->isSuccessful()); - } -} diff --git a/test/Result/Cli/CliResultTest.php b/test/Result/Cli/CliResultTest.php new file mode 100644 index 00000000..6c9db761 --- /dev/null +++ b/test/Result/Cli/CliResultTest.php @@ -0,0 +1,36 @@ + + */ +class CliResultTest extends TestCase +{ + public function testName() + { + $request = new RequestFailure(new ArrayObject, 'EXPECTED', 'ACTUAL'); + $cliResult = new CliResult([$request]); + $this->assertSame('CLI Program Runner', $cliResult->getCheckName()); + } + + public function testIsSuccessful() + { + $request = new RequestFailure(new ArrayObject, 'EXPECTED', 'ACTUAL'); + $cliResult = new CliResult([$request]); + + $this->assertFalse($cliResult->isSuccessful()); + + $cliResult = new CliResult([new Success(new ArrayObject)]); + $this->assertTrue($cliResult->isSuccessful()); + + $cliResult->add($request); + $this->assertFalse($cliResult->isSuccessful()); + } +} diff --git a/test/Result/Cli/GenericFailureTest.php b/test/Result/Cli/GenericFailureTest.php new file mode 100644 index 00000000..2063551f --- /dev/null +++ b/test/Result/Cli/GenericFailureTest.php @@ -0,0 +1,46 @@ + + */ +class GenericFailureTest extends PHPUnit_Framework_TestCase +{ + public function testFailure() + { + $args = new ArrayObject; + $failure = new GenericFailure($args, 'Oops'); + $this->assertInstanceOf(GenericFailure::class, $failure); + $this->assertEquals($args, $failure->getArgs()); + $this->assertEquals('Oops', $failure->getReason()); + $this->assertEquals('CLI Program Runner', $failure->getCheckName()); + } + + public function testFailureWithRequestAndReason() + { + $args = new ArrayObject; + $failure = GenericFailure::fromArgsAndReason($args, 'Oops'); + $this->assertInstanceOf(GenericFailure::class, $failure); + $this->assertEquals($args, $failure->getArgs()); + $this->assertEquals('Oops', $failure->getReason()); + $this->assertEquals('CLI Program Runner', $failure->getCheckName()); + } + + public function testFailureFromCodeExecutionException() + { + $args = new ArrayObject; + $e = new CodeExecutionException('Something went wrong yo'); + $failure = GenericFailure::fromArgsAndCodeExecutionFailure($args, $e); + $this->assertInstanceOf(GenericFailure::class, $failure); + $this->assertEquals($args, $failure->getArgs()); + $this->assertEquals('Something went wrong yo', $failure->getReason()); + $this->assertEquals('CLI Program Runner', $failure->getCheckName()); + } +} diff --git a/test/Result/Cli/RequestFailureTest.php b/test/Result/Cli/RequestFailureTest.php new file mode 100644 index 00000000..47f63817 --- /dev/null +++ b/test/Result/Cli/RequestFailureTest.php @@ -0,0 +1,41 @@ + + */ +class RequestFailureTest extends PHPUnit_Framework_TestCase +{ + public function setUp() + { + $args = new ArrayObject; + $failure = new RequestFailure($args, 'Expected Output', 'Actual Output'); + $this->assertSame('Request Failure', $failure->getCheckName()); + $this->assertSame($args, $failure->getArgs()); + } + + public function testGetters() + { + $args = new ArrayObject; + $failure = new RequestFailure($args, 'Expected Output', 'Actual Output'); + $this->assertEquals('Expected Output', $failure->getExpectedOutput()); + $this->assertEquals('Actual Output', $failure->getActualOutput()); + $this->assertSame($args, $failure->getArgs()); + } + + public function testFailureFromArgsAndOutput() + { + $args = new ArrayObject; + $failure = RequestFailure::fromArgsAndOutput($args, 'Expected Output', 'Actual Output'); + $this->assertEquals('Expected Output', $failure->getExpectedOutput()); + $this->assertEquals('Actual Output', $failure->getActualOutput()); + $this->assertSame($args, $failure->getArgs()); + } +} diff --git a/test/Result/Cli/SuccessTest.php b/test/Result/Cli/SuccessTest.php new file mode 100644 index 00000000..9ee5f15b --- /dev/null +++ b/test/Result/Cli/SuccessTest.php @@ -0,0 +1,22 @@ + + */ +class SuccessTest extends PHPUnit_Framework_TestCase +{ + public function testSuccess() + { + $args = new ArrayObject; + $success = new Success($args); + $this->assertInstanceOf(Success::class, $success); + $this->assertSame($args, $success->getArgs()); + $this->assertEquals('CLI Program Runner', $success->getCheckName()); + } +} diff --git a/test/Result/StdOutFailureTest.php b/test/Result/StdOutFailureTest.php deleted file mode 100644 index b1cb4278..00000000 --- a/test/Result/StdOutFailureTest.php +++ /dev/null @@ -1,37 +0,0 @@ - - */ -class StdOutFailureTest extends PHPUnit_Framework_TestCase -{ - public function testGetters() - { - $failure = new StdOutFailure('Some Check', 'Expected Output', 'Actual Output'); - $this->assertEquals('Expected Output', $failure->getExpectedOutput()); - $this->assertEquals('Actual Output', $failure->getActualOutput()); - $this->assertEquals('Some Check', $failure->getCheckName()); - } - - public function testFailureFromCheck() - { - $check = $this->createMock(CheckInterface::class); - $check - ->expects($this->any()) - ->method('getName') - ->will($this->returnValue('Some Check')); - - $failure = StdOutFailure::fromCheckAndOutput($check, 'Expected Output', 'Actual Output'); - $this->assertEquals('Expected Output', $failure->getExpectedOutput()); - $this->assertEquals('Actual Output', $failure->getActualOutput()); - $this->assertEquals('Some Check', $failure->getCheckName()); - } -} diff --git a/test/ResultAggregatorTest.php b/test/ResultAggregatorTest.php index 334a9e4e..f5151e50 100644 --- a/test/ResultAggregatorTest.php +++ b/test/ResultAggregatorTest.php @@ -4,10 +4,14 @@ namespace PhpSchool\PhpWorkshopTest; use PhpSchool\PhpWorkshop\Check\CheckInterface; +use PhpSchool\PhpWorkshop\Result\Cli\CliResult; +use PhpSchool\PhpWorkshop\Utils\ArrayObject; use PhpSchool\PhpWorkshopTest\Asset\ResultResultAggregator; use PHPUnit_Framework_TestCase; use PhpSchool\PhpWorkshop\Result\Failure; use PhpSchool\PhpWorkshop\Result\Success; +use PhpSchool\PhpWorkshop\Result\Cli\Success as CliSuccess; +use PhpSchool\PhpWorkshop\Result\Cli\GenericFailure as CliGenericFailure; use PhpSchool\PhpWorkshop\ResultAggregator; /** @@ -43,18 +47,18 @@ public function testIsSuccessful() $this->assertFalse($resultAggregator->isSuccessful()); } - public function testIsSuccessfulWithNestedResults() + public function testIsSuccessfulWithResultGroups() { $resultAggregator = new ResultAggregator; $this->assertTrue($resultAggregator->isSuccessful()); - $resultResultAggregator = new ResultResultAggregator; - $resultResultAggregator->add(new Success($this->check)); + $resultGroup = new CliResult; + $resultGroup->add(new CliSuccess(new ArrayObject)); - $resultAggregator->add($resultResultAggregator); + $resultAggregator->add($resultGroup); $this->assertTrue($resultAggregator->isSuccessful()); - $resultResultAggregator->add(new Failure($this->check, 'nope')); + $resultGroup->add(new CliGenericFailure(new ArrayObject, 'nop')); $this->assertFalse($resultAggregator->isSuccessful()); } diff --git a/test/ResultRenderer/AbstractResultRendererTest.php b/test/ResultRenderer/AbstractResultRendererTest.php index f8f29b6a..842763ce 100644 --- a/test/ResultRenderer/AbstractResultRendererTest.php +++ b/test/ResultRenderer/AbstractResultRendererTest.php @@ -6,7 +6,7 @@ use PhpSchool\CliMenu\Terminal\TerminalInterface; use PhpSchool\PhpWorkshop\Factory\ResultRendererFactory; use PhpSchool\PSX\Factory; -use PHPUnit_Framework_TestCase; +use PHPUnit\Framework\TestCase; use PhpSchool\PhpWorkshop\ExerciseRepository; use PhpSchool\PhpWorkshop\ResultRenderer\ResultsRenderer; @@ -15,36 +15,54 @@ * @package PhpSchool\PhpWorkshopTest\ResultRenderer * @author Aydin Hassan */ -abstract class AbstractResultRendererTest extends PHPUnit_Framework_TestCase +abstract class AbstractResultRendererTest extends TestCase { /** - * @var TerminalInterface + * @var ResultRendererFactory */ - protected $terminal; + private $resultRendererFactory; + + /** + * @var ResultsRenderer + */ + private $renderer; + + /** + * @return ResultRendererFactory + */ + public function getResultRendererFactory() + { + if (null === $this->resultRendererFactory) { + $this->resultRendererFactory = new ResultRendererFactory; + } + + return $this->resultRendererFactory; + } /** * @return ResultsRenderer */ protected function getRenderer() { - $color = new Color; - $color->setForceStyle(true); - - $this->terminal = $this->createMock(TerminalInterface::class); - $exerciseRepo = $this->createMock(ExerciseRepository::class); - $this->terminal - ->expects($this->any()) - ->method('getWidth') - ->will($this->returnValue(20)); - - $syntaxHighlighter = (new Factory)->__invoke(); - return new ResultsRenderer( - 'appName', - $color, - $this->terminal, - $exerciseRepo, - $syntaxHighlighter, - new ResultRendererFactory - ); + if (null === $this->renderer) { + $color = new Color; + $color->setForceStyle(true); + + $terminal = $this->prophesize(TerminalInterface::class); + $terminal->getWidth()->willReturn(50); + $exerciseRepo = $this->createMock(ExerciseRepository::class); + + $syntaxHighlighter = (new Factory)->__invoke(); + $this->renderer = new ResultsRenderer( + 'appName', + $color, + $terminal->reveal(), + $exerciseRepo, + $syntaxHighlighter, + $this->getResultRendererFactory() + ); + } + + return $this->renderer; } } diff --git a/test/ResultRenderer/Cgi/RequestFailureRendererTest.php b/test/ResultRenderer/Cgi/RequestFailureRendererTest.php new file mode 100644 index 00000000..118327a8 --- /dev/null +++ b/test/ResultRenderer/Cgi/RequestFailureRendererTest.php @@ -0,0 +1,82 @@ + + */ +class RequestFailureRendererTest extends AbstractResultRendererTest +{ + public function testRenderWhenOnlyHeadersDifferent() + { + $failure = new RequestFailure( + $this->request(), + 'OUTPUT', + 'OUTPUT', + ['header1' => 'val', 'header2' => 'val'], + ['header1' => 'val'] + ); + $renderer = new RequestFailureRenderer($failure); + + $expected = "\e[33m\e[1mYOUR HEADERS:\e[0m\e[0m \e[31mheader1: val\e[0m\n"; + $expected .= "\n"; + $expected .= "\e[33m\e[1mEXPECTED HEADERS:\e[0m\e[0m \e[32mheader1: val\e[0m\n"; + $expected .= " \e[32mheader2: val\e[0m\n"; + + $this->assertSame($expected, $renderer->render($this->getRenderer())); + } + + public function testRenderWhenOnlyOutputDifferent() + { + $failure = new RequestFailure( + $this->request(), + 'EXPECTED OUTPUT', + 'ACTUAL OUTPUT', + ['header1' => 'val'], + ['header1' => 'val'] + ); + $renderer = new RequestFailureRenderer($failure); + + $expected = "\e[33m\e[1mYOUR OUTPUT:\e[0m\e[0m \e[31m\"ACTUAL OUTPUT\"\e[0m\n"; + $expected .= "\n"; + $expected .= "\e[33m\e[1mEXPECTED OUTPUT:\e[0m\e[0m \e[32m\"EXPECTED OUTPUT\"\e[0m\n"; + + $this->assertSame($expected, $renderer->render($this->getRenderer())); + } + + public function testRenderWhenOutputAndHeadersDifferent() + { + $failure = new RequestFailure( + $this->request(), + 'EXPECTED OUTPUT', + 'ACTUAL OUTPUT', + ['header1' => 'val', 'header2' => 'val'], + ['header1' => 'val'] + ); + $renderer = new RequestFailureRenderer($failure); + + $expected = "\e[33m\e[1mYOUR HEADERS:\e[0m\e[0m \e[31mheader1: val\e[0m\n"; + $expected .= "\n"; + $expected .= "\e[33m\e[1mEXPECTED HEADERS:\e[0m\e[0m \e[32mheader1: val\e[0m\n"; + $expected .= " \e[32mheader2: val\e[0m\n\n"; + $expected .= "- - - - - - - - -\n"; + $expected .= "\n"; + $expected .= "\e[33m\e[1mYOUR OUTPUT:\e[0m\e[0m \e[31m\"ACTUAL OUTPUT\"\e[0m\n"; + $expected .= "\n"; + $expected .= "\e[33m\e[1mEXPECTED OUTPUT:\e[0m\e[0m \e[32m\"EXPECTED OUTPUT\"\e[0m\n"; + + $this->assertSame($expected, $renderer->render($this->getRenderer())); + } + + private function request() + { + return (new Request('http://www.test.com')) + ->withMethod('POST') + ->withHeader('Content-Type', 'application/json'); + } +} diff --git a/test/ResultRenderer/CgiOutResultRendererTest.php b/test/ResultRenderer/CgiOutResultRendererTest.php deleted file mode 100644 index 542bdb90..00000000 --- a/test/ResultRenderer/CgiOutResultRendererTest.php +++ /dev/null @@ -1,188 +0,0 @@ - - */ -class CgiOutResultRendererTest extends AbstractResultRendererTest -{ - public function testRenderWhenOnlyHeadersDifferent() - { - $failure = new CgiOutRequestFailure( - $this->createMock(RequestInterface::class), - 'OUTPUT', - 'OUTPUT', - ['header1' => 'val', 'header2' => 'val'], - ['header1' => 'val'] - ); - $result = new CgiOutResult('Some Check', [$failure]); - $renderer = new CgiOutResultRenderer($result); - - $expected = "\n"; - $expected .= "\e[32m\e[4m\e[1mRequest 01\n\n"; - $expected .= "\e[0m\e[0m\e[0m \e[33m\e[1mACTUAL HEADERS:\e[0m\e[0m \e[31mheader1: val\e[0m\n\n"; - $expected .= " \e[33m\e[1mEXPECTED HEADERS:\e[0m\e[0m \e[39mheader1: val\e[0m\n"; - $expected .= " \e[39mheader2: val\e[0m\n\n"; - $expected .= "\e[33m────────────────────\e[0m\n"; - - $this->assertSame($expected, $renderer->render($this->getRenderer())); - } - - public function testRenderWhenOnlyOutputDifferent() - { - $failure = new CgiOutRequestFailure( - $this->createMock(RequestInterface::class), - 'EXPECTED OUTPUT', - 'ACTUAL OUTPUT', - ['header1' => 'val'], - ['header1' => 'val'] - ); - $result = new CgiOutResult('Some Check', [$failure]); - $renderer = new CgiOutResultRenderer($result); - - $expected = "\n"; - $expected .= "\e[32m\e[4m\e[1mRequest 01\n\n"; - $expected .= "\e[0m\e[0m\e[0m \e[33m\e[1mACTUAL CONTENT:\e[0m\e[0m \e[31m\"ACTUAL OUTPUT\"\e[0m\n\n"; - $expected .= " \e[33m\e[1mEXPECTED CONTENT:\e[0m\e[0m \e[39m\"EXPECTED OUTPUT\"\e[0m\n"; - $expected .= "\e[33m────────────────────\e[0m\n"; - - $this->assertSame($expected, $renderer->render($this->getRenderer())); - } - - public function testRenderWhenOutputAndHeadersDifferent() - { - $failure = new CgiOutRequestFailure( - $this->createMock(RequestInterface::class), - 'EXPECTED OUTPUT', - 'ACTUAL OUTPUT', - ['header1' => 'val', 'header2' => 'val'], - ['header1' => 'val'] - ); - $result = new CgiOutResult('Some Check', [$failure]); - $renderer = new CgiOutResultRenderer($result); - - $expected = "\n"; - $expected .= "\e[32m\e[4m\e[1mRequest 01\n\n"; - $expected .= "\e[0m\e[0m\e[0m \e[33m\e[1mACTUAL HEADERS:\e[0m\e[0m \e[31mheader1: val\e[0m\n\n"; - $expected .= " \e[33m\e[1mEXPECTED HEADERS:\e[0m\e[0m \e[39mheader1: val\e[0m\n"; - $expected .= " \e[39mheader2: val\e[0m\n\n"; - $expected .= "\e[1m\e[32m * * * * * * * * *\n\n"; - $expected .= "\e[0m\e[0m \e[33m\e[1mACTUAL CONTENT:\e[0m\e[0m \e[31m\"ACTUAL OUTPUT\"\e[0m\n\n"; - $expected .= " \e[33m\e[1mEXPECTED CONTENT:\e[0m\e[0m \e[39m\"EXPECTED OUTPUT\"\e[0m\n"; - $expected .= "\e[33m────────────────────\e[0m\n"; - - $this->assertSame($expected, $renderer->render($this->getRenderer())); - } - - public function testNothingIsRenderedForSuccess() - { - $failure = new CgiOutRequestFailure( - $this->createMock(RequestInterface::class), - 'EXPECTED OUTPUT', - 'ACTUAL OUTPUT', - ['header1' => 'val', 'header2' => 'val'], - ['header1' => 'val'] - ); - $result = new CgiOutResult('Some Check', [$failure, new Success('Successful')]); - $renderer = new CgiOutResultRenderer($result); - - $expected = "\n"; - $expected .= "\e[32m\e[4m\e[1mRequest 01\n\n"; - $expected .= "\e[0m\e[0m\e[0m \e[33m\e[1mACTUAL HEADERS:\e[0m\e[0m \e[31mheader1: val\e[0m\n\n"; - $expected .= " \e[33m\e[1mEXPECTED HEADERS:\e[0m\e[0m \e[39mheader1: val\e[0m\n"; - $expected .= " \e[39mheader2: val\e[0m\n\n"; - $expected .= "\e[1m\e[32m * * * * * * * * *\n\n"; - $expected .= "\e[0m\e[0m \e[33m\e[1mACTUAL CONTENT:\e[0m\e[0m \e[31m\"ACTUAL OUTPUT\"\e[0m\n\n"; - $expected .= " \e[33m\e[1mEXPECTED CONTENT:\e[0m\e[0m \e[39m\"EXPECTED OUTPUT\"\e[0m\n"; - $expected .= "\e[33m────────────────────\e[0m\n"; - - $this->assertSame($expected, $renderer->render($this->getRenderer())); - } - - public function testMultipleFailedRequests() - { - $failure1 = new CgiOutRequestFailure( - $this->createMock(RequestInterface::class), - 'EXPECTED OUTPUT 1', - 'ACTUAL OUTPUT 1', - ['header1' => 'val', 'header2' => 'val'], - ['header1' => 'val'] - ); - - $failure2 = new CgiOutRequestFailure( - $this->createMock(RequestInterface::class), - 'EXPECTED OUTPUT 2', - 'ACTUAL OUTPUT 2', - ['header1' => 'val', 'header2' => 'val'], - ['header1' => 'val'] - ); - $result = new CgiOutResult('Some Check', [$failure1, $failure2]); - $renderer = new CgiOutResultRenderer($result); - - $expected = "\n"; - $expected .= "\e[32m\e[4m\e[1mRequest 01\n\n"; - $expected .= "\e[0m\e[0m\e[0m \e[33m\e[1mACTUAL HEADERS:\e[0m\e[0m \e[31mheader1: val\e[0m\n\n"; - $expected .= " \e[33m\e[1mEXPECTED HEADERS:\e[0m\e[0m \e[39mheader1: val\e[0m\n"; - $expected .= " \e[39mheader2: val\e[0m\n\n"; - $expected .= "\e[1m\e[32m * * * * * * * * *\n\n"; - $expected .= "\e[0m\e[0m \e[33m\e[1mACTUAL CONTENT:\e[0m\e[0m \e[31m\"ACTUAL OUTPUT 1\"\e[0m\n\n"; - $expected .= " \e[33m\e[1mEXPECTED CONTENT:\e[0m\e[0m \e[39m\"EXPECTED OUTPUT 1\"\e[0m\n"; - $expected .= "\e[33m────────────────────\e[0m\n"; - $expected .= "\e[32m\e[4m\e[1mRequest 02\n\n"; - $expected .= "\e[0m\e[0m\e[0m \e[33m\e[1mACTUAL HEADERS:\e[0m\e[0m \e[31mheader1: val\e[0m\n\n"; - $expected .= " \e[33m\e[1mEXPECTED HEADERS:\e[0m\e[0m \e[39mheader1: val\e[0m\n"; - $expected .= " \e[39mheader2: val\e[0m\n\n"; - $expected .= "\e[1m\e[32m * * * * * * * * *\n\n"; - $expected .= "\e[0m\e[0m \e[33m\e[1mACTUAL CONTENT:\e[0m\e[0m \e[31m\"ACTUAL OUTPUT 2\"\e[0m\n\n"; - $expected .= " \e[33m\e[1mEXPECTED CONTENT:\e[0m\e[0m \e[39m\"EXPECTED OUTPUT 2\"\e[0m\n"; - $expected .= "\e[33m────────────────────\e[0m\n"; - - $this->assertSame($expected, $renderer->render($this->getRenderer())); - } - - public function testCodeExecutionFailureIsDelegatedToMainRenderer() - { - $failure = new CgiOutRequestFailure( - $this->createMock(RequestInterface::class), - 'EXPECTED OUTPUT', - 'ACTUAL OUTPUT', - ['header1' => 'val', 'header2' => 'val'], - ['header1' => 'val'] - ); - - $codeExecutionFailure = new Failure('Test Check', 'Code Execution Failure'); - $result = new CgiOutResult('Some Check', [$failure, $codeExecutionFailure]); - $renderer = new CgiOutResultRenderer($result); - - $expected = "\n"; - $expected .= "\e[32m\e[4m\e[1mRequest 01\n\n"; - $expected .= "\e[0m\e[0m\e[0m \e[33m\e[1mACTUAL HEADERS:\e[0m\e[0m \e[31mheader1: val\e[0m\n\n"; - $expected .= " \e[33m\e[1mEXPECTED HEADERS:\e[0m\e[0m \e[39mheader1: val\e[0m\n"; - $expected .= " \e[39mheader2: val\e[0m\n\n"; - $expected .= "\e[1m\e[32m * * * * * * * * *\n\n"; - $expected .= "\e[0m\e[0m \e[33m\e[1mACTUAL CONTENT:\e[0m\e[0m \e[31m\"ACTUAL OUTPUT\"\e[0m\n\n"; - $expected .= " \e[33m\e[1mEXPECTED CONTENT:\e[0m\e[0m \e[39m\"EXPECTED OUTPUT\"\e[0m\n"; - $expected .= "\e[33m────────────────────\e[0m\n"; - $expected .= "\e[32m\e[4m\e[1mRequest 02\n\n"; - $expected .= "\e[0m\e[0m\e[0m Code Execution Failure\n"; - $expected .= "\e[33m────────────────────\e[0m\n"; - - $this->assertSame($expected, $renderer->render($this->getRenderer())); - } -} diff --git a/test/ResultRenderer/CgiResultRendererTest.php b/test/ResultRenderer/CgiResultRendererTest.php new file mode 100644 index 00000000..d050de7c --- /dev/null +++ b/test/ResultRenderer/CgiResultRendererTest.php @@ -0,0 +1,232 @@ + + */ +class CgiResultRendererTest extends AbstractResultRendererTest +{ + public function testNothingIsOutputIfNoFailures() + { + $result = new CgiResult([new Success($this->request())]); + $renderer = new CgiResultRenderer($result, new RequestRenderer); + + $this->assertEmpty($renderer->render($this->getRenderer())); + } + + public function testRenderWithFailedRequest() + { + $failureRenderer = $this->prophesize(RequestFailureRenderer::class); + $failureRenderer->render($this->getRenderer())->willReturn("REQUEST FAILURE\n"); + + $this->getResultRendererFactory()->registerRenderer( + RequestFailure::class, + RequestFailureRenderer::class, + function (RequestFailure $failure) use ($failureRenderer) { + return $failureRenderer->reveal(); + } + ); + + $failure = new RequestFailure( + $this->request(), + 'EXPECTED OUTPUT', + 'ACTUAL OUTPUT', + ['header1' => 'val', 'header2' => 'val'], + ['header1' => 'val'] + ); + $result = new CgiResult([$failure]); + $renderer = new CgiResultRenderer($result, new RequestRenderer); + + $expected = "Some requests to your solution produced incorrect output!\n"; + $expected .= "\e[33m──────────────────────────────────────────────────\e[0m\n"; + $expected .= "\e[34m\e[4m\e[1mRequest 1\e[0m\e[0m\e[0m \e[1m\e[41m FAILED \e[0m\e[0m\n"; + $expected .= "\n"; + $expected .= "Request Details:\n"; + $expected .= "\n"; + $expected .= "URL: http://www.test.com\n"; + $expected .= "METHOD: POST\n"; + $expected .= "HEADERS: Host: www.test.com\n"; + $expected .= " Content-Type: application/json\n\n"; + $expected .= "REQUEST FAILURE\n\n"; + + $this->assertSame($expected, $renderer->render($this->getRenderer())); + } + + public function testMultipleFailedRequests() + { + $failureRenderer = $this->prophesize(RequestFailureRenderer::class); + $failureRenderer->render($this->getRenderer())->willReturn("REQUEST FAILURE\n"); + + $this->getResultRendererFactory()->registerRenderer( + RequestFailure::class, + RequestFailureRenderer::class, + function (RequestFailure $failure) use ($failureRenderer) { + return $failureRenderer->reveal(); + } + ); + + $failure1 = new RequestFailure( + $this->request(), + 'EXPECTED OUTPUT 1', + 'ACTUAL OUTPUT 1', + ['header1' => 'val', 'header2' => 'val'], + ['header1' => 'val'] + ); + + $failure2 = new RequestFailure( + $this->request(), + 'EXPECTED OUTPUT 2', + 'ACTUAL OUTPUT 2', + ['header1' => 'val', 'header2' => 'val'], + ['header1' => 'val'] + ); + $result = new CgiResult([$failure1, $failure2]); + $renderer = new CgiResultRenderer($result, new RequestRenderer); + + $expected = "Some requests to your solution produced incorrect output!\n"; + $expected .= "\e[33m──────────────────────────────────────────────────\e[0m\n"; + $expected .= "\e[34m\e[4m\e[1mRequest 1\e[0m\e[0m\e[0m \e[1m\e[41m FAILED \e[0m\e[0m\n"; + $expected .= "\n"; + $expected .= "Request Details:\n"; + $expected .= "\n"; + $expected .= "URL: http://www.test.com\n"; + $expected .= "METHOD: POST\n"; + $expected .= "HEADERS: Host: www.test.com\n"; + $expected .= " Content-Type: application/json\n"; + $expected .= "\n"; + $expected .= "REQUEST FAILURE\n\n"; + $expected .= "\e[33m──────────────────────────────────────────────────\e[0m\n"; + $expected .= "\e[34m\e[4m\e[1mRequest 2\e[0m\e[0m\e[0m \e[1m\e[41m FAILED \e[0m\e[0m\n"; + $expected .= "\n"; + $expected .= "Request Details:\n"; + $expected .= "\n"; + $expected .= "URL: http://www.test.com\n"; + $expected .= "METHOD: POST\n"; + $expected .= "HEADERS: Host: www.test.com\n"; + $expected .= " Content-Type: application/json\n"; + $expected .= "\n"; + $expected .= "REQUEST FAILURE\n\n"; + + $this->assertSame($expected, $renderer->render($this->getRenderer())); + } + + public function testRenderWithFailedRequestAndSuccess() + { + $failureRenderer = $this->prophesize(RequestFailureRenderer::class); + $failureRenderer->render($this->getRenderer())->willReturn("REQUEST FAILURE\n"); + + $this->getResultRendererFactory()->registerRenderer( + RequestFailure::class, + RequestFailureRenderer::class, + function (RequestFailure $failure) use ($failureRenderer) { + return $failureRenderer->reveal(); + } + ); + + $failure = new RequestFailure( + $this->request(), + 'EXPECTED OUTPUT', + 'ACTUAL OUTPUT', + ['header1' => 'val', 'header2' => 'val'], + ['header1' => 'val'] + ); + $result = new CgiResult([$failure, new Success($this->request())]); + $renderer = new CgiResultRenderer($result, new RequestRenderer); + + $expected = "Some requests to your solution produced incorrect output!\n"; + $expected .= "\e[33m──────────────────────────────────────────────────\e[0m\n"; + $expected .= "\e[34m\e[4m\e[1mRequest 1\e[0m\e[0m\e[0m \e[1m\e[41m FAILED \e[0m\e[0m\n"; + $expected .= "\n"; + $expected .= "Request Details:\n"; + $expected .= "\n"; + $expected .= "URL: http://www.test.com\n"; + $expected .= "METHOD: POST\n"; + $expected .= "HEADERS: Host: www.test.com\n"; + $expected .= " Content-Type: application/json\n\n"; + $expected .= "REQUEST FAILURE\n\n"; + + $this->assertSame($expected, $renderer->render($this->getRenderer())); + } + + public function testRenderWithFailedRequestAndGenericFailure() + { + + $requestFailureRenderer = $this->prophesize(RequestFailureRenderer::class); + $requestFailureRenderer->render($this->getRenderer())->willReturn("REQUEST FAILURE\n"); + + $this->getResultRendererFactory()->registerRenderer( + RequestFailure::class, + RequestFailureRenderer::class, + function (RequestFailure $failure) use ($requestFailureRenderer) { + return $requestFailureRenderer->reveal(); + } + ); + + $genericFailureRenderer = $this->prophesize(FailureRenderer::class); + $genericFailureRenderer->render($this->getRenderer())->willReturn("Code Execution Failure\n"); + + $this->getResultRendererFactory()->registerRenderer( + GenericFailure::class, + FailureRenderer::class, + function (GenericFailure $failure) use ($genericFailureRenderer) { + return $genericFailureRenderer->reveal(); + } + ); + + $failure = new RequestFailure( + $this->request(), + 'EXPECTED OUTPUT', + 'ACTUAL OUTPUT', + ['header1' => 'val', 'header2' => 'val'], + ['header1' => 'val'] + ); + + $codeExecutionFailure = new GenericFailure($this->request(), 'Code Execution Failure'); + $result = new CgiResult([$failure, $codeExecutionFailure]); + $renderer = new CgiResultRenderer($result, new RequestRenderer); + + $expected = "Some requests to your solution produced incorrect output!\n"; + $expected .= "\e[33m──────────────────────────────────────────────────\e[0m\n"; + $expected .= "\e[34m\e[4m\e[1mRequest 1\e[0m\e[0m\e[0m \e[1m\e[41m FAILED \e[0m\e[0m\n"; + $expected .= "\n"; + $expected .= "Request Details:\n"; + $expected .= "\n"; + $expected .= "URL: http://www.test.com\n"; + $expected .= "METHOD: POST\n"; + $expected .= "HEADERS: Host: www.test.com\n"; + $expected .= " Content-Type: application/json\n"; + $expected .= "\n"; + $expected .= "REQUEST FAILURE\n\n"; + $expected .= "\e[33m──────────────────────────────────────────────────\e[0m\n"; + $expected .= "\e[34m\e[4m\e[1mRequest 2\e[0m\e[0m\e[0m \e[1m\e[41m FAILED \e[0m\e[0m\n"; + $expected .= "\n"; + $expected .= "Request Details:\n"; + $expected .= "\n"; + $expected .= "URL: http://www.test.com\n"; + $expected .= "METHOD: POST\n"; + $expected .= "HEADERS: Host: www.test.com\n"; + $expected .= " Content-Type: application/json\n"; + $expected .= "\n"; + $expected .= "Code Execution Failure\n\n"; + + $this->assertSame($expected, $renderer->render($this->getRenderer())); + } + + private function request() + { + return (new Request('http://www.test.com')) + ->withMethod('POST') + ->withHeader('Content-Type', 'application/json'); + } +} diff --git a/test/ResultRenderer/Cli/RequestFailureRendererTest.php b/test/ResultRenderer/Cli/RequestFailureRendererTest.php new file mode 100644 index 00000000..102b8132 --- /dev/null +++ b/test/ResultRenderer/Cli/RequestFailureRendererTest.php @@ -0,0 +1,27 @@ + + */ +class RequestFailureRendererTest extends AbstractResultRendererTest +{ + public function testRender() + { + $failure = new RequestFailure(new ArrayObject, 'EXPECTED OUTPUT', 'ACTUAL OUTPUT'); + $renderer = new RequestFailureRenderer($failure); + + $expected = " \e[33m\e[1mYOUR OUTPUT:\e[0m\e[0m\n"; + $expected .= " \e[31m\"ACTUAL OUTPUT\"\e[0m\n\n"; + $expected .= " \e[33m\e[1mEXPECTED OUTPUT:\e[0m\e[0m\n"; + $expected .= " \e[32m\"EXPECTED OUTPUT\"\e[0m\n"; + + $this->assertEquals($expected, $renderer->render($this->getRenderer())); + } +} diff --git a/test/ResultRenderer/CliResultRendererTest.php b/test/ResultRenderer/CliResultRendererTest.php new file mode 100644 index 00000000..bfdf4e50 --- /dev/null +++ b/test/ResultRenderer/CliResultRendererTest.php @@ -0,0 +1,85 @@ + + */ +class CliResultRendererTest extends AbstractResultRendererTest +{ + public function testNothingIsOutputIfNoFailures() + { + $result = new CliResult([new Success(new ArrayObject)]); + $renderer = new CliResultRenderer($result, new RequestRenderer); + + $this->assertEmpty($renderer->render($this->getRenderer())); + } + + public function testRenderWithFailedRequest() + { + $failureRenderer = $this->prophesize(RequestFailureRenderer::class); + $failureRenderer->render($this->getRenderer())->willReturn("REQUEST FAILURE\n"); + + $this->getResultRendererFactory()->registerRenderer( + RequestFailure::class, + RequestFailureRenderer::class, + function (RequestFailure $failure) use ($failureRenderer) { + return $failureRenderer->reveal(); + } + ); + + $failure = new RequestFailure( + new ArrayObject, + 'EXPECTED OUTPUT', + 'ACTUAL OUTPUT' + ); + $result = new CliResult([$failure]); + $renderer = new CliResultRenderer($result, new RequestRenderer); + + $expected = "Some executions of your solution produced incorrect output!\n"; + $expected .= "\e[33m──────────────────────────────────────────────────\e[0m\n"; + $expected .= "\e[34m\e[4m\e[1mExecution 1\e[0m\e[0m\e[0m \e[1m\e[41m FAILED \e[0m\e[0m\n"; + $expected .= "\n"; + $expected .= "Arguments: None\n\nREQUEST FAILURE\n\n"; + + $this->assertSame($expected, $renderer->render($this->getRenderer())); + } + + public function testRenderWithFailedRequestWithMultipleArgs() + { + $failureRenderer = $this->prophesize(RequestFailureRenderer::class); + $failureRenderer->render($this->getRenderer())->willReturn("REQUEST FAILURE\n"); + + $this->getResultRendererFactory()->registerRenderer( + RequestFailure::class, + RequestFailureRenderer::class, + function (RequestFailure $failure) use ($failureRenderer) { + return $failureRenderer->reveal(); + } + ); + + $failure = new RequestFailure( + new ArrayObject(['one', 'two', 'three']), + 'EXPECTED OUTPUT', + 'ACTUAL OUTPUT' + ); + $result = new CliResult([$failure]); + $renderer = new CliResultRenderer($result, new RequestRenderer); + + $expected = "Some executions of your solution produced incorrect output!\n"; + $expected .= "\e[33m──────────────────────────────────────────────────\e[0m\n"; + $expected .= "\e[34m\e[4m\e[1mExecution 1\e[0m\e[0m\e[0m \e[1m\e[41m FAILED \e[0m\e[0m\n"; + $expected .= "\n"; + $expected .= "Arguments: \"one\", \"two\", \"three\"\n\nREQUEST FAILURE\n\n"; + + $this->assertSame($expected, $renderer->render($this->getRenderer())); + } +} diff --git a/test/ResultRenderer/FailureRendererTest.php b/test/ResultRenderer/FailureRendererTest.php index f6026adf..7417e43e 100644 --- a/test/ResultRenderer/FailureRendererTest.php +++ b/test/ResultRenderer/FailureRendererTest.php @@ -20,6 +20,6 @@ public function testRender() { $failure = new Failure($this->createMock(CheckInterface::class), 'Something went wrong'); $renderer = new FailureRenderer($failure); - $this->assertEquals(" Something went wrong\n", $renderer->render($this->getRenderer())); + $this->assertEquals(" Something went wrong\n", $renderer->render($this->getRenderer())); } } diff --git a/test/ResultRenderer/OutputFailureRendererTest.php b/test/ResultRenderer/OutputFailureRendererTest.php deleted file mode 100644 index 94aa3eed..00000000 --- a/test/ResultRenderer/OutputFailureRendererTest.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ -class OutputFailureRendererTest extends AbstractResultRendererTest -{ - public function testRender() - { - $failure = new StdOutFailure($this->createMock(CheckInterface::class), 'EXPECTED OUTPUT', 'ACTUAL OUTPUT'); - $renderer = new OutputFailureRenderer($failure); - - $expected = " ACTUAL\n"; - $expected .= " \"ACTUAL OUTPUT\"\n\n"; - $expected .= " EXPECTED\n"; - $expected .= " \"EXPECTED OUTPUT\"\n"; - - $this->assertEquals($expected, $renderer->render($this->getRenderer())); - } -} diff --git a/test/ResultRenderer/ResultsRendererTest.php b/test/ResultRenderer/ResultsRendererTest.php index f7533ee7..ff444b13 100644 --- a/test/ResultRenderer/ResultsRendererTest.php +++ b/test/ResultRenderer/ResultsRendererTest.php @@ -4,15 +4,24 @@ use Colors\Color; use PhpSchool\CliMenu\Terminal\TerminalInterface; +use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface; +use PhpSchool\PhpWorkshop\Exercise\ProvidesSolution; use PhpSchool\PhpWorkshop\ExerciseRepository; use PhpSchool\PhpWorkshop\Factory\ResultRendererFactory; +use PhpSchool\PhpWorkshop\Output\StdOutput; use PhpSchool\PhpWorkshop\Result\Failure; use PhpSchool\PhpWorkshop\Result\ResultInterface; +use PhpSchool\PhpWorkshop\Result\Success; +use PhpSchool\PhpWorkshop\ResultAggregator; +use PhpSchool\PhpWorkshop\ResultRenderer\FailureRenderer; use PhpSchool\PhpWorkshop\ResultRenderer\ResultRendererInterface; use PhpSchool\PhpWorkshop\ResultRenderer\ResultsRenderer; +use PhpSchool\PhpWorkshop\Solution\SingleFileSolution; +use PhpSchool\PhpWorkshop\UserState; use PhpSchool\PSX\Factory; use PhpSchool\PSX\SyntaxHighlighter; use PHPUnit_Framework_TestCase; +use Prophecy\Argument; /** * Class ResultsRendererTest @@ -21,22 +30,30 @@ */ class ResultsRendererTest extends PHPUnit_Framework_TestCase { + public function testRenderIndividualResult() { $color = new Color; $color->setForceStyle(true); + $resultRendererFactory = new ResultRendererFactory; + $resultRendererFactory->registerRenderer(Failure::class, FailureRenderer::class); + + $terminal = $this->prophesize(TerminalInterface::class); + $terminal->getWidth()->willReturn(30); + $renderer = new ResultsRenderer( 'app', $color, - $this->createMock(TerminalInterface::class), + $terminal->reveal(), new ExerciseRepository([]), (new Factory)->__invoke(), - new ResultRendererFactory + $resultRendererFactory ); + $result = new Failure('Failure', 'Some Failure'); - $this->assertSame(" Some Failure\n", $renderer->renderResult($result)); + $this->assertSame(" Some Failure\n", $renderer->renderResult($result)); } public function testLineBreak() @@ -44,16 +61,13 @@ public function testLineBreak() $color = new Color; $color->setForceStyle(true); - $terminal = $this->createMock(TerminalInterface::class); - $terminal - ->expects($this->once()) - ->method('getWidth') - ->will($this->returnValue(10)); + $terminal = $this->prophesize(TerminalInterface::class); + $terminal->getWidth()->willReturn(10); $renderer = new ResultsRenderer( 'app', $color, - $terminal, + $terminal->reveal(), new ExerciseRepository([]), (new Factory)->__invoke(), new ResultRendererFactory @@ -61,4 +75,282 @@ public function testLineBreak() $this->assertSame("\e[33m──────────\e[0m", $renderer->lineBreak()); } + + public function testRenderSuccess() + { + $color = new Color; + $color->setForceStyle(true); + + $resultRendererFactory = new ResultRendererFactory; + + $terminal = $this->prophesize(TerminalInterface::class); + $terminal->getWidth()->willReturn(100); + $terminal = $terminal->reveal(); + + $exerciseRepo = $this->prophesize(ExerciseRepository::class); + $exerciseRepo->count()->willReturn(2); + + $renderer = new ResultsRenderer( + 'app', + $color, + $terminal, + $exerciseRepo->reveal(), + (new Factory)->__invoke(), + $resultRendererFactory + ); + + $resultSet = new ResultAggregator; + $resultSet->add(new Success('Success 1!')); + $resultSet->add(new Success('Success 2!')); + + $this->expectOutputString($this->getExpectedOutput()); + + $renderer->render( + $resultSet, + $this->createMock(ExerciseInterface::class), + new UserState(['exercise1']), + new StdOutput($color, $terminal) + ); + } + + public function testRenderSuccessWithSolution() + { + $color = new Color; + $color->setForceStyle(true); + + $resultRendererFactory = new ResultRendererFactory; + + $terminal = $this->prophesize(TerminalInterface::class); + $terminal->getWidth()->willReturn(100); + $terminal = $terminal->reveal(); + + $exerciseRepo = $this->prophesize(ExerciseRepository::class); + $exerciseRepo->count()->willReturn(2); + + $tmpFile = sprintf('%s/%s/some-file', sys_get_temp_dir(), $this->getName()); + mkdir(dirname($tmpFile)); + file_put_contents($tmpFile, 'FILE CONTENTS'); + + $solution = new SingleFileSolution($tmpFile); + + $exercise = $this->prophesize(ExerciseInterface::class); + $exercise->willImplement(ProvidesSolution::class); + $exercise->getSolution()->willReturn($solution); + + $renderer = new ResultsRenderer( + 'app', + $color, + $terminal, + $exerciseRepo->reveal(), + (new Factory)->__invoke(), + $resultRendererFactory + ); + + $resultSet = new ResultAggregator; + $resultSet->add(new Success('Success 1!')); + $resultSet->add(new Success('Success 2!')); + + $this->expectOutputString($this->getExpectedOutput()); + + $renderer->render( + $resultSet, + $exercise->reveal(), + new UserState(['exercise1']), + new StdOutput($color, $terminal) + ); + + unlink($tmpFile); + rmdir(dirname($tmpFile)); + } + + public function testRenderSuccessWithPhpSolutionFileIsSyntaxHighlighted() + { + $color = new Color; + $color->setForceStyle(true); + + $resultRendererFactory = new ResultRendererFactory; + + $terminal = $this->prophesize(TerminalInterface::class); + $terminal->getWidth()->willReturn(100); + $terminal = $terminal->reveal(); + + $exerciseRepo = $this->prophesize(ExerciseRepository::class); + $exerciseRepo->count()->willReturn(2); + + $tmpFile = sprintf('%s/%s/some-file.php', sys_get_temp_dir(), $this->getName()); + mkdir(dirname($tmpFile)); + file_put_contents($tmpFile, 'FILE CONTENTS'); + + $solution = new SingleFileSolution($tmpFile); + + $exercise = $this->prophesize(ExerciseInterface::class); + $exercise->willImplement(ProvidesSolution::class); + $exercise->getSolution()->willReturn($solution); + + $syntaxHighlighter = $this->prophesize(SyntaxHighlighter::class); + $syntaxHighlighter->highlight('FILE CONTENTS')->willReturn('FILE CONTENTS'); + + $renderer = new ResultsRenderer( + 'app', + $color, + $terminal, + $exerciseRepo->reveal(), + $syntaxHighlighter->reveal(), + $resultRendererFactory + ); + + $resultSet = new ResultAggregator; + $resultSet->add(new Success('Success 1!')); + $resultSet->add(new Success('Success 2!')); + + $this->expectOutputString($this->getExpectedOutput()); + + $renderer->render( + $resultSet, + $exercise->reveal(), + new UserState(['exercise1']), + new StdOutput($color, $terminal) + ); + + unlink($tmpFile); + rmdir(dirname($tmpFile)); + } + + public function testRenderSuccessAndFailure() + { + $color = new Color; + $color->setForceStyle(true); + + $resultRendererFactory = new ResultRendererFactory; + $resultRendererFactory->registerRenderer(Failure::class, FailureRenderer::class, function (Failure $failure) { + $renderer = $this->prophesize(FailureRenderer::class); + $renderer->render(Argument::type(ResultsRenderer::class))->willReturn($failure->getReason() . "\n"); + return $renderer->reveal(); + }); + + $terminal = $this->prophesize(TerminalInterface::class); + $terminal->getWidth()->willReturn(100); + $terminal = $terminal->reveal(); + + $exerciseRepo = $this->prophesize(ExerciseRepository::class); + $exerciseRepo->count()->willReturn(2); + + $renderer = new ResultsRenderer( + 'app', + $color, + $terminal, + $exerciseRepo->reveal(), + (new Factory)->__invoke(), + $resultRendererFactory + ); + + $resultSet = new ResultAggregator; + $resultSet->add(new Success('Success 1!')); + $resultSet->add(new Failure('Check 1', 'Failure')); + $resultSet->add(new Failure('Check 2', 'Failure')); + + $this->expectOutputString($this->getExpectedOutput()); + + $renderer->render( + $resultSet, + $this->createMock(ExerciseInterface::class), + new UserState, + new StdOutput($color, $terminal) + ); + } + + public function testAllSuccessResultsAreHoistedToTheTop() + { + $color = new Color; + $color->setForceStyle(true); + + $resultRendererFactory = new ResultRendererFactory; + $resultRendererFactory->registerRenderer(Failure::class, FailureRenderer::class, function (Failure $failure) { + $renderer = $this->prophesize(FailureRenderer::class); + $renderer->render(Argument::type(ResultsRenderer::class))->willReturn($failure->getReason() . "\n"); + return $renderer->reveal(); + }); + + $terminal = $this->prophesize(TerminalInterface::class); + $terminal->getWidth()->willReturn(100); + $terminal = $terminal->reveal(); + + $exerciseRepo = $this->prophesize(ExerciseRepository::class); + $exerciseRepo->count()->willReturn(2); + + $renderer = new ResultsRenderer( + 'app', + $color, + $terminal, + $exerciseRepo->reveal(), + (new Factory)->__invoke(), + $resultRendererFactory + ); + + $resultSet = new ResultAggregator; + $resultSet->add(new Failure('Failure 1', 'Failure 1')); + $resultSet->add(new Success('Success 1!')); + $resultSet->add(new Failure('Failure 2', 'Failure 2')); + $resultSet->add(new Success('Success 2!')); + + $this->expectOutputString($this->getExpectedOutput()); + + $renderer->render( + $resultSet, + $this->createMock(ExerciseInterface::class), + new UserState, + new StdOutput($color, $terminal) + ); + } + + public function testRenderAllFailures() + { + $color = new Color; + $color->setForceStyle(true); + + $resultRendererFactory = new ResultRendererFactory; + $resultRendererFactory->registerRenderer(Failure::class, FailureRenderer::class, function (Failure $failure) { + $renderer = $this->prophesize(FailureRenderer::class); + $renderer->render(Argument::type(ResultsRenderer::class))->willReturn($failure->getReason() . "\n"); + return $renderer->reveal(); + }); + + $terminal = $this->prophesize(TerminalInterface::class); + $terminal->getWidth()->willReturn(100); + $terminal = $terminal->reveal(); + + $exerciseRepo = $this->prophesize(ExerciseRepository::class); + $exerciseRepo->count()->willReturn(2); + + $renderer = new ResultsRenderer( + 'app', + $color, + $terminal, + $exerciseRepo->reveal(), + (new Factory)->__invoke(), + $resultRendererFactory + ); + + $resultSet = new ResultAggregator; + $resultSet->add(new Failure('Failure 1', 'Failure 1')); + $resultSet->add(new Failure('Failure 2', 'Failure 2')); + + $this->expectOutputString($this->getExpectedOutput()); + + $renderer->render( + $resultSet, + $this->createMock(ExerciseInterface::class), + new UserState, + new StdOutput($color, $terminal) + ); + } + + /** + * @return string + */ + private function getExpectedOutput() + { + $name = camel_case_to_kebab_case($this->getName()); + return file_get_contents(sprintf('%s/../res/exercise-renderer/%s.txt', __DIR__, $name)); + } } diff --git a/test/Util/ArrayObjectTest.php b/test/Util/ArrayObjectTest.php index b8450fde..b98e1fd2 100644 --- a/test/Util/ArrayObjectTest.php +++ b/test/Util/ArrayObjectTest.php @@ -130,4 +130,13 @@ public function testSet() $this->assertNotSame($new, $arrayObject); $this->assertSame(['one' => 1, 'two' => 2, 'three' => 4], $new->getArrayCopy()); } + + public function testIsEmpty() + { + $arrayObject = new ArrayObject([1, 2, 3]); + self::assertFalse($arrayObject->isEmpty()); + + $arrayObject = new ArrayObject; + self::assertTrue($arrayObject->isEmpty()); + } } diff --git a/test/Util/RequestRendererTest.php b/test/Util/RequestRendererTest.php new file mode 100644 index 00000000..8b729581 --- /dev/null +++ b/test/Util/RequestRendererTest.php @@ -0,0 +1,75 @@ + + */ +class RequestRendererTest extends TestCase +{ + public function testWriteRequestWithHeaders() + { + $request = (new Request('http://www.time.com/api/pt?iso=2016-01-21T18:14:33+0000')) + ->withMethod('GET'); + + $expected = "URL: http://www.time.com/api/pt?iso=2016-01-21T18:14:33+0000\n"; + $expected .= "METHOD: GET\n"; + $expected .= "HEADERS: Host: www.time.com\n"; + + $this->assertEquals($expected, (new RequestRenderer)->renderRequest($request)); + } + + public function testWriteRequestWithNoHeaders() + { + $request = (new Request('/endpoint')) + ->withMethod('GET'); + + $expected = "URL: /endpoint\n"; + $expected .= "METHOD: GET\n"; + + $this->assertEquals($expected, (new RequestRenderer)->renderRequest($request)); + } + + public function testWriteRequestWithPostBodyJson() + { + $request = (new Request('/endpoint')) + ->withMethod('POST') + ->withHeader('Content-Type', 'application/json'); + + $request->getBody()->write( + json_encode(['data' => 'test', 'other_data' => 'test2']) + ); + + $expected = "URL: /endpoint\n"; + $expected .= "METHOD: POST\n"; + $expected .= "HEADERS: Content-Type: application/json\n"; + $expected .= "BODY: {\n"; + $expected .= " \"data\": \"test\",\n"; + $expected .= " \"other_data\": \"test2\"\n"; + $expected .= "}\n"; + + $this->assertEquals($expected, (new RequestRenderer)->renderRequest($request)); + } + + public function testWriteRequestWithPostBodyUrlEncoded() + { + $request = (new Request('/endpoint')) + ->withMethod('POST') + ->withHeader('Content-Type', 'application/x-www-form-urlencoded'); + + $request->getBody()->write( + http_build_query(['data' => 'test', 'other_data' => 'test2']) + ); + + $expected = "URL: /endpoint\n"; + $expected .= "METHOD: POST\n"; + $expected .= "HEADERS: Content-Type: application/x-www-form-urlencoded\n"; + $expected .= "BODY: data=test&other_data=test2\n"; + + $this->assertEquals($expected, (new RequestRenderer)->renderRequest($request)); + } +} diff --git a/test/res/composer/good-solution/vendor/.gitkeep b/test/res/composer/good-solution/vendor/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/test/res/composer/no-klein/vendor/.gitkeep b/test/res/composer/no-klein/vendor/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/test/res/composer/no-stringy/vendor/.gitkeep b/test/res/composer/no-stringy/vendor/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/test/res/composer/no-vendor/composer.json b/test/res/composer/no-vendor/composer.json new file mode 100644 index 00000000..8c5208d4 --- /dev/null +++ b/test/res/composer/no-vendor/composer.json @@ -0,0 +1,14 @@ +{ + "name": "learn-you-php/dependency-heaven", + "description": "String manipulation with Composer", + "require": { + "danielstjules/stringy": "^2.1", + "klein/klein": "^2.1" + }, + "authors": [ + { + "name": "Michael Woodward", + "email": "mikeymike.mw@gmail.com" + } + ] +} diff --git a/test/res/composer/no-vendor/composer.lock b/test/res/composer/no-vendor/composer.lock new file mode 100644 index 00000000..bf58d5ba --- /dev/null +++ b/test/res/composer/no-vendor/composer.lock @@ -0,0 +1,75 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "4264c4fb6a73b5b782bb530a1c3a712d", + "content-hash": "6faa5b788af1a8b4e85e69c922d8fb71", + "packages": [ + { + "name": "klein/klein", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/chriso/klein.php.git", + "reference": "5dc073f05adb67ee09680545b4cc7b0590d2559e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chriso/klein.php/zipball/5dc073f05adb67ee09680545b4cc7b0590d2559e", + "reference": "5dc073f05adb67ee09680545b4cc7b0590d2559e", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/php-code-coverage": "1.2.x", + "phpunit/phpunit": "3.7.x", + "squizlabs/php_codesniffer": "1.4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Klein\\": "src/Klein/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris O'Hara", + "email": "cohara87@gmail.com", + "homepage": "http://chris6f.com/", + "role": "Creator/Developer" + }, + { + "name": "Trevor Suarez", + "email": "rican7@gmail.com", + "homepage": "http://about.me/tnsuarez", + "role": "Contributor/Developer" + } + ], + "description": "A lightning fast router for PHP", + "homepage": "https://github.com/chriso/klein.php", + "keywords": [ + "boilerplate", + "router", + "routing", + "sinatra" + ], + "time": "2014-11-07 07:35:23" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/test/res/composer/no-vendor/solution.php b/test/res/composer/no-vendor/solution.php new file mode 100644 index 00000000..1376dd70 --- /dev/null +++ b/test/res/composer/no-vendor/solution.php @@ -0,0 +1,23 @@ +respond('POST', '/reverse', function (Request $req, Response $res) { + $res->json(['result' => (new S($req->param('data', '')))->reverse()]); +}); + +$klein->respond('POST', '/swapcase', function (Request $req, Response $res) { + $res->json(['result' => (new S($req->param('data', '')))->swapCase()]); +}); + +$klein->respond('POST', '/titleize', function (Request $req, Response $res) { + $res->json(['result' => (new S($req->param('data', '')))->titleize()]); +}); diff --git a/test/res/exercise-renderer/test-all-success-results-are-hoisted-to-the-top.txt b/test/res/exercise-renderer/test-all-success-results-are-hoisted-to-the-top.txt new file mode 100644 index 00000000..f0769750 --- /dev/null +++ b/test/res/exercise-renderer/test-all-success-results-are-hoisted-to-the-top.txt @@ -0,0 +1,30 @@ + + *** RESULTS *** + +   +  ✔ Check: Success 1!  +   + +   +  ✔ Check: Success 2!  +   + +   +  ✗ Check: Failure 1  +   + +Failure 1 +   +  ✗ Check: Failure 2  +   + +Failure 2 +──────────────────────────────────────────────────────────────────────────────────────────────────── + +  + Your solution was unsuccessful!  +  + + Your solution to didn't pass. Try again! + + diff --git a/test/res/exercise-renderer/test-render-all-failures.txt b/test/res/exercise-renderer/test-render-all-failures.txt new file mode 100644 index 00000000..b2c21026 --- /dev/null +++ b/test/res/exercise-renderer/test-render-all-failures.txt @@ -0,0 +1,22 @@ + + *** RESULTS *** + +   +  ✗ Check: Failure 1  +   + +Failure 1 +   +  ✗ Check: Failure 2  +   + +Failure 2 +──────────────────────────────────────────────────────────────────────────────────────────────────── + +  + Your solution was unsuccessful!  +  + + Your solution to didn't pass. Try again! + + diff --git a/test/res/exercise-renderer/test-render-success-and-failure.txt b/test/res/exercise-renderer/test-render-success-and-failure.txt new file mode 100644 index 00000000..af68460b --- /dev/null +++ b/test/res/exercise-renderer/test-render-success-and-failure.txt @@ -0,0 +1,26 @@ + + *** RESULTS *** + +   +  ✔ Check: Success 1!  +   + +   +  ✗ Check: Check 1  +   + +Failure +   +  ✗ Check: Check 2  +   + +Failure +──────────────────────────────────────────────────────────────────────────────────────────────────── + +  + Your solution was unsuccessful!  +  + + Your solution to didn't pass. Try again! + + diff --git a/test/res/exercise-renderer/test-render-success-with-php-solution-file-is-syntax-highlighted.txt b/test/res/exercise-renderer/test-render-success-with-php-solution-file-is-syntax-highlighted.txt new file mode 100644 index 00000000..5f3cb88c --- /dev/null +++ b/test/res/exercise-renderer/test-render-success-with-php-solution-file-is-syntax-highlighted.txt @@ -0,0 +1,28 @@ + + *** RESULTS *** + +   +  ✔ Check: Success 1!  +   + +   +  ✔ Check: Success 2!  +   + +──────────────────────────────────────────────────────────────────────────────────────────────────── + +  + PASS!  +  + + Here's the official solution in case you want to compare notes: + +──────────────────────────────────────────────────────────────────────────────────────────────────── +some-file.php + +FILE CONTENTS +──────────────────────────────────────────────────────────────────────────────────────────────────── + + You have 1 challenges left. + Type "app" and hit enter to show the menu. + diff --git a/test/res/exercise-renderer/test-render-success-with-solution.txt b/test/res/exercise-renderer/test-render-success-with-solution.txt new file mode 100644 index 00000000..47afaf8b --- /dev/null +++ b/test/res/exercise-renderer/test-render-success-with-solution.txt @@ -0,0 +1,28 @@ + + *** RESULTS *** + +   +  ✔ Check: Success 1!  +   + +   +  ✔ Check: Success 2!  +   + +──────────────────────────────────────────────────────────────────────────────────────────────────── + +  + PASS!  +  + + Here's the official solution in case you want to compare notes: + +──────────────────────────────────────────────────────────────────────────────────────────────────── +some-file + +FILE CONTENTS +──────────────────────────────────────────────────────────────────────────────────────────────────── + + You have 1 challenges left. + Type "app" and hit enter to show the menu. + diff --git a/test/res/exercise-renderer/test-render-success.txt b/test/res/exercise-renderer/test-render-success.txt new file mode 100644 index 00000000..94ab89d0 --- /dev/null +++ b/test/res/exercise-renderer/test-render-success.txt @@ -0,0 +1,21 @@ + + *** RESULTS *** + +   +  ✔ Check: Success 1!  +   + +   +  ✔ Check: Success 2!  +   + +──────────────────────────────────────────────────────────────────────────────────────────────────── + +  + PASS!  +  + + + You have 1 challenges left. + Type "app" and hit enter to show the menu. +