| //===- llvm/unittest/ADT/DirectedGraphTest.cpp ------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines concrete derivations of the directed-graph base classes |
| // for testing purposes. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ADT/DirectedGraph.h" |
| #include "llvm/ADT/GraphTraits.h" |
| #include "llvm/ADT/SCCIterator.h" |
| #include "llvm/ADT/SmallPtrSet.h" |
| #include "gtest/gtest.h" |
| |
| namespace llvm { |
| |
| //===--------------------------------------------------------------------===// |
| // Derived nodes, edges and graph types based on DirectedGraph. |
| //===--------------------------------------------------------------------===// |
| |
| class DGTestNode; |
| class DGTestEdge; |
| using DGTestNodeBase = DGNode<DGTestNode, DGTestEdge>; |
| using DGTestEdgeBase = DGEdge<DGTestNode, DGTestEdge>; |
| using DGTestBase = DirectedGraph<DGTestNode, DGTestEdge>; |
| |
| class DGTestNode : public DGTestNodeBase { |
| public: |
| DGTestNode() = default; |
| }; |
| |
| class DGTestEdge : public DGTestEdgeBase { |
| public: |
| DGTestEdge() = delete; |
| DGTestEdge(DGTestNode &N) : DGTestEdgeBase(N) {} |
| }; |
| |
| class DGTestGraph : public DGTestBase { |
| public: |
| DGTestGraph() = default; |
| ~DGTestGraph(){}; |
| }; |
| |
| using EdgeListTy = SmallVector<DGTestEdge *, 2>; |
| |
| //===--------------------------------------------------------------------===// |
| // GraphTraits specializations for the DGTest |
| //===--------------------------------------------------------------------===// |
| |
| template <> struct GraphTraits<DGTestNode *> { |
| using NodeRef = DGTestNode *; |
| |
| static DGTestNode *DGTestGetTargetNode(DGEdge<DGTestNode, DGTestEdge> *P) { |
| return &P->getTargetNode(); |
| } |
| |
| // Provide a mapped iterator so that the GraphTrait-based implementations can |
| // find the target nodes without having to explicitly go through the edges. |
| using ChildIteratorType = |
| mapped_iterator<DGTestNode::iterator, decltype(&DGTestGetTargetNode)>; |
| using ChildEdgeIteratorType = DGTestNode::iterator; |
| |
| static NodeRef getEntryNode(NodeRef N) { return N; } |
| static ChildIteratorType child_begin(NodeRef N) { |
| return ChildIteratorType(N->begin(), &DGTestGetTargetNode); |
| } |
| static ChildIteratorType child_end(NodeRef N) { |
| return ChildIteratorType(N->end(), &DGTestGetTargetNode); |
| } |
| |
| static ChildEdgeIteratorType child_edge_begin(NodeRef N) { |
| return N->begin(); |
| } |
| static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); } |
| }; |
| |
| template <> |
| struct GraphTraits<DGTestGraph *> : public GraphTraits<DGTestNode *> { |
| using nodes_iterator = DGTestGraph::iterator; |
| static NodeRef getEntryNode(DGTestGraph *DG) { return *DG->begin(); } |
| static nodes_iterator nodes_begin(DGTestGraph *DG) { return DG->begin(); } |
| static nodes_iterator nodes_end(DGTestGraph *DG) { return DG->end(); } |
| }; |
| |
| //===--------------------------------------------------------------------===// |
| // Test various modification and query functions. |
| //===--------------------------------------------------------------------===// |
| |
| TEST(DirectedGraphTest, AddAndConnectNodes) { |
| DGTestGraph DG; |
| DGTestNode N1, N2, N3; |
| DGTestEdge E1(N1), E2(N2), E3(N3); |
| |
| // Check that new nodes can be added successfully. |
| EXPECT_TRUE(DG.addNode(N1)); |
| EXPECT_TRUE(DG.addNode(N2)); |
| EXPECT_TRUE(DG.addNode(N3)); |
| |
| // Check that duplicate nodes are not added to the graph. |
| EXPECT_FALSE(DG.addNode(N1)); |
| |
| // Check that nodes can be connected using valid edges with no errors. |
| EXPECT_TRUE(DG.connect(N1, N2, E2)); |
| EXPECT_TRUE(DG.connect(N2, N3, E3)); |
| EXPECT_TRUE(DG.connect(N3, N1, E1)); |
| |
| // The graph looks like this now: |
| // |
| // +---------------+ |
| // v | |
| // N1 -> N2 -> N3 -+ |
| |
| // Check that already connected nodes with the given edge are not connected |
| // again (ie. edges are between nodes are not duplicated). |
| EXPECT_FALSE(DG.connect(N3, N1, E1)); |
| |
| // Check that there are 3 nodes in the graph. |
| EXPECT_TRUE(DG.size() == 3); |
| |
| // Check that the added nodes can be found in the graph. |
| EXPECT_NE(DG.findNode(N3), DG.end()); |
| |
| // Check that nodes that are not part of the graph are not found. |
| DGTestNode N4; |
| EXPECT_EQ(DG.findNode(N4), DG.end()); |
| |
| // Check that findIncommingEdgesToNode works correctly. |
| EdgeListTy EL; |
| EXPECT_TRUE(DG.findIncomingEdgesToNode(N1, EL)); |
| EXPECT_TRUE(EL.size() == 1); |
| EXPECT_EQ(*EL[0], E1); |
| } |
| |
| TEST(DirectedGraphTest, AddRemoveEdge) { |
| DGTestGraph DG; |
| DGTestNode N1, N2, N3; |
| DGTestEdge E1(N1), E2(N2), E3(N3); |
| DG.addNode(N1); |
| DG.addNode(N2); |
| DG.addNode(N3); |
| DG.connect(N1, N2, E2); |
| DG.connect(N2, N3, E3); |
| DG.connect(N3, N1, E1); |
| |
| // The graph looks like this now: |
| // |
| // +---------------+ |
| // v | |
| // N1 -> N2 -> N3 -+ |
| |
| // Check that there are 3 nodes in the graph. |
| EXPECT_TRUE(DG.size() == 3); |
| |
| // Check that the target nodes of the edges are correct. |
| EXPECT_EQ(E1.getTargetNode(), N1); |
| EXPECT_EQ(E2.getTargetNode(), N2); |
| EXPECT_EQ(E3.getTargetNode(), N3); |
| |
| // Remove the edge from N1 to N2. |
| N1.removeEdge(E2); |
| |
| // The graph looks like this now: |
| // |
| // N2 -> N3 -> N1 |
| |
| // Check that there are no incoming edges to N2. |
| EdgeListTy EL; |
| EXPECT_FALSE(DG.findIncomingEdgesToNode(N2, EL)); |
| EXPECT_TRUE(EL.empty()); |
| |
| // Put the edge from N1 to N2 back in place. |
| N1.addEdge(E2); |
| |
| // Check that E2 is the only incoming edge to N2. |
| EL.clear(); |
| EXPECT_TRUE(DG.findIncomingEdgesToNode(N2, EL)); |
| EXPECT_EQ(*EL[0], E2); |
| } |
| |
| TEST(DirectedGraphTest, hasEdgeTo) { |
| DGTestGraph DG; |
| DGTestNode N1, N2, N3; |
| DGTestEdge E1(N1), E2(N2), E3(N3), E4(N1); |
| DG.addNode(N1); |
| DG.addNode(N2); |
| DG.addNode(N3); |
| DG.connect(N1, N2, E2); |
| DG.connect(N2, N3, E3); |
| DG.connect(N3, N1, E1); |
| DG.connect(N2, N1, E4); |
| |
| // The graph looks like this now: |
| // |
| // +-----+ |
| // v | |
| // N1 -> N2 -> N3 |
| // ^ | |
| // +-----------+ |
| |
| EXPECT_TRUE(N2.hasEdgeTo(N1)); |
| EXPECT_TRUE(N3.hasEdgeTo(N1)); |
| } |
| |
| TEST(DirectedGraphTest, AddRemoveNode) { |
| DGTestGraph DG; |
| DGTestNode N1, N2, N3; |
| DGTestEdge E1(N1), E2(N2), E3(N3); |
| DG.addNode(N1); |
| DG.addNode(N2); |
| DG.addNode(N3); |
| DG.connect(N1, N2, E2); |
| DG.connect(N2, N3, E3); |
| DG.connect(N3, N1, E1); |
| |
| // The graph looks like this now: |
| // |
| // +---------------+ |
| // v | |
| // N1 -> N2 -> N3 -+ |
| |
| // Check that there are 3 nodes in the graph. |
| EXPECT_TRUE(DG.size() == 3); |
| |
| // Check that a node in the graph can be removed, but not more than once. |
| EXPECT_TRUE(DG.removeNode(N1)); |
| EXPECT_EQ(DG.findNode(N1), DG.end()); |
| EXPECT_FALSE(DG.removeNode(N1)); |
| |
| // The graph looks like this now: |
| // |
| // N2 -> N3 |
| |
| // Check that there are 2 nodes in the graph and only N2 is connected to N3. |
| EXPECT_TRUE(DG.size() == 2); |
| EXPECT_TRUE(N3.getEdges().empty()); |
| EdgeListTy EL; |
| EXPECT_FALSE(DG.findIncomingEdgesToNode(N2, EL)); |
| EXPECT_TRUE(EL.empty()); |
| } |
| |
| TEST(DirectedGraphTest, SCC) { |
| |
| DGTestGraph DG; |
| DGTestNode N1, N2, N3, N4; |
| DGTestEdge E1(N1), E2(N2), E3(N3), E4(N4); |
| DG.addNode(N1); |
| DG.addNode(N2); |
| DG.addNode(N3); |
| DG.addNode(N4); |
| DG.connect(N1, N2, E2); |
| DG.connect(N2, N3, E3); |
| DG.connect(N3, N1, E1); |
| DG.connect(N3, N4, E4); |
| |
| // The graph looks like this now: |
| // |
| // +---------------+ |
| // v | |
| // N1 -> N2 -> N3 -+ N4 |
| // | ^ |
| // +--------+ |
| |
| // Test that there are two SCCs: |
| // 1. {N1, N2, N3} |
| // 2. {N4} |
| using NodeListTy = SmallPtrSet<DGTestNode *, 3>; |
| SmallVector<NodeListTy, 4> ListOfSCCs; |
| for (auto &SCC : make_range(scc_begin(&DG), scc_end(&DG))) |
| ListOfSCCs.push_back(NodeListTy(SCC.begin(), SCC.end())); |
| |
| EXPECT_TRUE(ListOfSCCs.size() == 2); |
| |
| for (auto &SCC : ListOfSCCs) { |
| if (SCC.size() > 1) |
| continue; |
| EXPECT_TRUE(SCC.size() == 1); |
| EXPECT_TRUE(SCC.count(&N4) == 1); |
| } |
| for (auto &SCC : ListOfSCCs) { |
| if (SCC.size() <= 1) |
| continue; |
| EXPECT_TRUE(SCC.size() == 3); |
| EXPECT_TRUE(SCC.count(&N1) == 1); |
| EXPECT_TRUE(SCC.count(&N2) == 1); |
| EXPECT_TRUE(SCC.count(&N3) == 1); |
| EXPECT_TRUE(SCC.count(&N4) == 0); |
| } |
| } |
| |
| } // namespace llvm |