Skip to content

Commit 4dad04a

Browse files
committed
1.x: DoAfterTerminate handle if action throws
1 parent e26096d commit 4dad04a

File tree

2 files changed

+50
-10
lines changed

2 files changed

+50
-10
lines changed

src/main/java/rx/internal/operators/OperatorDoAfterTerminate.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
import rx.Observable.Operator;
1919
import rx.Subscriber;
20+
import rx.exceptions.Exceptions;
2021
import rx.functions.Action0;
22+
import rx.plugins.RxJavaPlugins;
2123

2224
/**
2325
* Registers an action to be called after an Observable invokes {@code onComplete} or {@code onError}.
@@ -53,7 +55,7 @@ public void onError(Throwable e) {
5355
try {
5456
child.onError(e);
5557
} finally {
56-
action.call();
58+
callAction();
5759
}
5860
}
5961

@@ -62,7 +64,16 @@ public void onCompleted() {
6264
try {
6365
child.onCompleted();
6466
} finally {
67+
callAction();
68+
}
69+
}
70+
71+
void callAction() {
72+
try {
6573
action.call();
74+
} catch (Throwable ex) {
75+
Exceptions.throwIfFatal(ex);
76+
RxJavaPlugins.getInstance().getErrorHandler().handleError(ex);
6677
}
6778
}
6879
};

src/test/java/rx/internal/operators/OperatorDoAfterTerminateTest.java

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,14 @@
1515
*/
1616
package rx.internal.operators;
1717

18-
import static org.junit.Assert.assertEquals;
19-
import static org.junit.Assert.fail;
20-
import static org.mockito.Mockito.mock;
21-
import static org.mockito.Mockito.times;
22-
import static org.mockito.Mockito.verify;
18+
import static org.junit.Assert.*;
19+
import static org.mockito.Mockito.*;
2320

24-
import org.junit.Before;
25-
import org.junit.Test;
21+
import org.junit.*;
2622

27-
import rx.Observable;
28-
import rx.Observer;
23+
import rx.*;
2924
import rx.functions.Action0;
25+
import rx.observers.TestSubscriber;
3026

3127
public class OperatorDoAfterTerminateTest {
3228

@@ -65,4 +61,37 @@ public void nullActionShouldBeCheckedInConstructor() {
6561
assertEquals("Action can not be null", expected.getMessage());
6662
}
6763
}
64+
65+
@Test
66+
public void nullFinallyActionShouldBeCheckedASAP() {
67+
try {
68+
Observable
69+
.just("value")
70+
.doAfterTerminate(null);
71+
72+
fail();
73+
} catch (NullPointerException expected) {
74+
75+
}
76+
}
77+
78+
@Test
79+
public void ifFinallyActionThrowsExceptionShouldNotBeSwallowedAndActionShouldBeCalledOnce() {
80+
Action0 finallyAction = mock(Action0.class);
81+
doThrow(new IllegalStateException()).when(finallyAction).call();
82+
83+
TestSubscriber<String> testSubscriber = new TestSubscriber<String>();
84+
85+
Observable
86+
.just("value")
87+
.doAfterTerminate(finallyAction)
88+
.subscribe(testSubscriber);
89+
90+
testSubscriber.assertValue("value");
91+
92+
verify(finallyAction).call();
93+
// Actual result:
94+
// Not only IllegalStateException was swallowed
95+
// But finallyAction was called twice!
96+
}
6897
}

0 commit comments

Comments
 (0)