Files
qt-creator/tests/unit/unittest/projectstorageupdater-test.cpp
Marco Bubke 0827556621 QmlDesigner: Synchronize ProjectData to the Storage
Task-number: QDS-5644
Change-Id: Icede506974e88dd64fc866b65f4fa9d64a05aea0
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
2021-12-08 15:01:05 +00:00

813 lines
42 KiB
C++

/****************************************************************************
**
** Copyright (C) 2021 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 "filesystemmock.h"
#include "projectmanagermock.h"
#include "projectstoragemock.h"
#include "qmldocumentparsermock.h"
#include "qmltypesparsermock.h"
#include <projectstorage/filestatuscache.h>
#include <projectstorage/projectstorage.h>
#include <projectstorage/projectstorageupdater.h>
#include <projectstorage/sourcepathcache.h>
#include <sqlitedatabase.h>
namespace {
namespace Storage = QmlDesigner::Storage;
using QmlDesigner::FileStatus;
using QmlDesigner::ModuleId;
using QmlDesigner::SourceId;
using QmlDesigner::Storage::TypeAccessSemantics;
namespace Storage = QmlDesigner::Storage;
using QmlDesigner::IdPaths;
using QmlDesigner::Storage::FileType;
using QmlDesigner::Storage::SynchronizationPackage;
using QmlDesigner::Storage::Version;
MATCHER_P5(IsStorageType,
typeName,
prototype,
accessSemantics,
sourceId,
changeLevel,
std::string(negation ? "isn't " : "is ")
+ PrintToString(Storage::Type(typeName, prototype, accessSemantics, sourceId, changeLevel)))
{
const Storage::Type &type = arg;
return type.typeName == typeName && type.accessSemantics == accessSemantics
&& type.sourceId == sourceId && Storage::ImportedTypeName{prototype} == type.prototype
&& type.changeLevel == changeLevel;
}
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_P4(IsExportedType,
moduleId,
name,
majorVersion,
minorVersion,
std::string(negation ? "isn't " : "is ")
+ PrintToString(Storage::ExportedType{moduleId,
name,
Storage::Version{majorVersion, minorVersion}}))
{
const Storage::ExportedType &type = arg;
return type.moduleId == moduleId && type.name == name
&& type.version == Storage::Version{majorVersion, minorVersion};
}
MATCHER_P3(IsFileStatus,
sourceId,
size,
lastModified,
std::string(negation ? "isn't " : "is ")
+ PrintToString(FileStatus{sourceId, size, lastModified}))
{
const FileStatus &fileStatus = arg;
return fileStatus.sourceId == sourceId && fileStatus.size == size
&& fileStatus.lastModified == lastModified;
}
MATCHER_P4(IsProjectData,
projectSourceId,
sourceId,
moduleId,
fileType,
std::string(negation ? "isn't " : "is ")
+ PrintToString(Storage::ProjectData{projectSourceId, sourceId, moduleId, fileType}))
{
const Storage::ProjectData &projectData = arg;
return projectData.projectSourceId == projectSourceId && projectData.sourceId == sourceId
&& projectData.moduleId == moduleId && projectData.fileType == fileType;
}
MATCHER(PackageIsEmpty, std::string(negation ? "isn't empty" : "is empty"))
{
const Storage::SynchronizationPackage &package = arg;
return package.imports.empty() && package.types.empty() && package.fileStatuses.empty()
&& package.updatedSourceIds.empty() && package.projectDatas.empty()
&& package.updatedFileStatusSourceIds.empty() && package.updatedProjectSourceIds.empty();
}
class ProjectStorageUpdater : public testing::Test
{
public:
ProjectStorageUpdater()
{
ON_CALL(fileSystemMock, fileStatus(Eq(qmltypesPathSourceId)))
.WillByDefault(Return(FileStatus{qmltypesPathSourceId, 21, 421}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypesPathSourceId)))
.WillByDefault(Return(FileStatus{qmltypesPathSourceId, 2, 421}));
ON_CALL(fileSystemMock, fileStatus(Eq(qmltypes2PathSourceId)))
.WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 21, 421}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypes2PathSourceId)))
.WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 2, 421}));
ON_CALL(fileSystemMock, fileStatus(Eq(qmlDirPathSourceId)))
.WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 421}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDirPathSourceId)))
.WillByDefault(Return(FileStatus{qmlDirPathSourceId, 2, 421}));
ON_CALL(projectManagerMock, qtQmlDirs()).WillByDefault(Return(QStringList{"/path/qmldir"}));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir"))))
.WillByDefault(Return(qmldirContent));
ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId1)))
.WillByDefault(Return(FileStatus{qmlDocumentSourceId1, 22, 12}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId1)))
.WillByDefault(Return(FileStatus{qmlDocumentSourceId1, 22, 2}));
ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId2)))
.WillByDefault(Return(FileStatus{qmlDocumentSourceId2, 22, 13}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId2)))
.WillByDefault(Return(FileStatus{qmlDocumentSourceId2, 22, 2}));
ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId3)))
.WillByDefault(Return(FileStatus{qmlDocumentSourceId3, 22, 14}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId3)))
.WillByDefault(Return(FileStatus{qmlDocumentSourceId3, 22, 2}));
ON_CALL(projectStorageMock, moduleId(Eq("Example"))).WillByDefault(Return(exampleModuleId));
ON_CALL(projectStorageMock, moduleId(Eq("Qml"))).WillByDefault(Return(qmlModuleId));
firstType.prototype = Storage::ImportedType{"Object"};
secondType.prototype = Storage::ImportedType{"Object2"};
thirdType.prototype = Storage::ImportedType{"Object3"};
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.qml"))))
.WillByDefault(Return(qmlDocument1));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.2.qml"))))
.WillByDefault(Return(qmlDocument2));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/Second.qml"))))
.WillByDefault(Return(qmlDocument3));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes"))))
.WillByDefault(Return(qmltypes1));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example2.qmltypes"))))
.WillByDefault(Return(qmltypes2));
ON_CALL(qmlDocumentParserMock, parse(qmlDocument1, _)).WillByDefault([&](auto, auto &imports) {
imports.push_back(import1);
return firstType;
});
ON_CALL(qmlDocumentParserMock, parse(qmlDocument2, _)).WillByDefault([&](auto, auto &imports) {
imports.push_back(import2);
return secondType;
});
ON_CALL(qmlDocumentParserMock, parse(qmlDocument3, _)).WillByDefault([&](auto, auto &imports) {
imports.push_back(import3);
return thirdType;
});
ON_CALL(qmlTypesParserMock, parse(Eq(qmltypes1), _, _, _))
.WillByDefault([&](auto, auto &imports, auto &types, auto) {
types.push_back(objectType);
imports.push_back(import4);
});
ON_CALL(qmlTypesParserMock, parse(Eq(qmltypes2), _, _, _))
.WillByDefault([&](auto, auto &imports, auto &types, auto) {
types.push_back(itemType);
imports.push_back(import5);
});
}
protected:
NiceMock<ProjectManagerMock> projectManagerMock;
NiceMock<FileSystemMock> fileSystemMock;
NiceMock<ProjectStorageMock> projectStorageMock;
NiceMock<QmlTypesParserMock> qmlTypesParserMock;
NiceMock<QmlDocumentParserMock> qmlDocumentParserMock;
QmlDesigner::FileStatusCache fileStatusCache{fileSystemMock};
Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
QmlDesigner::ProjectStorage<Sqlite::Database> storage{database, database.isInitialized()};
QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage<Sqlite::Database>> sourcePathCache{
storage};
QmlDesigner::ProjectUpdater updater{projectManagerMock,
fileSystemMock,
projectStorageMock,
fileStatusCache,
sourcePathCache,
qmlDocumentParserMock,
qmlTypesParserMock};
SourceId qmltypesPathSourceId = sourcePathCache.sourceId("/path/example.qmltypes");
SourceId qmltypes2PathSourceId = sourcePathCache.sourceId("/path/example2.qmltypes");
SourceId qmlDirPathSourceId = sourcePathCache.sourceId("/path/qmldir");
SourceId qmlDocumentSourceId1 = sourcePathCache.sourceId("/path/First.qml");
SourceId qmlDocumentSourceId2 = sourcePathCache.sourceId("/path/First.2.qml");
SourceId qmlDocumentSourceId3 = sourcePathCache.sourceId("/path/Second.qml");
ModuleId qmlModuleId{storage.moduleId("Qml")};
ModuleId exampleModuleId{storage.moduleId("Example")};
Storage::Type objectType{"QObject",
Storage::NativeType{},
Storage::TypeAccessSemantics::Reference,
qmltypesPathSourceId,
{Storage::ExportedType{exampleModuleId, "Object"},
Storage::ExportedType{exampleModuleId, "Obj"}}};
Storage::Type itemType{"QItem",
Storage::NativeType{},
Storage::TypeAccessSemantics::Reference,
qmltypes2PathSourceId,
{Storage::ExportedType{exampleModuleId, "Item"}}};
QString qmlDocument1{"First{}"};
QString qmlDocument2{"Second{}"};
QString qmlDocument3{"Third{}"};
Storage::Type firstType;
Storage::Type secondType;
Storage::Type thirdType;
Storage::Import import1{qmlModuleId, Storage::Version{2, 3}, qmlDocumentSourceId1};
Storage::Import import2{qmlModuleId, Storage::Version{}, qmlDocumentSourceId2};
Storage::Import import3{qmlModuleId, Storage::Version{2}, qmlDocumentSourceId3};
Storage::Import import4{qmlModuleId, Storage::Version{2, 3}, qmltypesPathSourceId};
Storage::Import import5{qmlModuleId, Storage::Version{2, 3}, qmltypes2PathSourceId};
QString qmldirContent{"module Example\ntypeinfo example.qmltypes\n"};
QString qmltypes1{"Module {\ndependencies: [module1]}"};
QString qmltypes2{"Module {\ndependencies: [module2]}"};
};
TEST_F(ProjectStorageUpdater, GetContentForQmlDirPathsIfFileStatusIsDifferent)
{
SourceId qmlDir1PathSourceId = sourcePathCache.sourceId("/path/one/qmldir");
SourceId qmlDir2PathSourceId = sourcePathCache.sourceId("/path/two/qmldir");
SourceId qmlDir3PathSourceId = sourcePathCache.sourceId("/path/three/qmldir");
ON_CALL(projectManagerMock, qtQmlDirs())
.WillByDefault(
Return(QStringList{"/path/one/qmldir", "/path/two/qmldir", "/path/three/qmldir"}));
ON_CALL(fileSystemMock, fileStatus(_)).WillByDefault([](auto sourceId) {
return FileStatus{sourceId, 21, 421};
});
ON_CALL(projectStorageMock, fetchFileStatus(_)).WillByDefault([](auto sourceId) {
return FileStatus{sourceId, 2, 421};
});
ON_CALL(fileSystemMock, fileStatus(Eq(qmlDir3PathSourceId)))
.WillByDefault(Return(FileStatus{qmlDir3PathSourceId, 21, 421}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDir3PathSourceId)))
.WillByDefault(Return(FileStatus{qmlDir3PathSourceId, 21, 421}));
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/one/qmldir"))));
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/two/qmldir"))));
updater.update();
}
TEST_F(ProjectStorageUpdater, RequestFileStatusFromFileSystem)
{
EXPECT_CALL(fileSystemMock, fileStatus(Ne(qmlDirPathSourceId))).Times(AnyNumber());
EXPECT_CALL(fileSystemMock, fileStatus(Eq(qmlDirPathSourceId)));
updater.update();
}
TEST_F(ProjectStorageUpdater, GetContentForQmlTypes)
{
QString qmldir{R"(module Example
typeinfo example.qmltypes)"};
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir"))))
.WillRepeatedly(Return(qmldir));
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes"))));
updater.update();
}
TEST_F(ProjectStorageUpdater, GetContentForQmlTypesIfProjectStorageFileStatusIsInvalid)
{
QString qmldir{R"(module Example
typeinfo example.qmltypes)"};
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir"))))
.WillRepeatedly(Return(qmldir));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypesPathSourceId)))
.WillByDefault(Return(FileStatus{}));
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes"))));
updater.update();
}
TEST_F(ProjectStorageUpdater, DontGetContentForQmlTypesIfFileSystemFileStatusIsInvalid)
{
QString qmldir{R"(module Example
typeinfo example.qmltypes)"};
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir"))))
.WillRepeatedly(Return(qmldir));
ON_CALL(fileSystemMock, fileStatus(Eq(qmltypesPathSourceId))).WillByDefault(Return(FileStatus{}));
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes")))).Times(0);
updater.update();
}
TEST_F(ProjectStorageUpdater, ParseQmlTypes)
{
QString qmldir{R"(module Example
typeinfo example.qmltypes
typeinfo example2.qmltypes)"};
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
QString qmltypes{"Module {\ndependencies: []}"};
QString qmltypes2{"Module {\ndependencies: [foo]}"};
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes"))))
.WillByDefault(Return(qmltypes));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example2.qmltypes"))))
.WillByDefault(Return(qmltypes2));
EXPECT_CALL(qmlTypesParserMock, parse(qmltypes, _, _, _));
EXPECT_CALL(qmlTypesParserMock, parse(qmltypes2, _, _, _));
updater.update();
}
TEST_F(ProjectStorageUpdater, SynchronizeIsEmptyForNoChange)
{
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypesPathSourceId)))
.WillByDefault(Return(FileStatus{qmltypesPathSourceId, 21, 421}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypes2PathSourceId)))
.WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 21, 421}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDirPathSourceId)))
.WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 421}));
EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty()));
updater.update();
}
TEST_F(ProjectStorageUpdater, SynchronizeQmlTypes)
{
Storage::Import import{qmlModuleId, Storage::Version{2, 3}, qmltypesPathSourceId};
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) {
types.push_back(objectType);
imports.push_back(import);
});
EXPECT_CALL(projectStorageMock, moduleId(Eq("Example")));
EXPECT_CALL(projectStorageMock,
synchronize(
AllOf(Field(&SynchronizationPackage::imports, ElementsAre(import)),
Field(&SynchronizationPackage::types, ElementsAre(Eq(objectType))),
Field(&SynchronizationPackage::updatedSourceIds,
UnorderedElementsAre(qmlDirPathSourceId, qmltypesPathSourceId)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 421),
IsFileStatus(qmltypesPathSourceId, 21, 421))),
Field(&SynchronizationPackage::projectDatas,
UnorderedElementsAre(IsProjectData(qmlDirPathSourceId,
qmltypesPathSourceId,
exampleModuleId,
FileType::QmlTypes))),
Field(&SynchronizationPackage::updatedProjectSourceIds,
UnorderedElementsAre(qmlDirPathSourceId)))));
updater.update();
}
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 &, auto &types, auto) { types.push_back(objectType); });
ON_CALL(fileSystemMock, fileStatus(Eq(qmltypesPathSourceId)))
.WillByDefault(Return(FileStatus{qmltypesPathSourceId, 2, 421}));
ON_CALL(fileSystemMock, fileStatus(Eq(qmltypes2PathSourceId)))
.WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 2, 421}));
ON_CALL(fileSystemMock, fileStatus(Eq(qmlDirPathSourceId)))
.WillByDefault(Return(FileStatus{qmlDirPathSourceId, 2, 421}));
EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty()));
updater.update();
}
TEST_F(ProjectStorageUpdater, GetContentForQmlDocuments)
{
QString qmldir{"module Example\nFirstType 1.0 First.qml\nFirstTypeV2 2.2 "
"First.2.qml\nSecondType 2.1 OldSecond.qml\nSecondType 2.2 Second.qml\n"};
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir"))))
.WillRepeatedly(Return(qmldir));
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.qml"))));
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.2.qml"))));
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/Second.qml"))));
updater.update();
}
TEST_F(ProjectStorageUpdater, ParseQmlDocuments)
{
QString qmldir{R"(module Example
FirstType 1.0 First.qml
FirstTypeV2 2.2 First.2.qml
SecondType 2.2 Second.qml)"};
QString qmlDocument1{"First{}"};
QString qmlDocument2{"Second{}"};
QString qmlDocument3{"Third{}"};
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.qml"))))
.WillByDefault(Return(qmlDocument1));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.2.qml"))))
.WillByDefault(Return(qmlDocument2));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/Second.qml"))))
.WillByDefault(Return(qmlDocument3));
EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument1, _));
EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument2, _));
EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument3, _));
updater.update();
}
TEST_F(ProjectStorageUpdater, ParseQmlDocumentsWithNonExistingQmlDocumentThrows)
{
QString qmldir{R"(module Example
NonexitingType 1.0 NonexitingType.qml)"};
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
ASSERT_THROW(updater.update(), QmlDesigner::CannotParseQmlDocumentFile);
}
TEST_F(ProjectStorageUpdater, SynchronizeQmlDocuments)
{
QString qmldir{R"(module Example
FirstType 1.0 First.qml
FirstType 2.2 First.2.qml
SecondType 2.2 Second.qml)"};
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
EXPECT_CALL(
projectStorageMock,
synchronize(AllOf(
Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2, import3)),
Field(&SynchronizationPackage::types,
UnorderedElementsAre(
AllOf(IsStorageType("First.qml",
Storage::ImportedType{"Object"},
TypeAccessSemantics::Reference,
qmlDocumentSourceId1,
Storage::ChangeLevel::Full),
Field(&Storage::Type::exportedTypes,
ElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0)))),
AllOf(IsStorageType("First.2.qml",
Storage::ImportedType{"Object2"},
TypeAccessSemantics::Reference,
qmlDocumentSourceId2,
Storage::ChangeLevel::Full),
Field(&Storage::Type::exportedTypes,
ElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2)))),
AllOf(IsStorageType("Second.qml",
Storage::ImportedType{"Object3"},
TypeAccessSemantics::Reference,
qmlDocumentSourceId3,
Storage::ChangeLevel::Full),
Field(&Storage::Type::exportedTypes,
ElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2)))))),
Field(&SynchronizationPackage::updatedSourceIds,
UnorderedElementsAre(qmlDirPathSourceId,
qmlDocumentSourceId1,
qmlDocumentSourceId2,
qmlDocumentSourceId3)),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDirPathSourceId,
qmlDocumentSourceId1,
qmlDocumentSourceId2,
qmlDocumentSourceId3)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 421),
IsFileStatus(qmlDocumentSourceId1, 22, 12),
IsFileStatus(qmlDocumentSourceId2, 22, 13),
IsFileStatus(qmlDocumentSourceId3, 22, 14))),
Field(&SynchronizationPackage::updatedProjectSourceIds,
UnorderedElementsAre(qmlDirPathSourceId)),
Field(&SynchronizationPackage::projectDatas,
UnorderedElementsAre(IsProjectData(qmlDirPathSourceId,
qmlDocumentSourceId1,
exampleModuleId,
FileType::QmlDocument),
IsProjectData(qmlDirPathSourceId,
qmlDocumentSourceId2,
exampleModuleId,
FileType::QmlDocument),
IsProjectData(qmlDirPathSourceId,
qmlDocumentSourceId3,
exampleModuleId,
FileType::QmlDocument))))));
updater.update();
}
TEST_F(ProjectStorageUpdater, SynchronizeRemoved)
{
QString qmldir{R"(module Example
FirstType 1.0 First.qml
FirstType 2.2 First.2.qml
typeinfo example.qmltypes
typeinfo example2.qmltypes
)"};
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId2)))
.WillByDefault(Return(FileStatus{qmlDocumentSourceId2, 22, 13}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypes2PathSourceId)))
.WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 21, 421}));
ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId)))
.WillByDefault(Return(QmlDesigner::Storage::ProjectDatas{
{qmlDirPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{qmlDirPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
{qmlDirPathSourceId, qmlDocumentSourceId1, exampleModuleId, FileType::QmlDocument},
{qmlDirPathSourceId, qmlDocumentSourceId2, exampleModuleId, FileType::QmlDocument},
{qmlDirPathSourceId, qmlDocumentSourceId3, exampleModuleId, FileType::QmlDocument}}));
EXPECT_CALL(
projectStorageMock,
synchronize(AllOf(
Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import4)),
Field(&SynchronizationPackage::types,
UnorderedElementsAre(
Eq(objectType),
AllOf(IsStorageType("First.qml",
Storage::ImportedType{"Object"},
TypeAccessSemantics::Reference,
qmlDocumentSourceId1,
Storage::ChangeLevel::Full),
Field(&Storage::Type::exportedTypes,
ElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0)))),
AllOf(IsStorageType("First.2.qml",
Storage::NativeType{},
TypeAccessSemantics::Reference,
qmlDocumentSourceId2,
Storage::ChangeLevel::Minimal),
Field(&Storage::Type::exportedTypes,
ElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2)))))),
Field(&SynchronizationPackage::updatedSourceIds,
UnorderedElementsAre(qmlDirPathSourceId,
qmltypesPathSourceId,
qmlDocumentSourceId1,
qmlDocumentSourceId2,
qmlDocumentSourceId3)),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDirPathSourceId,
qmlDocumentSourceId1,
qmltypesPathSourceId,
qmlDocumentSourceId3)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 421),
IsFileStatus(qmlDocumentSourceId1, 22, 12),
IsFileStatus(qmltypesPathSourceId, 21, 421))),
Field(&SynchronizationPackage::updatedProjectSourceIds,
UnorderedElementsAre(qmlDirPathSourceId)),
Field(&SynchronizationPackage::projectDatas,
UnorderedElementsAre(IsProjectData(qmlDirPathSourceId,
qmlDocumentSourceId1,
exampleModuleId,
FileType::QmlDocument),
IsProjectData(qmlDirPathSourceId,
qmlDocumentSourceId2,
exampleModuleId,
FileType::QmlDocument),
IsProjectData(qmlDirPathSourceId,
qmltypesPathSourceId,
exampleModuleId,
FileType::QmlTypes),
IsProjectData(qmlDirPathSourceId,
qmltypes2PathSourceId,
exampleModuleId,
FileType::QmlTypes))))));
updater.update();
}
TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsDontUpdateIfUpToDate)
{
QString qmldir{R"(module Example
FirstType 1.0 First.qml
FirstType 2.2 First.2.qml
SecondType 2.2 Second.qml)"};
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId3)))
.WillByDefault(Return(FileStatus{qmlDocumentSourceId3, 22, 14}));
EXPECT_CALL(
projectStorageMock,
synchronize(AllOf(
Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2)),
Field(&SynchronizationPackage::types,
UnorderedElementsAre(
AllOf(IsStorageType("First.qml",
Storage::ImportedType{"Object"},
TypeAccessSemantics::Reference,
qmlDocumentSourceId1,
Storage::ChangeLevel::Full),
Field(&Storage::Type::exportedTypes,
ElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0)))),
AllOf(IsStorageType("First.2.qml",
Storage::ImportedType{"Object2"},
TypeAccessSemantics::Reference,
qmlDocumentSourceId2,
Storage::ChangeLevel::Full),
Field(&Storage::Type::exportedTypes,
ElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2)))),
AllOf(IsStorageType("Second.qml",
Storage::NativeType{},
TypeAccessSemantics::Reference,
qmlDocumentSourceId3,
Storage::ChangeLevel::Minimal),
Field(&Storage::Type::exportedTypes,
ElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2)))))),
Field(&SynchronizationPackage::updatedSourceIds,
UnorderedElementsAre(qmlDirPathSourceId,
qmlDocumentSourceId1,
qmlDocumentSourceId2,
qmlDocumentSourceId3)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 421),
IsFileStatus(qmlDocumentSourceId1, 22, 12),
IsFileStatus(qmlDocumentSourceId2, 22, 13))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)),
Field(&SynchronizationPackage::updatedProjectSourceIds,
UnorderedElementsAre(qmlDirPathSourceId)),
Field(&SynchronizationPackage::projectDatas,
UnorderedElementsAre(IsProjectData(qmlDirPathSourceId,
qmlDocumentSourceId1,
exampleModuleId,
FileType::QmlDocument),
IsProjectData(qmlDirPathSourceId,
qmlDocumentSourceId2,
exampleModuleId,
FileType::QmlDocument),
IsProjectData(qmlDirPathSourceId,
qmlDocumentSourceId3,
exampleModuleId,
FileType::QmlDocument))))));
updater.update();
}
TEST_F(ProjectStorageUpdater, UpdateQmldirDocuments)
{
QString qmldir{R"(module Example
FirstType 1.1 First.qml
FirstType 2.2 First.2.qml
SecondType 2.2 Second.qml)"};
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId3)))
.WillByDefault(Return(FileStatus{qmlDocumentSourceId3, 22, 14}));
updater.pathsWithIdsChanged({});
}
TEST_F(ProjectStorageUpdater, AddSourceIdForForInvalidQmldirFileStatus)
{
ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId)))
.WillByDefault(Return(QmlDesigner::Storage::ProjectDatas{
{qmlDirPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{qmlDirPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}));
ON_CALL(fileSystemMock, fileStatus(Eq(qmlDirPathSourceId))).WillByDefault(Return(FileStatus{}));
EXPECT_CALL(projectStorageMock,
synchronize(AllOf(Field(&SynchronizationPackage::imports, IsEmpty()),
Field(&SynchronizationPackage::types, IsEmpty()),
Field(&SynchronizationPackage::updatedSourceIds,
UnorderedElementsAre(qmlDirPathSourceId,
qmltypesPathSourceId,
qmltypes2PathSourceId)),
Field(&SynchronizationPackage::fileStatuses, IsEmpty()),
Field(&SynchronizationPackage::updatedFileStatusSourceIds, IsEmpty()),
Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
updater.update();
}
TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileHasNotChanged)
{
ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId)))
.WillByDefault(Return(QmlDesigner::Storage::ProjectDatas{
{qmlDirPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{qmlDirPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
{qmlDirPathSourceId, qmlDocumentSourceId1, exampleModuleId, FileType::QmlDocument},
{qmlDirPathSourceId, qmlDocumentSourceId2, exampleModuleId, FileType::QmlDocument}}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDirPathSourceId)))
.WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 421}));
EXPECT_CALL(projectStorageMock,
synchronize(
AllOf(Field(&SynchronizationPackage::imports,
UnorderedElementsAre(import1, import2, import4, import5)),
Field(&SynchronizationPackage::types,
UnorderedElementsAre(
Eq(objectType),
Eq(itemType),
AllOf(IsStorageType("First.qml",
Storage::ImportedType{"Object"},
TypeAccessSemantics::Reference,
qmlDocumentSourceId1,
Storage::ChangeLevel::ExcludeExportedTypes),
Field(&Storage::Type::exportedTypes, IsEmpty())),
AllOf(IsStorageType("First.2.qml",
Storage::ImportedType{"Object2"},
TypeAccessSemantics::Reference,
qmlDocumentSourceId2,
Storage::ChangeLevel::ExcludeExportedTypes),
Field(&Storage::Type::exportedTypes, IsEmpty())))),
Field(&SynchronizationPackage::updatedSourceIds,
UnorderedElementsAre(qmltypesPathSourceId,
qmltypes2PathSourceId,
qmlDocumentSourceId1,
qmlDocumentSourceId2)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 21, 421),
IsFileStatus(qmltypes2PathSourceId, 21, 421),
IsFileStatus(qmlDocumentSourceId1, 22, 12),
IsFileStatus(qmlDocumentSourceId2, 22, 13))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmltypesPathSourceId,
qmltypes2PathSourceId,
qmlDocumentSourceId1,
qmlDocumentSourceId2)),
Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
updater.update();
}
TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileHasNotChangedAndSomeUpdatedFiles)
{
ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId)))
.WillByDefault(Return(QmlDesigner::Storage::ProjectDatas{
{qmlDirPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
{qmlDirPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
{qmlDirPathSourceId, qmlDocumentSourceId1, exampleModuleId, FileType::QmlDocument},
{qmlDirPathSourceId, qmlDocumentSourceId2, exampleModuleId, FileType::QmlDocument}}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDirPathSourceId)))
.WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 421}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypes2PathSourceId)))
.WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 21, 421}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId2)))
.WillByDefault(Return(FileStatus{qmlDocumentSourceId2, 22, 13}));
EXPECT_CALL(
projectStorageMock,
synchronize(
AllOf(Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import4)),
Field(&SynchronizationPackage::types,
UnorderedElementsAre(Eq(objectType),
AllOf(IsStorageType("First.qml",
Storage::ImportedType{"Object"},
TypeAccessSemantics::Reference,
qmlDocumentSourceId1,
Storage::ChangeLevel::ExcludeExportedTypes),
Field(&Storage::Type::exportedTypes, IsEmpty())))),
Field(&SynchronizationPackage::updatedSourceIds,
UnorderedElementsAre(qmltypesPathSourceId, qmlDocumentSourceId1)),
Field(&SynchronizationPackage::fileStatuses,
UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 21, 421),
IsFileStatus(qmlDocumentSourceId1, 22, 12))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmltypesPathSourceId, qmlDocumentSourceId1)),
Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
updater.update();
}
} // namespace