// Standalone C++17 test: no Qt required. #include "dbc_tree_builder.h" #include #include #include static void Check(bool condition) { if (!condition) throw std::runtime_error("Tree navigation test failed"); } static void CheckNavigation(const TreeNode &node) { for (std::size_t row = 0; row < node.GetChildCount(); ++row) { const TreeNode *child = node.GetChild(row); Check(child->GetParent() == &node && child->GetRow() == row); Check(child->GetParent()->GetChild(child->GetRow()) == child); CheckNavigation(*child); } Check(!node.GetChild(node.GetChildCount())); Check(!node.GetChild(std::numeric_limits::max())); } static void CheckRejectedInsertion(TreeNode &parent, std::unique_ptr &owner) { const TreeNode *original = owner.get(); bool rejected = false; try { parent.AddChild(std::move(owner)); } catch (const std::invalid_argument &) { rejected = true; } Check(rejected && owner.get() == original); CheckNavigation(*owner); } int main() { static_assert(!std::is_copy_constructible::value, "Nodes have exclusive ownership"); static_assert(!std::is_copy_assignable::value, "Nodes have exclusive ownership"); static_assert(!std::is_move_constructible::value, "Node addresses must be stable"); static_assert(!std::is_move_assignable::value, "Node addresses must be stable"); CheckNavigation(*DbcTreeBuilder().Build(DbcDatabase())); DbcDatabase database; FrameInfo frame; frame.canId = 123U; frame.name = "Frame"; SignalInfo signal; signal.name = "Value"; frame.signals.push_back(signal); signal.name = "Other"; frame.signals.push_back(signal); database.frames.push_back(frame); frame.isExtended = true; database.frames.push_back(frame); std::unique_ptr root = DbcTreeBuilder().Build(database); Check(root->GetParent() == nullptr && root->GetRow() == 0); Check(!root->GetFrameKey() && !root->GetSignalKey()); root->AddChild(nullptr); Check(root->GetChildCount() == 2 && !root->GetChild(2)); Check(!root->GetChild(std::numeric_limits::max())); CheckNavigation(*root); const TreeNode *firstFrame = root->GetChild(0); const TreeNode *firstSignal = firstFrame->GetChild(0); const FrameKey standard(123U, false); const FrameKey extended(123U, true); Check(firstFrame->GetFrameKey() == standard && !firstFrame->GetSignalKey()); Check(firstSignal->GetFrameKey() == standard); Check(firstSignal->GetSignalKey() == SignalKey(standard, "Value")); Check(firstFrame->GetChild(1)->GetSignalKey() == SignalKey(standard, "Other")); Check(root->GetChild(1)->GetFrameKey() == extended); Check(root->GetChild(1)->GetChild(0)->GetSignalKey() == SignalKey(extended, "Value")); TreeNode detached(signal); Check(!detached.GetFrameKey() && !detached.GetSignalKey()); Check(!detached.GetParent() && detached.GetRow() == 0); TreeNode detachedFrame(frame); Check(detachedFrame.GetFrameKey() == extended && !detachedFrame.GetSignalKey()); // Reordering/reloading source metadata does not change the original tree's keys. database.frames.clear(); database.frames.push_back(frame); std::unique_ptr replacement = DbcTreeBuilder().Build(database); Check(replacement->GetChild(0)->GetFrameKey() == root->GetChild(1)->GetFrameKey()); Check(firstSignal->GetSignalKey() == SignalKey(standard, "Value")); // Grow both child vectors, retaining all existing addresses and rows. for (std::size_t index = 0; index < 64; ++index) { root->AddChild(std::make_unique(frame)); root->GetChild(0)->AddChild(std::make_unique(signal)); } Check(root->GetChild(0) == firstFrame && firstFrame->GetChild(0) == firstSignal); CheckNavigation(*root); CheckRejectedInsertion(*root, root); CheckRejectedInsertion(*root->GetChild(0)->GetChild(0), root); const TreeNode *original = root.get(); std::unique_ptr transferred = std::move(root); Check(!root && transferred.get() == original); CheckNavigation(*transferred); Check(firstSignal->GetSignalKey() == SignalKey(standard, "Value")); }