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.truth.Truth.assertThat;
20 import static com.google.common.truth.TruthJUnit.assume;
21 import static org.junit.Assert.assertTrue;
22 import static org.junit.Assert.fail;
23 
24 import com.google.common.testing.EqualsTester;
25 import java.util.Set;
26 import org.junit.After;
27 import org.junit.Test;
28 
29 /**
30  * Abstract base class for testing undirected {@link Graph} implementations defined in this package.
31  */
32 public abstract class AbstractStandardUndirectedGraphTest extends AbstractGraphTest {
33 
34   @After
validateUndirectedEdges()35   public void validateUndirectedEdges() {
36     for (Integer node : graph.nodes()) {
37       new EqualsTester()
38           .addEqualityGroup(
39               graph.predecessors(node), graph.successors(node), graph.adjacentNodes(node))
40           .testEquals();
41     }
42   }
43 
44   @Override
45   @Test
nodes_checkReturnedSetMutability()46   public void nodes_checkReturnedSetMutability() {
47     assume().that(graphIsMutable()).isTrue();
48 
49     Set<Integer> nodes = graph.nodes();
50     try {
51       nodes.add(N2);
52       fail(ERROR_MODIFIABLE_SET);
53     } catch (UnsupportedOperationException e) {
54       addNode(N1);
55       assertThat(graph.nodes()).containsExactlyElementsIn(nodes);
56     }
57   }
58 
59   @Override
60   @Test
adjacentNodes_checkReturnedSetMutability()61   public void adjacentNodes_checkReturnedSetMutability() {
62     assume().that(graphIsMutable()).isTrue();
63 
64     addNode(N1);
65     Set<Integer> adjacentNodes = graph.adjacentNodes(N1);
66     try {
67       adjacentNodes.add(N2);
68       fail(ERROR_MODIFIABLE_SET);
69     } catch (UnsupportedOperationException e) {
70       putEdge(N1, N2);
71       assertThat(graph.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes);
72     }
73   }
74 
75   @Override
76   @Test
predecessors_checkReturnedSetMutability()77   public void predecessors_checkReturnedSetMutability() {
78     assume().that(graphIsMutable()).isTrue();
79 
80     addNode(N2);
81     Set<Integer> predecessors = graph.predecessors(N2);
82     try {
83       predecessors.add(N1);
84       fail(ERROR_MODIFIABLE_SET);
85     } catch (UnsupportedOperationException e) {
86       putEdge(N1, N2);
87       assertThat(graph.predecessors(N2)).containsExactlyElementsIn(predecessors);
88     }
89   }
90 
91   @Override
92   @Test
successors_checkReturnedSetMutability()93   public void successors_checkReturnedSetMutability() {
94     assume().that(graphIsMutable()).isTrue();
95 
96     addNode(N1);
97     Set<Integer> successors = graph.successors(N1);
98     try {
99       successors.add(N2);
100       fail(ERROR_MODIFIABLE_SET);
101     } catch (UnsupportedOperationException e) {
102       putEdge(N1, N2);
103       assertThat(graph.successors(N1)).containsExactlyElementsIn(successors);
104     }
105   }
106 
107   @Override
108   @Test
incidentEdges_checkReturnedSetMutability()109   public void incidentEdges_checkReturnedSetMutability() {
110     assume().that(graphIsMutable()).isTrue();
111 
112     addNode(N1);
113     Set<EndpointPair<Integer>> incidentEdges = graph.incidentEdges(N1);
114     try {
115       incidentEdges.add(EndpointPair.unordered(N1, N2));
116       fail(ERROR_MODIFIABLE_SET);
117     } catch (UnsupportedOperationException e) {
118       putEdge(N1, N2);
119       assertThat(incidentEdges).containsExactlyElementsIn(graph.incidentEdges(N1));
120     }
121   }
122 
123   @Test
predecessors_oneEdge()124   public void predecessors_oneEdge() {
125     putEdge(N1, N2);
126     assertThat(graph.predecessors(N2)).containsExactly(N1);
127     assertThat(graph.predecessors(N1)).containsExactly(N2);
128   }
129 
130   @Test
successors_oneEdge()131   public void successors_oneEdge() {
132     putEdge(N1, N2);
133     assertThat(graph.successors(N1)).containsExactly(N2);
134     assertThat(graph.successors(N2)).containsExactly(N1);
135   }
136 
137   @Test
incidentEdges_oneEdge()138   public void incidentEdges_oneEdge() {
139     putEdge(N1, N2);
140     EndpointPair<Integer> expectedEndpoints = EndpointPair.unordered(N1, N2);
141     assertThat(graph.incidentEdges(N1)).containsExactly(expectedEndpoints);
142     assertThat(graph.incidentEdges(N2)).containsExactly(expectedEndpoints);
143   }
144 
145   @Test
inDegree_oneEdge()146   public void inDegree_oneEdge() {
147     putEdge(N1, N2);
148     assertThat(graph.inDegree(N2)).isEqualTo(1);
149     assertThat(graph.inDegree(N1)).isEqualTo(1);
150   }
151 
152   @Test
outDegree_oneEdge()153   public void outDegree_oneEdge() {
154     putEdge(N1, N2);
155     assertThat(graph.outDegree(N1)).isEqualTo(1);
156     assertThat(graph.outDegree(N2)).isEqualTo(1);
157   }
158 
159   @Test
hasEdgeConnecting_correct()160   public void hasEdgeConnecting_correct() {
161     putEdge(N1, N2);
162     assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(N1, N2))).isTrue();
163     assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(N2, N1))).isTrue();
164   }
165 
166   @Test
hasEdgeConnecting_mismatch()167   public void hasEdgeConnecting_mismatch() {
168     putEdge(N1, N2);
169     assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N1, N2))).isTrue();
170     assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N2, N1))).isTrue();
171   }
172 
173   @Test
adjacentNodes_selfLoop()174   public void adjacentNodes_selfLoop() {
175     assume().that(graph.allowsSelfLoops()).isTrue();
176 
177     putEdge(N1, N1);
178     putEdge(N1, N2);
179     assertThat(graph.adjacentNodes(N1)).containsExactly(N1, N2);
180   }
181 
182   @Test
predecessors_selfLoop()183   public void predecessors_selfLoop() {
184     assume().that(graph.allowsSelfLoops()).isTrue();
185 
186     putEdge(N1, N1);
187     assertThat(graph.predecessors(N1)).containsExactly(N1);
188     putEdge(N1, N2);
189     assertThat(graph.predecessors(N1)).containsExactly(N1, N2);
190   }
191 
192   @Test
successors_selfLoop()193   public void successors_selfLoop() {
194     assume().that(graph.allowsSelfLoops()).isTrue();
195 
196     putEdge(N1, N1);
197     assertThat(graph.successors(N1)).containsExactly(N1);
198     putEdge(N2, N1);
199     assertThat(graph.successors(N1)).containsExactly(N1, N2);
200   }
201 
202   @Test
incidentEdges_selfLoop()203   public void incidentEdges_selfLoop() {
204     assume().that(graph.allowsSelfLoops()).isTrue();
205 
206     putEdge(N1, N1);
207     assertThat(graph.incidentEdges(N1)).containsExactly(EndpointPair.unordered(N1, N1));
208     putEdge(N1, N2);
209     assertThat(graph.incidentEdges(N1))
210         .containsExactly(EndpointPair.unordered(N1, N1), EndpointPair.unordered(N1, N2));
211   }
212 
213   @Test
degree_selfLoop()214   public void degree_selfLoop() {
215     assume().that(graph.allowsSelfLoops()).isTrue();
216 
217     putEdge(N1, N1);
218     assertThat(graph.degree(N1)).isEqualTo(2);
219     putEdge(N1, N2);
220     assertThat(graph.degree(N1)).isEqualTo(3);
221   }
222 
223   @Test
inDegree_selfLoop()224   public void inDegree_selfLoop() {
225     assume().that(graph.allowsSelfLoops()).isTrue();
226 
227     putEdge(N1, N1);
228     assertThat(graph.inDegree(N1)).isEqualTo(2);
229     putEdge(N1, N2);
230     assertThat(graph.inDegree(N1)).isEqualTo(3);
231   }
232 
233   @Test
outDegree_selfLoop()234   public void outDegree_selfLoop() {
235     assume().that(graph.allowsSelfLoops()).isTrue();
236 
237     putEdge(N1, N1);
238     assertThat(graph.outDegree(N1)).isEqualTo(2);
239     putEdge(N2, N1);
240     assertThat(graph.outDegree(N1)).isEqualTo(3);
241   }
242 
243   // Stable order tests
244 
245   // Note: Stable order means that the ordering doesn't change between iterations and versions.
246   // Ideally, the ordering in test should never be updated.
247   @Test
stableIncidentEdgeOrder_edges_returnsInStableOrder()248   public void stableIncidentEdgeOrder_edges_returnsInStableOrder() {
249     assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
250 
251     populateTShapedGraph();
252 
253     assertThat(graph.edges())
254         .containsExactly(
255             EndpointPair.unordered(1, 2),
256             EndpointPair.unordered(1, 4),
257             EndpointPair.unordered(1, 3),
258             EndpointPair.unordered(4, 5))
259         .inOrder();
260   }
261 
262   @Test
stableIncidentEdgeOrder_adjacentNodes_returnsInConnectingEdgeInsertionOrder()263   public void stableIncidentEdgeOrder_adjacentNodes_returnsInConnectingEdgeInsertionOrder() {
264     assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
265 
266     populateTShapedGraph();
267 
268     assertThat(graph.adjacentNodes(1)).containsExactly(2, 4, 3).inOrder();
269   }
270 
271   @Test
stableIncidentEdgeOrder_predecessors_returnsInConnectingEdgeInsertionOrder()272   public void stableIncidentEdgeOrder_predecessors_returnsInConnectingEdgeInsertionOrder() {
273     assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
274 
275     populateTShapedGraph();
276 
277     assertThat(graph.adjacentNodes(1)).containsExactly(2, 4, 3).inOrder();
278   }
279 
280   @Test
stableIncidentEdgeOrder_successors_returnsInConnectingEdgeInsertionOrder()281   public void stableIncidentEdgeOrder_successors_returnsInConnectingEdgeInsertionOrder() {
282     assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
283 
284     populateTShapedGraph();
285 
286     assertThat(graph.adjacentNodes(1)).containsExactly(2, 4, 3).inOrder();
287   }
288 
289   @Test
stableIncidentEdgeOrder_incidentEdges_returnsInEdgeInsertionOrder()290   public void stableIncidentEdgeOrder_incidentEdges_returnsInEdgeInsertionOrder() {
291     assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
292 
293     populateTShapedGraph();
294 
295     assertThat(graph.incidentEdges(1))
296         .containsExactly(
297             EndpointPair.unordered(1, 2),
298             EndpointPair.unordered(1, 4),
299             EndpointPair.unordered(1, 3))
300         .inOrder();
301   }
302 
303   @Test
stableIncidentEdgeOrder_incidentEdges_withSelfLoop_returnsInEdgeInsertionOrder()304   public void stableIncidentEdgeOrder_incidentEdges_withSelfLoop_returnsInEdgeInsertionOrder() {
305     assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
306     assume().that(graph.allowsSelfLoops()).isTrue();
307 
308     putEdge(2, 1);
309     putEdge(1, 1);
310     putEdge(1, 3);
311 
312     assertThat(graph.incidentEdges(1))
313         .containsExactly(
314             EndpointPair.unordered(2, 1),
315             EndpointPair.unordered(1, 1),
316             EndpointPair.unordered(1, 3))
317         .inOrder();
318   }
319 
320   /**
321    * Populates the graph with nodes and edges in a star shape with node `1` in the middle.
322    *
323    * <p>Note that the edges are added in a shuffled order to properly test the effect of the
324    * insertion order.
325    */
populateTShapedGraph()326   private void populateTShapedGraph() {
327     putEdge(2, 1);
328     putEdge(1, 4);
329     putEdge(1, 3);
330     putEdge(1, 2); // Duplicate
331     putEdge(4, 5);
332   }
333 
334   // Element Mutation
335 
336   @Test
putEdge_existingNodes()337   public void putEdge_existingNodes() {
338     assume().that(graphIsMutable()).isTrue();
339 
340     // Adding nodes initially for safety (insulating from possible future
341     // modifications to proxy methods)
342     addNode(N1);
343     addNode(N2);
344 
345     assertThat(graphAsMutableGraph.putEdge(N1, N2)).isTrue();
346   }
347 
348   @Test
putEdge_existingEdgeBetweenSameNodes()349   public void putEdge_existingEdgeBetweenSameNodes() {
350     assume().that(graphIsMutable()).isTrue();
351 
352     putEdge(N1, N2);
353 
354     assertThat(graphAsMutableGraph.putEdge(N2, N1)).isFalse();
355   }
356 
357   /**
358    * Tests that the method {@code putEdge} will silently add the missing nodes to the graph, then
359    * add the edge connecting them. We are not using the proxy methods here as we want to test {@code
360    * putEdge} when the end-points are not elements of the graph.
361    */
362   @Test
putEdge_nodesNotInGraph()363   public void putEdge_nodesNotInGraph() {
364     assume().that(graphIsMutable()).isTrue();
365 
366     graphAsMutableGraph.addNode(N1);
367     assertTrue(graphAsMutableGraph.putEdge(N1, N5));
368     assertTrue(graphAsMutableGraph.putEdge(N4, N1));
369     assertTrue(graphAsMutableGraph.putEdge(N2, N3));
370     assertThat(graph.nodes()).containsExactly(N1, N5, N4, N2, N3).inOrder();
371     assertThat(graph.adjacentNodes(N1)).containsExactly(N4, N5);
372     assertThat(graph.adjacentNodes(N2)).containsExactly(N3);
373     assertThat(graph.adjacentNodes(N3)).containsExactly(N2);
374     assertThat(graph.adjacentNodes(N4)).containsExactly(N1);
375     assertThat(graph.adjacentNodes(N5)).containsExactly(N1);
376   }
377 
378   @Test
putEdge_doesntAllowSelfLoops()379   public void putEdge_doesntAllowSelfLoops() {
380     assume().that(graphIsMutable()).isTrue();
381     assume().that(graph.allowsSelfLoops()).isFalse();
382 
383     try {
384       putEdge(N1, N1);
385       fail(ERROR_ADDED_SELF_LOOP);
386     } catch (IllegalArgumentException e) {
387       assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP);
388     }
389   }
390 
391   @Test
putEdge_allowsSelfLoops()392   public void putEdge_allowsSelfLoops() {
393     assume().that(graphIsMutable()).isTrue();
394     assume().that(graph.allowsSelfLoops()).isTrue();
395 
396     assertThat(graphAsMutableGraph.putEdge(N1, N1)).isTrue();
397     assertThat(graph.adjacentNodes(N1)).containsExactly(N1);
398   }
399 
400   @Test
putEdge_existingSelfLoopEdgeBetweenSameNodes()401   public void putEdge_existingSelfLoopEdgeBetweenSameNodes() {
402     assume().that(graphIsMutable()).isTrue();
403     assume().that(graph.allowsSelfLoops()).isTrue();
404 
405     putEdge(N1, N1);
406     assertThat(graphAsMutableGraph.putEdge(N1, N1)).isFalse();
407   }
408 
409   @Test
removeEdge_antiparallelEdges()410   public void removeEdge_antiparallelEdges() {
411     assume().that(graphIsMutable()).isTrue();
412 
413     putEdge(N1, N2);
414     putEdge(N2, N1); // no-op
415 
416     assertThat(graphAsMutableGraph.removeEdge(N1, N2)).isTrue();
417     assertThat(graph.adjacentNodes(N1)).isEmpty();
418     assertThat(graph.edges()).isEmpty();
419     assertThat(graphAsMutableGraph.removeEdge(N2, N1)).isFalse();
420   }
421 
422   @Test
removeNode_existingNodeWithSelfLoopEdge()423   public void removeNode_existingNodeWithSelfLoopEdge() {
424     assume().that(graphIsMutable()).isTrue();
425     assume().that(graph.allowsSelfLoops()).isTrue();
426 
427     addNode(N1);
428     putEdge(N1, N1);
429     assertThat(graphAsMutableGraph.removeNode(N1)).isTrue();
430     assertThat(graph.nodes()).isEmpty();
431   }
432 
433   @Test
removeEdge_existingSelfLoopEdge()434   public void removeEdge_existingSelfLoopEdge() {
435     assume().that(graphIsMutable()).isTrue();
436     assume().that(graph.allowsSelfLoops()).isTrue();
437 
438     putEdge(N1, N1);
439     assertThat(graphAsMutableGraph.removeEdge(N1, N1)).isTrue();
440     assertThat(graph.nodes()).containsExactly(N1);
441     assertThat(graph.adjacentNodes(N1)).isEmpty();
442   }
443 }
444