Skip to content

Commit

Permalink
Fixes #2201 : Code review fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrey Kozel committed Jan 26, 2022
1 parent d86cb6b commit 77f7dbd
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 44 deletions.
Expand Up @@ -6,7 +6,10 @@

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import org.mockito.internal.invocation.AbstractAwareMethod;
import org.mockito.internal.util.MockUtil;
Expand All @@ -25,18 +28,36 @@ public InvocationInfo(InvocationOnMock theInvocation) {
this.invocation = theInvocation;
}

public boolean isValidException(Throwable throwable) {
public boolean isValidException(final Throwable throwable) {
if (isValidException(method, throwable)) {
return true;
}

return Arrays.stream(method.getDeclaringClass().getInterfaces())
.anyMatch(parent -> isValidExceptionForParent(parent, throwable));
return isValidExceptionForParents(method.getDeclaringClass(), throwable);
}

private boolean isValidExceptionForParent(Class<?> parent, Throwable throwable) {
private boolean isValidExceptionForParents(final Class<?> parent, final Throwable throwable) {
final List<Class<?>> ancestors = new ArrayList<>(Arrays.asList(parent.getInterfaces()));

if (parent.getSuperclass() != null) {
ancestors.add(parent.getSuperclass());
}

final boolean validException =
ancestors.stream()
.anyMatch(ancestor -> isValidExceptionForClass(ancestor, throwable));

if (validException) {
return true;
}

return ancestors.stream()
.anyMatch(ancestor -> isValidExceptionForParents(ancestor, throwable));
}

private boolean isValidExceptionForClass(final Class<?> parent, final Throwable throwable) {
try {
Method parentMethod =
final Method parentMethod =
parent.getMethod(this.method.getName(), this.method.getParameterTypes());
return isValidException(parentMethod, throwable);
} catch (NoSuchMethodException e) {
Expand All @@ -45,10 +66,10 @@ private boolean isValidExceptionForParent(Class<?> parent, Throwable throwable)
}
}

private boolean isValidException(Method method, Throwable throwable) {
Class<?>[] exceptions = method.getExceptionTypes();
Class<?> throwableClass = throwable.getClass();
for (Class<?> exception : exceptions) {
private boolean isValidException(final Method method, final Throwable throwable) {
final Class<?>[] exceptions = method.getExceptionTypes();
final Class<?> throwableClass = throwable.getClass();
for (final Class<?> exception : exceptions) {
if (exception.isAssignableFrom(throwableClass)) {
return true;
}
Expand Down
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2022 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.internal.stubbing.answers;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.mockito.internal.invocation.InvocationBuilder;
import org.mockito.invocation.Invocation;

import java.nio.charset.CharacterCodingException;
import java.util.Arrays;
import java.util.Collection;

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

@RunWith(Parameterized.class)
public class InvocationInfoExceptionTest {

private final String methodName;

public InvocationInfoExceptionTest(final String methodName) {
this.methodName = methodName;
}

@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(
new Object[][] {
{"throwException"},
{"parentThrowsException"},
{"grandParentThrowsException"},
{"interfaceThrowsException"},
{"grandInterfaceThrowsException"},
{"interfaceOfParentThrowsException"}
});
}

@Test
public void should_know_valid_throwables() throws Exception {
// when
final Invocation invocation =
new InvocationBuilder()
.method(methodName)
.mockClass(CurrentClass.class)
.toInvocation();
final InvocationInfo info = new InvocationInfo(invocation);

// then
assertThat(info.isValidException(new Exception())).isFalse();
assertThat(info.isValidException(new CharacterCodingException())).isTrue();
}

private abstract static class GrandParent {
public abstract void grandParentThrowsException() throws CharacterCodingException;
}

private interface InterfaceOfParent {
abstract void interfaceOfParentThrowsException() throws CharacterCodingException;
}

private abstract static class Parent extends GrandParent implements InterfaceOfParent {
public abstract void parentThrowsException() throws CharacterCodingException;
}

private interface GrandInterface {
void grandInterfaceThrowsException() throws CharacterCodingException;
}

private interface Interface extends GrandInterface {
void interfaceThrowsException() throws CharacterCodingException;
}

private static class CurrentClass extends Parent implements Interface {

public void throwException() throws CharacterCodingException {}

@Override
public void grandParentThrowsException() {}

@Override
public void parentThrowsException() {}

@Override
public void grandInterfaceThrowsException() {}

@Override
public void interfaceThrowsException() {}

@Override
public void interfaceOfParentThrowsException() {

}
}
}
Expand Up @@ -9,41 +9,13 @@
import static org.mockitoutil.TestBase.getLastInvocation;

import java.lang.reflect.Method;
import java.nio.charset.CharacterCodingException;

import org.junit.Test;
import org.mockito.internal.invocation.InvocationBuilder;
import org.mockito.invocation.Invocation;
import org.mockitousage.IMethods;
import org.mockitousage.MethodsImpl;

public class InvocationInfoTest {

@Test
public void should_know_valid_throwables() throws Exception {
// when
Invocation invocation = new InvocationBuilder().method("canThrowException").toInvocation();
InvocationInfo info = new InvocationInfo(invocation);

// then
assertThat(info.isValidException(new Exception())).isFalse();
assertThat(info.isValidException(new CharacterCodingException())).isTrue();
}

@Test
public void should_mark_interface_overridden_exceptions_as_valid() {
// when
Invocation invocation =
new InvocationBuilder()
.method("throwsRemovedInSubclass")
.mockClass(MethodsImpl.class)
.toInvocation();
InvocationInfo info = new InvocationInfo(invocation);

// then
assertThat(info.isValidException(new CharacterCodingException())).isTrue();
}

@Test
public void should_know_valid_return_types() throws Exception {
assertThat(
Expand Down
2 changes: 0 additions & 2 deletions src/test/java/org/mockitousage/IMethods.java
Expand Up @@ -159,8 +159,6 @@ String simpleMethod(

String canThrowException() throws CharacterCodingException;

String throwsRemovedInSubclass() throws CharacterCodingException;

String oneArray(String[] array);

void varargsString(int i, String... string);
Expand Down
5 changes: 0 additions & 5 deletions src/test/java/org/mockitousage/MethodsImpl.java
Expand Up @@ -303,11 +303,6 @@ public String canThrowException() throws CharacterCodingException {
return null;
}

@Override
public String throwsRemovedInSubclass() {
return null;
}

public String oneArray(String[] array) {
return null;
}
Expand Down

0 comments on commit 77f7dbd

Please sign in to comment.