Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide information about test results also in CLI output #44

Merged
merged 2 commits into from
Jan 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
<!-- There is always Unreleased section on the top. Subsections (Added, Changed, Fixed, Removed) should be added as needed. -->

## Unreleased
- Nothing yet - everything is released.
### Added
- Provide information about results of finished processes (verbose (`-v`) mode of `run` command must be enabled)

## 1.2.0 - 2016-01-11
### Added
Expand Down
1 change: 0 additions & 1 deletion src-tests/Component/LegacyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ public function testShouldFailIfTryingToAutomaticallySaveLegacyIfTestDoesntHaveP
$legacy->setFileDir(sys_get_temp_dir());

$legacy->save('data');

}

public function testShouldAutomaticallySaveAndLoadLegacyIfTestsHavePhaseInItsName()
Expand Down
53 changes: 52 additions & 1 deletion src-tests/Process/ProcessSetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,57 @@ public function testShouldSetDefinedProcessStatus()
$this->assertCount(0, $this->set->get(ProcessSet::PROCESS_STATUS_QUEUED));
}

/**
* @dataProvider processResultProvider
* @param int $exitCode
* @param string $expectedResult
*/
public function testShouldResolveAndStoreResultOfFinishedProcess($exitCode, $expectedResult)
{
$processMock = $this->getMockBuilder(Process::class)
->disableOriginalConstructor()
->getMock();

$processMock->expects($this->once())
->method('getExitCode')
->willReturn($exitCode);

$this->set->add($processMock, 'DoneTest');
$this->set->setStatus('DoneTest', ProcessSet::PROCESS_STATUS_DONE);

$doneProcesses = $this->set->get(ProcessSet::PROCESS_STATUS_DONE);

$this->assertSame($expectedResult, $doneProcesses['DoneTest']->result);
}

/**
* @return array[]
*/
public function processResultProvider()
{
return [
// $exitCode, $expectedResult
'Testcase succeeded' => [\PHPUnit_TextUI_TestRunner::SUCCESS_EXIT, ProcessSet::PROCESS_RESULT_PASSED],
'Exception thrown from PHPUnit' =>
[\PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT, ProcessSet::PROCESS_RESULT_FAILED],
'Some test failed' =>
[\PHPUnit_TextUI_TestRunner::FAILURE_EXIT, ProcessSet::PROCESS_RESULT_FAILED],
'PHP fatal error' => [255, ProcessSet::PROCESS_RESULT_FATAL],
'Process was killed' => [9, ProcessSet::PROCESS_RESULT_FATAL],
'Process was terminated' => [9, ProcessSet::PROCESS_RESULT_FATAL],
'Unrecognized exit error code should mark result as failed' => [66, ProcessSet::PROCESS_RESULT_FAILED],
];
}

public function testShouldNotStoreResultOfUnfinishedProcess()
{
$this->set->add(new Process(''), 'PreparedTest');
$this->set->setStatus('PreparedTest', ProcessSet::PROCESS_STATUS_PREPARED);

$preparedProcesses = $this->set->get(ProcessSet::PROCESS_STATUS_PREPARED);
$this->assertNull($preparedProcesses['PreparedTest']->result);
}

public function testShouldPublishProcessStatusWhenStatusWasSet()
{
$publisherMock = $this->getMockBuilder(XmlPublisher::class)
Expand All @@ -212,7 +263,7 @@ public function testShouldPublishProcessStatusWhenStatusWasSet()
->with(
'FooClassName',
ProcessSet::PROCESS_STATUS_DONE,
'passed',
ProcessSet::PROCESS_RESULT_PASSED,
$this->identicalTo(null),
$this->identicalTo(null)
);
Expand Down
29 changes: 26 additions & 3 deletions src/Console/Command/RunCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -380,12 +380,19 @@ protected function executionLoop(OutputInterface $output, ProcessSet $processSet
}
}

