From 597976ef91e2a770154fed09ff3f2fd66486ffbd Mon Sep 17 00:00:00 2001 From: Voidylikescandy Date: Tue, 30 Sep 2025 04:55:16 +0530 Subject: [PATCH] feat: added tests for dial and minor changes --- .../CXXGraph/Graph/Algorithm/Dial_impl.hpp | 2 +- include/CXXGraph/Utility/Typedef.hpp | 4 +- test/DialTest.cpp | 179 ++++++++++++++++++ 3 files changed, 182 insertions(+), 3 deletions(-) diff --git a/include/CXXGraph/Graph/Algorithm/Dial_impl.hpp b/include/CXXGraph/Graph/Algorithm/Dial_impl.hpp index 6b2bea28f..e936d8e63 100644 --- a/include/CXXGraph/Graph/Algorithm/Dial_impl.hpp +++ b/include/CXXGraph/Graph/Algorithm/Dial_impl.hpp @@ -47,7 +47,7 @@ const DialResult Graph::dial(const Node &source, int maxWeight) const { its bucket is stored so that vertex can be deleted in O(1) at time of updation. So dist[i].first = distance of ith vertex from src vertex - dits[i].second = vertex i in bucket number */ + dist[i].second = vertex i in bucket number */ auto V = nodeSet.size(); std::unordered_map>, std::pair>>, nodeHash> diff --git a/include/CXXGraph/Utility/Typedef.hpp b/include/CXXGraph/Utility/Typedef.hpp index 2d964afb6..b4806ca96 100644 --- a/include/CXXGraph/Utility/Typedef.hpp +++ b/include/CXXGraph/Utility/Typedef.hpp @@ -145,13 +145,13 @@ struct MstResult_struct { }; typedef MstResult_struct MstResult; -/// Struct that contains the information about Dijsktra's Algorithm results +/// Struct that contains the information about Dijkstra's Algorithm results struct DialResult_struct { bool success = false; // TRUE if the function does not return error, FALSE otherwise std::string errorMessage = ""; // message of error std::unordered_map minDistanceMap = - {}; // result a map that contains the node id and the minumum distance + {}; // result a map that contains the node id and the minimum distance // from source (valid only if success is TRUE) }; typedef DialResult_struct DialResult; diff --git a/test/DialTest.cpp b/test/DialTest.cpp index 782c21ca7..11ce98753 100644 --- a/test/DialTest.cpp +++ b/test/DialTest.cpp @@ -12,6 +12,12 @@ using shared = std::shared_ptr; using std::make_shared; using std::make_unique; +/* + * 3 Nodes + * Node1 -> Node2 weight = 1 + * Node2 -> Node3 weight = 1 + * Node1 <-> Node3 weight = 6 +*/ TEST(DialTest, test_1) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); @@ -34,6 +40,12 @@ TEST(DialTest, test_1) { ASSERT_EQ(res.minDistanceMap.at(node3.getId()), 2); } +/* + * 3 Nodes + * Node1 -> Node2 weight = 5 + * Node2 -> Node3 weight = 4 + * Node1 <-> Node3 weight = 6 +*/ TEST(DialTest, test_2) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); @@ -57,6 +69,12 @@ TEST(DialTest, test_2) { ASSERT_EQ(res.minDistanceMap.at(node3.getId()), 6); } +/* + * 3 Nodes + * Node1 -> Node2 weight = 5 + * Node2 -> Node3 unweighted + * Node1 <-> Node3 weight = 6 +*/ TEST(DialTest, test_3) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); @@ -77,6 +95,11 @@ TEST(DialTest, test_3) { ASSERT_TRUE(res.minDistanceMap.empty()); } +/* + * 3 Nodes + * Node2 -> Node3 unweighted + * Node1 <-> Node3 weight = 6 +*/ TEST(DialTest, test_4) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); @@ -96,6 +119,12 @@ TEST(DialTest, test_4) { ASSERT_EQ(res.minDistanceMap.at(node3.getId()), 6); } +/* + * 4 Nodes + * Node2 -> Node3 unweighted + * Node1 <-> Node3 weight = 6 + * Only nodes which are a part of the edgeSet are a part of the graph (nodeSet) +*/ TEST(DialTest, test_5) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); @@ -112,3 +141,153 @@ TEST(DialTest, test_5) { ASSERT_EQ(res.errorMessage, CXXGraph::ERR_SOURCE_NODE_NOT_IN_GRAPH); ASSERT_TRUE(res.minDistanceMap.empty()); } + +/* + * 3 Nodes, zero weight edges + * Node1 -> Node2 weight = 0 + * Node2 -> Node3 weight = 0 +*/ +TEST(DialTest, test_6) { + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + + CXXGraph::DirectedWeightedEdge edge12(1, node1, node2, 0); + CXXGraph::DirectedWeightedEdge edge23(1, node2, node3, 0); + + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge12)); + edgeSet.insert(make_shared>(edge23)); + + CXXGraph::Graph graph(edgeSet); + + // Setting maxWeight = 0 causes an early exit + // As a consequence all distances except source node are at infinity. + auto res = graph.dial(node1, 1); + ASSERT_TRUE(res.success); + ASSERT_EQ(res.errorMessage, ""); + ASSERT_EQ(res.minDistanceMap.at(node1.getId()), 0); + ASSERT_EQ(res.minDistanceMap.at(node2.getId()), 0); + ASSERT_EQ(res.minDistanceMap.at(node3.getId()), 0); +} + +/* + * 2 Nodes, multi edges, same end points, choose smaller edge + * Node1 -> Node2 weight = 3 + * Node1 -> Node2 weight = 5 +*/ +TEST(DialTest, test_7) { + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + + CXXGraph::DirectedWeightedEdge edge12First(1, node1, node2, 3); + CXXGraph::DirectedWeightedEdge edge12Second(1, node1, node2, 5); + + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge12First)); + edgeSet.insert(make_shared>(edge12Second)); + + CXXGraph::Graph graph(edgeSet); + + auto res = graph.dial(node1, 5); + ASSERT_TRUE(res.success); + ASSERT_EQ(res.errorMessage, ""); + ASSERT_EQ(res.minDistanceMap.at(node1.getId()), 0); + ASSERT_EQ(res.minDistanceMap.at(node2.getId()), 3); +} + +/* + * 3 Nodes, multi edges with a cycle, cycle should not trap algorithm + * Node1 -> Node2 weight = 3 + * Node2 -> Node3 weight = 5 + * Node3 -> Node1 weight = 8 + * Node1 -> Node3 weight = 2 +*/ +TEST(DialTest, test_8) { + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + + CXXGraph::DirectedWeightedEdge edge12(1, node1, node2, 3); + CXXGraph::DirectedWeightedEdge edge23(1, node2, node3, 5); + CXXGraph::DirectedWeightedEdge edge31(1, node3, node1, 8); + CXXGraph::DirectedWeightedEdge edge13(1, node1, node3, 2); + + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge12)); + edgeSet.insert(make_shared>(edge23)); + edgeSet.insert(make_shared>(edge31)); + edgeSet.insert(make_shared>(edge13)); + + CXXGraph::Graph graph(edgeSet); + + auto res = graph.dial(node1, 5); + ASSERT_TRUE(res.success); + ASSERT_EQ(res.errorMessage, ""); + ASSERT_EQ(res.minDistanceMap.at(node1.getId()), 0); + ASSERT_EQ(res.minDistanceMap.at(node2.getId()), 3); + ASSERT_EQ(res.minDistanceMap.at(node3.getId()), 2); +} + +/* + * 2 Nodes, self loop + * Node1 -> Node1 weight = 3 + * Node1 -> Node2 weight = 5 +*/ +TEST(DialTest, test_9) { + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + + CXXGraph::DirectedWeightedEdge edge11(1, node1, node1, 3); + CXXGraph::DirectedWeightedEdge edge12(1, node1, node2, 5); + + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge11)); + edgeSet.insert(make_shared>(edge12)); + + CXXGraph::Graph graph(edgeSet); + + auto res = graph.dial(node1, 5); + ASSERT_TRUE(res.success); + ASSERT_EQ(res.errorMessage, ""); + ASSERT_EQ(res.minDistanceMap.at(node1.getId()), 0); + ASSERT_EQ(res.minDistanceMap.at(node2.getId()), 5); +} + +template +class BadDirectedWeightedEdge : public CXXGraph::DirectedWeightedEdge { + public: + using Base = CXXGraph::DirectedWeightedEdge; + // forward to existing ctor + BadDirectedWeightedEdge(const CXXGraph::id_t id, + const CXXGraph::Node &n1, + const CXXGraph::Node &n2, + const double weight) + : Base(id, n1, n2, weight) {} + + const std::optional isDirected() const override { + return std::nullopt; + } +}; + +/* + * 2 Nodes, invalid directed weighted edge + * The edge claims it's weighted but does not tell us direction + * Node1 -> Node2 weight = 5 +*/ +TEST(DialTest, test_10) { + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + + auto badEdge = std::make_shared>(1, node1, node2, 5); + + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(badEdge); + + CXXGraph::Graph graph(edgeSet); + + auto res = graph.dial(node1, 5); + ASSERT_FALSE(res.success); + ASSERT_EQ(res.errorMessage, CXXGraph::ERR_NO_DIR_OR_UNDIR_EDGE); + ASSERT_TRUE(res.minDistanceMap.empty()); +}