package edu.uw.zookeeper.client; import static com.google.common.base.Preconditions.checkState; import static org.junit.Assert.*; import java.util.Iterator; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import com.google.common.base.Optional; import com.google.common.collect.ImmutableSortedMap; import com.google.common.util.concurrent.Service; import edu.uw.zookeeper.RandomSingleClientTest; import edu.uw.zookeeper.SimpleServerAndClient; import edu.uw.zookeeper.common.LoggingFutureListener; import edu.uw.zookeeper.common.Processor; import edu.uw.zookeeper.common.ServiceMonitor; import edu.uw.zookeeper.common.TimeValue; import edu.uw.zookeeper.data.AbstractNameTrie; import edu.uw.zookeeper.data.LockableZNodeCache; import edu.uw.zookeeper.data.NameTrie; import edu.uw.zookeeper.data.SimpleLabelTrie; import edu.uw.zookeeper.data.ZNodeCache; import edu.uw.zookeeper.data.ZNodeLabel; import edu.uw.zookeeper.data.ZNodeName; import edu.uw.zookeeper.data.ZNodePath; import edu.uw.zookeeper.data.NameTrie.Pointer; import edu.uw.zookeeper.protocol.Message; import edu.uw.zookeeper.protocol.Operation; import edu.uw.zookeeper.protocol.proto.Records; @RunWith(JUnit4.class) public class TreeWalkerTest { protected final Logger logger = LogManager.getLogger(this); @Test(timeout=30000) public void testRandom() throws Exception { SimpleServerAndClient client = SimpleServerAndClient.defaults().setDefaults(); ServiceMonitor monitor = client.getRuntimeModule().getServiceMonitor(); for (Service service: client.build()) { monitor.add(service); } monitor.startAsync().awaitRunning(); LockableZNodeCache<ZNodeCache.SimpleCacheNode, Operation.Request, Message.ServerResponse<?>> cache = RandomSingleClientTest.randomCache( 100, TimeValue.milliseconds(5000), client.getClientBuilder().getConnectionClientExecutor(), logger); SimpleLabelTrie<SimpleTrieBuilder.Node> walk = LoggingFutureListener.listen( logger, TreeWalker.forResult(SimpleTrieBuilder.create()) .setClient(client.getClientBuilder().getConnectionClientExecutor()) .setRequests(TreeWalker.toRequests(TreeWalker.parameters().setSync(true))) .build()).get().get(); Iterator<? extends NameTrie.Node<?>> cached = SortedTraversal.forRoot(cache.cache()); Iterator<? extends NameTrie.Node<?>> walked = SortedTraversal.forRoot(walk); while (cached.hasNext()) { assertEquals(cached.next().path(), walked.next().path()); } assertFalse(walked.hasNext()); monitor.stopAsync().awaitTerminated(); } public static class SortedTraversal<E extends NameTrie.Node<E>> extends AbstractNameTrie.PreOrderTraversal<E> { public static <E extends NameTrie.Node<E>> SortedTraversal<E> forRoot(NameTrie<E> trie) { return forNode(trie.root()); } public static <E extends NameTrie.Node<E>> SortedTraversal<E> forNode(E node) { return new SortedTraversal<E>(node); } protected SortedTraversal(E root) { super(root); } @Override protected Iterable<E> childrenOf(E node) { return ImmutableSortedMap.copyOf(node).values(); } } public static class SimpleTrieBuilder implements Processor<Optional<? extends SubmittedRequest<Records.Request,?>>, Optional<SimpleLabelTrie<SimpleTrieBuilder.Node>>> { public static SimpleTrieBuilder create() { return new SimpleTrieBuilder(SimpleLabelTrie.forRoot(Node.root())); } protected final SimpleLabelTrie<Node> trie; protected SimpleTrieBuilder(SimpleLabelTrie<Node> trie) { this.trie = trie; } @Override public synchronized Optional<SimpleLabelTrie<Node>> apply( Optional<? extends SubmittedRequest<Records.Request, ?>> input) throws Exception { if (input.isPresent()) { final Records.Response response = input.get().get().record(); if (response instanceof Records.ChildrenGetter) { final ZNodePath path = ZNodePath.fromString(((Records.PathGetter) input.get().getValue()).getPath()); final Node parent = trie.get(path); for (String child: ((Records.ChildrenGetter) response).getChildren()) { ZNodeLabel label = ZNodeLabel.fromString(child); Node existing = parent.put(label, Node.child(label, parent)); checkState(existing == null); } } return Optional.absent(); } else { return Optional.of(trie); } } public static final class Node extends AbstractNameTrie.SimpleNode<Node> { public static Node root() { return new Node(AbstractNameTrie.<Node>rootPointer()); } public static Node child(ZNodeName name, Node parent) { return new Node(SimpleLabelTrie.weakPointer(name, parent)); } protected Node(Pointer<? extends Node> parent, Map<ZNodeName, Node> children) { super(parent, children); } protected Node(Pointer<? extends Node> parent) { super(parent); } protected Node(ZNodePath path, Pointer<? extends Node> parent, Map<ZNodeName, Node> children) { super(path, parent, children); } } } }