/* * Copyright (C) 2017 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.graph; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Lists.charactersOf; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Multiset; import com.google.common.collect.Ordering; import com.google.common.primitives.Chars; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class TraverserTest { /** * The undirected graph in the {@link Traverser#breadthFirst(Object)} javadoc: * *
{@code
   * b ---- a ---- d
   * |      |
   * |      |
   * e ---- c ---- f
   * }
*/ private static final SuccessorsFunction JAVADOC_GRAPH = createUndirectedGraph("ba", "ad", "be", "ac", "ec", "cf"); /** * A diamond shaped directed graph (arrows going down): * *
{@code
   *   a
   *  / \
   * b   c
   *  \ /
   *   d
   * }
*/ private static final SuccessorsFunction DIAMOND_GRAPH = createDirectedGraph("ab", "ac", "bd", "cd"); /** * Same as {@link #DIAMOND_GRAPH}, but with an extra c->a edge and some self edges: * *
{@code
   *   a<>
   *  / \\
   * b   c
   *  \ /
   *   d<>
   * }
* * {@code <>} indicates a self-loop */ private static final SuccessorsFunction MULTI_GRAPH = createDirectedGraph("aa", "dd", "ab", "ac", "ca", "cd", "bd"); /** A directed graph with a single cycle: a -> b -> c -> d -> a. */ private static final SuccessorsFunction CYCLE_GRAPH = createDirectedGraph("ab", "bc", "cd", "da"); /** * Same as {@link #CYCLE_GRAPH}, but with an extra a->c edge. * *
{@code
   * |--------------|
   * v              |
   * a -> b -> c -> d
   * |         ^
   * |---------|
   * }
*/ private static final SuccessorsFunction TWO_CYCLES_GRAPH = createDirectedGraph("ab", "ac", "bc", "cd", "da"); /** * A tree-shaped graph that looks as follows (all edges are directed facing downwards): * *
{@code
   *        h
   *       /|\
   *      / | \
   *     /  |  \
   *    d   e   g
   *   /|\      |
   *  / | \     |
   * a  b  c    f
   * }
*/ private static final SuccessorsFunction TREE = createDirectedGraph("hd", "he", "hg", "da", "db", "dc", "gf"); /** * Two disjoint tree-shaped graphs that look as follows (all edges are directed facing downwards): * *
{@code
   * a   c
   * |   |
   * |   |
   * b   d
   * }
*/ private static final SuccessorsFunction TWO_TREES = createDirectedGraph("ab", "cd"); /** * A graph consisting of a single root {@code a}: * *
{@code
   * a
   * }
*/ private static final SuccessorsFunction SINGLE_ROOT = createSingleRootGraph(); /** * A graph that is not a tree (for example, it has two antiparallel edge between {@code e} and * {@code f} and thus has a cycle) but is a valid input to {@link Traverser#forTree} when starting * e.g. at node {@code a} (all edges without an arrow are directed facing downwards): * *
{@code
   *     a
   *    /
   *   b   e <----> f
   *  / \ /
   * c   d
   * }
*/ private static final SuccessorsFunction CYCLIC_GRAPH_CONTAINING_TREE = createDirectedGraph("ab", "bc", "bd", "ed", "ef", "fe"); /** * A graph that is not a tree (for example, {@code h} is reachable from {@code f} via both {@code * e} and {@code g}) but is a valid input to {@link Traverser#forTree} when starting e.g. at node * {@code a} (all edges are directed facing downwards): * *
{@code
   *     a   f
   *    /   / \
   *   b   e   g
   *  / \ / \ /
   * c   d   h
   * }
*/ private static final SuccessorsFunction GRAPH_CONTAINING_TREE_AND_DIAMOND = createDirectedGraph("ab", "fe", "fg", "bc", "bd", "ed", "eh", "gh"); @Test public void forGraph_breadthFirst_javadocExample_canBeIteratedMultipleTimes() { Iterable result = Traverser.forGraph(JAVADOC_GRAPH).breadthFirst('a'); assertEqualCharNodes(result, "abcdef"); assertEqualCharNodes(result, "abcdef"); } @Test public void forGraph_breadthFirstIterable_javadocExample_canBeIteratedMultipleTimes() { Iterable result = Traverser.forGraph(JAVADOC_GRAPH).breadthFirst(charactersOf("bf")); assertEqualCharNodes(result, "bfaecd"); assertEqualCharNodes(result, "bfaecd"); } @Test public void forGraph_breadthFirst_infinite() { Iterable result = Traverser.forGraph(fixedSuccessors(Iterables.cycle(1, 2, 3))).breadthFirst(0); assertThat(Iterables.limit(result, 4)).containsExactly(0, 1, 2, 3).inOrder(); } @Test public void forGraph_breadthFirst_diamond() { Traverser traverser = Traverser.forGraph(DIAMOND_GRAPH); assertEqualCharNodes(traverser.breadthFirst('a'), "abcd"); assertEqualCharNodes(traverser.breadthFirst('b'), "bd"); assertEqualCharNodes(traverser.breadthFirst('c'), "cd"); assertEqualCharNodes(traverser.breadthFirst('d'), "d"); } @Test public void forGraph_breadthFirstIterable_diamond() { Traverser traverser = Traverser.forGraph(DIAMOND_GRAPH); assertEqualCharNodes(traverser.breadthFirst(charactersOf("")), ""); assertEqualCharNodes(traverser.breadthFirst(charactersOf("bc")), "bcd"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("a")), "abcd"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("acdb")), "acdb"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("db")), "db"); } @Test public void forGraph_breadthFirst_multiGraph() { Traverser traverser = Traverser.forGraph(MULTI_GRAPH); assertEqualCharNodes(traverser.breadthFirst('a'), "abcd"); assertEqualCharNodes(traverser.breadthFirst('b'), "bd"); assertEqualCharNodes(traverser.breadthFirst('c'), "cadb"); assertEqualCharNodes(traverser.breadthFirst('d'), "d"); } @Test public void forGraph_breadthFirstIterable_multiGraph() { Traverser traverser = Traverser.forGraph(MULTI_GRAPH); assertEqualCharNodes(traverser.breadthFirst(charactersOf("ac")), "acbd"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("cb")), "cbad"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("db")), "db"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("d")), "d"); } @Test public void forGraph_breadthFirst_cycle() { Traverser traverser = Traverser.forGraph(CYCLE_GRAPH); assertEqualCharNodes(traverser.breadthFirst('a'), "abcd"); assertEqualCharNodes(traverser.breadthFirst('b'), "bcda"); assertEqualCharNodes(traverser.breadthFirst('c'), "cdab"); assertEqualCharNodes(traverser.breadthFirst('d'), "dabc"); } @Test public void forGraph_breadthFirstIterable_cycle() { Traverser traverser = Traverser.forGraph(CYCLE_GRAPH); assertEqualCharNodes(traverser.breadthFirst(charactersOf("a")), "abcd"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("bd")), "bdca"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("dc")), "dcab"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("bc")), "bcda"); } @Test public void forGraph_breadthFirst_twoCycles() { Traverser traverser = Traverser.forGraph(TWO_CYCLES_GRAPH); assertEqualCharNodes(traverser.breadthFirst('a'), "abcd"); assertEqualCharNodes(traverser.breadthFirst('b'), "bcda"); assertEqualCharNodes(traverser.breadthFirst('c'), "cdab"); assertEqualCharNodes(traverser.breadthFirst('d'), "dabc"); } @Test public void forGraph_breadthFirstIterable_twoCycles() { Traverser traverser = Traverser.forGraph(TWO_CYCLES_GRAPH); assertEqualCharNodes(traverser.breadthFirst(charactersOf("a")), "abcd"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("bd")), "bdca"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("dc")), "dcab"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("bc")), "bcda"); } @Test public void forGraph_breadthFirst_tree() throws Exception { Traverser traverser = Traverser.forGraph(TREE); assertEqualCharNodes(traverser.breadthFirst('h'), "hdegabcf"); assertEqualCharNodes(traverser.breadthFirst('d'), "dabc"); assertEqualCharNodes(traverser.breadthFirst('a'), "a"); } @Test public void forGraph_breadthFirstIterable_tree() throws Exception { Traverser traverser = Traverser.forGraph(TREE); assertEqualCharNodes(traverser.breadthFirst(charactersOf("hg")), "hgdefabc"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("gd")), "gdfabc"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("bdgh")), "bdghacfe"); } @Test public void forGraph_breadthFirst_twoTrees() { Iterable result = Traverser.forGraph(TWO_TREES).breadthFirst('a'); assertEqualCharNodes(result, "ab"); } @Test public void forGraph_breadthFirstIterable_twoTrees() { assertEqualCharNodes(Traverser.forGraph(TWO_TREES).breadthFirst(charactersOf("a")), "ab"); assertEqualCharNodes(Traverser.forGraph(TWO_TREES).breadthFirst(charactersOf("ac")), "acbd"); } @Test public void forGraph_breadthFirst_singleRoot() { Iterable result = Traverser.forGraph(SINGLE_ROOT).breadthFirst('a'); assertEqualCharNodes(result, "a"); } @Test public void forGraph_breadthFirstIterable_singleRoot() { Iterable result = Traverser.forGraph(SINGLE_ROOT).breadthFirst(charactersOf("a")); assertEqualCharNodes(result, "a"); } @Test public void forGraph_breadthFirst_emptyGraph() { try { Traverser.forGraph(createDirectedGraph()).breadthFirst('a'); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } /** * Checks that the elements of the iterable are calculated on the fly. Concretely, that means that * {@link SuccessorsFunction#successors(Object)} can only be called for a subset of all nodes. */ @Test public void forGraph_breadthFirstIterable_emptyGraph() { assertEqualCharNodes( Traverser.forGraph(createDirectedGraph()).breadthFirst(charactersOf("")), ""); try { Traverser.forGraph(createDirectedGraph()).breadthFirst(charactersOf("a")); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } /** * Checks that the elements of the iterable are calculated on the fly. Concretely, that means that * {@link SuccessorsFunction#successors(Object)} can only be called for a subset of all nodes. */ @Test public void forGraph_breadthFirst_iterableIsLazy() { RequestSavingGraph graph = new RequestSavingGraph(DIAMOND_GRAPH); Iterable result = Traverser.forGraph(graph).breadthFirst('a'); assertEqualCharNodes(Iterables.limit(result, 2), "ab"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'b'); // Iterate again to see if calculation is done again assertEqualCharNodes(Iterables.limit(result, 2), "ab"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'a', 'b', 'b'); } @Test public void forGraph_breadthFirstIterable_iterableIsLazy() { RequestSavingGraph graph = new RequestSavingGraph(DIAMOND_GRAPH); Iterable result = Traverser.forGraph(graph).breadthFirst(charactersOf("ab")); assertEqualCharNodes(Iterables.limit(result, 2), "ab"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'b', 'b'); // Iterate again to see if calculation is done again assertEqualCharNodes(Iterables.limit(result, 2), "ab"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'a', 'b', 'b', 'b'); } @Test public void forGraph_depthFirstPreOrder_javadocExample_canBeIteratedMultipleTimes() { Iterable result = Traverser.forGraph(JAVADOC_GRAPH).depthFirstPreOrder('a'); assertEqualCharNodes(result, "abecfd"); assertEqualCharNodes(result, "abecfd"); } @Test public void forGraph_depthFirstPreOrderIterable_javadocExample_canBeIteratedMultipleTimes() { Iterable result = Traverser.forGraph(JAVADOC_GRAPH).depthFirstPreOrder(charactersOf("bc")); assertEqualCharNodes(result, "bacefd"); assertEqualCharNodes(result, "bacefd"); } @Test public void forGraph_depthFirstPreOrder_infinite() { Iterable result = Traverser.forGraph(fixedSuccessors(Iterables.cycle(1, 2, 3))).depthFirstPreOrder(0); assertThat(Iterables.limit(result, 3)).containsExactly(0, 1, 2).inOrder(); } @Test public void forGraph_depthFirstPreOrder_diamond() { Traverser traverser = Traverser.forGraph(DIAMOND_GRAPH); assertEqualCharNodes(traverser.depthFirstPreOrder('a'), "abdc"); assertEqualCharNodes(traverser.depthFirstPreOrder('b'), "bd"); assertEqualCharNodes(traverser.depthFirstPreOrder('c'), "cd"); assertEqualCharNodes(traverser.depthFirstPreOrder('d'), "d"); } @Test public void forGraph_depthFirstPreOrderIterable_diamond() { Traverser traverser = Traverser.forGraph(DIAMOND_GRAPH); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("")), ""); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("bc")), "bdc"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("a")), "abdc"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("acdb")), "abdc"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("db")), "db"); } @Test public void forGraph_depthFirstPreOrder_multigraph() { Traverser traverser = Traverser.forGraph(MULTI_GRAPH); assertEqualCharNodes(traverser.depthFirstPreOrder('a'), "abdc"); assertEqualCharNodes(traverser.depthFirstPreOrder('b'), "bd"); assertEqualCharNodes(traverser.depthFirstPreOrder('c'), "cabd"); assertEqualCharNodes(traverser.depthFirstPreOrder('d'), "d"); } @Test public void forGraph_depthFirstPreOrderIterable_multigraph() { Traverser traverser = Traverser.forGraph(MULTI_GRAPH); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("ac")), "abdc"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("cb")), "cabd"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("db")), "db"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("d")), "d"); } @Test public void forGraph_depthFirstPreOrder_cycle() { Traverser traverser = Traverser.forGraph(CYCLE_GRAPH); assertEqualCharNodes(traverser.depthFirstPreOrder('a'), "abcd"); assertEqualCharNodes(traverser.depthFirstPreOrder('b'), "bcda"); assertEqualCharNodes(traverser.depthFirstPreOrder('c'), "cdab"); assertEqualCharNodes(traverser.depthFirstPreOrder('d'), "dabc"); } @Test public void forGraph_depthFirstPreOrderIterable_cycle() { Traverser traverser = Traverser.forGraph(CYCLE_GRAPH); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("a")), "abcd"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("bd")), "bcda"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("dc")), "dabc"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("bc")), "bcda"); } @Test public void forGraph_depthFirstPreOrder_twoCycles() { Traverser traverser = Traverser.forGraph(TWO_CYCLES_GRAPH); assertEqualCharNodes(traverser.depthFirstPreOrder('a'), "abcd"); assertEqualCharNodes(traverser.depthFirstPreOrder('b'), "bcda"); assertEqualCharNodes(traverser.depthFirstPreOrder('c'), "cdab"); assertEqualCharNodes(traverser.depthFirstPreOrder('d'), "dabc"); } @Test public void forGraph_depthFirstPreOrderIterable_twoCycles() { Traverser traverser = Traverser.forGraph(TWO_CYCLES_GRAPH); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("a")), "abcd"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("bd")), "bcda"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("dc")), "dabc"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("bc")), "bcda"); } @Test public void forGraph_depthFirstPreOrder_tree() throws Exception { Traverser traverser = Traverser.forGraph(TREE); assertEqualCharNodes(traverser.depthFirstPreOrder('h'), "hdabcegf"); assertEqualCharNodes(traverser.depthFirstPreOrder('d'), "dabc"); assertEqualCharNodes(traverser.depthFirstPreOrder('a'), "a"); } @Test public void forGraph_depthFirstPreOrderIterable_tree() throws Exception { Traverser traverser = Traverser.forGraph(TREE); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("hg")), "hdabcegf"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("gd")), "gfdabc"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("bdgh")), "bdacgfhe"); } @Test public void forGraph_depthFirstPreOrder_twoTrees() { Iterable result = Traverser.forGraph(TWO_TREES).depthFirstPreOrder('a'); assertEqualCharNodes(result, "ab"); } @Test public void forGraph_depthFirstPreOrderIterable_twoTrees() { assertEqualCharNodes(Traverser.forGraph(TWO_TREES).depthFirstPreOrder(charactersOf("a")), "ab"); assertEqualCharNodes( Traverser.forGraph(TWO_TREES).depthFirstPreOrder(charactersOf("ac")), "abcd"); } @Test public void forGraph_depthFirstPreOrder_singleRoot() { Iterable result = Traverser.forGraph(SINGLE_ROOT).depthFirstPreOrder('a'); assertEqualCharNodes(result, "a"); } @Test public void forGraph_depthFirstPreOrderIterable_singleRoot() { Iterable result = Traverser.forGraph(SINGLE_ROOT).depthFirstPreOrder(charactersOf("a")); assertEqualCharNodes(result, "a"); } @Test public void forGraph_depthFirstPreOrder_emptyGraph() { try { Traverser.forGraph(createDirectedGraph()).depthFirstPreOrder('a'); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } @Test public void forGraph_depthFirstPreOrderIterable_emptyGraph() { assertEqualCharNodes( Traverser.forGraph(createDirectedGraph()).depthFirstPreOrder(charactersOf("")), ""); try { Traverser.forGraph(createDirectedGraph()).depthFirstPreOrder(charactersOf("a")); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } @Test public void forGraph_depthFirstPreOrder_iterableIsLazy() { RequestSavingGraph graph = new RequestSavingGraph(DIAMOND_GRAPH); Iterable result = Traverser.forGraph(graph).depthFirstPreOrder('a'); assertEqualCharNodes(Iterables.limit(result, 2), "ab"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'b'); // Iterate again to see if calculation is done again assertEqualCharNodes(Iterables.limit(result, 2), "ab"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'a', 'b', 'b'); } @Test public void forGraph_depthFirstPreOrderIterable_iterableIsLazy() { RequestSavingGraph graph = new RequestSavingGraph(DIAMOND_GRAPH); Iterable result = Traverser.forGraph(graph).depthFirstPreOrder(charactersOf("ac")); assertEqualCharNodes(Iterables.limit(result, 2), "ab"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'b', 'c'); // Iterate again to see if calculation is done again assertEqualCharNodes(Iterables.limit(result, 2), "ab"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'a', 'b', 'b', 'c'); } @Test public void forGraph_depthFirstPostOrder_javadocExample_canBeIteratedMultipleTimes() { Iterable result = Traverser.forGraph(JAVADOC_GRAPH).depthFirstPostOrder('a'); assertEqualCharNodes(result, "fcebda"); assertEqualCharNodes(result, "fcebda"); } @Test public void forGraph_depthFirstPostOrderIterable_javadocExample_canBeIteratedMultipleTimes() { Iterable result = Traverser.forGraph(JAVADOC_GRAPH).depthFirstPostOrder(charactersOf("bf")); assertEqualCharNodes(result, "efcdab"); assertEqualCharNodes(result, "efcdab"); } @Test public void forGraph_depthFirstPostOrder_diamond() { Traverser traverser = Traverser.forGraph(DIAMOND_GRAPH); assertEqualCharNodes(traverser.depthFirstPostOrder('a'), "dbca"); assertEqualCharNodes(traverser.depthFirstPostOrder('b'), "db"); assertEqualCharNodes(traverser.depthFirstPostOrder('c'), "dc"); assertEqualCharNodes(traverser.depthFirstPostOrder('d'), "d"); } @Test public void forGraph_depthFirstPostOrderIterable_diamond() { Traverser traverser = Traverser.forGraph(DIAMOND_GRAPH); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("")), ""); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("bc")), "dbc"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("a")), "dbca"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("acdb")), "dbca"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("db")), "db"); } @Test public void forGraph_depthFirstPostOrder_multigraph() { Traverser traverser = Traverser.forGraph(MULTI_GRAPH); assertEqualCharNodes(traverser.depthFirstPostOrder('a'), "dbca"); assertEqualCharNodes(traverser.depthFirstPostOrder('b'), "db"); assertEqualCharNodes(traverser.depthFirstPostOrder('c'), "dbac"); assertEqualCharNodes(traverser.depthFirstPostOrder('d'), "d"); } @Test public void forGraph_depthFirstPostOrderIterable_multigraph() { Traverser traverser = Traverser.forGraph(MULTI_GRAPH); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("ac")), "dbca"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("cb")), "dbac"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("db")), "db"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("d")), "d"); } @Test public void forGraph_depthFirstPostOrder_cycle() { Traverser traverser = Traverser.forGraph(CYCLE_GRAPH); assertEqualCharNodes(traverser.depthFirstPostOrder('a'), "dcba"); assertEqualCharNodes(traverser.depthFirstPostOrder('b'), "adcb"); assertEqualCharNodes(traverser.depthFirstPostOrder('c'), "badc"); assertEqualCharNodes(traverser.depthFirstPostOrder('d'), "cbad"); } @Test public void forGraph_depthFirstPostOrderIterable_cycle() { Traverser traverser = Traverser.forGraph(CYCLE_GRAPH); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("a")), "dcba"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("bd")), "adcb"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("dc")), "cbad"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("bc")), "adcb"); } @Test public void forGraph_depthFirstPostOrder_twoCycles() { Traverser traverser = Traverser.forGraph(TWO_CYCLES_GRAPH); assertEqualCharNodes(traverser.depthFirstPostOrder('a'), "dcba"); assertEqualCharNodes(traverser.depthFirstPostOrder('b'), "adcb"); assertEqualCharNodes(traverser.depthFirstPostOrder('c'), "badc"); assertEqualCharNodes(traverser.depthFirstPostOrder('d'), "cbad"); } @Test public void forGraph_depthFirstPostOrderIterable_twoCycles() { Traverser traverser = Traverser.forGraph(TWO_CYCLES_GRAPH); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("a")), "dcba"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("bd")), "adcb"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("dc")), "cbad"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("bc")), "adcb"); } @Test public void forGraph_depthFirstPostOrder_tree() throws Exception { Traverser traverser = Traverser.forGraph(TREE); assertEqualCharNodes(traverser.depthFirstPostOrder('h'), "abcdefgh"); assertEqualCharNodes(traverser.depthFirstPostOrder('d'), "abcd"); assertEqualCharNodes(traverser.depthFirstPostOrder('a'), "a"); } @Test public void forGraph_depthFirstPostOrderIterable_tree() throws Exception { Traverser traverser = Traverser.forGraph(TREE); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("hg")), "abcdefgh"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("gd")), "fgabcd"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("bdgh")), "bacdfgeh"); } @Test public void forGraph_depthFirstPostOrder_twoTrees() { Iterable result = Traverser.forGraph(TWO_TREES).depthFirstPostOrder('a'); assertEqualCharNodes(result, "ba"); } @Test public void forGraph_depthFirstPostOrderIterable_twoTrees() { assertEqualCharNodes( Traverser.forGraph(TWO_TREES).depthFirstPostOrder(charactersOf("a")), "ba"); assertEqualCharNodes( Traverser.forGraph(TWO_TREES).depthFirstPostOrder(charactersOf("ac")), "badc"); } @Test public void forGraph_depthFirstPostOrder_singleRoot() { Iterable result = Traverser.forGraph(SINGLE_ROOT).depthFirstPostOrder('a'); assertEqualCharNodes(result, "a"); } @Test public void forGraph_depthFirstPostOrderIterable_singleRoot() { Iterable result = Traverser.forGraph(SINGLE_ROOT).depthFirstPostOrder(charactersOf("a")); assertEqualCharNodes(result, "a"); } @Test public void forGraph_depthFirstPostOrder_emptyGraph() { try { Traverser.forGraph(createDirectedGraph()).depthFirstPostOrder('a'); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } @Test public void forGraph_depthFirstPostOrderIterable_emptyGraph() { assertEqualCharNodes( Traverser.forGraph(createDirectedGraph()).depthFirstPostOrder(charactersOf("")), ""); try { Traverser.forGraph(createDirectedGraph()).depthFirstPostOrder(charactersOf("a")); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } @Test public void forGraph_depthFirstPostOrder_iterableIsLazy() { RequestSavingGraph graph = new RequestSavingGraph(DIAMOND_GRAPH); Iterable result = Traverser.forGraph(graph).depthFirstPostOrder('a'); assertEqualCharNodes(Iterables.limit(result, 2), "db"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'b', 'd'); // Iterate again to see if calculation is done again assertEqualCharNodes(Iterables.limit(result, 2), "db"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'a', 'b', 'b', 'd', 'd'); } @Test public void forGraph_depthFirstPostOrderIterable_iterableIsLazy() { RequestSavingGraph graph = new RequestSavingGraph(DIAMOND_GRAPH); Iterable result = Traverser.forGraph(graph).depthFirstPostOrder(charactersOf("ac")); assertEqualCharNodes(Iterables.limit(result, 2), "db"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'b', 'c', 'd'); // Iterate again to see if calculation is done again assertEqualCharNodes(Iterables.limit(result, 2), "db"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'a', 'b', 'b', 'c', 'd', 'd'); } @Test @SuppressWarnings("CheckReturnValue") public void forTree_acceptsDirectedGraph() throws Exception { MutableGraph graph = GraphBuilder.directed().build(); graph.putEdge("a", "b"); Traverser.forTree(graph); // Does not throw } @Test public void forTree_withUndirectedGraph_throws() throws Exception { MutableGraph graph = GraphBuilder.undirected().build(); graph.putEdge("a", "b"); try { Traverser.forTree(graph); fail("Expected exception"); } catch (IllegalArgumentException expected) { } } @Test @SuppressWarnings("CheckReturnValue") public void forTree_acceptsDirectedValueGraph() throws Exception { MutableValueGraph valueGraph = ValueGraphBuilder.directed().build(); valueGraph.putEdgeValue("a", "b", 11); Traverser.forTree(valueGraph); // Does not throw } @Test public void forTree_withUndirectedValueGraph_throws() throws Exception { MutableValueGraph valueGraph = ValueGraphBuilder.undirected().build(); valueGraph.putEdgeValue("a", "b", 11); try { Traverser.forTree(valueGraph); fail("Expected exception"); } catch (IllegalArgumentException expected) { } } @Test @SuppressWarnings("CheckReturnValue") public void forTree_acceptsDirectedNetwork() throws Exception { MutableNetwork network = NetworkBuilder.directed().build(); network.addEdge("a", "b", 11); Traverser.forTree(network); // Does not throw } @Test public void forTree_withUndirectedNetwork_throws() throws Exception { MutableNetwork network = NetworkBuilder.undirected().build(); network.addEdge("a", "b", 11); try { Traverser.forTree(network); fail("Expected exception"); } catch (IllegalArgumentException expected) { } } @Test public void forTree_breadthFirst_infinite() { Iterable result = Traverser.forTree(fixedSuccessors(Iterables.cycle(1, 2, 3))).breadthFirst(0); assertThat(Iterables.limit(result, 8)).containsExactly(0, 1, 2, 3, 1, 2, 3, 1).inOrder(); } @Test public void forTree_breadthFirst_tree() throws Exception { Traverser traverser = Traverser.forTree(TREE); assertEqualCharNodes(traverser.breadthFirst('h'), "hdegabcf"); assertEqualCharNodes(traverser.breadthFirst('d'), "dabc"); assertEqualCharNodes(traverser.breadthFirst('a'), "a"); } @Test public void forTree_breadthFirstIterable_tree() throws Exception { Traverser traverser = Traverser.forTree(TREE); assertEqualCharNodes(traverser.breadthFirst(charactersOf("")), ""); assertEqualCharNodes(traverser.breadthFirst(charactersOf("h")), "hdegabcf"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("gd")), "gdfabc"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("age")), "agef"); } @Test public void forTree_breadthFirst_cyclicGraphContainingTree() throws Exception { Traverser traverser = Traverser.forTree(CYCLIC_GRAPH_CONTAINING_TREE); assertEqualCharNodes(traverser.breadthFirst('a'), "abcd"); assertEqualCharNodes(traverser.breadthFirst('b'), "bcd"); assertEqualCharNodes(traverser.breadthFirst('d'), "d"); } @Test public void forTree_breadthFirstIterable_cyclicGraphContainingTree() throws Exception { Traverser traverser = Traverser.forTree(CYCLIC_GRAPH_CONTAINING_TREE); assertEqualCharNodes(traverser.breadthFirst(charactersOf("a")), "abcd"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("b")), "bcd"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("cd")), "cd"); } @Test public void forTree_breadthFirst_graphContainingTreeAndDiamond() throws Exception { Traverser traverser = Traverser.forTree(GRAPH_CONTAINING_TREE_AND_DIAMOND); assertEqualCharNodes(traverser.breadthFirst('a'), "abcd"); assertEqualCharNodes(traverser.breadthFirst('b'), "bcd"); assertEqualCharNodes(traverser.breadthFirst('d'), "d"); } @Test public void forTree_breadthFirstIterable_graphContainingTreeAndDiamond() throws Exception { Traverser traverser = Traverser.forTree(GRAPH_CONTAINING_TREE_AND_DIAMOND); assertEqualCharNodes(traverser.breadthFirst(charactersOf("a")), "abcd"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("bg")), "bgcdh"); assertEqualCharNodes(traverser.breadthFirst(charactersOf("ga")), "gahbcd"); } @Test public void forTree_breadthFirst_twoTrees() { Iterable result = Traverser.forTree(TWO_TREES).breadthFirst('a'); assertEqualCharNodes(result, "ab"); } @Test public void forTree_breadthFirstIterable_twoTrees() { assertEqualCharNodes(Traverser.forTree(TWO_TREES).breadthFirst(charactersOf("a")), "ab"); assertEqualCharNodes(Traverser.forTree(TWO_TREES).breadthFirst(charactersOf("ca")), "cadb"); } @Test public void forTree_breadthFirst_singleRoot() { Iterable result = Traverser.forTree(SINGLE_ROOT).breadthFirst('a'); assertEqualCharNodes(result, "a"); } @Test public void forTree_breadthFirstIterable_singleRoot() { Iterable result = Traverser.forTree(SINGLE_ROOT).breadthFirst(charactersOf("a")); assertEqualCharNodes(result, "a"); } @Test public void forTree_breadthFirst_emptyGraph() { try { Traverser.forTree(createDirectedGraph()).breadthFirst('a'); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } @Test public void forTree_breadthFirstIterable_emptyGraph() { assertEqualCharNodes( Traverser.forTree(createDirectedGraph()).breadthFirst(charactersOf("")), ""); try { Traverser.forTree(createDirectedGraph()).breadthFirst(charactersOf("a")); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } @Test public void forTree_breadthFirst_iterableIsLazy() { RequestSavingGraph graph = new RequestSavingGraph(TREE); Iterable result = Traverser.forGraph(graph).breadthFirst('h'); assertEqualCharNodes(Iterables.limit(result, 2), "hd"); assertThat(graph.requestedNodes).containsExactly('h', 'h', 'd'); // Iterate again to see if calculation is done again assertEqualCharNodes(Iterables.limit(result, 2), "hd"); assertThat(graph.requestedNodes).containsExactly('h', 'h', 'h', 'd', 'd'); } @Test public void forTree_breadthFirstIterable_iterableIsLazy() { RequestSavingGraph graph = new RequestSavingGraph(TREE); Iterable result = Traverser.forGraph(graph).breadthFirst(charactersOf("dg")); assertEqualCharNodes(Iterables.limit(result, 3), "dga"); assertThat(graph.requestedNodes).containsExactly('a', 'd', 'd', 'g', 'g'); // Iterate again to see if calculation is done again assertEqualCharNodes(Iterables.limit(result, 3), "dga"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'd', 'd', 'd', 'g', 'g', 'g'); } @Test public void forTree_depthFirstPreOrder_infinite() { Iterable result = Traverser.forTree(fixedSuccessors(Iterables.cycle(1, 2, 3))).depthFirstPreOrder(0); assertThat(Iterables.limit(result, 3)).containsExactly(0, 1, 1).inOrder(); } @Test public void forTree_depthFirstPreOrderIterable_tree() throws Exception { Traverser traverser = Traverser.forTree(TREE); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("h")), "hdabcegf"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("d")), "dabc"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("a")), "a"); } @Test public void forTree_depthFirstPreOrderIterableIterable_tree() throws Exception { Traverser traverser = Traverser.forTree(TREE); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("")), ""); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("h")), "hdabcegf"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("gd")), "gfdabc"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("age")), "agfe"); } @Test public void forTree_depthFirstPreOrder_cyclicGraphContainingTree() throws Exception { Traverser traverser = Traverser.forTree(CYCLIC_GRAPH_CONTAINING_TREE); assertEqualCharNodes(traverser.depthFirstPreOrder('a'), "abcd"); assertEqualCharNodes(traverser.depthFirstPreOrder('b'), "bcd"); assertEqualCharNodes(traverser.depthFirstPreOrder('d'), "d"); } @Test public void forTree_depthFirstPreOrderIterable_cyclicGraphContainingTree() throws Exception { Traverser traverser = Traverser.forTree(CYCLIC_GRAPH_CONTAINING_TREE); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("a")), "abcd"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("b")), "bcd"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("cd")), "cd"); } @Test public void forTree_depthFirstPreOrder_graphContainingTreeAndDiamond() throws Exception { Traverser traverser = Traverser.forTree(GRAPH_CONTAINING_TREE_AND_DIAMOND); assertEqualCharNodes(traverser.depthFirstPreOrder('a'), "abcd"); assertEqualCharNodes(traverser.depthFirstPreOrder('b'), "bcd"); assertEqualCharNodes(traverser.depthFirstPreOrder('d'), "d"); } @Test public void forTree_depthFirstPreOrderIterable_graphContainingTreeAndDiamond() throws Exception { Traverser traverser = Traverser.forTree(GRAPH_CONTAINING_TREE_AND_DIAMOND); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("a")), "abcd"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("bg")), "bcdgh"); assertEqualCharNodes(traverser.depthFirstPreOrder(charactersOf("ga")), "ghabcd"); } @Test public void forTree_depthFirstPreOrder_twoTrees() { Iterable result = Traverser.forTree(TWO_TREES).depthFirstPreOrder('a'); assertEqualCharNodes(result, "ab"); } @Test public void forTree_depthFirstPreOrderIterable_twoTrees() { assertEqualCharNodes(Traverser.forTree(TWO_TREES).depthFirstPreOrder(charactersOf("a")), "ab"); assertEqualCharNodes( Traverser.forTree(TWO_TREES).depthFirstPreOrder(charactersOf("ca")), "cdab"); } @Test public void forTree_depthFirstPreOrder_singleRoot() { Iterable result = Traverser.forTree(SINGLE_ROOT).depthFirstPreOrder('a'); assertEqualCharNodes(result, "a"); } @Test public void forTree_depthFirstPreOrderIterable_singleRoot() { Iterable result = Traverser.forTree(SINGLE_ROOT).depthFirstPreOrder(charactersOf("a")); assertEqualCharNodes(result, "a"); } @Test public void forTree_depthFirstPreOrder_emptyGraph() { try { Traverser.forTree(createDirectedGraph()).depthFirstPreOrder('a'); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } @Test public void forTree_depthFirstPreOrderIterable_emptyGraph() { assertEqualCharNodes( Traverser.forTree(createDirectedGraph()).depthFirstPreOrder(charactersOf("")), ""); try { Traverser.forTree(createDirectedGraph()).depthFirstPreOrder(charactersOf("a")); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } @Test public void forTree_depthFirstPreOrder_iterableIsLazy() { RequestSavingGraph graph = new RequestSavingGraph(TREE); Iterable result = Traverser.forGraph(graph).depthFirstPreOrder('h'); assertEqualCharNodes(Iterables.limit(result, 2), "hd"); assertThat(graph.requestedNodes).containsExactly('h', 'h', 'd'); // Iterate again to see if calculation is done again assertEqualCharNodes(Iterables.limit(result, 2), "hd"); assertThat(graph.requestedNodes).containsExactly('h', 'h', 'h', 'd', 'd'); } @Test public void forTree_depthFirstPreOrderIterable_iterableIsLazy() { RequestSavingGraph graph = new RequestSavingGraph(TREE); Iterable result = Traverser.forGraph(graph).depthFirstPreOrder(charactersOf("dg")); assertEqualCharNodes(Iterables.limit(result, 2), "da"); assertThat(graph.requestedNodes).containsExactly('a', 'd', 'd', 'g'); // Iterate again to see if calculation is done again assertEqualCharNodes(Iterables.limit(result, 2), "da"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'd', 'd', 'd', 'g'); } @Test public void forTree_depthFirstPostOrder_tree() throws Exception { Traverser traverser = Traverser.forTree(TREE); assertEqualCharNodes(traverser.depthFirstPostOrder('h'), "abcdefgh"); assertEqualCharNodes(traverser.depthFirstPostOrder('d'), "abcd"); assertEqualCharNodes(traverser.depthFirstPostOrder('a'), "a"); } @Test public void forTree_depthFirstPostOrderIterable_tree() throws Exception { Traverser traverser = Traverser.forTree(TREE); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("")), ""); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("h")), "abcdefgh"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("gd")), "fgabcd"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("age")), "afge"); } @Test public void forTree_depthFirstPostOrder_cyclicGraphContainingTree() throws Exception { Traverser traverser = Traverser.forTree(CYCLIC_GRAPH_CONTAINING_TREE); assertEqualCharNodes(traverser.depthFirstPostOrder('a'), "cdba"); assertEqualCharNodes(traverser.depthFirstPostOrder('b'), "cdb"); assertEqualCharNodes(traverser.depthFirstPostOrder('d'), "d"); } @Test public void forTree_depthFirstPostOrderIterable_cyclicGraphContainingTree() throws Exception { Traverser traverser = Traverser.forTree(CYCLIC_GRAPH_CONTAINING_TREE); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("a")), "cdba"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("b")), "cdb"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("cd")), "cd"); } @Test public void forTree_depthFirstPostOrder_graphContainingTreeAndDiamond() throws Exception { Traverser traverser = Traverser.forTree(GRAPH_CONTAINING_TREE_AND_DIAMOND); assertEqualCharNodes(traverser.depthFirstPostOrder('a'), "cdba"); assertEqualCharNodes(traverser.depthFirstPostOrder('b'), "cdb"); assertEqualCharNodes(traverser.depthFirstPostOrder('d'), "d"); } @Test public void forTree_depthFirstPostOrderIterable_graphContainingTreeAndDiamond() throws Exception { Traverser traverser = Traverser.forTree(GRAPH_CONTAINING_TREE_AND_DIAMOND); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("a")), "cdba"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("bg")), "cdbhg"); assertEqualCharNodes(traverser.depthFirstPostOrder(charactersOf("ga")), "hgcdba"); } @Test public void forTree_depthFirstPostOrder_twoTrees() { Iterable result = Traverser.forTree(TWO_TREES).depthFirstPostOrder('a'); assertEqualCharNodes(result, "ba"); } @Test public void forTree_depthFirstPostOrderIterable_twoTrees() { assertEqualCharNodes(Traverser.forTree(TWO_TREES).depthFirstPostOrder(charactersOf("a")), "ba"); assertEqualCharNodes( Traverser.forTree(TWO_TREES).depthFirstPostOrder(charactersOf("ca")), "dcba"); } @Test public void forTree_depthFirstPostOrder_singleRoot() { Iterable result = Traverser.forTree(SINGLE_ROOT).depthFirstPostOrder('a'); assertEqualCharNodes(result, "a"); } @Test public void forTree_depthFirstPostOrderIterable_singleRoot() { Iterable result = Traverser.forTree(SINGLE_ROOT).depthFirstPostOrder(charactersOf("a")); assertEqualCharNodes(result, "a"); } @Test public void forTree_depthFirstPostOrder_emptyGraph() { try { Traverser.forTree(createDirectedGraph()).depthFirstPostOrder('a'); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } @Test public void forTree_depthFirstPostOrderIterable_emptyGraph() { assertEqualCharNodes( Traverser.forTree(createDirectedGraph()).depthFirstPostOrder(charactersOf("")), ""); try { Traverser.forTree(createDirectedGraph()).depthFirstPostOrder(charactersOf("a")); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } @Test public void forTree_depthFirstPostOrder_iterableIsLazy() { RequestSavingGraph graph = new RequestSavingGraph(TREE); Iterable result = Traverser.forGraph(graph).depthFirstPostOrder('h'); assertEqualCharNodes(Iterables.limit(result, 2), "ab"); assertThat(graph.requestedNodes).containsExactly('h', 'h', 'd', 'a', 'b'); // Iterate again to see if calculation is done again assertEqualCharNodes(Iterables.limit(result, 2), "ab"); assertThat(graph.requestedNodes).containsExactly('h', 'h', 'h', 'd', 'd', 'a', 'a', 'b', 'b'); } @Test public void forTree_depthFirstPostOrderIterable_iterableIsLazy() { RequestSavingGraph graph = new RequestSavingGraph(TREE); Iterable result = Traverser.forGraph(graph).depthFirstPostOrder(charactersOf("dg")); assertEqualCharNodes(Iterables.limit(result, 2), "ab"); assertThat(graph.requestedNodes).containsExactly('a', 'b', 'd', 'd', 'g'); // Iterate again to see if calculation is done again assertEqualCharNodes(Iterables.limit(result, 2), "ab"); assertThat(graph.requestedNodes).containsExactly('a', 'a', 'b', 'b', 'd', 'd', 'd', 'g'); } private static SuccessorsFunction createDirectedGraph(String... edges) { return createGraph(/* directed = */ true, edges); } private static SuccessorsFunction createUndirectedGraph(String... edges) { return createGraph(/* directed = */ false, edges); } /** * Creates a graph from a list of node pairs (encoded as strings, e.g. "ab" means that this graph * has an edge between 'a' and 'b'). * *

The {@code successors} are always returned in alphabetical order. */ private static SuccessorsFunction createGraph(boolean directed, String... edges) { ImmutableMultimap.Builder graphMapBuilder = ImmutableMultimap.builder(); for (String edge : edges) { checkArgument( edge.length() == 2, "Expecting each edge to consist of 2 characters but got %s", edge); char node1 = edge.charAt(0); char node2 = edge.charAt(1); graphMapBuilder.put(node1, node2); if (!directed) { graphMapBuilder.put(node2, node1); } } final ImmutableMultimap graphMap = graphMapBuilder.build(); return new SuccessorsFunction() { @Override public Iterable successors(Character node) { checkArgument( graphMap.containsKey(node) || graphMap.containsValue(node), "Node %s is not an element of this graph", node); return Ordering.natural().immutableSortedCopy(graphMap.get(node)); } }; } private static ImmutableGraph createSingleRootGraph() { MutableGraph graph = GraphBuilder.directed().build(); graph.addNode('a'); return ImmutableGraph.copyOf(graph); } private static void assertEqualCharNodes(Iterable result, String expectedCharacters) { assertThat(ImmutableList.copyOf(result)) .containsExactlyElementsIn(Chars.asList(expectedCharacters.toCharArray())) .inOrder(); } private static class RequestSavingGraph implements SuccessorsFunction { private final SuccessorsFunction delegate; final Multiset requestedNodes = HashMultiset.create(); RequestSavingGraph(SuccessorsFunction delegate) { this.delegate = checkNotNull(delegate); } @Override public Iterable successors(Character node) { requestedNodes.add(node); return delegate.successors(node); } } private static SuccessorsFunction fixedSuccessors(final Iterable successors) { return new SuccessorsFunction() { @Override public Iterable successors(N n) { return successors; } }; } }