Skip to content

Commit

Permalink
Merge pull request #120 from ccsuperstar/maxExecutionTime
Browse files Browse the repository at this point in the history
Max execution time fix
  • Loading branch information
vansari committed Jan 2, 2023
2 parents 0e62d4b + d91d2bf commit 2700038
Show file tree
Hide file tree
Showing 9 changed files with 1,728 additions and 13 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
2 changes: 0 additions & 2 deletions src/Splitter/TestFileSplitterTask.php
Original file line number Diff line number Diff line change
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
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

0 comments on commit 2700038

Please sign in to comment.