From a1d9a2cc586a641c94a9ce18e6a1ae87cdd07f3c Mon Sep 17 00:00:00 2001 From: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> Date: Mon, 8 Nov 2021 10:56:51 +0100 Subject: [PATCH] Asset Browser Tests (#4948) * Added AssetBrowser Tests Signed-off-by: igarri * Added Entries to test AssetBrowser Signed-off-by: igarri * Added Print info. Signed-off-by: igarri * Added more folders Signed-off-by: igarri * Added Asset Browser Tests for the Search View Signed-off-by: igarri * Fixed Entry creation Signed-off-by: igarri * Removed optimize Signed-off-by: igarri * Cleanup AssetBrowserModel Signed-off-by: igarri * RowCount made public Signed-off-by: igarri * Delegated entry creation to RootAssetBrowserEntry and added Code review feedback Signed-off-by: igarri * removed unused helper class and fixed demo tests Signed-off-by: igarri * Fixed bus connections Signed-off-by: igarri * Refactored test environment and added basic tests Signed-off-by: igarri * Applied some code review feedback and added basic tests Signed-off-by: igarri * fixed naming Signed-off-by: igarri * Refactored Tests Signed-off-by: igarri * removed pointer reset, now handled by the AssetBrowserComponent Signed-off-by: igarri * Fixed conversion unsigned-signed Signed-off-by: igarri * Cleaned includes Signed-off-by: igarri * fixed test setup Signed-off-by: igarri * Fixed unused variables Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> * Added printer function Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> * cleaned up code Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> * Added Test to check the correctness of the setup Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> * Fixed basic tests Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> * Fixed Tests Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> --- .../AssetBrowser/AssetBrowserTableModel.h | 4 +- .../Tests/UI/AssetBrowserTests.cpp | 344 ++++++++++++++++++ .../Tests/aztoolsframeworktests_files.cmake | 1 + 3 files changed, 347 insertions(+), 2 deletions(-) create mode 100644 Code/Framework/AzToolsFramework/Tests/UI/AssetBrowserTests.cpp diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index 4048156b60..ee1ec49456 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -40,9 +40,9 @@ namespace AzToolsFramework QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QModelIndex parent(const QModelIndex& child) const override; QModelIndex sibling(int row, int column, const QModelIndex& idx) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; protected: - int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override; //////////////////////////////////////////////////////////////////// @@ -55,7 +55,7 @@ namespace AzToolsFramework private slots: void SourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); private: - AZ::u64 m_numberOfItemsDisplayed = 0; + AZ::u64 m_numberOfItemsDisplayed = 50; int m_displayedItemsCounter = 0; QPointer m_filterModel; QMap m_indexMap; diff --git a/Code/Framework/AzToolsFramework/Tests/UI/AssetBrowserTests.cpp b/Code/Framework/AzToolsFramework/Tests/UI/AssetBrowserTests.cpp new file mode 100644 index 0000000000..ae5ea5f0ae --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/UI/AssetBrowserTests.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + // Test fixture for the AssetBrowser model that uses a QAbstractItemModelTester to validate the state of the model + // when QAbstractItemModel signals fire. Tests will exit with a fatal error if an invalid state is detected. + class AssetBrowserTest + : public ToolsApplicationFixture + , public testing::WithParamInterface + { + protected: + enum class AssetEntryType + { + Root, + Folder, + Source, + Product + }; + + enum class FolderType + { + Root, + File + }; + + void SetUpEditorFixtureImpl() override; + void TearDownEditorFixtureImpl() override; + + //! Creates a Mock Scan Folder + void AddScanFolder(AZ::s64 folderID, AZStd::string folderPath, AZStd::string displayName, FolderType folderType = FolderType::File); + + //! Creates a Source entry from a mock file + AZ::Uuid CreateSourceEntry( + AZ::s64 fileID, AZ::s64 parentFolderID, AZStd::string filename, AssetEntryType sourceType = AssetEntryType::Source); + + //! Creates a product from a given sourceEntry + void CreateProduct(AZ::s64 productID, AZ::Uuid sourceUuid, AZStd::string productName); + + void SetupAssetBrowser(); + void PrintModel(const QAbstractItemModel* model, AZStd::function printer); + QModelIndex GetModelIndex(const QAbstractItemModel* model, int targetDepth, int row = 0); + AZStd::shared_ptr GetRootEntry(); + AZStd::vector GetVectorFromFormattedString(const QString& formattedString); + + protected: + QString m_assetBrowserHierarchy = QString(); + + AZStd::unique_ptr m_searchWidget; + AZStd::unique_ptr m_assetBrowserComponent; + + AZStd::unique_ptr m_filterModel; + AZStd::unique_ptr m_tableModel; + + AZStd::unique_ptr m_modelTesterAssetBrowser; + AZStd::unique_ptr m_modelTesterFilterModel; + AZStd::unique_ptr m_modelTesterTableModel; + + QVector m_folderIds = { 13, 14, 15 }; + QVector m_sourceIDs = { 1, 2, 3, 4, 5 }; + QVector m_productIDs = { 1, 2, 3, 4, 5 }; + }; + + void AssetBrowserTest::SetUpEditorFixtureImpl() + { + GetApplication()->RegisterComponentDescriptor(AzToolsFramework::EditorEntityContextComponent::CreateDescriptor()); + + m_assetBrowserComponent = AZStd::make_unique(); + m_assetBrowserComponent->Activate(); + + m_filterModel = AZStd::make_unique(); + m_tableModel = AZStd::make_unique(); + + m_filterModel->setSourceModel(m_assetBrowserComponent->GetAssetBrowserModel()); + m_tableModel->setSourceModel(m_filterModel.get()); + + m_modelTesterAssetBrowser = AZStd::make_unique(m_assetBrowserComponent->GetAssetBrowserModel()); + m_modelTesterFilterModel = AZStd::make_unique(m_filterModel.get()); + m_modelTesterTableModel = AZStd::make_unique(m_tableModel.get()); + m_searchWidget = AZStd::make_unique(); + + // Setup String filters + m_searchWidget->Setup(true, true); + m_filterModel->SetFilter(m_searchWidget->GetFilter()); + + SetupAssetBrowser(); + } + + void AssetBrowserTest::TearDownEditorFixtureImpl() + { + m_modelTesterAssetBrowser.reset(); + m_modelTesterFilterModel.reset(); + m_modelTesterTableModel.reset(); + + m_tableModel.reset(); + m_filterModel.reset(); + m_assetBrowserComponent->Deactivate(); + + m_assetBrowserComponent.reset(); + m_searchWidget.reset(); + } + + void AssetBrowserTest::AddScanFolder( + AZ::s64 folderID, AZStd::string folderPath, AZStd::string displayName, FolderType folderType /*= FolderType::File*/) + { + AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry scanFolder = AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry(); + scanFolder.m_scanFolderID = folderID; + scanFolder.m_scanFolder = folderPath; + scanFolder.m_displayName = displayName; + scanFolder.m_isRoot = folderType == FolderType::Root; + GetRootEntry()->AddScanFolder(scanFolder); + } + + AZ::Uuid AssetBrowserTest::CreateSourceEntry( + AZ::s64 fileID, AZ::s64 parentFolderID, AZStd::string filename, AssetEntryType sourceType /*= AssetEntryType::Source*/) + { + AzToolsFramework::AssetDatabase::FileDatabaseEntry entry = AzToolsFramework::AssetDatabase::FileDatabaseEntry(); + entry.m_scanFolderPK = parentFolderID; + entry.m_fileID = fileID; + entry.m_fileName = filename; + entry.m_isFolder = sourceType == AssetEntryType::Folder; + GetRootEntry()->AddFile(entry); + + if (!entry.m_isFolder) + { + AzToolsFramework::AssetBrowser::SourceWithFileID entrySource = AzToolsFramework::AssetBrowser::SourceWithFileID(); + entrySource.first = entry.m_fileID; + entrySource.second = AzToolsFramework::AssetDatabase::SourceDatabaseEntry(); + entrySource.second.m_scanFolderPK = parentFolderID; + entrySource.second.m_sourceName = filename; + entrySource.second.m_sourceID = fileID; + entrySource.second.m_sourceGuid = AZ::Uuid::CreateRandom(); + + GetRootEntry()->AddSource(entrySource); + + return entrySource.second.m_sourceGuid; + } + + return AZ::Uuid::CreateNull(); + } + + void AssetBrowserTest::CreateProduct(AZ::s64 productID, AZ::Uuid sourceUuid, AZStd::string productName) + { + AzToolsFramework::AssetBrowser::ProductWithUuid product = AzToolsFramework::AssetBrowser::ProductWithUuid(); + product.first = sourceUuid; + product.second = AzToolsFramework::AssetDatabase::ProductDatabaseEntry(); + product.second.m_productID = productID; + + product.second.m_subID = aznumeric_cast(productID); + product.second.m_productName = productName; + + GetRootEntry()->AddProduct(product); + } + + void AssetBrowserTest::SetupAssetBrowser() + { + // RootEntries : 1 | Folders : 4 | SourceEntries : 5 | ProductEntries : 9 + m_assetBrowserHierarchy = R"( + D: + \ + dev + o3de + GameProject + Assets + Source_1 + Product_1_1 + Product_1_0 + Source_0 + Product_0_3 + Product_0_2 + Product_0_1 + Product_0_0 + Scripts + Source_3 + Source_2 + Product_2_2 + Product_2_1 + Product_2_0 + Misc + Source_4 + Product_4_2 + Product_4_1 + Product_4_0 )"; + + namespace AzAssetBrowser = AzToolsFramework::AssetBrowser; + + AddScanFolder(m_folderIds.at(2), "D:/dev/o3de/GameProject/Misc", "Misc"); + AZ::Uuid sourceUuid_4 = CreateSourceEntry(m_sourceIDs.at(4), m_folderIds.at(2), "Source_4"); + CreateProduct(m_productIDs.at(0), sourceUuid_4, "Product_4_0"); + CreateProduct(m_productIDs.at(1), sourceUuid_4, "Product_4_1"); + CreateProduct(m_productIDs.at(2), sourceUuid_4, "Product_4_2"); + + AddScanFolder(m_folderIds.at(1), "D:/dev/o3de/GameProject/Scripts", "Scripts"); + + AZ::Uuid sourceUuid_2 = CreateSourceEntry(m_sourceIDs.at(2), m_folderIds.at(1), "Source_2"); + CreateProduct(m_productIDs.at(0), sourceUuid_2, "Product_2_0"); + CreateProduct(m_productIDs.at(1), sourceUuid_2, "Product_2_1"); + CreateProduct(m_productIDs.at(2), sourceUuid_2, "Product_2_2"); + + CreateSourceEntry(m_sourceIDs.at(3), m_folderIds.at(1), "Source_3"); + + AddScanFolder(m_folderIds.at(0), "D:/dev/o3de/GameProject/Assets", "Assets"); + + AZ::Uuid sourceUuid_0 = CreateSourceEntry(m_sourceIDs.at(0), m_folderIds.at(0), "Source_0"); + CreateProduct(m_productIDs.at(0), sourceUuid_0, "Product_0_0"); + CreateProduct(m_productIDs.at(1), sourceUuid_0, "Product_0_1"); + CreateProduct(m_productIDs.at(2), sourceUuid_0, "Product_0_2"); + CreateProduct(m_productIDs.at(3), sourceUuid_0, "Product_0_3"); + + AZ::Uuid sourceUuid_1 = CreateSourceEntry(m_sourceIDs.at(1), m_folderIds.at(0), "Source_1"); + CreateProduct(m_productIDs.at(0), sourceUuid_1, "Product_1_0"); + CreateProduct(m_productIDs.at(1), sourceUuid_1, "Product_1_1"); + } + + void AssetBrowserTest::PrintModel(const QAbstractItemModel* model, AZStd::function printer) + { + AZStd::deque> indices; + indices.push_back({ model->index(0, 0), 0 }); + while (!indices.empty()) + { + auto [index, depth] = indices.front(); + indices.pop_front(); + + QString indentString; + for (int i = 0; i < depth; ++i) + { + indentString += " "; + } + const QString message = indentString + index.data(Qt::DisplayRole).toString(); + printer(message); + + for (int i = 0; i < model->rowCount(index); ++i) + { + indices.emplace_front(model->index(i, 0, index), depth + 1); + } + } + } + + QModelIndex AssetBrowserTest::GetModelIndex(const QAbstractItemModel* model, int targetDepth, int row) + { + AZStd::deque> indices; + indices.push_back({ model->index(0, 0), 0 }); + while (!indices.empty()) + { + auto [index, depth] = indices.front(); + indices.pop_front(); + + for (int i = 0; i < model->rowCount(index); ++i) + { + if (depth + 1 == targetDepth && row == i) + { + return model->index(i, 0, index); + } + indices.emplace_front(model->index(i, 0, index), depth + 1); + } + } + return QModelIndex(); + } + + AZStd::shared_ptr AssetBrowserTest::GetRootEntry() + { + return m_assetBrowserComponent->GetAssetBrowserModel()->GetRootEntry(); + } + + AZStd::vector AssetBrowserTest::GetVectorFromFormattedString(const QString& formattedString) + { + AZStd::vector hierarchySections; + QStringList splittedList = formattedString.split('\n', Qt::SkipEmptyParts); + + for (auto& str : splittedList) + { + str.replace(" ", ""); + hierarchySections.push_back(str); + } + return hierarchySections; + } + + TEST_F(AssetBrowserTest, CheckCorrectNumberOfEntriesInTableView) + { + m_filterModel->FilterUpdatedSlotImmediate(); + const int tableViewRowcount = m_tableModel->rowCount(); + + // RowCount should be 17 -> 5 SourceEntries + 12 ProductEntries) + EXPECT_EQ(tableViewRowcount, 17); + } + + TEST_F(AssetBrowserTest, CheckCorrectNumberOfEntriesInTableViewAfterStringFilter) + { + /* + *-Source_1 + * | + * |-product_1_0 + * |-product_1_1 + * + * + * Matching entries = 3 + */ + + // Apply string filter + m_searchWidget->SetTextFilter(QString("source_1")); + m_filterModel->FilterUpdatedSlotImmediate(); + + const int tableViewRowcount = m_tableModel->rowCount(); + EXPECT_EQ(tableViewRowcount, 3); + } + + TEST_F(AssetBrowserTest, CheckScanFolderAddition) + { + EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 1); + const int newFolderId = 20; + AddScanFolder(newFolderId, "E:/TestFolder/TestFolder2", "TestFolder"); + + // Since the folder is empty it shouldn't be added to the model. + EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 1); + + CreateSourceEntry(123, newFolderId, "DummyFile"); + + // When we add a file to the folder it should be added to the model + EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 2); + } + +} // namespace UnitTest diff --git a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake index 008c09188b..d597c9b570 100644 --- a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake +++ b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake @@ -123,6 +123,7 @@ set(FILES UI/EntityIdQLineEditTests.cpp UI/EntityOutlinerTests.cpp UI/EntityPropertyEditorTests.cpp + UI/AssetBrowserTests.cpp UndoStack.cpp Viewport/ClusterTests.cpp Viewport/ViewportEditorModeTests.cpp