1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtTest module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include <QtTest/private/qtestjunitstreamer_p.h>
41 #include <QtTest/private/qjunittestlogger_p.h>
42 #include <QtTest/private/qtestelement_p.h>
43 #include <QtTest/private/qtestelementattribute_p.h>
44 #include <QtTest/qtestassert.h>
45 #include <QtTest/private/qtestlog_p.h>
46 #include <QtTest/private/qtestresult_p.h>
47 #include <QtTest/private/qxmltestlogger_p.h>
48 
49 QT_BEGIN_NAMESPACE
50 
QTestJUnitStreamer(QJUnitTestLogger * logger)51 QTestJUnitStreamer::QTestJUnitStreamer(QJUnitTestLogger *logger)
52     : testLogger(logger)
53 {
54     QTEST_ASSERT(testLogger);
55 }
56 
57 QTestJUnitStreamer::~QTestJUnitStreamer() = default;
58 
indentForElement(const QTestElement * element,char * buf,int size)59 void QTestJUnitStreamer::indentForElement(const QTestElement* element, char* buf, int size)
60 {
61     if (size == 0) return;
62 
63     buf[0] = 0;
64 
65     if (!element) return;
66 
67     char* endbuf = buf + size;
68     element = element->parentElement();
69     while (element && buf+2 < endbuf) {
70         *(buf++) = ' ';
71         *(buf++) = ' ';
72         *buf = 0;
73         element = element->parentElement();
74     }
75 }
76 
formatStart(const QTestElement * element,QTestCharBuffer * formatted) const77 void QTestJUnitStreamer::formatStart(const QTestElement *element, QTestCharBuffer *formatted) const
78 {
79     if (!element || !formatted )
80         return;
81 
82     char indent[20];
83     indentForElement(element, indent, sizeof(indent));
84 
85     // Errors are written as CDATA within system-err, comments elsewhere
86     if (element->elementType() == QTest::LET_Error) {
87         if (element->parentElement()->elementType() == QTest::LET_SystemError) {
88             QTest::qt_asprintf(formatted, "<![CDATA[");
89         } else {
90             QTest::qt_asprintf(formatted, "%s<!--", indent);
91         }
92         return;
93     }
94 
95     QTest::qt_asprintf(formatted, "%s<%s", indent, element->elementName());
96 }
97 
formatEnd(const QTestElement * element,QTestCharBuffer * formatted) const98 void QTestJUnitStreamer::formatEnd(const QTestElement *element, QTestCharBuffer *formatted) const
99 {
100     if (!element || !formatted )
101         return;
102 
103     if (!element->childElements()) {
104         formatted->data()[0] = '\0';
105         return;
106     }
107 
108     char indent[20];
109     indentForElement(element, indent, sizeof(indent));
110 
111     QTest::qt_asprintf(formatted, "%s</%s>\n", indent, element->elementName());
112 }
113 
formatAttributes(const QTestElement * element,const QTestElementAttribute * attribute,QTestCharBuffer * formatted) const114 void QTestJUnitStreamer::formatAttributes(const QTestElement* element, const QTestElementAttribute *attribute, QTestCharBuffer *formatted) const
115 {
116     if (!attribute || !formatted )
117         return;
118 
119     QTest::AttributeIndex attrindex = attribute->index();
120 
121     // For errors within system-err, we only want to output `message'
122     if (element && element->elementType() == QTest::LET_Error
123         && element->parentElement()->elementType() == QTest::LET_SystemError) {
124 
125         if (attrindex != QTest::AI_Description) return;
126 
127         QXmlTestLogger::xmlCdata(formatted, attribute->value());
128         return;
129     }
130 
131     char const* key = nullptr;
132     if (attrindex == QTest::AI_Description)
133         key = "message";
134     else if (attrindex != QTest::AI_File && attrindex != QTest::AI_Line)
135         key = attribute->name();
136 
137     if (key) {
138         QTestCharBuffer quotedValue;
139         QXmlTestLogger::xmlQuote(&quotedValue, attribute->value());
140         QTest::qt_asprintf(formatted, " %s=\"%s\"", key, quotedValue.constData());
141     } else {
142         formatted->data()[0] = '\0';
143     }
144 }
145 
formatAfterAttributes(const QTestElement * element,QTestCharBuffer * formatted) const146 void QTestJUnitStreamer::formatAfterAttributes(const QTestElement *element, QTestCharBuffer *formatted) const
147 {
148     if (!element || !formatted )
149         return;
150 
151     // Errors are written as CDATA within system-err, comments elsewhere
152     if (element->elementType() == QTest::LET_Error) {
153         if (element->parentElement()->elementType() == QTest::LET_SystemError) {
154             QTest::qt_asprintf(formatted, "]]>\n");
155         } else {
156             QTest::qt_asprintf(formatted, " -->\n");
157         }
158         return;
159     }
160 
161     if (!element->childElements())
162         QTest::qt_asprintf(formatted, "/>\n");
163     else
164         QTest::qt_asprintf(formatted, ">\n");
165 }
166 
output(QTestElement * element) const167 void QTestJUnitStreamer::output(QTestElement *element) const
168 {
169     QTEST_ASSERT(element);
170 
171     outputString("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
172     outputElements(element);
173 }
174 
outputElements(QTestElement * element,bool) const175 void QTestJUnitStreamer::outputElements(QTestElement *element, bool) const
176 {
177     QTestCharBuffer buf;
178     bool hasChildren;
179     /*
180         Elements are in reverse order of occurrence, so start from the end and work
181         our way backwards.
182     */
183     while (element && element->nextElement()) {
184         element = element->nextElement();
185     }
186     while (element) {
187         hasChildren = element->childElements();
188 
189         if (element->elementType() != QTest::LET_Benchmark) {
190             formatStart(element, &buf);
191             outputString(buf.data());
192 
193             outputElementAttributes(element, element->attributes());
194 
195             formatAfterAttributes(element, &buf);
196             outputString(buf.data());
197 
198             if (hasChildren)
199                 outputElements(element->childElements(), true);
200 
201             formatEnd(element, &buf);
202             outputString(buf.data());
203         }
204         element = element->previousElement();
205     }
206 }
207 
outputElementAttributes(const QTestElement * element,QTestElementAttribute * attribute) const208 void QTestJUnitStreamer::outputElementAttributes(const QTestElement* element, QTestElementAttribute *attribute) const
209 {
210     QTestCharBuffer buf;
211     while (attribute) {
212         formatAttributes(element, attribute, &buf);
213         outputString(buf.data());
214         attribute = attribute->nextElement();
215     }
216 }
217 
outputString(const char * msg) const218 void QTestJUnitStreamer::outputString(const char *msg) const
219 {
220     testLogger->outputString(msg);
221 }
222 
223 QT_END_NAMESPACE
224