• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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