@Override public void onDispatchEvent(E event) { loop.dispatchEvent(event); }
@Override public void accept(E event) { dispatchEvent(event); } };
@Override public void accept(E event) { dispatchEvent(event); } };
@Override public void onDispatchEvent(E event) { loop.dispatchEvent(event); }
@Test public void shouldSurviveEffectPerformerThrowingMultipleTimes() throws Exception { mobiusLoop.dispatchEvent(new EventWithCrashingEffect()); mobiusLoop.dispatchEvent(new TestEvent("should happen")); mobiusLoop.dispatchEvent(new EventWithCrashingEffect()); mobiusLoop.dispatchEvent(new TestEvent("should happen, too")); observer.assertStates( "init", "will crash", "will crash->should happen", "will crash", "will crash->should happen, too"); }
@Test public void shouldSurviveEffectPerformerThrowing() throws Exception { mobiusLoop.dispatchEvent(new EventWithCrashingEffect()); mobiusLoop.dispatchEvent(new TestEvent("should happen")); observer.assertStates("init", "will crash", "will crash->should happen"); }
@Test public void shouldTransitionToNextStateBasedOnInput() throws Exception { mobiusLoop.dispatchEvent(new TestEvent("first")); mobiusLoop.dispatchEvent(new TestEvent("second")); observer.assertStates("init", "init->first", "init->first->second"); }
@Test(expected = IllegalStateException.class) public void dispatchingEventsAfterDisposalThrowsException() throws Exception { mobiusLoop.dispose(); mobiusLoop.dispatchEvent(new TestEvent("2")); }
@Test public void shouldSupportEffectsThatGenerateEvents() throws Exception { setupWithEffects( eventConsumer -> new SimpleConnection<TestEffect>() { @Override public void accept(TestEffect effect) { eventConsumer.accept(new TestEvent(effect.toString())); } }, immediateRunner); mobiusLoop.dispatchEvent(new EventWithSafeEffect("hi")); observer.assertStates("init", "init->hi", "init->hi->effecthi"); }
@Test public void shouldSupportUnregisteringObserver() throws Exception { observer = new RecordingModelObserver<>(); mobiusLoop = MobiusLoop.create( mobiusStore, effectHandler, eventSource, immediateRunner, immediateRunner); Disposable unregister = mobiusLoop.observe(observer); mobiusLoop.dispatchEvent(new TestEvent("active observer")); unregister.dispose(); mobiusLoop.dispatchEvent(new TestEvent("shouldn't be seen")); observer.assertStates("init", "init->active observer"); }
@Test public void shouldSupportCreatingMultipleLoops() throws Exception { MobiusLoop.Factory<String, Integer, Boolean> factory = Mobius.loop(UPDATE, HANDLER); // one loop = factory.startFrom("first"); loop.dispatchEvent(97); await().atMost(Duration.ONE_SECOND).until(() -> loop.getMostRecentModel(), is("first97")); loop.dispose(); // then another one loop = factory.startFrom("second"); loop.dispatchEvent(97); await().atMost(Duration.ONE_SECOND).until(() -> loop.getMostRecentModel(), is("second97")); }
@Test public void shouldInstantiateBeginnerWithMinimumParams() throws Exception { MobiusLoop<String, Integer, ?> loop = MobiusExtras.beginnerLoop(BEGINNER_UPDATE).startFrom(MY_MODEL); loop.dispatchEvent(8); await().atMost(Duration.ONE_SECOND).until(() -> loop.getMostRecentModel(), is("start8")); } }
@Test public void shouldInstantiateWithMinimumParams() throws Exception { loop = Mobius.loop(UPDATE, HANDLER).startFrom(MY_MODEL); loop.dispatchEvent(8); await().atMost(Duration.ONE_SECOND).until(() -> loop.getMostRecentModel(), is("start83")); }
@Test public void shouldPermitUsingCustomEventRunner() throws Exception { TestableWorkRunner runner = new TestableWorkRunner(); loop = Mobius.loop(UPDATE, HANDLER).eventRunner(() -> runner).startFrom(MY_MODEL); loop.dispatchEvent(3); // 2 because the initial model dispatch is run on the event runner await().atMost(Duration.ONE_SECOND).until(() -> runner.runCounter.get() == 2); }
@Test public void shouldSupportCreatingFactory() throws Exception { MobiusLoop.Factory<String, Integer, Boolean> factory = Mobius.loop(UPDATE, HANDLER); loop = factory.startFrom("resume"); loop.dispatchEvent(97); await().atMost(Duration.ONE_SECOND).until(() -> loop.getMostRecentModel(), is("resume97")); }
@Test public void shouldPermitUsingCustomInit() throws Exception { Init<String, Boolean> init = new Init<String, Boolean>() { @Nonnull @Override public First<String, Boolean> init(String model) { return First.first(model, effects(true)); } }; loop = Mobius.loop(UPDATE, HANDLER).init(init).startFrom(MY_MODEL); loop.dispatchEvent(3); await().atMost(Duration.ONE_SECOND).until(() -> "start33".equals(loop.getMostRecentModel())); }
@Test public void shouldSupportHandlingEffectsWhenOneEffectNeverCompletes() throws Exception { setupWithEffects( eventConsumer -> new SimpleConnection<TestEffect>() { @Override public void accept(TestEffect effect) { if (effect instanceof SafeEffect) { if (((SafeEffect) effect).id.equals("1")) { try { // Rough approximation of waiting infinite amount of time. Thread.sleep(2000); } catch (InterruptedException e) { // ignored. } return; } } eventConsumer.accept(new TestEvent(effect.toString())); } }, new ExecutorServiceWorkRunner(Executors.newFixedThreadPool(2))); // the effectHandler associated with "1" should never happen mobiusLoop.dispatchEvent(new EventWithSafeEffect("1")); mobiusLoop.dispatchEvent(new TestEvent("2")); mobiusLoop.dispatchEvent(new EventWithSafeEffect("3")); await().atMost(Duration.FIVE_SECONDS).until(() -> observer.valueCount() >= 5); observer.assertStates( "init", "init->1", "init->1->2", "init->1->2->3", "init->1->2->3->effect3"); }
@Test public void shouldPermitUsingCustomEffectRunner() throws Exception { TestableWorkRunner runner = new TestableWorkRunner(); loop = Mobius.loop(UPDATE, HANDLER).effectRunner(() -> runner).startFrom(MY_MODEL); loop.dispatchEvent(3); await().atMost(Duration.ONE_SECOND).until(() -> runner.runCounter.get() == 1); }
@Test public void shouldOrderStateChangesCorrectlyWhenEffectsAreSlow() throws Exception { final SettableFuture<TestEvent> future = SettableFuture.create(); setupWithEffects( eventConsumer -> new SimpleConnection<TestEffect>() { @Override public void accept(TestEffect effect) { try { eventConsumer.accept(future.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } }, backgroundRunner); mobiusLoop.dispatchEvent(new EventWithSafeEffect("1")); mobiusLoop.dispatchEvent(new TestEvent("2")); await().atMost(Duration.ONE_SECOND).until(() -> observer.valueCount() >= 3); future.set(new TestEvent("3")); await().atMost(Duration.ONE_SECOND).until(() -> observer.valueCount() >= 4); observer.assertStates("init", "init->1", "init->1->2", "init->1->2->3"); }
@Test public void shouldPermitUsingCustomLogger() throws Exception { TestLogger logger = new TestLogger(); loop = Mobius.loop(UPDATE, HANDLER) .logger(logger) .eventRunner(ImmediateWorkRunner::new) .effectRunner(ImmediateWorkRunner::new) .startFrom(MY_MODEL); loop.dispatchEvent(7); assertThat( logger.history, contains( "before init: start", "after init: start, First{model=start, effects=[]}", "before update: start, 7", "after update: start, 7, Next{model=start7, effects=[false]}")); }