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