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