package org.opentrafficsim.base.parameters;
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.assertTrue;
import static org.junit.Assert.fail;
import java.lang.reflect.InvocationTargetException;
import org.djunits.unit.AccelerationUnit;
import org.djunits.unit.LengthUnit;
import org.djunits.unit.LinearDensityUnit;
import org.djunits.unit.SpeedUnit;
import org.djunits.value.vdouble.scalar.Acceleration;
import org.djunits.value.vdouble.scalar.Duration;
import org.djunits.value.vdouble.scalar.Frequency;
import org.djunits.value.vdouble.scalar.Length;
import org.djunits.value.vdouble.scalar.LinearDensity;
import org.djunits.value.vdouble.scalar.Speed;
import org.djutils.exceptions.Throw;
import org.junit.Test;
import org.opentrafficsim.base.parameters.constraint.Constraint;
import org.opentrafficsim.base.parameters.constraint.ConstraintInterface;
/**
*
* Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License.
*
* $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
* initial version 7 apr. 2016
* @author Alexander Verbraeck
* @author Peter Knoppers
* @author Wouter Schakel
*/
public class ParametersTest implements ConstraintInterface
{
/**
* Defaults tests.
*/
@Test
public final void defaultsTest()
{
Parameters params = new ParameterSet().setDefaultParameters(ParameterTypes.class);
try
{
assertTrue("Default value is not correctly set.",
params.getParameter(ParameterTypes.A).equals(ParameterTypes.A.getDefaultValue()));
}
catch (ParameterException exception)
{
fail("Default value is not set at all.");
}
}
/**
* Constructor tests.
*/
@Test
public final void constructorTest()
{
// Check Parameters constructor
ParameterSet params = new ParameterSet();
assertNotNull("Default constructor should not return null.", params);
if (!params.getParameters().isEmpty())
{
fail("Constructed Parameters has a non-empty parameter map.");
}
// Check ParameterType construction (id, description, class, defaultValue)
Length defaultValue = new Length(1.0, LengthUnit.SI);
ParameterTypeLength a = new ParameterTypeLength("a", "along", defaultValue);
assertEquals("Parameter type id not properly set.", "a", a.getId());
assertEquals("Parameter type description not properly set.", "along", a.getDescription());
assertTrue("has a default value", a.hasDefaultValue());
try
{
assertEquals("Parameter type default value not properly set.", defaultValue, a.getDefaultValue());
}
catch (ParameterException exception)
{
fail("Parameter type default value given in constructor was not set.");
}
// Check ParameterType construction (id, description, class)
ParameterTypeLength b = new ParameterTypeLength("b", "blong");
assertEquals("Parameter type id not properly set.", "b", b.getId());
assertEquals("Parameter type description not properly set.", "blong", b.getDescription());
assertFalse("does not have a default value", b.hasDefaultValue());
try
{
b.getDefaultValue();
fail("Parameter type returned a default value, while none was provided.");
}
catch (ParameterException pe)
{
// ignore expected exception
}
assertTrue("toString returns something with ParameterType in it", b.toString().contains("ParameterType"));
}
/**
* Parameter value default range tests.
*/
@Test
public final void defaultRangeTest()
{
// Check default values that should work
checkDefaultValue(1.0, POSITIVE, false);
checkDefaultValue(-1.0, NEGATIVE, false);
checkDefaultValue(1.0, POSITIVEZERO, false);
checkDefaultValue(0.0, POSITIVEZERO, false);
checkDefaultValue(-1.0, NEGATIVEZERO, false);
checkDefaultValue(-0.0, NEGATIVEZERO, false);
checkDefaultValue(-1.0, NONZERO, false);
checkDefaultValue(1.0, NONZERO, false);
checkDefaultValue(0.0, UNITINTERVAL, false);
checkDefaultValue(0.5, UNITINTERVAL, false);
checkDefaultValue(1.0, UNITINTERVAL, false);
checkDefaultValue(1.0, ATLEASTONE, false);
// Check default values that should not work
checkDefaultValue(-1.0, POSITIVE, true);
checkDefaultValue(0.0, POSITIVE, true);
checkDefaultValue(1.0, NEGATIVE, true);
checkDefaultValue(0.0, NEGATIVE, true);
checkDefaultValue(-1.0, POSITIVEZERO, true);
checkDefaultValue(1.0, NEGATIVEZERO, true);
checkDefaultValue(0.0, NONZERO, true);
checkDefaultValue(-0.01, UNITINTERVAL, true);
checkDefaultValue(1.01, UNITINTERVAL, true);
checkDefaultValue(0.99, ATLEASTONE, true);
// Check set values that should work
checkSetValue(1.0, POSITIVE, false);
checkSetValue(-1.0, NEGATIVE, false);
checkSetValue(1.0, POSITIVEZERO, false);
checkSetValue(0.0, POSITIVEZERO, false);
checkSetValue(-1.0, NEGATIVEZERO, false);
checkSetValue(-0.0, NEGATIVEZERO, false);
checkSetValue(-1.0, NONZERO, false);
checkSetValue(1.0, NONZERO, false);
checkSetValue(0.0, UNITINTERVAL, false);
checkSetValue(0.5, UNITINTERVAL, false);
checkSetValue(1.0, UNITINTERVAL, false);
checkSetValue(1.0, ATLEASTONE, false);
// Check set values that should not work
checkSetValue(-1.0, POSITIVE, true);
checkSetValue(0.0, POSITIVE, true);
checkSetValue(1.0, NEGATIVE, true);
checkSetValue(0.0, NEGATIVE, true);
checkSetValue(-1.0, POSITIVEZERO, true);
checkSetValue(1.0, NEGATIVEZERO, true);
checkSetValue(0.0, NONZERO, true);
checkSetValue(-0.01, UNITINTERVAL, true);
checkSetValue(1.01, UNITINTERVAL, true);
checkSetValue(0.99, ATLEASTONE, true);
}
/**
* Checks a default value.
* @param value Value to check.
* @param constraint Constraint to perform.
* @param shouldFail Whether the check should fail.
*/
private void checkDefaultValue(final double value, final Constraint constraint, final boolean shouldFail)
{
try
{
new ParameterTypeAcceleration("a", "along", new Acceleration(value, AccelerationUnit.SI), constraint);
if (shouldFail)
{
fail("Default value " + value + " fails default " + constraint + " constraint.");
}
}
catch (RuntimeException re)
{
if (!shouldFail)
{
re.printStackTrace();
fail("Default value " + value + " does not fail default " + constraint + " constraint.");
}
}
}
/**
* Checks a set value.
* @param value Value to check.
* @param constraint Constraint to perform.
* @param shouldFail Whether the check should fail.
*/
private void checkSetValue(final double value, final Constraint constraint, final boolean shouldFail)
{
try
{
Parameters params = new ParameterSet();
ParameterTypeAcceleration a = new ParameterTypeAcceleration("a", "along", constraint);
params.setParameter(a, new Acceleration(value, AccelerationUnit.SI));
if (shouldFail)
{
fail("Set value " + value + " fails default " + constraint + " constraint.");
}
}
catch (ParameterException pe)
{
if (!shouldFail)
{
fail("Set value " + value + " does not fail default " + constraint + " constraint.");
}
}
}
/**
* Parameter value custom constraint tests.
*/
@Test
public final void customConstraintTest()
{
// Check values that should work
Parameters params = new ParameterSet();
try
{
// requirement: v1 < v2
params.setParameter(v1, new Speed(3.0, SpeedUnit.KM_PER_HOUR));
params.setParameter(v2, new Speed(4.0, SpeedUnit.KM_PER_HOUR));
params.setParameter(v1, new Speed(2.0, SpeedUnit.KM_PER_HOUR));
params.setParameter(v2, new Speed(5.0, SpeedUnit.KM_PER_HOUR));
}
catch (ParameterException pe)
{
fail("Custom check of set parameter value with value of other parameter fails for correct values.");
}
// Check values that should not work, set v1 first
params = new ParameterSet();
try
{
// requirement: v1 < v2
params.setParameter(v1, new Speed(3.0, SpeedUnit.KM_PER_HOUR));
params.setParameter(v2, new Speed(2.0, SpeedUnit.KM_PER_HOUR));
fail("Custom check of set parameter value with value of other parameter does not fail for wrong values.");
}
catch (ParameterException pe)
{
// Should fail
}
// Check values that should not work, set v2 first
params = new ParameterSet();
try
{
// requirement: v1 < v2
params.setParameter(v2, new Speed(2.0, SpeedUnit.KM_PER_HOUR));
params.setParameter(v1, new Speed(3.0, SpeedUnit.KM_PER_HOUR));
fail("Custom check of set parameter value with value of other parameter does not fail for wrong values.");
}
catch (ParameterException pe)
{
// Should fail
}
}
/** Helper parameter type for custom constraint checks. */
private static ParameterTypeSpeed v1 = new ParameterTypeSpeed("v1", "v1long")
{
/** */
private static final long serialVersionUID = 20160400L;
@SuppressWarnings("synthetic-access")
@Override
public void check(final Speed v, final Parameters paramsa) throws ParameterException
{
Speed u2 = paramsa.getParameterOrNull(v2);
Throw.when(u2 != null && v.si > u2.si, ParameterException.class, "Value of v1 is larger than value of v2.");
}
};
/** Helper parameter type for custom constraint checks. */
private static ParameterTypeSpeed v2 = new ParameterTypeSpeed("v2", "v2long")
{
/** */
private static final long serialVersionUID = 20160400L;
@SuppressWarnings("synthetic-access")
@Override
public void check(final Speed v, final Parameters paramsa) throws ParameterException
{
Speed u1 = paramsa.getParameterOrNull(v1);
Throw.when(u1 != null && v.si < u1.si, ParameterException.class, "Value of v2 is smaller than value of v1.");
}
};
/**
* Tests the set/reset mechanism.
* @throws ParameterException Should not be thrown, is for untested methods (in this test) that throw the exception.
*/
@SuppressWarnings("cast")
@Test
public final void setResetTest() throws ParameterException
{
ParameterTypeInteger a = new ParameterTypeInteger("a", "along", 0);
// exception reset without set: no value -> reset
Parameters params = new ParameterSet();
try
{
params.resetParameter(a);
fail("Reset of parameter that was never set does not fail.");
}
catch (ParameterException pe)
{
// Should fail
}
// exception for get after reset to no value: no value -> set -> reset -> get
params = new ParameterSet();
params.setParameterResettable(a, 1);
params.resetParameter(a);
try
{
params.getParameter(a);
fail("Get of parameter that was not given before set and reset, does not fail.");
}
catch (ParameterException pe)
{
// Should fail
}
// exception for multiple resets: no value -> set -> reset -> reset
params = new ParameterSet();
params.setParameterResettable(a, 1);
params.resetParameter(a);
try
{
params.resetParameter(a);
fail("Second reset without intermediate set does not fail when first reset was to no value.");
}
catch (ParameterException pe)
{
// Should fail
}
// exception for multiple resets: set -> set -> reset -> reset
params = new ParameterSet();
params.setParameterResettable(a, 1);
params.setParameterResettable(a, 2);
params.resetParameter(a);
try
{
params.resetParameter(a);
fail("Second reset without intermediate set does not fail when first reset was to a value.");
}
catch (ParameterException pe)
{
// Should fail
}
// no exception: set -> reset -> set -> reset
params = new ParameterSet();
params.setParameterResettable(a, 1);
params.resetParameter(a);
params.setParameterResettable(a, 2);
try
{
params.resetParameter(a);
}
catch (ParameterException pe)
{
fail("Reset fails after set, with reset before that set.");
// Should not fail
}
// same value: set(1) -> set(2) -> reset -> get(1?)
params = new ParameterSet();
params.setParameterResettable(a, 1);
params.setParameterResettable(a, 2);
params.resetParameter(a);
assertEquals("Value after reset should be the same as before last set.", 1.0, (double) params.getParameter(a), 0.0);
// no reset after (none resettable) set
params = new ParameterSet();
params.setParameter(a, 1);
try
{
params.resetParameter(a);
fail("Reset should fail after regular set.");
}
catch (ParameterException pe)
{
// should fail
}
// no reset after (none resettable) set, even with resettable set before
params = new ParameterSet();
params.setParameterResettable(a, 1);
params.setParameter(a, 2);
try
{
params.resetParameter(a);
fail("Reset should fail after regular set, dispite resettable set before.");
}
catch (ParameterException pe)
{
// should fail
}
// same value: regular set(1) -> set(2) -> reset -> get(1?)
params = new ParameterSet();
params.setParameter(a, 1);
params.setParameterResettable(a, 2);
params.resetParameter(a);
assertEquals("Value after reset should be the same as before last set.", 1.0, (double) params.getParameter(a), 0.0);
// If null value is ever going to be allowed, use these tests to check proper set/reset.
// // check null is not the same as 'no value': no value -> set(null) -> reset -> get
// // (null setting does not work on primitive data types, parameter type 'a' cannot be used)
// ParameterTypeFrequency b = new ParameterTypeFrequency("b", "blong");
// bc = new BehavioralCharacteristics();
// params.setParameter(b, null);
// bc.resetParameter(b);
// try
// {
// // as there was no value before the null set, this should fail
// params.getParameter(b);
// fail("Reset after setting of null is not properly handled.");
// }
// catch (ParameterException pe)
// {
// // should fail
// }
//
// // check null is not the same as no value: no value -> set(null) -> set(value) -> reset -> get(null?)
// params.setParameter(b, null);
// params.setParameter(b, new Frequency(12, FrequencyUnit.SI));
// bc.resetParameter(b);
// // assertEquals() with null cannot be used (defaults into deprecated array method)
// if (params.getParameter(b) != null)
// {
// fail("Value after reset is not equal to null, which it was before the last set.");
// }
}
/**
* Tests equalizations.
* @throws ParameterException Should not be thrown, is for untested methods (in this test) that throw the exception.
*/
@Test
public final void equalizeTest() throws ParameterException
{
// equal double values from different parameters should be equal
ParameterTypeDouble a1 = new ParameterTypeDouble("a", "along", 0.0);
ParameterTypeDouble a2 = new ParameterTypeDouble("a", "along", 0.0);
Parameters params1 = new ParameterSet();
Parameters params2 = new ParameterSet();
params1.setParameter(a1, 4.0);
params2.setParameter(a2, 4.0);
assertEquals("Equal double values from different parameter types should be equal.", params1.getParameter(a1),
params2.getParameter(a2), 0.0);
// equal DoubleScalar.Rel values should be equal from different characteristic sets
ParameterTypeLinearDensity b1 = new ParameterTypeLinearDensity("b", "blong");
ParameterTypeLinearDensity b2 = new ParameterTypeLinearDensity("b", "blong");
params1.setParameter(b1, new LinearDensity(4.0, LinearDensityUnit.SI));
params2.setParameter(b2, new LinearDensity(4.0, LinearDensityUnit.SI));
assertEquals(
"Equal DoubleScalar.Rel values from different parameter types and different characteristics should be equal.",
params1.getParameter(b1), params2.getParameter(b2));
// equal DoubleScalar.Rel values should be equal from the same characteristic set
params1.setParameter(b2, new LinearDensity(4.0, LinearDensityUnit.SI));
assertEquals(
"Equal DoubleScalar.Rel values from different parameter types and the same characteristics should be equal.",
params1.getParameter(b1), params1.getParameter(b2));
// values of parameter types with different value classes are not equal
params1.setParameter(a1, 4.0);
params1.setParameter(b1, new LinearDensity(4.0, LinearDensityUnit.SI));
assertNotEquals("Values of different parameter type value classes should not be equal.", params1.getParameter(a1),
params1.getParameter(b1));
}
/**
* Test that exceptions are thrown when trying to use null values.
*/
@Test
public final void testNullNotAllowed()
{
// null default value
try
{
new ParameterTypeSpeed("v", "vlong", null, POSITIVE);
fail("Setting a default value of 'null' on ParameterType did not fail.");
}
catch (RuntimeException re)
{
// should fail
}
try
{
new ParameterTypeSpeed("v", "vlong", null, POSITIVE);
fail("Setting a default value of 'null' on ParameterTypeSpeed did not fail.");
}
catch (RuntimeException re)
{
// should fail
}
// set null value
ParameterTypeSpeed v = new ParameterTypeSpeed("v", "vlong");
Parameters params = new ParameterSet();
try
{
params.setParameter(v, null);
fail("Setting a value of 'null' did not fail.");
}
catch (ParameterException pe)
{
// should fail
}
}
/**
* Checks whether default values are properly set, or not in case not given.
* @throws SecurityException Reflection.
* @throws NoSuchMethodException Reflection.
* @throws InvocationTargetException Reflection.
* @throws IllegalArgumentException Reflection.
* @throws IllegalAccessException Reflection.
* @throws InstantiationException Reflection.
*/
@Test
public final void checkDefaultValues() throws InstantiationException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, NoSuchMethodException, SecurityException
{
// @formatter:off
checkDefaultValuesPerClass(ParameterTypeSpeed.class, Speed.instantiateSI(3));
checkDefaultValuesPerClass(ParameterTypeAcceleration.class, Acceleration.instantiateSI(3));
checkDefaultValuesPerClass(ParameterTypeLength.class, Length.instantiateSI(3));
checkDefaultValuesPerClass(ParameterTypeFrequency.class, Frequency.instantiateSI(3));
checkDefaultValuesPerClass(ParameterTypeDuration.class, Duration.instantiateSI(3));
checkDefaultValuesPerClass(ParameterTypeLinearDensity.class, LinearDensity.instantiateSI(3));
checkDefaultValuesPerClass(ParameterTypeBoolean.class, new Boolean(false));
checkDefaultValuesPerClass(ParameterTypeDouble.class, new Double(3));
checkDefaultValuesPerClass(ParameterTypeInteger.class, new Integer(3));
// @formatter:on
}
/**
* @param clazz AbstractParameterType subclass to test.
* @param defaultValue Default value to test with.
* @param Subclass of AbstractParameterType.
* @throws SecurityException Reflection.
* @throws NoSuchMethodException Reflection.
* @throws InvocationTargetException Reflection.
* @throws IllegalArgumentException Reflection.
* @throws IllegalAccessException Reflection.
* @throws InstantiationException Reflection.
*/
private > void checkDefaultValuesPerClass(final Class clazz, final Object defaultValue)
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
NoSuchMethodException, SecurityException
{
// none set
ParameterType> ld;
if (clazz.equals(ParameterTypeNumeric.class))
{
ld = clazz.getDeclaredConstructor(String.class, String.class, Class.class).newInstance("v", "vcong",
getClass(defaultValue));
}
else
{
ld = clazz.getDeclaredConstructor(String.class, String.class).newInstance("v", "vcong");
}
try
{
ld.getDefaultValue();
fail("Could obtain a default value that was not set.");
}
catch (ParameterException pe)
{
// should fail
}
String toStringResult = ld.toString();
// System.out.println("tostring yields \"" + toStringResult + "\"");
// System.out.println("clazz is " + clazz.getSimpleName());
assertTrue("toString returns something with the class name in it ", toStringResult.contains(clazz.getSimpleName()));
// none set, including default check
if (!clazz.equals(ParameterTypeBoolean.class)) // boolean has no checks
{
if (clazz.equals(ParameterTypeNumeric.class))
{
ld = clazz.getDeclaredConstructor(String.class, String.class, Class.class, Constraint.class).newInstance("v",
"vcong", getClass(defaultValue), POSITIVE);
}
else
{
ld = clazz.getDeclaredConstructor(String.class, String.class, Constraint.class).newInstance("v", "vcong",
POSITIVE);
}
try
{
ld.getDefaultValue();
fail("Could obtain a default value that was not set.");
}
catch (ParameterException pe)
{
// should fail
}
}
// value set
if (clazz.equals(ParameterTypeNumeric.class))
{
ld = clazz.getDeclaredConstructor(String.class, String.class, Class.class, Number.class).newInstance("v", "vcong",
getClass(defaultValue), defaultValue);
}
else
{
ld = clazz.getDeclaredConstructor(String.class, String.class, getClass(defaultValue)).newInstance("v", "vcong",
defaultValue);
}
try
{
ld.getDefaultValue();
}
catch (ParameterException pe)
{
fail("Could not obtain a default value that was set.");
}
// value set, including default check
if (!clazz.equals(ParameterTypeBoolean.class)) // boolean has no checks
{
if (clazz.equals(ParameterTypeNumeric.class))
{
ld = clazz.getDeclaredConstructor(String.class, String.class, Class.class, Number.class, Constraint.class)
.newInstance("v", "vcong", getClass(defaultValue), defaultValue, POSITIVE);
}
else
{
ld = clazz.getDeclaredConstructor(String.class, String.class, defaultValue.getClass(), Constraint.class)
.newInstance("v", "vcong", defaultValue, POSITIVE);
}
try
{
ld.getDefaultValue();
}
catch (ParameterException pe)
{
fail("Could not obtain a default value that was set.");
}
}
}
/**
* Returns the class of given default value for reflection.
* @param defaultValue Default value.
* @return Class of given default value for reflection.
*/
private Class extends Object> getClass(final Object defaultValue)
{
if (defaultValue instanceof Boolean)
{
return Boolean.TYPE;
}
else if (defaultValue instanceof Double)
{
return Double.TYPE;
}
else if (defaultValue instanceof Integer)
{
return Integer.TYPE;
}
return defaultValue.getClass();
}
/**
* Tests the merging of parameter sets using setAll.
* @throws ParameterException parameter exception
*/
@Test
public final void mergeTest() throws ParameterException
{
ParameterSet paramsA = new ParameterSet();
paramsA.setDefaultParameter(ParameterTypes.A);
ParameterSet paramsB = new ParameterSet();
paramsB.setDefaultParameter(ParameterTypes.B);
paramsB.setAllIn(paramsA);
assertTrue("When merging set B with set A, set A should contain the parameters of set B.",
paramsA.contains(ParameterTypes.B));
assertTrue("When merging set B with set A, parameter values should be equal.",
paramsA.getParameter(ParameterTypes.B).eq(paramsB.getParameter(ParameterTypes.B)));
assertFalse("When merging set B with set A, set B should not contain the parameters of set A.",
paramsB.contains(ParameterTypes.A));
}
}