1/****************************************************************************
2**
3** Copyright (C) 2016 Intel Corporation.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30#include <QtCore/QProcess>
31#include <QtCore/QRegularExpression>
32
33// We just need the DBUS_TYPE_* constants, so use our own copy
34#include "../../../../src/dbus/dbus_minimal_p.h"
35
36class tst_qdbusxml2cpp : public QObject
37{
38 Q_OBJECT
39
40 enum { Interface, Adaptor };
41
42private slots:
43 void initTestCase_data();
44 void process_data();
45 void process();
46};
47
48struct BasicTypeList {
49 char dbusType[3];
50 char cppType[24];
51};
52static const BasicTypeList basicTypeList[] =
53{
54 { DBUS_TYPE_BOOLEAN_AS_STRING, .cppType: "bool" },
55 { DBUS_TYPE_BYTE_AS_STRING, .cppType: "uchar" },
56 { DBUS_TYPE_INT16_AS_STRING, .cppType: "short" },
57 { DBUS_TYPE_UINT16_AS_STRING, .cppType: "ushort" },
58 { DBUS_TYPE_INT32_AS_STRING, .cppType: "int" },
59 { DBUS_TYPE_UINT32_AS_STRING, .cppType: "uint" },
60 { DBUS_TYPE_INT64_AS_STRING, .cppType: "qlonglong" },
61 { DBUS_TYPE_UINT64_AS_STRING, .cppType: "qulonglong" },
62 { DBUS_TYPE_DOUBLE_AS_STRING, .cppType: "double" },
63 { DBUS_TYPE_STRING_AS_STRING, .cppType: "QString" },
64 { DBUS_TYPE_OBJECT_PATH_AS_STRING, .cppType: "QDBusObjectPath" },
65 { DBUS_TYPE_SIGNATURE_AS_STRING, .cppType: "QDBusSignature" },
66#ifdef DBUS_TYPE_UNIX_FD_AS_STRING
67 { DBUS_TYPE_UNIX_FD_AS_STRING, .cppType: "QDBusUnixFileDescriptor" },
68#endif
69 { DBUS_TYPE_VARIANT_AS_STRING, .cppType: "QDBusVariant" },
70 { DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING, .cppType: "QByteArray" },
71 { DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING, .cppType: "QStringList" },
72 { DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_VARIANT_AS_STRING, .cppType: "QVariantList" }
73};
74static const int basicTypeCount = sizeof(basicTypeList) / sizeof(basicTypeList[0]);
75
76static QString stripHeader(QString output)
77{
78 static QRegularExpression header("^.*?(?=\\Rclass)", QRegularExpression::DotMatchesEverythingOption);
79 return output.remove(re: header);
80}
81
82void tst_qdbusxml2cpp::initTestCase_data()
83{
84 QTest::addColumn<int>(name: "outputMode");
85 QTest::addColumn<QString>(name: "commandLineArg");
86 QTest::newRow(dataTag: "interface") << int(Interface) << "-p";
87 QTest::newRow(dataTag: "adaptor") << int(Adaptor) << "-a";
88}
89
90void tst_qdbusxml2cpp::process_data()
91{
92 QTest::addColumn<QString>(name: "xmlSnippet");
93 QTest::addColumn<QRegularExpression>(name: "interfaceSearch");
94 QTest::addColumn<QRegularExpression>(name: "adaptorSearch");
95
96 // -- class info --
97 QTest::newRow(dataTag: "classinfo")
98 << ""
99 << QRegularExpression("staticInterfaceName\\(\\)\\s+"
100 "{ return \"local\\.name\\.is\\.not\\.important\"\\; }")
101 << QRegularExpression("Q_CLASSINFO\\(\"D-Bus Interface\", \"local\\.name\\.is\\.not\\.important\"\\)");
102
103 // -- properties --
104 for (int i = 0; i < basicTypeCount; ++i) {
105 QRegularExpression rx(QString("\\bQ_PROPERTY\\(%1 PropertyIsPresent "
106 "READ propertyIsPresent WRITE setPropertyIsPresent\\b")
107 .arg(a: basicTypeList[i].cppType));
108 QTest::newRow(dataTag: QByteArray("property-") + basicTypeList[i].dbusType)
109 << QString("<property type=\"%1\" name=\"PropertyIsPresent\" access=\"readwrite\" />")
110 .arg(a: basicTypeList[i].dbusType)
111 << rx << rx;
112 }
113
114 QTest::newRow(dataTag: "property-readonly-multi")
115 << "<property type=\"i\" name=\"Value\" access=\"read\"></property>"
116 << QRegularExpression("\\bQ_PROPERTY\\(int Value READ value(?! WRITE)")
117 << QRegularExpression("\\bQ_PROPERTY\\(int Value READ value(?! WRITE)");
118 QTest::newRow(dataTag: "property-readonly")
119 << "<property type=\"i\" name=\"Value\" access=\"read\" />"
120 << QRegularExpression("\\bQ_PROPERTY\\(int Value READ value(?! WRITE)")
121 << QRegularExpression("\\bQ_PROPERTY\\(int Value READ value(?! WRITE)");
122 QTest::newRow(dataTag: "property-writeonly")
123 << "<property type=\"i\" name=\"Value\" access=\"write\" />"
124 << QRegularExpression("\\bQ_PROPERTY\\(int Value WRITE setValue\\b")
125 << QRegularExpression("\\bQ_PROPERTY\\(int Value WRITE setValue\\b");
126
127 QTest::newRow(dataTag: "property-getter-setter")
128 << "<property type=\"b\" name=\"Enabled\" access=\"readwrite\">"
129 "<annotation name=\"org.qtproject.QtDBus.PropertyGetter\" value=\"wasEnabled\" />"
130 "<annotation name=\"org.qtproject.QtDBus.PropertySetter\" value=\"setEnabledFlag\" />"
131 "</property>"
132 << QRegularExpression("\\bQ_PROPERTY\\(bool Enabled READ wasEnabled WRITE setEnabledFlag\\b.*"
133 "\\bbool wasEnabled\\(\\) const.*" // no semi-colon
134 "\\bvoid setEnabledFlag\\(bool", QRegularExpression::DotMatchesEverythingOption)
135 << QRegularExpression("\\bQ_PROPERTY\\(bool Enabled READ wasEnabled WRITE setEnabledFlag\\b.*"
136 "\\bbool wasEnabled\\(\\) const;.*" // has semi-colon
137 "\\bvoid setEnabledFlag\\(bool", QRegularExpression::DotMatchesEverythingOption);
138
139 QTest::newRow(dataTag: "property-complex")
140 << "<property type=\"(ii)\" name=\"Position\" access=\"readwrite\">"
141 "<annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"Point\"/>"
142 "</property>"
143 << QRegularExpression("\\bQ_PROPERTY\\(Point Position READ position WRITE setPosition\\b")
144 << QRegularExpression("\\bQ_PROPERTY\\(Point Position READ position WRITE setPosition\\b");
145
146 // -- methods --
147 for (int i = 0; i < basicTypeCount; ++i) {
148 QTest::newRow(dataTag: QByteArray("method-") + basicTypeList[i].dbusType)
149 << QString("<method name=\"Method\">"
150 "<arg type=\"%1\" direction=\"out\"/>"
151 "<arg type=\"%1\" direction=\"in\"/>"
152 "</method>")
153 .arg(a: basicTypeList[i].dbusType)
154 << QRegularExpression(QString("Q_SLOTS:.*\\bQDBusPendingReply<%1> Method\\((const )?%1 ")
155 .arg(a: basicTypeList[i].cppType), QRegularExpression::DotMatchesEverythingOption)
156 << QRegularExpression(QString("Q_SLOTS:.*\\b%1 Method\\((const )?%1 ")
157 .arg(a: basicTypeList[i].cppType), QRegularExpression::DotMatchesEverythingOption);
158 }
159
160 QTest::newRow(dataTag: "method-name")
161 << "<method name=\"Method\">"
162 "<arg type=\"s\" direction=\"in\"/>"
163 "<annotation name=\"org.qtproject.QtDBus.MethodName\" value=\"MethodRenamed\" />"
164 "</method>"
165 << QRegularExpression("Q_SLOTS:.*QDBusPendingReply<> MethodRenamed\\(const QString &\\w*",
166 QRegularExpression::DotMatchesEverythingOption)
167 << QRegularExpression("Q_SLOTS:.*void MethodRenamed\\(const QString &\\w*",
168 QRegularExpression::DotMatchesEverythingOption);
169
170 QTest::newRow(dataTag: "method-complex")
171 << "<method name=\"Method\">"
172 "<arg type=\"(dd)\" direction=\"in\"/>"
173 "<arg type=\"(ii)\" direction=\"out\"/>"
174 "<annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"Point\"/>"
175 "<annotation name=\"org.qtproject.QtDBus.QtTypeName.In0\" value=\"PointF\"/>"
176 "</method>"
177 << QRegularExpression("Q_SLOTS:.*\\bQDBusPendingReply<Point> Method\\(PointF ",
178 QRegularExpression::DotMatchesEverythingOption)
179 << QRegularExpression("Q_SLOTS:.*\\bPoint Method\\(PointF ",
180 QRegularExpression::DotMatchesEverythingOption);
181
182 QTest::newRow(dataTag: "method-ss")
183 << "<method name=\"Method\">"
184 "<arg type=\"s\" direction=\"in\"/>"
185 "<arg type=\"s\" direction=\"in\"/>"
186 "<arg type=\"s\" direction=\"out\"/>"
187 "<arg type=\"s\" direction=\"out\"/>"
188 "</method>"
189 << QRegularExpression("Q_SLOTS:.*QDBusPendingReply<QString, QString> Method\\(const QString &\\w*, const QString &",
190 QRegularExpression::DotMatchesEverythingOption)
191 << QRegularExpression("Q_SLOTS:.*QString Method\\(const QString &\\w*, const QString &\\w*, QString &",
192 QRegularExpression::DotMatchesEverythingOption);
193
194 // -- signals --
195 for (int i = 0; i < basicTypeCount; ++i) {
196 QRegularExpression rx(QString("Q_SIGNALS:.*\\bvoid Signal\\((const )?%1\\b")
197 .arg(a: basicTypeList[i].cppType),
198 QRegularExpression::DotMatchesEverythingOption);
199 QTest::newRow(dataTag: QByteArray("signal-") + basicTypeList[i].dbusType)
200 << QString("<signal name=\"Signal\">"
201 "<arg type=\"%1\"/>"
202 "</signal>")
203 .arg(a: basicTypeList[i].dbusType)
204 << rx << rx;
205 }
206}
207
208void tst_qdbusxml2cpp::process()
209{
210 QFETCH(QString, xmlSnippet);
211 QFETCH(QRegularExpression, interfaceSearch);
212 QFETCH(QRegularExpression, adaptorSearch);
213 QVERIFY2(interfaceSearch.isValid(), qPrintable(interfaceSearch.errorString()));
214 QVERIFY2(adaptorSearch.isValid(), qPrintable(adaptorSearch.errorString()));
215
216 // test both interface and adaptor generation
217 QFETCH_GLOBAL(int, outputMode);
218 QFETCH_GLOBAL(QString, commandLineArg);
219
220 // Run the tool
221 const QString binpath = QLibraryInfo::location(QLibraryInfo::BinariesPath);
222 const QString command = binpath + QLatin1String("/qdbusxml2cpp");
223 QProcess process;
224 process.start(program: command, arguments: QStringList() << commandLineArg << "-" << "-N");
225 QVERIFY2(process.waitForStarted(), qPrintable(process.errorString()));
226
227 // feed it our XML data
228 static const char xmlHeader[] =
229 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
230 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE // \n is included
231 "<node>\n"
232 " <interface name=\"local.name.is.not.important\">\n"
233 " <!-- begin data -->\n";
234 static const char xmlFooter[] = "\n"
235 " <!-- end data -->\n"
236 " </interface>\n"
237 "</node>\n";
238
239 process.write(data: xmlHeader, len: int(sizeof xmlHeader) - 1);
240 process.write(data: xmlSnippet.toLatin1());
241 process.write(data: xmlFooter, len: int(sizeof xmlFooter) - 1);
242 while (process.bytesToWrite())
243 QVERIFY2(process.waitForBytesWritten(), qPrintable(process.errorString()));
244 // fprintf(stderr, "%s%s%s", xmlHeader, xmlSnippet.toLatin1().constData(), xmlFooter);
245
246 process.closeWriteChannel();
247 QVERIFY2(process.waitForFinished(), qPrintable(process.errorString()));
248
249 QByteArray errOutput = process.readAllStandardError();
250 QVERIFY2(errOutput.isEmpty(), errOutput);
251 QCOMPARE(process.exitCode(), 0);
252
253 QByteArray fullOutput = process.readAll();
254 QString output = stripHeader(output: QString::fromLatin1(str: fullOutput));
255 QVERIFY2(!output.isEmpty(), fullOutput);
256 if (outputMode == Interface)
257 QVERIFY2(output.count(interfaceSearch) == 1, qPrintable(interfaceSearch.pattern() + "\nin\n" + output));
258 else
259 QVERIFY2(output.count(adaptorSearch) == 1, qPrintable(adaptorSearch.pattern() + "\nin\n" + output));
260}
261
262QTEST_MAIN(tst_qdbusxml2cpp)
263
264#include "tst_qdbusxml2cpp.moc"
265

source code of qtbase/tests/auto/tools/qdbusxml2cpp/tst_qdbusxml2cpp.cpp