package rmblworx.tools.timey; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import java.util.Collection; import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import rmblworx.tools.timey.event.AlarmExpiredEvent; import rmblworx.tools.timey.event.AlarmsModifiedEvent; import rmblworx.tools.timey.event.TimeyEvent; import rmblworx.tools.timey.event.TimeyEventListener; import rmblworx.tools.timey.vo.AlarmDescriptor; import rmblworx.tools.timey.vo.TimeDescriptor; /* * Copyright 2014-2015 Christian Raue * MIT License http://opensource.org/licenses/mit-license.php */ /** * Tests für die Ereignisse der Alarm-Funktionalität. * @author Christian Raue {@literal <christian.raue@gmail.com>} */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "/spring-timey-context.xml", "/alarm-spring-timey-context.xml" }) public class AlarmEventsTest { /** * Max. Zeit (in ms), die auf das Eintreten eines Ereignisses gewartet werden soll. */ protected static final int WAIT_FOR_EVENT = 5000; private final ArgumentCaptor<TimeyEvent> eventCaptor = ArgumentCaptor.forClass(TimeyEvent.class); private final long now = System.currentTimeMillis(); private final long oneHourAgo = now - 60 * 60 * 1000; private final long inOneMinute = now + 60 * 1000; /** * Inaktiver Alarm, der vor einer Stunde ausgelöst wurde. */ private final AlarmDescriptor alarmOneHourAgo = new AlarmDescriptor(new TimeDescriptor(oneHourAgo), false, "alter Alarm", "", null); /** * Alarm, der in einer Minute ausgelöst wird. */ private final AlarmDescriptor alarmInOneMinute = new AlarmDescriptor(new TimeDescriptor(inOneMinute), true, "Zukunftsalarm", "", null); /** * Jetzt auzulösender Alarm. */ private final AlarmDescriptor alarmNow = new AlarmDescriptor(new TimeDescriptor(now), true, "relevanter Alarm", "", null); private ITimey facade; private TimeyEventListener eventListener; @Before public void setUp() { facade = new TimeyFacade(); // alle Alarme löschen for (final AlarmDescriptor alarm : facade.getAllAlarms()) { facade.removeAlarm(alarm); } eventListener = mock(TimeyEventListener.class); facade.addEventListener(eventListener); } /** * Testet Ereignisse beim Anlegen eines inaktiven Alarms. */ @Test public final void testCreateDisabledAlarmNoExpiredEvent() { facade.setAlarm(alarmOneHourAgo); final Class<?>[] expectedEvents = new Class<?>[] { AlarmsModifiedEvent.class }; // sicherstellen, dass erwartete Ereignisse ausgelöst wurden verify(eventListener, timeout(WAIT_FOR_EVENT).times(expectedEvents.length)).handleEvent(eventCaptor.capture()); final List<TimeyEvent> events = eventCaptor.getAllValues(); assertEquals(expectedEvents.length, events.size()); assertContainsAllAndOnlyTypes(events, expectedEvents); } /** * Testet Ereignisse beim Anlegen eines Alarms, der erst irgendwann später ausgelöst wird. */ @Test public final void testCreateFutureAlarmNoExpiredEvent() { facade.setAlarm(alarmInOneMinute); final Class<?>[] expectedEvents = new Class<?>[] { AlarmsModifiedEvent.class }; // sicherstellen, dass erwartete Ereignisse ausgelöst wurden verify(eventListener, timeout(WAIT_FOR_EVENT).times(expectedEvents.length)).handleEvent(eventCaptor.capture()); final List<TimeyEvent> events = eventCaptor.getAllValues(); assertEquals(expectedEvents.length, events.size()); assertContainsAllAndOnlyTypes(events, expectedEvents); } /** * Testet Ereignisse beim Anlegen eines jetzt auzulösenden Alarms. */ @Test public final void testCreateAlarmEvents() { facade.setAlarm(alarmNow); final Class<?>[] expectedEvents = new Class<?>[] { AlarmsModifiedEvent.class, // Anlegen des Alarms AlarmsModifiedEvent.class, // Deaktivierung des Alarms beim Auslösen AlarmExpiredEvent.class, // Auslösen des Alarms }; // sicherstellen, dass erwartete Ereignisse ausgelöst wurden verify(eventListener, timeout(WAIT_FOR_EVENT).times(expectedEvents.length)).handleEvent(eventCaptor.capture()); final List<TimeyEvent> events = eventCaptor.getAllValues(); assertEquals(expectedEvents.length, events.size()); assertContainsAllAndOnlyTypes(events, expectedEvents); for (final TimeyEvent event : events) { if (event instanceof AlarmExpiredEvent) { final AlarmDescriptor alarm = ((AlarmExpiredEvent) event).getAlarmDescriptor(); assertNotNull(alarm); assertEquals(alarmNow.getDescription(), alarm.getDescription()); assertFalse(alarm.getIsActive()); } } } /** * Testet Ereignisse beim Anlegen mehrerer Alarme. */ @Test public final void testCreateSeveralAlarmsEvents() { facade.setAlarm(alarmOneHourAgo); facade.setAlarm(alarmInOneMinute); facade.setAlarm(alarmNow); final Class<?>[] expectedEvents = new Class<?>[] { AlarmsModifiedEvent.class, // Anlegen ... AlarmsModifiedEvent.class, // ... der drei ... AlarmsModifiedEvent.class, // ... Alarme AlarmsModifiedEvent.class, // Deaktivierung des Alarms beim Auslösen AlarmExpiredEvent.class, // Auslösen des Alarms }; // sicherstellen, dass erwartete Ereignisse ausgelöst wurden verify(eventListener, timeout(WAIT_FOR_EVENT).times(expectedEvents.length)).handleEvent(eventCaptor.capture()); final List<TimeyEvent> events = eventCaptor.getAllValues(); assertEquals(expectedEvents.length, events.size()); assertContainsAllAndOnlyTypes(events, expectedEvents); } /** * Stellt sicher, * <ul> * <li>dass jedes Element in <code>items</code> von einem der in <code>types</code> aufgeführten Typen ist und</li> * <li>dass jeder in <code>types</code> aufgeführte Typ in <code>items</code> vorkommt.</li> * </ul> * @param items Elemente * @param types Typen */ protected final void assertContainsAllAndOnlyTypes(final Collection<?> items, final Class<?>... types) { assertContainsAllTypes(items, types); assertContainsOnlyTypes(items, types); } /** * Stellt sicher, dass jeder in <code>types</code> aufgeführte Typ in <code>items</code> vorkommt. * @param items Elemente * @param types Typen */ protected final void assertContainsOnlyTypes(final Collection<?> items, final Class<?>... types) { for (final Object item : items) { assertIsOfOneType(item, types); } } /** * Stellt sicher, dass das Element <code>item</code> von einem der in <code>types</code> aufgeführten Typen ist. * @param item Element * @param types Typen */ protected final void assertIsOfOneType(final Object item, final Class<?>... types) { for (final Class<?> klass : types) { if (klass.isInstance(item)) { return; } } fail(String.format("%s ist vom unerwarteten Typ %s.", item, item.getClass())); } /** * Stellt sicher, dass jedes Element in <code>items</code> von einem der in <code>types</code> aufgeführten Typen ist. * @param items Elemente * @param types Typen */ protected final void assertContainsAllTypes(final Collection<?> items, final Class<?>... types) { for (final Class<?> klass : types) { assertContainsElementOfType(items, klass); } } /** * Stellt sicher, dass ein Element des Typs <code>type</code> in <code>items</code> vorkommt. * @param items Elemente * @param type Typ */ protected final void assertContainsElementOfType(final Collection<?> items, final Class<?> type) { for (final Object item : items) { if (type.isInstance(item)) { return; } } fail(String.format("%s enthält kein Element vom Typ %s.", items, type)); } }