| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.commons.configuration2.tree; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNotSame; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertSame; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.commons.configuration2.ex.ConfigurationRuntimeException; |
| import org.easymock.EasyMock; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| |
| /** |
| * A special test class for {@code InMemoryNodeModel} which tests the facilities |
| * for tracking nodes. |
| * |
| */ |
| public class TestInMemoryNodeModelTrackedNodes |
| { |
| /** Constant for the name of a new table field. */ |
| private static final String NEW_FIELD = "newTableField"; |
| |
| /** Constant for a test key. */ |
| private static final String TEST_KEY = "someTestKey"; |
| |
| /** Constant for the key used by the test selector. */ |
| private static final String SELECTOR_KEY = "tables.table(1)"; |
| |
| /** The root node for the test hierarchy. */ |
| private static ImmutableNode root; |
| |
| /** A default node selector initialized with a test key. */ |
| private static NodeSelector selector; |
| |
| /** The model to be tested. */ |
| private InMemoryNodeModel model; |
| |
| @BeforeClass |
| public static void setUpBeforeClass() throws Exception |
| { |
| root = |
| new ImmutableNode.Builder(1).addChild( |
| NodeStructureHelper.ROOT_TABLES_TREE).create(); |
| selector = new NodeSelector(SELECTOR_KEY); |
| } |
| |
| @Before |
| public void setUp() throws Exception |
| { |
| model = new InMemoryNodeModel(root); |
| } |
| |
| /** |
| * Creates a default resolver which supports arbitrary queries on a target |
| * node. |
| * |
| * @return the resolver |
| */ |
| private static NodeKeyResolver<ImmutableNode> createResolver() |
| { |
| return createResolver(true); |
| } |
| |
| /** |
| * Creates a default resolver which supports arbitrary queries on a target |
| * node and allows specifying the replay flag. If the boolean parameter is |
| * false, the mock is not replayed; so additional behaviors can be defined. |
| * |
| * @param replay the replay flag |
| * @return the resolver mock |
| */ |
| private static NodeKeyResolver<ImmutableNode> createResolver(final boolean replay) |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = |
| NodeStructureHelper.createResolverMock(); |
| NodeStructureHelper.expectResolveKeyForQueries(resolver); |
| if (replay) |
| { |
| EasyMock.replay(resolver); |
| } |
| return resolver; |
| } |
| |
| /** |
| * Tries to call trackNode() with a key that does not yield any results. |
| */ |
| @Test(expected = ConfigurationRuntimeException.class) |
| public void testTrackNodeKeyNoResults() |
| { |
| model.trackNode(new NodeSelector("tables.unknown"), createResolver()); |
| } |
| |
| /** |
| * Tries to call trackNode() with a key that selects multiple results. |
| */ |
| @Test(expected = ConfigurationRuntimeException.class) |
| public void testTrackNodeKeyMultipleResults() |
| { |
| model.trackNode(new NodeSelector("tables.table.fields.field.name"), |
| createResolver()); |
| } |
| |
| /** |
| * Tests whether a tracked node can be queried. |
| */ |
| @Test |
| public void testGetTrackedNodeExisting() |
| { |
| final ImmutableNode node = |
| NodeStructureHelper.nodeForKey(model, "tables/table(1)"); |
| model.trackNode(selector, createResolver()); |
| assertSame("Wrong node", node, model.getTrackedNode(selector)); |
| } |
| |
| /** |
| * Tries to obtain a tracked node which is unknown. |
| */ |
| @Test(expected = ConfigurationRuntimeException.class) |
| public void testGetTrackedNodeNonExisting() |
| { |
| model.getTrackedNode(selector); |
| } |
| |
| /** |
| * Tests whether a tracked node survives updates of the node model. |
| */ |
| @Test |
| public void testGetTrackedNodeAfterUpdate() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| model.clearProperty("tables.table(1).fields.field(1).name", resolver); |
| final ImmutableNode node = model.getTrackedNode(selector); |
| assertEquals("Wrong node", NodeStructureHelper.table(1), node |
| .getChildren().get(0).getValue()); |
| } |
| |
| /** |
| * Tests whether a tracked node can be queried even if it was removed from |
| * the structure. |
| */ |
| @Test |
| public void testGetTrackedNodeAfterUpdateNoLongerExisting() |
| { |
| final ImmutableNode node = |
| NodeStructureHelper.nodeForKey(model, "tables/table(1)"); |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| initDetachedNode(resolver); |
| assertSame("Wrong node", node, model.getTrackedNode(selector)); |
| } |
| |
| /** |
| * Produces a tracked node with the default selector and executes an |
| * operation which detaches this node. |
| * |
| * @param resolver the {@code NodeKeyResolver} |
| */ |
| private void initDetachedNode(final NodeKeyResolver<ImmutableNode> resolver) |
| { |
| model.trackNode(selector, resolver); |
| model.clearTree("tables.table(0)", resolver); |
| } |
| |
| /** |
| * Tests whether a tracked node can be queried even after the model was |
| * cleared. |
| */ |
| @Test |
| public void testGetTrackedNodeAfterClear() |
| { |
| final ImmutableNode node = |
| NodeStructureHelper.nodeForKey(model, "tables/table(1)"); |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| model.clear(resolver); |
| assertSame("Wrong node", node, model.getTrackedNode(selector)); |
| } |
| |
| /** |
| * Tests whether a tracked node can be queried after the root node was |
| * changed. |
| */ |
| @Test |
| public void testGetTrackedNodeAfterSetRootNode() |
| { |
| final ImmutableNode node = |
| NodeStructureHelper.nodeForKey(model, "tables/table(1)"); |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| model.setRootNode(root); |
| assertSame("Wrong node", node, model.getTrackedNode(selector)); |
| } |
| |
| /** |
| * Tries to stop tracking of a node which is not tracked. |
| */ |
| @Test(expected = ConfigurationRuntimeException.class) |
| public void testUntrackNodeNonExisting() |
| { |
| model.untrackNode(selector); |
| } |
| |
| /** |
| * Tests whether tracking of a node can be stopped. |
| */ |
| @Test |
| public void testUntrackNode() |
| { |
| model.trackNode(selector, createResolver()); |
| model.untrackNode(selector); |
| try |
| { |
| model.getTrackedNode(selector); |
| fail("Could get untracked node!"); |
| } |
| catch (final ConfigurationRuntimeException crex) |
| { |
| // expected |
| } |
| } |
| |
| /** |
| * Tests whether a single node can be tracked multiple times. |
| */ |
| @Test |
| public void testTrackNodeMultipleTimes() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| model.trackNode(selector, resolver); |
| model.untrackNode(selector); |
| assertNotNull("No tracked node", model.getTrackedNode(selector)); |
| } |
| |
| /** |
| * Tests isDetached() for a node which has just been tracked. |
| */ |
| @Test |
| public void testIsDetachedFalseNoUpdates() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| assertFalse("Node is detached", model.isTrackedNodeDetached(selector)); |
| } |
| |
| /** |
| * Tests isDetached() for a life node. |
| */ |
| @Test |
| public void testIsDetachedFalseAfterUpdate() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| model.clearProperty("tables.table(1).fields.field(1).name", resolver); |
| assertFalse("Node is detached", model.isTrackedNodeDetached(selector)); |
| } |
| |
| /** |
| * Tests isDetached() for an actually detached node. |
| */ |
| @Test |
| public void testIsDetachedTrue() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| initDetachedNode(resolver); |
| assertTrue("Node is not detached", |
| model.isTrackedNodeDetached(selector)); |
| } |
| |
| /** |
| * Tests whether a clear() operation causes nodes to be detached. |
| */ |
| @Test |
| public void testIsDetachedAfterClear() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| model.clear(resolver); |
| assertTrue("Node is not detached", |
| model.isTrackedNodeDetached(selector)); |
| } |
| |
| /** |
| * Tests whether tracked nodes become detached when a new root node is set. |
| */ |
| @Test |
| public void testIsDetachedAfterSetRoot() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| model.clearProperty("tables.table(1).fields.field(1).name", resolver); |
| model.setRootNode(root); |
| assertTrue("Node is not detached", |
| model.isTrackedNodeDetached(selector)); |
| } |
| |
| /** |
| * Returns the fields node from the model. |
| * |
| * @return the fields node |
| */ |
| private ImmutableNode fieldsNodeFromModel() |
| { |
| return NodeStructureHelper.nodeForKey(model, "tables/table(1)/fields"); |
| } |
| |
| /** |
| * Returns the fields node from a tracked node. |
| * |
| * @return the fields node |
| */ |
| private ImmutableNode fieldsNodeFromTrackedNode() |
| { |
| return NodeStructureHelper.nodeForKey(model.getTrackedNode(selector), |
| "fields"); |
| } |
| |
| /** |
| * Helper method for checking whether the expected field node was removed. |
| * |
| * @param nodeFields the fields node |
| * @param idx the index of the removed field |
| */ |
| private static void checkForRemovedField(final ImmutableNode nodeFields, final int idx) |
| { |
| assertEquals("Field not removed", |
| NodeStructureHelper.fieldsLength(1) - 1, nodeFields |
| .getChildren().size()); |
| final Set<String> expectedNames = new HashSet<>(); |
| final Set<String> actualNames = new HashSet<>(); |
| for (int i = 0; i < NodeStructureHelper.fieldsLength(1); i++) |
| { |
| if (idx != i) |
| { |
| expectedNames.add(NodeStructureHelper.field(1, i)); |
| } |
| } |
| for (final ImmutableNode field : nodeFields) |
| { |
| final ImmutableNode nodeName = field.getChildren().get(0); |
| actualNames.add(String.valueOf(nodeName.getValue())); |
| } |
| assertEquals("Wrong field names", expectedNames, actualNames); |
| } |
| |
| /** |
| * Tests whether clearProperty() can operate on a tracked node. |
| */ |
| @Test |
| public void testClearPropertyOnTrackedNode() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| model.clearProperty("fields.field(0).name", selector, resolver); |
| final ImmutableNode nodeFields = fieldsNodeFromModel(); |
| checkForRemovedField(nodeFields, 0); |
| } |
| |
| /** |
| * Tests a clearProperty() operation on a tracked node which is detached. |
| */ |
| @Test |
| public void testClearPropertyOnDetachedNode() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| initDetachedNode(resolver); |
| final ImmutableNode rootNode = model.getRootNode(); |
| model.clearProperty("fields.field(0).name", selector, resolver); |
| assertSame("Model root was changed", rootNode, model.getRootNode()); |
| final ImmutableNode nodeFields = fieldsNodeFromTrackedNode(); |
| checkForRemovedField(nodeFields, 0); |
| } |
| |
| /** |
| * Tests whether clearTree() can operate on a tracked node. |
| */ |
| @Test |
| public void testClearTreeOnTrackedNode() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| model.clearTree("fields.field(1)", selector, resolver); |
| final ImmutableNode nodeFields = fieldsNodeFromModel(); |
| checkForRemovedField(nodeFields, 1); |
| } |
| |
| /** |
| * Tests a clearTree() operation on a tracked node which is detached. |
| */ |
| @Test |
| public void testClearTreeOnDetachedNode() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| initDetachedNode(resolver); |
| final ImmutableNode rootNode = model.getRootNode(); |
| model.clearTree("fields.field(1)", selector, resolver); |
| assertSame("Model root was changed", rootNode, model.getRootNode()); |
| final ImmutableNode nodeFields = fieldsNodeFromTrackedNode(); |
| checkForRemovedField(nodeFields, 1); |
| } |
| |
| /** |
| * Tests whether a field node was added. |
| * |
| * @param nodeFields the fields node |
| */ |
| private static void checkForAddedField(final ImmutableNode nodeFields) |
| { |
| assertEquals("Wrong number of children", |
| NodeStructureHelper.fieldsLength(1) + 1, nodeFields |
| .getChildren().size()); |
| final ImmutableNode nodeField = |
| nodeFields.getChildren().get( |
| NodeStructureHelper.fieldsLength(1)); |
| checkFieldNode(nodeField, NEW_FIELD); |
| } |
| |
| /** |
| * Checks whether a field node has the expected content. |
| * |
| * @param nodeField the field node to be checked |
| * @param name the expected name of this field |
| */ |
| private static void checkFieldNode(final ImmutableNode nodeField, final String name) |
| { |
| assertEquals("Wrong node name", "field", nodeField.getNodeName()); |
| assertEquals("Wrong number of children of field node", 1, nodeField |
| .getChildren().size()); |
| final ImmutableNode nodeName = nodeField.getChildren().get(0); |
| assertEquals("Wrong name of name node", "name", nodeName.getNodeName()); |
| assertEquals("Wrong node value", name, nodeName.getValue()); |
| } |
| |
| /** |
| * Tests whether an addProperty() operation works on a tracked node. |
| */ |
| @Test |
| public void testAddPropertyOnTrackedNode() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(false); |
| NodeStructureHelper.expectResolveAddKeys(resolver); |
| EasyMock.replay(resolver); |
| model.trackNode(selector, resolver); |
| model.addProperty("fields.field(-1).name", selector, |
| Collections.singleton(NEW_FIELD), resolver); |
| checkForAddedField(fieldsNodeFromModel()); |
| checkForAddedField(fieldsNodeFromTrackedNode()); |
| } |
| |
| /** |
| * Tests an addProperty() operation on a tracked node that is detached. |
| */ |
| @Test |
| public void testAddPropertyOnDetachedNode() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(false); |
| NodeStructureHelper.expectResolveAddKeys(resolver); |
| EasyMock.replay(resolver); |
| model.trackNode(selector, resolver); |
| initDetachedNode(resolver); |
| final ImmutableNode rootNode = model.getRootNode(); |
| model.addProperty("fields.field(-1).name", selector, |
| Collections.singleton(NEW_FIELD), resolver); |
| assertSame("Root node was changed", rootNode, model.getRootNode()); |
| checkForAddedField(fieldsNodeFromTrackedNode()); |
| } |
| |
| /** |
| * Tests whether an addNodes() operation works on a tracked node. |
| */ |
| @Test |
| public void testAddNodesOnTrackedNode() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(false); |
| NodeStructureHelper.expectResolveAddKeys(resolver); |
| EasyMock.replay(resolver); |
| model.trackNode(selector, resolver); |
| model.addNodes("fields", selector, Collections |
| .singleton(NodeStructureHelper.createFieldNode(NEW_FIELD)), |
| resolver); |
| checkForAddedField(fieldsNodeFromModel()); |
| checkForAddedField(fieldsNodeFromTrackedNode()); |
| } |
| |
| /** |
| * Tests an addNodes() operation on a tracked node that is detached. |
| */ |
| @Test |
| public void testAddNodesOnDetachedNode() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(false); |
| NodeStructureHelper.expectResolveAddKeys(resolver); |
| EasyMock.replay(resolver); |
| model.trackNode(selector, resolver); |
| initDetachedNode(resolver); |
| final ImmutableNode rootNode = model.getRootNode(); |
| model.addNodes("fields", selector, Collections |
| .singleton(NodeStructureHelper.createFieldNode(NEW_FIELD)), |
| resolver); |
| assertSame("Root node was changed", rootNode, model.getRootNode()); |
| checkForAddedField(fieldsNodeFromTrackedNode()); |
| } |
| |
| /** |
| * Prepares a mock for a resolver to handle keys for update operations. |
| * Support is limited. It is expected that only a single value is changed. |
| * |
| * @param resolver the {@code NodeKeyResolver} mock |
| */ |
| private static void prepareResolverForUpdateKeys( |
| final NodeKeyResolver<ImmutableNode> resolver) |
| { |
| EasyMock.expect( |
| resolver.resolveUpdateKey( |
| EasyMock.anyObject(ImmutableNode.class), |
| EasyMock.anyString(), EasyMock.anyObject(), |
| EasyMock.anyObject(TreeData.class))) |
| .andAnswer(() -> { |
| final ImmutableNode root = |
| (ImmutableNode) EasyMock.getCurrentArguments()[0]; |
| final String key = (String) EasyMock.getCurrentArguments()[1]; |
| final TreeData handler = |
| (TreeData) EasyMock.getCurrentArguments()[3]; |
| final List<QueryResult<ImmutableNode>> results = |
| DefaultExpressionEngine.INSTANCE.query(root, |
| key, handler); |
| assertEquals("Wrong number of query results", 1, |
| results.size()); |
| return new NodeUpdateData<>(Collections |
| .singletonMap(results.get(0), |
| EasyMock.getCurrentArguments()[2]), |
| null, null, null); |
| }).anyTimes(); |
| } |
| |
| /** |
| * Checks whether a fields node was correctly changed by an update |
| * operation. |
| * |
| * @param nodeFields the fields node |
| * @param idx the index of the changed node |
| */ |
| private static void checkedForChangedField(final ImmutableNode nodeFields, final int idx) |
| { |
| assertEquals("Wrong number of field nodes", |
| NodeStructureHelper.fieldsLength(1), nodeFields.getChildren() |
| .size()); |
| int childIndex = 0; |
| for (final ImmutableNode field : nodeFields) |
| { |
| final String expName = |
| childIndex == idx ? NEW_FIELD : NodeStructureHelper |
| .field(1, childIndex); |
| checkFieldNode(field, expName); |
| childIndex++; |
| } |
| } |
| |
| /** |
| * Tests whether a setProperty() operation works on a tracked node. |
| */ |
| @Test |
| public void testSetPropertyOnTrackedNode() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(false); |
| prepareResolverForUpdateKeys(resolver); |
| EasyMock.replay(resolver); |
| model.trackNode(selector, resolver); |
| model.setProperty("fields.field(0).name", selector, NEW_FIELD, resolver); |
| checkedForChangedField(fieldsNodeFromModel(), 0); |
| checkedForChangedField(fieldsNodeFromTrackedNode(), 0); |
| } |
| |
| /** |
| * Tests a setProperty() operation on a tracked node that is detached. |
| */ |
| @Test |
| public void testSetPropertyOnDetachedNode() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(false); |
| prepareResolverForUpdateKeys(resolver); |
| EasyMock.replay(resolver); |
| model.trackNode(selector, resolver); |
| initDetachedNode(resolver); |
| final ImmutableNode rootNode = model.getRootNode(); |
| model.setProperty("fields.field(0).name", selector, NEW_FIELD, resolver); |
| assertSame("Root node of model was changed", rootNode, |
| model.getRootNode()); |
| checkedForChangedField(fieldsNodeFromTrackedNode(), 0); |
| } |
| |
| /** |
| * Tests whether a tracked node is handled correctly if an operation is |
| * executed on this node which causes the node to be detached. In this case, |
| * the node should be cleared (it makes no sense to use the last defined |
| * node instance). |
| */ |
| @Test |
| public void testTrackedNodeClearedInOperation() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| model.clearTree(null, selector, resolver); |
| assertTrue("Node not detached", model.isTrackedNodeDetached(selector)); |
| final ImmutableNode node = model.getTrackedNode(selector); |
| assertEquals("Name was changed", "table", node.getNodeName()); |
| assertFalse("Node is defined", model.getNodeHandler().isDefined(node)); |
| } |
| |
| /** |
| * Tests whether a node handler for a tracked node can be queried which is |
| * still active. |
| */ |
| @Test |
| public void testGetTrackedNodeHandlerActive() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| final NodeHandler<ImmutableNode> handler = |
| model.getTrackedNodeHandler(selector); |
| assertTrue("Wrong node handler: " + handler, |
| handler instanceof TrackedNodeHandler); |
| assertSame("Wrong root node", model.getTrackedNode(selector), |
| handler.getRootNode()); |
| final TrackedNodeHandler tnh = (TrackedNodeHandler) handler; |
| assertSame("Wrong parent handler", model.getTreeData(), |
| tnh.getParentHandler()); |
| } |
| |
| /** |
| * Tests whether a node handler for a detached tracked node can be queried. |
| */ |
| @Test |
| public void testGetTrackedNodeHandlerDetached() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| initDetachedNode(resolver); |
| final NodeHandler<ImmutableNode> handler = |
| model.getTrackedNodeHandler(selector); |
| assertSame("Wrong root node", model.getTrackedNode(selector), |
| handler.getRootNode()); |
| assertTrue("Wrong handler: " + handler, handler instanceof TreeData); |
| assertNotSame("Shared handler", model.getNodeHandler(), handler); |
| } |
| |
| /** |
| * Helper method for testing whether a tracked node can be replaced. |
| */ |
| private void checkReplaceTrackedNode() |
| { |
| final ImmutableNode newNode = |
| new ImmutableNode.Builder().name("newNode").create(); |
| model.replaceTrackedNode(selector, newNode); |
| assertSame("Node not changed", newNode, model.getTrackedNode(selector)); |
| assertTrue("Node not detached", model.isTrackedNodeDetached(selector)); |
| } |
| |
| /** |
| * Tests whether an active tracked node can be replaced. |
| */ |
| @Test |
| public void testReplaceTrackedNodeForActiveTrackedNode() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| checkReplaceTrackedNode(); |
| } |
| |
| /** |
| * Tests whether a detached tracked node can be replaced. |
| */ |
| @Test |
| public void testReplaceTrackedNodeForDetachedNode() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| initDetachedNode(resolver); |
| checkReplaceTrackedNode(); |
| } |
| |
| /** |
| * Tries to replace a tracked node with a null node. |
| */ |
| @Test(expected = IllegalArgumentException.class) |
| public void testReplaceTrackedNodeNull() |
| { |
| model.trackNode(selector, createResolver()); |
| model.replaceTrackedNode(selector, null); |
| } |
| |
| /** |
| * Prepares the resolver mock to expect a nodeKey() request. |
| * |
| * @param resolver the {@code NodeKeyResolver} |
| * @param node the node whose name is to be resolved |
| * @param key the key to be returned for this node |
| */ |
| private void expectNodeKey(final NodeKeyResolver<ImmutableNode> resolver, |
| final ImmutableNode node, final String key) |
| { |
| final Map<ImmutableNode, String> cache = new HashMap<>(); |
| EasyMock.expect(resolver.nodeKey(node, cache, model.getNodeHandler())) |
| .andReturn(key); |
| } |
| |
| /** |
| * Tests whether tracked nodes can be created from a key. |
| */ |
| @Test |
| public void testSelectAndTrackNodes() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(false); |
| final String nodeKey1 = "tables/table(0)"; |
| final String nodeKey2 = "tables/table(1)"; |
| final ImmutableNode node1 = NodeStructureHelper.nodeForKey(root, nodeKey1); |
| final ImmutableNode node2 = NodeStructureHelper.nodeForKey(root, nodeKey2); |
| EasyMock.expect( |
| resolver.resolveNodeKey(root, TEST_KEY, model.getNodeHandler())) |
| .andReturn(Arrays.asList(node1, node2)); |
| expectNodeKey(resolver, node1, nodeKey1); |
| expectNodeKey(resolver, node2, nodeKey2); |
| EasyMock.replay(resolver); |
| |
| final Collection<NodeSelector> selectors = |
| model.selectAndTrackNodes(TEST_KEY, resolver); |
| final Iterator<NodeSelector> it = selectors.iterator(); |
| NodeSelector sel = it.next(); |
| assertEquals("Wrong selector 1", new NodeSelector(nodeKey1), sel); |
| assertSame("Wrong tracked node 1", node1, model.getTrackedNode(sel)); |
| sel = it.next(); |
| assertEquals("Wrong selector 2", new NodeSelector(nodeKey2), sel); |
| assertSame("Wrong tracked node 2", node2, model.getTrackedNode(sel)); |
| assertFalse("Too many selectors", it.hasNext()); |
| } |
| |
| /** |
| * Tests selectAndTrackNodes() if the key does not select any nodes. |
| */ |
| @Test |
| public void testSelectAndTrackNodesNoSelection() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(false); |
| EasyMock.expect( |
| resolver.resolveNodeKey(root, TEST_KEY, model.getNodeHandler())) |
| .andReturn(Collections.<ImmutableNode>emptyList()); |
| EasyMock.replay(resolver); |
| |
| assertTrue("Got selectors", |
| model.selectAndTrackNodes(TEST_KEY, resolver).isEmpty()); |
| } |
| |
| /** |
| * Tests whether selectAndTrackNodes() works for nodes that are already |
| * tracked. |
| */ |
| @Test |
| public void testSelectAndTrackNodesNodeAlreadyTracked() |
| { |
| NodeKeyResolver<ImmutableNode> resolver = createResolver(); |
| model.trackNode(selector, resolver); |
| resolver = createResolver(false); |
| final ImmutableNode node = model.getTrackedNode(selector); |
| EasyMock.expect( |
| resolver.resolveNodeKey(root, TEST_KEY, model.getNodeHandler())) |
| .andReturn(Collections.singletonList(node)); |
| expectNodeKey(resolver, node, SELECTOR_KEY); |
| EasyMock.replay(resolver); |
| |
| final Collection<NodeSelector> selectors = |
| model.selectAndTrackNodes(TEST_KEY, resolver); |
| assertEquals("Wrong number of selectors", 1, selectors.size()); |
| assertEquals("Wrong selector", selector, selectors.iterator().next()); |
| model.untrackNode(selector); |
| assertSame("Node not tracked", node, model.getTrackedNode(selector)); |
| } |
| |
| /** |
| * Tests whether all children of a node can be tracked at once. |
| */ |
| @Test |
| public void testTrackChildNodes() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(false); |
| final ImmutableNode node = NodeStructureHelper.nodeForKey(root, "tables"); |
| final String[] keys = new String[node.getChildren().size()]; |
| for (int i = 0; i < keys.length; i++) |
| { |
| final ImmutableNode child = node.getChildren().get(i); |
| keys[i] = |
| String.format("%s.%s(%d)", node.getNodeName(), |
| child.getNodeName(), i); |
| expectNodeKey(resolver, child, keys[i]); |
| } |
| EasyMock.expect( |
| resolver.resolveNodeKey(root, TEST_KEY, model.getNodeHandler())) |
| .andReturn(Collections.singletonList(node)); |
| EasyMock.replay(resolver); |
| |
| final Collection<NodeSelector> selectors = |
| model.trackChildNodes(TEST_KEY, resolver); |
| assertEquals("Wrong number of selectors", node.getChildren().size(), |
| selectors.size()); |
| int idx = 0; |
| for (final NodeSelector sel : selectors) |
| { |
| assertEquals("Wrong selector", new NodeSelector(keys[idx]), sel); |
| assertEquals("Wrong tracked node for " + sel, node.getChildren() |
| .get(idx), model.getTrackedNode(sel)); |
| idx++; |
| } |
| } |
| |
| /** |
| * Checks trackChildNodes() if the passed in key has a result set which |
| * causes the operation to be aborted. |
| * |
| * @param queryResult the result set of the key |
| */ |
| private void checkTrackChildNodesNoResult(final List<ImmutableNode> queryResult) |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(false); |
| EasyMock.expect( |
| resolver.resolveNodeKey(root, TEST_KEY, model.getNodeHandler())) |
| .andReturn(queryResult); |
| EasyMock.replay(resolver); |
| final TreeData oldData = model.getTreeData(); |
| |
| assertTrue("Got selectors", model.trackChildNodes(TEST_KEY, resolver) |
| .isEmpty()); |
| assertSame("Model was changed", oldData, model.getTreeData()); |
| } |
| |
| /** |
| * Tests trackChildNodes() for a key that does not return any results. |
| */ |
| @Test |
| public void testTrackChildNodesNoResults() |
| { |
| checkTrackChildNodesNoResult(Collections.<ImmutableNode> emptyList()); |
| } |
| |
| /** |
| * Tests trackChildNodes() for a key that returns more than a single result. |
| */ |
| @Test |
| public void testTrackChildNodesMultipleResults() |
| { |
| checkTrackChildNodesNoResult(Arrays.asList( |
| NodeStructureHelper.nodeForKey(root, "tables/table(0)"), |
| NodeStructureHelper.nodeForKey(root, "tables/table(1)"))); |
| } |
| |
| /** |
| * Tests trackChildNodes() for a key pointing to a node with no children. |
| */ |
| @Test |
| public void testTrackChildNodesNodeWithNoChildren() |
| { |
| checkTrackChildNodesNoResult(Collections |
| .singletonList(NodeStructureHelper.nodeForKey(root, |
| "tables/table(0)/name"))); |
| } |
| |
| /** |
| * Tests whether an existing child of a selected node can be tracked. |
| */ |
| @Test |
| public void testTrackChildNodeWithCreationExisting() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(false); |
| final String childName = "name"; |
| final String parentKey = "tables/table(0)"; |
| final String childKey = parentKey + "/" + childName; |
| final ImmutableNode node = NodeStructureHelper.nodeForKey(model, parentKey); |
| final ImmutableNode child = NodeStructureHelper.nodeForKey(node, childName); |
| EasyMock.expect( |
| resolver.resolveNodeKey(root, TEST_KEY, model.getNodeHandler())) |
| .andReturn(Collections.singletonList(node)); |
| expectNodeKey(resolver, child, childKey); |
| EasyMock.replay(resolver); |
| |
| final NodeSelector childSelector = |
| model.trackChildNodeWithCreation(TEST_KEY, childName, resolver); |
| assertEquals("Wrong selector", new NodeSelector(childKey), |
| childSelector); |
| assertSame("Wrong tracked node", child, |
| model.getTrackedNode(childSelector)); |
| } |
| |
| /** |
| * Tests whether a child node to be tracked is created if necessary. |
| */ |
| @Test |
| public void testTrackChildNodeWithCreationNonExisting() |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(false); |
| final String childName = "space"; |
| final String parentKey = "tables/table(0)"; |
| final String childKey = parentKey + "/" + childName; |
| final ImmutableNode node = NodeStructureHelper.nodeForKey(model, parentKey); |
| EasyMock.expect( |
| resolver.resolveNodeKey(root, TEST_KEY, model.getNodeHandler())) |
| .andReturn(Collections.singletonList(node)); |
| EasyMock.expect( |
| resolver.nodeKey(EasyMock.anyObject(ImmutableNode.class), |
| EasyMock.eq(new HashMap<>()), |
| EasyMock.anyObject(TreeData.class))) |
| .andReturn(childKey); |
| EasyMock.replay(resolver); |
| |
| final NodeSelector childSelector = |
| model.trackChildNodeWithCreation(TEST_KEY, childName, resolver); |
| assertEquals("Wrong selector", new NodeSelector(childKey), |
| childSelector); |
| final ImmutableNode child = model.getTrackedNode(childSelector); |
| assertEquals("Wrong child name", childName, child.getNodeName()); |
| assertNull("Got a value", child.getValue()); |
| final ImmutableNode parent = model.getNodeHandler().getParent(child); |
| assertEquals("Wrong parent node", "table", parent.getNodeName()); |
| assertEquals("Wrong node path", child, |
| NodeStructureHelper.nodeForKey(model, childKey)); |
| } |
| |
| /** |
| * Helper method for testing trackChildNodeWithCreation() if invalid query |
| * results are generated. |
| * |
| * @param queryResult the result set of the key |
| */ |
| private void checkTrackChildNodeWithCreationInvalidKey( |
| final List<ImmutableNode> queryResult) |
| { |
| final NodeKeyResolver<ImmutableNode> resolver = createResolver(false); |
| EasyMock.expect( |
| resolver.resolveNodeKey(model.getRootNode(), TEST_KEY, |
| model.getNodeHandler())).andReturn(queryResult); |
| EasyMock.replay(resolver); |
| model.trackChildNodeWithCreation(TEST_KEY, "someChild", resolver); |
| } |
| |
| /** |
| * Tests trackChildNodeWithCreation() if the passed in key does not select a |
| * node. |
| */ |
| @Test(expected = ConfigurationRuntimeException.class) |
| public void testTrackChildNodeWithCreationNoResults() |
| { |
| checkTrackChildNodeWithCreationInvalidKey(new ArrayList<>()); |
| } |
| |
| /** |
| * Tests trackChildNodeWithCreation() if the passed in key selects multiple |
| * nodes. |
| */ |
| @Test(expected = ConfigurationRuntimeException.class) |
| public void testTrackChildNodeWithCreationMultipleResults() |
| { |
| final List<ImmutableNode> nodes = |
| Arrays.asList( |
| NodeStructureHelper.nodeForKey(root, "tables/table(0)"), |
| NodeStructureHelper.nodeForKey(root, "tables/table(1)")); |
| checkTrackChildNodeWithCreationInvalidKey(nodes); |
| } |
| } |