blob: 08c7aa4e9ac37835f27f0214792934e4ce0c6476 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.beam.sdk.util;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isA;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import org.hamcrest.Description;
import org.hamcrest.FeatureMatcher;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for {@link UserCodeException} functionality. */
@RunWith(JUnit4.class)
public class UserCodeExceptionTest {
@Rule public ExpectedException thrown = ExpectedException.none();
@Test
public void existingUserCodeExceptionsNotWrapped() {
UserCodeException existing = UserCodeException.wrap(new IOException());
UserCodeException wrapped = UserCodeException.wrap(existing);
assertEquals(existing, wrapped);
}
@Test
public void testCauseIsSet() {
thrown.expectCause(isA(IOException.class));
throwUserCodeException();
}
@Test
public void testStackTraceIsTruncatedToUserCode() {
thrown.expectCause(hasBottomStackFrame(method("userCode")));
throwUserCodeException();
}
@Test
public void testStackTraceIsTruncatedProperlyFromHelperMethod() {
thrown.expectCause(hasBottomStackFrame(method("userCode")));
throwUserCodeExceptionFromHelper();
}
@Test
public void testWrapIfOnlyWrapsWhenTrue() {
IOException cause = new IOException();
RuntimeException wrapped = UserCodeException.wrapIf(true, cause);
assertThat(wrapped, is(instanceOf(UserCodeException.class)));
}
@Test
public void testWrapIfReturnsRuntimeExceptionWhenFalse() {
IOException cause = new IOException();
RuntimeException wrapped = UserCodeException.wrapIf(false, cause);
assertThat(wrapped, is(not(instanceOf(UserCodeException.class))));
assertEquals(cause, wrapped.getCause());
}
@Test
public void testWrapIfReturnsSourceRuntimeExceptionWhenFalse() {
RuntimeException runtimeException = new RuntimeException("oh noes!");
RuntimeException wrapped = UserCodeException.wrapIf(false, runtimeException);
assertEquals(runtimeException, wrapped);
}
@Test
public void robustAgainstEmptyStackTrace() {
RuntimeException runtimeException = new RuntimeException("empty stack");
runtimeException.setStackTrace(new StackTraceElement[0]);
RuntimeException wrapped = UserCodeException.wrap(runtimeException);
assertEquals(runtimeException, wrapped.getCause());
}
private void throwUserCodeException() {
try {
userCode();
} catch (Exception ex) {
throw UserCodeException.wrap(ex);
}
}
private void throwUserCodeExceptionFromHelper() {
try {
userCode();
} catch (Exception ex) {
throw wrap(ex);
}
}
private UserCodeException wrap(Throwable t) {
throw UserCodeException.wrap(t);
}
private void userCode() throws IOException {
userCode2();
}
private void userCode2() throws IOException {
userCode3();
}
private void userCode3() throws IOException {
IOException ex = new IOException("User processing error!");
throw ex;
}
private static ThrowableBottomStackFrameMethodMatcher hasBottomStackFrame(
Matcher<StackTraceElement> frameMatcher) {
return new ThrowableBottomStackFrameMethodMatcher(frameMatcher);
}
private static StackFrameMethodMatcher method(String methodName) {
return new StackFrameMethodMatcher(is(methodName));
}
static class ThrowableBottomStackFrameMethodMatcher
extends FeatureMatcher<Throwable, StackTraceElement> {
public ThrowableBottomStackFrameMethodMatcher(Matcher<StackTraceElement> subMatcher) {
super(subMatcher, "Throwable with bottom stack frame:", "stack frame");
}
@Override
protected StackTraceElement featureValueOf(Throwable actual) {
StackTraceElement[] stackTrace = actual.getStackTrace();
return stackTrace[stackTrace.length - 1];
}
}
static class StackFrameMethodMatcher extends TypeSafeMatcher<StackTraceElement> {
private Matcher<String> methodNameMatcher;
public StackFrameMethodMatcher(Matcher<String> methodNameMatcher) {
this.methodNameMatcher = methodNameMatcher;
}
@Override
public void describeTo(Description description) {
description.appendText("stack frame where method name ");
methodNameMatcher.describeTo(description);
}
@Override
protected boolean matchesSafely(StackTraceElement item) {
return methodNameMatcher.matches(item.getMethodName());
}
}
}