From 12ba5936b736a2886220f9ae4a6492558dba4e14 Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Thu, 16 Jul 2020 20:44:22 +0200 Subject: [PATCH] Fixes #1855 and #939: improve error message when the inline mock maker cannot be used. --- .../InjectingAnnotationEngine.java | 18 +++++++- .../bytebuddy/InlineByteBuddyMockMaker.java | 42 ++++++++++++++----- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/mockito/internal/configuration/InjectingAnnotationEngine.java b/src/main/java/org/mockito/internal/configuration/InjectingAnnotationEngine.java index 2f6667015c..59239a4674 100644 --- a/src/main/java/org/mockito/internal/configuration/InjectingAnnotationEngine.java +++ b/src/main/java/org/mockito/internal/configuration/InjectingAnnotationEngine.java @@ -58,7 +58,7 @@ private List processInjectMocks( List closeables = new ArrayList<>(); Class classContext = clazz; while (classContext != Object.class) { - closeables.add(injectMocks(testInstance)); + closeables.add(injectCloseableMocks(testInstance)); classContext = classContext.getSuperclass(); } return closeables; @@ -79,6 +79,20 @@ private List processIndependentAnnotations( return closeables; } + /** + * Required by PowerMockito and retained to avoid API breakage despite being internal API. + * + * @deprecated Use {@link InjectingAnnotationEngine#injectCloseableMocks(Object)}. + */ + @Deprecated + public void injectMocks(Object testClassInstance) { + try { + injectCloseableMocks(testClassInstance).close(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + /** * Initializes mock/spies dependencies for objects annotated with * @InjectMocks for given testClassInstance. @@ -88,7 +102,7 @@ private List processIndependentAnnotations( * @param testClassInstance * Test class, usually this */ - private AutoCloseable injectMocks(final Object testClassInstance) { + private AutoCloseable injectCloseableMocks(final Object testClassInstance) { Class clazz = testClassInstance.getClass(); Set mockDependentFields = new HashSet(); Set mocks = newMockSafeHashSet(); diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java index 89c1ac8622..097b8c8aae 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java @@ -4,15 +4,13 @@ */ package org.mockito.internal.creation.bytebuddy; -import static org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.EXCLUDES; -import static org.mockito.internal.util.StringUtil.join; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.instrument.Instrumentation; import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; @@ -20,8 +18,6 @@ import java.util.jar.JarFile; import java.util.jar.JarOutputStream; -import javax.tools.ToolProvider; - import net.bytebuddy.agent.ByteBuddyAgent; import org.mockito.Incubating; import org.mockito.creation.instance.Instantiator; @@ -35,6 +31,9 @@ import org.mockito.mock.MockCreationSettings; import org.mockito.plugins.InlineMockMaker; +import static org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.*; +import static org.mockito.internal.util.StringUtil.*; + /** * Agent and subclass based mock maker. *

@@ -191,12 +190,34 @@ public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker, InlineM public InlineByteBuddyMockMaker() { if (INITIALIZATION_ERROR != null) { + String detail; + if (System.getProperty("java.specification.vendor", "") + .toLowerCase() + .contains("android")) { + detail = + "It appears as if you are trying to run this mock maker on Android which does not support the instrumentation API."; + } else { + try { + if (Class.forName("javax.tools.ToolProvider") + .getMethod("getSystemJavaCompiler") + .invoke(null) + == null) { + detail = + "It appears as if you are running on a JRE. Either install a JDK or add JNA to the class path."; + } else { + detail = + "It appears as if your JDK does not supply a working agent attachment mechanism."; + } + } catch (Throwable ignored) { + detail = + "It appears as if you are running an incomplete JVM installation that might not support all tooling APIs"; + } + } throw new MockitoInitializationException( join( - "Could not initialize inline Byte Buddy mock maker. (This mock maker is not supported on Android.)", - ToolProvider.getSystemJavaCompiler() == null - ? "Are you running a JRE instead of a JDK? The inline mock maker needs to be run on a JDK.\n" - : "", + "Could not initialize inline Byte Buddy mock maker.", + "", + detail, Platform.describe()), INITIALIZATION_ERROR); } @@ -384,11 +405,12 @@ public StaticMockControl createStaticMock( + "to avoid infinitive loops within Mockito's implementation of static mock handling"); } else if (type == Thread.class || type == System.class + || type == Arrays.class || ClassLoader.class.isAssignableFrom(type)) { throw new MockitoException( "It is not possible to mock static methods of " + type.getTypeName() - + "to avoid interfering with the class loading mechanism"); + + " to avoid interfering with class loading what leads to infinite loops"); } bytecodeGenerator.mockClassStatic(type);