Skip to content

Commit

Permalink
Merge pull request apache#2420 from balesh2/concurrency-rule-improvem…
Browse files Browse the repository at this point in the history
…ents

GEODE-5684: Add option to check type of cause
  • Loading branch information
nonbinaryprogrammer authored Sep 6, 2018
2 parents 4dee766 + 8bf38d8 commit 2f18763
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
package org.apache.geode.test.junit.rules;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;

import java.lang.reflect.Field;
import java.time.Duration;
Expand Down Expand Up @@ -253,26 +252,32 @@ public static class ConcurrentOperation<T> implements Callable<Void> {

private Callable<T> callable;
private int iterations;
private Throwable expectedException;
private T expectedValue;
private Duration duration;
private Boolean expectedResultIsSet;
private T expectedValue;
private Throwable expectedException;
private Class expectedExceptionType;
private Class expectedExceptionCauseType;

public ConcurrentOperation() {
callable = null;
iterations = DEFAULT_ITERATIONS;
duration = DEFAULT_DURATION;
this.expectedResultIsSet = false;
expectedException = null;
expectedExceptionType = null;
expectedExceptionCauseType = null;
expectedValue = null;
}

public ConcurrentOperation(Callable<T> toAdd) {
this.callable = toAdd;
iterations = DEFAULT_ITERATIONS;
duration = DEFAULT_DURATION;
this.expectedResultIsSet = false;
expectedException = null;
expectedExceptionType = null;
expectedExceptionCauseType = null;
expectedValue = null;
}

Expand Down Expand Up @@ -321,11 +326,12 @@ public ConcurrentOperation repeatForDuration(Duration duration) {
* @return this, the ConcurrentOperation (containing a callable) that has been set to repeat
*/
public ConcurrentOperation expectException(Throwable expectedException) {
if (expectedExceptionType != null || expectedValue != null) {
if (expectedResultIsSet) {
throw new IllegalArgumentException("Specify only one expected outcome.");
}

this.expectedException = expectedException;
this.expectedResultIsSet = true;
return this;
}

Expand All @@ -342,6 +348,25 @@ public ConcurrentOperation expectExceptionType(Class expectedExceptionType) {
}

this.expectedExceptionType = expectedExceptionType;
this.expectedResultIsSet = true;
return this;
}

/**
* Sets the expected result of running the thread to be an exception with a cause that is an
* instance of the given class
*
* @param expectedExceptionCauseType the class of the expected exception cause. The exception
* itself will not be checked.
* @return this, the ConcurrentOperation (containing a callable) that has been set to repeat
*/
public ConcurrentOperation expectExceptionCauseType(Class expectedExceptionCauseType) {
if (expectedException != null || expectedValue != null) {
throw new IllegalArgumentException("Specify only one expected outcome.");
}

this.expectedExceptionCauseType = expectedExceptionCauseType;
this.expectedResultIsSet = true;
return this;
}

Expand All @@ -353,11 +378,12 @@ public ConcurrentOperation expectExceptionType(Class expectedExceptionType) {
* @return this, the ConcurrentOperation (containing a callable) that has been set to repeat
*/
public ConcurrentOperation expectValue(T expectedValue) {
if (expectedExceptionType != null || expectedException != null) {
if (this.expectedResultIsSet) {
throw new IllegalArgumentException("Specify only one expected outcome.");
}

this.expectedValue = expectedValue;
this.expectedResultIsSet = true;
return this;
}

Expand All @@ -376,28 +402,44 @@ public Void call() throws Exception {
}

private void callAndValidate() throws Exception {
if (expectedValue != null) {
assertThat(this.callable.call()).isEqualTo(this.expectedValue);
} else if (expectedException != null) {
Throwable thrown = catchThrowable(() -> this.callable.call());
checkThrown(this.expectedException, thrown);
} else if (expectedExceptionType != null) {
Throwable thrown = catchThrowable(() -> this.callable.call());
assertThat(thrown).isInstanceOf(this.expectedExceptionType);
Exception exception = null;

try {
T retVal = this.callable.call();

if (this.expectedValue != null) {
assertThat(retVal).isEqualTo(this.expectedValue);
}
} catch (Exception e) {
exception = e;
}

if (this.expectedExceptionCauseType != null && this.expectedExceptionType != null) {
assertThat(exception).isInstanceOf(this.expectedExceptionType)
.hasCauseInstanceOf(this.expectedExceptionCauseType);
} else if (this.expectedExceptionType != null) {
assertThat(exception).isInstanceOf(this.expectedExceptionType);
} else if (this.expectedExceptionCauseType != null) {
assertThat(exception).hasCauseInstanceOf(this.expectedExceptionCauseType);
} else if (this.expectedException != null) {
checkThrown(exception, this.expectedException);
} else {
this.callable.call();
if (exception != null) {
throw exception; // rethrow if we weren't expecting any exception and got one
}
}

}

private void checkThrown(Throwable expected, Throwable actual) {
private void checkThrown(Throwable actual, Throwable expected) {
assertThat(actual).isInstanceOf(expected.getClass());

if (expected.getMessage() != null) {
assertThat(actual).hasMessage(expected.getMessage());
}

if (expected.getCause() != null) {
checkThrown(expected.getCause(), actual.getCause());
checkThrown(actual.getCause(), expected.getCause());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,23 @@ public class ConcurrencyRuleTest {
return Integer.valueOf(72);
};

private final Callable<Void> callWithExceptionAndCause = () -> {
Exception e = new IllegalStateException("Oh boy, here I go testin' again");
e.initCause(new NullPointerException());
throw e;
};

private final Callable<Void> callWithExceptionAndRepeatCount = () -> {
iterations.incrementAndGet();
throw new IllegalStateException("Oh boy, here I go testin' again");
};

private final Callable<Void> callWithOneExceptionAndRepeatCount = () -> {
private final Callable<Integer> callWithOneExceptionAndRepeatCount = () -> {
int currentIteration = iterations.incrementAndGet();
if (currentIteration == stopIteration) {
throw new IllegalStateException("Oh boy, here I go testin' again");
}
return null;
return Integer.valueOf(72);
};

@Rule
Expand Down Expand Up @@ -115,26 +121,16 @@ public void runAndExpectException(Execution execution) {
@Test
@Parameters({"EXECUTE_IN_SERIES", "EXECUTE_IN_PARALLEL"})
public void runAndExpectException_throwableInstanceWithCauses(Execution execution) {
Callable<?> callable = () -> {
NullPointerException cause = new NullPointerException();
IllegalStateException toThrow = new IllegalStateException("Oh boy, here I go testin' again");
toThrow.initCause(cause);
throw toThrow;
};

concurrencyRule.add(callable).expectException(expectedExceptionWithCause);
concurrencyRule.add(callWithExceptionAndCause).expectException(expectedExceptionWithCause);
execution.execute(concurrencyRule);
}

@Test
@Parameters({"EXECUTE_IN_SERIES", "EXECUTE_IN_PARALLEL"})
public void runAndExpectException_throwableInstanceWithCauses_failsIfCauseDoesNotMatch(
Execution execution) {
Callable<Void> callable = () -> {
throw new IllegalStateException("Oh boy, here I go testin' again");
};

concurrencyRule.add(callable).expectException(expectedExceptionWithCause);
concurrencyRule.add(callWithExceptionAndRepeatCount)
.expectException(expectedExceptionWithCause);

assertThatThrownBy(() -> execution.execute(concurrencyRule))
.isInstanceOf(AssertionError.class)
Expand Down Expand Up @@ -165,6 +161,58 @@ public void runAndExpectException_throwableInstance_wrongClass_fails(Execution e
.isInstanceOf(AssertionError.class);
}

@Test
@Parameters({"EXECUTE_IN_SERIES", "EXECUTE_IN_PARALLEL"})
public void runAndExpectException_failsIfNoExceptionIsThrown(Execution execution) {
concurrencyRule.add(callWithRetVal).expectException(expectedExceptionWithCause);
assertThatThrownBy(() -> execution.execute(concurrencyRule))
.isInstanceOf(AssertionError.class)
.hasMessageContaining("Expecting actual not to be null");
}

@Test
@Parameters({"EXECUTE_IN_SERIES", "EXECUTE_IN_PARALLEL"})
public void runAndExpectExceptionCauseType(Execution execution) {
concurrencyRule.add(callWithExceptionAndCause)
.expectExceptionCauseType(expectedExceptionWithCause.getCause().getClass());
execution.execute(concurrencyRule);
}

@Test
@Parameters({"EXECUTE_IN_SERIES", "EXECUTE_IN_PARALLEL"})
public void runAndExpectExceptionAndCauseTypes(Execution execution) {
concurrencyRule.add(callWithExceptionAndCause)
.expectExceptionType(expectedExceptionWithCause.getClass())
.expectExceptionCauseType(expectedExceptionWithCause.getCause().getClass());
execution.execute(concurrencyRule);
}

@Test
@Parameters({"EXECUTE_IN_SERIES", "EXECUTE_IN_PARALLEL"})
public void runAndExpectExceptionAndCauseTypes_wrongExceptionTypeFails(Execution execution) {
concurrencyRule.add(callWithExceptionAndCause)
.expectExceptionType(NullPointerException.class)
.expectExceptionCauseType(expectedExceptionWithCause.getCause().getClass());
assertThatThrownBy(() -> execution.execute(concurrencyRule)).isInstanceOf(AssertionError.class);
}

@Test
@Parameters({"EXECUTE_IN_SERIES", "EXECUTE_IN_PARALLEL"})
public void runAndExpectExceptionAndCauseTypes_wrongCauseTypeFails(Execution execution) {
concurrencyRule.add(callWithExceptionAndCause)
.expectExceptionType(expectedExceptionWithCause.getClass())
.expectExceptionCauseType(IllegalStateException.class);
assertThatThrownBy(() -> execution.execute(concurrencyRule)).isInstanceOf(AssertionError.class);
}

@Test
@Parameters({"EXECUTE_IN_SERIES", "EXECUTE_IN_PARALLEL"})
public void runAndExpectExceptionCauseType_wrongTypeFails(Execution execution) {
concurrencyRule.add(callWithExceptionAndCause)
.expectExceptionCauseType(expectedExceptionWithCause.getClass());
assertThatThrownBy(() -> execution.execute(concurrencyRule)).isInstanceOf(AssertionError.class);
}

@Test
@Parameters({"EXECUTE_IN_SERIES", "EXECUTE_IN_PARALLEL"})
public void runAndExpectNoException_withNoReturn(Execution execution) {
Expand Down Expand Up @@ -201,6 +249,77 @@ public void runAndExpectValue_failsForWrongValue(Execution execution) {
assertThat(invoked.get()).isTrue();
}

@Test
@Parameters({"EXECUTE_IN_SERIES", "EXECUTE_IN_PARALLEL"})
public void runAndExpectValueRepeatedly_failsIfExceptionIsThrown(Execution execution) {
concurrencyRule.add(callWithOneExceptionAndRepeatCount).expectValue(expectedRetVal)
.repeatForIterations(5);
assertThatThrownBy(() -> execution.execute(concurrencyRule))
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining(expectedException.getMessage());
assertThat(iterations.get()).isEqualTo(stopIteration);
}

@Test
public void failsWhenMultipleReturnValuesExpected_ExceptionAndReturn() {
try {
Throwable thrown = catchThrowable(() -> concurrencyRule.add(callWithRetVal)
.expectException(expectedException)
.expectValue(expectedRetVal));

assertThat(thrown)
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Specify only one expected outcome.");
} finally {
concurrencyRule.clear();
}
}

@Test
public void failsWhenMultipleReturnValuesExpected_ExceptionAndType() {
try {
Throwable thrown = catchThrowable(() -> concurrencyRule.add(callWithRetVal)
.expectException(expectedException)
.expectExceptionType(expectedException.getClass()));

assertThat(thrown)
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Specify only one expected outcome.");
} finally {
concurrencyRule.clear();
}
}

@Test
public void failsWhenMultipleReturnValuesExpected_ResultAndType() {
try {
Throwable thrown = catchThrowable(() -> concurrencyRule.add(callWithRetVal)
.expectValue(expectedRetVal)
.expectExceptionType(expectedException.getClass()));

assertThat(thrown)
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Specify only one expected outcome.");
} finally {
concurrencyRule.clear();
}
}

@Test
public void failsWhenMultipleReturnValuesExpected_ResultAndCauseType() {
try {
Throwable thrown = catchThrowable(() -> concurrencyRule.add(callWithRetVal)
.expectValue(expectedRetVal)
.expectExceptionCauseType(expectedException.getClass()));

assertThat(thrown)
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Specify only one expected outcome.");
} finally {
concurrencyRule.clear();
}
}

@Test
@Parameters({"EXECUTE_IN_SERIES", "EXECUTE_IN_PARALLEL"})
public void repeatForIterations(Execution execution) {
Expand Down

0 comments on commit 2f18763

Please sign in to comment.