This commit is contained in:
2020-02-01 16:47:12 +07:00
commit 4c619ad6e6
16739 changed files with 3329179 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
<?php
namespace Codeception\PHPUnit;
/**
* Printer implementing this interface prints output to console, thus should be marked as printer and not just a logger
*
* Interface ConsolePrinter
* @package Codeception\PHPUnit
*/
interface ConsolePrinter
{
public function write($buffer);
public function printResult(\PHPUnit\Framework\TestResult $result);
}

View File

@@ -0,0 +1,74 @@
<?php
namespace Codeception\PHPUnit\Constraint;
use Codeception\Exception\ElementNotFound;
use Codeception\Lib\Console\Message;
use Symfony\Component\DomCrawler\Crawler as DomCrawler;
use SebastianBergmann\Comparator\ComparisonFailure;
class Crawler extends Page
{
protected function matches($nodes)
{
/** @var $nodes DomCrawler * */
if (!$nodes->count()) {
return false;
}
if ($this->string === '') {
return true;
}
foreach ($nodes as $node) {
if (parent::matches($node->nodeValue)) {
return true;
}
}
return false;
}
protected function fail($nodes, $selector, ComparisonFailure $comparisonFailure = null)
{
/** @var $nodes DomCrawler * */
if (!$nodes->count()) {
throw new ElementNotFound($selector, 'Element located either by name, CSS or XPath');
}
$output = "Failed asserting that any element by '$selector'";
$output .= $this->uriMessage('on page');
$output .= " ";
if ($nodes->count() < 10) {
$output .= $this->nodesList($nodes);
} else {
$message = new Message("[total %s elements]");
$output .= $message->with($nodes->count())->getMessage();
}
$output .= "\ncontains text '{$this->string}'";
throw new \PHPUnit\Framework\ExpectationFailedException(
$output,
$comparisonFailure
);
}
protected function failureDescription($other)
{
$desc = '';
foreach ($other as $o) {
$desc .= parent::failureDescription($o->textContent);
}
return $desc;
}
protected function nodesList(DomCrawler $nodes, $contains = null)
{
$output = "";
foreach ($nodes as $node) {
if ($contains && strpos($node->nodeValue, $contains) === false) {
continue;
}
$output .= "\n+ " . $node->C14N();
}
return $output;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Codeception\PHPUnit\Constraint;
use SebastianBergmann\Comparator\ComparisonFailure;
class CrawlerNot extends Crawler
{
protected function matches($nodes)
{
return !parent::matches($nodes);
}
protected function fail($nodes, $selector, ComparisonFailure $comparisonFailure = null)
{
if (!$this->string) {
throw new \PHPUnit\Framework\ExpectationFailedException(
"Element '$selector' was found",
$comparisonFailure
);
}
/** @var $nodes DomCrawler * */
$output = "There was '$selector' element";
$output .= $this->uriMessage('on page');
$output .= $this->nodesList($nodes, $this->string);
$output .= "\ncontaining '{$this->string}'";
throw new \PHPUnit\Framework\ExpectationFailedException(
$output,
$comparisonFailure
);
}
public function toString()
{
if ($this->string) {
return 'that contains text "' . $this->string . '"';
}
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Codeception\PHPUnit\Constraint;
use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Comparator\ArrayComparator;
use SebastianBergmann\Comparator\Factory;
use Codeception\Util\JsonArray;
class JsonContains extends \PHPUnit\Framework\Constraint\Constraint
{
/**
* @var
*/
protected $expected;
public function __construct(array $expected)
{
parent::__construct();
$this->expected = $expected;
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other Value or object to evaluate.
*
* @return bool
*/
protected function matches($other)
{
$jsonResponseArray = new JsonArray($other);
if (!is_array($jsonResponseArray->toArray())) {
throw new \PHPUnit\Framework\AssertionFailedError('JSON response is not an array: ' . $other);
}
if ($jsonResponseArray->containsArray($this->expected)) {
return true;
}
$comparator = new ArrayComparator();
$comparator->setFactory(new Factory);
try {
$comparator->assertEquals($this->expected, $jsonResponseArray->toArray());
} catch (ComparisonFailure $failure) {
throw new \PHPUnit\Framework\ExpectationFailedException(
"Response JSON does not contain the provided JSON\n",
$failure
);
}
}
/**
* Returns a string representation of the constraint.
*
* @return string
*/
public function toString()
{
//unused
return '';
}
protected function failureDescription($other)
{
//unused
return '';
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Codeception\PHPUnit\Constraint;
use Codeception\Util\JsonType as JsonTypeUtil;
use Codeception\Util\JsonArray;
class JsonType extends \PHPUnit\Framework\Constraint\Constraint
{
protected $jsonType;
private $match;
public function __construct(array $jsonType, $match = true)
{
parent::__construct();
$this->jsonType = $jsonType;
$this->match = $match;
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $jsonArray Value or object to evaluate.
*
* @return bool
*/
protected function matches($jsonArray)
{
if ($jsonArray instanceof JsonArray) {
$jsonArray = $jsonArray->toArray();
}
$matched = (new JsonTypeUtil($jsonArray))->matches($this->jsonType);
if ($this->match) {
if ($matched !== true) {
throw new \PHPUnit\Framework\ExpectationFailedException($matched);
}
} else {
if ($matched === true) {
throw new \PHPUnit\Framework\ExpectationFailedException('Unexpectedly response matched: ' . json_encode($jsonArray));
}
}
return true;
}
/**
* Returns a string representation of the constraint.
*
* @return string
*/
public function toString()
{
//unused
return '';
}
protected function failureDescription($other)
{
//unused
return '';
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace Codeception\PHPUnit\Constraint;
use Codeception\Lib\Console\Message;
class Page extends \PHPUnit\Framework\Constraint\Constraint
{
protected $uri;
protected $string;
public function __construct($string, $uri = '')
{
parent::__construct();
$this->string = $this->normalizeText((string)$string);
$this->uri = $uri;
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other Value or object to evaluate.
*
* @return bool
*/
protected function matches($other)
{
$other = $this->normalizeText($other);
return mb_stripos($other, $this->string, null, 'UTF-8') !== false;
}
/**
* @param $text
* @return string
*/
private function normalizeText($text)
{
$text = strtr($text, "\r\n", " ");
return trim(preg_replace('/\\s{2,}/', ' ', $text));
}
/**
* Returns a string representation of the constraint.
*
* @return string
*/
public function toString()
{
return sprintf(
'contains "%s"',
$this->string
);
}
protected function failureDescription($pageContent)
{
$message = $this->uriMessage('on page');
$message->append("\n--> ");
$message->append(substr($pageContent, 0, 300));
if (strlen($pageContent) > 300) {
$debugMessage = new Message(
"[Content too long to display. See complete response in '" . codecept_output_dir() . "' directory]"
);
$message->append("\n")->append($debugMessage);
}
$message->append("\n--> ");
return $message->getMessage() . $this->toString();
}
protected function uriMessage($onPage = "")
{
if (!$this->uri) {
return new Message('');
}
$message = new Message($this->uri);
$message->prepend(" $onPage ");
return $message;
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace Codeception\PHPUnit\Constraint;
use Codeception\Exception\ElementNotFound;
use Codeception\Lib\Console\Message;
use Codeception\Util\Locator;
use SebastianBergmann\Comparator\ComparisonFailure;
class WebDriver extends Page
{
protected function matches($nodes)
{
if (!count($nodes)) {
return false;
}
if ($this->string === '') {
return true;
}
foreach ($nodes as $node) {
/** @var $node \WebDriverElement * */
if (!$node->isDisplayed()) {
continue;
}
if (parent::matches(htmlspecialchars_decode($node->getText()))) {
return true;
}
}
return false;
}
protected function fail($nodes, $selector, ComparisonFailure $comparisonFailure = null)
{
if (!count($nodes)) {
throw new ElementNotFound($selector, 'Element located either by name, CSS or XPath');
}
$output = "Failed asserting that any element by " . Locator::humanReadableString($selector);
$output .= $this->uriMessage('on page');
if (count($nodes) < 5) {
$output .= "\nElements: ";
$output .= $this->nodesList($nodes);
} else {
$message = new Message("[total %s elements]");
$output .= $message->with(count($nodes));
}
$output .= "\ncontains text '" . $this->string . "'";
throw new \PHPUnit\Framework\ExpectationFailedException(
$output,
$comparisonFailure
);
}
protected function failureDescription($nodes)
{
$desc = '';
foreach ($nodes as $node) {
$desc .= parent::failureDescription($node->getText());
}
return $desc;
}
protected function nodesList($nodes, $contains = null)
{
$output = "";
foreach ($nodes as $node) {
if ($contains && strpos($node->getText(), $contains) === false) {
continue;
}
/** @var $node \WebDriverElement * */
$message = new Message("\n+ <%s> %s");
$output .= $message->with($node->getTagName(), $node->getText());
}
return $output;
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Codeception\PHPUnit\Constraint;
use SebastianBergmann\Comparator\ComparisonFailure;
use Codeception\Util\Locator;
class WebDriverNot extends WebDriver
{
protected function matches($nodes)
{
return !parent::matches($nodes);
}
protected function fail($nodes, $selector, ComparisonFailure $comparisonFailure = null)
{
$selectorString = Locator::humanReadableString($selector);
if (!$this->string) {
throw new \PHPUnit\Framework\ExpectationFailedException(
"Element $selectorString was found",
$comparisonFailure
);
}
$output = "There was $selectorString element";
$output .= $this->uriMessage("on page");
$output .= $this->nodesList($nodes, $this->string);
$output .= "\ncontaining '{$this->string}'";
throw new \PHPUnit\Framework\ExpectationFailedException(
$output,
$comparisonFailure
);
}
public function toString()
{
if ($this->string) {
return 'that contains text "' . $this->string . '"';
}
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Codeception\PHPUnit;
use Codeception\Test\Descriptor;
/**
* Extended Filter Test from PHPUnit to use Codeception's Descriptor to locate tests.
*
* Class FilterTest
* @package Codeception\PHPUnit
*/
class FilterTest extends \PHPUnit\Runner\Filter\NameFilterIterator
{
public function accept()
{
$test = $this->getInnerIterator()->current();
if ($test instanceof \PHPUnit\Framework\TestSuite) {
return true;
}
$name = Descriptor::getTestSignature($test);
$index = Descriptor::getTestDataSetIndex($test);
if (!is_null($index)) {
$name .= " with data set #{$index}";
}
$accepted = preg_match($this->filter, $name, $matches);
// This fix the issue when an invalid dataprovider method generate a warning
// See issue https://github.com/Codeception/Codeception/issues/4888
if($test instanceof \PHPUnit\Framework\WarningTestCase) {
$message = $test->getMessage();
$accepted = preg_match($this->filter, $message, $matches);
}
if ($accepted && isset($this->filterMax)) {
$set = end($matches);
$accepted = $set >= $this->filterMin && $set <= $this->filterMax;
}
return $accepted;
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Codeception\PHPUnit;
class Init
{
/**
* @api
*/
public static function init()
{
require_once __DIR__ . DIRECTORY_SEPARATOR . 'shim.php';
}
}

View File

@@ -0,0 +1,135 @@
<?php
namespace Codeception\PHPUnit;
use Codeception\Event\FailEvent;
use Codeception\Event\SuiteEvent;
use Codeception\Event\TestEvent;
use Codeception\Events;
use Codeception\TestInterface;
use Exception;
use PHPUnit\Framework\Test;
use Symfony\Component\EventDispatcher\EventDispatcher;
class Listener implements \PHPUnit\Framework\TestListener
{
/**
* @var \Symfony\Component\EventDispatcher\EventDispatcher
*/
protected $dispatcher;
protected $unsuccessfulTests = [];
protected $skippedTests = [];
protected $startedTests = [];
public function __construct(EventDispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
/**
* Risky test.
*
* @param PHPUnit\Framework\Test $test
* @param Exception $e
* @param float $time
* @since Method available since Release 4.0.0
*/
public function addRiskyTest(\PHPUnit\Framework\Test $test, Exception $e, $time)
{
}
public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time)
{
$this->unsuccessfulTests[] = spl_object_hash($test);
$this->fire(Events::TEST_FAIL, new FailEvent($test, $time, $e));
}
public function addError(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->unsuccessfulTests[] = spl_object_hash($test);
$this->fire(Events::TEST_ERROR, new FailEvent($test, $time, $e));
}
// This method was added in PHPUnit 6
public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, $time)
{
$this->unsuccessfulTests[] = spl_object_hash($test);
$this->fire(Events::TEST_WARNING, new FailEvent($test, $time, $e));
}
public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
if (in_array(spl_object_hash($test), $this->skippedTests)) {
return;
}
$this->unsuccessfulTests[] = spl_object_hash($test);
$this->fire(Events::TEST_INCOMPLETE, new FailEvent($test, $time, $e));
$this->skippedTests[] = spl_object_hash($test);
}
public function addSkippedTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
if (in_array(spl_object_hash($test), $this->skippedTests)) {
return;
}
$this->unsuccessfulTests[] = spl_object_hash($test);
$this->fire(Events::TEST_SKIPPED, new FailEvent($test, $time, $e));
$this->skippedTests[] = spl_object_hash($test);
}
public function startTestSuite(\PHPUnit\Framework\TestSuite $suite)
{
$this->dispatcher->dispatch('suite.start', new SuiteEvent($suite));
}
public function endTestSuite(\PHPUnit\Framework\TestSuite $suite)
{
$this->dispatcher->dispatch('suite.end', new SuiteEvent($suite));
}
public function startTest(\PHPUnit\Framework\Test $test)
{
$this->dispatcher->dispatch(Events::TEST_START, new TestEvent($test));
if (!$test instanceof TestInterface) {
return;
}
if ($test->getMetadata()->isBlocked()) {
return;
}
try {
$this->startedTests[] = spl_object_hash($test);
$this->fire(Events::TEST_BEFORE, new TestEvent($test));
} catch (\PHPUnit\Framework\IncompleteTestError $e) {
$test->getTestResultObject()->addFailure($test, $e, 0);
} catch (\PHPUnit\Framework\SkippedTestError $e) {
$test->getTestResultObject()->addFailure($test, $e, 0);
} catch (\Exception $e) {
$test->getTestResultObject()->addError($test, $e, 0);
}
}
public function endTest(\PHPUnit\Framework\Test $test, $time)
{
$hash = spl_object_hash($test);
if (!in_array($hash, $this->unsuccessfulTests)) {
$this->fire(Events::TEST_SUCCESS, new TestEvent($test, $time));
}
if (in_array($hash, $this->startedTests)) {
$this->fire(Events::TEST_AFTER, new TestEvent($test, $time));
}
$this->dispatcher->dispatch(Events::TEST_END, new TestEvent($test, $time));
}
protected function fire($event, TestEvent $eventType)
{
$test = $eventType->getTest();
if ($test instanceof TestInterface) {
foreach ($test->getMetadata()->getGroups() as $group) {
$this->dispatcher->dispatch($event . '.' . $group, $eventType);
}
}
$this->dispatcher->dispatch($event, $eventType);
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace Codeception\PHPUnit\Log;
use Codeception\Configuration;
use Codeception\Test\Interfaces\Reported;
use Codeception\Test\Test;
class JUnit extends \PHPUnit\Util\Log\JUnit
{
protected $strictAttributes = ['file', 'name', 'class'];
public function startTest(\PHPUnit\Framework\Test $test)
{
if (!$test instanceof Reported) {
return parent::startTest($test);
}
$this->currentTestCase = $this->document->createElement('testcase');
$isStrict = Configuration::config()['settings']['strict_xml'];
foreach ($test->getReportFields() as $attr => $value) {
if ($isStrict and !in_array($attr, $this->strictAttributes)) {
continue;
}
$this->currentTestCase->setAttribute($attr, $value);
}
}
public function endTest(\PHPUnit\Framework\Test $test, $time)
{
if ($this->currentTestCase !== null and $test instanceof Test) {
$numAssertions = $test->getNumAssertions();
$this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions;
$this->currentTestCase->setAttribute(
'assertions',
$numAssertions
);
}
parent::endTest($test, $time);
}
}

View File

@@ -0,0 +1,117 @@
<?php
namespace PHPUnit\Util;
// @codingStandardsIgnoreStart
class Filter
{
// @codingStandardsIgnoreEnd
protected static $filteredClassesPattern = [
'Symfony\Component\Console',
'Codeception\Command\\',
'Codeception\TestCase\\',
];
public static function getFilteredStackTrace($e, $asString = true, $filter = true)
{
$stackTrace = $asString ? '' : [];
$trace = $e->getPrevious() ? $e->getPrevious()->getTrace() : $e->getTrace();
if ($e instanceof \PHPUnit\Framework\ExceptionWrapper) {
$trace = $e->getSerializableTrace();
}
$eFile = $e->getFile();
$eLine = $e->getLine();
if (!self::frameExists($trace, $eFile, $eLine)) {
array_unshift(
$trace,
['file' => $eFile, 'line' => $eLine]
);
}
foreach ($trace as $step) {
if (self::classIsFiltered($step) and $filter) {
continue;
}
if (self::fileIsFiltered($step) and $filter) {
continue;
}
if (!$asString) {
$stackTrace[] = $step;
continue;
}
if (!isset($step['file'])) {
continue;
}
$stackTrace .= $step['file'] . ':' . $step['line'] . "\n";
}
return $stackTrace;
}
protected static function classIsFiltered($step)
{
if (!isset($step['class'])) {
return false;
}
$className = $step['class'];
foreach (self::$filteredClassesPattern as $filteredClassName) {
if (strpos($className, $filteredClassName) === 0) {
return true;
}
}
return false;
}
protected static function fileIsFiltered($step)
{
if (!isset($step['file'])) {
return false;
}
if (strpos($step['file'], 'codecept.phar/') !== false) {
return true;
}
if (strpos($step['file'], 'vendor' . DIRECTORY_SEPARATOR . 'phpunit') !== false) {
return true;
}
if (strpos($step['file'], 'vendor' . DIRECTORY_SEPARATOR . 'codeception') !== false) {
return true;
}
$modulePath = 'src' . DIRECTORY_SEPARATOR . 'Codeception' . DIRECTORY_SEPARATOR . 'Module';
if (strpos($step['file'], $modulePath) !== false) {
return false; // don`t filter modules
}
if (strpos($step['file'], 'src' . DIRECTORY_SEPARATOR . 'Codeception' . DIRECTORY_SEPARATOR) !== false) {
return true;
}
return false;
}
/**
* @param array $trace
* @param string $file
* @param int $line
*
* @return bool
*/
private static function frameExists(array $trace, $file, $line)
{
foreach ($trace as $frame) {
if (isset($frame['file']) && $frame['file'] == $file &&
isset($frame['line']) && $frame['line'] == $line) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,96 @@
<?php
namespace Codeception\PHPUnit;
use \PHPUnit\Framework\AssertionFailedError;
use \PHPUnit\Framework\Test;
use \PHPUnit\Runner\BaseTestRunner;
class ResultPrinter extends \PHPUnit\Util\TestDox\ResultPrinter
{
/**
* An error occurred.
*
* @param \PHPUnit\Framework\Test $test
* @param \Exception $e
* @param float $time
*/
public function addError(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR;
$this->failed++;
}
/**
* A failure occurred.
*
* @param \PHPUnit\Framework\Test $test
* @param \PHPUnit\Framework\AssertionFailedError $e
* @param float $time
*/
public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time)
{
$this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE;
$this->failed++;
}
/**
* A warning occurred.
*
* @param \PHPUnit\Framework\Test $test
* @param \PHPUnit\Framework\Warning $e
* @param float $time
*/
public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, $time)
{
$this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING;
$this->warned++;
}
/**
* Incomplete test.
*
* @param \PHPUnit\Framework\Test $test
* @param \Exception $e
* @param float $time
*/
public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE;
$this->incomplete++;
}
/**
* Risky test.
*
* @param \PHPUnit\Framework\Test $test
* @param \Exception $e
* @param float $time
*
* @since Method available since Release 4.0.0
*/
public function addRiskyTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY;
$this->risky++;
}
/**
* Skipped test.
*
* @param \PHPUnit\Framework\Test $test
* @param \Exception $e
* @param float $time
*
* @since Method available since Release 3.0.0
*/
public function addSkippedTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED;
$this->skipped++;
}
public function startTest(\PHPUnit\Framework\Test $test)
{
$this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED;
}
}

View File

@@ -0,0 +1,303 @@
<?php
namespace Codeception\PHPUnit\ResultPrinter;
use Codeception\PHPUnit\ResultPrinter as CodeceptionResultPrinter;
use Codeception\Step;
use Codeception\Step\Meta;
use Codeception\Test\Descriptor;
use Codeception\Test\Interfaces\ScenarioDriven;
use Codeception\TestInterface;
use Codeception\Util\PathResolver;
class HTML extends CodeceptionResultPrinter
{
/**
* @var boolean
*/
protected $printsHTML = true;
/**
* @var integer
*/
protected $id = 0;
/**
* @var string
*/
protected $scenarios = '';
/**
* @var string
*/
protected $templatePath;
/**
* @var int
*/
protected $timeTaken = 0;
protected $failures = [];
/**
* Constructor.
*
* @param mixed $out
* @throws InvalidArgumentException
*/
public function __construct($out = null)
{
parent::__construct($out);
$this->templatePath = sprintf(
'%s%stemplate%s',
__DIR__,
DIRECTORY_SEPARATOR,
DIRECTORY_SEPARATOR
);
}
/**
* Handler for 'start class' event.
*
* @param string $name
*/
protected function startClass($name)
{
}
public function endTest(\PHPUnit\Framework\Test $test, $time)
{
$steps = [];
$success = ($this->testStatus == \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED);
if ($success) {
$this->successful++;
}
if ($test instanceof ScenarioDriven) {
$steps = $test->getScenario()->getSteps();
}
$this->timeTaken += $time;
switch ($this->testStatus) {
case \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE:
$scenarioStatus = 'scenarioFailed';
break;
case \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED:
$scenarioStatus = 'scenarioSkipped';
break;
case \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE:
$scenarioStatus = 'scenarioIncomplete';
break;
case \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR:
$scenarioStatus = 'scenarioFailed';
break;
default:
$scenarioStatus = 'scenarioSuccess';
}
$stepsBuffer = '';
$subStepsBuffer = '';
$subStepsRendered = [];
foreach ($steps as $step) {
if ($step->getMetaStep()) {
$subStepsRendered[$step->getMetaStep()->getAction()][] = $this->renderStep($step);
}
}
foreach ($steps as $step) {
if ($step->getMetaStep()) {
if (! empty($subStepsRendered[$step->getMetaStep()->getAction()])) {
$subStepsBuffer = implode('', $subStepsRendered[$step->getMetaStep()->getAction()]);
unset($subStepsRendered[$step->getMetaStep()->getAction()]);
$stepsBuffer .= $this->renderSubsteps($step->getMetaStep(), $subStepsBuffer);
}
} else {
$stepsBuffer .= $this->renderStep($step);
}
}
$scenarioTemplate = new \Text_Template(
$this->templatePath . 'scenario.html'
);
$failures = '';
$name = Descriptor::getTestSignatureUnique($test);
if (isset($this->failures[$name])) {
$failTemplate = new \Text_Template(
$this->templatePath . 'fail.html'
);
foreach ($this->failures[$name] as $failure) {
$failTemplate->setVar(['fail' => nl2br($failure)]);
$failures .= $failTemplate->render() . PHP_EOL;
}
$this->failures[$name] = [];
}
$png = '';
$html = '';
if ($test instanceof TestInterface) {
$reports = $test->getMetadata()->getReports();
if (isset($reports['png'])) {
$localPath = PathResolver::getRelativeDir($reports['png'], codecept_output_dir());
$png = "<tr><td class='error'><div class='screenshot'><img src='$localPath' alt='failure screenshot'></div></td></tr>";
}
if (isset($reports['html'])) {
$localPath = PathResolver::getRelativeDir($reports['html'], codecept_output_dir());
$html = "<tr><td class='error'>See <a href='$localPath' target='_blank'>HTML snapshot</a> of a failed page</td></tr>";
}
}
$toggle = $stepsBuffer ? '<span class="toggle">+</span>' : '';
$testString = htmlspecialchars(ucfirst(Descriptor::getTestAsString($test)));
$testString = preg_replace('~^([\s\w\\\]+):\s~', '<span class="quiet">$1 &raquo;</span> ', $testString);
$scenarioTemplate->setVar(
[
'id' => ++$this->id,
'name' => $testString,
'scenarioStatus' => $scenarioStatus,
'steps' => $stepsBuffer,
'toggle' => $toggle,
'failure' => $failures,
'png' => $png,
'html' => $html,
'time' => round($time, 2)
]
);
$this->scenarios .= $scenarioTemplate->render();
}
public function startTestSuite(\PHPUnit\Framework\TestSuite $suite)
{
$suiteTemplate = new \Text_Template(
$this->templatePath . 'suite.html'
);
if (!$suite->getName()) {
return;
}
$suiteTemplate->setVar(['suite' => ucfirst($suite->getName())]);
$this->scenarios .= $suiteTemplate->render();
}
/**
* Handler for 'end run' event.
*/
protected function endRun()
{
$scenarioHeaderTemplate = new \Text_Template(
$this->templatePath . 'scenario_header.html'
);
$status = !$this->failed
? '<span style="color: green">OK</span>'
: '<span style="color: #e74c3c">FAILED</span>';
$scenarioHeaderTemplate->setVar(
[
'name' => 'Codeception Results',
'status' => $status,
'time' => round($this->timeTaken, 1)
]
);
$header = $scenarioHeaderTemplate->render();
$scenariosTemplate = new \Text_Template(
$this->templatePath . 'scenarios.html'
);
$scenariosTemplate->setVar(
[
'header' => $header,
'scenarios' => $this->scenarios,
'successfulScenarios' => $this->successful,
'failedScenarios' => $this->failed,
'skippedScenarios' => $this->skipped,
'incompleteScenarios' => $this->incomplete
]
);
$this->write($scenariosTemplate->render());
}
/**
* An error occurred.
*
* @param \PHPUnit\Framework\Test $test
* @param \Exception $e
* @param float $time
*/
public function addError(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->failures[Descriptor::getTestSignatureUnique($test)][] = $this->cleanMessage($e);
parent::addError($test, $e, $time);
}
/**
* A failure occurred.
*
* @param \PHPUnit\Framework\Test $test
* @param \PHPUnit\Framework\AssertionFailedError $e
* @param float $time
*/
public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time)
{
$this->failures[Descriptor::getTestSignatureUnique($test)][] = $this->cleanMessage($e);
parent::addFailure($test, $e, $time);
}
/**
* Starts test.
*
* @param \PHPUnit\Framework\Test $test
*/
public function startTest(\PHPUnit\Framework\Test $test)
{
$name = Descriptor::getTestSignatureUnique($test);
if (isset($this->failures[$name])) {
// test failed in before hook
return;
}
// start test and mark initialize as passed
parent::startTest($test);
}
/**
* @param $step
* @return string
*/
protected function renderStep(Step $step)
{
$stepTemplate = new \Text_Template($this->templatePath . 'step.html');
$stepTemplate->setVar(['action' => $step->getHtml(), 'error' => $step->hasFailed() ? 'failedStep' : '']);
return $stepTemplate->render();
}
/**
* @param $metaStep
* @param $substepsBuffer
* @return string
*/
protected function renderSubsteps(Meta $metaStep, $substepsBuffer)
{
$metaTemplate = new \Text_Template($this->templatePath . 'substeps.html');
$metaTemplate->setVar(['metaStep' => $metaStep->getHtml(), 'error' => $metaStep->hasFailed() ? 'failedStep' : '', 'steps' => $substepsBuffer, 'id' => uniqid()]);
return $metaTemplate->render();
}
private function cleanMessage($exception)
{
$msg = $exception->getMessage();
$msg = str_replace(['<info>','</info>','<bold>','</bold>'], ['','','',''], $msg);
return htmlentities($msg);
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Codeception\PHPUnit\ResultPrinter;
use Codeception\Lib\Console\Output;
use Codeception\PHPUnit\ConsolePrinter;
use Codeception\PHPUnit\ResultPrinter;
use Codeception\Test\Descriptor;
class Report extends ResultPrinter implements ConsolePrinter
{
/**
* @param \PHPUnit\Framework\Test $test
* @param float $time
*/
public function endTest(\PHPUnit\Framework\Test $test, $time)
{
$name = Descriptor::getTestAsString($test);
$success = ($this->testStatus == \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED);
if ($success) {
$this->successful++;
}
if ($this->testStatus == \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE) {
$status = "\033[41;37mFAIL\033[0m";
} elseif ($this->testStatus == \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED) {
$status = 'Skipped';
} elseif ($this->testStatus == \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE) {
$status = 'Incomplete';
} elseif ($this->testStatus == \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR) {
$status = 'ERROR';
} else {
$status = 'Ok';
}
if (strlen($name) > 75) {
$name = substr($name, 0, 70);
}
$line = $name . str_repeat('.', 75 - strlen($name));
$line .= $status;
$this->write($line . "\n");
}
protected function endRun()
{
$this->write("\nCodeception Results\n");
$this->write(sprintf(
"Successful: %s. Failed: %s. Incomplete: %s. Skipped: %s",
$this->successful,
$this->failed,
$this->skipped,
$this->incomplete
) . "\n");
}
public function printResult(\PHPUnit\Framework\TestResult $result)
{
}
public function write($buffer)
{
}
}

View File

@@ -0,0 +1,100 @@
<?php
namespace Codeception\PHPUnit\ResultPrinter;
use Codeception\Event\FailEvent;
use Codeception\Events;
use Codeception\Test\Unit;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
class UI extends \PHPUnit\TextUI\ResultPrinter
{
/**
* @var EventDispatcher
*/
protected $dispatcher;
public function __construct(EventDispatcher $dispatcher, $options, $out = null)
{
parent::__construct($out, $options['verbosity'] > OutputInterface::VERBOSITY_NORMAL, $options['colors'] ? 'always' : 'never');
$this->dispatcher = $dispatcher;
}
protected function printDefect(\PHPUnit\Framework\TestFailure $defect, $count)
{
$this->write("\n---------\n");
$this->dispatcher->dispatch(
Events::TEST_FAIL_PRINT,
new FailEvent($defect->failedTest(), null, $defect->thrownException(), $count)
);
}
/**
* @param \PHPUnit\Framework\TestFailure $defect
*/
protected function printDefectTrace(\PHPUnit\Framework\TestFailure $defect)
{
$this->write($defect->getExceptionAsString());
$this->writeNewLine();
$stackTrace = \PHPUnit\Util\Filter::getFilteredStacktrace($defect->thrownException(), false);
foreach ($stackTrace as $i => $frame) {
if (!isset($frame['file'])) {
continue;
}
$this->write(
sprintf(
"#%d %s(%s)",
$i + 1,
$frame['file'],
isset($frame['line']) ? $frame['line'] : '?'
)
);
$this->writeNewLine();
}
}
public function startTest(\PHPUnit\Framework\Test $test)
{
if ($test instanceof Unit) {
parent::startTest($test);
}
}
public function endTest(\PHPUnit\Framework\Test $test, $time)
{
if ($test instanceof \PHPUnit\Framework\TestCase or $test instanceof \Codeception\Test\Test) {
$this->numAssertions += $test->getNumAssertions();
}
$this->lastTestFailed = false;
}
public function addError(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->lastTestFailed = true;
}
public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time)
{
$this->lastTestFailed = true;
}
public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, $time)
{
$this->lastTestFailed = true;
}
public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->lastTestFailed = true;
}
public function addSkippedTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->lastTestFailed = true;
}
}

View File

@@ -0,0 +1,5 @@
<tr >
<td class="error">
{fail}
</td>
</tr>

View File

@@ -0,0 +1,22 @@
<tr class="scenarioRow {scenarioStatus}">
<td>
<p class="{scenarioStatus}" onclick="showHide('{id}', this)">{toggle}
{name} <span style="color: #34495e; font-size: 70%;">{time}s</span></p>
</td>
</tr>
<tr class="scenarioRow {scenarioStatus}">
<td>
<table border="0" width="100%" class="{scenarioStatus} scenarioStepsTable" id="stepContainer{id}">
{steps}
{failure}
{png}
{html}
</table>
</td>
</tr>

View File

@@ -0,0 +1,2 @@
<h1>{name} <small>{status} ({time}s)</small></h1>

View File

@@ -0,0 +1,250 @@
<html>
<head>
<title>Test results</title>
<meta charset='utf-8'>
<link href='http://fonts.googleapis.com/css?family=Varela+Round&v2' rel='stylesheet' type='text/css'>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.layout {
margin: 0 auto;
max-width: 1000px;
}
body { font-family: arial, serif; margin: 0; padding: 0; background: #ecf0f1; font-size: 20px; }
h1,h2,h3 { font-family: arial, serif; color: #7f8c8d; }
h1 { font-size: 2.5em; }
h2 { font-size: 1.3em; }
h3 { font-size: 1em; color: #84BBDD; margin: 0.5em 0; }
table { border: none; margin: 0; padding: 0; font-size: 0.9em;}
.scenarioStepsTable .stepName { padding: 5px; }
.scenarioStepsTable td {
background: #fff;
}
.quiet {
color: #333;
font-size: 0.8em;
}
.screenshot {
max-height: 400px;
overflow-y: scroll;
display: block;
}
.screenshot img {
zoom: 0.5;
}
@media (max-width: 1200px) {
#toolbar-filter {
display: none !important;
}
}
.scenarioStepsTable .nostyle {
background: none;
border: none;
}
p {
cursor: pointer;
}
.scenarioRow>td>p {
padding: 5px;
}
.scenarioStepsTable .failedStep {
padding: 10px;
background: #ecf0f1;
border: 2px solid #e74c3c;
border-radius: 0px;
color: #e74c3c;
}
.scenarioStepsTable .error {
background: #999;
padding: 10px;
color: #fff;
border-radius: 0px;
}
.scenarioStepsTable .error a {
color: #eef;
}
.scenarioStepsTable.substeps td {
background: #bdc3c7;
}
.header { font-size: large; font-weight: bold; }
p.scenarioSuccess {
background: rgb(157,213,58); /* Old browsers */
}
.scenario { color: black; }
p.scenarioFailed, p.scenarioError { color: black;
background: #e74c3c
}
table.scenarioFailed tr:last-child { font-weight: bold; }
td.scenarioSuccess { color: green }
td.scenarioFailed { color: red }
.scenarioSkipped { color: teal; }
.scenarioIncomplete { color: gray; }
.scenarioStepsTable { margin-left: 10px; display: none; color: #333; }
#stepContainerSummary {
background: white;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
padding: 20px;
}
.toggle {
background: rgba(255,255,255,0.5);
border-radius: 10px;
display: inline-block;
width: 20px;
height: 20px;
text-align: center;
margin: auto;
color: #666
}
ul#toolbar-filter {
display: block;
position: fixed;
top: 20px;
left: 0px;
padding: 0px;
}
ul#toolbar-filter li {
list-style: none;
text-align: center;
padding: 20px;
background-color: #3498db;
}
ul#toolbar-filter li a, ul#toolbar-filter li a:hover, ul#toolbar-filter li a:visited {
color: #34495e;
text-decoration: none;
}
ul#toolbar-filter li.disabled {
background-color: #bdc3c7;
}
</style>
<script type="text/javascript">
var showAll = true;
function showHide(nodeId, linkObj)
{
var subObj = document.getElementById('stepContainer' + nodeId);
var toggle = linkObj.childNodes[0];
if (toggle.innerHTML != '-') {
toggle.innerHTML = '-';
subObj.style.display='block';
subObj.style.width = '100%';
} else {
toggle.innerHTML = '+';
subObj.style.display='none';
}
}
function showAllScenarios() {
var toolbar = document.getElementById('toolbar-filter');
for (var i = 0; i < toolbar.children.length; i++) {
toolbar.children[i].className = '';
}
var trs = document.getElementsByTagName('tr');
for(var z = 0; z < trs.length; z++) {
trs[z].style.display = '';
}
showAll = true;
}
function toggleScenarios(name, linkObj) {
var links = document.getElementById('toolbar-filter').children;
var rows = document.getElementsByClassName('scenarioRow');
if (showAll) {
for (var i = 0; i < links.length; i++) {
links[i].className = 'disabled';
}
for (var i = 0; i < rows.length; i++) {
rows[i].style.display = 'none';
}
}
showAll = false;
if (linkObj.className == '') {
linkObj.className = 'disabled';
for (var i = 0; i < rows.length; i++) {
if (rows[i].classList.contains(name)) {
rows[i].style.display = 'none';
}
}
return;
}
if (linkObj.className == 'disabled') {
linkObj.className = '';
for (var i = 0; i < rows.length; i++) {
if (rows[i].classList.contains(name)) {
rows[i].style.display = 'table-row';
}
}
return;
}
}
</script>
</head>
<body>
<ul id="toolbar-filter">
<li> <a href="#" title="Show all" onClick="showAllScenarios()">◯</a></li>
<li> <a href="#" title="Successful" onClick="toggleScenarios('scenarioSuccess', this.parentElement)"><strong>✔</strong> {successfulScenarios}</a></li>
<li> <a href="#" title="Failed" onClick="toggleScenarios('scenarioFailed', this.parentElement)"><strong>✗</strong> {failedScenarios}</a></li>
<li> <a href="#" title="Skipped" onClick="toggleScenarios('scenarioSkipped', this.parentElement)"><strong>S</strong> {skippedScenarios}</a></li>
<li> <a href="#" title="Incomplete" onClick="toggleScenarios('scenarioIncomplete', this.parentElement)"><strong>I</strong> {incompleteScenarios}</a></li>
</ul>
<div class="layout">
{header}
<table border="0" style="width: 100%;">
{scenarios}
<tr>
<td>
<h2>Summary</h2>
<div id="stepContainerSummary">
<table border="0">
<tr>
<td width="250" class="scenarioSuccess">Successful scenarios:</td>
<td class="scenarioSuccessValue"><strong>{successfulScenarios}</strong></td>
</tr>
<tr>
<td class="scenarioFailed">Failed scenarios:</td>
<td class="scenarioFailedValue"><strong>{failedScenarios}</strong></td>
</tr>
<tr>
<td class="scenarioSkipped">Skipped scenarios:</td>
<td class="scenarioSkippedValue"><strong>{skippedScenarios}</strong></td>
</tr>
<tr>
<td class="scenarioIncomplete">Incomplete scenarios:</td>
<td class="scenarioIncompleteValue"><strong>{incompleteScenarios}</strong></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
</div>
</body>
</html>

View File

@@ -0,0 +1,4 @@
<tr>
<td class="stepName {error}">&nbsp;&nbsp;&nbsp;&nbsp;{action}</td>
</tr>

View File

@@ -0,0 +1,12 @@
<tr>
<td class="stepName {error}" ><p onclick="showHide('{id}', this)"><span class="toggle">+</span> {metaStep}</p>
</td>
</tr>
<tr>
<td class="nostyle">
<table border="0" width="100%" class="substeps scenarioStepsTable" id="stepContainer{id}">
{steps}
</table>
</td>
</tr>

View File

@@ -0,0 +1,5 @@
<tr>
<td>
<h3>{suite} Tests</h3>
</td>
</tr>

View File

@@ -0,0 +1,184 @@
<?php
namespace Codeception\PHPUnit;
use Codeception\Configuration;
use Codeception\Exception\ConfigurationException;
class Runner extends \PHPUnit\TextUI\TestRunner
{
public static $persistentListeners = [];
protected $defaultListeners = [
'xml' => false,
'html' => false,
'tap' => false,
'json' => false,
'report' => false
];
protected $config = [];
protected $logDir = null;
public function __construct()
{
$this->config = Configuration::config();
$this->logDir = Configuration::outputDir(); // prepare log dir
$this->phpUnitOverriders();
parent::__construct();
}
public function phpUnitOverriders()
{
require_once __DIR__ . DIRECTORY_SEPARATOR . 'Overrides/Filter.php';
}
/**
* @return null|\PHPUnit\TextUI\ResultPrinter
*/
public function getPrinter()
{
return $this->printer;
}
public function prepareSuite(\PHPUnit\Framework\Test $suite, array &$arguments)
{
$this->handleConfiguration($arguments);
$filterFactory = new \PHPUnit\Runner\Filter\Factory();
if ($arguments['groups']) {
$filterFactory->addFilter(
new \ReflectionClass('PHPUnit\Runner\Filter\IncludeGroupFilterIterator'),
$arguments['groups']
);
}
if ($arguments['excludeGroups']) {
$filterFactory->addFilter(
new \ReflectionClass('PHPUnit\Runner\Filter\ExcludeGroupFilterIterator'),
$arguments['excludeGroups']
);
}
if ($arguments['filter']) {
$filterFactory->addFilter(
new \ReflectionClass('Codeception\PHPUnit\FilterTest'),
$arguments['filter']
);
}
$suite->injectFilter($filterFactory);
}
public function doEnhancedRun(
\PHPUnit\Framework\Test $suite,
\PHPUnit\Framework\TestResult $result,
array $arguments = []
) {
unset($GLOBALS['app']); // hook for not to serialize globals
$result->convertErrorsToExceptions(false);
if (isset($arguments['report_useless_tests'])) {
$result->beStrictAboutTestsThatDoNotTestAnything((bool)$arguments['report_useless_tests']);
}
if (isset($arguments['disallow_test_output'])) {
$result->beStrictAboutOutputDuringTests((bool)$arguments['disallow_test_output']);
}
if (empty(self::$persistentListeners)) {
$this->applyReporters($result, $arguments);
}
if (class_exists('\Symfony\Bridge\PhpUnit\SymfonyTestsListener')) {
$arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : [];
$arguments['listeners'][] = new \Symfony\Bridge\PhpUnit\SymfonyTestsListener();
}
$arguments['listeners'][] = $this->printer;
// clean up listeners between suites
foreach ($arguments['listeners'] as $listener) {
$result->addListener($listener);
}
$suite->run($result);
unset($suite);
foreach ($arguments['listeners'] as $listener) {
$result->removeListener($listener);
}
return $result;
}
/**
* @param \PHPUnit\Framework\TestResult $result
* @param array $arguments
*
* @return array
*/
protected function applyReporters(\PHPUnit\Framework\TestResult $result, array $arguments)
{
foreach ($this->defaultListeners as $listener => $value) {
if (!isset($arguments[$listener])) {
$arguments[$listener] = $value;
}
}
if ($arguments['report']) {
self::$persistentListeners[] = $this->instantiateReporter('report');
}
if ($arguments['html']) {
codecept_debug('Printing HTML report into ' . $arguments['html']);
self::$persistentListeners[] = $this->instantiateReporter(
'html',
[$this->absolutePath($arguments['html'])]
);
}
if ($arguments['xml']) {
codecept_debug('Printing JUNIT report into ' . $arguments['xml']);
self::$persistentListeners[] = $this->instantiateReporter(
'xml',
[$this->absolutePath($arguments['xml']), (bool)$arguments['log_incomplete_skipped']]
);
}
if ($arguments['tap']) {
codecept_debug('Printing TAP report into ' . $arguments['tap']);
self::$persistentListeners[] = $this->instantiateReporter('tap', [$this->absolutePath($arguments['tap'])]);
}
if ($arguments['json']) {
codecept_debug('Printing JSON report into ' . $arguments['json']);
self::$persistentListeners[] = $this->instantiateReporter(
'json',
[$this->absolutePath($arguments['json'])]
);
}
foreach (self::$persistentListeners as $listener) {
if ($listener instanceof ConsolePrinter) {
$this->printer = $listener;
continue;
}
$result->addListener($listener);
}
}
protected function instantiateReporter($name, $args = [])
{
if (!isset($this->config['reporters'][$name])) {
throw new ConfigurationException("Reporter $name not defined");
}
return (new \ReflectionClass($this->config['reporters'][$name]))->newInstanceArgs($args);
}
private function absolutePath($path)
{
if ((strpos($path, '/') === 0) or (strpos($path, ':') === 1)) { // absolute path
return $path;
}
return $this->logDir . $path;
}
}

View File

@@ -0,0 +1,612 @@
<?php
// @codingStandardsIgnoreStart
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace {
if (!class_exists('PHPUnit_Util_String')) {
/**
* String helpers.
*/
class PHPUnit_Util_String
{
/**
* Converts a string to UTF-8 encoding.
*
* @param string $string
*
* @return string
*/
public static function convertToUtf8($string)
{
return mb_convert_encoding($string, 'UTF-8');
}
/**
* Checks a string for UTF-8 encoding.
*
* @param string $string
*
* @return bool
*/
protected static function isUtf8($string)
{
$length = strlen($string);
for ($i = 0; $i < $length; $i++) {
if (ord($string[$i]) < 0x80) {
$n = 0;
} elseif ((ord($string[$i]) & 0xE0) == 0xC0) {
$n = 1;
} elseif ((ord($string[$i]) & 0xF0) == 0xE0) {
$n = 2;
} elseif ((ord($string[$i]) & 0xF0) == 0xF0) {
$n = 3;
} else {
return false;
}
for ($j = 0; $j < $n; $j++) {
if ((++$i == $length) || ((ord($string[$i]) & 0xC0) != 0x80)) {
return false;
}
}
}
return true;
}
}
}
}
namespace PHPUnit\Util\Log {
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A TestListener that generates JSON messages.
*/
if (!class_exists('\PHPUnit\Util\Log\JSON')) {
class JSON extends \PHPUnit\Util\Printer implements \PHPUnit\Framework\TestListener
{
/**
* @var string
*/
protected $currentTestSuiteName = '';
/**
* @var string
*/
protected $currentTestName = '';
/**
* @var bool
*/
protected $currentTestPass = true;
/**
* @var array
*/
protected $logEvents = [];
/**
* An error occurred.
*
* @param \PHPUnit\Framework\Test $test
* @param Exception $e
* @param float $time
*/
public function addError(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->writeCase(
'error',
$time,
\PHPUnit\Util\Filter::getFilteredStacktrace($e, false),
\PHPUnit\Framework\TestFailure::exceptionToString($e),
$test
);
$this->currentTestPass = false;
}
/**
* A warning occurred.
*
* @param \PHPUnit\Framework\Test $test
* @param \PHPUnit\Framework\Warning $e
* @param float $time
*/
public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, $time)
{
$this->writeCase(
'warning',
$time,
\PHPUnit\Util\Filter::getFilteredStacktrace($e, false),
\PHPUnit\Framework\TestFailure::exceptionToString($e),
$test
);
$this->currentTestPass = false;
}
/**
* A failure occurred.
*
* @param \PHPUnit\Framework\Test $test
* @param \PHPUnit\Framework\AssertionFailedError $e
* @param float $time
*/
public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time)
{
$this->writeCase(
'fail',
$time,
\PHPUnit\Util\Filter::getFilteredStacktrace($e, false),
\PHPUnit\Framework\TestFailure::exceptionToString($e),
$test
);
$this->currentTestPass = false;
}
/**
* Incomplete test.
*
* @param \PHPUnit\Framework\Test $test
* @param Exception $e
* @param float $time
*/
public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->writeCase(
'error',
$time,
\PHPUnit\Util\Filter::getFilteredStacktrace($e, false),
'Incomplete Test: ' . $e->getMessage(),
$test
);
$this->currentTestPass = false;
}
/**
* Risky test.
*
* @param \PHPUnit\Framework\Test $test
* @param Exception $e
* @param float $time
*/
public function addRiskyTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->writeCase(
'error',
$time,
\PHPUnit\Util\Filter::getFilteredStacktrace($e, false),
'Risky Test: ' . $e->getMessage(),
$test
);
$this->currentTestPass = false;
}
/**
* Skipped test.
*
* @param \PHPUnit\Framework\Test $test
* @param Exception $e
* @param float $time
*/
public function addSkippedTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->writeCase(
'error',
$time,
\PHPUnit\Util\Filter::getFilteredStacktrace($e, false),
'Skipped Test: ' . $e->getMessage(),
$test
);
$this->currentTestPass = false;
}
/**
* A testsuite started.
*
* @param \PHPUnit\Framework\TestSuite $suite
*/
public function startTestSuite(\PHPUnit\Framework\TestSuite $suite)
{
$this->currentTestSuiteName = $suite->getName();
$this->currentTestName = '';
$this->addLogEvent(
[
'event' => 'suiteStart',
'suite' => $this->currentTestSuiteName,
'tests' => count($suite)
]
);
}
/**
* A testsuite ended.
*
* @param \PHPUnit\Framework\TestSuite $suite
*/
public function endTestSuite(\PHPUnit\Framework\TestSuite $suite)
{
$this->currentTestSuiteName = '';
$this->currentTestName = '';
$this->writeAll($this->logEvents);
}
/**
* A test started.
*
* @param \PHPUnit\Framework\Test $test
*/
public function startTest(\PHPUnit\Framework\Test $test)
{
$this->currentTestName = \PHPUnit\Util\Test::describe($test);
$this->currentTestPass = true;
$this->addLogEvent(
[
'event' => 'testStart',
'suite' => $this->currentTestSuiteName,
'test' => $this->currentTestName
]
);
}
/**
* A test ended.
*
* @param \PHPUnit\Framework\Test $test
* @param float $time
*/
public function endTest(\PHPUnit\Framework\Test $test, $time)
{
if ($this->currentTestPass) {
$this->writeCase('pass', $time, [], '', $test);
}
}
/**
* @param string $status
* @param float $time
* @param array $trace
* @param string $message
* @param \PHPUnit\Framework\TestCase|null $test
*/
protected function writeCase($status, $time, array $trace = [], $message = '', $test = null)
{
$output = '';
// take care of TestSuite producing error (e.g. by running into exception) as TestSuite doesn't have hasOutput
if ($test !== null && method_exists($test, 'hasOutput') && $test->hasOutput()) {
$output = $test->getActualOutput();
}
$this->addLogEvent(
[
'event' => 'test',
'suite' => $this->currentTestSuiteName,
'test' => $this->currentTestName,
'status' => $status,
'time' => $time,
'trace' => $trace,
'message' => \PHPUnit_Util_String::convertToUtf8($message),
'output' => $output,
]
);
}
/**
* @param array $event_data
*/
protected function addLogEvent($event_data = [])
{
if (count($event_data)) {
array_push($this->logEvents, $event_data);
}
}
/**
* @param array $buffer
*/
public function writeAll($buffer)
{
array_walk_recursive(
$buffer, function (&$input) {
if (is_string($input)) {
$input = \PHPUnit_Util_String::convertToUtf8($input);
}
}
);
parent::write(json_encode($buffer, JSON_PRETTY_PRINT));
}
}
}
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (!class_exists('\PHPUnit\Util\Log\TAP')) {
/**
* A TestListener that generates a logfile of the
* test execution using the Test Anything Protocol (TAP).
*/
class TAP extends \PHPUnit\Util\Printer implements \PHPUnit\Framework\TestListener
{
/**
* @var int
*/
protected $testNumber = 0;
/**
* @var int
*/
protected $testSuiteLevel = 0;
/**
* @var bool
*/
protected $testSuccessful = true;
/**
* Constructor.
*
* @param mixed $out
*
* @throws \PHPUnit\Framework\Exception
*/
public function __construct($out = null)
{
parent::__construct($out);
$this->write("TAP version 13\n");
}
/**
* An error occurred.
*
* @param \PHPUnit\Framework\Test $test
* @param Exception $e
* @param float $time
*/
public function addError(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->writeNotOk($test, 'Error');
}
/**
* A warning occurred.
*
* @param \PHPUnit\Framework\Test $test
* @param \PHPUnit\Framework\Warning $e
* @param float $time
*/
public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, $time)
{
$this->writeNotOk($test, 'Warning');
}
/**
* A failure occurred.
*
* @param \PHPUnit\Framework\Test $test
* @param \PHPUnit\Framework\AssertionFailedError $e
* @param float $time
*/
public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time)
{
$this->writeNotOk($test, 'Failure');
$message = explode(
"\n",
\PHPUnit\Framework\TestFailure::exceptionToString($e)
);
$diagnostic = [
'message' => $message[0],
'severity' => 'fail'
];
if ($e instanceof \PHPUnit\Framework\ExpectationFailedException) {
$cf = $e->getComparisonFailure();
if ($cf !== null) {
$diagnostic['data'] = [
'got' => $cf->getActual(),
'expected' => $cf->getExpected()
];
}
}
$yaml = new \Symfony\Component\Yaml\Dumper;
$this->write(
sprintf(
" ---\n%s ...\n",
$yaml->dump($diagnostic, 2, 2)
)
);
}
/**
* Incomplete test.
*
* @param \PHPUnit\Framework\Test $test
* @param \Exception $e
* @param float $time
*/
public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->writeNotOk($test, '', 'TODO Incomplete Test');
}
/**
* Risky test.
*
* @param \PHPUnit\Framework\Test $test
* @param Exception $e
* @param float $time
*/
public function addRiskyTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->write(
sprintf(
"ok %d - # RISKY%s\n",
$this->testNumber,
$e->getMessage() != '' ? ' ' . $e->getMessage() : ''
)
);
$this->testSuccessful = false;
}
/**
* Skipped test.
*
* @param \PHPUnit\Framework\Test $test
* @param Exception $e
* @param float $time
*/
public function addSkippedTest(\PHPUnit\Framework\Test $test, \Exception $e, $time)
{
$this->write(
sprintf(
"ok %d - # SKIP%s\n",
$this->testNumber,
$e->getMessage() != '' ? ' ' . $e->getMessage() : ''
)
);
$this->testSuccessful = false;
}
/**
* A testsuite started.
*
* @param \PHPUnit\Framework\TestSuite $suite
*/
public function startTestSuite(\PHPUnit\Framework\TestSuite $suite)
{
$this->testSuiteLevel++;
}
/**
* A testsuite ended.
*
* @param \PHPUnit\Framework\TestSuite $suite
*/
public function endTestSuite(\PHPUnit\Framework\TestSuite $suite)
{
$this->testSuiteLevel--;
if ($this->testSuiteLevel == 0) {
$this->write(sprintf("1..%d\n", $this->testNumber));
}
}
/**
* A test started.
*
* @param \PHPUnit\Framework\Test $test
*/
public function startTest(\PHPUnit\Framework\Test $test)
{
$this->testNumber++;
$this->testSuccessful = true;
}
/**
* A test ended.
*
* @param \PHPUnit\Framework\Test $test
* @param float $time
*/
public function endTest(\PHPUnit\Framework\Test $test, $time)
{
if ($this->testSuccessful === true) {
$this->write(
sprintf(
"ok %d - %s\n",
$this->testNumber,
\PHPUnit\Util\Test::describe($test)
)
);
}
$this->writeDiagnostics($test);
}
/**
* @param \PHPUnit\Framework\Test $test
* @param string $prefix
* @param string $directive
*/
protected function writeNotOk(\PHPUnit\Framework\Test $test, $prefix = '', $directive = '')
{
$this->write(
sprintf(
"not ok %d - %s%s%s\n",
$this->testNumber,
$prefix != '' ? $prefix . ': ' : '',
\PHPUnit\Util\Test::describe($test),
$directive != '' ? ' # ' . $directive : ''
)
);
$this->testSuccessful = false;
}
/**
* @param \PHPUnit\Framework\Test $test
*/
private function writeDiagnostics(\PHPUnit\Framework\Test $test)
{
if (!$test instanceof \PHPUnit\Framework\TestCase) {
return;
}
if (!$test->hasOutput()) {
return;
}
foreach (explode("\n", trim($test->getActualOutput())) as $line) {
$this->write(
sprintf(
"# %s\n",
$line
)
);
}
}
}
}
}
// @codingStandardsIgnoreEnd

View File

@@ -0,0 +1,71 @@
<?php
// @codingStandardsIgnoreStart
// Add aliases for PHPUnit 6
namespace {
if (!class_exists('PHPUnit\Framework\Assert') && class_exists('PHPUnit_Framework_Assert')) {
class_alias('PHPUnit_Framework_Assert', 'PHPUnit\Framework\Assert');
}
// load PHPUnit 4.8 classes avoiding its so-called compatibility layer
if (class_exists('PHPUnit_Framework_TestCase') && !class_exists('PHPUnit\Framework\TestCase', false)) {
class_alias('PHPUnit_Framework_AssertionFailedError', 'PHPUnit\Framework\AssertionFailedError');
class_alias('PHPUnit_Framework_Test', 'PHPUnit\Framework\Test');
class_alias('PHPUnit_Framework_TestCase', 'PHPUnit\Framework\TestCase');
class_alias('PHPUnit_Runner_BaseTestRunner', 'PHPUnit\Runner\BaseTestRunner');
class_alias('PHPUnit_Framework_TestListener', 'PHPUnit\Framework\TestListener');
class_alias('PHPUnit_Framework_TestSuite', 'PHPUnit\Framework\TestSuite');
class_alias('PHPUnit_Framework_Constraint', 'PHPUnit\Framework\Constraint\Constraint');
class_alias('PHPUnit_Framework_Constraint_Not', 'PHPUnit\Framework\Constraint\LogicalNot');
class_alias('PHPUnit_Framework_TestSuite_DataProvider', 'PHPUnit\Framework\DataProviderTestSuite');
class_alias('PHPUnit_Framework_Exception', 'PHPUnit\Framework\Exception');
class_alias('PHPUnit_Framework_ExceptionWrapper', 'PHPUnit\Framework\ExceptionWrapper');
class_alias('PHPUnit_Framework_ExpectationFailedException', 'PHPUnit\Framework\ExpectationFailedException');
class_alias('PHPUnit_Framework_IncompleteTestError', 'PHPUnit\Framework\IncompleteTestError');
class_alias('PHPUnit_Framework_SelfDescribing', 'PHPUnit\Framework\SelfDescribing');
class_alias('PHPUnit_Framework_SkippedTestError', 'PHPUnit\Framework\SkippedTestError');
class_alias('PHPUnit_Framework_TestFailure', 'PHPUnit\Framework\TestFailure');
class_alias('PHPUnit_Framework_TestResult', 'PHPUnit\Framework\TestResult');
class_alias('PHPUnit_Framework_Warning', 'PHPUnit\Framework\Warning');
class_alias('PHPUnit_Runner_Filter_Factory', 'PHPUnit\Runner\Filter\Factory');
class_alias('PHPUnit_Runner_Filter_Test', 'PHPUnit\Runner\Filter\NameFilterIterator');
class_alias('PHPUnit_Runner_Filter_Group_Include', 'PHPUnit\Runner\Filter\IncludeGroupFilterIterator');
class_alias('PHPUnit_Runner_Filter_Group_Exclude', 'PHPUnit\Runner\Filter\ExcludeGroupFilterIterator');
class_alias('PHPUnit_Runner_Version', 'PHPUnit\Runner\Version');
class_alias('PHPUnit_TextUI_ResultPrinter', 'PHPUnit\TextUI\ResultPrinter');
class_alias('PHPUnit_TextUI_TestRunner', 'PHPUnit\TextUI\TestRunner');
class_alias('PHPUnit_Util_Log_JUnit', 'PHPUnit\Util\Log\JUnit');
class_alias('PHPUnit_Util_Printer', 'PHPUnit\Util\Printer');
class_alias('PHPUnit_Util_Test', 'PHPUnit\Util\Test');
class_alias('PHPUnit_Util_TestDox_ResultPrinter', 'PHPUnit\Util\TestDox\ResultPrinter');
}
if (!class_exists('PHPUnit\Util\Log\JSON') || !class_exists('PHPUnit\Util\Log\TAP')) {
if (class_exists('PHPUnit\Util\Printer')) {
require_once __DIR__ . '/phpunit5-loggers.php'; // TAP and JSON loggers were removed in PHPUnit 6
}
}
// phpunit codecoverage updates
if (class_exists('PHP_CodeCoverage') && !class_exists('SebastianBergmann\CodeCoverage\CodeCoverage')) {
class_alias('PHP_CodeCoverage', 'SebastianBergmann\CodeCoverage\CodeCoverage');
class_alias('PHP_CodeCoverage_Report_Text', 'SebastianBergmann\CodeCoverage\Report\Text');
class_alias('PHP_CodeCoverage_Report_PHP', 'SebastianBergmann\CodeCoverage\Report\PHP');
class_alias('PHP_CodeCoverage_Report_Clover', 'SebastianBergmann\CodeCoverage\Report\Clover');
class_alias('PHP_CodeCoverage_Report_Crap4j', 'SebastianBergmann\CodeCoverage\Report\Crap4j');
class_alias('PHP_CodeCoverage_Report_HTML', 'SebastianBergmann\CodeCoverage\Report\Html\Facade');
class_alias('PHP_CodeCoverage_Report_XML', 'SebastianBergmann\CodeCoverage\Report\Xml\Facade');
class_alias('PHP_CodeCoverage_Exception', 'SebastianBergmann\CodeCoverage\Exception');
class_alias('PHP_CodeCoverage_Driver', 'SebastianBergmann\CodeCoverage\Driver\Driver');
}
if (class_exists('PHP_Timer') && !class_exists('SebastianBergmann\Timer\Timer')) {
class_alias('PHP_Timer', 'SebastianBergmann\Timer\Timer');
}
if (!class_exists('\PHPUnit\Framework\Constraint\LogicalNot') && class_exists('\PHPUnit\Framework\Constraint\Not')) {
class_alias('\PHPUnit\Framework\Constraint\Not', '\PHPUnit\Framework\Constraint\LogicalNot');
}
}
// @codingStandardsIgnoreEnd