Files
splash/tests/unit_tests/core/tree.cpp
2023-10-10 03:54:33 +03:00

294 lines
9.8 KiB
C++

#include <doctest.h>
#include <chrono>
#include <utility>
#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"));
}