diff options
52 files changed, 3578 insertions, 3669 deletions
diff --git a/qbs-resources/imports/QbsAutotest.qbs b/qbs-resources/imports/QbsAutotest.qbs index 66e076677..7fc959394 100644 --- a/qbs-resources/imports/QbsAutotest.qbs +++ b/qbs-resources/imports/QbsAutotest.qbs @@ -9,7 +9,10 @@ QtApplication { Depends { name: "Qt.testlib" } Depends { name: "qbscore" } Depends { name: "qbsbuildconfig" } - cpp.includePaths: "../../../src" + cpp.includePaths: [ + "../../../src", + "../../../src/app/shared", // for the logger + ] cpp.cxxLanguageVersion: "c++11" destinationDirectory: "bin" Group { diff --git a/src/lib/corelib/buildgraph/artifact.h b/src/lib/corelib/buildgraph/artifact.h index 466169d46..ea0b6a6dc 100644 --- a/src/lib/corelib/buildgraph/artifact.h +++ b/src/lib/corelib/buildgraph/artifact.h @@ -64,7 +64,7 @@ using ArtifactSet = Set<Artifact *>; * * */ -class Artifact : public FileResourceBase, public BuildGraphNode +class QBS_AUTOTEST_EXPORT Artifact : public FileResourceBase, public BuildGraphNode { public: Artifact(); diff --git a/src/lib/corelib/buildgraph/buildgraph.h b/src/lib/corelib/buildgraph/buildgraph.h index 1d127af30..0620cebb2 100644 --- a/src/lib/corelib/buildgraph/buildgraph.h +++ b/src/lib/corelib/buildgraph/buildgraph.h @@ -41,6 +41,7 @@ #include "forward_decls.h" #include <language/forward_decls.h> +#include <tools/qbs_export.h> #include <QtCore/qstringlist.h> @@ -73,7 +74,7 @@ void dumpProductBuildData(const ResolvedProductConstPtr &product); bool findPath(BuildGraphNode *u, BuildGraphNode *v, QList<BuildGraphNode*> &path); -void connect(BuildGraphNode *p, BuildGraphNode *c); +void QBS_AUTOTEST_EXPORT connect(BuildGraphNode *p, BuildGraphNode *c); void loggedConnect(BuildGraphNode *u, BuildGraphNode *v, const Logger &logger); bool safeConnect(Artifact *u, Artifact *v, const Logger &logger); void removeGeneratedArtifactFromDisk(Artifact *artifact, const Logger &logger); diff --git a/src/lib/corelib/buildgraph/buildgraph.pri b/src/lib/corelib/buildgraph/buildgraph.pri index fabba661e..4229ad414 100644 --- a/src/lib/corelib/buildgraph/buildgraph.pri +++ b/src/lib/corelib/buildgraph/buildgraph.pri @@ -70,11 +70,6 @@ HEADERS += \ $$PWD/timestampsupdater.h \ $$PWD/transformer.h -qbs_enable_unit_tests { - HEADERS += $$PWD/tst_buildgraph.h - SOURCES += $$PWD/tst_buildgraph.cpp -} - !qbs_no_dev_install { buildgraph_headers.files = $$PWD/forward_decls.h buildgraph_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/buildgraph diff --git a/src/lib/corelib/buildgraph/cycledetector.h b/src/lib/corelib/buildgraph/cycledetector.h index a3275e0d5..d280f6ecd 100644 --- a/src/lib/corelib/buildgraph/cycledetector.h +++ b/src/lib/corelib/buildgraph/cycledetector.h @@ -49,7 +49,7 @@ namespace Internal { class BuildGraphNode; -class CycleDetector : private BuildGraphVisitor +class QBS_AUTOTEST_EXPORT CycleDetector : private BuildGraphVisitor { public: CycleDetector(const Logger &logger); diff --git a/src/lib/corelib/buildgraph/productbuilddata.h b/src/lib/corelib/buildgraph/productbuilddata.h index 1bac76593..6027de282 100644 --- a/src/lib/corelib/buildgraph/productbuilddata.h +++ b/src/lib/corelib/buildgraph/productbuilddata.h @@ -53,7 +53,7 @@ namespace Internal { class Logger; -class ProductBuildData : public PersistentObject +class QBS_AUTOTEST_EXPORT ProductBuildData : public PersistentObject { public: ~ProductBuildData(); diff --git a/src/lib/corelib/buildgraph/projectbuilddata.h b/src/lib/corelib/buildgraph/projectbuilddata.h index ebcdf53fc..12c943880 100644 --- a/src/lib/corelib/buildgraph/projectbuilddata.h +++ b/src/lib/corelib/buildgraph/projectbuilddata.h @@ -59,7 +59,7 @@ class FileDependency; class FileResourceBase; class ScriptEngine; -class ProjectBuildData : public PersistentObject +class QBS_AUTOTEST_EXPORT ProjectBuildData : public PersistentObject { public: ProjectBuildData(const ProjectBuildData *other = 0); diff --git a/src/lib/corelib/buildgraph/rescuableartifactdata.h b/src/lib/corelib/buildgraph/rescuableartifactdata.h index 1a6b33cdc..0058885a1 100644 --- a/src/lib/corelib/buildgraph/rescuableartifactdata.h +++ b/src/lib/corelib/buildgraph/rescuableartifactdata.h @@ -54,7 +54,7 @@ namespace qbs { namespace Internal { class PersistentPool; -class RescuableArtifactData +class QBS_AUTOTEST_EXPORT RescuableArtifactData { public: ~RescuableArtifactData(); diff --git a/src/lib/corelib/buildgraph/tst_buildgraph.cpp b/src/lib/corelib/buildgraph/tst_buildgraph.cpp deleted file mode 100644 index 9cc502cf4..000000000 --- a/src/lib/corelib/buildgraph/tst_buildgraph.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qbs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "tst_buildgraph.h" - -#include <buildgraph/artifact.h> -#include <buildgraph/buildgraph.h> -#include <buildgraph/cycledetector.h> -#include <buildgraph/productbuilddata.h> -#include <buildgraph/projectbuilddata.h> -#include <language/language.h> -#include <logging/logger.h> -#include <tools/error.h> - -#include <QtTest/qtest.h> - -namespace qbs { -namespace Internal { - -const TopLevelProjectPtr project = TopLevelProject::create(); - -TestBuildGraph::TestBuildGraph(ILogSink *logSink) : m_logSink(logSink) -{ - project->buildData.reset(new ProjectBuildData); -} - -void TestBuildGraph::initTestCase() -{ -} - -void TestBuildGraph::cleanupTestCase() -{ -} - - -bool TestBuildGraph::cycleDetected(const ResolvedProductConstPtr &product) -{ - try { - CycleDetector(Logger(m_logSink)).visitProduct(product); - return false; - } catch (const ErrorInfo &) { - return true; - } -} - -ResolvedProductConstPtr TestBuildGraph::productWithDirectCycle() -{ - const ResolvedProductPtr product = ResolvedProduct::create(); - product->project = project; - product->buildData.reset(new ProductBuildData); - Artifact * const root = new Artifact; - root->product = product; - Artifact * const child = new Artifact; - child->product = product; - product->buildData->roots.insert(root); - product->buildData->nodes << root << child; - qbs::Internal::connect(root, child); - qbs::Internal::connect(child, root); - return product; -} - -ResolvedProductConstPtr TestBuildGraph::productWithLessDirectCycle() -{ - const ResolvedProductPtr product = ResolvedProduct::create(); - product->project = project; - product->buildData.reset(new ProductBuildData); - Artifact * const root = new Artifact; - Artifact * const child = new Artifact; - Artifact * const grandchild = new Artifact; - root->product = product; - child->product = product; - grandchild->product = product; - product->buildData->roots << root; - product->buildData->nodes << root << child << grandchild; - qbs::Internal::connect(root, child); - qbs::Internal::connect(child, grandchild); - qbs::Internal::connect(grandchild, root); - return product; -} - -// root appears as a child, but in a different tree -ResolvedProductConstPtr TestBuildGraph::productWithNoCycle() -{ - const ResolvedProductPtr product = ResolvedProduct::create(); - product->project = project; - product->buildData.reset(new ProductBuildData); - Artifact * const root = new Artifact; - Artifact * const root2 = new Artifact; - root->product = product; - root2->product = product; - product->buildData->roots << root << root2; - product->buildData->nodes << root << root2; - qbs::Internal::connect(root2, root); - return product; -} - -void TestBuildGraph::testCycle() -{ - QVERIFY(cycleDetected(productWithDirectCycle())); - QVERIFY(cycleDetected(productWithLessDirectCycle())); - QVERIFY(!cycleDetected(productWithNoCycle())); -} - -} // namespace Internal -} // namespace qbs diff --git a/src/lib/corelib/corelib.pro b/src/lib/corelib/corelib.pro index 97093e319..8e53891fd 100644 --- a/src/lib/corelib/corelib.pro +++ b/src/lib/corelib/corelib.pro @@ -8,7 +8,6 @@ isEmpty(QBS_RELATIVE_LIBEXEC_PATH) { DEFINES += QBS_RELATIVE_LIBEXEC_PATH=\\\"$${QBS_RELATIVE_LIBEXEC_PATH}\\\" QT += core-private network script -qbs_enable_unit_tests:QT += testlib qbs_enable_project_file_updates: QT += gui INCLUDEPATH += $$PWD diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs index eac9e709c..6f470902a 100644 --- a/src/lib/corelib/corelib.qbs +++ b/src/lib/corelib/corelib.qbs @@ -4,7 +4,6 @@ QbsLibrary { Depends { name: "cpp" } Depends { name: "Qt"; submodules: ["core-private", "network", "script", "xml"] } Depends { condition: qbsbuildconfig.enableProjectFileUpdates; name: "Qt.gui" } - Depends { condition: qbsbuildconfig.enableUnitTests; name: "Qt.testlib" } Depends { condition: Qt.core.staticBuild; productTypes: ["qbsplugin"] } name: "qbscore" cpp.includePaths: base.concat([ @@ -13,13 +12,14 @@ QbsLibrary { ]) property stringList projectFileUpdateDefines: qbsbuildconfig.enableProjectFileUpdates ? ["QBS_ENABLE_PROJECT_FILE_UPDATES"] : [] + property stringList enableUnitTestsDefines: + qbsbuildconfig.enableUnitTests ? ["QBS_ENABLE_UNIT_TESTS"] : [] // TODO: Use Utilities.cStringQuote cpp.defines: base.concat([ 'QBS_RELATIVE_LIBEXEC_PATH="' + qbsbuildconfig.relativeLibexecPath + '"', "QBS_VERSION=\"" + version + "\"", "QT_CREATOR", "QML_BUILD_STATIC_LIB", // needed for QmlJS - "SRCDIR=\"" + path + "\"" - ]).concat(projectFileUpdateDefines) + ]).concat(projectFileUpdateDefines).concat(enableUnitTestsDefines) Properties { condition: qbs.targetOS.contains("windows") @@ -455,19 +455,6 @@ QbsLibrary { qbs.install: qbsbuildconfig.installApiHeaders qbs.installDir: headerInstallPrefix } - Group { - condition: qbsbuildconfig.enableUnitTests - name: "tests" - cpp.defines: outer.filter(function(def) { return def !== "QT_NO_CAST_FROM_ASCII"; }) - files: [ - "buildgraph/tst_buildgraph.cpp", - "buildgraph/tst_buildgraph.h", - "language/tst_language.cpp", - "language/tst_language.h", - "tools/tst_tools.h", - "tools/tst_tools.cpp" - ] - } Export { Depends { name: "cpp" } cpp.defines: product.projectFileUpdateDefines diff --git a/src/lib/corelib/language/evaluator.h b/src/lib/corelib/language/evaluator.h index b602326ca..c86f0375d 100644 --- a/src/lib/corelib/language/evaluator.h +++ b/src/lib/corelib/language/evaluator.h @@ -55,7 +55,7 @@ class FileTags; class Logger; class ScriptEngine; -class Evaluator : private ItemObserver +class QBS_AUTOTEST_EXPORT Evaluator : private ItemObserver { friend class SVConverter; diff --git a/src/lib/corelib/language/filecontext.h b/src/lib/corelib/language/filecontext.h index 7a0511632..001e64066 100644 --- a/src/lib/corelib/language/filecontext.h +++ b/src/lib/corelib/language/filecontext.h @@ -53,7 +53,7 @@ class ItemPool; class FileContext : public FileContextBase { public: - static FileContextPtr create(); + static FileContextPtr QBS_AUTOTEST_EXPORT create(); void setContent(const QString &content) { m_content = content; } const QString &content() const { return m_content; } diff --git a/src/lib/corelib/language/identifiersearch.h b/src/lib/corelib/language/identifiersearch.h index 999366314..e466c576a 100644 --- a/src/lib/corelib/language/identifiersearch.h +++ b/src/lib/corelib/language/identifiersearch.h @@ -42,13 +42,14 @@ #include <parser/qmljsastfwd_p.h> #include <parser/qmljsastvisitor_p.h> +#include <tools/qbs_export.h> #include <QtCore/qmap.h> #include <QtCore/qstring.h> namespace qbs { namespace Internal { -class IdentifierSearch : private QbsQmlJS::AST::Visitor +class QBS_AUTOTEST_EXPORT IdentifierSearch : private QbsQmlJS::AST::Visitor { public: IdentifierSearch(); diff --git a/src/lib/corelib/language/item.h b/src/lib/corelib/language/item.h index 6e2621d07..3df4c5903 100644 --- a/src/lib/corelib/language/item.h +++ b/src/lib/corelib/language/item.h @@ -60,7 +60,7 @@ class ItemObserver; class ItemPool; class Logger; -class Item : public QbsQmlJS::Managed +class QBS_AUTOTEST_EXPORT Item : public QbsQmlJS::Managed { friend class ASTPropertiesItemHandler; friend class ItemPool; diff --git a/src/lib/corelib/language/itempool.h b/src/lib/corelib/language/itempool.h index 7ab62d6c1..a4837a057 100644 --- a/src/lib/corelib/language/itempool.h +++ b/src/lib/corelib/language/itempool.h @@ -41,6 +41,7 @@ #define QBS_ITEMPOOL_H #include <parser/qmljsmemorypool_p.h> +#include <tools/qbs_export.h> #include <QtCore/qlist.h> @@ -50,7 +51,7 @@ namespace Internal { class Item; enum class ItemType; -class ItemPool +class QBS_AUTOTEST_EXPORT ItemPool { Q_DISABLE_COPY(ItemPool) public: diff --git a/src/lib/corelib/language/language.h b/src/lib/corelib/language/language.h index b27fb7c71..f3c0f058e 100644 --- a/src/lib/corelib/language/language.h +++ b/src/lib/corelib/language/language.h @@ -252,7 +252,7 @@ private: void store(PersistentPool &pool) const; }; -class ResolvedGroup : public PersistentObject +class QBS_AUTOTEST_EXPORT ResolvedGroup : public PersistentObject { public: static GroupPtr create() { return GroupPtr(new ResolvedGroup); } @@ -393,7 +393,7 @@ private: class TopLevelProject; class ScriptEngine; -class ResolvedProduct : public PersistentObject +class QBS_AUTOTEST_EXPORT ResolvedProduct : public PersistentObject { public: static ResolvedProductPtr create() { return ResolvedProductPtr(new ResolvedProduct); } @@ -471,7 +471,7 @@ private: mutable std::mutex m_executablePathCacheLock; }; -class ResolvedProject : public PersistentObject +class QBS_AUTOTEST_EXPORT ResolvedProject : public PersistentObject { public: static ResolvedProjectPtr create() { return ResolvedProjectPtr(new ResolvedProject); } @@ -502,7 +502,7 @@ private: TopLevelProject *m_topLevelProject; }; -class TopLevelProject : public ResolvedProject +class QBS_AUTOTEST_EXPORT TopLevelProject : public ResolvedProject { friend class BuildGraphLoader; public: diff --git a/src/lib/corelib/language/language.pri b/src/lib/corelib/language/language.pri index c3bcbb674..d1961df9e 100644 --- a/src/lib/corelib/language/language.pri +++ b/src/lib/corelib/language/language.pri @@ -73,11 +73,6 @@ SOURCES += \ $$PWD/scriptimporter.cpp \ $$PWD/value.cpp -qbs_enable_unit_tests { - HEADERS += $$PWD/tst_language.h - SOURCES += $$PWD/tst_language.cpp -} - !qbs_no_dev_install { language_headers.files = $$PWD/forward_decls.h language_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/language diff --git a/src/lib/corelib/language/loader.h b/src/lib/corelib/language/loader.h index f923f1700..b94fb7553 100644 --- a/src/lib/corelib/language/loader.h +++ b/src/lib/corelib/language/loader.h @@ -52,7 +52,7 @@ class Logger; class ProgressObserver; class ScriptEngine; -class Loader +class QBS_AUTOTEST_EXPORT Loader { public: Loader(ScriptEngine *engine, const Logger &logger); diff --git a/src/lib/corelib/language/propertymapinternal.h b/src/lib/corelib/language/propertymapinternal.h index 9874f5546..69890fef3 100644 --- a/src/lib/corelib/language/propertymapinternal.h +++ b/src/lib/corelib/language/propertymapinternal.h @@ -42,12 +42,13 @@ #include "forward_decls.h" #include <tools/persistentobject.h> +#include <tools/qbs_export.h> #include <QtCore/qvariant.h> namespace qbs { namespace Internal { -class PropertyMapInternal : public PersistentObject +class QBS_AUTOTEST_EXPORT PropertyMapInternal : public PersistentObject { public: static PropertyMapPtr create() { return PropertyMapPtr(new PropertyMapInternal); } @@ -76,8 +77,9 @@ inline bool operator==(const PropertyMapInternal &lhs, const PropertyMapInternal return lhs.m_value == rhs.m_value; } -QVariant moduleProperty(const QVariantMap &properties, const QString &moduleName, - const QString &key); +QVariant QBS_AUTOTEST_EXPORT moduleProperty(const QVariantMap &properties, + const QString &moduleName, + const QString &key); } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/language/qualifiedid.h b/src/lib/corelib/language/qualifiedid.h index 007133be8..1415f668e 100644 --- a/src/lib/corelib/language/qualifiedid.h +++ b/src/lib/corelib/language/qualifiedid.h @@ -49,7 +49,7 @@ namespace qbs { namespace Internal { -class QualifiedId : public QStringList +class QBS_AUTOTEST_EXPORT QualifiedId : public QStringList { public: QualifiedId(); @@ -60,7 +60,7 @@ public: QString toString() const; }; -bool operator<(const QualifiedId &a, const QualifiedId &b); +bool QBS_AUTOTEST_EXPORT operator<(const QualifiedId &a, const QualifiedId &b); inline uint qHash(const QualifiedId &qid) { return qHash(qid.toString()); } using QualifiedIdSet = Set<QualifiedId>; diff --git a/src/lib/corelib/language/scriptengine.h b/src/lib/corelib/language/scriptengine.h index 732ce8a09..da0b6028c 100644 --- a/src/lib/corelib/language/scriptengine.h +++ b/src/lib/corelib/language/scriptengine.h @@ -76,7 +76,7 @@ public: }; using DubiousContextList = std::vector<DubiousContext>; -class ScriptEngine : public QScriptEngine +class QBS_AUTOTEST_EXPORT ScriptEngine : public QScriptEngine { Q_OBJECT public: diff --git a/src/lib/corelib/language/tst_language.cpp b/src/lib/corelib/language/tst_language.cpp deleted file mode 100644 index 64a0e9790..000000000 --- a/src/lib/corelib/language/tst_language.cpp +++ /dev/null @@ -1,2444 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qbs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#undef QT_NO_CAST_FROM_ASCII // I am qmake, and I approve this hack. - -#include "tst_language.h" - -#include "../../../../tests/auto/shared.h" - -#include <language/evaluator.h> -#include <language/filecontext.h> -#include <language/identifiersearch.h> -#include <language/item.h> -#include <language/itempool.h> -#include <language/language.h> -#include <language/propertymapinternal.h> -#include <language/scriptengine.h> -#include <language/value.h> -#include <parser/qmljslexer_p.h> -#include <parser/qmljsparser_p.h> -#include <tools/scripttools.h> -#include <tools/error.h> -#include <tools/fileinfo.h> -#include <tools/hostosinfo.h> -#include <tools/jsliterals.h> -#include <tools/profile.h> -#include <tools/settings.h> - -#include <QtCore/qprocess.h> - -#include <algorithm> -#include <utility> -#include <vector> - -Q_DECLARE_METATYPE(QList<bool>) - -namespace qbs { -namespace Internal { -static QString testDataDir() { - return FileInfo::resolvePath(QLatin1String(SRCDIR), - QLatin1String("../../../tests/auto/language/testdata")); -} -static QString testProject(const char *fileName) { - return testDataDir() + QLatin1Char('/') + QLatin1String(fileName); -} - -TestLanguage::TestLanguage(ILogSink *logSink, Settings *settings) - : m_logSink(logSink) - , m_settings(settings) - , m_wildcardsTestDirPath(QDir::tempPath() + QLatin1String("/_wildcards_test_dir_")) -{ - qsrand(QTime::currentTime().msec()); - qRegisterMetaType<QList<bool> >("QList<bool>"); - defaultParameters.setBuildRoot("/some/build/directory"); - defaultParameters.setPropertyCheckingMode(ErrorHandlingMode::Strict); - defaultParameters.setSettingsDirectory(m_settings->baseDirectory()); -} - -TestLanguage::~TestLanguage() -{ -} - -QHash<QString, ResolvedProductPtr> TestLanguage::productsFromProject(ResolvedProjectPtr project) -{ - QHash<QString, ResolvedProductPtr> result; - foreach (const ResolvedProductPtr &product, project->allProducts()) - result.insert(product->name, product); - return result; -} - -ResolvedModuleConstPtr TestLanguage::findModuleByName(ResolvedProductPtr product, const QString &name) -{ - foreach (const ResolvedModuleConstPtr &module, product->modules) - if (module->name == name) - return module; - return ResolvedModuleConstPtr(); -} - -QVariant TestLanguage::productPropertyValue(ResolvedProductPtr product, QString propertyName) -{ - QStringList propertyNameComponents = propertyName.split(QLatin1Char('.')); - if (propertyNameComponents.count() > 1) { - propertyNameComponents.prepend(QLatin1String("modules")); - return product->moduleProperties->property(propertyNameComponents); - } - return getConfigProperty(product->productProperties, propertyNameComponents); -} - -void TestLanguage::handleInitCleanupDataTags(const char *projectFileName, bool *handled) -{ - const QByteArray dataTag = QTest::currentDataTag(); - if (dataTag == "init") { - *handled = true; - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject(projectFileName)); - project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); - } else if (dataTag == "cleanup") { - *handled = true; - project.reset(); - } else { - *handled = false; - } -} - -void TestLanguage::init() -{ - m_logSink->setLogLevel(LoggerInfo); -} - -#define HANDLE_INIT_CLEANUP_DATATAGS(fn) {\ - bool handled;\ - handleInitCleanupDataTags(fn, &handled);\ - if (handled)\ - return;\ - QVERIFY(!!project);\ -} - -void TestLanguage::initTestCase() -{ - m_logger = Logger(m_logSink); - m_engine = new ScriptEngine(m_logger, EvalContext::PropertyEvaluation, this); - loader = new Loader(m_engine, m_logger); - loader->setSearchPaths(QStringList() - << QLatin1String(SRCDIR "/../../../share/qbs")); - defaultParameters.setTopLevelProfile(profileName()); - defaultParameters.setConfigurationName("default"); - defaultParameters.expandBuildConfiguration(); - defaultParameters.setEnvironment(QProcessEnvironment::systemEnvironment()); - QVERIFY(QFileInfo(m_wildcardsTestDirPath).isAbsolute()); -} - -void TestLanguage::cleanupTestCase() -{ - delete loader; -} - -void TestLanguage::baseProperty() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("baseproperty.qbs")); - project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - ResolvedProductPtr product = products.value("product1"); - QVERIFY(!!product); - QVariantMap cfg = product->productProperties; - QCOMPARE(cfg.value("narf").toStringList(), QStringList() << "boo"); - QCOMPARE(cfg.value("zort").toStringList(), QStringList() << "bar" << "boo"); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::baseValidation() -{ - qbs::SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("base-validate/base-validate.qbs")); - try { - project = loader->loadProject(params); - QVERIFY2(false, "exception expected"); - } catch (const qbs::ErrorInfo &e) { - QVERIFY2(e.toString().contains("Parent succeeded, child failed."), - qPrintable(e.toString())); - } -} - -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); - QVERIFY(!!project); - QCOMPARE(project->projectProperties().value("someStrings").toStringList(), - QStringList() << "foo" << "bar" << "baz"); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::builtinFunctionInSearchPathsProperty() -{ - bool exceptionCaught = false; - try { - SetupProjectParameters parameters = defaultParameters; - parameters.setProjectFilePath(testProject("builtinFunctionInSearchPathsProperty.qbs")); - QVERIFY(!!loader->loadProject(parameters)); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::chainedProbes() -{ - bool exceptionCaught = false; - try { - SetupProjectParameters parameters = defaultParameters; - parameters.setProjectFilePath(testProject("chained-probes/chained-probes.qbs")); - const TopLevelProjectConstPtr project = loader->loadProject(parameters); - QVERIFY(!!project); - QCOMPARE(project->products.count(), 1); - const QString prop2Val = project->products.first()->moduleProperties - ->moduleProperty("m", "prop2").toString(); - QCOMPARE(prop2Val, QLatin1String("probe1Valprobe2Val")); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); - -} - -void TestLanguage::versionCompare() -{ - bool exceptionCaught = false; - try { - SetupProjectParameters parameters = defaultParameters; - parameters.setProjectFilePath(testProject("versionCompare.qbs")); - QVERIFY(!!loader->loadProject(parameters)); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::canonicalArchitecture() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("canonicalArchitecture.qbs")); - project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - ResolvedProductPtr product = products.value(QLatin1String("x86")); - QVERIFY(!!product); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::rfc1034Identifier() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("rfc1034identifier.qbs")); - project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - ResolvedProductPtr product = products.value(QLatin1String("this-has-special-characters-" - "uh-oh-Undersc0r3s-Are.Bad")); - QVERIFY(!!product); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::conditionalDepends() -{ - bool exceptionCaught = false; - ResolvedProductPtr product; - ResolvedModuleConstPtr dependency; - try { - defaultParameters.setProjectFilePath(testProject("conditionaldepends.qbs")); - project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - - product = products.value("conditionaldepends_derived"); - QVERIFY(!!product); - dependency = findModuleByName(product, "dummy"); - QVERIFY(!!dependency); - - product = products.value("conditionaldepends_derived_false"); - QVERIFY(!!product); - dependency = findModuleByName(product, "dummy"); - QCOMPARE(dependency, ResolvedModuleConstPtr()); - - product = products.value("product_props_true"); - QVERIFY(!!product); - dependency = findModuleByName(product, "dummy"); - QVERIFY(!!dependency); - - product = products.value("product_props_false"); - QVERIFY(!!product); - dependency = findModuleByName(product, "dummy"); - QCOMPARE(dependency, ResolvedModuleConstPtr()); - - product = products.value("project_props_true"); - QVERIFY(!!product); - dependency = findModuleByName(product, "dummy"); - QVERIFY(!!dependency); - - product = products.value("project_props_false"); - QVERIFY(!!product); - dependency = findModuleByName(product, "dummy"); - QCOMPARE(dependency, ResolvedModuleConstPtr()); - - product = products.value("module_props_true"); - QVERIFY(!!product); - dependency = findModuleByName(product, "dummy2"); - QVERIFY(!!dependency); - dependency = findModuleByName(product, "dummy"); - QVERIFY(!!dependency); - - product = products.value("module_props_false"); - QVERIFY(!!product); - dependency = findModuleByName(product, "dummy2"); - QVERIFY(!!dependency); - dependency = findModuleByName(product, "dummy"); - QCOMPARE(dependency, ResolvedModuleConstPtr()); - - product = products.value("contradictory_conditions1"); - QVERIFY(!!product); - dependency = findModuleByName(product, "dummy"); - QVERIFY(!!dependency); - - product = products.value("contradictory_conditions2"); - QVERIFY(!!product); - dependency = findModuleByName(product, "dummy"); - QVERIFY(!!dependency); - - product = products.value("unknown_dependency_condition_false"); - QVERIFY(!!product); - dependency = findModuleByName(product, "doesonlyexistifhellfreezesover"); - QVERIFY(!dependency); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -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); - QCOMPARE(productEnabled, false); - QVERIFY(!!project); - QCOMPARE(project->products.count(), 1); - const ResolvedProductConstPtr theProduct = productsFromProject(project).value("theProduct"); - QVERIFY(!!theProduct); - QCOMPARE(theProduct->enabled, false); - } catch (const ErrorInfo &e) { - if (!productEnabled) - qDebug() << e.toString(); - QCOMPARE(productEnabled, true); - } -} - -void TestLanguage::delayedError_data() -{ - QTest::addColumn<QString>("projectFileName"); - QTest::addColumn<bool>("productEnabled"); - QTest::newRow("product enabled, module validation error") - << "delayed-error/validation.qbs" << true; - QTest::newRow("product disabled, module validation error") - << "delayed-error/validation.qbs" << false; - QTest::newRow("product enabled, module not found") - << "delayed-error/nonexisting.qbs" << true; - QTest::newRow("product disabled, module not found") - << "delayed-error/nonexisting.qbs" << false; -} - -void qbs::Internal::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); - p2.p.setValue("qbs.architecture", "arch2"); - QVariantMap overriddenValues; - overriddenValues.insert("project.profile1", "p1"); - overriddenValues.insert("project.profile2", "p2"); - params.setOverriddenValues(overriddenValues); - project = loader->loadProject(params); - QVERIFY(!!project); - QCOMPARE(project->products.count(), 3); - const ResolvedProductConstPtr mainProduct = productsFromProject(project).value("main"); - QVERIFY(!!mainProduct); - QCOMPARE(mainProduct->dependencies.count(), 2); - foreach (const ResolvedProductConstPtr &p, mainProduct->dependencies) { - QCOMPARE(p->name, QLatin1String("dep")); - QVERIFY(p->profile == "p1" || p->profile == "p2"); - } - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::derivedSubProject() -{ - bool exceptionCaught = false; - try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("derived-sub-project/project.qbs")); - const TopLevelProjectPtr project = loader->loadProject(params); - QVERIFY(!!project); - const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 1); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::enumerateProjectProperties() -{ - bool exceptionCaught = false; - try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("enum-project-props.qbs")); - auto project = loader->loadProject(params); - QVERIFY(!!project); - auto products = productsFromProject(project); - QCOMPARE(products.count(), 1); - auto product = products.values().first(); - auto files = product->groups.first()->allFiles(); - QCOMPARE(product->groups.count(), 1); - QCOMPARE(files.count(), 1); - auto fileName = FileInfo::fileName(files.first()->absoluteFilePath); - QCOMPARE(fileName, QString("dummy.txt")); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -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); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 2); - const ResolvedProductPtr product = products.value("egon"); - QVERIFY(!!product); - QStringList propertyName = QStringList() << "modules" << "lower" << "prop2"; - QVariant propertyValue = product->moduleProperties->property(propertyName); - QFETCH(QVariant, expectedProp2Value); - QCOMPARE(propertyValue, expectedProp2Value); - propertyName = QStringList() << "modules" << "lower" << "listProp"; - propertyValue = product->moduleProperties->property(propertyName); - QFETCH(QVariant, expectedListPropValue); - QCOMPARE(propertyValue.toStringList(), expectedListPropValue.toStringList()); - } - catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::defaultValue_data() -{ - QTest::addColumn<QString>("prop1Value"); - QTest::addColumn<QVariant>("expectedProp2Value"); - QTest::addColumn<QVariant>("expectedListPropValue"); - QTest::newRow("controlling property with random value") << "random" << QVariant("withoutBlubb") - << QVariant(QStringList({"other"})); - QTest::newRow("controlling property with blubb value") << "blubb" << QVariant("withBlubb") - << QVariant(QStringList({"blubb", "other"})); - QTest::newRow("controlling property with egon value") << "egon" << QVariant("withEgon") - << QVariant(QStringList({"egon", "other"})); - QTest::newRow("controlling property not overwritten") << "" << QVariant("withBlubb") - << QVariant(QStringList({"blubb", "other"})); -} - -void TestLanguage::environmentVariable() -{ - bool exceptionCaught = false; - try { - // Create new environment: - const QString varName = QLatin1String("PRODUCT_NAME"); - const QString productName = QLatin1String("MyApp") + QString::number(qrand()); - 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 - - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - ResolvedProductPtr product = products.value(productName); - QVERIFY(!!product); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::erroneousFiles_data() -{ - QTest::addColumn<QString>("errorMessage"); - QTest::newRow("unknown_module") - << "Dependency 'neitherModuleNorProduct' not found"; - QTest::newRow("multiple_exports") - << "Multiple Export items in one product are prohibited."; - QTest::newRow("multiple_properties_in_subproject") - << "Multiple instances of item 'Properties' found where at most one " - "is allowed."; - QTest::newRow("importloop1") - << "Loop detected when importing"; - QTest::newRow("nonexistentouter") - << "Can't find variable: outer"; - QTest::newRow("invalid_file") - << "does not exist"; - QTest::newRow("invalid-parameter-rhs") - << "ReferenceError: Can't find variable: access"; - QTest::newRow("invalid-parameter-type") - << "Value assigned to property 'stringParameter' does not have type 'string'."; - QTest::newRow("invalid_property_type") - << "Unknown type 'nonsense' in property declaration."; - QTest::newRow("reserved_name_in_import") - << "Cannot reuse the name of built-in extension 'TextFile'."; - QTest::newRow("throw_in_property_binding") - << "something is wrong"; - QTest::newRow("dependency_cycle") - << "Cyclic dependencies detected."; - QTest::newRow("dependency_cycle2") - << "Cyclic dependencies detected."; - QTest::newRow("dependency_cycle3") - << "Cyclic dependencies detected."; - QTest::newRow("dependency_cycle4") - << "Cyclic dependencies detected."; - QTest::newRow("references_cycle") - << "Cycle detected while referencing file 'references_cycle.qbs'."; - QTest::newRow("subproject_cycle") - << "Cycle detected while loading subproject file 'subproject_cycle.qbs'."; - QTest::newRow("invalid_stringlist_element") - << "Element at index 1 of list property 'files' does not have string type."; - QTest::newRow("undefined_stringlist_element") - << "Element at index 1 of list property 'files' is undefined. String expected."; - QTest::newRow("undeclared_item") - << "Item 'cpp' is not declared."; - QTest::newRow("undeclared-parameter1") - << "Parameter 'prefix2.suffix.nope' is not declared."; - QTest::newRow("undeclared-parameter2") - << "Cannot set parameter 'foo.bar', " - "because 'myproduct' does not have a dependency on 'foo'."; - QTest::newRow("undeclared_property_wrapper") - << "Property 'doesntexist' is not declared."; - QTest::newRow("undeclared_property_in_export_item") - << "Property 'blubb' is not declared."; - QTest::newRow("undeclared_property_in_export_item2") - << "Item 'something' is not declared."; - QTest::newRow("undeclared_property_in_export_item3") - << "Property 'blubb' is not declared."; - QTest::newRow("unknown_item_type") - << "Unexpected item type 'Narf'"; - QTest::newRow("invalid_child_item_type") - << "Items of type 'Project' cannot contain items of type 'Depends'."; - QTest::newRow("conflicting_fileTagsFilter") - << "Conflicting fileTagsFilter in Group items"; - QTest::newRow("duplicate_sources") - << "Duplicate source file '.*main.cpp'" - ".*duplicate_sources.qbs:4:12.*duplicate_sources.qbs:6:16."; - QTest::newRow("duplicate_sources_wildcards") - << "Duplicate source file '.*duplicate_sources_wildcards.qbs'" - ".*duplicate_sources_wildcards.qbs:4:12" - ".*duplicate_sources_wildcards.qbs:6:16."; - QTest::newRow("oldQbsVersion") - << "The project requires at least qbs version \\d+\\.\\d+.\\d+, " - "but this is qbs version " QBS_VERSION "."; - QTest::newRow("wrongQbsVersionFormat") - << "The value '.*' of Project.minimumQbsVersion is not a valid version string."; - QTest::newRow("properties-item-with-invalid-condition") - << "TypeError: Result of expression 'cpp.nonexistingproperty'"; - 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?"; - QTest::newRow("wrong-toplevel-item") - << "wrong-toplevel-item.qbs:3:1.*The top-level item must be of type 'Project' or " - "'Product', but it is of type 'Artifact'."; - QTest::newRow("module-depends-on-product") - << "module-with-product-dependency.qbs:4:5.*Modules cannot depend on products."; - QTest::newRow("overwrite-inherited-readonly-property") - << "overwrite-inherited-readonly-property.qbs" - ":4:21.*Cannot set read-only property 'readOnlyString'."; - QTest::newRow("overwrite-readonly-module-property") - << "overwrite-readonly-module-property.qbs" - ":5:30.*Cannot set read-only property 'readOnlyString'."; - QTest::newRow("mismatching-multiplex-dependency") - << "mismatching-multiplex-dependency.qbs:9:5 Dependency from product 'b' to " - "product 'a' not fulfilled.\nNo product 'a' found with a matching multiplex " - "configuration:\n\tqbs.architecture: mips"; -} - -void TestLanguage::erroneousFiles() -{ - QFETCH(QString, errorMessage); - QString fileName = QString::fromLocal8Bit(QTest::currentDataTag()) + QLatin1String(".qbs"); - try { - defaultParameters.setProjectFilePath(testProject("/erroneous/") + fileName); - loader->loadProject(defaultParameters); - } catch (const ErrorInfo &e) { - if (!e.toString().contains(QRegExp(errorMessage))) { - qDebug() << "Message: " << e.toString(); - qDebug() << "Expected: " << errorMessage; - QFAIL("Unexpected error message."); - } - return; - } - QEXPECT_FAIL("undeclared_property_in_Properties_item", "Too expensive to check", Continue); - QVERIFY(!"No error thrown on invalid input."); -} - -void TestLanguage::exports() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("exports.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 17); - ResolvedProductPtr product; - product = products.value("myapp"); - QVERIFY(!!product); - QStringList propertyName = QStringList() << "modules" << "dummy" << "defines"; - QVariant propertyValue = product->moduleProperties->property(propertyName); - QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYAPP" << "USE_MYLIB" - << "USE_MYLIB2"); - propertyName = QStringList() << "modules" << "dummy" << "includePaths"; - QVariantList propertyValues = product->moduleProperties->property(propertyName).toList(); - QCOMPARE(propertyValues.count(), 3); - QVERIFY(propertyValues.at(0).toString().endsWith("/app")); - QVERIFY(propertyValues.at(1).toString().endsWith("/subdir/lib")); - QVERIFY(propertyValues.at(2).toString().endsWith("/subdir2/lib")); - - QCOMPARE(product->moduleProperties->moduleProperty("dummy", "productName").toString(), - QString("myapp")); - - product = products.value("mylib"); - QVERIFY(!!product); - propertyName = QStringList() << "modules" << "dummy" << "defines"; - propertyValue = product->moduleProperties->property(propertyName); - QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYLIB"); - - product = products.value("mylib2"); - QVERIFY(!!product); - propertyName = QStringList() << "modules" << "dummy" << "defines"; - propertyValue = product->moduleProperties->property(propertyName); - QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYLIB2"); - - product = products.value("A"); - QVERIFY(!!product); - QVERIFY(product->dependencies.contains(products.value("B"))); - QVERIFY(product->dependencies.contains(products.value("C"))); - QVERIFY(product->dependencies.contains(products.value("D"))); - product = products.value("B"); - QVERIFY(!!product); - QVERIFY(product->dependencies.isEmpty()); - product = products.value("C"); - QVERIFY(!!product); - QVERIFY(product->dependencies.isEmpty()); - product = products.value("D"); - QVERIFY(!!product); - QVERIFY(product->dependencies.isEmpty()); - - product = products.value("myapp2"); - QVERIFY(!!product); - propertyName = QStringList() << "modules" << "dummy" << "cFlags"; - propertyValue = product->moduleProperties->property(propertyName); - QCOMPARE(propertyValue.toStringList(), QStringList() - << "BASE_PRODUCTWITHINHERITEDEXPORTITEM" - << "PRODUCT_PRODUCTWITHINHERITEDEXPORTITEM"); - propertyName = QStringList() << "modules" << "dummy" << "cxxFlags"; - propertyValue = product->moduleProperties->property(propertyName); - QCOMPARE(propertyValue.toStringList(), QStringList() << "-bar"); - propertyName = QStringList() << "modules" << "dummy" << "defines"; - propertyValue = product->moduleProperties->property(propertyName); - QCOMPARE(propertyValue.toStringList(), QStringList() << "ABC"); - QCOMPARE(product->moduleProperties->moduleProperty("dummy", "productName").toString(), - QString("myapp2")); - QCOMPARE(product->moduleProperties->moduleProperty("dummy", - "upperCaseProductName").toString(), QString("MYAPP2")); - - // Check whether we're returning incorrect cached values. - product = products.value("myapp3"); - QVERIFY(!!product); - QCOMPARE(product->moduleProperties->moduleProperty("dummy", "productName").toString(), - QString("myapp3")); - QCOMPARE(product->moduleProperties->moduleProperty("dummy", - "upperCaseProductName").toString(), QString("MYAPP3")); - - // Verify we refer to the right "project" variable. - product = products.value("sub p2"); - QVERIFY(!!product); - QCOMPARE(product->moduleProperties->moduleProperty("dummy", "someString").toString(), - QString("sub1")); - - product = products.value("libE"); - QVERIFY(!!product); - propertyName = QStringList() << "modules" << "dummy" << "defines"; - propertyValue = product->moduleProperties->property(propertyName); - QCOMPARE(propertyValue.toStringList(), - QStringList() << "LIBA" << "LIBB" << "LIBC" << "LIBD"); - propertyName = QStringList() << "modules" << "dummy" << "productName"; - propertyValue = product->moduleProperties->property(propertyName); - QCOMPARE(propertyValue.toString(), QString("libE")); - } - catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::fileContextProperties() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("filecontextproperties.qbs")); - project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - ResolvedProductPtr product = products.value("product1"); - QVERIFY(!!product); - QVariantMap cfg = product->productProperties; - QCOMPARE(cfg.value("narf").toString(), defaultParameters.projectFilePath()); - QString dirPath = QFileInfo(defaultParameters.projectFilePath()).absolutePath(); - QCOMPARE(cfg.value("zort").toString(), dirPath); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::getNativeSetting() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("getNativeSetting.qbs")); - project = loader->loadProject(defaultParameters); - - QString expectedTargetName; - if (HostOsInfo::isMacosHost()) - expectedTargetName = QLatin1String("Mac OS X"); - else if (HostOsInfo::isWindowsHost()) - expectedTargetName = QLatin1String("Windows"); - else - expectedTargetName = QLatin1String("Unix"); - - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products; - for (const ResolvedProductPtr &product : project->allProducts()) - products.insert(product->targetName, product); - ResolvedProductPtr product = products.value(expectedTargetName); - QVERIFY(!!product); - ResolvedProductPtr product2 = products.value(QLatin1String("fallback")); - QVERIFY(!!product2); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::groupConditions_data() -{ - QTest::addColumn<int>("groupCount"); - QTest::addColumn<QList<bool> >("groupEnabled"); - QTest::newRow("init") << 0 << QList<bool>(); - QTest::newRow("no_condition_no_group") - << 1 << (QList<bool>() << true); - QTest::newRow("no_condition") - << 2 << (QList<bool>() << true << true); - QTest::newRow("true_condition") - << 2 << (QList<bool>() << true << true); - QTest::newRow("false_condition") - << 2 << (QList<bool>() << true << false); - QTest::newRow("true_condition_from_product") - << 2 << (QList<bool>() << true << true); - QTest::newRow("true_condition_from_project") - << 2 << (QList<bool>() << true << true); - QTest::newRow("condition_accessing_module_property") - << 2 << (QList<bool>() << true << false); - QTest::newRow("cleanup") << 0 << QList<bool>(); -} - -void TestLanguage::groupConditions() -{ - HANDLE_INIT_CLEANUP_DATATAGS("groupconditions.qbs"); - QFETCH(int, groupCount); - QFETCH(QList<bool>, groupEnabled); - QCOMPARE(groupCount, groupEnabled.count()); - const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - const QString productName = QString::fromLocal8Bit(QTest::currentDataTag()); - ResolvedProductPtr product = products.value(productName); - QVERIFY(!!product); - QCOMPARE(product->name, productName); - QCOMPARE(product->groups.count(), groupCount); - for (int i = 0; i < groupCount; ++i) { - if (product->groups.at(i)->enabled != groupEnabled.at(i)) { - QFAIL(qPrintable( - QString("groups.at(%1)->enabled != %2").arg(i).arg(groupEnabled.at(i)))); - } - } -} - -void TestLanguage::groupName() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("groupname.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 2); - - ResolvedProductPtr product = products.value("MyProduct"); - QVERIFY(!!product); - QCOMPARE(product->groups.count(), 2); - GroupConstPtr group = product->groups.at(0); - QVERIFY(!!group); - QCOMPARE(group->name, QString("MyProduct")); - group = product->groups.at(1); - QVERIFY(!!group); - QCOMPARE(group->name, QString("MyProduct.MyGroup")); - - product = products.value("My2ndProduct"); - QVERIFY(!!product); - QCOMPARE(product->groups.count(), 3); - group = product->groups.at(0); - QVERIFY(!!group); - QCOMPARE(group->name, QString("My2ndProduct")); - group = product->groups.at(1); - QVERIFY(!!group); - QCOMPARE(group->name, QString("My2ndProduct.MyGroup")); - group = product->groups.at(2); - QVERIFY(!!group); - QCOMPARE(group->name, QString("Group 2")); - } - catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::homeDirectory() -{ - try { - defaultParameters.setProjectFilePath(testProject("homeDirectory.qbs")); - ResolvedProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 1); - - ResolvedProductPtr product = products.value("home"); - QVERIFY(!!product); - - QDir dir = QDir::home(); - QCOMPARE(product->productProperties.value("home").toString(), dir.path()); - QCOMPARE(product->productProperties.value("homeSlash").toString(), dir.path()); - - dir.cdUp(); - QCOMPARE(product->productProperties.value("homeUp").toString(), dir.path()); - - dir = QDir::home(); - QCOMPARE(product->productProperties.value("homeFile").toString(), - dir.filePath("a")); - - QCOMPARE(product->productProperties.value("bogus1").toString(), - FileInfo::resolvePath(product->sourceDirectory, QLatin1String("a~b"))); - QCOMPARE(product->productProperties.value("bogus2").toString(), - FileInfo::resolvePath(product->sourceDirectory, QLatin1String("a/~/bb"))); - QCOMPARE(product->productProperties.value("user").toString(), - FileInfo::resolvePath(product->sourceDirectory, QLatin1String("~foo/bar"))); - } - catch (const ErrorInfo &e) { - qDebug() << e.toString(); - } -} - -void TestLanguage::identifierSearch_data() -{ - QTest::addColumn<bool>("expectedHasNarf"); - QTest::addColumn<bool>("expectedHasZort"); - QTest::addColumn<QString>("sourceCode"); - QTest::newRow("no narf, no zort") << false << false << QString( - "Product {\n" - " name: {\n" - " var foo = 'bar';\n" - " console.info(foo);\n" - " return foo;\n" - " }\n" - "}\n"); - QTest::newRow("narf, no zort") << true << false << QString( - "Product {\n" - " name: {\n" - " var foo = 'zort';\n" - " console.info(narf + foo);\n" - " return foo;\n" - " }\n" - "}\n"); - QTest::newRow("no narf, zort") << false << true << QString( - "Product {\n" - " name: {\n" - " var foo = 'narf';\n" - " console.info(zort + foo);\n" - " return foo;\n" - " }\n" - "}\n"); - QTest::newRow("narf, zort") << true << true << QString( - "Product {\n" - " name: {\n" - " var foo = narf;\n" - " foo = foo + zort;\n" - " return foo;\n" - " }\n" - "}\n"); - QTest::newRow("2 narfs, 1 zort") << true << true << QString( - "Product {\n" - " name: {\n" - " var foo = narf;\n" - " foo = narf + foo + zort;\n" - " return foo;\n" - " }\n" - "}\n"); -} - -void TestLanguage::identifierSearch() -{ - QFETCH(bool, expectedHasNarf); - QFETCH(bool, expectedHasZort); - QFETCH(QString, sourceCode); - - bool hasNarf = !expectedHasNarf; - bool hasZort = !expectedHasZort; - IdentifierSearch isearch; - isearch.add("narf", &hasNarf); - isearch.add("zort", &hasZort); - - QbsQmlJS::Engine engine; - QbsQmlJS::Lexer lexer(&engine); - lexer.setCode(sourceCode, 1); - QbsQmlJS::Parser parser(&engine); - QVERIFY(parser.parse()); - QVERIFY(parser.ast()); - isearch.start(parser.ast()); - QCOMPARE(hasNarf, expectedHasNarf); - QCOMPARE(hasZort, expectedHasZort); -} - -void TestLanguage::idUsage() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("idusage.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 4); - QVERIFY(products.contains("product1_1")); - QVERIFY(products.contains("product2_2")); - QVERIFY(products.contains("product3_3")); - ResolvedProductPtr product4 = products.value("product4_4"); - QVERIFY(!!product4); - QEXPECT_FAIL("", "QBS-1016", Continue); - QCOMPARE(product4->productProperties.value("productName").toString(), product4->name); - } - catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QVERIFY(!exceptionCaught); -} - -void TestLanguage::idUniqueness() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("id-uniqueness.qbs")); - loader->loadProject(defaultParameters); - } - catch (const ErrorInfo &e) { - exceptionCaught = true; - const QList<ErrorItem> items = e.items(); - QCOMPARE(items.count(), 3); - QCOMPARE(items.at(0).toString(), QString::fromUtf8("The id 'baseProduct' is not unique.")); - QVERIFY(items.at(1).toString().contains("id-uniqueness.qbs:6:5 First occurrence is here.")); - QVERIFY(items.at(2).toString().contains("id-uniqueness.qbs:9:5 Next occurrence is here.")); - } - QVERIFY(exceptionCaught); -} - -void TestLanguage::importCollection() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("import-collection/project.qbs")); - const TopLevelProjectPtr project = loader->loadProject(defaultParameters); - 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) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QVERIFY(!exceptionCaught); -} - -void TestLanguage::invalidBindingInDisabledItem() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("invalidBindingInDisabledItem.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 2); - } - catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QVERIFY(!exceptionCaught); -} - -class JSSourceValueCreator -{ - FileContextPtr m_fileContext; - QList<QString *> m_strings; -public: - JSSourceValueCreator(const FileContextPtr &fileContext) - : m_fileContext(fileContext) - { - } - - ~JSSourceValueCreator() - { - qDeleteAll(m_strings); - } - - JSSourceValuePtr create(const QString &sourceCode) - { - JSSourceValuePtr value = JSSourceValue::create(); - value->setFile(m_fileContext); - QString *str = new QString(sourceCode); - m_strings += str; - value->setSourceCode(QStringRef(str)); - return value; - } -}; - -void TestLanguage::itemPrototype() -{ - FileContextPtr fileContext = FileContext::create(); - fileContext->setFilePath("/dev/null"); - JSSourceValueCreator sourceValueCreator(fileContext); - ItemPool pool; - Item *proto = Item::create(&pool, ItemType::Product); - proto->setProperty("x", sourceValueCreator.create("1")); - proto->setProperty("y", sourceValueCreator.create("1")); - Item *item = Item::create(&pool, ItemType::Product); - item->setPrototype(proto); - 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); -} - -void TestLanguage::itemScope() -{ - FileContextPtr fileContext = FileContext::create(); - fileContext->setFilePath("/dev/null"); - JSSourceValueCreator sourceValueCreator(fileContext); - ItemPool pool; - Item *scope1 = Item::create(&pool, ItemType::Scope); - scope1->setProperty("x", sourceValueCreator.create("1")); - Item *scope2 = Item::create(&pool, ItemType::Scope); - scope2->setScope(scope1); - scope2->setProperty("y", sourceValueCreator.create("x + 1")); - Item *item = Item::create(&pool, ItemType::Scope); - 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); -} - -void TestLanguage::jsExtensions() -{ - QFile file(testProject("jsextensions.js")); - QVERIFY(file.open(QFile::ReadOnly)); - 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))); - } -} - -void TestLanguage::jsImportUsedInMultipleScopes_data() -{ - QTest::addColumn<QString>("buildVariant"); - QTest::addColumn<QString>("expectedProductName"); - QTest::newRow("debug") << QString("debug") << QString("MyProduct_debug"); - QTest::newRow("release") << QString("release") << QString("MyProduct"); -} - -void TestLanguage::jsImportUsedInMultipleScopes() -{ - QFETCH(QString, buildVariant); - QFETCH(QString, expectedProductName); - - bool exceptionCaught = false; - try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("jsimportsinmultiplescopes.qbs")); - params.setOverriddenValues({std::make_pair(QLatin1String("qbs.buildVariant"), - buildVariant)}); - params.expandBuildConfiguration(); - TopLevelProjectPtr project = loader->loadProject(params); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 1); - ResolvedProductPtr product = products.values().first(); - QVERIFY(!!product); - QCOMPARE(product->name, expectedProductName); - } - catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QVERIFY(!exceptionCaught); -} - -void TestLanguage::moduleProperties_data() -{ - QTest::addColumn<QString>("propertyName"); - QTest::addColumn<QVariant>("expectedValue"); - QTest::newRow("init") << QString() << QVariant(); - QTest::newRow("merge_lists") - << "defines" - << QVariant(QStringList() << "THE_PRODUCT" << "QT_CORE" << "QT_GUI" << "QT_NETWORK"); - QTest::newRow("merge_lists_and_values") - << "defines" - << QVariant(QStringList() << "THE_PRODUCT" << "QT_CORE" << "QT_GUI" << "QT_NETWORK"); - QTest::newRow("merge_lists_with_duplicates") - << "cxxFlags" - << QVariant(QStringList() << "-foo" << "BAR" << "-foo" << "BAZ"); - QTest::newRow("merge_lists_with_prototype_values") - << "rpaths" - << QVariant(QStringList() << "/opt/qt/lib" << "$ORIGIN"); - QTest::newRow("list_property_that_references_product") - << "listProp" - << QVariant(QStringList() << "x" << "123"); - QTest::newRow("list_property_depending_on_overridden_property") - << "listProp2" - << QVariant(QStringList() << "PRODUCT_STUFF" << "DEFAULT_STUFF" << "EXTRA_STUFF"); - QTest::newRow("overridden_list_property") - << "listProp" - << QVariant(QStringList() << "PRODUCT_STUFF"); - QTest::newRow("shadowed-list-property") - << "defines" - << QVariant(QStringList() << "MyProject" << "shadowed-list-property"); - QTest::newRow("shadowed-scalar-property") - << "someString" - << QVariant(QString("MyProject_shadowed-scalar-property")); - QTest::newRow("cleanup") << QString() << QVariant(); -} - -void TestLanguage::moduleProperties() -{ - HANDLE_INIT_CLEANUP_DATATAGS("moduleproperties.qbs"); - QFETCH(QString, propertyName); - QFETCH(QVariant, expectedValue); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - const QString productName = QString::fromLocal8Bit(QTest::currentDataTag()); - ResolvedProductPtr product = products.value(productName); - QVERIFY(!!product); - const QVariant value = product->moduleProperties->moduleProperty("dummy", propertyName); - QCOMPARE(value, expectedValue); -} - -void TestLanguage::modulePropertiesInGroups() -{ - defaultParameters.setProjectFilePath(testProject("modulepropertiesingroups.qbs")); - bool exceptionCaught = false; - try { - TopLevelProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - const ResolvedProductPtr product = products.value("grouptest"); - QVERIFY(!!product); - GroupConstPtr g1; - GroupConstPtr g11; - GroupConstPtr g12; - GroupConstPtr g2; - GroupConstPtr g21; - GroupConstPtr g211; - foreach (const GroupConstPtr &g, product->groups) { - if (g->name == "g1") - g1= g; - else if (g->name == "g2") - g2 = g; - else if (g->name == "g1.1") - g11 = g; - else if (g->name == "g1.2") - g12 = g; - else if (g->name == "g2.1") - g21 = g; - else if (g->name == "g2.1.1") - g211 = g; - } - QVERIFY(!!g1); - QVERIFY(!!g2); - QVERIFY(!!g11); - QVERIFY(!!g12); - QVERIFY(!!g21); - QVERIFY(!!g211); - - const QVariantMap productProps = product->moduleProperties->value(); - const auto &productGmod1List1 = moduleProperty(productProps, "gmod.gmod1", "gmod1_list1") - .toStringList(); - QCOMPARE(productGmod1List1, QStringList() << "gmod1_list1_proto" << "gmod1_string_proto"); - const auto &productGmod1List2 = moduleProperty(productProps, "gmod.gmod1", "gmod1_list2") - .toStringList(); - QCOMPARE(productGmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" - << "gmod1_list2_proto"); - const auto &productGmod1List3 = moduleProperty(productProps, "gmod.gmod1", "gmod1_list3") - .toStringList(); - QCOMPARE(productGmod1List3, QStringList() << "product" << "gmod1_string_proto"); - const int productP0 = moduleProperty(productProps, "gmod.gmod1", "p0").toInt(); - QCOMPARE(productP0, 1); - const int productDepProp = moduleProperty(productProps, "gmod.gmod1", "depProp").toInt(); - QCOMPARE(productDepProp, 0); - const auto &productGmod2String = moduleProperty(productProps, "gmod2", "gmod2_string") - .toString(); - QCOMPARE(productGmod2String, QString("gmod1_string_proto")); - const auto &productGmod2List = moduleProperty(productProps, "gmod2", "gmod2_list") - .toStringList(); - QCOMPARE(productGmod2List, QStringList() << "gmod1_string_proto" << "commonName_in_gmod1" - << "gmod4_string_proto_gmod3_string_proto" << "gmod3_string_proto" - << "gmod2_list_proto"); - - const QVariantMap g1Props = g1->properties->value(); - const auto &g1Gmod1List1 = moduleProperty(g1Props, "gmod.gmod1", "gmod1_list1") - .toStringList(); - QCOMPARE(g1Gmod1List1, QStringList() << "gmod1_list1_proto" << "g1"); - const auto &g1Gmod1List2 = moduleProperty(g1Props, "gmod.gmod1", "gmod1_list2") - .toStringList(); - QCOMPARE(g1Gmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" - << "gmod1_list2_proto" << "g1"); - const auto &g1Gmod1List3 = moduleProperty(g1Props, "gmod.gmod1", "gmod1_list3") - .toStringList(); - QCOMPARE(g1Gmod1List3, QStringList() << "product" << "g1"); - const int g1P0 = moduleProperty(g1Props, "gmod.gmod1", "p0").toInt(); - QCOMPARE(g1P0, 3); - const int g1DepProp = moduleProperty(g1Props, "gmod.gmod1", "depProp").toInt(); - QCOMPARE(g1DepProp, 1); - const auto &g1Gmod2String = moduleProperty(g1Props, "gmod2", "gmod2_string").toString(); - QCOMPARE(g1Gmod2String, QString("g1")); - const auto &g1Gmod2List = moduleProperty(g1Props, "gmod2", "gmod2_list") - .toStringList(); - QCOMPARE(g1Gmod2List, QStringList() << "g1" << "commonName_in_gmod1" << "g1_gmod4_g1_gmod3" - << "g1_gmod3" << "gmod2_list_proto"); - - const QVariantMap g11Props = g11->properties->value(); - const auto &g11Gmod1List1 = moduleProperty(g11Props, "gmod.gmod1", "gmod1_list1") - .toStringList(); - QCOMPARE(g11Gmod1List1, QStringList() << "gmod1_list1_proto" << "g1.1"); - const auto &g11Gmod1List2 = moduleProperty(g11Props, "gmod.gmod1", "gmod1_list2") - .toStringList(); - QCOMPARE(g11Gmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" - << "gmod1_list2_proto" << "g1" << "g1.1"); - const auto &g11Gmod1List3 = moduleProperty(g11Props, "gmod.gmod1", "gmod1_list3") - .toStringList(); - QCOMPARE(g11Gmod1List3, QStringList() << "product" << "g1.1"); - const int g11P0 = moduleProperty(g11Props, "gmod.gmod1", "p0").toInt(); - QCOMPARE(g11P0, 5); - const int g11DepProp = moduleProperty(g11Props, "gmod.gmod1", "depProp").toInt(); - QCOMPARE(g11DepProp, 2); - const auto &g11Gmod2String = moduleProperty(g11Props, "gmod2", "gmod2_string").toString(); - QCOMPARE(g11Gmod2String, QString("g1.1")); - const auto &g11Gmod2List = moduleProperty(g11Props, "gmod2", "gmod2_list") - .toStringList(); - QCOMPARE(g11Gmod2List, QStringList() << "g1.1" << "commonName_in_gmod1" - << "g1.1_gmod4_g1.1_gmod3" << "g1.1_gmod3" << "gmod2_list_proto"); - - const QVariantMap g12Props = g12->properties->value(); - const auto &g12Gmod1List1 = moduleProperty(g12Props, "gmod.gmod1", "gmod1_list1") - .toStringList(); - QCOMPARE(g12Gmod1List1, QStringList() << "gmod1_list1_proto" << "g1.2"); - const auto &g12Gmod1List2 = moduleProperty(g12Props, "gmod.gmod1", "gmod1_list2") - .toStringList(); - QCOMPARE(g12Gmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" - << "gmod1_list2_proto" << "g1" << "g1.2"); - const auto &g12Gmod1List3 = moduleProperty(g12Props, "gmod.gmod1", "gmod1_list3") - .toStringList(); - QCOMPARE(g12Gmod1List3, QStringList() << "product" << "g1.2"); - const int g12P0 = moduleProperty(g12Props, "gmod.gmod1", "p0").toInt(); - QCOMPARE(g12P0, 9); - const int g12DepProp = moduleProperty(g12Props, "gmod.gmod1", "depProp").toInt(); - QCOMPARE(g12DepProp, 1); - const auto &g12Gmod2String = moduleProperty(g12Props, "gmod2", "gmod2_string").toString(); - QCOMPARE(g12Gmod2String, QString("g1.2")); - const auto &g12Gmod2List = moduleProperty(g12Props, "gmod2", "gmod2_list") - .toStringList(); - QCOMPARE(g12Gmod2List, QStringList() << "g1.2" << "commonName_in_gmod1" - << "g1_gmod4_g1.2_gmod3" << "g1.2_gmod3" << "gmod2_list_proto"); - - const QVariantMap g2Props = g2->properties->value(); - const auto &g2Gmod1List1 = moduleProperty(g2Props, "gmod.gmod1", "gmod1_list1") - .toStringList(); - 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"); - const int g2P0 = moduleProperty(g2Props, "gmod.gmod1", "p0").toInt(); - QCOMPARE(g2P0, 6); - const int g2DepProp = moduleProperty(g2Props, "gmod.gmod1", "depProp").toInt(); - QCOMPARE(g2DepProp, 2); - const auto &g2Gmod2String = moduleProperty(g2Props, "gmod2", "gmod2_string").toString(); - QCOMPARE(g2Gmod2String, QString("g2")); - const auto &g2Gmod2List = moduleProperty(g2Props, "gmod2", "gmod2_list") - .toStringList(); - QCOMPARE(g2Gmod2List, QStringList() << "g2" << "commonName_in_gmod1" << "g2_gmod4_g2_gmod3" - << "g2_gmod3" << "gmod2_list_proto"); - - const QVariantMap g21Props = g21->properties->value(); - const auto &g21Gmod1List1 = moduleProperty(g21Props, "gmod.gmod1", "gmod1_list1") - .toStringList(); - 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); - QCOMPARE(g21Gmod1List2, QStringList() << "grouptest" << "g2.1" << "gmod1_list2_proto"); - const int g21P0 = moduleProperty(g21Props, "gmod.gmod1", "p0").toInt(); - QCOMPARE(g21P0, 6); - const int g21DepProp = moduleProperty(g21Props, "gmod.gmod1", "depProp").toInt(); - QCOMPARE(g21DepProp, 2); - const auto &g21Gmod2String = moduleProperty(g21Props, "gmod2", "gmod2_string").toString(); - 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); - QCOMPARE(g21Gmod2List, QStringList() << "g2" << "commonName_in_gmod1" - << "g2.1_gmod4_g2.1_gmod3" << "g2.1_gmod3" << "gmod2_list_proto"); - - const QVariantMap g211Props = g211->properties->value(); - const auto &g211Gmod1List1 = moduleProperty(g211Props, "gmod.gmod1", "gmod1_list1") - .toStringList(); - QCOMPARE(g211Gmod1List1, QStringList() << "gmod1_list1_proto" << "g2"); - const auto &g211Gmod1List2 = moduleProperty(g211Props, "gmod.gmod1", "gmod1_list2") - .toStringList(); - QCOMPARE(g211Gmod1List2, QStringList() << "g2.1.1"); - const int g211P0 = moduleProperty(g211Props, "gmod.gmod1", "p0").toInt(); - QCOMPARE(g211P0, 17); - const int g211DepProp = moduleProperty(g211Props, "gmod.gmod1", "depProp").toInt(); - QCOMPARE(g211DepProp, 2); - const auto &g211Gmod2String - = moduleProperty(g211Props, "gmod2", "gmod2_string").toString(); - QEXPECT_FAIL(0, "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); - QCOMPARE(g211Gmod2List, QStringList() << "g2.1.1" << "commonName_in_gmod1" - << "g2.1.1_gmod4_g2.1.1_gmod3" << "g2.1.1_gmod3" << "gmod2_list_proto"); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::modulePropertyOverridesPerProduct() -{ - bool exceptionCaught = false; - try { - SetupProjectParameters params = defaultParameters; - params.setOverriddenValues({ - std::make_pair("modules.dummy.someString", "m"), - std::make_pair("products.b.dummy.someString", "b"), - std::make_pair("products.c.dummy.someString", "c") - }); - params.setProjectFilePath( - testProject("module-property-overrides-per-product.qbs")); - const TopLevelProjectPtr project = loader->loadProject(params); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 3); - const ResolvedProductConstPtr a = products.value("a"); - QVERIFY(!!a); - const ResolvedProductConstPtr b = products.value("b"); - QVERIFY(!!b); - const ResolvedProductConstPtr c = products.value("c"); - QVERIFY(!!c); - - const auto propertyValue = [](const ResolvedProductConstPtr &p) -> QString - { - return p->moduleProperties->moduleProperty("dummy", "someString").toString(); - }; - - QCOMPARE(propertyValue(a), QString("m")); - QCOMPARE(propertyValue(b), QString("b")); - QCOMPARE(propertyValue(c), QString("c")); - } - catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::moduleScope() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("modulescope.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 1); - ResolvedProductPtr product = products.value("product1"); - QVERIFY(!!product); - - auto intModuleValue = [product] (const QString &name) -> int - { - return product->moduleProperties->moduleProperty("scopemod", name).toInt(); - }; - - QCOMPARE(intModuleValue("a"), 2); // overridden in module instance - QCOMPARE(intModuleValue("b"), 1); // genuine - QCOMPARE(intModuleValue("c"), 3); // genuine, dependent on overridden value - QCOMPARE(intModuleValue("d"), 2); // genuine, dependent on genuine value - QCOMPARE(intModuleValue("e"), 1); // genuine - 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) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); - -} - -void TestLanguage::modules_data() -{ - QTest::addColumn<QStringList>("expectedModulesInProduct"); - QTest::addColumn<QString>("expectedProductProperty"); - QTest::newRow("init") << QStringList(); - QTest::newRow("no_modules") - << (QStringList() << "qbs") - << QString(); - QTest::newRow("qt_core") - << (QStringList() << "qbs" << "dummy" << "dummyqt.core") - << QString("1.2.3"); - QTest::newRow("qt_gui") - << (QStringList() << "qbs" << "dummy" << "dummyqt.core" << "dummyqt.gui") - << QString("guiProperty"); - QTest::newRow("qt_gui_network") - << (QStringList() << "qbs" << "dummy" << "dummyqt.core" << "dummyqt.gui" - << "dummyqt.network") - << QString("guiProperty,networkProperty"); - QTest::newRow("deep_module_name") - << (QStringList() << "qbs" << "deepdummy.deep.moat" << "dummy") - << QString("abysmal"); - QTest::newRow("deep_module_name_submodule_syntax1") - << (QStringList() << "qbs" << "deepdummy.deep.moat" << "dummy") - << QString("abysmal"); - QTest::newRow("deep_module_name_submodule_syntax2") - << (QStringList() << "qbs" << "deepdummy.deep.moat" << "dummy") - << QString("abysmal"); - QTest::newRow("dummy_twice") - << (QStringList() << "qbs" << "dummy") - << QString(); - QTest::newRow("cleanup") << QStringList(); -} - -void TestLanguage::modules() -{ - HANDLE_INIT_CLEANUP_DATATAGS("modules.qbs"); - QFETCH(QStringList, expectedModulesInProduct); - QFETCH(QString, expectedProductProperty); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - const QString productName = QString::fromLocal8Bit(QTest::currentDataTag()); - ResolvedProductPtr product = products.value(productName); - QVERIFY(!!product); - QCOMPARE(product->name, productName); - QStringList modulesInProduct; - foreach (ResolvedModuleConstPtr m, product->modules) - modulesInProduct += m->name; - modulesInProduct.sort(); - expectedModulesInProduct.sort(); - QCOMPARE(modulesInProduct, expectedModulesInProduct); - QCOMPARE(product->productProperties.value("foo").toString(), expectedProductProperty); -} - -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; - if (!subProjectEnabled) - overriddenValues.insert("projects.subproject.condition", false); - else if (!dependeeEnabled) - overriddenValues.insert("products.dependee.condition", false); - params.setOverriddenValues(overriddenValues); - const TopLevelProjectPtr project = loader->loadProject(params); - QVERIFY(!!project); - const auto products = productsFromProject(project); - QCOMPARE(products.count(), 4 + !!subProjectEnabled); - const ResolvedProductConstPtr dependee = products.value("dependee"); - QCOMPARE(subProjectEnabled, !!dependee); - if (dependee) - QCOMPARE(dependeeEnabled, dependee->enabled); - const ResolvedProductConstPtr depender = products.value("depender"); - QVERIFY(!!depender); - const QStringList defines = depender->moduleProperties->moduleProperty("dummy", "defines") - .toStringList(); - QCOMPARE(subProjectEnabled && dependeeEnabled, defines.contains("WITH_DEPENDEE")); - - for (const auto &name : std::vector<const char *>({ "p3", "p2", "p1"})) { - const ResolvedProductConstPtr &product = products.value(name); - QVERIFY2(product, name); - QVERIFY2(!product->enabled, name); - } - } - catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::nonRequiredProducts_data() -{ - QTest::addColumn<bool>("subProjectEnabled"); - QTest::addColumn<bool>("dependeeEnabled"); - QTest::newRow("dependee enabled") << true << true; - QTest::newRow("dependee disabled") << true << false; - QTest::newRow("sub project disabled") << false << true; -} - -void TestLanguage::outerInGroup() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("outerInGroup.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 1); - ResolvedProductPtr product = products.value("OuterInGroup"); - QVERIFY(!!product); - QCOMPARE(product->groups.count(), 2); - GroupPtr group = product->groups.at(0); - QVERIFY(!!group); - QCOMPARE(group->name, product->name); - QCOMPARE(group->files.count(), 1); - SourceArtifactConstPtr artifact = group->files.first(); - QVariant installDir = artifact->properties->qbsPropertyValue("installDir"); - QCOMPARE(installDir.toString(), QString("/somewhere")); - group = product->groups.at(1); - QVERIFY(!!group); - QCOMPARE(group->name, QString("Special Group")); - QCOMPARE(group->files.count(), 1); - artifact = group->files.first(); - installDir = artifact->properties->qbsPropertyValue("installDir"); - QCOMPARE(installDir.toString(), QString("/somewhere/else")); - } - catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::parameterTypes() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("parameter-types.qbs")); - loader->loadProject(defaultParameters); - } - catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::pathProperties() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("pathproperties.qbs")); - project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - ResolvedProductPtr product = products.value("product1"); - QVERIFY(!!product); - QString projectFileDir = QFileInfo(defaultParameters.projectFilePath()).absolutePath(); - const QVariantMap productProps = product->productProperties; - QCOMPARE(productProps.value("projectFileDir").toString(), projectFileDir); - QStringList filesInProjectFileDir = QStringList() - << FileInfo::resolvePath(projectFileDir, "aboutdialog.h") - << FileInfo::resolvePath(projectFileDir, "aboutdialog.cpp"); - QCOMPARE(productProps.value("filesInProjectFileDir").toStringList(), filesInProjectFileDir); - QStringList includePaths = product->moduleProperties->property( - QStringList() << "modules" << "dummy" << "includePaths").toStringList(); - QCOMPARE(includePaths, QStringList() << projectFileDir); - QCOMPARE(productProps.value("base_fileInProductDir").toString(), - FileInfo::resolvePath(projectFileDir, QLatin1String("foo"))); - QCOMPARE(productProps.value("base_fileInBaseProductDir").toString(), - FileInfo::resolvePath(projectFileDir, QLatin1String("subdir/bar"))); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::profileValuesAndOverriddenValues() -{ - bool exceptionCaught = false; - try { - TemporaryProfile tp(QLatin1String("tst_lang_profile"), m_settings); - Profile profile = tp.p; - profile.setValue("dummy.defines", "IN_PROFILE"); - profile.setValue("dummy.cFlags", "IN_PROFILE"); - profile.setValue("dummy.cxxFlags", "IN_PROFILE"); - profile.setValue("qbs.architecture", "x86"); - SetupProjectParameters parameters = defaultParameters; - parameters.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); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - ResolvedProductPtr product = products.value("product1"); - QVERIFY(!!product); - QVariantList values; - values = product->moduleProperties->moduleProperty("dummy", "cxxFlags").toList(); - QCOMPARE(values.length(), 1); - QCOMPARE(values.first().toString(), QString("IN_PROFILE")); - values = product->moduleProperties->moduleProperty("dummy", "defines").toList(); - QCOMPARE(values, QVariantList() << QLatin1String("IN_FILE") << QLatin1String("IN_PROFILE")); - values = product->moduleProperties->moduleProperty("dummy", "cFlags").toList(); - QCOMPARE(values.length(), 1); - QCOMPARE(values.first().toString(), QString("OVERRIDDEN")); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::productConditions() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("productconditions.qbs")); - TopLevelProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 6); - ResolvedProductPtr product; - product = products.value("product_no_condition"); - QVERIFY(!!product); - QVERIFY(product->enabled); - - product = products.value("product_true_condition"); - QVERIFY(!!product); - QVERIFY(product->enabled); - - product = products.value("product_condition_dependent_of_module"); - QVERIFY(!!product); - QVERIFY(product->enabled); - - product = products.value("product_false_condition"); - QVERIFY(!!product); - QVERIFY(!product->enabled); - - product = products.value("product_probe_condition_false"); - QVERIFY(!!product); - QVERIFY(!product->enabled); - - product = products.value("product_probe_condition_true"); - QVERIFY(!!product); - QVERIFY(product->enabled); - } - catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::productDirectories() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("productdirectories.qbs")); - ResolvedProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 1); - ResolvedProductPtr product; - product = products.value("MyApp"); - QVERIFY(!!product); - const QVariantMap config = product->productProperties; - QCOMPARE(config.value(QLatin1String("buildDirectory")).toString(), - product->buildDirectory()); - QCOMPARE(config.value(QLatin1String("sourceDirectory")).toString(), testDataDir()); - } - catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::propertiesBlocks_data() -{ - QTest::addColumn<QString>("propertyName"); - QTest::addColumn<QStringList>("expectedValues"); - QTest::addColumn<QString>("expectedStringValue"); - - QTest::newRow("init") << QString() << QStringList() << QString(); - QTest::newRow("property_overwrite") - << QString("dummy.defines") - << QStringList("OVERWRITTEN") - << QString(); - QTest::newRow("property_set_indirect") - << QString("dummy.cFlags") - << QStringList("VAL") - << QString(); - QTest::newRow("property_overwrite_no_outer") - << QString("dummy.defines") - << QStringList("OVERWRITTEN") - << QString(); - QTest::newRow("property_append_to_outer") - << QString("dummy.defines") - << (QStringList() << QString("ONE") << QString("TWO")) - << QString(); - - QTest::newRow("multiple_exclusive_properties") - << QString("dummy.defines") - << QStringList("OVERWRITTEN") - << QString(); - QTest::newRow("multiple_exclusive_properties_no_outer") - << QString("dummy.defines") - << QStringList("OVERWRITTEN") - << QString(); - QTest::newRow("multiple_exclusive_properties_append_to_outer") - << QString("dummy.defines") - << (QStringList() << QString("ONE") << QString("TWO")) - << QString(); - - QTest::newRow("condition_refers_to_product_property") - << QString("dummy.defines") - << QStringList("OVERWRITTEN") - << QString("OVERWRITTEN"); - QTest::newRow("condition_refers_to_project_property") - << QString("dummy.defines") - << QStringList("OVERWRITTEN") - << QString("OVERWRITTEN"); - - QTest::newRow("ambiguous_properties") - << QString("dummy.defines") - << (QStringList() << QString("ONE") << QString("TWO")) - << QString(); - QTest::newRow("inheritance_overwrite_in_subitem") - << QString("dummy.defines") - << (QStringList() << QString("OVERWRITTEN_IN_SUBITEM")) - << QString(); - QTest::newRow("inheritance_retain_base1") - << QString("dummy.defines") - << (QStringList() << QString("BASE") << QString("SUB")) - << QString(); - QTest::newRow("inheritance_retain_base2") - << QString("dummy.defines") - << (QStringList() << QString("BASE") << QString("SUB")) - << QString(); - QTest::newRow("inheritance_retain_base3") - << QString("dummy.defines") - << (QStringList() << QString("BASE") << QString("SUB")) - << QString(); - QTest::newRow("inheritance_retain_base4") - << QString("dummy.defines") - << (QStringList() << QString("BASE")) - << QString(); - QTest::newRow("inheritance_condition_in_subitem1") - << QString("dummy.defines") - << (QStringList() << QString("SOMETHING") << QString("SUB")) - << QString(); - QTest::newRow("inheritance_condition_in_subitem2") - << QString("dummy.defines") - << (QStringList() << QString("SOMETHING")) - << QString(); - QTest::newRow("condition_references_id") - << QString("dummy.defines") - << (QStringList() << QString("OVERWRITTEN")) - << QString(); - QTest::newRow("using_derived_Properties_item") << "dummy.defines" - << (QStringList() << "string from MyProperties") << QString(); - QTest::newRow("conditional-depends") - << QString("dummy.defines") - << QStringList() - << QString(); - QTest::newRow("cleanup") << QString() << QStringList() << QString(); -} - -void TestLanguage::propertiesBlocks() -{ - HANDLE_INIT_CLEANUP_DATATAGS("propertiesblocks.qbs"); - QFETCH(QString, propertyName); - QFETCH(QStringList, expectedValues); - QFETCH(QString, expectedStringValue); - QVERIFY(!!project); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - const QString productName = QString::fromLocal8Bit(QTest::currentDataTag()); - ResolvedProductPtr product = products.value(productName); - QVERIFY(!!product); - QCOMPARE(product->name, productName); - QVariant v = productPropertyValue(product, propertyName); - QCOMPARE(v.toStringList(), expectedValues); - if (!expectedStringValue.isEmpty()) { - v = productPropertyValue(product, "someString"); - QCOMPARE(v.toString(), expectedStringValue); - } -} - -void TestLanguage::propertiesBlockInGroup() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath(testProject("properties-block-in-group.qbs")); - const TopLevelProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - QCOMPARE(project->allProducts().count(), 1); - const ResolvedProductConstPtr product = project->allProducts().first(); - const auto groupIt = std::find_if(product->groups.constBegin(), product->groups.constEnd(), - [](const GroupConstPtr &g) { return g->name == "the group"; }); - QVERIFY(groupIt != product->groups.constEnd()); - const QVariantMap propertyMap = (*groupIt)->properties->value(); - const QVariantList value = moduleProperty(propertyMap, "dummy", "defines").toList(); - QStringList stringListValue; - std::transform(value.constBegin(), value.constEnd(), std::back_inserter(stringListValue), - [](const QVariant &v) { return v.toString(); }); - QCOMPARE(stringListValue, QStringList() << "BASEDEF" << "FEATURE_ENABLED"); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::propertiesItemInModule() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath( - testProject("properties-item-in-module.qbs")); - const TopLevelProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 2); - for (const ResolvedProductConstPtr &p : products) { - QCOMPARE(p->moduleProperties->moduleProperty("dummy", "productName").toString(), - p->name); - } - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::qbsPropertiesInProjectCondition() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath( - testProject("qbs-properties-in-project-condition.qbs")); - const TopLevelProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 0); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -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); - QVERIFY(!strictMode); - const auto productMap = productsFromProject(project); - const ResolvedProductConstPtr brokenProduct = productMap.value("broken"); - QVERIFY(!brokenProduct->enabled); - QVERIFY(brokenProduct->location.isValid()); - QCOMPARE(brokenProduct->allFiles().count(), 0); - const ResolvedProductConstPtr dependerRequired = productMap.value("depender required"); - QVERIFY(!dependerRequired->enabled); - QVERIFY(dependerRequired->location.isValid()); - QCOMPARE(dependerRequired->allFiles().count(), 1); - const ResolvedProductConstPtr dependerNonRequired - = productMap.value("depender nonrequired"); - QVERIFY(dependerNonRequired->enabled); - QCOMPARE(dependerNonRequired->allFiles().count(), 1); - const ResolvedProductConstPtr recursiveDepender = productMap.value("recursive depender"); - QVERIFY(!recursiveDepender->enabled); - QVERIFY(recursiveDepender->location.isValid()); - QCOMPARE(recursiveDepender->allFiles().count(), 1); - const ResolvedProductConstPtr missingFile = productMap.value("missing file"); - QVERIFY(missingFile->enabled); - QCOMPARE(missingFile->groups.count(), 1); - QVERIFY(missingFile->groups.first()->enabled); - QCOMPARE(missingFile->groups.first()->allFiles().count(), 2); - const ResolvedProductConstPtr fine = productMap.value("fine"); - QVERIFY(fine->enabled); - QCOMPARE(fine->allFiles().count(), 1); - } catch (const ErrorInfo &e) { - QVERIFY2(strictMode, qPrintable(e.toString())); - } -} - -void TestLanguage::relaxedErrorMode_data() -{ - QTest::addColumn<bool>("strictMode"); - - QTest::newRow("strict mode") << true; - QTest::newRow("relaxed mode") << false; -} - -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); - QVERIFY(!!project); - QVERIFY(!exceptionExpected); - } catch (const ErrorInfo &e) { - QVERIFY(exceptionExpected); - QVERIFY2(e.toString().contains("validation error!"), qPrintable(e.toString())); - } -} - -void TestLanguage::requiredAndNonRequiredDependencies_data() -{ - QTest::addColumn<QString>("projectFile"); - QTest::addColumn<bool>("exceptionExpected"); - - QTest::newRow("same file") << "direct-dependencies.qbs" << true; - QTest::newRow("dependency via module") << "dependency-via-module.qbs" << true; - QTest::newRow("dependency via export") << "dependency-via-export.qbs" << true; - QTest::newRow("more indirection") << "complicated.qbs" << true; - QTest::newRow("required chain (module)") << "required-chain-module.qbs" << false; - QTest::newRow("required chain (export)") << "required-chain-export.qbs" << false; - QTest::newRow("required chain (export indirect)") << "required-chain-export-indirect.qbs" - << false; -} - -void TestLanguage::throwingProbe() -{ - QFETCH(bool, enableProbe); - try { - SetupProjectParameters params = defaultParameters; - params.setProjectFilePath(testProject("throwing-probe.qbs")); - QVariantMap properties; - properties.insert(QLatin1String("products.theProduct.enableProbe"), enableProbe); - params.setOverriddenValues(properties); - const TopLevelProjectPtr project = loader->loadProject(params); - QVERIFY(!!project); - QVERIFY(!enableProbe); - } catch (const ErrorInfo &e) { - QVERIFY2(enableProbe, qPrintable(e.toString())); - } -} - -void TestLanguage::throwingProbe_data() -{ - QTest::addColumn<bool>("enableProbe"); - - QTest::newRow("enabled probe") << true; - QTest::newRow("disabled probe") << false; -} - -void TestLanguage::qualifiedId() -{ - QString str = "foo.bar.baz"; - QualifiedId id = QualifiedId::fromString(str); - QCOMPARE(id.count(), 3); - QCOMPARE(id.toString(), str); - - id = QualifiedId("blubb.di.blubb"); // c'tor does not split - QCOMPARE(id.count(), 1); - - QList<QualifiedId> ids; - ids << QualifiedId::fromString("a") - << QualifiedId::fromString("a.a") - << QualifiedId::fromString("b") - << QualifiedId::fromString("c.a") - << QualifiedId::fromString("c.b.a") - << QualifiedId::fromString("c.c"); - QList<QualifiedId> sorted = ids; - std::sort(sorted.begin(), sorted.end()); - QCOMPARE(ids, sorted); -} - -void TestLanguage::recursiveProductDependencies() -{ - bool exceptionCaught = false; - try { - defaultParameters.setProjectFilePath( - testProject("recursive-dependencies/recursive-dependencies.qbs")); - const TopLevelProjectPtr project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 4); - const ResolvedProductConstPtr p1 = products.value("p1"); - QVERIFY(!!p1); - const ResolvedProductConstPtr p2 = products.value("p2"); - QVERIFY(!!p2); - const ResolvedProductPtr p3 = products.value("p3"); - QVERIFY(!!p3); - const ResolvedProductPtr p4 = products.value("p4"); - QVERIFY(!!p4); - QVERIFY(p1->dependencies == Set<ResolvedProductPtr>() << p3 << p4); - QVERIFY(p2->dependencies == Set<ResolvedProductPtr>() << p3 << p4); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -void TestLanguage::fileTags_data() -{ - QTest::addColumn<int>("numberOfGroups"); - QTest::addColumn<QStringList>("expectedFileTags"); - - QTest::newRow("init") << 0 << QStringList(); - QTest::newRow("filetagger_project_scope") << 1 << (QStringList() << "cpp"); - QTest::newRow("filetagger_product_scope") << 1 << (QStringList() << "asm"); - QTest::newRow("filetagger_static_pattern") << 1 << (QStringList() << "yellow"); - QTest::newRow("unknown_file_tag") << 1 << (QStringList() << "unknown-file-tag"); - QTest::newRow("set_file_tag_via_group") << 2 << (QStringList() << "c++"); - QTest::newRow("override_file_tag_via_group") << 2 << (QStringList() << "c++"); - QTest::newRow("add_file_tag_via_group") << 2 << (QStringList() << "cpp" << "zzz"); - QTest::newRow("cleanup") << 0 << QStringList(); -} - -void TestLanguage::fileTags() -{ - HANDLE_INIT_CLEANUP_DATATAGS("filetags.qbs"); - QFETCH(int, numberOfGroups); - QFETCH(QStringList, expectedFileTags); - QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - ResolvedProductPtr product; - const QString productName = QString::fromLocal8Bit(QTest::currentDataTag()); - QVERIFY(!!(product = products.value(productName))); - QCOMPARE(product->groups.count(), numberOfGroups); - GroupPtr group = product->groups.last(); - QVERIFY(!!group); - QCOMPARE(group->files.count(), 1); - SourceArtifactConstPtr sourceFile = group->files.first(); - QStringList fileTags = sourceFile->fileTags.toStringList(); - fileTags.sort(); - QCOMPARE(fileTags, expectedFileTags); -} - -void TestLanguage::wildcards_data() -{ - QTest::addColumn<bool>("useGroup"); - QTest::addColumn<QStringList>("filesToCreate"); - QTest::addColumn<QString>("projectFileSubDir"); - QTest::addColumn<QString>("prefix"); - QTest::addColumn<QStringList>("patterns"); - QTest::addColumn<QStringList>("excludePatterns"); - QTest::addColumn<QStringList>("expected"); - - const bool useGroup = true; - for (int i = 0; i <= 1; ++i) { - const bool useGroup = i; - const QByteArray dataTagSuffix = useGroup ? " group" : " nogroup"; - QTest::newRow(QByteArray("simple 1") + dataTagSuffix) - << useGroup - << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp") - << QString() - << QString() - << (QStringList() << "*.h") - << QStringList() - << (QStringList() << "foo.h" << "bar.h"); - QTest::newRow(QByteArray("simple 2") + dataTagSuffix) - << useGroup - << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp") - << QString() - << QString() - << (QStringList() << "foo.*") - << QStringList() - << (QStringList() << "foo.h" << "foo.cpp"); - QTest::newRow(QByteArray("simple 3") + dataTagSuffix) - << useGroup - << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp") - << QString() - << QString() - << (QStringList() << "*.h" << "*.cpp") - << QStringList() - << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp"); - QTest::newRow(QByteArray("exclude 1") + dataTagSuffix) - << useGroup - << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp") - << QString() - << QString() - << (QStringList() << "*.h" << "*.cpp") - << (QStringList() << "bar*") - << (QStringList() << "foo.h" << "foo.cpp"); - QTest::newRow(QByteArray("exclude 2") + dataTagSuffix) - << useGroup - << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp") - << QString() - << QString() - << (QStringList() << "*") - << (QStringList() << "*.qbs") - << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp"); - QTest::newRow(QByteArray("non-recursive") + dataTagSuffix) - << useGroup - << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp") - << QString() - << QString() - << (QStringList() << "a/*") - << QStringList() - << (QStringList() << "a/foo.h" << "a/foo.cpp"); - QTest::newRow(QByteArray("absolute paths") + dataTagSuffix) - << useGroup - << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp") - << QString() - << QString() - << (QStringList() << m_wildcardsTestDirPath + "/?oo.*") - << QStringList() - << (QStringList() << "foo.h" << "foo.cpp"); - QTest::newRow(QByteArray("relative paths with dotdot") + dataTagSuffix) - << useGroup - << (QStringList() << "bar.h" << "bar.cpp") - << QString("TheLaughingLlama") - << QString() - << (QStringList() << "../bar.*") - << QStringList() - << (QStringList() << "bar.h" << "bar.cpp"); - } - QTest::newRow(QByteArray("recursive 1")) - << useGroup - << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp") - << QString() - << QString() - << (QStringList() << "a/**") - << QStringList() - << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp"); - QTest::newRow(QByteArray("recursive 2")) - << useGroup - << (QStringList() - << "d/1.h" << "b/d/1.h" << "b/c/d/1.h" - << "d/e/1.h" << "b/d/e/1.h" << "b/c/d/e/1.h" - << "a/d/1.h" << "a/b/d/1.h" << "a/b/c/d/1.h" - << "a/d/e/1.h" << "a/b/d/e/1.h" << "a/b/c/d/e/1.h" - << "a/d/1.cpp" << "a/b/d/1.cpp" << "a/b/c/d/1.h" - << "a/d/e/1.cpp" << "a/b/d/e/1.cpp" << "a/b/c/d/e/1.cpp") - << QString() - << QString() - << (QStringList() << "a/**/d/*.h") - << QStringList() - << (QStringList() << "a/d/1.h" << "a/b/d/1.h" << "a/b/c/d/1.h"); - QTest::newRow(QByteArray("recursive 3")) - << useGroup - << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp") - << QString() - << QString() - << (QStringList() << "a/**/**/**") - << QStringList() - << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp"); - QTest::newRow(QByteArray("prefix")) - << useGroup - << (QStringList() << "subdir/foo.h" << "subdir/foo.cpp" << "subdir/bar.h" - << "subdir/bar.cpp") - << QString() - << QString("subdir/") - << (QStringList() << "*.h") - << QStringList() - << (QStringList() << "subdir/foo.h" << "subdir/bar.h"); - QTest::newRow(QByteArray("non-existing absolute path")) - << useGroup - << QStringList() - << QString() - << QString("/dir") - << (QStringList() << "*.whatever") - << QStringList() - << QStringList(); -} - -void TestLanguage::wildcards() -{ - QFETCH(bool, useGroup); - QFETCH(QStringList, filesToCreate); - QFETCH(QString, projectFileSubDir); - QFETCH(QString, prefix); - QFETCH(QStringList, patterns); - QFETCH(QStringList, excludePatterns); - QFETCH(QStringList, expected); - - // create test directory - QDir::setCurrent(QDir::tempPath()); - { - QString errorMessage; - if (QFile::exists(m_wildcardsTestDirPath)) { - if (!removeDirectoryWithContents(m_wildcardsTestDirPath, &errorMessage)) { - qDebug() << errorMessage; - QVERIFY2(false, "removeDirectoryWithContents failed"); - } - } - QVERIFY(QDir().mkdir(m_wildcardsTestDirPath)); - } - - // create project file - const QString groupName = "Keks"; - QString dataTag = QString::fromLocal8Bit(QTest::currentDataTag()); - dataTag.replace(' ', '_'); - if (!projectFileSubDir.isEmpty()) { - if (!projectFileSubDir.startsWith('/')) - projectFileSubDir.prepend('/'); - if (projectFileSubDir.endsWith('/')) - projectFileSubDir.chop(1); - QVERIFY(QDir().mkpath(m_wildcardsTestDirPath + projectFileSubDir)); - } - const QString projectFilePath = m_wildcardsTestDirPath + projectFileSubDir + "/test_" + dataTag - + ".qbs"; - { - QFile projectFile(projectFilePath); - QVERIFY(projectFile.open(QIODevice::WriteOnly)); - QTextStream s(&projectFile); - s << "import qbs.base 1.0" << endl << endl - << "Application {" << endl - << " name: \"MyProduct\"" << endl; - if (useGroup) { - s << " Group {" << endl - << " name: " << toJSLiteral(groupName) << endl; - } - if (!prefix.isEmpty()) - s << " prefix: " << toJSLiteral(prefix) << endl; - if (!patterns.isEmpty()) - s << " files: " << toJSLiteral(patterns) << endl; - if (!excludePatterns.isEmpty()) - s << " excludeFiles: " << toJSLiteral(excludePatterns) << endl; - if (useGroup) - s << " }" << endl; - s << "}" << endl << endl; - } - - // create files - foreach (QString filePath, filesToCreate) { - filePath.prepend(m_wildcardsTestDirPath + '/'); - QFileInfo fi(filePath); - if (!QDir(fi.path()).exists()) - QVERIFY(QDir().mkpath(fi.path())); - QFile file(filePath); - QVERIFY(file.open(QIODevice::WriteOnly)); - } - - // read the project - bool exceptionCaught = false; - ResolvedProductPtr product; - try { - defaultParameters.setProjectFilePath(projectFilePath); - project = loader->loadProject(defaultParameters); - QVERIFY(!!project); - const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - product = products.value("MyProduct"); - QVERIFY(!!product); - GroupPtr group; - if (useGroup) { - QCOMPARE(product->groups.count(), HostOsInfo::isMacosHost() ? 3 : 2); - foreach (const GroupPtr &rg, product->groups) { - if (rg->name == groupName) { - group = rg; - break; - } - } - } else { - QCOMPARE(product->groups.count(), HostOsInfo::isMacosHost() ? 2 : 1); - group = product->groups.first(); - } - QVERIFY(!!group); - QCOMPARE(group->files.count(), 0); - SourceWildCards::Ptr wildcards = group->wildcards; - QVERIFY(!!wildcards); - QStringList actualFilePaths; - foreach (const SourceArtifactConstPtr &artifact, wildcards->files) { - QString str = artifact->absoluteFilePath; - int idx = str.indexOf(m_wildcardsTestDirPath); - if (idx != -1) - str.remove(0, idx + m_wildcardsTestDirPath.count() + 1); - actualFilePaths << str; - } - actualFilePaths.sort(); - expected.sort(); - QCOMPARE(actualFilePaths, expected); - } catch (const ErrorInfo &e) { - exceptionCaught = true; - qDebug() << e.toString(); - } - QCOMPARE(exceptionCaught, false); -} - -} // namespace Internal -} // namespace qbs diff --git a/src/lib/corelib/language/value.h b/src/lib/corelib/language/value.h index ab2c3187a..6b0c98dc9 100644 --- a/src/lib/corelib/language/value.h +++ b/src/lib/corelib/language/value.h @@ -109,7 +109,7 @@ class JSSourceValue : public Value Q_DECLARE_FLAGS(Flags, Flag) public: - static JSSourceValuePtr create(bool createdByPropertiesBlock = false); + static JSSourceValuePtr QBS_AUTOTEST_EXPORT create(bool createdByPropertiesBlock = false); ~JSSourceValue(); void apply(ValueHandler *handler) { handler->handle(this); } diff --git a/src/lib/corelib/parser/qmljsastvisitor_p.h b/src/lib/corelib/parser/qmljsastvisitor_p.h index f0eff5ce7..aa4471c6b 100644 --- a/src/lib/corelib/parser/qmljsastvisitor_p.h +++ b/src/lib/corelib/parser/qmljsastvisitor_p.h @@ -53,11 +53,12 @@ #include "qmljsastfwd_p.h" #include "qmljsglobal_p.h" +#include <tools/qbs_export.h> namespace QbsQmlJS { namespace AST { -class QML_PARSER_EXPORT Visitor +class QML_PARSER_EXPORT QBS_AUTOTEST_EXPORT Visitor { public: Visitor(); diff --git a/src/lib/corelib/parser/qmljsengine_p.h b/src/lib/corelib/parser/qmljsengine_p.h index be88b917b..2fdd60b30 100644 --- a/src/lib/corelib/parser/qmljsengine_p.h +++ b/src/lib/corelib/parser/qmljsengine_p.h @@ -54,6 +54,7 @@ #include "qmljsglobal_p.h" #include "qmljsastfwd_p.h" #include "qmljsmemorypool_p.h" +#include <tools/qbs_export.h> #include <QtCore/qstring.h> @@ -87,7 +88,7 @@ public: QString message; }; -class QML_PARSER_EXPORT Engine +class QML_PARSER_EXPORT QBS_AUTOTEST_EXPORT Engine { Lexer *_lexer; Directives *_directives; diff --git a/src/lib/corelib/parser/qmljslexer_p.h b/src/lib/corelib/parser/qmljslexer_p.h index 7a77ddabe..e0d61b226 100644 --- a/src/lib/corelib/parser/qmljslexer_p.h +++ b/src/lib/corelib/parser/qmljslexer_p.h @@ -53,6 +53,7 @@ #include "qmljsglobal_p.h" #include "qmljsgrammar_p.h" +#include <tools/qbs_export.h> #include <QtCore/qstring.h> namespace QbsQmlJS { @@ -81,7 +82,7 @@ public: } }; -class QML_PARSER_EXPORT Lexer: public QmlJSGrammar +class QML_PARSER_EXPORT QBS_AUTOTEST_EXPORT Lexer: public QmlJSGrammar { public: enum { diff --git a/src/lib/corelib/parser/qmljsparser_p.h b/src/lib/corelib/parser/qmljsparser_p.h index 93c096367..38fc1f5ce 100644 --- a/src/lib/corelib/parser/qmljsparser_p.h +++ b/src/lib/corelib/parser/qmljsparser_p.h @@ -61,6 +61,7 @@ #include "qmljsgrammar_p.h" #include "qmljsast_p.h" #include "qmljsengine_p.h" +#include <tools/qbs_export.h> #include <QtCore/qlist.h> #include <QtCore/qstring.h> @@ -69,7 +70,7 @@ namespace QbsQmlJS { class Engine; -class QML_PARSER_EXPORT Parser: protected QmlJSGrammar +class QML_PARSER_EXPORT QBS_AUTOTEST_EXPORT Parser: protected QmlJSGrammar { public: union Value { diff --git a/src/lib/corelib/tools/fileinfo.h b/src/lib/corelib/tools/fileinfo.h index 1655e37d9..71d178265 100644 --- a/src/lib/corelib/tools/fileinfo.h +++ b/src/lib/corelib/tools/fileinfo.h @@ -55,7 +55,7 @@ QT_FORWARD_DECLARE_CLASS(QFileInfo) namespace qbs { namespace Internal { -class FileInfo +class QBS_AUTOTEST_EXPORT FileInfo { public: FileInfo(const QString &fileName); diff --git a/src/lib/corelib/tools/filetime.h b/src/lib/corelib/tools/filetime.h index 30687833f..c9b98e9c0 100644 --- a/src/lib/corelib/tools/filetime.h +++ b/src/lib/corelib/tools/filetime.h @@ -60,7 +60,7 @@ namespace qbs { namespace Internal { -class FileTime +class QBS_AUTOTEST_EXPORT FileTime { public: #if defined(Q_OS_UNIX) diff --git a/src/lib/corelib/tools/id.h b/src/lib/corelib/tools/id.h index 334acc686..aedecd66e 100644 --- a/src/lib/corelib/tools/id.h +++ b/src/lib/corelib/tools/id.h @@ -40,6 +40,8 @@ #ifndef QBS_TOOLS_ID_H #define QBS_TOOLS_ID_H +#include "qbs_export.h" + #include <QtCore/qmetatype.h> #include <QtCore/qstring.h> #include <QtCore/qvariant.h> @@ -47,7 +49,7 @@ namespace qbs { namespace Internal { -class Id +class QBS_AUTOTEST_EXPORT Id { public: enum { IdsPerPlugin = 10000, ReservedPlugins = 1000 }; diff --git a/src/lib/corelib/tools/processutils.h b/src/lib/corelib/tools/processutils.h index 5a210289d..14ab13812 100644 --- a/src/lib/corelib/tools/processutils.h +++ b/src/lib/corelib/tools/processutils.h @@ -40,13 +40,15 @@ #ifndef QBS_PROCESSUTILS_H #define QBS_PROCESSUTILS_H +#include <tools/qbs_export.h> + #include <QtCore/qglobal.h> #include <QtCore/qstring.h> namespace qbs { namespace Internal { -QString processNameByPid(qint64 pid); +QString QBS_AUTOTEST_EXPORT processNameByPid(qint64 pid); } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/tools/qbs_export.h b/src/lib/corelib/tools/qbs_export.h index 5bb28f98d..a92150c63 100644 --- a/src/lib/corelib/tools/qbs_export.h +++ b/src/lib/corelib/tools/qbs_export.h @@ -43,11 +43,22 @@ #ifdef QBS_STATIC_LIB # define QBS_EXPORT +# define QBS_AUTOTEST_EXPORT #else # ifdef QBS_LIBRARY # define QBS_EXPORT Q_DECL_EXPORT +# ifdef QBS_ENABLE_UNIT_TESTS +# define QBS_AUTOTEST_EXPORT Q_DECL_EXPORT +# else +# define QBS_AUTOTEST_EXPORT +# endif # else # define QBS_EXPORT Q_DECL_IMPORT +# ifdef QBS_ENABLE_UNIT_TESTS +# define QBS_AUTOTEST_EXPORT Q_DECL_IMPORT +# else +# define QBS_AUTOTEST_EXPORT +# endif # endif #endif diff --git a/src/lib/corelib/tools/scripttools.h b/src/lib/corelib/tools/scripttools.h index 56731c730..8346aea66 100644 --- a/src/lib/corelib/tools/scripttools.h +++ b/src/lib/corelib/tools/scripttools.h @@ -63,7 +63,7 @@ QScriptValue toScriptValue(QScriptEngine *scriptEngine, const C &container) } void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value); -QVariant getConfigProperty(const QVariantMap &cfg, const QStringList &name); +QVariant QBS_AUTOTEST_EXPORT getConfigProperty(const QVariantMap &cfg, const QStringList &name); template <class T> void attachPointerTo(QScriptValue &scriptValue, T *ptr) diff --git a/src/lib/corelib/tools/tools.pri b/src/lib/corelib/tools/tools.pri index ed0942294..301d00d05 100644 --- a/src/lib/corelib/tools/tools.pri +++ b/src/lib/corelib/tools/tools.pri @@ -104,11 +104,6 @@ osx { LIBS += -framework Security } -qbs_enable_unit_tests { - HEADERS += $$PWD/tst_tools.h - SOURCES += $$PWD/tst_tools.cpp -} - !qbs_no_dev_install { tools_headers.files = \ $$PWD/architectures.h \ diff --git a/src/lib/corelib/tools/tst_tools.cpp b/src/lib/corelib/tools/tst_tools.cpp deleted file mode 100644 index a6fb433c9..000000000 --- a/src/lib/corelib/tools/tst_tools.cpp +++ /dev/null @@ -1,950 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qbs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#undef QT_NO_CAST_FROM_ASCII // I am qmake, and I approve this hack. - -#include "tst_tools.h" - -#include "buildoptions.h" -#include "error.h" -#include "fileinfo.h" -#include "hostosinfo.h" -#include "processutils.h" -#include "profile.h" -#include "set.h" -#include "settings.h" -#include "setupprojectparameters.h" -#include "version.h" - -#include <QtCore/qdir.h> -#include <QtCore/qfile.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qsettings.h> -#include <QtCore/qtemporarydir.h> -#include <QtCore/qtemporaryfile.h> - -#include <QtTest/qtest.h> - -namespace qbs { -namespace Internal { - -TestTools::TestTools(Settings *settings) : m_settings(settings) -{ -} - -TestTools::~TestTools() -{ - qDeleteAll(m_tmpDirs); -} - -void TestTools::testFileInfo() -{ - QCOMPARE(FileInfo::fileName("C:/waffl/copter.exe"), QString("copter.exe")); - QCOMPARE(FileInfo::baseName("C:/waffl/copter.exe.lib"), QString("copter")); - QCOMPARE(FileInfo::completeBaseName("C:/waffl/copter.exe.lib"), QString("copter.exe")); - QCOMPARE(FileInfo::path("abc"), QString(".")); - QCOMPARE(FileInfo::path("/abc/lol"), QString("/abc")); - QCOMPARE(FileInfo::path("/fileInRoot"), QString(QLatin1Char('/'))); - if (HostOsInfo::isWindowsHost()) - QCOMPARE(FileInfo::path("C:/fileInDriveRoot"), QString("C:/")); - QVERIFY(!FileInfo::isAbsolute("bla/lol")); - QVERIFY(FileInfo::isAbsolute("/bla/lol")); - if (HostOsInfo::isWindowsHost()) - QVERIFY(FileInfo::isAbsolute("C:\\bla\\lol")); - QCOMPARE(FileInfo::resolvePath("/abc/lol", "waffl"), QString("/abc/lol/waffl")); - QCOMPARE(FileInfo::resolvePath("/abc/def/ghi/jkl/", "../foo/bar"), QString("/abc/def/ghi/foo/bar")); - QCOMPARE(FileInfo::resolvePath("/abc/def/ghi/jkl/", "../../foo/bar"), QString("/abc/def/foo/bar")); - QCOMPARE(FileInfo::resolvePath("/abc", "../../../foo/bar"), QString("/foo/bar")); - QCOMPARE(FileInfo("/does/not/exist").lastModified(), FileTime()); -} - -void TestTools::fileCaseCheck() -{ - QTemporaryFile tempFile(QDir::tempPath() + QLatin1String("/CamelCase")); - QVERIFY2(tempFile.open(), qPrintable(tempFile.errorString())); - QFileInfo tempFileInfo(tempFile.fileName()); - const QString lowerFilePath = tempFileInfo.absolutePath() + QLatin1Char('/') - + tempFileInfo.fileName().toLower(); - const QString upperFilePath = tempFileInfo.absolutePath() + QLatin1Char('/') - + tempFileInfo.fileName().toUpper(); - QVERIFY(FileInfo::isFileCaseCorrect(tempFileInfo.absoluteFilePath())); - if (QFile::exists(lowerFilePath)) - QVERIFY(!FileInfo::isFileCaseCorrect(lowerFilePath)); - if (QFile::exists(upperFilePath)) - QVERIFY(!FileInfo::isFileCaseCorrect(upperFilePath)); -} - -void TestTools::testProfiles() -{ - TemporaryProfile tpp("parent", m_settings); - Profile parentProfile = tpp.p; - TemporaryProfile tpc("child", m_settings); - Profile childProfile = tpc.p; - parentProfile.removeBaseProfile(); - parentProfile.remove("testKey"); - QCOMPARE(parentProfile.value("testKey", "none").toString(), QLatin1String("none")); - parentProfile.setValue("testKey", "testValue"); - QCOMPARE(parentProfile.value("testKey").toString(), QLatin1String("testValue")); - - childProfile.remove("testKey"); - childProfile.removeBaseProfile(); - QCOMPARE(childProfile.value("testKey", "none").toString(), QLatin1String("none")); - childProfile.setBaseProfile("parent"); - QCOMPARE(childProfile.value("testKey").toString(), QLatin1String("testValue")); - - // Change base profile and check if the inherited value also changes. - TemporaryProfile tpf("foo", m_settings); - Profile fooProfile = tpf.p; - fooProfile.setValue("testKey", "gnampf"); - childProfile.setBaseProfile("foo"); - QCOMPARE(childProfile.value("testKey", "none").toString(), QLatin1String("gnampf")); - - ErrorInfo errorInfo; - childProfile.setBaseProfile("SmurfAlongWithMe"); - childProfile.value("blubb", QString(), &errorInfo); - QVERIFY(errorInfo.hasError()); - - errorInfo.clear(); - childProfile.setBaseProfile("parent"); - parentProfile.setBaseProfile("child"); - QVERIFY(!childProfile.value("blubb", QString(), &errorInfo).isValid()); - QVERIFY(errorInfo.hasError()); - - QVERIFY(!childProfile.allKeys(Profile::KeySelectionNonRecursive).isEmpty()); - - errorInfo.clear(); - QVERIFY(childProfile.allKeys(Profile::KeySelectionRecursive, &errorInfo).isEmpty()); - QVERIFY(errorInfo.hasError()); -} - -void TestTools::testSettingsMigration() -{ - QFETCH(QString, baseDir); - QFETCH(bool, hasOldSettings); - Settings settings(baseDir); - if (hasOldSettings) { - QVERIFY(QFileInfo(settings.baseDirectory() + "/qbs/" QBS_VERSION "/profiles/right.txt") - .exists()); - QCOMPARE(settings.value("key").toString(), - settings.baseDirectory() + "/qbs/" QBS_VERSION "/profilesright"); - } else { - QVERIFY(!QFileInfo(settings.baseDirectory() + "/qbs/" QBS_VERSION "/profiles").exists()); - QVERIFY(settings.allKeys().isEmpty()); - } -} - -void TestTools::testSettingsMigration_data() -{ - QTest::addColumn<QString>("baseDir"); - QTest::addColumn<bool>("hasOldSettings"); - QTest::newRow("settings dir with lots of versions") << setupSettingsDir1() << true; - QTest::newRow("settings dir with only a fallback") << setupSettingsDir2() << true; - QTest::newRow("no previous settings") << setupSettingsDir3() << false; -} - -QString TestTools::setupSettingsDir1() -{ - QTemporaryDir * const baseDir = new QTemporaryDir; - m_tmpDirs << baseDir; - - const Version thisVersion = Version::fromString(QBS_VERSION); - Version predecessor; - if (thisVersion.patchLevel() > 0) { - predecessor.setMajorVersion(thisVersion.majorVersion()); - predecessor.setMinorVersion(thisVersion.minorVersion()); - predecessor.setPatchLevel(thisVersion.patchLevel() - 1); - } else if (thisVersion.minorVersion() > 0) { - predecessor.setMajorVersion(thisVersion.majorVersion()); - predecessor.setMinorVersion(thisVersion.minorVersion() - 1); - predecessor.setPatchLevel(99); - } else { - predecessor.setMajorVersion(thisVersion.majorVersion() - 1); - predecessor.setMajorVersion(99); - predecessor.setPatchLevel(99); - } - const auto versions = QList<Version>() << Version(0, 1, 0) << Version(1, 0, 5) << predecessor - << Version(thisVersion.majorVersion() + 1, thisVersion.minorVersion(), - thisVersion.patchLevel()) - << Version(thisVersion.majorVersion(), thisVersion.minorVersion() + 1, - thisVersion.patchLevel()) - << Version(thisVersion.majorVersion(), thisVersion.minorVersion(), - thisVersion.patchLevel() + 1) - << Version(99, 99, 99); - foreach (const Version &v, versions) { - const QString settingsDir = baseDir->path() + "/qbs/" + v.toString(); - QSettings s(settingsDir + "/qbs.conf", - HostOsInfo::isWindowsHost() ? QSettings::IniFormat : QSettings::NativeFormat); - const QString profilesDir = settingsDir + "/profiles"; - QDir::root().mkpath(profilesDir); - const QString magicString = v == predecessor ? "right" : "wrong"; - QFile f(profilesDir + '/' + magicString + ".txt"); - f.open(QIODevice::WriteOnly); - s.setValue("org/qt-project/qbs/key", profilesDir + magicString); - } - - return baseDir->path(); -} - -QString TestTools::setupSettingsDir2() -{ - QTemporaryDir * const baseDir = new QTemporaryDir; - m_tmpDirs << baseDir; - const QString settingsDir = baseDir->path(); - QSettings s(settingsDir + QLatin1String("/qbs.conf"), - HostOsInfo::isWindowsHost() ? QSettings::IniFormat : QSettings::NativeFormat); - const QString profilesDir = settingsDir + QLatin1String("/qbs/profiles"); - QDir::root().mkpath(profilesDir); - QFile f(profilesDir + "/right.txt"); - f.open(QIODevice::WriteOnly); - s.setValue("org/qt-project/qbs/key", profilesDir + "right"); - - return baseDir->path(); -} - -QString TestTools::setupSettingsDir3() -{ - auto * const baseDir = new QTemporaryDir; - m_tmpDirs << baseDir; - return baseDir->path(); -} - -void TestTools::testBuildConfigMerging() -{ - TemporaryProfile tp(QLatin1String("tst_tools_profile"), m_settings); - Profile profile = tp.p; - profile.setValue(QLatin1String("topLevelKey"), QLatin1String("topLevelValue")); - profile.setValue(QLatin1String("qbs.toolchain"), QLatin1String("gcc")); - profile.setValue(QLatin1String("qbs.architecture"), - QLatin1String("Jean-Claude Pillemann")); - profile.setValue(QLatin1String("cpp.treatWarningsAsErrors"), true); - QVariantMap overrideMap; - overrideMap.insert(QLatin1String("qbs.toolchain"), QLatin1String("clang")); - overrideMap.insert(QLatin1String("qbs.installRoot"), QLatin1String("/blubb")); - SetupProjectParameters params; - params.setSettingsDirectory(m_settings->baseDirectory()); - params.setTopLevelProfile(profile.name()); - params.setConfigurationName(QLatin1String("debug")); - params.setOverriddenValues(overrideMap); - const ErrorInfo error = params.expandBuildConfiguration(); - QVERIFY2(!error.hasError(), qPrintable(error.toString())); - const QVariantMap finalMap = params.finalBuildConfigurationTree(); - QCOMPARE(finalMap.count(), 3); - QCOMPARE(finalMap.value(QLatin1String("topLevelKey")).toString(), - QString::fromLatin1("topLevelValue")); - const QVariantMap finalQbsMap = finalMap.value(QLatin1String("qbs")).toMap(); - QCOMPARE(finalQbsMap.count(), 4); - QCOMPARE(finalQbsMap.value(QLatin1String("toolchain")).toString(), - QString::fromLatin1("clang")); - QCOMPARE(finalQbsMap.value(QLatin1String("configurationName")).toString(), - QString::fromLatin1("debug")); - QCOMPARE(finalQbsMap.value(QLatin1String("architecture")).toString(), - QString::fromLatin1("Jean-Claude Pillemann")); - QCOMPARE(finalQbsMap.value(QLatin1String("installRoot")).toString(), QLatin1String("/blubb")); - const QVariantMap finalCppMap = finalMap.value(QLatin1String("cpp")).toMap(); - QCOMPARE(finalCppMap.count(), 1); - QCOMPARE(finalCppMap.value(QLatin1String("treatWarningsAsErrors")).toBool(), true); -} - -void TestTools::testProcessNameByPid() -{ - QCOMPARE(qAppName(), processNameByPid(QCoreApplication::applicationPid())); -} - - -int toNumber(const QString &str) -{ - int res = 0; - for (int i = 0; i < str.length(); ++i) - res = (res * 10) + str[i].digitValue(); - return res; -} - -void TestTools::set_operator_eq() -{ - { - Set<int> set1, set2; - QVERIFY(set1 == set2); - QVERIFY(!(set1 != set2)); - - set1.insert(1); - QVERIFY(set1 != set2); - QVERIFY(!(set1 == set2)); - - set2.insert(1); - QVERIFY(set1 == set2); - QVERIFY(!(set1 != set2)); - - set2.insert(1); - QVERIFY(set1 == set2); - QVERIFY(!(set1 != set2)); - - set1.insert(2); - QVERIFY(set1 != set2); - QVERIFY(!(set1 == set2)); - } - - { - Set<QString> set1, set2; - QVERIFY(set1 == set2); - QVERIFY(!(set1 != set2)); - - set1.insert("one"); - QVERIFY(set1 != set2); - QVERIFY(!(set1 == set2)); - - set2.insert("one"); - QVERIFY(set1 == set2); - QVERIFY(!(set1 != set2)); - - set2.insert("one"); - QVERIFY(set1 == set2); - QVERIFY(!(set1 != set2)); - - set1.insert("two"); - QVERIFY(set1 != set2); - QVERIFY(!(set1 == set2)); - } - - { - Set<QString> a; - Set<QString> b; - - a += "otto"; - b += "willy"; - - QVERIFY(a != b); - QVERIFY(!(a == b)); - } - - { - Set<int> s1, s2; - s1.reserve(100); - s2.reserve(4); - QVERIFY(s1 == s2); - s1 << 100 << 200 << 300 << 400; - s2 << 400 << 300 << 200 << 100; - QVERIFY(s1 == s2); - } -} - -void TestTools::set_swap() -{ - Set<int> s1, s2; - s1.insert(1); - s2.insert(2); - s1.swap(s2); - QCOMPARE(*s1.begin(),2); - QCOMPARE(*s2.begin(),1); -} - -void TestTools::set_size() -{ - Set<int> set; - QVERIFY(set.size() == 0); - QVERIFY(set.isEmpty()); - QVERIFY(set.count() == set.size()); - - set.insert(1); - QVERIFY(set.size() == 1); - QVERIFY(!set.isEmpty()); - QVERIFY(set.count() == set.size()); - - set.insert(1); - QVERIFY(set.size() == 1); - QVERIFY(!set.isEmpty()); - QVERIFY(set.count() == set.size()); - - set.insert(2); - QVERIFY(set.size() == 2); - QVERIFY(!set.isEmpty()); - QVERIFY(set.count() == set.size()); - - set.remove(1); - QVERIFY(set.size() == 1); - QVERIFY(!set.isEmpty()); - QVERIFY(set.count() == set.size()); - - set.remove(1); - QVERIFY(set.size() == 1); - QVERIFY(!set.isEmpty()); - QVERIFY(set.count() == set.size()); - - set.remove(2); - QVERIFY(set.size() == 0); - QVERIFY(set.isEmpty()); - QVERIFY(set.count() == set.size()); -} - -void TestTools::set_capacity() -{ - Set<int> set; - int n = set.capacity(); - QVERIFY(n == 0); - - for (int i = 0; i < 1000; ++i) { - set.insert(i); - QVERIFY(set.capacity() >= set.size()); - } -} - -void TestTools::set_reserve() -{ - Set<int> set; - - set.reserve(1000); - QVERIFY(set.capacity() >= 1000); - - for (int i = 0; i < 500; ++i) - set.insert(i); - - QVERIFY(set.capacity() >= 1000); - - for (int j = 0; j < 500; ++j) - set.remove(j); - - QVERIFY(set.capacity() >= 1000); -} - -void TestTools::set_clear() -{ - Set<QString> set1, set2; - QVERIFY(set1.size() == 0); - - set1.clear(); - QVERIFY(set1.size() == 0); - - set1.insert("foo"); - QVERIFY(set1.size() != 0); - - set2 = set1; - - set1.clear(); - QVERIFY(set1.size() == 0); - QVERIFY(set2.size() != 0); - - set2.clear(); - QVERIFY(set1.size() == 0); - QVERIFY(set2.size() == 0); -} - -void TestTools::set_remove() -{ - Set<QString> set1; - - for (int i = 0; i < 500; ++i) - set1.insert(QString::number(i)); - - QCOMPARE(set1.size(), 500); - - for (int j = 0; j < 500; ++j) { - set1.remove(QString::number((j * 17) % 500)); - QCOMPARE(set1.size(), 500 - j - 1); - } -} - -void TestTools::set_contains() -{ - Set<QString> set1; - - for (int i = 0; i < 500; ++i) { - QVERIFY(!set1.contains(QString::number(i))); - set1.insert(QString::number(i)); - QVERIFY(set1.contains(QString::number(i))); - } - - QCOMPARE(set1.size(), 500); - - for (int j = 0; j < 500; ++j) { - int i = (j * 17) % 500; - QVERIFY(set1.contains(QString::number(i))); - set1.remove(QString::number(i)); - QVERIFY(!set1.contains(QString::number(i))); - } -} - -void TestTools::set_containsSet() -{ - Set<QString> set1; - Set<QString> set2; - - // empty set contains the empty set - QVERIFY(set1.contains(set2)); - - for (int i = 0; i < 500; ++i) { - set1.insert(QString::number(i)); - set2.insert(QString::number(i)); - } - QVERIFY(set1.contains(set2)); - - set2.remove(QString::number(19)); - set2.remove(QString::number(82)); - set2.remove(QString::number(7)); - QVERIFY(set1.contains(set2)); - - set1.remove(QString::number(23)); - QVERIFY(!set1.contains(set2)); - - // filled set contains the empty set as well - Set<QString> set3; - QVERIFY(set1.contains(set3)); - - // the empty set doesn't contain a filled set - QVERIFY(!set3.contains(set1)); - - // verify const signature - const Set<QString> set4; - QVERIFY(set3.contains(set4)); -} - -void TestTools::set_begin() -{ - Set<int> set1; - Set<int> set2 = set1; - - { - Set<int>::const_iterator i = set1.constBegin(); - Set<int>::const_iterator j = set1.cbegin(); - Set<int>::const_iterator k = set2.constBegin(); - Set<int>::const_iterator ell = set2.cbegin(); - - QVERIFY(i == j); - QVERIFY(k == ell); - } - - set1.insert(44); - - { - Set<int>::const_iterator i = set1.constBegin(); - Set<int>::const_iterator j = set1.cbegin(); - Set<int>::const_iterator k = set2.constBegin(); - Set<int>::const_iterator ell = set2.cbegin(); - - QVERIFY(i == j); - QVERIFY(k == ell); - } - - set2 = set1; - - { - Set<int>::const_iterator i = set1.constBegin(); - Set<int>::const_iterator j = set1.cbegin(); - Set<int>::const_iterator k = set2.constBegin(); - Set<int>::const_iterator ell = set2.cbegin(); - - QVERIFY(i == j); - QVERIFY(k == ell); - } -} - -void TestTools::set_end() -{ - Set<int> set1; - Set<int> set2 = set1; - - { - Set<int>::const_iterator i = set1.constEnd(); - Set<int>::const_iterator j = set1.cend(); - Set<int>::const_iterator k = set2.constEnd(); - Set<int>::const_iterator ell = set2.cend(); - - QVERIFY(i == j); - QVERIFY(k == ell); - - QVERIFY(set1.constBegin() == set1.constEnd()); - QVERIFY(set2.constBegin() == set2.constEnd()); - } - - set1.insert(44); - - { - Set<int>::const_iterator i = set1.constEnd(); - Set<int>::const_iterator j = set1.cend(); - Set<int>::const_iterator k = set2.constEnd(); - Set<int>::const_iterator ell = set2.cend(); - - QVERIFY(i == j); - QVERIFY(k == ell); - - QVERIFY(set1.constBegin() != set1.constEnd()); - QVERIFY(set2.constBegin() == set2.constEnd()); - } - - set2 = set1; - - { - Set<int>::const_iterator i = set1.constEnd(); - Set<int>::const_iterator j = set1.cend(); - Set<int>::const_iterator k = set2.constEnd(); - Set<int>::const_iterator ell = set2.cend(); - - QVERIFY(i == j); - QVERIFY(k == ell); - - QVERIFY(set1.constBegin() != set1.constEnd()); - QVERIFY(set2.constBegin() != set2.constEnd()); - } - - set1.clear(); - set2.clear(); - QVERIFY(set1.constBegin() == set1.constEnd()); - QVERIFY(set2.constBegin() == set2.constEnd()); -} - -struct IdentityTracker { - int value, id; -}; -inline bool operator==(IdentityTracker lhs, IdentityTracker rhs) { return lhs.value == rhs.value; } -inline bool operator<(IdentityTracker lhs, IdentityTracker rhs) { return lhs.value < rhs.value; } - -void TestTools::set_insert() -{ - { - Set<int> set1; - QVERIFY(set1.size() == 0); - set1.insert(1); - QVERIFY(set1.size() == 1); - set1.insert(2); - QVERIFY(set1.size() == 2); - set1.insert(2); - QVERIFY(set1.size() == 2); - QVERIFY(set1.contains(2)); - set1.remove(2); - QVERIFY(set1.size() == 1); - QVERIFY(!set1.contains(2)); - set1.insert(2); - QVERIFY(set1.size() == 2); - QVERIFY(set1.contains(2)); - } - - { - Set<int> set1; - QVERIFY(set1.size() == 0); - set1 << 1; - QVERIFY(set1.size() == 1); - set1 << 2; - QVERIFY(set1.size() == 2); - set1 << 2; - QVERIFY(set1.size() == 2); - QVERIFY(set1.contains(2)); - set1.remove(2); - QVERIFY(set1.size() == 1); - QVERIFY(!set1.contains(2)); - set1 << 2; - QVERIFY(set1.size() == 2); - QVERIFY(set1.contains(2)); - } - - { - Set<IdentityTracker> set; - QCOMPARE(set.size(), 0); - const int dummy = -1; - IdentityTracker id00 = {0, 0}, id01 = {0, 1}, searchKey = {0, dummy}; - QCOMPARE(set.insert(id00).first->id, id00.id); - QCOMPARE(set.size(), 1); - QCOMPARE(set.insert(id01).first->id, id00.id); // first inserted is kept - QCOMPARE(set.size(), 1); - QCOMPARE(set.find(searchKey)->id, id00.id); - } -} - -void TestTools::set_reverseIterators() -{ - Set<int> s; - s << 1 << 17 << 61 << 127 << 911; - std::vector<int> v(s.begin(), s.end()); - std::reverse(v.begin(), v.end()); - const Set<int> &cs = s; - QVERIFY(std::equal(v.begin(), v.end(), s.rbegin())); - QVERIFY(std::equal(v.begin(), v.end(), s.crbegin())); - QVERIFY(std::equal(v.begin(), v.end(), cs.rbegin())); - QVERIFY(std::equal(s.rbegin(), s.rend(), v.begin())); - QVERIFY(std::equal(s.crbegin(), s.crend(), v.begin())); - QVERIFY(std::equal(cs.rbegin(), cs.rend(), v.begin())); -} - -void TestTools::set_stlIterator() -{ - Set<QString> set1; - for (int i = 0; i < 25000; ++i) - set1.insert(QString::number(i)); - - { - int sum = 0; - Set<QString>::const_iterator i = set1.cbegin(); - while (i != set1.end()) { - sum += toNumber(*i); - ++i; - } - QVERIFY(sum == 24999 * 25000 / 2); - } - - { - int sum = 0; - Set<QString>::const_iterator i = set1.cend(); - while (i != set1.begin()) { - --i; - sum += toNumber(*i); - } - QVERIFY(sum == 24999 * 25000 / 2); - } -} - -void TestTools::set_stlMutableIterator() -{ - Set<QString> set1; - for (int i = 0; i < 25000; ++i) - set1.insert(QString::number(i)); - - { - int sum = 0; - Set<QString>::iterator i = set1.begin(); - while (i != set1.end()) { - sum += toNumber(*i); - ++i; - } - QVERIFY(sum == 24999 * 25000 / 2); - } - - { - int sum = 0; - Set<QString>::iterator i = set1.end(); - while (i != set1.begin()) { - --i; - sum += toNumber(*i); - } - QVERIFY(sum == 24999 * 25000 / 2); - } - - { - Set<QString> set2 = set1; - Set<QString> set3 = set2; - - Set<QString>::iterator i = set2.begin(); - Set<QString>::iterator j = set3.begin(); - - while (i != set2.end()) { - i = set2.erase(i); - } - QVERIFY(set2.isEmpty()); - QVERIFY(!set3.isEmpty()); - - j = set3.end(); - while (j != set3.begin()) { - j--; - if (j + 1 != set3.end()) - set3.erase(j + 1); - } - if (set3.begin() != set3.end()) - set3.erase(set3.begin()); - - QVERIFY(set2.isEmpty()); - QVERIFY(set3.isEmpty()); - - i = set2.insert("foo").first; - QCOMPARE(*i, QLatin1String("foo")); - } -} - -void TestTools::set_setOperations() -{ - Set<QString> set1, set2; - set1 << "alpha" << "beta" << "gamma" << "delta" << "zeta" << "omega"; - set2 << "beta" << "gamma" << "delta" << "epsilon" << "iota" << "omega"; - - Set<QString> set3 = set1; - set3.unite(set2); - QVERIFY(set3.size() == 8); - QVERIFY(set3.contains("alpha")); - QVERIFY(set3.contains("beta")); - QVERIFY(set3.contains("gamma")); - QVERIFY(set3.contains("delta")); - QVERIFY(set3.contains("epsilon")); - QVERIFY(set3.contains("zeta")); - QVERIFY(set3.contains("iota")); - QVERIFY(set3.contains("omega")); - - Set<QString> set4 = set2; - set4.unite(set1); - QVERIFY(set4.size() == 8); - QVERIFY(set4.contains("alpha")); - QVERIFY(set4.contains("beta")); - QVERIFY(set4.contains("gamma")); - QVERIFY(set4.contains("delta")); - QVERIFY(set4.contains("epsilon")); - QVERIFY(set4.contains("zeta")); - QVERIFY(set4.contains("iota")); - QVERIFY(set4.contains("omega")); - - QVERIFY(set3 == set4); - - Set<QString> set5 = set1; - set5.intersect(set2); - QVERIFY(set5.size() == 4); - QVERIFY(set5.contains("beta")); - QVERIFY(set5.contains("gamma")); - QVERIFY(set5.contains("delta")); - QVERIFY(set5.contains("omega")); - - Set<QString> set6 = set2; - set6.intersect(set1); - QVERIFY(set6.size() == 4); - QVERIFY(set6.contains("beta")); - QVERIFY(set6.contains("gamma")); - QVERIFY(set6.contains("delta")); - QVERIFY(set6.contains("omega")); - - QVERIFY(set5 == set6); - - Set<QString> set7 = set1; - set7.subtract(set2); - QVERIFY(set7.size() == 2); - QVERIFY(set7.contains("alpha")); - QVERIFY(set7.contains("zeta")); - - Set<QString> set8 = set2; - set8.subtract(set1); - QVERIFY(set8.size() == 2); - QVERIFY(set8.contains("epsilon")); - QVERIFY(set8.contains("iota")); - - Set<QString> set9 = set1 | set2; - QVERIFY(set9 == set3); - - Set<QString> set10 = set1 & set2; - QVERIFY(set10 == set5); - - Set<QString> set11 = set1 + set2; - QVERIFY(set11 == set3); - - Set<QString> set12 = set1 - set2; - QVERIFY(set12 == set7); - - Set<QString> set13 = set2 - set1; - QVERIFY(set13 == set8); - - Set<QString> set14 = set1; - set14 |= set2; - QVERIFY(set14 == set3); - - Set<QString> set15 = set1; - set15 &= set2; - QVERIFY(set15 == set5); - - Set<QString> set16 = set1; - set16 += set2; - QVERIFY(set16 == set3); - - Set<QString> set17 = set1; - set17 -= set2; - QVERIFY(set17 == set7); - - Set<QString> set18 = set2; - set18 -= set1; - QVERIFY(set18 == set8); -} - -void TestTools::set_makeSureTheComfortFunctionsCompile() -{ - Set<int> set1, set2, set3; - set1 << 5; - set1 |= set2; - set1 |= 5; - set1 &= set2; - set1 &= 5; - set1 += set2; - set1 += 5; - set1 -= set2; - set1 -= 5; - set1 = set2 | set3; - set1 = set2 & set3; - set1 = set2 + set3; - set1 = set2 - set3; -} - -void TestTools::set_initializerList() -{ - Set<int> set = {1, 1, 2, 3, 4, 5}; - QCOMPARE(set.count(), 5); - QVERIFY(set.contains(1)); - QVERIFY(set.contains(2)); - QVERIFY(set.contains(3)); - QVERIFY(set.contains(4)); - QVERIFY(set.contains(5)); - - // check _which_ of the equal elements gets inserted (in the QHash/QMap case, it's the last): - const Set<IdentityTracker> set2 = {{1, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; - QCOMPARE(set2.count(), 5); - const int dummy = -1; - const IdentityTracker searchKey = {1, dummy}; - QCOMPARE(set2.find(searchKey)->id, 0); - - Set<int> emptySet{}; - QVERIFY(emptySet.isEmpty()); - - Set<int> set3{{}, {}, {}}; - QVERIFY(!set3.isEmpty()); -} - -void TestTools::set_intersects() -{ - Set<int> s1; - Set<int> s2; - - QVERIFY(!s1.intersects(s1)); - QVERIFY(!s1.intersects(s2)); - - s1 << 100; - QVERIFY(s1.intersects(s1)); - QVERIFY(!s1.intersects(s2)); - - s2 << 200; - QVERIFY(!s1.intersects(s2)); - - s1 << 200; - QVERIFY(s1.intersects(s2)); - - Set<int> s3; - s3 << 500; - QVERIFY(!s1.intersects(s3)); - s3 << 200; - QVERIFY(s1.intersects(s3)); -} - -} // namespace Internal -} // namespace qbs diff --git a/src/lib/corelib/use_corelib.pri b/src/lib/corelib/use_corelib.pri index 4b0751470..e79fcce04 100644 --- a/src/lib/corelib/use_corelib.pri +++ b/src/lib/corelib/use_corelib.pri @@ -45,3 +45,4 @@ CONFIG(static, static|shared) { DEFINES += QBS_STATIC_LIB } qbs_enable_project_file_updates:DEFINES += QBS_ENABLE_PROJECT_FILE_UPDATES +qbs_enable_unit_tests:DEFINES += QBS_ENABLE_UNIT_TESTS diff --git a/src/lib/corelib/use_installed_corelib.pri b/src/lib/corelib/use_installed_corelib.pri index d8bed5038..fefa774ff 100644 --- a/src/lib/corelib/use_installed_corelib.pri +++ b/src/lib/corelib/use_installed_corelib.pri @@ -35,3 +35,4 @@ CONFIG(static, static|shared) { DEFINES += QBS_STATIC_LIB } qbs_enable_project_file_updates:DEFINES += QBS_ENABLE_PROJECT_FILE_UPDATES +qbs_enable_unit_tests:DEFINES += QBS_ENABLE_UNIT_TESTS diff --git a/src/lib/library.pri b/src/lib/library.pri index ce7ab7e45..63f48a9a7 100644 --- a/src/lib/library.pri +++ b/src/lib/library.pri @@ -13,6 +13,7 @@ CONFIG(static, static|shared) { DEFINES += QBS_LIBRARY } DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_PROCESS_COMBINED_ARGUMENT_START +qbs_enable_unit_tests:DEFINES += QBS_ENABLE_UNIT_TESTS INCLUDEPATH += $${PWD}/../ contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols win32:CONFIG(debug, debug|release):TARGET = $${TARGET}d diff --git a/tests/auto/auto.pri b/tests/auto/auto.pri index 7ea658313..f0d6e9be4 100644 --- a/tests/auto/auto.pri +++ b/tests/auto/auto.pri @@ -1,7 +1,7 @@ TEMPLATE = app DESTDIR = ../../../bin DEFINES += SRCDIR=\\\"$$_PRO_FILE_PWD_\\\" -INCLUDEPATH += $$PWD/../../src +INCLUDEPATH += $$PWD/../../src $$PWD/../../src/app/shared QT = core testlib CONFIG += depend_includepath testcase console diff --git a/tests/auto/buildgraph/buildgraph.pro b/tests/auto/buildgraph/buildgraph.pro index 4388aac1e..10f8c071b 100644 --- a/tests/auto/buildgraph/buildgraph.pro +++ b/tests/auto/buildgraph/buildgraph.pro @@ -1,6 +1,7 @@ TARGET = tst_buildgraph SOURCES = tst_buildgraph.cpp +HEADERS = tst_buildgraph.h include(../auto.pri) include(../../../src/app/shared/logging/logging.pri) diff --git a/tests/auto/buildgraph/buildgraph.qbs b/tests/auto/buildgraph/buildgraph.qbs index f6c1cd1f7..aa3cdc3f0 100644 --- a/tests/auto/buildgraph/buildgraph.qbs +++ b/tests/auto/buildgraph/buildgraph.qbs @@ -3,5 +3,8 @@ import qbs QbsAutotest { testName: "buildgraph" condition: qbsbuildconfig.enableUnitTests - files: "tst_buildgraph.cpp" + files: [ + "tst_buildgraph.cpp", + "tst_buildgraph.h" + ] } diff --git a/tests/auto/buildgraph/tst_buildgraph.cpp b/tests/auto/buildgraph/tst_buildgraph.cpp index be45f5f03..04d32c1fd 100644 --- a/tests/auto/buildgraph/tst_buildgraph.cpp +++ b/tests/auto/buildgraph/tst_buildgraph.cpp @@ -5,7 +5,7 @@ ** ** This file is part of Qbs. ** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -14,26 +14,133 @@ ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include <app/shared/logging/consolelogger.h> -#include <buildgraph/tst_buildgraph.h> +#include "tst_buildgraph.h" + +#include <buildgraph/artifact.h> +#include <buildgraph/buildgraph.h> +#include <buildgraph/cycledetector.h> +#include <buildgraph/productbuilddata.h> +#include <buildgraph/projectbuilddata.h> +#include <language/language.h> +#include <logging/logger.h> +#include <tools/error.h> + +#include "../shared/logging/consolelogger.h" -#include <QtCore/qcoreapplication.h> #include <QtTest/qtest.h> +using namespace qbs; +using namespace qbs::Internal; + +const TopLevelProjectPtr project = TopLevelProject::create(); + +TestBuildGraph::TestBuildGraph(ILogSink *logSink) : m_logSink(logSink) +{ + project->buildData.reset(new ProjectBuildData); +} + +void TestBuildGraph::initTestCase() +{ +} + +void TestBuildGraph::cleanupTestCase() +{ +} + + +bool TestBuildGraph::cycleDetected(const ResolvedProductConstPtr &product) +{ + try { + CycleDetector(Logger(m_logSink)).visitProduct(product); + return false; + } catch (const ErrorInfo &) { + return true; + } +} + +ResolvedProductConstPtr TestBuildGraph::productWithDirectCycle() +{ + const ResolvedProductPtr product = ResolvedProduct::create(); + product->project = project; + product->buildData.reset(new ProductBuildData); + Artifact * const root = new Artifact; + root->product = product; + Artifact * const child = new Artifact; + child->product = product; + product->buildData->roots.insert(root); + product->buildData->nodes << root << child; + qbs::Internal::connect(root, child); + qbs::Internal::connect(child, root); + return product; +} + +ResolvedProductConstPtr TestBuildGraph::productWithLessDirectCycle() +{ + const ResolvedProductPtr product = ResolvedProduct::create(); + product->project = project; + product->buildData.reset(new ProductBuildData); + Artifact * const root = new Artifact; + Artifact * const child = new Artifact; + Artifact * const grandchild = new Artifact; + root->product = product; + child->product = product; + grandchild->product = product; + product->buildData->roots << root; + product->buildData->nodes << root << child << grandchild; + qbs::Internal::connect(root, child); + qbs::Internal::connect(child, grandchild); + qbs::Internal::connect(grandchild, root); + return product; +} + +// root appears as a child, but in a different tree +ResolvedProductConstPtr TestBuildGraph::productWithNoCycle() +{ + const ResolvedProductPtr product = ResolvedProduct::create(); + product->project = project; + product->buildData.reset(new ProductBuildData); + Artifact * const root = new Artifact; + Artifact * const root2 = new Artifact; + root->product = product; + root2->product = product; + product->buildData->roots << root << root2; + product->buildData->nodes << root << root2; + qbs::Internal::connect(root2, root); + return product; +} + +void TestBuildGraph::testCycle() +{ + QVERIFY(cycleDetected(productWithDirectCycle())); + QVERIFY(cycleDetected(productWithLessDirectCycle())); + QVERIFY(!cycleDetected(productWithNoCycle())); +} + int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); - qbs::Internal::TestBuildGraph tbg(ConsoleLogger::instance().logSink()); + TestBuildGraph tbg(ConsoleLogger::instance().logSink()); return QTest::qExec(&tbg, argc, argv); } diff --git a/src/lib/corelib/buildgraph/tst_buildgraph.h b/tests/auto/buildgraph/tst_buildgraph.h index e33037b30..756be2f0a 100644 --- a/src/lib/corelib/buildgraph/tst_buildgraph.h +++ b/tests/auto/buildgraph/tst_buildgraph.h @@ -42,19 +42,15 @@ #include <buildgraph/forward_decls.h> #include <language/forward_decls.h> #include <logging/ilogsink.h> -#include <tools/qbs_export.h> #include <QtCore/qlist.h> #include <QtCore/qobject.h> -namespace qbs { -namespace Internal { - -class QBS_EXPORT TestBuildGraph : public QObject +class TestBuildGraph : public QObject { Q_OBJECT public: - TestBuildGraph(ILogSink *logSink); + TestBuildGraph(qbs::ILogSink *logSink); private slots: void initTestCase(); @@ -62,15 +58,13 @@ private slots: void testCycle(); private: - ResolvedProductConstPtr productWithDirectCycle(); - ResolvedProductConstPtr productWithLessDirectCycle(); - ResolvedProductConstPtr productWithNoCycle(); - bool cycleDetected(const ResolvedProductConstPtr &product); + qbs::Internal::ResolvedProductConstPtr productWithDirectCycle(); + qbs::Internal::ResolvedProductConstPtr productWithLessDirectCycle(); + qbs::Internal::ResolvedProductConstPtr productWithNoCycle(); + bool cycleDetected(const qbs::Internal::ResolvedProductConstPtr &product); - ILogSink * const m_logSink; + qbs::ILogSink * const m_logSink; }; -} // namespace Internal -} // namespace qbs - #endif // TST_BUILDGRAPH_H + diff --git a/tests/auto/language/language.pro b/tests/auto/language/language.pro index 1206bd328..118d0f95c 100644 --- a/tests/auto/language/language.pro +++ b/tests/auto/language/language.pro @@ -1,10 +1,13 @@ TARGET = tst_language SOURCES = tst_language.cpp +HEADERS = tst_language.h include(../auto.pri) include(../../../src/app/shared/logging/logging.pri) +QT += script + DATA_DIRS = testdata for(data_dir, DATA_DIRS) { diff --git a/tests/auto/language/language.qbs b/tests/auto/language/language.qbs index b78c568a6..27f390e94 100644 --- a/tests/auto/language/language.qbs +++ b/tests/auto/language/language.qbs @@ -1,9 +1,21 @@ import qbs QbsAutotest { + Depends { name: "qbsversion" } + Depends { name: "Qt.script" } + testName: "language" condition: qbsbuildconfig.enableUnitTests - files: "tst_language.cpp" + files: [ + "tst_language.cpp", + "tst_language.h" + ] + + // TODO: Use Utilities.cStringQuote + cpp.defines: base.concat([ + 'QBS_VERSION="' + qbsversion.version + '"', + "SRCDIR=\"" + path + "\"" + ]) Group { name: "testdata" diff --git a/tests/auto/language/tst_language.cpp b/tests/auto/language/tst_language.cpp index 688b20089..3b076032c 100644 --- a/tests/auto/language/tst_language.cpp +++ b/tests/auto/language/tst_language.cpp @@ -5,7 +5,7 @@ ** ** This file is part of Qbs. ** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -14,33 +14,2440 @@ ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ +#undef QT_NO_CAST_FROM_ASCII // I am qmake, and I approve this hack. + +#include "tst_language.h" + #include "../shared.h" -#include <language/tst_language.h> +#include <language/evaluator.h> +#include <language/filecontext.h> +#include <language/identifiersearch.h> +#include <language/item.h> +#include <language/itempool.h> +#include <language/language.h> +#include <language/propertymapinternal.h> +#include <language/scriptengine.h> +#include <language/value.h> +#include <parser/qmljslexer_p.h> +#include <parser/qmljsparser_p.h> +#include <tools/scripttools.h> +#include <tools/error.h> +#include <tools/fileinfo.h> +#include <tools/hostosinfo.h> +#include <tools/jsliterals.h> +#include <tools/profile.h> +#include <tools/settings.h> + +#include "../shared/logging/consolelogger.h" -#include <app/shared/logging/consolelogger.h> +#include <QtCore/qprocess.h> -#include <QtCore/qcoreapplication.h> +#include <algorithm> +#include <utility> +#include <vector> + +Q_DECLARE_METATYPE(QList<bool>) + +using namespace qbs; +using namespace qbs::Internal; + +static QString testDataDir() { + return FileInfo::resolvePath(QLatin1String(SRCDIR), + QLatin1String("../../../tests/auto/language/testdata")); +} +static QString testProject(const char *fileName) { + return testDataDir() + QLatin1Char('/') + QLatin1String(fileName); +} -#include <QtTest/qtest.h> +TestLanguage::TestLanguage(ILogSink *logSink, Settings *settings) + : m_logSink(logSink) + , m_settings(settings) + , m_wildcardsTestDirPath(QDir::tempPath() + QLatin1String("/_wildcards_test_dir_")) +{ + qsrand(QTime::currentTime().msec()); + qRegisterMetaType<QList<bool> >("QList<bool>"); + defaultParameters.setBuildRoot("/some/build/directory"); + defaultParameters.setPropertyCheckingMode(ErrorHandlingMode::Strict); + defaultParameters.setSettingsDirectory(m_settings->baseDirectory()); +} + +TestLanguage::~TestLanguage() +{ +} + +QHash<QString, ResolvedProductPtr> TestLanguage::productsFromProject(ResolvedProjectPtr project) +{ + QHash<QString, ResolvedProductPtr> result; + foreach (const ResolvedProductPtr &product, project->allProducts()) + result.insert(product->name, product); + return result; +} + +ResolvedModuleConstPtr TestLanguage::findModuleByName(ResolvedProductPtr product, const QString &name) +{ + foreach (const ResolvedModuleConstPtr &module, product->modules) + if (module->name == name) + return module; + return ResolvedModuleConstPtr(); +} + +QVariant TestLanguage::productPropertyValue(ResolvedProductPtr product, QString propertyName) +{ + QStringList propertyNameComponents = propertyName.split(QLatin1Char('.')); + if (propertyNameComponents.count() > 1) { + propertyNameComponents.prepend(QLatin1String("modules")); + return product->moduleProperties->property(propertyNameComponents); + } + return getConfigProperty(product->productProperties, propertyNameComponents); +} + +void TestLanguage::handleInitCleanupDataTags(const char *projectFileName, bool *handled) +{ + const QByteArray dataTag = QTest::currentDataTag(); + if (dataTag == "init") { + *handled = true; + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject(projectFileName)); + project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); + } else if (dataTag == "cleanup") { + *handled = true; + project.reset(); + } else { + *handled = false; + } +} + +void TestLanguage::init() +{ + m_logSink->setLogLevel(LoggerInfo); +} + +#define HANDLE_INIT_CLEANUP_DATATAGS(fn) {\ + bool handled;\ + handleInitCleanupDataTags(fn, &handled);\ + if (handled)\ + return;\ + QVERIFY(!!project);\ +} + +void TestLanguage::initTestCase() +{ + m_logger = Logger(m_logSink); + m_engine = new ScriptEngine(m_logger, EvalContext::PropertyEvaluation, this); + loader = new Loader(m_engine, m_logger); + loader->setSearchPaths(QStringList() + << QLatin1String(SRCDIR "/../../../share/qbs")); + defaultParameters.setTopLevelProfile(profileName()); + defaultParameters.setConfigurationName("default"); + defaultParameters.expandBuildConfiguration(); + defaultParameters.setEnvironment(QProcessEnvironment::systemEnvironment()); + QVERIFY(QFileInfo(m_wildcardsTestDirPath).isAbsolute()); +} + +void TestLanguage::cleanupTestCase() +{ + delete loader; +} + +void TestLanguage::baseProperty() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("baseproperty.qbs")); + project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + ResolvedProductPtr product = products.value("product1"); + QVERIFY(!!product); + QVariantMap cfg = product->productProperties; + QCOMPARE(cfg.value("narf").toStringList(), QStringList() << "boo"); + QCOMPARE(cfg.value("zort").toStringList(), QStringList() << "bar" << "boo"); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::baseValidation() +{ + qbs::SetupProjectParameters params = defaultParameters; + params.setProjectFilePath(testProject("base-validate/base-validate.qbs")); + try { + project = loader->loadProject(params); + QVERIFY2(false, "exception expected"); + } catch (const qbs::ErrorInfo &e) { + QVERIFY2(e.toString().contains("Parent succeeded, child failed."), + qPrintable(e.toString())); + } +} + +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); + QVERIFY(!!project); + QCOMPARE(project->projectProperties().value("someStrings").toStringList(), + QStringList() << "foo" << "bar" << "baz"); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::builtinFunctionInSearchPathsProperty() +{ + bool exceptionCaught = false; + try { + SetupProjectParameters parameters = defaultParameters; + parameters.setProjectFilePath(testProject("builtinFunctionInSearchPathsProperty.qbs")); + QVERIFY(!!loader->loadProject(parameters)); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::chainedProbes() +{ + bool exceptionCaught = false; + try { + SetupProjectParameters parameters = defaultParameters; + parameters.setProjectFilePath(testProject("chained-probes/chained-probes.qbs")); + const TopLevelProjectConstPtr project = loader->loadProject(parameters); + QVERIFY(!!project); + QCOMPARE(project->products.count(), 1); + const QString prop2Val = project->products.first()->moduleProperties + ->moduleProperty("m", "prop2").toString(); + QCOMPARE(prop2Val, QLatin1String("probe1Valprobe2Val")); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); + +} + +void TestLanguage::versionCompare() +{ + bool exceptionCaught = false; + try { + SetupProjectParameters parameters = defaultParameters; + parameters.setProjectFilePath(testProject("versionCompare.qbs")); + QVERIFY(!!loader->loadProject(parameters)); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::canonicalArchitecture() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("canonicalArchitecture.qbs")); + project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + ResolvedProductPtr product = products.value(QLatin1String("x86")); + QVERIFY(!!product); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::rfc1034Identifier() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("rfc1034identifier.qbs")); + project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + ResolvedProductPtr product = products.value(QLatin1String("this-has-special-characters-" + "uh-oh-Undersc0r3s-Are.Bad")); + QVERIFY(!!product); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::conditionalDepends() +{ + bool exceptionCaught = false; + ResolvedProductPtr product; + ResolvedModuleConstPtr dependency; + try { + defaultParameters.setProjectFilePath(testProject("conditionaldepends.qbs")); + project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + + product = products.value("conditionaldepends_derived"); + QVERIFY(!!product); + dependency = findModuleByName(product, "dummy"); + QVERIFY(!!dependency); + + product = products.value("conditionaldepends_derived_false"); + QVERIFY(!!product); + dependency = findModuleByName(product, "dummy"); + QCOMPARE(dependency, ResolvedModuleConstPtr()); + + product = products.value("product_props_true"); + QVERIFY(!!product); + dependency = findModuleByName(product, "dummy"); + QVERIFY(!!dependency); + + product = products.value("product_props_false"); + QVERIFY(!!product); + dependency = findModuleByName(product, "dummy"); + QCOMPARE(dependency, ResolvedModuleConstPtr()); + + product = products.value("project_props_true"); + QVERIFY(!!product); + dependency = findModuleByName(product, "dummy"); + QVERIFY(!!dependency); + + product = products.value("project_props_false"); + QVERIFY(!!product); + dependency = findModuleByName(product, "dummy"); + QCOMPARE(dependency, ResolvedModuleConstPtr()); + + product = products.value("module_props_true"); + QVERIFY(!!product); + dependency = findModuleByName(product, "dummy2"); + QVERIFY(!!dependency); + dependency = findModuleByName(product, "dummy"); + QVERIFY(!!dependency); + + product = products.value("module_props_false"); + QVERIFY(!!product); + dependency = findModuleByName(product, "dummy2"); + QVERIFY(!!dependency); + dependency = findModuleByName(product, "dummy"); + QCOMPARE(dependency, ResolvedModuleConstPtr()); + + product = products.value("contradictory_conditions1"); + QVERIFY(!!product); + dependency = findModuleByName(product, "dummy"); + QVERIFY(!!dependency); + + product = products.value("contradictory_conditions2"); + QVERIFY(!!product); + dependency = findModuleByName(product, "dummy"); + QVERIFY(!!dependency); + + product = products.value("unknown_dependency_condition_false"); + QVERIFY(!!product); + dependency = findModuleByName(product, "doesonlyexistifhellfreezesover"); + QVERIFY(!dependency); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +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); + QCOMPARE(productEnabled, false); + QVERIFY(!!project); + QCOMPARE(project->products.count(), 1); + const ResolvedProductConstPtr theProduct = productsFromProject(project).value("theProduct"); + QVERIFY(!!theProduct); + QCOMPARE(theProduct->enabled, false); + } catch (const ErrorInfo &e) { + if (!productEnabled) + qDebug() << e.toString(); + QCOMPARE(productEnabled, true); + } +} + +void TestLanguage::delayedError_data() +{ + QTest::addColumn<QString>("projectFileName"); + QTest::addColumn<bool>("productEnabled"); + QTest::newRow("product enabled, module validation error") + << "delayed-error/validation.qbs" << true; + QTest::newRow("product disabled, module validation error") + << "delayed-error/validation.qbs" << false; + QTest::newRow("product enabled, module not found") + << "delayed-error/nonexisting.qbs" << true; + QTest::newRow("product disabled, module not found") + << "delayed-error/nonexisting.qbs" << false; +} + +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); + p2.p.setValue("qbs.architecture", "arch2"); + QVariantMap overriddenValues; + overriddenValues.insert("project.profile1", "p1"); + overriddenValues.insert("project.profile2", "p2"); + params.setOverriddenValues(overriddenValues); + project = loader->loadProject(params); + QVERIFY(!!project); + QCOMPARE(project->products.count(), 3); + const ResolvedProductConstPtr mainProduct = productsFromProject(project).value("main"); + QVERIFY(!!mainProduct); + QCOMPARE(mainProduct->dependencies.count(), 2); + foreach (const ResolvedProductConstPtr &p, mainProduct->dependencies) { + QCOMPARE(p->name, QLatin1String("dep")); + QVERIFY(p->profile == "p1" || p->profile == "p2"); + } + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::derivedSubProject() +{ + bool exceptionCaught = false; + try { + SetupProjectParameters params = defaultParameters; + params.setProjectFilePath(testProject("derived-sub-project/project.qbs")); + const TopLevelProjectPtr project = loader->loadProject(params); + QVERIFY(!!project); + const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 1); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::enumerateProjectProperties() +{ + bool exceptionCaught = false; + try { + SetupProjectParameters params = defaultParameters; + params.setProjectFilePath(testProject("enum-project-props.qbs")); + auto project = loader->loadProject(params); + QVERIFY(!!project); + auto products = productsFromProject(project); + QCOMPARE(products.count(), 1); + auto product = products.values().first(); + auto files = product->groups.first()->allFiles(); + QCOMPARE(product->groups.count(), 1); + QCOMPARE(files.count(), 1); + auto fileName = FileInfo::fileName(files.first()->absoluteFilePath); + QCOMPARE(fileName, QString("dummy.txt")); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +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); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 2); + const ResolvedProductPtr product = products.value("egon"); + QVERIFY(!!product); + QStringList propertyName = QStringList() << "modules" << "lower" << "prop2"; + QVariant propertyValue = product->moduleProperties->property(propertyName); + QFETCH(QVariant, expectedProp2Value); + QCOMPARE(propertyValue, expectedProp2Value); + propertyName = QStringList() << "modules" << "lower" << "listProp"; + propertyValue = product->moduleProperties->property(propertyName); + QFETCH(QVariant, expectedListPropValue); + QCOMPARE(propertyValue.toStringList(), expectedListPropValue.toStringList()); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::defaultValue_data() +{ + QTest::addColumn<QString>("prop1Value"); + QTest::addColumn<QVariant>("expectedProp2Value"); + QTest::addColumn<QVariant>("expectedListPropValue"); + QTest::newRow("controlling property with random value") << "random" << QVariant("withoutBlubb") + << QVariant(QStringList({"other"})); + QTest::newRow("controlling property with blubb value") << "blubb" << QVariant("withBlubb") + << QVariant(QStringList({"blubb", "other"})); + QTest::newRow("controlling property with egon value") << "egon" << QVariant("withEgon") + << QVariant(QStringList({"egon", "other"})); + QTest::newRow("controlling property not overwritten") << "" << QVariant("withBlubb") + << QVariant(QStringList({"blubb", "other"})); +} + +void TestLanguage::environmentVariable() +{ + bool exceptionCaught = false; + try { + // Create new environment: + const QString varName = QLatin1String("PRODUCT_NAME"); + const QString productName = QLatin1String("MyApp") + QString::number(qrand()); + 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 + + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + ResolvedProductPtr product = products.value(productName); + QVERIFY(!!product); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::erroneousFiles_data() +{ + QTest::addColumn<QString>("errorMessage"); + QTest::newRow("unknown_module") + << "Dependency 'neitherModuleNorProduct' not found"; + QTest::newRow("multiple_exports") + << "Multiple Export items in one product are prohibited."; + QTest::newRow("multiple_properties_in_subproject") + << "Multiple instances of item 'Properties' found where at most one " + "is allowed."; + QTest::newRow("importloop1") + << "Loop detected when importing"; + QTest::newRow("nonexistentouter") + << "Can't find variable: outer"; + QTest::newRow("invalid_file") + << "does not exist"; + QTest::newRow("invalid-parameter-rhs") + << "ReferenceError: Can't find variable: access"; + QTest::newRow("invalid-parameter-type") + << "Value assigned to property 'stringParameter' does not have type 'string'."; + QTest::newRow("invalid_property_type") + << "Unknown type 'nonsense' in property declaration."; + QTest::newRow("reserved_name_in_import") + << "Cannot reuse the name of built-in extension 'TextFile'."; + QTest::newRow("throw_in_property_binding") + << "something is wrong"; + QTest::newRow("dependency_cycle") + << "Cyclic dependencies detected."; + QTest::newRow("dependency_cycle2") + << "Cyclic dependencies detected."; + QTest::newRow("dependency_cycle3") + << "Cyclic dependencies detected."; + QTest::newRow("dependency_cycle4") + << "Cyclic dependencies detected."; + QTest::newRow("references_cycle") + << "Cycle detected while referencing file 'references_cycle.qbs'."; + QTest::newRow("subproject_cycle") + << "Cycle detected while loading subproject file 'subproject_cycle.qbs'."; + QTest::newRow("invalid_stringlist_element") + << "Element at index 1 of list property 'files' does not have string type."; + QTest::newRow("undefined_stringlist_element") + << "Element at index 1 of list property 'files' is undefined. String expected."; + QTest::newRow("undeclared_item") + << "Item 'cpp' is not declared."; + QTest::newRow("undeclared-parameter1") + << "Parameter 'prefix2.suffix.nope' is not declared."; + QTest::newRow("undeclared-parameter2") + << "Cannot set parameter 'foo.bar', " + "because 'myproduct' does not have a dependency on 'foo'."; + QTest::newRow("undeclared_property_wrapper") + << "Property 'doesntexist' is not declared."; + QTest::newRow("undeclared_property_in_export_item") + << "Property 'blubb' is not declared."; + QTest::newRow("undeclared_property_in_export_item2") + << "Item 'something' is not declared."; + QTest::newRow("undeclared_property_in_export_item3") + << "Property 'blubb' is not declared."; + QTest::newRow("unknown_item_type") + << "Unexpected item type 'Narf'"; + QTest::newRow("invalid_child_item_type") + << "Items of type 'Project' cannot contain items of type 'Depends'."; + QTest::newRow("conflicting_fileTagsFilter") + << "Conflicting fileTagsFilter in Group items"; + QTest::newRow("duplicate_sources") + << "Duplicate source file '.*main.cpp'" + ".*duplicate_sources.qbs:4:12.*duplicate_sources.qbs:6:16."; + QTest::newRow("duplicate_sources_wildcards") + << "Duplicate source file '.*duplicate_sources_wildcards.qbs'" + ".*duplicate_sources_wildcards.qbs:4:12" + ".*duplicate_sources_wildcards.qbs:6:16."; + QTest::newRow("oldQbsVersion") + << "The project requires at least qbs version \\d+\\.\\d+.\\d+, " + "but this is qbs version " QBS_VERSION "."; + QTest::newRow("wrongQbsVersionFormat") + << "The value '.*' of Project.minimumQbsVersion is not a valid version string."; + QTest::newRow("properties-item-with-invalid-condition") + << "TypeError: Result of expression 'cpp.nonexistingproperty'"; + 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?"; + QTest::newRow("wrong-toplevel-item") + << "wrong-toplevel-item.qbs:3:1.*The top-level item must be of type 'Project' or " + "'Product', but it is of type 'Artifact'."; + QTest::newRow("module-depends-on-product") + << "module-with-product-dependency.qbs:4:5.*Modules cannot depend on products."; + QTest::newRow("overwrite-inherited-readonly-property") + << "overwrite-inherited-readonly-property.qbs" + ":4:21.*Cannot set read-only property 'readOnlyString'."; + QTest::newRow("overwrite-readonly-module-property") + << "overwrite-readonly-module-property.qbs" + ":5:30.*Cannot set read-only property 'readOnlyString'."; + QTest::newRow("mismatching-multiplex-dependency") + << "mismatching-multiplex-dependency.qbs:9:5 Dependency from product 'b' to " + "product 'a' not fulfilled.\nNo product 'a' found with a matching multiplex " + "configuration:\n\tqbs.architecture: mips"; +} + +void TestLanguage::erroneousFiles() +{ + QFETCH(QString, errorMessage); + QString fileName = QString::fromLocal8Bit(QTest::currentDataTag()) + QLatin1String(".qbs"); + try { + defaultParameters.setProjectFilePath(testProject("/erroneous/") + fileName); + loader->loadProject(defaultParameters); + } catch (const ErrorInfo &e) { + if (!e.toString().contains(QRegExp(errorMessage))) { + qDebug() << "Message: " << e.toString(); + qDebug() << "Expected: " << errorMessage; + QFAIL("Unexpected error message."); + } + return; + } + QEXPECT_FAIL("undeclared_property_in_Properties_item", "Too expensive to check", Continue); + QVERIFY(!"No error thrown on invalid input."); +} + +void TestLanguage::exports() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("exports.qbs")); + TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 17); + ResolvedProductPtr product; + product = products.value("myapp"); + QVERIFY(!!product); + QStringList propertyName = QStringList() << "modules" << "dummy" << "defines"; + QVariant propertyValue = product->moduleProperties->property(propertyName); + QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYAPP" << "USE_MYLIB" + << "USE_MYLIB2"); + propertyName = QStringList() << "modules" << "dummy" << "includePaths"; + QVariantList propertyValues = product->moduleProperties->property(propertyName).toList(); + QCOMPARE(propertyValues.count(), 3); + QVERIFY(propertyValues.at(0).toString().endsWith("/app")); + QVERIFY(propertyValues.at(1).toString().endsWith("/subdir/lib")); + QVERIFY(propertyValues.at(2).toString().endsWith("/subdir2/lib")); + + QCOMPARE(product->moduleProperties->moduleProperty("dummy", "productName").toString(), + QString("myapp")); + + product = products.value("mylib"); + QVERIFY(!!product); + propertyName = QStringList() << "modules" << "dummy" << "defines"; + propertyValue = product->moduleProperties->property(propertyName); + QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYLIB"); + + product = products.value("mylib2"); + QVERIFY(!!product); + propertyName = QStringList() << "modules" << "dummy" << "defines"; + propertyValue = product->moduleProperties->property(propertyName); + QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYLIB2"); + + product = products.value("A"); + QVERIFY(!!product); + QVERIFY(product->dependencies.contains(products.value("B"))); + QVERIFY(product->dependencies.contains(products.value("C"))); + QVERIFY(product->dependencies.contains(products.value("D"))); + product = products.value("B"); + QVERIFY(!!product); + QVERIFY(product->dependencies.isEmpty()); + product = products.value("C"); + QVERIFY(!!product); + QVERIFY(product->dependencies.isEmpty()); + product = products.value("D"); + QVERIFY(!!product); + QVERIFY(product->dependencies.isEmpty()); + + product = products.value("myapp2"); + QVERIFY(!!product); + propertyName = QStringList() << "modules" << "dummy" << "cFlags"; + propertyValue = product->moduleProperties->property(propertyName); + QCOMPARE(propertyValue.toStringList(), QStringList() + << "BASE_PRODUCTWITHINHERITEDEXPORTITEM" + << "PRODUCT_PRODUCTWITHINHERITEDEXPORTITEM"); + propertyName = QStringList() << "modules" << "dummy" << "cxxFlags"; + propertyValue = product->moduleProperties->property(propertyName); + QCOMPARE(propertyValue.toStringList(), QStringList() << "-bar"); + propertyName = QStringList() << "modules" << "dummy" << "defines"; + propertyValue = product->moduleProperties->property(propertyName); + QCOMPARE(propertyValue.toStringList(), QStringList() << "ABC"); + QCOMPARE(product->moduleProperties->moduleProperty("dummy", "productName").toString(), + QString("myapp2")); + QCOMPARE(product->moduleProperties->moduleProperty("dummy", + "upperCaseProductName").toString(), QString("MYAPP2")); + + // Check whether we're returning incorrect cached values. + product = products.value("myapp3"); + QVERIFY(!!product); + QCOMPARE(product->moduleProperties->moduleProperty("dummy", "productName").toString(), + QString("myapp3")); + QCOMPARE(product->moduleProperties->moduleProperty("dummy", + "upperCaseProductName").toString(), QString("MYAPP3")); + + // Verify we refer to the right "project" variable. + product = products.value("sub p2"); + QVERIFY(!!product); + QCOMPARE(product->moduleProperties->moduleProperty("dummy", "someString").toString(), + QString("sub1")); + + product = products.value("libE"); + QVERIFY(!!product); + propertyName = QStringList() << "modules" << "dummy" << "defines"; + propertyValue = product->moduleProperties->property(propertyName); + QCOMPARE(propertyValue.toStringList(), + QStringList() << "LIBA" << "LIBB" << "LIBC" << "LIBD"); + propertyName = QStringList() << "modules" << "dummy" << "productName"; + propertyValue = product->moduleProperties->property(propertyName); + QCOMPARE(propertyValue.toString(), QString("libE")); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::fileContextProperties() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("filecontextproperties.qbs")); + project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + ResolvedProductPtr product = products.value("product1"); + QVERIFY(!!product); + QVariantMap cfg = product->productProperties; + QCOMPARE(cfg.value("narf").toString(), defaultParameters.projectFilePath()); + QString dirPath = QFileInfo(defaultParameters.projectFilePath()).absolutePath(); + QCOMPARE(cfg.value("zort").toString(), dirPath); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::getNativeSetting() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("getNativeSetting.qbs")); + project = loader->loadProject(defaultParameters); + + QString expectedTargetName; + if (HostOsInfo::isMacosHost()) + expectedTargetName = QLatin1String("Mac OS X"); + else if (HostOsInfo::isWindowsHost()) + expectedTargetName = QLatin1String("Windows"); + else + expectedTargetName = QLatin1String("Unix"); + + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products; + for (const ResolvedProductPtr &product : project->allProducts()) + products.insert(product->targetName, product); + ResolvedProductPtr product = products.value(expectedTargetName); + QVERIFY(!!product); + ResolvedProductPtr product2 = products.value(QLatin1String("fallback")); + QVERIFY(!!product2); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::groupConditions_data() +{ + QTest::addColumn<int>("groupCount"); + QTest::addColumn<QList<bool> >("groupEnabled"); + QTest::newRow("init") << 0 << QList<bool>(); + QTest::newRow("no_condition_no_group") + << 1 << (QList<bool>() << true); + QTest::newRow("no_condition") + << 2 << (QList<bool>() << true << true); + QTest::newRow("true_condition") + << 2 << (QList<bool>() << true << true); + QTest::newRow("false_condition") + << 2 << (QList<bool>() << true << false); + QTest::newRow("true_condition_from_product") + << 2 << (QList<bool>() << true << true); + QTest::newRow("true_condition_from_project") + << 2 << (QList<bool>() << true << true); + QTest::newRow("condition_accessing_module_property") + << 2 << (QList<bool>() << true << false); + QTest::newRow("cleanup") << 0 << QList<bool>(); +} + +void TestLanguage::groupConditions() +{ + HANDLE_INIT_CLEANUP_DATATAGS("groupconditions.qbs"); + QFETCH(int, groupCount); + QFETCH(QList<bool>, groupEnabled); + QCOMPARE(groupCount, groupEnabled.count()); + const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + const QString productName = QString::fromLocal8Bit(QTest::currentDataTag()); + ResolvedProductPtr product = products.value(productName); + QVERIFY(!!product); + QCOMPARE(product->name, productName); + QCOMPARE(product->groups.count(), groupCount); + for (int i = 0; i < groupCount; ++i) { + if (product->groups.at(i)->enabled != groupEnabled.at(i)) { + QFAIL(qPrintable( + QString("groups.at(%1)->enabled != %2").arg(i).arg(groupEnabled.at(i)))); + } + } +} + +void TestLanguage::groupName() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("groupname.qbs")); + TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 2); + + ResolvedProductPtr product = products.value("MyProduct"); + QVERIFY(!!product); + QCOMPARE(product->groups.count(), 2); + GroupConstPtr group = product->groups.at(0); + QVERIFY(!!group); + QCOMPARE(group->name, QString("MyProduct")); + group = product->groups.at(1); + QVERIFY(!!group); + QCOMPARE(group->name, QString("MyProduct.MyGroup")); + + product = products.value("My2ndProduct"); + QVERIFY(!!product); + QCOMPARE(product->groups.count(), 3); + group = product->groups.at(0); + QVERIFY(!!group); + QCOMPARE(group->name, QString("My2ndProduct")); + group = product->groups.at(1); + QVERIFY(!!group); + QCOMPARE(group->name, QString("My2ndProduct.MyGroup")); + group = product->groups.at(2); + QVERIFY(!!group); + QCOMPARE(group->name, QString("Group 2")); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::homeDirectory() +{ + try { + defaultParameters.setProjectFilePath(testProject("homeDirectory.qbs")); + ResolvedProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 1); + + ResolvedProductPtr product = products.value("home"); + QVERIFY(!!product); + + QDir dir = QDir::home(); + QCOMPARE(product->productProperties.value("home").toString(), dir.path()); + QCOMPARE(product->productProperties.value("homeSlash").toString(), dir.path()); + + dir.cdUp(); + QCOMPARE(product->productProperties.value("homeUp").toString(), dir.path()); + + dir = QDir::home(); + QCOMPARE(product->productProperties.value("homeFile").toString(), + dir.filePath("a")); + + QCOMPARE(product->productProperties.value("bogus1").toString(), + FileInfo::resolvePath(product->sourceDirectory, QLatin1String("a~b"))); + QCOMPARE(product->productProperties.value("bogus2").toString(), + FileInfo::resolvePath(product->sourceDirectory, QLatin1String("a/~/bb"))); + QCOMPARE(product->productProperties.value("user").toString(), + FileInfo::resolvePath(product->sourceDirectory, QLatin1String("~foo/bar"))); + } + catch (const ErrorInfo &e) { + qDebug() << e.toString(); + } +} + +void TestLanguage::identifierSearch_data() +{ + QTest::addColumn<bool>("expectedHasNarf"); + QTest::addColumn<bool>("expectedHasZort"); + QTest::addColumn<QString>("sourceCode"); + QTest::newRow("no narf, no zort") << false << false << QString( + "Product {\n" + " name: {\n" + " var foo = 'bar';\n" + " console.info(foo);\n" + " return foo;\n" + " }\n" + "}\n"); + QTest::newRow("narf, no zort") << true << false << QString( + "Product {\n" + " name: {\n" + " var foo = 'zort';\n" + " console.info(narf + foo);\n" + " return foo;\n" + " }\n" + "}\n"); + QTest::newRow("no narf, zort") << false << true << QString( + "Product {\n" + " name: {\n" + " var foo = 'narf';\n" + " console.info(zort + foo);\n" + " return foo;\n" + " }\n" + "}\n"); + QTest::newRow("narf, zort") << true << true << QString( + "Product {\n" + " name: {\n" + " var foo = narf;\n" + " foo = foo + zort;\n" + " return foo;\n" + " }\n" + "}\n"); + QTest::newRow("2 narfs, 1 zort") << true << true << QString( + "Product {\n" + " name: {\n" + " var foo = narf;\n" + " foo = narf + foo + zort;\n" + " return foo;\n" + " }\n" + "}\n"); +} + +void TestLanguage::identifierSearch() +{ + QFETCH(bool, expectedHasNarf); + QFETCH(bool, expectedHasZort); + QFETCH(QString, sourceCode); + + bool hasNarf = !expectedHasNarf; + bool hasZort = !expectedHasZort; + IdentifierSearch isearch; + isearch.add("narf", &hasNarf); + isearch.add("zort", &hasZort); + + QbsQmlJS::Engine engine; + QbsQmlJS::Lexer lexer(&engine); + lexer.setCode(sourceCode, 1); + QbsQmlJS::Parser parser(&engine); + QVERIFY(parser.parse()); + QVERIFY(parser.ast()); + isearch.start(parser.ast()); + QCOMPARE(hasNarf, expectedHasNarf); + QCOMPARE(hasZort, expectedHasZort); +} + +void TestLanguage::idUsage() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("idusage.qbs")); + TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 4); + QVERIFY(products.contains("product1_1")); + QVERIFY(products.contains("product2_2")); + QVERIFY(products.contains("product3_3")); + ResolvedProductPtr product4 = products.value("product4_4"); + QVERIFY(!!product4); + QEXPECT_FAIL("", "QBS-1016", Continue); + QCOMPARE(product4->productProperties.value("productName").toString(), product4->name); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QVERIFY(!exceptionCaught); +} + +void TestLanguage::idUniqueness() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("id-uniqueness.qbs")); + loader->loadProject(defaultParameters); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + const QList<ErrorItem> items = e.items(); + QCOMPARE(items.count(), 3); + QCOMPARE(items.at(0).toString(), QString::fromUtf8("The id 'baseProduct' is not unique.")); + QVERIFY(items.at(1).toString().contains("id-uniqueness.qbs:6:5 First occurrence is here.")); + QVERIFY(items.at(2).toString().contains("id-uniqueness.qbs:9:5 Next occurrence is here.")); + } + QVERIFY(exceptionCaught); +} + +void TestLanguage::importCollection() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("import-collection/project.qbs")); + const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + 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) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QVERIFY(!exceptionCaught); +} + +void TestLanguage::invalidBindingInDisabledItem() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("invalidBindingInDisabledItem.qbs")); + TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 2); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QVERIFY(!exceptionCaught); +} + +class JSSourceValueCreator +{ + FileContextPtr m_fileContext; + QList<QString *> m_strings; +public: + JSSourceValueCreator(const FileContextPtr &fileContext) + : m_fileContext(fileContext) + { + } + + ~JSSourceValueCreator() + { + qDeleteAll(m_strings); + } + + JSSourceValuePtr create(const QString &sourceCode) + { + JSSourceValuePtr value = JSSourceValue::create(); + value->setFile(m_fileContext); + QString *str = new QString(sourceCode); + m_strings += str; + value->setSourceCode(QStringRef(str)); + return value; + } +}; + +void TestLanguage::itemPrototype() +{ + FileContextPtr fileContext = FileContext::create(); + fileContext->setFilePath("/dev/null"); + JSSourceValueCreator sourceValueCreator(fileContext); + ItemPool pool; + Item *proto = Item::create(&pool, ItemType::Product); + proto->setProperty("x", sourceValueCreator.create("1")); + proto->setProperty("y", sourceValueCreator.create("1")); + Item *item = Item::create(&pool, ItemType::Product); + item->setPrototype(proto); + 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); +} + +void TestLanguage::itemScope() +{ + FileContextPtr fileContext = FileContext::create(); + fileContext->setFilePath("/dev/null"); + JSSourceValueCreator sourceValueCreator(fileContext); + ItemPool pool; + Item *scope1 = Item::create(&pool, ItemType::Scope); + scope1->setProperty("x", sourceValueCreator.create("1")); + Item *scope2 = Item::create(&pool, ItemType::Scope); + scope2->setScope(scope1); + scope2->setProperty("y", sourceValueCreator.create("x + 1")); + Item *item = Item::create(&pool, ItemType::Scope); + 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); +} + +void TestLanguage::jsExtensions() +{ + QFile file(testProject("jsextensions.js")); + QVERIFY(file.open(QFile::ReadOnly)); + 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))); + } +} + +void TestLanguage::jsImportUsedInMultipleScopes_data() +{ + QTest::addColumn<QString>("buildVariant"); + QTest::addColumn<QString>("expectedProductName"); + QTest::newRow("debug") << QString("debug") << QString("MyProduct_debug"); + QTest::newRow("release") << QString("release") << QString("MyProduct"); +} + +void TestLanguage::jsImportUsedInMultipleScopes() +{ + QFETCH(QString, buildVariant); + QFETCH(QString, expectedProductName); + + bool exceptionCaught = false; + try { + SetupProjectParameters params = defaultParameters; + params.setProjectFilePath(testProject("jsimportsinmultiplescopes.qbs")); + params.setOverriddenValues({std::make_pair(QLatin1String("qbs.buildVariant"), + buildVariant)}); + params.expandBuildConfiguration(); + TopLevelProjectPtr project = loader->loadProject(params); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 1); + ResolvedProductPtr product = products.values().first(); + QVERIFY(!!product); + QCOMPARE(product->name, expectedProductName); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QVERIFY(!exceptionCaught); +} + +void TestLanguage::moduleProperties_data() +{ + QTest::addColumn<QString>("propertyName"); + QTest::addColumn<QVariant>("expectedValue"); + QTest::newRow("init") << QString() << QVariant(); + QTest::newRow("merge_lists") + << "defines" + << QVariant(QStringList() << "THE_PRODUCT" << "QT_CORE" << "QT_GUI" << "QT_NETWORK"); + QTest::newRow("merge_lists_and_values") + << "defines" + << QVariant(QStringList() << "THE_PRODUCT" << "QT_CORE" << "QT_GUI" << "QT_NETWORK"); + QTest::newRow("merge_lists_with_duplicates") + << "cxxFlags" + << QVariant(QStringList() << "-foo" << "BAR" << "-foo" << "BAZ"); + QTest::newRow("merge_lists_with_prototype_values") + << "rpaths" + << QVariant(QStringList() << "/opt/qt/lib" << "$ORIGIN"); + QTest::newRow("list_property_that_references_product") + << "listProp" + << QVariant(QStringList() << "x" << "123"); + QTest::newRow("list_property_depending_on_overridden_property") + << "listProp2" + << QVariant(QStringList() << "PRODUCT_STUFF" << "DEFAULT_STUFF" << "EXTRA_STUFF"); + QTest::newRow("overridden_list_property") + << "listProp" + << QVariant(QStringList() << "PRODUCT_STUFF"); + QTest::newRow("shadowed-list-property") + << "defines" + << QVariant(QStringList() << "MyProject" << "shadowed-list-property"); + QTest::newRow("shadowed-scalar-property") + << "someString" + << QVariant(QString("MyProject_shadowed-scalar-property")); + QTest::newRow("cleanup") << QString() << QVariant(); +} + +void TestLanguage::moduleProperties() +{ + HANDLE_INIT_CLEANUP_DATATAGS("moduleproperties.qbs"); + QFETCH(QString, propertyName); + QFETCH(QVariant, expectedValue); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + const QString productName = QString::fromLocal8Bit(QTest::currentDataTag()); + ResolvedProductPtr product = products.value(productName); + QVERIFY(!!product); + const QVariant value = product->moduleProperties->moduleProperty("dummy", propertyName); + QCOMPARE(value, expectedValue); +} + +void TestLanguage::modulePropertiesInGroups() +{ + defaultParameters.setProjectFilePath(testProject("modulepropertiesingroups.qbs")); + bool exceptionCaught = false; + try { + TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + const ResolvedProductPtr product = products.value("grouptest"); + QVERIFY(!!product); + GroupConstPtr g1; + GroupConstPtr g11; + GroupConstPtr g12; + GroupConstPtr g2; + GroupConstPtr g21; + GroupConstPtr g211; + foreach (const GroupConstPtr &g, product->groups) { + if (g->name == "g1") + g1= g; + else if (g->name == "g2") + g2 = g; + else if (g->name == "g1.1") + g11 = g; + else if (g->name == "g1.2") + g12 = g; + else if (g->name == "g2.1") + g21 = g; + else if (g->name == "g2.1.1") + g211 = g; + } + QVERIFY(!!g1); + QVERIFY(!!g2); + QVERIFY(!!g11); + QVERIFY(!!g12); + QVERIFY(!!g21); + QVERIFY(!!g211); + + const QVariantMap productProps = product->moduleProperties->value(); + const auto &productGmod1List1 = moduleProperty(productProps, "gmod.gmod1", "gmod1_list1") + .toStringList(); + QCOMPARE(productGmod1List1, QStringList() << "gmod1_list1_proto" << "gmod1_string_proto"); + const auto &productGmod1List2 = moduleProperty(productProps, "gmod.gmod1", "gmod1_list2") + .toStringList(); + QCOMPARE(productGmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" + << "gmod1_list2_proto"); + const auto &productGmod1List3 = moduleProperty(productProps, "gmod.gmod1", "gmod1_list3") + .toStringList(); + QCOMPARE(productGmod1List3, QStringList() << "product" << "gmod1_string_proto"); + const int productP0 = moduleProperty(productProps, "gmod.gmod1", "p0").toInt(); + QCOMPARE(productP0, 1); + const int productDepProp = moduleProperty(productProps, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(productDepProp, 0); + const auto &productGmod2String = moduleProperty(productProps, "gmod2", "gmod2_string") + .toString(); + QCOMPARE(productGmod2String, QString("gmod1_string_proto")); + const auto &productGmod2List = moduleProperty(productProps, "gmod2", "gmod2_list") + .toStringList(); + QCOMPARE(productGmod2List, QStringList() << "gmod1_string_proto" << "commonName_in_gmod1" + << "gmod4_string_proto_gmod3_string_proto" << "gmod3_string_proto" + << "gmod2_list_proto"); + + const QVariantMap g1Props = g1->properties->value(); + const auto &g1Gmod1List1 = moduleProperty(g1Props, "gmod.gmod1", "gmod1_list1") + .toStringList(); + QCOMPARE(g1Gmod1List1, QStringList() << "gmod1_list1_proto" << "g1"); + const auto &g1Gmod1List2 = moduleProperty(g1Props, "gmod.gmod1", "gmod1_list2") + .toStringList(); + QCOMPARE(g1Gmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" + << "gmod1_list2_proto" << "g1"); + const auto &g1Gmod1List3 = moduleProperty(g1Props, "gmod.gmod1", "gmod1_list3") + .toStringList(); + QCOMPARE(g1Gmod1List3, QStringList() << "product" << "g1"); + const int g1P0 = moduleProperty(g1Props, "gmod.gmod1", "p0").toInt(); + QCOMPARE(g1P0, 3); + const int g1DepProp = moduleProperty(g1Props, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(g1DepProp, 1); + const auto &g1Gmod2String = moduleProperty(g1Props, "gmod2", "gmod2_string").toString(); + QCOMPARE(g1Gmod2String, QString("g1")); + const auto &g1Gmod2List = moduleProperty(g1Props, "gmod2", "gmod2_list") + .toStringList(); + QCOMPARE(g1Gmod2List, QStringList() << "g1" << "commonName_in_gmod1" << "g1_gmod4_g1_gmod3" + << "g1_gmod3" << "gmod2_list_proto"); + + const QVariantMap g11Props = g11->properties->value(); + const auto &g11Gmod1List1 = moduleProperty(g11Props, "gmod.gmod1", "gmod1_list1") + .toStringList(); + QCOMPARE(g11Gmod1List1, QStringList() << "gmod1_list1_proto" << "g1.1"); + const auto &g11Gmod1List2 = moduleProperty(g11Props, "gmod.gmod1", "gmod1_list2") + .toStringList(); + QCOMPARE(g11Gmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" + << "gmod1_list2_proto" << "g1" << "g1.1"); + const auto &g11Gmod1List3 = moduleProperty(g11Props, "gmod.gmod1", "gmod1_list3") + .toStringList(); + QCOMPARE(g11Gmod1List3, QStringList() << "product" << "g1.1"); + const int g11P0 = moduleProperty(g11Props, "gmod.gmod1", "p0").toInt(); + QCOMPARE(g11P0, 5); + const int g11DepProp = moduleProperty(g11Props, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(g11DepProp, 2); + const auto &g11Gmod2String = moduleProperty(g11Props, "gmod2", "gmod2_string").toString(); + QCOMPARE(g11Gmod2String, QString("g1.1")); + const auto &g11Gmod2List = moduleProperty(g11Props, "gmod2", "gmod2_list") + .toStringList(); + QCOMPARE(g11Gmod2List, QStringList() << "g1.1" << "commonName_in_gmod1" + << "g1.1_gmod4_g1.1_gmod3" << "g1.1_gmod3" << "gmod2_list_proto"); + + const QVariantMap g12Props = g12->properties->value(); + const auto &g12Gmod1List1 = moduleProperty(g12Props, "gmod.gmod1", "gmod1_list1") + .toStringList(); + QCOMPARE(g12Gmod1List1, QStringList() << "gmod1_list1_proto" << "g1.2"); + const auto &g12Gmod1List2 = moduleProperty(g12Props, "gmod.gmod1", "gmod1_list2") + .toStringList(); + QCOMPARE(g12Gmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" + << "gmod1_list2_proto" << "g1" << "g1.2"); + const auto &g12Gmod1List3 = moduleProperty(g12Props, "gmod.gmod1", "gmod1_list3") + .toStringList(); + QCOMPARE(g12Gmod1List3, QStringList() << "product" << "g1.2"); + const int g12P0 = moduleProperty(g12Props, "gmod.gmod1", "p0").toInt(); + QCOMPARE(g12P0, 9); + const int g12DepProp = moduleProperty(g12Props, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(g12DepProp, 1); + const auto &g12Gmod2String = moduleProperty(g12Props, "gmod2", "gmod2_string").toString(); + QCOMPARE(g12Gmod2String, QString("g1.2")); + const auto &g12Gmod2List = moduleProperty(g12Props, "gmod2", "gmod2_list") + .toStringList(); + QCOMPARE(g12Gmod2List, QStringList() << "g1.2" << "commonName_in_gmod1" + << "g1_gmod4_g1.2_gmod3" << "g1.2_gmod3" << "gmod2_list_proto"); + + const QVariantMap g2Props = g2->properties->value(); + const auto &g2Gmod1List1 = moduleProperty(g2Props, "gmod.gmod1", "gmod1_list1") + .toStringList(); + 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"); + const int g2P0 = moduleProperty(g2Props, "gmod.gmod1", "p0").toInt(); + QCOMPARE(g2P0, 6); + const int g2DepProp = moduleProperty(g2Props, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(g2DepProp, 2); + const auto &g2Gmod2String = moduleProperty(g2Props, "gmod2", "gmod2_string").toString(); + QCOMPARE(g2Gmod2String, QString("g2")); + const auto &g2Gmod2List = moduleProperty(g2Props, "gmod2", "gmod2_list") + .toStringList(); + QCOMPARE(g2Gmod2List, QStringList() << "g2" << "commonName_in_gmod1" << "g2_gmod4_g2_gmod3" + << "g2_gmod3" << "gmod2_list_proto"); + + const QVariantMap g21Props = g21->properties->value(); + const auto &g21Gmod1List1 = moduleProperty(g21Props, "gmod.gmod1", "gmod1_list1") + .toStringList(); + 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); + QCOMPARE(g21Gmod1List2, QStringList() << "grouptest" << "g2.1" << "gmod1_list2_proto"); + const int g21P0 = moduleProperty(g21Props, "gmod.gmod1", "p0").toInt(); + QCOMPARE(g21P0, 6); + const int g21DepProp = moduleProperty(g21Props, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(g21DepProp, 2); + const auto &g21Gmod2String = moduleProperty(g21Props, "gmod2", "gmod2_string").toString(); + 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); + QCOMPARE(g21Gmod2List, QStringList() << "g2" << "commonName_in_gmod1" + << "g2.1_gmod4_g2.1_gmod3" << "g2.1_gmod3" << "gmod2_list_proto"); + + const QVariantMap g211Props = g211->properties->value(); + const auto &g211Gmod1List1 = moduleProperty(g211Props, "gmod.gmod1", "gmod1_list1") + .toStringList(); + QCOMPARE(g211Gmod1List1, QStringList() << "gmod1_list1_proto" << "g2"); + const auto &g211Gmod1List2 = moduleProperty(g211Props, "gmod.gmod1", "gmod1_list2") + .toStringList(); + QCOMPARE(g211Gmod1List2, QStringList() << "g2.1.1"); + const int g211P0 = moduleProperty(g211Props, "gmod.gmod1", "p0").toInt(); + QCOMPARE(g211P0, 17); + const int g211DepProp = moduleProperty(g211Props, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(g211DepProp, 2); + const auto &g211Gmod2String + = moduleProperty(g211Props, "gmod2", "gmod2_string").toString(); + QEXPECT_FAIL(0, "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); + QCOMPARE(g211Gmod2List, QStringList() << "g2.1.1" << "commonName_in_gmod1" + << "g2.1.1_gmod4_g2.1.1_gmod3" << "g2.1.1_gmod3" << "gmod2_list_proto"); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::modulePropertyOverridesPerProduct() +{ + bool exceptionCaught = false; + try { + SetupProjectParameters params = defaultParameters; + params.setOverriddenValues({ + std::make_pair("modules.dummy.someString", "m"), + std::make_pair("products.b.dummy.someString", "b"), + std::make_pair("products.c.dummy.someString", "c") + }); + params.setProjectFilePath( + testProject("module-property-overrides-per-product.qbs")); + const TopLevelProjectPtr project = loader->loadProject(params); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 3); + const ResolvedProductConstPtr a = products.value("a"); + QVERIFY(!!a); + const ResolvedProductConstPtr b = products.value("b"); + QVERIFY(!!b); + const ResolvedProductConstPtr c = products.value("c"); + QVERIFY(!!c); + + const auto propertyValue = [](const ResolvedProductConstPtr &p) -> QString + { + return p->moduleProperties->moduleProperty("dummy", "someString").toString(); + }; + + QCOMPARE(propertyValue(a), QString("m")); + QCOMPARE(propertyValue(b), QString("b")); + QCOMPARE(propertyValue(c), QString("c")); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::moduleScope() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("modulescope.qbs")); + TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 1); + ResolvedProductPtr product = products.value("product1"); + QVERIFY(!!product); + + auto intModuleValue = [product] (const QString &name) -> int + { + return product->moduleProperties->moduleProperty("scopemod", name).toInt(); + }; + + QCOMPARE(intModuleValue("a"), 2); // overridden in module instance + QCOMPARE(intModuleValue("b"), 1); // genuine + QCOMPARE(intModuleValue("c"), 3); // genuine, dependent on overridden value + QCOMPARE(intModuleValue("d"), 2); // genuine, dependent on genuine value + QCOMPARE(intModuleValue("e"), 1); // genuine + 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) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); + +} + +void TestLanguage::modules_data() +{ + QTest::addColumn<QStringList>("expectedModulesInProduct"); + QTest::addColumn<QString>("expectedProductProperty"); + QTest::newRow("init") << QStringList(); + QTest::newRow("no_modules") + << (QStringList() << "qbs") + << QString(); + QTest::newRow("qt_core") + << (QStringList() << "qbs" << "dummy" << "dummyqt.core") + << QString("1.2.3"); + QTest::newRow("qt_gui") + << (QStringList() << "qbs" << "dummy" << "dummyqt.core" << "dummyqt.gui") + << QString("guiProperty"); + QTest::newRow("qt_gui_network") + << (QStringList() << "qbs" << "dummy" << "dummyqt.core" << "dummyqt.gui" + << "dummyqt.network") + << QString("guiProperty,networkProperty"); + QTest::newRow("deep_module_name") + << (QStringList() << "qbs" << "deepdummy.deep.moat" << "dummy") + << QString("abysmal"); + QTest::newRow("deep_module_name_submodule_syntax1") + << (QStringList() << "qbs" << "deepdummy.deep.moat" << "dummy") + << QString("abysmal"); + QTest::newRow("deep_module_name_submodule_syntax2") + << (QStringList() << "qbs" << "deepdummy.deep.moat" << "dummy") + << QString("abysmal"); + QTest::newRow("dummy_twice") + << (QStringList() << "qbs" << "dummy") + << QString(); + QTest::newRow("cleanup") << QStringList(); +} + +void TestLanguage::modules() +{ + HANDLE_INIT_CLEANUP_DATATAGS("modules.qbs"); + QFETCH(QStringList, expectedModulesInProduct); + QFETCH(QString, expectedProductProperty); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + const QString productName = QString::fromLocal8Bit(QTest::currentDataTag()); + ResolvedProductPtr product = products.value(productName); + QVERIFY(!!product); + QCOMPARE(product->name, productName); + QStringList modulesInProduct; + foreach (ResolvedModuleConstPtr m, product->modules) + modulesInProduct += m->name; + modulesInProduct.sort(); + expectedModulesInProduct.sort(); + QCOMPARE(modulesInProduct, expectedModulesInProduct); + QCOMPARE(product->productProperties.value("foo").toString(), expectedProductProperty); +} + +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; + if (!subProjectEnabled) + overriddenValues.insert("projects.subproject.condition", false); + else if (!dependeeEnabled) + overriddenValues.insert("products.dependee.condition", false); + params.setOverriddenValues(overriddenValues); + const TopLevelProjectPtr project = loader->loadProject(params); + QVERIFY(!!project); + const auto products = productsFromProject(project); + QCOMPARE(products.count(), 4 + !!subProjectEnabled); + const ResolvedProductConstPtr dependee = products.value("dependee"); + QCOMPARE(subProjectEnabled, !!dependee); + if (dependee) + QCOMPARE(dependeeEnabled, dependee->enabled); + const ResolvedProductConstPtr depender = products.value("depender"); + QVERIFY(!!depender); + const QStringList defines = depender->moduleProperties->moduleProperty("dummy", "defines") + .toStringList(); + QCOMPARE(subProjectEnabled && dependeeEnabled, defines.contains("WITH_DEPENDEE")); + + for (const auto &name : std::vector<const char *>({ "p3", "p2", "p1"})) { + const ResolvedProductConstPtr &product = products.value(name); + QVERIFY2(product, name); + QVERIFY2(!product->enabled, name); + } + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::nonRequiredProducts_data() +{ + QTest::addColumn<bool>("subProjectEnabled"); + QTest::addColumn<bool>("dependeeEnabled"); + QTest::newRow("dependee enabled") << true << true; + QTest::newRow("dependee disabled") << true << false; + QTest::newRow("sub project disabled") << false << true; +} + +void TestLanguage::outerInGroup() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("outerInGroup.qbs")); + TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 1); + ResolvedProductPtr product = products.value("OuterInGroup"); + QVERIFY(!!product); + QCOMPARE(product->groups.count(), 2); + GroupPtr group = product->groups.at(0); + QVERIFY(!!group); + QCOMPARE(group->name, product->name); + QCOMPARE(group->files.count(), 1); + SourceArtifactConstPtr artifact = group->files.first(); + QVariant installDir = artifact->properties->qbsPropertyValue("installDir"); + QCOMPARE(installDir.toString(), QString("/somewhere")); + group = product->groups.at(1); + QVERIFY(!!group); + QCOMPARE(group->name, QString("Special Group")); + QCOMPARE(group->files.count(), 1); + artifact = group->files.first(); + installDir = artifact->properties->qbsPropertyValue("installDir"); + QCOMPARE(installDir.toString(), QString("/somewhere/else")); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::parameterTypes() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("parameter-types.qbs")); + loader->loadProject(defaultParameters); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::pathProperties() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("pathproperties.qbs")); + project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + ResolvedProductPtr product = products.value("product1"); + QVERIFY(!!product); + QString projectFileDir = QFileInfo(defaultParameters.projectFilePath()).absolutePath(); + const QVariantMap productProps = product->productProperties; + QCOMPARE(productProps.value("projectFileDir").toString(), projectFileDir); + QStringList filesInProjectFileDir = QStringList() + << FileInfo::resolvePath(projectFileDir, "aboutdialog.h") + << FileInfo::resolvePath(projectFileDir, "aboutdialog.cpp"); + QCOMPARE(productProps.value("filesInProjectFileDir").toStringList(), filesInProjectFileDir); + QStringList includePaths = product->moduleProperties->property( + QStringList() << "modules" << "dummy" << "includePaths").toStringList(); + QCOMPARE(includePaths, QStringList() << projectFileDir); + QCOMPARE(productProps.value("base_fileInProductDir").toString(), + FileInfo::resolvePath(projectFileDir, QLatin1String("foo"))); + QCOMPARE(productProps.value("base_fileInBaseProductDir").toString(), + FileInfo::resolvePath(projectFileDir, QLatin1String("subdir/bar"))); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::profileValuesAndOverriddenValues() +{ + bool exceptionCaught = false; + try { + TemporaryProfile tp(QLatin1String("tst_lang_profile"), m_settings); + Profile profile = tp.p; + profile.setValue("dummy.defines", "IN_PROFILE"); + profile.setValue("dummy.cFlags", "IN_PROFILE"); + profile.setValue("dummy.cxxFlags", "IN_PROFILE"); + profile.setValue("qbs.architecture", "x86"); + SetupProjectParameters parameters = defaultParameters; + parameters.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); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + ResolvedProductPtr product = products.value("product1"); + QVERIFY(!!product); + QVariantList values; + values = product->moduleProperties->moduleProperty("dummy", "cxxFlags").toList(); + QCOMPARE(values.length(), 1); + QCOMPARE(values.first().toString(), QString("IN_PROFILE")); + values = product->moduleProperties->moduleProperty("dummy", "defines").toList(); + QCOMPARE(values, QVariantList() << QLatin1String("IN_FILE") << QLatin1String("IN_PROFILE")); + values = product->moduleProperties->moduleProperty("dummy", "cFlags").toList(); + QCOMPARE(values.length(), 1); + QCOMPARE(values.first().toString(), QString("OVERRIDDEN")); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::productConditions() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("productconditions.qbs")); + TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 6); + ResolvedProductPtr product; + product = products.value("product_no_condition"); + QVERIFY(!!product); + QVERIFY(product->enabled); + + product = products.value("product_true_condition"); + QVERIFY(!!product); + QVERIFY(product->enabled); + + product = products.value("product_condition_dependent_of_module"); + QVERIFY(!!product); + QVERIFY(product->enabled); + + product = products.value("product_false_condition"); + QVERIFY(!!product); + QVERIFY(!product->enabled); + + product = products.value("product_probe_condition_false"); + QVERIFY(!!product); + QVERIFY(!product->enabled); + + product = products.value("product_probe_condition_true"); + QVERIFY(!!product); + QVERIFY(product->enabled); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::productDirectories() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("productdirectories.qbs")); + ResolvedProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 1); + ResolvedProductPtr product; + product = products.value("MyApp"); + QVERIFY(!!product); + const QVariantMap config = product->productProperties; + QCOMPARE(config.value(QLatin1String("buildDirectory")).toString(), + product->buildDirectory()); + QCOMPARE(config.value(QLatin1String("sourceDirectory")).toString(), testDataDir()); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::propertiesBlocks_data() +{ + QTest::addColumn<QString>("propertyName"); + QTest::addColumn<QStringList>("expectedValues"); + QTest::addColumn<QString>("expectedStringValue"); + + QTest::newRow("init") << QString() << QStringList() << QString(); + QTest::newRow("property_overwrite") + << QString("dummy.defines") + << QStringList("OVERWRITTEN") + << QString(); + QTest::newRow("property_set_indirect") + << QString("dummy.cFlags") + << QStringList("VAL") + << QString(); + QTest::newRow("property_overwrite_no_outer") + << QString("dummy.defines") + << QStringList("OVERWRITTEN") + << QString(); + QTest::newRow("property_append_to_outer") + << QString("dummy.defines") + << (QStringList() << QString("ONE") << QString("TWO")) + << QString(); + + QTest::newRow("multiple_exclusive_properties") + << QString("dummy.defines") + << QStringList("OVERWRITTEN") + << QString(); + QTest::newRow("multiple_exclusive_properties_no_outer") + << QString("dummy.defines") + << QStringList("OVERWRITTEN") + << QString(); + QTest::newRow("multiple_exclusive_properties_append_to_outer") + << QString("dummy.defines") + << (QStringList() << QString("ONE") << QString("TWO")) + << QString(); + + QTest::newRow("condition_refers_to_product_property") + << QString("dummy.defines") + << QStringList("OVERWRITTEN") + << QString("OVERWRITTEN"); + QTest::newRow("condition_refers_to_project_property") + << QString("dummy.defines") + << QStringList("OVERWRITTEN") + << QString("OVERWRITTEN"); + + QTest::newRow("ambiguous_properties") + << QString("dummy.defines") + << (QStringList() << QString("ONE") << QString("TWO")) + << QString(); + QTest::newRow("inheritance_overwrite_in_subitem") + << QString("dummy.defines") + << (QStringList() << QString("OVERWRITTEN_IN_SUBITEM")) + << QString(); + QTest::newRow("inheritance_retain_base1") + << QString("dummy.defines") + << (QStringList() << QString("BASE") << QString("SUB")) + << QString(); + QTest::newRow("inheritance_retain_base2") + << QString("dummy.defines") + << (QStringList() << QString("BASE") << QString("SUB")) + << QString(); + QTest::newRow("inheritance_retain_base3") + << QString("dummy.defines") + << (QStringList() << QString("BASE") << QString("SUB")) + << QString(); + QTest::newRow("inheritance_retain_base4") + << QString("dummy.defines") + << (QStringList() << QString("BASE")) + << QString(); + QTest::newRow("inheritance_condition_in_subitem1") + << QString("dummy.defines") + << (QStringList() << QString("SOMETHING") << QString("SUB")) + << QString(); + QTest::newRow("inheritance_condition_in_subitem2") + << QString("dummy.defines") + << (QStringList() << QString("SOMETHING")) + << QString(); + QTest::newRow("condition_references_id") + << QString("dummy.defines") + << (QStringList() << QString("OVERWRITTEN")) + << QString(); + QTest::newRow("using_derived_Properties_item") << "dummy.defines" + << (QStringList() << "string from MyProperties") << QString(); + QTest::newRow("conditional-depends") + << QString("dummy.defines") + << QStringList() + << QString(); + QTest::newRow("cleanup") << QString() << QStringList() << QString(); +} + +void TestLanguage::propertiesBlocks() +{ + HANDLE_INIT_CLEANUP_DATATAGS("propertiesblocks.qbs"); + QFETCH(QString, propertyName); + QFETCH(QStringList, expectedValues); + QFETCH(QString, expectedStringValue); + QVERIFY(!!project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + const QString productName = QString::fromLocal8Bit(QTest::currentDataTag()); + ResolvedProductPtr product = products.value(productName); + QVERIFY(!!product); + QCOMPARE(product->name, productName); + QVariant v = productPropertyValue(product, propertyName); + QCOMPARE(v.toStringList(), expectedValues); + if (!expectedStringValue.isEmpty()) { + v = productPropertyValue(product, "someString"); + QCOMPARE(v.toString(), expectedStringValue); + } +} + +void TestLanguage::propertiesBlockInGroup() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("properties-block-in-group.qbs")); + const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + QCOMPARE(project->allProducts().count(), 1); + const ResolvedProductConstPtr product = project->allProducts().first(); + const auto groupIt = std::find_if(product->groups.constBegin(), product->groups.constEnd(), + [](const GroupConstPtr &g) { return g->name == "the group"; }); + QVERIFY(groupIt != product->groups.constEnd()); + const QVariantMap propertyMap = (*groupIt)->properties->value(); + const QVariantList value = moduleProperty(propertyMap, "dummy", "defines").toList(); + QStringList stringListValue; + std::transform(value.constBegin(), value.constEnd(), std::back_inserter(stringListValue), + [](const QVariant &v) { return v.toString(); }); + QCOMPARE(stringListValue, QStringList() << "BASEDEF" << "FEATURE_ENABLED"); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::propertiesItemInModule() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath( + testProject("properties-item-in-module.qbs")); + const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 2); + for (const ResolvedProductConstPtr &p : products) { + QCOMPARE(p->moduleProperties->moduleProperty("dummy", "productName").toString(), + p->name); + } + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::qbsPropertiesInProjectCondition() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath( + testProject("qbs-properties-in-project-condition.qbs")); + const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 0); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +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); + QVERIFY(!strictMode); + const auto productMap = productsFromProject(project); + const ResolvedProductConstPtr brokenProduct = productMap.value("broken"); + QVERIFY(!brokenProduct->enabled); + QVERIFY(brokenProduct->location.isValid()); + QCOMPARE(brokenProduct->allFiles().count(), 0); + const ResolvedProductConstPtr dependerRequired = productMap.value("depender required"); + QVERIFY(!dependerRequired->enabled); + QVERIFY(dependerRequired->location.isValid()); + QCOMPARE(dependerRequired->allFiles().count(), 1); + const ResolvedProductConstPtr dependerNonRequired + = productMap.value("depender nonrequired"); + QVERIFY(dependerNonRequired->enabled); + QCOMPARE(dependerNonRequired->allFiles().count(), 1); + const ResolvedProductConstPtr recursiveDepender = productMap.value("recursive depender"); + QVERIFY(!recursiveDepender->enabled); + QVERIFY(recursiveDepender->location.isValid()); + QCOMPARE(recursiveDepender->allFiles().count(), 1); + const ResolvedProductConstPtr missingFile = productMap.value("missing file"); + QVERIFY(missingFile->enabled); + QCOMPARE(missingFile->groups.count(), 1); + QVERIFY(missingFile->groups.first()->enabled); + QCOMPARE(missingFile->groups.first()->allFiles().count(), 2); + const ResolvedProductConstPtr fine = productMap.value("fine"); + QVERIFY(fine->enabled); + QCOMPARE(fine->allFiles().count(), 1); + } catch (const ErrorInfo &e) { + QVERIFY2(strictMode, qPrintable(e.toString())); + } +} + +void TestLanguage::relaxedErrorMode_data() +{ + QTest::addColumn<bool>("strictMode"); + + QTest::newRow("strict mode") << true; + QTest::newRow("relaxed mode") << false; +} + +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); + QVERIFY(!!project); + QVERIFY(!exceptionExpected); + } catch (const ErrorInfo &e) { + QVERIFY(exceptionExpected); + QVERIFY2(e.toString().contains("validation error!"), qPrintable(e.toString())); + } +} + +void TestLanguage::requiredAndNonRequiredDependencies_data() +{ + QTest::addColumn<QString>("projectFile"); + QTest::addColumn<bool>("exceptionExpected"); + + QTest::newRow("same file") << "direct-dependencies.qbs" << true; + QTest::newRow("dependency via module") << "dependency-via-module.qbs" << true; + QTest::newRow("dependency via export") << "dependency-via-export.qbs" << true; + QTest::newRow("more indirection") << "complicated.qbs" << true; + QTest::newRow("required chain (module)") << "required-chain-module.qbs" << false; + QTest::newRow("required chain (export)") << "required-chain-export.qbs" << false; + QTest::newRow("required chain (export indirect)") << "required-chain-export-indirect.qbs" + << false; +} + +void TestLanguage::throwingProbe() +{ + QFETCH(bool, enableProbe); + try { + SetupProjectParameters params = defaultParameters; + params.setProjectFilePath(testProject("throwing-probe.qbs")); + QVariantMap properties; + properties.insert(QLatin1String("products.theProduct.enableProbe"), enableProbe); + params.setOverriddenValues(properties); + const TopLevelProjectPtr project = loader->loadProject(params); + QVERIFY(!!project); + QVERIFY(!enableProbe); + } catch (const ErrorInfo &e) { + QVERIFY2(enableProbe, qPrintable(e.toString())); + } +} + +void TestLanguage::throwingProbe_data() +{ + QTest::addColumn<bool>("enableProbe"); + + QTest::newRow("enabled probe") << true; + QTest::newRow("disabled probe") << false; +} + +void TestLanguage::qualifiedId() +{ + QString str = "foo.bar.baz"; + QualifiedId id = QualifiedId::fromString(str); + QCOMPARE(id.count(), 3); + QCOMPARE(id.toString(), str); + + id = QualifiedId("blubb.di.blubb"); // c'tor does not split + QCOMPARE(id.count(), 1); + + QList<QualifiedId> ids; + ids << QualifiedId::fromString("a") + << QualifiedId::fromString("a.a") + << QualifiedId::fromString("b") + << QualifiedId::fromString("c.a") + << QualifiedId::fromString("c.b.a") + << QualifiedId::fromString("c.c"); + QList<QualifiedId> sorted = ids; + std::sort(sorted.begin(), sorted.end()); + QCOMPARE(ids, sorted); +} + +void TestLanguage::recursiveProductDependencies() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath( + testProject("recursive-dependencies/recursive-dependencies.qbs")); + const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + QCOMPARE(products.count(), 4); + const ResolvedProductConstPtr p1 = products.value("p1"); + QVERIFY(!!p1); + const ResolvedProductConstPtr p2 = products.value("p2"); + QVERIFY(!!p2); + const ResolvedProductPtr p3 = products.value("p3"); + QVERIFY(!!p3); + const ResolvedProductPtr p4 = products.value("p4"); + QVERIFY(!!p4); + QVERIFY(p1->dependencies == Set<ResolvedProductPtr>() << p3 << p4); + QVERIFY(p2->dependencies == Set<ResolvedProductPtr>() << p3 << p4); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::fileTags_data() +{ + QTest::addColumn<int>("numberOfGroups"); + QTest::addColumn<QStringList>("expectedFileTags"); + + QTest::newRow("init") << 0 << QStringList(); + QTest::newRow("filetagger_project_scope") << 1 << (QStringList() << "cpp"); + QTest::newRow("filetagger_product_scope") << 1 << (QStringList() << "asm"); + QTest::newRow("filetagger_static_pattern") << 1 << (QStringList() << "yellow"); + QTest::newRow("unknown_file_tag") << 1 << (QStringList() << "unknown-file-tag"); + QTest::newRow("set_file_tag_via_group") << 2 << (QStringList() << "c++"); + QTest::newRow("override_file_tag_via_group") << 2 << (QStringList() << "c++"); + QTest::newRow("add_file_tag_via_group") << 2 << (QStringList() << "cpp" << "zzz"); + QTest::newRow("cleanup") << 0 << QStringList(); +} + +void TestLanguage::fileTags() +{ + HANDLE_INIT_CLEANUP_DATATAGS("filetags.qbs"); + QFETCH(int, numberOfGroups); + QFETCH(QStringList, expectedFileTags); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + ResolvedProductPtr product; + const QString productName = QString::fromLocal8Bit(QTest::currentDataTag()); + QVERIFY(!!(product = products.value(productName))); + QCOMPARE(product->groups.count(), numberOfGroups); + GroupPtr group = product->groups.last(); + QVERIFY(!!group); + QCOMPARE(group->files.count(), 1); + SourceArtifactConstPtr sourceFile = group->files.first(); + QStringList fileTags = sourceFile->fileTags.toStringList(); + fileTags.sort(); + QCOMPARE(fileTags, expectedFileTags); +} + +void TestLanguage::wildcards_data() +{ + QTest::addColumn<bool>("useGroup"); + QTest::addColumn<QStringList>("filesToCreate"); + QTest::addColumn<QString>("projectFileSubDir"); + QTest::addColumn<QString>("prefix"); + QTest::addColumn<QStringList>("patterns"); + QTest::addColumn<QStringList>("excludePatterns"); + QTest::addColumn<QStringList>("expected"); + + const bool useGroup = true; + for (int i = 0; i <= 1; ++i) { + const bool useGroup = i; + const QByteArray dataTagSuffix = useGroup ? " group" : " nogroup"; + QTest::newRow(QByteArray("simple 1") + dataTagSuffix) + << useGroup + << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp") + << QString() + << QString() + << (QStringList() << "*.h") + << QStringList() + << (QStringList() << "foo.h" << "bar.h"); + QTest::newRow(QByteArray("simple 2") + dataTagSuffix) + << useGroup + << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp") + << QString() + << QString() + << (QStringList() << "foo.*") + << QStringList() + << (QStringList() << "foo.h" << "foo.cpp"); + QTest::newRow(QByteArray("simple 3") + dataTagSuffix) + << useGroup + << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp") + << QString() + << QString() + << (QStringList() << "*.h" << "*.cpp") + << QStringList() + << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp"); + QTest::newRow(QByteArray("exclude 1") + dataTagSuffix) + << useGroup + << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp") + << QString() + << QString() + << (QStringList() << "*.h" << "*.cpp") + << (QStringList() << "bar*") + << (QStringList() << "foo.h" << "foo.cpp"); + QTest::newRow(QByteArray("exclude 2") + dataTagSuffix) + << useGroup + << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp") + << QString() + << QString() + << (QStringList() << "*") + << (QStringList() << "*.qbs") + << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp"); + QTest::newRow(QByteArray("non-recursive") + dataTagSuffix) + << useGroup + << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp") + << QString() + << QString() + << (QStringList() << "a/*") + << QStringList() + << (QStringList() << "a/foo.h" << "a/foo.cpp"); + QTest::newRow(QByteArray("absolute paths") + dataTagSuffix) + << useGroup + << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp") + << QString() + << QString() + << (QStringList() << m_wildcardsTestDirPath + "/?oo.*") + << QStringList() + << (QStringList() << "foo.h" << "foo.cpp"); + QTest::newRow(QByteArray("relative paths with dotdot") + dataTagSuffix) + << useGroup + << (QStringList() << "bar.h" << "bar.cpp") + << QString("TheLaughingLlama") + << QString() + << (QStringList() << "../bar.*") + << QStringList() + << (QStringList() << "bar.h" << "bar.cpp"); + } + QTest::newRow(QByteArray("recursive 1")) + << useGroup + << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp") + << QString() + << QString() + << (QStringList() << "a/**") + << QStringList() + << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp"); + QTest::newRow(QByteArray("recursive 2")) + << useGroup + << (QStringList() + << "d/1.h" << "b/d/1.h" << "b/c/d/1.h" + << "d/e/1.h" << "b/d/e/1.h" << "b/c/d/e/1.h" + << "a/d/1.h" << "a/b/d/1.h" << "a/b/c/d/1.h" + << "a/d/e/1.h" << "a/b/d/e/1.h" << "a/b/c/d/e/1.h" + << "a/d/1.cpp" << "a/b/d/1.cpp" << "a/b/c/d/1.h" + << "a/d/e/1.cpp" << "a/b/d/e/1.cpp" << "a/b/c/d/e/1.cpp") + << QString() + << QString() + << (QStringList() << "a/**/d/*.h") + << QStringList() + << (QStringList() << "a/d/1.h" << "a/b/d/1.h" << "a/b/c/d/1.h"); + QTest::newRow(QByteArray("recursive 3")) + << useGroup + << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp") + << QString() + << QString() + << (QStringList() << "a/**/**/**") + << QStringList() + << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp"); + QTest::newRow(QByteArray("prefix")) + << useGroup + << (QStringList() << "subdir/foo.h" << "subdir/foo.cpp" << "subdir/bar.h" + << "subdir/bar.cpp") + << QString() + << QString("subdir/") + << (QStringList() << "*.h") + << QStringList() + << (QStringList() << "subdir/foo.h" << "subdir/bar.h"); + QTest::newRow(QByteArray("non-existing absolute path")) + << useGroup + << QStringList() + << QString() + << QString("/dir") + << (QStringList() << "*.whatever") + << QStringList() + << QStringList(); +} + +void TestLanguage::wildcards() +{ + QFETCH(bool, useGroup); + QFETCH(QStringList, filesToCreate); + QFETCH(QString, projectFileSubDir); + QFETCH(QString, prefix); + QFETCH(QStringList, patterns); + QFETCH(QStringList, excludePatterns); + QFETCH(QStringList, expected); + + // create test directory + QDir::setCurrent(QDir::tempPath()); + { + QString errorMessage; + if (QFile::exists(m_wildcardsTestDirPath)) { + if (!removeDirectoryWithContents(m_wildcardsTestDirPath, &errorMessage)) { + qDebug() << errorMessage; + QVERIFY2(false, "removeDirectoryWithContents failed"); + } + } + QVERIFY(QDir().mkdir(m_wildcardsTestDirPath)); + } + + // create project file + const QString groupName = "Keks"; + QString dataTag = QString::fromLocal8Bit(QTest::currentDataTag()); + dataTag.replace(' ', '_'); + if (!projectFileSubDir.isEmpty()) { + if (!projectFileSubDir.startsWith('/')) + projectFileSubDir.prepend('/'); + if (projectFileSubDir.endsWith('/')) + projectFileSubDir.chop(1); + QVERIFY(QDir().mkpath(m_wildcardsTestDirPath + projectFileSubDir)); + } + const QString projectFilePath = m_wildcardsTestDirPath + projectFileSubDir + "/test_" + dataTag + + ".qbs"; + { + QFile projectFile(projectFilePath); + QVERIFY(projectFile.open(QIODevice::WriteOnly)); + QTextStream s(&projectFile); + s << "import qbs.base 1.0" << endl << endl + << "Application {" << endl + << " name: \"MyProduct\"" << endl; + if (useGroup) { + s << " Group {" << endl + << " name: " << toJSLiteral(groupName) << endl; + } + if (!prefix.isEmpty()) + s << " prefix: " << toJSLiteral(prefix) << endl; + if (!patterns.isEmpty()) + s << " files: " << toJSLiteral(patterns) << endl; + if (!excludePatterns.isEmpty()) + s << " excludeFiles: " << toJSLiteral(excludePatterns) << endl; + if (useGroup) + s << " }" << endl; + s << "}" << endl << endl; + } + + // create files + foreach (QString filePath, filesToCreate) { + filePath.prepend(m_wildcardsTestDirPath + '/'); + QFileInfo fi(filePath); + if (!QDir(fi.path()).exists()) + QVERIFY(QDir().mkpath(fi.path())); + QFile file(filePath); + QVERIFY(file.open(QIODevice::WriteOnly)); + } + + // read the project + bool exceptionCaught = false; + ResolvedProductPtr product; + try { + defaultParameters.setProjectFilePath(projectFilePath); + project = loader->loadProject(defaultParameters); + QVERIFY(!!project); + const QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + product = products.value("MyProduct"); + QVERIFY(!!product); + GroupPtr group; + if (useGroup) { + QCOMPARE(product->groups.count(), HostOsInfo::isMacosHost() ? 3 : 2); + foreach (const GroupPtr &rg, product->groups) { + if (rg->name == groupName) { + group = rg; + break; + } + } + } else { + QCOMPARE(product->groups.count(), HostOsInfo::isMacosHost() ? 2 : 1); + group = product->groups.first(); + } + QVERIFY(!!group); + QCOMPARE(group->files.count(), 0); + SourceWildCards::Ptr wildcards = group->wildcards; + QVERIFY(!!wildcards); + QStringList actualFilePaths; + foreach (const SourceArtifactConstPtr &artifact, wildcards->files) { + QString str = artifact->absoluteFilePath; + int idx = str.indexOf(m_wildcardsTestDirPath); + if (idx != -1) + str.remove(0, idx + m_wildcardsTestDirPath.count() + 1); + actualFilePaths << str; + } + actualFilePaths.sort(); + expected.sort(); + QCOMPARE(actualFilePaths, expected); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); const SettingsPtr s = settings(); - qbs::Internal::TestLanguage tl(ConsoleLogger::instance().logSink(), s.get()); + TestLanguage tl(ConsoleLogger::instance().logSink(), s.get()); return QTest::qExec(&tl, argc, argv); } diff --git a/src/lib/corelib/language/tst_language.h b/tests/auto/language/tst_language.h index e77c1f360..9d63414a5 100644 --- a/src/lib/corelib/language/tst_language.h +++ b/tests/auto/language/tst_language.h @@ -44,32 +44,31 @@ #include <language/loader.h> #include <logging/ilogsink.h> #include <tools/setupprojectparameters.h> -#include <tools/qbs_export.h> -#include <QtTest/qtest.h> -namespace qbs { -namespace Internal { +#include <QtTest/qtest.h> -class QBS_EXPORT TestLanguage : public QObject +class TestLanguage : public QObject { Q_OBJECT public: - TestLanguage(ILogSink *logSink, Settings *settings); + TestLanguage(qbs::ILogSink *logSink, qbs::Settings *settings); ~TestLanguage(); private: - ILogSink *m_logSink; - Settings * const m_settings; - Logger m_logger; - ScriptEngine *m_engine; - Loader *loader; - TopLevelProjectPtr project; - SetupProjectParameters defaultParameters; + qbs::ILogSink *m_logSink; + qbs::Settings * const m_settings; + qbs::Internal::Logger m_logger; + qbs::Internal::ScriptEngine *m_engine; + qbs::Internal::Loader *loader; + qbs::Internal::TopLevelProjectPtr project; + qbs::SetupProjectParameters defaultParameters; const QString m_wildcardsTestDirPath; - QHash<QString, ResolvedProductPtr> productsFromProject(ResolvedProjectPtr project); - ResolvedModuleConstPtr findModuleByName(ResolvedProductPtr product, const QString &name); - QVariant productPropertyValue(ResolvedProductPtr product, QString propertyName); + QHash<QString, qbs::Internal::ResolvedProductPtr> productsFromProject( + qbs::Internal::ResolvedProjectPtr project); + qbs::Internal::ResolvedModuleConstPtr findModuleByName( + qbs::Internal::ResolvedProductPtr product, const QString &name); + QVariant productPropertyValue(qbs::Internal::ResolvedProductPtr product, QString propertyName); void handleInitCleanupDataTags(const char *projectFileName, bool *handled); private slots: @@ -148,7 +147,5 @@ private slots: void wildcards(); }; -} // namespace Internal -} // namespace qbs - #endif // TST_LANGUAGE_H + diff --git a/tests/auto/tools/tools.pro b/tests/auto/tools/tools.pro index 0ee10216e..ba293f417 100644 --- a/tests/auto/tools/tools.pro +++ b/tests/auto/tools/tools.pro @@ -1,5 +1,6 @@ TARGET = tst_tools SOURCES = tst_tools.cpp ../../../src/app/qbs/qbstool.cpp +HEADERS = tst_tools.h include(../auto.pri) diff --git a/tests/auto/tools/tools.qbs b/tests/auto/tools/tools.qbs index 612a70a40..c626c7704 100644 --- a/tests/auto/tools/tools.qbs +++ b/tests/auto/tools/tools.qbs @@ -1,7 +1,15 @@ import qbs QbsAutotest { + Depends { name: "qbsversion" } + testName: "tools" condition: qbsbuildconfig.enableUnitTests - files: ["tst_tools.cpp"] + files: [ + "tst_tools.cpp", + "tst_tools.h" + ] + + // TODO: Use Utilities.cStringQuote + cpp.defines: ['QBS_VERSION="' + qbsversion.version + '"'] } diff --git a/tests/auto/tools/tst_tools.cpp b/tests/auto/tools/tst_tools.cpp index 88337787e..13bc18b36 100644 --- a/tests/auto/tools/tst_tools.cpp +++ b/tests/auto/tools/tst_tools.cpp @@ -5,7 +5,7 @@ ** ** This file is part of Qbs. ** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -14,30 +14,944 @@ ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ +#undef QT_NO_CAST_FROM_ASCII // I am qmake, and I approve this hack. + +#include "tst_tools.h" #include "../shared.h" -#include <tools/tst_tools.h> -#include <QtCore/qcoreapplication.h> +#include <tools/buildoptions.h> +#include <tools/error.h> +#include <tools/fileinfo.h> +#include <tools/hostosinfo.h> +#include <tools/processutils.h> +#include <tools/profile.h> +#include <tools/set.h> +#include <tools/settings.h> +#include <tools/setupprojectparameters.h> +#include <tools/version.h> + +#include <QtCore/qdir.h> +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qsettings.h> +#include <QtCore/qtemporarydir.h> +#include <QtCore/qtemporaryfile.h> #include <QtTest/qtest.h> +using namespace qbs; +using namespace qbs::Internal; + +TestTools::TestTools(Settings *settings) : m_settings(settings) +{ +} + +TestTools::~TestTools() +{ + qDeleteAll(m_tmpDirs); +} + +void TestTools::testFileInfo() +{ + QCOMPARE(FileInfo::fileName("C:/waffl/copter.exe"), QString("copter.exe")); + QCOMPARE(FileInfo::baseName("C:/waffl/copter.exe.lib"), QString("copter")); + QCOMPARE(FileInfo::completeBaseName("C:/waffl/copter.exe.lib"), QString("copter.exe")); + QCOMPARE(FileInfo::path("abc"), QString(".")); + QCOMPARE(FileInfo::path("/abc/lol"), QString("/abc")); + QCOMPARE(FileInfo::path("/fileInRoot"), QString(QLatin1Char('/'))); + if (HostOsInfo::isWindowsHost()) + QCOMPARE(FileInfo::path("C:/fileInDriveRoot"), QString("C:/")); + QVERIFY(!FileInfo::isAbsolute("bla/lol")); + QVERIFY(FileInfo::isAbsolute("/bla/lol")); + if (HostOsInfo::isWindowsHost()) + QVERIFY(FileInfo::isAbsolute("C:\\bla\\lol")); + QCOMPARE(FileInfo::resolvePath("/abc/lol", "waffl"), QString("/abc/lol/waffl")); + QCOMPARE(FileInfo::resolvePath("/abc/def/ghi/jkl/", "../foo/bar"), QString("/abc/def/ghi/foo/bar")); + QCOMPARE(FileInfo::resolvePath("/abc/def/ghi/jkl/", "../../foo/bar"), QString("/abc/def/foo/bar")); + QCOMPARE(FileInfo::resolvePath("/abc", "../../../foo/bar"), QString("/foo/bar")); + QCOMPARE(FileInfo("/does/not/exist").lastModified(), FileTime()); +} + +void TestTools::fileCaseCheck() +{ + QTemporaryFile tempFile(QDir::tempPath() + QLatin1String("/CamelCase")); + QVERIFY2(tempFile.open(), qPrintable(tempFile.errorString())); + QFileInfo tempFileInfo(tempFile.fileName()); + const QString lowerFilePath = tempFileInfo.absolutePath() + QLatin1Char('/') + + tempFileInfo.fileName().toLower(); + const QString upperFilePath = tempFileInfo.absolutePath() + QLatin1Char('/') + + tempFileInfo.fileName().toUpper(); + QVERIFY(FileInfo::isFileCaseCorrect(tempFileInfo.absoluteFilePath())); + if (QFile::exists(lowerFilePath)) + QVERIFY(!FileInfo::isFileCaseCorrect(lowerFilePath)); + if (QFile::exists(upperFilePath)) + QVERIFY(!FileInfo::isFileCaseCorrect(upperFilePath)); +} + +void TestTools::testProfiles() +{ + TemporaryProfile tpp("parent", m_settings); + Profile parentProfile = tpp.p; + TemporaryProfile tpc("child", m_settings); + Profile childProfile = tpc.p; + parentProfile.removeBaseProfile(); + parentProfile.remove("testKey"); + QCOMPARE(parentProfile.value("testKey", "none").toString(), QLatin1String("none")); + parentProfile.setValue("testKey", "testValue"); + QCOMPARE(parentProfile.value("testKey").toString(), QLatin1String("testValue")); + + childProfile.remove("testKey"); + childProfile.removeBaseProfile(); + QCOMPARE(childProfile.value("testKey", "none").toString(), QLatin1String("none")); + childProfile.setBaseProfile("parent"); + QCOMPARE(childProfile.value("testKey").toString(), QLatin1String("testValue")); + + // Change base profile and check if the inherited value also changes. + TemporaryProfile tpf("foo", m_settings); + Profile fooProfile = tpf.p; + fooProfile.setValue("testKey", "gnampf"); + childProfile.setBaseProfile("foo"); + QCOMPARE(childProfile.value("testKey", "none").toString(), QLatin1String("gnampf")); + + ErrorInfo errorInfo; + childProfile.setBaseProfile("SmurfAlongWithMe"); + childProfile.value("blubb", QString(), &errorInfo); + QVERIFY(errorInfo.hasError()); + + errorInfo.clear(); + childProfile.setBaseProfile("parent"); + parentProfile.setBaseProfile("child"); + QVERIFY(!childProfile.value("blubb", QString(), &errorInfo).isValid()); + QVERIFY(errorInfo.hasError()); + + QVERIFY(!childProfile.allKeys(Profile::KeySelectionNonRecursive).isEmpty()); + + errorInfo.clear(); + QVERIFY(childProfile.allKeys(Profile::KeySelectionRecursive, &errorInfo).isEmpty()); + QVERIFY(errorInfo.hasError()); +} + +void TestTools::testSettingsMigration() +{ + QFETCH(QString, baseDir); + QFETCH(bool, hasOldSettings); + Settings settings(baseDir); + if (hasOldSettings) { + QVERIFY(QFileInfo(settings.baseDirectory() + "/qbs/" QBS_VERSION "/profiles/right.txt") + .exists()); + QCOMPARE(settings.value("key").toString(), + settings.baseDirectory() + "/qbs/" QBS_VERSION "/profilesright"); + } else { + QVERIFY(!QFileInfo(settings.baseDirectory() + "/qbs/" QBS_VERSION "/profiles").exists()); + QVERIFY(settings.allKeys().isEmpty()); + } +} + +void TestTools::testSettingsMigration_data() +{ + QTest::addColumn<QString>("baseDir"); + QTest::addColumn<bool>("hasOldSettings"); + QTest::newRow("settings dir with lots of versions") << setupSettingsDir1() << true; + QTest::newRow("settings dir with only a fallback") << setupSettingsDir2() << true; + QTest::newRow("no previous settings") << setupSettingsDir3() << false; +} + +QString TestTools::setupSettingsDir1() +{ + QTemporaryDir * const baseDir = new QTemporaryDir; + m_tmpDirs << baseDir; + + const Version thisVersion = Version::fromString(QBS_VERSION); + Version predecessor; + if (thisVersion.patchLevel() > 0) { + predecessor.setMajorVersion(thisVersion.majorVersion()); + predecessor.setMinorVersion(thisVersion.minorVersion()); + predecessor.setPatchLevel(thisVersion.patchLevel() - 1); + } else if (thisVersion.minorVersion() > 0) { + predecessor.setMajorVersion(thisVersion.majorVersion()); + predecessor.setMinorVersion(thisVersion.minorVersion() - 1); + predecessor.setPatchLevel(99); + } else { + predecessor.setMajorVersion(thisVersion.majorVersion() - 1); + predecessor.setMajorVersion(99); + predecessor.setPatchLevel(99); + } + const auto versions = QList<Version>() << Version(0, 1, 0) << Version(1, 0, 5) << predecessor + << Version(thisVersion.majorVersion() + 1, thisVersion.minorVersion(), + thisVersion.patchLevel()) + << Version(thisVersion.majorVersion(), thisVersion.minorVersion() + 1, + thisVersion.patchLevel()) + << Version(thisVersion.majorVersion(), thisVersion.minorVersion(), + thisVersion.patchLevel() + 1) + << Version(99, 99, 99); + foreach (const Version &v, versions) { + const QString settingsDir = baseDir->path() + "/qbs/" + v.toString(); + QSettings s(settingsDir + "/qbs.conf", + HostOsInfo::isWindowsHost() ? QSettings::IniFormat : QSettings::NativeFormat); + const QString profilesDir = settingsDir + "/profiles"; + QDir::root().mkpath(profilesDir); + const QString magicString = v == predecessor ? "right" : "wrong"; + QFile f(profilesDir + '/' + magicString + ".txt"); + f.open(QIODevice::WriteOnly); + s.setValue("org/qt-project/qbs/key", profilesDir + magicString); + } + + return baseDir->path(); +} + +QString TestTools::setupSettingsDir2() +{ + QTemporaryDir * const baseDir = new QTemporaryDir; + m_tmpDirs << baseDir; + const QString settingsDir = baseDir->path(); + QSettings s(settingsDir + QLatin1String("/qbs.conf"), + HostOsInfo::isWindowsHost() ? QSettings::IniFormat : QSettings::NativeFormat); + const QString profilesDir = settingsDir + QLatin1String("/qbs/profiles"); + QDir::root().mkpath(profilesDir); + QFile f(profilesDir + "/right.txt"); + f.open(QIODevice::WriteOnly); + s.setValue("org/qt-project/qbs/key", profilesDir + "right"); + + return baseDir->path(); +} + +QString TestTools::setupSettingsDir3() +{ + auto * const baseDir = new QTemporaryDir; + m_tmpDirs << baseDir; + return baseDir->path(); +} + +void TestTools::testBuildConfigMerging() +{ + TemporaryProfile tp(QLatin1String("tst_tools_profile"), m_settings); + Profile profile = tp.p; + profile.setValue(QLatin1String("topLevelKey"), QLatin1String("topLevelValue")); + profile.setValue(QLatin1String("qbs.toolchain"), QLatin1String("gcc")); + profile.setValue(QLatin1String("qbs.architecture"), + QLatin1String("Jean-Claude Pillemann")); + profile.setValue(QLatin1String("cpp.treatWarningsAsErrors"), true); + QVariantMap overrideMap; + overrideMap.insert(QLatin1String("qbs.toolchain"), QLatin1String("clang")); + overrideMap.insert(QLatin1String("qbs.installRoot"), QLatin1String("/blubb")); + SetupProjectParameters params; + params.setSettingsDirectory(m_settings->baseDirectory()); + params.setTopLevelProfile(profile.name()); + params.setConfigurationName(QLatin1String("debug")); + params.setOverriddenValues(overrideMap); + const ErrorInfo error = params.expandBuildConfiguration(); + QVERIFY2(!error.hasError(), qPrintable(error.toString())); + const QVariantMap finalMap = params.finalBuildConfigurationTree(); + QCOMPARE(finalMap.count(), 3); + QCOMPARE(finalMap.value(QLatin1String("topLevelKey")).toString(), + QString::fromLatin1("topLevelValue")); + const QVariantMap finalQbsMap = finalMap.value(QLatin1String("qbs")).toMap(); + QCOMPARE(finalQbsMap.count(), 4); + QCOMPARE(finalQbsMap.value(QLatin1String("toolchain")).toString(), + QString::fromLatin1("clang")); + QCOMPARE(finalQbsMap.value(QLatin1String("configurationName")).toString(), + QString::fromLatin1("debug")); + QCOMPARE(finalQbsMap.value(QLatin1String("architecture")).toString(), + QString::fromLatin1("Jean-Claude Pillemann")); + QCOMPARE(finalQbsMap.value(QLatin1String("installRoot")).toString(), QLatin1String("/blubb")); + const QVariantMap finalCppMap = finalMap.value(QLatin1String("cpp")).toMap(); + QCOMPARE(finalCppMap.count(), 1); + QCOMPARE(finalCppMap.value(QLatin1String("treatWarningsAsErrors")).toBool(), true); +} + +void TestTools::testProcessNameByPid() +{ + QCOMPARE(qAppName(), processNameByPid(QCoreApplication::applicationPid())); +} + + +int toNumber(const QString &str) +{ + int res = 0; + for (int i = 0; i < str.length(); ++i) + res = (res * 10) + str[i].digitValue(); + return res; +} + +void TestTools::set_operator_eq() +{ + { + Set<int> set1, set2; + QVERIFY(set1 == set2); + QVERIFY(!(set1 != set2)); + + set1.insert(1); + QVERIFY(set1 != set2); + QVERIFY(!(set1 == set2)); + + set2.insert(1); + QVERIFY(set1 == set2); + QVERIFY(!(set1 != set2)); + + set2.insert(1); + QVERIFY(set1 == set2); + QVERIFY(!(set1 != set2)); + + set1.insert(2); + QVERIFY(set1 != set2); + QVERIFY(!(set1 == set2)); + } + + { + Set<QString> set1, set2; + QVERIFY(set1 == set2); + QVERIFY(!(set1 != set2)); + + set1.insert("one"); + QVERIFY(set1 != set2); + QVERIFY(!(set1 == set2)); + + set2.insert("one"); + QVERIFY(set1 == set2); + QVERIFY(!(set1 != set2)); + + set2.insert("one"); + QVERIFY(set1 == set2); + QVERIFY(!(set1 != set2)); + + set1.insert("two"); + QVERIFY(set1 != set2); + QVERIFY(!(set1 == set2)); + } + + { + Set<QString> a; + Set<QString> b; + + a += "otto"; + b += "willy"; + + QVERIFY(a != b); + QVERIFY(!(a == b)); + } + + { + Set<int> s1, s2; + s1.reserve(100); + s2.reserve(4); + QVERIFY(s1 == s2); + s1 << 100 << 200 << 300 << 400; + s2 << 400 << 300 << 200 << 100; + QVERIFY(s1 == s2); + } +} + +void TestTools::set_swap() +{ + Set<int> s1, s2; + s1.insert(1); + s2.insert(2); + s1.swap(s2); + QCOMPARE(*s1.begin(),2); + QCOMPARE(*s2.begin(),1); +} + +void TestTools::set_size() +{ + Set<int> set; + QVERIFY(set.size() == 0); + QVERIFY(set.isEmpty()); + QVERIFY(set.count() == set.size()); + + set.insert(1); + QVERIFY(set.size() == 1); + QVERIFY(!set.isEmpty()); + QVERIFY(set.count() == set.size()); + + set.insert(1); + QVERIFY(set.size() == 1); + QVERIFY(!set.isEmpty()); + QVERIFY(set.count() == set.size()); + + set.insert(2); + QVERIFY(set.size() == 2); + QVERIFY(!set.isEmpty()); + QVERIFY(set.count() == set.size()); + + set.remove(1); + QVERIFY(set.size() == 1); + QVERIFY(!set.isEmpty()); + QVERIFY(set.count() == set.size()); + + set.remove(1); + QVERIFY(set.size() == 1); + QVERIFY(!set.isEmpty()); + QVERIFY(set.count() == set.size()); + + set.remove(2); + QVERIFY(set.size() == 0); + QVERIFY(set.isEmpty()); + QVERIFY(set.count() == set.size()); +} + +void TestTools::set_capacity() +{ + Set<int> set; + int n = set.capacity(); + QVERIFY(n == 0); + + for (int i = 0; i < 1000; ++i) { + set.insert(i); + QVERIFY(set.capacity() >= set.size()); + } +} + +void TestTools::set_reserve() +{ + Set<int> set; + + set.reserve(1000); + QVERIFY(set.capacity() >= 1000); + + for (int i = 0; i < 500; ++i) + set.insert(i); + + QVERIFY(set.capacity() >= 1000); + + for (int j = 0; j < 500; ++j) + set.remove(j); + + QVERIFY(set.capacity() >= 1000); +} + +void TestTools::set_clear() +{ + Set<QString> set1, set2; + QVERIFY(set1.size() == 0); + + set1.clear(); + QVERIFY(set1.size() == 0); + + set1.insert("foo"); + QVERIFY(set1.size() != 0); + + set2 = set1; + + set1.clear(); + QVERIFY(set1.size() == 0); + QVERIFY(set2.size() != 0); + + set2.clear(); + QVERIFY(set1.size() == 0); + QVERIFY(set2.size() == 0); +} + +void TestTools::set_remove() +{ + Set<QString> set1; + + for (int i = 0; i < 500; ++i) + set1.insert(QString::number(i)); + + QCOMPARE(set1.size(), 500); + + for (int j = 0; j < 500; ++j) { + set1.remove(QString::number((j * 17) % 500)); + QCOMPARE(set1.size(), 500 - j - 1); + } +} + +void TestTools::set_contains() +{ + Set<QString> set1; + + for (int i = 0; i < 500; ++i) { + QVERIFY(!set1.contains(QString::number(i))); + set1.insert(QString::number(i)); + QVERIFY(set1.contains(QString::number(i))); + } + + QCOMPARE(set1.size(), 500); + + for (int j = 0; j < 500; ++j) { + int i = (j * 17) % 500; + QVERIFY(set1.contains(QString::number(i))); + set1.remove(QString::number(i)); + QVERIFY(!set1.contains(QString::number(i))); + } +} + +void TestTools::set_containsSet() +{ + Set<QString> set1; + Set<QString> set2; + + // empty set contains the empty set + QVERIFY(set1.contains(set2)); + + for (int i = 0; i < 500; ++i) { + set1.insert(QString::number(i)); + set2.insert(QString::number(i)); + } + QVERIFY(set1.contains(set2)); + + set2.remove(QString::number(19)); + set2.remove(QString::number(82)); + set2.remove(QString::number(7)); + QVERIFY(set1.contains(set2)); + + set1.remove(QString::number(23)); + QVERIFY(!set1.contains(set2)); + + // filled set contains the empty set as well + Set<QString> set3; + QVERIFY(set1.contains(set3)); + + // the empty set doesn't contain a filled set + QVERIFY(!set3.contains(set1)); + + // verify const signature + const Set<QString> set4; + QVERIFY(set3.contains(set4)); +} + +void TestTools::set_begin() +{ + Set<int> set1; + Set<int> set2 = set1; + + { + Set<int>::const_iterator i = set1.constBegin(); + Set<int>::const_iterator j = set1.cbegin(); + Set<int>::const_iterator k = set2.constBegin(); + Set<int>::const_iterator ell = set2.cbegin(); + + QVERIFY(i == j); + QVERIFY(k == ell); + } + + set1.insert(44); + + { + Set<int>::const_iterator i = set1.constBegin(); + Set<int>::const_iterator j = set1.cbegin(); + Set<int>::const_iterator k = set2.constBegin(); + Set<int>::const_iterator ell = set2.cbegin(); + + QVERIFY(i == j); + QVERIFY(k == ell); + } + + set2 = set1; + + { + Set<int>::const_iterator i = set1.constBegin(); + Set<int>::const_iterator j = set1.cbegin(); + Set<int>::const_iterator k = set2.constBegin(); + Set<int>::const_iterator ell = set2.cbegin(); + + QVERIFY(i == j); + QVERIFY(k == ell); + } +} + +void TestTools::set_end() +{ + Set<int> set1; + Set<int> set2 = set1; + + { + Set<int>::const_iterator i = set1.constEnd(); + Set<int>::const_iterator j = set1.cend(); + Set<int>::const_iterator k = set2.constEnd(); + Set<int>::const_iterator ell = set2.cend(); + + QVERIFY(i == j); + QVERIFY(k == ell); + + QVERIFY(set1.constBegin() == set1.constEnd()); + QVERIFY(set2.constBegin() == set2.constEnd()); + } + + set1.insert(44); + + { + Set<int>::const_iterator i = set1.constEnd(); + Set<int>::const_iterator j = set1.cend(); + Set<int>::const_iterator k = set2.constEnd(); + Set<int>::const_iterator ell = set2.cend(); + + QVERIFY(i == j); + QVERIFY(k == ell); + + QVERIFY(set1.constBegin() != set1.constEnd()); + QVERIFY(set2.constBegin() == set2.constEnd()); + } + + set2 = set1; + + { + Set<int>::const_iterator i = set1.constEnd(); + Set<int>::const_iterator j = set1.cend(); + Set<int>::const_iterator k = set2.constEnd(); + Set<int>::const_iterator ell = set2.cend(); + + QVERIFY(i == j); + QVERIFY(k == ell); + + QVERIFY(set1.constBegin() != set1.constEnd()); + QVERIFY(set2.constBegin() != set2.constEnd()); + } + + set1.clear(); + set2.clear(); + QVERIFY(set1.constBegin() == set1.constEnd()); + QVERIFY(set2.constBegin() == set2.constEnd()); +} + +struct IdentityTracker { + int value, id; +}; +inline bool operator==(IdentityTracker lhs, IdentityTracker rhs) { return lhs.value == rhs.value; } +inline bool operator<(IdentityTracker lhs, IdentityTracker rhs) { return lhs.value < rhs.value; } + +void TestTools::set_insert() +{ + { + Set<int> set1; + QVERIFY(set1.size() == 0); + set1.insert(1); + QVERIFY(set1.size() == 1); + set1.insert(2); + QVERIFY(set1.size() == 2); + set1.insert(2); + QVERIFY(set1.size() == 2); + QVERIFY(set1.contains(2)); + set1.remove(2); + QVERIFY(set1.size() == 1); + QVERIFY(!set1.contains(2)); + set1.insert(2); + QVERIFY(set1.size() == 2); + QVERIFY(set1.contains(2)); + } + + { + Set<int> set1; + QVERIFY(set1.size() == 0); + set1 << 1; + QVERIFY(set1.size() == 1); + set1 << 2; + QVERIFY(set1.size() == 2); + set1 << 2; + QVERIFY(set1.size() == 2); + QVERIFY(set1.contains(2)); + set1.remove(2); + QVERIFY(set1.size() == 1); + QVERIFY(!set1.contains(2)); + set1 << 2; + QVERIFY(set1.size() == 2); + QVERIFY(set1.contains(2)); + } + + { + Set<IdentityTracker> set; + QCOMPARE(set.size(), 0); + const int dummy = -1; + IdentityTracker id00 = {0, 0}, id01 = {0, 1}, searchKey = {0, dummy}; + QCOMPARE(set.insert(id00).first->id, id00.id); + QCOMPARE(set.size(), 1); + QCOMPARE(set.insert(id01).first->id, id00.id); // first inserted is kept + QCOMPARE(set.size(), 1); + QCOMPARE(set.find(searchKey)->id, id00.id); + } +} + +void TestTools::set_reverseIterators() +{ + Set<int> s; + s << 1 << 17 << 61 << 127 << 911; + std::vector<int> v(s.begin(), s.end()); + std::reverse(v.begin(), v.end()); + const Set<int> &cs = s; + QVERIFY(std::equal(v.begin(), v.end(), s.rbegin())); + QVERIFY(std::equal(v.begin(), v.end(), s.crbegin())); + QVERIFY(std::equal(v.begin(), v.end(), cs.rbegin())); + QVERIFY(std::equal(s.rbegin(), s.rend(), v.begin())); + QVERIFY(std::equal(s.crbegin(), s.crend(), v.begin())); + QVERIFY(std::equal(cs.rbegin(), cs.rend(), v.begin())); +} + +void TestTools::set_stlIterator() +{ + Set<QString> set1; + for (int i = 0; i < 25000; ++i) + set1.insert(QString::number(i)); + + { + int sum = 0; + Set<QString>::const_iterator i = set1.cbegin(); + while (i != set1.end()) { + sum += toNumber(*i); + ++i; + } + QVERIFY(sum == 24999 * 25000 / 2); + } + + { + int sum = 0; + Set<QString>::const_iterator i = set1.cend(); + while (i != set1.begin()) { + --i; + sum += toNumber(*i); + } + QVERIFY(sum == 24999 * 25000 / 2); + } +} + +void TestTools::set_stlMutableIterator() +{ + Set<QString> set1; + for (int i = 0; i < 25000; ++i) + set1.insert(QString::number(i)); + + { + int sum = 0; + Set<QString>::iterator i = set1.begin(); + while (i != set1.end()) { + sum += toNumber(*i); + ++i; + } + QVERIFY(sum == 24999 * 25000 / 2); + } + + { + int sum = 0; + Set<QString>::iterator i = set1.end(); + while (i != set1.begin()) { + --i; + sum += toNumber(*i); + } + QVERIFY(sum == 24999 * 25000 / 2); + } + + { + Set<QString> set2 = set1; + Set<QString> set3 = set2; + + Set<QString>::iterator i = set2.begin(); + Set<QString>::iterator j = set3.begin(); + + while (i != set2.end()) { + i = set2.erase(i); + } + QVERIFY(set2.isEmpty()); + QVERIFY(!set3.isEmpty()); + + j = set3.end(); + while (j != set3.begin()) { + j--; + if (j + 1 != set3.end()) + set3.erase(j + 1); + } + if (set3.begin() != set3.end()) + set3.erase(set3.begin()); + + QVERIFY(set2.isEmpty()); + QVERIFY(set3.isEmpty()); + + i = set2.insert("foo").first; + QCOMPARE(*i, QLatin1String("foo")); + } +} + +void TestTools::set_setOperations() +{ + Set<QString> set1, set2; + set1 << "alpha" << "beta" << "gamma" << "delta" << "zeta" << "omega"; + set2 << "beta" << "gamma" << "delta" << "epsilon" << "iota" << "omega"; + + Set<QString> set3 = set1; + set3.unite(set2); + QVERIFY(set3.size() == 8); + QVERIFY(set3.contains("alpha")); + QVERIFY(set3.contains("beta")); + QVERIFY(set3.contains("gamma")); + QVERIFY(set3.contains("delta")); + QVERIFY(set3.contains("epsilon")); + QVERIFY(set3.contains("zeta")); + QVERIFY(set3.contains("iota")); + QVERIFY(set3.contains("omega")); + + Set<QString> set4 = set2; + set4.unite(set1); + QVERIFY(set4.size() == 8); + QVERIFY(set4.contains("alpha")); + QVERIFY(set4.contains("beta")); + QVERIFY(set4.contains("gamma")); + QVERIFY(set4.contains("delta")); + QVERIFY(set4.contains("epsilon")); + QVERIFY(set4.contains("zeta")); + QVERIFY(set4.contains("iota")); + QVERIFY(set4.contains("omega")); + + QVERIFY(set3 == set4); + + Set<QString> set5 = set1; + set5.intersect(set2); + QVERIFY(set5.size() == 4); + QVERIFY(set5.contains("beta")); + QVERIFY(set5.contains("gamma")); + QVERIFY(set5.contains("delta")); + QVERIFY(set5.contains("omega")); + + Set<QString> set6 = set2; + set6.intersect(set1); + QVERIFY(set6.size() == 4); + QVERIFY(set6.contains("beta")); + QVERIFY(set6.contains("gamma")); + QVERIFY(set6.contains("delta")); + QVERIFY(set6.contains("omega")); + + QVERIFY(set5 == set6); + + Set<QString> set7 = set1; + set7.subtract(set2); + QVERIFY(set7.size() == 2); + QVERIFY(set7.contains("alpha")); + QVERIFY(set7.contains("zeta")); + + Set<QString> set8 = set2; + set8.subtract(set1); + QVERIFY(set8.size() == 2); + QVERIFY(set8.contains("epsilon")); + QVERIFY(set8.contains("iota")); + + Set<QString> set9 = set1 | set2; + QVERIFY(set9 == set3); + + Set<QString> set10 = set1 & set2; + QVERIFY(set10 == set5); + + Set<QString> set11 = set1 + set2; + QVERIFY(set11 == set3); + + Set<QString> set12 = set1 - set2; + QVERIFY(set12 == set7); + + Set<QString> set13 = set2 - set1; + QVERIFY(set13 == set8); + + Set<QString> set14 = set1; + set14 |= set2; + QVERIFY(set14 == set3); + + Set<QString> set15 = set1; + set15 &= set2; + QVERIFY(set15 == set5); + + Set<QString> set16 = set1; + set16 += set2; + QVERIFY(set16 == set3); + + Set<QString> set17 = set1; + set17 -= set2; + QVERIFY(set17 == set7); + + Set<QString> set18 = set2; + set18 -= set1; + QVERIFY(set18 == set8); +} + +void TestTools::set_makeSureTheComfortFunctionsCompile() +{ + Set<int> set1, set2, set3; + set1 << 5; + set1 |= set2; + set1 |= 5; + set1 &= set2; + set1 &= 5; + set1 += set2; + set1 += 5; + set1 -= set2; + set1 -= 5; + set1 = set2 | set3; + set1 = set2 & set3; + set1 = set2 + set3; + set1 = set2 - set3; +} + +void TestTools::set_initializerList() +{ + Set<int> set = {1, 1, 2, 3, 4, 5}; + QCOMPARE(set.count(), 5); + QVERIFY(set.contains(1)); + QVERIFY(set.contains(2)); + QVERIFY(set.contains(3)); + QVERIFY(set.contains(4)); + QVERIFY(set.contains(5)); + + // check _which_ of the equal elements gets inserted (in the QHash/QMap case, it's the last): + const Set<IdentityTracker> set2 = {{1, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; + QCOMPARE(set2.count(), 5); + const int dummy = -1; + const IdentityTracker searchKey = {1, dummy}; + QCOMPARE(set2.find(searchKey)->id, 0); + + Set<int> emptySet{}; + QVERIFY(emptySet.isEmpty()); + + Set<int> set3{{}, {}, {}}; + QVERIFY(!set3.isEmpty()); +} + +void TestTools::set_intersects() +{ + Set<int> s1; + Set<int> s2; + + QVERIFY(!s1.intersects(s1)); + QVERIFY(!s1.intersects(s2)); + + s1 << 100; + QVERIFY(s1.intersects(s1)); + QVERIFY(!s1.intersects(s2)); + + s2 << 200; + QVERIFY(!s1.intersects(s2)); + + s1 << 200; + QVERIFY(s1.intersects(s2)); + + Set<int> s3; + s3 << 500; + QVERIFY(!s1.intersects(s3)); + s3 << 200; + QVERIFY(s1.intersects(s3)); +} + int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); const SettingsPtr s = settings(); - qbs::Internal::TestTools tt(s.get()); + TestTools tt(s.get()); return QTest::qExec(&tt, argc, argv); } diff --git a/src/lib/corelib/tools/tst_tools.h b/tests/auto/tools/tst_tools.h index 27a6b5da0..cb6828ee8 100644 --- a/src/lib/corelib/tools/tst_tools.h +++ b/tests/auto/tools/tst_tools.h @@ -36,7 +36,6 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include "qbs_export.h" #include <QtCore/qlist.h> #include <QtCore/qobject.h> @@ -47,15 +46,14 @@ QT_END_NAMESPACE namespace qbs { class Settings; +} -namespace Internal { - -class QBS_EXPORT TestTools : public QObject +class TestTools : public QObject { Q_OBJECT public: - TestTools(Settings *settings); + TestTools(qbs::Settings *settings); ~TestTools(); private slots: @@ -92,9 +90,6 @@ private: QString setupSettingsDir2(); QString setupSettingsDir3(); - Settings * const m_settings; + qbs::Settings * const m_settings; QList<QTemporaryDir *> m_tmpDirs; }; - -} // namespace Internal -} // namespace qbs |