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