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 // 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
98void 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
114void 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
146void 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
167void 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
175void QTestJUnitStreamer::outputElements(QTestElement *element, bool) const
176{
177 QTestCharBuffer buf;
178 bool hasChildren;
179
180 // Elements are in reverse order of occurrence, so
181 // start from the end and work our way backwards.
182 while (element && element->nextElement())
183 element = element->nextElement();
184
185 while (element) {
186 hasChildren = element->childElements();
187
188 if (element->elementType() != QTest::LET_Benchmark) {
189 formatStart(element, &buf);
190 outputString(buf.data());
191
192 outputElementAttributes(element, element->attributes());
193
194 formatAfterAttributes(element, &buf);
195 outputString(buf.data());
196
197 if (hasChildren)
198 outputElements(element->childElements(), true);
199
200 formatEnd(element, &buf);
201 outputString(buf.data());
202 }
203 element = element->previousElement();
204 }
205}
206
207void QTestJUnitStreamer::outputElementAttributes(const QTestElement* element, QTestElementAttribute *attribute) const
208{
209 QTestCharBuffer buf;
210
211 // Attributes are in reverse order of occurrence, so
212 // start from the end and work our way backwards.
213 while (attribute && attribute->nextElement())
214 attribute = attribute->nextElement();
215
216 while (attribute) {
217 formatAttributes(element, attribute, &buf);
218 outputString(buf.data());
219 attribute = attribute->previousElement();
220 }
221}
222
223void QTestJUnitStreamer::outputString(const char *msg) const
224{
225 testLogger->outputString(msg);
226}
227
228QT_END_NAMESPACE
229