blob: 59ef21794d60a871f8f8d73adaf2c9c7fdcd7530 [file] [log] [blame]
/*
* 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.apache.commons.configuration2.tree.NodeStructureHelper.ROOT_AUTHORS_TREE;
import static org.apache.commons.configuration2.tree.NodeStructureHelper.ROOT_PERSONAE_TREE;
import static org.apache.commons.configuration2.tree.NodeStructureHelper.nodeForKey;
import static org.apache.commons.configuration2.tree.NodeStructureHelper.nodePathWithEndNode;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
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.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.easymock.EasyMock;
import org.junit.Test;
/**
* Test class for {@code InMemoryNodeModel}.
*
*/
public class TestInMemoryNodeModel
{
/** Constant for a test key. */
private static final String KEY = "aTestKey";
/**
* Tests whether an undefined default root node is created if none is
* specified.
*/
@Test
public void testInitDefaultRoot()
{
final InMemoryNodeModel model = new InMemoryNodeModel();
final ImmutableNode root = model.getRootNode();
assertNull("Got a name", root.getNodeName());
assertNull("Got a value", root.getValue());
assertTrue("Got children", root.getChildren().isEmpty());
assertTrue("Got attributes", root.getAttributes().isEmpty());
}
/**
* Tests whether the correct root node is returned if a tree was passed at
* construction time.
*/
@Test
public void testGetRootNodeFromConstructor()
{
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
assertSame("Wrong root node", ROOT_AUTHORS_TREE, model.getRootNode());
}
/**
* Tests whether the correct node handler is returned.
*/
@Test
public void testGetNodeHandler()
{
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_PERSONAE_TREE);
assertSame("Wrong node handler", model.getTreeData(), model.getNodeHandler());
}
/**
* Creates a mock for a {@code NodeKeyResolver}.
*
* @return the mock for the resolver
*/
private static NodeKeyResolver<ImmutableNode> createResolver()
{
@SuppressWarnings("unchecked")
final
NodeKeyResolver<ImmutableNode> resolver =
EasyMock.createMock(NodeKeyResolver.class);
return resolver;
}
/**
* Tests whether a property can be added to the node model if there are some
* additional path nodes to be created.
*/
@Test
public void testAddPropertyWithPathNodes()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final NodeAddData<ImmutableNode> addData =
new NodeAddData<>(nodeForKey(ROOT_AUTHORS_TREE,
"Homer/Ilias"), "location", false,
Collections.singleton("locations"));
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
EasyMock.expect(resolver.resolveAddKey(ROOT_AUTHORS_TREE, KEY, model.getNodeHandler()))
.andReturn(addData);
EasyMock.replay(resolver);
final String[] locations = {
"Troja", "Beach", "Olympos"
};
model.addProperty(KEY, Arrays.asList(locations), resolver);
final ImmutableNode nodeLocs = nodeForKey(model, "Homer/Ilias/locations");
assertEquals("Wrong number of children", locations.length, nodeLocs
.getChildren().size());
int idx = 0;
for (final ImmutableNode c : nodeLocs.getChildren())
{
assertEquals("Wrong node name", "location", c.getNodeName());
assertEquals("Wrong value", locations[idx], c.getValue());
assertTrue("Got children", c.getChildren().isEmpty());
assertTrue("Got attributes", c.getAttributes().isEmpty());
idx++;
}
assertNotNull("Could not find other nodes",
nodeForKey(model, "Homer/Ilias/Hektor"));
}
/**
* Tests whether a property can be added if there are no intermediate path
* nodes.
*/
@Test
public void testAddPropertyNoPathNodes()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final NodeAddData<ImmutableNode> addData =
new NodeAddData<>(nodeForKey(ROOT_AUTHORS_TREE,
"Homer"), "work", false, null);
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
EasyMock.expect(
resolver.resolveAddKey(ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(addData);
EasyMock.replay(resolver);
model.addProperty(KEY, Collections.singleton("Odyssee"), resolver);
final ImmutableNode node = nodeForKey(model, "Homer/work");
assertEquals("Wrong node value", "Odyssee", node.getValue());
assertNotNull("Could not find other nodes",
nodeForKey(model, "Homer/Ilias/Hektor"));
}
/**
* Tests whether the parent node references are updated when nodes are
* added.
*/
@Test
public void testAddPropertyUpdateParentReferences()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final NodeAddData<ImmutableNode> addData =
new NodeAddData<>(nodeForKey(ROOT_AUTHORS_TREE,
"Homer/Ilias"), "location", false,
Collections.singleton("locations"));
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
EasyMock.expect(
resolver.resolveAddKey(ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(addData);
EasyMock.replay(resolver);
final String[] locations = {
"Troja", "Beach", "Olympos"
};
model.addProperty(KEY, Arrays.asList(locations), resolver);
final String[] path = {
"Homer", "Ilias", "locations"
};
final ImmutableNode node =
nodeForKey(model, nodePathWithEndNode("location(1)", path));
checkPathToRoot(model, node, path);
}
/**
* Helper method for checking whether the expected nodes are encountered on
* a path from a start node to the root node.
*
* @param model the node model
* @param node the start node in the path
* @param path an array with the expected node names on the path
*/
private static void checkPathToRoot(final InMemoryNodeModel model,
ImmutableNode node, final String... path)
{
final NodeHandler<ImmutableNode> handler = model.getNodeHandler();
for (int i = path.length - 1; i >= 0; i--)
{
node = handler.getParent(node);
assertEquals("Wrong node name", path[i], node.getNodeName());
}
assertSame("Wrong root node", model.getRootNode(),
handler.getParent(node));
}
/**
* Tests whether an attribute can be added if there are some path nodes.
*/
@Test
public void testAddPropertyAttributeWithPathNodes()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final NodeAddData<ImmutableNode> addData =
new NodeAddData<>(nodeForKey(ROOT_AUTHORS_TREE,
"Homer/Ilias"), "number", true, Arrays.asList("scenes",
"scene"));
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
EasyMock.expect(
resolver.resolveAddKey(ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(addData);
EasyMock.replay(resolver);
model.addProperty(KEY, Collections.singleton(1), resolver);
final ImmutableNode node = nodeForKey(model, "Homer/Ilias/scenes/scene");
assertEquals("Attribute not set", 1, node.getAttributes().get("number"));
}
/**
* Tests the special case that an attribute is added with a single path
* node.
*/
@Test
public void testAddPropertyAttributeWithSinglePathNode()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final NodeAddData<ImmutableNode> addData =
new NodeAddData<>(nodeForKey(ROOT_AUTHORS_TREE,
NodeStructureHelper.author(0)), "year", true,
Arrays.asList("dateOfBirth"));
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
EasyMock.expect(
resolver.resolveAddKey(ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(addData);
EasyMock.replay(resolver);
final Integer year = 1564;
model.addProperty(KEY, Collections.singleton(year), resolver);
final ImmutableNode node = nodeForKey(model, "Shakespeare/dateOfBirth");
assertEquals("Attribute not set", year, node.getAttributes()
.get("year"));
}
/**
* Tests whether an attribute property can be added if there are no path
* nodes.
*/
@Test
public void testAddPropertyAttributeNoPathNodes()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final NodeAddData<ImmutableNode> addData =
new NodeAddData<>(nodeForKey(ROOT_AUTHORS_TREE,
"Shakespeare/The Tempest"), "year", true, null);
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
EasyMock.expect(
resolver.resolveAddKey(ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(addData);
EasyMock.replay(resolver);
model.addProperty(KEY, Collections.singleton(1611), resolver);
final ImmutableNode node = nodeForKey(model, "Shakespeare/The Tempest");
assertEquals("Attribute not set", 1611, node.getAttributes()
.get("year"));
}
/**
* Tests an addProperty() operation if no values are provided.
*/
@Test
public void testAddPropertyNoValues()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
EasyMock.replay(resolver);
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
model.addProperty(KEY, Collections.emptySet(), resolver);
assertSame("Root node was changed", ROOT_AUTHORS_TREE,
model.getRootNode());
}
/**
* Tests whether a clearTree() operation can be performed if only nodes are
* involved.
*/
@Test
public void testClearTreeNodes()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
final QueryResult<ImmutableNode> result =
QueryResult.createNodeResult(nodeForKey(model,
"Homer/Ilias/Achilles"));
EasyMock.expect(
resolver.resolveKey(ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(
Collections.singletonList(result));
EasyMock.replay(resolver);
final List<QueryResult<ImmutableNode>> removed = model.clearTree(KEY, resolver);
final ImmutableNode node = nodeForKey(model, "Homer/Ilias");
assertEquals("Wrong number of children", 2, node.getChildren().size());
for (final ImmutableNode c : node.getChildren())
{
assertNotEquals("Node still found", result.getNode().getNodeName(),
c.getNodeName());
}
assertEquals("Wrong number of removed elements", 1, removed.size());
assertTrue("Wrong removed element", removed.contains(result));
}
/**
* Helper method for testing whether nodes removed from the model can no
* longer be looked up in the parent mapping.
*
* @param pathToRemove the path to the node to be removed
* @param nodeToCheck the node to check in the parent mapping
*/
private void checkClearTreeUpdatedParentMapping(final String pathToRemove,
final ImmutableNode nodeToCheck)
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
final QueryResult<ImmutableNode> result =
QueryResult.createNodeResult(nodeForKey(model, pathToRemove));
EasyMock.expect(
resolver.resolveKey(ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(
Collections.singletonList(result));
EasyMock.replay(resolver);
model.clearTree(KEY, resolver);
try
{
model.getNodeHandler().getParent(nodeToCheck);
fail("Removed node still in parent mapping!");
}
catch (final IllegalArgumentException iaex)
{
// expected result
}
}
/**
* Tests whether a removed node can no longer be passed to getParent().
*/
@Test
public void testClearTreeNodeRemovedFromParentMapping()
{
final String path = "Homer/Ilias/Achilles";
checkClearTreeUpdatedParentMapping(path,
nodeForKey(ROOT_AUTHORS_TREE, path));
}
/**
* Tests whether the children of removed nodes are also removed from the
* parent mapping.
*/
@Test
public void testClearTreeChildrenRemovedFromParentMapping()
{
final String path = "Homer/Ilias";
checkClearTreeUpdatedParentMapping(path,
nodeForKey(ROOT_AUTHORS_TREE, path + "/Achilles"));
}
/**
* Tests whether references to parent nodes are updated correctly when
* clearing properties.
*/
@Test
public void testClearTreeUpdateParentReferences()
{
final String[] path = {
"Homer", "Ilias"
};
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
final QueryResult<ImmutableNode> result =
QueryResult.createNodeResult(nodeForKey(model,
nodePathWithEndNode("Achilles", path)));
EasyMock.expect(
resolver.resolveKey(ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(
Collections.singletonList(result));
EasyMock.replay(resolver);
model.clearTree(KEY, resolver);
checkPathToRoot(model,
nodeForKey(model, nodePathWithEndNode("Hektor", path)), path);
}
/**
* Tests whether undefined nodes are removed from the hierarchy when
* clearing properties.
*/
@Test
public void testClearTreeRemoveUndefinedNodes()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
final ImmutableNode node = nodeForKey(model, "Homer/Ilias");
final List<QueryResult<ImmutableNode>> results =
new ArrayList<>(node.getChildren()
.size());
for (final ImmutableNode child : node.getChildren())
{
results.add(QueryResult.createNodeResult(child));
}
EasyMock.expect(
resolver.resolveKey(ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(results);
EasyMock.replay(resolver);
model.clearTree(KEY, resolver);
assertEquals("Child of root not removed",
NodeStructureHelper.authorsLength() - 1, model.getRootNode()
.getChildren().size());
for (final ImmutableNode child : model.getRootNode().getChildren())
{
assertNotEquals("Child still found", "Homer", child.getNodeName());
}
}
/**
* Tests a clearTree() operation which should yield an empty tree structure.
*/
@Test
public void testClearTreeResultIsEmpty()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final ImmutableNode child =
new ImmutableNode.Builder().name("child").value("test")
.create();
final ImmutableNode root =
new ImmutableNode.Builder(1).addChild(child).create();
final InMemoryNodeModel model = new InMemoryNodeModel(root);
EasyMock.expect(resolver.resolveKey(root, KEY, model.getNodeHandler()))
.andReturn(
Collections.singletonList(QueryResult
.createNodeResult(child)));
EasyMock.replay(resolver);
model.clearTree(KEY, resolver);
assertFalse("Root node still defined",
model.getNodeHandler().isDefined(model.getRootNode()));
}
/**
* Tests whether attributes can be cleared with clearTree().
*/
@Test
public void testClearTreeAttribute()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_PERSONAE_TREE);
final String nodeName = "Puck";
final QueryResult<ImmutableNode> result = QueryResult.createAttributeResult(
nodeForKey(model, nodeName),
NodeStructureHelper.ATTR_AUTHOR);
EasyMock.expect(
resolver.resolveKey(ROOT_PERSONAE_TREE, KEY,
model.getNodeHandler())).andReturn(
Collections.singletonList(result));
EasyMock.replay(resolver);
final List<QueryResult<ImmutableNode>> removed = model.clearTree(KEY, resolver);
final ImmutableNode node = nodeForKey(model, nodeName);
assertTrue("Got still attributes", node.getAttributes().isEmpty());
assertEquals("Wrong number of removed elements", 1, removed.size());
assertTrue("Wrong removed element", removed.contains(result));
}
/**
* Tests whether both nodes and attributes can be removed by a clearTree()
* operation. We remove all attributes and children from a node. The node
* becomes undefined and should be removed.
*/
@Test
public void testClearTreeNodesAndAttributes()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_PERSONAE_TREE);
final String nodeName = "Puck";
final ImmutableNode orgNode = nodeForKey(model, nodeName);
final List<QueryResult<ImmutableNode>> results =
new ArrayList<>(2);
results.add(QueryResult.createAttributeResult(orgNode,
NodeStructureHelper.ATTR_AUTHOR));
results.add(QueryResult.createNodeResult(orgNode.getChildren().get(0)));
EasyMock.expect(
resolver.resolveKey(ROOT_PERSONAE_TREE, KEY,
model.getNodeHandler())).andReturn(results);
EasyMock.replay(resolver);
model.clearTree(KEY, resolver);
try
{
nodeForKey(model, nodeName);
fail("Node still present!");
}
catch (final NoSuchElementException nex)
{
// expected
}
}
/**
* Tests clearTree() if the passed in key does not exist.
*/
@Test
public void testClearTreeNonExistingKey()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_PERSONAE_TREE);
EasyMock.expect(
resolver.resolveKey(ROOT_PERSONAE_TREE, KEY,
model.getNodeHandler())).andReturn(
Collections.<QueryResult<ImmutableNode>> emptyList());
EasyMock.replay(resolver);
final TreeData treeDataOld = model.getTreeData();
assertTrue("Elements removed", model.clearTree(KEY, resolver).isEmpty());
assertNotNull("No root node", model.getNodeHandler().getRootNode());
assertSame("Data was changed", treeDataOld, model.getTreeData());
}
/**
* Tests whether the whole node structure can be cleared.
*/
@Test
public void testClear()
{
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
model.clear(createResolver());
assertFalse("Got still data",
model.getNodeHandler().isDefined(model.getRootNode()));
assertEquals("Root name was changed", ROOT_AUTHORS_TREE.getNodeName(),
model.getRootNode().getNodeName());
}
/**
* Tests whether clearTree() handles the root node in a special way.
*/
@Test
public void testClearTreeRootNode()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
final List<QueryResult<ImmutableNode>> results =
new ArrayList<>(2);
results.add(QueryResult.createNodeResult(nodeForKey(model,
NodeStructureHelper.author(0))));
results.add(QueryResult.createNodeResult(ROOT_AUTHORS_TREE));
EasyMock.expect(
resolver.resolveKey(ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(results);
EasyMock.replay(resolver);
model.clearTree(KEY, resolver);
assertFalse("Got still data",
model.getNodeHandler().isDefined(model.getRootNode()));
}
/**
* Tests whether the replacement mapping is automatically compacted if it
* gets too large.
*/
@Test
public void testCompactReplacementMapping()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
final int numberOfOperations = 200;
final String key = "Homer/Ilias";
for (int i = 0; i < numberOfOperations; i++)
{
final int index = i;
EasyMock.expect(
resolver.resolveAddKey(
EasyMock.anyObject(ImmutableNode.class),
EasyMock.eq(KEY),
EasyMock.anyObject(TreeData.class))).andAnswer(
() -> {
assertSame("Wrong root node", model.getRootNode(),
EasyMock.getCurrentArguments()[0]);
final ImmutableNode addParent = nodeForKey(model, key);
return new NodeAddData<>(addParent,
"Warrior" + index, false, null);
});
}
EasyMock.replay(resolver);
for (int i = 0; i < numberOfOperations; i++)
{
model.addProperty(KEY, Collections.singleton(i), resolver);
}
final ImmutableNode orgNode = nodeForKey(ROOT_AUTHORS_TREE, key);
final ImmutableNode changedNode = nodeForKey(model, key);
assertEquals("Wrong number of children", orgNode.getChildren().size()
+ numberOfOperations, changedNode.getChildren().size());
final Map<ImmutableNode, ImmutableNode> replacementMapping =
model.getTreeData().copyReplacementMapping();
assertTrue("Replacement mapping too big: " + replacementMapping.size(),
replacementMapping.size() < numberOfOperations);
}
/**
* Tests whether concurrent updates of the model are handled correctly. This
* test adds a number of authors in parallel. Then it is checked whether all
* authors have been added correctly.
*/
@Test
public void testConcurrentUpdate() throws InterruptedException
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_AUTHORS_TREE);
EasyMock.expect(
resolver.resolveAddKey(EasyMock.anyObject(ImmutableNode.class),
EasyMock.eq(KEY), EasyMock.anyObject(TreeData.class)))
.andAnswer(() -> {
final ImmutableNode addParent =
(ImmutableNode) EasyMock.getCurrentArguments()[0];
return new NodeAddData<>(addParent,
"name", false, Collections.singleton("author"));
}).anyTimes();
EasyMock.replay(resolver);
final CountDownLatch latch = new CountDownLatch(1);
final String authorPrefix = "newAuthor";
final int threadCount = 32;
final Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++)
{
final String authorName = authorPrefix + i;
threads[i] = new Thread()
{
@Override
public void run()
{
try
{
latch.await();
model.addProperty(KEY,
Collections.singleton(authorName), resolver);
}
catch (final InterruptedException iex)
{
// ignore
}
}
};
threads[i].start();
}
latch.countDown();
for (final Thread t : threads)
{
t.join();
}
final Pattern patternAuthorName =
Pattern.compile(Pattern.quote(authorPrefix) + "(\\d+)");
final Set<Integer> indices = new HashSet<>();
for (int i = 0; i < threadCount; i++)
{
final ImmutableNode node = nodeForKey(model, "author(" + i + ")/name");
final Matcher m =
patternAuthorName.matcher(String.valueOf(node.getValue()));
assertTrue("Wrong value: " + node.getValue(), m.matches());
final int idx = Integer.parseInt(m.group(1));
assertTrue("Invalid index: " + idx, idx >= 0 && idx < threadCount);
indices.add(idx);
}
assertEquals("Not all authors were created", threadCount,
indices.size());
}
/**
* Tests whether a property value can be cleared on a node.
*/
@Test
public void testClearPropertyNode()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_PERSONAE_TREE);
final String nodeKey =
"Ariel/The Tempest/" + NodeStructureHelper.ELEM_ORG_VALUE;
EasyMock.expect(
resolver.resolveKey(model.getRootNode(), KEY,
model.getNodeHandler())).andReturn(
Collections.singletonList(QueryResult
.createNodeResult(nodeForKey(model, nodeKey))));
EasyMock.replay(resolver);
model.clearProperty(KEY, resolver);
final ImmutableNode node = nodeForKey(model, nodeKey);
assertNull("Value not cleared", node.getValue());
}
/**
* Tests whether a property value stored as an attribute can be cleared.
*/
@Test
public void testClearPropertyAttribute()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_PERSONAE_TREE);
final String nodeKey =
"Prospero/The Tempest/" + NodeStructureHelper.ELEM_ORG_VALUE;
EasyMock.expect(
resolver.resolveKey(model.getRootNode(), KEY,
model.getNodeHandler())).andReturn(
Collections.singletonList(QueryResult.createAttributeResult(
nodeForKey(model, nodeKey),
NodeStructureHelper.ATTR_TESTED)));
EasyMock.replay(resolver);
model.clearProperty(KEY, resolver);
final ImmutableNode node = nodeForKey(model, nodeKey);
assertTrue("Attribute not removed", node.getAttributes().isEmpty());
}
/**
* Tests clearProperty() for a non existing property.
*/
@Test
public void testClearPropertyNonExisting()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_PERSONAE_TREE);
EasyMock.expect(
resolver.resolveKey(model.getRootNode(), KEY,
model.getNodeHandler())).andReturn(
Collections.<QueryResult<ImmutableNode>> emptyList());
EasyMock.replay(resolver);
final TreeData treeDataOld = model.getTreeData();
model.clearProperty(KEY, resolver);
assertNotNull("No root node", model.getNodeHandler().getRootNode());
assertSame("Data was changed", treeDataOld, model.getTreeData());
}
/**
* Tests whether setProperty() can handle newly added values.
*/
@Test
public void testSetPropertyNewValues()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final NodeAddData<ImmutableNode> addData =
new NodeAddData<>(nodeForKey(ROOT_AUTHORS_TREE,
"Homer"), "work", false, null);
final NodeUpdateData<ImmutableNode> updateData =
new NodeUpdateData<>(null,
Collections.<Object> singleton("Odyssee"), null, KEY);
final InMemoryNodeModel model = new InMemoryNodeModel(ROOT_AUTHORS_TREE);
EasyMock.expect(
resolver.resolveUpdateKey(ROOT_AUTHORS_TREE, KEY, this,
model.getNodeHandler())).andReturn(updateData);
EasyMock.expect(
resolver.resolveAddKey(ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(addData);
EasyMock.replay(resolver);
model.setProperty(KEY, this, resolver);
final ImmutableNode node = nodeForKey(model, "Homer/work");
assertEquals("Wrong node value", "Odyssee", node.getValue());
assertNotNull("Could not find other nodes",
nodeForKey(model, "Homer/Ilias/Hektor"));
}
/**
* Tests whether setProperty() can handle nodes to be cleared.
*/
@Test
public void testSetPropertyClearValues()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_PERSONAE_TREE);
final String nodeKey =
"Ariel/The Tempest/" + NodeStructureHelper.ELEM_ORG_VALUE;
final NodeUpdateData<ImmutableNode> updateData =
new NodeUpdateData<>(null, null,
Collections.singletonList(QueryResult
.createNodeResult(nodeForKey(model, nodeKey))),
null);
EasyMock.expect(
resolver.resolveUpdateKey(
NodeStructureHelper.ROOT_PERSONAE_TREE, KEY, this,
model.getNodeHandler())).andReturn(updateData);
EasyMock.replay(resolver);
model.setProperty(KEY, this, resolver);
final ImmutableNode node = nodeForKey(model, nodeKey);
assertNull("Value not cleared", node.getValue());
}
/**
* Tests whether setProperty() can handle changes in node values.
*/
@Test
public void testSetPropertyChangedValues()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_PERSONAE_TREE);
final String nodeKey =
"Ariel/The Tempest/" + NodeStructureHelper.ELEM_ORG_VALUE;
final Map<QueryResult<ImmutableNode>, Object> changedValues =
new HashMap<>();
final String newValue = "of course";
final ImmutableNode changedNode = nodeForKey(model, nodeKey);
changedValues.put(QueryResult.createAttributeResult(changedNode,
NodeStructureHelper.ATTR_TESTED), newValue);
changedValues.put(QueryResult.createNodeResult(changedNode), newValue);
final NodeUpdateData<ImmutableNode> updateData =
new NodeUpdateData<>(changedValues, null, null,
null);
EasyMock.expect(
resolver.resolveUpdateKey(
NodeStructureHelper.ROOT_PERSONAE_TREE, KEY, this,
model.getNodeHandler())).andReturn(updateData);
EasyMock.replay(resolver);
model.setProperty(KEY, this, resolver);
final ImmutableNode node = nodeForKey(model, nodeKey);
assertEquals("Attribute value not changed", newValue, node
.getAttributes().get(NodeStructureHelper.ATTR_TESTED));
assertEquals("Node value not changed", newValue, node.getValue());
}
/**
* Tests a set property operation which is a no-op.
*/
@Test
public void testSetPropertyNoChanges()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_PERSONAE_TREE);
EasyMock.expect(
resolver.resolveUpdateKey(
NodeStructureHelper.ROOT_PERSONAE_TREE, KEY, this,
model.getNodeHandler())).andReturn(
new NodeUpdateData<ImmutableNode>(null, null, null, null));
EasyMock.replay(resolver);
model.setProperty(KEY, this, resolver);
assertSame("Model was changed", NodeStructureHelper.ROOT_PERSONAE_TREE,
model.getRootNode());
}
/**
* Tests whether new nodes can be added to an existing node in the model.
*/
@Test
public void testAddNodesToExistingNode()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_AUTHORS_TREE);
final String key = NodeStructureHelper.author(0);
final ImmutableNode newWork1 =
new ImmutableNode.Builder().name("King Lear").create();
final ImmutableNode newWork2 =
new ImmutableNode.Builder().name("The Taming of the Shrew")
.create();
EasyMock.expect(
resolver.resolveKey(NodeStructureHelper.ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(
Collections.singletonList(QueryResult
.createNodeResult(nodeForKey(model, key))));
EasyMock.replay(resolver);
model.addNodes(KEY, Arrays.asList(newWork1, newWork2), resolver);
final ImmutableNode node = nodeForKey(model, key);
final int size = node.getChildren().size();
assertSame("New child 1 not added", newWork1,
node.getChildren().get(size - 2));
assertSame("New child 2 not added", newWork2,
node.getChildren().get(size - 1));
}
/**
* Tests whether nodes can be added to a node which has to be created.
*/
@Test
public void testAddNodesToNewNode()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_AUTHORS_TREE);
final String newAuthor = "Goethe";
final String newWork = "Faust";
final String newPersona = "Mephisto";
EasyMock.expect(
resolver.resolveKey(NodeStructureHelper.ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(
new ArrayList<QueryResult<ImmutableNode>>(0));
EasyMock.expect(
resolver.resolveAddKey(NodeStructureHelper.ROOT_AUTHORS_TREE,
KEY, model.getNodeHandler())).andReturn(
new NodeAddData<>(
NodeStructureHelper.ROOT_AUTHORS_TREE, newWork, false,
Arrays.asList(newAuthor)));
EasyMock.replay(resolver);
final ImmutableNode personaNode =
new ImmutableNode.Builder().name(newPersona).create();
model.addNodes(KEY, Collections.singleton(personaNode), resolver);
assertSame("Wrong added node", personaNode,
nodeForKey(model, newAuthor + "/" + newWork + "/" + newPersona));
}
/**
* Tries to add new nodes if the key references an attribute.
*/
@Test(expected = IllegalArgumentException.class)
public void testAddNodesToAttribute()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_AUTHORS_TREE);
EasyMock.expect(
resolver.resolveKey(NodeStructureHelper.ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(
Collections.singletonList(QueryResult.createAttributeResult(
nodeForKey(model, NodeStructureHelper.author(1)),
"test")));
EasyMock.replay(resolver);
final ImmutableNode newNode =
new ImmutableNode.Builder().name("newNode").create();
model.addNodes(KEY, Collections.singleton(newNode), resolver);
}
/**
* Tries to add new nodes to an non-existing key pointing to an attribute.
*/
@Test(expected = IllegalArgumentException.class)
public void testAddNodesToNewAttributeKey()
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_AUTHORS_TREE);
EasyMock.expect(
resolver.resolveKey(NodeStructureHelper.ROOT_AUTHORS_TREE, KEY,
model.getNodeHandler())).andReturn(
Collections.<QueryResult<ImmutableNode>> emptyList());
EasyMock.expect(
resolver.resolveAddKey(NodeStructureHelper.ROOT_AUTHORS_TREE,
KEY, model.getNodeHandler())).andReturn(
new NodeAddData<>(
NodeStructureHelper.ROOT_AUTHORS_TREE, "test", true,
null));
EasyMock.replay(resolver);
final ImmutableNode newNode =
new ImmutableNode.Builder().name("newNode").create();
model.addNodes(KEY, Collections.singleton(newNode), resolver);
}
/**
* Helper method for testing the behavior of addNodes() if no nodes to be
* added are provided.
*
* @param newNodes the collection with new nodes
*/
private void checkAddNodesNoNodes(final Collection<ImmutableNode> newNodes)
{
final NodeKeyResolver<ImmutableNode> resolver = createResolver();
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_AUTHORS_TREE);
EasyMock.replay(resolver);
model.addNodes(KEY, newNodes, resolver);
assertSame("Model was changed", NodeStructureHelper.ROOT_AUTHORS_TREE,
model.getRootNode());
}
/**
* Tests an add nodes operation if a null collection is passed in.
*/
@Test
public void testAddNodesNullCollection()
{
checkAddNodesNoNodes(null);
}
/**
* Tests an add nodes operation if an empty collection is passed in.
*/
@Test
public void testAddNodesEmptyCollection()
{
checkAddNodesNoNodes(Collections.<ImmutableNode> emptySet());
}
/**
* Tests whether a new root node can be set.
*/
@Test
public void testSetRoot()
{
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_PERSONAE_TREE);
model.setRootNode(NodeStructureHelper.ROOT_AUTHORS_TREE);
assertSame("Root node not changed",
NodeStructureHelper.ROOT_AUTHORS_TREE, model.getRootNode());
final ImmutableNode node = nodeForKey(model, "Homer/Ilias");
assertEquals("Wrong parent mapping", nodeForKey(model, "Homer"),
model.getNodeHandler().getParent(node));
}
/**
* Tests whether the root node can be set to null.
*/
@Test
public void testSetRootNull()
{
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_PERSONAE_TREE);
model.setRootNode(null);
final ImmutableNode rootNode = model.getRootNode();
assertTrue("Got children", rootNode.getChildren().isEmpty());
}
/**
* Tests whether the model's data can be represented as immutable node
* objects (which is trivial in this case).
*/
@Test
public void testGetInMemoryRepresentation()
{
final InMemoryNodeModel model =
new InMemoryNodeModel(NodeStructureHelper.ROOT_AUTHORS_TREE);
assertSame("Wrong in-memory representation",
NodeStructureHelper.ROOT_AUTHORS_TREE,
model.getInMemoryRepresentation());
}
}