diff unittests/ADT/DirectedGraphTest.cpp @ 147:c2174574ed3a

LLVM 10
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Wed, 14 Aug 2019 16:55:33 +0900
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/unittests/ADT/DirectedGraphTest.cpp	Wed Aug 14 16:55:33 2019 +0900
@@ -0,0 +1,295 @@
+//===- 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