// Add queued tasks to prepared if their dependent task is done and delay has passed
$done = $processSet->get(ProcessSet::PROCESS_STATUS_DONE);
$doneClasses = [];
$resultsCount = [
ProcessSet::PROCESS_RESULT_PASSED => 0,
ProcessSet::PROCESS_RESULT_FAILED => 0,
ProcessSet::PROCESS_RESULT_FATAL => 0,
];
// Retrieve names of done tests and count their results
foreach ($done as $testClass => $processObject) {
$doneClasses[] = $testClass;
$resultsCount[$processObject->result]++;
}
// Add queued tasks to prepared if their dependent task is done and delay has passed
foreach ($queued as $testClass => $processObject) {
$delaySeconds = $processObject->delayMinutes * 60;

Expand All @@ -405,13 +412,29 @@ protected function executionLoop(OutputInterface $output, ProcessSet $processSet
if ($counterProcesses === $counterProcessesLast && $counterWaitingOutput % 10 !== 0) {
$counterWaitingOutput++;
} else {
// prepare information about results of finished processes
$resultsInfo = [];
if ($output->isVerbose() && $countProcessesDone > 0) {
foreach (ProcessSet::$processResults as $resultType) {
if ($resultsCount[$resultType] > 0) {
$resultsInfo[] = sprintf(
'%s: <fg=%s>%d</>',
$resultType,
$resultType == ProcessSet::PROCESS_RESULT_PASSED ? 'green' : 'red',
$resultsCount[$resultType]
);
}
}
}

$output->writeln(
sprintf(
"[%s]: waiting (running: %d, queued: %d, done: %d)",
"[%s]: waiting (running: %d, queued: %d, done: %d%s)",
date("Y-m-d H:i:s"),
$countProcessesPrepared,
$countProcessesQueued,
$countProcessesDone
$countProcessesDone,
count($resultsInfo) ? ' [' . implode(', ', $resultsInfo) . ']' : ''
)
);
$counterWaitingOutput = 1;
Expand Down
53 changes: 34 additions & 19 deletions src/Process/ProcessSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ public function add(Process $process, $className, $delayAfter = '', $delayMinute

$this->processes[$className] = (object) [
'status' => self::PROCESS_STATUS_QUEUED,
'result' => null,
'process' => $process,
'delayAfter' => $delayAfter,
'delayMinutes' => $delayMinutes,
Expand Down Expand Up @@ -191,26 +192,10 @@ public function setStatus($className, $status)
}
$this->processes[$className]->status = $status;

$result = '';
$result = null;
if ($status == self::PROCESS_STATUS_DONE) {
switch ($this->processes[$className]->process->getExitCode()) {
case \PHPUnit_TextUI_TestRunner::STATUS_PASSED: // all tests passed
$result = self::PROCESS_RESULT_PASSED;
// for passed process save just the status and result; end time was saved by TestStatusListener
break;
case 15: // Process killed because of timeout, or
case 9: // Process terminated because of timeout
$result = self::PROCESS_RESULT_FATAL;
break;
case 255: // PHP fatal error
$result = self::PROCESS_RESULT_FATAL;
break;
case \PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT: // exception thrown from phpunit
case \PHPUnit_TextUI_TestRunner::FAILURE_EXIT: // some test failed
default:
$result = self::PROCESS_RESULT_FAILED;
break;
}
$result = $this->resolveResult($className);
$this->processes[$className]->result = $result;
}

if ($this->publisher) {
Expand Down Expand Up @@ -311,4 +296,34 @@ public function optimizeOrder(OptimizeOrderInterface $optimizeStrategy)
// Sort processes descending according to corresponding values in $sortingArray
array_multisort($sortingArray, SORT_DESC, SORT_NUMERIC, $this->processes);
}

/**
* Resolve result of finished process of given class
*
* @param string $className
* @return string
*/
private function resolveResult($className)
{
switch ($this->processes[$className]->process->getExitCode()) {
case \PHPUnit_TextUI_TestRunner::SUCCESS_EXIT: // all tests passed
$result = self::PROCESS_RESULT_PASSED;
// for passed process save just the status and result; end time was saved by TestStatusListener
break;
case 15: // Process killed because of timeout, or
case 9: // Process terminated because of timeout
$result = self::PROCESS_RESULT_FATAL;
break;
case 255: // PHP fatal error
$result = self::PROCESS_RESULT_FATAL;
break;
case \PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT: // exception thrown from phpunit
case \PHPUnit_TextUI_TestRunner::FAILURE_EXIT: // some test failed
default:
$result = self::PROCESS_RESULT_FAILED;
break;
}

return $result;
}
}
3 changes: 2 additions & 1 deletion src/Publisher/AbstractPublisher.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
namespace Lmc\Steward\Publisher;

/**
* Abstract test results publisher.
* Abstract test results publisher could be extended and used for reporting test results into some custom system.
* Any ancestor class must be registered to TestStatusListener (using phpunit.xml).
*/
abstract class AbstractPublisher
{
Expand Down