aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/api/testdata/project-editing/existingfile1.txt0
-rw-r--r--tests/auto/api/testdata/project-editing/existingfile2.txt0
-rw-r--r--tests/auto/api/testdata/project-editing/existingfile3.txt0
-rw-r--r--tests/auto/api/testdata/project-editing/file.cpp0
-rw-r--r--tests/auto/api/testdata/project-editing/file.h0
-rw-r--r--tests/auto/api/testdata/project-editing/main.cpp0
-rw-r--r--tests/auto/api/testdata/project-editing/newfile1.txt0
-rw-r--r--tests/auto/api/testdata/project-editing/newfile2.txt0
-rw-r--r--tests/auto/api/testdata/project-editing/newfile3.txt0
-rw-r--r--tests/auto/api/testdata/project-editing/newfile4.txt0
-rw-r--r--tests/auto/api/testdata/project-editing/project.qbs31
-rw-r--r--tests/auto/api/testdata/project-editing/subdir/file.txt0
-rw-r--r--tests/auto/api/testdata/references/invalid1.qbs5
-rw-r--r--tests/auto/api/testdata/references/invalid2.qbs5
-rw-r--r--tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject1.qbs0
-rw-r--r--tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject2.qbs0
-rw-r--r--tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject3.qbs0
-rw-r--r--tests/auto/api/testdata/references/subdir-with-no-project/test.txt0
-rw-r--r--tests/auto/api/testdata/references/subdir-with-one-project/p.qbs3
-rw-r--r--tests/auto/api/testdata/references/subdir-with-one-project/test.txt0
-rw-r--r--tests/auto/api/testdata/references/valid.qbs5
-rw-r--r--tests/auto/api/testdata/source-file-in-build-dir/file.cpp0
-rw-r--r--tests/auto/api/testdata/source-file-in-build-dir/project.qbs5
-rw-r--r--tests/auto/api/testdata/source-file-in-build-dir/qt-debug/moc_blubb.cpp0
-rw-r--r--tests/auto/api/testdata/source-file-in-build-dir/qt-debug/qt-debug.bg0
-rw-r--r--tests/auto/api/tst_api.cpp330
-rw-r--r--tests/auto/api/tst_api.h16
-rw-r--r--tests/auto/auto.pri2
-rw-r--r--tests/auto/blackbox/testdata/dynamicRuleOutputs/after/numbers.l77
-rw-r--r--tests/auto/blackbox/testdata/dynamicRuleOutputs/before/flexoptionsreader.js103
-rw-r--r--tests/auto/blackbox/testdata/dynamicRuleOutputs/before/genlexer.qbs103
-rw-r--r--tests/auto/blackbox/testdata/dynamicRuleOutputs/before/numbers.l78
-rw-r--r--tests/auto/blackbox/testdata/embedInfoPlist/embedInfoPlist.qbs13
-rw-r--r--tests/auto/blackbox/testdata/embedInfoPlist/main.m9
-rw-r--r--tests/auto/blackbox/testdata/explicitlyDependsOn/project.qbs2
-rw-r--r--tests/auto/blackbox/testdata/jsextensions/file.qbs4
-rw-r--r--tests/auto/blackbox/testdata/jsextensions/propertylist.qbs38
-rwxr-xr-xtests/auto/blackbox/testdata/nsis/hello.bat1
-rwxr-xr-xtests/auto/blackbox/testdata/nsis/hello.nsi19
-rwxr-xr-xtests/auto/blackbox/testdata/nsis/hello.qbs10
-rw-r--r--tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs2
-rw-r--r--tests/auto/blackbox/testdata/propertyChanges/project.qbs17
-rw-r--r--tests/auto/blackbox/testdata/ruleConditions/modules/narfzort/narfzort.qbs2
-rw-r--r--tests/auto/blackbox/testdata/soft-dependency/main.cpp4
-rw-r--r--tests/auto/blackbox/testdata/soft-dependency/project.qbs14
-rw-r--r--tests/auto/blackbox/testdata/subprojects/toplevelproject.qbs2
-rw-r--r--tests/auto/blackbox/testdata/trackExternalProductChanges/fileList.js2
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp221
-rw-r--r--tests/auto/blackbox/tst_blackbox.h12
-rw-r--r--tests/auto/cmdlineparser/tst_cmdlineparser.cpp12
-rw-r--r--tests/manual/WiXInstallers/ExampleScript.bat1
-rwxr-xr-xtests/manual/WiXInstallers/QbsBootstrapper.wxs10
-rwxr-xr-xtests/manual/WiXInstallers/QbsSetup.wxs34
-rw-r--r--tests/manual/WiXInstallers/WiXInstallers.qbs18
54 files changed, 1157 insertions, 53 deletions
diff --git a/tests/auto/api/testdata/project-editing/existingfile1.txt b/tests/auto/api/testdata/project-editing/existingfile1.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/project-editing/existingfile1.txt
diff --git a/tests/auto/api/testdata/project-editing/existingfile2.txt b/tests/auto/api/testdata/project-editing/existingfile2.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/project-editing/existingfile2.txt
diff --git a/tests/auto/api/testdata/project-editing/existingfile3.txt b/tests/auto/api/testdata/project-editing/existingfile3.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/project-editing/existingfile3.txt
diff --git a/tests/auto/api/testdata/project-editing/file.cpp b/tests/auto/api/testdata/project-editing/file.cpp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/project-editing/file.cpp
diff --git a/tests/auto/api/testdata/project-editing/file.h b/tests/auto/api/testdata/project-editing/file.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/project-editing/file.h
diff --git a/tests/auto/api/testdata/project-editing/main.cpp b/tests/auto/api/testdata/project-editing/main.cpp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/project-editing/main.cpp
diff --git a/tests/auto/api/testdata/project-editing/newfile1.txt b/tests/auto/api/testdata/project-editing/newfile1.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/project-editing/newfile1.txt
diff --git a/tests/auto/api/testdata/project-editing/newfile2.txt b/tests/auto/api/testdata/project-editing/newfile2.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/project-editing/newfile2.txt
diff --git a/tests/auto/api/testdata/project-editing/newfile3.txt b/tests/auto/api/testdata/project-editing/newfile3.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/project-editing/newfile3.txt
diff --git a/tests/auto/api/testdata/project-editing/newfile4.txt b/tests/auto/api/testdata/project-editing/newfile4.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/project-editing/newfile4.txt
diff --git a/tests/auto/api/testdata/project-editing/project.qbs b/tests/auto/api/testdata/project-editing/project.qbs
new file mode 100644
index 000000000..9190f846c
--- /dev/null
+++ b/tests/auto/api/testdata/project-editing/project.qbs
@@ -0,0 +1,31 @@
+import qbs
+
+CppApplication {
+ Group {
+ name: "Existing Group 1"
+ files: ["existingfile1.txt"]
+ }
+ property string aFile: "existingfile2.txt"
+ Group {
+ name: "Existing Group 2"
+ files: product.aFile
+ }
+ Group {
+ name: "Existing Group 3"
+ files: {
+ var file = "existingfile3.txt";
+ return file;
+ }
+ }
+ Group {
+ name: "Existing Group 4"
+ prefix: "subdir/"
+ files: []
+ }
+ Group {
+ name: "Existing Group 5"
+ prefix: "blubb"
+ files: []
+ }
+ files: "main.cpp"
+}
diff --git a/tests/auto/api/testdata/project-editing/subdir/file.txt b/tests/auto/api/testdata/project-editing/subdir/file.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/project-editing/subdir/file.txt
diff --git a/tests/auto/api/testdata/references/invalid1.qbs b/tests/auto/api/testdata/references/invalid1.qbs
new file mode 100644
index 000000000..4bbb26d3a
--- /dev/null
+++ b/tests/auto/api/testdata/references/invalid1.qbs
@@ -0,0 +1,5 @@
+import qbs
+
+Project {
+ references: "subdir-with-no-project"
+} \ No newline at end of file
diff --git a/tests/auto/api/testdata/references/invalid2.qbs b/tests/auto/api/testdata/references/invalid2.qbs
new file mode 100644
index 000000000..1946e2221
--- /dev/null
+++ b/tests/auto/api/testdata/references/invalid2.qbs
@@ -0,0 +1,5 @@
+import qbs
+
+Project {
+ references: "subdir-with-multiple-projects"
+} \ No newline at end of file
diff --git a/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject1.qbs b/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject1.qbs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject1.qbs
diff --git a/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject2.qbs b/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject2.qbs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject2.qbs
diff --git a/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject3.qbs b/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject3.qbs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject3.qbs
diff --git a/tests/auto/api/testdata/references/subdir-with-no-project/test.txt b/tests/auto/api/testdata/references/subdir-with-no-project/test.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/references/subdir-with-no-project/test.txt
diff --git a/tests/auto/api/testdata/references/subdir-with-one-project/p.qbs b/tests/auto/api/testdata/references/subdir-with-one-project/p.qbs
new file mode 100644
index 000000000..7d453a671
--- /dev/null
+++ b/tests/auto/api/testdata/references/subdir-with-one-project/p.qbs
@@ -0,0 +1,3 @@
+import qbs
+
+Project { }
diff --git a/tests/auto/api/testdata/references/subdir-with-one-project/test.txt b/tests/auto/api/testdata/references/subdir-with-one-project/test.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/references/subdir-with-one-project/test.txt
diff --git a/tests/auto/api/testdata/references/valid.qbs b/tests/auto/api/testdata/references/valid.qbs
new file mode 100644
index 000000000..43d728a4d
--- /dev/null
+++ b/tests/auto/api/testdata/references/valid.qbs
@@ -0,0 +1,5 @@
+import qbs
+
+Project {
+ references: "subdir-with-one-project"
+}
diff --git a/tests/auto/api/testdata/source-file-in-build-dir/file.cpp b/tests/auto/api/testdata/source-file-in-build-dir/file.cpp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/source-file-in-build-dir/file.cpp
diff --git a/tests/auto/api/testdata/source-file-in-build-dir/project.qbs b/tests/auto/api/testdata/source-file-in-build-dir/project.qbs
new file mode 100644
index 000000000..47ae50a93
--- /dev/null
+++ b/tests/auto/api/testdata/source-file-in-build-dir/project.qbs
@@ -0,0 +1,5 @@
+import qbs
+
+CppApplication {
+ files: "**/*.cpp"
+}
diff --git a/tests/auto/api/testdata/source-file-in-build-dir/qt-debug/moc_blubb.cpp b/tests/auto/api/testdata/source-file-in-build-dir/qt-debug/moc_blubb.cpp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/source-file-in-build-dir/qt-debug/moc_blubb.cpp
diff --git a/tests/auto/api/testdata/source-file-in-build-dir/qt-debug/qt-debug.bg b/tests/auto/api/testdata/source-file-in-build-dir/qt-debug/qt-debug.bg
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/source-file-in-build-dir/qt-debug/qt-debug.bg
diff --git a/tests/auto/api/tst_api.cpp b/tests/auto/api/tst_api.cpp
index c784f6a86..7536f4302 100644
--- a/tests/auto/api/tst_api.cpp
+++ b/tests/auto/api/tst_api.cpp
@@ -29,13 +29,15 @@
#include "tst_api.h"
-#include "../../src/app/shared/qbssettings.h"
+#include "../../../src/app/shared/qbssettings.h"
#include <api/jobs.h>
#include <api/project.h>
#include <api/projectdata.h>
#include <logging/ilogsink.h>
+#include <tools/fileinfo.h>
#include <tools/hostosinfo.h>
+#include <tools/buildoptions.h>
#include <tools/installoptions.h>
#include <tools/preferences.h>
#include <tools/setupprojectparameters.h>
@@ -43,7 +45,9 @@
#include <QCoreApplication>
#include <QDir>
#include <QEventLoop>
+#include <QFileInfo>
#include <QScopedPointer>
+#include <QStringList>
#include <QTest>
class LogSink: public qbs::ILogSink
@@ -54,7 +58,22 @@ class LogSink: public qbs::ILogSink
void doPrintMessage(qbs::LoggerLevel, const QString &, const QString &) { }
};
-TestApi::TestApi() : m_logSink(new LogSink)
+class BuildDescriptionReveiver : public QObject
+{
+ Q_OBJECT
+public:
+ QString descriptions;
+
+private slots:
+ void handleDescription(const QString &, const QString &description) {
+ descriptions += description;
+ }
+};
+
+TestApi::TestApi()
+ : m_logSink(new LogSink)
+ , m_sourceDataDir(QDir::cleanPath(SRCDIR "/testdata"))
+ , m_workingDataDir(QCoreApplication::applicationDirPath() + "/../tests/auto/api/testWorkDir")
{
}
@@ -63,6 +82,14 @@ TestApi::~TestApi()
delete m_logSink;
}
+void TestApi::initTestCase()
+{
+ QString errorMessage;
+ qbs::Internal::removeDirectoryWithContents(m_workingDataDir, &errorMessage);
+ QVERIFY2(qbs::Internal::copyFileRecursion(m_sourceDataDir,
+ m_workingDataDir, false, &errorMessage), qPrintable(errorMessage));
+}
+
static void waitForFinished(qbs::AbstractJob *job)
{
QEventLoop loop;
@@ -70,11 +97,240 @@ static void waitForFinished(qbs::AbstractJob *job)
loop.exec();
}
+
+void printProjectData(const qbs::ProjectData &project)
+{
+ foreach (const qbs::ProductData &p, project.products()) {
+ qDebug(" Product '%s' at %s", qPrintable(p.name()), qPrintable(p.location().toString()));
+ foreach (const qbs::GroupData &g, p.groups()) {
+ qDebug(" Group '%s' at %s", qPrintable(g.name()), qPrintable(g.location().toString()));
+ qDebug(" Files: %s", qPrintable(g.filePaths().join(QLatin1String(", "))));
+ }
+ }
+}
+
+qbs::GroupData findGroup(const qbs::ProductData &product, const QString &name)
+{
+ foreach (const qbs::GroupData &g, product.groups()) {
+ if (g.name() == name)
+ return g;
+ }
+ return qbs::GroupData();
+}
+
+void TestApi::changeContent()
+{
+ qbs::SetupProjectParameters setupParams = defaultSetupParameters();
+ setupParams.setProjectFilePath(QDir::cleanPath(m_workingDataDir +
+ "/project-editing/project.qbs"));
+ QScopedPointer<qbs::SetupProjectJob> job(qbs::Project::setupProject(setupParams,
+ m_logSink, 0));
+ waitForFinished(job.data());
+ QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
+ qbs::Project project = job->project();
+ qbs::ProjectData projectData = project.projectData();
+ QCOMPARE(projectData.allProducts().count(), 1);
+ qbs::ProductData product = projectData.allProducts().first();
+ QCOMPARE(product.groups().count(), 6);
+
+ // Error handling: Invalid product.
+ qbs::ErrorInfo errorInfo = project.addGroup(qbs::ProductData(), "blubb");
+ QVERIFY(errorInfo.hasError());
+ QVERIFY(errorInfo.toString().contains("invalid"));
+
+ // Error handling: Empty group name.
+ errorInfo = project.addGroup(product, QString());
+ QVERIFY(errorInfo.hasError());
+ QVERIFY(errorInfo.toString().contains("empty"));
+
+ errorInfo = project.addGroup(product, "New Group 1");
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+
+ errorInfo = project.addGroup(product, "New Group 2");
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+
+ // Error handling: Group already inserted.
+ errorInfo = project.addGroup(product, "New Group 1");
+ QVERIFY(errorInfo.hasError());
+ QVERIFY(errorInfo.toString().contains("already"));
+
+ // Error handling: Add list of files with double entries.
+ errorInfo = project.addFiles(product, qbs::GroupData(), QStringList() << "file.cpp"
+ << "file.cpp");
+ QVERIFY(errorInfo.hasError());
+ QVERIFY2(errorInfo.toString().contains("more than once"), qPrintable(errorInfo.toString()));
+
+ // Add files to empty array literal.
+ projectData = project.projectData();
+ QVERIFY(projectData.products().count() == 1);
+ product = projectData.products().first();
+ QCOMPARE(product.groups().count(), 8);
+ qbs::GroupData group = findGroup(product, "New Group 1");
+ QVERIFY(group.isValid());
+ errorInfo = project.addFiles(product, group, QStringList() << "file.h" << "file.cpp");
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+
+ // Error handling: Add the same file again.
+ projectData = project.projectData();
+ QVERIFY(projectData.products().count() == 1);
+ product = projectData.products().first();
+ QCOMPARE(product.groups().count(), 8);
+ group = findGroup(product, "New Group 1");
+ QVERIFY(group.isValid());
+ errorInfo = project.addFiles(product, group, QStringList() << "file.cpp");
+ QVERIFY(errorInfo.hasError());
+ QVERIFY2(errorInfo.toString().contains("already"), qPrintable(errorInfo.toString()));
+
+ // Remove one of the newly added files again.
+ errorInfo = project.removeFiles(product, group, QStringList("file.h"));
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+
+ // Error handling: Try to remove the same file again.
+ projectData = project.projectData();
+ QVERIFY(projectData.products().count() == 1);
+ product = projectData.products().first();
+ QCOMPARE(product.groups().count(), 8);
+ group = findGroup(product, "New Group 1");
+ QVERIFY(group.isValid());
+ errorInfo = project.removeFiles(product, group, QStringList() << "file.h");
+ QVERIFY(errorInfo.hasError());
+ QVERIFY2(errorInfo.toString().contains("not known"), qPrintable(errorInfo.toString()));
+
+ // Error handling: Try to remove a file from a complex list.
+ group = findGroup(product, "Existing Group 2");
+ QVERIFY(group.isValid());
+ errorInfo = project.removeFiles(product, group, QStringList() << "existingfile2.txt");
+ QVERIFY(errorInfo.hasError());
+ QVERIFY2(errorInfo.toString().contains("complex"), qPrintable(errorInfo.toString()));
+
+ // Remove file from product's 'files' binding.
+ errorInfo = project.removeFiles(product, qbs::GroupData(), QStringList("main.cpp"));
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+
+ // Add file to non-empty array literal.
+ projectData = project.projectData();
+ QVERIFY(projectData.products().count() == 1);
+ product = projectData.products().first();
+ group = findGroup(product, "Existing Group 1");
+ QVERIFY(group.isValid());
+ errorInfo = project.addFiles(product, group, QStringList() << "newfile1.txt");
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+
+ // Add files to list represented as a single string.
+ projectData = project.projectData();
+ QVERIFY(projectData.products().count() == 1);
+ product = projectData.products().first();
+ errorInfo = project.addFiles(product, qbs::GroupData(), QStringList() << "newfile2.txt");
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+
+ // Add files to list represented as an identifier.
+ projectData = project.projectData();
+ QVERIFY(projectData.products().count() == 1);
+ product = projectData.products().first();
+ group = findGroup(product, "Existing Group 2");
+ QVERIFY(group.isValid());
+ errorInfo = project.addFiles(product, group, QStringList() << "newfile3.txt");
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+
+ // Add files to list represented as a block of code (not yet implemented).
+ projectData = project.projectData();
+ QVERIFY(projectData.products().count() == 1);
+ product = projectData.products().first();
+ group = findGroup(product, "Existing Group 3");
+ QVERIFY(group.isValid());
+ errorInfo = project.addFiles(product, group, QStringList() << "newfile4.txt");
+ QVERIFY(errorInfo.hasError());
+ QVERIFY2(errorInfo.toString().contains("complex"), qPrintable(errorInfo.toString()));
+
+ // Add file to group with directory prefix.
+ projectData = project.projectData();
+ QVERIFY(projectData.products().count() == 1);
+ product = projectData.products().first();
+ group = findGroup(product, "Existing Group 4");
+ QVERIFY(group.isValid());
+ errorInfo = project.addFiles(product, group, QStringList() << "file.txt");
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+
+ // Error handling: Add file to group with non-directory prefix.
+ projectData = project.projectData();
+ QVERIFY(projectData.products().count() == 1);
+ product = projectData.products().first();
+ group = findGroup(product, "Existing Group 5");
+ QVERIFY(group.isValid());
+ errorInfo = project.addFiles(product, group, QStringList() << "newfile1.txt");
+ QVERIFY(errorInfo.hasError());
+ QVERIFY2(errorInfo.toString().contains("prefix"), qPrintable(errorInfo.toString()));
+
+ // Remove group.
+ projectData = project.projectData();
+ QVERIFY(projectData.products().count() == 1);
+ product = projectData.products().first();
+ group = findGroup(product, "Existing Group 5");
+ QVERIFY(group.isValid());
+ errorInfo = project.removeGroup(product, group);
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+ projectData = project.projectData();
+ QVERIFY(projectData.products().count() == 1);
+ QCOMPARE(projectData.products().first().groups().count(), 7);
+
+ // Error handling: Try to remove the same group again.
+ errorInfo = project.removeGroup(product, group);
+ QVERIFY(errorInfo.hasError());
+ QVERIFY2(errorInfo.toString().contains("does not exist"), qPrintable(errorInfo.toString()));
+
+ // Check whether building will take the added and removed cpp files into account.
+ // This must not be moved below the re-resolving test!!!
+ qbs::BuildOptions buildOptions;
+ buildOptions.setDryRun(true);
+ BuildDescriptionReveiver rcvr;
+ QScopedPointer<qbs::BuildJob> buildJob(project.buildAllProducts(buildOptions, this));
+ connect(buildJob.data(), SIGNAL(reportCommandDescription(QString, QString)), &rcvr,
+ SLOT(handleDescription(QString,QString)));
+ waitForFinished(buildJob.data());
+ QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString()));
+ QVERIFY(rcvr.descriptions.contains("compiling file.cpp"));
+ QVERIFY(!rcvr.descriptions.contains("compiling main.cpp"));
+
+ // Now check whether the data updates were done correctly.
+ projectData = project.projectData();
+ job.reset(qbs::Project::setupProject(setupParams, m_logSink, 0));
+ waitForFinished(job.data());
+ QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
+ project = job->project();
+ const qbs::ProjectData newProjectData = project.projectData();
+ const bool projectDataMatches = newProjectData == projectData;
+ if (!projectDataMatches) {
+ qDebug("This is the assumed project:");
+ printProjectData(projectData);
+ qDebug("This is the actual project:");
+ printProjectData(newProjectData);
+ }
+ QVERIFY(projectDataMatches); // Will fail if e.g. code locations don't match.
+
+ // Now try building again and check if the newly resolved product behaves the same way.
+ buildJob.reset(project.buildAllProducts(buildOptions, this));
+ connect(buildJob.data(), SIGNAL(reportCommandDescription(QString, QString)), &rcvr,
+ SLOT(handleDescription(QString,QString)));
+ waitForFinished(buildJob.data());
+ QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString()));
+ QVERIFY(rcvr.descriptions.contains("compiling file.cpp"));
+ QVERIFY(!rcvr.descriptions.contains("compiling main.cpp"));
+
+ // Error handling: Try to change the project during a build.
+ buildJob.reset(project.buildAllProducts(buildOptions, this));
+ errorInfo = project.addGroup(newProjectData.products().first(), "blubb");
+ QVERIFY(errorInfo.hasError());
+ QVERIFY2(errorInfo.toString().contains("in process"), qPrintable(errorInfo.toString()));
+ waitForFinished(buildJob.data());
+ errorInfo = project.addGroup(newProjectData.products().first(), "blubb");
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+}
+
void TestApi::disabledInstallGroup()
{
qbs::SetupProjectParameters setupParams = defaultSetupParameters();
- setupParams.setProjectFilePath(QDir::cleanPath(QLatin1String(SRCDIR "/testdata"
- "/disabled_install_group/project.qbs")));
+ setupParams.setProjectFilePath(QDir::cleanPath(m_workingDataDir +
+ "/disabled_install_group/project.qbs"));
QScopedPointer<qbs::SetupProjectJob> job(qbs::Project::setupProject(setupParams,
m_logSink, 0));
waitForFinished(job.data());
@@ -95,8 +351,8 @@ void TestApi::disabledInstallGroup()
void TestApi::fileTagsFilterOverride()
{
qbs::SetupProjectParameters setupParams = defaultSetupParameters();
- setupParams.setProjectFilePath(QDir::cleanPath(QLatin1String(SRCDIR "/testdata"
- "/filetagsfilter_override/project.qbs")));
+ setupParams.setProjectFilePath(QDir::cleanPath(m_workingDataDir +
+ "/filetagsfilter_override/project.qbs"));
QScopedPointer<qbs::SetupProjectJob> job(qbs::Project::setupProject(setupParams,
m_logSink, 0));
waitForFinished(job.data());
@@ -180,7 +436,7 @@ void TestApi::nonexistingProjectPropertyFromProduct()
{
qbs::SetupProjectParameters setupParams = defaultSetupParameters();
const QString projectDir
- = QDir::cleanPath(QLatin1String(SRCDIR "/testdata/nonexistingprojectproperties"));
+ = QDir::cleanPath(m_workingDataDir + "/nonexistingprojectproperties");
const QString topLevelProjectFile = projectDir + QLatin1String("/invalidaccessfromproduct.qbs");
setupParams.setProjectFilePath(topLevelProjectFile);
QScopedPointer<qbs::SetupProjectJob> job(qbs::Project::setupProject(setupParams,
@@ -195,8 +451,7 @@ void TestApi::nonexistingProjectPropertyFromProduct()
void TestApi::nonexistingProjectPropertyFromCommandLine()
{
qbs::SetupProjectParameters setupParams = defaultSetupParameters();
- const QString projectDir
- = QDir::cleanPath(QLatin1String(SRCDIR "/testdata/nonexistingprojectproperties"));
+ const QString projectDir = QDir::cleanPath(m_workingDataDir + "/nonexistingprojectproperties");
const QString topLevelProjectFile = projectDir + QLatin1String("/project.qbs");
setupParams.setProjectFilePath(topLevelProjectFile);
QVariantMap projectProperties;
@@ -220,14 +475,65 @@ qbs::SetupProjectParameters TestApi::defaultSetupParameters() const
const QString qbsRootPath = QDir::cleanPath(QCoreApplication::applicationDirPath()
+ QLatin1String("/../"));
SettingsPtr settings = qbsSettings();
- setupParams.setSearchPaths(qbs::Preferences(settings.data()).searchPaths(qbsRootPath));
- setupParams.setPluginPaths(qbs::Preferences(settings.data()).pluginPaths(qbsRootPath));
+ const QString profileName = QLatin1String("qbs_autotests");
+ const qbs::Preferences prefs(settings.data(), profileName);
+ setupParams.setSearchPaths(prefs.searchPaths(qbsRootPath));
+ setupParams.setPluginPaths(prefs.pluginPaths(qbsRootPath));
QVariantMap buildConfig;
- buildConfig.insert(QLatin1String("qbs.profile"), QLatin1String("qbs_autotests"));
+ buildConfig.insert(QLatin1String("qbs.profile"), profileName);
buildConfig.insert(QLatin1String("qbs.buildVariant"), QLatin1String("debug"));
setupParams.setBuildConfiguration(buildConfig);
setupParams.expandBuildConfiguration(settings.data());
return setupParams;
}
+void TestApi::references()
+{
+ qbs::SetupProjectParameters setupParams = defaultSetupParameters();
+ const QString projectDir = QDir::cleanPath(m_workingDataDir + "/references");
+ setupParams.setProjectFilePath(projectDir + QLatin1String("/invalid1.qbs"));
+ QScopedPointer<qbs::SetupProjectJob> job(qbs::Project::setupProject(setupParams,
+ m_logSink, 0));
+ waitForFinished(job.data());
+ QVERIFY(job->error().hasError());
+ QString errorString = job->error().toString();
+ QVERIFY2(errorString.contains("does not contain"), qPrintable(errorString));
+
+ setupParams.setProjectFilePath(projectDir + QLatin1String("/invalid2.qbs"));
+ job.reset(qbs::Project::setupProject(setupParams, m_logSink, 0));
+ waitForFinished(job.data());
+ QVERIFY(job->error().hasError());
+ errorString = job->error().toString();
+ QVERIFY2(errorString.contains("contains more than one"), qPrintable(errorString));
+
+ setupParams.setProjectFilePath(projectDir + QLatin1String("/valid.qbs"));
+ job.reset(qbs::Project::setupProject(setupParams, m_logSink, 0));
+ waitForFinished(job.data());
+ QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
+ const qbs::ProjectData topLevelProject = job->project().projectData();
+ QCOMPARE(topLevelProject.subProjects().count(), 1);
+ const QString subProjectFileName
+ = QFileInfo(topLevelProject.subProjects().first().location().fileName()).fileName();
+ QCOMPARE(subProjectFileName, QString("p.qbs"));
+}
+
+void TestApi::sourceFileInBuildDir()
+{
+ qbs::SetupProjectParameters setupParams = defaultSetupParameters();
+ const QString projectDir = QDir::cleanPath(m_workingDataDir + "/source-file-in-build-dir");
+ setupParams.setProjectFilePath(projectDir + QLatin1String("/project.qbs"));
+ QScopedPointer<qbs::SetupProjectJob> job(qbs::Project::setupProject(setupParams,
+ m_logSink, 0));
+ waitForFinished(job.data());
+ QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
+ const qbs::ProjectData projectData = job->project().projectData();
+ QCOMPARE(projectData.allProducts().count(), 1);
+ const qbs::ProductData product = projectData.allProducts().first();
+ QCOMPARE(product.groups().count(), 1);
+ const qbs::GroupData group = product.groups().first();
+ QCOMPARE(group.allFilePaths().count(), 1);
+}
+
QTEST_MAIN(TestApi)
+
+#include "tst_api.moc"
diff --git a/tests/auto/api/tst_api.h b/tests/auto/api/tst_api.h
index f0a7d6aeb..4f2ce62f9 100644
--- a/tests/auto/api/tst_api.h
+++ b/tests/auto/api/tst_api.h
@@ -32,10 +32,9 @@
#include <QObject>
-namespace qbs {
-class ILogSink;
-class SetupProjectParameters;
-}
+namespace qbs { class SetupProjectParameters; }
+
+class LogSink;
class TestApi : public QObject
{
@@ -46,17 +45,24 @@ public:
~TestApi();
private slots:
+ void initTestCase();
+
+ void changeContent();
void disabledInstallGroup();
void fileTagsFilterOverride();
void installableFiles();
void listBuildSystemFiles();
void nonexistingProjectPropertyFromProduct();
void nonexistingProjectPropertyFromCommandLine();
+ void references();
+ void sourceFileInBuildDir();
private:
qbs::SetupProjectParameters defaultSetupParameters() const;
- qbs::ILogSink * const m_logSink;
+ LogSink * const m_logSink;
+ const QString m_sourceDataDir;
+ const QString m_workingDataDir;
};
#endif // Include guard.
diff --git a/tests/auto/auto.pri b/tests/auto/auto.pri
index 79330e3ad..979efe8ad 100644
--- a/tests/auto/auto.pri
+++ b/tests/auto/auto.pri
@@ -8,4 +8,4 @@ CONFIG += depend_includepath testcase console
CONFIG -= app_bundle
target.CONFIG += no_default_install
-include(../../src/lib/use.pri)
+include(../../src/lib/corelib/use_corelib.pri)
diff --git a/tests/auto/blackbox/testdata/dynamicRuleOutputs/after/numbers.l b/tests/auto/blackbox/testdata/dynamicRuleOutputs/after/numbers.l
new file mode 100644
index 000000000..b29f8798f
--- /dev/null
+++ b/tests/auto/blackbox/testdata/dynamicRuleOutputs/after/numbers.l
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Build Suite.
+**
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+****************************************************************************/
+
+/* scanner for integer and float numbers */
+
+%option noyywrap
+
+%{
+/* need this for the call to atof() below */
+#include <math.h>
+%}
+
+DIGIT [0-9]
+
+%%
+
+{DIGIT}+ {
+ printf("integer: %s (%d)\n", yytext, atoi(yytext));
+ }
+
+{DIGIT}+"."{DIGIT}* {
+ printf("float: %s (%g)\n", yytext, atof(yytext));
+ }
+
+"{"[\^{}}\n]*"}" /* eat up one-line comments */
+
+[ \t\n]+ /* eat up whitespace */
+
+. printf("Unexpected character: %s\n", yytext);
+
+%%
+
+int main(int argc, char **argv)
+{
+ if (argc > 1)
+ yyin = fopen(argv[1], "r");
+ else
+ yyin = stdin;
+
+ yylex();
+ return 0;
+}
+
diff --git a/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/flexoptionsreader.js b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/flexoptionsreader.js
new file mode 100644
index 000000000..232e8338f
--- /dev/null
+++ b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/flexoptionsreader.js
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Build Suite.
+**
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+****************************************************************************/
+
+// needs import qbs.TextFile
+
+function readFlexOptions(filePath)
+{
+ function splitOptions(str)
+ {
+ var options = [];
+ var opt = "";
+ var inquote = false;
+ for (var i = 0; i < str.length; ++i) {
+ if (str[i] === '"') {
+ opt += '"';
+ inquote = !inquote;
+ } else if (str[i] === ' ' && !inquote) {
+ options.push(opt);
+ opt = "";
+ } else {
+ opt += str[i];
+ }
+ }
+ if (opt.length)
+ options.push(opt);
+ return options;
+ }
+
+ function unquote(str)
+ {
+ var l = str.length;
+ if (l > 2 && str[0] === '"' && str[l - 1] === '"')
+ return str.substr(1, l - 2);
+ return str;
+ }
+
+ function parseOptionLine(result, str)
+ {
+ var options = splitOptions(str);
+ var re = /^(outfile|header-file)=(.*)$/;
+ var reres;
+ for (var k in options) {
+ re.lastIndex = 0;
+ reres = re.exec(options[k]);
+ if (reres === null)
+ continue;
+ result[reres[1]] = unquote(reres[2]);
+ }
+ }
+
+ var tf = new TextFile(input.fileName);
+ var line;
+ var optrex = /^%option\s+(.*$)/;
+ var res;
+ var options = {};
+ while (!tf.atEof()) {
+ line = tf.readLine();
+ if (line === "%%")
+ break;
+ optrex.lastIndex = 0;
+ res = optrex.exec(line);
+ if (res === null)
+ continue;
+ parseOptionLine(options, res[1]);
+ }
+ tf.close();
+ return options;
+}
+
diff --git a/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/genlexer.qbs b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/genlexer.qbs
new file mode 100644
index 000000000..b822e852e
--- /dev/null
+++ b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/genlexer.qbs
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Build Suite.
+**
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+****************************************************************************/
+
+import qbs 1.0
+import qbs.File
+import qbs.FileInfo
+import qbs.TextFile
+import "flexoptionsreader.js" as FlexOptionsReader
+
+Project {
+ Product {
+ name: "genlexer"
+ type: "application"
+ Depends { name: "cpp" }
+ Group {
+ files: ["numbers.l"]
+ fileTags: ["flex"]
+ }
+ property bool isFlexAvailable: File.exists("/usr/bin/flex") // ### replace with PathProbe
+ Rule {
+ inputs: ["flex"]
+ outputFileTags: ["c", "hpp"]
+ outputArtifacts: {
+ var options = FlexOptionsReader.readFlexOptions(input.fileName);
+ var sourceFileName = options["outfile"] || "lex.yy.c";
+ var headerFileName = options["header-file"];
+ var result = [{
+ filePath: "GeneratedFiles/" + product.name + "/" + sourceFileName,
+ fileTags: ["c"]
+ }];
+ if (headerFileName) {
+ result.push({
+ filePath: "GeneratedFiles/" + product.name + "/" + headerFileName,
+ fileTags: ["hpp"]
+ });
+ }
+ return result;
+ }
+ prepare: {
+ var cmd;
+ if (product.isFlexAvailable) {
+ // flex is available. Let's call it.
+ cmd = new Command("flex", [input.fileName]);
+ cmd.workingDirectory = product.buildDirectory + "/GeneratedFiles/" + product.name;
+ } else {
+ // No flex available here, generate some C source and header.
+ cmd = new JavaScriptCommand();
+ cmd.sourceFileName = outputs["c"][0].fileName;
+ cmd.headerFileName = outputs["hpp"] ? outputs["hpp"][0].fileName : "";
+ cmd.sourceCode = function() {
+ var fsrc = new TextFile(sourceFileName, TextFile.WriteOnly);
+ if (headerFileName) {
+ fsrc.write("#include \"" + FileInfo.fileName(headerFileName)
+ + "\"\n\n");
+ var fhdr = new TextFile(headerFileName, TextFile.WriteOnly);
+ fhdr.write("// a rather empty header file\n");
+ fhdr.close();
+ }
+ fsrc.write("int main() { return 0; }\n");
+ fsrc.close();
+ };
+ }
+ cmd.description = "flexing " + FileInfo.fileName(input.fileName);
+ return cmd;
+ }
+ }
+ }
+}
+
diff --git a/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/numbers.l b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/numbers.l
new file mode 100644
index 000000000..19f503562
--- /dev/null
+++ b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/numbers.l
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Build Suite.
+**
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+****************************************************************************/
+
+/* scanner for integer and float numbers */
+
+%option noyywrap
+%option outfile="numberscanner.c" header-file="numberscanner.h"
+
+%{
+/* need this for the call to atof() below */
+#include <math.h>
+%}
+
+DIGIT [0-9]
+
+%%
+
+{DIGIT}+ {
+ printf("integer: %s (%d)\n", yytext, atoi(yytext));
+ }
+
+{DIGIT}+"."{DIGIT}* {
+ printf("float: %s (%g)\n", yytext, atof(yytext));
+ }
+
+"{"[\^{}}\n]*"}" /* eat up one-line comments */
+
+[ \t\n]+ /* eat up whitespace */
+
+. printf("Unexpected character: %s\n", yytext);
+
+%%
+
+int main(int argc, char **argv)
+{
+ if (argc > 1)
+ yyin = fopen(argv[1], "r");
+ else
+ yyin = stdin;
+
+ yylex();
+ return 0;
+}
+
diff --git a/tests/auto/blackbox/testdata/embedInfoPlist/embedInfoPlist.qbs b/tests/auto/blackbox/testdata/embedInfoPlist/embedInfoPlist.qbs
new file mode 100644
index 000000000..32f7f7292
--- /dev/null
+++ b/tests/auto/blackbox/testdata/embedInfoPlist/embedInfoPlist.qbs
@@ -0,0 +1,13 @@
+import qbs
+
+CppApplication {
+ condition: qbs.targetOS.contains("darwin")
+ type: ["application"]
+ files: ["main.m"]
+ cpp.frameworks: ["Foundation"]
+ cpp.infoPlist: {
+ return {
+ "QBS": "org.qt-project.qbs.testdata.embedInfoPlist"
+ };
+ }
+}
diff --git a/tests/auto/blackbox/testdata/embedInfoPlist/main.m b/tests/auto/blackbox/testdata/embedInfoPlist/main.m
new file mode 100644
index 000000000..b3f362223
--- /dev/null
+++ b/tests/auto/blackbox/testdata/embedInfoPlist/main.m
@@ -0,0 +1,9 @@
+#import <Foundation/Foundation.h>
+
+int main()
+{
+ NSDictionary *infoPlist = [[NSBundle mainBundle] infoDictionary];
+ BOOL success = [[infoPlist objectForKey:@"QBS"] isEqualToString:@"org.qt-project.qbs.testdata.embedInfoPlist"];
+ fprintf(success ? stdout : stderr, "%s\n", [[infoPlist description] UTF8String]);
+ return success ? 0 : 1;
+}
diff --git a/tests/auto/blackbox/testdata/explicitlyDependsOn/project.qbs b/tests/auto/blackbox/testdata/explicitlyDependsOn/project.qbs
index ab98740c6..12ae1bd7e 100644
--- a/tests/auto/blackbox/testdata/explicitlyDependsOn/project.qbs
+++ b/tests/auto/blackbox/testdata/explicitlyDependsOn/project.qbs
@@ -5,7 +5,7 @@ Product {
type: "mytype"
files: "dependency.txt"
FileTagger {
- pattern: "*.txt"
+ patterns: "*.txt"
fileTags: ["txt"]
}
Transformer {
diff --git a/tests/auto/blackbox/testdata/jsextensions/file.qbs b/tests/auto/blackbox/testdata/jsextensions/file.qbs
index 123665f60..f6b8ebf79 100644
--- a/tests/auto/blackbox/testdata/jsextensions/file.qbs
+++ b/tests/auto/blackbox/testdata/jsextensions/file.qbs
@@ -7,6 +7,10 @@ Product {
var original = new TextFile("original.txt", TextFile.WriteOnly);
original.close();
File.copy("original.txt", "copy.txt");
+ var origTimestamp = File.lastModified("original.txt");
+ var copyTimestamp = File.lastModified("copy.txt");
+ if (origTimestamp > copyTimestamp)
+ throw new Error("Older file has newer timestamp.");
File.remove("original.txt");
var copy = new TextFile("copy.txt", TextFile.WriteOnly);
copy.writeLine(File.exists("original.txt"));
diff --git a/tests/auto/blackbox/testdata/jsextensions/propertylist.qbs b/tests/auto/blackbox/testdata/jsextensions/propertylist.qbs
new file mode 100644
index 000000000..94a1e1b31
--- /dev/null
+++ b/tests/auto/blackbox/testdata/jsextensions/propertylist.qbs
@@ -0,0 +1,38 @@
+import qbs
+import qbs.Process
+import qbs.PropertyList
+import qbs.TextFile
+
+Product {
+ type: {
+ var obj = {
+ "Array": ["ListItem1", "ListItem2", "ListItem3"],
+ "Integer": 1,
+ "Boolean": true,
+ "String": "otherString"
+ };
+
+ var infoplist = new TextFile("test.xml", TextFile.WriteOnly);
+ infoplist.write(JSON.stringify(obj));
+ infoplist.close();
+
+ var process = new Process();
+ process.exec("plutil", ["-convert", "xml1", "test.xml"]);
+ process.close();
+
+ var xmlfile = new TextFile("test.xml", TextFile.ReadOnly);
+ var propertyList = new PropertyList();
+ propertyList.read(xmlfile.readAll());
+ xmlfile.close();
+
+ var jsontextfile = new TextFile("test.json", TextFile.WriteOnly);
+ jsontextfile.write(propertyList.toJSON());
+ jsontextfile.close();
+
+ process = new Process();
+ process.exec("plutil", ["-convert", "json", "test.xml"]);
+ process.close();
+
+ return "Pineapple Steve";
+ }
+}
diff --git a/tests/auto/blackbox/testdata/nsis/hello.bat b/tests/auto/blackbox/testdata/nsis/hello.bat
new file mode 100755
index 000000000..3af583cd8
--- /dev/null
+++ b/tests/auto/blackbox/testdata/nsis/hello.bat
@@ -0,0 +1 @@
+echo Hello world!
diff --git a/tests/auto/blackbox/testdata/nsis/hello.nsi b/tests/auto/blackbox/testdata/nsis/hello.nsi
new file mode 100755
index 000000000..70b73056a
--- /dev/null
+++ b/tests/auto/blackbox/testdata/nsis/hello.nsi
@@ -0,0 +1,19 @@
+SetCompressor zlib
+
+!ifdef Win64
+ Name "Qbs Hello - ${qbs.architecture} (64-bit)"
+!else
+ Name "Qbs Hello - ${qbs.architecture} (32-bit)"
+!endif
+
+OutFile "you-should-not-see-a-file-with-this-name.exe"
+InstallDir "$DESKTOP\Qbs Hello"
+RequestExecutionLevel user
+
+Page directory
+Page instfiles
+
+Section ""
+ SetOutPath "$INSTDIR"
+ File "${batchFile}"
+SectionEnd
diff --git a/tests/auto/blackbox/testdata/nsis/hello.qbs b/tests/auto/blackbox/testdata/nsis/hello.qbs
new file mode 100755
index 000000000..b7f3da505
--- /dev/null
+++ b/tests/auto/blackbox/testdata/nsis/hello.qbs
@@ -0,0 +1,10 @@
+import qbs
+
+NSISSetup {
+ condition: qbs.targetOS.contains("windows")
+ name: "Qbs Hello"
+ targetName: "qbs-hello-" + qbs.architecture
+ files: ["hello.nsi", "hello.bat"]
+ nsis.defines: ["batchFile=hello.bat"]
+ nsis.compressor: "lzma-solid"
+}
diff --git a/tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs b/tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs
index ef14d3f47..e1ab9bddc 100644
--- a/tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs
+++ b/tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs
@@ -3,7 +3,7 @@ import qbs.File
Module {
FileTagger {
- pattern: "*.in"
+ patterns: ["*.in"]
fileTags: "test-input"
}
diff --git a/tests/auto/blackbox/testdata/propertyChanges/project.qbs b/tests/auto/blackbox/testdata/propertyChanges/project.qbs
index 9bccb5d06..0cba4f630 100644
--- a/tests/auto/blackbox/testdata/propertyChanges/project.qbs
+++ b/tests/auto/blackbox/testdata/propertyChanges/project.qbs
@@ -3,6 +3,7 @@ import qbs.TextFile
Project {
property var projectDefines: ["blubb2"]
+ property string fileContentSuffix: "suffix 1"
CppApplication {
name: qbs.enableDebugCode ? "product 1.debug" : "product 1.release"
cpp.defines: ["blubb1"]
@@ -16,7 +17,7 @@ Project {
}
CppApplication {
name: "product 3"
- cpp.defines: qbs.getenv("QBS_BLACKBOX_DEFINE")
+ cpp.defines: qbs.getEnv("QBS_BLACKBOX_DEFINE")
files: "source3.cpp"
}
DynamicLibrary {
@@ -27,6 +28,17 @@ Project {
Product {
name: "generated text file"
+ property string fileContentPrefix: "prefix 1"
+
+ Transformer {
+ Artifact { fileName: "nothing" }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.silent = true;
+ cmd.sourceCode = function() { print(product.fileContentPrefix); }
+ return cmd;
+ }
+ }
Transformer {
Artifact { fileName: "generated.txt" }
@@ -37,7 +49,8 @@ Project {
cmd.sourceCode = function() {
file = new TextFile(output.fileName, TextFile.WriteOnly);
file.truncate();
- file.write("contents 1");
+ file.write(product.fileContentPrefix + "contents 1"
+ + project.fileContentSuffix);
file.close();
}
return cmd;
diff --git a/tests/auto/blackbox/testdata/ruleConditions/modules/narfzort/narfzort.qbs b/tests/auto/blackbox/testdata/ruleConditions/modules/narfzort/narfzort.qbs
index 45d4b748d..75bebf74d 100644
--- a/tests/auto/blackbox/testdata/ruleConditions/modules/narfzort/narfzort.qbs
+++ b/tests/auto/blackbox/testdata/ruleConditions/modules/narfzort/narfzort.qbs
@@ -5,7 +5,7 @@ import qbs.TextFile
Module {
property bool buildZort: true
FileTagger {
- pattern: "*.narf"
+ patterns: "*.narf"
fileTags: ["narf"]
}
Rule {
diff --git a/tests/auto/blackbox/testdata/soft-dependency/main.cpp b/tests/auto/blackbox/testdata/soft-dependency/main.cpp
new file mode 100644
index 000000000..5f3248c7f
--- /dev/null
+++ b/tests/auto/blackbox/testdata/soft-dependency/main.cpp
@@ -0,0 +1,4 @@
+int main()
+{
+ thisShouldNotLink();
+}
diff --git a/tests/auto/blackbox/testdata/soft-dependency/project.qbs b/tests/auto/blackbox/testdata/soft-dependency/project.qbs
new file mode 100644
index 000000000..bbf37fda8
--- /dev/null
+++ b/tests/auto/blackbox/testdata/soft-dependency/project.qbs
@@ -0,0 +1,14 @@
+import qbs
+
+Application {
+ Depends {
+ name: "nosuchmodule"
+ required: false
+ }
+ Depends {
+ name: "cpp"
+ condition: nosuchmodule.present
+ }
+
+ files: "main.cpp"
+}
diff --git a/tests/auto/blackbox/testdata/subprojects/toplevelproject.qbs b/tests/auto/blackbox/testdata/subprojects/toplevelproject.qbs
index 84a2a211f..f167ccabc 100644
--- a/tests/auto/blackbox/testdata/subprojects/toplevelproject.qbs
+++ b/tests/auto/blackbox/testdata/subprojects/toplevelproject.qbs
@@ -2,7 +2,7 @@ import qbs
Project {
name: "top level project"
- references: ["subproject2/subproject2.qbs"]
+ references: ["subproject2"]
Project {
condition: true
diff --git a/tests/auto/blackbox/testdata/trackExternalProductChanges/fileList.js b/tests/auto/blackbox/testdata/trackExternalProductChanges/fileList.js
index d5c40cde1..5174f8ce7 100644
--- a/tests/auto/blackbox/testdata/trackExternalProductChanges/fileList.js
+++ b/tests/auto/blackbox/testdata/trackExternalProductChanges/fileList.js
@@ -1,6 +1,6 @@
function fileList() { return []; }
-function filesFromEnv(qbs) { return qbs.getenv("QBS_TEST_PULL_IN_FILE_VIA_ENV") ? ["environmentChange.cpp"] : []; }
+function filesFromEnv(qbs) { return qbs.getEnv("QBS_TEST_PULL_IN_FILE_VIA_ENV") ? ["environmentChange.cpp"] : []; }
function filesFromFs(qbs) { return File.exists(path + "/fileExists.cpp") ? ["fileExists.cpp"] : []; }
diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp
index f6f68d849..08cf15a9b 100644
--- a/tests/auto/blackbox/tst_blackbox.cpp
+++ b/tests/auto/blackbox/tst_blackbox.cpp
@@ -71,7 +71,16 @@ TestBlackbox::TestBlackbox()
int TestBlackbox::runQbs(const QbsRunParameters &params)
{
- QStringList args = params.arguments;
+ QStringList args;
+ if (!params.command.isEmpty())
+ args << params.command;
+ if ((QStringList() << QLatin1String("") << QLatin1String("build") << QLatin1String("clean")
+ << QLatin1String("install") << QLatin1String("resolve") << QLatin1String("run")
+ << QLatin1String("shell") << QLatin1String("status") << QLatin1String("update-timestamps"))
+ .contains(params.command)) {
+ args.append(QStringList(QLatin1String("-d")) << QLatin1String("."));
+ }
+ args << params.arguments;
if (params.useProfile)
args.append(QLatin1String("profile:") + buildProfileName);
QString cmdLine = qbsExecutableFilePath;
@@ -273,7 +282,7 @@ void TestBlackbox::build_project_dry_run()
QDir::setCurrent(testDataDir + projectSubDir);
rmDirR(buildDir);
- QCOMPARE(runQbs(QbsRunParameters("-n")), 0);
+ QCOMPARE(runQbs(QbsRunParameters(QStringList("-n"))), 0);
const QStringList &buildDirContents
= QDir(buildDir).entryList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
QVERIFY2(buildDirContents.isEmpty(), qPrintable(buildDirContents.join(" ")));
@@ -359,7 +368,7 @@ void TestBlackbox::resolve_project_dry_run()
QDir::setCurrent(testDataDir + projectSubDir);
rmDirR(buildDir);
- QCOMPARE(runQbs(QbsRunParameters(QStringList("resolve") << "-n")), 0);
+ QCOMPARE(runQbs(QbsRunParameters(QLatin1String("resolve"), QStringList("-n"))), 0);
QVERIFY2(!QFile::exists(productFileName), qPrintable(productFileName));
QVERIFY2(!QFile::exists(buildGraphPath), qPrintable(buildGraphPath));
}
@@ -408,7 +417,7 @@ void TestBlackbox::clean()
QCOMPARE(runQbs(), 0);
QVERIFY(QFile(appObjectFilePath).exists());
QVERIFY(QFile(appExeFilePath).exists());
- QCOMPARE(runQbs(QbsRunParameters(QStringList("clean") << "--all-artifacts")), 0);
+ QCOMPARE(runQbs(QbsRunParameters(QLatin1String("clean"), QStringList("--all-artifacts"))), 0);
QVERIFY(!QFile(appObjectFilePath).exists());
QVERIFY(!QFile(appExeFilePath).exists());
QVERIFY(!QFile(depObjectFilePath).exists());
@@ -420,7 +429,8 @@ void TestBlackbox::clean()
QCOMPARE(runQbs(), 0);
QVERIFY(QFile(appObjectFilePath).exists());
QVERIFY(QFile(appExeFilePath).exists());
- QCOMPARE(runQbs(QbsRunParameters(QStringList("clean") << "--all-artifacts" << "-n")), 0);
+ QCOMPARE(runQbs(QbsRunParameters(QLatin1String("clean"),
+ QStringList("--all-artifacts") << "-n")), 0);
QVERIFY(QFile(appObjectFilePath).exists());
QVERIFY(QFile(appExeFilePath).exists());
QVERIFY(QFile(depObjectFilePath).exists());
@@ -434,7 +444,8 @@ void TestBlackbox::clean()
QVERIFY(QFile(appExeFilePath).exists());
QVERIFY(QFile(depObjectFilePath).exists());
QVERIFY(QFile(depLibFilePath).exists());
- QCOMPARE(runQbs(QbsRunParameters(QStringList("clean") << "--all-artifacts" << "-p" << "dep")),
+ QCOMPARE(runQbs(QbsRunParameters(QLatin1String("clean"),
+ QStringList("--all-artifacts") << "-p" << "dep")),
0);
QVERIFY(QFile(appObjectFilePath).exists());
QVERIFY(QFile(appExeFilePath).exists());
@@ -449,7 +460,8 @@ void TestBlackbox::clean()
QVERIFY(QFile(appExeFilePath).exists());
QVERIFY(QFile(depObjectFilePath).exists());
QVERIFY(QFile(depLibFilePath).exists());
- QCOMPARE(runQbs(QbsRunParameters(QStringList("clean") << "--all-artifacts" << "-p" << "app")),
+ QCOMPARE(runQbs(QbsRunParameters(QLatin1String("clean"),
+ QStringList("--all-artifacts") << "-p" << "app")),
0);
QVERIFY(!QFile(appObjectFilePath).exists());
QVERIFY(!QFile(appExeFilePath).exists());
@@ -545,6 +557,12 @@ void TestBlackbox::renameTargetArtifact()
QCOMPARE(m_qbsStdout.count("linking"), 2);
}
+void TestBlackbox::softDependency()
+{
+ QDir::setCurrent(testDataDir + "/soft-dependency");
+ QCOMPARE(runQbs(), 0);
+}
+
void TestBlackbox::subProjects()
{
QDir::setCurrent(testDataDir + "/subprojects");
@@ -957,7 +975,7 @@ void TestBlackbox::wildcardRenaming()
QCOMPARE(runQbs(QbsRunParameters("install")), 0);
QVERIFY(QFileInfo(defaultInstallRoot + "/pioniere.txt").exists());
QFile::rename(QDir::currentPath() + "/pioniere.txt", QDir::currentPath() + "/fdj.txt");
- QCOMPARE(runQbs(QbsRunParameters(QStringList("install") << "--remove-first")), 0);
+ QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"), QStringList("--remove-first"))), 0);
QVERIFY(!QFileInfo(defaultInstallRoot + "/pioniere.txt").exists());
QVERIFY(QFileInfo(defaultInstallRoot + "/fdj.txt").exists());
}
@@ -970,7 +988,7 @@ void TestBlackbox::recursiveRenaming()
QVERIFY(QFileInfo(defaultInstallRoot + "/dir/subdir/blubb.txt").exists());
waitForNewTimestamp();
QVERIFY(QFile::rename(QDir::currentPath() + "/dir/wasser.txt", QDir::currentPath() + "/dir/wein.txt"));
- QCOMPARE(runQbs(QbsRunParameters(QStringList("install") << "--remove-first")), 0);
+ QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"), QStringList("--remove-first"))), 0);
QVERIFY(!QFileInfo(defaultInstallRoot + "/dir/wasser.txt").exists());
QVERIFY(QFileInfo(defaultInstallRoot + "/dir/wein.txt").exists());
QVERIFY(QFileInfo(defaultInstallRoot + "/dir/subdir/blubb.txt").exists());
@@ -1096,7 +1114,7 @@ void TestBlackbox::propertyChanges()
QVERIFY(m_qbsStdout.contains("Making output from input"));
QFile generatedFile(buildDir + QLatin1String("/generated.txt"));
QVERIFY(generatedFile.open(QIODevice::ReadOnly));
- QCOMPARE(generatedFile.readAll(), QByteArray("contents 1"));
+ QCOMPARE(generatedFile.readAll(), QByteArray("prefix 1contents 1suffix 1"));
generatedFile.close();
// Incremental build with no changes.
@@ -1238,7 +1256,45 @@ void TestBlackbox::propertyChanges()
QVERIFY(m_qbsStdout.contains("generated.txt"));
QVERIFY(!m_qbsStdout.contains("Making output from input"));
QVERIFY(generatedFile.open(QIODevice::ReadOnly));
- QCOMPARE(generatedFile.readAll(), QByteArray("contents 2"));
+ QCOMPARE(generatedFile.readAll(), QByteArray("prefix 1contents 2suffix 1"));
+ generatedFile.close();
+
+ // Incremental build, product property used in JavaScript command changed.
+ waitForNewTimestamp();
+ QVERIFY(projectFile.open(QIODevice::ReadWrite));
+ contents = projectFile.readAll();
+ contents.replace("prefix 1", "prefix 2");
+ projectFile.resize(0);
+ projectFile.write(contents);
+ projectFile.close();
+ QCOMPARE(runQbs(params), 0);
+ QVERIFY(!m_qbsStdout.contains("compiling source1.cpp"));
+ QVERIFY(!m_qbsStdout.contains("compiling source2.cpp"));
+ QVERIFY(!m_qbsStdout.contains("compiling source3.cpp"));
+ QVERIFY(!m_qbsStdout.contains("compiling lib.cpp"));
+ QVERIFY(m_qbsStdout.contains("generated.txt"));
+ QVERIFY(!m_qbsStdout.contains("Making output from input"));
+ QVERIFY(generatedFile.open(QIODevice::ReadOnly));
+ QCOMPARE(generatedFile.readAll(), QByteArray("prefix 2contents 2suffix 1"));
+ generatedFile.close();
+
+ // Incremental build, project property used in JavaScript command changed.
+ waitForNewTimestamp();
+ QVERIFY(projectFile.open(QIODevice::ReadWrite));
+ contents = projectFile.readAll();
+ contents.replace("suffix 1", "suffix 2");
+ projectFile.resize(0);
+ projectFile.write(contents);
+ projectFile.close();
+ QCOMPARE(runQbs(params), 0);
+ QVERIFY(!m_qbsStdout.contains("compiling source1.cpp"));
+ QVERIFY(!m_qbsStdout.contains("compiling source2.cpp"));
+ QVERIFY(!m_qbsStdout.contains("compiling source3.cpp"));
+ QVERIFY(!m_qbsStdout.contains("compiling lib.cpp"));
+ QVERIFY(m_qbsStdout.contains("generated.txt"));
+ QVERIFY(!m_qbsStdout.contains("Making output from input"));
+ QVERIFY(generatedFile.open(QIODevice::ReadOnly));
+ QCOMPARE(generatedFile.readAll(), QByteArray("prefix 2contents 2suffix 2"));
generatedFile.close();
// Incremental build, prepare script of a rule in a module changed.
@@ -1312,6 +1368,59 @@ void TestBlackbox::dynamicLibs()
QCOMPARE(runQbs(), 0);
}
+void TestBlackbox::dynamicRuleOutputs()
+{
+ SKIP_TEST("QBS-370");
+ const QString testDir = testDataDir + "/dynamicRuleOutputs";
+ QDir::setCurrent(testDir);
+ if (QFile::exists("work"))
+ rmDirR("work");
+ QDir().mkdir("work");
+ ccp("before", "work");
+ QDir::setCurrent(testDir + "/work");
+ QCOMPARE(runQbs(), 0);
+
+ const QString appFile = buildDir + "/genlexer" + QTC_HOST_EXE_SUFFIX;
+ const QString headerFile1 = buildDir + "/GeneratedFiles/genlexer/numberscanner.h";
+ const QString sourceFile1 = buildDir + "/GeneratedFiles/genlexer/numberscanner.c";
+ const QString sourceFile2 = buildDir + "/GeneratedFiles/genlexer/lex.yy.c";
+
+ // Check build #1: source and header file name are specified in numbers.l
+ QVERIFY(QFile::exists(appFile));
+ QVERIFY(QFile::exists(headerFile1));
+ QVERIFY(QFile::exists(sourceFile1));
+ QVERIFY(!QFile::exists(sourceFile2));
+
+ QDateTime appFileTimeStamp1 = QFileInfo(appFile).lastModified();
+ waitForNewTimestamp();
+ QFile::remove("numbers.l");
+ QFile::copy("../after/numbers.l", "numbers.l");
+ touch("numbers.l");
+ QCOMPARE(runQbs(), 0);
+
+ // Check build #2: no file names are specified in numbers.l
+ // flex will default to lex.yy.c without header file.
+ QDateTime appFileTimeStamp2 = QFileInfo(appFile).lastModified();
+ QVERIFY(appFileTimeStamp1 < appFileTimeStamp2);
+ QVERIFY(!QFile::exists(headerFile1));
+ QVERIFY(!QFile::exists(sourceFile1));
+ QVERIFY(QFile::exists(sourceFile2));
+
+ waitForNewTimestamp();
+ QFile::remove("numbers.l");
+ QFile::copy("../before/numbers.l", "numbers.l");
+ touch("numbers.l");
+ QCOMPARE(runQbs(), 0);
+
+ // Check build #3: source and header file name are specified in numbers.l
+ QDateTime appFileTimeStamp3 = QFileInfo(appFile).lastModified();
+ QVERIFY(appFileTimeStamp2 < appFileTimeStamp3);
+ QVERIFY(QFile::exists(appFile));
+ QVERIFY(QFile::exists(headerFile1));
+ QVERIFY(QFile::exists(sourceFile1));
+ QVERIFY(!QFile::exists(sourceFile2));
+}
+
void TestBlackbox::explicitlyDependsOn()
{
QDir::setCurrent(testDataDir + "/explicitlyDependsOn");
@@ -1414,6 +1523,23 @@ void TestBlackbox::jsExtensionsProcess()
QCOMPARE(lines.at(6).trimmed().constData(), "false");
}
+void TestBlackbox::jsExtensionsPropertyList()
+{
+ if (!HostOsInfo::isOsxHost())
+ SKIP_TEST("temporarily only applies on OS X");
+
+ QDir::setCurrent(testDataDir + "/jsextensions");
+ QbsRunParameters params(QStringList() << "-nf" << "propertylist.qbs");
+ QCOMPARE(runQbs(params), 0);
+ QFile file1("test.json");
+ QVERIFY(file1.exists());
+ QVERIFY(file1.open(QIODevice::ReadOnly));
+ QFile file2("test.xml");
+ QVERIFY(file2.exists());
+ QVERIFY(file2.open(QIODevice::ReadOnly));
+ QCOMPARE(file1.readAll(), file2.readAll());
+}
+
void TestBlackbox::jsExtensionsTextFile()
{
QDir::setCurrent(testDataDir + "/jsextensions");
@@ -1451,7 +1577,7 @@ void TestBlackbox::properQuoting()
{
QDir::setCurrent(testDataDir + "/proper quoting");
QCOMPARE(runQbs(), 0);
- QbsRunParameters params(QStringList() << "run" << "-qp" << "Hello World");
+ QbsRunParameters params(QLatin1String("run"), QStringList() << "-q" << "-p" << "Hello World");
params.expectFailure = true; // Because the exit code is non-zero.
QCOMPARE(runQbs(params), 156);
const char * const expectedOutput = "whitespaceless\ncontains space\ncontains\ttab\n"
@@ -1473,7 +1599,7 @@ void TestBlackbox::installedApp()
QVERIFY(QFile::exists(defaultInstallRoot
+ HostOsInfo::appendExecutableSuffix(QLatin1String("/usr/bin/installedApp"))));
- QCOMPARE(runQbs(QbsRunParameters(QStringList("install") << "--install-root"
+ QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"), QStringList("--install-root")
<< (testDataDir + "/installed-app"))), 0);
QVERIFY(QFile::exists(testDataDir
+ HostOsInfo::appendExecutableSuffix("/installed-app/usr/bin/installedApp")));
@@ -1482,7 +1608,7 @@ void TestBlackbox::installedApp()
QVERIFY(addedFile.open(QIODevice::WriteOnly));
addedFile.close();
QVERIFY(addedFile.exists());
- QCOMPARE(runQbs(QbsRunParameters(QStringList("install") << "--remove-first")), 0);
+ QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"), QStringList("--remove-first"))), 0);
QVERIFY(QFile::exists(defaultInstallRoot
+ HostOsInfo::appendExecutableSuffix(QLatin1String("/usr/bin/installedApp"))));
QVERIFY(QFile::exists(defaultInstallRoot + QLatin1String("/usr/src/main.cpp")));
@@ -1497,7 +1623,7 @@ void TestBlackbox::installedApp()
projectFile.resize(0);
projectFile.write(content);
QVERIFY(projectFile.flush());
- QCOMPARE(runQbs(QbsRunParameters(QStringList("install"))), 0);
+ QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"))), 0);
QVERIFY(QFile::exists(defaultInstallRoot
+ HostOsInfo::appendExecutableSuffix(QLatin1String("/usr/local/bin/installedApp"))));
QVERIFY(QFile::exists(defaultInstallRoot + QLatin1String("/usr/local/src/main.cpp")));
@@ -1508,7 +1634,7 @@ void TestBlackbox::installedApp()
projectFile.resize(0);
projectFile.write(content);
QVERIFY(projectFile.flush());
- QCOMPARE(runQbs(QbsRunParameters(QStringList("install"))), 0);
+ QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"))), 0);
QVERIFY(QFile::exists(defaultInstallRoot
+ HostOsInfo::appendExecutableSuffix(QLatin1String("/usr/local/custom/installedApp"))));
@@ -1518,12 +1644,13 @@ void TestBlackbox::installedApp()
projectFile.resize(0);
projectFile.write(content);
projectFile.close();
- QCOMPARE(runQbs(QbsRunParameters(QStringList("install"))), 0);
+ QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"))), 0);
QVERIFY(QFile::exists(defaultInstallRoot + QLatin1String("/usr/local/source/main.cpp")));
rmDirR(buildDir);
QbsRunParameters params;
- params.arguments << "install" << "--no-build";
+ params.command = "install";
+ params.arguments << "--no-build";
params.expectFailure = true;
QVERIFY(runQbs(params) != 0);
QVERIFY(m_qbsStderr.contains("No build graph"));
@@ -1531,7 +1658,7 @@ void TestBlackbox::installedApp()
void TestBlackbox::toolLookup()
{
- QbsRunParameters params(QStringList("detect-toolchains") << "--help");
+ QbsRunParameters params(QLatin1String("detect-toolchains"), QStringList("--help"));
params.useProfile = false;
QCOMPARE(runQbs(params), 0);
}
@@ -1603,4 +1730,60 @@ void TestBlackbox::testAssembly()
QCOMPARE((bool)m_qbsStdout.contains("creating libtestc.a"), haveGcc);
}
+void TestBlackbox::testNsis()
+{
+ QStringList regKeys;
+ regKeys << QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS")
+ << QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS");
+
+ QStringList paths = QProcessEnvironment::systemEnvironment().value("PATH")
+ .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
+
+ foreach (const QString &key, regKeys) {
+ QSettings settings(key, QSettings::NativeFormat);
+ QString str = settings.value(QLatin1String(".")).toString();
+ if (!str.isEmpty())
+ paths.prepend(str);
+ }
+
+ bool haveMakeNsis = false;
+ foreach (const QString &path, paths) {
+ if (QFile::exists(QDir::fromNativeSeparators(path) +
+ HostOsInfo::appendExecutableSuffix(QLatin1String("/makensis")))) {
+ haveMakeNsis = true;
+ break;
+ }
+ }
+
+ if (!haveMakeNsis) {
+ SKIP_TEST("makensis is not installed");
+ return;
+ }
+
+ SettingsPtr settings = qbsSettings();
+ Profile profile(buildProfileName, settings.data());
+ bool targetIsWindows = profile.value("qbs.targetOS").toStringList().contains("windows");
+ QDir::setCurrent(testDataDir + "/nsis");
+ QVERIFY(runQbs() == 0);
+ QCOMPARE((bool)m_qbsStdout.contains("compiling hello.nsi"), targetIsWindows);
+ QCOMPARE((bool)m_qbsStdout.contains("SetCompressor ignored due to previous call with the /FINAL switch"), targetIsWindows);
+ QVERIFY(!QFile::exists(defaultInstallRoot + "/you-should-not-see-a-file-with-this-name.exe"));
+}
+
+void TestBlackbox::testEmbedInfoPlist()
+{
+ if (!HostOsInfo::isOsxHost())
+ SKIP_TEST("only applies on OS X");
+
+ QDir::setCurrent(testDataDir + QLatin1String("/embedInfoPlist"));
+
+ QbsRunParameters params;
+ params.command = QLatin1String("run");
+ QCOMPARE(runQbs(params), 0);
+
+ params.arguments = QStringList(QLatin1String("cpp.embedInfoPlist:false"));
+ params.expectFailure = true;
+ QVERIFY(runQbs(params) != 0);
+}
+
QTEST_MAIN(TestBlackbox)
diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h
index ab789374a..79b03e21d 100644
--- a/tests/auto/blackbox/tst_blackbox.h
+++ b/tests/auto/blackbox/tst_blackbox.h
@@ -45,12 +45,14 @@ public:
init();
}
- QbsRunParameters(const QStringList &args) : arguments(args)
+ QbsRunParameters(const QString &cmd, const QStringList &args = QStringList())
+ : command(cmd), arguments(args)
{
init();
}
- QbsRunParameters(const QString &arg) : arguments(arg)
+ QbsRunParameters(const QStringList &args)
+ : arguments(args)
{
init();
}
@@ -62,6 +64,7 @@ public:
environment = QProcessEnvironment::systemEnvironment();
}
+ QString command;
QStringList arguments;
QProcessEnvironment environment;
bool expectFailure;
@@ -104,11 +107,13 @@ private slots:
void duplicateProductNames();
void duplicateProductNames_data();
void dynamicLibs();
+ void dynamicRuleOutputs();
void explicitlyDependsOn();
void fileDependencies();
void jsExtensionsFile();
void jsExtensionsFileInfo();
void jsExtensionsProcess();
+ void jsExtensionsPropertyList();
void jsExtensionsTextFile();
void inheritQbsSearchPaths();
void objC();
@@ -125,6 +130,7 @@ private slots:
void rc();
void renameProduct();
void renameTargetArtifact();
+ void softDependency();
void subProjects();
void track_qrc();
void track_qobject_change();
@@ -153,6 +159,8 @@ private slots:
void checkProjectFilePath();
void missingProfile();
void testAssembly();
+ void testNsis();
+ void testEmbedInfoPlist();
private:
QByteArray m_qbsStderr;
diff --git a/tests/auto/cmdlineparser/tst_cmdlineparser.cpp b/tests/auto/cmdlineparser/tst_cmdlineparser.cpp
index 79a6b4661..63287ce23 100644
--- a/tests/auto/cmdlineparser/tst_cmdlineparser.cpp
+++ b/tests/auto/cmdlineparser/tst_cmdlineparser.cpp
@@ -151,20 +151,18 @@ private slots:
QVERIFY(projectFile.open());
const QStringList fileArgs = QStringList() << "-f" << projectFile.fileName();
CommandLineParser parser;
- QVERIFY(!parser.parseCommandLine(QStringList() << "-x" << fileArgs, settings.data())); // Unknown short option.
- QVERIFY(!parser.parseCommandLine(QStringList() << "--xyz" << fileArgs, settings.data())); // Unknown long option.
- QVERIFY(!parser.parseCommandLine(QStringList() << "-vjv" << fileArgs, settings.data())); // Invalid position.
- QVERIFY(!parser.parseCommandLine(QStringList() << "-j" << fileArgs, settings.data())); // Missing argument.
+ QVERIFY(!parser.parseCommandLine(QStringList() << fileArgs << "-x", settings.data())); // Unknown short option.
+ QVERIFY(!parser.parseCommandLine(QStringList() << fileArgs << "--xyz", settings.data())); // Unknown long option.
+ QVERIFY(!parser.parseCommandLine(QStringList() << fileArgs << "-vjv", settings.data())); // Invalid position.
+ QVERIFY(!parser.parseCommandLine(QStringList() << fileArgs << "-j", settings.data())); // Missing argument.
QVERIFY(!parser.parseCommandLine(QStringList() << "-j" << "0" << fileArgs,
settings.data())); // Wrong argument.
- QVERIFY(!parser.parseCommandLine(QStringList() << "--products" << fileArgs,
+ QVERIFY(!parser.parseCommandLine(QStringList() << fileArgs << "--products",
settings.data())); // Missing argument.
QVERIFY(!parser.parseCommandLine(QStringList() << "--changed-files" << "," << fileArgs,
settings.data())); // Wrong argument.
QVERIFY(!parser.parseCommandLine(QStringList() << "--log-level" << "blubb" << fileArgs,
settings.data())); // Wrong argument.
- QVERIFY(!parser.parseCommandLine(QStringList("properties") << fileArgs << "--force",
- settings.data())); // Invalid option for command.
}
void testProjectFileLookup()
diff --git a/tests/manual/WiXInstallers/ExampleScript.bat b/tests/manual/WiXInstallers/ExampleScript.bat
new file mode 100644
index 000000000..3af583cd8
--- /dev/null
+++ b/tests/manual/WiXInstallers/ExampleScript.bat
@@ -0,0 +1 @@
+echo Hello world!
diff --git a/tests/manual/WiXInstallers/QbsBootstrapper.wxs b/tests/manual/WiXInstallers/QbsBootstrapper.wxs
new file mode 100755
index 000000000..272f6af5b
--- /dev/null
+++ b/tests/manual/WiXInstallers/QbsBootstrapper.wxs
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
+ <Bundle Name="QbsBootstrapper" Version="1.0.0.0" Manufacturer="Qt Project" UpgradeCode="7b05b159-c9ce-477c-9fb5-7fce3cd50396">
+ <BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense" />
+
+ <Chain>
+ <MsiPackage SourceFile="$(var.msiName)" />
+ </Chain>
+ </Bundle>
+</Wix>
diff --git a/tests/manual/WiXInstallers/QbsSetup.wxs b/tests/manual/WiXInstallers/QbsSetup.wxs
new file mode 100755
index 000000000..8f97ff667
--- /dev/null
+++ b/tests/manual/WiXInstallers/QbsSetup.wxs
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
+ <Product Id="*" Name="QbsSetup" Language="1033" Version="1.0.0.0" Manufacturer="Qt Project" UpgradeCode="f60f643e-b002-44d5-b3f4-edafd078314c">
+ <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
+
+ <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
+ <MediaTemplate />
+
+ <Feature Id="ProductFeature" Title="QbsSetup" Level="1">
+ <ComponentGroupRef Id="ProductComponents" />
+ </Feature>
+ </Product>
+
+ <Fragment>
+ <Directory Id="TARGETDIR" Name="SourceDir">
+ <?ifdef Win64 ?>
+ <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
+ <?else ?>
+ <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
+ <?endif ?>
+ <Directory Id="$(var.PlatformProgramFilesFolder)">
+ <Directory Id="INSTALLFOLDER" Name="QbsSetup" />
+ </Directory>
+ </Directory>
+ </Fragment>
+
+ <Fragment>
+ <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
+ <Component Id="ProductComponent">
+ <File Source="$(var.project.path)/$(var.scriptName)" />
+ </Component>
+ </ComponentGroup>
+ </Fragment>
+</Wix>
diff --git a/tests/manual/WiXInstallers/WiXInstallers.qbs b/tests/manual/WiXInstallers/WiXInstallers.qbs
new file mode 100644
index 000000000..59cdf7f4c
--- /dev/null
+++ b/tests/manual/WiXInstallers/WiXInstallers.qbs
@@ -0,0 +1,18 @@
+import qbs
+
+Project {
+ WindowsInstallerPackage {
+ name: "QbsSetup"
+ targetName: "qbs-" + qbs.architecture
+ files: ["QbsSetup.wxs", "ExampleScript.bat"]
+ wix.defines: ["scriptName=ExampleScript.bat"]
+ }
+
+ BurnSetupPackage {
+ Depends { name: "QbsSetup" }
+ name: "QbsBootstrapper"
+ targetName: "qbs-setup-" + qbs.architecture
+ files: ["QbsBootstrapper.wxs"]
+ wix.defines: ["msiName=qbs-" + qbs.architecture + ".msi"]
+ }
+}