/* * ModeShape (http://www.modeshape.org) * * 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 org.modeshape.jcr.cache.document; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.notNullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.modeshape.jcr.cache.CachedNode; import org.modeshape.jcr.cache.ChildReferences; import org.modeshape.jcr.cache.NodeCache; import org.modeshape.jcr.cache.NodeKey; public class NodeCacheIteratorTest { private final static String SOURCE_KEY = NodeKey.keyForSourceName("source"); private final static String WORKSPACE_KEY = NodeKey.keyForSourceName("workspace"); protected NodeCache cache; private List<NodeKey> allKeys; @Before public void beforeEach() { allKeys = new ArrayList<NodeKey>(); cache = Mockito.mock(NodeCache.class); addNode("root", "node1", "node2", "node3"); addNode("node1", "node11", "node12", "node13"); addNode("node2", "node21", "node22", "node23"); addNode("node3", "node31", "node32", "node33"); addNodeWithLeafChildren("node11", "node111", "node112", "node113"); addNodeWithLeafChildren("node12", "node121", "node122", "node123"); addNodeWithLeafChildren("node13", "node131", "node132", "node133"); addNodeWithLeafChildren("node21", "node211", "node212", "node213"); addNodeWithLeafChildren("node22", "node221", "node222", "node223"); addNodeWithLeafChildren("node23", "node231", "node232", "node233"); addNodeWithLeafChildren("node31", "node311", "node312", "node313"); addNodeWithLeafChildren("node32", "node321", "node322", "node323"); addNodeWithLeafChildren("node33", "node331", "node332", "node333"); when(cache.getAllNodeKeys()).thenAnswer(new Answer<Iterator<NodeKey>>() { @Override public Iterator<NodeKey> answer( InvocationOnMock invocation ) throws Throwable { // Need to create a new iterator every time ... return new NodeCacheIterator(cache, nodeKey("root")); } }); when(cache.getAllNodeKeysAtAndBelow((NodeKey)anyObject())).thenAnswer(new Answer<Iterator<NodeKey>>() { @Override public Iterator<NodeKey> answer( InvocationOnMock invocation ) throws Throwable { // Need to create a new iterator every time ... NodeKey nodeKey = (NodeKey)invocation.getArguments()[0]; return new NodeCacheIterator(cache, nodeKey); } }); } @After public void afterEach() { cache = null; } @Test public void shouldIterateOverAllNodeKeysInTheCacheWhenUsingHasNextAndNext() { Set<NodeKey> expected = new HashSet<NodeKey>(allKeys); Iterator<NodeKey> iter = cache.getAllNodeKeys(); while (iter.hasNext()) { NodeKey key = iter.next(); assertThat(key, is(notNullValue())); assertThat(expected.remove(key), is(true)); } assertThat(expected.isEmpty(), is(true)); } @Test public void shouldIterateOverAllNodeKeysInTheCacheWhenUsingOnlyNext() { Set<NodeKey> expected = new HashSet<NodeKey>(allKeys); Iterator<NodeKey> iter = cache.getAllNodeKeys(); for (int i = 0; i != allKeys.size(); ++i) { NodeKey key = iter.next(); assertThat(key, is(notNullValue())); assertThat(expected.remove(key), is(true)); } assertThat(expected.isEmpty(), is(true)); try { iter.next(); fail("Should have thrown a NoSuchElementException"); } catch (NoSuchElementException e) { // expected } } @Test public void shouldIterateOverAllNodeKeysInTheCacheBelowBranchWhenUsingHasNextAndNext() { assertIterateOverSubtreeWhenUsingHasNextAndNext(nodeKey("node1")); } @Test public void shouldIterateOverAllNodeKeysInTheCacheBelowBranchWhenUsingOnlyNext() { assertIterateOverSubtreeWhenUsingOnlyNext(nodeKey("node1")); } protected void assertIterateOverSubtreeWhenUsingHasNextAndNext( NodeKey startingKey ) { Set<NodeKey> expected = findAllNodesAtOrBelow(startingKey); Iterator<NodeKey> iter = cache.getAllNodeKeysAtAndBelow(startingKey); while (iter.hasNext()) { NodeKey key = iter.next(); assertThat(key, is(notNullValue())); assertThat(expected.remove(key), is(true)); } assertThat(expected.isEmpty(), is(true)); } protected void assertIterateOverSubtreeWhenUsingOnlyNext( NodeKey startingKey ) { Set<NodeKey> expected = findAllNodesAtOrBelow(startingKey); Iterator<NodeKey> iter = cache.getAllNodeKeysAtAndBelow(startingKey); long size = expected.size(); for (int i = 0; i != size; ++i) { NodeKey key = iter.next(); assertThat(key, is(notNullValue())); assertThat(expected.remove(key), is(true)); } assertThat(expected.isEmpty(), is(true)); try { iter.next(); fail("Should have thrown a NoSuchElementException"); } catch (NoSuchElementException e) { // expected } } protected Set<NodeKey> findAllNodesAtOrBelow( NodeKey key ) { Set<NodeKey> foundKeys = new HashSet<NodeKey>(); findAllNodesAtOrBelow(key, foundKeys); return foundKeys; } protected void findAllNodesAtOrBelow( NodeKey key, Set<NodeKey> foundKeys ) { CachedNode node = cache.getNode(key); assertThat(node, is(notNullValue())); foundKeys.add(key); Iterator<NodeKey> iter = node.getChildReferences(cache).getAllKeys(); while (iter.hasNext()) { findAllNodesAtOrBelow(iter.next(), foundKeys); } } // ---------------------------------------------------------------- // Utility methods to construct the NodeCache ... // ---------------------------------------------------------------- protected NodeKey nodeKey( String id ) { return new NodeKey(SOURCE_KEY, WORKSPACE_KEY, id); } protected void addNode( String key, String... childKeys ) { // Mock the ChildReferences and stub out the only method we'll call ... final List<NodeKey> childKeyList = new ArrayList<NodeKey>(); for (String childKey : childKeys) { childKeyList.add(nodeKey(childKey)); } ChildReferences childRefs = Mockito.mock(ChildReferences.class); when(childRefs.getAllKeys()).thenAnswer(new Answer<Iterator<NodeKey>>() { @Override public Iterator<NodeKey> answer( InvocationOnMock invocation ) throws Throwable { // Need to create a new iterator every time ... return childKeyList.iterator(); } }); // Mock the CachedNode and stub out the only method we'll call ... NodeKey nodeKey = nodeKey(key); CachedNode node = Mockito.mock(CachedNode.class); when(node.getChildReferences(cache)).thenReturn(childRefs); // Stub the cache invocation ... when(cache.getNode(nodeKey)).thenReturn(node); // Add the key to our master list ... allKeys.add(nodeKey); } protected void addNodeWithLeafChildren( String key, String... childKeys ) { addNode(key, childKeys); for (String childKey : childKeys) { addNode(childKey); } } }