| /******************************************************************************* |
| * Copyright (c) 2006, 2013 Oracle and/or its affiliates. All rights reserved. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 |
| * which accompanies this distribution. |
| * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html |
| * and the Eclipse Distribution License is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * Contributors: |
| * Oracle - initial API and implementation |
| * |
| ******************************************************************************/ |
| package org.eclipse.persistence.tools.mapping.tests; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Random; |
| import org.eclipse.persistence.tools.mapping.ExternalForm; |
| import org.eclipse.persistence.tools.utility.ClassTools; |
| import org.junit.Test; |
| import org.w3c.dom.Node; |
| import static org.junit.Assert.*; |
| |
| /** |
| * This test defines the behavior to test the manipulation of an XML document through an SPI. |
| * |
| * @version 2.6 |
| */ |
| @SuppressWarnings("nls") |
| public abstract class AbstractExternalFormTests<T extends ExternalForm> { |
| |
| /** |
| * Populates the given tester with the appropriate {@link NodeTester}. |
| * |
| * @param tester The tester of the node to test |
| */ |
| protected abstract void populate(RootNodeTester<T> tester); |
| |
| /** |
| * Performs the actual tests on the document for a particular node that is being manipulated by |
| * an {@link ExternalForm}. |
| */ |
| @Test |
| public final void test() throws Exception { |
| DefaultRootNodeTester tester = new DefaultRootNodeTester(); |
| populate(tester); |
| tester.test(); |
| } |
| |
| /** |
| * An <code>AttributeNodeTester</code> tests setting and retrieving the value associated with |
| * an element's attribute. |
| */ |
| public interface AttributeNodeTester<T, VALUE> extends PropertyTester<T, VALUE> { |
| } |
| |
| /** |
| * An <code>AttributeNodeTesterRunner</code> tests setting and retrieving the value associated |
| * with an element's attribute. |
| * <p> |
| * <div nowrap>Form: <code><b><nodeName attributeName="value"/></b></div> |
| */ |
| private class AttributeNodeTesterRunner extends PropertyNodeTesterRunner { |
| |
| /** |
| * Creates a new <code>AttributeNodeTesterRunner</code>. |
| * |
| * @param tester This object defines a single node to test an attribute node |
| */ |
| AttributeNodeTesterRunner(AttributeNodeTester<T, ?> tester) { |
| super(tester); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| String displayString() { |
| return "<" + parentNodeName + " " + getNodeName() + "=\"\">"; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| int getChildrenCount(Node parentNode) { |
| return parentNode.getAttributes().getLength(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| Node getNode(Node parentNode) { |
| return parentNode.getAttributes().getNamedItem(getNodeName()); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| String getNodeValue(Node node) { |
| return node.getTextContent(); |
| } |
| } |
| |
| private class ChildListNodeRunnerAddingController extends Controller { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void log() { |
| System.out.println("<" + runner.parentNodeName + "><" + runner.tester.getNodeName() + "*> : addition"); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| void test(T form, Node node) { |
| |
| ChildListNodeTesterRunner runner = (ChildListNodeTesterRunner) this.runner; |
| |
| // Create a random range within the list of values used to create the child nodes |
| Random random = new Random(); |
| int count = runner.getDefaultChildrenCount(); |
| |
| int startIndex = random.nextInt(count); |
| int endIndex = random.nextInt(count); |
| |
| // Make sure the start and end indices are not the same |
| while (endIndex == startIndex) { |
| endIndex = random.nextInt(count); |
| } |
| |
| runner.currentChildrenCount = currentChildrenCount; |
| runner.addRange(startIndex, endIndex); |
| runner.rangeIndex = runner.ranges.size() - 1; |
| runner.testInitialState(form, node); |
| runner.testAdding(form, node); |
| runner.testReading(form, node); |
| |
| currentChildrenCount = runner.currentChildrenCount; |
| } |
| } |
| |
| private class ChildListNodeRunnerReadingController extends Controller { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void log() { |
| System.out.println("<" + runner.parentNodeName + "><" + runner.tester.getNodeName() + "*> : read"); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| void test(T form, Node node) { |
| |
| ChildListNodeTesterRunner runner = (ChildListNodeTesterRunner) this.runner; |
| int rangesCount = runner.ranges.size(); |
| |
| // Nothing to read |
| if (rangesCount == 0) { |
| return; |
| } |
| |
| // Read a range randomly |
| Random random = new Random(); |
| int rangeIndex = random.nextInt(rangesCount); |
| runner.rangeIndex = rangeIndex; |
| |
| runner.currentChildrenCount = currentChildrenCount; |
| runner.testInitialState(form, node); |
| runner.testReading(form, node); |
| |
| currentChildrenCount = runner.currentChildrenCount; |
| } |
| } |
| |
| private class ChildListNodeRunnerRemovingController extends Controller { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void log() { |
| System.out.println("<" + runner.parentNodeName + "><" + runner.tester.getNodeName() + "*> : removal"); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| void test(T form, Node node) { |
| |
| ChildListNodeTesterRunner runner = (ChildListNodeTesterRunner) this.runner; |
| int rangesCount = runner.ranges.size(); |
| |
| // Nothing to read |
| if (rangesCount == 0) { |
| return; |
| } |
| |
| // Read a range randomly |
| Random random = new Random(); |
| int rangeIndex = random.nextInt(rangesCount); |
| runner.rangeIndex = rangeIndex; |
| |
| runner.currentChildrenCount = currentChildrenCount; |
| runner.testInitialState(form, node); |
| runner.testRemoving(form, node); |
| |
| currentChildrenCount = runner.currentChildrenCount; |
| } |
| } |
| |
| /** |
| * This <code>ChildListNodeTester</code> tests a form when the node it represents can have zero |
| * or many child nodes of the same type (i.e. with the same node name). |
| */ |
| public interface ChildListNodeTester<T, VALUE, CHILD_VALUE> extends NodeTester<T, VALUE> { |
| |
| /** |
| * Adds a new child node to the node represented by the given form. |
| * |
| * @param form The external form being tested |
| * @param value The value used when creating the child node |
| * @return The external form representing the child node that was added to the node represented |
| * by the given form |
| */ |
| VALUE addChild(T form, CHILD_VALUE value); |
| |
| /** |
| * Returns the child node from the node represented by the given form at the given position. |
| * |
| * @param form The external form being tested |
| * @param index The position of the child node to retrieve |
| * @return The external form representing the child node at the given position |
| */ |
| VALUE getChild(T form, int index); |
| |
| /** |
| * Returns a list of the children node with the same type (i.e. with the same node name) that |
| * are children of the node represented by the given form. |
| * |
| * @param form The external form being tested |
| * @return An ordered list based on the sequence of children node |
| */ |
| List<VALUE> getChildren(T form); |
| |
| /** |
| * Returns the number of children of the node represented by the given form with the same type |
| * (i.e. with the same node name). |
| * |
| * @param form The external form being tested |
| * @return The count of children of the same type |
| */ |
| int getChildrenSize(T form); |
| |
| /** |
| * Returns the value that part of the list ({@link #getChildValues()}) and that was used to |
| * create a child form. |
| * |
| * @param childForm The child form from which the value used to create it should be returned |
| * @return The value retrieved from the child node |
| */ |
| CHILD_VALUE getChildValue(VALUE childForm); |
| |
| /** |
| * Returns a list of values that will be used to create children of the node represented by |
| * the form being tested. Each value will be used when calling {@link #addChild(Object, Object)}. |
| * |
| * @return A list of values, which should have more than 1 item |
| */ |
| List<CHILD_VALUE> getChildValues(); |
| |
| /** |
| * Removes the child node from the node represented by the given form. |
| * |
| * @param form The external form being tested |
| * @param index The position of the child node within the list of children of the same type |
| * (i.e. with the same node name) |
| */ |
| void removeChild(T form, int index); |
| } |
| |
| /** |
| * A <code>ChildListNodeTesterRunner</code> TODO. |
| */ |
| private class ChildListNodeTesterRunner extends NodeTesterRunner { |
| |
| int currentChildListCount; |
| int rangeIndex; |
| List<Integer[]> ranges; |
| |
| /** |
| * Creates a new <code>ChildListNodeTesterRunner</code>. |
| * |
| * @param tester This object defines a single child node to test |
| */ |
| ChildListNodeTesterRunner(ChildListNodeTester<T, ?, ?> tester) { |
| super(tester); |
| initialize(tester); |
| } |
| |
| void addRange(int startIndex, int endIndex) { |
| ranges.add(new Integer[] { Math.min(startIndex, endIndex), Math.max(startIndex, endIndex) }); |
| } |
| |
| private String displayString() { |
| return "<" + getNodeName() + ">"; |
| } |
| |
| private List<Node> getChildrenNodes(Node node) { |
| |
| node = node.getFirstChild(); |
| List<Node> children = new ArrayList<Node>(); |
| |
| while (node != null) { |
| |
| if (getNodeName().equals(node.getNodeName())) { |
| children.add(node); |
| } |
| |
| node = node.getNextSibling(); |
| } |
| |
| return children; |
| } |
| |
| @SuppressWarnings("unchecked") |
| int getDefaultChildrenCount() { |
| ChildListNodeTester<T, Object, Object> tester = (ChildListNodeTester<T, Object, Object>) this.tester; |
| return tester.getChildValues().size(); |
| } |
| |
| private void initialize(ChildListNodeTester<T, ?, ?> tester) { |
| |
| ranges = new ArrayList<Integer[]>(); |
| |
| List<?> childValues = tester.getChildValues(); |
| |
| if (!childValues.isEmpty()) { |
| assertTrue( |
| "The list of child values should contain at least 10 items for proper testing", |
| childValues.size() >= 10 |
| ); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| boolean isMultipleSupported() { |
| return true; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void test(T form, Node node) { |
| rangeIndex = ranges.size(); |
| ranges.add(new Integer[] { 0, getDefaultChildrenCount() }); |
| super.test(form, node); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| void testAdding(T form, Node parent) { |
| |
| ChildListNodeTester<T, Object, Object> tester = (ChildListNodeTester<T, Object, Object>) this.tester; |
| |
| // Retrieve the values that will be used to create the child nodes |
| List<Object> values = tester.getChildValues(); |
| Integer[] range = ranges.get(rangeIndex); |
| |
| // Create each child node |
| for (int index = range[0]; index < range[1]; index++) { |
| |
| Object value = values.get(index); |
| assertNotNull("The child value cannot be null", value); |
| |
| // Create the child node |
| Object childForm = tester.addChild(form, value); |
| currentChildrenCount++; |
| currentChildListCount++; |
| |
| assertNotNull( |
| displayString() + " : The child form should have been created", |
| childForm |
| ); |
| |
| // Retrieve the list of child nodes and make sure it matches |
| // the number of child nodes that have been created so far |
| List<Node> childrenNode = getChildrenNodes(parent); |
| |
| assertEquals( |
| displayString() + " : The child node was not created correctly", |
| currentChildListCount, |
| childrenNode.size() |
| ); |
| |
| // Make sure nothing else was created |
| assertEquals( |
| "The child node was not created correctly", |
| currentChildrenCount, |
| parent.getChildNodes().getLength() |
| ); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| void testInitialState(T form, Node parent) { |
| |
| ChildListNodeTester<T, Object, Object> tester = (ChildListNodeTester<T, Object, Object>) this.tester; |
| |
| assertEquals( |
| displayString() + " : Incorrect number of children was retrieved", |
| currentChildListCount, |
| tester.getChildren(form).size() |
| ); |
| |
| // Test to make sure the number of child nodes is correctly retrieved |
| assertEquals( |
| displayString() + " : The number of children is inconsistent", |
| currentChildListCount, |
| tester.getChildrenSize(form) |
| ); |
| |
| // Make sure nothing changed |
| assertEquals( |
| displayString() + " : The number of children is inconsistent", |
| currentChildrenCount, |
| parent.getChildNodes().getLength() |
| ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| void testReading(T form, Node node) { |
| |
| ChildListNodeTester<T, Object, Object> tester = (ChildListNodeTester<T, Object, Object>) this.tester; |
| List<Object> values = tester.getChildValues(); |
| List<Object> childForms = tester.getChildren(form); |
| |
| assertEquals( |
| displayString() + " : The number of children is inconsistent", |
| currentChildListCount, |
| childForms.size() |
| ); |
| |
| Integer[] range = ranges.get(rangeIndex); |
| |
| // Read each child within the range |
| for (int index = range[0]; index < range[1]; index++) { |
| |
| // Translate the position to the actual position within the list of children |
| int translatedPosition = translatePosition(rangeIndex, index); |
| |
| // Retrieve the child |
| Object childForm = tester.getChild(form, translatedPosition); |
| |
| assertNotNull( |
| displayString() + " : The child form cannot be null", |
| childForm |
| ); |
| |
| // Retrieve the child value from the child form |
| Object childValue = tester.getChildValue(childForm); |
| Object expectedChildValue = values.get(index); |
| |
| assertEquals( |
| displayString() + " : The child value was not retrieved correctly", |
| expectedChildValue, |
| childValue |
| ); |
| |
| // Retrieve the child |
| childForm = childForms.get(translatedPosition); |
| |
| assertNotNull( |
| displayString() + " : The child form cannot be null", |
| childForm |
| ); |
| |
| // Retrieve the child value from the child form |
| childValue = tester.getChildValue(childForm); |
| |
| assertEquals( |
| displayString() + " : The child value was not retrieved correctly", |
| expectedChildValue, |
| childValue |
| ); |
| } |
| |
| // Now test reading each child randomly |
| Random random = new Random(); |
| List<Integer> positions = new ArrayList<Integer>(); |
| |
| for (int index = range[0]; index < range[1]; index++) { |
| positions.add(index); |
| } |
| |
| while (!positions.isEmpty()) { |
| |
| // Get a new random position |
| int position = (positions.size() == 1) ? 0 : random.nextInt(positions.size()); |
| int index = positions.remove(position); |
| |
| // Translate the position to the actual position within the list of children |
| int translatedPosition = translatePosition(rangeIndex, index); |
| |
| // Retrieve the child form |
| Object childForm = tester.getChild(form, translatedPosition); |
| |
| assertNotNull( |
| displayString() + " : The child form cannot be null", |
| childForm |
| ); |
| |
| // Retrieve the child value from the child form |
| Object childValue = tester.getChildValue(childForm); |
| Object expectedChildValue = values.get(index); |
| |
| // Now retrieve the translated position within the list of values |
| assertEquals( |
| displayString() + " : The child value was not retrieved correctly", |
| expectedChildValue, |
| childValue |
| ); |
| |
| // Retrieve the child |
| childForm = childForms.get(translatedPosition); |
| |
| assertNotNull( |
| displayString() + " : The child form cannot be null", |
| childForm |
| ); |
| |
| // Retrieve the child value from the child form |
| childValue = tester.getChildValue(childForm); |
| |
| assertEquals( |
| displayString() + " : The child value was not retrieved correctly", |
| expectedChildValue, |
| childValue |
| ); |
| } |
| } |
| |
| private void testReadingAfterRemoval(ChildListNodeTester<T, Object, Object> tester, |
| T form, |
| List<Object> childForms, |
| List<Object> values, |
| int rangesIndexToSkip, |
| int offset) { |
| |
| // Iterate through all the ranges to make sure every single child is correct |
| for (int rangesIndex = 0; rangesIndex < ranges.size(); rangesIndex++) { |
| |
| // Skip the range that is being used to delete children |
| if (rangesIndex == rangesIndexToSkip) { |
| continue; |
| } |
| |
| Integer[] range = ranges.get(rangesIndex); |
| |
| // Iterate through a single range and check every child is read correctly |
| for (int rangeIndex = range[0]; rangeIndex < range[1]; rangeIndex++) { |
| |
| // Translate the position to the actual position within the list of children |
| // but skip the range that is used to delete children, the offset the number |
| // of children contained by that range |
| int translatedPosition = translatePosition(rangesIndex, rangeIndex, rangesIndexToSkip); |
| |
| // The position is after the index used to delete children, adjust the translated |
| // position by adding the count of remaining children that have not being deleted yet |
| if (rangesIndex > rangesIndexToSkip) { |
| translatedPosition += offset; |
| } |
| |
| // Retrieve the child form |
| Object childForm = tester.getChild(form, translatedPosition); |
| |
| assertNotNull( |
| displayString() + " : The child form cannot be null", |
| childForm |
| ); |
| |
| // Retrieve the child value from the child form |
| Object childValue = tester.getChildValue(childForm); |
| Object expectedChildValue = values.get(rangeIndex); |
| |
| assertEquals( |
| displayString() + " : The child value was not retrieved correctly", |
| expectedChildValue, |
| childValue |
| ); |
| |
| // Retrieve the child |
| childForm = childForms.get(translatedPosition); |
| |
| assertNotNull( |
| displayString() + " : The child form cannot be null", |
| childForm |
| ); |
| |
| // Retrieve the child value from the child form |
| childValue = tester.getChildValue(childForm); |
| |
| assertEquals( |
| displayString() + " : The child value was not retrieved correctly", |
| expectedChildValue, |
| childValue |
| ); |
| } |
| } |
| } |
| |
| private void testReadingAfterRemoval(ChildListNodeTester<T, Object, Object> tester, |
| T form, |
| List<Object> childForms, |
| List<Object> values, |
| List<Integer> positions) { |
| |
| int childIndex = 0; |
| |
| for (Integer position : positions) { |
| |
| // Retrieve the child form |
| Object childForm = tester.getChild(form, childIndex++); |
| |
| assertNotNull( |
| displayString() + " : The child form cannot be null", |
| childForm |
| ); |
| |
| // Retrieve the child value from the child form |
| Object childValue = tester.getChildValue(childForm); |
| Object expectedChildValue = values.get(position); |
| |
| assertEquals( |
| displayString() + " : The child value was not retrieved correctly", |
| expectedChildValue, |
| childValue |
| ); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| void testRemoving(T form, Node node) { |
| |
| // Nothing to test |
| if (ranges.isEmpty()) { |
| return; |
| } |
| |
| ChildListNodeTester<T, Object, Object> tester = (ChildListNodeTester<T, Object, Object>) this.tester; |
| Random random = new Random(); |
| List<Object> values = tester.getChildValues(); |
| List<Object> readOnlyChildForms = tester.getChildren(form); |
| |
| // Now remove all the children at random position and |
| // test reading the remaining between each removal |
| Integer[] range = ranges.get(rangeIndex); |
| int rangeCount = ranges.size(); |
| |
| // Cache the positions |
| List<Integer> positions = new ArrayList<Integer>(); |
| |
| for (int index = range[0]; index < range[1]; index++) { |
| positions.add(index); |
| } |
| |
| List<Integer> readOnlyPositions = new ArrayList<Integer>(); |
| readOnlyPositions.addAll(positions); |
| |
| while (!positions.isEmpty()) { |
| |
| // Get a new random position |
| int position = (positions.size() == 1) ? 0 : random.nextInt(positions.size()); |
| positions.remove(position); |
| readOnlyPositions.remove(position); |
| |
| // Translate the position to the actual position within the list of children |
| // plus the position, which the number of items within the range used to delete |
| // children before the child being deleted |
| int translatedPosition = position; |
| |
| if (rangeIndex > 0) { |
| int previousRangeIndex = rangeIndex - 1; |
| int lastRangeIndex = ranges.get(previousRangeIndex)[1]; |
| translatedPosition += translatePosition(previousRangeIndex, lastRangeIndex); |
| } |
| |
| // Retrieve the child form |
| tester.removeChild(form, translatedPosition); |
| currentChildrenCount--; |
| currentChildListCount--; |
| |
| // Adjust the cached positions |
| for (int positionIndex = position; positionIndex < positions.size(); positionIndex++) { |
| positions.set(positionIndex, positions.get(positionIndex) - 1); |
| } |
| |
| // |
| // Test 1: Make sure only one node was removed |
| // |
| // Test to make sure the parent node has the right amount of children left |
| assertEquals( |
| displayString() + " : The number of children is inconsistent", |
| currentChildListCount, |
| tester.getChildrenSize(form) |
| ); |
| |
| assertEquals( |
| displayString() + " : The number of children is inconsistent", |
| currentChildrenCount, |
| node.getChildNodes().getLength() |
| ); |
| |
| // Retrieve the values that will be used to create the child nodes |
| List<Object> childForms = tester.getChildren(form); |
| |
| assertEquals( |
| displayString() + " : The number of children is inconsistent", |
| currentChildListCount, |
| childForms.size() |
| ); |
| |
| // |
| // Test 2: Read the other children to make sure the right one was removed |
| // |
| // Retrieve the child |
| if (rangeCount == 1) { |
| testReadingAfterRemoval(tester, form, readOnlyChildForms, values, readOnlyPositions); |
| } |
| else { |
| testReadingAfterRemoval(tester, form, childForms, values, rangeIndex, positions.size()); |
| } |
| } |
| |
| // Completed removing the children from the selected range |
| ranges.remove(rangeIndex); |
| } |
| |
| private int translatePosition(int endRangesIndex, int rangeIndexToAdjust) { |
| return translatePosition(endRangesIndex, rangeIndexToAdjust, -1); |
| } |
| |
| private int translatePosition(final int endRangesIndex, |
| final int rangeIndexToAdjust, |
| final int rangesIndexToSkip) { |
| |
| int translatedPosition = 0; |
| |
| for (int rangesIndex = 0; rangesIndex <= endRangesIndex; rangesIndex++) { |
| |
| // Skip the specified range since work is being done with it |
| if (rangesIndex == rangesIndexToSkip) { |
| continue; |
| } |
| |
| Integer[] range = ranges.get(rangesIndex); |
| |
| // Quick calculation |
| if (rangesIndex != endRangesIndex) { |
| translatedPosition += (range[1] - range[0]); |
| } |
| // Adjust the index within the range that ends the translation |
| else { |
| translatedPosition += (rangeIndexToAdjust - range[0]); |
| } |
| } |
| |
| return translatedPosition; |
| } |
| } |
| |
| private class ChildNodeRunnerAddingController extends Controller { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void log() { |
| System.out.println("<" + runner.parentNodeName + "><" + runner.tester.getNodeName() + "> : addition"); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| void test(T form, Node node) { |
| |
| ChildNodeTesterRunner runner = (ChildNodeTesterRunner) this.runner; |
| runner.currentChildrenCount = currentChildrenCount; |
| runner.testInitialState(form, node); |
| runner.testAdding(form, node); |
| |
| currentChildrenCount = runner.currentChildrenCount; |
| } |
| } |
| |
| private class ChildNodeRunnerReadingController extends Controller { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void log() { |
| System.out.println("<" + runner.parentNodeName + "><" + runner.tester.getNodeName() + "> : addition"); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| void test(T form, Node node) { |
| |
| ChildNodeTesterRunner runner = (ChildNodeTesterRunner) this.runner; |
| runner.currentChildrenCount = currentChildrenCount; |
| runner.testInitialState(form, node); |
| runner.testReading(form, node); |
| |
| currentChildrenCount = runner.currentChildrenCount; |
| } |
| } |
| |
| private class ChildNodeRunnerRemovingController extends Controller { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void log() { |
| System.out.println("<" + runner.parentNodeName + "><" + runner.tester.getNodeName() + "> : addition"); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| void test(T form, Node node) { |
| |
| ChildNodeTesterRunner runner = (ChildNodeTesterRunner) this.runner; |
| runner.currentChildrenCount = currentChildrenCount; |
| runner.testInitialState(form, node); |
| runner.testRemoving(form, node); |
| |
| currentChildrenCount = runner.currentChildrenCount; |
| } |
| } |
| |
| /** |
| * This <code>ChildNodeTester</code> tests when a form add or remove a single child node. |
| */ |
| public interface ChildNodeTester<T, VALUE extends ExternalForm> extends NodeTester<T, VALUE> { |
| |
| /** |
| * Adds the single child node to the node represented by the given form. |
| * |
| * @param form The external form being tested |
| * @return The {@link ExternalForm} of the child node that was added |
| */ |
| VALUE addChild(T form); |
| |
| /** |
| * Retrieves the {@link ExternalForm} representing the single child node. |
| * |
| * @param form The external form being tested |
| * @return The {@link ExternalForm} representing the child node |
| */ |
| VALUE getChild(T form); |
| |
| /** |
| * Determines whether the given form has a child node with the specific node name. |
| * |
| * @param form The external form being tested |
| * @return <code>true</code> if the document has the single node as a child node of the node |
| * for which the given form represents; <code>false</code> if the node does not exist |
| */ |
| boolean hasChild(T form); |
| |
| /** |
| * Removes the single child node from the node represented by the given form. |
| * |
| * @param form The external form being tested |
| */ |
| void removeChild(T form); |
| } |
| |
| /** |
| * A <code>ChildNodeTesterRunner</code> tests to make sure the single child node is added and |
| * removed correctly from its parent node. |
| */ |
| private class ChildNodeTesterRunner extends NodeTesterRunner { |
| |
| /** |
| * |
| */ |
| private boolean hasChild; |
| |
| /** |
| * Creates a new <code>ChildNodeTesterRunner</code>. |
| * |
| * @param tester This object defines a single child node to test |
| */ |
| ChildNodeTesterRunner(ChildNodeTester<T, ?> tester) { |
| super(tester); |
| } |
| |
| private String displayString() { |
| return "<" + getNodeName() + ">"; |
| } |
| |
| private Node getChildNode(Node node) { |
| |
| node = node.getFirstChild(); |
| |
| while (node != null) { |
| |
| if (getNodeName().equals(node.getNodeName())) { |
| return node; |
| } |
| |
| node = node.getNextSibling(); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| boolean isMultipleSupported() { |
| return false; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void testAdding(T form, Node node) { |
| |
| // Don't add the child if it's not present |
| if (hasChild) { |
| return; |
| } |
| |
| ChildNodeTester<T, ? extends ExternalForm> tester = (ChildNodeTester<T, ?>) this.tester; |
| |
| // Add the child node |
| ExternalForm childForm = tester.addChild(form); |
| hasChild = true; |
| currentChildrenCount++; |
| |
| assertNotNull( |
| displayString() + " : The child form should exist", |
| childForm |
| ); |
| |
| // Make sure the child node was indeed added |
| assertEquals( |
| displayString() + " : The number of children is inconsistent", |
| currentChildrenCount, |
| node.getChildNodes().getLength() |
| ); |
| |
| // Make sure the child node is correctly retrieved |
| assertTrue( |
| displayString() + " : The child node should have been found", |
| tester.hasChild(form) |
| ); |
| |
| // Make sure the right child node was added and is retrieved by the ExternalForm |
| assertNotNull( |
| displayString() + " : The child node should exist", |
| getChildNode(node) |
| ); |
| |
| // Make sure nothing changed |
| assertEquals( |
| displayString() + " : The child node should exist", |
| currentChildrenCount, |
| node.getChildNodes().getLength() |
| ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void testInitialState(T form, Node node) { |
| |
| ChildNodeTester<T, ?> tester = (ChildNodeTester<T, ?>) this.tester; |
| |
| // Make sure the child node does not exist |
| assertEquals( |
| displayString() + " : The child node should not exist", |
| hasChild, |
| tester.hasChild(form) |
| ); |
| |
| // Make sure nothing changed |
| assertEquals( |
| displayString() + " : The child node should not exist", |
| currentChildrenCount, |
| node.getChildNodes().getLength() |
| ); |
| |
| // The child node should not exist |
| if (hasChild) { |
| assertNotNull( |
| displayString() + " : The child node should exist", |
| getChildNode(node) |
| ); |
| } |
| else { |
| assertNull( |
| displayString() + " : The child node should not exist", |
| getChildNode(node) |
| ); |
| } |
| |
| // Make sure nothing changed |
| assertEquals( |
| displayString() + " : The child node should not exist", |
| currentChildrenCount, |
| node.getChildNodes().getLength() |
| ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void testReading(T form, Node node) { |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void testRemoving(T form, Node node) { |
| |
| // Nothing to remove |
| if (!hasChild) { |
| return; |
| } |
| |
| ChildNodeTester<T, ?> tester = (ChildNodeTester<T, ?>) this.tester; |
| |
| // Make sure the child node exists |
| assertTrue( |
| displayString() + " : The child node should exist", |
| tester.hasChild(form) |
| ); |
| |
| // Make sure nothing changed with the previous check |
| assertEquals( |
| displayString() + " : The child node should exist", |
| currentChildrenCount, |
| node.getChildNodes().getLength() |
| ); |
| |
| // Remove the child node |
| tester.removeChild(form); |
| currentChildrenCount--; |
| hasChild = false; |
| |
| // Make sure the child node was indeed removed |
| assertEquals( |
| displayString() + " : The child node should have been removed", |
| currentChildrenCount, |
| node.getChildNodes().getLength() |
| ); |
| |
| // Make sure the right child node was removed |
| assertNull( |
| displayString() + " : The wrong child node was removed", |
| getChildNode(node) |
| ); |
| |
| // Test to make sure the child node is not found |
| assertFalse( |
| displayString() + " : The child node should not exist", |
| tester.hasChild(form) |
| ); |
| } |
| } |
| |
| /** |
| * A <code>Controller</code> wraps a {@link NodeTesterRunner} and executes one of the possible |
| * tests, which is either add, read or remove the property from the node being manipulated by |
| * the {@link ExternalForm}. |
| */ |
| private abstract class Controller { |
| |
| /** |
| * The number of children the node currently has, which is required to make sure the runner |
| * tests the accuracy of the {@link ExternalForm} when it manipulates the node. |
| */ |
| int currentChildrenCount; |
| |
| /** |
| * |
| */ |
| NodeTesterRunner runner; |
| |
| /** |
| * Outputs what is being executed. |
| */ |
| abstract void log(); |
| |
| /** |
| * Executes one of the operations that can be performed on an node. |
| * |
| * @param form The {@link ExternalForm} to test |
| * @param node The node that is manipulated by the given {@link ExternalForm} |
| */ |
| abstract void test(T form, Node node); |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public String toString() { |
| return getClass().getSimpleName(); |
| } |
| } |
| |
| private class DefaultRootNodeTester implements RootNodeTester<T> { |
| |
| /** |
| * The list of testers that used to test every single attribute |
| * nodes of the one represented by this tester. |
| */ |
| private List<AttributeNodeTesterRunner> attributes; |
| |
| /** |
| * The builder of the {@link ExternalForm} to test. |
| */ |
| private ExternalFormBuilder<T> builder; |
| |
| /** |
| * The list of testers that used to test every single child nodes of the one represented by |
| * this tester. |
| */ |
| private List<NodeTesterRunner> children; |
| |
| /** |
| * Creates a new <code>DefaultRootNodeTester</code>. |
| */ |
| private DefaultRootNodeTester() { |
| super(); |
| attributes = new ArrayList<AttributeNodeTesterRunner>(); |
| children = new ArrayList<NodeTesterRunner>(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addAttribute(AttributeNodeTester<T, ?> tester) { |
| attributes.add(new AttributeNodeTesterRunner(tester)); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addListNodes(ChildListNodeTester<T, ?, ?> tester) { |
| children.add(new ChildListNodeTesterRunner(tester)); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addNode(ChildNodeTester<T, ?> tester) { |
| children.add(new ChildNodeTesterRunner(tester)); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addTextNode(TextNodeTester<T, ?> tester) { |
| children.add(new TextNodeTesterRunner(tester)); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addUnsupportedNode(String nodeName) { |
| children.add(new NotSupportedNodeTesterRunner(nodeName)); |
| } |
| |
| @SuppressWarnings({"unchecked", "rawtypes"}) |
| private Map<Class<NodeTesterRunner>, Class<Controller>[]> buildControllerTypes() { |
| |
| Map classes = new HashMap(); |
| |
| classes.put( |
| ChildNodeTesterRunner.class, |
| new Class<?>[] { |
| ChildNodeRunnerAddingController.class, |
| ChildNodeRunnerReadingController.class, |
| ChildNodeRunnerRemovingController.class |
| } |
| ); |
| |
| classes.put( |
| TextNodeTesterRunner.class, |
| new Class<?>[] { |
| TextNodeRunnerAddingController.class, |
| TextNodeRunnerReadingController.class, |
| TextNodeRunnerRemovingController.class |
| } |
| ); |
| |
| classes.put( |
| ChildListNodeTesterRunner.class, |
| new Class<?>[] { |
| ChildListNodeRunnerAddingController.class, |
| ChildListNodeRunnerReadingController.class, |
| ChildListNodeRunnerRemovingController.class |
| } |
| ); |
| |
| return classes; |
| } |
| |
| private String displayString() { |
| return "<" + builder.getNodeName() + ">"; |
| } |
| |
| private Node getNode(Node node) { |
| |
| // The builder is for the root node |
| if (node.getNodeName().equals(builder.getNodeName())) { |
| return node; |
| } |
| |
| // Dig into the DOM tree to find the child node |
| return getNode(node, builder.getTreeNodeNames()); |
| } |
| |
| private Node getNode(Node node, List<String> nodeNames) { |
| |
| String nodeName = nodeNames.get(0); |
| node = node.getFirstChild(); |
| |
| while (node != null) { |
| |
| if (node.getNodeName().equals(nodeName)) { |
| |
| if (nodeNames.size() == 1) { |
| return node; |
| } |
| |
| return getNode(node, nodeNames.subList(1, nodeName.length())); |
| } |
| |
| node = node.getNextSibling(); |
| } |
| |
| return null; |
| } |
| |
| private int getNodePositionOfInsertion(Node node) { |
| |
| String nodeName = node.getNodeName(); |
| |
| for (int index = 0, count = children.size(); index < count; index++) { |
| |
| NodeTesterRunner tester = children.get(index); |
| |
| if (tester.getNodeName().equals(nodeName)) { |
| return index; |
| } |
| } |
| |
| fail("The child node named <" + nodeName + "> is not included into the test"); |
| return -1; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| public void setBuilder(ExternalFormBuilder<? extends T> builder) { |
| this.builder = (ExternalFormBuilder<T>) builder; |
| } |
| |
| /** |
| * Tests the {@link ExternalForm}, which might have attributes and child nodes. |
| */ |
| void test() throws Exception { |
| |
| // Creates the ExternalForm |
| T form = builder.buildExternalForm(); |
| |
| assertNotNull( |
| displayString() + " : The external form cannot be null", |
| form |
| ); |
| |
| // Retrieve the node represented by the ExternalForm |
| Node node = builder.getNode(form); |
| |
| assertNotNull( |
| displayString() + " : The node cannot be null", |
| node |
| ); |
| |
| // Retrieve the node directly from the document |
| Node expectedNode = getNode(node.getOwnerDocument()); |
| |
| assertSame( |
| displayString() + " : The node was not retrieved correctly", |
| expectedNode, |
| node |
| ); |
| |
| // Test each attribute |
| int defaultAttributeCount = builder.getDefaultAttributeCount(); |
| |
| for (AttributeNodeTesterRunner runner : attributes) { |
| |
| runner.currentChildrenCount = defaultAttributeCount; |
| runner.parentNodeName = builder.getNodeName(); |
| runner.test(form, node); |
| |
| assertEquals( |
| displayString() + " : No number of attributes is inconsistent", |
| defaultAttributeCount, |
| node.getAttributes().getLength() |
| ); |
| } |
| |
| // Test each child node |
| for (NodeTesterRunner runner : children) { |
| |
| runner.parentNodeName = builder.getNodeName(); |
| runner.test(form, node); |
| |
| assertEquals( |
| displayString() + " : No child nodes should have been left after a test", |
| 0, |
| node.getChildNodes().getLength() |
| ); |
| } |
| |
| // Now test manipulating the attributes in random order |
| testAttributesRandomly(form, node); |
| |
| // Now test manipulating the child nodes in random order |
| testChildNodesRandomly(form, node); |
| } |
| |
| private void testAttributesRandomly(T form, Node node) { |
| } |
| |
| private void testChildNodesRandomly(T form, Node parent) throws Exception { |
| |
| // The list of controllers is used to execute a single test for a particular, |
| // a list will be generated randomly to test the order |
| Map<Class<NodeTesterRunner>, Class<Controller>[]> controllerClasses = buildControllerTypes(); |
| List<Controller> controllers = new ArrayList<Controller>(); |
| Random random = new Random(); |
| |
| int childrenCount = children.size(); |
| int controllerTypeCount = controllerClasses.size(); |
| int executionCount = childrenCount * controllerTypeCount * 20; |
| |
| // Let's run each Runner something like 20 times |
| for (int index = 0; index < executionCount; index++) { |
| |
| // Get the runner randomly |
| int childIndex = random.nextInt(childrenCount); |
| NodeTesterRunner runner = children.get(childIndex); |
| |
| // Get the controller type randomly |
| int controllerIndex = random.nextInt(controllerTypeCount); |
| Class<Controller>[] controllerTypes = controllerClasses.get(runner.getClass()); |
| |
| // Happen for unsupported node name |
| if (controllerTypes == null) { |
| continue; |
| } |
| |
| Class<Controller> controllerType = controllerTypes[controllerIndex]; |
| |
| // Create the controller |
| Controller controller = ClassTools.newInstance( |
| controllerType, |
| AbstractExternalFormTests.class, |
| AbstractExternalFormTests.this |
| ); |
| |
| controller.runner = runner; |
| controllers.add(controller); |
| } |
| |
| // Run each controller and keep the child nodes count up to date |
| for (Controller controller : controllers) { |
| |
| controller.currentChildrenCount = parent.getChildNodes().getLength(); |
| // controller.log(); |
| controller.test(form, parent); |
| |
| // Retrieve the ordinal of each child node |
| Node childNode = parent.getFirstChild(); |
| |
| if (childNode != null) { |
| |
| List<Integer> nodePositions = new ArrayList<Integer>(); |
| |
| do { |
| int nodePosition = getNodePositionOfInsertion(childNode); |
| nodePositions.add(nodePosition); |
| childNode = childNode.getNextSibling(); |
| } |
| while (childNode != null); |
| |
| // Make sure the ordinal numbers are from the smaller number to the biggest number |
| int previousPosition = -1; |
| |
| for (int nodePosition : nodePositions) { |
| |
| if (nodePosition < previousPosition) { |
| fail("The insertion was not performed following the ordering."); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * A <code>ExternalFormBuilder</code> is responsible to create the {@link ExternalForm} to be tested. |
| */ |
| public interface ExternalFormBuilder<T extends ExternalForm> { |
| |
| /** |
| * Creates the {@link ExternalForm} to test. |
| * |
| * @return The {@link ExternalForm} to test |
| * @throws IOException If an error occurred during the creation process |
| */ |
| T buildExternalForm() throws IOException; |
| |
| /** |
| * Returns the number of attributes the {@link Node} contains before any manipulation has been performed. |
| * |
| * @return The count of children before any testing has been done |
| */ |
| int getDefaultAttributeCount(); |
| |
| /** |
| * Returns the node represented by the {@link ExternalForm}. |
| * |
| * @param form The {@link ExternalForm} for which to return its node |
| * @return The node from the document that is been manipulated |
| */ |
| Node getNode(T form); |
| |
| String getNodeName(); |
| |
| List<String> getTreeNodeNames(); |
| } |
| |
| private interface NodeTester<T, Value> { |
| |
| /** |
| * Retrieves the name of the node for which retrieving and setting its value is tested. |
| * |
| * @return The name of the node to test |
| */ |
| String getNodeName(); |
| } |
| |
| private abstract class NodeTesterRunner { |
| |
| /** |
| * |
| */ |
| int currentChildrenCount; |
| |
| /** |
| * |
| */ |
| String parentNodeName; |
| |
| /** |
| * This object defines a single node to test (which is either a child element or an attribute). |
| */ |
| final NodeTester<T, Object> tester; |
| |
| /** |
| * Creates a new <code>AbstractNodeTester</code>. |
| * |
| * @param tester The bridge between this tester and the document's node being tested |
| */ |
| @SuppressWarnings("unchecked") |
| NodeTesterRunner(NodeTester<T, ?> tester) { |
| super(); |
| this.tester = (NodeTester<T, Object>) tester; |
| assertNotNull("The tester cannot be null", tester); |
| assertNotNull("The node name cannot be null", tester.getNodeName()); |
| } |
| |
| /** |
| * Returns the name of the node for which this runner tests. |
| * |
| * @return The node name |
| */ |
| final String getNodeName() { |
| return tester.getNodeName(); |
| } |
| |
| /** |
| * Determines whether |
| * |
| * @return |
| */ |
| abstract boolean isMultipleSupported(); |
| |
| /** |
| * Tests the given {@link ExternalForm} by manipulating the node for a single property. |
| * |
| * @param form The {@link ExternalForm} to test |
| * @param node The node that is manipulated by the given {@link ExternalForm} |
| */ |
| void test(T form, Node node) { |
| testInitialState(form, node); |
| testAdding(form, node); |
| testReading(form, node); |
| testRemoving(form, node); |
| } |
| |
| /** |
| * Tests the given {@link ExternalForm} by adding the property to the node. |
| * |
| * @param form The {@link ExternalForm} to test |
| * @param node The node that is manipulated by the given {@link ExternalForm} |
| */ |
| abstract void testAdding(T form, Node node); |
| |
| /** |
| * Tests the given {@link ExternalForm} by making sure the given node is in its original state. |
| * |
| * @param form The {@link ExternalForm} to test |
| * @param node The node that is manipulated by the given {@link ExternalForm} |
| */ |
| abstract void testInitialState(T form, Node node); |
| |
| /** |
| * Tests the given {@link ExternalForm} by reading the property from the node. |
| * |
| * @param form The {@link ExternalForm} to test |
| * @param node The node that is manipulated by the given {@link ExternalForm} |
| */ |
| abstract void testReading(T form, Node node); |
| |
| /** |
| * Tests the given {@link ExternalForm} by removing the property from the node. |
| * |
| * @param form The {@link ExternalForm} to test |
| * @param node The node that is manipulated by the given {@link ExternalForm} |
| */ |
| abstract void testRemoving(T form, Node node); |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public String toString() { |
| return getClass().getSimpleName() + " : " + tester.getNodeName(); |
| } |
| } |
| |
| private class NotSupportedNodeTester implements NodeTester<T, Object> { |
| |
| /** |
| * The name of the node that is currently not supported by the {@link ExternalForm}. |
| */ |
| private String nodeName; |
| |
| /** |
| * Creates a new <code>NotSupportedNodeTester</code>. |
| * |
| * @param nodeName The name of the node that is currently not supported by the {@link ExternalForm} |
| */ |
| NotSupportedNodeTester(String nodeName) { |
| super(); |
| this.nodeName = nodeName; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public String getNodeName() { |
| return nodeName; |
| } |
| } |
| |
| private class NotSupportedNodeTesterRunner extends NodeTesterRunner { |
| |
| /** |
| * Creates a new <code>NotSupportedNodeTesterRunner</code>. |
| * |
| * @param nodeName The name of the node that is currently not supported by the {@link ExternalForm} |
| */ |
| NotSupportedNodeTesterRunner(String nodeName) { |
| super(new NotSupportedNodeTester(nodeName)); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| boolean isMultipleSupported() { |
| return false; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void testAdding(T form, Node node) { |
| // Nothing to do |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void testInitialState(T form, Node node) { |
| // Nothing to do |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void testReading(T form, Node node) { |
| // Nothing to do |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void testRemoving(T form, Node node) { |
| // Nothing to do |
| } |
| } |
| |
| private abstract class PropertyNodeTesterRunner extends NodeTesterRunner { |
| |
| /** |
| * Keeps track of the status of the node's existence. |
| */ |
| private boolean nodeExists; |
| |
| /** |
| * Creates a new <code>PropertyNodeTesterRunner</code>. |
| * |
| * @param tester This object defines a single node to test a child text node |
| */ |
| PropertyNodeTesterRunner(PropertyTester<T, ?> tester) { |
| super(tester); |
| } |
| |
| /** |
| * Returns |
| * |
| * @return |
| */ |
| abstract String displayString(); |
| |
| /** |
| * Returns the number of children owned by the given parent {@link Node}. |
| * |
| * @param parentNode The owner of the child node being tested |
| * @return The count of children, which could be either attributes or child nodes |
| */ |
| abstract int getChildrenCount(Node parentNode); |
| |
| /** |
| * Returns |
| * |
| * @param parentNode |
| * @return |
| */ |
| abstract Node getNode(Node parentNode); |
| |
| /** |
| * Returns |
| * |
| * @param node |
| * @return |
| */ |
| abstract String getNodeValue(Node node); |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| final boolean isMultipleSupported() { |
| return false; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void testAdding(T form, Node parentNode) { |
| |
| PropertyTester<T, Object> tester = (PropertyTester<T, Object>) this.tester; |
| |
| // Change the node value to null |
| if (tester.isNullAllowed()) { |
| |
| tester.setValue(form, null); |
| |
| assertEquals( |
| displayString() + " : The element should not have any children", |
| 0, |
| getChildrenCount(parentNode) - currentChildrenCount |
| ); |
| |
| // Make sure nothing changed |
| Object result = tester.getValue(form); |
| |
| assertNull( |
| displayString() + " : The element's value should be null", |
| result |
| ); |
| |
| // Make sure nothing changed |
| assertEquals( |
| displayString() + " : The element should not have any children", |
| 0, |
| getChildrenCount(parentNode) - currentChildrenCount |
| ); |
| } |
| |
| // Change the value to something |
| Object expectedValue1 = tester.getValue1(); |
| |
| assertNotNull( |
| displayString() + " : Value 1 cannot be null", |
| expectedValue1 |
| ); |
| |
| tester.setValue(form, expectedValue1); |
| |
| // The child node should have been added |
| Node childNode = getNode(parentNode); |
| |
| assertNotNull( |
| displayString() + " : The node cannot be null", |
| childNode |
| ); |
| |
| assertEquals( |
| displayString() + " : The element should have a " + (nodeExists ? currentChildrenCount : currentChildrenCount + 1) + " children", |
| nodeExists ? 0 : 1, |
| getChildrenCount(parentNode) - currentChildrenCount |
| ); |
| |
| // Get the value |
| Object result = tester.getValue(form); |
| |
| assertEquals( |
| displayString() + "The element's value was not set correctly", |
| expectedValue1, |
| result |
| ); |
| |
| // Get the value directly |
| String stringResult = getNodeValue(childNode); |
| String stringNodeValue = tester.toString(expectedValue1); |
| |
| assertEquals( |
| displayString() + " : The value was not set correctly", |
| stringNodeValue, |
| stringResult |
| ); |
| |
| // Change the value to something else |
| Object expectedValue2 = tester.getValue2(); |
| assertNotNull(displayString() + " : Value 2 cannot be null", expectedValue2); |
| |
| assertNotSame( |
| displayString() + " : Value 1 and value 2 cannot be the same", |
| expectedValue1, |
| expectedValue2 |
| ); |
| |
| tester.setValue(form, expectedValue2); |
| |
| // Get the value |
| result = tester.getValue(form); |
| |
| assertEquals( |
| displayString() + " The element's value was not set correctly", |
| expectedValue2, |
| result |
| ); |
| |
| // Get the value directly |
| stringResult = getNodeValue(childNode); |
| stringNodeValue = tester.toString(expectedValue2); |
| |
| assertEquals( |
| displayString() + " : The element's value was not set correctly", |
| stringNodeValue, |
| stringResult |
| ); |
| |
| // Change the value back to its original value |
| tester.setValue(form, tester.getDefaultValue()); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void testInitialState(T form, Node parent) { |
| |
| PropertyTester<T, Object> tester = (PropertyTester<T, Object>) this.tester; |
| nodeExists |= tester.doesNodeExistAlready(); |
| |
| // Node name |
| String nodeName = getNodeName(); |
| assertNotNull("The node name cannot be null", nodeName); |
| |
| // Test the initial state of the parent node |
| assertEquals( |
| displayString() + " : The parent node child count is incorrect", |
| 0, |
| getChildrenCount(parent) - currentChildrenCount |
| ); |
| |
| // The child node should either not exist or already being present |
| Node childNode = getNode(parent); |
| |
| if (nodeExists) { |
| assertNotNull( |
| displayString() + " : The node should not be null", |
| childNode |
| ); |
| } |
| else { |
| assertNull( |
| displayString() + " : The node should be null", |
| childNode |
| ); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void testReading(T form, Node node) { |
| |
| PropertyTester<T, Object> tester = (PropertyTester<T, Object>) this.tester; |
| |
| // The node value should either be null or not null |
| Object result = tester.getValue(form); |
| |
| // Make sure the node's value is not null |
| if (nodeExists) { |
| assertNotNull( |
| displayString() + " : The element's value should not be null", |
| result |
| ); |
| assertSame( |
| displayString() + " : The element's value was not retrived correctly", |
| tester.getDefaultValue(), |
| result |
| ); |
| } |
| // The node is not present, make sure reading its value returns null |
| else { |
| assertNull( |
| displayString() + " : The element's value should be null", |
| result |
| ); |
| } |
| |
| // Make sure nothing changed by reading the node's value |
| assertEquals( |
| displayString() + " : The element should not have any children", |
| 0, |
| getChildrenCount(node) - currentChildrenCount |
| ); |
| |
| // Retrieve the actual node |
| Node childNode = getNode(node); |
| |
| if (nodeExists) { |
| assertNotNull( |
| displayString() + " : The node should not be null", |
| childNode |
| ); |
| } |
| else { |
| assertNull( |
| displayString() + " : The node should be null", |
| childNode |
| ); |
| } |
| |
| // Make sure nothing changed after retrieving the node |
| assertEquals( |
| displayString() + " : The element should not have any children", |
| 0, |
| getChildrenCount(node) - currentChildrenCount |
| ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void testRemoving(T form, Node parentNode) { |
| |
| PropertyTester<T, Object> tester = (PropertyTester<T, Object>) this.tester; |
| |
| // Change the node value to null, which will remove it from the parent node |
| if (tester.isNullAllowed()) { |
| |
| tester.setValue(form, null); |
| |
| assertEquals( |
| displayString() + " : The child count does not match the current state", |
| 0, |
| getChildrenCount(parentNode) - currentChildrenCount |
| ); |
| } |
| } |
| } |
| |
| /** |
| * A <code>PropertyTester</code> handles testing either an attribute or a single child node of |
| * the node being tested. |
| */ |
| private interface PropertyTester<T, VALUE> extends NodeTester<T, VALUE> { |
| |
| /** |
| * Determines whether the property is by default present in the document. |
| * |
| * @return <code>true</code> if the property exists before any changes is done to the document; |
| * <code>false</code> otherwise |
| */ |
| boolean doesNodeExistAlready(); |
| |
| /** |
| * If {@link #doesNodeExistAlready()} returns <code>true</code>, then this should return the |
| * node value that is already present in the document, which is done before the document is |
| * being changed. |
| * |
| * @return Either a non-<code>null</code> value if the node is present in the document or |
| * <code>null</code> if the node is not by default present in the document |
| */ |
| VALUE getDefaultValue(); |
| |
| /** |
| * Retrieves the value of the node. |
| * |
| * @param form The external form that will retrieve the value |
| * @return The value, which can be <code>null</code> |
| */ |
| VALUE getValue(T form); |
| |
| /** |
| * Retrieves a non-<code>null</code> value that will be used for testing that is different |
| * than the one returned by {@link #getValue2()}. |
| * |
| * @return A non-<code>null</code> value used for testing |
| */ |
| VALUE getValue1(); |
| |
| /** |
| * Retrieves a non-<code>null</code> value that will be used for testing that is different |
| * than the one returned by {@link #getValue1()}. |
| * |
| * @return A non-<code>null</code> value used for testing |
| */ |
| VALUE getValue2(); |
| |
| /** |
| * Determines whether the node can receive a <code>null</code> value or not. |
| * |
| * @return <code>true</code> if <code>null</code> is a valid value; <code>false</code> otherwise |
| */ |
| boolean isNullAllowed(); |
| |
| /** |
| * Sets the value of the node. |
| * |
| * @param form The external form that will set the value |
| * @return The value, which can be <code>null</code> |
| */ |
| void setValue(T form, VALUE value); |
| |
| /** |
| * Converts the value to its string representation. |
| * |
| * @param value A non-<code>null</code> value to convert into a string |
| * @return The given value represented as a string |
| */ |
| String toString(VALUE value); |
| } |
| |
| /** |
| * A <code>RootNodeTester</code> is the container of all the testers that will test every single |
| * property of the node to test, i.e. its attributes and child nodes. |
| */ |
| public interface RootNodeTester<T extends ExternalForm> { |
| |
| /** |
| * Adds the given tester when the form has an attribute. |
| * |
| * @param tester The tester for a single attribute |
| */ |
| void addAttribute(AttributeNodeTester<T, ?> tester); |
| |
| /** |
| * Adds the given tester when the form representing the node it represents can have zero or |
| * many child nodes of the same type (i.e. with the same node name). |
| * |
| * @param tester The tester for a list of child nodes with the same type |
| */ |
| void addListNodes(ChildListNodeTester<T, ?, ?> tester); |
| |
| /** |
| * Adds the given tester when the form representing the node it represents can have one child |
| * node of a certain type. |
| * |
| * @param tester The tester for a single child node |
| */ |
| void addNode(ChildNodeTester<T, ?> tester); |
| |
| /** |
| * Adds the given tester when the form has a single child node that is a text node. |
| * |
| * @param tester The tester for a single child text node |
| */ |
| void addTextNode(TextNodeTester<T, ?> tester); |
| |
| /** |
| * Adds the given node name to indicate it is part of the "root" node but it is not supported |
| * by the {@link ExternalForm} yet. |
| * |
| * @param nodeName The name of the node not currently supported |
| */ |
| void addUnsupportedNode(String nodeName); |
| |
| /** |
| * Sets the builder that will create the document and tree node representation down to node to test. |
| * |
| * @param builder The builder of the node to test |
| */ |
| void setBuilder(ExternalFormBuilder<? extends T> builder); |
| } |
| |
| private class TextNodeRunnerAddingController extends Controller { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void log() { |
| System.out.println("<" + runner.parentNodeName + "><" + runner.tester.getNodeName() + "> : addition"); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| void test(T form, Node node) { |
| |
| TextNodeTesterRunner runner = (TextNodeTesterRunner) this.runner; |
| runner.currentChildrenCount = currentChildrenCount; |
| runner.testInitialState(form, node); |
| runner.testAdding(form, node); |
| |
| currentChildrenCount = runner.currentChildrenCount; |
| } |
| } |
| |
| private class TextNodeRunnerReadingController extends Controller { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void log() { |
| System.out.println("<" + runner.parentNodeName + "><" + runner.tester.getNodeName() + "> addition"); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| void test(T form, Node node) { |
| |
| TextNodeTesterRunner runner = (TextNodeTesterRunner) this.runner; |
| runner.currentChildrenCount = currentChildrenCount; |
| runner.testInitialState(form, node); |
| runner.testReading(form, node); |
| |
| currentChildrenCount = runner.currentChildrenCount; |
| } |
| } |
| |
| private class TextNodeRunnerRemovingController extends Controller { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void log() { |
| System.out.println("<" + runner.parentNodeName + "><" + runner.tester.getNodeName() + "> : addition"); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| void test(T form, Node node) { |
| |
| TextNodeTesterRunner runner = (TextNodeTesterRunner) this.runner; |
| runner.currentChildrenCount = currentChildrenCount; |
| runner.testInitialState(form, node); |
| runner.testRemoving(form, node); |
| |
| currentChildrenCount = runner.currentChildrenCount; |
| } |
| } |
| |
| /** |
| * A <code>TextNodeTester</code> tests setting and retrieving the value associated with a text node. |
| */ |
| public interface TextNodeTester<T, VALUE> extends PropertyTester<T, VALUE> { |
| } |
| |
| /** |
| * <p>A <code>TextNodeTesterRunner</code> tests setting and retrieving the value associated with |
| * a text node.</p> |
| * <div nowrap>Form: <code><b><node_name>text</node_name></b></code>.</div> |
| */ |
| private class TextNodeTesterRunner extends PropertyNodeTesterRunner { |
| |
| /** |
| * Creates a new <code>TextNodeTesterRunner</code>. |
| * |
| * @param tester This object defines a single node to test a child text node |
| */ |
| TextNodeTesterRunner(TextNodeTester<T, ?> tester) { |
| super(tester); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| String displayString() { |
| return "<" + parentNodeName + "><" + getNodeName() + "></>"; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| int getChildrenCount(Node parentNode) { |
| return parentNode.getChildNodes().getLength(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| Node getNode(Node node) { |
| |
| node = node.getFirstChild(); |
| |
| while (node != null) { |
| |
| if (getNodeName().equals(node.getNodeName())) { |
| return node; |
| } |
| |
| node = node.getNextSibling(); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| String getNodeValue(Node node) { |
| return node.getTextContent(); |
| } |
| } |
| } |