Mobius { public static <M, E, F> MobiusLoop.Builder<M, E, F> loop( Update<M, E, F> update, Connectable<F, E> effectHandler) { return new Builder<>( update, effectHandler, null, (Connectable<M, E>) NOOP_EVENT_SOURCE, (MobiusLoop.Logger<M, E, F>) NOOP_LOGGER, new Producer<WorkRunner>() { @Nonnull @Override public WorkRunner get() { return WorkRunners.from(Executors.newSingleThreadExecutor(Builder.THREAD_FACTORY)); } }, new Producer<WorkRunner>() { @Nonnull @Override public WorkRunner get() { return WorkRunners.from(Executors.newCachedThreadPool(Builder.THREAD_FACTORY)); } }); } private  Mobius(); static MobiusLoop.Builder<M, E, F> loop(
      Update<M, E, F> update, Connectable<F, E> effectHandler); static MobiusLoop.Controller<M, E> controller(
      MobiusLoop.Factory<M, E, F> loopFactory, M defaultModel); static MobiusLoop.Controller<M, E> controller(
      MobiusLoop.Factory<M, E, F> loopFactory, M defaultModel, Init<M, F> init); static MobiusLoop.Controller<M, E> controller(
      MobiusLoop.Factory<M, E, F> loopFactory, M defaultModel, WorkRunner modelRunner); static MobiusLoop.Controller<M, E> controller(
      MobiusLoop.Factory<M, E, F> loopFactory,
      M defaultModel,
      Init<M, F> init,
      WorkRunner modelRunner);  }
@Test public void shouldInstantiateWithMinimumParams() throws Exception { loop = Mobius.loop(UPDATE, HANDLER).startFrom(MY_MODEL); loop.dispatchEvent(8); await().atMost(Duration.ofSeconds(1)).until(() -> loop.getMostRecentModel(), is("start83")); }
@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.ofSeconds(1)).until(() -> "start33".equals(loop.getMostRecentModel())); }
@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.ofSeconds(1)).until(() -> runner.runCounter.get() == 1); }
@Test public void shouldPermitUsingCustomEventRunner() throws Exception { TestableWorkRunner runner = new TestableWorkRunner(); loop = Mobius.loop(UPDATE, HANDLER).eventRunner(() -> runner).startFrom(MY_MODEL); loop.dispatchEvent(3); await().atMost(Duration.ofSeconds(1)).until(() -> runner.runCounter.get() == 1); }
@Test public void shouldPermitUsingEventSource() throws Exception { TestEventSource eventSource = new TestEventSource(); loop = Mobius.loop(UPDATE, HANDLER) .eventRunner(WorkRunners::immediate) .eventSource(eventSource) .startFrom(MY_MODEL); eventSource.consumer.accept(7); await().atMost(Duration.ofSeconds(1)).until(() -> loop.getMostRecentModel(), is("start7")); }
@Test public void shouldPermitUsingConnectablesAsAnEventSource() throws Exception { ConnectableTestEventSource eventSource = new ConnectableTestEventSource(); loop = Mobius.loop(UPDATE, HANDLER) .eventRunner(WorkRunners::immediate) .eventSource(eventSource) .startFrom(MY_MODEL); eventSource.consumer.accept(7); await().atMost(Duration.ofSeconds(1)).until(() -> loop.getMostRecentModel(), is("start7")); }
@Test public void shouldPermitUsingCustomLogger() throws Exception { TestLogger logger = new TestLogger(); loop = Mobius.loop(UPDATE, HANDLER) .init(First::first) .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]}")); }
@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.ofSeconds(1)).until(() -> loop.getMostRecentModel(), is("resume97")); }
@Test public void shouldSupportCreatingMultipleLoops() throws Exception { MobiusLoop.Factory<String, Integer, Boolean> factory = Mobius.loop(UPDATE, HANDLER); loop = factory.startFrom("first"); loop.dispatchEvent(97); await().atMost(Duration.ofSeconds(1)).until(() -> loop.getMostRecentModel(), is("first97")); loop.dispose(); loop = factory.startFrom("second"); loop.dispatchEvent(97); await().atMost(Duration.ofSeconds(1)).until(() -> loop.getMostRecentModel(), is("second97")); }
@Test public void shouldAllowStartingWithEffects() throws Exception { TestableWorkRunner runner = new TestableWorkRunner(); loop = Mobius.loop(UPDATE, HANDLER).effectRunner(() -> runner).startFrom(MY_MODEL, effects(false)); await().atMost(Duration.ofSeconds(1)).until(() -> runner.runCounter.get() == 1); }
@Test public void shouldDisallowBothInitAndStartEffects() throws Exception { final Set<Boolean> startEffects = effects(true); Init<String, Boolean> init = model -> First.first(model, startEffects); MobiusLoop.Factory<String, Integer, Boolean> factory = Mobius.loop(UPDATE, HANDLER).init(init); assertThatThrownBy( () -> { factory.startFrom(MY_MODEL, startEffects); }) .hasMessageContaining("has init defined"); }