From e5de0f5b308fb4f5357633279816f211c799e8bb Mon Sep 17 00:00:00 2001
From: Kai Koehne <kai.koehne@qt.io>
Date: Fri, 4 Aug 2017 08:54:31 +0200
Subject: [PATCH] qtattributionsscanner: Allow to use it for one single file

This allows a saner use with qbs, where we can and should
actually handle the dependencies correctly.

In addition, this patch adds an optional '--basedir' argument
that is needed to correctly root relative paths that are
printed in the documentation.

Change-Id: I0abf769ad5c0a3ac15b6907f83b77d369eb78d1f
GPush-Base: 5c8d5742dc7b5e00a6aed447e8f288680b58e225
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
---
 src/qtattributionsscanner/main.cpp            | 49 ++++++++++++++-----
 src/qtattributionsscanner/scanner.cpp         |  2 +-
 src/qtattributionsscanner/scanner.h           |  1 +
 .../testdata/good/minimal/expected.error      |  0
 .../testdata/good/minimal/expected.json       | 21 ++++++++
 .../tst_qtattributionsscanner.cpp             | 44 ++++++++++++-----
 6 files changed, 92 insertions(+), 25 deletions(-)
 create mode 100644 tests/auto/qtattributionsscanner/testdata/good/minimal/expected.error
 create mode 100644 tests/auto/qtattributionsscanner/testdata/good/minimal/expected.json

