Skip to content

Commit

Permalink
Merge pull request #128 from Codeception/master
Browse files Browse the repository at this point in the history
Master into 2.1
  • Loading branch information
vansari committed Jan 2, 2023
2 parents 82a7ac7 + 2700038 commit 6875f03
Show file tree
Hide file tree
Showing 11 changed files with 1,772 additions and 13 deletions.
31 changes: 31 additions & 0 deletions Makefile
@@ -0,0 +1,31 @@
.DEFAULT_GOAL := help

mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(dir $(mkfile_path))

help:
@echo "Use this makefile to execute your tests in correct php version"
@echo "\tr.php-7.4\t\trun Tests with PHP 7.4"
@echo "\tr.php-8.0\t\trun Tests with PHP 8.0"
@echo "\tr.php-8.1\t\trun Tests with PHP 8.1"
@echo "\tr.php-8.2\t\trun Tests with PHP 8.2"

r.php-7.4:
docker build -t robo:php-7.4 --target PHP74 --build-arg PHP_VERSION=7.4 docker
docker run --rm -v $(current_dir):/app -w /app robo:php-7.4 composer install
docker run --rm -v $(current_dir):/app -w /app robo:php-7.4 composer test

r.php-8.0:
docker build -t robo:php-8.0 --target PHP8 --build-arg PHP_VERSION=8.0 docker
docker run --rm -v $(current_dir):/app -w /app robo:php-8.0 composer install
docker run --rm -v $(current_dir):/app -w /app robo:php-8.0 composer test

r.php-8.1:
docker build -t robo:php-8.1 --target PHP8 --build-arg PHP_VERSION=8.1 docker
docker run --rm -v $(current_dir):/app -w /app robo:php-8.1 composer install
docker run --rm -v $(current_dir):/app -w /app robo:php-8.1 composer test

r.php-8.2:
docker build -t robo:php-8.2 --target PHP8 --build-arg PHP_VERSION=8.2 docker
docker run --rm -v $(current_dir):/app -w /app robo:php-8.2 composer install
docker run --rm -v $(current_dir):/app -w /app robo:php-8.2 composer test
13 changes: 13 additions & 0 deletions docker/Dockerfile
@@ -0,0 +1,13 @@
ARG PHP_VERSION=7.4
FROM php:${PHP_VERSION}-cli as base
COPY --from=composer /usr/bin/composer /usr/bin/composer

RUN apt update \
&& apt upgrade -y \
&& apt install -y apt-utils libxml2-dev libzip-dev

FROM base as PHP74
RUN docker-php-ext-install dom json xml zip

FROM base as PHP8
RUN docker-php-ext-install dom xml zip
83 changes: 77 additions & 6 deletions src/Merger/HtmlReportMerger.php
Expand Up @@ -50,7 +50,20 @@ class HtmlReportMerger extends AbstractMerger

protected bool $previousLibXmlUseErrors = false;

private float $executionTimeSum = 0;
protected bool $maxTime = false;

protected array $executionTime = [];

/**
* @var string|float
*/
private $executionTimeSum = 0;


public function maxTime(): void
{
$this->maxTime = true;
}

