Skip to content

Commit

Permalink
Max execution time for suite in final report xml and max execution ti…
Browse files Browse the repository at this point in the history
…me of all html reports for report.html
  • Loading branch information
ccsuperstar committed Dec 19, 2022
1 parent e35b32f commit d91d2bf
Show file tree
Hide file tree
Showing 8 changed files with 1,728 additions and 11 deletions.
83 changes: 77 additions & 6 deletions src/Merger/HtmlReportMerger.php
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
152 changes: 152 additions & 0 deletions tests/Merger/HtmlReportMergerTest.php
Original file line number Diff line number Diff line change
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));
}
}
26 changes: 24 additions & 2 deletions tests/Merger/XmlReportMergerTaskTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ public function testMergeReports(): void
$this->assertFileExists(TEST_PATH . '/result/merged.xml');
$xml = file_get_contents(TEST_PATH . '/result/merged.xml');
$this->assertStringContainsString(
'<testsuite name="cli" tests="53" assertions="209" failures="0" errors="0"',
'<testsuite name="cli" tests="53" assertions="209" failures="0" errors="0" time="5.215475">',
$xml
);
$this->assertStringContainsString(
'<testsuite name="unit" tests="22" assertions="52"',
'<testsuite name="unit" tests="22" assertions="52" failures="0" errors="0" time="0.160419">',
$xml
);
$this->assertStringContainsString(
Expand All @@ -45,6 +45,28 @@ public function testMergeReports(): void
);
}

public function testMergeReportsMaxSuiteTime(): void
{
$task = new XmlReportMergerTask();
$task->setLogger(new Logger(new NullOutput()));
$task->maxSuiteTime();
$task->from(TEST_PATH . '/fixtures/result1.xml')
->from(TEST_PATH . '/fixtures/result2.xml')
->into(TEST_PATH . '/result/merged.xml')
->run();

$this->assertFileExists(TEST_PATH . '/result/merged.xml');
$xml = file_get_contents(TEST_PATH . '/result/merged.xml');
$this->assertStringContainsString(
'<testsuite name="cli" tests="53" assertions="209" failures="0" errors="0" time="4.980045">',
$xml
);
$this->assertStringContainsString(
'<testsuite name="unit" tests="22" assertions="52" failures="0" errors="0" time="0.160419">',
$xml
);
}

public function testMergeRewriteReports(): void
{
$task = new XmlReportMergerTask();
Expand Down
Loading

0 comments on commit d91d2bf

Please sign in to comment.