diff --git a/src/qtattributionsscanner/main.cpp b/src/qtattributionsscanner/main.cpp
index cd9bc1fed..700bf8814 100644
--- a/src/qtattributionsscanner/main.cpp
+++ b/src/qtattributionsscanner/main.cpp
@@ -44,12 +44,13 @@ int main(int argc, char *argv[])
 {
     QCoreApplication a(argc, argv);
     a.setApplicationName(QStringLiteral("Qt Attributions Scanner"));
-    a.setApplicationVersion(QStringLiteral("1.0"));
+    a.setApplicationVersion(QStringLiteral("1.1"));
 
     QCommandLineParser parser;
-    parser.setApplicationDescription(tr("Searches and processes qt_attribution.json files in Qt sources."));
-    parser.addPositionalArgument(QStringLiteral("directory"),
-                                 tr("The directory to scan recursively."));
+    parser.setApplicationDescription(tr("Processes qt_attribution.json files in Qt sources."));
+    parser.addPositionalArgument(QStringLiteral("path"),
+                                 tr("Path to a qt_attribution.json file, "
+                                    "or a directory to be scannned recursively."));
     parser.addHelpOption();
     parser.addVersionOption();
 
@@ -60,6 +61,10 @@ int main(int argc, char *argv[])
     QCommandLineOption filterOption(QStringLiteral("filter"),
                                     tr("Filter packages according to <filter> (e.g. QDocModule=qtcore)"),
                                     QStringLiteral("expression"));
+    QCommandLineOption baseDirOption(QStringLiteral("basedir"),
+                                     tr("Paths in documentation are made relative to this "
+                                        "directory."),
+                                     QStringLiteral("directory"));
     QCommandLineOption outputOption({ QStringLiteral("o"), QStringLiteral("output") },
                                     tr("Write generated data to <file>."),
                                     QStringLiteral("file"));
@@ -70,6 +75,7 @@ int main(int argc, char *argv[])
 
     parser.addOption(generatorOption);
     parser.addOption(filterOption);
+    parser.addOption(baseDirOption);
     parser.addOption(outputOption);
     parser.addOption(verboseOption);
     parser.addOption(silentOption);
@@ -90,13 +96,22 @@ int main(int argc, char *argv[])
     if (parser.positionalArguments().size() != 1)
         parser.showHelp(2);
 
-    const QString directory = parser.positionalArguments().last();
-
-    if (logLevel == VerboseLog)
-        std::cerr << qPrintable(tr("Recursively scanning %1 for qt_attribution.json files...").arg(
-                                    QDir::toNativeSeparators(directory))) << std::endl;
-
-    QVector<Package> packages = Scanner::scanDirectory(directory, logLevel);
+    const QString path = parser.positionalArguments().last();
+
+    QVector<Package> packages;
+    const QFileInfo pathInfo(path);
+    if (pathInfo.isDir()) {
+        if (logLevel == VerboseLog)
+            std::cerr << qPrintable(tr("Recursively scanning %1 for qt_attribution.json files...").arg(
+                                        QDir::toNativeSeparators(path))) << std::endl;
+        packages = Scanner::scanDirectory(path, logLevel);
+    } else if (pathInfo.isFile()) {
+        packages = Scanner::readFile(path, logLevel);
+    } else {
+        std::cerr << qPrintable(tr("%1 is not a valid file or directory.").arg(
+                                    QDir::toNativeSeparators(path))) << std::endl << std::endl;
+        parser.showHelp(7);
+    }
 
     if (parser.isSet(filterOption)) {
         PackageFilter filter(parser.value(filterOption));
@@ -124,8 +139,16 @@ int main(int argc, char *argv[])
 
     QString generator = parser.value(generatorOption);
     if (generator == QLatin1String("qdoc")) {
-        // include top level module name in printed paths
-        QString baseDirectory = QDir(directory).absoluteFilePath(QStringLiteral(".."));
+        QString baseDirectory = parser.value(baseDirOption);
+        if (baseDirectory.isEmpty()) {
+            if (pathInfo.isDir()) {
+                // include top level module name in printed paths
+                baseDirectory = pathInfo.dir().absoluteFilePath(QStringLiteral(".."));
+            } else {
+                baseDirectory = pathInfo.absoluteDir().absoluteFilePath(QStringLiteral(".."));
+            }
+        }
+
         QDocGenerator::generate(out, packages, baseDirectory, logLevel);
     } else if (generator == QLatin1String("json")) {
         JsonGenerator::generate(out, packages, logLevel);
diff --git a/src/qtattributionsscanner/scanner.cpp b/src/qtattributionsscanner/scanner.cpp
index 488417e89..d7d958138 100644
--- a/src/qtattributionsscanner/scanner.cpp
+++ b/src/qtattributionsscanner/scanner.cpp
@@ -140,7 +140,7 @@ static Package readPackage(const QJsonObject &object, const QString &filePath, L
     return p;
 }
 
-static QVector<Package> readFile(const QString &filePath, LogLevel logLevel)
+QVector<Package> readFile(const QString &filePath, LogLevel logLevel)
 {
     if (logLevel == VerboseLog) {
         std::cerr << qPrintable(tr("Reading file %1...").arg(
diff --git a/src/qtattributionsscanner/scanner.h b/src/qtattributionsscanner/scanner.h
index 5d93cb8ee..f3ae9f0ea 100644
--- a/src/qtattributionsscanner/scanner.h
+++ b/src/qtattributionsscanner/scanner.h
@@ -37,6 +37,7 @@
 
 namespace Scanner {
 
+QVector<Package> readFile(const QString &filePath, LogLevel logLevel);
 QVector<Package> scanDirectory(const QString &directory, LogLevel logLevel);
 
 }
diff --git a/tests/auto/qtattributionsscanner/testdata/good/minimal/expected.error b/tests/auto/qtattributionsscanner/testdata/good/minimal/expected.error
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/auto/qtattributionsscanner/testdata/good/minimal/expected.json b/tests/auto/qtattributionsscanner/testdata/good/minimal/expected.json
new file mode 100644
index 000000000..6a75269c0
--- /dev/null
+++ b/tests/auto/qtattributionsscanner/testdata/good/minimal/expected.json
@@ -0,0 +1,21 @@
+[
+    {
+        "Copyright": "Copyright",
+        "Description": "",
+        "Homepage": "",
+        "Id": "minimal",
+        "License": "License",
+        "LicenseFile": "",
+        "LicenseId": "",
+        "Name": "Minimal",
+        "Path": "%{PWD}",
+        "Files": "",
+        "QDocModule": "qtest",
+        "QtParts": [
+            "libs"
+        ],
+        "QtUsage": "Usage",
+        "Version": "",
+        "DownloadLocation": ""
+    }
+]
diff --git a/tests/auto/qtattributionsscanner/tst_qtattributionsscanner.cpp b/tests/auto/qtattributionsscanner/tst_qtattributionsscanner.cpp
index c3e8bb9b6..7740fde9b 100644
--- a/tests/auto/qtattributionsscanner/tst_qtattributionsscanner.cpp
+++ b/tests/auto/qtattributionsscanner/tst_qtattributionsscanner.cpp
@@ -47,6 +47,8 @@ private slots:
     void test();
 
 private:
+    void readExpectedFile(const QString &baseDir, const QString &fileName, QByteArray *content);
+
     QString m_cmd;
     QString m_basePath;
 };
@@ -62,25 +64,45 @@ tst_qtattributionsscanner::tst_qtattributionsscanner()
 
 void tst_qtattributionsscanner::test_data()
 {
-    QTest::addColumn<QString>("directory");
-    QTest::newRow("good") << QStringLiteral("good");
-    QTest::newRow("warnings (incomplete)") << QStringLiteral("warnings/incomplete");
-    QTest::newRow("warnings (unknown attribute)") << QStringLiteral("warnings/unknown");
+    QTest::addColumn<QString>("input");
+    QTest::addColumn<QString>("stdout_file");
+    QTest::addColumn<QString>("stderr_file");
+
+    QTest::newRow("good")
+            << QStringLiteral("good")
+            << QStringLiteral("good/expected.json")
+            << QStringLiteral("good/expected.error");
+    QTest::newRow("warnings (incomplete)")
+            << QStringLiteral("warnings/incomplete")
+            << QStringLiteral("warnings/incomplete/expected.json")
+            << QStringLiteral("warnings/incomplete/expected.error");
+    QTest::newRow("warnings (unknown attribute)")
+            << QStringLiteral("warnings/unknown")
+            << QStringLiteral("warnings/unknown/expected.json")
+            << QStringLiteral("warnings/unknown/expected.error");
+    QTest::newRow("singlefile")
+            << QStringLiteral("good/minimal/qt_attribution.json")
+            << QStringLiteral("good/minimal/expected.json")
+            << QStringLiteral("good/minimal/expected.error");
 }
 
-static void readExpectedFile(const QString &dir, const QString &fileName, QByteArray *content)
+void tst_qtattributionsscanner::readExpectedFile(const QString &baseDir, const QString &fileName, QByteArray *content)
 {
-    QFile file(QDir(dir).absoluteFilePath(fileName));
+    QFile file(QDir(m_basePath).absoluteFilePath(fileName));
     QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Text), "Could not open " + file.fileName().toLocal8Bit());
     *content = file.readAll();
-    content->replace("%{PWD}", dir.toUtf8());
+    content->replace("%{PWD}", baseDir.toUtf8());
 }
 
 void tst_qtattributionsscanner::test()
 {
-    QFETCH(QString, directory);
+    QFETCH(QString, input);
+    QFETCH(QString, stdout_file);
+    QFETCH(QString, stderr_file);
 
-    QString dir = QDir(m_basePath).absoluteFilePath(directory);
+    QString dir = QDir(m_basePath).absoluteFilePath(input);
+    if (QFileInfo(dir).isFile())
+        dir = QFileInfo(dir).absolutePath();
 
     QProcess proc;
     QString command = m_cmd + " " + dir + " --output-format json";
@@ -100,7 +122,7 @@ void tst_qtattributionsscanner::test()
         stdErr.replace(QDir::separator(), "/");
 
         QByteArray expectedErrorOutput;
-        readExpectedFile(dir, "expected.error", &expectedErrorOutput);
+        readExpectedFile(dir, stderr_file, &expectedErrorOutput);
 
         QCOMPARE(stdErr, expectedErrorOutput);
     }
@@ -113,7 +135,7 @@ void tst_qtattributionsscanner::test()
         QVERIFY2(!actualJson.isNull(), "Invalid output: " + jsonError.errorString().toLatin1());
 
         QByteArray expectedOutput;
-        readExpectedFile(dir, "expected.json", &expectedOutput);
+        readExpectedFile(dir, stdout_file, &expectedOutput);
         QJsonDocument expectedJson = QJsonDocument::fromJson(expectedOutput);
 
         if (!QTest::qCompare(actualJson, expectedJson, "actualJson", "expectedJson", __FILE__, __LINE__)) {
-- 
GitLab