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
49QT_BEGIN_NAMESPACE
50
51QTestJUnitStreamer::QTestJUnitStreamer(QJUnitTestLogger *logger)
52 : testLogger(logger)
53{
54 QTEST_ASSERT(testLogger);
55}
56
57QTestJUnitStreamer::~QTestJUnitStreamer() = default;
58
59void 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
77void 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 // Messages/errors are written as CDATA within system-out, system-err,
86 // respectively, comments elsewhere
87 if (element->elementType() == QTest::LET_Message) {
88 switch (element->parentElement()->elementType()) {
89 case QTest::LET_SystemOutput:
90 case QTest::LET_SystemError:
91 QTest::qt_asprintf(formatted, "<![CDATA[");
92 break;
93 default:
94 QTest::qt_asprintf(formatted, "%s<!--", indent);
95 break;
96 }
97 return;
98 }
99
100 QTest::qt_asprintf(formatted, "%s<%s", indent, element->elementName());
101}
102
103void QTestJUnitStreamer::formatEnd(const QTestElement *element, QTestCharBuffer *formatted) const
104{
105 if (!element || !formatted )
106 return;
107
108 if (!element->childElements()) {
109 formatted->data()[0] = '\0';
110 return;
111 }
112
113 char indent[20];
114 indentForElement(element, indent, sizeof(indent));
115
116 QTest::qt_asprintf(formatted, "%s</%s>\n", indent, element->elementName());
117}
118
119void QTestJUnitStreamer::formatAttributes(const QTestElement* element, const QTestElementAttribute *attribute, QTestCharBuffer *formatted) const
120{
121 if (!attribute || !formatted )
122 return;
123
124 QTest::AttributeIndex attrindex = attribute->index();
125
126 // For messages/errors within system-out, system-err, respectively,
127 // we only want to output `message'
128 if (element && element->elementType() == QTest::LET_Message
129 && (element->parentElement()->elementType() == QTest::LET_SystemOutput
130 || element->parentElement()->elementType() == QTest::LET_SystemError)) {
131
132 if (attrindex != QTest::AI_Description) return;
133
134 QXmlTestLogger::xmlCdata(formatted, attribute->value());
135 return;
136 }
137
138 char const* key = nullptr;
139 if (attrindex == QTest::AI_Description)
140 key = "message";
141 else if (attrindex != QTest::AI_File && attrindex != QTest::AI_Line)
142 key = attribute->name();
143
144 if (key) {
145 QTestCharBuffer quotedValue;
146 QXmlTestLogger::xmlQuote(&quotedValue, attribute->value());
147 QTest::qt_asprintf(formatted, " %s=\"%s\"", key, quotedValue.constData());
148 } else {
149 formatted->data()[0] = '\0';
150 }
151}
152
153void QTestJUnitStreamer::formatAfterAttributes(const QTestElement *element, QTestCharBuffer *formatted) const
154{
155 if (!element || !formatted )
156 return;
157
158 // Messages/errors are written as CDATA within system-out, system-err,
159 // respectively, comments elsewhere
160 if (element->elementType() == QTest::LET_Message) {
161 switch (element->parentElement()->elementType()) {
162 case QTest::LET_SystemOutput:
163 case QTest::LET_SystemError:
164 QTest::qt_asprintf(formatted, "]]>\n");
165 break;
166 default:
167 QTest::qt_asprintf(formatted, " -->\n");
168 break;
169 }
170 return;
171 }
172
173 if (!element->childElements())
174 QTest::qt_asprintf(formatted, "/>\n");
175 else
176 QTest::qt_asprintf(formatted, ">\n");
177}
178
179void QTestJUnitStreamer::output(QTestElement *element) const
180{
181 QTEST_ASSERT(element);
182
183 outputString("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
184 outputElements(element);
185}
186
187void QTestJUnitStreamer::outputElements(QTestElement *element, bool) const
188{
189 QTestCharBuffer buf;
190 bool hasChildren;
191
192 // Elements are in reverse order of occurrence, so
193 // start from the end and work our way backwards.
194 while (element && element->nextElement())
195 element = element->nextElement();
196
197 while (element) {
198 hasChildren = element->childElements();
199
200 if (element->elementType() != QTest::LET_Benchmark) {
201 formatStart(element, &buf);
202 outputString(buf.data());
203
204 outputElementAttributes(element, element->attributes());
205
206 formatAfterAttributes(element, &buf);
207 outputString(buf.data());
208
209 if (hasChildren)
210 outputElements(element->childElements(), true);
211
212 formatEnd(element, &buf);
213 outputString(buf.data());
214 }
215 element = element->previousElement();
216 }
217}
218
219void QTestJUnitStreamer::outputElementAttributes(const QTestElement* element, QTestElementAttribute *attribute) const
220{
221 QTestCharBuffer buf;
222
223 // Attributes are in reverse order of occurrence, so
224 // start from the end and work our way backwards.
225 while (attribute && attribute->nextElement())
226 attribute = attribute->nextElement();
227
228 while (attribute) {
229 formatAttributes(element, attribute, &buf);
230 outputString(buf.data());
231 attribute = attribute->previousElement();
232 }
233}
234
235void QTestJUnitStreamer::outputString(const char *msg) const
236{
237 testLogger->outputString(msg);
238}
239
240QT_END_NAMESPACE
241