diff options
author | Marco Bubke <marco.bubke@qt.io> | 2021-09-28 17:05:23 +0200 |
---|---|---|
committer | Marco Bubke <marco.bubke@qt.io> | 2021-10-14 07:40:49 +0000 |
commit | 1b16eb209ce05aa6e36215a79d21931b164926d6 (patch) | |
tree | a362cb43c13827059df00342edd84f1a4602f474 /tests | |
parent | cbf96341a1681de0898706a7c3896b69d4b6d362 (diff) |
QmlDesigner: Add QmlDocumentParser and QmlTypesParser
Task-number: QDS-5174
Task-number: QDS-5228
Change-Id: I0889e8d63b0260aeb0efae1b3c8a373c18ea1f03
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/unit/unittest/CMakeLists.txt | 18 | ||||
-rw-r--r-- | tests/unit/unittest/gtest-creator-printing.cpp | 29 | ||||
-rw-r--r-- | tests/unit/unittest/gtest-creator-printing.h | 2 | ||||
-rw-r--r-- | tests/unit/unittest/projectstorageupdater-test.cpp | 15 | ||||
-rw-r--r-- | tests/unit/unittest/qmldocumentparser-test.cpp | 243 | ||||
-rw-r--r-- | tests/unit/unittest/qmldom-test.cpp | 51 | ||||
-rw-r--r-- | tests/unit/unittest/qmltypesparser-test.cpp | 410 | ||||
-rw-r--r-- | tests/unit/unittest/qmltypesparsermock.h | 3 |
8 files changed, 701 insertions, 70 deletions
diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt index 6252f4517d..36aa86e631 100644 --- a/tests/unit/unittest/CMakeLists.txt +++ b/tests/unit/unittest/CMakeLists.txt @@ -481,8 +481,7 @@ get_filename_component( ABSOLUTE ) - -if (EXISTS ../../../../qmldom_standalone/src/qmldom/standalone) +if(EXISTS ${QMLDOM_STANDALONE_CMAKELISTS} AND Qt6_FOUND) add_subdirectory( ../../../../qmldom_standalone/src/qmldom/standalone ${CMAKE_CURRENT_BINARY_DIR}/qmldom_standalone) @@ -491,9 +490,16 @@ if (EXISTS ../../../../qmldom_standalone/src/qmldom/standalone) RUNTIME_OUTPUT_DIRECTORY "$<TARGET_PROPERTY:QmlJS,RUNTIME_OUTPUT_DIRECTORY>" LIBRARY_OUTPUT_DIRECTORY "$<TARGET_PROPERTY:QmlJS,LIBRARY_OUTPUT_DIRECTORY>") - extend_qtc_test(unittest - DEPENDS qmldomlib - SOURCES - qmldom-test.cpp + extend_qtc_test(unittest + DEPENDS qmldomlib + SOURCES + qmldocumentparser-test.cpp + qmltypesparser-test.cpp + ) + extend_qtc_test(unittest + SOURCES_PREFIX "${QmlDesignerDir}/designercore" + SOURCES + projectstorage/qmldocumentparser.cpp projectstorage/qmldocumentparser.h + projectstorage/qmltypesparser.cpp projectstorage/qmltypesparser.h ) endif() diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp index 89577c1f05..85ff406f95 100644 --- a/tests/unit/unittest/gtest-creator-printing.cpp +++ b/tests/unit/unittest/gtest-creator-printing.cpp @@ -934,7 +934,7 @@ std::ostream &operator<<(std::ostream &out, const Diagnostic &diag) { } // namespace ClangTools namespace QmlDesigner { - +namespace { const char *sourceTypeToText(SourceType sourceType) { switch (sourceType) { @@ -950,6 +950,7 @@ const char *sourceTypeToText(SourceType sourceType) return ""; } +} // namespace std::ostream &operator<<(std::ostream &out, const FileStatus &fileStatus) { @@ -1015,8 +1016,8 @@ TypeAccessSemantics cleanFlags(TypeAccessSemantics accessSemantics) const char *typeAccessSemanticsToString(TypeAccessSemantics accessSemantics) { switch (cleanFlags(accessSemantics)) { - case TypeAccessSemantics::Invalid: - return "Invalid"; + case TypeAccessSemantics::None: + return "None"; case TypeAccessSemantics::Reference: return "Reference"; case TypeAccessSemantics::Sequence: @@ -1055,6 +1056,20 @@ const char *isQualifiedToString(IsQualified isQualified) return ""; } +const char *importKindToText(ImportKind kind) +{ + switch (kind) { + case ImportKind::Module: + return "Module"; + case ImportKind::Directory: + return "Directory"; + case ImportKind::QmlTypesDependency: + return "QmlTypesDependency"; + } + + return ""; +} + } // namespace std::ostream &operator<<(std::ostream &out, TypeAccessSemantics accessSemantics) @@ -1176,9 +1191,15 @@ std::ostream &operator<<(std::ostream &out, const Module &module) return out << "(" << module.name << ", " << module.sourceId << ")"; } +std::ostream &operator<<(std::ostream &out, const ImportKind &importKind) +{ + return out << importKindToText(importKind); +} + std::ostream &operator<<(std::ostream &out, const Import &import) { - return out << "(" << import.name << ", " << import.version << ", " << import.sourceId << ")"; + return out << "(" << import.name << ", " << import.version << ", " << import.sourceId << ", " + << import.moduleId << ", " << import.kind << ")"; } } // namespace Storage diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h index d2c20d92b5..5b8cb51e8e 100644 --- a/tests/unit/unittest/gtest-creator-printing.h +++ b/tests/unit/unittest/gtest-creator-printing.h @@ -262,6 +262,7 @@ class EnumerationDeclaration; class EnumeratorDeclaration; class Module; class ModuleDependency; +enum class ImportKind : char; class Import; enum class IsQualified : int; @@ -282,6 +283,7 @@ std::ostream &operator<<(std::ostream &out, const EnumerationDeclaration &enumer std::ostream &operator<<(std::ostream &out, const EnumeratorDeclaration &enumeratorDeclaration); std::ostream &operator<<(std::ostream &out, const Module &module); std::ostream &operator<<(std::ostream &out, const ModuleDependency &module); +std::ostream &operator<<(std::ostream &out, const ImportKind &importKind); std::ostream &operator<<(std::ostream &out, const Import &import); std::ostream &operator<<(std::ostream &out, IsQualified isQualified); diff --git a/tests/unit/unittest/projectstorageupdater-test.cpp b/tests/unit/unittest/projectstorageupdater-test.cpp index d198587821..d6a26bb109 100644 --- a/tests/unit/unittest/projectstorageupdater-test.cpp +++ b/tests/unit/unittest/projectstorageupdater-test.cpp @@ -305,8 +305,8 @@ TEST_F(ProjectStorageUpdater, ParseQmlTypes) ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example2.qmltypes")))) .WillByDefault(Return(qmltypes2)); - EXPECT_CALL(qmlTypesParserMock, parse(qmltypes, _, _, _)); - EXPECT_CALL(qmlTypesParserMock, parse(qmltypes2, _, _, _)); + EXPECT_CALL(qmlTypesParserMock, parse(qmltypes, _, _, _, _)); + EXPECT_CALL(qmlTypesParserMock, parse(qmltypes2, _, _, _, _)); updater.update(); } @@ -334,8 +334,8 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlTypes) QString qmltypes{"Module {\ndependencies: []}"}; ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes")))) .WillByDefault(Return(qmltypes)); - ON_CALL(qmlTypesParserMock, parse(qmltypes, _, _, _)) - .WillByDefault([&](auto, auto &imports, auto &types, auto &sourceIds) { + ON_CALL(qmlTypesParserMock, parse(qmltypes, _, _, _, _)) + .WillByDefault([&](auto, auto &imports, auto &types, auto, auto) { types.push_back(objectType); imports.push_back(import); }); @@ -356,10 +356,9 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlTypesAreEmptyIfFileDoesNotChanged) QString qmltypes{"Module {\ndependencies: []}"}; ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes")))) .WillByDefault(Return(qmltypes)); - ON_CALL(qmlTypesParserMock, parse(qmltypes, _, _, _)) - .WillByDefault([&](auto, auto &imports, auto &types, auto &sourceIds) { - types.push_back(objectType); - }); + ON_CALL(qmlTypesParserMock, parse(qmltypes, _, _, _, _)) + .WillByDefault( + [&](auto, auto &imports, auto &types, auto, auto) { types.push_back(objectType); }); ON_CALL(fileSystemMock, fileStatus(Eq(qmltypesPathSourceId))) .WillByDefault(Return(FileStatus{qmltypesPathSourceId, 2, 421})); ON_CALL(fileSystemMock, fileStatus(Eq(qmltypes2PathSourceId))) diff --git a/tests/unit/unittest/qmldocumentparser-test.cpp b/tests/unit/unittest/qmldocumentparser-test.cpp new file mode 100644 index 0000000000..4cadf8253d --- /dev/null +++ b/tests/unit/unittest/qmldocumentparser-test.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 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 +** 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. +** +****************************************************************************/ + +#include "googletest.h" + +#include <sqlitedatabase.h> + +#include <projectstorage/projectstorage.h> +#include <projectstorage/qmldocumentparser.h> +#include <projectstorage/sourcepathcache.h> + +namespace { + +namespace Storage = QmlDesigner::Storage; +using QmlDesigner::ModuleId; +using QmlDesigner::SourceContextId; +using QmlDesigner::SourceId; + +MATCHER_P(HasPrototype, prototype, std::string(negation ? "isn't " : "is ") + PrintToString(prototype)) +{ + const Storage::Type &type = arg; + + return Storage::ImportedTypeName{prototype} == type.prototype; +} + +MATCHER_P3(IsPropertyDeclaration, + name, + typeName, + traits, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::PropertyDeclaration{name, typeName, traits})) +{ + const Storage::PropertyDeclaration &propertyDeclaration = arg; + + return propertyDeclaration.name == name + && Storage::ImportedTypeName{typeName} == propertyDeclaration.typeName + && propertyDeclaration.traits == traits; +} + +MATCHER_P2(IsFunctionDeclaration, + name, + returnTypeName, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::FunctionDeclaration{name, returnTypeName})) +{ + const Storage::FunctionDeclaration &declaration = arg; + + return declaration.name == name && declaration.returnTypeName == returnTypeName; +} + +MATCHER_P(IsSignalDeclaration, + name, + std::string(negation ? "isn't " : "is ") + PrintToString(Storage::SignalDeclaration{name})) +{ + const Storage::SignalDeclaration &declaration = arg; + + return declaration.name == name; +} + +MATCHER_P2(IsParameter, + name, + typeName, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::ParameterDeclaration{name, typeName})) +{ + const Storage::ParameterDeclaration &declaration = arg; + + return declaration.name == name && declaration.typeName == typeName; +} + +MATCHER_P(IsEnumeration, + name, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::EnumerationDeclaration{name, {}})) +{ + const Storage::EnumerationDeclaration &declaration = arg; + + return declaration.name == name; +} + +MATCHER_P(IsEnumerator, + name, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::EnumeratorDeclaration{name})) +{ + const Storage::EnumeratorDeclaration &declaration = arg; + + return declaration.name == name && !declaration.hasValue; +} + +MATCHER_P2(IsEnumerator, + name, + value, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::EnumeratorDeclaration{name, value, true})) +{ + const Storage::EnumeratorDeclaration &declaration = arg; + + return declaration.name == name && declaration.value == value && declaration.hasValue; +} + +class QmlDocumentParser : public ::testing::Test +{ +public: +protected: + Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + QmlDesigner::ProjectStorage<Sqlite::Database> storage{database, database.isInitialized()}; + QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage<Sqlite::Database>> sourcePathCache{ + storage}; + QmlDesigner::QmlDocumentParser parser{sourcePathCache}; + Storage::Imports imports; + SourceId qmlFileSourceId{sourcePathCache.sourceId("path/to/qmlfile.qml")}; + SourceContextId qmlFileSourceContextId{sourcePathCache.sourceContextId(qmlFileSourceId)}; + SourceId directorySourceId{sourcePathCache.sourceId("path/to/.")}; + ModuleId directoryModuleId{&directorySourceId}; +}; + +TEST_F(QmlDocumentParser, Prototype) +{ + auto type = parser.parse("Example{}", imports, qmlFileSourceId, qmlFileSourceContextId); + + ASSERT_THAT(type, HasPrototype(Storage::ImportedType("Example"))); +} + +TEST_F(QmlDocumentParser, DISABLED_QualifiedPrototype) +{ + auto type = parser.parse("import Example as Example\n Example.Item{}", + imports, + qmlFileSourceId, + qmlFileSourceContextId); + + ASSERT_THAT(type, + HasPrototype(Storage::QualifiedImportedType( + "Item", Storage::Import{"Example", Storage::Version{}, qmlFileSourceId}))); +} + +TEST_F(QmlDocumentParser, Properties) +{ + auto type = parser.parse("Example{\n property int foo\n}", + imports, + qmlFileSourceId, + qmlFileSourceContextId); + + ASSERT_THAT(type.propertyDeclarations, + UnorderedElementsAre(IsPropertyDeclaration("foo", + Storage::ImportedType{"int"}, + Storage::PropertyDeclarationTraits::None))); +} + +TEST_F(QmlDocumentParser, DISABLED_Imports) +{ + ModuleId fooDirectoryModuleId{&sourcePathCache.sourceId("path/to/foo/.")}; + auto type = parser.parse("import QtQuick\n import \"../foo\"\nExample{}", + imports, + qmlFileSourceId, + qmlFileSourceContextId); + + ASSERT_THAT(imports, + UnorderedElementsAre( + Storage::Import{Storage::Version{}, directoryModuleId, qmlFileSourceId}, + Storage::Import{Storage::Version{}, fooDirectoryModuleId, qmlFileSourceId}, + Storage::Import{"QML", Storage::Version{1, 0}, qmlFileSourceId}, + Storage::Import{"QtQml", Storage::Version{6, 0}, qmlFileSourceId}, + Storage::Import{"QtQuick", Storage::Version{}, qmlFileSourceId})); +} + +TEST_F(QmlDocumentParser, Functions) +{ + auto type = parser.parse( + "Example{\n function someScript(x, y) {}\n function otherFunction() {}\n}", + imports, + qmlFileSourceId, + qmlFileSourceContextId); + + ASSERT_THAT(type.functionDeclarations, + UnorderedElementsAre(AllOf(IsFunctionDeclaration("otherFunction", ""), + Field(&Storage::FunctionDeclaration::parameters, IsEmpty())), + AllOf(IsFunctionDeclaration("someScript", ""), + Field(&Storage::FunctionDeclaration::parameters, + ElementsAre(IsParameter("x", ""), + IsParameter("y", "")))))); +} + +TEST_F(QmlDocumentParser, Signals) +{ + auto type = parser.parse("Example{\n signal someSignal(int x, real y)\n signal signal2()\n}", + imports, + qmlFileSourceId, + qmlFileSourceContextId); + + ASSERT_THAT(type.signalDeclarations, + UnorderedElementsAre(AllOf(IsSignalDeclaration("someSignal"), + Field(&Storage::SignalDeclaration::parameters, + ElementsAre(IsParameter("x", "int"), + IsParameter("y", "real")))), + AllOf(IsSignalDeclaration("signal2"), + Field(&Storage::SignalDeclaration::parameters, IsEmpty())))); +} + +TEST_F(QmlDocumentParser, Enumeration) +{ + auto type = parser.parse("Example{\n enum Color{red, green, blue=10, white}\n enum " + "State{On,Off}\n}", + imports, + qmlFileSourceId, + qmlFileSourceContextId); + + ASSERT_THAT(type.enumerationDeclarations, + UnorderedElementsAre( + AllOf(IsEnumeration("Color"), + Field(&Storage::EnumerationDeclaration::enumeratorDeclarations, + ElementsAre(IsEnumerator("red", 0), + IsEnumerator("green", 1), + IsEnumerator("blue", 10), + IsEnumerator("white", 11)))), + AllOf(IsEnumeration("State"), + Field(&Storage::EnumerationDeclaration::enumeratorDeclarations, + ElementsAre(IsEnumerator("On", 0), IsEnumerator("Off", 1)))))); +} + +} // namespace diff --git a/tests/unit/unittest/qmldom-test.cpp b/tests/unit/unittest/qmldom-test.cpp deleted file mode 100644 index b7e8557f10..0000000000 --- a/tests/unit/unittest/qmldom-test.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** 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 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 -** 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. -** -****************************************************************************/ - -#include "googletest.h" - -// cast of the top level items (DomEnvironments,...) -#include <qmldom/qqmldomtop_p.h> - -// everything is in the QQmlJS::Dom namespace -using namespace QQmlJS::Dom; - -namespace { - -class QmlDom : public ::testing::Test -{ -public: -// static void SetUpTestCase(); -// static void TearDownTestCase(); - -protected: -}; - -TEST_F(QmlDom, First) -{ - DomItem env = DomEnvironment::create({}, DomEnvironment::Option::SingleThreaded - | DomEnvironment::Option::NoDependencies); -} - -} // anonymous diff --git a/tests/unit/unittest/qmltypesparser-test.cpp b/tests/unit/unittest/qmltypesparser-test.cpp new file mode 100644 index 0000000000..fcd182b920 --- /dev/null +++ b/tests/unit/unittest/qmltypesparser-test.cpp @@ -0,0 +1,410 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 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 +** 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. +** +****************************************************************************/ + +#include "googletest.h" + +#include <sqlitedatabase.h> + +#include <projectstorage/projectstorage.h> +#include <projectstorage/projectstoragetypes.h> +#include <projectstorage/qmltypesparser.h> +#include <projectstorage/sourcepathcache.h> + +namespace { + +namespace Storage = QmlDesigner::Storage; +using QmlDesigner::ModuleId; +using QmlDesigner::SourceContextId; +using QmlDesigner::SourceId; + +MATCHER_P4(IsImport, + name, + version, + sourceId, + kind, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::Import{name, version, sourceId, kind})) +{ + const Storage::Import &import = arg; + + return import.name == name && import.version == version && import.sourceId == sourceId + && import.kind == kind; +} + +MATCHER_P(HasPrototype, prototype, std::string(negation ? "isn't " : "is ") + PrintToString(prototype)) +{ + const Storage::Type &type = arg; + + return Storage::ImportedTypeName{prototype} == type.prototype; +} + +MATCHER_P5(IsType, + moduleId, + typeName, + prototype, + accessSemantics, + sourceId, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::Type{moduleId, typeName, prototype, accessSemantics, sourceId})) +{ + const Storage::Type &type = arg; + + return type.moduleId == moduleId && type.typeName == typeName + && type.prototype == Storage::ImportedTypeName{prototype} + && type.accessSemantics == accessSemantics && type.sourceId == sourceId; +} + +MATCHER_P3(IsPropertyDeclaration, + name, + typeName, + traits, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::PropertyDeclaration{name, typeName, traits})) +{ + const Storage::PropertyDeclaration &propertyDeclaration = arg; + + return propertyDeclaration.name == name + && Storage::ImportedTypeName{typeName} == propertyDeclaration.typeName + && propertyDeclaration.traits == traits + && propertyDeclaration.kind == Storage::PropertyKind::Property; +} + +MATCHER_P2(IsFunctionDeclaration, + name, + returnTypeName, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::FunctionDeclaration{name, returnTypeName})) +{ + const Storage::FunctionDeclaration &declaration = arg; + + return declaration.name == name && declaration.returnTypeName == returnTypeName; +} + +MATCHER_P(IsSignalDeclaration, + name, + std::string(negation ? "isn't " : "is ") + PrintToString(Storage::SignalDeclaration{name})) +{ + const Storage::SignalDeclaration &declaration = arg; + + return declaration.name == name; +} + +MATCHER_P2(IsParameter, + name, + typeName, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::ParameterDeclaration{name, typeName})) +{ + const Storage::ParameterDeclaration &declaration = arg; + + return declaration.name == name && declaration.typeName == typeName; +} + +MATCHER_P(IsEnumeration, + name, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::EnumerationDeclaration{name, {}})) +{ + const Storage::EnumerationDeclaration &declaration = arg; + + return declaration.name == name; +} + +MATCHER_P(IsEnumerator, + name, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::EnumeratorDeclaration{name})) +{ + const Storage::EnumeratorDeclaration &declaration = arg; + + return declaration.name == name && !declaration.hasValue; +} + +MATCHER_P2(IsEnumerator, + name, + value, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::EnumeratorDeclaration{name, value, true})) +{ + const Storage::EnumeratorDeclaration &declaration = arg; + + return declaration.name == name && declaration.value == value && declaration.hasValue; +} + +MATCHER_P3(IsExportedType, + moduleId, + name, + version, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::ExportedType{moduleId, name, version})) +{ + const Storage::ExportedType &type = arg; + + return type.name == name && type.moduleId == moduleId && type.version == version; +} + +class QmlTypesParser : public ::testing::Test +{ +public: +protected: + Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + QmlDesigner::ProjectStorage<Sqlite::Database> storage{database, database.isInitialized()}; + QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage<Sqlite::Database>> sourcePathCache{ + storage}; + QmlDesigner::QmlTypesParser parser{sourcePathCache}; + Storage::Imports imports; + Storage::Types types; + SourceId qmltypesFileSourceId{sourcePathCache.sourceId("path/to/types.qmltypes")}; + SourceContextId qmltypesFileSourceContextId{sourcePathCache.sourceContextId(qmltypesFileSourceId)}; + SourceId directorySourceId{sourcePathCache.sourceId("path/to/.")}; + ModuleId directoryModuleId{&directorySourceId}; +}; + +TEST_F(QmlTypesParser, Imports) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + dependencies: + ["QtQuick 2.15", "QtQuick.Window 2.1", "QtFoo 6"]})"}; + + parser.parse(source, imports, types, qmltypesFileSourceId, directoryModuleId); + + ASSERT_THAT(imports, + UnorderedElementsAre(IsImport("QtQuick", + Storage::Version{2, 15}, + qmltypesFileSourceId, + Storage::ImportKind::QmlTypesDependency), + IsImport("QtQuick.Window", + Storage::Version{2, 1}, + qmltypesFileSourceId, + Storage::ImportKind::QmlTypesDependency), + IsImport("QML", + Storage::Version{}, + qmltypesFileSourceId, + Storage::ImportKind::QmlTypesDependency), + IsImport("QtQml", + Storage::Version{}, + qmltypesFileSourceId, + Storage::ImportKind::QmlTypesDependency), + IsImport("QtFoo", + Storage::Version{6}, + qmltypesFileSourceId, + Storage::ImportKind::QmlTypesDependency))); +} + +TEST_F(QmlTypesParser, Types) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject"} + Component { name: "QQmlComponent" + prototype: "QObject"}})"}; + + parser.parse(source, imports, types, qmltypesFileSourceId, directoryModuleId); + + ASSERT_THAT(types, + UnorderedElementsAre(IsType(directoryModuleId, + "QObject", + Storage::NativeType{}, + Storage::TypeAccessSemantics::Reference, + qmltypesFileSourceId), + IsType(directoryModuleId, + "QQmlComponent", + Storage::NativeType{"QObject"}, + Storage::TypeAccessSemantics::Reference, + qmltypesFileSourceId))); +} + +TEST_F(QmlTypesParser, ExportedTypes) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject" + exports: ["QtQml/QtObject 1.0", "QtQml/QtObject 2.1"] + }})"}; + + parser.parse(source, imports, types, qmltypesFileSourceId, directoryModuleId); + + ASSERT_THAT( + types, + ElementsAre( + Field(&Storage::Type::exportedTypes, + ElementsAre(IsExportedType(directoryModuleId, "QtObject", Storage::Version{1, 0}), + IsExportedType(directoryModuleId, "QtObject", Storage::Version{2, 1}))))); +} + +TEST_F(QmlTypesParser, Properties) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject" + Property { name: "objectName"; type: "string" } + Property { name: "target"; type: "QObject"; isPointer: true } + Property { name: "progress"; type: "double"; isReadonly: true } + Property { name: "targets"; type: "QQuickItem"; isList: true; isReadonly: true; isPointer: true } + }})"}; + + parser.parse(source, imports, types, qmltypesFileSourceId, directoryModuleId); + + ASSERT_THAT(types, + ElementsAre(Field( + &Storage::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("objectName", + Storage::NativeType{"string"}, + Storage::PropertyDeclarationTraits::None), + IsPropertyDeclaration("target", + Storage::NativeType{"QObject"}, + Storage::PropertyDeclarationTraits::IsPointer), + IsPropertyDeclaration("progress", + Storage::NativeType{"double"}, + Storage::PropertyDeclarationTraits::IsReadOnly), + IsPropertyDeclaration("targets", + Storage::NativeType{"QQuickItem"}, + Storage::PropertyDeclarationTraits::IsReadOnly + | Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsPointer))))); +} + +TEST_F(QmlTypesParser, Functions) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject" + Method { name: "movieUpdate" } + Method { + name: "advance" + Parameter { name: "frames"; type: "int" } + Parameter { name: "fps"; type: "double" } + } + Method { + name: "isImageLoading" + type: "bool" + Parameter { name: "url"; type: "QUrl" } + } + Method { + name: "getContext" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + }})"}; + + parser.parse(source, imports, types, qmltypesFileSourceId, directoryModuleId); + + ASSERT_THAT(types, + ElementsAre( + Field(&Storage::Type::functionDeclarations, + UnorderedElementsAre( + AllOf(IsFunctionDeclaration("advance", ""), + Field(&Storage::FunctionDeclaration::parameters, + UnorderedElementsAre(IsParameter("frames", "int"), + IsParameter("fps", "double")))), + AllOf(IsFunctionDeclaration("isImageLoading", "bool"), + Field(&Storage::FunctionDeclaration::parameters, + UnorderedElementsAre(IsParameter("url", "QUrl")))), + AllOf(IsFunctionDeclaration("getContext", ""), + Field(&Storage::FunctionDeclaration::parameters, + UnorderedElementsAre(IsParameter("args", "QQmlV4Function")))), + AllOf(IsFunctionDeclaration("movieUpdate", ""), + Field(&Storage::FunctionDeclaration::parameters, IsEmpty())))))); +} + +TEST_F(QmlTypesParser, Signals) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject" + Method { name: "movieUpdate" } + Signal { + name: "advance" + Parameter { name: "frames"; type: "int" } + Parameter { name: "fps"; type: "double" } + } + Signal { + name: "isImageLoading" + Parameter { name: "url"; type: "QUrl" } + } + Signal { + name: "getContext" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + }})"}; + + parser.parse(source, imports, types, qmltypesFileSourceId, directoryModuleId); + + ASSERT_THAT(types, + ElementsAre(Field(&Storage::Type::signalDeclarations, + UnorderedElementsAre( + AllOf(IsSignalDeclaration("advance"), + Field(&Storage::SignalDeclaration::parameters, + UnorderedElementsAre(IsParameter("frames", "int"), + IsParameter("fps", "double")))), + AllOf(IsSignalDeclaration("isImageLoading"), + Field(&Storage::SignalDeclaration::parameters, + UnorderedElementsAre(IsParameter("url", "QUrl")))), + AllOf(IsSignalDeclaration("getContext"), + Field(&Storage::SignalDeclaration::parameters, + UnorderedElementsAre( + IsParameter("args", "QQmlV4Function")))))))); +} + +TEST_F(QmlTypesParser, Enumerations) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject" + Enum { + name: "NamedColorSpace" + values: [ + "Unknown", + "SRgb", + "AdobeRgb", + "DisplayP3", + ] + } + Enum { + name: "VerticalLayoutDirection" + values: ["TopToBottom", "BottomToTop"] + } + }})"}; + + parser.parse(source, imports, types, qmltypesFileSourceId, directoryModuleId); + + ASSERT_THAT(types, + ElementsAre( + Field(&Storage::Type::enumerationDeclarations, + UnorderedElementsAre( + AllOf(IsEnumeration("NamedColorSpace"), + Field(&Storage::EnumerationDeclaration::enumeratorDeclarations, + UnorderedElementsAre(IsEnumerator("Unknown"), + IsEnumerator("SRgb"), + IsEnumerator("AdobeRgb"), + IsEnumerator("DisplayP3")))), + AllOf(IsEnumeration("VerticalLayoutDirection"), + Field(&Storage::EnumerationDeclaration::enumeratorDeclarations, + UnorderedElementsAre(IsEnumerator("TopToBottom"), + IsEnumerator("BottomToTop")))))))); +} + +} // namespace diff --git a/tests/unit/unittest/qmltypesparsermock.h b/tests/unit/unittest/qmltypesparsermock.h index 337c0f05ff..cfbb872984 100644 --- a/tests/unit/unittest/qmltypesparsermock.h +++ b/tests/unit/unittest/qmltypesparsermock.h @@ -37,6 +37,7 @@ public: (const QString &sourceContent, QmlDesigner::Storage::Imports &imports, QmlDesigner::Storage::Types &types, - QmlDesigner::SourceIds &sourceIds), + QmlDesigner::SourceId sourceId, + QmlDesigner::ModuleId moduleId), (override)); }; |