/*
* Copyright 2014-2016 Cel Skeggs
*
* This file is part of the CCRE, the Common Chicken Runtime Engine.
*
* The CCRE is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* The CCRE is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the CCRE. If not, see <http://www.gnu.org/licenses/>.
*/
package ccre.testing;
import static org.junit.Assert.*;
import ccre.channel.FloatOutput;
/**
* A class used to ensure that an output is only set to the correct value and in
* the correct interval of time - and not anywhen else.
*
* Set {@link #valueExpected} to the expected value and {@link #ifExpected} to
* true, let the code run that should update the value, and then call
* {@link #check()}.
*
* If a value is received when ifExpected is not set, an exception will be
* thrown. Note that this also happens if a value is received more than once,
* because ifExpected is cleared after the first value written.
*
* check() will fail if ifExpected is still true, because that means that means
* that the value was never received.
*
* @author skeggsc
*/
public class CountingFloatOutput implements FloatOutput {
/**
* The value expected to be received.
*/
public float valueExpected;
/**
* Whether or not we're still expected a value to be received.
*/
public boolean ifExpected;
/**
* The maximum delta between the gotten value and the expected value.
*/
public float maxDelta = 0;
private final boolean allowExtras;
private boolean anyUnexpected;
/**
* Creates a new CountingFloatOutput that doesn't allow values to be sent
* multiple times.
*/
public CountingFloatOutput() {
this(false);
}
/**
* Creates a new CountingFloatOutput that might or might not allow values to
* be sent multiple times.
*
* @param allowExtras if multiple set events in a row should be tolerated,
* rather than throw an exception.
*/
public CountingFloatOutput(boolean allowExtras) {
this.allowExtras = allowExtras;
}
@Override
public synchronized void set(float value) {
if (!ifExpected && !allowExtras) {
anyUnexpected = true;
fail("Unexpected set of " + value + "!");
}
boolean correct = false;
if (Float.isNaN(valueExpected)) {
correct = Float.isNaN(value);
} else if (!Float.isNaN(value)) {
if (maxDelta != 0) {
correct = value == valueExpected || Math.abs(value - valueExpected) <= maxDelta;
} else {
correct = Float.floatToIntBits(value) == Float.floatToIntBits(valueExpected);
}
}
if (!correct) {
anyUnexpected = true;
fail("Incorrect set: " + value + " instead of " + valueExpected);
}
ifExpected = false;
}
/**
* Ensure that the correct value has been received since the last time that
* ifExpected was set to true.
*
* @throws RuntimeException if a write did not occur.
*/
public void check() throws RuntimeException {
assertFalse("Already failed earlier!", anyUnexpected);
if (ifExpected) {
anyUnexpected = true;
fail("Did not get expected set of " + valueExpected + "!");
}
}
}