Skip to content

Commit

Permalink
Fix stack overflow on TooManyInvocationsError
Browse files Browse the repository at this point in the history
  • Loading branch information
manuel-alvarez-alvarez committed Sep 24, 2024
1 parent 0dcde79 commit 30d17af
Showing 1 changed file with 55 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.JarFile;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.dynamic.ClassFileLocator;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;
import org.spockframework.mock.IMockInvocation;
import org.spockframework.mock.TooManyInvocationsError;

/**
* Runs a spock test in an agent-friendly way.
Expand Down Expand Up @@ -129,10 +134,13 @@ private static Class<?> shadowTestClass(final Class<?> clazz) {
@Override
public void run(final RunNotifier notifier) {
final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
final RunListener listener = new FixTooManyInvocationsErrorListener();
try {
Thread.currentThread().setContextClassLoader(customLoader);
notifier.addFirstListener(listener);
super.run(notifier);
} finally {
notifier.removeListener(listener);
Thread.currentThread().setContextClassLoader(contextLoader);
}
}
Expand Down Expand Up @@ -222,4 +230,51 @@ protected Class<?> loadClass(final String name, final boolean resolve)
}
}
}

/**
* This class tries to fix {@link TooManyInvocationsError} exceptions when the assertion error is
* caught by a mock triggering a stack overflow while composing the failure message.
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
@RunListener.ThreadSafe
private static class FixTooManyInvocationsErrorListener extends RunListener {

@Override
public void testFailure(final Failure failure) throws Exception {
final Throwable cause = failure.getException();
if (cause instanceof TooManyInvocationsError) {
try {
// try to trigger an error (e.g. stack overflow when composing the description)
failure.getMessage();
} catch (final Throwable e) {
fixTooManyInvocationsError((TooManyInvocationsError) cause);
}
}
}

private void fixTooManyInvocationsError(final TooManyInvocationsError error) {
final List<IMockInvocation> accepted = error.getAcceptedInvocations();
for (final IMockInvocation invocation : accepted) {
try {
invocation.toString();
} catch (final Throwable t) {
final List<Object> arguments = invocation.getArguments();
for (int i = 0; i < arguments.size(); i++) {
final Object arg = arguments.get(i);
if (arg instanceof AssertionError) {
final AssertionError updatedAssertion =
new AssertionError(
"'"
+ arg.getClass().getName()
+ "' hidden due to '"
+ t.getClass().getName()
+ "'",
t);
invocation.getArguments().set(i, updatedAssertion);
}
}
}
}
}
}
}

0 comments on commit 30d17af

Please sign in to comment.