diff --git a/mobius-rx2/build.gradle b/mobius-rx2/build.gradle index 90488c96..0bda939f 100644 --- a/mobius-rx2/build.gradle +++ b/mobius-rx2/build.gradle @@ -14,6 +14,7 @@ dependencies { testImplementation "org.hamcrest:hamcrest-library:${versions.hamcrestLibrary}" testImplementation "ch.qos.logback:logback-classic:${versions.logback}" testImplementation "org.awaitility:awaitility:${versions.awaitility}" + testImplementation "org.assertj:assertj-core:${versions.assertjcore}" testImplementation "com.google.auto.value:auto-value-annotations:${versions.autoValue}" } diff --git a/mobius-rx2/src/main/java/com/spotify/mobius/rx2/EffectHandlerException.java b/mobius-rx2/src/main/java/com/spotify/mobius/rx2/EffectHandlerException.java new file mode 100644 index 00000000..3aca6e1c --- /dev/null +++ b/mobius-rx2/src/main/java/com/spotify/mobius/rx2/EffectHandlerException.java @@ -0,0 +1,47 @@ +/* + * -\-\- + * Mobius + * -- + * Copyright (c) 2017-2018 Spotify AB + * -- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * -/-/- + */ +package com.spotify.mobius.rx2; + +import com.spotify.mobius.ConnectionException; + +/** + * Helper exception type that enables capturing the correct class and method for exceptions + * happening in {@link io.reactivex.ObservableTransformer}s. + */ +class EffectHandlerException extends ConnectionException { + + private EffectHandlerException(Throwable throwable) { + super("Error in effect handler", throwable); + } + + public static EffectHandlerException in(Object effectHandler, Throwable cause) { + EffectHandlerException e = new EffectHandlerException(cause); + + final StackTraceElement[] stackTrace = e.getStackTrace(); + + // add a synthetic StackTraceElement so that the effect handler class name will be reported in + // the exception. This helps troubleshooting where the issue originated from. + stackTrace[0] = new StackTraceElement(effectHandler.getClass().getName(), "apply", null, -1); + + e.setStackTrace(stackTrace); + + return e; + } +} diff --git a/mobius-rx2/src/main/java/com/spotify/mobius/rx2/RxMobius.java b/mobius-rx2/src/main/java/com/spotify/mobius/rx2/RxMobius.java index ea7d235d..0e2ed63d 100644 --- a/mobius-rx2/src/main/java/com/spotify/mobius/rx2/RxMobius.java +++ b/mobius-rx2/src/main/java/com/spotify/mobius/rx2/RxMobius.java @@ -21,7 +21,6 @@ import static com.spotify.mobius.internal_util.Preconditions.checkNotNull; -import com.spotify.mobius.ConnectionException; import com.spotify.mobius.Mobius; import com.spotify.mobius.MobiusLoop; import com.spotify.mobius.Update; @@ -481,9 +480,7 @@ public ObservableTransformer build() { private static Consumer defaultOnError( final ObservableTransformer effectHandler) { return throwable -> - RxJavaPlugins.onError( - new ConnectionException( - "in effect handler: " + effectHandler.getClass().toString(), throwable)); + RxJavaPlugins.onError(EffectHandlerException.in(effectHandler, throwable)); } private interface OnErrorFunction extends Function { diff --git a/mobius-rx2/src/test/java/com/spotify/mobius/rx2/EffectHandlerExceptionTest.java b/mobius-rx2/src/test/java/com/spotify/mobius/rx2/EffectHandlerExceptionTest.java new file mode 100644 index 00000000..33503903 --- /dev/null +++ b/mobius-rx2/src/test/java/com/spotify/mobius/rx2/EffectHandlerExceptionTest.java @@ -0,0 +1,41 @@ +/* + * -\-\- + * Mobius + * -- + * Copyright (c) 2017-2018 Spotify AB + * -- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * -/-/- + */ +package com.spotify.mobius.rx2; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.Test; + +public class EffectHandlerExceptionTest { + + private static class PretendEffectHandler {} + + @Test + public void shouldProvideAGoodStackTrace() throws Exception { + final RuntimeException cause = new RuntimeException("hey"); + + assertThatThrownBy( + () -> { + throw EffectHandlerException.in(new PretendEffectHandler(), cause); + }) + .hasStackTraceContaining(PretendEffectHandler.class.getName()) + .hasCause(cause); + } +}