1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite 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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <QtCore/QtCore>
42 #include <QtTest/QtTest>
43
44 class tst_Headers: public QObject
45 {
46 Q_OBJECT
47 public:
48 tst_Headers();
49
50 private slots:
51 void initTestCase();
52
licenseCheck_data()53 void licenseCheck_data() { allSourceFilesData(); }
54 void licenseCheck();
55
privateSlots_data()56 void privateSlots_data() { allHeadersData(); }
57 void privateSlots();
58
macros_data()59 void macros_data() { allHeadersData(); }
60 void macros();
61
62 private:
63 static QStringList getFiles(const QString &path,
64 const QStringList dirFilters,
65 const QRegExp &exclude);
66 static QStringList getHeaders(const QString &path);
67 static QStringList getQmlFiles(const QString &path);
68 static QStringList getCppFiles(const QString &path);
69 static QStringList getQDocFiles(const QString &path);
70
71 void allSourceFilesData();
72 void allHeadersData();
73 QStringList headers;
74 const QRegExp copyrightPattern;
75 const QRegExp licensePattern;
76 const QRegExp moduleTest;
77 QString qtSrcDir;
78 };
79
tst_Headers()80 tst_Headers::tst_Headers() :
81 copyrightPattern("\\*\\* Copyright \\(C\\) 20[0-9][0-9] .*"),
82 licensePattern("\\*\\* \\$QT_BEGIN_LICENSE:(LGPL|BSD|3RDPARTY|LGPL-ONLY|FDL)\\$"),
83 moduleTest(QLatin1String("\\*\\* This file is part of the .+ of the Qt Toolkit."))
84 {
85 }
86
getFiles(const QString & path,const QStringList dirFilters,const QRegExp & excludeReg)87 QStringList tst_Headers::getFiles(const QString &path,
88 const QStringList dirFilters,
89 const QRegExp &excludeReg)
90 {
91 const QDir dir(path);
92 const QStringList dirs(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot));
93 QStringList result;
94
95 foreach (QString subdir, dirs)
96 result += getFiles(path + "/" + subdir, dirFilters, excludeReg);
97
98 QStringList entries = dir.entryList(dirFilters, QDir::Files);
99 entries = entries.filter(excludeReg);
100 foreach (QString entry, entries)
101 result += path + "/" + entry;
102
103 return result;
104 }
105
getHeaders(const QString & path)106 QStringList tst_Headers::getHeaders(const QString &path)
107 {
108 return getFiles(path, QStringList("*.h"), QRegExp("^(?!ui_)"));
109 }
110
getCppFiles(const QString & path)111 QStringList tst_Headers::getCppFiles(const QString &path)
112 {
113 return getFiles(path, QStringList("*.cpp"), QRegExp("^(?!(moc_|qrc_))"));
114 }
115
getQmlFiles(const QString & path)116 QStringList tst_Headers::getQmlFiles(const QString &path)
117 {
118 return getFiles(path, QStringList("*.qml"), QRegExp("."));
119 }
120
getQDocFiles(const QString & path)121 QStringList tst_Headers::getQDocFiles(const QString &path)
122 {
123 return getFiles(path, QStringList("*.qdoc"), QRegExp("."));
124 }
125
initTestCase()126 void tst_Headers::initTestCase()
127 {
128 qtSrcDir = QString::fromLocal8Bit(qgetenv("QTSRCDIR").isEmpty()
129 ? qgetenv("QTDIR")
130 : qgetenv("QTSRCDIR"));
131
132 headers = getHeaders(qtSrcDir + "/src");
133
134 #ifndef Q_OS_WINCE
135 // Windows CE does not have any headers on the test device
136 QVERIFY2(!headers.isEmpty(), "No headers were found, something is wrong with the auto test setup.");
137 #endif
138
139 QVERIFY(copyrightPattern.isValid());
140 QVERIFY(licensePattern.isValid());
141 }
142
allSourceFilesData()143 void tst_Headers::allSourceFilesData()
144 {
145 QTest::addColumn<QString>("sourceFile");
146
147 QStringList sourceFiles;
148 static char const * const subdirs[] = {
149 "/config.tests",
150 "/demos",
151 "/doc",
152 "/examples",
153 "/mkspecs",
154 "/qmake",
155 "/src",
156 "/tests",
157 "/tools",
158 "/util"
159 };
160
161 for (int i = 0; i < sizeof(subdirs) / sizeof(subdirs[0]); ++i) {
162 sourceFiles << getCppFiles(qtSrcDir + subdirs[i]);
163 if (subdirs[i] != QLatin1String("/tests"))
164 sourceFiles << getQmlFiles(qtSrcDir + subdirs[i]);
165 sourceFiles << getHeaders(qtSrcDir + subdirs[i]);
166 sourceFiles << getQDocFiles(qtSrcDir + subdirs[i]);
167 }
168
169 foreach (QString sourceFile, sourceFiles) {
170 if (sourceFile.contains("/3rdparty/")
171 || sourceFile.contains("/tests/auto/qmake/testdata/bundle-spaces/main.cpp")
172 || sourceFile.contains("/demos/embedded/fluidlauncher/pictureflow.cpp")
173 || sourceFile.contains("/tools/porting/src/")
174 || sourceFile.contains("/tools/assistant/lib/fulltextsearch/")
175 || sourceFile.endsWith("_pch.h.cpp")
176 || sourceFile.endsWith(".ui.h")
177 || sourceFile.endsWith("/src/corelib/global/qconfig.h")
178 || sourceFile.endsWith("/src/corelib/global/qconfig.cpp")
179 || sourceFile.endsWith("/src/tools/uic/qclass_lib_map.h")
180 || sourceFile.endsWith("src/corelib/io/qurltlds_p.h")
181 )
182 continue;
183
184 QTest::newRow(qPrintable(sourceFile)) << sourceFile;
185 }
186 }
187
allHeadersData()188 void tst_Headers::allHeadersData()
189 {
190 QTest::addColumn<QString>("header");
191
192 if (headers.isEmpty())
193 QSKIP("can't find any headers in your $QTDIR/src", SkipAll);
194
195 foreach (QString hdr, headers) {
196 if (hdr.contains("/3rdparty/") || hdr.endsWith("/src/tools/uic/qclass_lib_map.h"))
197 continue;
198
199 QTest::newRow(qPrintable(hdr)) << hdr;
200 }
201 }
202
licenseCheck()203 void tst_Headers::licenseCheck()
204 {
205 QFETCH(QString, sourceFile);
206
207 QFile f(sourceFile);
208 QVERIFY2(f.open(QIODevice::ReadOnly), qPrintable(f.errorString()));
209 QByteArray data = f.readAll();
210 data.replace("\r\n", "\n"); // Windows
211 data.replace('\r', '\n'); // Mac OS9
212 QStringList content = QString::fromLocal8Bit(data).split("\n", QString::SkipEmptyParts);
213
214 if (content.count() <= 2) // likely a #include line and empty line only. Not a copyright issue.
215 return;
216
217 if (content.first().contains("generated")) {
218 // don't scan generated files
219 return;
220 }
221
222 if (sourceFile.endsWith("/tests/auto/linguist/lupdate/testdata/good/merge_ordering/foo.cpp")
223 || sourceFile.endsWith("/tests/auto/linguist/lupdate/testdata/good/mergecpp/finddialog.cpp"))
224 {
225 // These files are meant to start with empty lines.
226 while (content.first().startsWith("//"))
227 content.takeFirst();
228 }
229
230 if (sourceFile.endsWith("/doc/src/classes/phonon-api.qdoc")) {
231 // This is an external file
232 return;
233 }
234
235 // Compare the licensePattern ($QT_BEGIN_LICENSE:(LGPL|BSD|3RDPARTY|LGPL-ONLY|FDL)) pattern
236 // with the file under test. This pattern can be found in either lines 5, 7 or 8 of the
237 // license header
238 QVERIFY(licensePattern.exactMatch(content.value(8)) ||
239 licensePattern.exactMatch(content.value(7)) ||
240 licensePattern.exactMatch(content.value(5)));
241 QString licenseType = licensePattern.cap(1);
242
243 int i = 0;
244
245 QCOMPARE(content.at(i++), QString("/****************************************************************************"));
246 if (licenseType != "3RDPARTY") {
247 QCOMPARE(content.at(i++), QString("**"));
248 if (sourceFile.endsWith("/doc/src/snippets/code/src_gui_itemviews_qidentityproxymodel.cpp")
249 || sourceFile.endsWith("/src/gui/itemviews/qidentityproxymodel.cpp")
250 || sourceFile.endsWith("/src/gui/itemviews/qidentityproxymodel.h")
251 || sourceFile.endsWith("/src/network/kernel/qnetworkproxy_p.h")
252 || sourceFile.endsWith("/tests/auto/modeltest/dynamictreemodel.cpp")
253 || sourceFile.endsWith("/tests/auto/modeltest/dynamictreemodel.h")
254 || sourceFile.endsWith("/tests/auto/qidentityproxymodel/tst_qidentityproxymodel.cpp"))
255 {
256 // These files are not copyrighted by Nokia.
257 ++i;
258 } else {
259 QVERIFY(copyrightPattern.exactMatch(content.at(i++)));
260 }
261 QCOMPARE(content.at(i++), QString("** Contact: http://www.qt.io/licensing/"));
262 }
263 QCOMPARE(content.at(i++), QString("**"));
264 QVERIFY(moduleTest.exactMatch(content.at(i++)));
265 QCOMPARE(content.at(i++), QString("**"));
266 }
267
privateSlots()268 void tst_Headers::privateSlots()
269 {
270 QFETCH(QString, header);
271
272 if (header.endsWith("/qobjectdefs.h"))
273 return;
274
275 QFile f(header);
276 QVERIFY2(f.open(QIODevice::ReadOnly), qPrintable(f.errorString()));
277
278 QStringList content = QString::fromLocal8Bit(f.readAll()).split("\n");
279 foreach (QString line, content) {
280 if (line.contains("Q_PRIVATE_SLOT("))
281 QVERIFY(line.contains("_q_"));
282 }
283 }
284
macros()285 void tst_Headers::macros()
286 {
287 QFETCH(QString, header);
288
289 if (header.endsWith("_p.h") || header.endsWith("_pch.h")
290 || header.contains("global/qconfig-") || header.endsWith("/qconfig.h")
291 || header.contains("/src/tools/") || header.contains("/src/plugins/")
292 || header.contains("/src/imports/")
293 || header.endsWith("/qiconset.h") || header.endsWith("/qfeatures.h")
294 || header.endsWith("qt_windows.h"))
295 return;
296
297 QFile f(header);
298 QVERIFY2(f.open(QIODevice::ReadOnly), qPrintable(f.errorString()));
299
300 QByteArray data = f.readAll();
301 QStringList content = QString::fromLocal8Bit(data.replace('\r', "")).split("\n");
302
303 int beginHeader = content.indexOf("QT_BEGIN_HEADER");
304 int endHeader = content.lastIndexOf("QT_END_HEADER");
305
306 QVERIFY(beginHeader >= 0);
307 QVERIFY(endHeader >= 0);
308 QVERIFY(beginHeader < endHeader);
309
310 QVERIFY(content.indexOf(QRegExp("\\bslots\\s*:")) == -1);
311 QVERIFY(content.indexOf(QRegExp("\\bsignals\\s*:")) == -1);
312
313 if (header.contains("/sql/drivers/") || header.contains("/arch/qatomic")
314 || header.endsWith("qglobal.h")
315 || header.endsWith("qwindowdefs_win.h"))
316 return;
317
318 int qtmodule = content.indexOf(QRegExp("^QT_MODULE\\(.*\\)$"));
319 QVERIFY(qtmodule != -1);
320 QVERIFY(qtmodule > beginHeader);
321 QVERIFY(qtmodule < endHeader);
322
323 int beginNamespace = content.indexOf("QT_BEGIN_NAMESPACE");
324 int endNamespace = content.lastIndexOf("QT_END_NAMESPACE");
325 QVERIFY(beginNamespace != -1);
326 QVERIFY(endNamespace != -1);
327 QVERIFY(beginHeader < beginNamespace);
328 QVERIFY(beginNamespace < endNamespace);
329 QVERIFY(endNamespace < endHeader);
330 }
331
332 QTEST_MAIN(tst_Headers)
333 #include "tst_headers.moc"
334