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 |
} |