diff options
author | Sybren A. Stüvel <sybren@blender.org> | 2021-10-12 13:39:24 +0300 |
---|---|---|
committer | Sybren A. Stüvel <sybren@blender.org> | 2021-10-12 13:42:44 +0300 |
commit | a06435e43a6516207c617c2b4ad9961f865b66d8 (patch) | |
tree | d21e378f4c33f1d203cefeeca03dbefa81691158 /source/blender/blenkernel/intern/asset_catalog_test.cc | |
parent | b67a9373945d07700b378aaa8be06aa2fd6d2cb9 (diff) |
Asset Catalogs: undo stack for catalog edits
Add an undo stack for catalog edits. This only implements the backend,
no operators or UI yet.
A bunch of `this->xxx` has been replaced by `catalog_collection_->xxx`.
Things are getting a bit long, and the class is turning into a god
object; refactoring the class is tracked in T92114.
Reviewed By: Severin
Maniphest Tasks: T92047
Differential Revision: https://developer.blender.org/D12825
Diffstat (limited to 'source/blender/blenkernel/intern/asset_catalog_test.cc')
-rw-r--r-- | source/blender/blenkernel/intern/asset_catalog_test.cc | 187 |
1 files changed, 185 insertions, 2 deletions
diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index cf06638bcdd..3522ad80bdd 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -55,7 +55,7 @@ class TestableAssetCatalogService : public AssetCatalogService { AssetCatalogDefinitionFile *get_catalog_definition_file() { - return catalog_definition_file_.get(); + return AssetCatalogService::get_catalog_definition_file(); } void create_missing_catalogs() @@ -66,7 +66,7 @@ class TestableAssetCatalogService : public AssetCatalogService { int64_t count_catalogs_with_path(const CatalogFilePath &path) { int64_t count = 0; - for (auto &catalog_uptr : catalogs_.values()) { + for (auto &catalog_uptr : get_catalogs().values()) { if (catalog_uptr->path == path) { count++; } @@ -1054,4 +1054,187 @@ TEST_F(AssetCatalogTest, create_catalog_filter_for_unassigned_assets) EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE)); } +TEST_F(AssetCatalogTest, cat_collection_deep_copy__empty) +{ + const AssetCatalogCollection empty; + auto copy = empty.deep_copy(); + EXPECT_NE(&empty, copy.get()); +} + +class TestableAssetCatalogCollection : public AssetCatalogCollection { + public: + OwningAssetCatalogMap &get_catalogs() + { + return catalogs_; + } + OwningAssetCatalogMap &get_deleted_catalogs() + { + return deleted_catalogs_; + } + AssetCatalogDefinitionFile *get_catalog_definition_file() + { + return catalog_definition_file_.get(); + } + AssetCatalogDefinitionFile *allocate_catalog_definition_file() + { + catalog_definition_file_ = std::make_unique<AssetCatalogDefinitionFile>(); + return get_catalog_definition_file(); + } +}; + +TEST_F(AssetCatalogTest, cat_collection_deep_copy__nonempty_nocdf) +{ + TestableAssetCatalogCollection catcoll; + auto cat1 = std::make_unique<AssetCatalog>(UUID_POSES_RUZENA, "poses/Henrik", ""); + auto cat2 = std::make_unique<AssetCatalog>(UUID_POSES_RUZENA_FACE, "poses/Henrik/face", ""); + auto cat3 = std::make_unique<AssetCatalog>(UUID_POSES_RUZENA_HAND, "poses/Henrik/hands", ""); + cat3->flags.is_deleted = true; + + AssetCatalog *cat1_ptr = cat1.get(); + AssetCatalog *cat3_ptr = cat3.get(); + + catcoll.get_catalogs().add_new(cat1->catalog_id, std::move(cat1)); + catcoll.get_catalogs().add_new(cat2->catalog_id, std::move(cat2)); + catcoll.get_deleted_catalogs().add_new(cat3->catalog_id, std::move(cat3)); + + auto copy = catcoll.deep_copy(); + EXPECT_NE(&catcoll, copy.get()); + + TestableAssetCatalogCollection *testcopy = reinterpret_cast<TestableAssetCatalogCollection *>( + copy.get()); + + /* Test catalogs & deleted catalogs. */ + EXPECT_EQ(2, testcopy->get_catalogs().size()); + EXPECT_EQ(1, testcopy->get_deleted_catalogs().size()); + + ASSERT_TRUE(testcopy->get_catalogs().contains(UUID_POSES_RUZENA)); + ASSERT_TRUE(testcopy->get_catalogs().contains(UUID_POSES_RUZENA_FACE)); + ASSERT_TRUE(testcopy->get_deleted_catalogs().contains(UUID_POSES_RUZENA_HAND)); + + EXPECT_NE(nullptr, testcopy->get_catalogs().lookup(UUID_POSES_RUZENA)); + EXPECT_NE(cat1_ptr, testcopy->get_catalogs().lookup(UUID_POSES_RUZENA).get()) + << "AssetCatalogs should be actual copies."; + + EXPECT_NE(nullptr, testcopy->get_deleted_catalogs().lookup(UUID_POSES_RUZENA_HAND)); + EXPECT_NE(cat3_ptr, testcopy->get_deleted_catalogs().lookup(UUID_POSES_RUZENA_HAND).get()) + << "AssetCatalogs should be actual copies."; +} + +class TestableAssetCatalogDefinitionFile : public AssetCatalogDefinitionFile { + public: + Map<CatalogID, AssetCatalog *> get_catalogs() + { + return catalogs_; + } +}; + +TEST_F(AssetCatalogTest, cat_collection_deep_copy__nonempty_cdf) +{ + TestableAssetCatalogCollection catcoll; + auto cat1 = std::make_unique<AssetCatalog>(UUID_POSES_RUZENA, "poses/Henrik", ""); + auto cat2 = std::make_unique<AssetCatalog>(UUID_POSES_RUZENA_FACE, "poses/Henrik/face", ""); + auto cat3 = std::make_unique<AssetCatalog>(UUID_POSES_RUZENA_HAND, "poses/Henrik/hands", ""); + cat3->flags.is_deleted = true; + + AssetCatalog *cat1_ptr = cat1.get(); + AssetCatalog *cat2_ptr = cat2.get(); + AssetCatalog *cat3_ptr = cat3.get(); + + catcoll.get_catalogs().add_new(cat1->catalog_id, std::move(cat1)); + catcoll.get_catalogs().add_new(cat2->catalog_id, std::move(cat2)); + catcoll.get_deleted_catalogs().add_new(cat3->catalog_id, std::move(cat3)); + + AssetCatalogDefinitionFile *cdf = catcoll.allocate_catalog_definition_file(); + cdf->file_path = "path/to/somewhere.cats.txt"; + cdf->add_new(cat1_ptr); + cdf->add_new(cat2_ptr); + cdf->add_new(cat3_ptr); + + /* Test CDF remapping. */ + auto copy = catcoll.deep_copy(); + TestableAssetCatalogCollection *testable_copy = static_cast<TestableAssetCatalogCollection *>( + copy.get()); + + TestableAssetCatalogDefinitionFile *cdf_copy = static_cast<TestableAssetCatalogDefinitionFile *>( + testable_copy->get_catalog_definition_file()); + EXPECT_EQ(testable_copy->get_catalogs().lookup(UUID_POSES_RUZENA).get(), + cdf_copy->get_catalogs().lookup(UUID_POSES_RUZENA)) + << "AssetCatalog pointers should have been remapped to the copy."; + + EXPECT_EQ(testable_copy->get_deleted_catalogs().lookup(UUID_POSES_RUZENA_HAND).get(), + cdf_copy->get_catalogs().lookup(UUID_POSES_RUZENA_HAND)) + << "Deleted AssetCatalog pointers should have been remapped to the copy."; +} + +TEST_F(AssetCatalogTest, undo_redo_one_step) +{ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(); + + EXPECT_FALSE(service.is_undo_possbile()); + EXPECT_FALSE(service.is_redo_possbile()); + + service.create_catalog("some/catalog/path"); + EXPECT_FALSE(service.is_undo_possbile()) + << "Undo steps should be created explicitly, and not after creating any catalog."; + + service.store_undo_snapshot(); + const bUUID other_catalog_id = service.create_catalog("other/catalog/path")->catalog_id; + EXPECT_TRUE(service.is_undo_possbile()) + << "Undo should be possible after creating an undo snapshot."; + + // Undo the creation of the catalog. + service.undo(); + EXPECT_FALSE(service.is_undo_possbile()) + << "Undoing the only stored step should make it impossible to undo further."; + EXPECT_TRUE(service.is_redo_possbile()) << "Undoing a step should make redo possible."; + EXPECT_EQ(nullptr, service.find_catalog_by_path("other/catalog/path")) + << "Undone catalog should not exist after undo."; + EXPECT_NE(nullptr, service.find_catalog_by_path("some/catalog/path")) + << "First catalog should still exist after undo."; + EXPECT_FALSE(service.get_catalog_definition_file()->contains(other_catalog_id)) + << "The CDF should also not contain the undone catalog."; + + // Redo the creation of the catalog. + service.redo(); + EXPECT_TRUE(service.is_undo_possbile()) + << "Undoing and then redoing a step should make it possible to undo again."; + EXPECT_FALSE(service.is_redo_possbile()) + << "Undoing and then redoing a step should make redo impossible."; + EXPECT_NE(nullptr, service.find_catalog_by_path("other/catalog/path")) + << "Redone catalog should exist after redo."; + EXPECT_NE(nullptr, service.find_catalog_by_path("some/catalog/path")) + << "First catalog should still exist after redo."; + EXPECT_TRUE(service.get_catalog_definition_file()->contains(other_catalog_id)) + << "The CDF should contain the redone catalog."; +} + +TEST_F(AssetCatalogTest, undo_redo_more_complex) +{ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(); + + service.store_undo_snapshot(); + service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)->simple_name = "Edited simple name"; + + service.store_undo_snapshot(); + service.find_catalog(UUID_POSES_ELLIE)->path = "poselib/EllieWithEditedPath"; + + service.undo(); + service.undo(); + + service.store_undo_snapshot(); + service.find_catalog(UUID_POSES_ELLIE)->simple_name = "Ellie Simple"; + + EXPECT_FALSE(service.is_redo_possbile()) + << "After storing an undo snapshot, the redo buffer should be empty."; + EXPECT_TRUE(service.is_undo_possbile()) + << "After storing an undo snapshot, undoing should be possible"; + + EXPECT_EQ(service.find_catalog(UUID_POSES_ELLIE)->simple_name, "Ellie Simple"); /* Not undone. */ + EXPECT_EQ(service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)->simple_name, + "POSES_ELLIE WHITESPACE"); /* Undone. */ + EXPECT_EQ(service.find_catalog(UUID_POSES_ELLIE)->path, "character/Ellie/poselib"); /* Undone. */ +} + } // namespace blender::bke::tests |