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