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