diff options
Diffstat (limited to 'tests/auto')
108 files changed, 2084 insertions, 412 deletions
diff --git a/tests/auto/api/testdata/multiplexing/multiplexing.qbs b/tests/auto/api/testdata/multiplexing/multiplexing.qbs index 243c73d46..75958ed60 100644 --- a/tests/auto/api/testdata/multiplexing/multiplexing.qbs +++ b/tests/auto/api/testdata/multiplexing/multiplexing.qbs @@ -74,6 +74,13 @@ Project { qbs.architectures: ["TRS-80", "C64"] qbs.buildVariants: ["debug", "release"] } + Product { + name: "multiplex-without-aggregator-4-depends-2" + multiplexByQbsProperties: ["architectures", "buildVariants"] + qbs.architectures: ["TRS-80", "C64"] + qbs.buildVariants: ["debug", "release"] + Depends { name: "multiplex-without-aggregator-2" } + } } Product { diff --git a/tests/auto/api/testdata/process-result/process-result.qbs b/tests/auto/api/testdata/process-result/process-result.qbs index 52eb1a3ec..5b71ecaaa 100644 --- a/tests/auto/api/testdata/process-result/process-result.qbs +++ b/tests/auto/api/testdata/process-result/process-result.qbs @@ -1,6 +1,7 @@ Project { CppApplication { name: "app" + consoleApplication: true files: ["main.cpp"] } Product { diff --git a/tests/auto/api/tst_api.cpp b/tests/auto/api/tst_api.cpp index 34f5090d7..151690c63 100644 --- a/tests/auto/api/tst_api.cpp +++ b/tests/auto/api/tst_api.cpp @@ -144,7 +144,7 @@ static bool waitForFinished(qbs::AbstractJob *job, int timeout = 0) TestApi::TestApi() : m_logSink(new LogSink) - , m_sourceDataDir(QDir::cleanPath(SRCDIR "/testdata")) + , m_sourceDataDir(testDataSourceDir(SRCDIR "/testdata")) , m_workingDataDir(testWorkDir(QStringLiteral("api"))) { } @@ -502,6 +502,8 @@ void TestApi::canonicalToolchainList() QStringList({"xcode", "clang", "llvm", "gcc"})); QCOMPARE(qbs::canonicalToolchain(QStringList({"clang", "llvm", "gcc"})), QStringList({"clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang-cl", "msvc"})), + QStringList({"clang-cl", "msvc"})); QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm", "gcc"})), QStringList({"llvm", "gcc"})); QCOMPARE(qbs::canonicalToolchain(QStringList({"mingw", "gcc"})), @@ -516,6 +518,8 @@ void TestApi::canonicalToolchainList() QStringList({"xcode", "clang", "llvm", "gcc"})); QCOMPARE(qbs::canonicalToolchain(QStringList({"clang"})), QStringList({"clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang-cl"})), + QStringList({"clang-cl", "msvc"})); QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm"})), QStringList({"llvm", "gcc"})); QCOMPARE(qbs::canonicalToolchain(QStringList({"mingw"})), @@ -623,30 +627,24 @@ static qbs::Project::ProductSelection defaultProducts() return qbs::Project::ProductSelectionDefaultOnly; } -static void printProjectData(const qbs::ProjectData &project) -{ - const auto products = project.products(); - for (const qbs::ProductData &p : products) { - qDebug(" Product '%s' at %s", qPrintable(p.name()), qPrintable(p.location().toString())); - const auto groups = p.groups(); - for (const qbs::GroupData &g : groups) { - qDebug(" Group '%s' at %s", qPrintable(g.name()), qPrintable(g.location().toString())); - qDebug(" Files: %s", qPrintable(g.allFilePaths().join(QLatin1String(", ")))); - } - } -} - void TestApi::changeContent() { qbs::SetupProjectParameters setupParams = defaultSetupParameters("project-editing"); - std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams, - m_logSink, 0)); - waitForFinished(job.get()); - QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); - qbs::Project project = job->project(); - qbs::ProjectData projectData = project.projectData(); - QCOMPARE(projectData.allProducts().size(), 1); - qbs::ProductData product = projectData.allProducts().front(); + std::unique_ptr<qbs::SetupProjectJob> job; + qbs::Project project; + qbs::ProjectData projectData; + qbs::ProductData product; + + const auto resolve = [&] { + job.reset(project.setupProject(setupParams, m_logSink, 0)); + waitForFinished(job.get()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + project = job->project(); + projectData = project.projectData(); + QCOMPARE(projectData.allProducts().size(), 1); + product = projectData.allProducts().front(); + }; + resolve(); QVERIFY(product.groups().size() >= 8); // Error handling: Invalid product. @@ -659,12 +657,16 @@ void TestApi::changeContent() QVERIFY(errorInfo.hasError()); QVERIFY(errorInfo.toString().contains("empty")); + WAIT_FOR_NEW_TIMESTAMP(); errorInfo = project.addGroup(product, "New Group 1"); VERIFY_NO_ERROR(errorInfo); errorInfo = project.addGroup(product, "New Group 2"); VERIFY_NO_ERROR(errorInfo); + resolve(); + QVERIFY(product.groups().size() >= 10); + // Error handling: Group already inserted. errorInfo = project.addGroup(product, "New Group 1"); QVERIFY(errorInfo.hasError()); @@ -677,20 +679,14 @@ void TestApi::changeContent() QVERIFY2(errorInfo.toString().contains("more than once"), qPrintable(errorInfo.toString())); // Add files to empty array literal. - projectData = project.projectData(); - QVERIFY(projectData.products().size() == 1); - product = projectData.products().front(); - QVERIFY(product.groups().size() >= 10); + WAIT_FOR_NEW_TIMESTAMP(); qbs::GroupData group = findGroup(product, "New Group 1"); QVERIFY(group.isValid()); errorInfo = project.addFiles(product, group, QStringList() << "file.h" << "file.cpp"); VERIFY_NO_ERROR(errorInfo); // Error handling: Add the same file again. - projectData = project.projectData(); - QVERIFY(projectData.products().size() == 1); - product = projectData.products().front(); - QVERIFY(product.groups().size() >= 10); + resolve(); group = findGroup(product, "New Group 1"); QVERIFY(group.isValid()); errorInfo = project.addFiles(product, group, QStringList() << "file.cpp"); @@ -698,14 +694,12 @@ void TestApi::changeContent() QVERIFY2(errorInfo.toString().contains("already"), qPrintable(errorInfo.toString())); // Remove one of the newly added files again. + WAIT_FOR_NEW_TIMESTAMP(); errorInfo = project.removeFiles(product, group, QStringList("file.h")); VERIFY_NO_ERROR(errorInfo); // Error handling: Try to remove the same file again. - projectData = project.projectData(); - QVERIFY(projectData.products().size() == 1); - product = projectData.products().front(); - QVERIFY(product.groups().size() >= 10); + resolve(); group = findGroup(product, "New Group 1"); QVERIFY(group.isValid()); errorInfo = project.removeFiles(product, group, QStringList() << "file.h"); @@ -720,38 +714,34 @@ void TestApi::changeContent() QVERIFY2(errorInfo.toString().contains("complex"), qPrintable(errorInfo.toString())); // Remove file from product's 'files' binding. + WAIT_FOR_NEW_TIMESTAMP(); errorInfo = project.removeFiles(product, qbs::GroupData(), QStringList("main.cpp")); VERIFY_NO_ERROR(errorInfo); + resolve(); // Add file to non-empty array literal. - projectData = project.projectData(); - QVERIFY(projectData.products().size() == 1); - product = projectData.products().front(); + WAIT_FOR_NEW_TIMESTAMP(); group = findGroup(product, "Existing Group 1"); QVERIFY(group.isValid()); errorInfo = project.addFiles(product, group, QStringList() << "newfile1.txt"); VERIFY_NO_ERROR(errorInfo); + resolve(); // Add files to list represented as a single string. - projectData = project.projectData(); - QVERIFY(projectData.products().size() == 1); - product = projectData.products().front(); + WAIT_FOR_NEW_TIMESTAMP(); errorInfo = project.addFiles(product, qbs::GroupData(), QStringList() << "newfile2.txt"); VERIFY_NO_ERROR(errorInfo); + resolve(); // Add files to list represented as an identifier. - projectData = project.projectData(); - QVERIFY(projectData.products().size() == 1); - product = projectData.products().front(); + WAIT_FOR_NEW_TIMESTAMP(); group = findGroup(product, "Existing Group 2"); QVERIFY(group.isValid()); errorInfo = project.addFiles(product, group, QStringList() << "newfile3.txt"); VERIFY_NO_ERROR(errorInfo); + resolve(); // Add files to list represented as a block of code (not yet implemented). - projectData = project.projectData(); - QVERIFY(projectData.products().size() == 1); - product = projectData.products().front(); group = findGroup(product, "Existing Group 3"); QVERIFY(group.isValid()); errorInfo = project.addFiles(product, group, QStringList() << "newfile4.txt"); @@ -759,18 +749,14 @@ void TestApi::changeContent() QVERIFY2(errorInfo.toString().contains("complex"), qPrintable(errorInfo.toString())); // Add file to group with directory prefix. - projectData = project.projectData(); - QVERIFY(projectData.products().size() == 1); - product = projectData.products().front(); + WAIT_FOR_NEW_TIMESTAMP(); group = findGroup(product, "Existing Group 4"); QVERIFY(group.isValid()); errorInfo = project.addFiles(product, group, QStringList() << "file.txt"); VERIFY_NO_ERROR(errorInfo); + resolve(); // Error handling: Add file to group with non-directory prefix. - projectData = project.projectData(); - QVERIFY(projectData.products().size() == 1); - product = projectData.products().front(); group = findGroup(product, "Existing Group 5"); QVERIFY(group.isValid()); errorInfo = project.addFiles(product, group, QStringList() << "newfile1.txt"); @@ -778,16 +764,12 @@ void TestApi::changeContent() QVERIFY2(errorInfo.toString().contains("prefix"), qPrintable(errorInfo.toString())); // Remove group. - projectData = project.projectData(); - QVERIFY(projectData.products().size() == 1); - product = projectData.products().front(); + WAIT_FOR_NEW_TIMESTAMP(); group = findGroup(product, "Existing Group 5"); QVERIFY(group.isValid()); errorInfo = project.removeGroup(product, group); VERIFY_NO_ERROR(errorInfo); - projectData = project.projectData(); - QVERIFY(projectData.products().size() == 1); - QVERIFY(projectData.products().front().groups().size() >= 9); + resolve(); // Error handling: Try to remove the same group again. errorInfo = project.removeGroup(product, group); @@ -805,9 +787,7 @@ void TestApi::changeContent() newFile.close(); errorInfo = project.addFiles(product, group, QStringList() << newFile.fileName()); VERIFY_NO_ERROR(errorInfo); - projectData = project.projectData(); - QVERIFY(projectData.products().size() == 1); - product = projectData.products().front(); + resolve(); group = findGroup(product, "Group with wildcards"); QVERIFY(group.isValid()); QCOMPARE(group.sourceArtifactsFromWildcards().size(), 1); @@ -838,46 +818,13 @@ void TestApi::changeContent() 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(project.setupProject(setupParams, m_logSink, 0)); - waitForFinished(job.get()); - QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); - project = job->project(); - qbs::ProjectData newProjectData = project.projectData(); - - // Can't use Project::operator== here, as the target artifacts will differ due to the build - // not having run yet. - bool projectDataMatches = newProjectData.products().size() == 1 - && projectData.products().size() == 1 - && newProjectData.products().front().groups() == projectData.products().front().groups(); - 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, defaultProducts(), this)); - connect(buildJob.get(), &qbs::BuildJob::reportCommandDescription, - &rcvr, &BuildDescriptionReceiver::handleDescription); - waitForFinished(buildJob.get()); - QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString())); - QVERIFY(rcvr.descriptions.contains("compiling file.cpp")); - QVERIFY(!rcvr.descriptions.contains("compiling main.cpp")); - - // Now, after the build, the project data must be entirely identical. - QVERIFY(projectData == project.projectData()); - // Error handling: Try to change the project during a build. buildJob.reset(project.buildAllProducts(buildOptions, defaultProducts(), this)); - errorInfo = project.addGroup(newProjectData.products().front(), "blubb"); + errorInfo = project.addGroup(projectData.products().front(), "blubb"); QVERIFY(errorInfo.hasError()); QVERIFY2(errorInfo.toString().contains("in progress"), qPrintable(errorInfo.toString())); waitForFinished(buildJob.get()); - errorInfo = project.addGroup(newProjectData.products().front(), "blubb"); + errorInfo = project.addGroup(projectData.products().front(), "blubb"); VERIFY_NO_ERROR(errorInfo); project = qbs::Project(); @@ -888,16 +835,11 @@ void TestApi::changeContent() setupParams.setProjectFilePath(QDir::cleanPath(m_workingDataDir + "/project-editing/project-with-no-files.qbs")); - job.reset(project.setupProject(setupParams, m_logSink, 0)); - waitForFinished(job.get()); - QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); - project = job->project(); - projectData = project.projectData(); - QCOMPARE(projectData.allProducts().size(), 1); - product = projectData.allProducts().front(); + resolve(); + WAIT_FOR_NEW_TIMESTAMP(); errorInfo = project.addFiles(product, qbs::GroupData(), QStringList("main.cpp")); VERIFY_NO_ERROR(errorInfo); - projectData = project.projectData(); + resolve(); rcvr.descriptions.clear(); buildJob.reset(project.buildAllProducts(buildOptions, defaultProducts(), this)); connect(buildJob.get(), &qbs::BuildJob::reportCommandDescription, @@ -908,18 +850,6 @@ void TestApi::changeContent() job.reset(project.setupProject(setupParams, m_logSink, 0)); waitForFinished(job.get()); QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); - // Can't use Project::operator== here, as the target artifacts will differ due to the build - // not having run yet. - newProjectData = job->project().projectData(); - projectDataMatches = newProjectData.products().size() == 1 - && projectData.products().size() == 1 - && newProjectData.products().front().groups() == projectData.products().front().groups(); - if (!projectDataMatches) { - printProjectData(projectData); - qDebug("\n====\n"); - printProjectData(newProjectData); - } - QVERIFY(projectDataMatches); } #endif // QBS_ENABLE_PROJECT_FILE_UPDATES @@ -1129,7 +1059,7 @@ void TestApi::excludedInputs() QCOMPARE(dummyCount, 3); } -static qbs::ErrorInfo forceRuleEvaluation(const qbs::Project project) +static qbs::ErrorInfo forceRuleEvaluation(const qbs::Project &project) { qbs::BuildOptions buildOptions; buildOptions.setDryRun(true); @@ -1537,7 +1467,7 @@ void TestApi::linkDynamicAndStaticLibs() // The dependent static libs should not appear in the link command for the executable. const SettingsPtr s = settings(); const qbs::Profile buildProfile(profileName(), s.get()); - if (buildProfile.value("qbs.toolchain").toStringList().contains("gcc")) { + if (profileToolchain(buildProfile).contains("gcc")) { static const std::regex appLinkCmdRex(" -o [^ ]*/HelloWorld" QBS_HOST_EXE_SUFFIX " "); QString appLinkCmd; for (const QString &line : qAsConst(bdr.descriptionLines)) { @@ -1570,7 +1500,7 @@ void TestApi::linkStaticAndDynamicLibs() // executable. The -rpath-link line for libdynamic1.so must be there. const SettingsPtr s = settings(); const qbs::Profile buildProfile(profileName(), s.get()); - if (buildProfile.value("qbs.toolchain").toStringList().contains("gcc")) { + if (profileToolchain(buildProfile).contains("gcc")) { static const std::regex appLinkCmdRex(" -o [^ ]*/HelloWorld" QBS_HOST_EXE_SUFFIX " "); QString appLinkCmd; for (const QString &line : qAsConst(bdr.descriptionLines)) { @@ -2037,6 +1967,31 @@ void TestApi::multiplexing() QVERIFY(product.dependencies().empty()); selector.clear(); + selector.name = "multiplex-without-aggregator-4-depends-2"; + selector.qbsProperties["architecture"] = "C64"; + selector.qbsProperties["buildVariant"] = "debug"; + product = takeMatchingProduct(products, selector); + QVERIFY(product.isValid()); + QVERIFY(product.isMultiplexed()); + QCOMPARE(product.dependencies().size(), 1); + selector.qbsProperties["buildVariant"] = "release"; + product = takeMatchingProduct(products, selector); + QVERIFY(product.isValid()); + QVERIFY(product.isMultiplexed()); + QCOMPARE(product.dependencies().size(), 1); + selector.qbsProperties["architecture"] = "TRS-80"; + selector.qbsProperties["buildVariant"] = "debug"; + product = takeMatchingProduct(products, selector); + QVERIFY(product.isValid()); + QVERIFY(product.isMultiplexed()); + QCOMPARE(product.dependencies().size(), 1); + selector.qbsProperties["buildVariant"] = "release"; + product = takeMatchingProduct(products, selector); + QVERIFY(product.isValid()); + QVERIFY(product.isMultiplexed()); + QCOMPARE(product.dependencies().size(), 1); + + selector.clear(); selector.name = "multiplex-with-aggregator-2"; selector.qbsProperties["architecture"] = "C64"; product = takeMatchingProduct(products, selector); @@ -3054,7 +3009,7 @@ void TestApi::uic() qbs::ErrorInfo TestApi::doBuildProject( const QString &projectFilePath, BuildDescriptionReceiver *buildDescriptionReceiver, ProcessResultReceiver *procResultReceiver, TaskReceiver *taskReceiver, - const qbs::BuildOptions &options, const QVariantMap overriddenValues) + const qbs::BuildOptions &options, const QVariantMap &overriddenValues) { qbs::SetupProjectParameters params = defaultSetupParameters(projectFilePath); params.setOverriddenValues(overriddenValues); diff --git a/tests/auto/api/tst_api.h b/tests/auto/api/tst_api.h index aa00ddc99..39eada57a 100644 --- a/tests/auto/api/tst_api.h +++ b/tests/auto/api/tst_api.h @@ -161,7 +161,7 @@ private: ProcessResultReceiver *procResultReceiver = 0, TaskReceiver *taskReceiver = 0, const qbs::BuildOptions &options = qbs::BuildOptions(), - const QVariantMap overriddenValues = QVariantMap()); + const QVariantMap &overriddenValues = QVariantMap()); LogSink * const m_logSink; const QString m_sourceDataDir; diff --git a/tests/auto/blackbox/find/find-android.qbs b/tests/auto/blackbox/find/find-android.qbs index 26dedc60f..de5c78d10 100644 --- a/tests/auto/blackbox/find/find-android.qbs +++ b/tests/auto/blackbox/find/find-android.qbs @@ -3,12 +3,23 @@ import qbs.TextFile Product { property string packageName: "" qbs.targetPlatform: "android" + multiplexByQbsProperties: ["architectures"] + + Properties { + condition: qbs.architectures && qbs.architectures.length > 1 + aggregate: true + multiplexedType: "json_arch" + } Depends { name: "Android.sdk"; required: false } Depends { name: "Android.ndk"; required: false } type: ["json"] + Rule { multiplex: true + property stringList inputTags: "json_arch" + inputsFromDependencies: inputTags + inputs: product.aggregate ? [] : inputTags Artifact { filePath: ["android.json"] fileTags: ["json"] @@ -18,17 +29,50 @@ Product { cmd.description = output.filePath; cmd.sourceCode = function() { var tools = {}; + + for (var i in inputs["json_arch"]) { + var tf = new TextFile(inputs["json_arch"][i].filePath, TextFile.ReadOnly); + var json = JSON.parse(tf.readAll()); + tools["ndk"] = json["ndk"]; + tools["ndk-samples"] = json["ndk-samples"]; + tf.close(); + } + if (product.moduleProperty("Android.sdk", "present")) { tools["sdk"] = product.moduleProperty("Android.sdk", "sdkDir"); tools["sdk-build-tools-dx"] = product.Android.sdk.dxFilePath; } + if (product.java && product.java.present) + tools["jar"] = product.java.jarFilePath; + + var tf; + try { + tf = new TextFile(output.filePath, TextFile.WriteOnly); + tf.writeLine(JSON.stringify(tools, undefined, 4)); + } finally { + if (tf) + tf.close(); + } + }; + return cmd; + } + } + Rule { + multiplex: true + Artifact { + filePath: ["android_arch.json"] + fileTags: ["json_arch"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = output.filePath; + cmd.sourceCode = function() { + var tools = {}; if (product.moduleProperty("Android.ndk", "present")) { tools["ndk"] = product.moduleProperty("Android.ndk", "ndkDir"); tools["ndk-samples"] = product.Android.ndk.ndkSamplesDir; } - if (product.java && product.java.present) - tools["jar"] = product.java.jarFilePath; var tf; try { @@ -43,3 +87,4 @@ Product { } } } + diff --git a/tests/auto/blackbox/testdata-android/minimal-native/src/main/native/native.c b/tests/auto/blackbox/testdata-android/minimal-native/src/main/native/native.c index 6b625858b..f49b4f90f 100644 --- a/tests/auto/blackbox/testdata-android/minimal-native/src/main/native/native.c +++ b/tests/auto/blackbox/testdata-android/minimal-native/src/main/native/native.c @@ -4,5 +4,6 @@ jstring Java_minimalnative_MinimalNative_stringFromNative(JNIEnv* env, jobject thiz) { + (void)thiz; return (*env)->NewStringUTF(env, "This message comes from native code."); } diff --git a/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/src/main/AndroidManifest.xml index 289969409..272fe55de 100644 --- a/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/src/main/AndroidManifest.xml +++ b/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.qt.dummy1" android:versionCode="1" android:versionName="1.0"> - <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="19"/> + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/> <uses-feature android:glEsVersion="0x00020000"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:allowBackup="true" android:hasCode="true"> diff --git a/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/src/main/AndroidManifest.xml index ef0fbe54f..871aadbe6 100644 --- a/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/src/main/AndroidManifest.xml +++ b/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.qt.dummy2" android:versionCode="1" android:versionName="1.0"> - <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="19"/> + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/> <uses-feature android:glEsVersion="0x00020000"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:allowBackup="true" android:hasCode="true"> diff --git a/tests/auto/blackbox/testdata-android/multiple-libs-per-apk/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-android/multiple-libs-per-apk/src/main/AndroidManifest.xml index 6694afc18..f184a8f1f 100644 --- a/tests/auto/blackbox/testdata-android/multiple-libs-per-apk/src/main/AndroidManifest.xml +++ b/tests/auto/blackbox/testdata-android/multiple-libs-per-apk/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.qt.dummy" android:versionCode="1" android:versionName="1.0"> - <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="19"/> + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/> <uses-feature android:glEsVersion="0x00020000"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:allowBackup="true" android:hasCode="true"> diff --git a/tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml index 066ec0a63..542794825 100644 --- a/tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml +++ b/tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml @@ -67,7 +67,7 @@ </application> - <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16"/> + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/> <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/> <!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application. diff --git a/tests/auto/blackbox/testdata-apple/aggregateDependencyLinking/aggregateDependencyLinking.qbs b/tests/auto/blackbox/testdata-apple/aggregateDependencyLinking/aggregateDependencyLinking.qbs index e7c8867bd..a65dcd023 100644 --- a/tests/auto/blackbox/testdata-apple/aggregateDependencyLinking/aggregateDependencyLinking.qbs +++ b/tests/auto/blackbox/testdata-apple/aggregateDependencyLinking/aggregateDependencyLinking.qbs @@ -14,6 +14,7 @@ Project { // This will generate 2 multiplex configs and an aggregate. qbs.architectures: ["x86", "x86_64"] qbs.buildVariant: "debug" + cpp.minimumMacosVersion: "10.8" } CppApplication { @@ -30,6 +31,7 @@ Project { qbs.architecture: "x86_64" qbs.buildVariant: "debug" + cpp.minimumMacosVersion: "10.8" multiplexByQbsProperties: [] } } diff --git a/tests/auto/blackbox/testdata-apple/overrideInfoPlist/Override-Info.plist b/tests/auto/blackbox/testdata-apple/overrideInfoPlist/Override-Info.plist new file mode 100644 index 000000000..f2621e983 --- /dev/null +++ b/tests/auto/blackbox/testdata-apple/overrideInfoPlist/Override-Info.plist @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>DefaultValue</key> + <string>The default value</string> + <key>OverriddenValue</key> + <string>The default value</string> +</dict> +</plist> diff --git a/tests/auto/blackbox/testdata-apple/overrideInfoPlist/main.c b/tests/auto/blackbox/testdata-apple/overrideInfoPlist/main.c new file mode 100644 index 000000000..76e819701 --- /dev/null +++ b/tests/auto/blackbox/testdata-apple/overrideInfoPlist/main.c @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata-apple/overrideInfoPlist/overrideInfoPlist.qbs b/tests/auto/blackbox/testdata-apple/overrideInfoPlist/overrideInfoPlist.qbs new file mode 100644 index 000000000..e70584ed8 --- /dev/null +++ b/tests/auto/blackbox/testdata-apple/overrideInfoPlist/overrideInfoPlist.qbs @@ -0,0 +1,16 @@ +CppApplication { + Depends { name: "bundle" } + cpp.minimumMacosVersion: "10.7" + files: ["main.c", "Override-Info.plist"] + + Properties { + condition: qbs.targetOS.contains("darwin") + bundle.isBundle: true + bundle.identifierPrefix: "com.test" + + bundle.infoPlist: ({ + "CFBundleName": "My Bundle", + "OverriddenValue": "The overridden value", + }) + } +} diff --git a/tests/auto/blackbox/testdata-apple/xcode/xcode-project.qbs b/tests/auto/blackbox/testdata-apple/xcode/xcode-project.qbs index fbab6d0b1..fa4c67b96 100644 --- a/tests/auto/blackbox/testdata-apple/xcode/xcode-project.qbs +++ b/tests/auto/blackbox/testdata-apple/xcode/xcode-project.qbs @@ -43,8 +43,11 @@ Project { } for (var i = 0; i < a.length; ++i) { - if (a[i] !== b[i]) { - throw msg; + var version1 = a[i].split('.'); + var version2 = b[i].split('.'); + for (var j = 0; j < version1.length; ++j) { + if (version1[j] !== version2[j]) + throw msg; } } } diff --git a/tests/auto/blackbox/testdata-qt/metatypes/metatypes.qbs b/tests/auto/blackbox/testdata-qt/metatypes/metatypes.qbs new file mode 100644 index 000000000..bbc98c934 --- /dev/null +++ b/tests/auto/blackbox/testdata-qt/metatypes/metatypes.qbs @@ -0,0 +1,28 @@ +import qbs.Utilities + +StaticLibrary { + name: "mylib" + + Depends { name: "Qt.core" } + + qbs.installPrefix: "some-prefix" + + Probe { + id: capabilitiesChecker + property string version: Qt.core.version + configure: { + if (Utilities.versionCompare(version, "5.15") >= 0) + console.info("can generate"); + else + console.info("cannot generate"); + found = true; + } + } + + files: [ + "mocableclass1.cpp", + "mocableclass1.h", + "mocableclass2.cpp", + "unmocableclass.cpp", + ] +} diff --git a/tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.cpp b/tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.cpp new file mode 100644 index 000000000..06adc8ca5 --- /dev/null +++ b/tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.cpp @@ -0,0 +1,3 @@ +#include "mocableclass1.h" + +MocableClass1::MocableClass1(QObject *parent) : QObject(parent) {} diff --git a/tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.h b/tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.h new file mode 100644 index 000000000..020c15179 --- /dev/null +++ b/tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.h @@ -0,0 +1,8 @@ +#include <QObject> + +class MocableClass1 : public QObject +{ + Q_OBJECT +public: + MocableClass1(QObject *parent = nullptr); +}; diff --git a/tests/auto/blackbox/testdata-qt/metatypes/mocableclass2.cpp b/tests/auto/blackbox/testdata-qt/metatypes/mocableclass2.cpp new file mode 100644 index 000000000..bf538913a --- /dev/null +++ b/tests/auto/blackbox/testdata-qt/metatypes/mocableclass2.cpp @@ -0,0 +1,10 @@ +#include <QObject> + +class MocableClass2 : public QObject +{ + Q_OBJECT +public: + MocableClass2(QObject *parent) : QObject(parent) {} +}; + +#include <mocableclass2.moc> diff --git a/tests/auto/blackbox/testdata-qt/metatypes/unmocableclass.cpp b/tests/auto/blackbox/testdata-qt/metatypes/unmocableclass.cpp new file mode 100644 index 000000000..34330d189 --- /dev/null +++ b/tests/auto/blackbox/testdata-qt/metatypes/unmocableclass.cpp @@ -0,0 +1,7 @@ +#include <QObject> + +class UnmocableClass : public QObject +{ +public: + UnmocableClass(QObject *parent) : QObject(parent) {} +}; diff --git a/tests/auto/blackbox/testdata-qt/qmltyperegistrar/example.qml b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/example.qml new file mode 100644 index 000000000..ef97df12d --- /dev/null +++ b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/example.qml @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// ![0] +import People 1.0 + +Person { + name: "Bob Jones" + shoeSize: 12 +} +// ![0] diff --git a/tests/auto/blackbox/testdata-qt/qmltyperegistrar/main.cpp b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/main.cpp new file mode 100644 index 000000000..6c3920f04 --- /dev/null +++ b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/main.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QCoreApplication> +#include <QQmlEngine> +#include <QQmlComponent> +#include <QDebug> +#include "person.h" + +int main(int argc, char ** argv) +{ + QCoreApplication app(argc, argv); + + QQmlEngine engine; + QQmlComponent component(&engine, QUrl("qrc:example.qml")); + auto *person = qobject_cast<Person *>(component.create()); + if (person) { + qWarning() << "The person's name is" << person->name(); + qWarning() << "They wear a" << person->shoeSize() << "sized shoe"; + } else { + qWarning() << component.errors(); + } + + return EXIT_SUCCESS; +} diff --git a/tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.cpp b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.cpp new file mode 100644 index 000000000..de4a33dd0 --- /dev/null +++ b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "person.h" + +// ![0] +Person::Person(QObject *parent) +: QObject(parent), m_shoeSize(0) +{ +} + +QString Person::name() const +{ + return m_name; +} + +void Person::setName(const QString &n) +{ + m_name = n; +} + +int Person::shoeSize() const +{ + return m_shoeSize; +} + +void Person::setShoeSize(int s) +{ + m_shoeSize = s; +} + +// ![0] diff --git a/tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.h b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.h new file mode 100644 index 000000000..530c335de --- /dev/null +++ b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef PERSON_H +#define PERSON_H + +#include <QObject> +#include <QtQml/qqml.h> + +//![0] +class Person : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize) + QML_ELEMENT +public: + Person(QObject *parent = nullptr); + + QString name() const; + void setName(const QString &); + + int shoeSize() const; + void setShoeSize(int); + +private: + QString m_name; + int m_shoeSize; +}; +//![0] + +#endif // PERSON_H diff --git a/tests/auto/blackbox/testdata-qt/qmltyperegistrar/qmltyperegistrar.qbs b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/qmltyperegistrar.qbs new file mode 100644 index 000000000..68dc83743 --- /dev/null +++ b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/qmltyperegistrar.qbs @@ -0,0 +1,33 @@ +import qbs.Utilities + +CppApplication { + name: "myapp" + Depends { name: "Qt.qml" } + + Qt.qml.importVersion: "1" + cpp.includePaths: sourceDirectory + qbs.installPrefix: "" + + files: [ + "main.cpp", + "person.cpp", + "person.h", + ] + + Group { + files: "example.qml" + fileTags: "qt.core.resource_data" + } + + Probe { + id: versionProbe + property string version: Qt.core.version + configure: { + if (Utilities.versionCompare(version, "5.15") >= 0) + console.info("has registrar"); + else + console.info("does not have registrar"); + found = true; + } + } +} diff --git a/tests/auto/blackbox/testdata-qt/qtscxml/qtscxml.qbs b/tests/auto/blackbox/testdata-qt/qtscxml/qtscxml.qbs index 38ed5fac9..90b968ec9 100644 --- a/tests/auto/blackbox/testdata-qt/qtscxml/qtscxml.qbs +++ b/tests/auto/blackbox/testdata-qt/qtscxml/qtscxml.qbs @@ -39,18 +39,22 @@ Project { prepare: { var cmd = new Command(input.filePath); cmd.description = "running " + input.filePath; - var pathVar; - var pathValue; + + var envVars = {}; if (product.qbs.hostOS.contains("windows")) { - pathVar = "PATH"; - pathValue = FileInfo.toWindowsSeparators(input["Qt.core"].binPath); + envVars["PATH"] = FileInfo.toWindowsSeparators(input["Qt.core"].binPath); + } else if (product.qbs.hostOS.contains("macos")) { + envVars["DYLD_LIBRARY_PATH"] = input["Qt.core"].libPath; + envVars["DYLD_FRAMEWORK_PATH"] = input["Qt.core"].libPath; } else { - pathVar = "LD_LIBRARY_PATH"; - pathValue = input["Qt.core"].libPath; + envVars["LD_LIBRARY_PATH"] = input["Qt.core"].libPath; + } + for (var varName in envVars) { + var oldValue = Environment.getEnv(varName) || ""; + var newValue = envVars[varName] + product.qbs.pathListSeparator + oldValue; + cmd.environment.push(varName + '=' + newValue); } - var oldValue = Environment.getEnv(pathVar) || ""; - var newValue = pathValue + product.qbs.pathListSeparator + oldValue; - cmd.environment = [pathVar + '=' + newValue]; + return [cmd]; } } diff --git a/tests/auto/blackbox/testdata/build-variant-defaults/build-variant-defaults.qbs b/tests/auto/blackbox/testdata/build-variant-defaults/build-variant-defaults.qbs new file mode 100644 index 000000000..4015817ca --- /dev/null +++ b/tests/auto/blackbox/testdata/build-variant-defaults/build-variant-defaults.qbs @@ -0,0 +1,16 @@ +CppApplication { + property bool validate: { + var valid = true; + if (qbs.buildVariant === "release") { + valid = !qbs.enableDebugCode && !qbs.debugInformation && qbs.optimization === "fast"; + } else if (qbs.buildVariant === "debug") { + valid = qbs.enableDebugCode && qbs.debugInformation && qbs.optimization === "none"; + } else if (qbs.buildVariant === "profiling") { + valid = !qbs.enableDebugCode && qbs.debugInformation && qbs.optimization === "fast"; + } + + if (!valid) + throw "Invalid defaults"; + return valid; + } +} diff --git a/tests/auto/blackbox/testdata/build-variant-defaults/main.cpp b/tests/auto/blackbox/testdata/build-variant-defaults/main.cpp new file mode 100644 index 000000000..76e819701 --- /dev/null +++ b/tests/auto/blackbox/testdata/build-variant-defaults/main.cpp @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile-probe-project.qbs b/tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile-probe-project.qbs new file mode 100644 index 000000000..ab1c68385 --- /dev/null +++ b/tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile-probe-project.qbs @@ -0,0 +1,22 @@ +import qbs.Probes +import qbs.TextFile + +Project { + + Probes.ConanfileProbe { + id: conan + conanfilePath: path + "/conanfile.py" + options: ({opt: "True"}) + settings: ({os: "AIX"}) + } + + property var check: { + tf = new TextFile(buildDirectory + "/results.json", TextFile.WriteOnly); + var o = { + json: conan.json.deps_env_info["ENV_VAR"], + dependencies: conan.dependencies["testlib"].libs, + generatedFilesPath: conan.generatedFilesPath + }; + tf.write(JSON.stringify(o)); + } +} diff --git a/tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile.py b/tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile.py new file mode 100644 index 000000000..630cf0283 --- /dev/null +++ b/tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile.py @@ -0,0 +1,25 @@ +from conans import ConanFile + +class TestApp(ConanFile): + name = "testapp" + description = "Our project package, to be inspected by the Qbs ConanfileProbe" + license = "none" + version = "6.6.6" + + settings = "os" + options = {"opt": [True, False]} + default_options = {"opt": False} + + requires = "testlib/1.2.3@qbs/testing" + + def configure(self): + self.options["testlib"].opt = self.options.opt + + def source(self): + pass + + def build(self): + pass + + def package(self): + pass diff --git a/tests/auto/blackbox/testdata/conanfile-probe/testlib/conanfile.py b/tests/auto/blackbox/testdata/conanfile-probe/testlib/conanfile.py new file mode 100644 index 000000000..983c22599 --- /dev/null +++ b/tests/auto/blackbox/testdata/conanfile-probe/testlib/conanfile.py @@ -0,0 +1,25 @@ +from conans import ConanFile + +class Testlib(ConanFile): + name = "testlib" + description = "Represents an arbitrary package, for instance on bintray" + license = "none" + version = "1.2.3" + + settings = "os" + options = {"opt": [True, False]} + default_options = {"opt": False} + + def source(self): + pass + + def build(self): + pass + + def package(self): + pass + + def package_info(self): + self.cpp_info.libs = ["testlib1","testlib2"] + self.env_info.ENV_VAR = "TESTLIB_ENV_VAL" + self.user_info.user_var = "testlib_user_val" diff --git a/tests/auto/blackbox/testdata/dependency-profile-mismatch/dependency-profile-mismatch.qbs b/tests/auto/blackbox/testdata/dependency-profile-mismatch/dependency-profile-mismatch.qbs deleted file mode 100644 index 8a6f733c0..000000000 --- a/tests/auto/blackbox/testdata/dependency-profile-mismatch/dependency-profile-mismatch.qbs +++ /dev/null @@ -1,12 +0,0 @@ -Project { - property string mainProfile - property string depProfile - Product { - name: "dep" - qbs.profiles: [project.depProfile] - } - Product { - name: "main" - Depends { name: "dep"; profiles: [project.mainProfile]; } - } -} diff --git a/tests/auto/blackbox/testdata/dependency-scanning-loop/dependency-scanning-loop.qbs b/tests/auto/blackbox/testdata/dependency-scanning-loop/dependency-scanning-loop.qbs new file mode 100644 index 000000000..ac8e7258c --- /dev/null +++ b/tests/auto/blackbox/testdata/dependency-scanning-loop/dependency-scanning-loop.qbs @@ -0,0 +1,34 @@ +import qbs.FileInfo +import qbs.TextFile + +CppApplication { + name: "app" + cpp.includePaths: buildDirectory + Group { + files: "main.cpp" + fileTags: ["cpp", "custom.in"] + } + Rule { + inputs: "custom.in" + Artifact { + filePath: FileInfo.completeBaseName(input.filePath) + ".h" + fileTags: "hpp" + } + Artifact { + filePath: "custom.txt" + fileTags: "whatever" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating " + outputs.hpp[0].fileName; + cmd.sourceCode = function() { + var f = new TextFile(outputs.hpp[0].filePath, TextFile.WriteOnly); + f.writeLine("int main() {}"); + f.close(); + f = new TextFile(outputs.whatever[0].filePath, TextFile.WriteOnly); + f.close(); + } + return cmd; + } + } +} diff --git a/tests/auto/blackbox/testdata/dependency-scanning-loop/main.cpp b/tests/auto/blackbox/testdata/dependency-scanning-loop/main.cpp new file mode 100644 index 000000000..5e8dda41b --- /dev/null +++ b/tests/auto/blackbox/testdata/dependency-scanning-loop/main.cpp @@ -0,0 +1 @@ +#include <main.h> diff --git a/tests/auto/blackbox/testdata/empty-profile/empty-profile.qbs b/tests/auto/blackbox/testdata/empty-profile/empty-profile.qbs new file mode 100644 index 000000000..da7536315 --- /dev/null +++ b/tests/auto/blackbox/testdata/empty-profile/empty-profile.qbs @@ -0,0 +1,3 @@ +CppApplication { + files: ["main.cpp"] +} diff --git a/tests/auto/blackbox/testdata/empty-profile/main.cpp b/tests/auto/blackbox/testdata/empty-profile/main.cpp new file mode 100644 index 000000000..76e819701 --- /dev/null +++ b/tests/auto/blackbox/testdata/empty-profile/main.cpp @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/freedesktop/freedesktop.qbs b/tests/auto/blackbox/testdata/freedesktop/freedesktop.qbs new file mode 100644 index 000000000..60c3d304f --- /dev/null +++ b/tests/auto/blackbox/testdata/freedesktop/freedesktop.qbs @@ -0,0 +1,25 @@ +import qbs 1.0 + +Project { + CppApplication { + name: "main" + install: true + files: [ + "main.cpp", + "myapp.desktop", + "myapp.appdata.xml", + ] + + Depends { name: "freedesktop" } + + freedesktop.name: "My App" + freedesktop.desktopKeys: ({ + 'Icon': "myapp.png" + }) + + Group { + files: "myapp.png" + fileTags: "freedesktop.appIcon" + } + } +} diff --git a/tests/auto/blackbox/testdata/freedesktop/main.cpp b/tests/auto/blackbox/testdata/freedesktop/main.cpp new file mode 100644 index 000000000..905869dfa --- /dev/null +++ b/tests/auto/blackbox/testdata/freedesktop/main.cpp @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/tests/auto/blackbox/testdata/freedesktop/myapp.appdata.xml b/tests/auto/blackbox/testdata/freedesktop/myapp.appdata.xml new file mode 100644 index 000000000..3cf0a5641 --- /dev/null +++ b/tests/auto/blackbox/testdata/freedesktop/myapp.appdata.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component type="desktop"> + <id>myapp.desktop</id> + <metadata_license>CC0</metadata_license> + <name>MyApp</name> + <summary>The coolest app ever</summary> + + <description> + <p>This is a cool application.</p> + </description> + + <url type="homepage">https://software.house/myapp</url> + <project_license>GPL-2.0+</project_license> + <developer_name>Coding Wizard</developer_name> +</component> diff --git a/tests/auto/blackbox/testdata/freedesktop/myapp.desktop b/tests/auto/blackbox/testdata/freedesktop/myapp.desktop new file mode 100644 index 000000000..dac3014c3 --- /dev/null +++ b/tests/auto/blackbox/testdata/freedesktop/myapp.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +GenericName=Image Editor +Comment=Create images and edit photographs +Icon=overridden.png diff --git a/tests/auto/blackbox/testdata/freedesktop/myapp.png b/tests/auto/blackbox/testdata/freedesktop/myapp.png new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/auto/blackbox/testdata/freedesktop/myapp.png diff --git a/tests/auto/blackbox/testdata/generate-linker-map-file/generate-linker-map-file.qbs b/tests/auto/blackbox/testdata/generate-linker-map-file/generate-linker-map-file.qbs index 815e64853..8c971a747 100644 --- a/tests/auto/blackbox/testdata/generate-linker-map-file/generate-linker-map-file.qbs +++ b/tests/auto/blackbox/testdata/generate-linker-map-file/generate-linker-map-file.qbs @@ -2,11 +2,14 @@ Project { CppApplication { name: "app-map" files: ["main.cpp"] + // lld-link has different flag for map files, test it by switching to "lld" linkerVariant + Properties { condition: qbs.toolchain.contains("clang-cl"); cpp.linkerVariant: "lld" } cpp.generateLinkerMapFile: true } CppApplication { name: "app-nomap" files: ["main.cpp"] + Properties { condition: qbs.toolchain.contains("clang-cl"); cpp.linkerVariant: "lld" } cpp.generateLinkerMapFile: false } CppApplication { diff --git a/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs b/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs index 8ee3dd9c9..b7a594c13 100644 --- a/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs +++ b/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs @@ -7,6 +7,7 @@ CppApplication { Depends { name: "cpp" } cpp.cxxLanguageVersion: "c++11" + cpp.minimumMacosVersion: "10.8" cpp.warningLevel: "none" Depends { name: "protobuf.cpp"; required: false } diff --git a/tests/auto/blackbox/testdata/host-os-properties/host-os-properties.qbs b/tests/auto/blackbox/testdata/host-os-properties/host-os-properties.qbs new file mode 100644 index 000000000..b6b862d1c --- /dev/null +++ b/tests/auto/blackbox/testdata/host-os-properties/host-os-properties.qbs @@ -0,0 +1,8 @@ +CppApplication { + consoleApplication: true + cpp.defines: [ + 'HOST_ARCHITECTURE="' + qbs.hostArchitecture + '"', + 'HOST_PLATFORM="' + qbs.hostPlatform + '"' + ] + files: "main.cpp" +} diff --git a/tests/auto/blackbox/testdata/host-os-properties/main.cpp b/tests/auto/blackbox/testdata/host-os-properties/main.cpp new file mode 100644 index 000000000..b0c239e20 --- /dev/null +++ b/tests/auto/blackbox/testdata/host-os-properties/main.cpp @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main() { + printf("HOST_ARCHITECTURE = %s\n", HOST_ARCHITECTURE); + printf("HOST_PLATFORM = %s\n", HOST_PLATFORM); + return 0; +} diff --git a/tests/auto/blackbox/testdata/install-locations/install-locations.qbs b/tests/auto/blackbox/testdata/install-locations/install-locations.qbs index ed0e1810a..722b233c4 100644 --- a/tests/auto/blackbox/testdata/install-locations/install-locations.qbs +++ b/tests/auto/blackbox/testdata/install-locations/install-locations.qbs @@ -13,7 +13,9 @@ Project { CppApplication { name: "theapp" install: true + installDebugInformation: true files: "main.cpp" + cpp.separateDebugInformation: true Group { fileTagsFilter: "application" fileTags: "some-tag" @@ -23,7 +25,17 @@ Project { name: "thelib" install: true installImportLib: true + installDebugInformation: true Depends { name: "cpp" } + cpp.separateDebugInformation: true files: "thelib.cpp" } + LoadableModule { + name: "theplugin" + install: true + installDebugInformation: true + Depends { name: "cpp" } + cpp.separateDebugInformation: true + files: "theplugin.cpp" + } } diff --git a/tests/auto/blackbox/testdata/install-locations/theplugin.cpp b/tests/auto/blackbox/testdata/install-locations/theplugin.cpp new file mode 100644 index 000000000..ac1ede090 --- /dev/null +++ b/tests/auto/blackbox/testdata/install-locations/theplugin.cpp @@ -0,0 +1,3 @@ +#include "../dllexport.h" + +DLL_EXPORT void pluginFunc() {} diff --git a/tests/auto/blackbox/testdata/last-module-candidate-broken/last-module-candidate-broken.qbs b/tests/auto/blackbox/testdata/last-module-candidate-broken/last-module-candidate-broken.qbs new file mode 100644 index 000000000..db7dc2265 --- /dev/null +++ b/tests/auto/blackbox/testdata/last-module-candidate-broken/last-module-candidate-broken.qbs @@ -0,0 +1,5 @@ +CppApplication { + qbsSearchPaths: "qbs" + Depends { name: "Foo" } + files: "main.cpp" +} diff --git a/tests/auto/blackbox/testdata/last-module-candidate-broken/main.cpp b/tests/auto/blackbox/testdata/last-module-candidate-broken/main.cpp new file mode 100644 index 000000000..76e819701 --- /dev/null +++ b/tests/auto/blackbox/testdata/last-module-candidate-broken/main.cpp @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo1.qbs b/tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo1.qbs new file mode 100644 index 000000000..ba08b862b --- /dev/null +++ b/tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo1.qbs @@ -0,0 +1,3 @@ +Module { + condition: false +} diff --git a/tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo2.qbs b/tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo2.qbs new file mode 100644 index 000000000..0bc383b86 --- /dev/null +++ b/tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo2.qbs @@ -0,0 +1,2 @@ +Group { +} diff --git a/tests/auto/blackbox/testdata/list-property-order/modules/lower/lower.qbs b/tests/auto/blackbox/testdata/list-property-order/modules/lower/lower.qbs index c7462676d..c47a40aea 100644 --- a/tests/auto/blackbox/testdata/list-property-order/modules/lower/lower.qbs +++ b/tests/auto/blackbox/testdata/list-property-order/modules/lower/lower.qbs @@ -1,5 +1,5 @@ Module { - property stringList listProp + property stringList listProp: [ "lower" ] Rule { inputs: ["intype"] @@ -10,7 +10,7 @@ Module { prepare: { var cmd = new JavaScriptCommand(); cmd.sourceCode = function() { - console.info("listProp = " + JSON.stringify(product.lower.listProp)); + console.warn("listProp = " + JSON.stringify(product.lower.listProp)); }; cmd.silent = true; return [cmd]; diff --git a/tests/auto/blackbox/testdata/list-property-order/product.qbs b/tests/auto/blackbox/testdata/list-property-order/product.qbs index e92494693..bec122214 100644 --- a/tests/auto/blackbox/testdata/list-property-order/product.qbs +++ b/tests/auto/blackbox/testdata/list-property-order/product.qbs @@ -4,6 +4,7 @@ Product { Depends { name: "higher1" } Depends { name: "higher2" } Depends { name: "higher3" } + lower.listProp: ["product"] Group { files: ["dummy.txt"] fileTags: ["intype"] diff --git a/tests/auto/blackbox/testdata/path-probe/BaseApp.qbs b/tests/auto/blackbox/testdata/path-probe/BaseApp.qbs index 84c00c240..93172579f 100644 --- a/tests/auto/blackbox/testdata/path-probe/BaseApp.qbs +++ b/tests/auto/blackbox/testdata/path-probe/BaseApp.qbs @@ -28,6 +28,7 @@ ** ****************************************************************************/ +import qbs.FileInfo import qbs.Probes CppApplication { @@ -40,6 +41,7 @@ CppApplication { property var inputCandidateFilter property stringList outputFilePaths + property var outputCandidatePaths Probes.PathProbe { id: probe @@ -49,6 +51,7 @@ CppApplication { nameFilter: inputNameFilter candidateFilter: inputCandidateFilter searchPaths: inputSearchPaths + platformSearchPaths: [] } property bool validate: { @@ -56,19 +59,72 @@ CppApplication { if (lhs.length !== rhs.length) return false; for (var i = 0; i < lhs.length; ++i) { - if (lhs[i] !== rhs[i]) + if (Array.isArray(lhs[i]) && Array.isArray(rhs[i])) { + if (!compareArrays(lhs[i], rhs[i])) + return false; + } else if (lhs[i] !== rhs[i]) { return false; + } } return true; }; - if (!probe.found) + if (outputCandidatePaths) { + var actual = probe.allResults.map(function(file) { return file.candidatePaths; }); + if (!compareArrays(actual, outputCandidatePaths)) { + throw "Invalid canndidatePaths: actual = " + JSON.stringify(actual) + + ", expected = " + JSON.stringify(outputCandidatePaths); + } + } + + if (!probe.found) { + if (probe.filePath) { + throw "Invalid filePath: actual = " + JSON.stringify(probe.filePath) + + ", expected = 'undefined'"; + } + if (probe.fileName) { + throw "Invalid fileName: actual = " + JSON.stringify(probe.fileName) + + ", expected = 'undefined'"; + } + if (probe.path) { + throw "Invalid path: actual = " + JSON.stringify(probe.path) + + ", expected = 'undefined'"; + } + throw "Probe failed to find files"; + } if (outputFilePaths) { var actual = probe.allResults.map(function(file) { return file.filePath; }); - if (!compareArrays(actual, outputFilePaths)) - throw "Invalid filePaths: actual = " + actual + ", expected = " + outputFilePaths; + if (!compareArrays(actual, outputFilePaths)) { + throw "Invalid filePaths: actual = " + JSON.stringify(actual) + + ", expected = " + JSON.stringify(outputFilePaths); + } + } + + if (probe.allResults.length !== 1) + return; + + // check that single-file interface matches the first value in allResults + var expectedFilePath = probe.allResults[0].filePath; + if (probe.filePath !== expectedFilePath) { + throw "Invalid filePath: actual = " + probe.filePath + + ", expected = " + expectedFilePath; + } + var expectedFileName = probe.allResults[0].fileName; + if (probe.fileName !== expectedFileName) { + throw "Invalid fileName: actual = " + probe.fileName + + ", expected = " + expectedFileName; + } + var expectedPath = probe.allResults[0].path; + if (probe.path !== expectedPath) { + throw "Invalid path: actual = " + probe.path + + ", expected = " + expectedPath; + } + var expectedCandidatePaths = probe.allResults[0].candidatePaths; + if (!compareArrays(probe.candidatePaths, expectedCandidatePaths)) { + throw "Invalid candidatePaths: actual = " + JSON.stringify(probe.candidatePaths) + + ", expected = " + JSON.stringify(expectedCandidatePaths); } } diff --git a/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs b/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs index a65256a68..c40f22736 100644 --- a/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs +++ b/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs @@ -3,10 +3,11 @@ import qbs.FileInfo BaseApp { inputNames: ["tool.1", "tool.2"] inputSearchPaths: "bin" - outputFilePaths: ["bin/tool.2"] inputCandidateFilter: { return function(f) { return FileInfo.fileName(f) == "tool.2"; } } + outputFilePaths: ["bin/tool.2"] + outputCandidatePaths: [["bin/tool.1", "bin/tool.2"]] } diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files-mult-suffixes.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files-mult-suffixes.qbs index b112db44d..33656d4e6 100644 --- a/tests/auto/blackbox/testdata/path-probe/mult-files-mult-suffixes.qbs +++ b/tests/auto/blackbox/testdata/path-probe/mult-files-mult-suffixes.qbs @@ -1,8 +1,9 @@ BaseApp { inputSelectors: [ - {names : "tool", nameSuffixes: [".1", ".2"]}, + {names : "tool", nameSuffixes: [".0", ".1", ".2"]}, {names : "super-tool", nameSuffixes: [".1"]}, ] inputSearchPaths: "bin" outputFilePaths: ["bin/tool.1", "bin/super-tool.1"] + outputCandidatePaths: [["bin/tool.0", "bin/tool.1"], ["bin/super-tool.1"]] } diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files-mult-variants.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files-mult-variants.qbs index 60c56e6b4..dd0b58aa2 100644 --- a/tests/auto/blackbox/testdata/path-probe/mult-files-mult-variants.qbs +++ b/tests/auto/blackbox/testdata/path-probe/mult-files-mult-variants.qbs @@ -1,9 +1,10 @@ BaseApp { inputSelectors: [ "tool", - ["tool.1", "tool.2"], + ["tool.0", "tool.1", "tool.2"], {names : ["tool.3", "tool.4"]}, ] inputSearchPaths: "bin" outputFilePaths: ["bin/tool", "bin/tool.1", "bin/tool.3"] + outputCandidatePaths: [["bin/tool"], ["bin/tool.0", "bin/tool.1"], ["bin/tool.3"]] } diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files-suffixes.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files-suffixes.qbs index 5e4fc27ca..7ae78de24 100644 --- a/tests/auto/blackbox/testdata/path-probe/mult-files-suffixes.qbs +++ b/tests/auto/blackbox/testdata/path-probe/mult-files-suffixes.qbs @@ -5,4 +5,5 @@ BaseApp { ] inputSearchPaths: "bin" outputFilePaths: ["bin/tool.2", "bin/super-tool.1"] + outputCandidatePaths: [["bin/tool.2"], ["bin/super-tool.1"]] } diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files.qbs index 08727ac01..aa08befc8 100644 --- a/tests/auto/blackbox/testdata/path-probe/mult-files.qbs +++ b/tests/auto/blackbox/testdata/path-probe/mult-files.qbs @@ -7,4 +7,5 @@ BaseApp { ] inputSearchPaths: "bin" outputFilePaths: ["bin/tool.1", "bin/tool.2", "bin/tool.3", "bin/tool.4"] + outputCandidatePaths: [["bin/tool.1"], ["bin/tool.2"], ["bin/tool.3"], ["bin/tool.4"]] } diff --git a/tests/auto/blackbox/testdata/path-probe/name-filter.qbs b/tests/auto/blackbox/testdata/path-probe/name-filter.qbs index 406988fed..b2840443b 100644 --- a/tests/auto/blackbox/testdata/path-probe/name-filter.qbs +++ b/tests/auto/blackbox/testdata/path-probe/name-filter.qbs @@ -7,4 +7,5 @@ BaseApp { }; } outputFilePaths: ["bin/tool.2"] + outputCandidatePaths: [["bin/tool.2"]] } diff --git a/tests/auto/blackbox/testdata/path-probe/non-existent-selector.qbs b/tests/auto/blackbox/testdata/path-probe/non-existent-selector.qbs index aaa27042c..aabb0fe7b 100644 --- a/tests/auto/blackbox/testdata/path-probe/non-existent-selector.qbs +++ b/tests/auto/blackbox/testdata/path-probe/non-existent-selector.qbs @@ -5,4 +5,5 @@ BaseApp { "tool.2", ] inputSearchPaths: "bin" + outputCandidatePaths: [["bin/tool.1"], ["bin/nonexistent"], ["bin/tool.2"]] } diff --git a/tests/auto/blackbox/testdata/path-probe/non-existent.qbs b/tests/auto/blackbox/testdata/path-probe/non-existent.qbs index f0c58fa6c..aad01c31b 100644 --- a/tests/auto/blackbox/testdata/path-probe/non-existent.qbs +++ b/tests/auto/blackbox/testdata/path-probe/non-existent.qbs @@ -1,4 +1,5 @@ BaseApp { inputNames: "nonexistent" inputSearchPaths: "bin" + outputCandidatePaths: [["bin/nonexistent"]] } diff --git a/tests/auto/blackbox/testdata/path-probe/single-file-mult-variants.qbs b/tests/auto/blackbox/testdata/path-probe/single-file-mult-variants.qbs index 992a0bea4..98f5b141a 100644 --- a/tests/auto/blackbox/testdata/path-probe/single-file-mult-variants.qbs +++ b/tests/auto/blackbox/testdata/path-probe/single-file-mult-variants.qbs @@ -2,4 +2,5 @@ BaseApp { inputNames: ["tool.1", "tool.2"] inputSearchPaths: "bin" outputFilePaths: ["bin/tool.1"] + outputCandidatePaths: [["bin/tool.1"]] } diff --git a/tests/auto/blackbox/testdata/path-probe/single-file-selector-array.qbs b/tests/auto/blackbox/testdata/path-probe/single-file-selector-array.qbs index 697665242..292df4add 100644 --- a/tests/auto/blackbox/testdata/path-probe/single-file-selector-array.qbs +++ b/tests/auto/blackbox/testdata/path-probe/single-file-selector-array.qbs @@ -2,4 +2,5 @@ BaseApp { inputSelectors: ["tool"] inputSearchPaths: "bin" outputFilePaths: ["bin/tool"] + outputCandidatePaths: [["bin/tool"]] } diff --git a/tests/auto/blackbox/testdata/path-probe/single-file-selector.qbs b/tests/auto/blackbox/testdata/path-probe/single-file-selector.qbs index d57700baf..cf7cfe436 100644 --- a/tests/auto/blackbox/testdata/path-probe/single-file-selector.qbs +++ b/tests/auto/blackbox/testdata/path-probe/single-file-selector.qbs @@ -2,4 +2,5 @@ BaseApp { inputSelectors: "tool" inputSearchPaths: "bin" outputFilePaths: ["bin/tool"] + outputCandidatePaths: [["bin/tool"]] } diff --git a/tests/auto/blackbox/testdata/path-probe/single-file-suffixes.qbs b/tests/auto/blackbox/testdata/path-probe/single-file-suffixes.qbs index 4442e719a..3436a49c3 100644 --- a/tests/auto/blackbox/testdata/path-probe/single-file-suffixes.qbs +++ b/tests/auto/blackbox/testdata/path-probe/single-file-suffixes.qbs @@ -1,6 +1,7 @@ BaseApp { inputNames: "tool" inputSearchPaths: "bin" - inputNameSuffixes: [".1", ".2"] + inputNameSuffixes: [".0", ".1", ".2"] outputFilePaths: ["bin/tool.1"] + outputCandidatePaths: [["bin/tool.0", "bin/tool.1"]] } diff --git a/tests/auto/blackbox/testdata/path-probe/single-file.qbs b/tests/auto/blackbox/testdata/path-probe/single-file.qbs index 3590e7664..e22d7ba0d 100644 --- a/tests/auto/blackbox/testdata/path-probe/single-file.qbs +++ b/tests/auto/blackbox/testdata/path-probe/single-file.qbs @@ -2,4 +2,5 @@ BaseApp { inputNames: "tool" inputSearchPaths: "bin" outputFilePaths: ["bin/tool"] + outputCandidatePaths: [["bin/tool"]] } diff --git a/tests/auto/blackbox/testdata/probeProperties/probeProperties.qbs b/tests/auto/blackbox/testdata/probeProperties/probeProperties.qbs index 9846eacef..ce89d11f4 100644 --- a/tests/auto/blackbox/testdata/probeProperties/probeProperties.qbs +++ b/tests/auto/blackbox/testdata/probeProperties/probeProperties.qbs @@ -1,29 +1,40 @@ import qbs.Probes -CppApplication { - Probes.PathProbe { - id: probe1 - names: ["bin/tool"] - platformSearchPaths: [product.sourceDirectory] - } +Project { - Probes.PathProbe { - id: probe2 - names: ["tool"] - platformSearchPaths: [product.sourceDirectory + "/bin"] - } + CppApplication { + Probes.PathProbe { + id: probe1 + names: ["bin/tool"] + platformSearchPaths: [product.sourceDirectory] + } - targetName: { - console.info("probe1.fileName=" + probe1.fileName); - console.info("probe1.path=" + probe1.path); - console.info("probe1.filePath=" + probe1.filePath); + Probes.PathProbe { + id: probe2 + names: ["tool"] + platformSearchPaths: [product.sourceDirectory + "/bin"] + } - console.info("probe2.fileName=" + probe2.fileName); - console.info("probe2.path=" + probe2.path); - console.info("probe2.filePath=" + probe2.filePath); + targetName: { + console.info("probe1.fileName=" + probe1.fileName); + console.info("probe1.path=" + probe1.path); + console.info("probe1.filePath=" + probe1.filePath); - return name; + console.info("probe2.fileName=" + probe2.fileName); + console.info("probe2.path=" + probe2.path); + console.info("probe2.filePath=" + probe2.filePath); + + console.info("probe3.fileName=" + probe3.fileName); + console.info("probe3.path=" + probe3.path); + console.info("probe3.filePath=" + probe3.filePath); + return name; + } + } + + Probes.PathProbe { + id: probe3 + names: ["tool"] + platformSearchPaths: [project.sourceDirectory + "/bin"] } - files: ["main.c"] } diff --git a/tests/auto/blackbox/testdata/property-evaluation-context/modules/base/base.qbs b/tests/auto/blackbox/testdata/property-evaluation-context/modules/base/base.qbs new file mode 100644 index 000000000..a97538751 --- /dev/null +++ b/tests/auto/blackbox/testdata/property-evaluation-context/modules/base/base.qbs @@ -0,0 +1,4 @@ +Module { + property string productInBase: product.name + property string productInTop: "" +} diff --git a/tests/auto/blackbox/testdata/property-evaluation-context/modules/top/top.qbs b/tests/auto/blackbox/testdata/property-evaluation-context/modules/top/top.qbs new file mode 100644 index 000000000..fa073ff78 --- /dev/null +++ b/tests/auto/blackbox/testdata/property-evaluation-context/modules/top/top.qbs @@ -0,0 +1,6 @@ +Module { + Depends { name: "base" } + base.productInTop: product.name + property string productInTop: product.name + property string productInExport: "" +} diff --git a/tests/auto/blackbox/testdata/property-evaluation-context/property-evaluation-context.qbs b/tests/auto/blackbox/testdata/property-evaluation-context/property-evaluation-context.qbs new file mode 100644 index 000000000..28216c150 --- /dev/null +++ b/tests/auto/blackbox/testdata/property-evaluation-context/property-evaluation-context.qbs @@ -0,0 +1,34 @@ +Project { + qbsSearchPaths: [ path ] + Product { + name: "mylib" + Export { + Depends { name: "top" } + top.productInExport: product.name + } + } + + Product { + type: "rule-output" + name: "myapp" + Depends { name: "mylib" } + + Rule { + alwaysRun: true + multiplex: true + requiresInputs: false + outputFileTags: "rule-output" + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + console.info("base.productInBase evaluated in: " + product.base.productInBase); + console.info("base.productInTop evaluated in: " + product.base.productInTop); + console.info("top.productInExport evaluated in: " + product.top.productInExport); + console.info("top.productInTop evaluated in: " + product.top.productInTop); + } + return [cmd]; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/protobuf/addressbook_cpp.qbs b/tests/auto/blackbox/testdata/protobuf/addressbook_cpp.qbs index f09706b47..5e6ffc508 100644 --- a/tests/auto/blackbox/testdata/protobuf/addressbook_cpp.qbs +++ b/tests/auto/blackbox/testdata/protobuf/addressbook_cpp.qbs @@ -12,6 +12,7 @@ CppApplication { Depends { name: "cpp" } cpp.cxxLanguageVersion: "c++11" + cpp.minimumMacosVersion: "10.8" Depends { name: "protobuf.cpp"; required: false } property bool hasProtobuf: { diff --git a/tests/auto/blackbox/testdata/protobuf/import.qbs b/tests/auto/blackbox/testdata/protobuf/import.qbs index 59a094dce..ef4e80c1b 100644 --- a/tests/auto/blackbox/testdata/protobuf/import.qbs +++ b/tests/auto/blackbox/testdata/protobuf/import.qbs @@ -13,6 +13,7 @@ CppApplication { protobuf.cpp.importPaths: [sourceDirectory] cpp.cxxLanguageVersion: "c++11" + cpp.minimumMacosVersion: "10.8" Depends { name: "protobuf.cpp"; required: false } property bool hasProtobuf: { diff --git a/tests/auto/blackbox/testdata/protobuf/needs-import-dir.qbs b/tests/auto/blackbox/testdata/protobuf/needs-import-dir.qbs index 475c1c6c7..493632a0e 100644 --- a/tests/auto/blackbox/testdata/protobuf/needs-import-dir.qbs +++ b/tests/auto/blackbox/testdata/protobuf/needs-import-dir.qbs @@ -14,6 +14,7 @@ CppApplication { protobuf.cpp.importPaths: (theImportDir ? [theImportDir] : []).concat([sourceDirectory]) cpp.cxxLanguageVersion: "c++11" + cpp.minimumMacosVersion: "10.8" Depends { name: "protobuf.cpp"; required: false } property bool hasProtobuf: { diff --git a/tests/auto/blackbox/testdata/response-files/response-files.qbs b/tests/auto/blackbox/testdata/response-files/response-files.qbs index efed1dc9f..168cdf66a 100644 --- a/tests/auto/blackbox/testdata/response-files/response-files.qbs +++ b/tests/auto/blackbox/testdata/response-files/response-files.qbs @@ -44,6 +44,9 @@ Project { Product { name: "lotsofobjects" type: ["dynamiclibrary"] + // clang-cl does not use response file internally, thus linker complains that command is + // too long. This can be worked around by calling the linker directly + cpp.linkerMode: qbs.toolchain.contains("clang-cl") ? "manual" : original Depends { name: "cpp" } Rule { multiplex: true diff --git a/tests/auto/blackbox/testdata/sanitizer/sanitizer.cpp b/tests/auto/blackbox/testdata/sanitizer/sanitizer.cpp new file mode 100644 index 000000000..4a7c3ee32 --- /dev/null +++ b/tests/auto/blackbox/testdata/sanitizer/sanitizer.cpp @@ -0,0 +1,4 @@ +int main(int argc, char *argv[]) +{ + return 0; +} diff --git a/tests/auto/blackbox/testdata/sanitizer/sanitizer.qbs b/tests/auto/blackbox/testdata/sanitizer/sanitizer.qbs new file mode 100644 index 000000000..7b5054316 --- /dev/null +++ b/tests/auto/blackbox/testdata/sanitizer/sanitizer.qbs @@ -0,0 +1,30 @@ +CppApplication { + property string sanitizer + + property bool supportsSanitizer: { + if (qbs.toolchain.contains("clang-cl")) + // only these are supported + return sanitizer === "address" || sanitizer === "undefined"; + if (!qbs.toolchain.contains("gcc")) + return false; + if (qbs.toolchain.contains("mingw")) + return false; + return true; + } + + condition: { + if (!sanitizer) + return true; + if (!supportsSanitizer) + console.info("Compiler does not support sanitizer"); + return supportsSanitizer; + } + qbs.buildVariant: "release" + cpp.cxxLanguageVersion: "c++11" + cpp.minimumMacosVersion: "10.8" + consoleApplication: true + cpp.runtimeLibrary: "static" + cpp.driverFlags: sanitizer ? ["-fsanitize=" + sanitizer] : [] + cpp.debugInformation: true + files: "sanitizer.cpp" +} diff --git a/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.h b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.h new file mode 100644 index 000000000..a82b12fbd --- /dev/null +++ b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.h @@ -0,0 +1 @@ +#include "lib.h" diff --git a/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.qbs b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.qbs new file mode 100644 index 000000000..e931b853c --- /dev/null +++ b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.qbs @@ -0,0 +1,4 @@ +CppApplication { + cpp.includePaths: project.sourceDirectory + "/lib" + files: "main.cpp" +} diff --git a/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/main.cpp b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/main.cpp new file mode 100644 index 000000000..2e7bedac8 --- /dev/null +++ b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/main.cpp @@ -0,0 +1,3 @@ +#include "app.h" + +int main() { } diff --git a/tests/auto/blackbox/testdata/scan-result-in-non-dependency/lib/lib.h b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/lib/lib.h new file mode 100644 index 000000000..af6f627b7 --- /dev/null +++ b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/lib/lib.h @@ -0,0 +1,3 @@ +#pragma once + +void lib1_foo();
\ No newline at end of file diff --git a/tests/auto/blackbox/testdata/scan-result-in-non-dependency/other/other.qbs b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/other/other.qbs new file mode 100644 index 000000000..29682da1c --- /dev/null +++ b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/other/other.qbs @@ -0,0 +1,24 @@ +import qbs.TextFile + +Product { + type: "testproduct" + files: "../lib/lib.h" + + Rule { + multiplex: true + Artifact { + fileTags: ["testproduct"] + filePath: "fubar" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating text file"; + cmd.sourceCode = function() { + var tf = new TextFile(output.filePath, TextFile.WriteOnly); + tf.writeLine("blubb"); + tf.close(); + } + return cmd; + } + } +} diff --git a/tests/auto/blackbox/testdata/scan-result-in-non-dependency/p.qbs b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/p.qbs new file mode 100644 index 000000000..bcbd5ebce --- /dev/null +++ b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/p.qbs @@ -0,0 +1,6 @@ +Project { + references: [ + "app/app.qbs", + "other/other.qbs", + ] +} diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.h b/tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.h new file mode 100644 index 000000000..a82b12fbd --- /dev/null +++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.h @@ -0,0 +1 @@ +#include "lib.h" diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.qbs b/tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.qbs new file mode 100644 index 000000000..984e9aca9 --- /dev/null +++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.qbs @@ -0,0 +1,4 @@ +CppApplication { + Depends { name: "lib" } + files: "main.cpp" +} diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/app/main.cpp b/tests/auto/blackbox/testdata/scan-result-in-other-product/app/main.cpp new file mode 100644 index 000000000..2e7bedac8 --- /dev/null +++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/app/main.cpp @@ -0,0 +1,3 @@ +#include "app.h" + +int main() { } diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.h b/tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.h new file mode 100644 index 000000000..af6f627b7 --- /dev/null +++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.h @@ -0,0 +1,3 @@ +#pragma once + +void lib1_foo();
\ No newline at end of file diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.qbs b/tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.qbs new file mode 100644 index 000000000..fe2916714 --- /dev/null +++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.qbs @@ -0,0 +1,7 @@ +Product { + files: "lib.h" + Export { + Depends { name: "cpp" } + cpp.includePaths: product.sourceDirectory + } +} diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/other/other.qbs b/tests/auto/blackbox/testdata/scan-result-in-other-product/other/other.qbs new file mode 100644 index 000000000..29682da1c --- /dev/null +++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/other/other.qbs @@ -0,0 +1,24 @@ +import qbs.TextFile + +Product { + type: "testproduct" + files: "../lib/lib.h" + + Rule { + multiplex: true + Artifact { + fileTags: ["testproduct"] + filePath: "fubar" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating text file"; + cmd.sourceCode = function() { + var tf = new TextFile(output.filePath, TextFile.WriteOnly); + tf.writeLine("blubb"); + tf.close(); + } + return cmd; + } + } +} diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/p.qbs b/tests/auto/blackbox/testdata/scan-result-in-other-product/p.qbs new file mode 100644 index 000000000..fedf84989 --- /dev/null +++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/p.qbs @@ -0,0 +1,7 @@ +Project { + references: [ + "app/app.qbs", + "lib/lib.qbs", + "other/other.qbs", + ] +} diff --git a/tests/auto/blackbox/testdata/undefined-target-platform/undefined-target-platform.qbs b/tests/auto/blackbox/testdata/undefined-target-platform/undefined-target-platform.qbs new file mode 100644 index 000000000..2b3724c26 --- /dev/null +++ b/tests/auto/blackbox/testdata/undefined-target-platform/undefined-target-platform.qbs @@ -0,0 +1,13 @@ +import qbs.File +import qbs.FileInfo + +Product { + name: "undefined-target-platform" + qbs.targetPlatform: undefined + + readonly property bool _validate: { + if (Array.isArray(qbs.targetOS) && qbs.targetOS.length === 0) + return true; + throw "Invalid qbs.targetOS value: " + qbs.targetOS; + } +} diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 861790acd..25e36816c 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -713,6 +713,25 @@ void TestBlackbox::buildGraphVersions() QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData()); } +void TestBlackbox::buildVariantDefaults_data() +{ + QTest::addColumn<QString>("buildVariant"); + QTest::newRow("default") << QString(); + QTest::newRow("debug") << QStringLiteral("debug"); + QTest::newRow("release") << QStringLiteral("release"); + QTest::newRow("profiling") << QStringLiteral("profiling"); +} + +void TestBlackbox::buildVariantDefaults() +{ + QFETCH(QString, buildVariant); + QDir::setCurrent(testDataDir + "/build-variant-defaults"); + QbsRunParameters params{QStringLiteral("resolve")}; + if (!buildVariant.isEmpty()) + params.arguments << ("modules.qbs.buildVariant:" + buildVariant); + QCOMPARE(runQbs(params), 0); +} + void TestBlackbox::changedFiles_data() { QTest::addColumn<bool>("useChangedFilesForInitialBuild"); @@ -942,20 +961,11 @@ void TestBlackbox::dependenciesProperty() QCOMPARE(product2_cpp_defines.first().toString(), QLatin1String("DIGEDAG")); } -void TestBlackbox::dependencyProfileMismatch() +void TestBlackbox::dependencyScanningLoop() { - QDir::setCurrent(testDataDir + "/dependency-profile-mismatch"); - const SettingsPtr s = settings(); - qbs::Internal::TemporaryProfile depProfile("qbs_autotests_profileMismatch", s.get()); - depProfile.p.setValue("qbs.architecture", "x86"); // Profiles must not be empty... - s->sync(); - QbsRunParameters params(QStringList() << ("project.mainProfile:" + profileName()) - << ("project.depProfile:" + depProfile.p.name())); - params.expectFailure = true; - QVERIFY2(runQbs(params) != 0, m_qbsStderr.constData()); - QVERIFY2(m_qbsStderr.contains(profileName().toLocal8Bit()) - && m_qbsStderr.contains("', which does not exist"), - m_qbsStderr.constData()); + QDir::setCurrent(testDataDir + "/dependency-scanning-loop"); + QCOMPARE(runQbs(), 0); + QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData()); } void TestBlackbox::deprecatedProperty() @@ -1442,7 +1452,7 @@ void TestBlackbox::versionScript() { const SettingsPtr s = settings(); Profile buildProfile(profileName(), s.get()); - QStringList toolchain = buildProfile.value("qbs.toolchain").toStringList(); + QStringList toolchain = profileToolchain(buildProfile); if (!toolchain.contains("gcc") || targetOs() != HostOsInfo::HostOsLinux) QSKIP("version script test only applies to Linux"); QDir::setCurrent(testDataDir + "/versionscript"); @@ -1765,6 +1775,40 @@ void TestBlackbox::cxxLanguageVersion_data() std::make_pair(QString("msvc-new"), QString("/std:"))}); } +void TestBlackbox::conanfileProbe() +{ + QString executable = findExecutable({"conan"}); + if (executable.isEmpty()) + QSKIP("conan is not installed or not available in PATH."); + + // We first build a dummy package testlib and use that as dependency + // in the testapp package. + QDir::setCurrent(testDataDir + "/conanfile-probe/testlib"); + QStringList arguments { "create", "-o", "opt=True", "-s", "os=AIX", ".", + "testlib/1.2.3@qbs/testing" }; + QProcess conan; + conan.start(executable, arguments); + QVERIFY(waitForProcessSuccess(conan)); + + QDir::setCurrent(testDataDir + "/conanfile-probe/testapp"); + QCOMPARE(runQbs(QbsRunParameters("resolve", {"--force-probe-execution"})), 0); + + QFile file(relativeBuildDir() + "/results.json"); + QVERIFY(file.open(QIODevice::ReadOnly)); + QVariantMap actualResults = QJsonDocument::fromJson(file.readAll()).toVariant().toMap(); + const auto generatedFilesPath = actualResults.take("generatedFilesPath").toString(); + // We want to make sure that generatedFilesPath is under the project directory, + // but we don't care about the actual name. + QVERIFY(directoryExists(relativeBuildDir() + "/genconan/" + + QFileInfo(generatedFilesPath).baseName())); + + const QVariantMap expectedResults = { + { "json", "TESTLIB_ENV_VAL" }, + { "dependencies", QVariantList{"testlib1", "testlib2"} }, + }; + QCOMPARE(actualResults, expectedResults); +} + void TestBlackbox::cpuFeatures() { QDir::setCurrent(testDataDir + "/cpu-features"); @@ -1832,7 +1876,7 @@ void TestBlackbox::separateDebugInfo() const SettingsPtr s = settings(); Profile buildProfile(profileName(), s.get()); - QStringList toolchain = buildProfile.value("qbs.toolchain").toStringList(); + QStringList toolchain = profileToolchain(buildProfile); if (isDarwin) { QVERIFY(directoryExists(relativeProductBuildDir("app1") + "/app1.app.dSYM")); QVERIFY(regularFileExists(relativeProductBuildDir("app1") @@ -2035,7 +2079,7 @@ void TestBlackbox::trackExternalProductChanges() rmDirR(relativeBuildDir()); const SettingsPtr s = settings(); const Profile profile(profileName(), s.get()); - const QStringList toolchainTypes = profile.value("qbs.toolchain").toStringList(); + const QStringList toolchainTypes = profileToolchain(profile); if (!toolchainTypes.contains("gcc")) QSKIP("Need GCC-like compiler to run this test"); params.environment = QProcessEnvironment::systemEnvironment(); @@ -2332,9 +2376,11 @@ void TestBlackbox::reproducibleBuild() { const SettingsPtr s = settings(); const Profile profile(profileName(), s.get()); - const QStringList toolchains = profile.value("qbs.toolchain").toStringList(); - if (!toolchains.contains("gcc") || toolchains.contains("clang")) + const QStringList toolchains = profileToolchain(profile); + if (!toolchains.contains("gcc")) QSKIP("reproducible builds only supported for gcc"); + if (toolchains.contains("clang")) + QSKIP("reproducible builds are not supported for clang"); QFETCH(bool, reproducible); @@ -2495,6 +2541,36 @@ void TestBlackbox::ruleWithNonRequiredInputs() QVERIFY2(m_qbsStdout.contains("Generating"), m_qbsStdout.constData()); } +void TestBlackbox::sanitizer_data() +{ + QTest::addColumn<QString>("sanitizer"); + QTest::newRow("none") << QString(); + QTest::newRow("address") << QStringLiteral("address"); + QTest::newRow("undefined") << QStringLiteral("undefined"); + QTest::newRow("thread") << QStringLiteral("thread"); +} + +void TestBlackbox::sanitizer() +{ + QFETCH(QString, sanitizer); + QDir::setCurrent(testDataDir + "/sanitizer"); + rmDirR(relativeBuildDir()); + QbsRunParameters params("build", {"--command-echo-mode", "command-line"}); + if (!sanitizer.isEmpty()) { + params.arguments.append( + {QStringLiteral("products.sanitizer.sanitizer:\"") + sanitizer + "\""}); + } + QCOMPARE(runQbs(params), 0); + if (m_qbsStdout.contains(QByteArrayLiteral("Compiler does not support sanitizer"))) + QSKIP("Compiler does not support the specified sanitizer"); + if (!sanitizer.isEmpty()) { + QVERIFY2(m_qbsStdout.contains(QByteArrayLiteral("-fsanitize=") + sanitizer.toLatin1()), + qPrintable(m_qbsStdout)); + } else { + QVERIFY2(!m_qbsStdout.contains(QByteArrayLiteral("-fsanitize=")), qPrintable(m_qbsStdout)); + } +} + void TestBlackbox::scannerItem() { QDir::setCurrent(testDataDir + "/scanner-item"); @@ -2513,6 +2589,47 @@ void TestBlackbox::scannerItem() QVERIFY2(m_qbsStdout.contains("handling file2.in"), m_qbsStdout.constData()); } +void TestBlackbox::scanResultInOtherProduct() +{ + QDir::setCurrent(testDataDir + "/scan-result-in-other-product"); + QCOMPARE(runQbs(QStringList("-vv")), 0); + QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("generating text file"), m_qbsStdout.constData()); + QVERIFY2(!m_qbsStderr.contains("The file dependency might get lost during change tracking"), + m_qbsStderr.constData()); + WAIT_FOR_NEW_TIMESTAMP(); + REPLACE_IN_FILE("other/other.qbs", "blubb", "blubb2"); + QCOMPARE(runQbs(), 0); + QVERIFY2(!m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("generating text file"), m_qbsStdout.constData()); + WAIT_FOR_NEW_TIMESTAMP(); + touch("lib/lib.h"); + QCOMPARE(runQbs(), 0); + QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData()); + QVERIFY2(!m_qbsStdout.contains("generating text file"), m_qbsStdout.constData()); +} + +void TestBlackbox::scanResultInNonDependency() +{ + QDir::setCurrent(testDataDir + "/scan-result-in-non-dependency"); + QCOMPARE(runQbs(QStringList("-vv")), 0); + QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("generating text file"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStderr.contains("The file dependency might get lost during change tracking"), + m_qbsStderr.constData()); + WAIT_FOR_NEW_TIMESTAMP(); + REPLACE_IN_FILE("other/other.qbs", "blubb", "blubb2"); + QCOMPARE(runQbs(), 0); + QVERIFY2(!m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("generating text file"), m_qbsStdout.constData()); + WAIT_FOR_NEW_TIMESTAMP(); + touch("lib/lib.h"); + QCOMPARE(runQbs(), 0); + QEXPECT_FAIL("", "QBS-1532", Continue); + QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData()); + QVERIFY2(!m_qbsStdout.contains("generating text file"), m_qbsStdout.constData()); +} + void TestBlackbox::setupBuildEnvironment() { QDir::setCurrent(testDataDir + "/setup-build-environment"); @@ -2821,6 +2938,8 @@ void TestBlackbox::pathProbe() QbsRunParameters buildParams("build", QStringList{"-f", projectFile}); buildParams.expectFailure = !successExpected; QCOMPARE(runQbs(buildParams) == 0, successExpected); + if (!successExpected) + QVERIFY2(m_qbsStderr.contains("Probe failed to find files"), m_qbsStderr); } void TestBlackbox::pchChangeTracking() @@ -3077,6 +3196,9 @@ void TestBlackbox::probeProperties() QVERIFY2(m_qbsStdout.contains("probe2.fileName=tool"), m_qbsStdout.constData()); QVERIFY2(m_qbsStdout.contains("probe2.path=" + dir + "/bin"), m_qbsStdout.constData()); QVERIFY2(m_qbsStdout.contains("probe2.filePath=" + dir + "/bin/tool"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("probe3.fileName=tool"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("probe3.path=" + dir + "/bin"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("probe3.filePath=" + dir + "/bin/tool"), m_qbsStdout.constData()); } void TestBlackbox::probesAndShadowProducts() @@ -3099,7 +3221,7 @@ void TestBlackbox::probeInExportedModule() << QStringLiteral("probe-in-exported-module.qbs"))), 0); QVERIFY2(m_qbsStdout.contains("found: true"), m_qbsStdout.constData()); QVERIFY2(m_qbsStdout.contains("prop: yes"), m_qbsStdout.constData()); - QVERIFY2(m_qbsStdout.contains("listProp: my,myother"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("listProp: myother,my"), m_qbsStdout.constData()); } void TestBlackbox::probesAndArrayProperties() @@ -3368,6 +3490,17 @@ void TestBlackbox::propertyChanges() QVERIFY(m_qbsStdout.contains("Making output from other output")); } +void TestBlackbox::propertyEvaluationContext() +{ + const QString testDir = testDataDir + "/property-evaluation-context"; + QDir::setCurrent(testDir); + QCOMPARE(runQbs(), 0); + QCOMPARE(m_qbsStdout.count("base.productInBase evaluated in: myapp"), 1); + QCOMPARE(m_qbsStdout.count("base.productInTop evaluated in: myapp"), 1); + QCOMPARE(m_qbsStdout.count("top.productInExport evaluated in: mylib"), 1); + QCOMPARE(m_qbsStdout.count("top.productInTop evaluated in: myapp"), 1); +} + void TestBlackbox::qtBug51237() { const QString profileName = "profile-qtBug51237"; @@ -3452,6 +3585,42 @@ void TestBlackbox::dynamicRuleOutputs() QVERIFY(!QFile::exists(sourceFile2)); } +void TestBlackbox::emptyProfile() +{ + QDir::setCurrent(testDataDir + "/empty-profile"); + + const SettingsPtr s = settings(); + const Profile buildProfile(profileName(), s.get()); + bool isMsvc = false; + auto toolchainType = buildProfile.value(QStringLiteral("qbs.toolchainType")).toString(); + QbsRunParameters params; + params.profile = "none"; + + if (toolchainType.isEmpty()) { + const auto toolchain = buildProfile.value(QStringLiteral("qbs.toolchain")).toStringList(); + if (!toolchain.isEmpty()) + toolchainType = toolchain.first(); + } + if (!toolchainType.isEmpty()) { + params.arguments = QStringList{QStringLiteral("qbs.toolchainType:") + toolchainType}; + isMsvc = toolchainType == "msvc" || toolchainType == "clang-cl"; + } + + if (!isMsvc) { + const auto tcPath = + QDir::toNativeSeparators( + buildProfile.value(QStringLiteral("cpp.toolchainInstallPath")).toString()); + auto paths = params.environment.value(QStringLiteral("PATH")) + .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts); + if (!tcPath.isEmpty() && !paths.contains(tcPath)) { + paths.prepend(tcPath); + params.environment.insert( + QStringLiteral("PATH"), paths.join(HostOsInfo::pathListSeparator())); + } + } + QCOMPARE(runQbs(params), 0); +} + void TestBlackbox::erroneousFiles_data() { QTest::addColumn<QString>("errorMessage"); @@ -3544,9 +3713,11 @@ void TestBlackbox::escapedLinkerFlags() { const SettingsPtr s = settings(); const Profile buildProfile(profileName(), s.get()); - const QStringList toolchain = buildProfile.value("qbs.toolchain").toStringList(); - if (!toolchain.contains("gcc") || targetOs() == HostOsInfo::HostOsMacos) + const QStringList toolchain = profileToolchain(buildProfile); + if (!toolchain.contains("gcc")) QSKIP("escaped linker flags test only applies with gcc and GNU ld"); + if (targetOs() == HostOsInfo::HostOsMacos) + QSKIP("Does not apply on macOS"); QDir::setCurrent(testDataDir + "/escaped-linker-flags"); QbsRunParameters params(QStringList("products.app.escapeLinkerFlags:false")); QCOMPARE(runQbs(params), 0); @@ -3836,6 +4007,40 @@ void TestBlackbox::fileTagsFilterMerging() QVERIFY2(QFile::exists(otherOutput), qPrintable(otherOutput)); } +void TestBlackbox::freedesktop() +{ + if (!HostOsInfo::isAnyUnixHost()) + QSKIP("only applies on Unix"); + if (HostOsInfo::isMacosHost()) + QSKIP("Does not apply on macOS"); + QDir::setCurrent(testDataDir + "/freedesktop"); + QCOMPARE(runQbs(), 0); + + // Check desktop file + QString desktopFilePath = + defaultInstallRoot + "/usr/local/share/applications/myapp.desktop"; + QVERIFY(QFile::exists(desktopFilePath)); + QFile desktopFile(desktopFilePath); + QVERIFY2(desktopFile.open(QIODevice::ReadOnly), qPrintable(desktopFile.errorString())); + QByteArrayList lines = desktopFile.readAll().split('\n'); + // Automatically filled line: + QVERIFY(lines.contains("Exec=main")); + // Name specified in `freedesktop.name` property + QVERIFY(lines.contains("Name=My App")); + // Overridden line: + QVERIFY(lines.contains("Icon=myapp.png")); + // Untouched line: + QVERIFY(lines.contains("Terminal=false")); + + // Check AppStream file + QVERIFY(QFile::exists(defaultInstallRoot + + "/usr/local/share/metainfo/myapp.appdata.xml")); + + // Check icon file + QVERIFY(QFile::exists(defaultInstallRoot + + "/usr/local/share/icons/hicolor/scalable/apps/myapp.png")); +} + void TestBlackbox::installedTransformerOutput() { QDir::setCurrent(testDataDir + "/installed-transformer-output"); @@ -3849,8 +4054,16 @@ void TestBlackbox::installLocations_data() QTest::addColumn<QString>("binDir"); QTest::addColumn<QString>("dllDir"); QTest::addColumn<QString>("libDir"); - QTest::newRow("explicit values") << QString("bindir") << QString("dlldir") << QString("libdir"); - QTest::newRow("default values") << QString() << QString() << QString(); + QTest::addColumn<QString>("pluginDir"); + QTest::addColumn<QString>("dsymDir"); + QTest::newRow("explicit values") + << QString("bindir") + << QString("dlldir") + << QString("libdir") + << QString("pluginDir") + << QString("dsymDir"); + QTest::newRow("default values") + << QString() << QString() << QString() << QString() << QString(); } void TestBlackbox::installLocations() @@ -3859,6 +4072,8 @@ void TestBlackbox::installLocations() QFETCH(QString, binDir); QFETCH(QString, dllDir); QFETCH(QString, libDir); + QFETCH(QString, pluginDir); + QFETCH(QString, dsymDir); QbsRunParameters params("resolve"); if (!binDir.isEmpty()) params.arguments.push_back("products.theapp.installDir:" + binDir); @@ -3866,6 +4081,13 @@ void TestBlackbox::installLocations() params.arguments.push_back("products.thelib.installDir:" + dllDir); if (!libDir.isEmpty()) params.arguments.push_back("products.thelib.importLibInstallDir:" + libDir); + if (!pluginDir.isEmpty()) + params.arguments.push_back("products.theplugin.installDir:" + pluginDir); + if (!dsymDir.isEmpty()) { + params.arguments.push_back("products.theapp.debugInformationInstallDir:" + dsymDir); + params.arguments.push_back("products.thelib.debugInformationInstallDir:" + dsymDir); + params.arguments.push_back("products.theplugin.debugInformationInstallDir:" + dsymDir); + } QCOMPARE(runQbs(params), 0); const bool isWindows = m_qbsStdout.contains("is windows"); const bool isDarwin = m_qbsStdout.contains("is darwin"); @@ -3873,33 +4095,77 @@ void TestBlackbox::installLocations() const bool isUnix = m_qbsStdout.contains("is unix"); QVERIFY(isWindows || isDarwin || isUnix); QCOMPARE(runQbs(QbsRunParameters(QStringList("--clean-install-root"))), 0); - const QString dllFileName = - isWindows ? "thelib.dll" : isDarwin ? "thelib" : "libthelib.so"; - const QString appFileName = isWindows ? "theapp.exe" : "theapp"; - if (binDir.isEmpty()) - binDir = isDarwin ? "/Applications" : "/bin"; - if (dllDir.isEmpty()) - dllDir = isDarwin ? "/Library/Frameworks" : isWindows ? "/bin" : "/lib"; - if (libDir.isEmpty()) - libDir = "/lib"; - if (isDarwin) { - if (isMac) - binDir += "/theapp.app/Contents/MacOS"; - else - binDir += "/theapp.app/"; - dllDir += "/thelib.framework"; - } + + struct BinaryInfo + { + QString fileName; + QString installDir; + QString subDir; + + QString absolutePath(const QString &prefix) const + { + return QDir::cleanPath(prefix + '/' + installDir + '/' + subDir + '/' + fileName); + } + }; + + const BinaryInfo dll = { + isWindows ? "thelib.dll" : isDarwin ? "thelib" : "libthelib.so", + dllDir.isEmpty() + ? (isDarwin ? "/Library/Frameworks" : (isWindows ? "/bin" : "/lib")) + : dllDir, + isDarwin ? "thelib.framework" : "" + }; + const BinaryInfo dllDsym = { + isWindows ? "thelib.pdb" : isDarwin ? "thelib.framework.dSYM" : "libthelib.so.debug", + dsymDir.isEmpty() ? dll.installDir : dsymDir, + {} + }; + const BinaryInfo plugin = { + isWindows ? "theplugin.dll" : isDarwin ? "theplugin" : "libtheplugin.so", + pluginDir.isEmpty() ? dll.installDir : pluginDir, + isDarwin ? (isMac ? "theplugin.bundle/Contents/MacOS" : "theplugin.bundle") : "" + }; + const BinaryInfo pluginDsym = { + isWindows ? "theplugin.pdb" : isDarwin ? "theplugin.bundle.dSYM" : "libtheplugin.so.debug", + dsymDir.isEmpty() ? plugin.installDir : dsymDir, + {} + }; + const BinaryInfo app = { + isWindows ? "theapp.exe" : "theapp", + binDir.isEmpty() ? (isDarwin ? "/Applications" : "/bin") : binDir, + isDarwin ? (isMac ? "theapp.app/Contents/MacOS" : "theapp.app") : "" + }; + const BinaryInfo appDsym = { + isWindows ? "theapp.pdb" : isDarwin ? "theapp.app.dSYM" : "theapp.debug", + dsymDir.isEmpty() ? app.installDir : dsymDir, + {} + }; + const QString installRoot = QDir::currentPath() + "/default/install-root"; const QString installPrefix = isWindows ? QString() : "/usr/local"; const QString fullInstallPrefix = installRoot + '/' + installPrefix + '/'; - const QString appFilePath = fullInstallPrefix + binDir + '/' + appFileName; + const QString appFilePath = app.absolutePath(fullInstallPrefix); QVERIFY2(QFile::exists(appFilePath), qPrintable(appFilePath)); - const QString dllFilePath = fullInstallPrefix + dllDir + '/' + dllFileName; + const QString dllFilePath = dll.absolutePath(fullInstallPrefix); QVERIFY2(QFile::exists(dllFilePath), qPrintable(dllFilePath)); if (isWindows) { - const QString libFilePath = fullInstallPrefix + libDir + "/thelib.lib"; + const BinaryInfo lib = { + "thelib.lib", + libDir.isEmpty() ? "/lib" : libDir, + "" + }; + const QString libFilePath = lib.absolutePath(fullInstallPrefix); QVERIFY2(QFile::exists(libFilePath), qPrintable(libFilePath)); } + const QString pluginFilePath = plugin.absolutePath(fullInstallPrefix); + QVERIFY2(QFile::exists(pluginFilePath), qPrintable(pluginFilePath)); + + const QString appDsymFilePath = appDsym.absolutePath(fullInstallPrefix); + QVERIFY2(QFileInfo(appDsymFilePath).exists(), qPrintable(appDsymFilePath)); + const QString dllDsymFilePath = dllDsym.absolutePath(fullInstallPrefix); + QVERIFY2(QFileInfo(dllDsymFilePath).exists(), qPrintable(dllDsymFilePath)); + const QString pluginDsymFilePath = pluginDsym.absolutePath(fullInstallPrefix); + QVERIFY2(QFile::exists(pluginDsymFilePath), qPrintable(pluginDsymFilePath)); } void TestBlackbox::inputsFromDependencies() @@ -4080,7 +4346,7 @@ void TestBlackbox::cli() const SettingsPtr s = settings(); Profile p("qbs_autotests-cli", s.get()); - const QStringList toolchain = p.value("qbs.toolchain").toStringList(); + const QStringList toolchain = profileToolchain(p); if (!p.exists() || !(toolchain.contains("dotnet") || toolchain.contains("mono"))) QSKIP("No suitable Common Language Infrastructure test profile"); @@ -4311,6 +4577,15 @@ void TestBlackbox::jsExtensionsBinaryFile() QCOMPARE(data.at(7), char(0xFF)); } +void TestBlackbox::lastModuleCandidateBroken() +{ + QDir::setCurrent(testDataDir + "/last-module-candidate-broken"); + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("Module Foo could not be loaded"), m_qbsStderr); +} + void TestBlackbox::ld() { QDir::setCurrent(testDataDir + "/ld"); @@ -4549,7 +4824,7 @@ void TestBlackbox::linkerLibraryDuplicates() { const SettingsPtr s = settings(); Profile buildProfile(profileName(), s.get()); - QStringList toolchain = buildProfile.value("qbs.toolchain").toStringList(); + QStringList toolchain = profileToolchain(buildProfile); if (!toolchain.contains("gcc")) QSKIP("linkerLibraryDuplicates test only applies to GCC toolchain"); @@ -4629,7 +4904,7 @@ void TestBlackbox::linkerScripts() { const SettingsPtr s = settings(); Profile buildProfile(profileName(), s.get()); - QStringList toolchain = buildProfile.value("qbs.toolchain").toStringList(); + QStringList toolchain = profileToolchain(buildProfile); if (!toolchain.contains("gcc") || targetOs() != HostOsInfo::HostOsLinux) QSKIP("linker script test only applies to Linux "); @@ -4743,9 +5018,10 @@ void TestBlackbox::listPropertiesWithOuter() void TestBlackbox::listPropertyOrder() { QDir::setCurrent(testDataDir + "/list-property-order"); - const QbsRunParameters params(QStringList() << "-qq"); + const QbsRunParameters params(QStringList() << "-q"); QCOMPARE(runQbs(params), 0); const QByteArray firstOutput = m_qbsStderr; + QVERIFY(firstOutput.contains("listProp = [\"product\",\"higher3\",\"higher2\",\"higher1\",\"lower\"]")); for (int i = 0; i < 25; ++i) { rmDirR(relativeBuildDir()); QCOMPARE(runQbs(params), 0); @@ -4987,6 +5263,8 @@ void TestBlackbox::propertyPrecedence() // Case 1: [cmdline=0,prod=0,export=0,nonleaf=0,profile=0] QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); + QVERIFY2(m_qbsStdout.contains("scalar prop: leaf\n") && m_qbsStdout.contains("list prop: [\"leaf\"]\n"), m_qbsStdout.constData()); @@ -4995,6 +5273,8 @@ void TestBlackbox::propertyPrecedence() // Case 2: [cmdline=0,prod=0,export=0,nonleaf=0,profile=1] switchProfileContents(profile.p, s.get(), true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); + QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: profile\n") && m_qbsStdout.contains("list prop: [\"profile\"]\n"), @@ -5006,6 +5286,7 @@ void TestBlackbox::propertyPrecedence() switchProfileContents(profile.p, s.get(), false); switchFileContents(nonleafFile, true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: nonleaf\n") && m_qbsStdout.contains("list prop: [\"nonleaf\",\"leaf\"]\n"), @@ -5014,6 +5295,7 @@ void TestBlackbox::propertyPrecedence() // Case 4: [cmdline=0,prod=0,export=0,nonleaf=1,profile=1] switchProfileContents(profile.p, s.get(), true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: nonleaf\n") && m_qbsStdout.contains("list prop: [\"nonleaf\",\"profile\"]\n"), @@ -5026,6 +5308,7 @@ void TestBlackbox::propertyPrecedence() switchFileContents(nonleafFile, false); switchFileContents(depFile, true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: export\n") && m_qbsStdout.contains("list prop: [\"export\",\"leaf\"]\n"), @@ -5034,15 +5317,21 @@ void TestBlackbox::propertyPrecedence() // Case 6: [cmdline=0,prod=0,export=1,nonleaf=0,profile=1] switchProfileContents(profile.p, s.get(), true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: export\n") && m_qbsStdout.contains("list prop: [\"export\",\"profile\"]\n"), m_qbsStdout.constData()); + // Case 7: [cmdline=0,prod=0,export=1,nonleaf=1,profile=0] switchProfileContents(profile.p, s.get(), false); switchFileContents(nonleafFile, true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.contains("WARNING: Conflicting scalar values at") + && m_qbsStderr.contains("nonleaf.qbs:4:22") + && m_qbsStderr.contains("dep.qbs:6:26"), + m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: export\n") && m_qbsStdout.contains("list prop: [\"export\",\"nonleaf\",\"leaf\"]\n"), @@ -5051,6 +5340,10 @@ void TestBlackbox::propertyPrecedence() // Case 8: [cmdline=0,prod=0,export=1,nonleaf=1,profile=1] switchProfileContents(profile.p, s.get(), true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.contains("WARNING: Conflicting scalar values at") + && m_qbsStderr.contains("nonleaf.qbs:4:22") + && m_qbsStderr.contains("dep.qbs:6:26"), + m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: export\n") && m_qbsStdout.contains("list prop: [\"export\",\"nonleaf\",\"profile\"]\n"), @@ -5064,6 +5357,7 @@ void TestBlackbox::propertyPrecedence() switchFileContents(depFile, false); switchFileContents(productFile, true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") && m_qbsStdout.contains("list prop: [\"product\",\"leaf\"]\n"), @@ -5072,6 +5366,7 @@ void TestBlackbox::propertyPrecedence() // Case 10: [cmdline=0,prod=1,export=0,nonleaf=0,profile=1] switchProfileContents(profile.p, s.get(), true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") && m_qbsStdout.contains("list prop: [\"product\",\"profile\"]\n"), @@ -5081,6 +5376,7 @@ void TestBlackbox::propertyPrecedence() switchProfileContents(profile.p, s.get(), false); switchFileContents(nonleafFile, true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") && m_qbsStdout.contains("list prop: [\"product\",\"nonleaf\",\"leaf\"]\n"), @@ -5089,6 +5385,7 @@ void TestBlackbox::propertyPrecedence() // Case 12: [cmdline=0,prod=1,export=0,nonleaf=1,profile=1] switchProfileContents(profile.p, s.get(), true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") && m_qbsStdout.contains("list prop: [\"product\",\"nonleaf\",\"profile\"]\n"), @@ -5099,6 +5396,7 @@ void TestBlackbox::propertyPrecedence() switchFileContents(nonleafFile, false); switchFileContents(depFile, true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") && m_qbsStdout.contains("list prop: [\"product\",\"export\",\"leaf\"]\n"), @@ -5107,6 +5405,7 @@ void TestBlackbox::propertyPrecedence() // Case 14: [cmdline=0,prod=1,export=1,nonleaf=0,profile=1] switchProfileContents(profile.p, s.get(), true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") && m_qbsStdout.contains("list prop: [\"product\",\"export\",\"profile\"]\n"), @@ -5116,6 +5415,7 @@ void TestBlackbox::propertyPrecedence() switchProfileContents(profile.p, s.get(), false); switchFileContents(nonleafFile, true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") && m_qbsStdout.contains("list prop: [\"product\",\"export\",\"nonleaf\",\"leaf\"]\n"), @@ -5124,6 +5424,7 @@ void TestBlackbox::propertyPrecedence() // Case 16: [cmdline=0,prod=1,export=1,nonleaf=1,profile=1] switchProfileContents(profile.p, s.get(), true); QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") && m_qbsStdout.contains("list prop: [\"product\",\"export\",\"nonleaf\",\"profile\"]\n"), @@ -5137,6 +5438,7 @@ void TestBlackbox::propertyPrecedence() switchFileContents(productFile, false); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5146,6 +5448,7 @@ void TestBlackbox::propertyPrecedence() switchProfileContents(profile.p, s.get(), true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5156,6 +5459,7 @@ void TestBlackbox::propertyPrecedence() switchFileContents(nonleafFile, true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5165,6 +5469,7 @@ void TestBlackbox::propertyPrecedence() switchProfileContents(profile.p, s.get(), true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5176,6 +5481,7 @@ void TestBlackbox::propertyPrecedence() switchFileContents(depFile, true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5185,6 +5491,7 @@ void TestBlackbox::propertyPrecedence() switchProfileContents(profile.p, s.get(), true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5195,6 +5502,7 @@ void TestBlackbox::propertyPrecedence() switchFileContents(nonleafFile, true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5204,6 +5512,7 @@ void TestBlackbox::propertyPrecedence() switchProfileContents(profile.p, s.get(), true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5216,6 +5525,7 @@ void TestBlackbox::propertyPrecedence() switchFileContents(productFile, true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5225,6 +5535,7 @@ void TestBlackbox::propertyPrecedence() switchProfileContents(profile.p, s.get(), true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5235,6 +5546,7 @@ void TestBlackbox::propertyPrecedence() switchFileContents(nonleafFile, true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5244,6 +5556,7 @@ void TestBlackbox::propertyPrecedence() switchProfileContents(profile.p, s.get(), true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5255,6 +5568,7 @@ void TestBlackbox::propertyPrecedence() switchFileContents(depFile, true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5264,6 +5578,7 @@ void TestBlackbox::propertyPrecedence() switchProfileContents(profile.p, s.get(), true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5274,6 +5589,7 @@ void TestBlackbox::propertyPrecedence() switchFileContents(nonleafFile, true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5283,6 +5599,7 @@ void TestBlackbox::propertyPrecedence() switchProfileContents(profile.p, s.get(), true); resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline"; QCOMPARE(runQbs(resolveParams), 0); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0); QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), @@ -5563,8 +5880,8 @@ void TestBlackbox::qbsSession() // Wait for and verify hello packet. QJsonObject receivedMessage = getNextSessionPacket(sessionProc, incomingData); QCOMPARE(receivedMessage.value("type"), "hello"); - QCOMPARE(receivedMessage.value("api-level").toInt(), 1); - QCOMPARE(receivedMessage.value("api-compat-level").toInt(), 1); + QCOMPARE(receivedMessage.value("api-level").toInt(), 2); + QCOMPARE(receivedMessage.value("api-compat-level").toInt(), 2); // Resolve & verify structure QJsonObject resolveMessage; @@ -5622,7 +5939,7 @@ void TestBlackbox::qbsSession() const QJsonObject group = v.toObject(); const QJsonArray sourceArtifacts = group.value("source-artifacts").toArray(); - const auto findArtifact = [&sourceArtifacts](const QString fileName) { + const auto findArtifact = [&sourceArtifacts](const QString &fileName) { for (const QJsonValue &v : sourceArtifacts) { const QJsonObject artifact = v.toObject(); if (QFileInfo(artifact.value("file-path").toString()).fileName() @@ -5892,34 +6209,51 @@ void TestBlackbox::qbsSession() qDebug() << error; } QVERIFY(error.isEmpty()); - QJsonObject projectData = receivedMessage.value("project-data").toObject(); - QJsonArray products = projectData.value("products").toArray(); - bool file1 = false; - bool file2 = false; - for (const QJsonValue &v : products) { - const QJsonObject product = v.toObject(); - const QString productName = product.value("full-display-name").toString(); - const QJsonArray groups = product.value("groups").toArray(); - for (const QJsonValue &v : groups) { - const QJsonObject group = v.toObject(); - const QString groupName = group.value("name").toString(); - const QJsonArray sourceArtifacts = group.value("source-artifacts").toArray(); - for (const QJsonValue &v : sourceArtifacts) { - const QString filePath = v.toObject().value("file-path").toString(); - if (filePath.endsWith("file1.cpp")) { - QCOMPARE(productName, QString("theLib")); - QCOMPARE(groupName, QString("sources")); - file1 = true; - } else if (filePath.endsWith("file2.cpp")) { - QCOMPARE(productName, QString("theLib")); - QCOMPARE(groupName, QString("sources")); - file2 = true; + + receivedReply = false; + sendPacket(resolveMessage); + while (!receivedReply) { + receivedMessage = getNextSessionPacket(sessionProc, incomingData); + QVERIFY(!receivedMessage.isEmpty()); + const QString msgType = receivedMessage.value("type").toString(); + if (msgType == "project-resolved") { + receivedReply = true; + const QJsonObject error = receivedMessage.value("error").toObject(); + if (!error.isEmpty()) + qDebug() << error; + QVERIFY(error.isEmpty()); + const QJsonObject projectData = receivedMessage.value("project-data").toObject(); + QJsonArray products = projectData.value("products").toArray(); + bool file1 = false; + bool file2 = false; + for (const QJsonValue &v : products) { + const QJsonObject product = v.toObject(); + const QString productName = product.value("full-display-name").toString(); + const QJsonArray groups = product.value("groups").toArray(); + for (const QJsonValue &v : groups) { + const QJsonObject group = v.toObject(); + const QString groupName = group.value("name").toString(); + const QJsonArray sourceArtifacts = group.value("source-artifacts").toArray(); + for (const QJsonValue &v : sourceArtifacts) { + const QString filePath = v.toObject().value("file-path").toString(); + if (filePath.endsWith("file1.cpp")) { + QCOMPARE(productName, QString("theLib")); + QCOMPARE(groupName, QString("sources")); + file1 = true; + } else if (filePath.endsWith("file2.cpp")) { + QCOMPARE(productName, QString("theLib")); + QCOMPARE(groupName, QString("sources")); + file2 = true; + } + } } } + QVERIFY(file1); + QVERIFY(file2); } } - QVERIFY(file1); - QVERIFY(file2); + QVERIFY(receivedReply); + receivedReply = false; receivedProcessResult = false; bool compiledFile1 = false; @@ -5978,32 +6312,48 @@ void TestBlackbox::qbsSession() if (!error.isEmpty()) qDebug() << error; QVERIFY(error.isEmpty()); - projectData = receivedMessage.value("project-data").toObject(); - products = projectData.value("products").toArray(); - file1 = false; - file2 = false; - for (const QJsonValue &v : products) { - const QJsonObject product = v.toObject(); - const QString productName = product.value("full-display-name").toString(); - const QJsonArray groups = product.value("groups").toArray(); - for (const QJsonValue &v : groups) { - const QJsonObject group = v.toObject(); - const QString groupName = group.value("name").toString(); - const QJsonArray sourceArtifacts = group.value("source-artifacts").toArray(); - for (const QJsonValue &v : sourceArtifacts) { - const QString filePath = v.toObject().value("file-path").toString(); - if (filePath.endsWith("file1.cpp")) { - file1 = true; - } else if (filePath.endsWith("file2.cpp")) { - QCOMPARE(productName, QString("theLib")); - QCOMPARE(groupName, QString("sources")); - file2 = true; + receivedReply = false; + sendPacket(resolveMessage); + while (!receivedReply) { + receivedMessage = getNextSessionPacket(sessionProc, incomingData); + QVERIFY(!receivedMessage.isEmpty()); + const QString msgType = receivedMessage.value("type").toString(); + if (msgType == "project-resolved") { + receivedReply = true; + const QJsonObject error = receivedMessage.value("error").toObject(); + if (!error.isEmpty()) + qDebug() << error; + QVERIFY(error.isEmpty()); + const QJsonObject projectData = receivedMessage.value("project-data").toObject(); + QJsonArray products = projectData.value("products").toArray(); + bool file1 = false; + bool file2 = false; + for (const QJsonValue &v : products) { + const QJsonObject product = v.toObject(); + const QString productName = product.value("full-display-name").toString(); + const QJsonArray groups = product.value("groups").toArray(); + for (const QJsonValue &v : groups) { + const QJsonObject group = v.toObject(); + const QString groupName = group.value("name").toString(); + const QJsonArray sourceArtifacts = group.value("source-artifacts").toArray(); + for (const QJsonValue &v : sourceArtifacts) { + const QString filePath = v.toObject().value("file-path").toString(); + if (filePath.endsWith("file1.cpp")) { + file1 = true; + } else if (filePath.endsWith("file2.cpp")) { + QCOMPARE(productName, QString("theLib")); + QCOMPARE(groupName, QString("sources")); + file2 = true; + } + } } } + QVERIFY(!file1); + QVERIFY(file2); } } - QVERIFY(!file1); - QVERIFY(file2); + QVERIFY(receivedReply); + receivedReply = false; receivedProcessResult = false; compiledFile1 = false; @@ -7012,6 +7362,12 @@ void TestBlackbox::typescript() QVERIFY(regularFileExists(relativeProductBuildDir("animals") + "/main.js")); } +void TestBlackbox::undefinedTargetPlatform() +{ + QDir::setCurrent(testDataDir + "/undefined-target-platform"); + QCOMPARE(runQbs(), 0); +} + void TestBlackbox::importInPropertiesCondition() { QDir::setCurrent(testDataDir + "/import-in-properties-condition"); @@ -7776,6 +8132,17 @@ void TestBlackbox::grpc() QCOMPARE(runQbs(runParams), 0); } +void TestBlackbox::hostOsProperties() +{ + QDir::setCurrent(testDataDir + "/host-os-properties"); + QCOMPARE(runQbs(QStringLiteral("run")), 0); + QVERIFY2(m_qbsStdout.contains( + ("HOST_ARCHITECTURE = " + HostOsInfo::hostOSArchitecture()).data()), + m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains(("HOST_PLATFORM = " + HostOsInfo::hostOSIdentifier()).data()), + m_qbsStdout.constData()); +} + void TestBlackbox::ico() { QDir::setCurrent(testDataDir + "/ico"); diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h index 4e2755724..757462b80 100644 --- a/tests/auto/blackbox/tst_blackbox.h +++ b/tests/auto/blackbox/tst_blackbox.h @@ -60,6 +60,8 @@ private slots: void buildDirectories(); void buildEnvChange(); void buildGraphVersions(); + void buildVariantDefaults_data(); + void buildVariantDefaults(); void changedFiles_data(); void changedFiles(); void changedInputsFromDependencies(); @@ -83,9 +85,10 @@ private slots: void conflictingArtifacts(); void cxxLanguageVersion(); void cxxLanguageVersion_data(); + void conanfileProbe(); void cpuFeatures(); void dependenciesProperty(); - void dependencyProfileMismatch(); + void dependencyScanningLoop(); void deprecatedProperty(); void disappearedProfile(); void discardUnusedData(); @@ -96,6 +99,7 @@ private slots: void dynamicMultiplexRule(); void dynamicProject(); void dynamicRuleOutputs(); + void emptyProfile(); void enableExceptions(); void enableExceptions_data(); void enableRtti(); @@ -118,6 +122,7 @@ private slots: void externalLibs(); void fileDependencies(); void fileTagsFilterMerging(); + void freedesktop(); void generatedArtifactAsInputToDynamicRule(); void generateLinkerMapFile(); void generator(); @@ -125,6 +130,7 @@ private slots: void groupsInModules(); void grpc_data(); void grpc(); + void hostOsProperties(); void ico(); void importAssignment(); void importChangeTracking(); @@ -164,6 +170,7 @@ private slots: void jsExtensionsTemporaryDir(); void jsExtensionsTextFile(); void jsExtensionsBinaryFile(); + void lastModuleCandidateBroken(); void ld(); void linkerMode(); void linkerVariant_data(); @@ -236,6 +243,7 @@ private slots: void propertyAssignmentOnNonPresentModule(); void propertyAssignmentInFailedModule(); void propertyChanges(); + void propertyEvaluationContext(); void propertyPrecedence(); void properQuoting(); void propertiesInExportItems(); @@ -265,7 +273,11 @@ private slots: void ruleCycle(); void ruleWithNoInputs(); void ruleWithNonRequiredInputs(); + void sanitizer_data(); + void sanitizer(); void scannerItem(); + void scanResultInOtherProduct(); + void scanResultInNonDependency(); void setupBuildEnvironment(); void setupRunEnvironment(); void smartRelinking(); @@ -301,6 +313,7 @@ private slots: void trackRemoveProduct(); void transitiveOptionalDependencies(); void typescript(); + void undefinedTargetPlatform(); void usingsAsSoleInputsNonMultiplexed(); void variantSuffix(); void variantSuffix_data(); diff --git a/tests/auto/blackbox/tst_blackboxandroid.cpp b/tests/auto/blackbox/tst_blackboxandroid.cpp index e312c4493..eb0303a07 100644 --- a/tests/auto/blackbox/tst_blackboxandroid.cpp +++ b/tests/auto/blackbox/tst_blackboxandroid.cpp @@ -79,7 +79,7 @@ void TestBlackboxAndroid::android() QFETCH(QString, projectDir); QFETCH(QStringList, productNames); QFETCH(QList<QByteArrayList>, expectedFilesLists); - QFETCH(QStringList, customProperties); + QFETCH(QStringList, qmlAppCustomProperties); const SettingsPtr s = settings(); Profile p(theProfileName(projectDir == "qml-app"), s.get()); @@ -110,7 +110,7 @@ void TestBlackboxAndroid::android() auto currentExpectedFilesLists = expectedFilesLists; const QString configArgument = "config:" + configName; QbsRunParameters resolveParams("resolve"); - resolveParams.arguments << configArgument << customProperties; + resolveParams.arguments << configArgument << qmlAppCustomProperties; resolveParams.profile = p.name(); QCOMPARE(runQbs(resolveParams), 0); QbsRunParameters buildParams(QStringList{"--command-echo-mode", "command-line", @@ -206,14 +206,41 @@ void TestBlackboxAndroid::android_data() .toString() == "clang"; return QByteArray("lib/${ARCH}/") + (usesClang ? "libc++_shared.so" : oldcxxLib); }; - const QByteArrayList archsForQt = { pQt.value("qbs.architecture").toString().toUtf8() }; - QByteArrayList ndkArchsForQt = archsForQt; - if (ndkArchsForQt.first() == "armv7a") - ndkArchsForQt.first() = "armeabi-v7a"; - else if (ndkArchsForQt.first() == "armv5te") - ndkArchsForQt.first() = "armeabi"; - else if (ndkArchsForQt.first() == "arm64") - ndkArchsForQt.first() = "arm64-v8a"; + + bool usingOldQt = true; + QStringList qmakeFilePaths = pQt.value(QStringLiteral("moduleProviders.Qt.qmakeFilePaths")). + toStringList(); + if (qmakeFilePaths.size() == 1) { + qbs::Version version = TestBlackboxBase::qmakeVersion(qmakeFilePaths[0]); + if (version.isValid() && version >= qbs::Version(5, 14)) + usingOldQt = false; + } + + QByteArrayList archsForQt; + if (usingOldQt) { + archsForQt = { pQt.value("qbs.architecture").toString().toUtf8() }; + if (archsStringList.empty()) + archsStringList << QStringLiteral("armv7a"); // must match default in common.qbs + } else { + QStringList archsForQtStringList = pQt.value(QStringLiteral("qbs.architectures")) + .toStringList(); + if (archsForQtStringList.empty()) + archsForQtStringList << pQt.value("qbs.architecture").toString(); + std::transform(archsForQtStringList.begin(), + archsForQtStringList.end(), + std::back_inserter(archsForQt), + [] (const QString &s) { + return s.toUtf8(); + }); + } + + QByteArrayList ndkArchsForQt; + std::transform(archsForQt.begin(), archsForQt.end(), std::back_inserter(ndkArchsForQt), + [] (const QString &s) { + return s.toUtf8().replace("armv7a", "armeabi-v7a") + .replace("armv5te", "armeabi") + .replace("arm64", "arm64-v8a"); + }); auto expandArchs = [] (const QByteArrayList &archs, const QByteArrayList &lst) { const QByteArray &archPlaceHolder = "${ARCH}"; @@ -237,7 +264,7 @@ void TestBlackboxAndroid::android_data() QTest::addColumn<QString>("projectDir"); QTest::addColumn<QStringList>("productNames"); QTest::addColumn<QList<QByteArrayList>>("expectedFilesLists"); - QTest::addColumn<QStringList>("customProperties"); + QTest::addColumn<QStringList>("qmlAppCustomProperties"); QTest::newRow("teapot") << "teapot" << QStringList("TeapotNativeActivity") << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, { @@ -257,9 +284,12 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libdependency.so"})) << QStringList{"products.minimalnative.multiplexByQbsProperties:[]", "modules.qbs.architecture:" + archsStringList.first()}; - QTest::newRow("qml app") - << "qml-app" << QStringList("qmlapp") - << (QList<QByteArrayList>() << commonFiles + expandArchs(ndkArchsForQt, { + QByteArrayList qmlAppExpectedFiles; + QByteArrayList qmlAppMinistroExpectedFiles; + QByteArrayList qmlAppCustomMetaDataExpectedFiles; + QStringList qmlAppCustomProperties; + if (usingOldQt) { + qmlAppExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { "resources.arsc", "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes", "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/qmldir", @@ -298,64 +328,157 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libQt5QuickParticles.so", "lib/${ARCH}/libQt5Quick.so", "lib/${ARCH}/libqmlapp.so", - "res/layout/splash.xml"})) - << QStringList{"modules.Android.sdk.automaticSources:false", - "modules.qbs.architecture:" + archsForQt.first()}; - QTest::newRow("qml app using Ministro") - << "qml-app" << QStringList("qmlapp") - << (QList<QByteArrayList>() << commonFiles + expandArchs(ndkArchsForQt, { + "res/layout/splash.xml"}); + qmlAppMinistroExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { + "resources.arsc", + "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list", + "lib/${ARCH}/libgdbserver.so", + cxxLibPath("libgnustl_shared.so", true), + "lib/${ARCH}/libqmlapp.so", + "res/layout/splash.xml"}); + qmlAppCustomMetaDataExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { + "resources.arsc", + "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes", + "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/qmldir", + "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/plugins.qmltypes", + "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/qmldir", + "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list", + "assets/dummyasset.txt", + "lib/${ARCH}/libgdbserver.so", + cxxLibPath("libgnustl_shared.so", true), + "lib/${ARCH}/libplugins_bearer_libqandroidbearer.so", + "lib/${ARCH}/libplugins_imageformats_libqgif.so", + "lib/${ARCH}/libplugins_imageformats_libqicns.so", + "lib/${ARCH}/libplugins_imageformats_libqico.so", + "lib/${ARCH}/libplugins_imageformats_libqjpeg.so", + "lib/${ARCH}/libplugins_imageformats_libqtga.so", + "lib/${ARCH}/libplugins_imageformats_libqtiff.so", + "lib/${ARCH}/libplugins_imageformats_libqwbmp.so", + "lib/${ARCH}/libplugins_imageformats_libqwebp.so", + "lib/${ARCH}/libplugins_platforms_android_libqtforandroid.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_debugger.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_inspector.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_local.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_messages.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_native.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_nativedebugger.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_profiler.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_preview.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_quickprofiler.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_server.so", + "lib/${ARCH}/libplugins_qmltooling_libqmldbg_tcp.so", + "lib/${ARCH}/libqml_QtQuick.2_libqtquick2plugin.so", + "lib/${ARCH}/libqml_QtQuick_Window.2_libwindowplugin.so", + "lib/${ARCH}/libQt5Core.so", + "lib/${ARCH}/libQt5Gui.so", + "lib/${ARCH}/libQt5Network.so", + "lib/${ARCH}/libQt5Qml.so", + "lib/${ARCH}/libQt5QuickParticles.so", + "lib/${ARCH}/libQt5Quick.so", + "lib/${ARCH}/libqmlapp.so", + "res/layout/splash.xml"}); + qmlAppCustomProperties = QStringList{"modules.Android.sdk.automaticSources:false", + "modules.qbs.architecture:" + archsForQt.first()}; + } else { + qmlAppExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { "resources.arsc", - "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list", + "assets/android_rcc_bundle.rcc", "lib/${ARCH}/libgdbserver.so", cxxLibPath("libgnustl_shared.so", true), - "lib/${ARCH}/libqmlapp.so", - "res/layout/splash.xml"})) + "lib/${ARCH}/libplugins_bearer_qandroidbearer_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qgif_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qicns_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qico_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qjpeg_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qtga_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qtiff_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qwbmp_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qwebp_${ARCH}.so", + "lib/${ARCH}/libplugins_platforms_qtforandroid_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_debugger_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_inspector_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_local_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_messages_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_native_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_nativedebugger_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_profiler_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_preview_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_quickprofiler_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_server_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_tcp_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick.2_qtquick2plugin_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick_Window.2_windowplugin_${ARCH}.so", + "lib/${ARCH}/libQt5Core_${ARCH}.so", + "lib/${ARCH}/libQt5Gui_${ARCH}.so", + "lib/${ARCH}/libQt5Network_${ARCH}.so", + "lib/${ARCH}/libQt5Qml_${ARCH}.so", + "lib/${ARCH}/libQt5QuickParticles_${ARCH}.so", + "lib/${ARCH}/libQt5Quick_${ARCH}.so", + "lib/${ARCH}/libQt5QmlModels_${ARCH}.so", + "lib/${ARCH}/libQt5QmlWorkerScript_${ARCH}.so", + "lib/${ARCH}/libqmlapp_${ARCH}.so", + "res/layout/splash.xml"}); + qmlAppMinistroExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { + "resources.arsc", + "assets/android_rcc_bundle.rcc", + "lib/${ARCH}/libgdbserver.so", + cxxLibPath("libgnustl_shared.so", true), + "lib/${ARCH}/libqmlapp_${ARCH}.so", + "res/layout/splash.xml"}); + qmlAppCustomMetaDataExpectedFiles << commonFiles + expandArchs(ndkArchsForQt, { + "resources.arsc", + "assets/android_rcc_bundle.rcc", + "assets/dummyasset.txt", + "lib/${ARCH}/libgdbserver.so", + cxxLibPath("libgnustl_shared.so", true), + "lib/${ARCH}/libplugins_bearer_qandroidbearer_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qgif_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qicns_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qico_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qjpeg_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qtga_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qtiff_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qwbmp_${ARCH}.so", + "lib/${ARCH}/libplugins_imageformats_qwebp_${ARCH}.so", + "lib/${ARCH}/libplugins_platforms_qtforandroid_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_debugger_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_inspector_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_local_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_messages_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_native_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_nativedebugger_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_profiler_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_preview_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_quickprofiler_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_server_${ARCH}.so", + "lib/${ARCH}/libplugins_qmltooling_qmldbg_tcp_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick.2_qtquick2plugin_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick_Window.2_windowplugin_${ARCH}.so", + "lib/${ARCH}/libQt5Core_${ARCH}.so", + "lib/${ARCH}/libQt5Gui_${ARCH}.so", + "lib/${ARCH}/libQt5Network_${ARCH}.so", + "lib/${ARCH}/libQt5Qml_${ARCH}.so", + "lib/${ARCH}/libQt5QuickParticles_${ARCH}.so", + "lib/${ARCH}/libQt5Quick_${ARCH}.so", + "lib/${ARCH}/libQt5QmlModels_${ARCH}.so", + "lib/${ARCH}/libQt5QmlWorkerScript_${ARCH}.so", + "lib/${ARCH}/libqmlapp_${ARCH}.so", + "res/layout/splash.xml"}); + qmlAppCustomProperties = QStringList{"modules.Android.sdk.automaticSources:false"}; + } + QTest::newRow("qml app") + << "qml-app" << QStringList("qmlapp") + << (QList<QByteArrayList>() << qmlAppExpectedFiles) + << qmlAppCustomProperties; + QTest::newRow("qml app using Ministro") + << "qml-app" << QStringList("qmlapp") + << (QList<QByteArrayList>() << qmlAppMinistroExpectedFiles) << QStringList{"modules.Qt.android_support.useMinistro:true", "modules.Android.sdk.automaticSources:false"}; QTest::newRow("qml app with custom metadata") << "qml-app" << QStringList("qmlapp") - << (QList<QByteArrayList>() << commonFiles + expandArchs(ndkArchsForQt, { - "resources.arsc", - "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes", - "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/qmldir", - "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/plugins.qmltypes", - "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/qmldir", - "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list", - "assets/dummyasset.txt", - "lib/${ARCH}/libgdbserver.so", - cxxLibPath("libgnustl_shared.so", true), - "lib/${ARCH}/libplugins_bearer_libqandroidbearer.so", - "lib/${ARCH}/libplugins_imageformats_libqgif.so", - "lib/${ARCH}/libplugins_imageformats_libqicns.so", - "lib/${ARCH}/libplugins_imageformats_libqico.so", - "lib/${ARCH}/libplugins_imageformats_libqjpeg.so", - "lib/${ARCH}/libplugins_imageformats_libqtga.so", - "lib/${ARCH}/libplugins_imageformats_libqtiff.so", - "lib/${ARCH}/libplugins_imageformats_libqwbmp.so", - "lib/${ARCH}/libplugins_imageformats_libqwebp.so", - "lib/${ARCH}/libplugins_platforms_android_libqtforandroid.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_debugger.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_inspector.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_local.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_messages.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_native.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_nativedebugger.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_profiler.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_preview.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_quickprofiler.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_server.so", - "lib/${ARCH}/libplugins_qmltooling_libqmldbg_tcp.so", - "lib/${ARCH}/libqml_QtQuick.2_libqtquick2plugin.so", - "lib/${ARCH}/libqml_QtQuick_Window.2_libwindowplugin.so", - "lib/${ARCH}/libQt5Core.so", - "lib/${ARCH}/libQt5Gui.so", - "lib/${ARCH}/libQt5Network.so", - "lib/${ARCH}/libQt5Qml.so", - "lib/${ARCH}/libQt5QuickParticles.so", - "lib/${ARCH}/libQt5Quick.so", - "lib/${ARCH}/libqmlapp.so", - "res/layout/splash.xml"})) - << QStringList("modules.Android.sdk.automaticSources:true"); + << (QList<QByteArrayList>() << qmlAppCustomMetaDataExpectedFiles) + << QStringList("modules.Android.sdk.automaticSources:true"); QTest::newRow("no native") << "no-native" << QStringList("com.example.android.basicmediadecoder") @@ -390,7 +513,7 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/liblib2.so", cxxLibPath("libstlport_shared.so", false)})) << QStringList(); - QByteArrayList expectedFiles1 = (commonFiles + QByteArrayList expectedFiles1 = qbs::toList(qbs::toSet(commonFiles + expandArchs(QByteArrayList{"armeabi-v7a", "x86"}, { "resources.arsc", "lib/${ARCH}/libgdbserver.so", @@ -400,7 +523,7 @@ void TestBlackboxAndroid::android_data() "resources.arsc", "lib/${ARCH}/libgdbserver.so", "lib/${ARCH}/libp1lib2.so", - cxxLibPath("libstlport_shared.so", false)})).toSet().toList(); + cxxLibPath("libstlport_shared.so", false)}))); QByteArrayList expectedFiles2 = commonFiles + expandArchs(archs, { "lib/${ARCH}/libgdbserver.so", "lib/${ARCH}/libp2lib1.so", diff --git a/tests/auto/blackbox/tst_blackboxapple.cpp b/tests/auto/blackbox/tst_blackboxapple.cpp index cde742f3c..3623dc51b 100644 --- a/tests/auto/blackbox/tst_blackboxapple.cpp +++ b/tests/auto/blackbox/tst_blackboxapple.cpp @@ -60,6 +60,65 @@ static QString getEmbeddedBinaryPlist(const QString &file) return QString::fromUtf8(p.readAllStandardOutput()).trimmed(); } +static QVariantMap readInfoPlistFile(const QString &infoPlistPath) +{ + if (!QFile::exists(infoPlistPath)) { + qWarning() << infoPlistPath << "doesn't exist"; + return {}; + } + + QProcess plutil; + plutil.start("plutil", { + QStringLiteral("-convert"), + QStringLiteral("json"), + infoPlistPath + }); + if (!plutil.waitForStarted()) { + qWarning() << plutil.errorString(); + return {}; + } + if (!plutil.waitForFinished()) { + qWarning() << plutil.errorString(); + return {}; + } + if (plutil.exitCode() != 0) { + qWarning() << plutil.readAllStandardError().constData(); + return {}; + } + + QFile infoPlist(infoPlistPath); + if (!infoPlist.open(QIODevice::ReadOnly)) { + qWarning() << infoPlist.errorString(); + return {}; + } + QJsonParseError error; + const auto json = QJsonDocument::fromJson(infoPlist.readAll(), &error); + if (error.error != QJsonParseError::NoError) { + qWarning() << error.errorString(); + return {}; + } + return json.object().toVariantMap(); +} + +static QString getInfoPlistPath(const QString &bundlePath) +{ + QFileInfo contents(bundlePath + "/Contents"); + if (contents.exists() && contents.isDir()) + return contents.filePath() + "/Info.plist"; // macOS bundle + return bundlePath + "/Info.plist"; +} + +static bool testVariantListType(const QVariant &variant, QMetaType::Type type) +{ + if (variant.userType() != QMetaType::QVariantList) + return false; + for (const auto &value : variant.toList()) { + if (value.userType() != type) + return false; + } + return true; +} + TestBlackboxApple::TestBlackboxApple() : TestBlackboxBase (SRCDIR "/testdata-apple", "blackbox-apple") { @@ -190,8 +249,11 @@ void TestBlackboxApple::appleMultiConfig() void TestBlackboxApple::aggregateDependencyLinking() { - if (HostOsInfo::hostOsVersion() > qbs::Version(10, 13, 4)) - QSKIP("32-bit arch build is no longer supported on macOS versions higher than 10.13.4."); + // XCode 11 produces warning about deprecation of 32-bit apps, so skip the test + // for future XCode versions as well + const auto xcodeVersion = findXcodeVersion(); + if (xcodeVersion >= qbs::Version(11)) + QSKIP("32-bit arch build is no longer supported on macOS higher than 10.13.4."); QDir::setCurrent(testDataDir + "/aggregateDependencyLinking"); QCOMPARE(runQbs(QStringList{"-p", "multi_arch_lib"}), 0); @@ -623,47 +685,47 @@ void TestBlackboxApple::deploymentTarget_data() } QTest::newRow("macos x86_64") << "macosx" << macos << "x86_64" << "-triple x86_64-apple-macosx10.6" - << "-macosx_version_min 10.6"; + << "10.6"; if (xcodeVersion >= qbs::Version(6)) QTest::newRow("macos x86_64h") << "macosx" << macos << "x86_64h" << "-triple x86_64h-apple-macosx10.12" - << "-macosx_version_min 10.12"; + << "10.12"; QTest::newRow("ios armv7a") << "iphoneos" << ios << "armv7a" << "-triple thumbv7-apple-ios6.0" - << "-iphoneos_version_min 6.0"; + << "6.0"; QTest::newRow("ios armv7s") << "iphoneos" <<ios << "armv7s" << "-triple thumbv7s-apple-ios7.0" - << "-iphoneos_version_min 7.0"; + << "7.0"; if (xcodeVersion >= qbs::Version(5)) QTest::newRow("ios arm64") << "iphoneos" <<ios << "arm64" << "-triple arm64-apple-ios7.0" - << "-iphoneos_version_min 7.0"; + << "7.0"; QTest::newRow("ios-simulator x86") << "iphonesimulator" << ios_sim << "x86" << "-triple i386-apple-ios6.0" - << "-ios_simulator_version_min 6.0"; + << "6.0"; if (xcodeVersion >= qbs::Version(5)) QTest::newRow("ios-simulator x86_64") << "iphonesimulator" << ios_sim << "x86_64" << "-triple x86_64-apple-ios7.0" - << "-ios_simulator_version_min 7.0"; + << "7.0"; if (xcodeVersion >= qbs::Version(7)) { if (xcodeVersion >= qbs::Version(7, 1)) { QTest::newRow("tvos arm64") << "appletvos" << tvos << "arm64" << "-triple arm64-apple-tvos9.0" - << "-tvos_version_min 9.0"; + << "9.0"; QTest::newRow("tvos-simulator x86_64") << "appletvsimulator" << tvos_sim << "x86_64" << "-triple x86_64-apple-tvos9.0" - << "-tvos_simulator_version_min 9.0"; + << "9.0"; } QTest::newRow("watchos armv7k") << "watchos" << watchos << "armv7k" << "-triple thumbv7k-apple-watchos2.0" - << "-watchos_version_min 2.0"; + << "2.0"; QTest::newRow("watchos-simulator x86") << "watchsimulator" << watchos_sim << "x86" << "-triple i386-apple-watchos2.0" - << "-watchos_simulator_version_min 2.0"; + << "2.0"; } } @@ -772,37 +834,35 @@ void TestBlackboxApple::infoPlist() params.arguments = QStringList() << "-f" << "infoplist.qbs"; QCOMPARE(runQbs(params), 0); - auto infoplistPath = relativeProductBuildDir("infoplist") - + "/infoplist.app/Contents/Info.plist"; - if (!QFile::exists(infoplistPath)) - infoplistPath = relativeProductBuildDir("infoplist") + "/infoplist.app/Info.plist"; - QVERIFY(QFile::exists(infoplistPath)); - QProcess plutil; - plutil.start("plutil", { - QStringLiteral("-convert"), - QStringLiteral("json"), - infoplistPath - }); - QVERIFY2(plutil.waitForStarted(), qPrintable(plutil.errorString())); - QVERIFY2(plutil.waitForFinished(), qPrintable(plutil.errorString())); - QVERIFY2(plutil.exitCode() == 0, qPrintable(plutil.readAllStandardError().constData())); + const auto infoPlistPath = getInfoPlistPath( + relativeProductBuildDir("infoplist") + "/infoplist.app"); + QVERIFY(QFile::exists(infoPlistPath)); + const auto content = readInfoPlistFile(infoPlistPath); + QVERIFY(!content.isEmpty()); - QFile infoplist(infoplistPath); - QVERIFY(infoplist.open(QIODevice::ReadOnly)); - QJsonParseError error; - const auto json = QJsonDocument::fromJson(infoplist.readAll(), &error); - QCOMPARE(error.error, QJsonParseError::NoError); - QVERIFY(json.isObject()); // common values - QCOMPARE(json.object().value(QStringLiteral("CFBundleIdentifier")), + QCOMPARE(content.value(QStringLiteral("CFBundleIdentifier")), QStringLiteral("org.example.infoplist")); - QCOMPARE(json.object().value(QStringLiteral("CFBundleName")), QStringLiteral("infoplist")); - QCOMPARE(json.object().value(QStringLiteral("CFBundleExecutable")), + QCOMPARE(content.value(QStringLiteral("CFBundleName")), QStringLiteral("infoplist")); + QCOMPARE(content.value(QStringLiteral("CFBundleExecutable")), QStringLiteral("infoplist")); - if (!json.object().contains(QStringLiteral("SDKROOT"))) { // macOS-specific values - QCOMPARE(json.object().value("LSMinimumSystemVersion"), QStringLiteral("10.7")); - QVERIFY(json.object().contains("NSPrincipalClass")); + if (!content.contains(QStringLiteral("SDKROOT"))) { // macOS-specific values + QCOMPARE(content.value("LSMinimumSystemVersion"), QStringLiteral("10.7")); + QCOMPARE(content.value("NSPrincipalClass"), QStringLiteral("NSApplication")); + } else { + // QBS-1447: UIDeviceFamily was set to a string instead of an array + const auto family = content.value(QStringLiteral("UIDeviceFamily")); + if (family.isValid()) { + // int gets converted to a double when exporting plist as JSON + QVERIFY(testVariantListType(family, QMetaType::Double)); + } + const auto caps = content.value(QStringLiteral("UIRequiredDeviceCapabilities")); + if (caps.isValid()) + QVERIFY(testVariantListType(caps, QMetaType::QString)); + const auto orientations = content.value(QStringLiteral("UIRequiredDeviceCapabilities")); + if (orientations.isValid()) + QVERIFY(testVariantListType(orientations, QMetaType::QString)); } } @@ -813,6 +873,31 @@ void TestBlackboxApple::objcArc() QCOMPARE(runQbs(), 0); } +void TestBlackboxApple::overrideInfoPlist() +{ + QDir::setCurrent(testDataDir + "/overrideInfoPlist"); + + QCOMPARE(runQbs(), 0); + + const auto infoPlistPath = getInfoPlistPath( + relativeProductBuildDir("overrideInfoPlist") + "/overrideInfoPlist.app"); + QVERIFY(QFile::exists(infoPlistPath)); + const auto content = readInfoPlistFile(infoPlistPath); + QVERIFY(!content.isEmpty()); + + // test we do not override custom values by default + QCOMPARE(content.value(QStringLiteral("DefaultValue")), + QStringLiteral("The default value")); + // test we can override custom values + QCOMPARE(content.value(QStringLiteral("OverriddenValue")), + QStringLiteral("The overridden value")); + // test we do not override special values set by Qbs by default + QCOMPARE(content.value(QStringLiteral("CFBundleExecutable")), + QStringLiteral("overrideInfoPlist")); + // test we can override special values set by Qbs + QCOMPARE(content.value(QStringLiteral("CFBundleName")), QStringLiteral("My Bundle")); +} + void TestBlackboxApple::xcode() { QProcess xcodeSelect; diff --git a/tests/auto/blackbox/tst_blackboxapple.h b/tests/auto/blackbox/tst_blackboxapple.h index 76711ddf5..be2e5c5b5 100644 --- a/tests/auto/blackbox/tst_blackboxapple.h +++ b/tests/auto/blackbox/tst_blackboxapple.h @@ -63,6 +63,7 @@ private slots: void iconsetApp(); void infoPlist(); void objcArc(); + void overrideInfoPlist(); void xcode(); private: diff --git a/tests/auto/blackbox/tst_blackboxbase.cpp b/tests/auto/blackbox/tst_blackboxbase.cpp index 61b0271f6..90afaabfc 100644 --- a/tests/auto/blackbox/tst_blackboxbase.cpp +++ b/tests/auto/blackbox/tst_blackboxbase.cpp @@ -64,7 +64,7 @@ static bool supportsSettingsDirOption(const QString &command) { TestBlackboxBase::TestBlackboxBase(const QString &testDataSrcDir, const QString &testName) : testDataDir(testWorkDir(testName)), - testSourceDir(QDir::cleanPath(testDataSrcDir)), + testSourceDir(testDataSourceDir(testDataSrcDir)), qbsExecutableFilePath(initQbsExecutableFilePath()), defaultInstallRoot(relativeBuildDir() + QLatin1Char('/') + InstallOptions::defaultInstallRoot()) { @@ -232,3 +232,21 @@ QMap<QString, QString> TestBlackboxBase::findJdkTools(int *status) {"jar", QDir::fromNativeSeparators(tools["jar"].toString())} }; } + +qbs::Version TestBlackboxBase::qmakeVersion(const QString &qmakeFilePath) +{ + QStringList arguments; + arguments << "-query" << "QT_VERSION"; + QProcess qmakeProcess; + qmakeProcess.start(qmakeFilePath, arguments); + if (!qmakeProcess.waitForStarted() || !qmakeProcess.waitForFinished() + || qmakeProcess.exitStatus() != QProcess::NormalExit) { + qDebug() << "qmake '" << qmakeFilePath << "' could not be run."; + return qbs::Version(); + } + QByteArray result = qmakeProcess.readAll().simplified(); + qbs::Version version = qbs::Version::fromString(result); + if (!version.isValid()) + qDebug() << "qmake '" << qmakeFilePath << "' version is not valid."; + return version; +} diff --git a/tests/auto/blackbox/tst_blackboxbase.h b/tests/auto/blackbox/tst_blackboxbase.h index 251f3752d..518cc80d0 100644 --- a/tests/auto/blackbox/tst_blackboxbase.h +++ b/tests/auto/blackbox/tst_blackboxbase.h @@ -42,14 +42,14 @@ public: init(); } - QbsRunParameters(const QString &cmd, const QStringList &args = QStringList()) - : command(cmd), arguments(args) + QbsRunParameters(QString cmd, QStringList args = QStringList()) + : command(std::move(cmd)), arguments(std::move(args)) { init(); } - QbsRunParameters(const QStringList &args) - : arguments(args) + QbsRunParameters(QStringList args) + : arguments(std::move(args)) { init(); } @@ -92,6 +92,7 @@ protected: static void ccp(const QString &sourceDirPath, const QString &targetDirPath); static QString findExecutable(const QStringList &fileNames); QMap<QString, QString> findJdkTools(int *status); + static qbs::Version qmakeVersion(const QString &qmakeFilePath); const QString testDataDir; const QString testSourceDir; diff --git a/tests/auto/blackbox/tst_blackboxjava.cpp b/tests/auto/blackbox/tst_blackboxjava.cpp index f7feb0612..a815a84ff 100644 --- a/tests/auto/blackbox/tst_blackboxjava.cpp +++ b/tests/auto/blackbox/tst_blackboxjava.cpp @@ -119,7 +119,7 @@ void TestBlackboxJava::java() if (process.waitForStarted()) { QVERIFY2(process.waitForFinished(), qPrintable(process.errorString())); const QByteArray stdOut = process.readAllStandardOutput(); - QVERIFY2(stdOut.contains("Class-Path: car_jar.jar random_stuff.jar"), stdOut.constData()); + QVERIFY2(stdOut.contains("Class-Path: random_stuff.jar car_jar.jar"), stdOut.constData()); QVERIFY2(stdOut.contains("Main-Class: Vehicles"), stdOut.constData()); QVERIFY2(stdOut.contains("Some-Property: Some-Value"), stdOut.constData()); QVERIFY2(stdOut.contains("Additional-Property: Additional-Value"), stdOut.constData()); @@ -184,6 +184,7 @@ void TestBlackboxJava::javaDependencyTracking_data() "/usr/lib/jvm/java-" + minorVersion + "-openjdk" + dpkgArch("-"), // Debian "/usr/lib/jvm/java-" + minorVersion + "-openjdk", // Arch "/usr/lib/jvm/jre-1." + minorVersion + ".0-openjdk", // Fedora + "/usr/lib64/jvm/java-1." + minorVersion + ".0-openjdk", // OpenSuSE }; for (const QString &searchPath : searchPaths) { if (QFile::exists(searchPath + "/bin/javac")) diff --git a/tests/auto/blackbox/tst_blackboxqt.cpp b/tests/auto/blackbox/tst_blackboxqt.cpp index 035af959c..595a173a2 100644 --- a/tests/auto/blackbox/tst_blackboxqt.cpp +++ b/tests/auto/blackbox/tst_blackboxqt.cpp @@ -121,7 +121,7 @@ void TestBlackboxQt::combinedMoc() void TestBlackboxQt::createProject() { QDir::setCurrent(testDataDir + "/create-project"); - QVERIFY(QFile::copy(SRCDIR "/../../../examples/helloworld-qt/main.cpp", + QVERIFY(QFile::copy(testSourceDir + "/../../../../examples/helloworld-qt/main.cpp", QDir::currentPath() + "/main.cpp")); QbsRunParameters createParams("create-project"); createParams.profile.clear(); @@ -210,12 +210,48 @@ void TestBlackboxQt::lrelease() QVERIFY(!regularFileExists(relativeProductBuildDir("lrelease-test") + "/hu.qm")); } +void TestBlackboxQt::metaTypes_data() +{ + QTest::addColumn<bool>("generate"); + QTest::addColumn<QString>("installDir"); + QTest::newRow("don't generate") << false << QString(); + QTest::newRow("don't generate with install info") << false << QString("blubb"); + QTest::newRow("generate only") << true << QString(); + QTest::newRow("generate and install") << true << QString("blubb"); +} + +void TestBlackboxQt::metaTypes() +{ + QDir::setCurrent(testDataDir + "/metatypes"); + QFETCH(bool, generate); + QFETCH(QString, installDir); + const QStringList args{"modules.Qt.core.generateMetaTypesFile:" + + QString(generate ? "true" : "false"), + "modules.Qt.core.metaTypesInstallDir:" + installDir, + "-v", "--force-probe-execution"}; + QCOMPARE(runQbs(QbsRunParameters("resolve", args)), 0); + const bool canGenerate = m_qbsStdout.contains("can generate"); + const bool cannotGenerate = m_qbsStdout.contains("cannot generate"); + QVERIFY(canGenerate != cannotGenerate); + const bool expectFiles = generate && canGenerate; + const bool expectInstalledFiles = expectFiles && !installDir.isEmpty(); + QCOMPARE(runQbs(QStringList("--clean-install-root")), 0); + const QString productDir = relativeProductBuildDir("mylib"); + const QString outputDir = productDir + "/qt.headers"; + QVERIFY(!regularFileExists(outputDir + "/moc_unmocableclass.cpp.json")); + QCOMPARE(regularFileExists(outputDir + "/moc_mocableclass1.cpp.json"), expectFiles); + QCOMPARE(regularFileExists(outputDir + "/mocableclass2.moc.json"), expectFiles); + QCOMPARE(regularFileExists(productDir + "/mylib_metatypes.json"), expectFiles); + QCOMPARE(regularFileExists(relativeBuildDir() + "/install-root/some-prefix/" + installDir + + "/mylib_metatypes.json"), expectInstalledFiles); +} + void TestBlackboxQt::mixedBuildVariants() { QDir::setCurrent(testDataDir + "/mixed-build-variants"); const SettingsPtr s = settings(); Profile profile(profileName(), s.get()); - if (profile.value("qbs.toolchain").toStringList().contains("msvc")) { + if (profileToolchain(profile).contains("msvc")) { QbsRunParameters params; params.arguments << "qbs.buildVariant:debug"; params.expectFailure = true; @@ -339,7 +375,7 @@ void TestBlackboxQt::qmlDebugging() QCOMPARE(runQbs(), 0); const SettingsPtr s = settings(); Profile profile(profileName(), s.get()); - if (!profile.value("qbs.toolchain").toStringList().contains("gcc")) + if (!profileToolchain(profile).contains("gcc")) return; QProcess nm; nm.start("nm", QStringList(relativeExecutableFilePath("debuggable-app"))); @@ -360,6 +396,40 @@ void TestBlackboxQt::qobjectInObjectiveCpp() QCOMPARE(runQbs(), 0); } +void TestBlackboxQt::qmlTypeRegistrar_data() +{ + QTest::addColumn<QString>("importName"); + QTest::addColumn<QString>("installDir"); + QTest::newRow("don't generate") << QString() << QString(); + QTest::newRow("don't generate with install info") << QString() << QString("blubb"); + QTest::newRow("generate only") << QString("People") << QString(); + QTest::newRow("generate and install") << QString("People") << QString("blubb"); +} + +void TestBlackboxQt::qmlTypeRegistrar() +{ + QDir::setCurrent(testDataDir + "/qmltyperegistrar"); + QFETCH(QString, importName); + QFETCH(QString, installDir); + rmDirR(relativeBuildDir()); + const QStringList args{"modules.Qt.qml.importName:" + importName, + "modules.Qt.qml.typesInstallDir:" + installDir}; + QCOMPARE(runQbs(QbsRunParameters("resolve", args)), 0); + const bool hasRegistrar = m_qbsStdout.contains("has registrar"); + const bool doesNotHaveRegistrar = m_qbsStdout.contains("does not have registrar"); + QVERIFY(hasRegistrar != doesNotHaveRegistrar); + if (doesNotHaveRegistrar) + QSKIP("Qt version too old"); + QCOMPARE(runQbs(), 0); + const bool enabled = !importName.isEmpty(); + QCOMPARE(m_qbsStdout.contains("running qmltyperegistrar"), enabled); + QCOMPARE(m_qbsStdout.contains("compiling myapp_qmltyperegistrations.cpp"), enabled); + const QString buildDir = relativeProductBuildDir("myapp"); + QCOMPARE(regularFileExists(buildDir + "/myapp.qmltypes"), enabled); + QCOMPARE(regularFileExists(relativeBuildDir() + "/install-root/" + installDir + + "/myapp.qmltypes"), enabled && !installDir.isEmpty()); +} + void TestBlackboxQt::qtKeywords() { QDir::setCurrent(testDataDir + "/qt-keywords"); diff --git a/tests/auto/blackbox/tst_blackboxqt.h b/tests/auto/blackbox/tst_blackboxqt.h index 180f9e0c0..4008b14ca 100644 --- a/tests/auto/blackbox/tst_blackboxqt.h +++ b/tests/auto/blackbox/tst_blackboxqt.h @@ -53,6 +53,8 @@ private slots: void includedMocCpp(); void linkerVariant(); void lrelease(); + void metaTypes_data(); + void metaTypes(); void mixedBuildVariants(); void mocAndCppCombining(); void mocFlags(); @@ -63,6 +65,8 @@ private slots: void pluginSupport(); void qmlDebugging(); void qobjectInObjectiveCpp(); + void qmlTypeRegistrar_data(); + void qmlTypeRegistrar(); void qtKeywords(); void quickCompiler(); void qtScxml(); diff --git a/tests/auto/blackbox/tst_clangdb.cpp b/tests/auto/blackbox/tst_clangdb.cpp index 3a6dd2d92..65e562484 100644 --- a/tests/auto/blackbox/tst_clangdb.cpp +++ b/tests/auto/blackbox/tst_clangdb.cpp @@ -202,9 +202,9 @@ void TestClangDb::checkClangDetectsSourceCodeProblems() // clang-check.exe does not understand MSVC command-line syntax const SettingsPtr s = settings(); qbs::Profile profile(profileName(), s.get()); - if (profile.value("qbs.toolchain").toStringList().contains("msvc")) { + if (profileToolchain(profile).contains("msvc")) { arguments << "-extra-arg-before=--driver-mode=cl"; - } else if (profile.value("qbs.toolchain").toStringList().contains("mingw")) { + } else if (profileToolchain(profile).contains("mingw")) { arguments << "-extra-arg-before=--driver-mode=g++"; } diff --git a/tests/auto/buildgraph/tst_buildgraph.cpp b/tests/auto/buildgraph/tst_buildgraph.cpp index 95314ad20..20f2cc6a5 100644 --- a/tests/auto/buildgraph/tst_buildgraph.cpp +++ b/tests/auto/buildgraph/tst_buildgraph.cpp @@ -51,6 +51,8 @@ #include <QtTest/qtest.h> +#include <memory> + using namespace qbs; using namespace qbs::Internal; @@ -58,7 +60,7 @@ const TopLevelProjectPtr project = TopLevelProject::create(); TestBuildGraph::TestBuildGraph(ILogSink *logSink) : m_logSink(logSink) { - project->buildData.reset(new ProjectBuildData); + project->buildData = std::make_unique<ProjectBuildData>(); } void TestBuildGraph::initTestCase() @@ -84,7 +86,7 @@ ResolvedProductConstPtr TestBuildGraph::productWithDirectCycle() { const ResolvedProductPtr product = ResolvedProduct::create(); product->project = project; - product->buildData.reset(new ProductBuildData); + product->buildData = std::make_unique<ProductBuildData>(); const auto root = new Artifact; root->product = product; const auto child = new Artifact; @@ -101,7 +103,7 @@ ResolvedProductConstPtr TestBuildGraph::productWithLessDirectCycle() { const ResolvedProductPtr product = ResolvedProduct::create(); product->project = project; - product->buildData.reset(new ProductBuildData); + product->buildData = std::make_unique<ProductBuildData>(); const auto root = new Artifact; const auto child = new Artifact; const auto grandchild = new Artifact; @@ -123,7 +125,7 @@ ResolvedProductConstPtr TestBuildGraph::productWithNoCycle() { const ResolvedProductPtr product = ResolvedProduct::create(); product->project = project; - product->buildData.reset(new ProductBuildData); + product->buildData = std::make_unique<ProductBuildData>(); const auto root = new Artifact; const auto root2 = new Artifact; root->product = product; diff --git a/tests/auto/language/testdata/erroneous/ambiguous-multiplex-dependency.qbs b/tests/auto/language/testdata/erroneous/ambiguous-multiplex-dependency.qbs new file mode 100644 index 000000000..6f60b1faf --- /dev/null +++ b/tests/auto/language/testdata/erroneous/ambiguous-multiplex-dependency.qbs @@ -0,0 +1,14 @@ +Project { + Product { + name: "a" + multiplexByQbsProperties: ["architectures", "buildVariants"] + qbs.architectures: ["x86", "arm"] + qbs.buildVariants: ["debug", "release"] + } + Product { + name: "b" + Depends { name: "a" } + multiplexByQbsProperties: ["architectures"] + qbs.architectures: ["x86", "arm"] + } +} diff --git a/tests/auto/language/testdata/erroneous/dependency-profile-mismatch-2.qbs b/tests/auto/language/testdata/erroneous/dependency-profile-mismatch-2.qbs new file mode 100644 index 000000000..d76907a35 --- /dev/null +++ b/tests/auto/language/testdata/erroneous/dependency-profile-mismatch-2.qbs @@ -0,0 +1,17 @@ +Project { + Profile { + name: "profile1" + } + Profile { + name: "profile2" + } + + Product { + name: "dep" + qbs.profiles: ["profile1", "profile2"] + } + Product { + name: "main" + Depends { name: "dep"; profiles: ["profile47"]; } + } +} diff --git a/tests/auto/language/testdata/erroneous/dependency-profile-mismatch.qbs b/tests/auto/language/testdata/erroneous/dependency-profile-mismatch.qbs new file mode 100644 index 000000000..e014fa9b1 --- /dev/null +++ b/tests/auto/language/testdata/erroneous/dependency-profile-mismatch.qbs @@ -0,0 +1,14 @@ +Project { + Profile { + name: "profile1" + } + + Product { + name: "dep" + qbs.profiles: ["profile1"] + } + Product { + name: "main" + Depends { name: "dep"; profiles: ["profile47"]; } + } +} diff --git a/tests/auto/language/tst_language.cpp b/tests/auto/language/tst_language.cpp index 31aebfa3a..25e549d18 100644 --- a/tests/auto/language/tst_language.cpp +++ b/tests/auto/language/tst_language.cpp @@ -79,8 +79,7 @@ using namespace qbs; using namespace qbs::Internal; static QString testDataDir() { - return FileInfo::resolvePath(QStringLiteral(SRCDIR), - QStringLiteral("../../../tests/auto/language/testdata")); + return testDataSourceDir(SRCDIR "/testdata"); } static QString testProject(const char *fileName) { return testDataDir() + QLatin1Char('/') + QLatin1String(fileName); @@ -180,7 +179,7 @@ void TestLanguage::initTestCase() m_engine = ScriptEngine::create(m_logger, EvalContext::PropertyEvaluation, this); loader = new Loader(m_engine, m_logger); loader->setSearchPaths(QStringList() - << QStringLiteral(SRCDIR "/../../../share/qbs")); + << (testDataDir() + "/../../../../share/qbs")); defaultParameters.setTopLevelProfile(profileName()); defaultParameters.setConfigurationName("default"); defaultParameters.expandBuildConfiguration(); @@ -925,9 +924,20 @@ void TestLanguage::erroneousFiles_data() << "original-in-export-item3.qbs:6:9.*Item 'x.y' is not declared. Did you forget " "to add a Depends item"; QTest::newRow("mismatching-multiplex-dependency") - << "mismatching-multiplex-dependency.qbs:7:5.*Dependency from product " - "'b \\{\"architecture\":\"mips\"\\}' to product 'a \\{\"architecture\":\"mips\"\\}'" - " not fulfilled."; + << "mismatching-multiplex-dependency.qbs:9:9.*Dependency from product " + "'b \\{\"architecture\":\"mips\"\\}' to product 'a'" + " not fulfilled. There are no eligible multiplex candidates."; + QTest::newRow("ambiguous-multiplex-dependency") + << "ambiguous-multiplex-dependency.qbs:10:9.*Dependency from product 'b " + "\\{\"architecture\":\"x86\"\\}' to product 'a' is ambiguous. Eligible multiplex " + "candidates: a \\{\"architecture\":\"x86\",\"buildVariant\":\"debug\"\\}, " + "a \\{\"architecture\":\"x86\",\"buildVariant\":\"release\"\\}."; + QTest::newRow("dependency-profile-mismatch") + << "dependency-profile-mismatch.qbs:10:5.*Product 'main' depends on 'dep', " + "which does not exist for the requested profile 'profile47'."; + QTest::newRow("dependency-profile-mismatch-2") + << "dependency-profile-mismatch-2.qbs:15:9 Dependency from product 'main' to " + "product 'dep' not fulfilled. There are no eligible multiplex candidates."; QTest::newRow("duplicate-multiplex-value") << "duplicate-multiplex-value.qbs:3:1.*Duplicate entry 'x86' in qbs.architectures."; QTest::newRow("duplicate-multiplex-value2") @@ -1051,7 +1061,7 @@ void TestLanguage::exports() propertyName = QStringList() << "dummy" << "defines"; propertyValue = product->moduleProperties->property(propertyName); QCOMPARE(propertyValue.toStringList(), - QStringList() << "LIBA" << "LIBB" << "LIBC" << "LIBD"); + QStringList() << "LIBD" << "LIBC" << "LIBA" << "LIBB"); propertyName = QStringList() << "dummy" << "productName"; propertyValue = product->moduleProperties->property(propertyName); QCOMPARE(propertyValue.toString(), QString("libE")); @@ -1730,10 +1740,10 @@ void TestLanguage::moduleProperties_data() QTest::newRow("init") << QString() << QVariant(); QTest::newRow("merge_lists") << "defines" - << QVariant(QStringList() << "THE_PRODUCT" << "QT_CORE" << "QT_GUI" << "QT_NETWORK"); + << QVariant(QStringList() << "THE_PRODUCT" << "QT_NETWORK" << "QT_GUI" << "QT_CORE"); QTest::newRow("merge_lists_and_values") << "defines" - << QVariant(QStringList() << "THE_PRODUCT" << "QT_CORE" << "QT_GUI" << "QT_NETWORK"); + << QVariant(QStringList() << "THE_PRODUCT" << "QT_NETWORK" << "QT_GUI" << "QT_CORE"); QTest::newRow("merge_lists_with_duplicates") << "cxxFlags" << QVariant(QStringList() << "-foo" << "BAR" << "-foo" << "BAZ"); @@ -2469,7 +2479,7 @@ void TestLanguage::projectFileLookup_data() QTest::addColumn<QString>("projectFileOutput"); QTest::addColumn<bool>("failureExpected"); - const QString baseDir = QLatin1String(SRCDIR) + "/testdata"; + const QString baseDir = testDataDir(); const QString multiProjectsDir = baseDir + "/dirwithmultipleprojects"; const QString noProjectsDir = baseDir + "/dirwithnoprojects"; const QString oneProjectDir = baseDir + "/dirwithoneproject"; @@ -3182,6 +3192,7 @@ void TestLanguage::wildcards() QFile projectFile(projectFilePath); QVERIFY(projectFile.open(QIODevice::WriteOnly)); QTextStream s(&projectFile); + using Qt::endl; s << "import qbs.base 1.0" << endl << endl << "Application {" << endl << " name: \"MyProduct\"" << endl; diff --git a/tests/auto/shared.h b/tests/auto/shared.h index 8f85f5d6c..beda8fff3 100644 --- a/tests/auto/shared.h +++ b/tests/auto/shared.h @@ -31,6 +31,7 @@ #include <tools/hostosinfo.h> #include <tools/profile.h> #include <tools/settings.h> +#include <tools/toolchains.h> #include <QtCore/qbytearray.h> #include <QtCore/qcryptographichash.h> @@ -46,6 +47,8 @@ #include <memory> + + #define REPLACE_IN_FILE(filePath, oldContent, newContent) \ do { \ QFile f((filePath)); \ @@ -83,7 +86,7 @@ using SettingsPtr = std::unique_ptr<qbs::Settings>; inline SettingsPtr settings() { const QString settingsDir = QLatin1String(qgetenv("QBS_AUTOTEST_SETTINGS_DIR")); - return SettingsPtr(new qbs::Settings(settingsDir)); + return std::make_unique<qbs::Settings>(settingsDir); } inline QString profileName() @@ -268,14 +271,22 @@ inline void copyFileAndUpdateTimestamp(const QString &source, const QString &tar touch(target); } +inline QStringList profileToolchain(const qbs::Profile &profile) +{ + const auto toolchainType = profile.value(QStringLiteral("qbs.toolchainType")).toString(); + if (!toolchainType.isEmpty()) + return qbs::canonicalToolchain(toolchainType); + return profile.value(QStringLiteral("qbs.toolchain")).toStringList(); +} + inline QString objectFileName(const QString &baseName, const QString &profileName) { const SettingsPtr s = settings(); qbs::Profile profile(profileName, s.get()); - const auto tc = profile.value("qbs.toolchainType").toString(); - const auto tcList = profile.value("qbs.toolchain").toStringList(); - const bool isMsvc = tc == "msvc" || tcList.contains("msvc") - || (tc.isEmpty() && tcList.isEmpty() && qbs::Internal::HostOsInfo::isWindowsHost()); + + const auto tcList = profileToolchain(profile); + const bool isMsvc = tcList.contains("msvc") + || (tcList.isEmpty() && qbs::Internal::HostOsInfo::isWindowsHost()); const QString suffix = isMsvc ? "obj" : "o"; return baseName + '.' + suffix; } @@ -285,6 +296,30 @@ inline QString inputDirHash(const QString &dir) return QCryptographicHash::hash(dir.toLatin1(), QCryptographicHash::Sha1).toHex().left(16); } +inline QString testDataSourceDir(const QString &dir) +{ + QDir result; + QString testSourceRootDirFromEnv = QDir::fromNativeSeparators(qEnvironmentVariable("QBS_TEST_SOURCE_ROOT")); + if (testSourceRootDirFromEnv.isEmpty()) { + result.setPath(dir); + } else { + QDir testSourceRootDir(dir); + while (testSourceRootDir.dirName() != "tests") + testSourceRootDir = QFileInfo(testSourceRootDir, "").dir(); + + QString relativeDataPath = testSourceRootDir.relativeFilePath(dir); + QString absoluteDataPath = QDir(testSourceRootDirFromEnv).absoluteFilePath(relativeDataPath); + result.setPath(absoluteDataPath); + } + + if (!result.exists()) + qFatal("Expected data folder '%s' to be present, but it does not exist. You may set " + "QBS_TEST_SOURCE_ROOT to the 'tests' folder in your qbs repository to configure " + "a custom location.", qPrintable(result.absolutePath())); + + return result.absolutePath(); +} + inline QString testWorkDir(const QString &testName) { QString dir = QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv("QBS_TEST_WORK_ROOT"))); |