Passed
Push — master ( 31593e...785a7b )
by FX
03:35
created

JunitParserService::parse()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 3
nop 1
dl 0
loc 17
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Copyright (c) 2017 Francois-Xavier Soubirou.
5
 *
6
 * This file is part of ci-report.
7
 *
8
 * ci-report is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * ci-report is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with ci-report. If not, see <http://www.gnu.org/licenses/>.
20
 */
21
declare(strict_types=1);
22
23
namespace AppBundle\Service;
24
25
use AppBundle\DTO\SuiteDTO;
26
use AppBundle\DTO\TestDTO;
27
use AppBundle\Entity\Status;
28
use AppBundle\Util\SuiteTests;
29
use DateTime;
30
use DOMDocument;
31
use ReflectionClass;
32
use SimpleXMLElement;
33
34
/**
35
 * Junit Parser service class.
36
 *
37
 * @category  ci-report app
38
 *
39
 * @author    Francois-Xavier Soubirou <[email protected]>
40
 * @copyright 2017 Francois-Xavier Soubirou
41
 * @license   http://www.gnu.org/licenses/   GPLv3
42
 *
43
 * @see      https://www.ci-report.io
44
 */
45
class JunitParserService
46
{
47
    /**
48
     * @var string
49
     */
50
    protected $schemaRelativePath = '/../../../junit/xsd/junit-10.xsd';
51
52
    /**
53
     * Validate the XML document.
54
     *
55
     * @param DOMDocument $domDoc XML document to validate
56
     *
57
     * @return array
58
     */
59
    public function validate(DOMDocument $domDoc): array
60
    {
61
        libxml_use_internal_errors(true);
62
63
        $reflClass = new ReflectionClass(get_class($this));
64
        $schemaAbsolutePath = dirname($reflClass->getFileName()).$this->schemaRelativePath;
65
        $domDoc->schemaValidate($schemaAbsolutePath);
66
        $errors = libxml_get_errors();
67
68
        libxml_clear_errors();
69
70
        return $errors;
71
    }
72
73
    /**
74
     * Parse the XML string.
75
     *
76
     * @param DOMDocument $domDoc XML document to parse
77
     *
78
     * @return array array(suiteTests, ...)
79
     */
80
    public function parse(DOMDocument $domDoc): array
81
    {
82
        $suitesArray = array();
83
84
        $xml = simplexml_import_dom($domDoc);
85
86
        if ('testsuites' === $xml->getName()) {
87
            foreach ($xml->testsuite as $xmlTestsuite) {
88
                $suiteTests = $this->parseSuiteTests($xmlTestsuite);
89
                $suitesArray[] = $suiteTests;
90
            }
91
        } elseif ('testsuite' === $xml->getName()) {
92
            $suiteTests = $this->parseSuiteTests($xml);
93
            $suitesArray[] = $suiteTests;
94
        }
95
96
        return $suitesArray;
97
    }
98
99
    /**
100
     * Parse the XML element.
101
     *
102
     * @param SimpleXMLElement $xmlTestsuite Element to parse
103
     *
104
     * @return suiteTests
105
     */
106
    private function parseSuiteTests(SimpleXMLElement $xmlTestsuite): suiteTests
107
    {
108
        $suite = new SuiteDTO();
109
        $suiteTests = new SuiteTests($suite);
110
111
        // Name is required
112
        $suite->setName((string) $xmlTestsuite['name']);
113
114
        // If timestamp not set, initialize with now value
115
        if (isset($xmlTestsuite['timestamp'])) {
116
            $datetime = new DateTime((string) $xmlTestsuite['timestamp']);
117
        } else {
118
            $datetime = new DateTime();
119
        }
120
        $suite->setDatetime($datetime);
121
122
        $totalTestDuration = 0;
123
        $testsCount = 0;
124
125
        foreach ($xmlTestsuite->testcase as $xmlTestcase) {
126
            $test = $this->parseTest($xmlTestcase);
127
128
            $totalTestDuration += $test->getDuration();
129
            ++$testsCount;
130
131
            $suiteTests->addTest($test);
132
        }
133
        // If time not set, set it to sum of tests duration
134
        if (isset($xmlTestsuite['time'])) {
135
            $suite->setDuration((float) $xmlTestsuite['time']);
136
        } else {
137
            $suite->setDuration((float) $totalTestDuration);
138
        }
139
        // If disabled not set, compare tests attribute value and total of tests
140
        if (isset($xmlTestsuite['disabled'])) {
141
            $suite->setDisabled((int) $xmlTestsuite['disabled']);
142
        } else {
143
            // tests attribute is required
144
            $deltaTests = ((int) $xmlTestsuite['tests']) - $testsCount;
145
            if ($deltaTests > 0) {
146
                $suite->setDisabled($deltaTests);
147
            }
148
        }
149
150
        return $suiteTests;
151
    }
152
153
    /**
154
     * Parse the XML element.
155
     *
156
     * @param SimpleXMLElement $xmlTestcase Element to parse
157
     *
158
     * @return testDTO
159
     */
160
    private function parseTest(SimpleXMLElement $xmlTestcase): testDTO
161
    {
162
        $test = new TestDTO();
163
164
        // Name is required
165
        $test->setName((string) $xmlTestcase['name']);
166
167
        // Classname is required.
168
        // set package.class, if no dot use default package
169
        $test->setFullclassname((string) $xmlTestcase['classname']);
170
171
        // If time not set, initialize at 0
172
        if (isset($xmlTestcase['time'])) {
173
            $test->setDuration((float) $xmlTestcase['time']);
174
        } else {
175
            $test->setDuration(0);
176
        }
177
178
        // If system-out set
179
        if (isset($xmlTestcase->{'system-out'})) {
180
            $test->setSystemout((string) $xmlTestcase->{'system-out'});
181
        }
182
183
        // If system-err set
184
        if (isset($xmlTestcase->{'system-err'})) {
185
            $test->setSystemerr((string) $xmlTestcase->{'system-err'});
186
        }
187
188
        // If error
189
        if (isset($xmlTestcase->error)) {
190
            $test->setStatus(Status::ERROR);
191
            $message = $this->formatErrorFailSkipMessage(
192
                $xmlTestcase->error
193
            );
194
            $test->setPbmessage($message);
195
        } elseif (isset($xmlTestcase->failure)) {
196
            $test->setStatus(Status::FAILED);
197
            $message = $this->formatErrorFailSkipMessage(
198
                $xmlTestcase->failure
199
            );
200
            $test->setPbmessage($message);
201
        } elseif (isset($xmlTestcase->skipped)) {
202
            $test->setStatus(Status::SKIPPED);
203
            $message = $this->formatErrorFailSkipMessage(
204
                $xmlTestcase->skipped
205
            );
206
            $test->setPbmessage($message);
207
        } else {
208
            $test->setStatus(Status::SUCCESS);
209
        }
210
211
        return $test;
212
    }
213
214
    /**
215
     * Parse the XML element.
216
     *
217
     * @param SimpleXMLElement $elt Element to parse
218
     *
219
     * @return string
220
     */
221
    private function formatErrorFailSkipMessage(SimpleXMLElement $elt): string
222
    {
223
        $type = '';
224
        if (isset($elt['type'])) {
225
            $type = (string) $elt['type'];
226
        }
227
        $message = '';
228
        if (isset($elt['message'])) {
229
            $message = (string) $elt['message'];
230
        }
231
        $value = '';
232
        if (isset($elt)) {
233
            $value = (string) $elt;
234
        }
235 View Code Duplication
        if ((strlen($type) > 0) && (strlen($message) > 0)) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
236
            $fullmessage = $type.'---'.$message;
237
        } else {
238
            $fullmessage = $type.$message;
239
        }
240 View Code Duplication
        if ((strlen($fullmessage) > 0) && (strlen($value) > 0)) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
241
            $fullmessage = $fullmessage.'---'.$value;
242
        } else {
243
            $fullmessage = $fullmessage.$value;
244
        }
245
246
        return $fullmessage;
247
    }
248
}
249