1
/****************************************************************************
2
**
3
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the test suite of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17
**
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21
**
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
29
**
30
** Other Usage
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
33
**
34
**
35
**
36
**
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
53
    void licenseCheck_data() { allSourceFilesData(); }
54
    void licenseCheck();
55
56
    void privateSlots_data() { allHeadersData(); }
57
    void privateSlots();
58
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
80
tst_Headers::tst_Headers() :
81
    copyrightPattern("\\*\\* Copyright \\(C\\) 20[0-9][0-9] Nokia Corporation and/or its subsidiary\\(-ies\\)."),
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
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
106
QStringList tst_Headers::getHeaders(const QString &path)
107
{
108
    return getFiles(path, QStringList("*.h"), QRegExp("^(?!ui_)"));
109
}
110
111
QStringList tst_Headers::getCppFiles(const QString &path)
112
{
113
    return getFiles(path, QStringList("*.cpp"), QRegExp("^(?!(moc_|qrc_))"));
114
}
115
116
QStringList tst_Headers::getQmlFiles(const QString &path)
117
{
118
    return getFiles(path, QStringList("*.qml"), QRegExp("."));
119
}
120
121
QStringList tst_Headers::getQDocFiles(const QString &path)
122
{
123
    return getFiles(path, QStringList("*.qdoc"), QRegExp("."));
124
}
125
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
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/network/access/qnetworkcookiejartlds_p.h")
181
            )
182
            continue;
183
184
        QTest::newRow(qPrintable(sourceFile)) << sourceFile;
185
    }
186
}
187
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
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
    QVERIFY(licensePattern.exactMatch(content.value(8)) ||
236
            licensePattern.exactMatch(content.value(5)));
237
    QString licenseType = licensePattern.cap(1);
238
239
    int i = 0;
240
241
    QCOMPARE(content.at(i++), QString("/****************************************************************************"));
242
    if (licenseType != "3RDPARTY") {
243
        QCOMPARE(content.at(i++), QString("**"));
244
        if (sourceFile.endsWith("/tests/auto/modeltest/dynamictreemodel.cpp")
245
            || sourceFile.endsWith("/tests/auto/modeltest/dynamictreemodel.h")
246
            || sourceFile.endsWith("/src/network/kernel/qnetworkproxy_p.h"))
247
        {
248
            // These files are not copyrighted by Nokia.
249
            ++i;
250
        } else {
251
            QVERIFY(copyrightPattern.exactMatch(content.at(i++)));
252
        }
253
        i++;
254
        QCOMPARE(content.at(i++), QString("** Contact: Nokia Corporation (qt-info@nokia.com)"));
255
    }
256
    QCOMPARE(content.at(i++), QString("**"));
257
    QVERIFY(moduleTest.exactMatch(content.at(i++)));
258
    QCOMPARE(content.at(i++), QString("**"));
259
}
260
261
void tst_Headers::privateSlots()
262
{
263
    QFETCH(QString, header);
264
265
    if (header.endsWith("/qobjectdefs.h"))
266
        return;
267
268
    QFile f(header);
269
    QVERIFY2(f.open(QIODevice::ReadOnly), qPrintable(f.errorString()));
270
271
    QStringList content = QString::fromLocal8Bit(f.readAll()).split("\n");
272
    foreach (QString line, content) {
273
        if (line.contains("Q_PRIVATE_SLOT("))
274
            QVERIFY(line.contains("_q_"));
275
    }
276
}
277
278
void tst_Headers::macros()
279
{
280
    QFETCH(QString, header);
281
282
    if (header.endsWith("_p.h") || header.endsWith("_pch.h")
283
        || header.contains("global/qconfig-") || header.endsWith("/qconfig.h")
284
        || header.contains("/src/tools/") || header.contains("/src/plugins/")
285
        || header.contains("/src/imports/")
286
        || header.endsWith("/qiconset.h") || header.endsWith("/qfeatures.h")
287
        || header.endsWith("qt_windows.h"))
288
        return;
289
290
    QFile f(header);
291
    QVERIFY2(f.open(QIODevice::ReadOnly), qPrintable(f.errorString()));
292
293
    QByteArray data = f.readAll();
294
    QStringList content = QString::fromLocal8Bit(data.replace('\r', "")).split("\n");
295
296
    int beginHeader = content.indexOf("QT_BEGIN_HEADER");
297
    int endHeader = content.lastIndexOf("QT_END_HEADER");
298
299
    QVERIFY(beginHeader >= 0);
300
    QVERIFY(endHeader >= 0);
301
    QVERIFY(beginHeader < endHeader);
302
303
    QVERIFY(content.indexOf(QRegExp("\\bslots\\s*:")) == -1);
304
    QVERIFY(content.indexOf(QRegExp("\\bsignals\\s*:")) == -1);
305
306
    if (header.contains("/sql/drivers/") || header.contains("/arch/qatomic")
307
        || header.endsWith("qglobal.h")
308
        || header.endsWith("qwindowdefs_win.h"))
309
        return;
310
311
    int qtmodule = content.indexOf(QRegExp("^QT_MODULE\\(.*\\)$"));
312
    QVERIFY(qtmodule != -1);
313
    QVERIFY(qtmodule > beginHeader);
314
    QVERIFY(qtmodule < endHeader);
315
316
    int beginNamespace = content.indexOf("QT_BEGIN_NAMESPACE");
317
    int endNamespace = content.lastIndexOf("QT_END_NAMESPACE");
318
    QVERIFY(beginNamespace != -1);
319
    QVERIFY(endNamespace != -1);
320
    QVERIFY(beginHeader < beginNamespace);
321
    QVERIFY(beginNamespace < endNamespace);
322
    QVERIFY(endNamespace < endHeader);
323
}
324
325
QTEST_MAIN(tst_Headers)
326
#include "tst_headers.moc"