1 //===- llvm/unittest/ADT/DirectedGraphTest.cpp ------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines concrete derivations of the directed-graph base classes 10 // for testing purposes. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/ADT/DirectedGraph.h" 15 #include "llvm/ADT/GraphTraits.h" 16 #include "llvm/ADT/SCCIterator.h" 17 #include "llvm/ADT/SmallPtrSet.h" 18 #include "gtest/gtest.h" 19 20 namespace llvm { 21 22 //===--------------------------------------------------------------------===// 23 // Derived nodes, edges and graph types based on DirectedGraph. 24 //===--------------------------------------------------------------------===// 25 26 class DGTestNode; 27 class DGTestEdge; 28 using DGTestNodeBase = DGNode<DGTestNode, DGTestEdge>; 29 using DGTestEdgeBase = DGEdge<DGTestNode, DGTestEdge>; 30 using DGTestBase = DirectedGraph<DGTestNode, DGTestEdge>; 31 32 class DGTestNode : public DGTestNodeBase { 33 public: 34 DGTestNode() = default; 35 }; 36 37 class DGTestEdge : public DGTestEdgeBase { 38 public: 39 DGTestEdge() = delete; 40 DGTestEdge(DGTestNode &N) : DGTestEdgeBase(N) {} 41 }; 42 43 class DGTestGraph : public DGTestBase { 44 public: 45 DGTestGraph() = default; 46 ~DGTestGraph(){}; 47 }; 48 49 using EdgeListTy = SmallVector<DGTestEdge *, 2>; 50 51 //===--------------------------------------------------------------------===// 52 // GraphTraits specializations for the DGTest 53 //===--------------------------------------------------------------------===// 54 55 template <> struct GraphTraits<DGTestNode *> { 56 using NodeRef = DGTestNode *; 57 58 static DGTestNode *DGTestGetTargetNode(DGEdge<DGTestNode, DGTestEdge> *P) { 59 return &P->getTargetNode(); 60 } 61 62 // Provide a mapped iterator so that the GraphTrait-based implementations can 63 // find the target nodes without having to explicitly go through the edges. 64 using ChildIteratorType = 65 mapped_iterator<DGTestNode::iterator, decltype(&DGTestGetTargetNode)>; 66 using ChildEdgeIteratorType = DGTestNode::iterator; 67 68 static NodeRef getEntryNode(NodeRef N) { return N; } 69 static ChildIteratorType child_begin(NodeRef N) { 70 return ChildIteratorType(N->begin(), &DGTestGetTargetNode); 71 } 72 static ChildIteratorType child_end(NodeRef N) { 73 return ChildIteratorType(N->end(), &DGTestGetTargetNode); 74 } 75 76 static ChildEdgeIteratorType child_edge_begin(NodeRef N) { 77 return N->begin(); 78 } 79 static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); } 80 }; 81 82 template <> 83 struct GraphTraits<DGTestGraph *> : public GraphTraits<DGTestNode *> { 84 using nodes_iterator = DGTestGraph::iterator; 85 static NodeRef getEntryNode(DGTestGraph *DG) { return *DG->begin(); } 86 static nodes_iterator nodes_begin(DGTestGraph *DG) { return DG->begin(); } 87 static nodes_iterator nodes_end(DGTestGraph *DG) { return DG->end(); } 88 }; 89 90 //===--------------------------------------------------------------------===// 91 // Test various modification and query functions. 92 //===--------------------------------------------------------------------===// 93 94 TEST(DirectedGraphTest, AddAndConnectNodes) { 95 DGTestGraph DG; 96 DGTestNode N1, N2, N3; 97 DGTestEdge E1(N1), E2(N2), E3(N3); 98 99 // Check that new nodes can be added successfully. 100 EXPECT_TRUE(DG.addNode(N1)); 101 EXPECT_TRUE(DG.addNode(N2)); 102 EXPECT_TRUE(DG.addNode(N3)); 103 104 // Check that duplicate nodes are not added to the graph. 105 EXPECT_FALSE(DG.addNode(N1)); 106 107 // Check that nodes can be connected using valid edges with no errors. 108 EXPECT_TRUE(DG.connect(N1, N2, E2)); 109 EXPECT_TRUE(DG.connect(N2, N3, E3)); 110 EXPECT_TRUE(DG.connect(N3, N1, E1)); 111 112 // The graph looks like this now: 113 // 114 // +---------------+ 115 // v | 116 // N1 -> N2 -> N3 -+ 117 118 // Check that already connected nodes with the given edge are not connected 119 // again (ie. edges are between nodes are not duplicated). 120 EXPECT_FALSE(DG.connect(N3, N1, E1)); 121 122 // Check that there are 3 nodes in the graph. 123 EXPECT_TRUE(DG.size() == 3); 124 125 // Check that the added nodes can be found in the graph. 126 EXPECT_NE(DG.findNode(N3), DG.end()); 127 128 // Check that nodes that are not part of the graph are not found. 129 DGTestNode N4; 130 EXPECT_EQ(DG.findNode(N4), DG.end()); 131 132 // Check that findIncommingEdgesToNode works correctly. 133 EdgeListTy EL; 134 EXPECT_TRUE(DG.findIncomingEdgesToNode(N1, EL)); 135 EXPECT_TRUE(EL.size() == 1); 136 EXPECT_EQ(*EL[0], E1); 137 } 138 139 TEST(DirectedGraphTest, AddRemoveEdge) { 140 DGTestGraph DG; 141 DGTestNode N1, N2, N3; 142 DGTestEdge E1(N1), E2(N2), E3(N3); 143 DG.addNode(N1); 144 DG.addNode(N2); 145 DG.addNode(N3); 146 DG.connect(N1, N2, E2); 147 DG.connect(N2, N3, E3); 148 DG.connect(N3, N1, E1); 149 150 // The graph looks like this now: 151 // 152 // +---------------+ 153 // v | 154 // N1 -> N2 -> N3 -+ 155 156 // Check that there are 3 nodes in the graph. 157 EXPECT_TRUE(DG.size() == 3); 158 159 // Check that the target nodes of the edges are correct. 160 EXPECT_EQ(E1.getTargetNode(), N1); 161 EXPECT_EQ(E2.getTargetNode(), N2); 162 EXPECT_EQ(E3.getTargetNode(), N3); 163 164 // Remove the edge from N1 to N2. 165 N1.removeEdge(E2); 166 167 // The graph looks like this now: 168 // 169 // N2 -> N3 -> N1 170 171 // Check that there are no incoming edges to N2. 172 EdgeListTy EL; 173 EXPECT_FALSE(DG.findIncomingEdgesToNode(N2, EL)); 174 EXPECT_TRUE(EL.empty()); 175 176 // Put the edge from N1 to N2 back in place. 177 N1.addEdge(E2); 178 179 // Check that E2 is the only incoming edge to N2. 180 EL.clear(); 181 EXPECT_TRUE(DG.findIncomingEdgesToNode(N2, EL)); 182 EXPECT_EQ(*EL[0], E2); 183 } 184 185 TEST(DirectedGraphTest, hasEdgeTo) { 186 DGTestGraph DG; 187 DGTestNode N1, N2, N3; 188 DGTestEdge E1(N1), E2(N2), E3(N3), E4(N1); 189 DG.addNode(N1); 190 DG.addNode(N2); 191 DG.addNode(N3); 192 DG.connect(N1, N2, E2); 193 DG.connect(N2, N3, E3); 194 DG.connect(N3, N1, E1); 195 DG.connect(N2, N1, E4); 196 197 // The graph looks like this now: 198 // 199 // +-----+ 200 // v | 201 // N1 -> N2 -> N3 202 // ^ | 203 // +-----------+ 204 205 EXPECT_TRUE(N2.hasEdgeTo(N1)); 206 EXPECT_TRUE(N3.hasEdgeTo(N1)); 207 } 208 209 TEST(DirectedGraphTest, AddRemoveNode) { 210 DGTestGraph DG; 211 DGTestNode N1, N2, N3; 212 DGTestEdge E1(N1), E2(N2), E3(N3); 213 DG.addNode(N1); 214 DG.addNode(N2); 215 DG.addNode(N3); 216 DG.connect(N1, N2, E2); 217 DG.connect(N2, N3, E3); 218 DG.connect(N3, N1, E1); 219 220 // The graph looks like this now: 221 // 222 // +---------------+ 223 // v | 224 // N1 -> N2 -> N3 -+ 225 226 // Check that there are 3 nodes in the graph. 227 EXPECT_TRUE(DG.size() == 3); 228 229 // Check that a node in the graph can be removed, but not more than once. 230 EXPECT_TRUE(DG.removeNode(N1)); 231 EXPECT_EQ(DG.findNode(N1), DG.end()); 232 EXPECT_FALSE(DG.removeNode(N1)); 233 234 // The graph looks like this now: 235 // 236 // N2 -> N3 237 238 // Check that there are 2 nodes in the graph and only N2 is connected to N3. 239 EXPECT_TRUE(DG.size() == 2); 240 EXPECT_TRUE(N3.getEdges().empty()); 241 EdgeListTy EL; 242 EXPECT_FALSE(DG.findIncomingEdgesToNode(N2, EL)); 243 EXPECT_TRUE(EL.empty()); 244 } 245 246 TEST(DirectedGraphTest, SCC) { 247 248 DGTestGraph DG; 249 DGTestNode N1, N2, N3, N4; 250 DGTestEdge E1(N1), E2(N2), E3(N3), E4(N4); 251 DG.addNode(N1); 252 DG.addNode(N2); 253 DG.addNode(N3); 254 DG.addNode(N4); 255 DG.connect(N1, N2, E2); 256 DG.connect(N2, N3, E3); 257 DG.connect(N3, N1, E1); 258 DG.connect(N3, N4, E4); 259 260 // The graph looks like this now: 261 // 262 // +---------------+ 263 // v | 264 // N1 -> N2 -> N3 -+ N4 265 // | ^ 266 // +--------+ 267 268 // Test that there are two SCCs: 269 // 1. {N1, N2, N3} 270 // 2. {N4} 271 using NodeListTy = SmallPtrSet<DGTestNode *, 3>; 272 SmallVector<NodeListTy, 4> ListOfSCCs; 273 for (auto &SCC : make_range(scc_begin(&DG), scc_end(&DG))) 274 ListOfSCCs.push_back(NodeListTy(SCC.begin(), SCC.end())); 275 276 EXPECT_TRUE(ListOfSCCs.size() == 2); 277 278 for (auto &SCC : ListOfSCCs) { 279 if (SCC.size() > 1) 280 continue; 281 EXPECT_TRUE(SCC.size() == 1); 282 EXPECT_TRUE(SCC.count(&N4) == 1); 283 } 284 for (auto &SCC : ListOfSCCs) { 285 if (SCC.size() <= 1) 286 continue; 287 EXPECT_TRUE(SCC.size() == 3); 288 EXPECT_TRUE(SCC.count(&N1) == 1); 289 EXPECT_TRUE(SCC.count(&N2) == 1); 290 EXPECT_TRUE(SCC.count(&N3) == 1); 291 EXPECT_TRUE(SCC.count(&N4) == 0); 292 } 293 } 294 295 } // namespace llvm 296