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/qtestxunitstreamer_p.h>
41#include <QtTest/private/qxunittestlogger_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
51QTestXunitStreamer::QTestXunitStreamer(QXunitTestLogger *logger)
52 : testLogger(logger)
53{
54 QTEST_ASSERT(testLogger);
55}
56
57QTestXunitStreamer::~QTestXunitStreamer() = default;
58
59void QTestXunitStreamer::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 QTestXunitStreamer::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 QTestXunitStreamer::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 QTestXunitStreamer::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 QTestXunitStreamer::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 QTestXunitStreamer::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 QTestXunitStreamer::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
208void QTestXunitStreamer::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
218void QTestXunitStreamer::outputString(const char *msg) const
219{
220 testLogger->outputString(msg);
221}
222
223QT_END_NAMESPACE
224