/* * 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.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.scheduler.VirtualTime; import ccre.testing.CountingEventOutput; import ccre.util.Values; @SuppressWarnings("javadoc") public class EventOutputTest { private static final String ERROR_STRING = "safeEvent purposeful failure."; private final EventOutput evil = () -> { throw new NoSuchElementException(ERROR_STRING); }; private CountingEventOutput ceo, ceo2; @Before public void setUp() throws Exception { ceo = new CountingEventOutput(); ceo2 = new CountingEventOutput(); VerifyingLogger.begin(); // for 'debounce' testing VirtualTime.startFakeTime(); } @After public void tearDown() throws Exception { VirtualTime.endFakeTime(); VerifyingLogger.checkAndEnd(); ceo = ceo2 = null; } @Test public void testIgnored() { // nothing should happen... but we don't have much of a way to check // that. EventOutput.ignored.event(); } @Test public void testIgnoredCombine() { assertEquals(ceo, EventOutput.ignored.combine(ceo)); } @Test(expected = NullPointerException.class) public void testIgnoredCombineNull() { EventOutput.ignored.combine((EventOutput) null); } @Test public void testEvent() { for (int i = 0; i < 10; i++) { ceo.ifExpected = true; ceo.event(); ceo.check(); } } @Test public void testCombine() { EventOutput eo = ceo.combine(ceo2); for (int i = 0; i < 10; i++) { ceo.ifExpected = ceo2.ifExpected = true; eo.event(); ceo.check(); ceo2.check(); } } @Test(expected = NullPointerException.class) public void testCombineNull() { ceo.combine(null); } @Test public void testStaticCombineSimplification() { assertEquals(EventOutput.combine(), EventOutput.ignored); assertEquals(EventOutput.combine(new EventOutput[] { ceo }), ceo); } @Test public void testStaticCombine() { for (int n = 0; n < 20; n++) { CountingEventOutput[] ceos = new CountingEventOutput[n]; for (int i = 0; i < n; i++) { ceos[i] = new CountingEventOutput(); } EventOutput combined = EventOutput.combine(ceos); for (int l = 0; l < 10; l++) { for (int i = 0; i < n; i++) { ceos[i].ifExpected = true; } combined.event(); for (int i = 0; i < n; i++) { ceos[i].check(); } } } } @Test(expected = NullPointerException.class) public void testStaticCombineNull() { EventOutput.combine((EventOutput[]) null); } @Test(expected = NullPointerException.class) public void testStaticCombineNullElem() { EventOutput.combine(new EventOutput[] { null }); } @Test(expected = NullPointerException.class) public void testStaticCombineNullEarlierElem() { EventOutput.combine(null, ceo); } @Test(expected = NullPointerException.class) public void testStaticCombineNullLaterElem() { EventOutput.combine(ceo, null); } @Test public void testFilter() { BooleanCell allow = new BooleanCell(); EventOutput eo = ceo.filter(allow); for (boolean b : Values.interestingBooleans) { allow.set(b); for (int i = 0; i < 3; i++) { ceo.ifExpected = b; eo.event(); ceo.check(); } } } @Test(expected = NullPointerException.class) public void testFilterNull() { ceo.filter(null); } @Test public void testFilterNot() { BooleanCell deny = new BooleanCell(); EventOutput eo = ceo.filterNot(deny); for (boolean b : Values.interestingBooleans) { deny.set(b); for (int i = 0; i < 3; i++) { ceo.ifExpected = !b; eo.event(); ceo.check(); } } } @Test(expected = NullPointerException.class) public void testFilterNotNull() { ceo.filterNot(null); } @Test public void testDebounce() throws InterruptedException { long millis = 72; EventOutput db = ceo.debounce(millis); for (int i = 0; i < 100; i++) { ceo.ifExpected = (i % 6 == 0); db.event(); ceo.check(); if (i % 3 == 2) { VirtualTime.forward(millis / 2); } } } @Test(expected = IllegalArgumentException.class) public void testDebounceNegative() { ceo.debounce(-1); } @Test(expected = IllegalArgumentException.class) public void testDebounceZero() { ceo.debounce(0); } private boolean gotProperly; @Test public void testOn() { gotProperly = false; CancelOutput cex = ceo2::event; assertEquals(cex, ceo.on(new EventInput() { @Override public CancelOutput send(EventOutput notify) { assertEquals(ceo, notify); gotProperly = true; return cex; } @Override public CancelOutput onUpdate(EventOutput notify) { fail("supposed to go to send() directly!"); return null; } })); assertTrue(gotProperly); } @Test(expected = NullPointerException.class) public void testOnNull() { ceo.on(null); } @Test public void testSafeSet() { VerifyingLogger.configure(LogLevel.SEVERE, "Error during event propagation", (t) -> t.getClass() == NoSuchElementException.class && ERROR_STRING.equals(t.getMessage())); evil.safeEvent(); VerifyingLogger.check(); } @Test(expected = NoSuchElementException.class) public void testExceptionPropagation() { evil.event(); } @Test(expected = NoSuchElementException.class) public void testCombineWithError1CausesError() { ceo.ifExpected = true; ceo.combine(evil).event(); } @Test(expected = NoSuchElementException.class) public void testCombineWithError2CausesError() { ceo.ifExpected = true; evil.combine(ceo).event(); } @Test public void testCombineWithError1Succeeds() { ceo.ifExpected = true; VerifyingLogger.configure(LogLevel.SEVERE, "Error during event propagation", (t) -> t.getClass() == NoSuchElementException.class && ERROR_STRING.equals(t.getMessage())); ceo.combine(evil).safeEvent(); VerifyingLogger.check(); ceo.check(); } @Test public void testCombineWithError2Succeeds() { ceo.ifExpected = true; VerifyingLogger.configure(LogLevel.SEVERE, "Error during event propagation", (t) -> t.getClass() == NoSuchElementException.class && ERROR_STRING.equals(t.getMessage())); evil.combine(ceo).safeEvent(); VerifyingLogger.check(); ceo.check(); } @Test public void testCombineWithError3CausesError() { boolean errored = false; try { evil.combine(evil).event(); } 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++) { CountingEventOutput[] ceos = new CountingEventOutput[n]; for (int i = 0; i < n; i++) { ceos[i] = new CountingEventOutput(); } for (int bad = 0; bad < n; bad++) { EventOutput[] reals = new EventOutput[n]; System.arraycopy(ceos, 0, reals, 0, n); reals[bad] = evil; EventOutput combined = EventOutput.combine(reals); for (int l = 0; l < 10; l++) { for (int i = 0; i < n; i++) { ceos[i].ifExpected = i != bad; } try { combined.event(); fail(); } catch (NoSuchElementException ex) { assertEquals(0, ex.getSuppressed().length); } for (int i = 0; i < n; i++) { ceos[i].check(); } } } } } @Test public void testStaticCombineManyErrors() { for (int n = 1; n < 6; n++) { CountingEventOutput[] ceos = new CountingEventOutput[n]; for (int i = 0; i < n; i++) { ceos[i] = new CountingEventOutput(); } for (int bad = 0; bad < 4 * n * n; bad++) { boolean[] evils = new boolean[n]; int evil_count = 0; EventOutput[] reals = new EventOutput[n]; System.arraycopy(ceos, 0, reals, 0, n); for (int i = 0; i < n; i++) { evils[i] = Values.getRandomBoolean(); if (evils[i]) { evil_count++; reals[i] = evil; } } EventOutput combined = EventOutput.combine(reals); for (int l = 0; l < 10; l++) { for (int i = 0; i < n; i++) { ceos[i].ifExpected = !evils[i]; } try { combined.event(); 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++) { ceos[i].check(); } } } } } @Test public void testCell() { EventOutput out = ceo::event; EventIO eio = out.cell(); eio.send(ceo2); for (int i = 0; i < 10; i++) { ceo.ifExpected = true; ceo2.ifExpected = true; eio.event(); ceo2.check(); ceo.check(); } } }