Skip to content

Commit

Permalink
Avoid JupiterTestEngine crash if Hamcrest is not on the classpath
Browse files Browse the repository at this point in the history
Prior to this commit, the JupiterTestEngine would crash without
executing any tests if JUnit 4 was on the classpath but Hamcrest was
not.

This commit fixes this bug by ensuring that initialization of the
OpenTest4JAndJUnit4AwareThrowableCollector class no longer fails if the
org.junit.internal.AssumptionViolatedException class cannot be loaded
from the classpath -- for example, due to a missing Hamcrest dependency.

Fixes #2004
  • Loading branch information
sbrannen committed Sep 7, 2019
1 parent e9baa52 commit 835d800
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ No changes.

==== Bug Fixes

* ❓
* The `JupiterTestEngine` no longer crashes without executing any tests if JUnit 4 is on
the classpath but Hamcrest is not. Specifically, initialization of the
`OpenTest4JAndJUnit4AwareThrowableCollector` class no longer fails if the
`org.junit.internal.AssumptionViolatedException` class cannot be loaded from the
classpath due to a missing Hamcrest dependency.


[[release-notes-5.5.2-junit-vintage]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import java.util.function.Predicate;

import org.junit.platform.commons.util.BlacklistedExceptions;
import org.junit.platform.commons.util.ReflectionUtils;
import org.junit.platform.engine.support.hierarchical.ThrowableCollector;
import org.opentest4j.TestAbortedException;
Expand All @@ -37,10 +38,16 @@ private static Predicate<? super Throwable> createAbortedExecutionPredicate() {
Predicate<Throwable> otaPredicate = TestAbortedException.class::isInstance;

// Additionally support JUnit 4's AssumptionViolatedException?
Class<?> clazz = ReflectionUtils.tryToLoadClass(
"org.junit.internal.AssumptionViolatedException").toOptional().orElse(null);
if (clazz != null) {
return otaPredicate.or(clazz::isInstance);
try {
Class<?> clazz = ReflectionUtils.tryToLoadClass("org.junit.internal.AssumptionViolatedException").get();
if (clazz != null) {
return otaPredicate.or(clazz::isInstance);
}
}
catch (Throwable throwable) {
BlacklistedExceptions.rethrowIfBlacklisted(throwable);
// Otherwise ignore it since it's likely a ClassNotFoundException,
// NoClassDefFoundError, or similar.
}

// Else just OTA's TestAbortedException
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2015-2019 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* https://www.eclipse.org/legal/epl-v20.html
*/

package org.junit.jupiter.engine.support;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.net.URL;
import java.net.URLClassLoader;

import org.junit.internal.AssumptionViolatedException;
import org.junit.jupiter.api.Test;
import org.junit.platform.commons.util.ReflectionUtils;

/**
* Unit tests for {@link OpenTest4JAndJUnit4AwareThrowableCollector}.
*
* @since 5.6
*/
class OpenTest4JAndJUnit4AwareThrowableCollectorTests {

@Test
void simulateHamcrestNotInTheClasspath() throws Exception {
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
HamcrestHidingClassLoader classLoader = new HamcrestHidingClassLoader();

// We have to set our custom ClassLoader as the TCCL so that
// ReflectionUtils uses it (indirectly via ClassLoaderUtils).
Thread.currentThread().setContextClassLoader(classLoader);

// Ensure that our custom ClassLoader actually throws a NoClassDefFoundError
// when attempting to load the AssumptionViolatedException class.
assertThrows(NoClassDefFoundError.class,
() -> ReflectionUtils.tryToLoadClass(AssumptionViolatedException.class.getName()));

Class<?> clazz = classLoader.loadClass(OpenTest4JAndJUnit4AwareThrowableCollector.class.getName());
assertNotNull(ReflectionUtils.newInstance(clazz));
}
finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
}

private static class HamcrestHidingClassLoader extends URLClassLoader {

HamcrestHidingClassLoader() {
super(new URL[] {
OpenTest4JAndJUnit4AwareThrowableCollector.class.getProtectionDomain().getCodeSource().getLocation() },
getSystemClassLoader());
}

@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {

// Load a new instance of the OpenTest4JAndJUnit4AwareThrowableCollector class
if (name.equals(OpenTest4JAndJUnit4AwareThrowableCollector.class.getName())) {
return findClass(name);
}

// Simulate that Hamcrest is not in the classpath when loading AssumptionViolatedException
if (name.equals(AssumptionViolatedException.class.getName())) {
throw new NoClassDefFoundError("org/hamcrest/SelfDescribing");
}

// Else
return super.loadClass(name);
}

}

}

0 comments on commit 835d800

Please sign in to comment.