• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.graph;
18 
19 import static com.google.common.graph.TestUtil.assertNodeNotInGraphErrorMessage;
20 import static com.google.common.graph.TestUtil.assertStronglyEquivalent;
21 import static com.google.common.graph.TestUtil.sanityCheckSet;
22 import static com.google.common.truth.Truth.assertThat;
23 import static com.google.common.truth.TruthJUnit.assume;
24 import static org.junit.Assert.assertThrows;
25 
26 import com.google.common.collect.ImmutableSet;
27 import java.util.HashSet;
28 import java.util.Set;
29 import org.junit.After;
30 import org.junit.Before;
31 import org.junit.Test;
32 
33 /**
34  * Abstract base class for testing implementations of {@link Graph} interface. Graph instances
35  * created for testing should have Integer node and String edge objects.
36  *
37  * <p>Test cases that should be handled similarly in any graph implementation are included in this
38  * class. For example, testing that {@code nodes()} method returns the set of the nodes in the
39  * graph. The following test cases are left for the subclasses to handle:
40  *
41  * <ul>
42  *   <li>Test cases related to whether the graph is directed or undirected.
43  *   <li>Test cases related to the specific implementation of the {@link Graph} interface.
44  * </ul>
45  *
46  * TODO(user): Make this class generic (using <N, E>) for all node and edge types.
47  * TODO(user): Differentiate between directed and undirected edge strings.
48  */
49 public abstract class AbstractGraphTest {
50 
51   Graph<Integer> graph;
52 
53   /**
54    * The same reference as {@link #graph}, except as a mutable graph. This field is null in case
55    * {@link #createGraph()} didn't return a mutable graph.
56    */
57   MutableGraph<Integer> graphAsMutableGraph;
58 
59   static final Integer N1 = 1;
60   static final Integer N2 = 2;
61   static final Integer N3 = 3;
62   static final Integer N4 = 4;
63   static final Integer N5 = 5;
64   static final Integer NODE_NOT_IN_GRAPH = 1000;
65 
66   // TODO(user): Consider separating Strings that we've defined here to capture
67   // identifiable substrings of expected error messages, from Strings that we've defined
68   // here to provide error messages.
69   // TODO(user): Some Strings used in the subclasses can be added as static Strings
70   // here too.
71   static final String ERROR_MODIFIABLE_SET = "Set returned is unexpectedly modifiable";
72   static final String ERROR_SELF_LOOP = "self-loops are not allowed";
73   static final String ERROR_ADDED_SELF_LOOP = "Should not be allowed to add a self-loop edge.";
74 
75   /** Creates and returns an instance of the graph to be tested. */
createGraph()76   abstract Graph<Integer> createGraph();
77 
78   /**
79    * A proxy method that adds the node {@code n} to the graph being tested. In case of Immutable
80    * graph implementations, this method should replace {@link #graph} with a new graph that includes
81    * this node.
82    */
addNode(Integer n)83   abstract void addNode(Integer n);
84 
85   /**
86    * A proxy method that adds the edge {@code e} to the graph being tested. In case of Immutable
87    * graph implementations, this method should replace {@link #graph} with a new graph that includes
88    * this edge.
89    */
putEdge(Integer n1, Integer n2)90   abstract void putEdge(Integer n1, Integer n2);
91 
graphIsMutable()92   final boolean graphIsMutable() {
93     return graphAsMutableGraph != null;
94   }
95 
96   @Before
init()97   public final void init() {
98     graph = createGraph();
99     if (graph instanceof MutableGraph) {
100       graphAsMutableGraph = (MutableGraph<Integer>) graph;
101     }
102   }
103 
104   @After
validateGraphState()105   public final void validateGraphState() {
106     validateGraph(graph);
107   }
108 
validateGraph(Graph<N> graph)109   static <N> void validateGraph(Graph<N> graph) {
110     assertStronglyEquivalent(graph, Graphs.copyOf(graph));
111     assertStronglyEquivalent(graph, ImmutableGraph.copyOf(graph));
112 
113     String graphString = graph.toString();
114     assertThat(graphString).contains("isDirected: " + graph.isDirected());
115     assertThat(graphString).contains("allowsSelfLoops: " + graph.allowsSelfLoops());
116 
117     int nodeStart = graphString.indexOf("nodes:");
118     int edgeStart = graphString.indexOf("edges:");
119     String nodeString = graphString.substring(nodeStart, edgeStart);
120 
121     Set<EndpointPair<N>> allEndpointPairs = new HashSet<>();
122 
123     for (N node : sanityCheckSet(graph.nodes())) {
124       assertThat(nodeString).contains(node.toString());
125 
126       if (graph.isDirected()) {
127         assertThat(graph.degree(node)).isEqualTo(graph.inDegree(node) + graph.outDegree(node));
128         assertThat(graph.predecessors(node)).hasSize(graph.inDegree(node));
129         assertThat(graph.successors(node)).hasSize(graph.outDegree(node));
130       } else {
131         int selfLoopCount = graph.adjacentNodes(node).contains(node) ? 1 : 0;
132         assertThat(graph.degree(node)).isEqualTo(graph.adjacentNodes(node).size() + selfLoopCount);
133         assertThat(graph.predecessors(node)).isEqualTo(graph.adjacentNodes(node));
134         assertThat(graph.successors(node)).isEqualTo(graph.adjacentNodes(node));
135         assertThat(graph.inDegree(node)).isEqualTo(graph.degree(node));
136         assertThat(graph.outDegree(node)).isEqualTo(graph.degree(node));
137       }
138 
139       for (N adjacentNode : sanityCheckSet(graph.adjacentNodes(node))) {
140         if (!graph.allowsSelfLoops()) {
141           assertThat(node).isNotEqualTo(adjacentNode);
142         }
143         assertThat(
144                 graph.predecessors(node).contains(adjacentNode)
145                     || graph.successors(node).contains(adjacentNode))
146             .isTrue();
147       }
148 
149       for (N predecessor : sanityCheckSet(graph.predecessors(node))) {
150         assertThat(graph.successors(predecessor)).contains(node);
151         assertThat(graph.hasEdgeConnecting(predecessor, node)).isTrue();
152         assertThat(graph.incidentEdges(node)).contains(EndpointPair.of(graph, predecessor, node));
153       }
154 
155       for (N successor : sanityCheckSet(graph.successors(node))) {
156         allEndpointPairs.add(EndpointPair.of(graph, node, successor));
157         assertThat(graph.predecessors(successor)).contains(node);
158         assertThat(graph.hasEdgeConnecting(node, successor)).isTrue();
159         assertThat(graph.incidentEdges(node)).contains(EndpointPair.of(graph, node, successor));
160       }
161 
162       for (EndpointPair<N> endpoints : sanityCheckSet(graph.incidentEdges(node))) {
163         if (graph.isDirected()) {
164           assertThat(graph.hasEdgeConnecting(endpoints.source(), endpoints.target())).isTrue();
165         } else {
166           assertThat(graph.hasEdgeConnecting(endpoints.nodeU(), endpoints.nodeV())).isTrue();
167         }
168       }
169     }
170 
171     sanityCheckSet(graph.edges());
172     assertThat(graph.edges()).doesNotContain(EndpointPair.of(graph, new Object(), new Object()));
173     assertThat(graph.edges()).isEqualTo(allEndpointPairs);
174   }
175 
176   /**
177    * Verifies that the {@code Set} returned by {@code nodes} has the expected mutability property
178    * (see the {@code Graph} documentation for more information).
179    */
180   @Test
nodes_checkReturnedSetMutability()181   public abstract void nodes_checkReturnedSetMutability();
182 
183   /**
184    * Verifies that the {@code Set} returned by {@code adjacentNodes} has the expected mutability
185    * property (see the {@code Graph} documentation for more information).
186    */
187   @Test
adjacentNodes_checkReturnedSetMutability()188   public abstract void adjacentNodes_checkReturnedSetMutability();
189 
190   /**
191    * Verifies that the {@code Set} returned by {@code predecessors} has the expected mutability
192    * property (see the {@code Graph} documentation for more information).
193    */
194   @Test
predecessors_checkReturnedSetMutability()195   public abstract void predecessors_checkReturnedSetMutability();
196 
197   /**
198    * Verifies that the {@code Set} returned by {@code successors} has the expected mutability
199    * property (see the {@code Graph} documentation for more information).
200    */
201   @Test
successors_checkReturnedSetMutability()202   public abstract void successors_checkReturnedSetMutability();
203 
204   /**
205    * Verifies that the {@code Set} returned by {@code incidentEdges} has the expected mutability
206    * property (see the {@code Graph} documentation for more information).
207    */
208   @Test
incidentEdges_checkReturnedSetMutability()209   public abstract void incidentEdges_checkReturnedSetMutability();
210 
211   @Test
nodes_oneNode()212   public void nodes_oneNode() {
213     addNode(N1);
214     assertThat(graph.nodes()).containsExactly(N1);
215   }
216 
217   @Test
nodes_noNodes()218   public void nodes_noNodes() {
219     assertThat(graph.nodes()).isEmpty();
220   }
221 
222   @Test
adjacentNodes_oneEdge()223   public void adjacentNodes_oneEdge() {
224     putEdge(N1, N2);
225     assertThat(graph.adjacentNodes(N1)).containsExactly(N2);
226     assertThat(graph.adjacentNodes(N2)).containsExactly(N1);
227   }
228 
229   @Test
adjacentNodes_noAdjacentNodes()230   public void adjacentNodes_noAdjacentNodes() {
231     addNode(N1);
232     assertThat(graph.adjacentNodes(N1)).isEmpty();
233   }
234 
235   @Test
adjacentNodes_nodeNotInGraph()236   public void adjacentNodes_nodeNotInGraph() {
237     IllegalArgumentException e =
238         assertThrows(IllegalArgumentException.class, () -> graph.adjacentNodes(NODE_NOT_IN_GRAPH));
239     assertNodeNotInGraphErrorMessage(e);
240   }
241 
242   @Test
predecessors_noPredecessors()243   public void predecessors_noPredecessors() {
244     addNode(N1);
245     assertThat(graph.predecessors(N1)).isEmpty();
246   }
247 
248   @Test
predecessors_nodeNotInGraph()249   public void predecessors_nodeNotInGraph() {
250     IllegalArgumentException e =
251         assertThrows(IllegalArgumentException.class, () -> graph.predecessors(NODE_NOT_IN_GRAPH));
252     assertNodeNotInGraphErrorMessage(e);
253   }
254 
255   @Test
successors_noSuccessors()256   public void successors_noSuccessors() {
257     addNode(N1);
258     assertThat(graph.successors(N1)).isEmpty();
259   }
260 
261   @Test
successors_nodeNotInGraph()262   public void successors_nodeNotInGraph() {
263     IllegalArgumentException e =
264         assertThrows(IllegalArgumentException.class, () -> graph.successors(NODE_NOT_IN_GRAPH));
265     assertNodeNotInGraphErrorMessage(e);
266   }
267 
268   @Test
incidentEdges_noIncidentEdges()269   public void incidentEdges_noIncidentEdges() {
270     addNode(N1);
271     assertThat(graph.incidentEdges(N1)).isEmpty();
272   }
273 
274   @Test
incidentEdges_nodeNotInGraph()275   public void incidentEdges_nodeNotInGraph() {
276     IllegalArgumentException e =
277         assertThrows(IllegalArgumentException.class, () -> graph.incidentEdges(NODE_NOT_IN_GRAPH));
278     assertNodeNotInGraphErrorMessage(e);
279   }
280 
281   @Test
degree_oneEdge()282   public void degree_oneEdge() {
283     putEdge(N1, N2);
284     assertThat(graph.degree(N1)).isEqualTo(1);
285     assertThat(graph.degree(N2)).isEqualTo(1);
286   }
287 
288   @Test
degree_isolatedNode()289   public void degree_isolatedNode() {
290     addNode(N1);
291     assertThat(graph.degree(N1)).isEqualTo(0);
292   }
293 
294   @Test
degree_nodeNotInGraph()295   public void degree_nodeNotInGraph() {
296     IllegalArgumentException e =
297         assertThrows(IllegalArgumentException.class, () -> graph.degree(NODE_NOT_IN_GRAPH));
298     assertNodeNotInGraphErrorMessage(e);
299   }
300 
301   @Test
inDegree_isolatedNode()302   public void inDegree_isolatedNode() {
303     addNode(N1);
304     assertThat(graph.inDegree(N1)).isEqualTo(0);
305   }
306 
307   @Test
inDegree_nodeNotInGraph()308   public void inDegree_nodeNotInGraph() {
309     IllegalArgumentException e =
310         assertThrows(IllegalArgumentException.class, () -> graph.inDegree(NODE_NOT_IN_GRAPH));
311     assertNodeNotInGraphErrorMessage(e);
312   }
313 
314   @Test
outDegree_isolatedNode()315   public void outDegree_isolatedNode() {
316     addNode(N1);
317     assertThat(graph.outDegree(N1)).isEqualTo(0);
318   }
319 
320   @Test
outDegree_nodeNotInGraph()321   public void outDegree_nodeNotInGraph() {
322     IllegalArgumentException e =
323         assertThrows(IllegalArgumentException.class, () -> graph.outDegree(NODE_NOT_IN_GRAPH));
324     assertNodeNotInGraphErrorMessage(e);
325   }
326 
327   @Test
addNode_newNode()328   public void addNode_newNode() {
329     assume().that(graphIsMutable()).isTrue();
330 
331     assertThat(graphAsMutableGraph.addNode(N1)).isTrue();
332     assertThat(graph.nodes()).contains(N1);
333   }
334 
335   @Test
addNode_existingNode()336   public void addNode_existingNode() {
337     assume().that(graphIsMutable()).isTrue();
338 
339     addNode(N1);
340     ImmutableSet<Integer> nodes = ImmutableSet.copyOf(graph.nodes());
341     assertThat(graphAsMutableGraph.addNode(N1)).isFalse();
342     assertThat(graph.nodes()).containsExactlyElementsIn(nodes);
343   }
344 
345   @Test
removeNode_existingNode()346   public void removeNode_existingNode() {
347     assume().that(graphIsMutable()).isTrue();
348 
349     putEdge(N1, N2);
350     putEdge(N4, N1);
351     assertThat(graphAsMutableGraph.removeNode(N1)).isTrue();
352     assertThat(graphAsMutableGraph.removeNode(N1)).isFalse();
353     assertThat(graph.nodes()).containsExactly(N2, N4);
354     assertThat(graph.adjacentNodes(N2)).isEmpty();
355     assertThat(graph.adjacentNodes(N4)).isEmpty();
356   }
357 
358   @Test
removeNode_antiparallelEdges()359   public void removeNode_antiparallelEdges() {
360     assume().that(graphIsMutable()).isTrue();
361 
362     putEdge(N1, N2);
363     putEdge(N2, N1);
364 
365     assertThat(graphAsMutableGraph.removeNode(N1)).isTrue();
366     assertThat(graph.nodes()).containsExactly(N2);
367     assertThat(graph.edges()).isEmpty();
368 
369     assertThat(graphAsMutableGraph.removeNode(N2)).isTrue();
370     assertThat(graph.nodes()).isEmpty();
371     assertThat(graph.edges()).isEmpty();
372   }
373 
374   @Test
removeNode_nodeNotPresent()375   public void removeNode_nodeNotPresent() {
376     assume().that(graphIsMutable()).isTrue();
377 
378     addNode(N1);
379     ImmutableSet<Integer> nodes = ImmutableSet.copyOf(graph.nodes());
380     assertThat(graphAsMutableGraph.removeNode(NODE_NOT_IN_GRAPH)).isFalse();
381     assertThat(graph.nodes()).containsExactlyElementsIn(nodes);
382   }
383 
384   @Test
removeNode_queryAfterRemoval()385   public void removeNode_queryAfterRemoval() {
386     assume().that(graphIsMutable()).isTrue();
387 
388     putEdge(N1, N2);
389     putEdge(N2, N1);
390     Set<Integer> n1AdjacentNodes = graph.adjacentNodes(N1);
391     Set<Integer> n2AdjacentNodes = graph.adjacentNodes(N2);
392     assertThat(graphAsMutableGraph.removeNode(N1)).isTrue();
393     assertThat(n1AdjacentNodes).isEmpty();
394     assertThat(n2AdjacentNodes).isEmpty();
395     IllegalArgumentException e =
396         assertThrows(IllegalArgumentException.class, () -> graph.adjacentNodes(N1));
397     assertNodeNotInGraphErrorMessage(e);
398   }
399 
400   @Test
removeEdge_existingEdge()401   public void removeEdge_existingEdge() {
402     assume().that(graphIsMutable()).isTrue();
403 
404     putEdge(N1, N2);
405     assertThat(graph.successors(N1)).containsExactly(N2);
406     assertThat(graph.predecessors(N2)).containsExactly(N1);
407     assertThat(graphAsMutableGraph.removeEdge(N1, N2)).isTrue();
408     assertThat(graphAsMutableGraph.removeEdge(N1, N2)).isFalse();
409     assertThat(graph.successors(N1)).isEmpty();
410     assertThat(graph.predecessors(N2)).isEmpty();
411   }
412 
413   @Test
removeEdge_oneOfMany()414   public void removeEdge_oneOfMany() {
415     assume().that(graphIsMutable()).isTrue();
416 
417     putEdge(N1, N2);
418     putEdge(N1, N3);
419     putEdge(N1, N4);
420     assertThat(graphAsMutableGraph.removeEdge(N1, N3)).isTrue();
421     assertThat(graph.adjacentNodes(N1)).containsExactly(N2, N4);
422   }
423 
424   @Test
removeEdge_nodeNotPresent()425   public void removeEdge_nodeNotPresent() {
426     assume().that(graphIsMutable()).isTrue();
427 
428     putEdge(N1, N2);
429     assertThat(graphAsMutableGraph.removeEdge(N1, NODE_NOT_IN_GRAPH)).isFalse();
430     assertThat(graph.successors(N1)).contains(N2);
431   }
432 
433   @Test
removeEdge_edgeNotPresent()434   public void removeEdge_edgeNotPresent() {
435     assume().that(graphIsMutable()).isTrue();
436 
437     putEdge(N1, N2);
438     addNode(N3);
439 
440     assertThat(graphAsMutableGraph.removeEdge(N1, N3)).isFalse();
441     assertThat(graph.successors(N1)).contains(N2);
442   }
443 }
444