/* * Copyright 2015 S. Webber * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.oakgp.function; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.oakgp.node.NodeType.isFunction; import static org.oakgp.util.Utils.createIntegerTypeArray; import java.io.IOException; import java.io.UncheckedIOException; import java.util.Observable; import java.util.Observer; import org.junit.Test; import org.oakgp.Arguments; import org.oakgp.Assignments; import org.oakgp.NodeSimplifier; import org.oakgp.Type; import org.oakgp.node.ConstantNode; import org.oakgp.node.FunctionNode; import org.oakgp.node.Node; import org.oakgp.primitive.VariableSet; import org.oakgp.serialize.NodeReader; public abstract class AbstractFunctionTest { private static final Type[] DEFAULT_VARIABLE_TYPES = createIntegerTypeArray(100); private final Function[] functions; /** * Observable allows other objects to be notified of the tests that are run. * <p> * This is used to support the automatic creation of http://www.oakgp.org/functions */ private final Observable observable = new Observable() { @Override public void notifyObservers(Object arg) { super.setChanged(); super.notifyObservers(arg); } }; protected AbstractFunctionTest() { functions = getFunctionSet(); } protected abstract Function getFunction(); @Test public abstract void testEvaluate(); @Test public abstract void testCanSimplify(); @Test public abstract void testCannotSimplify(); @Test public void testSignatureReused() { Function function = getFunction(); assertNotNull(function.getSignature()); assertSame(function.getSignature(), function.getSignature()); } @Test public void testDisplayNameValid() { String displayName = getFunction().getDisplayName(); assertTrue(NodeReader.isValidDisplayName(displayName)); } protected Function[] getFunctionSet() { return new Function[] { getFunction() }; } protected void cannotSimplify(String input, Type... variableTypes) { FunctionNode node = readFunctionNode(input, variableTypes); assertSame(node, NodeSimplifier.simplify(node)); } void addObserver(Observer o) { observable.addObserver(o); } private FunctionNode readFunctionNode(String input, Type... variableTypes) { return readFunctionNode(input, VariableSet.createVariableSet(variableTypes)); } private FunctionNode readFunctionNode(String input, VariableSet variableSet) { FunctionNode functionNode = (FunctionNode) readNode(input, variableSet); assertSame(getFunction().getClass(), functionNode.getFunction().getClass()); return functionNode; } private Node readNode(String input, VariableSet variableSet) { try (NodeReader nodeReader = new NodeReader(input, functions, new ConstantNode[0], variableSet)) { return nodeReader.readNode(); } catch (IOException e) { throw new UncheckedIOException(e); } } public EvaluateExpectation evaluate(String input) { return new EvaluateExpectation(input); } protected class EvaluateExpectation { private final String input; private ConstantNode[] assignedValues = {}; private EvaluateExpectation(String input) { this.input = input; } public EvaluateExpectation assigned(ConstantNode... assignedValues) { this.assignedValues = assignedValues; return this; } public void to(Object expectedResult) { Type[] variableTypes = toVariableTypes(assignedValues); FunctionNode functionNode = readFunctionNode(input, variableTypes); Assignments assignments = toAssignments(assignedValues); // assert evaluate consistently returns the expected result assertEquals(expectedResult, functionNode.evaluate(assignments)); assertEquals(expectedResult, functionNode.evaluate(assignments)); observable.notifyObservers(new Notification(functionNode, assignedValues, expectedResult)); } private Assignments toAssignments(ConstantNode[] constants) { Object[] values = new Object[constants.length]; for (int i = 0; i < constants.length; i++) { values[i] = constants[i].evaluate(null); } return Assignments.createAssignments(values); } private Type[] toVariableTypes(ConstantNode[] constants) { Type[] types = new Type[constants.length]; for (int i = 0; i < constants.length; i++) { types[i] = constants[i].getType(); } return types; } } static class Notification { final FunctionNode input; final ConstantNode[] assignedValues; final Object output; private Notification(FunctionNode input, ConstantNode[] assignedValues, Object output) { this.input = input; this.assignedValues = assignedValues; this.output = output; } } public SimplifyExpectation simplify(String input) { return new SimplifyExpectation(input); } protected class SimplifyExpectation { private final String input; private Type[] variableTypes = DEFAULT_VARIABLE_TYPES; private FunctionNode inputNode; private Node simplifiedNode; public SimplifyExpectation(String input) { this.input = input; } public SimplifyExpectation with(Type... variableTypes) { this.variableTypes = variableTypes; return this; } public SimplifyExpectation to(String expected) { VariableSet variableSet = VariableSet.createVariableSet(variableTypes); Node expectedNode = readNode(expected, variableSet); inputNode = readFunctionNode(input, variableSet); simplifiedNode = NodeSimplifier.simplify(inputNode); // assert actual matched expected assertEquals(expectedNode, simplifiedNode); assertSame(inputNode.getType(), simplifiedNode.getType()); if (isFunction(simplifiedNode)) { // assert that signature of function matches the // return type and argument types of the function node the function belongs to FunctionNode fn = (FunctionNode) simplifiedNode; Arguments fnArguments = fn.getArguments(); Signature fnSignature = fn.getFunction().getSignature(); assertSame(fn.getType(), fnSignature.getReturnType()); assertSameArgumentTypes(fnArguments, fnSignature); } // assert multiple calls to simplify with the same argument produces results that are equal assertEquals(NodeSimplifier.simplify(inputNode), NodeSimplifier.simplify(inputNode)); return this; } private void assertSameArgumentTypes(Arguments args, Signature signature) { assertEquals(args.getArgCount(), signature.getArgumentTypesLength()); for (int i = 0; i < signature.getArgumentTypesLength(); i++) { assertSame(args.getArg(i).getType(), signature.getArgumentType(i)); } } public SimplifyExpectation verify(Object... values) { Assignments assignments = Assignments.createAssignments(values); Object expectedOutcome = inputNode.evaluate(assignments); Object actualOutcome = simplifiedNode.evaluate(assignments); assertEquals(expectedOutcome, actualOutcome); return this; } public void verifyAll(Object[][] values) { for (Object[] a : values) { verify(a); } } } }