blob: 23223effb9361b1cfe05bfacad93027321fd03e0 [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* 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.google.web.bindery.requestfactory.gwt.client;
import com.google.gwt.core.client.GWT;
import com.google.web.bindery.event.shared.UmbrellaException;
import com.google.web.bindery.requestfactory.shared.Receiver;
import com.google.web.bindery.requestfactory.shared.RequestContext;
import com.google.web.bindery.requestfactory.shared.ServerFailure;
import com.google.web.bindery.requestfactory.shared.SimpleFooProxy;
import com.google.web.bindery.requestfactory.shared.SimpleFooRequest;
import java.util.Set;
import javax.validation.ConstraintViolation;
/**
* Tests that an exception thrown by a {@link Receiver} does not prevent other
* {@link Receiver}s from being called.
*/
public class RequestFactoryExceptionPropagationTest extends
RequestFactoryTestBase {
/*
* DO NOT USE finishTest(). Instead, call finishTestAndReset();
*/
private class CountingReceiver extends Receiver<Object> {
int constraintViolationCallCount;
int failureCallCount;
int successCallCount;
int violationCallCount;
public void assertCounts(int expectedFailureCallCount,
int expectedSuccessCallCount, int expectedViolationCallCount) {
assertEquals(expectedFailureCallCount, failureCallCount);
assertEquals(expectedSuccessCallCount, successCallCount);
assertEquals(expectedViolationCallCount, constraintViolationCallCount);
assertEquals(expectedViolationCallCount, violationCallCount);
}
@Override
public void onConstraintViolation(Set<ConstraintViolation<?>> errors) {
constraintViolationCallCount++;
// Forward to onViolation
super.onConstraintViolation(errors);
}
@Override
public void onFailure(ServerFailure error) {
failureCallCount++;
}
@Override
public void onSuccess(Object response) {
successCallCount++;
}
@SuppressWarnings("deprecation")
@Override
public void onViolation(Set<com.google.web.bindery.requestfactory.shared.Violation> errors) {
violationCallCount++;
}
}
private class ThrowingReceiver<T> extends Receiver<T> {
private final RuntimeException e;
public ThrowingReceiver(RuntimeException e) {
this.e = e;
}
@Override
public void onFailure(ServerFailure error) {
throw e;
}
@Override
public void onSuccess(T response) {
throw e;
}
@SuppressWarnings("deprecation")
@Override
public void onViolation(Set<com.google.web.bindery.requestfactory.shared.Violation> errors) {
throw e;
}
}
private static final int DELAY_TEST_FINISH = 10 * 1000;
GWT.UncaughtExceptionHandler defaultUncaughtExceptionHandler;
@Override
public String getModuleName() {
return "com.google.web.bindery.requestfactory.gwt.RequestFactorySuite";
}
@Override
public void gwtSetUp() {
super.gwtSetUp();
defaultUncaughtExceptionHandler = GWT.getUncaughtExceptionHandler();
}
/**
* Test mixed invocation failure and success. One receiver will throw from
* onSuccess and another from onFailure. Other receivers onSuccess and
* onFailure will corectly be called.
*/
public void testMixedSuccessAndFailureThrow() {
delayTestFinish(DELAY_TEST_FINISH);
final RuntimeException exception1 = new RuntimeException("first exception");
final RuntimeException exception2 = new RuntimeException("second exception");
final CountingReceiver count = new CountingReceiver();
SimpleFooRequest context = req.simpleFooRequest();
// 42 is the crash causing magic number for a runtime exception
context.pleaseCrash(42).to(count);
context.returnNullString().to(new ThrowingReceiver<String>(exception1));
context.returnNullString().to(count);
fireContextAndCatch(context, new ThrowingReceiver<Void>(exception2),
new GWT.UncaughtExceptionHandler() {
public void onUncaughtException(Throwable e) {
if (e instanceof UmbrellaException) {
count.assertCounts(1, 1, 0);
Set<Throwable> causes = ((UmbrellaException) e).getCauses();
assertEquals(2, causes.size());
assertTrue(causes.contains(exception1));
assertTrue(causes.contains(exception2));
finishTestAndReset();
} else {
defaultUncaughtExceptionHandler.onUncaughtException(e);
}
}
});
}
/**
* Test invocation failure. Other invocations' onSuccess should correctly be
* called.
*/
public void testOnFailureThrow() {
delayTestFinish(DELAY_TEST_FINISH);
final RuntimeException exception1 = new RuntimeException("first exception");
final RuntimeException exception2 = new RuntimeException("second exception");
final CountingReceiver count = new CountingReceiver();
SimpleFooRequest context = req.simpleFooRequest();
context.returnNullString().to(count);
// 42 is the crash causing magic number for a runtime exception
context.pleaseCrash(42).to(new ThrowingReceiver<Void>(exception1));
context.returnNullString().to(count);
fireContextAndCatch(context, new ThrowingReceiver<Void>(exception2),
new GWT.UncaughtExceptionHandler() {
public void onUncaughtException(Throwable e) {
if (e instanceof UmbrellaException) {
count.assertCounts(0, 2, 0);
Set<Throwable> causes = ((UmbrellaException) e).getCauses();
assertEquals(2, causes.size());
assertTrue(causes.contains(exception1));
assertTrue(causes.contains(exception2));
finishTestAndReset();
} else {
defaultUncaughtExceptionHandler.onUncaughtException(e);
}
}
});
}
/**
* Test global failure. All receivers will have their onFailure called, and
* some of them will throw.
*/
public void testOnGlobalFailureThrow() {
delayTestFinish(DELAY_TEST_FINISH);
final RuntimeException exception1 = new RuntimeException("first exception");
final RuntimeException exception2 = new RuntimeException("second exception");
final CountingReceiver count = new CountingReceiver();
SimpleFooRequest context = req.simpleFooRequest();
SimpleFooProxy newFoo = context.create(SimpleFooProxy.class);
context.returnNullString().to(count);
context.persist().using(newFoo).to(new ThrowingReceiver<Void>(exception1));
context.returnNullString().to(count);
final SimpleFooProxy mutableFoo = context.edit(newFoo);
// 42 is the crash causing magic number for a runtime exception
mutableFoo.setPleaseCrash(42);
fireContextAndCatch(context, new ThrowingReceiver<Void>(exception2),
new GWT.UncaughtExceptionHandler() {
public void onUncaughtException(Throwable e) {
if (e instanceof UmbrellaException) {
count.assertCounts(2, 0, 0);
Set<Throwable> causes = ((UmbrellaException) e).getCauses();
assertEquals(2, causes.size());
assertTrue(causes.contains(exception1));
assertTrue(causes.contains(exception2));
finishTestAndReset();
} else {
defaultUncaughtExceptionHandler.onUncaughtException(e);
}
}
});
}
/**
* All receivers will have their onSuccess called, and some of them will
* throw.
*/
public void testOnSuccessThrow() {
delayTestFinish(DELAY_TEST_FINISH);
final RuntimeException exception1 = new RuntimeException("first exception");
final RuntimeException exception2 = new RuntimeException("second exception");
final CountingReceiver count = new CountingReceiver();
SimpleFooRequest context = req.simpleFooRequest();
context.returnNullString().to(count);
context.returnNullString().to(new ThrowingReceiver<String>(exception1));
context.returnNullString().to(count);
fireContextAndCatch(context, new ThrowingReceiver<Void>(exception2),
new GWT.UncaughtExceptionHandler() {
public void onUncaughtException(Throwable e) {
if (e instanceof UmbrellaException) {
count.assertCounts(0, 2, 0);
Set<Throwable> causes = ((UmbrellaException) e).getCauses();
assertEquals(2, causes.size());
assertTrue(causes.contains(exception1));
assertTrue(causes.contains(exception2));
finishTestAndReset();
} else {
defaultUncaughtExceptionHandler.onUncaughtException(e);
}
}
});
}
/**
* Test violations. All receivers will have their onViolation called, and some
* of them will throw.
*/
public void testOnViolationThrow() {
delayTestFinish(DELAY_TEST_FINISH);
final RuntimeException exception1 = new RuntimeException("first exception");
final RuntimeException exception2 = new RuntimeException("second exception");
final CountingReceiver count = new CountingReceiver();
SimpleFooRequest context = req.simpleFooRequest();
SimpleFooProxy newFoo = context.create(SimpleFooProxy.class);
newFoo.setUserName("a"); // too short
context.returnNullString().to(count);
context.persist().using(newFoo).to(new ThrowingReceiver<Void>(exception1));
context.returnNullString().to(count);
fireContextAndCatch(context, new ThrowingReceiver<Void>(exception2),
new GWT.UncaughtExceptionHandler() {
public void onUncaughtException(Throwable e) {
if (e instanceof UmbrellaException) {
count.assertCounts(0, 0, 2);
Set<Throwable> causes = ((UmbrellaException) e).getCauses();
assertEquals(2, causes.size());
assertTrue(causes.contains(exception1));
assertTrue(causes.contains(exception2));
finishTestAndReset();
} else {
defaultUncaughtExceptionHandler.onUncaughtException(e);
}
}
});
}
protected void fireContextAndCatch(RequestContext context,
Receiver<Void> receiver, GWT.UncaughtExceptionHandler exceptionHandler) {
GWT.setUncaughtExceptionHandler(exceptionHandler);
if (receiver == null) {
context.fire();
} else {
context.fire(receiver);
}
}
@Override
protected void gwtTearDown() {
GWT.setUncaughtExceptionHandler(defaultUncaughtExceptionHandler);
}
}