forked from qt-creator/qt-creator
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>
813 lines
42 KiB
C++
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
|