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