1 /* 2 * Copyright (C) 2016 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.GraphConstants.ENDPOINTS_MISMATCH; 20 import static com.google.common.graph.TestUtil.assertStronglyEquivalent; 21 import static com.google.common.truth.Truth.assertThat; 22 import static java.util.concurrent.Executors.newFixedThreadPool; 23 import static org.junit.Assert.assertThrows; 24 25 import com.google.common.collect.ImmutableList; 26 import java.util.Set; 27 import java.util.concurrent.Callable; 28 import java.util.concurrent.CyclicBarrier; 29 import java.util.concurrent.ExecutorService; 30 import java.util.concurrent.Future; 31 import org.checkerframework.checker.nullness.qual.Nullable; 32 import org.junit.After; 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 import org.junit.runners.JUnit4; 36 37 /** Tests for {@link StandardMutableValueGraph} and related functionality. */ 38 // TODO(user): Expand coverage and move to proper test suite. 39 @RunWith(JUnit4.class) 40 public final class ValueGraphTest { 41 private static final String DEFAULT = "default"; 42 43 MutableValueGraph<Integer, String> graph; 44 45 @After validateGraphState()46 public void validateGraphState() { 47 assertStronglyEquivalent(graph, Graphs.copyOf(graph)); 48 assertStronglyEquivalent(graph, ImmutableValueGraph.copyOf(graph)); 49 50 Graph<Integer> asGraph = graph.asGraph(); 51 AbstractGraphTest.validateGraph(asGraph); 52 assertThat(graph.nodes()).isEqualTo(asGraph.nodes()); 53 assertThat(graph.edges()).isEqualTo(asGraph.edges()); 54 assertThat(graph.nodeOrder()).isEqualTo(asGraph.nodeOrder()); 55 assertThat(graph.incidentEdgeOrder()).isEqualTo(asGraph.incidentEdgeOrder()); 56 assertThat(graph.isDirected()).isEqualTo(asGraph.isDirected()); 57 assertThat(graph.allowsSelfLoops()).isEqualTo(asGraph.allowsSelfLoops()); 58 59 for (Integer node : graph.nodes()) { 60 assertThat(graph.adjacentNodes(node)).isEqualTo(asGraph.adjacentNodes(node)); 61 assertThat(graph.predecessors(node)).isEqualTo(asGraph.predecessors(node)); 62 assertThat(graph.successors(node)).isEqualTo(asGraph.successors(node)); 63 assertThat(graph.degree(node)).isEqualTo(asGraph.degree(node)); 64 assertThat(graph.inDegree(node)).isEqualTo(asGraph.inDegree(node)); 65 assertThat(graph.outDegree(node)).isEqualTo(asGraph.outDegree(node)); 66 67 for (Integer otherNode : graph.nodes()) { 68 boolean hasEdge = graph.hasEdgeConnecting(node, otherNode); 69 assertThat(hasEdge).isEqualTo(asGraph.hasEdgeConnecting(node, otherNode)); 70 assertThat(graph.edgeValueOrDefault(node, otherNode, null) != null).isEqualTo(hasEdge); 71 assertThat(!graph.edgeValueOrDefault(node, otherNode, DEFAULT).equals(DEFAULT)) 72 .isEqualTo(hasEdge); 73 } 74 } 75 } 76 77 @Test directedGraph()78 public void directedGraph() { 79 graph = ValueGraphBuilder.directed().allowsSelfLoops(true).build(); 80 graph.putEdgeValue(1, 2, "valueA"); 81 graph.putEdgeValue(2, 1, "valueB"); 82 graph.putEdgeValue(2, 3, "valueC"); 83 graph.putEdgeValue(4, 4, "valueD"); 84 85 assertThat(graph.edgeValueOrDefault(1, 2, null)).isEqualTo("valueA"); 86 assertThat(graph.edgeValueOrDefault(2, 1, null)).isEqualTo("valueB"); 87 assertThat(graph.edgeValueOrDefault(2, 3, null)).isEqualTo("valueC"); 88 assertThat(graph.edgeValueOrDefault(4, 4, null)).isEqualTo("valueD"); 89 assertThat(graph.edgeValueOrDefault(1, 2, DEFAULT)).isEqualTo("valueA"); 90 assertThat(graph.edgeValueOrDefault(2, 1, DEFAULT)).isEqualTo("valueB"); 91 assertThat(graph.edgeValueOrDefault(2, 3, DEFAULT)).isEqualTo("valueC"); 92 assertThat(graph.edgeValueOrDefault(4, 4, DEFAULT)).isEqualTo("valueD"); 93 94 String toString = graph.toString(); 95 assertThat(toString).contains("valueA"); 96 assertThat(toString).contains("valueB"); 97 assertThat(toString).contains("valueC"); 98 assertThat(toString).contains("valueD"); 99 } 100 101 @Test undirectedGraph()102 public void undirectedGraph() { 103 graph = ValueGraphBuilder.undirected().allowsSelfLoops(true).build(); 104 graph.putEdgeValue(1, 2, "valueA"); 105 graph.putEdgeValue(2, 1, "valueB"); // overwrites valueA in undirected case 106 graph.putEdgeValue(2, 3, "valueC"); 107 graph.putEdgeValue(4, 4, "valueD"); 108 109 assertThat(graph.edgeValueOrDefault(1, 2, null)).isEqualTo("valueB"); 110 assertThat(graph.edgeValueOrDefault(2, 1, null)).isEqualTo("valueB"); 111 assertThat(graph.edgeValueOrDefault(2, 3, null)).isEqualTo("valueC"); 112 assertThat(graph.edgeValueOrDefault(4, 4, null)).isEqualTo("valueD"); 113 assertThat(graph.edgeValueOrDefault(1, 2, DEFAULT)).isEqualTo("valueB"); 114 assertThat(graph.edgeValueOrDefault(2, 1, DEFAULT)).isEqualTo("valueB"); 115 assertThat(graph.edgeValueOrDefault(2, 3, DEFAULT)).isEqualTo("valueC"); 116 assertThat(graph.edgeValueOrDefault(4, 4, DEFAULT)).isEqualTo("valueD"); 117 118 String toString = graph.toString(); 119 assertThat(toString).doesNotContain("valueA"); 120 assertThat(toString).contains("valueB"); 121 assertThat(toString).contains("valueC"); 122 assertThat(toString).contains("valueD"); 123 } 124 125 @Test incidentEdgeOrder_unordered()126 public void incidentEdgeOrder_unordered() { 127 graph = ValueGraphBuilder.directed().incidentEdgeOrder(ElementOrder.unordered()).build(); 128 assertThat(graph.incidentEdgeOrder()).isEqualTo(ElementOrder.unordered()); 129 } 130 131 @Test incidentEdgeOrder_stable()132 public void incidentEdgeOrder_stable() { 133 graph = ValueGraphBuilder.directed().incidentEdgeOrder(ElementOrder.stable()).build(); 134 assertThat(graph.incidentEdgeOrder()).isEqualTo(ElementOrder.stable()); 135 } 136 137 @Test hasEdgeConnecting_directed_correct()138 public void hasEdgeConnecting_directed_correct() { 139 graph = ValueGraphBuilder.directed().build(); 140 graph.putEdgeValue(1, 2, "A"); 141 assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(1, 2))).isTrue(); 142 } 143 144 @Test hasEdgeConnecting_directed_backwards()145 public void hasEdgeConnecting_directed_backwards() { 146 graph = ValueGraphBuilder.directed().build(); 147 graph.putEdgeValue(1, 2, "A"); 148 assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(2, 1))).isFalse(); 149 } 150 151 @Test hasEdgeConnecting_directed_mismatch()152 public void hasEdgeConnecting_directed_mismatch() { 153 graph = ValueGraphBuilder.directed().build(); 154 graph.putEdgeValue(1, 2, "A"); 155 assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(1, 2))).isFalse(); 156 assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(2, 1))).isFalse(); 157 } 158 159 @Test hasEdgeConnecting_undirected_correct()160 public void hasEdgeConnecting_undirected_correct() { 161 graph = ValueGraphBuilder.undirected().build(); 162 graph.putEdgeValue(1, 2, "A"); 163 assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(1, 2))).isTrue(); 164 } 165 166 @Test hasEdgeConnecting_undirected_backwards()167 public void hasEdgeConnecting_undirected_backwards() { 168 graph = ValueGraphBuilder.undirected().build(); 169 graph.putEdgeValue(1, 2, "A"); 170 assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(2, 1))).isTrue(); 171 } 172 173 @Test hasEdgeConnecting_undirected_mismatch()174 public void hasEdgeConnecting_undirected_mismatch() { 175 graph = ValueGraphBuilder.undirected().build(); 176 graph.putEdgeValue(1, 2, "A"); 177 assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(1, 2))).isFalse(); 178 assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(2, 1))).isFalse(); 179 } 180 181 @Test edgeValueOrDefault_directed_correct()182 public void edgeValueOrDefault_directed_correct() { 183 graph = ValueGraphBuilder.directed().build(); 184 graph.putEdgeValue(1, 2, "A"); 185 assertThat(graph.edgeValueOrDefault(EndpointPair.ordered(1, 2), "default")).isEqualTo("A"); 186 } 187 188 @Test edgeValueOrDefault_directed_backwards()189 public void edgeValueOrDefault_directed_backwards() { 190 graph = ValueGraphBuilder.directed().build(); 191 graph.putEdgeValue(1, 2, "A"); 192 assertThat(graph.edgeValueOrDefault(EndpointPair.ordered(2, 1), "default")) 193 .isEqualTo("default"); 194 } 195 196 @Test edgeValueOrDefault_directed_mismatch()197 public void edgeValueOrDefault_directed_mismatch() { 198 graph = ValueGraphBuilder.directed().build(); 199 graph.putEdgeValue(1, 2, "A"); 200 IllegalArgumentException e = 201 assertThrows( 202 IllegalArgumentException.class, 203 () -> graph.edgeValueOrDefault(EndpointPair.unordered(1, 2), "default")); 204 assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); 205 e = 206 assertThrows( 207 IllegalArgumentException.class, 208 () -> graph.edgeValueOrDefault(EndpointPair.unordered(2, 1), "default")); 209 assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); 210 } 211 212 @Test edgeValueOrDefault_undirected_correct()213 public void edgeValueOrDefault_undirected_correct() { 214 graph = ValueGraphBuilder.undirected().build(); 215 graph.putEdgeValue(1, 2, "A"); 216 assertThat(graph.edgeValueOrDefault(EndpointPair.unordered(1, 2), "default")).isEqualTo("A"); 217 } 218 219 @Test edgeValueOrDefault_undirected_backwards()220 public void edgeValueOrDefault_undirected_backwards() { 221 graph = ValueGraphBuilder.undirected().build(); 222 graph.putEdgeValue(1, 2, "A"); 223 assertThat(graph.edgeValueOrDefault(EndpointPair.unordered(2, 1), "default")).isEqualTo("A"); 224 } 225 226 @Test edgeValueOrDefault_undirected_mismatch()227 public void edgeValueOrDefault_undirected_mismatch() { 228 graph = ValueGraphBuilder.undirected().build(); 229 graph.putEdgeValue(1, 2, "A"); 230 // Check that edgeValueOrDefault() throws on each possible ordering of an ordered EndpointPair 231 IllegalArgumentException e = 232 assertThrows( 233 IllegalArgumentException.class, 234 () -> graph.edgeValueOrDefault(EndpointPair.ordered(1, 2), "default")); 235 assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); 236 e = 237 assertThrows( 238 IllegalArgumentException.class, 239 () -> graph.edgeValueOrDefault(EndpointPair.ordered(2, 1), "default")); 240 assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); 241 } 242 243 @Test putEdgeValue_directed()244 public void putEdgeValue_directed() { 245 graph = ValueGraphBuilder.directed().build(); 246 247 assertThat(graph.putEdgeValue(1, 2, "valueA")).isNull(); 248 assertThat(graph.putEdgeValue(2, 1, "valueB")).isNull(); 249 assertThat(graph.putEdgeValue(1, 2, "valueC")).isEqualTo("valueA"); 250 assertThat(graph.putEdgeValue(2, 1, "valueD")).isEqualTo("valueB"); 251 } 252 253 @Test putEdgeValue_directed_orderMismatch()254 public void putEdgeValue_directed_orderMismatch() { 255 graph = ValueGraphBuilder.directed().build(); 256 IllegalArgumentException e = 257 assertThrows( 258 IllegalArgumentException.class, 259 () -> graph.putEdgeValue(EndpointPair.unordered(1, 2), "irrelevant")); 260 assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); 261 } 262 263 @Test putEdgeValue_undirected_orderMismatch()264 public void putEdgeValue_undirected_orderMismatch() { 265 graph = ValueGraphBuilder.undirected().build(); 266 IllegalArgumentException e = 267 assertThrows( 268 IllegalArgumentException.class, 269 () -> graph.putEdgeValue(EndpointPair.ordered(1, 2), "irrelevant")); 270 assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); 271 } 272 273 @Test putEdgeValue_undirected()274 public void putEdgeValue_undirected() { 275 graph = ValueGraphBuilder.undirected().build(); 276 277 assertThat(graph.putEdgeValue(1, 2, "valueA")).isNull(); 278 assertThat(graph.putEdgeValue(2, 1, "valueB")).isEqualTo("valueA"); 279 assertThat(graph.putEdgeValue(1, 2, "valueC")).isEqualTo("valueB"); 280 assertThat(graph.putEdgeValue(2, 1, "valueD")).isEqualTo("valueC"); 281 } 282 283 @Test removeEdge_directed()284 public void removeEdge_directed() { 285 graph = ValueGraphBuilder.directed().build(); 286 graph.putEdgeValue(1, 2, "valueA"); 287 graph.putEdgeValue(2, 1, "valueB"); 288 graph.putEdgeValue(2, 3, "valueC"); 289 290 assertThat(graph.removeEdge(1, 2)).isEqualTo("valueA"); 291 assertThat(graph.removeEdge(1, 2)).isNull(); 292 assertThat(graph.removeEdge(2, 1)).isEqualTo("valueB"); 293 assertThat(graph.removeEdge(2, 1)).isNull(); 294 assertThat(graph.removeEdge(2, 3)).isEqualTo("valueC"); 295 assertThat(graph.removeEdge(2, 3)).isNull(); 296 } 297 298 @Test removeEdge_undirected()299 public void removeEdge_undirected() { 300 graph = ValueGraphBuilder.undirected().build(); 301 graph.putEdgeValue(1, 2, "valueA"); 302 graph.putEdgeValue(2, 1, "valueB"); 303 graph.putEdgeValue(2, 3, "valueC"); 304 305 assertThat(graph.removeEdge(1, 2)).isEqualTo("valueB"); 306 assertThat(graph.removeEdge(1, 2)).isNull(); 307 assertThat(graph.removeEdge(2, 1)).isNull(); 308 assertThat(graph.removeEdge(2, 3)).isEqualTo("valueC"); 309 assertThat(graph.removeEdge(2, 3)).isNull(); 310 } 311 312 @Test removeEdge_directed_orderMismatch()313 public void removeEdge_directed_orderMismatch() { 314 graph = ValueGraphBuilder.directed().build(); 315 graph.putEdgeValue(1, 2, "1->2"); 316 graph.putEdgeValue(2, 1, "2->1"); 317 IllegalArgumentException e = 318 assertThrows( 319 IllegalArgumentException.class, () -> graph.removeEdge(EndpointPair.unordered(1, 2))); 320 assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); 321 e = 322 assertThrows( 323 IllegalArgumentException.class, () -> graph.removeEdge(EndpointPair.unordered(2, 1))); 324 assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); 325 } 326 327 @Test removeEdge_undirected_orderMismatch()328 public void removeEdge_undirected_orderMismatch() { 329 graph = ValueGraphBuilder.undirected().build(); 330 graph.putEdgeValue(1, 2, "1-2"); 331 // Check that removeEdge() throws on each possible ordering of an ordered EndpointPair 332 IllegalArgumentException e = 333 assertThrows( 334 IllegalArgumentException.class, () -> graph.removeEdge(EndpointPair.ordered(1, 2))); 335 assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); 336 e = 337 assertThrows( 338 IllegalArgumentException.class, () -> graph.removeEdge(EndpointPair.ordered(2, 1))); 339 assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); 340 } 341 342 @Test edgeValue_missing()343 public void edgeValue_missing() { 344 graph = ValueGraphBuilder.directed().build(); 345 346 assertThat(graph.edgeValueOrDefault(1, 2, DEFAULT)).isEqualTo(DEFAULT); 347 assertThat(graph.edgeValueOrDefault(2, 1, DEFAULT)).isEqualTo(DEFAULT); 348 assertThat(graph.edgeValueOrDefault(1, 2, null)).isNull(); 349 assertThat(graph.edgeValueOrDefault(2, 1, null)).isNull(); 350 351 graph.putEdgeValue(1, 2, "valueA"); 352 graph.putEdgeValue(2, 1, "valueB"); 353 assertThat(graph.edgeValueOrDefault(1, 2, DEFAULT)).isEqualTo("valueA"); 354 assertThat(graph.edgeValueOrDefault(2, 1, DEFAULT)).isEqualTo("valueB"); 355 assertThat(graph.edgeValueOrDefault(1, 2, null)).isEqualTo("valueA"); 356 assertThat(graph.edgeValueOrDefault(2, 1, null)).isEqualTo("valueB"); 357 358 graph.removeEdge(1, 2); 359 graph.putEdgeValue(2, 1, "valueC"); 360 assertThat(graph.edgeValueOrDefault(1, 2, DEFAULT)).isEqualTo(DEFAULT); 361 assertThat(graph.edgeValueOrDefault(2, 1, DEFAULT)).isEqualTo("valueC"); 362 assertThat(graph.edgeValueOrDefault(1, 2, null)).isNull(); 363 assertThat(graph.edgeValueOrDefault(2, 1, null)).isEqualTo("valueC"); 364 } 365 366 @Test equivalence_considersEdgeValue()367 public void equivalence_considersEdgeValue() { 368 graph = ValueGraphBuilder.undirected().build(); 369 graph.putEdgeValue(1, 2, "valueA"); 370 371 MutableValueGraph<Integer, String> otherGraph = ValueGraphBuilder.undirected().build(); 372 otherGraph.putEdgeValue(1, 2, "valueA"); 373 assertThat(graph).isEqualTo(otherGraph); 374 375 otherGraph.putEdgeValue(1, 2, "valueB"); 376 assertThat(graph).isNotEqualTo(otherGraph); // values differ 377 } 378 379 @Test incidentEdges_stableIncidentEdgeOrder_preservesIncidentEdgesOrder_directed()380 public void incidentEdges_stableIncidentEdgeOrder_preservesIncidentEdgesOrder_directed() { 381 graph = ValueGraphBuilder.directed().incidentEdgeOrder(ElementOrder.stable()).build(); 382 graph.putEdgeValue(2, 1, "2-1"); 383 graph.putEdgeValue(2, 3, "2-3"); 384 graph.putEdgeValue(1, 2, "1-2"); 385 386 assertThat(graph.incidentEdges(2)) 387 .containsExactly( 388 EndpointPair.ordered(2, 1), EndpointPair.ordered(2, 3), EndpointPair.ordered(1, 2)) 389 .inOrder(); 390 } 391 392 @Test incidentEdges_stableIncidentEdgeOrder_preservesIncidentEdgesOrder_undirected()393 public void incidentEdges_stableIncidentEdgeOrder_preservesIncidentEdgesOrder_undirected() { 394 graph = ValueGraphBuilder.undirected().incidentEdgeOrder(ElementOrder.stable()).build(); 395 graph.putEdgeValue(2, 3, "2-3"); 396 graph.putEdgeValue(2, 1, "2-1"); 397 graph.putEdgeValue(2, 4, "2-4"); 398 graph.putEdgeValue(1, 2, "1-2"); // Duplicate nodes, different value 399 400 assertThat(graph.incidentEdges(2)) 401 .containsExactly( 402 EndpointPair.unordered(2, 3), 403 EndpointPair.unordered(1, 2), 404 EndpointPair.unordered(2, 4)) 405 .inOrder(); 406 } 407 408 @Test concurrentIteration()409 public void concurrentIteration() throws Exception { 410 graph = ValueGraphBuilder.directed().build(); 411 graph.putEdgeValue(1, 2, "A"); 412 graph.putEdgeValue(3, 4, "B"); 413 graph.putEdgeValue(5, 6, "C"); 414 415 int threadCount = 20; 416 ExecutorService executor = newFixedThreadPool(threadCount); 417 final CyclicBarrier barrier = new CyclicBarrier(threadCount); 418 ImmutableList.Builder<Future<?>> futures = ImmutableList.builder(); 419 for (int i = 0; i < threadCount; i++) { 420 futures.add( 421 executor.submit( 422 new Callable<@Nullable Void>() { 423 @Override 424 public @Nullable Void call() throws Exception { 425 barrier.await(); 426 Integer first = graph.nodes().iterator().next(); 427 for (Integer node : graph.nodes()) { 428 Set<Integer> unused = graph.successors(node); 429 } 430 /* 431 * Also look up an earlier node so that, if the graph is using MapRetrievalCache, 432 * we read one of the fields declared in that class. 433 */ 434 Set<Integer> unused = graph.successors(first); 435 return null; 436 } 437 })); 438 } 439 440 // For more about this test, see the equivalent in AbstractNetworkTest. 441 for (Future<?> future : futures.build()) { 442 future.get(); 443 } 444 executor.shutdown(); 445 } 446 } 447