/* * 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.*; import java.util.NoSuchElementException; import java.util.Random; 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 BooleanOutputTest { private static final String ERR_STRING = "safeSet purposeful failure."; private final BooleanOutput evil = (v) -> { throw new NoSuchElementException(ERR_STRING); }; private CountingBooleanOutput cbo, cbo2; @Before public void setUp() throws Exception { cbo = new CountingBooleanOutput(); cbo2 = new CountingBooleanOutput(); VerifyingLogger.begin(); } @After public void tearDown() throws Exception { VerifyingLogger.checkAndEnd(); cbo = null; cbo2 = null; } @Test public void testIgnored() { for (boolean b : Values.interestingBooleans) { BooleanOutput.ignored.set(b); } } @Test public void testIgnoredCombine() { assertEquals(cbo, BooleanOutput.ignored.combine(cbo)); } @Test(expected = NullPointerException.class) public void testIgnoredCombineNull() { BooleanOutput.ignored.combine((BooleanOutput) null); } @Test public void testInvert() { BooleanOutput bo = cbo.invert(); for (boolean b : Values.interestingBooleans) { cbo.ifExpected = true; cbo.valueExpected = !b; bo.set(b); cbo.check(); } } @Test public void testInvertInvert() { assertEquals(cbo, cbo.invert().invert()); } @Test public void testCombine() { BooleanOutput bo = cbo.combine(cbo2); for (boolean b : Values.interestingBooleans) { cbo.ifExpected = cbo2.ifExpected = true; cbo.valueExpected = cbo2.valueExpected = b; bo.set(b); cbo.check(); cbo2.check(); } } @Test(expected = NullPointerException.class) public void testCombineNull() { cbo.combine(null); } @Test public void testStaticCombineSimplification() { assertEquals(BooleanOutput.combine(), BooleanOutput.ignored); assertEquals(BooleanOutput.combine(new BooleanOutput[] { cbo }), cbo); } @Test public void testStaticCombine() { for (int n = 0; n < 20; n++) { CountingBooleanOutput[] cbos = new CountingBooleanOutput[n]; for (int i = 0; i < n; i++) { cbos[i] = new CountingBooleanOutput(); } BooleanOutput combined = BooleanOutput.combine(cbos); for (int l = 0; l < 10; l++) { for (int i = 0; i < n; i++) { cbos[i].ifExpected = true; cbos[i].valueExpected = l % 2 == 0; } combined.set(l % 2 == 0); for (int i = 0; i < n; i++) { cbos[i].check(); } } } } @Test(expected = NullPointerException.class) public void testStaticCombineNull() { BooleanOutput.combine((BooleanOutput[]) null); } @Test(expected = NullPointerException.class) public void testStaticCombineNullElem() { BooleanOutput.combine(new BooleanOutput[] { null }); } @Test(expected = NullPointerException.class) public void testStaticCombineNullEarlierElem() { BooleanOutput.combine(null, cbo); } @Test(expected = NullPointerException.class) public void testStaticCombineNullLaterElem() { BooleanOutput.combine(cbo, null); } @Test public void testLimitUpdatesTo() { EventCell es = new EventCell(); BooleanOutput ob = cbo.limitUpdatesTo(es); es.event(); cbo.check();// nothing should have gone through yet, since no value has // been sent yet! Random rand = new Random(); for (int i = 0; i < 1000; i++) { boolean v = i % 2 == 0; ob.set(v); if (i % 73 == 0 || rand.nextInt(10) == 0) { cbo.ifExpected = true; cbo.valueExpected = v; es.event(); cbo.check(); if (rand.nextBoolean()) { cbo.ifExpected = true; es.event(); cbo.check(); } } } } @Test(expected = NullPointerException.class) public void testLimitUpdatesToNull() { cbo.limitUpdatesTo(null); } @Test public void testEventSetBoolean() { trySet(cbo.eventSet(false), false); trySet(cbo.eventSet(true), true); } private void trySet(EventOutput doSet, boolean expect) { for (int i = 0; i < 10; i++) { cbo.ifExpected = true; cbo.valueExpected = expect; doSet.event(); cbo.check(); } } @Test public void testEventSetBooleanInput() { BooleanCell bs = new BooleanCell(); EventOutput evt = cbo.eventSet(bs); for (int i = 0; i < 10; i++) { cbo.ifExpected = true; bs.set(cbo.valueExpected = (i % 2) == 0); evt.event(); cbo.check(); } } @Test(expected = NullPointerException.class) public void testEventSetBooleanInputNull() { cbo.eventSet(null); } @Test public void testSetWhenBooleanEventInputFalse() { EventCell set = new EventCell(); cbo.setWhen(false, set); trySet(set, false); } @Test(expected = NullPointerException.class) public void testSetWhenBooleanEventInputFalseNull() { cbo.setWhen(false, null); } @Test public void testSetWhenBooleanEventInputTrue() { EventCell set = new EventCell(); cbo.setWhen(true, set); trySet(set, true); } @Test(expected = NullPointerException.class) public void testSetWhenBooleanEventInputTrueNull() { cbo.setWhen(true, null); } @Test public void testSetWhenBooleanInputEventInput() { BooleanCell bs = new BooleanCell(); EventCell evt = new EventCell(); cbo.setWhen(bs, evt); for (int i = 0; i < 10; i++) { cbo.ifExpected = true; bs.set(cbo.valueExpected = (i % 2) == 0); evt.event(); cbo.check(); } } @Test(expected = NullPointerException.class) public void testSetWhenBooleanInputEventInputTrueNullA() { cbo.setWhen(null, EventInput.never); } @Test(expected = NullPointerException.class) public void testSetWhenBooleanInputEventInputTrueNullB() { cbo.setWhen(BooleanInput.alwaysFalse, null); } @Test public void testSetTrueWhen() { EventCell set = new EventCell(); cbo.setTrueWhen(set); trySet(set, true); } @Test(expected = NullPointerException.class) public void testSetTrueWhenNull() { cbo.setTrueWhen(null); } @Test public void testSetFalseWhen() { EventCell set = new EventCell(); cbo.setFalseWhen(set); trySet(set, false); } @Test(expected = NullPointerException.class) public void testSetFalseWhenNull() { cbo.setFalseWhen(null); } @Test public void testPolarize() { CountingEventOutput toFalse = new CountingEventOutput(), toTrue = new CountingEventOutput(); BooleanOutput bo = BooleanOutput.polarize(toFalse, toTrue); boolean last = false; for (boolean b : Values.interestingBooleans) { (b ? toTrue : toFalse).ifExpected = (last != b); bo.set(b); toTrue.check(); toFalse.check(); last = b; } } @Test public void testPolarizeNullA() { CountingEventOutput toTrue = new CountingEventOutput(); BooleanOutput bo = BooleanOutput.polarize(null, toTrue); boolean last = false; for (boolean b : Values.interestingBooleans) { toTrue.ifExpected = b && !last; bo.set(b); toTrue.check(); last = b; } } @Test public void testPolarizeNullB() { CountingEventOutput toFalse = new CountingEventOutput(); BooleanOutput bo = BooleanOutput.polarize(toFalse, null); boolean last = false; for (boolean b : Values.interestingBooleans) { toFalse.ifExpected = !b && last; bo.set(b); toFalse.check(); last = b; } } @Test(expected = NullPointerException.class) public void testPolarizeNullAB() { BooleanOutput.polarize(null, null); } @Test public void testFilter() { for (boolean not : new boolean[] { false, true }) { BooleanCell allowDeny = new BooleanCell(true), out = new BooleanCell(); BooleanOutput bo = not ? out.filterNot(allowDeny) : out.filter(allowDeny); boolean expect = false, next = false; for (int i = 0; i < 110; i++) { boolean nvalue = ((i % 19) < 10) ^ not; if (allowDeny.get() == not && nvalue != not) { // switching to active: expect an update as necessary bo.set(next); assertEquals(expect, out.get()); allowDeny.set(nvalue); expect = next; assertEquals(expect, out.get()); next = !next; } else { allowDeny.set(nvalue); } bo.set(next); if (allowDeny.get() ^ not) { expect = next; } assertEquals(expect, out.get()); next = !next; } } } @Test(expected = NullPointerException.class) public void testFilterNull() { cbo.filter(null); } @Test(expected = NullPointerException.class) public void testFilterNotNull() { cbo.filterNot(null); } @Test public void testSafeSet() { VerifyingLogger.configure(LogLevel.SEVERE, "Error during channel propagation", (t) -> t.getClass() == NoSuchElementException.class && ERR_STRING.equals(t.getMessage())); evil.safeSet(false); VerifyingLogger.check(); VerifyingLogger.configure(LogLevel.SEVERE, "Error during channel propagation", (t) -> t.getClass() == NoSuchElementException.class && ERR_STRING.equals(t.getMessage())); evil.safeSet(true); VerifyingLogger.check(); } @Test(expected = NoSuchElementException.class) public void testExceptionPropagationFalse() { evil.set(false); } @Test(expected = NoSuchElementException.class) public void testExceptionPropagationTrue() { evil.set(true); } @Test(expected = NoSuchElementException.class) public void testCombineWithError1CausesError() { cbo.ifExpected = true; cbo.valueExpected = true; cbo.combine(evil).set(true); } @Test(expected = NoSuchElementException.class) public void testCombineWithError2CausesError() { cbo.ifExpected = true; cbo.valueExpected = true; evil.combine(cbo).set(true); } @Test public void testCombineWithError1Succeeds() { cbo.ifExpected = true; cbo.valueExpected = true; VerifyingLogger.configure(LogLevel.SEVERE, "Error during channel propagation", (t) -> t.getClass() == NoSuchElementException.class && ERR_STRING.equals(t.getMessage())); cbo.combine(evil).safeSet(true); VerifyingLogger.check(); cbo.check(); } @Test public void testCombineWithError2Succeeds() { cbo.ifExpected = true; cbo.valueExpected = true; VerifyingLogger.configure(LogLevel.SEVERE, "Error during channel propagation", (t) -> t.getClass() == NoSuchElementException.class && ERR_STRING.equals(t.getMessage())); evil.combine(cbo).safeSet(true); VerifyingLogger.check(); cbo.check(); } @Test public void testCombineWithError3CausesError() { boolean errored = false; try { evil.combine(evil).set(true); } catch (NoSuchElementException ex) { errored = true; assertEquals(ex.getSuppressed().length, 1); assertTrue(ex.getSuppressed()[0] instanceof NoSuchElementException); } assertTrue(errored); } @Test public void testStaticCombineSingleError() { for (int n = 1; n < 6; n++) { CountingBooleanOutput[] cbos = new CountingBooleanOutput[n]; for (int i = 0; i < n; i++) { cbos[i] = new CountingBooleanOutput(); } for (int bad = 0; bad < n; bad++) { BooleanOutput[] reals = new BooleanOutput[n]; System.arraycopy(cbos, 0, reals, 0, n); reals[bad] = evil; BooleanOutput combined = BooleanOutput.combine(reals); for (int l = 0; l < 10; l++) { for (int i = 0; i < n; i++) { cbos[i].ifExpected = i != bad; cbos[i].valueExpected = l % 2 == 0; } try { combined.set(l % 2 == 0); fail(); } catch (NoSuchElementException ex) { assertEquals(0, ex.getSuppressed().length); } for (int i = 0; i < n; i++) { cbos[i].check(); } } } } } @Test public void testStaticCombineManyErrors() { for (int n = 1; n < 6; n++) { CountingBooleanOutput[] cbos = new CountingBooleanOutput[n]; for (int i = 0; i < n; i++) { cbos[i] = new CountingBooleanOutput(); } for (int bad = 0; bad < 4 * n * n; bad++) { boolean[] evils = new boolean[n]; int evil_count = 0; BooleanOutput[] reals = new BooleanOutput[n]; System.arraycopy(cbos, 0, reals, 0, n); for (int i = 0; i < n; i++) { evils[i] = Values.getRandomBoolean(); if (evils[i]) { evil_count++; reals[i] = evil; } } BooleanOutput combined = BooleanOutput.combine(reals); for (int l = 0; l < 10; l++) { for (int i = 0; i < n; i++) { cbos[i].ifExpected = !evils[i]; cbos[i].valueExpected = l % 2 == 0; } try { combined.set(l % 2 == 0); assertEquals(0, evil_count); } catch (NoSuchElementException ex) { Throwable[] suppressed = ex.getSuppressed(); assertEquals(evil_count - 1, suppressed.length); for (int i = 0; i < suppressed.length; i++) { assertTrue(suppressed[i] instanceof NoSuchElementException); } } for (int i = 0; i < n; i++) { cbos[i].check(); } } } } } @Test public void testCell() { for (boolean ob : Values.interestingBooleans) { BooleanOutput out = cbo::set; cbo.ifExpected = true; cbo.valueExpected = ob; BooleanIO bio = out.cell(ob); cbo.check(); cbo2.ifExpected = true; cbo2.valueExpected = ob; bio.send(cbo2); cbo2.check(); for (int i = 0; i < 20; i++) { boolean b = Values.getRandomBoolean(); cbo.ifExpected = cbo2.ifExpected = bio.get() != b; cbo.valueExpected = cbo2.valueExpected = b; bio.set(b); cbo2.check(); cbo.check(); assertEquals(b, bio.get()); } } } }