/** * Copyright (C) 2010-14 diirt developers. See COPYRIGHT.TXT * All rights reserved. Use is subject to license terms. See LICENSE.TXT */ package org.diirt.datasource.formula; import java.time.Instant; import java.util.Arrays; import java.util.Collection; import java.util.Objects; import org.diirt.util.array.ArrayDouble; import org.diirt.util.text.NumberFormats; import org.diirt.vtype.Alarm; import org.diirt.vtype.AlarmSeverity; import org.diirt.vtype.Display; import org.diirt.vtype.Time; import org.diirt.vtype.VBoolean; import org.diirt.vtype.VDouble; import org.diirt.vtype.VNumber; import org.diirt.vtype.VNumberArray; import org.diirt.vtype.VString; import org.diirt.vtype.VStringArray; import org.diirt.vtype.VType; import org.diirt.vtype.VTypeToString; import org.diirt.vtype.VTypeValueEquals; import org.diirt.vtype.ValueFactory; import static org.diirt.vtype.ValueFactory.*; import org.diirt.vtype.ValueUtil; import org.diirt.vtype.table.Column; import org.hamcrest.Matcher; import static org.hamcrest.Matchers.*; import org.junit.Assert; import static org.junit.Assert.assertThat; /** * * @author carcassi */ public class FunctionTester { private final FormulaFunction function; private boolean convertTypes = true; private FunctionTester(FormulaFunction function) { this.function = function; } public static FunctionTester findByName(FormulaFunctionSet set, String name) { Collection<FormulaFunction> functions = set.findFunctions(name); assertThat("Function '" + name + "' not found.", functions.isEmpty(), equalTo(false)); assertThat("Multiple matches for function '" + name + "'.", functions.size(), equalTo(1)); return new FunctionTester(functions.iterator().next()); } public static FunctionTester findBySignature(FormulaFunctionSet set, String name, Class<?>... argTypes) { Collection<FormulaFunction> functions = set.findFunctions(name); assertThat("Function '" + name + "' not found.", functions.isEmpty(), equalTo(false)); functions = FormulaFunctions.findArgTypeMatch(Arrays.asList(argTypes), functions); assertThat("No matches found for function '" + name + "'.", functions.isEmpty(), equalTo(false)); assertThat("Multiple matches for function '" + name + "'.", functions.size(), equalTo(1)); return new FunctionTester(functions.iterator().next()); } public FunctionTester convertTypes(boolean convertTypes) { this.convertTypes = convertTypes; return this; } public FunctionTester matchReturnValue(Matcher<Object> matcher, Object... args) { if (convertTypes) { args = convertTypes(args); } Object result = function.calculate(Arrays.asList(args)); Assert.assertThat(result, matcher); return this; } public FunctionTester compareReturnValue(Object expected, Object... args) { if (convertTypes) { expected = convertType(expected); args = convertTypes(args); } Object result = function.calculate(Arrays.asList(args)); if (result instanceof VDouble && expected instanceof VDouble) { assertThat("Wrong result for function '" + function.getName() + "(" + Arrays.toString(args) + ")'.", ((VDouble) result).getValue().doubleValue(), closeTo(((VDouble) expected).getValue().doubleValue(), 0.0001)); } else { assertThat( "Wrong result for function '" + function.getName() + "(" + Arrays.toString(args) + ")'. Was (" + result + ") expected (" + expected + ")", compareValues(result, expected), equalTo(true)); } return this; } public static boolean compareValues(Object obj1, Object obj2) { if (Objects.equals(obj1, obj2)) { return true; } if (obj1 instanceof VType && obj2 instanceof VType) { return VTypeValueEquals.valueEquals(obj1, obj2); } else if (obj1 instanceof Column && obj2 instanceof Column) { Column column1 = (Column) obj1; Column column2 = (Column) obj2; return column1.getName().equals(column2.getName()) && column1.isGenerated() == column2.isGenerated() && column1.getType().equals(column2.getType()) && column1.getData(column1.isGenerated() ? 10 : -1).equals(column2.getData(column2.isGenerated() ? 10 : -1)); } return false; } private Object convertType(Object obj) { if (obj instanceof VType) { return obj; } Object converted = ValueFactory.toVType(obj); if (converted != null) { return converted; } return obj; } private Object[] convertTypes(Object... obj) { Object[] result = new Object[obj.length]; for (int i = 0; i < result.length; i++) { result[i] = convertType(obj[i]); } return result; } public FunctionTester compareReturnAlarm(Alarm expected, Object... args) { if (convertTypes) { args = convertTypes(args); } Alarm result = ValueUtil.alarmOf(function.calculate(Arrays.asList(args))); assertThat( "Wrong result for function '" + function.getName() + "(" + Arrays.toString(args) + ")'. Was (" + VTypeToString.alarmToString(result) + ") expected (" + VTypeToString.alarmToString(expected) + ")", VTypeValueEquals.alarmEquals(result, expected), equalTo(true)); return this; } public FunctionTester compareReturnTime(Time expected, Object... args) { if (convertTypes) { args = convertTypes(args); } Time result = ValueUtil.timeOf(function.calculate(Arrays.asList(args))); assertThat( "Wrong result for function '" + function.getName() + "(" + Arrays.toString(args) + ")'. Was (" + VTypeToString.timeToString(result) + ") expected (" + VTypeToString.timeToString(expected) + ")", VTypeValueEquals.timeEquals(result, expected), equalTo(true)); return this; } public FunctionTester highestAlarmReturned() { if (function.isVarArgs() || function.getArgumentTypes().size() > 1) { highestAlarmReturnedMultipleArgs(function); } else { highestAlarmReturnedSingleArg(function); } return this; } private Object createValue(Class<?> clazz, Alarm alarm, Time time, Display display) { if (clazz.equals(VNumber.class)) { return newVNumber(1.0, alarm, time, display); } else if (clazz.equals(VNumberArray.class)) { return newVNumberArray(new ArrayDouble(1.0), alarm, time, display); } else if (clazz.equals(VString.class)) { return newVString("A", alarm, time); } else if (clazz.equals(VStringArray.class)) { return newVStringArray(Arrays.asList("A"), alarm, time); } else if (clazz.equals(VBoolean.class)) { return newVBoolean(true, alarm, time); } else { throw new IllegalArgumentException("Can't create sample argument for class " + clazz); } } private void highestAlarmReturnedSingleArg(FormulaFunction function) { Display display = newDisplay(-5.0, -4.0, -3.0, "m", NumberFormats.toStringFormat(), 3.0, 4.0, 5.0, -5.0, 5.0); Alarm none = alarmNone(); Alarm minor = newAlarm(AlarmSeverity.MINOR, "HIGH"); Alarm major = newAlarm(AlarmSeverity.MAJOR, "LOLO"); compareReturnAlarm(none, createValue(function.getArgumentTypes().get(0), none, timeNow(), display)); compareReturnAlarm(minor, createValue(function.getArgumentTypes().get(0), minor, timeNow(), display)); compareReturnAlarm(major, createValue(function.getArgumentTypes().get(0), major, timeNow(), display)); } private void highestAlarmReturnedMultipleArgs(FormulaFunction function) { Display display = newDisplay(-5.0, -4.0, -3.0, "m", NumberFormats.toStringFormat(), 3.0, 4.0, 5.0, -5.0, 5.0); Object[] args; if (function.isVarArgs()) { args = new Object[function.getArgumentTypes().size() + 1]; } else { args = new Object[function.getArgumentTypes().size()]; } Alarm none = alarmNone(); Alarm minor = newAlarm(AlarmSeverity.MINOR, "HIGH"); Alarm major = newAlarm(AlarmSeverity.MAJOR, "LOLO"); // Prepare arguments with no alarm for (int i = 0; i < function.getArgumentTypes().size(); i++) { args[i] = createValue(function.getArgumentTypes().get(i), none, timeNow(), display); } if (function.isVarArgs()) { args[args.length - 1] = createValue(function.getArgumentTypes().get(args.length - 2), none, timeNow(), display); } compareReturnAlarm(none, args); // Prepare arguments with one minor and everything else none for (int i = 0; i < function.getArgumentTypes().size(); i++) { if (i == args.length - 1) { args[i] = createValue(function.getArgumentTypes().get(i), none, timeNow(), display); } else { args[i] = createValue(function.getArgumentTypes().get(i), minor, timeNow(), display); } } if (function.isVarArgs()) { args[args.length - 1] = createValue(function.getArgumentTypes().get(args.length - 2), none, timeNow(), display); } compareReturnAlarm(minor, args); // Prepare arguments with one minor and everything else major for (int i = 0; i < function.getArgumentTypes().size(); i++) { if (i == args.length - 1) { args[i] = createValue(function.getArgumentTypes().get(i), major, timeNow(), display); } else { args[i] = createValue(function.getArgumentTypes().get(i), minor, timeNow(), display); } } if (function.isVarArgs()) { args[args.length - 1] = createValue(function.getArgumentTypes().get(args.length - 2), major, timeNow(), display); } compareReturnAlarm(major, args); } public FunctionTester latestTimeReturned() { if (function.isVarArgs() || function.getArgumentTypes().size() > 1) { latestTimeReturnedMultipleArgs(function); } else { latestTimeReturnedSingleArg(function); } return this; } private void latestTimeReturnedSingleArg(FormulaFunction function) { Display display = newDisplay(-5.0, -4.0, -3.0, "m", NumberFormats.toStringFormat(), 3.0, 4.0, 5.0, -5.0, 5.0); Object[] args; Time time1 = newTime(Instant.ofEpochSecond(12340000, 0)); Time time2 = newTime(Instant.ofEpochSecond(12350000, 0)); compareReturnTime(time1, createValue(function.getArgumentTypes().get(0), alarmNone(), time1, display)); compareReturnTime(time2, createValue(function.getArgumentTypes().get(0), alarmNone(), time2, display)); } private void latestTimeReturnedMultipleArgs(FormulaFunction function) { Display display = newDisplay(-5.0, -4.0, -3.0, "m", NumberFormats.toStringFormat(), 3.0, 4.0, 5.0, -5.0, 5.0); Object[] args; if (function.isVarArgs()) { args = new Object[function.getArgumentTypes().size() + 1]; } else { args = new Object[function.getArgumentTypes().size()]; } Time time1 = newTime(Instant.ofEpochSecond(12340000, 0)); Time time2 = newTime(Instant.ofEpochSecond(12350000, 0)); // Prepare arguments with all time1 for (int i = 0; i < function.getArgumentTypes().size(); i++) { args[i] = createValue(function.getArgumentTypes().get(i), alarmNone(), time1, display); } if (function.isVarArgs()) { args[args.length - 1] = createValue(function.getArgumentTypes().get(args.length - 2), alarmNone(), time1, display); } compareReturnTime(time1, args); // Prepare arguments with one time2 and everything else time1 for (int i = 0; i < function.getArgumentTypes().size(); i++) { if (i == args.length - 1) { args[i] = createValue(function.getArgumentTypes().get(i), alarmNone(), time1, display); } else { args[i] = createValue(function.getArgumentTypes().get(i), alarmNone(), time2, display); } } if (function.isVarArgs()) { args[args.length - 1] = createValue(function.getArgumentTypes().get(args.length - 2), alarmNone(), time1, display); } compareReturnTime(time2, args); // Prepare arguments with one minor and everything else major for (int i = 0; i < function.getArgumentTypes().size(); i++) { if (i == args.length - 1) { args[i] = createValue(function.getArgumentTypes().get(i), alarmNone(), time2, display); } else { args[i] = createValue(function.getArgumentTypes().get(i), alarmNone(), time1, display); } } if (function.isVarArgs()) { args[args.length - 1] = createValue(function.getArgumentTypes().get(args.length - 2), alarmNone(), time2, display); } compareReturnTime(time2, args); } }