• 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.ERROR_NODE_NOT_IN_GRAPH;
20 import static com.google.common.graph.TestUtil.assertEdgeNotInGraphErrorMessage;
21 import static com.google.common.graph.TestUtil.assertNodeNotInGraphErrorMessage;
22 import static com.google.common.graph.TestUtil.assertStronglyEquivalent;
23 import static com.google.common.graph.TestUtil.sanityCheckSet;
24 import static com.google.common.truth.Truth.assertThat;
25 import static com.google.common.truth.TruthJUnit.assume;
26 import static java.util.concurrent.Executors.newFixedThreadPool;
27 import static org.junit.Assert.assertFalse;
28 import static org.junit.Assert.assertTrue;
29 import static org.junit.Assert.fail;
30 
31 import com.google.common.collect.ImmutableList;
32 import com.google.common.collect.ImmutableSet;
33 import com.google.common.collect.Sets;
34 import java.util.Set;
35 import java.util.concurrent.Callable;
36 import java.util.concurrent.CyclicBarrier;
37 import java.util.concurrent.ExecutorService;
38 import java.util.concurrent.Future;
39 import org.junit.After;
40 import org.junit.Before;
41 import org.junit.Test;
42 
43 /**
44  * Abstract base class for testing implementations of {@link Network} interface. Network instances
45  * created for testing should have Integer node and String edge objects.
46  *
47  * <p>Test cases that should be handled similarly in any graph implementation are included in this
48  * class. For example, testing that {@code nodes()} method returns the set of the nodes in the
49  * graph. The following test cases are left for the subclasses to handle:
50  *
51  * <ul>
52  *   <li>Test cases related to whether the graph is directed, undirected, mutable, or immutable.
53  *   <li>Test cases related to the specific implementation of the {@link Network} interface.
54  * </ul>
55  *
56  * TODO(user): Make this class generic (using <N, E>) for all node and edge types.
57  * TODO(user): Differentiate between directed and undirected edge strings.
58  */
59 public abstract class AbstractNetworkTest {
60 
61   Network<Integer, String> network;
62 
63   /**
64    * The same reference as {@link #network}, except as a mutable network. This field is null in case
65    * {@link #createGraph()} didn't return a mutable network.
66    */
67   MutableNetwork<Integer, String> networkAsMutableNetwork;
68 
69   static final Integer N1 = 1;
70   static final Integer N2 = 2;
71   static final Integer N3 = 3;
72   static final Integer N4 = 4;
73   static final Integer N5 = 5;
74   static final Integer NODE_NOT_IN_GRAPH = 1000;
75 
76   static final String E11 = "1-1";
77   static final String E11_A = "1-1a";
78   static final String E12 = "1-2";
79   static final String E12_A = "1-2a";
80   static final String E12_B = "1-2b";
81   static final String E21 = "2-1";
82   static final String E13 = "1-3";
83   static final String E14 = "1-4";
84   static final String E23 = "2-3";
85   static final String E31 = "3-1";
86   static final String E34 = "3-4";
87   static final String E41 = "4-1";
88   static final String E15 = "1-5";
89   static final String EDGE_NOT_IN_GRAPH = "edgeNotInGraph";
90 
91   // TODO(user): Consider separating Strings that we've defined here to capture
92   // identifiable substrings of expected error messages, from Strings that we've defined
93   // here to provide error messages.
94   // TODO(user): Some Strings used in the subclasses can be added as static Strings
95   // here too.
96   static final String ERROR_PARALLEL_EDGE = "connected by a different edge";
97   static final String ERROR_REUSE_EDGE = "it cannot be reused to connect";
98   static final String ERROR_MODIFIABLE_COLLECTION =
99       "Collection returned is unexpectedly modifiable";
100   static final String ERROR_SELF_LOOP = "self-loops are not allowed";
101   static final String ERROR_EDGE_NOT_IN_GRAPH =
102       "Should not be allowed to pass an edge that is not an element of the graph.";
103   static final String ERROR_ADDED_SELF_LOOP = "Should not be allowed to add a self-loop edge.";
104   static final String ERROR_ADDED_PARALLEL_EDGE = "Should not be allowed to add a parallel edge.";
105   static final String ERROR_ADDED_EXISTING_EDGE =
106       "Reusing an existing edge to connect different nodes succeeded";
107 
108   /** Creates and returns an instance of the graph to be tested. */
createGraph()109   abstract Network<Integer, String> createGraph();
110 
111   /**
112    * A proxy method that adds the node {@code n} to the graph being tested. In case of Immutable
113    * graph implementations, this method should replace {@link #network} with a new graph that
114    * includes this node.
115    */
addNode(Integer n)116   abstract void addNode(Integer n);
117 
118   /**
119    * A proxy method that adds the edge {@code e} to the graph being tested. In case of Immutable
120    * graph implementations, this method should replace {@link #network} with a new graph that
121    * includes this edge.
122    */
addEdge(Integer n1, Integer n2, String e)123   abstract void addEdge(Integer n1, Integer n2, String e);
124 
graphIsMutable()125   final boolean graphIsMutable() {
126     return networkAsMutableNetwork != null;
127   }
128 
129   @Before
init()130   public void init() {
131     network = createGraph();
132     if (network instanceof MutableNetwork) {
133       networkAsMutableNetwork = (MutableNetwork<Integer, String>) network;
134     }
135   }
136 
137   @After
validateNetworkState()138   public void validateNetworkState() {
139     validateNetwork(network);
140   }
141 
validateNetwork(Network<N, E> network)142   static <N, E> void validateNetwork(Network<N, E> network) {
143     assertStronglyEquivalent(network, Graphs.copyOf(network));
144     assertStronglyEquivalent(network, ImmutableNetwork.copyOf(network));
145 
146     String networkString = network.toString();
147     assertThat(networkString).contains("isDirected: " + network.isDirected());
148     assertThat(networkString).contains("allowsParallelEdges: " + network.allowsParallelEdges());
149     assertThat(networkString).contains("allowsSelfLoops: " + network.allowsSelfLoops());
150 
151     int nodeStart = networkString.indexOf("nodes:");
152     int edgeStart = networkString.indexOf("edges:");
153     String nodeString = networkString.substring(nodeStart, edgeStart);
154     String edgeString = networkString.substring(edgeStart);
155 
156     Graph<N> asGraph = network.asGraph();
157     AbstractGraphTest.validateGraph(asGraph);
158     assertThat(network.nodes()).isEqualTo(asGraph.nodes());
159     assertThat(network.edges().size()).isAtLeast(asGraph.edges().size());
160     assertThat(network.nodeOrder()).isEqualTo(asGraph.nodeOrder());
161     assertThat(network.isDirected()).isEqualTo(asGraph.isDirected());
162     assertThat(network.allowsSelfLoops()).isEqualTo(asGraph.allowsSelfLoops());
163 
164     for (E edge : sanityCheckSet(network.edges())) {
165       // TODO(b/27817069): Consider verifying the edge's incident nodes in the string.
166       assertThat(edgeString).contains(edge.toString());
167 
168       EndpointPair<N> endpointPair = network.incidentNodes(edge);
169       N nodeU = endpointPair.nodeU();
170       N nodeV = endpointPair.nodeV();
171       assertThat(asGraph.edges()).contains(EndpointPair.of(network, nodeU, nodeV));
172       assertThat(network.edgesConnecting(nodeU, nodeV)).contains(edge);
173       assertThat(network.successors(nodeU)).contains(nodeV);
174       assertThat(network.adjacentNodes(nodeU)).contains(nodeV);
175       assertThat(network.outEdges(nodeU)).contains(edge);
176       assertThat(network.incidentEdges(nodeU)).contains(edge);
177       assertThat(network.predecessors(nodeV)).contains(nodeU);
178       assertThat(network.adjacentNodes(nodeV)).contains(nodeU);
179       assertThat(network.inEdges(nodeV)).contains(edge);
180       assertThat(network.incidentEdges(nodeV)).contains(edge);
181 
182       for (N incidentNode : network.incidentNodes(edge)) {
183         assertThat(network.nodes()).contains(incidentNode);
184         for (E adjacentEdge : network.incidentEdges(incidentNode)) {
185           assertTrue(
186               edge.equals(adjacentEdge) || network.adjacentEdges(edge).contains(adjacentEdge));
187         }
188       }
189     }
190 
191     for (N node : sanityCheckSet(network.nodes())) {
192       assertThat(nodeString).contains(node.toString());
193 
194       assertThat(network.adjacentNodes(node)).isEqualTo(asGraph.adjacentNodes(node));
195       assertThat(network.predecessors(node)).isEqualTo(asGraph.predecessors(node));
196       assertThat(network.successors(node)).isEqualTo(asGraph.successors(node));
197 
198       int selfLoopCount = network.edgesConnecting(node, node).size();
199       assertThat(network.incidentEdges(node).size() + selfLoopCount)
200           .isEqualTo(network.degree(node));
201 
202       if (network.isDirected()) {
203         assertThat(network.incidentEdges(node).size() + selfLoopCount)
204             .isEqualTo(network.inDegree(node) + network.outDegree(node));
205         assertThat(network.inEdges(node)).hasSize(network.inDegree(node));
206         assertThat(network.outEdges(node)).hasSize(network.outDegree(node));
207       } else {
208         assertThat(network.predecessors(node)).isEqualTo(network.adjacentNodes(node));
209         assertThat(network.successors(node)).isEqualTo(network.adjacentNodes(node));
210         assertThat(network.inEdges(node)).isEqualTo(network.incidentEdges(node));
211         assertThat(network.outEdges(node)).isEqualTo(network.incidentEdges(node));
212         assertThat(network.inDegree(node)).isEqualTo(network.degree(node));
213         assertThat(network.outDegree(node)).isEqualTo(network.degree(node));
214       }
215 
216       for (N otherNode : network.nodes()) {
217         Set<E> edgesConnecting = sanityCheckSet(network.edgesConnecting(node, otherNode));
218         switch (edgesConnecting.size()) {
219           case 0:
220             assertThat(network.edgeConnectingOrNull(node, otherNode)).isNull();
221             assertThat(network.edgeConnecting(node, otherNode).isPresent()).isFalse();
222             assertThat(network.hasEdgeConnecting(node, otherNode)).isFalse();
223             break;
224           case 1:
225             E edge = edgesConnecting.iterator().next();
226             assertThat(network.edgeConnectingOrNull(node, otherNode)).isEqualTo(edge);
227             assertThat(network.edgeConnecting(node, otherNode).get()).isEqualTo(edge);
228             assertThat(network.hasEdgeConnecting(node, otherNode)).isTrue();
229             break;
230           default:
231             assertThat(network.hasEdgeConnecting(node, otherNode)).isTrue();
232             try {
233               network.edgeConnectingOrNull(node, otherNode);
234               fail();
235             } catch (IllegalArgumentException expected) {
236             }
237             try {
238               network.edgeConnecting(node, otherNode);
239               fail();
240             } catch (IllegalArgumentException expected) {
241             }
242         }
243 
244         boolean isSelfLoop = node.equals(otherNode);
245         boolean connected = !edgesConnecting.isEmpty();
246         if (network.isDirected() || !isSelfLoop) {
247           assertThat(edgesConnecting)
248               .isEqualTo(Sets.intersection(network.outEdges(node), network.inEdges(otherNode)));
249         }
250         if (!network.allowsParallelEdges()) {
251           assertThat(edgesConnecting.size()).isAtMost(1);
252         }
253         if (!network.allowsSelfLoops() && isSelfLoop) {
254           assertThat(connected).isFalse();
255         }
256 
257         assertThat(network.successors(node).contains(otherNode)).isEqualTo(connected);
258         assertThat(network.predecessors(otherNode).contains(node)).isEqualTo(connected);
259         for (E edge : edgesConnecting) {
260           assertThat(network.incidentNodes(edge))
261               .isEqualTo(EndpointPair.of(network, node, otherNode));
262           assertThat(network.outEdges(node)).contains(edge);
263           assertThat(network.inEdges(otherNode)).contains(edge);
264         }
265       }
266 
267       for (N adjacentNode : sanityCheckSet(network.adjacentNodes(node))) {
268         assertTrue(
269             network.predecessors(node).contains(adjacentNode)
270                 || network.successors(node).contains(adjacentNode));
271         assertTrue(
272             !network.edgesConnecting(node, adjacentNode).isEmpty()
273                 || !network.edgesConnecting(adjacentNode, node).isEmpty());
274       }
275 
276       for (N predecessor : sanityCheckSet(network.predecessors(node))) {
277         assertThat(network.successors(predecessor)).contains(node);
278         assertThat(network.edgesConnecting(predecessor, node)).isNotEmpty();
279       }
280 
281       for (N successor : sanityCheckSet(network.successors(node))) {
282         assertThat(network.predecessors(successor)).contains(node);
283         assertThat(network.edgesConnecting(node, successor)).isNotEmpty();
284       }
285 
286       for (E incidentEdge : sanityCheckSet(network.incidentEdges(node))) {
287         assertTrue(
288             network.inEdges(node).contains(incidentEdge)
289                 || network.outEdges(node).contains(incidentEdge));
290         assertThat(network.edges()).contains(incidentEdge);
291         assertThat(network.incidentNodes(incidentEdge)).contains(node);
292       }
293 
294       for (E inEdge : sanityCheckSet(network.inEdges(node))) {
295         assertThat(network.incidentEdges(node)).contains(inEdge);
296         assertThat(network.outEdges(network.incidentNodes(inEdge).adjacentNode(node)))
297             .contains(inEdge);
298         if (network.isDirected()) {
299           assertThat(network.incidentNodes(inEdge).target()).isEqualTo(node);
300         }
301       }
302 
303       for (E outEdge : sanityCheckSet(network.outEdges(node))) {
304         assertThat(network.incidentEdges(node)).contains(outEdge);
305         assertThat(network.inEdges(network.incidentNodes(outEdge).adjacentNode(node)))
306             .contains(outEdge);
307         if (network.isDirected()) {
308           assertThat(network.incidentNodes(outEdge).source()).isEqualTo(node);
309         }
310       }
311     }
312   }
313 
314   /**
315    * Verifies that the {@code Set} returned by {@code nodes} has the expected mutability property
316    * (see the {@code Network} documentation for more information).
317    */
318   @Test
nodes_checkReturnedSetMutability()319   public abstract void nodes_checkReturnedSetMutability();
320 
321   /**
322    * Verifies that the {@code Set} returned by {@code edges} has the expected mutability property
323    * (see the {@code Network} documentation for more information).
324    */
325   @Test
edges_checkReturnedSetMutability()326   public abstract void edges_checkReturnedSetMutability();
327 
328   /**
329    * Verifies that the {@code Set} returned by {@code incidentEdges} has the expected mutability
330    * property (see the {@code Network} documentation for more information).
331    */
332   @Test
incidentEdges_checkReturnedSetMutability()333   public abstract void incidentEdges_checkReturnedSetMutability();
334 
335   /**
336    * Verifies that the {@code Set} returned by {@code adjacentNodes} has the expected mutability
337    * property (see the {@code Network} documentation for more information).
338    */
339   @Test
adjacentNodes_checkReturnedSetMutability()340   public abstract void adjacentNodes_checkReturnedSetMutability();
341 
342   /**
343    * Verifies that the {@code Set} returned by {@code adjacentEdges} has the expected mutability
344    * property (see the {@code Network} documentation for more information).
345    */
346   @Test
adjacentEdges_checkReturnedSetMutability()347   public abstract void adjacentEdges_checkReturnedSetMutability();
348 
349   /**
350    * Verifies that the {@code Set} returned by {@code edgesConnecting} has the expected mutability
351    * property (see the {@code Network} documentation for more information).
352    */
353   @Test
edgesConnecting_checkReturnedSetMutability()354   public abstract void edgesConnecting_checkReturnedSetMutability();
355 
356   /**
357    * Verifies that the {@code Set} returned by {@code inEdges} has the expected mutability property
358    * (see the {@code Network} documentation for more information).
359    */
360   @Test
inEdges_checkReturnedSetMutability()361   public abstract void inEdges_checkReturnedSetMutability();
362 
363   /**
364    * Verifies that the {@code Set} returned by {@code outEdges} has the expected mutability property
365    * (see the {@code Network} documentation for more information).
366    */
367   @Test
outEdges_checkReturnedSetMutability()368   public abstract void outEdges_checkReturnedSetMutability();
369 
370   /**
371    * Verifies that the {@code Set} returned by {@code predecessors} has the expected mutability
372    * property (see the {@code Network} documentation for more information).
373    */
374   @Test
predecessors_checkReturnedSetMutability()375   public abstract void predecessors_checkReturnedSetMutability();
376 
377   /**
378    * Verifies that the {@code Set} returned by {@code successors} has the expected mutability
379    * property (see the {@code Network} documentation for more information).
380    */
381   @Test
successors_checkReturnedSetMutability()382   public abstract void successors_checkReturnedSetMutability();
383 
384   @Test
nodes_oneNode()385   public void nodes_oneNode() {
386     addNode(N1);
387     assertThat(network.nodes()).containsExactly(N1);
388   }
389 
390   @Test
nodes_noNodes()391   public void nodes_noNodes() {
392     assertThat(network.nodes()).isEmpty();
393   }
394 
395   @Test
edges_oneEdge()396   public void edges_oneEdge() {
397     addEdge(N1, N2, E12);
398     assertThat(network.edges()).containsExactly(E12);
399   }
400 
401   @Test
edges_noEdges()402   public void edges_noEdges() {
403     assertThat(network.edges()).isEmpty();
404     // Network with no edges, given disconnected nodes
405     addNode(N1);
406     addNode(N2);
407     assertThat(network.edges()).isEmpty();
408   }
409 
410   @Test
incidentEdges_oneEdge()411   public void incidentEdges_oneEdge() {
412     addEdge(N1, N2, E12);
413     assertThat(network.incidentEdges(N2)).containsExactly(E12);
414     assertThat(network.incidentEdges(N1)).containsExactly(E12);
415   }
416 
417   @Test
incidentEdges_isolatedNode()418   public void incidentEdges_isolatedNode() {
419     addNode(N1);
420     assertThat(network.incidentEdges(N1)).isEmpty();
421   }
422 
423   @Test
incidentEdges_nodeNotInGraph()424   public void incidentEdges_nodeNotInGraph() {
425     try {
426       network.incidentEdges(NODE_NOT_IN_GRAPH);
427       fail(ERROR_NODE_NOT_IN_GRAPH);
428     } catch (IllegalArgumentException e) {
429       assertNodeNotInGraphErrorMessage(e);
430     }
431   }
432 
433   @Test
incidentNodes_oneEdge()434   public void incidentNodes_oneEdge() {
435     addEdge(N1, N2, E12);
436     assertThat(network.incidentNodes(E12)).containsExactly(N1, N2);
437   }
438 
439   @Test
incidentNodes_edgeNotInGraph()440   public void incidentNodes_edgeNotInGraph() {
441     try {
442       network.incidentNodes(EDGE_NOT_IN_GRAPH);
443       fail(ERROR_EDGE_NOT_IN_GRAPH);
444     } catch (IllegalArgumentException e) {
445       assertEdgeNotInGraphErrorMessage(e);
446     }
447   }
448 
449   @Test
adjacentNodes_oneEdge()450   public void adjacentNodes_oneEdge() {
451     addEdge(N1, N2, E12);
452     assertThat(network.adjacentNodes(N1)).containsExactly(N2);
453     assertThat(network.adjacentNodes(N2)).containsExactly(N1);
454   }
455 
456   @Test
adjacentNodes_noAdjacentNodes()457   public void adjacentNodes_noAdjacentNodes() {
458     addNode(N1);
459     assertThat(network.adjacentNodes(N1)).isEmpty();
460   }
461 
462   @Test
adjacentNodes_nodeNotInGraph()463   public void adjacentNodes_nodeNotInGraph() {
464     try {
465       network.adjacentNodes(NODE_NOT_IN_GRAPH);
466       fail(ERROR_NODE_NOT_IN_GRAPH);
467     } catch (IllegalArgumentException e) {
468       assertNodeNotInGraphErrorMessage(e);
469     }
470   }
471 
472   @Test
adjacentEdges_bothEndpoints()473   public void adjacentEdges_bothEndpoints() {
474     addEdge(N1, N2, E12);
475     addEdge(N2, N3, E23);
476     addEdge(N3, N1, E31);
477     addEdge(N3, N4, E34);
478     assertThat(network.adjacentEdges(E12)).containsExactly(E31, E23);
479   }
480 
481   @Test
adjacentEdges_noAdjacentEdges()482   public void adjacentEdges_noAdjacentEdges() {
483     addEdge(N1, N2, E12);
484     addEdge(N3, N4, E34);
485     assertThat(network.adjacentEdges(E12)).isEmpty();
486   }
487 
488   @Test
adjacentEdges_edgeNotInGraph()489   public void adjacentEdges_edgeNotInGraph() {
490     try {
491       network.adjacentEdges(EDGE_NOT_IN_GRAPH);
492       fail(ERROR_EDGE_NOT_IN_GRAPH);
493     } catch (IllegalArgumentException e) {
494       assertEdgeNotInGraphErrorMessage(e);
495     }
496   }
497 
498   @Test
adjacentEdges_parallelEdges()499   public void adjacentEdges_parallelEdges() {
500     assume().that(network.allowsParallelEdges()).isTrue();
501 
502     addEdge(N1, N2, E12);
503     addEdge(N1, N2, E12_A);
504     addEdge(N1, N2, E12_B);
505     addEdge(N3, N4, E34);
506 
507     assertThat(network.adjacentEdges(E12)).containsExactly(E12_A, E12_B);
508   }
509 
510   @Test
edgesConnecting_disconnectedNodes()511   public void edgesConnecting_disconnectedNodes() {
512     addNode(N1);
513     addNode(N2);
514     assertThat(network.edgesConnecting(N1, N2)).isEmpty();
515   }
516 
517   @Test
edgesConnecting_nodesNotInGraph()518   public void edgesConnecting_nodesNotInGraph() {
519     addNode(N1);
520     addNode(N2);
521     try {
522       network.edgesConnecting(N1, NODE_NOT_IN_GRAPH);
523       fail(ERROR_NODE_NOT_IN_GRAPH);
524     } catch (IllegalArgumentException e) {
525       assertNodeNotInGraphErrorMessage(e);
526     }
527     try {
528       network.edgesConnecting(NODE_NOT_IN_GRAPH, N2);
529       fail(ERROR_NODE_NOT_IN_GRAPH);
530     } catch (IllegalArgumentException e) {
531       assertNodeNotInGraphErrorMessage(e);
532     }
533     try {
534       network.edgesConnecting(NODE_NOT_IN_GRAPH, NODE_NOT_IN_GRAPH);
535       fail(ERROR_NODE_NOT_IN_GRAPH);
536     } catch (IllegalArgumentException e) {
537       assertNodeNotInGraphErrorMessage(e);
538     }
539   }
540 
541   @Test
edgesConnecting_parallelEdges_directed()542   public void edgesConnecting_parallelEdges_directed() {
543     assume().that(network.allowsParallelEdges()).isTrue();
544     assume().that(network.isDirected()).isTrue();
545 
546     addEdge(N1, N2, E12);
547     addEdge(N1, N2, E12_A);
548 
549     assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12, E12_A);
550     // Passed nodes should be in the correct edge direction, first is the
551     // source node and the second is the target node
552     assertThat(network.edgesConnecting(N2, N1)).isEmpty();
553   }
554 
555   @Test
edgesConnecting_parallelEdges_undirected()556   public void edgesConnecting_parallelEdges_undirected() {
557     assume().that(network.allowsParallelEdges()).isTrue();
558     assume().that(network.isDirected()).isFalse();
559 
560     addEdge(N1, N2, E12);
561     addEdge(N1, N2, E12_A);
562     addEdge(N2, N1, E21);
563 
564     assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12, E12_A, E21);
565     assertThat(network.edgesConnecting(N2, N1)).containsExactly(E12, E12_A, E21);
566   }
567 
568   @Test
edgesConnecting_parallelSelfLoopEdges()569   public void edgesConnecting_parallelSelfLoopEdges() {
570     assume().that(network.allowsParallelEdges()).isTrue();
571     assume().that(network.allowsSelfLoops()).isTrue();
572 
573     addEdge(N1, N1, E11);
574     addEdge(N1, N1, E11_A);
575 
576     assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11, E11_A);
577   }
578 
579   @Test
hasEdgeConnecting_disconnectedNodes()580   public void hasEdgeConnecting_disconnectedNodes() {
581     addNode(N1);
582     addNode(N2);
583     assertThat(network.hasEdgeConnecting(N1, N2)).isFalse();
584   }
585 
586   @Test
hasEdgesConnecting_nodesNotInGraph()587   public void hasEdgesConnecting_nodesNotInGraph() {
588     addNode(N1);
589     addNode(N2);
590     assertThat(network.hasEdgeConnecting(N1, NODE_NOT_IN_GRAPH)).isFalse();
591     assertThat(network.hasEdgeConnecting(NODE_NOT_IN_GRAPH, N2)).isFalse();
592     assertThat(network.hasEdgeConnecting(NODE_NOT_IN_GRAPH, NODE_NOT_IN_GRAPH)).isFalse();
593   }
594 
595   @Test
inEdges_noInEdges()596   public void inEdges_noInEdges() {
597     addNode(N1);
598     assertThat(network.inEdges(N1)).isEmpty();
599   }
600 
601   @Test
inEdges_nodeNotInGraph()602   public void inEdges_nodeNotInGraph() {
603     try {
604       network.inEdges(NODE_NOT_IN_GRAPH);
605       fail(ERROR_NODE_NOT_IN_GRAPH);
606     } catch (IllegalArgumentException e) {
607       assertNodeNotInGraphErrorMessage(e);
608     }
609   }
610 
611   @Test
outEdges_noOutEdges()612   public void outEdges_noOutEdges() {
613     addNode(N1);
614     assertThat(network.outEdges(N1)).isEmpty();
615   }
616 
617   @Test
outEdges_nodeNotInGraph()618   public void outEdges_nodeNotInGraph() {
619     try {
620       network.outEdges(NODE_NOT_IN_GRAPH);
621       fail(ERROR_NODE_NOT_IN_GRAPH);
622     } catch (IllegalArgumentException e) {
623       assertNodeNotInGraphErrorMessage(e);
624     }
625   }
626 
627   @Test
predecessors_noPredecessors()628   public void predecessors_noPredecessors() {
629     addNode(N1);
630     assertThat(network.predecessors(N1)).isEmpty();
631   }
632 
633   @Test
predecessors_nodeNotInGraph()634   public void predecessors_nodeNotInGraph() {
635     try {
636       network.predecessors(NODE_NOT_IN_GRAPH);
637       fail(ERROR_NODE_NOT_IN_GRAPH);
638     } catch (IllegalArgumentException e) {
639       assertNodeNotInGraphErrorMessage(e);
640     }
641   }
642 
643   @Test
successors_noSuccessors()644   public void successors_noSuccessors() {
645     addNode(N1);
646     assertThat(network.successors(N1)).isEmpty();
647   }
648 
649   @Test
successors_nodeNotInGraph()650   public void successors_nodeNotInGraph() {
651     try {
652       network.successors(NODE_NOT_IN_GRAPH);
653       fail(ERROR_NODE_NOT_IN_GRAPH);
654     } catch (IllegalArgumentException e) {
655       assertNodeNotInGraphErrorMessage(e);
656     }
657   }
658 
659   @Test
addNode_newNode()660   public void addNode_newNode() {
661     assume().that(graphIsMutable()).isTrue();
662 
663     assertTrue(networkAsMutableNetwork.addNode(N1));
664     assertThat(networkAsMutableNetwork.nodes()).contains(N1);
665   }
666 
667   @Test
addNode_existingNode()668   public void addNode_existingNode() {
669     assume().that(graphIsMutable()).isTrue();
670 
671     addNode(N1);
672     ImmutableSet<Integer> nodes = ImmutableSet.copyOf(networkAsMutableNetwork.nodes());
673     assertFalse(networkAsMutableNetwork.addNode(N1));
674     assertThat(networkAsMutableNetwork.nodes()).containsExactlyElementsIn(nodes);
675   }
676 
677   @Test
removeNode_existingNode()678   public void removeNode_existingNode() {
679     assume().that(graphIsMutable()).isTrue();
680 
681     addEdge(N1, N2, E12);
682     addEdge(N4, N1, E41);
683     assertTrue(networkAsMutableNetwork.removeNode(N1));
684     assertFalse(networkAsMutableNetwork.removeNode(N1));
685     assertThat(networkAsMutableNetwork.nodes()).containsExactly(N2, N4);
686     assertThat(networkAsMutableNetwork.edges()).doesNotContain(E12);
687     assertThat(networkAsMutableNetwork.edges()).doesNotContain(E41);
688   }
689 
690   @Test
removeNode_nodeNotPresent()691   public void removeNode_nodeNotPresent() {
692     assume().that(graphIsMutable()).isTrue();
693 
694     addNode(N1);
695     ImmutableSet<Integer> nodes = ImmutableSet.copyOf(networkAsMutableNetwork.nodes());
696     assertFalse(networkAsMutableNetwork.removeNode(NODE_NOT_IN_GRAPH));
697     assertThat(networkAsMutableNetwork.nodes()).containsExactlyElementsIn(nodes);
698   }
699 
700   @Test
removeNode_queryAfterRemoval()701   public void removeNode_queryAfterRemoval() {
702     assume().that(graphIsMutable()).isTrue();
703 
704     addNode(N1);
705     @SuppressWarnings("unused")
706     Set<Integer> unused =
707         networkAsMutableNetwork.adjacentNodes(N1); // ensure cache (if any) is populated
708     assertTrue(networkAsMutableNetwork.removeNode(N1));
709     try {
710       networkAsMutableNetwork.adjacentNodes(N1);
711       fail(ERROR_NODE_NOT_IN_GRAPH);
712     } catch (IllegalArgumentException e) {
713       assertNodeNotInGraphErrorMessage(e);
714     }
715   }
716 
717   @Test
removeEdge_existingEdge()718   public void removeEdge_existingEdge() {
719     assume().that(graphIsMutable()).isTrue();
720 
721     addEdge(N1, N2, E12);
722     assertTrue(networkAsMutableNetwork.removeEdge(E12));
723     assertFalse(networkAsMutableNetwork.removeEdge(E12));
724     assertThat(networkAsMutableNetwork.edges()).doesNotContain(E12);
725     assertThat(networkAsMutableNetwork.edgesConnecting(N1, N2)).isEmpty();
726   }
727 
728   @Test
removeEdge_oneOfMany()729   public void removeEdge_oneOfMany() {
730     assume().that(graphIsMutable()).isTrue();
731 
732     addEdge(N1, N2, E12);
733     addEdge(N1, N3, E13);
734     addEdge(N1, N4, E14);
735     assertThat(networkAsMutableNetwork.edges()).containsExactly(E12, E13, E14);
736     assertTrue(networkAsMutableNetwork.removeEdge(E13));
737     assertThat(networkAsMutableNetwork.edges()).containsExactly(E12, E14);
738   }
739 
740   @Test
removeEdge_edgeNotPresent()741   public void removeEdge_edgeNotPresent() {
742     assume().that(graphIsMutable()).isTrue();
743 
744     addEdge(N1, N2, E12);
745     ImmutableSet<String> edges = ImmutableSet.copyOf(networkAsMutableNetwork.edges());
746     assertFalse(networkAsMutableNetwork.removeEdge(EDGE_NOT_IN_GRAPH));
747     assertThat(networkAsMutableNetwork.edges()).containsExactlyElementsIn(edges);
748   }
749 
750   @Test
removeEdge_queryAfterRemoval()751   public void removeEdge_queryAfterRemoval() {
752     assume().that(graphIsMutable()).isTrue();
753 
754     addEdge(N1, N2, E12);
755     @SuppressWarnings("unused")
756     EndpointPair<Integer> unused =
757         networkAsMutableNetwork.incidentNodes(E12); // ensure cache (if any) is populated
758     assertTrue(networkAsMutableNetwork.removeEdge(E12));
759     try {
760       networkAsMutableNetwork.incidentNodes(E12);
761       fail(ERROR_EDGE_NOT_IN_GRAPH);
762     } catch (IllegalArgumentException e) {
763       assertEdgeNotInGraphErrorMessage(e);
764     }
765   }
766 
767   @Test
removeEdge_parallelEdge()768   public void removeEdge_parallelEdge() {
769     assume().that(graphIsMutable()).isTrue();
770     assume().that(network.allowsParallelEdges()).isTrue();
771 
772     addEdge(N1, N2, E12);
773     addEdge(N1, N2, E12_A);
774     assertTrue(networkAsMutableNetwork.removeEdge(E12_A));
775     assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
776   }
777 
778   @Test
removeEdge_parallelSelfLoopEdge()779   public void removeEdge_parallelSelfLoopEdge() {
780     assume().that(graphIsMutable()).isTrue();
781     assume().that(network.allowsParallelEdges()).isTrue();
782     assume().that(network.allowsSelfLoops()).isTrue();
783 
784     addEdge(N1, N1, E11);
785     addEdge(N1, N1, E11_A);
786     addEdge(N1, N2, E12);
787     assertTrue(networkAsMutableNetwork.removeEdge(E11_A));
788     assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
789     assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
790     assertTrue(networkAsMutableNetwork.removeEdge(E11));
791     assertThat(network.edgesConnecting(N1, N1)).isEmpty();
792     assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
793   }
794 
795 
796   @Test
concurrentIteration()797   public void concurrentIteration() throws Exception {
798     addEdge(1, 2, "foo");
799     addEdge(3, 4, "bar");
800     addEdge(5, 6, "baz");
801 
802     int threadCount = 20;
803     ExecutorService executor = newFixedThreadPool(threadCount);
804     final CyclicBarrier barrier = new CyclicBarrier(threadCount);
805     ImmutableList.Builder<Future<?>> futures = ImmutableList.builder();
806     for (int i = 0; i < threadCount; i++) {
807       futures.add(
808           executor.submit(
809               new Callable<Object>() {
810                 @Override
811                 public Object call() throws Exception {
812                   barrier.await();
813                   Integer first = network.nodes().iterator().next();
814                   for (Integer node : network.nodes()) {
815                     Set<Integer> unused = network.successors(node);
816                   }
817                   /*
818                    * Also look up an earlier node so that, if the graph is using MapRetrievalCache,
819                    * we read one of the fields declared in that class.
820                    */
821                   Set<Integer> unused = network.successors(first);
822                   return null;
823                 }
824               }));
825     }
826 
827     /*
828      * It's unlikely that any operations would fail by throwing an exception, but let's check them
829      * just to be safe.
830      *
831      * The real purpose of this test is to produce a TSAN failure if MapIteratorCache is unsafe for
832      * reads from multiple threads -- unsafe, in fact, even in the absence of a concurrent write.
833      * The specific problem we had was unsafe reads of lastEntryReturnedBySomeIterator. (To fix the
834      * problem, we've since marked that field as volatile.)
835      *
836      * When MapIteratorCache is used from Immutable* classes, the TSAN failure doesn't indicate a
837      * real problem: The Entry objects are ImmutableMap entries, whose fields are all final and thus
838      * safe to read even when the Entry object is unsafely published. But with a mutable graph, the
839      * Entry object is likely to have a non-final value field, which is not safe to read when
840      * unsafely published. (The Entry object might even be newly created by each iterator.next()
841      * call, so we can't assume that writes to the Entry have been safely published by some other
842      * synchronization actions.)
843      *
844      * All that said: I haven't actually managed to make this particular test produce a TSAN error
845      * for the field accesses in MapIteratorCache. This teset *has* found other TSAN errors,
846      * including in MapRetrievalCache, so I'm not sure why this one is different. I did at least
847      * confirm that my change to MapIteratorCache fixes the TSAN error in the (larger) test it was
848      * originally reported in.
849      */
850     for (Future<?> future : futures.build()) {
851       future.get();
852     }
853     executor.shutdown();
854   }
855 }
856