/**
* HtmlReportMerger constructor.
Expand Down Expand Up @@ -198,9 +211,10 @@ private function countExecutionTime(DOMDocument $dstFile): void
if (!$nodeList) {
throw XPathExpressionException::malformedXPath($xpathHeadline);
}

$hoursMinutesSeconds = '(([0-1]?\d|2[0-3])(?::([0-5]?\d))?(?::([0-5]?\d))\.\d+)';
$seconds = '\d+\.\d+s';
$pregResult = preg_match(
'#^Codeception Results .* \((?<timesum>\d+\.\d+)s\)$#',
"#^Codeception Results .* \((?<timesum>$hoursMinutesSeconds|$seconds)\)$#",
$nodeList[0]->nodeValue,
$matches
);
Expand All @@ -213,7 +227,18 @@ private function countExecutionTime(DOMDocument $dstFile): void
return;
}

$this->executionTimeSum += (float)$matches['timesum'];
if (str_contains($matches['timesum'], 's')) {
$matches['timesum'] = str_replace('s', '', $matches['timesum']);
}
if (!$this->maxTime) {
if (str_contains($matches['timesum'], ':')) {
$this->executionTimeSum = $this->sumTime(strval($this->executionTimeSum), (string)$matches['timesum']);
} else {
$this->executionTimeSum += (float)$matches['timesum'];
}
} else {
$this->executionTime[] = (string)$matches['timesum'];
}
}

/**
Expand All @@ -238,8 +263,20 @@ private function updateHeaderLine(DOMDocument $dstFile): void
$statusNode->nodeValue = 'FAILED';
$statusAttr->value = 'color: red';
}

$executionTimeNode->nodeValue = sprintf(' (%ss)', $this->executionTimeSum);
if (!$this->maxTime) {
$executionTime = (string)$this->executionTimeSum;
} else {
usort($this->executionTime, function ($a, $b) {
return strcmp($a, $b);
});
$executionTime = max($this->executionTime);
}
$executionTimeNode->nodeValue = sprintf(
(preg_match('#([0-1]?\d|2[0-3])(?::([0-5]?\d))?(?::([0-5]?\d))\.\d+#', $executionTime))
? ' (%s)'
: ' (%ss)',
$executionTime
);
}

/**
Expand Down Expand Up @@ -342,4 +379,38 @@ private function updateButtons(DOMDocument $dstFile): void
$table->setAttribute('id', "stepContainer" . $n);
}
}

private function sumTime(string $time1, string $time2): string
{
$times = [$time1, $time2];
$seconds = 0;
$milliseconds = 0;
$isHour = false;
foreach ($times as $time) {
if ($time !== '0') {
$output = explode(':', $time);
if (count($output) > 2) {
$isHour = true;
[$hour, $minute, $second] = $output;
$seconds += $hour * 3600;
} else {
[$minute, $second] = $output;
}
$seconds += $minute * 60;
[$second, $millisecond] = explode('.', $second);
$seconds += $second;
$milliseconds += $millisecond;
}
}
if ($isHour) {
$hours = floor($seconds / 3600);
$seconds -= $hours * 3600;
}
$minutes = floor($seconds / 60);
$seconds -= $minutes * 60;

return $isHour
? sprintf('%02d:%02d:%02d.%02d', $hours, $minutes, $seconds, $milliseconds)
: sprintf('%02d:%02d.%02d', $minutes, $seconds, $milliseconds);
}
}
22 changes: 19 additions & 3 deletions src/Merger/XmlReportMergerTask.php
Expand Up @@ -18,8 +18,10 @@ class XmlReportMergerTask extends AbstractMerger
protected array $src = [];

protected string $dst = '';
protected array $suiteDuration = [];

protected bool $summarizeTime = true;
protected bool $maxSuiteTime = false;

protected bool $mergeRewrite = false;

Expand All @@ -36,6 +38,12 @@ public function maxTime(): void
$this->summarizeTime = false;
}

public function maxSuiteTime(): void
{
$this->summarizeTime = false;
$this->maxSuiteTime = true;
}

public function mergeRewrite(): self
{
$this->mergeRewrite = true;
Expand Down Expand Up @@ -111,6 +119,7 @@ public function run(): void

protected function loadSuites(DOMElement $current): void
{
$this->suiteDuration[$current->getAttribute('name')][] = (float) $current->getAttribute('time');
/** @var DOMNode $node */
foreach ($current->childNodes as $node) {
if ($node instanceof DOMElement) {
Expand All @@ -136,13 +145,20 @@ protected function mergeSuites(DOMDocument $dstXml): void
'errors' => 0,
'time' => 0,
];

foreach ($tests as $test) {
$resultNode->appendChild($test);

$data['assertions'] += (int)$test->getAttribute('assertions');
$data['time'] = $this->summarizeTime
? ((float)$test->getAttribute('time') + $data['time'])
: max($test->getAttribute('time'), $data['time']);
if ($this->summarizeTime) {
$data['time'] = ((float)$test->getAttribute('time') + $data['time']);
} else {
if ($this->maxSuiteTime) {
$data['time'] = max($this->suiteDuration[$suiteName]);
} else {
$data['time'] = max($test->getAttribute('time'), $data['time']);
}
}

$data['failures'] += $test->getElementsByTagName('failure')->length;
$data['errors'] += $test->getElementsByTagName('error')->length;
Expand Down
2 changes: 0 additions & 2 deletions src/Splitter/TestFileSplitterTask.php
Expand Up @@ -20,8 +20,6 @@
* ->testsFrom('tests/unit/Acme')
* ->codeceptionRoot('projects/tested')
* ->groupsTo('tests/_log/paratest_')
* ->addFilter(new Filter1())
* ->addFilter(new Filter2())
* ->run();
* ```
*
Expand Down
152 changes: 152 additions & 0 deletions tests/Merger/HtmlReportMergerTest.php
Expand Up @@ -62,4 +62,156 @@ public function testRun(): void

$this->assertSame($expectedTimeInSeconds, (float)$matches['timesum']);
}

/**
* @covers ::run
*/
public function testRunWithCodeception5Reports(): void
{
$expectedTimeInSeconds = '03:34.98';
$expectedSuccess= 3;

$reportPath = TEST_PATH . '/fixtures/reports/html/';
$task = new HtmlReportMerger();
$task->setLogger(new Logger(new NullOutput()));

$resultReport = TEST_PATH . '/result/report_codeception5.html';
$task
->from(
[
$reportPath . 'report_0_codeception5.html', // this file did not exists and it should not fail
$reportPath . 'report_1_codeception5.html',
$reportPath . 'report_2_codeception5.html',
$reportPath . 'report_3_codeception5.html',
]
)
->into($resultReport)
->run();

$this->assertFileExists($resultReport);

//read first source file as main
$dstHTML = new DOMDocument();
$dstHTML->loadHTMLFile($resultReport, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
/** @var DOMNodeList $values */
$values = (new DOMXPath($dstHTML))
->query("//*[contains(@class,'scenarioSuccessValue')]");

$this->assertCount(1, $values);
$this->assertSame($expectedSuccess, (int)$values[0]->nodeValue);

$values = (new DOMXPath($dstHTML))
->query("//h1[text() = 'Codeception Results ']");
preg_match(
'#^Codeception Results .* \((?<timesum>(([0-1]?\d|2[0-3])(?::([0-5]?\d))?(?::([0-5]?\d))\.\d+))\)$#',
$values[0]->nodeValue,
$matches
);

$this->assertSame($expectedTimeInSeconds, (string)$matches['timesum']);
}

/**
* @covers ::run
*/
public function testRunMaxTimeReports(): void
{
$expectedTime = '129.25';
$expectedSuccess= 3;

$reportPath = TEST_PATH . '/fixtures/reports/html/';
$task = new HtmlReportMerger();
$task->setLogger(new Logger(new NullOutput()));

$resultReport = TEST_PATH . '/result/report_max_time.html';
$task->maxTime();
$task
->from(
[
$reportPath . 'report_0.html', // this file did not exists and it should not fail
$reportPath . 'report_1.html',
$reportPath . 'report_2.html',
$reportPath . 'report_3.html',
]
)
->into($resultReport)
->run();

$this->assertFileExists($resultReport);

//read first source file as main
$dstHTML = new DOMDocument();
$dstHTML->loadHTMLFile($resultReport, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
/** @var DOMNodeList $values */
$values = (new DOMXPath($dstHTML))
->query("//*[contains(@class,'scenarioSuccessValue')]");

$this->assertCount(1, $values);
$this->assertSame($expectedSuccess, (int)$values[0]->nodeValue);

$values = (new DOMXPath($dstHTML))
->query("//h1[text() = 'Codeception Results ']");
preg_match(
'#^Codeception Results .* \((?<timesum>\d+\.\d+)s\)$#',
$values[0]->nodeValue,
$matches
);
$executionTime[] = (string)$matches['timesum'];
usort($executionTime, function ($a, $b) {
return strcmp($a, $b);
});
$this->assertSame($expectedTime, max($executionTime));
}

/**
* @covers ::run
*/
public function testRunMaxTimeWithCodeception5Reports(): void
{
$expectedTime = '02:09.25';
$expectedSuccess= 3;

$reportPath = TEST_PATH . '/fixtures/reports/html/';
$task = new HtmlReportMerger();
$task->setLogger(new Logger(new NullOutput()));

$resultReport = TEST_PATH . '/result/report_codeception5_max_time.html';
$task->maxTime();
$task
->from(
[
$reportPath . 'report_0_codeception5.html', // this file did not exists and it should not fail
$reportPath . 'report_1_codeception5.html',
$reportPath . 'report_2_codeception5.html',
$reportPath . 'report_3_codeception5.html',
]
)
->into($resultReport)
->run();

$this->assertFileExists($resultReport);

//read first source file as main
$dstHTML = new DOMDocument();
$dstHTML->loadHTMLFile($resultReport, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
/** @var DOMNodeList $values */
$values = (new DOMXPath($dstHTML))
->query("//*[contains(@class,'scenarioSuccessValue')]");

$this->assertCount(1, $values);
$this->assertSame($expectedSuccess, (int)$values[0]->nodeValue);

$values = (new DOMXPath($dstHTML))
->query("//h1[text() = 'Codeception Results ']");
preg_match(
'#^Codeception Results .* \((?<timesum>(([0-1]?\d|2[0-3])(?::([0-5]?\d))?(?::([0-5]?\d))\.\d+))\)$#',
$values[0]->nodeValue,
$matches
);
$executionTime[] = (string)$matches['timesum'];
usort($executionTime, function ($a, $b) {
return strcmp($a, $b);
});
$this->assertSame($expectedTime, max($executionTime));
}
}

0 comments on commit 6875f03

Please sign in to comment.