#include #include #include #include "./core/tree.h" #include "./utils/log.h" namespace chrono = std::chrono; using namespace Splash; /*************/ TEST_CASE("Testing the basic functionnalities of the Tree") { Log::get().setVerbosity(Log::ERROR); Tree::Root tree; CHECK(tree.createBranchAt("/some_object") == true); CHECK(tree.createBranchAt("/some_object/some_other_object") == true); CHECK(tree.createBranchAt("/some_object/some_other_object") == false); CHECK(tree.removeBranchAt("/some_object") == true); CHECK(tree.removeBranchAt("/some_object") == false); CHECK(tree.createBranchAt("/some_object") == true); CHECK(tree.createLeafAt("/some_object/a_leaf") == true); CHECK(tree.createLeafAt("/some_object/a_leaf") == false); CHECK(tree.createBranchAt("/some/branch/hidden/in/tree") == true); CHECK(tree.hasBranchAt("/some/branch/hidden/in/tree") == true); CHECK(tree.createLeafAt("/some/leaf/hidden/in/tree") == true); CHECK(tree.hasLeafAt("/some/leaf/hidden/in/tree") == true); auto value = Values({1.0, "I've got a flying machine", false}); CHECK(tree.createLeafAt("/some_object/another_leaf", {1.0, "I've got a flying machine", false}) == true); Value leafValue; tree.getValueForLeafAt("/some_object/another_leaf", leafValue); CHECK(leafValue == value); value = {"No you don't"}; CHECK(tree.setValueForLeafAt("/some_object/another_leaf", value, chrono::system_clock::now())); tree.getValueForLeafAt("/some_object/another_leaf", leafValue); CHECK(leafValue == value); CHECK(tree.removeLeafAt("/some_object/a_leaf") == true); CHECK(tree.removeLeafAt("/some_object/a_leaf") == false); tree.createBranchAt("/random_branch"); tree.createBranchAt("/randomer_branch"); CHECK(tree.renameBranchAt("/random_branch", "randomer_branch") == false); CHECK(tree.renameBranchAt("/random_branch", "randomerer_branch") == true); CHECK(tree.hasBranchAt("/randomerer_branch") == true); CHECK(tree.hasBranchAt("/random_branch") == false); tree.createLeafAt("/randomerer_branch/potatoe"); tree.createLeafAt("/randomerer_branch/salad"); CHECK(tree.renameLeafAt("/randomerer_branch/potatoe", "salad") == false); CHECK(tree.renameLeafAt("/randomerer_branch/potatoe", "burger") == true); CHECK(tree.hasLeafAt("/randomerer_branch/burger") == true); CHECK(tree.hasLeafAt("/randomerer_branch/potatoe") == false); } /*************/ TEST_CASE("Testing tree printing") { Log::get().setVerbosity(Log::ERROR); Tree::Root tree; tree.createBranchAt("/first/branch"); tree.createBranchAt("/second/branch"); tree.createLeafAt("/first/branch/leaf_1"); tree.setValueForLeafAt("/first/branch/leaf_1", Values({1.0, "One"}), chrono::system_clock::now()); tree.createLeafAt("/first/branch/leaf_2"); tree.setValueForLeafAt("/first/branch/leaf_2", Value("Some interesting stuff"), chrono::system_clock::now()); tree.createLeafAt("/second/branch/leaf_1"); tree.setValueForLeafAt("/second/branch/leaf_1", Value(false), chrono::system_clock::now()); auto treeAsString = tree.print(); auto correctString = std::string(R"(|-- | |-- first | | |-- branch | | | |-- leaf_1 | | | | |-- 1.000000 | | | | |-- One | | | |-- leaf_2 | | | | |-- Some interesting stuff | |-- second | | |-- branch | | | |-- leaf_1 | | | | |-- false )"); CHECK(treeAsString == correctString); } /*************/ TEST_CASE("Testing the Seed queue") { Tree::Root tree; tree.addSeedToQueue(Tree::Task::AddBranch, Values({"/some_object"})); tree.addSeedToQueue(Tree::Task::AddLeaf, Values({"/some_object/a_leaf"})); CHECK_NOTHROW(tree.processQueue()); CHECK(tree.createBranchAt("/some_object") == false); CHECK(tree.createLeafAt("/some_object/a_leaf") == false); tree.addSeedToQueue(Tree::Task::RemoveLeaf, Values({"/some_object/a_leaf"})); tree.addSeedToQueue(Tree::Task::RemoveBranch, Values({"/some_object"})); CHECK_NOTHROW(tree.processQueue()); CHECK(tree.createBranchAt("/some_object") == true); CHECK(tree.createLeafAt("/some_object/a_leaf") == true); auto value = Values({1.0, "I've got a flying machine", false}); tree.addSeedToQueue(Tree::Task::SetLeaf, Values({"/some_object/a_leaf", value})); CHECK_NOTHROW(tree.processQueue()); Value leafValue; CHECK(tree.getValueForLeafAt("/some_object/a_leaf", leafValue) == true); CHECK(leafValue == value); tree.addSeedToQueue(Tree::Task::RemoveLeaf, Values({"/some_object/a_leaf"})); tree.addSeedToQueue(Tree::Task::RemoveBranch, Values({"/some_object"})); CHECK_NOTHROW(tree.processQueue()); } /*************/ TEST_CASE("Testing the synchronization between trees") { std::string error{}; Tree::Root maple, oak; auto value = Values({1.0, "I've got a flying machine", false}); maple.createBranchAt("/some_branch"); maple.createLeafAt("/some_branch/some_leaf", value); maple.createBranchAt("/some_branch/child_branch"); maple.renameBranchAt("/some_branch/child_branch", "you_are_my_son"); auto updates = maple.getUpdateSeedList(); oak.addSeedsToQueue(updates); CHECK_NOTHROW(oak.processQueue()); CHECK(maple == oak); CHECK(oak.createBranchAt("/some_branch") == false); CHECK(oak.createLeafAt("/some_branch/some_leaf") == false); CHECK(oak.getError(error)); CHECK(!error.empty()); maple.removeLeafAt("/some_branch/some_leaf"); maple.removeBranchAt("/some_branch"); updates = maple.getUpdateSeedList(); oak.addSeedsToQueue(updates); CHECK_NOTHROW(oak.processQueue()); CHECK(!oak.getError(error)); CHECK(error.empty()); CHECK(maple == oak); } /*************/ TEST_CASE("Testing adding and cutting existing branches and leaves") { Tree::Root maple, oak, beech; oak.createBranchAt("/a_branch"); oak.createLeafAt("/a_branch/some_leaf"); oak.setValueForLeafAt("/a_branch/some_leaf", {"This is not a pie", 3.14159f}); oak.createLeafAt("/a_leaf"); oak.setValueForLeafAt("/a_leaf", "Some oak's leaf"); auto oakSeeds = oak.getUpdateSeedList(); beech.addSeedsToQueue(oakSeeds); beech.processQueue(); CHECK(oak == beech); auto branch = oak.cutBranchAt("/a_branch"); CHECK(branch.get() != nullptr); auto leaf = oak.cutLeafAt("/a_leaf"); CHECK(leaf.get() != nullptr); oakSeeds = oak.getUpdateSeedList(); maple.addBranchAt("/", std::move(branch)); maple.addLeafAt("/", std::move(leaf)); CHECK(maple == beech); CHECK(oak != beech); auto mapleSeeds = maple.getUpdateSeedList(); oak.addSeedsToQueue(mapleSeeds); oak.processQueue(); CHECK(maple == oak); maple.addSeedsToQueue(oakSeeds); maple.processQueue(); CHECK(maple == Tree::Root()); } /*************/ TEST_CASE("Testing the chronology handling of updates") { Tree::Root maple, oak, beech; maple.createBranchAt("/a_branch"); oak.createBranchAt("/a_branch"); maple.createLeafAt("/a_branch/a_leaf"); oak.createLeafAt("/a_branch/a_leaf"); oak.setValueForLeafAt("/a_branch/a_leaf", "Fresh meat!"); maple.setValueForLeafAt("/a_branch/a_leaf", "Stop clicking on me!"); beech.addSeedsToQueue(maple.getUpdateSeedList()); beech.addSeedsToQueue(oak.getUpdateSeedList()); Value leafValue; CHECK_NOTHROW(beech.processQueue()); CHECK(beech.getValueForLeafAt("/a_branch/a_leaf", leafValue) == true); CHECK(leafValue == Value("Stop clicking on me!")); CHECK(beech.hasError()); } /*************/ TEST_CASE("Testing the Branch's callbacks") { Tree::Root maple; std::string lastName = ""; maple.addCallbackToBranchAt("/", Tree::Branch::Task::AddBranch, [&lastName](Tree::Branch& branch, std::string name) { lastName = name; }); maple.addCallbackToBranchAt("/", Tree::Branch::Task::RemoveBranch, [&lastName](Tree::Branch& branch, std::string name) { lastName = name; }); maple.addCallbackToBranchAt("/", Tree::Branch::Task::AddLeaf, [&lastName](Tree::Branch& branch, std::string name) { lastName = name; }); maple.addCallbackToBranchAt("/", Tree::Branch::Task::RemoveLeaf, [&lastName](Tree::Branch& branch, std::string name) { lastName = name; }); maple.createBranchAt("/some_branch"); CHECK(lastName == "some_branch"); lastName = ""; maple.removeBranchAt("/some_branch"); CHECK(lastName == "some_branch"); maple.createLeafAt("/some_leaf"); CHECK(lastName == "some_leaf"); lastName = ""; maple.removeLeafAt("/some_leaf"); CHECK(lastName == "some_leaf"); } /*************/ TEST_CASE("Testing the Leaf's callbacks") { Tree::Root maple; maple.createLeafAt("/a_leaf"); CHECK(maple.hasLeafAt("/a_leaf")); Value extValue{""}; auto callbackID = maple.addCallbackToLeafAt("/a_leaf", [&extValue](Value value, chrono::system_clock::time_point timestamp) { extValue = value; }); CHECK(callbackID > 0); maple.setValueForLeafAt("/a_leaf", "Ceci n'est pas un test"); CHECK(extValue == Value("Ceci n'est pas un test")); CHECK(maple.removeCallbackFromLeafAt("/a_leaf", callbackID)); maple.setValueForLeafAt("/a_leaf", "Ceci non plus"); CHECK(extValue == Value("Ceci n'est pas un test")); } /*************/ TEST_CASE("Testing propagation through a main tree") { Tree::Root main, maple, oak; maple.createLeafAt("/some_leaf"); maple.createBranchAt("/a_branch"); auto seeds = maple.getUpdateSeedList(); main.addSeedsToQueue(seeds); main.processQueue(true); seeds = main.getUpdateSeedList(); oak.addSeedsToQueue(seeds); oak.processQueue(); CHECK(main == maple); CHECK(main == oak); } /*************/ TEST_CASE("Testing RootHandle") { Tree::Root main; main.createLeafAt("/first_leaf"); { auto handle = main.getHandle(); handle->createBranchAt("/first_branch"); CHECK(handle->hasLeafAt("/first_leaf")); } CHECK(main.hasBranchAt("/first_branch")); }