-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1766 from loganj/uncaught
Unhandled errors go to UncaughtExceptionHandler
- Loading branch information
Showing
6 changed files
with
166 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package rx.schedulers; | ||
|
||
import rx.Observable; | ||
import rx.Observer; | ||
import rx.Scheduler; | ||
|
||
import java.util.concurrent.CountDownLatch; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.fail; | ||
|
||
final class SchedulerTests { | ||
private SchedulerTests() { | ||
// No instances. | ||
} | ||
|
||
/** | ||
* Verifies that the given Scheduler delivers unhandled errors to its executing thread's | ||
* {@link java.lang.Thread.UncaughtExceptionHandler}. | ||
* <p> | ||
* Schedulers which execute on a separate thread from their calling thread should exhibit this behavior. Schedulers | ||
* which execute on their calling thread may not. | ||
*/ | ||
static void testUnhandledErrorIsDeliveredToThreadHandler(Scheduler scheduler) throws InterruptedException { | ||
Thread.UncaughtExceptionHandler originalHandler = Thread.getDefaultUncaughtExceptionHandler(); | ||
try { | ||
CapturingUncaughtExceptionHandler handler = new CapturingUncaughtExceptionHandler(); | ||
Thread.setDefaultUncaughtExceptionHandler(handler); | ||
IllegalStateException error = new IllegalStateException("Should be delivered to handler"); | ||
Observable.error(error) | ||
.subscribeOn(scheduler) | ||
.subscribe(); | ||
|
||
if (!handler.completed.await(3, TimeUnit.SECONDS)) { | ||
fail("timed out"); | ||
} | ||
|
||
assertEquals("Should have received exactly 1 exception", 1, handler.count); | ||
Throwable cause = handler.caught; | ||
while (cause != null) { | ||
if (error.equals(cause)) break; | ||
if (cause == cause.getCause()) break; | ||
cause = cause.getCause(); | ||
} | ||
assertEquals("Our error should have been delivered to the handler", error, cause); | ||
} finally { | ||
Thread.setDefaultUncaughtExceptionHandler(originalHandler); | ||
} | ||
} | ||
|
||
/** | ||
* Verifies that the given Scheduler does not deliver handled errors to its executing Thread's | ||
* {@link java.lang.Thread.UncaughtExceptionHandler}. | ||
* <p> | ||
* This is a companion test to {@link #testUnhandledErrorIsDeliveredToThreadHandler}, and is needed only for the | ||
* same Schedulers. | ||
*/ | ||
static void testHandledErrorIsNotDeliveredToThreadHandler(Scheduler scheduler) throws InterruptedException { | ||
Thread.UncaughtExceptionHandler originalHandler = Thread.getDefaultUncaughtExceptionHandler(); | ||
try { | ||
CapturingUncaughtExceptionHandler handler = new CapturingUncaughtExceptionHandler(); | ||
CapturingObserver<Object> observer = new CapturingObserver<Object>(); | ||
Thread.setDefaultUncaughtExceptionHandler(handler); | ||
IllegalStateException error = new IllegalStateException("Should be delivered to handler"); | ||
Observable.error(error) | ||
.subscribeOn(scheduler) | ||
.subscribe(observer); | ||
|
||
if (!observer.completed.await(3, TimeUnit.SECONDS)) { | ||
fail("timed out"); | ||
} | ||
|
||
assertEquals("Handler should not have received anything", 0, handler.count); | ||
assertEquals("Observer should have received an error", 1, observer.errorCount); | ||
assertEquals("Observer should not have received a next value", 0, observer.nextCount); | ||
|
||
Throwable cause = observer.error; | ||
while (cause != null) { | ||
if (error.equals(cause)) break; | ||
if (cause == cause.getCause()) break; | ||
cause = cause.getCause(); | ||
} | ||
assertEquals("Our error should have been delivered to the observer", error, cause); | ||
} finally { | ||
Thread.setDefaultUncaughtExceptionHandler(originalHandler); | ||
} | ||
} | ||
|
||
private static final class CapturingUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { | ||
int count = 0; | ||
Throwable caught; | ||
CountDownLatch completed = new CountDownLatch(1); | ||
|
||
@Override | ||
public void uncaughtException(Thread t, Throwable e) { | ||
count++; | ||
caught = e; | ||
completed.countDown(); | ||
} | ||
} | ||
|
||
private static final class CapturingObserver<T> implements Observer<T> { | ||
CountDownLatch completed = new CountDownLatch(1); | ||
int errorCount = 0; | ||
int nextCount = 0; | ||
Throwable error; | ||
|
||
@Override | ||
public void onCompleted() { | ||
} | ||
|
||
@Override | ||
public void onError(Throwable e) { | ||
errorCount++; | ||
error = e; | ||
completed.countDown(); | ||
} | ||
|
||
@Override | ||
public void onNext(T t) { | ||
nextCount++; | ||
} | ||
} | ||
} |