diff options
Diffstat (limited to 'tests/auto/language/tst_language.cpp')
-rw-r--r-- | tests/auto/language/tst_language.cpp | 990 |
1 files changed, 626 insertions, 364 deletions
diff --git a/tests/auto/language/tst_language.cpp b/tests/auto/language/tst_language.cpp index 31aebfa3a..6a3344842 100644 --- a/tests/auto/language/tst_language.cpp +++ b/tests/auto/language/tst_language.cpp @@ -43,6 +43,7 @@ #include "../shared.h" +#include <app/shared/logging/consolelogger.h> #include <language/evaluator.h> #include <language/filecontext.h> #include <language/identifiersearch.h> @@ -52,6 +53,7 @@ #include <language/propertymapinternal.h> #include <language/scriptengine.h> #include <language/value.h> +#include <loader/projectresolver.h> #include <parser/qmljslexer_p.h> #include <parser/qmljsparser_p.h> #include <tools/scripttools.h> @@ -64,8 +66,7 @@ #include <tools/settings.h> #include <tools/stlutils.h> -#include "../shared/logging/consolelogger.h" - +#include <QtCore/qjsonobject.h> #include <QtCore/qprocess.h> #include <algorithm> @@ -79,28 +80,43 @@ 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); } +class JSSourceValueCreator +{ + FileContextPtr m_fileContext; + std::vector<std::unique_ptr<QString>> m_strings; +public: + JSSourceValueCreator(const FileContextPtr &fileContext) + : m_fileContext(fileContext) + { + } + + JSSourceValuePtr create(const QString &sourceCode) + { + JSSourceValuePtr value = JSSourceValue::create(); + value->setFile(m_fileContext); + auto str = std::make_unique<QString>(sourceCode); + value->setSourceCode(*str.get()); + m_strings.push_back(std::move(str)); + return value; + } +}; + TestLanguage::TestLanguage(ILogSink *logSink, Settings *settings) : m_logSink(logSink) , m_settings(settings) , m_wildcardsTestDirPath(QDir::tempPath() + QLatin1String("/_wildcards_test_dir_")) { - qsrand(QTime::currentTime().msec()); + m_rand.seed(QTime::currentTime().msec()); qRegisterMetaType<QList<bool> >("QList<bool>"); - defaultParameters.setBuildRoot(m_tempDir.path() + "/buildroot"); - defaultParameters.setPropertyCheckingMode(ErrorHandlingMode::Strict); - defaultParameters.setSettingsDirectory(m_settings->baseDirectory()); } -TestLanguage::~TestLanguage() -{ -} +TestLanguage::~TestLanguage() = default; QHash<QString, ResolvedProductPtr> TestLanguage::productsFromProject(ResolvedProjectPtr project) { @@ -144,8 +160,7 @@ void TestLanguage::handleInitCleanupDataTags(const char *projectFileName, bool * *handled = true; bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject(projectFileName)); - project = loader->loadProject(defaultParameters); + resolveProject(projectFileName); QVERIFY(!!project); } catch (const ErrorInfo &e) { exceptionCaught = true; @@ -160,9 +175,31 @@ void TestLanguage::handleInitCleanupDataTags(const char *projectFileName, bool * } } +TopLevelProjectPtr TestLanguage::resolveProject(const char *relProjectFilePath) +{ + if (relProjectFilePath) + defaultParameters.setProjectFilePath(testProject(relProjectFilePath)); + defaultParameters.expandBuildConfiguration(); + ProjectResolver resolver(defaultParameters, m_engine.get(), m_logger); + return project = resolver.resolve(); +} + void TestLanguage::init() { + // clear caches, otherwise StoredVariantValues may end up being at the same address + // as the destroyed value + m_engine->reset(); m_logSink->setLogLevel(LoggerInfo); + defaultParameters = {}; + defaultParameters.setBuildRoot(m_tempDir.path() + "/buildroot"); + defaultParameters.setPropertyCheckingMode(ErrorHandlingMode::Strict); + defaultParameters.setSettingsDirectory(m_settings->baseDirectory()); + defaultParameters.setTopLevelProfile(profileName()); + defaultParameters.setMaxJobCount(1); + defaultParameters.setConfigurationName("default"); + defaultParameters.setEnvironment(QProcessEnvironment::systemEnvironment()); + defaultParameters.setSearchPaths({SRCDIR "/../../../share/qbs"}); + QVERIFY(m_tempDir.isValid()); } @@ -177,28 +214,16 @@ void TestLanguage::init() void TestLanguage::initTestCase() { m_logger = Logger(m_logSink); - m_engine = ScriptEngine::create(m_logger, EvalContext::PropertyEvaluation, this); - loader = new Loader(m_engine, m_logger); - loader->setSearchPaths(QStringList() - << QStringLiteral(SRCDIR "/../../../share/qbs")); - defaultParameters.setTopLevelProfile(profileName()); - defaultParameters.setConfigurationName("default"); - defaultParameters.expandBuildConfiguration(); - defaultParameters.setEnvironment(QProcessEnvironment::systemEnvironment()); - QVERIFY(QFileInfo(m_wildcardsTestDirPath).isAbsolute()); -} + m_engine = ScriptEngine::create(m_logger, EvalContext::PropertyEvaluation); -void TestLanguage::cleanupTestCase() -{ - delete loader; + QVERIFY(QFileInfo(m_wildcardsTestDirPath).isAbsolute()); } void TestLanguage::additionalProductTypes() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("additional-product-types.qbs")); - project = loader->loadProject(defaultParameters); + resolveProject("additional-product-types.qbs"); QVERIFY(!!project); const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); const ResolvedProductConstPtr product = products.value("p"); @@ -219,8 +244,7 @@ void TestLanguage::baseProperty() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("baseproperty.qbs")); - project = loader->loadProject(defaultParameters); + resolveProject("baseproperty.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); ResolvedProductPtr product = products.value("product1"); @@ -237,10 +261,8 @@ void TestLanguage::baseProperty() void TestLanguage::baseValidation() { - qbs::SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("base-validate/base-validate.qbs")); try { - project = loader->loadProject(params); + resolveProject("base-validate/base-validate.qbs"); QVERIFY2(false, "exception expected"); } catch (const qbs::ErrorInfo &e) { QVERIFY2(e.toString().contains("Parent succeeded, child failed."), @@ -250,11 +272,9 @@ void TestLanguage::baseValidation() void TestLanguage::brokenDependencyCycle() { - qbs::SetupProjectParameters params = defaultParameters; QFETCH(QString, projectFileName); - params.setProjectFilePath(testProject(qPrintable(projectFileName))); try { - project = loader->loadProject(params); + resolveProject(qPrintable(projectFileName)); } catch (const qbs::ErrorInfo &e) { QVERIFY2(false, qPrintable(e.toString())); } @@ -271,12 +291,10 @@ void TestLanguage::buildConfigStringListSyntax() { bool exceptionCaught = false; try { - SetupProjectParameters parameters = defaultParameters; QVariantMap overriddenValues; overriddenValues.insert("project.someStrings", "foo,bar,baz"); - parameters.setOverriddenValues(overriddenValues); - parameters.setProjectFilePath(testProject("buildconfigstringlistsyntax.qbs")); - project = loader->loadProject(parameters); + defaultParameters.setOverriddenValues(overriddenValues); + resolveProject("buildconfigstringlistsyntax.qbs"); QVERIFY(!!project); QCOMPARE(project->projectProperties().value("someStrings").toStringList(), QStringList() << "foo" << "bar" << "baz"); @@ -291,9 +309,7 @@ void TestLanguage::builtinFunctionInSearchPathsProperty() { bool exceptionCaught = false; try { - SetupProjectParameters parameters = defaultParameters; - parameters.setProjectFilePath(testProject("builtinFunctionInSearchPathsProperty.qbs")); - QVERIFY(!!loader->loadProject(parameters)); + QVERIFY(resolveProject("builtinFunctionInSearchPathsProperty.qbs")); } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); @@ -305,11 +321,12 @@ void TestLanguage::chainedProbes() { bool exceptionCaught = false; try { - SetupProjectParameters parameters = defaultParameters; - parameters.setProjectFilePath(testProject("chained-probes/chained-probes.qbs")); - const TopLevelProjectConstPtr project = loader->loadProject(parameters); + resolveProject("chained-probes/chained-probes.qbs"); QVERIFY(!!project); QCOMPARE(project->products.size(), size_t(1)); + const QString prop1Val = project->products.front()->moduleProperties + ->moduleProperty("m", "prop1").toString(); + QCOMPARE(prop1Val, QLatin1String("probe1Val")); const QString prop2Val = project->products.front()->moduleProperties ->moduleProperty("m", "prop2").toString(); QCOMPARE(prop2Val, QLatin1String("probe1Valprobe2Val")); @@ -325,9 +342,7 @@ void TestLanguage::versionCompare() { bool exceptionCaught = false; try { - SetupProjectParameters parameters = defaultParameters; - parameters.setProjectFilePath(testProject("versionCompare.qbs")); - QVERIFY(!!loader->loadProject(parameters)); + QVERIFY(resolveProject("versionCompare.qbs")); } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); @@ -339,8 +354,7 @@ void TestLanguage::canonicalArchitecture() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("canonicalArchitecture.qbs")); - project = loader->loadProject(defaultParameters); + resolveProject("canonicalArchitecture.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); ResolvedProductPtr product = products.value(QStringLiteral("x86")); @@ -356,8 +370,7 @@ void TestLanguage::rfc1034Identifier() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("rfc1034identifier.qbs")); - project = loader->loadProject(defaultParameters); + resolveProject("rfc1034identifier.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); ResolvedProductPtr product = products.value(QStringLiteral("this-has-special-characters-" @@ -370,17 +383,47 @@ void TestLanguage::rfc1034Identifier() QCOMPARE(exceptionCaught, false); } +void TestLanguage::throwThings_data() +{ + QTest::addColumn<QString>("type"); + QTest::addColumn<QString>("result"); + QTest::addRow("bool") << "bool" << "true"; + QTest::addRow("int") << "int" << "43"; + QTest::addRow("string") << "string" << "an error"; + QTest::addRow("list") << "list" << R"([ + "an", + "error" +])"; + QTest::addRow("object") << "object" << R"({ + "reason": "overheating", + "result": "crash" +})"; +} + +void TestLanguage::throwThings() +{ + QFETCH(QString, type); + QFETCH(QString, result); + bool exceptionCaught = false; + try { + defaultParameters.setOverriddenValues({{"project.throwType", type}}); + resolveProject("throw.qbs"); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + QVERIFY2(e.toString().contains(result), qPrintable(e.toString())); + } + QVERIFY(exceptionCaught); +} + void TestLanguage::conditionalDepends() { bool exceptionCaught = false; ResolvedProductPtr product; ResolvedModuleConstPtr dependency; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("conditionaldepends.qbs")); - params.setOverriddenValues({std::make_pair(QString("products." + defaultParameters.setOverriddenValues({std::make_pair(QString("products." "multilevel_module_props_overridden.dummy3.loadDummy"), true)}); - project = loader->loadProject(params); + resolveProject("conditionaldepends.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); @@ -479,17 +522,38 @@ void TestLanguage::conditionalDepends() QCOMPARE(exceptionCaught, false); } +void TestLanguage::convertStringList() +{ + FileContextPtr fileContext = FileContext::create(); + fileContext->setFilePath("/dev/null"); + JSSourceValueCreator sourceValueCreator(fileContext); + ItemPool pool; + Item *scope = Item::create(&pool, ItemType::Scope); + scope->setProperty("x", sourceValueCreator.create("[\"a\", \"b\"]")); + + Evaluator evaluator(m_engine.get()); + auto variantValue = evaluator.variantValue(scope, "x"); + // despite we have a stringList prop, we evaluate it as a QVariantList + QCOMPARE(variantValue.userType(), QMetaType::Type::QVariantList); + // and we have to convert it explicitly +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + variantValue.convert(QMetaType(QMetaType::QStringList)); +#else + variantValue.convert(QMetaType::QStringList); +#endif + QCOMPARE(variantValue.userType(), QMetaType::Type::QStringList); + QCOMPARE(variantValue, QStringList({"a", "b"})); +} + void TestLanguage::delayedError() { QFETCH(bool, productEnabled); try { QFETCH(QString, projectFileName); - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject(projectFileName.toLatin1())); QVariantMap overriddenValues; overriddenValues.insert("project.enableProduct", productEnabled); - params.setOverriddenValues(overriddenValues); - project = loader->loadProject(params); + defaultParameters.setOverriddenValues(overriddenValues); + resolveProject(projectFileName.toLatin1()); QCOMPARE(productEnabled, false); QVERIFY(!!project); QCOMPARE(project->products.size(), size_t(1)); @@ -521,8 +585,6 @@ void TestLanguage::dependencyOnAllProfiles() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("dependencyOnAllProfiles.qbs")); TemporaryProfile p1("p1", m_settings); p1.p.setValue("qbs.architecture", "arch1"); TemporaryProfile p2("p2", m_settings); @@ -530,14 +592,14 @@ void TestLanguage::dependencyOnAllProfiles() QVariantMap overriddenValues; overriddenValues.insert("project.profile1", "p1"); overriddenValues.insert("project.profile2", "p2"); - params.setOverriddenValues(overriddenValues); - project = loader->loadProject(params); + defaultParameters.setOverriddenValues(overriddenValues); + resolveProject("dependencyOnAllProfiles.qbs"); QVERIFY(!!project); QCOMPARE(project->products.size(), size_t(3)); const ResolvedProductConstPtr mainProduct = productsFromProject(project).value("main"); QVERIFY(!!mainProduct); QCOMPARE(mainProduct->dependencies.size(), size_t { 2 }); - for (const ResolvedProductConstPtr &p : mainProduct->dependencies) { + for (const ResolvedProductPtr &p : mainProduct->dependencies) { QCOMPARE(p->name, QLatin1String("dep")); QVERIFY(p->profile() == "p1" || p->profile() == "p2"); } @@ -552,9 +614,7 @@ void TestLanguage::derivedSubProject() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("derived-sub-project/project.qbs")); - const TopLevelProjectPtr project = loader->loadProject(params); + resolveProject("derived-sub-project/project.qbs"); QVERIFY(!!project); const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 1); @@ -569,9 +629,7 @@ void TestLanguage::disabledSubProject() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("disabled-subproject.qbs")); - const TopLevelProjectPtr project = loader->loadProject(params); + resolveProject("disabled-subproject.qbs"); QVERIFY(!!project); const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 0); @@ -600,16 +658,14 @@ void TestLanguage::dottedNames() { QFETCH(bool, expectSuccess); try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("dotted-names/dotted-names.qbs")); QFETCH(bool, useProduct); QFETCH(bool, useModule); const QVariantMap overridden{ std::make_pair("projects.theProject.includeDottedProduct", useProduct), std::make_pair("projects.theProject.includeDottedModule", useModule) }; - params.setOverriddenValues(overridden); - TopLevelProjectPtr project = loader->loadProject(params); + defaultParameters.setOverriddenValues(overridden); + resolveProject("dotted-names/dotted-names.qbs"); QVERIFY(expectSuccess); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); @@ -626,13 +682,44 @@ void TestLanguage::dottedNames() } } +void TestLanguage::duplicateMultiplexValues_data() +{ + QTest::addColumn<bool>("dummy"); + QTest::newRow("duplicate-multiplex-value") << true; + QTest::newRow("duplicate-multiplex-value2") << true; +} + +void TestLanguage::duplicateMultiplexValues() +{ + bool exceptionCaught = false; + try { + resolveProject(qPrintable(QString::fromLocal8Bit(QTest::currentDataTag()) + + QLatin1String(".qbs"))); + QVERIFY(project); + const std::vector<ResolvedProductPtr> products = project->allProducts(); + QCOMPARE(products.size(), 2); + bool x86 = false; + bool arm = false; + for (const ResolvedProductPtr &p : products) { + if (p->moduleProperties->moduleProperty("qbs", "architecture").toString() == "x86") + x86 = true; + else if (p->moduleProperties->moduleProperty("qbs", "architecture").toString() == "arm") + arm = true; + } + QVERIFY(x86); + QVERIFY(arm); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QVERIFY(!exceptionCaught); +} + void TestLanguage::emptyJsFile() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("empty-js-file.qbs")); - const TopLevelProjectPtr project = loader->loadProject(params); + resolveProject("empty-js-file.qbs"); QVERIFY(!!project); const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 1); @@ -647,14 +734,12 @@ void TestLanguage::enumerateProjectProperties() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("enum-project-props.qbs")); - auto project = loader->loadProject(params); + resolveProject("enum-project-props.qbs"); QVERIFY(!!project); auto products = productsFromProject(project); QCOMPARE(products.size(), 1); auto product = products.values().front(); - auto files = product->groups.front()->allFiles(); + auto files = product->groups.front()->files; QCOMPARE(product->groups.size(), size_t(1)); QCOMPARE(files.size(), size_t(1)); auto fileName = FileInfo::fileName(files.front()->absoluteFilePath); @@ -672,7 +757,7 @@ void TestLanguage::evalErrorInNonPresentModule_data() QTest::addColumn<QString>("errorMessage"); QTest::newRow("module required") - << true << "broken.qbs:4:5 Element at index 0 of list property 'broken' " + << true << "broken.qbs:2:5 Element at index 0 of list property 'broken' " "does not have string type"; QTest::newRow("module not required") << false << QString(); } @@ -682,19 +767,16 @@ void TestLanguage::evalErrorInNonPresentModule() QFETCH(bool, moduleRequired); QFETCH(QString, errorMessage); try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("eval-error-in-non-present-module.qbs")); QVariantMap overridden{std::make_pair("products.p.moduleRequired", moduleRequired)}; - params.setOverriddenValues(overridden); - TopLevelProjectPtr project = loader->loadProject(params); + defaultParameters.setOverriddenValues(overridden); + resolveProject("eval-error-in-non-present-module.qbs"); QVERIFY(errorMessage.isEmpty()); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 1); const ResolvedProductPtr product = products.value("p"); QVERIFY(!!product); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { QVERIFY(!errorMessage.isEmpty()); QVERIFY2(e.toString().contains(errorMessage), qPrintable(e.toString())); } @@ -704,14 +786,12 @@ void TestLanguage::defaultValue() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("defaultvalue/egon.qbs")); QFETCH(QString, prop1Value); QVariantMap overridden; if (!prop1Value.isEmpty()) overridden.insert("modules.lower.prop1", prop1Value); - params.setOverriddenValues(overridden); - TopLevelProjectPtr project = loader->loadProject(params); + defaultParameters.setOverriddenValues(overridden); + resolveProject("defaultvalue/egon.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 2); @@ -725,8 +805,7 @@ void TestLanguage::defaultValue() propertyValue = product->moduleProperties->property(propertyName); QFETCH(QVariant, expectedListPropValue); QCOMPARE(propertyValue.toStringList(), expectedListPropValue.toStringList()); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -754,17 +833,14 @@ void TestLanguage::environmentVariable() try { // Create new environment: const QString varName = QStringLiteral("PRODUCT_NAME"); - const QString productName = QLatin1String("MyApp") + QString::number(qrand()); + const QString productName = QLatin1String("MyApp") + QString::number(m_rand.generate()); QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert(varName, productName); QProcessEnvironment origEnv = defaultParameters.environment(); // store orig environment defaultParameters.setEnvironment(env); - defaultParameters.setProjectFilePath(testProject("environmentvariable.qbs")); - project = loader->loadProject(defaultParameters); - - defaultParameters.setEnvironment(origEnv); // reset environment + resolveProject("environmentvariable.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); @@ -781,9 +857,7 @@ void TestLanguage::errorInDisabledProduct() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("error-in-disabled-product.qbs")); - auto project = loader->loadProject(params); + resolveProject("error-in-disabled-product.qbs"); QVERIFY(!!project); auto products = productsFromProject(project); QCOMPARE(products.size(), 5); @@ -807,11 +881,11 @@ void TestLanguage::erroneousFiles_data() QTest::newRow("importloop1") << "Loop detected when importing"; QTest::newRow("nonexistentouter") - << "Can't find variable: outer"; + << "'outer' is not defined"; QTest::newRow("invalid_file") << "does not exist"; QTest::newRow("invalid-parameter-rhs") - << "ReferenceError: Can't find variable: access"; + << "'access' is not defined"; QTest::newRow("invalid-parameter-type") << "Value assigned to property 'stringParameter' does not have type 'string'."; QTest::newRow("invalid_property_type") @@ -828,6 +902,8 @@ void TestLanguage::erroneousFiles_data() << "Cyclic dependencies detected."; QTest::newRow("dependency_cycle3") << "Cyclic dependencies detected."; + QTest::newRow("dependency_cycle3a") + << "Cyclic dependencies detected."; QTest::newRow("dependency_cycle4") << "Cyclic dependencies detected."; QTest::newRow("references_cycle") @@ -876,30 +952,24 @@ void TestLanguage::erroneousFiles_data() QTest::newRow("wrongQbsVersionFormat") << "The value '.*' of Project.minimumQbsVersion is not a valid version string."; QTest::newRow("properties-item-with-invalid-condition") - << "properties-item-with-invalid-condition.qbs:4:19.*TypeError: Result of expression " - "'cpp.nonexistingproperty'"; + << "properties-item-with-invalid-condition.qbs:4:19.*" + "cannot read property 'includes' of undefined"; QTest::newRow("misused-inherited-property") << "Binding to non-item property"; QTest::newRow("undeclared_property_in_Properties_item") << "Item 'blubb' is not declared"; - QTest::newRow("same-module-prefix1") << "The name of module 'prefix1' is equal to the first " - "component of the name of module 'prefix1.suffix'"; - QTest::newRow("same-module-prefix2") << "The name of module 'prefix2' is equal to the first " - "component of the name of module 'prefix2.suffix'"; QTest::newRow("conflicting-properties-in-export-items") << "Export item in inherited item redeclares property 'theProp' with different type."; QTest::newRow("invalid-property-option") << "PropertyOptions item refers to non-existing property 's0meProp'"; QTest::newRow("missing-colon") - << "Invalid item 'cpp.dynamicLibraries'. Did you mean to set a module property?"; + << "Invalid item 'dummy.cxxFlags'. Did you mean to set a module property?"; QTest::newRow("syntax-error-in-probe") - << "syntax-error-in-probe.qbs:4:20.*ReferenceError"; + << "syntax-error-in-probe.qbs:4:20.*'fngkgsdjfgklkf' is not defined"; QTest::newRow("wrong-toplevel-item") << "wrong-toplevel-item.qbs:1:1.*The top-level item must be of type 'Project' or " "'Product', but it is of type 'Artifact'."; QTest::newRow("conflicting-module-instances") << "There is more than one equally prioritized candidate for module " "'conflicting-instances'."; - QTest::newRow("module-depends-on-product") - << "module-with-product-dependency.qbs:2:5.*Modules cannot depend on products."; QTest::newRow("overwrite-inherited-readonly-property") << "overwrite-inherited-readonly-property.qbs" ":2:21.*Cannot set read-only property 'readOnlyString'."; @@ -916,25 +986,38 @@ void TestLanguage::erroneousFiles_data() << "module-with-invalid-original.qbs:2:24.*The special value 'original' cannot be used " "on the right-hand side of a property declaration."; QTest::newRow("original-in-export-item") - << "original-in-export-item.qbs:7:32.*The special value 'original' cannot be used " + << "original-in-export-item.qbs:5:32.*The special value 'original' cannot be used " "on the right-hand side of a property declaration."; QTest::newRow("original-in-export-item2") - << "original-in-export-item2.qbs:6:9.*Item 'x.y' is not declared. Did you forget " + << "original-in-export-item2.qbs:4:9.*Item 'x.y' is not declared. Did you forget " "to add a Depends item"; QTest::newRow("original-in-export-item3") << "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."; - QTest::newRow("duplicate-multiplex-value") - << "duplicate-multiplex-value.qbs:3:1.*Duplicate entry 'x86' in qbs.architectures."; - QTest::newRow("duplicate-multiplex-value2") - << "duplicate-multiplex-value2.qbs:3:1.*Duplicate entry 'architecture' in " - "Product.multiplexByQbsProperties."; + << "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("invalid-references") << "invalid-references.qbs:2:17.*Cannot open '.*nosuchproject.qbs'"; + QTest::newRow("missing-js-file") + << "missing-js-file-module.qbs.*Cannot open '.*javascriptfile.js'"; + QTest::newRow("frozen-object") << "'key' is read-only"; + QTest::newRow("frozen-object-list") << "object is not extensible"; + QTest::newRow("module-property-binding-in-project") + << "Module properties cannot be set in Project items"; + QTest::newRow("module-with-id") << "Module items cannot have an id property"; } void TestLanguage::erroneousFiles() @@ -942,10 +1025,10 @@ void TestLanguage::erroneousFiles() QFETCH(QString, errorMessage); QString fileName = QString::fromLocal8Bit(QTest::currentDataTag()) + QLatin1String(".qbs"); try { - defaultParameters.setProjectFilePath(testProject("/erroneous/") + fileName); - loader->loadProject(defaultParameters); + resolveProject(qPrintable("/erroneous/" + fileName)); } catch (const ErrorInfo &e) { - if (!e.toString().contains(QRegExp(errorMessage))) { + const QRegularExpression reg(errorMessage, QRegularExpression::DotMatchesEverythingOption); + if (!e.toString().contains(reg)) { qDebug() << "Message: " << e.toString(); qDebug() << "Expected: " << errorMessage; QFAIL("Unexpected error message."); @@ -961,8 +1044,7 @@ void TestLanguage::exports() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("exports.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("exports.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 22); @@ -1007,6 +1089,7 @@ void TestLanguage::exports() product = products.value("B"); QVERIFY(!!product); QVERIFY(product->dependencies.empty()); + QCOMPARE(product->exportedModule.productDependencies, std::vector<QString>{"C"}); product = products.value("C"); QVERIFY(!!product); QVERIFY(product->dependencies.empty()); @@ -1051,7 +1134,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")); @@ -1059,7 +1142,7 @@ void TestLanguage::exports() product = products.value("depender"); QVERIFY(!!product); QCOMPARE(product->modules.size(), size_t(2)); - for (const ResolvedModuleConstPtr &m : product->modules) { + for (const ResolvedModulePtr &m : product->modules) { QVERIFY2(m->name == QString("qbs") || m->name == QString("dependency"), qPrintable(m->name)); } @@ -1070,13 +1153,12 @@ void TestLanguage::exports() product = products.value("broken_cycle3"); QVERIFY(!!product); QCOMPARE(product->modules.size(), size_t(3)); - for (const ResolvedModuleConstPtr &m : product->modules) { + for (const ResolvedModulePtr &m : product->modules) { QVERIFY2(m->name == QString("qbs") || m->name == QString("broken_cycle1") || m->name == QString("broken_cycle2"), qPrintable(m->name)); } - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -1087,8 +1169,7 @@ void TestLanguage::fileContextProperties() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("filecontextproperties.qbs")); - project = loader->loadProject(defaultParameters); + resolveProject("filecontextproperties.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); ResolvedProductPtr product = products.value("product1"); @@ -1134,14 +1215,12 @@ void TestLanguage::fileInProductAndModule() QFETCH(bool, addFileToProduct); QFETCH(bool, successExpected); try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("file-in-product-and-module.qbs")); - params.setOverriddenValues(QVariantMap{ + defaultParameters.setOverriddenValues(QVariantMap{ std::make_pair("modules.module_with_file.file1IsTarget", file1IsTarget), std::make_pair("modules.module_with_file.file2IsTarget", file2IsTarget), std::make_pair("products.p.addFileToProduct", addFileToProduct), }); - project = loader->loadProject(params); + resolveProject("file-in-product-and-module.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 1); @@ -1156,16 +1235,19 @@ void TestLanguage::getNativeSetting() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("getNativeSetting.qbs")); - project = loader->loadProject(defaultParameters); + resolveProject("getNativeSetting.qbs"); QString expectedTargetName; - if (HostOsInfo::isMacosHost()) - expectedTargetName = QStringLiteral("Mac OS X"); - else if (HostOsInfo::isWindowsHost()) + if (HostOsInfo::isMacosHost()) { + if (HostOsInfo::hostOsVersion() >= qbs::Version(11)) + expectedTargetName = QStringLiteral("macOS"); + else + expectedTargetName = QStringLiteral("Mac OS X"); + } else if (HostOsInfo::isWindowsHost()) { expectedTargetName = QStringLiteral("Windows"); - else + } else { expectedTargetName = QStringLiteral("Unix"); + } QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products; @@ -1228,8 +1310,7 @@ void TestLanguage::groupName() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("groupname.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("groupname.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 2); @@ -1256,8 +1337,7 @@ void TestLanguage::groupName() group = product->groups.at(2); QVERIFY(!!group); QCOMPARE(group->name, QString("Group 2")); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -1267,8 +1347,7 @@ void TestLanguage::groupName() void TestLanguage::homeDirectory() { try { - defaultParameters.setProjectFilePath(testProject("homeDirectory.qbs")); - ResolvedProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("homeDirectory.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 1); @@ -1293,8 +1372,7 @@ void TestLanguage::homeDirectory() FileInfo::resolvePath(product->sourceDirectory, QStringLiteral("a/~/bb"))); QCOMPARE(product->productProperties.value("user").toString(), FileInfo::resolvePath(product->sourceDirectory, QStringLiteral("~foo/bar"))); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { qDebug() << e.toString(); } } @@ -1373,8 +1451,7 @@ void TestLanguage::idUsage() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("idusage.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("idusage.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 5); @@ -1398,8 +1475,7 @@ void TestLanguage::idUsage() QVERIFY(!!product5); QCOMPARE(product5->moduleProperties->moduleProperty("deepdummy.deep.moat", "zort") .toString(), QString("zort in dummy")); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -1410,10 +1486,8 @@ void TestLanguage::idUniqueness() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("id-uniqueness.qbs")); - loader->loadProject(defaultParameters); - } - catch (const ErrorInfo &e) { + resolveProject("id-uniqueness.qbs"); + } catch (const ErrorInfo &e) { exceptionCaught = true; const QList<ErrorItem> items = e.items(); QCOMPARE(items.size(), 3); @@ -1428,15 +1502,13 @@ void TestLanguage::importCollection() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("import-collection/project.qbs")); - const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("import-collection/project.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); const ResolvedProductConstPtr product = products.value("da product"); QCOMPARE(product->productProperties.value("targetName").toString(), QLatin1String("C1f1C1f2C2f1C2f2")); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -1455,19 +1527,15 @@ void TestLanguage::inheritedPropertiesItems() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; QFETCH(QString, buildVariant); QFETCH(QString, productName); - params.setProjectFilePath - (testProject("inherited-properties-items/inherited-properties-items.qbs")); - params.setOverriddenValues(QVariantMap{std::make_pair("qbs.buildVariant", buildVariant)}); - TopLevelProjectPtr project = loader->loadProject(params); + defaultParameters.setOverriddenValues(QVariantMap{std::make_pair("qbs.buildVariant", buildVariant)}); + resolveProject("inherited-properties-items/inherited-properties-items.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 1); QVERIFY(!!products.value(productName)); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -1478,13 +1546,11 @@ void TestLanguage::invalidBindingInDisabledItem() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("invalidBindingInDisabledItem.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("invalidBindingInDisabledItem.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 2); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -1498,13 +1564,10 @@ void TestLanguage::invalidOverrides() const bool successExpected = expectedErrorMessage.isEmpty(); bool exceptionCaught = false; try { - qbs::SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("invalid-overrides.qbs")); - params.setOverriddenValues(QVariantMap{std::make_pair(key, true)}); - const TopLevelProjectPtr project = loader->loadProject(params); + defaultParameters.setOverriddenValues(QVariantMap{std::make_pair(key, true)}); + resolveProject("invalid-overrides.qbs"); QVERIFY(!!project); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; if (successExpected) qDebug() << e.toString(); @@ -1548,31 +1611,32 @@ void TestLanguage::invalidOverrides_data() << "products.MyOtherProduct.cpp.useRPaths" << QString(); } -class JSSourceValueCreator +void TestLanguage::invalidPropOnNonRequiredModule_data() { - FileContextPtr m_fileContext; - QList<QString *> m_strings; -public: - JSSourceValueCreator(const FileContextPtr &fileContext) - : m_fileContext(fileContext) - { - } + QTest::addColumn<bool>("useExistingModule"); + QTest::addColumn<bool>("errorExpected"); - ~JSSourceValueCreator() - { - qDeleteAll(m_strings); - } + QTest::newRow("existing module") << true << true; + QTest::newRow("non-existing module") << false << false; +} - JSSourceValuePtr create(const QString &sourceCode) - { - JSSourceValuePtr value = JSSourceValue::create(); - value->setFile(m_fileContext); - const auto str = new QString(sourceCode); - m_strings.push_back(str); - value->setSourceCode(QStringRef(str)); - return value; +void TestLanguage::invalidPropOnNonRequiredModule() +{ + QFETCH(bool, useExistingModule); + QFETCH(bool, errorExpected); + + try { + defaultParameters.setOverriddenValues( + {std::make_pair("project.useExistingModule", useExistingModule)}); + resolveProject("invalid-prop-on-non-required-module"); + QVERIFY(!errorExpected); + } catch (const ErrorInfo &e) { + const QString errorString = e.toString(); + QVERIFY2(errorExpected, qPrintable(errorString)); + QVERIFY2(errorString.contains("Property 'nosuchprop' is not declared"), + qPrintable(errorString)); } -}; +} void TestLanguage::itemPrototype() { @@ -1588,13 +1652,14 @@ void TestLanguage::itemPrototype() item->setProperty("y", sourceValueCreator.create("x + 1")); item->setProperty("z", sourceValueCreator.create("2")); - Evaluator evaluator(m_engine); - QCOMPARE(evaluator.property(proto, "x").toVariant().toInt(), 1); - QCOMPARE(evaluator.property(proto, "y").toVariant().toInt(), 1); - QVERIFY(!evaluator.property(proto, "z").isValid()); - QCOMPARE(evaluator.property(item, "x").toVariant().toInt(), 1); - QCOMPARE(evaluator.property(item, "y").toVariant().toInt(), 2); - QCOMPARE(evaluator.property(item, "z").toVariant().toInt(), 2); + Evaluator evaluator(m_engine.get()); + JSContext * const ctx = m_engine->context(); + QCOMPARE(getJsVariant(ctx, evaluator.property(proto, "x")).toInt(), 1); + QCOMPARE(getJsVariant(ctx, evaluator.property(proto, "y")).toInt(), 1); + QVERIFY(JS_IsUndefined(evaluator.property(proto, "z"))); + QCOMPARE(getJsVariant(ctx, evaluator.property(item, "x")).toInt(), 1); + QCOMPARE(getJsVariant(ctx, evaluator.property(item, "y")).toInt(), 2); + QCOMPARE(getJsVariant(ctx, evaluator.property(item, "z")).toInt(), 2); } void TestLanguage::itemScope() @@ -1612,11 +1677,12 @@ void TestLanguage::itemScope() item->setScope(scope2); item->setProperty("z", sourceValueCreator.create("x + y")); - Evaluator evaluator(m_engine); - QCOMPARE(evaluator.property(scope1, "x").toVariant().toInt(), 1); - QCOMPARE(evaluator.property(scope2, "y").toVariant().toInt(), 2); - QVERIFY(!evaluator.property(scope2, "x").isValid()); - QCOMPARE(evaluator.property(item, "z").toVariant().toInt(), 3); + Evaluator evaluator(m_engine.get()); + JSContext * const ctx = m_engine->context(); + QCOMPARE(getJsVariant(ctx, evaluator.property(scope1, "x")).toInt(), 1); + QCOMPARE(getJsVariant(ctx, evaluator.property(scope2, "y")).toInt(), 2); + QVERIFY(JS_IsUndefined(evaluator.property(scope2, "x"))); + QCOMPARE(getJsVariant(ctx, evaluator.property(item, "z")).toInt(), 3); } void TestLanguage::jsExtensions() @@ -1626,10 +1692,10 @@ void TestLanguage::jsExtensions() QTextStream ts(&file); QString code = ts.readAll(); QVERIFY(!code.isEmpty()); - QScriptValue evaluated = m_engine->evaluate(code, file.fileName(), 1); - if (m_engine->hasErrorOrException(evaluated)) { - qDebug() << m_engine->uncaughtExceptionBacktrace(); - QFAIL(qPrintable(m_engine->lastErrorString(evaluated))); + m_engine->evaluate(JsValueOwner::Caller, code, file.fileName(), 1); + if (JsException ex = m_engine->checkAndClearException({})) { + qDebug() << ex.stackTrace(); + QFAIL(qPrintable(ex.message())); } } @@ -1648,35 +1714,45 @@ void TestLanguage::jsImportUsedInMultipleScopes() bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("jsimportsinmultiplescopes.qbs")); - params.setOverriddenValues({std::make_pair(QStringLiteral("qbs.buildVariant"), - buildVariant)}); - params.expandBuildConfiguration(); - TopLevelProjectPtr project = loader->loadProject(params); + defaultParameters.setOverriddenValues({std::make_pair(QStringLiteral("qbs.buildVariant"), + buildVariant)}); + resolveProject("jsimportsinmultiplescopes.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 1); ResolvedProductPtr product = products.values().front(); QVERIFY(!!product); QCOMPARE(product->name, expectedProductName); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } QVERIFY(!exceptionCaught); } +void TestLanguage::localProfileAsTopLevelProfile() +{ + bool exceptionCaught = false; + try { + defaultParameters.setTopLevelProfile("test-profile"); + resolveProject("local-profile-as-top-level-profile.qbs"); + QVERIFY(!!project); + QCOMPARE(int(project->products.size()), 1); + const PropertyMapConstPtr &props = project->products.front()->moduleProperties; + QCOMPARE(props->qbsPropertyValue("architecture"), "arm"); + QCOMPARE(props->qbsPropertyValue("targetPlatform"), "macos"); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + void TestLanguage::moduleMergingVariantValues() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath - (testProject("module-merging-variant-values/module-merging-variant-values.qbs")); - params.expandBuildConfiguration(); - const TopLevelProjectPtr project = loader->loadProject(params); + resolveProject("module-merging-variant-values/module-merging-variant-values.qbs"); QVERIFY(!!project); QCOMPARE(int(project->products.size()), 2); } catch (const ErrorInfo &e) { @@ -1686,6 +1762,185 @@ void TestLanguage::moduleMergingVariantValues() QCOMPARE(exceptionCaught, false); } +void TestLanguage::moduleNameCollisions_data() +{ + QTest::addColumn<QString>("projectFile"); + QTest::addColumn<bool>("collisionExpected"); + + QTest::newRow("simple collision (one order)") << "simple-collision1.qbs" << true; + QTest::newRow("simple collision (other order)") << "simple-collision2.qbs" << true; + QTest::newRow("collision with more components") << "complex-collision.qbs" << true; + QTest::newRow("no collision (same length)") << "no-collision1.qbs" << false; + QTest::newRow("no collision (different length)") << "no-collision2.qbs" << false; +} + +void TestLanguage::moduleNameCollisions() +{ + QFETCH(QString, projectFile); + QFETCH(bool, collisionExpected); + + try { + const QString compositeProjectFilePath = QString("module-name-collisions/") + projectFile; + QVERIFY(resolveProject(qPrintable(compositeProjectFilePath))); + QVERIFY(!collisionExpected); + } catch (const ErrorInfo &e) { + const QString errorString = e.toString(); + QVERIFY2(collisionExpected, qPrintable(errorString)); + QVERIFY2(errorString.contains("not allowed"), qPrintable(errorString)); + } +} + +void TestLanguage::moduleParameters_data() +{ + QTest::addColumn<QVariantMap>("inputProperties"); + QTest::addColumn<QVariantMap>("expectedModuleParameters"); + QTest::addColumn<bool>("errorExpected"); + + QTest::newRow("no overrides") + << QVariantMap{ + std::make_pair("project.overrideFromModule", "false"), + std::make_pair("project.overrideFromExport", "false"), + std::make_pair("project.overrideFromProduct", "false")} + << QVariantMap{ + std::make_pair("higher", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromParameters")})}), + std::make_pair("dep", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromParameters")})})} + << false; + QTest::newRow("override from product") + << QVariantMap{ + std::make_pair("project.overrideFromModule", "false"), + std::make_pair("project.overrideFromExport", "false"), + std::make_pair("project.overrideFromProduct", "true")} + << QVariantMap{ + std::make_pair("higher", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromProductDepends")})}), + std::make_pair("dep", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromProductDepends")})})} + << false; + QTest::newRow("override from export") + << QVariantMap{ + std::make_pair("project.overrideFromModule", "false"), + std::make_pair("project.overrideFromExport", "true"), + std::make_pair("project.overrideFromProduct", "false")} + << QVariantMap{ + std::make_pair("higher", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromExportDepends")})}), + std::make_pair("dep", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromParameters")})})} + << false; + QTest::newRow("override from export and product") + << QVariantMap{ + std::make_pair("project.overrideFromModule", "false"), + std::make_pair("project.overrideFromExport", "true"), + std::make_pair("project.overrideFromProduct", "true")} + << QVariantMap{ + std::make_pair("higher", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromProductDepends")})}), + std::make_pair("dep", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromProductDepends")})})} + << false; + QTest::newRow("override from module") + << QVariantMap{ + std::make_pair("project.overrideFromModule", "true"), + std::make_pair("project.overrideFromExport", "false"), + std::make_pair("project.overrideFromProduct", "false")} + << QVariantMap{ + std::make_pair("higher", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromModuleDepends")})}), + std::make_pair("dep", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromParameters")})})} + << false; + QTest::newRow("override from module and product") + << QVariantMap{ + std::make_pair("project.overrideFromModule", "true"), + std::make_pair("project.overrideFromExport", "false"), + std::make_pair("project.overrideFromProduct", "true")} + << QVariantMap{ + std::make_pair("higher", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromProductDepends")})}), + std::make_pair("dep", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromProductDepends")})})} + << false; + QTest::newRow("override from module and export") + << QVariantMap{ + std::make_pair("project.overrideFromModule", "true"), + std::make_pair("project.overrideFromExport", "true"), + std::make_pair("project.overrideFromProduct", "false")} + << QVariantMap{ + std::make_pair("higher", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromExportDepends")})}), + std::make_pair("dep", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromParameters")})})} + << true; + QTest::newRow("override from module, export and product") + << QVariantMap{ + std::make_pair("project.overrideFromModule", "true"), + std::make_pair("project.overrideFromExport", "true"), + std::make_pair("project.overrideFromProduct", "true")} + << QVariantMap{ + std::make_pair("higher", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromProductDepends")})}), + std::make_pair("dep", + QVariantMap{std::make_pair("lower", + QVariantMap{std::make_pair("param", "fromProductDepends")})})} + << false; +} + +void TestLanguage::moduleParameters() +{ + QFETCH(QVariantMap, inputProperties); + QFETCH(QVariantMap, expectedModuleParameters); + QFETCH(bool, errorExpected); + + try { + defaultParameters.setOverriddenValues(inputProperties); + resolveProject("module-parameters/module-parameters.qbs"); + QVERIFY(!errorExpected); + QVERIFY(project); + QCOMPARE(int(project->products.size()), 2); + const ResolvedProductPtr mainProduct = productsFromProject(project).value("main"); + QVERIFY(mainProduct); + QCOMPARE(int(mainProduct->moduleParameters.size()), 2); + for (auto it = expectedModuleParameters.cbegin(); it != expectedModuleParameters.cend(); + ++it) { + const auto findInProduct = [&](const QString &moduleName) { + for (auto it = mainProduct->moduleParameters.cbegin(); + it != mainProduct->moduleParameters.cend(); ++it) { + if (it.key()->name == moduleName) + return it.value(); + } + return QVariantMap(); + }; + const QVariantMap actual = findInProduct(it.key()); + const QVariantMap expected = it.value().toMap(); + const bool same = qVariantMapsEqual(actual, expected); + if (!same) { + qDebug().noquote() << "---" << expected; + qDebug().noquote() << "+++" << actual; + } + QVERIFY(same); + } + } catch (const ErrorInfo &e) { + QVERIFY2(errorExpected, qPrintable(e.toString())); + } +} + void TestLanguage::modulePrioritizationBySearchPath_data() { QTest::addColumn<QStringList>("searchPaths"); @@ -1701,12 +1956,10 @@ void TestLanguage::modulePrioritizationBySearchPath() bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("module-prioritization-by-search-path/project.qbs")); - params.setOverriddenValues({std::make_pair(QStringLiteral("project.qbsSearchPaths"), - searchPaths)}); - params.expandBuildConfiguration(); - TopLevelProjectPtr project = loader->loadProject(params); + defaultParameters.setOverriddenValues( + {std::make_pair(QStringLiteral("project.qbsSearchPaths"), + searchPaths)}); + resolveProject("module-prioritization-by-search-path/project.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 1); @@ -1715,8 +1968,7 @@ void TestLanguage::modulePrioritizationBySearchPath() const QString actualVariant = product->moduleProperties->moduleProperty ("conflicting-instances", "moduleVariant").toString(); QCOMPARE(actualVariant, expectedVariant); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -1730,10 +1982,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"); @@ -1782,7 +2034,7 @@ void TestLanguage::modulePropertiesInGroups() defaultParameters.setProjectFilePath(testProject("modulepropertiesingroups.qbs")); bool exceptionCaught = false; try { - TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject(); QVERIFY(!!project); const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); ResolvedProductPtr product = products.value("grouptest"); @@ -1793,7 +2045,7 @@ void TestLanguage::modulePropertiesInGroups() GroupConstPtr g2; GroupConstPtr g21; GroupConstPtr g211; - for (const GroupConstPtr &g : product->groups) { + for (const GroupPtr &g : product->groups) { if (g->name == "g1") g1= g; else if (g->name == "g2") @@ -1910,7 +2162,7 @@ void TestLanguage::modulePropertiesInGroups() QCOMPARE(g2Gmod1List1, QStringList() << "gmod1_list1_proto" << "g2"); const auto &g2Gmod1List2 = moduleProperty(g2Props, "gmod.gmod1", "gmod1_list2") .toStringList(); - QCOMPARE(g2Gmod1List2, QStringList() << "grouptest" << "g2" << "gmod1_list2_proto"); + QCOMPARE(g2Gmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" << "gmod1_list2_proto"); const int g2P0 = moduleProperty(g2Props, "gmod.gmod1", "p0").toInt(); QCOMPARE(g2P0, 6); const int g2DepProp = moduleProperty(g2Props, "gmod.gmod1", "depProp").toInt(); @@ -1928,7 +2180,7 @@ void TestLanguage::modulePropertiesInGroups() QCOMPARE(g21Gmod1List1, QStringList() << "gmod1_list1_proto" << "g2"); const auto &g21Gmod1List2 = moduleProperty(g21Props, "gmod.gmod1", "gmod1_list2") .toStringList(); - QEXPECT_FAIL(0, "no re-eval when no module props set", Continue); + QEXPECT_FAIL(nullptr, "no re-eval when no module props set", Continue); QCOMPARE(g21Gmod1List2, QStringList() << "grouptest" << "g2.1" << "gmod1_list2_proto"); const int g21P0 = moduleProperty(g21Props, "gmod.gmod1", "p0").toInt(); QCOMPARE(g21P0, 6); @@ -1938,7 +2190,7 @@ void TestLanguage::modulePropertiesInGroups() QCOMPARE(g21Gmod2String, QString("g2")); const auto &g21Gmod2List = moduleProperty(g21Props, "gmod2", "gmod2_list") .toStringList(); - QEXPECT_FAIL(0, "no re-eval when no module props set", Continue); + QEXPECT_FAIL(nullptr, "no re-eval when no module props set", Continue); QCOMPARE(g21Gmod2List, QStringList() << "g2" << "commonName_in_gmod1" << "g2.1_gmod4_g2.1_gmod3" << "g2.1_gmod3" << "gmod2_list_proto"); @@ -1955,11 +2207,11 @@ void TestLanguage::modulePropertiesInGroups() QCOMPARE(g211DepProp, 2); const auto &g211Gmod2String = moduleProperty(g211Props, "gmod2", "gmod2_string").toString(); - QEXPECT_FAIL(0, "re-eval not triggered", Continue); + QEXPECT_FAIL(nullptr, "re-eval not triggered", Continue); QCOMPARE(g211Gmod2String, QString("g2.1.1")); const auto &g211Gmod2List = moduleProperty(g211Props, "gmod2", "gmod2_list") .toStringList(); - QEXPECT_FAIL(0, "re-eval not triggered", Continue); + QEXPECT_FAIL(nullptr, "re-eval not triggered", Continue); QCOMPARE(g211Gmod2List, QStringList() << "g2.1.1" << "commonName_in_gmod1" << "g2.1.1_gmod4_g2.1.1_gmod3" << "g2.1.1_gmod3" << "gmod2_list_proto"); @@ -1967,7 +2219,7 @@ void TestLanguage::modulePropertiesInGroups() QVERIFY(!!product); g1.reset(); g11.reset(); - for (const GroupConstPtr &g : product->groups) { + for (const GroupPtr &g : product->groups) { if (g->name == "g1") g1= g; else if (g->name == "g1.1") @@ -1994,17 +2246,14 @@ void TestLanguage::modulePropertyOverridesPerProduct() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setOverriddenValues({ + defaultParameters.setOverriddenValues({ std::make_pair("modules.dummy.rpaths", QStringList({"/usr/lib"})), std::make_pair("modules.dummy.someString", "m"), std::make_pair("products.b.dummy.someString", "b"), std::make_pair("products.c.dummy.someString", "c"), std::make_pair("products.c.dummy.rpaths", QStringList({"/home", "/tmp"})) }); - params.setProjectFilePath( - testProject("module-property-overrides-per-product.qbs")); - const TopLevelProjectPtr project = loader->loadProject(params); + resolveProject("module-property-overrides-per-product.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 3); @@ -2037,8 +2286,7 @@ void TestLanguage::modulePropertyOverridesPerProduct() QCOMPARE(listPropertyValue(a), productPropertyValue(a)); QCOMPARE(listPropertyValue(b), productPropertyValue(b)); QCOMPARE(listPropertyValue(c), productPropertyValue(c)); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -2050,7 +2298,7 @@ void TestLanguage::moduleScope() bool exceptionCaught = false; try { defaultParameters.setProjectFilePath(testProject("modulescope.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject(); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 1); @@ -2070,13 +2318,30 @@ void TestLanguage::moduleScope() QCOMPARE(intModuleValue("f"), 2); // overridden QCOMPARE(intModuleValue("g"), 156); // overridden, dependent on product properties QCOMPARE(intModuleValue("h"), 158); // overridden, base dependent on product properties - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } QCOMPARE(exceptionCaught, false); +} +void TestLanguage::moduleWithProductDependency() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("module-depends-on-product.qbs")); + resolveProject(); + QVERIFY(project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.size(), 2); + ResolvedProductPtr product = products.value("p1"); + QVERIFY(product); + QCOMPARE(int(product->dependencies.size()), 1); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); } void TestLanguage::modules_data() @@ -2135,9 +2400,7 @@ void TestLanguage::multiplexedExports() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("multiplexed-exports.qbs")); - const TopLevelProjectPtr project = loader->loadProject(params); + resolveProject("multiplexed-exports.qbs"); QVERIFY(!!project); const auto products = project->allProducts(); QCOMPARE(products.size(), size_t(4)); @@ -2171,11 +2434,9 @@ void TestLanguage::multiplexingByProfile() { QFETCH(QString, projectFileName); QFETCH(bool, successExpected); - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testDataDir() + "/multiplexing-by-profile/" + projectFileName); try { - params.setDryRun(true); - const TopLevelProjectPtr project = loader->loadProject(params); + defaultParameters.setDryRun(true); + resolveProject(qPrintable("/multiplexing-by-profile/" + projectFileName)); QVERIFY(successExpected); QVERIFY(!!project); } catch (const ErrorInfo &e) { @@ -2199,11 +2460,9 @@ void TestLanguage::nonApplicableModulePropertyInProfile() QFETCH(QString, toolchain); QFETCH(bool, successExpected); try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("non-applicable-module-property-in-profile.qbs")); - params.setOverriddenValues(QVariantMap{std::make_pair("project.targetOS", targetOS), + defaultParameters.setOverriddenValues({std::make_pair("project.targetOS", targetOS), std::make_pair("project.toolchain", toolchain)}); - const TopLevelProjectPtr project = loader->loadProject(params); + resolveProject("non-applicable-module-property-in-profile.qbs"); QVERIFY(!!project); QVERIFY(successExpected); } catch (const ErrorInfo &e) { @@ -2233,8 +2492,6 @@ void TestLanguage::nonRequiredProducts() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("non-required-products.qbs")); QFETCH(bool, subProjectEnabled); QFETCH(bool, dependeeEnabled); QVariantMap overriddenValues; @@ -2242,8 +2499,8 @@ void TestLanguage::nonRequiredProducts() overriddenValues.insert("projects.subproject.condition", false); else if (!dependeeEnabled) overriddenValues.insert("products.dependee.condition", false); - params.setOverriddenValues(overriddenValues); - const TopLevelProjectPtr project = loader->loadProject(params); + defaultParameters.setOverriddenValues(overriddenValues); + resolveProject("non-required-products.qbs"); QVERIFY(!!project); const auto products = productsFromProject(project); QCOMPARE(products.size(), 4 + !!subProjectEnabled); @@ -2262,8 +2519,7 @@ void TestLanguage::nonRequiredProducts() QVERIFY2(product, name); QVERIFY2(!product->enabled, name); } - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -2284,7 +2540,7 @@ void TestLanguage::outerInGroup() bool exceptionCaught = false; try { defaultParameters.setProjectFilePath(testProject("outerInGroup.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject(); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 1); @@ -2305,8 +2561,7 @@ void TestLanguage::outerInGroup() artifact = group->files.front(); installDir = artifact->properties->qbsPropertyValue("installDir"); QCOMPARE(installDir.toString(), QString("/somewhere/else")); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -2319,16 +2574,14 @@ void TestLanguage::overriddenPropertiesAndPrototypes() try { QFETCH(QString, osName); QFETCH(QString, backendName); - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("overridden-properties-and-prototypes.qbs")); - params.setOverriddenValues({std::make_pair("modules.qbs.targetPlatform", osName)}); - TopLevelProjectConstPtr project = loader->loadProject(params); + defaultParameters.setOverriddenValues({std::make_pair("modules.qbs.targetPlatform", + osName)}); + resolveProject("overridden-properties-and-prototypes.qbs"); QVERIFY(!!project); QCOMPARE(project->products.size(), size_t(1)); QCOMPARE(project->products.front()->moduleProperties->moduleProperty( "multiple_backends", "prop").toString(), backendName); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -2347,11 +2600,9 @@ void TestLanguage::overriddenVariantProperty() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; const QVariantMap objectValue{std::make_pair("x", 1), std::make_pair("y", 2)}; - params.setOverriddenValues({std::make_pair("products.p.myObject", objectValue)}); - params.setProjectFilePath(testProject("overridden-variant-property.qbs")); - TopLevelProjectConstPtr project = loader->loadProject(params); + defaultParameters.setOverriddenValues({std::make_pair("products.p.myObject", objectValue)}); + resolveProject("overridden-variant-property.qbs"); QVERIFY(!!project); QCOMPARE(project->products.size(), size_t(1)); QCOMPARE(project->products.front()->productProperties.value("myObject").toMap(), @@ -2368,9 +2619,8 @@ void TestLanguage::parameterTypes() bool exceptionCaught = false; try { defaultParameters.setProjectFilePath(testProject("parameter-types.qbs")); - loader->loadProject(defaultParameters); - } - catch (const ErrorInfo &e) { + resolveProject(); + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -2381,8 +2631,7 @@ void TestLanguage::pathProperties() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("pathproperties.qbs")); - project = loader->loadProject(defaultParameters); + resolveProject("pathproperties.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); ResolvedProductPtr product = products.value("product1"); @@ -2408,6 +2657,35 @@ void TestLanguage::pathProperties() QCOMPARE(exceptionCaught, false); } +void TestLanguage::probesAndMultiplexing() +{ + bool exceptionCaught = false; + try { + resolveProject("probes-and-multiplexing.qbs"); + QVERIFY(project); + QCOMPARE(int(project->products.size()), 3); + QStringList architectures{"x86", "x86_64", "arm"}; + for (const ResolvedProductPtr &product : project->products) { + const QString arch = product->moduleProperties->moduleProperty("qbs", "architecture") + .toString(); + QVERIFY2(architectures.removeOne(arch), qPrintable(arch)); + QCOMPARE(product->productProperties.value("archFromProbe").toString(), arch); + bool foundGroup = false; + for (const GroupPtr &group : product->groups) { + if (group->name == "theGroup") { + foundGroup = true; + QCOMPARE(group->properties->moduleProperty("qbs", "sysroot"), "/" + arch); + } + } + QVERIFY(foundGroup); + } + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + void TestLanguage::profileValuesAndOverriddenValues() { bool exceptionCaught = false; @@ -2418,14 +2696,11 @@ void TestLanguage::profileValuesAndOverriddenValues() profile.setValue("dummy.cFlags", "IN_PROFILE"); profile.setValue("dummy.cxxFlags", "IN_PROFILE"); profile.setValue("qbs.architecture", "x86"); - SetupProjectParameters parameters = defaultParameters; - parameters.setTopLevelProfile(profile.name()); + defaultParameters.setTopLevelProfile(profile.name()); QVariantMap overriddenValues; overriddenValues.insert("modules.dummy.cFlags", "OVERRIDDEN"); - parameters.setOverriddenValues(overriddenValues); - parameters.setProjectFilePath(testProject("profilevaluesandoverriddenvalues.qbs")); - parameters.expandBuildConfiguration(); - project = loader->loadProject(parameters); + defaultParameters.setOverriddenValues(overriddenValues); + resolveProject("profilevaluesandoverriddenvalues.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); ResolvedProductPtr product = products.value("product1"); @@ -2455,7 +2730,7 @@ void TestLanguage::projectFileLookup() try { SetupProjectParameters params; params.setProjectFilePath(projectFileInput); - Loader::setupProjectFilePath(params); + params.finalizeProjectFilePath(); QVERIFY(!failureExpected); QCOMPARE(params.projectFilePath(), projectFileOutput); } catch (const ErrorInfo &) { @@ -2469,7 +2744,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"; @@ -2486,8 +2761,7 @@ void TestLanguage::productConditions() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("productconditions.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("productconditions.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 6); @@ -2515,8 +2789,7 @@ void TestLanguage::productConditions() product = products.value("product_probe_condition_true"); QVERIFY(!!product); QVERIFY(product->enabled); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -2527,8 +2800,7 @@ void TestLanguage::productDirectories() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("productdirectories.qbs")); - ResolvedProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("productdirectories.qbs"); QVERIFY(!!project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 1); @@ -2539,8 +2811,7 @@ void TestLanguage::productDirectories() QCOMPARE(config.value(QStringLiteral("buildDirectory")).toString(), product->buildDirectory()); QCOMPARE(config.value(QStringLiteral("sourceDirectory")).toString(), testDataDir()); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); } @@ -2686,8 +2957,7 @@ void TestLanguage::propertiesBlockInGroup() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("properties-block-in-group.qbs")); - const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("properties-block-in-group.qbs"); QVERIFY(!!project); QCOMPARE(project->allProducts().size(), size_t(1)); const ResolvedProductConstPtr product = project->allProducts().front(); @@ -2711,13 +2981,11 @@ void TestLanguage::propertiesItemInModule() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath( - testProject("properties-item-in-module.qbs")); - const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("properties-item-in-module.qbs"); QVERIFY(!!project); const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 2); - for (const ResolvedProductConstPtr &p : products) { + for (const ResolvedProductPtr &p : products) { QCOMPARE(p->moduleProperties->moduleProperty("dummy", "productName").toString(), p->name); } @@ -2732,16 +3000,14 @@ void TestLanguage::propertyAssignmentInExportedGroup() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath( - testProject("property-assignment-in-exported-group.qbs")); - const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("property-assignment-in-exported-group.qbs"); QVERIFY(!!project); const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 2); - for (const ResolvedProductConstPtr &p : products) { + for (const ResolvedProductPtr &p : products) { QCOMPARE(p->moduleProperties->moduleProperty("dummy", "someString").toString(), QString()); - for (const GroupConstPtr &g : p->groups) { + for (const GroupPtr &g : p->groups) { const QString propValue = g->properties->moduleProperty("dummy", "someString").toString(); if (g->name == "exported_group") @@ -2761,8 +3027,7 @@ void TestLanguage::qbs1275() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath(testProject("qbs1275.qbs")); - const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("qbs1275.qbs"); QVERIFY(!!project); const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.count(), 5); @@ -2777,10 +3042,9 @@ void TestLanguage::qbsPropertiesInProjectCondition() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath( - testProject("qbs-properties-in-project-condition.qbs")); - const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("qbs-properties-in-project-condition.qbs"); QVERIFY(!!project); + QVERIFY(!project->enabled); const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 0); } catch (const ErrorInfo &e) { @@ -2794,16 +3058,14 @@ void TestLanguage::qbsPropertyConvenienceOverride() { bool exceptionCaught = false; try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("qbs-property-convenience-override.qbs")); - params.setOverriddenValues({std::make_pair("qbs.installPrefix", "/opt")}); - TopLevelProjectConstPtr project = loader->loadProject(params); + defaultParameters.setOverriddenValues({std::make_pair("qbs.installPrefix", "/opt")}); + resolveProject("qbs-property-convenience-override.qbs"); QVERIFY(!!project); QCOMPARE(project->products.size(), size_t(1)); QCOMPARE(project->products.front()->moduleProperties->qbsPropertyValue("installPrefix") .toString(), QString("/opt")); - } - catch (const ErrorInfo &e) { + } catch (const ErrorInfo &e) { + exceptionCaught = true; qDebug() << e.toString(); } QCOMPARE(exceptionCaught, false); @@ -2814,11 +3076,9 @@ void TestLanguage::relaxedErrorMode() m_logSink->setLogLevel(LoggerMinLevel); QFETCH(bool, strictMode); try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("relaxed-error-mode/relaxed-error-mode.qbs")); - params.setProductErrorMode(strictMode ? ErrorHandlingMode::Strict - : ErrorHandlingMode::Relaxed); - const TopLevelProjectPtr project = loader->loadProject(params); + defaultParameters.setProductErrorMode(strictMode ? ErrorHandlingMode::Strict + : ErrorHandlingMode::Relaxed); + resolveProject("relaxed-error-mode/relaxed-error-mode.qbs"); QVERIFY(!strictMode); const auto productMap = productsFromProject(project); const ResolvedProductConstPtr brokenProduct = productMap.value("broken"); @@ -2841,7 +3101,7 @@ void TestLanguage::relaxedErrorMode() QVERIFY(missingFile->enabled); QCOMPARE(missingFile->groups.size(), size_t(1)); QVERIFY(missingFile->groups.front()->enabled); - QCOMPARE(missingFile->groups.front()->allFiles().size(), size_t(2)); + QCOMPARE(missingFile->groups.front()->files.size(), size_t(2)); const ResolvedProductConstPtr fine = productMap.value("fine"); QVERIFY(fine->enabled); QCOMPARE(fine->allFiles().size(), size_t(1)); @@ -2863,10 +3123,7 @@ void TestLanguage::requiredAndNonRequiredDependencies() QFETCH(QString, projectFile); QFETCH(bool, exceptionExpected); try { - SetupProjectParameters params = defaultParameters; - const QString projectFilePath = "required-and-nonrequired-dependencies/" + projectFile; - params.setProjectFilePath(testProject(projectFilePath.toLocal8Bit())); - const TopLevelProjectConstPtr project = loader->loadProject(params); + resolveProject(qPrintable("required-and-nonrequired-dependencies/" + projectFile)); QVERIFY(!!project); QVERIFY(!exceptionExpected); } catch (const ErrorInfo &e) { @@ -2893,10 +3150,7 @@ void TestLanguage::requiredAndNonRequiredDependencies_data() void TestLanguage::suppressedAndNonSuppressedErrors() { try { - SetupProjectParameters params = defaultParameters; - const QString projectFilePath = "suppressed-and-non-suppressed-errors.qbs"; - params.setProjectFilePath(testProject(projectFilePath.toLocal8Bit())); - const TopLevelProjectConstPtr project = loader->loadProject(params); + resolveProject("suppressed-and-non-suppressed-errors.qbs"); QFAIL("failure expected"); } catch (const ErrorInfo &e) { QVERIFY2(e.toString().contains("easter bunny"), qPrintable(e.toString())); @@ -2908,12 +3162,10 @@ void TestLanguage::throwingProbe() { QFETCH(bool, enableProbe); try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("throwing-probe.qbs")); QVariantMap properties; properties.insert(QStringLiteral("products.theProduct.enableProbe"), enableProbe); - params.setOverriddenValues(properties); - const TopLevelProjectPtr project = loader->loadProject(params); + defaultParameters.setOverriddenValues(properties); + resolveProject("throwing-probe.qbs"); QVERIFY(!!project); QVERIFY(!enableProbe); } catch (const ErrorInfo &e) { @@ -2955,9 +3207,7 @@ void TestLanguage::recursiveProductDependencies() { bool exceptionCaught = false; try { - defaultParameters.setProjectFilePath( - testProject("recursive-dependencies/recursive-dependencies.qbs")); - const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + resolveProject("recursive-dependencies/recursive-dependencies.qbs"); QVERIFY(!!project); const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); QCOMPARE(products.size(), 4); @@ -3014,6 +3264,19 @@ void TestLanguage::fileTags() QCOMPARE(fileTags, expectedFileTags); } +void TestLanguage::useInternalProfile() +{ + const QString profile(QStringLiteral("theprofile")); + defaultParameters.setTopLevelProfile(profile); + resolveProject("use-internal-profile.qbs"); + QVERIFY(!!project); + QCOMPARE(project->profile(), profile); + QCOMPARE(project->products.size(), size_t(1)); + const ResolvedProductConstPtr product = project->products[0]; + QCOMPARE(product->profile(), profile); + QCOMPARE(product->moduleProperties->moduleProperty("dummy", "defines").toString(), profile); +} + void TestLanguage::wildcards_data() { QTest::addColumn<bool>("useGroup"); @@ -3182,8 +3445,8 @@ void TestLanguage::wildcards() QFile projectFile(projectFilePath); QVERIFY(projectFile.open(QIODevice::WriteOnly)); QTextStream s(&projectFile); - s << "import qbs.base 1.0" << endl << endl - << "Application {" << endl + using Qt::endl; + s << "Application {" << endl << " name: \"MyProduct\"" << endl; if (useGroup) { s << " Group {" << endl @@ -3201,7 +3464,7 @@ void TestLanguage::wildcards() } // create files - for (QString filePath : qAsConst(filesToCreate)) { + for (QString filePath : std::as_const(filesToCreate)) { filePath.prepend(m_wildcardsTestDirPath + '/'); QFileInfo fi(filePath); if (!QDir(fi.path()).exists()) @@ -3215,7 +3478,7 @@ void TestLanguage::wildcards() ResolvedProductPtr product; try { defaultParameters.setProjectFilePath(projectFilePath); - project = loader->loadProject(defaultParameters); + resolveProject(); QVERIFY(!!project); const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); product = products.value("MyProduct"); @@ -3234,10 +3497,10 @@ void TestLanguage::wildcards() group = product->groups.front(); } QVERIFY(!!group); - QCOMPARE(group->files.size(), size_t(0)); + QCOMPARE(group->files.size(), expected.size()); // we assume all files are wildcards QVERIFY(!!group->wildcards); QStringList actualFilePaths; - for (const SourceArtifactConstPtr &artifact : group->wildcards->files) { + for (const SourceArtifactPtr &artifact : group->files) { QString str = artifact->absoluteFilePath; int idx = str.indexOf(m_wildcardsTestDirPath); if (idx != -1) @@ -3261,4 +3524,3 @@ int main(int argc, char *argv[]) TestLanguage tl(ConsoleLogger::instance().logSink(), s.get()); return QTest::qExec(&tl, argc, argv); } - |