Skip to content

Commit

Permalink
Merge pull request #181 from r-gurzkowski/use-default-workers-as-disp…
Browse files Browse the repository at this point in the history
…atchers

Convert default workers to providers
  • Loading branch information
togi authored Dec 10, 2024
2 parents 97015e7 + 39db4b6 commit 4248520
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 16 deletions.
39 changes: 23 additions & 16 deletions mobius-core/src/main/java/com/spotify/mobius/MobiusPlugins.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,48 +19,55 @@
*/
package com.spotify.mobius;

import com.spotify.mobius.functions.Producer;
import com.spotify.mobius.runners.WorkRunner;
import com.spotify.mobius.runners.WorkRunners;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class MobiusPlugins {

@Nullable private static WorkRunner EFFECT_RUNNER_OVERRIDE;
@Nullable private static WorkRunner EVENT_RUNNER_OVERRIDE;
@Nullable private static Producer<WorkRunner> EFFECT_RUNNER_OVERRIDE_PRODUCER;
@Nullable private static Producer<WorkRunner> EVENT_RUNNER_OVERRIDE_PRODUCER;

/**
* Sets the effect runner that will be used in {@link MobiusLoop} when effectRunner was not
* provided to {@link MobiusLoop.Builder}.
* Sets the effect runner producer that will be used in {@link MobiusLoop} when effectRunner was
* not provided to {@link MobiusLoop.Builder}. Affects only the loops created after this call.
*
* @param defaultEffectRunner the {@link WorkRunner} to use as the default effect runner
* @param defaultEffectRunnerProducer the {@link WorkRunner} producer to use as the default effect
* runner (a new instance needs to be provided each time the producer is called) or null to
* restore the default one
*/
public static void setDefaultEffectRunner(WorkRunner defaultEffectRunner) {
EFFECT_RUNNER_OVERRIDE = defaultEffectRunner;
public static void setDefaultEffectRunner(
@Nullable Producer<WorkRunner> defaultEffectRunnerProducer) {
EFFECT_RUNNER_OVERRIDE_PRODUCER = defaultEffectRunnerProducer;
}

/**
* Sets the event runner that will be used in {@link MobiusLoop} when eventRunner was not provided
* to {@link MobiusLoop.Builder}.
* Sets the event runner producer that will be used in {@link MobiusLoop} when eventRunner was not
* provided to {@link MobiusLoop.Builder}. Affects only the loops created after this call.
*
* @param defaultEventRunner the {@link WorkRunner} to use as the default event runner
* @param defaultEventRunnerProducer the {@link WorkRunner} producer to use as the default event
* runner (a new instance needs to be provided each time the producer is called) or null to
* restore the default one
*/
public static void setDefaultEventRunner(WorkRunner defaultEventRunner) {
EVENT_RUNNER_OVERRIDE = defaultEventRunner;
public static void setDefaultEventRunner(
@Nullable Producer<WorkRunner> defaultEventRunnerProducer) {
EVENT_RUNNER_OVERRIDE_PRODUCER = defaultEventRunnerProducer;
}

@Nonnull
static WorkRunner defaultEffectRunner() {
if (EFFECT_RUNNER_OVERRIDE != null) {
return EFFECT_RUNNER_OVERRIDE;
if (EFFECT_RUNNER_OVERRIDE_PRODUCER != null) {
return EFFECT_RUNNER_OVERRIDE_PRODUCER.get();
}
return WorkRunners.cachedThreadPool();
}

@Nonnull
static WorkRunner defaultEventRunner() {
if (EVENT_RUNNER_OVERRIDE != null) {
return EVENT_RUNNER_OVERRIDE;
if (EVENT_RUNNER_OVERRIDE_PRODUCER != null) {
return EVENT_RUNNER_OVERRIDE_PRODUCER.get();
}
return WorkRunners.singleThread();
}
Expand Down
123 changes: 123 additions & 0 deletions mobius-core/src/test/java/com/spotify/mobius/MobiusPluginsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package com.spotify.mobius;

import static com.spotify.mobius.Effects.effects;
import static org.assertj.core.api.Assertions.assertThat;

import com.spotify.mobius.disposables.Disposable;
import com.spotify.mobius.functions.Producer;
import com.spotify.mobius.test.RecordingModelObserver;
import com.spotify.mobius.test.TestWorkRunner;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import org.junit.After;
import org.junit.Test;

public class MobiusPluginsTest {
private boolean wasEffectRun = false;
private boolean wasEventRun = false;

private final Connectable<String, String> effectHandler =
eventConsumer ->
new Connection<>() {
@Override
public void accept(String value) {
wasEffectRun = true;
}

@Override
public void dispose() {}
};

private final Update<String, String, String> update =
(String model, String event) -> {
wasEventRun = true;
return Next.dispatch(effects("effect"));
};

@After
public void tearDown() {
MobiusPlugins.setDefaultEventRunner(null);
MobiusPlugins.setDefaultEffectRunner(null);
}

@Test
public void shouldUseDefaultEventAndEffectRunners() {
TestWorkRunner testEffectWorkRunner = new TestWorkRunner();
TestWorkRunner testEventWorkRunner = new TestWorkRunner();
MobiusPlugins.setDefaultEffectRunner(() -> testEffectWorkRunner);
MobiusPlugins.setDefaultEventRunner(() -> testEventWorkRunner);

MobiusLoop<String, String, String> loop =
Mobius.loop(update, effectHandler).startFrom("init-model");
RecordingModelObserver<String> observer = new RecordingModelObserver<>();
Disposable unregister = loop.observe(observer);

loop.dispatchEvent("event");

assertThat(wasEventRun).isFalse();
assertThat(wasEffectRun).isFalse();

testEventWorkRunner.runAll();
testEffectWorkRunner.runAll();

assertThat(wasEventRun).isTrue();
assertThat(wasEffectRun).isTrue();

unregister.dispose();
loop.dispose();
}

@Test
public void shouldDisposeDefaultEventAndEffectRunners() {
TestWorkRunner testEffectWorkRunner = new TestWorkRunner();
TestWorkRunner testEventWorkRunner = new TestWorkRunner();
MobiusPlugins.setDefaultEffectRunner(() -> testEffectWorkRunner);
MobiusPlugins.setDefaultEventRunner(() -> testEventWorkRunner);

Mobius.loop(update, effectHandler).startFrom("init-model").dispose();

assertThat(testEffectWorkRunner.isDisposed()).isTrue();
assertThat(testEventWorkRunner.isDisposed()).isTrue();
}

@Test
public void shouldUseNewWorkerForEachLoop() {
TestProducer<TestWorkRunner> effectRunnerProducer = new TestProducer<>(TestWorkRunner::new);
TestProducer<TestWorkRunner> eventRunnerProducer = new TestProducer<>(TestWorkRunner::new);
MobiusPlugins.setDefaultEffectRunner(effectRunnerProducer::get);
MobiusPlugins.setDefaultEventRunner(eventRunnerProducer::get);

Mobius.loop(update, effectHandler).startFrom("init-model").dispose();
assertThat(effectRunnerProducer.producedItems).hasSize(1);
assertThat(eventRunnerProducer.producedItems).hasSize(1);

Mobius.loop(update, effectHandler).startFrom("init-model").dispose();
assertThat(effectRunnerProducer.producedItems).hasSize(2);
assertThat(eventRunnerProducer.producedItems).hasSize(2);

assertThat(effectRunnerProducer.producedItems.stream().map(TestWorkRunner::isDisposed))
.containsExactly(true, true);
assertThat(eventRunnerProducer.producedItems.stream().map(TestWorkRunner::isDisposed))
.containsExactly(true, true);
}

private static class TestProducer<T> implements Producer<T> {

private final Producer<T> originalProducer;
public List<T> producedItems = new ArrayList<>();

public TestProducer(Producer<T> originalProducer) {

this.originalProducer = originalProducer;
}

@Nonnull
@Override
public T get() {
T produced = originalProducer.get();
producedItems.add(produced);
return produced;
}
}
}

0 comments on commit 4248520

Please sign in to comment.