84% of 922OPs |
99% of 138Lines |
64% of 89Branches |
0% of 4109Paths |
| Method | OPs | OPs % | Lines | Line % | Branches | Branches % | Paths | Path % |
|---|---|---|---|---|---|---|---|---|
| mageekguy\atoum\reports\asynchronous\xunit::__construct() | 35 | 97% | 6 | 100% | 4 | 50% | 2 | 100% |
| mageekguy\atoum\reports\asynchronous\xunit::handleEvent() | 82 | 100% | 16 | 100% | 10 | 100% | 6 | 50% |
| mageekguy\atoum\reports\asynchronous\xunit::getTestedClasses() | 95 | 100% | 33 | 100% | 1 | 100% | 1 | 100% |
| mageekguy\atoum\reports\asynchronous\xunit::build() | 594 | 77% | 66 | 100% | 66 | 59% | 4096 | 0% |
| mageekguy\atoum\reports\asynchronous\xunit::getTestCase() | 75 | 100% | 11 | 100% | 4 | 75% | 2 | 50% |
| mageekguy\atoum\reports\asynchronous\xunit::findTestCase() | 41 | 80% | 6 | 83% | 4 | 50% | 2 | 50% |
| # | |
|---|---|
| 1 |
<?php |
| 2 |
|
| 3 |
namespace mageekguy\atoum\reports\asynchronous; |
| 4 |
|
| 5 |
use |
| 6 |
mageekguy\atoum, |
| 7 |
mageekguy\atoum\exceptions, |
| 8 |
mageekguy\atoum\report\fields |
| 9 |
; |
| 10 |
|
| 11 |
class xunit extends atoum\reports\asynchronous |
| 12 |
{
|
| 13 |
const defaultTitle = 'atoum testsuite'; |
| 14 |
|
| 15 |
protected $score = null; |
| 16 |
protected $assertions = array(); |
| 17 |
|
| 18 |
public function __construct(atoum\adapter $adapter = null)100% |
| 19 |
{
|
| 20 |
parent::__construct(); |
| 21 |
|
| 22 |
$this->setAdapter($adapter); |
| 23 |
|
| 24 |
if ($this->adapter->extension_loaded('libxml') === false)
|
| 25 |
{
|
| 26 |
throw new exceptions\runtime('libxml PHP extension is mandatory for xunit report');
|
| 27 |
} |
| 28 |
} |
| 29 |
|
| 30 |
public function handleEvent($event, atoum\observable $observable)100% |
| 31 |
{
|
| 32 |
$this->score = null; |
| 33 |
|
| 34 |
if ($event === atoum\test::afterTestMethod) |
| 35 |
{
|
| 36 |
$classname = $this->adapter->get_class($observable); |
| 37 |
$method = $observable->getCurrentMethod(); |
| 38 |
|
| 39 |
if (isset($this->assertions[$classname]) === false) |
| 40 |
{
|
| 41 |
$this->assertions[$classname] = array(); |
| 42 |
} |
| 43 |
|
| 44 |
$this->assertions[$classname][$method] = $observable->getScore()->getAssertionNumber() - array_sum($this->assertions[$classname]); |
| 45 |
} |
| 46 |
|
| 47 |
if ($event === atoum\runner::runStop) |
| 48 |
{
|
| 49 |
$this->score = $observable->getScore(); |
| 50 |
} |
| 51 |
|
| 52 |
return parent::handleEvent($event, $observable); |
| 53 |
} |
| 54 |
|
| 55 |
protected function getTestedClasses()100% |
| 56 |
{
|
| 57 |
$durations = $this->score->getDurations(); |
| 58 |
$errors = $this->score->getErrors(); |
| 59 |
$excepts = $this->score->getExceptions(); |
| 60 |
$fails = $this->score->getFailAssertions(); |
| 61 |
$uncomplete = $this->score->getUncompletedMethods(); |
| 62 |
$skipped = $this->score->getSkippedMethods(); |
| 63 |
$assertions = $this->assertions; |
| 64 |
|
| 65 |
$filterClass = function($element) use (& $clname) { return ($element['class'] == $clname); };
|
| 66 |
$extractClasses = function($list) use (& $clname, & $classes, & $assertions, $durations, $errors, $excepts, $fails, $uncomplete, $skipped, $filterClass) {
|
| 67 |
foreach ($list as $entry) |
| 68 |
{
|
| 69 |
$clname = ltrim($entry['class'], '\\'); |
| 70 |
|
| 71 |
if (isset($classes[$clname]) === false) |
| 72 |
{
|
| 73 |
$classes[$clname] = array( |
| 74 |
'errors' => array_filter($errors, $filterClass), |
| 75 |
'excepts' => array_filter($excepts, $filterClass), |
| 76 |
'fails' => array_filter($fails, $filterClass), |
| 77 |
'durations' => array_filter($durations, $filterClass), |
| 78 |
'uncomplete' => array_filter($uncomplete, $filterClass), |
| 79 |
'skipped' => array_filter($skipped, $filterClass), |
| 80 |
'assertions' => isset($assertions[$clname]) ? $assertions[$clname] : array() |
| 81 |
); |
| 82 |
} |
| 83 |
} |
| 84 |
}; |
| 85 |
|
| 86 |
$classes = array(); |
| 87 |
$extractClasses($durations); |
| 88 |
$extractClasses($errors); |
| 89 |
$extractClasses($excepts); |
| 90 |
$extractClasses($fails); |
| 91 |
$extractClasses($uncomplete); |
| 92 |
$extractClasses($skipped); |
| 93 |
|
| 94 |
return $classes; |
| 95 |
} |
| 96 |
|
| 97 |
public function build($event)100% |
| 98 |
{
|
| 99 |
$this->string = ''; |
| 100 |
|
| 101 |
if ($event === atoum\runner::runStop) |
| 102 |
{
|
| 103 |
$this->title = $this->title ?: self::defaultTitle; |
| 104 |
|
| 105 |
$document = new \DOMDocument('1.0', 'UTF-8');
|
| 106 |
$document->formatOutput = true; |
| 107 |
$document->appendChild($root = $document->createElement('testsuites'));
|
| 108 |
$root->setAttribute('name', $this->title);
|
| 109 |
$classes = $this->getTestedClasses(); |
| 110 |
|
| 111 |
foreach ($classes as $name => $class) |
| 112 |
{
|
| 113 |
$clname = $package = $name; |
| 114 |
$antiSlashOffset = strrpos($clname, '\\'); |
| 115 |
if ($antiSlashOffset !== false) |
| 116 |
{
|
| 117 |
$clname = substr($clname, $antiSlashOffset + 1); |
| 118 |
$package = substr($name, 0, $antiSlashOffset); |
| 119 |
} |
| 120 |
|
| 121 |
$root->appendChild($testSuite = $document->createElement('testsuite'));
|
| 122 |
|
| 123 |
$testSuite->setAttribute('name', $clname);
|
| 124 |
$testSuite->setAttribute('package', $package);
|
| 125 |
$testSuite->setAttribute('tests', sizeof($class['durations']) + ($fails = sizeof($class['fails'])) + ($errors = sizeof($class['excepts']) + sizeof($class['errors']) + sizeof($class['uncomplete'])) + sizeof($class['skipped']));
|
| 126 |
$testSuite->setAttribute('failures', $fails);
|
| 127 |
$testSuite->setAttribute('errors', $errors);
|
| 128 |
$testSuite->setAttribute('skipped', sizeof($class['skipped']));
|
| 129 |
|
| 130 |
$time = 0; |
| 131 |
foreach ($class['durations'] as $duration) |
| 132 |
{
|
| 133 |
$time += $duration['value']; |
| 134 |
|
| 135 |
self::getTestCase($document, $testSuite, $name, $duration['method'], $duration['value'], $duration['path'], isset($class['assertions'][$duration['method']]) ? $class['assertions'][$duration['method']] : 0); |
| 136 |
} |
| 137 |
|
| 138 |
$testSuite->setAttribute('time', $time);
|
| 139 |
|
| 140 |
foreach ($class['errors'] as $error) |
| 141 |
{
|
| 142 |
$testCase = self::getTestCase($document, $testSuite, $name, $error['method'], 0, $error['file'], isset($class['assertions'][$error['method']]) ? $class['assertions'][$error['method']] : 0); |
| 143 |
$testCase->appendChild($xError = $document->createElement('error'));
|
| 144 |
|
| 145 |
$xError->setAttribute('type', $error['type']);
|
| 146 |
$xError->appendChild($document->createCDATASection($error['message'])); |
| 147 |
} |
| 148 |
|
| 149 |
foreach ($class['uncomplete'] as $uncomplete) |
| 150 |
{
|
| 151 |
$testCase = self::getTestCase($document, $testSuite, $name, $uncomplete['method'], 0, null, isset($class['assertions'][$uncomplete['method']]) ? $class['assertions'][$uncomplete['method']] : 0); |
| 152 |
$testCase->appendChild($xFail = $document->createElement('error'));
|
| 153 |
|
| 154 |
$xFail->setAttribute('type', $uncomplete['exitCode']);
|
| 155 |
$xFail->appendChild($document->createCDATASection($uncomplete['output'])); |
| 156 |
} |
| 157 |
|
| 158 |
foreach ($class['fails'] as $fail) |
| 159 |
{
|
| 160 |
$testCase = self::getTestCase($document, $testSuite, $name, $fail['method'], 0, $fail['file'], isset($class['assertions'][$fail['method']]) ? $class['assertions'][$fail['method']] : 0); |
| 161 |
$testCase->appendChild($xFail = $document->createElement('failure'));
|
| 162 |
|
| 163 |
$xFail->setAttribute('type', 'Failure');
|
| 164 |
$xFail->setAttribute('message', $fail['asserter']);
|
| 165 |
|
| 166 |
$xFail->appendChild($document->createCDATASection($fail['fail'])); |
| 167 |
} |
| 168 |
|
| 169 |
foreach ($class['excepts'] as $exc) |
| 170 |
{
|
| 171 |
$testCase = self::getTestCase($document, $testSuite, $name, $exc['method'], 0, $exc['file'], isset($class['assertions'][$exc['method']]) ? $class['assertions'][$exc['method']] : 0); |
| 172 |
$testCase->appendChild($xError = $document->createElement('error'));
|
| 173 |
|
| 174 |
$xError->setAttribute('type', 'Exception');
|
| 175 |
$xError->appendChild($document->createCDATASection($exc['value'])); |
| 176 |
} |
| 177 |
|
| 178 |
foreach ($class['skipped'] as $skipped) |
| 179 |
{
|
| 180 |
$testCase = self::getTestCase($document, $testSuite, $name, $skipped['method'], 0, null, isset($class['assertions'][$skipped['method']]) ? $class['assertions'][$skipped['method']] : 0); |
| 181 |
$testCase->appendChild($xFail = $document->createElement('skipped'));
|
| 182 | |
| 183 |
$xFail->setAttribute('type', 'Skipped');
|
| 184 |
|
| 185 |
$xFail->appendChild($document->createCDATASection($skipped['message'])); |
| 186 |
} |
| 187 |
} |
| 188 |
|
| 189 |
$this->string = $document->saveXML(); |
| 190 |
} |
| 191 |
|
| 192 |
return $this; |
| 193 |
} |
| 194 |
|
| 195 |
private static function getTestCase(\DOMDocument $document, \DOMElement $testSuite, $class, $method, $time, $path, $assertions)100% |
| 196 |
{
|
| 197 |
if (($testCase = self::findTestCase($document, $class, $method)) === null) |
| 198 |
{
|
| 199 |
$testCase = $document->createElement('testcase');
|
| 200 |
$testCase->setAttribute('name', $method);
|
| 201 |
$testCase->setIdAttribute('name', true);
|
| 202 |
$testCase->setAttribute('time', $time);
|
| 203 |
$testCase->setAttribute('classname', $class);
|
| 204 |
$testCase->setAttribute('assertions', $assertions);
|
| 205 |
|
| 206 |
$testSuite->appendChild($testCase); |
| 207 |
} |
| 208 |
|
| 209 |
return $testCase; |
| 210 |
} |
| 211 |
|
| 212 |
private static function findTestCase(\DOMDocument $document, $class, $method)83% |
| 213 |
{
|
| 214 |
$xpath = new \DOMXPath($document); |
| 215 |
$query = $xpath->query("//testcase[@classname='$class' and @name='$method']");
|
| 216 |
|
| 217 |
if ($query->length > 0) |
| 218 |
{
|
| 219 |
return $query->item(0); |
| 220 |
} |
| 221 |
|
| 222 |
return null; |
| 223 |
} |
| 224 |
} |