/*
* Copyright 2015-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.channel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.NoSuchElementException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ccre.log.LogLevel;
import ccre.log.VerifyingLogger;
import ccre.testing.CountingBooleanOutput;
import ccre.testing.CountingEventOutput;
import ccre.util.Values;
@SuppressWarnings("javadoc")
public class BooleanInputTest {
private static final String ERROR_STRING = "Purposeful failure.";
private CountingEventOutput expected, expected2;
private EventCell es = null;
private BooleanInput bi = null;
private boolean result;// temporary variable
@Before
public void setUp() throws Exception {
expected = new CountingEventOutput();
expected2 = new CountingEventOutput();
es = new EventCell();
bi = new BooleanInput() {
@Override
public CancelOutput onUpdate(EventOutput notify) {
return es.onUpdate(notify);
}
@Override
public boolean get() {
return result;
}
};
VerifyingLogger.begin();
}
@After
public void tearDown() throws Exception {
VerifyingLogger.checkAndEnd();
bi = null;
es = null;
expected = null;
expected2 = null;
}
@Test
public void testAlwaysBoolean() {
assertFalse(BooleanInput.alwaysFalse.get());
assertTrue(BooleanInput.alwaysTrue.get());
CountingBooleanOutput cbo = new CountingBooleanOutput();
cbo.ifExpected = true;
cbo.valueExpected = false;
BooleanInput.alwaysFalse.send(cbo);
cbo.check();
cbo = new CountingBooleanOutput();
cbo.ifExpected = true;
cbo.valueExpected = true;
BooleanInput.alwaysTrue.send(cbo);
cbo.check();
}
@Test
public void testAlways() {
assertEquals(BooleanInput.alwaysFalse, BooleanInput.always(false));
assertEquals(BooleanInput.alwaysTrue, BooleanInput.always(true));
}
private boolean gotProperly;
@Test
public void testSend() {
final CountingBooleanOutput cbo = new CountingBooleanOutput();
gotProperly = false;
CancelOutput cex = expected::event;
bi = new BooleanInput() {
@Override
public CancelOutput onUpdate(EventOutput notify) {
assertFalse(gotProperly);
cbo.check();// the earlier setup from outside this
for (boolean b : new boolean[] { false, true }) {
result = b;
cbo.ifExpected = true;
cbo.valueExpected = b;
notify.event();
cbo.check();
}
gotProperly = true;
return cex;
}
@Override
public boolean get() {
return result;
}
};
for (boolean b : new boolean[] { false, true }) {
result = b;
assertEquals(b, bi.get());
}
for (boolean initial : new boolean[] { false, true }) {
cbo.ifExpected = true;
cbo.valueExpected = result = initial;
assertFalse(gotProperly);
assertEquals(cex, bi.send(cbo));
assertTrue(gotProperly);
gotProperly = false;
cbo.check(); // the real check is in onUpdate above
}
}
@Test(expected = NullPointerException.class)
public void testSendNull() {
bi.send(null);
}
@Test
public void testNotRule1() {
bi = new BooleanInput() {
@Override
public CancelOutput onUpdate(EventOutput notify) {
fail();
return null;
}
@Override
public boolean get() {
return result;
}
};
BooleanInput bi2 = bi.not();
// no update fired because not() is required to work without that.
result = false;
assertTrue(bi2.get());
result = true;
assertFalse(bi2.get());
}
@Test
public void testNotRule2() {
CancelOutput cex2 = expected2::event;
bi = new BooleanInput() {
@Override
public CancelOutput onUpdate(EventOutput notify) {
assertFalse(gotProperly);
assertEquals(expected, notify);
gotProperly = true;
return cex2;
}
@Override
public boolean get() {
fail();
return false;
}
};
BooleanInput bi2 = bi.not();
gotProperly = false;
bi2.onUpdate(expected);
assertTrue(gotProperly);
gotProperly = false;
assertEquals(cex2, bi2.onUpdate(expected));
assertTrue(gotProperly);
}
@Test
public void testNotNot() {
bi = new BooleanInput() {
@Override
public CancelOutput onUpdate(EventOutput notify) {
assertFalse(gotProperly);
assertEquals(expected, notify);
gotProperly = true;
return expected2::event;
}
@Override
public boolean get() {
fail();
return false;
}
};
assertEquals(bi, bi.not().not());
}
@Test
public void testAnd() {
BooleanCell alpha = new BooleanCell(), beta = new BooleanCell();
BooleanCell abi = new BooleanCell(), bai = new BooleanCell();
BooleanInput ab = alpha.and(beta), ba = beta.and(alpha);
ab.send(abi);
ba.send(bai);
for (boolean a : Values.interestingBooleans) {
for (boolean b : Values.interestingBooleans) {
alpha.set(a);
beta.set(b);
assertEquals(a && b, ab.get());
assertEquals(a && b, ba.get());
assertEquals(a && b, abi.get());
assertEquals(a && b, bai.get());
}
}
}
@Test(expected = NullPointerException.class)
public void testAndNull() {
bi.and(null);
}
@Test
public void testAndNot() {
BooleanCell alpha = new BooleanCell(), beta = new BooleanCell();
BooleanCell abi = new BooleanCell(), bai = new BooleanCell();
BooleanInput ab = alpha.andNot(beta), ba = beta.andNot(alpha);
ab.send(abi);
ba.send(bai);
for (boolean a : Values.interestingBooleans) {
for (boolean b : Values.interestingBooleans) {
alpha.set(a);
beta.set(b);
assertEquals(a && !b, ab.get());
assertEquals(!a && b, ba.get());
assertEquals(a && !b, abi.get());
assertEquals(!a && b, bai.get());
}
}
}
@Test(expected = NullPointerException.class)
public void testAndNotNull() {
bi.andNot(null);
}
@Test
public void testXor() {
BooleanCell alpha = new BooleanCell(), beta = new BooleanCell();
BooleanCell abi = new BooleanCell(), bai = new BooleanCell();
BooleanInput ab = alpha.xor(beta), ba = beta.xor(alpha);
ab.send(abi);
ba.send(bai);
for (boolean a : Values.interestingBooleans) {
for (boolean b : Values.interestingBooleans) {
alpha.set(a);
beta.set(b);
assertEquals(a ^ b, ab.get());
assertEquals(a ^ b, ba.get());
assertEquals(a ^ b, abi.get());
assertEquals(a ^ b, bai.get());
}
}
}
@Test(expected = NullPointerException.class)
public void testXorNull() {
bi.xor(null);
}
@Test
public void testOr() {
BooleanCell alpha = new BooleanCell(), beta = new BooleanCell();
BooleanCell abi = new BooleanCell(), bai = new BooleanCell();
BooleanInput ab = alpha.or(beta), ba = beta.or(alpha);
ab.send(abi);
ba.send(bai);
for (boolean a : Values.interestingBooleans) {
for (boolean b : Values.interestingBooleans) {
alpha.set(a);
beta.set(b);
assertEquals(a || b, ab.get());
assertEquals(a || b, ba.get());
assertEquals(a || b, abi.get());
assertEquals(a || b, bai.get());
}
}
}
@Test(expected = NullPointerException.class)
public void testOrNull() {
bi.or(null);
}
@Test
public void testOrNot() {
BooleanCell alpha = new BooleanCell(), beta = new BooleanCell();
BooleanCell abi = new BooleanCell(), bai = new BooleanCell();
BooleanInput ab = alpha.orNot(beta), ba = beta.orNot(alpha);
ab.send(abi);
ba.send(bai);
for (boolean a : Values.interestingBooleans) {
for (boolean b : Values.interestingBooleans) {
alpha.set(a);
beta.set(b);
assertEquals(a || !b, ab.get());
assertEquals(!a || b, ba.get());
assertEquals(a || !b, abi.get());
assertEquals(!a || b, bai.get());
}
}
}
@Test(expected = NullPointerException.class)
public void testOrNotNull() {
bi.orNot(null);
}
@Test
public void testOnPress() {
for (boolean b : new boolean[] { false, true }) {
BooleanCell a = new BooleanCell(b);
CountingEventOutput ceo = new CountingEventOutput();
a.onPress(ceo);
for (int i = 0; i < 5; i++) {
a.set(false);
ceo.ifExpected = true;
a.set(true);
ceo.check();
}
}
}
@Test
public void testOnPressNoRepeat() {
CountingEventOutput ceo = new CountingEventOutput();
bi.onPress(ceo);
for (int i = 0; i < 10; i++) {
result = true;
ceo.ifExpected = true;
es.event();
ceo.check();
result = false;
ceo.check();
result = true;
ceo.ifExpected = true;
es.event();
ceo.check();
result = false;
es.event();
ceo.check();
}
}
@Test
public void testOnRelease() {
for (boolean b : new boolean[] { false, true }) {
BooleanCell a = new BooleanCell(b);
CountingEventOutput ceo = new CountingEventOutput();
a.onRelease(ceo);
for (int i = 0; i < 5; i++) {
a.set(true);
ceo.ifExpected = true;
a.set(false);
ceo.check();
}
}
}
@Test
public void testOnReleaseNoRepeat() {
CountingEventOutput ceo = new CountingEventOutput();
bi.onRelease(ceo);
for (int i = 0; i < 10; i++) {
result = false;
ceo.ifExpected = true;
es.event();
ceo.check();
result = true;
ceo.check();
result = false;
ceo.ifExpected = true;
es.event();
ceo.check();
result = true;
es.event();
ceo.check();
}
}
@Test
public void testOnChange() {
CountingEventOutput ceo = new CountingEventOutput();
bi.onChange(ceo);
for (int i = 0; i < 10; i++) {
result = true;
ceo.ifExpected = true;
es.event();
ceo.check();
result = false;
ceo.ifExpected = true;
es.event();
ceo.check();
}
}
@Test
public void testFilterUpdates() {
for (boolean not : new boolean[] { false, true }) {
BooleanCell a = new BooleanCell(), allowDeny = new BooleanCell(true);
BooleanCell out = new BooleanCell();
bi = not ? a.filterUpdatesNot(allowDeny) : a.filterUpdates(allowDeny);
bi.send(out);
boolean expect = false;
for (int i = 0; i < 110; i++) {
allowDeny.set(((i % 19) < 10) ^ not);
a.toggle();
if (allowDeny.get() ^ not) {
expect = a.get();
}
assertEquals(expect, bi.get());
assertEquals(expect, out.get());
}
}
}
@Test(expected = NullPointerException.class)
public void testFilterUpdatesNull() {
bi.filterUpdates(null);
}
@Test(expected = NullPointerException.class)
public void testFilterUpdatesNotNull() {
bi.filterUpdatesNot(null);
}
@Test
public void testToFloatFloatFloat() {
BooleanCell bs = new BooleanCell();
FloatCell out = new FloatCell();
FloatInput fi = bs.toFloat(6.4f, 3.2f);
fi.send(out);
assertEquals(6.4f, fi.get(), 0);
assertEquals(6.4f, out.get(), 0);
for (int i = 0; i < 30; i++) {
bs.toggle();
assertEquals(bs.get() ? 3.2f : 6.4f, fi.get(), 0);
assertEquals(bs.get() ? 3.2f : 6.4f, out.get(), 0);
}
}
@Test
public void testToFloatFloatFloatInput() {
BooleanCell bs = new BooleanCell();
FloatCell out = new FloatCell(), tru = new FloatCell();
FloatInput fi = bs.toFloat(6.4f, tru);
fi.send(out);
assertEquals(6.4f, fi.get(), 0);
assertEquals(6.4f, out.get(), 0);
for (int i = 0; i < 30; i++) {
tru.set(17.3f);
bs.toggle();
assertEquals(bs.get() ? 17.3f : 6.4f, fi.get(), 0);
assertEquals(bs.get() ? 17.3f : 6.4f, out.get(), 0);
for (float f : Values.interestingFloats) {
tru.set(f);
assertEquals(bs.get() ? f : 6.4f, fi.get(), 0);
assertEquals(bs.get() ? f : 6.4f, out.get(), 0);
}
}
}
@Test(expected = NullPointerException.class)
public void testToFloatFloatFloatInputNull() {
bi.toFloat(0, null);
}
@Test
public void testToFloatFloatInputFloat() {
BooleanCell bs = new BooleanCell();
FloatCell out = new FloatCell(), fals = new FloatCell(6.4f);
FloatInput fi = bs.toFloat(fals, 3.2f);
fi.send(out);
assertEquals(6.4f, fi.get(), 0);
assertEquals(6.4f, out.get(), 0);
for (int i = 0; i < 30; i++) {
fals.set(-17.3f);
bs.toggle();
assertEquals(bs.get() ? 3.2f : -17.3f, fi.get(), 0);
assertEquals(bs.get() ? 3.2f : -17.3f, out.get(), 0);
for (float f : Values.interestingFloats) {
fals.set(f);
assertEquals(bs.get() ? 3.2f : f, fi.get(), 0);
assertEquals(bs.get() ? 3.2f : f, out.get(), 0);
}
}
}
@Test(expected = NullPointerException.class)
public void testToFloatFloatInputFloatNull() {
bi.toFloat(null, 0);
}
@Test
public void testToFloatFloatInputFloatInput() {
BooleanCell bs = new BooleanCell();
FloatCell out = new FloatCell(), tru = new FloatCell(), fals = new FloatCell(6.4f);
FloatInput fi = bs.toFloat(fals, tru);
fi.send(out);
assertEquals(6.4f, fi.get(), 0);
assertEquals(6.4f, out.get(), 0);
for (int i = 0; i < 30; i++) {
fals.set(13.2f);
tru.set(-13.2f);
bs.toggle();
assertEquals(bs.get() ? tru.get() : fals.get(), fi.get(), 0);
assertEquals(bs.get() ? tru.get() : fals.get(), out.get(), 0);
FloatCell var = i % 2 == 0 ? tru : fals;
for (float f : Values.interestingFloats) {
var.set(f);// switch which one we vary each time outside
assertEquals(bs.get() ? tru.get() : fals.get(), fi.get(), 0);
assertEquals(bs.get() ? tru.get() : fals.get(), out.get(), 0);
}
}
}
@Test(expected = NullPointerException.class)
public void testToFloatFloatInputFloatInputNull() {
bi.toFloat(null, null);
}
@Test
public void testSelectBooleanBoolean() {
for (boolean def1 : Values.interestingBooleans) {
for (boolean def2 : Values.interestingBooleans) {
BooleanCell bs = new BooleanCell();
BooleanCell out = new BooleanCell();
BooleanInput bi = bs.select(def2, def1);
bi.send(out);
assertEquals(def2, bi.get());
assertEquals(def2, out.get());
for (int i = 0; i < 30; i++) {
bs.toggle();
assertEquals(bs.get() ? def1 : def2, bi.get());
assertEquals(bs.get() ? def1 : def2, out.get());
}
}
}
}
@Test
public void testSelectBooleanBooleanInput() {
for (boolean def : Values.interestingBooleans) {
BooleanCell bs = new BooleanCell();
BooleanCell out = new BooleanCell(), tru = new BooleanCell();
BooleanInput bi = bs.select(def, tru);
bi.send(out);
assertEquals(def, bi.get());
assertEquals(def, out.get());
for (int i = 0; i < 30; i++) {
tru.set(true);
bs.toggle();
assertEquals(bs.get() ? true : def, bi.get());
assertEquals(bs.get() ? true : def, out.get());
for (boolean b : Values.interestingBooleans) {
tru.set(b);
assertEquals(bs.get() ? b : def, bi.get());
assertEquals(bs.get() ? b : def, out.get());
}
}
}
}
@Test(expected = NullPointerException.class)
public void testSelectBooleanBooleanInputNull() {
bi.select(false, null);
}
@Test
public void testSelectBooleanInputBoolean() {
for (boolean def : Values.interestingBooleans) {
BooleanCell bs = new BooleanCell();
BooleanCell out = new BooleanCell(), fals = new BooleanCell(false);
BooleanInput bi = bs.select(fals, def);
bi.send(out);
assertEquals(false, bi.get());
assertEquals(false, out.get());
for (int i = 0; i < 30; i++) {
fals.set(false);
bs.toggle();
assertEquals(bs.get() ? def : false, bi.get());
assertEquals(bs.get() ? def : false, out.get());
for (boolean b : Values.interestingBooleans) {
fals.set(b);
assertEquals(bs.get() ? def : b, bi.get());
assertEquals(bs.get() ? def : b, out.get());
}
}
}
}
@Test(expected = NullPointerException.class)
public void testSelectBooleanInputBooleanNull() {
bi.select(null, true);
}
@Test
public void testSelectBooleanInputBooleanInput() {
BooleanCell bs = new BooleanCell();
BooleanCell out = new BooleanCell(), tru = new BooleanCell(), fals = new BooleanCell(false);
BooleanInput bi = bs.select(fals, tru);
bi.send(out);
assertEquals(false, bi.get());
assertEquals(false, out.get());
for (int i = 0; i < 30; i++) {
fals.set(true);
tru.set(false);
bs.toggle();
assertEquals(bs.get() ? tru.get() : fals.get(), bi.get());
assertEquals(bs.get() ? tru.get() : fals.get(), out.get());
BooleanCell var = i % 2 == 0 ? tru : fals;
for (boolean b : Values.interestingBooleans) {
var.set(b);// switch which one we vary each time outside
assertEquals(bs.get() ? tru.get() : fals.get(), bi.get());
assertEquals(bs.get() ? tru.get() : fals.get(), out.get());
}
}
}
@Test(expected = NullPointerException.class)
public void testSelectBooleanInputBooleanInputNull() {
bi.select(null, null);
}
@Test
public void testOnUpdate() {
CountingEventOutput ceo = new CountingEventOutput();
bi = new BooleanInput() {
@Override
public CancelOutput onUpdate(EventOutput notify) {
assertFalse(gotProperly);
assertEquals(notify, ceo);
gotProperly = true;
return null;
}
@Override
public boolean get() {
fail();
return false;
}
};
gotProperly = false;
bi.onUpdate(ceo);
assertTrue(gotProperly);
}
@Test
public void testSendError() {
bi = new BooleanInput() {
@Override
public CancelOutput onUpdate(EventOutput notify) {
expected.event();
return CancelOutput.nothing;
}
@Override
public boolean get() {
expected2.event();
return true;
}
};
CountingBooleanOutput cbo = new CountingBooleanOutput();
BooleanOutput evil = new BooleanOutput() {
@Override
public void set(boolean value) {
cbo.set(value);
// TODO: check logging.
throw new NoSuchElementException(ERROR_STRING);
}
};
expected.ifExpected = expected2.ifExpected = cbo.ifExpected = true;
cbo.valueExpected = true;
VerifyingLogger.configure(LogLevel.SEVERE, "Error during channel propagation", (t) -> t.getClass() == NoSuchElementException.class && ERROR_STRING.equals(t.getMessage()));
bi.send(evil);
VerifyingLogger.check();
expected.check();
expected2.check();
cbo.check();
}
}