From 9bc76015b6087c11f99df07130d2437168795ca0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Jul 2022 20:20:21 +0200 Subject: [PATCH 01/17] Bump kotlinVersion from 1.7.0 to 1.7.10 (#2705) Bumps `kotlinVersion` from 1.7.0 to 1.7.10. Updates `kotlin-gradle-plugin` from 1.7.0 to 1.7.10 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/commits) Updates `kotlin-stdlib` from 1.7.0 to 1.7.10 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/commits) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin:kotlin-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlin:kotlin-stdlib dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- gradle/dependencies.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index f9415563a3..016fd23f45 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { classpath 'com.google.googlejavaformat:google-java-format:1.15.0' classpath 'com.android.tools.build:gradle:4.2.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.0" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10" } } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index c568bdfb5e..a8499741be 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -34,7 +34,7 @@ libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.3.1' libraries.groovy = 'org.codehaus.groovy:groovy:3.0.11' -def kotlinVersion = '1.7.0' +def kotlinVersion = '1.7.10' libraries.kotlin = [ version: kotlinVersion, From ce8f617574787f822c125b9dbd11dd8ef40b6a05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 20:22:39 +0200 Subject: [PATCH 02/17] Bump shipkit-auto-version from 1.2.0 to 1.2.1 (#2709) Bumps shipkit-auto-version from 1.2.0 to 1.2.1. --- updated-dependencies: - dependency-name: org.shipkit:shipkit-auto-version dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 016fd23f45..7d2058ad10 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { classpath "io.github.gradle-nexus:publish-plugin:1.1.0" classpath 'org.shipkit:shipkit-changelog:1.2.0' - classpath 'org.shipkit:shipkit-auto-version:1.2.0' + classpath 'org.shipkit:shipkit-auto-version:1.2.1' classpath 'com.google.googlejavaformat:google-java-format:1.15.0' classpath 'com.android.tools.build:gradle:4.2.0' From de63c24352d035dff77e9ef8e17481d9d85e87d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Jul 2022 20:21:17 +0200 Subject: [PATCH 03/17] Bump groovy from 3.0.11 to 3.0.12 (#2711) Bumps [groovy](https://github.com/apache/groovy) from 3.0.11 to 3.0.12. - [Release notes](https://github.com/apache/groovy/releases) - [Commits](https://github.com/apache/groovy/commits) --- updated-dependencies: - dependency-name: org.codehaus.groovy:groovy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index a8499741be..9b5eb4fc94 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -32,7 +32,7 @@ libraries.osgi = 'org.osgi:osgi.core:8.0.0' libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.18.0' libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.3.1' -libraries.groovy = 'org.codehaus.groovy:groovy:3.0.11' +libraries.groovy = 'org.codehaus.groovy:groovy:3.0.12' def kotlinVersion = '1.7.10' libraries.kotlin = [ From 98a0a0bddd220d0e92318c061d70d1da44462e9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Jul 2022 21:32:22 +0200 Subject: [PATCH 04/17] Bump versions.junitJupiter from 5.8.2 to 5.9.0 (#2712) Bumps `versions.junitJupiter` from 5.8.2 to 5.9.0. Updates `junit-jupiter-api` from 5.8.2 to 5.9.0 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) Updates `junit-jupiter-engine` from 5.8.2 to 5.9.0 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) Updates `junit-vintage-engine` from 5.8.2 to 5.9.0 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.junit.vintage:junit-vintage-engine dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 9b5eb4fc94..53543ea02a 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -5,7 +5,7 @@ ext { def versions = [:] versions.bytebuddy = '1.12.12' -versions.junitJupiter = '5.8.2' +versions.junitJupiter = '5.9.0' versions.errorprone = '2.14.0' libraries.junit4 = 'junit:junit:4.13.2' From 2bcc19a668dd74590e88213225f9e70fb79952c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Jul 2022 21:32:31 +0200 Subject: [PATCH 05/17] Bump junit-platform-launcher from 1.8.2 to 1.9.0 (#2713) Bumps [junit-platform-launcher](https://github.com/junit-team/junit5) from 1.8.2 to 1.9.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/commits) --- updated-dependencies: - dependency-name: org.junit.platform:junit-platform-launcher dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 53543ea02a..b9a2b066cd 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -10,7 +10,7 @@ versions.errorprone = '2.14.0' libraries.junit4 = 'junit:junit:4.13.2' libraries.junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}" -libraries.junitPlatformLauncher = 'org.junit.platform:junit-platform-launcher:1.8.2' +libraries.junitPlatformLauncher = 'org.junit.platform:junit-platform-launcher:1.9.0' libraries.junitJupiterEngine = "org.junit.jupiter:junit-jupiter-engine:${versions.junitJupiter}" libraries.junitVintageEngine = "org.junit.vintage:junit-vintage-engine:${versions.junitJupiter}" libraries.assertj = 'org.assertj:assertj-core:3.23.1' From 6e9c5facf08b0cbb527892b04b88e74ba31d9ab8 Mon Sep 17 00:00:00 2001 From: 198812345678 Date: Thu, 28 Jul 2022 19:54:13 +0800 Subject: [PATCH 06/17] Fix typo in comment of InternalRunner.java (#2715) Co-authored-by: wwang7 --- src/main/java/org/mockito/internal/runners/InternalRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/internal/runners/InternalRunner.java b/src/main/java/org/mockito/internal/runners/InternalRunner.java index d06d005c32..bc8d66cb62 100644 --- a/src/main/java/org/mockito/internal/runners/InternalRunner.java +++ b/src/main/java/org/mockito/internal/runners/InternalRunner.java @@ -10,7 +10,7 @@ /** * I'm using this surrogate interface to hide internal Runner implementations. - * Surrogate cannot be used with @RunWith therefore it is less likely clients will use interal runners. + * Surrogate cannot be used with @RunWith therefore it is less likely clients will use internal runners. */ public interface InternalRunner extends Filterable { From 507ac8770d7e8ef78db465c54f47e7b57258c370 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Jul 2022 20:26:25 +0200 Subject: [PATCH 07/17] Bump com.diffplug.spotless from 6.8.0 to 6.9.0 (#2717) Bumps com.diffplug.spotless from 6.8.0 to 6.9.0. --- updated-dependencies: - dependency-name: com.diffplug.spotless dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7d2058ad10..d86a154dff 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.8.0' + id 'com.diffplug.spotless' version '6.9.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.3.1' From b4d8f5aa360a90ac5e4a7a38de2e660bdf2865e0 Mon Sep 17 00:00:00 2001 From: heqiang <18710386490@163.com> Date: Tue, 2 Aug 2022 01:29:14 +0800 Subject: [PATCH 08/17] Fix Javadoc for Mockito. (#2718) --- src/main/java/org/mockito/Mockito.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 8b7b92776c..9f7383f232 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -1259,11 +1259,11 @@ * void receive(String item); * * // Java 8 - style 1 - * doAnswer(AdditionalAnswers.<String,Callback>answerVoid((operand, callback) -> callback.receive("dummy")) + * doAnswer(AdditionalAnswers.<String,Callback>answerVoid((operand, callback) -> callback.receive("dummy"))) * .when(mock).execute(anyString(), any(Callback.class)); * * // Java 8 - style 2 - assuming static import of AdditionalAnswers - * doAnswer(answerVoid((String operand, Callback callback) -> callback.receive("dummy")) + * doAnswer(answerVoid((String operand, Callback callback) -> callback.receive("dummy"))) * .when(mock).execute(anyString(), any(Callback.class)); * * // Java 8 - style 3 - where mocking function to is a static member of test class @@ -1271,7 +1271,7 @@ * callback.receive("dummy"); * } * - * doAnswer(answerVoid(TestClass::dummyCallbackImpl) + * doAnswer(answerVoid(TestClass::dummyCallbackImpl)) * .when(mock).execute(anyString(), any(Callback.class)); * * // Java 7 @@ -1287,7 +1287,7 @@ * * // this could be mocked * // Java 8 - * doAnswer(AdditionalAnswers.<Boolean,String,String>answer((input1, input2) -> input1.equals(input2)))) + * doAnswer(AdditionalAnswers.<Boolean,String,String>answer((input1, input2) -> input1.equals(input2))) * .when(mock).execute(anyString(), anyString()); * * // Java 7 From 95b43e50101af2e598d247c618e334d0a6d8093e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 20:26:10 +0200 Subject: [PATCH 09/17] Bump versions.bytebuddy from 1.12.12 to 1.12.13 (#2719) Bumps `versions.bytebuddy` from 1.12.12 to 1.12.13. Updates `byte-buddy` from 1.12.12 to 1.12.13 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.12.12...byte-buddy-1.12.13) Updates `byte-buddy-agent` from 1.12.12 to 1.12.13 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.12.12...byte-buddy-1.12.13) Updates `byte-buddy-android` from 1.12.12 to 1.12.13 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.12.12...byte-buddy-1.12.13) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: net.bytebuddy:byte-buddy-agent dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: net.bytebuddy:byte-buddy-android dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index b9a2b066cd..aebe3791cb 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.12.12' +versions.bytebuddy = '1.12.13' versions.junitJupiter = '5.9.0' versions.errorprone = '2.14.0' From 70c1fe9f377ff55826633d590991441bc9339c71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Aug 2022 21:40:02 +0200 Subject: [PATCH 10/17] Bump com.diffplug.spotless from 6.9.0 to 6.9.1 (#2725) Bumps com.diffplug.spotless from 6.9.0 to 6.9.1. --- updated-dependencies: - dependency-name: com.diffplug.spotless dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d86a154dff..f3f9a1dd26 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.9.0' + id 'com.diffplug.spotless' version '6.9.1' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.3.1' From 89698baaebdf6954324d5bb252e49da2f7bf860a Mon Sep 17 00:00:00 2001 From: James Baker Date: Sat, 13 Aug 2022 15:59:56 +0100 Subject: [PATCH 11/17] Optimize `TypeSafeMatching` iteration over class methods (#2729) `Class.getMethods` is an inefficient method call which is being called on each mock invocation. It ends up constructing new `Method` objects for each method on the class, and this can dominate the overall performance of Mockito mocks. This commit caches the result of the computation. Once concern is that this change uses some static state. I considered: - Instance state - based on where this type is constructed it seemed like it'd be a big imposition on code readability elsewhere. - Weakly referenced map. Mockito has a type for this, but the constructor of that type produces a Thread with which to clean up. Both of these seemed like overkill compared to the overhead expected in the real world (which should be on the order of a few kilobytes of RAM at most). Fixes #2723 --- .../internal/invocation/TypeSafeMatching.java | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/mockito/internal/invocation/TypeSafeMatching.java b/src/main/java/org/mockito/internal/invocation/TypeSafeMatching.java index b4ca07aca6..8f8af6dbdb 100644 --- a/src/main/java/org/mockito/internal/invocation/TypeSafeMatching.java +++ b/src/main/java/org/mockito/internal/invocation/TypeSafeMatching.java @@ -4,15 +4,26 @@ */ package org.mockito.internal.invocation; -import java.lang.reflect.Method; - import org.mockito.ArgumentMatcher; +import java.lang.reflect.Method; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + @SuppressWarnings({"unchecked", "rawtypes"}) public class TypeSafeMatching implements ArgumentMatcherAction { private static final ArgumentMatcherAction TYPE_SAFE_MATCHING_ACTION = new TypeSafeMatching(); + /** + * This cache is in theory unbounded. However, its max size is bounded by the number of types of argument matchers + * that are both in the system and being used, which is expected to bound the cache's size to a low number + * (<200) in all but the most contrived cases, and form a small percentage of the overall memory usage of those + * classes. + */ + private static final ConcurrentMap, Class> argumentTypeCache = + new ConcurrentHashMap<>(); + private TypeSafeMatching() {} public static ArgumentMatcherAction matchesTypeSafe() { @@ -39,11 +50,23 @@ private static boolean isCompatible(ArgumentMatcher argumentMatcher, Object a return expectedArgumentType.isInstance(argument); } + private static Class getArgumentType(ArgumentMatcher matcher) { + Class argumentMatcherType = matcher.getClass(); + Class cached = argumentTypeCache.get(argumentMatcherType); + // Avoids a lambda allocation on invocations >=2 for worse perf on invocation 1. + if (cached != null) { + return cached; + } else { + return argumentTypeCache.computeIfAbsent( + argumentMatcherType, unusedKey -> getArgumentTypeUncached(matcher)); + } + } + /** * Returns the type of {@link ArgumentMatcher#matches(Object)} of the given * {@link ArgumentMatcher} implementation. */ - private static Class getArgumentType(ArgumentMatcher argumentMatcher) { + private static Class getArgumentTypeUncached(ArgumentMatcher argumentMatcher) { Method[] methods = argumentMatcher.getClass().getMethods(); for (Method method : methods) { From 73a861f778e117ad0ccd7388b8d7a614e65c1623 Mon Sep 17 00:00:00 2001 From: James Baker Date: Sat, 13 Aug 2022 21:15:05 +0100 Subject: [PATCH 12/17] Fixes #2720: Use StackWalker on Java 9+ to create Locations (#2723) In terms of memory allocations, this reduces the overall memory allocations of creating a location by an order of magnitude in Java 9, and as compared to Java 8. The implementation is somewhat involved due to these desires: - Minimize the amount of work done if the Location is never used. This is done by not converting the StackFrame into a StackTraceElement, instead wrapping in an intermediate state. The StackTraceElement conversion will only occur (internally) if the .getFileName() method is called. - Ensure the implementation is still Serializable. This is ensured with a .writeReplace method. - Minimize the number of allocations, which is basically an exercise in lambda caching. - Ensuring the old mechanism still works on Java 8. Presently on Java 9+, on a stack depth of 1000 the old mechanism will allocate 40kB of RAM per call. The new one will allocate 1.5kB of RAM per call, which is a huge improvement. This is still sadly not the 'close-to-no-overhead' solution I was looking for. I therefore also added a system property that can be used to fully disable Location creation. I'm aware that this is likely not the right approach given Mockito has plugins and settings - mostly looking for guidance here given I'm not sure what would be idiomatic here. --- .../stacktrace/StackTraceCleaner.java | 25 ++ .../internal/MockedConstructionImpl.java | 4 +- .../mockito/internal/MockedStaticImpl.java | 4 +- .../creation/bytebuddy/MockMethodAdvice.java | 10 +- .../bytebuddy/MockMethodInterceptor.java | 4 +- .../creation/proxy/ProxyMockMaker.java | 9 +- ...cationImpl.java => Java8LocationImpl.java} | 14 +- .../debugging/Java9PlusLocationImpl.java | 304 ++++++++++++++++++ .../mockito/internal/debugging/Localized.java | 2 +- .../internal/debugging/LocationFactory.java | 49 +++ .../mockito/internal/exceptions/Reporter.java | 28 +- .../stacktrace/DefaultStackTraceCleaner.java | 13 +- .../invocation/DefaultInvocationFactory.java | 4 +- .../internal/matchers/LocalizedMatcher.java | 4 +- .../progress/MockingProgressImpl.java | 4 +- .../defaultanswers/ReturnsSmartNulls.java | 5 +- .../invocation/InvocationBuilder.java | 4 +- .../defaultanswers/ReturnsSmartNullsTest.java | 6 +- .../ClassLoadabilityChecker.java | 70 ++++ .../NoByteCodeDependenciesTest.java | 18 +- .../NoJUnitDependenciesTest.java | 19 +- .../debugging/LocationFactoryTest.java | 38 +++ .../internal/debugging/LocationImplTest.java | 63 ---- src/test/java/org/mockitoutil/TestBase.java | 4 +- .../LocationFactoryAllocationRateTest.java | 77 +++++ ...emoryOnLargeStackTraceInvocationsTest.java | 9 +- 26 files changed, 634 insertions(+), 157 deletions(-) rename src/main/java/org/mockito/internal/debugging/{LocationImpl.java => Java8LocationImpl.java} (84%) create mode 100644 src/main/java/org/mockito/internal/debugging/Java9PlusLocationImpl.java create mode 100644 src/main/java/org/mockito/internal/debugging/LocationFactory.java create mode 100644 src/test/java/org/mockitointegration/ClassLoadabilityChecker.java create mode 100644 src/test/java/org/mockitousage/internal/debugging/LocationFactoryTest.java delete mode 100644 src/test/java/org/mockitousage/internal/debugging/LocationImplTest.java create mode 100644 subprojects/memory-test/src/test/java/org/mockito/memorytest/LocationFactoryAllocationRateTest.java diff --git a/src/main/java/org/mockito/exceptions/stacktrace/StackTraceCleaner.java b/src/main/java/org/mockito/exceptions/stacktrace/StackTraceCleaner.java index a3229a8c80..467809aa01 100644 --- a/src/main/java/org/mockito/exceptions/stacktrace/StackTraceCleaner.java +++ b/src/main/java/org/mockito/exceptions/stacktrace/StackTraceCleaner.java @@ -25,4 +25,29 @@ public interface StackTraceCleaner { * @return whether the element should be excluded from cleaned stack trace. */ boolean isIn(StackTraceElement candidate); + + /** + * It's recommended to override this method in subclasses to avoid potentially costly re-boxing operations. + */ + default boolean isIn(StackFrameMetadata candidate) { + return isIn( + new StackTraceElement( + candidate.getClassName(), + candidate.getMethodName(), + candidate.getFileName(), + candidate.getLineNumber())); + } + + /** + * Very similar to the StackFrame class declared on the StackWalker api. + */ + interface StackFrameMetadata { + String getClassName(); + + String getMethodName(); + + String getFileName(); + + int getLineNumber(); + } } diff --git a/src/main/java/org/mockito/internal/MockedConstructionImpl.java b/src/main/java/org/mockito/internal/MockedConstructionImpl.java index 47bd8089c6..5541ba07cd 100644 --- a/src/main/java/org/mockito/internal/MockedConstructionImpl.java +++ b/src/main/java/org/mockito/internal/MockedConstructionImpl.java @@ -11,7 +11,7 @@ import org.mockito.MockedConstruction; import org.mockito.exceptions.base.MockitoException; -import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.debugging.LocationFactory; import org.mockito.invocation.Location; import org.mockito.plugins.MockMaker; @@ -21,7 +21,7 @@ public final class MockedConstructionImpl implements MockedConstruction { private boolean closed; - private final Location location = new LocationImpl(); + private final Location location = LocationFactory.create(); protected MockedConstructionImpl(MockMaker.ConstructionMockControl control) { this.control = control; diff --git a/src/main/java/org/mockito/internal/MockedStaticImpl.java b/src/main/java/org/mockito/internal/MockedStaticImpl.java index fbfb54b004..f6705fb81a 100644 --- a/src/main/java/org/mockito/internal/MockedStaticImpl.java +++ b/src/main/java/org/mockito/internal/MockedStaticImpl.java @@ -17,7 +17,7 @@ import org.mockito.Mockito; import org.mockito.exceptions.base.MockitoAssertionError; import org.mockito.exceptions.base.MockitoException; -import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.debugging.LocationFactory; import org.mockito.internal.listeners.VerificationStartedNotifier; import org.mockito.internal.progress.MockingProgress; import org.mockito.internal.stubbing.InvocationContainerImpl; @@ -35,7 +35,7 @@ public final class MockedStaticImpl implements MockedStatic { private boolean closed; - private final Location location = new LocationImpl(); + private final Location location = LocationFactory.create(); protected MockedStaticImpl(MockMaker.StaticMockControl control) { this.control = control; diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java index fc92e49acf..1dd744171f 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java @@ -51,7 +51,7 @@ import org.mockito.exceptions.base.MockitoException; import org.mockito.internal.configuration.plugins.Plugins; import org.mockito.internal.creation.bytebuddy.inject.MockMethodDispatcher; -import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.debugging.LocationFactory; import org.mockito.internal.exceptions.stacktrace.ConditionalStackTraceFilter; import org.mockito.internal.invocation.RealMethod; import org.mockito.internal.invocation.SerializableMethod; @@ -132,11 +132,7 @@ public Callable handle(Object instance, Method origin, Object[] arguments) th } return new ReturnValueWrapper( interceptor.doIntercept( - instance, - origin, - arguments, - realMethod, - new LocationImpl(new Throwable(), true))); + instance, origin, arguments, realMethod, LocationFactory.create(true))); } @Override @@ -154,7 +150,7 @@ public Callable handleStatic(Class type, Method origin, Object[] arguments origin, arguments, new StaticMethodCall(selfCallInfo, type, origin, arguments), - new LocationImpl(new Throwable(), true))); + LocationFactory.create(true))); } @Override diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java index 83908cace6..406dea39a5 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java @@ -22,7 +22,7 @@ import net.bytebuddy.implementation.bind.annotation.StubValue; import net.bytebuddy.implementation.bind.annotation.SuperCall; import net.bytebuddy.implementation.bind.annotation.This; -import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.debugging.LocationFactory; import org.mockito.internal.invocation.RealMethod; import org.mockito.invocation.Location; import org.mockito.invocation.MockHandler; @@ -53,7 +53,7 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo Object doIntercept(Object mock, Method invokedMethod, Object[] arguments, RealMethod realMethod) throws Throwable { - return doIntercept(mock, invokedMethod, arguments, realMethod, new LocationImpl()); + return doIntercept(mock, invokedMethod, arguments, realMethod, LocationFactory.create()); } Object doIntercept( diff --git a/src/main/java/org/mockito/internal/creation/proxy/ProxyMockMaker.java b/src/main/java/org/mockito/internal/creation/proxy/ProxyMockMaker.java index faa97e849b..88e6886111 100644 --- a/src/main/java/org/mockito/internal/creation/proxy/ProxyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/proxy/ProxyMockMaker.java @@ -5,7 +5,7 @@ package org.mockito.internal.creation.proxy; import org.mockito.exceptions.base.MockitoException; -import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.debugging.LocationFactory; import org.mockito.internal.invocation.RealMethod; import org.mockito.internal.util.Platform; import org.mockito.invocation.MockHandler; @@ -153,7 +153,12 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl return handler.get() .handle( createInvocation( - proxy, method, args, realMethod, settings, new LocationImpl())); + proxy, + method, + args, + realMethod, + settings, + LocationFactory.create())); } } diff --git a/src/main/java/org/mockito/internal/debugging/LocationImpl.java b/src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java similarity index 84% rename from src/main/java/org/mockito/internal/debugging/LocationImpl.java rename to src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java index cf255013e2..e8ee387c0a 100644 --- a/src/main/java/org/mockito/internal/debugging/LocationImpl.java +++ b/src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java @@ -9,7 +9,7 @@ import org.mockito.internal.exceptions.stacktrace.StackTraceFilter; import org.mockito.invocation.Location; -public class LocationImpl implements Location, Serializable { +class Java8LocationImpl implements Location, Serializable { private static final long serialVersionUID = -9054861157390980624L; // Limit the amount of objects being created, as this class is heavily instantiated: @@ -18,19 +18,11 @@ public class LocationImpl implements Location, Serializable { private String stackTraceLine; private String sourceFile; - public LocationImpl() { - this(new Throwable(), false); - } - - public LocationImpl(Throwable stackTraceHolder, boolean isInline) { + public Java8LocationImpl(Throwable stackTraceHolder, boolean isInline) { this(stackTraceFilter, stackTraceHolder, isInline); } - public LocationImpl(StackTraceFilter stackTraceFilter) { - this(stackTraceFilter, new Throwable(), false); - } - - private LocationImpl( + private Java8LocationImpl( StackTraceFilter stackTraceFilter, Throwable stackTraceHolder, boolean isInline) { computeStackTraceInformation(stackTraceFilter, stackTraceHolder, isInline); } diff --git a/src/main/java/org/mockito/internal/debugging/Java9PlusLocationImpl.java b/src/main/java/org/mockito/internal/debugging/Java9PlusLocationImpl.java new file mode 100644 index 0000000000..219835513e --- /dev/null +++ b/src/main/java/org/mockito/internal/debugging/Java9PlusLocationImpl.java @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.debugging; + +import org.mockito.exceptions.base.MockitoException; +import org.mockito.exceptions.stacktrace.StackTraceCleaner; +import org.mockito.exceptions.stacktrace.StackTraceCleaner.StackFrameMetadata; +import org.mockito.internal.configuration.plugins.Plugins; +import org.mockito.internal.exceptions.stacktrace.DefaultStackTraceCleaner; +import org.mockito.invocation.Location; + +import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class Java9PlusLocationImpl implements Location, Serializable { + private static final long serialVersionUID = 2954388321980069195L; + + private static final String UNEXPECTED_ERROR_SUFFIX = + "\nThis is unexpected and is likely due to a change in either Java's StackWalker or Reflection APIs." + + "\nIt's worth trying to upgrade to a newer version of Mockito, or otherwise to file a bug report."; + + private static final String STACK_WALKER = "java.lang.StackWalker"; + private static final String STACK_FRAME = STACK_WALKER + "$StackFrame"; + private static final String OPTION = STACK_WALKER + "$Option"; + private static final String SHOW_REFLECT_FRAMES = "SHOW_REFLECT_FRAMES"; + + /** + * This is an unfortunate buffer. Inside StackWalker, a buffer is created, which is resized by + * doubling. The resizing also allocates a tonne of StackFrame elements. If we traverse more than + * BUFFER_SIZE elements, the resulting resize can significantly affect the overall cost of the operation. + * If we traverse fewer than this number, we are inefficient. Empirically, 16 is enough stack frames + * for a simple stub+call operation to succeed without resizing, as measured on Java 11. + */ + private static final int BUFFER_SIZE = 16; + + private static final Class stackWalkerClazz = clazz(STACK_WALKER); + private static final Class stackFrameClazz = clazz(STACK_FRAME); + private static final Class optionClazz = clazz(OPTION); + + private static final Object stackWalker = stackWalker(); + private static final Method walk = walk(); + + private static final String PREFIX = "-> at "; + + private static final StackTraceCleaner CLEANER = + Plugins.getStackTraceCleanerProvider() + .getStackTraceCleaner(new DefaultStackTraceCleaner()); + + /** + * In Java, allocating lambdas is cheap, but not free. stream.map(this::doSomething) + * will allocate a Function object each time the function is called (although not + * per element). By assigning these Functions and Predicates to variables, we can + * avoid the memory allocation. + */ + private static final Function toStackFrameMetadata = + MetadataShim::new; + + private static final Predicate cleanerIsIn = CLEANER::isIn; + + private static final int FRAMES_TO_SKIP = framesToSkip(); + + private final StackFrameMetadata sfm; + private volatile String stackTraceLine; + + Java9PlusLocationImpl(boolean isInline) { + this.sfm = getStackFrame(isInline); + } + + @Override + public String getSourceFile() { + return sfm.getFileName(); + } + + @Override + public String toString() { + return stackTraceLine(); + } + + private String stackTraceLine() { + if (stackTraceLine == null) { + synchronized (this) { + if (stackTraceLine == null) { + stackTraceLine = PREFIX + sfm.toString(); + } + } + } + return stackTraceLine; + } + + private static StackFrameMetadata getStackFrame(boolean isInline) { + return stackWalk( + stream -> + stream.map(toStackFrameMetadata) + .skip(FRAMES_TO_SKIP) + .filter(cleanerIsIn) + .skip(isInline ? 1 : 0) + .findFirst() + .orElseThrow( + () -> new MockitoException(noStackTraceFailureMessage()))); + } + + private static boolean usingDefaultStackTraceCleaner() { + return CLEANER instanceof DefaultStackTraceCleaner; + } + + private static String noStackTraceFailureMessage() { + if (usingDefaultStackTraceCleaner()) { + return "Mockito could not find the first non-Mockito stack frame." + + UNEXPECTED_ERROR_SUFFIX; + } else { + String cleanerType = CLEANER.getClass().getName(); + String fmt = + "Mockito could not find the first non-Mockito stack frame. A custom stack frame cleaner \n" + + "(type %s) is in use and this has mostly likely filtered out all the relevant stack frames."; + return String.format(fmt, cleanerType); + } + } + + /** + * In order to trigger the stack walker, we create some reflective frames. These need to be skipped so as to + * ensure there are no non-Mockito frames at the top of the stack trace. + */ + private static int framesToSkip() { + return stackWalk( + stream -> { + List metadata = + stream.map(toStackFrameMetadata) + .map(StackFrameMetadata::getClassName) + .collect(Collectors.toList()); + return metadata.indexOf(Java9PlusLocationImpl.class.getName()); + }); + } + + @SuppressWarnings("unchecked") + private static T stackWalk(Function, T> function) { + try { + return (T) walk.invoke(stackWalker, function); + } catch (IllegalAccessException e) { + throw new MockitoException( + "Unexpected access exception while stack walking." + UNEXPECTED_ERROR_SUFFIX, + e); + } catch (InvocationTargetException e) { + throw new MockitoException(stackWalkFailureMessage()); + } + } + + private static String stackWalkFailureMessage() { + if (usingDefaultStackTraceCleaner()) { + return "Caught an unexpected exception while stack walking." + UNEXPECTED_ERROR_SUFFIX; + } else { + String className = CLEANER.getClass().getName(); + String fmt = + "Caught an unexpected exception while stack walking." + + "\nThis is likely caused by the custom stack trace cleaner in use (class %s)."; + return String.format(fmt, className); + } + } + + private static Method walk() { + try { + return stackWalkerClazz.getMethod("walk", Function.class); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + private static Class clazz(String name) { + try { + return Class.forName(name); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private static Object stackWalker() { + try { + Set options = + Collections.singleton(Enum.valueOf((Class) optionClazz, SHOW_REFLECT_FRAMES)); + Method getInstance = + stackWalkerClazz.getDeclaredMethod("getInstance", Set.class, int.class); + return getInstance.invoke(null, options, BUFFER_SIZE); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new MockitoException( + "Mockito received an exception while trying to acquire a StackWalker." + + UNEXPECTED_ERROR_SUFFIX); + } + } + + private static final class MetadataShim implements StackFrameMetadata, Serializable { + private static final long serialVersionUID = 8491903719411428648L; + private static final Method getClassName = getter("getClassName"); + private static final Method getMethodName = getter("getMethodName"); + private static final Method getFileName = getter("getFileName"); + private static final Method getLineNumber = getter("getLineNumber"); + private static final Method toString = getter(Object.class, "toString"); + + private final Object stackFrame; + + private MetadataShim(Object stackFrame) { + this.stackFrame = stackFrame; + } + + @Override + public String getClassName() { + return (String) get(getClassName); + } + + @Override + public String getMethodName() { + return (String) get(getMethodName); + } + + @Override + public String getFileName() { + return (String) get(getFileName); + } + + @Override + public int getLineNumber() { + return (int) get(getLineNumber); + } + + @Override + public String toString() { + return (String) get(toString); + } + + /** + * Ensure that this type remains serializable. + */ + private Object writeReplace() { + return new SerializableShim(toStackTraceElement()); + } + + private StackTraceElement toStackTraceElement() { + try { + Method method = stackFrameClazz.getMethod("toStackTraceElement"); + return (StackTraceElement) method.invoke(stackFrame); + } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private Object get(Method handle) { + try { + return handle.invoke(stackFrame); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static Method getter(String name) { + return getter(stackFrameClazz, name); + } + + private static Method getter(Class clazz, String name) { + try { + return clazz.getDeclaredMethod(name); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + } + + private static final class SerializableShim implements StackFrameMetadata, Serializable { + private static final long serialVersionUID = 7908320459080898690L; + private final StackTraceElement ste; + + private SerializableShim(StackTraceElement ste) { + this.ste = ste; + } + + @Override + public String getClassName() { + return ste.getClassName(); + } + + @Override + public String getMethodName() { + return ste.getMethodName(); + } + + @Override + public String getFileName() { + return ste.getFileName(); + } + + @Override + public int getLineNumber() { + return ste.getLineNumber(); + } + } +} diff --git a/src/main/java/org/mockito/internal/debugging/Localized.java b/src/main/java/org/mockito/internal/debugging/Localized.java index d1d7912dc4..3abcf29555 100644 --- a/src/main/java/org/mockito/internal/debugging/Localized.java +++ b/src/main/java/org/mockito/internal/debugging/Localized.java @@ -13,7 +13,7 @@ public class Localized { public Localized(T object) { this.object = object; - location = new LocationImpl(); + location = LocationFactory.create(); } public T getObject() { diff --git a/src/main/java/org/mockito/internal/debugging/LocationFactory.java b/src/main/java/org/mockito/internal/debugging/LocationFactory.java new file mode 100644 index 0000000000..daafddedaf --- /dev/null +++ b/src/main/java/org/mockito/internal/debugging/LocationFactory.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.debugging; + +import org.mockito.invocation.Location; + +public final class LocationFactory { + private static final Factory factory = createLocationFactory(); + + private LocationFactory() {} + + public static Location create() { + return create(false); + } + + public static Location create(boolean inline) { + return factory.create(inline); + } + + private interface Factory { + Location create(boolean inline); + } + + private static Factory createLocationFactory() { + try { + Class.forName("java.lang.StackWalker"); + return new Java9PlusLocationFactory(); + } catch (ClassNotFoundException e) { + return new Java8LocationFactory(); + } + } + + private static final class Java8LocationFactory implements Factory { + @Override + public Location create(boolean inline) { + return new Java8LocationImpl(new Throwable(), inline); + } + } + + private static final class Java9PlusLocationFactory implements Factory { + + @Override + public Location create(boolean inline) { + return new Java9PlusLocationImpl(inline); + } + } +} diff --git a/src/main/java/org/mockito/internal/exceptions/Reporter.java b/src/main/java/org/mockito/internal/exceptions/Reporter.java index ea670407e7..9a5ea90d21 100644 --- a/src/main/java/org/mockito/internal/exceptions/Reporter.java +++ b/src/main/java/org/mockito/internal/exceptions/Reporter.java @@ -23,7 +23,7 @@ import org.mockito.exceptions.verification.TooManyActualInvocations; import org.mockito.exceptions.verification.VerificationInOrderFailure; import org.mockito.exceptions.verification.WantedButNotInvoked; -import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.debugging.LocationFactory; import org.mockito.internal.exceptions.util.ScenarioPrinter; import org.mockito.internal.junit.ExceptionFactory; import org.mockito.internal.matchers.LocalizedMatcher; @@ -84,7 +84,7 @@ public static MockitoException incorrectUseOfApi() { return new MockitoException( join( "Incorrect use of API detected here:", - new LocationImpl(), + LocationFactory.create(), "", "You probably stored a reference to OngoingStubbing returned by when() and called stubbing methods like thenReturn() on this reference more than once.", "Examples of correct usage:", @@ -262,7 +262,7 @@ public static MockitoException incorrectUseOfAdditionalMatchers( "Invalid use of argument matchers inside additional matcher " + additionalMatcherName + " !", - new LocationImpl(), + LocationFactory.create(), "", expectedSubMatchersCount + " sub matchers expected, " @@ -295,7 +295,7 @@ public static MockitoException reportNoSubMatchersFound(String additionalMatcher return new InvalidUseOfMatchersException( join( "No matchers found for additional matcher " + additionalMatcherName, - new LocationImpl(), + LocationFactory.create(), "")); } @@ -322,7 +322,7 @@ public static AssertionError argumentsAreDifferent( .append("Argument(s) are different! Wanted:\n") .append(wanted) .append("\n") - .append(new LocationImpl()) + .append(LocationFactory.create()) .append("\n") .append("Actual invocations have different arguments:\n"); @@ -365,7 +365,7 @@ public static MockitoAssertionError wantedButNotInvoked( } private static String createWantedButNotInvokedMessage(DescribedInvocation wanted) { - return join("Wanted but not invoked:", wanted.toString(), new LocationImpl(), ""); + return join("Wanted but not invoked:", wanted.toString(), LocationFactory.create(), ""); } public static MockitoAssertionError wantedButNotInvokedInOrder( @@ -375,7 +375,7 @@ public static MockitoAssertionError wantedButNotInvokedInOrder( "Verification in order failure", "Wanted but not invoked:", wanted.toString(), - new LocationImpl(), + LocationFactory.create(), "Wanted anywhere AFTER following interaction:", previous.toString(), previous.getLocation(), @@ -400,7 +400,7 @@ private static String createTooManyInvocationsMessage( return join( wanted.toString(), "Wanted " + pluralize(wantedCount) + ":", - new LocationImpl(), + LocationFactory.create(), "But was " + pluralize(actualCount) + ":", createAllLocationsMessage(invocations), ""); @@ -413,7 +413,7 @@ public static MockitoAssertionError neverWantedButInvoked( join( wanted.toString(), "Never wanted here:", - new LocationImpl(), + LocationFactory.create(), "But invoked here:", createAllLocationsArgsMessage(invocations))); } @@ -463,7 +463,7 @@ private static String createTooFewInvocationsMessage( "Wanted " + discrepancy.getPluralizedWantedCount() + (discrepancy.getWantedCount() == 0 ? "." : ":"), - new LocationImpl(), + LocationFactory.create(), "But was " + discrepancy.getPluralizedActualCount() + (discrepancy.getActualCount() == 0 ? "." : ":"), @@ -496,7 +496,7 @@ public static MockitoAssertionError noMoreInteractionsWanted( return new NoInteractionsWanted( join( "No interactions wanted here:", - new LocationImpl(), + LocationFactory.create(), "But found this interaction on mock '" + MockUtil.getMockName(undesired.getMock()) + "':", @@ -508,7 +508,7 @@ public static MockitoAssertionError noMoreInteractionsWantedInOrder(Invocation u return new VerificationInOrderFailure( join( "No interactions wanted here:", - new LocationImpl(), + LocationFactory.create(), "But found this interaction on mock '" + MockUtil.getMockName(undesired.getMock()) + "':", @@ -527,7 +527,7 @@ public static MockitoAssertionError noInteractionsWanted( return new NoInteractionsWanted( join( "No interactions wanted here:", - new LocationImpl(), + LocationFactory.create(), "But found these interactions on mock '" + MockUtil.getMockName(mock) + "':", @@ -645,7 +645,7 @@ public static MockitoException smartNullPointerException(String invocation, Loca return new SmartNullPointerException( join( "You have a NullPointerException here:", - new LocationImpl(), + LocationFactory.create(), "because this method call was *not* stubbed correctly:", location, invocation, diff --git a/src/main/java/org/mockito/internal/exceptions/stacktrace/DefaultStackTraceCleaner.java b/src/main/java/org/mockito/internal/exceptions/stacktrace/DefaultStackTraceCleaner.java index 6b0575273e..6437ae4aa4 100644 --- a/src/main/java/org/mockito/internal/exceptions/stacktrace/DefaultStackTraceCleaner.java +++ b/src/main/java/org/mockito/internal/exceptions/stacktrace/DefaultStackTraceCleaner.java @@ -13,9 +13,18 @@ public class DefaultStackTraceCleaner implements StackTraceCleaner { @Override public boolean isIn(StackTraceElement e) { - if (isFromMockitoRunner(e.getClassName()) || isFromMockitoRule(e.getClassName())) { + return isIn(e.getClassName()); + } + + @Override + public boolean isIn(StackFrameMetadata e) { + return isIn(e.getClassName()); + } + + private boolean isIn(String className) { + if (isFromMockitoRunner(className) || isFromMockitoRule(className)) { return true; - } else if (isMockDispatcher(e.getClassName()) || isFromMockito(e.getClassName())) { + } else if (isMockDispatcher(className) || isFromMockito(className)) { return false; } else { return true; diff --git a/src/main/java/org/mockito/internal/invocation/DefaultInvocationFactory.java b/src/main/java/org/mockito/internal/invocation/DefaultInvocationFactory.java index 81f801518f..4921f4006d 100644 --- a/src/main/java/org/mockito/internal/invocation/DefaultInvocationFactory.java +++ b/src/main/java/org/mockito/internal/invocation/DefaultInvocationFactory.java @@ -8,7 +8,7 @@ import java.util.concurrent.Callable; import org.mockito.internal.creation.DelegatingMethod; -import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.debugging.LocationFactory; import org.mockito.internal.invocation.mockref.MockWeakReference; import org.mockito.internal.progress.SequenceNumber; import org.mockito.invocation.Invocation; @@ -71,7 +71,7 @@ private static InterceptedInvocation createInvocation( RealMethod realMethod, MockCreationSettings settings) { return createInvocation( - mock, invokedMethod, arguments, realMethod, settings, new LocationImpl()); + mock, invokedMethod, arguments, realMethod, settings, LocationFactory.create()); } private static MockitoMethod createMockitoMethod(Method method, MockCreationSettings settings) { diff --git a/src/main/java/org/mockito/internal/matchers/LocalizedMatcher.java b/src/main/java/org/mockito/internal/matchers/LocalizedMatcher.java index 00b47de7a9..a54eb6d79e 100644 --- a/src/main/java/org/mockito/internal/matchers/LocalizedMatcher.java +++ b/src/main/java/org/mockito/internal/matchers/LocalizedMatcher.java @@ -5,7 +5,7 @@ package org.mockito.internal.matchers; import org.mockito.ArgumentMatcher; -import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.debugging.LocationFactory; import org.mockito.invocation.Location; @SuppressWarnings("unchecked") @@ -16,7 +16,7 @@ public class LocalizedMatcher { public LocalizedMatcher(ArgumentMatcher matcher) { this.matcher = matcher; - this.location = new LocationImpl(); + this.location = LocationFactory.create(); } public Location getLocation() { diff --git a/src/main/java/org/mockito/internal/progress/MockingProgressImpl.java b/src/main/java/org/mockito/internal/progress/MockingProgressImpl.java index 991b5e4734..2585d32cfc 100644 --- a/src/main/java/org/mockito/internal/progress/MockingProgressImpl.java +++ b/src/main/java/org/mockito/internal/progress/MockingProgressImpl.java @@ -14,7 +14,7 @@ import org.mockito.internal.configuration.GlobalConfiguration; import org.mockito.internal.debugging.Localized; -import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.debugging.LocationFactory; import org.mockito.internal.exceptions.Reporter; import org.mockito.internal.listeners.AutoCleanableListener; import org.mockito.invocation.Location; @@ -105,7 +105,7 @@ public VerificationMode pullVerificationMode() { @Override public void stubbingStarted() { validateState(); - stubbingInProgress = new LocationImpl(); + stubbingInProgress = LocationFactory.create(); } @Override diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java index 2402f364a6..bcca2ef52a 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java @@ -10,7 +10,7 @@ import java.io.Serializable; import org.mockito.Mockito; -import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.debugging.LocationFactory; import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.Location; import org.mockito.stubbing.Answer; @@ -57,7 +57,8 @@ public Object apply(Class type) { } return Mockito.mock( - type, new ThrowsSmartNullPointer(invocation, new LocationImpl())); + type, + new ThrowsSmartNullPointer(invocation, LocationFactory.create())); } }); } diff --git a/src/test/java/org/mockito/internal/invocation/InvocationBuilder.java b/src/test/java/org/mockito/internal/invocation/InvocationBuilder.java index 9900db9d98..d5cf3e5577 100644 --- a/src/test/java/org/mockito/internal/invocation/InvocationBuilder.java +++ b/src/test/java/org/mockito/internal/invocation/InvocationBuilder.java @@ -13,7 +13,7 @@ import java.util.List; import org.mockito.Mockito; -import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.debugging.LocationFactory; import org.mockito.internal.invocation.mockref.MockReference; import org.mockito.internal.invocation.mockref.MockStrongReference; import org.mockito.invocation.Invocation; @@ -72,7 +72,7 @@ public Invocation toInvocation() { new SerializableMethod(method), args, NO_OP, - location == null ? new LocationImpl() : location, + location == null ? LocationFactory.create() : location, 1); if (verified) { i.markVerified(); diff --git a/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNullsTest.java b/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNullsTest.java index 984e07da2a..267c6d6966 100644 --- a/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNullsTest.java +++ b/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNullsTest.java @@ -20,7 +20,7 @@ import org.assertj.core.api.ThrowableAssert; import org.junit.Test; import org.mockito.exceptions.verification.SmartNullPointerException; -import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.debugging.LocationFactory; import org.mockito.internal.invocation.InterceptedInvocation; import org.mockito.internal.invocation.SerializableMethod; import org.mockito.internal.invocation.mockref.MockStrongReference; @@ -146,7 +146,7 @@ private static InterceptedInvocation invocationMethodWithArgs(final T obj) GenericFooBar.class.getMethod("methodWithArgs", int.class, Object.class)), new Object[] {1, obj}, InterceptedInvocation.NO_OP, - new LocationImpl(), + LocationFactory.create(), 1); } @@ -269,7 +269,7 @@ private static InterceptedInvocation invocationMethodWithVarArgs(final T[] o "methodWithVarArgs", int.class, Object[].class)), new Object[] {1, obj}, InterceptedInvocation.NO_OP, - new LocationImpl(), + LocationFactory.create(), 1); } diff --git a/src/test/java/org/mockitointegration/ClassLoadabilityChecker.java b/src/test/java/org/mockitointegration/ClassLoadabilityChecker.java new file mode 100644 index 0000000000..d19c51fb74 --- /dev/null +++ b/src/test/java/org/mockitointegration/ClassLoadabilityChecker.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitointegration; + +import java.util.HashSet; +import java.util.Set; + +/** + * Check that classes can be loaded and initialized on a provided classloader. Used + * for checking that Mockito has no dependency on libraries like JUnit. + *

+ * Some classes are excluded from this checking - namely, classes that fail due to + * the absence of Java classes. It's assumed that this is due to a specific optional + * dependency on APIs available in certain Java versions and so other elements of the + * test Matrix will check that those classes do not depend on JUnit or ByteBuddy. We + * exclude based on the failure of a ClassNotFoundException, or a NoClassDefFoundError + * caused by the failing to load of a failing parent class. + */ +public final class ClassLoadabilityChecker { + private static final boolean INITIALIZE_CLASSES = true; + private final Set excludedClasses = new HashSet<>(); + private final ClassLoader classLoader; + private final String purpose; + + public ClassLoadabilityChecker(ClassLoader classLoader, String purpose) { + this.classLoader = classLoader; + this.purpose = purpose; + } + + public void checkLoadability(String className) { + try { + Class.forName(className, INITIALIZE_CLASSES, classLoader); + } catch (ClassNotFoundException | LinkageError e) { + if (isFailureExcluded(className, e)) { + return; + } + e.printStackTrace(); + throw new AssertionError( + String.format("'%s' has some dependency to %s", className, purpose)); + } + } + + private boolean isFailureExcluded(String loadedClass, Throwable thrown) { + if (thrown == null) { + return false; + } + if (thrown instanceof ClassNotFoundException) { + ClassNotFoundException cnf = (ClassNotFoundException) thrown; + if (cnf.getMessage().startsWith("java.")) { + excludedClasses.add(loadedClass); + return true; + } + } else if (thrown instanceof NoClassDefFoundError) { + NoClassDefFoundError ncdf = (NoClassDefFoundError) thrown; + // if Foo fails due to depending on a Java class, Foo$Bar will fail with a NCDFE + int lastInnerClass = loadedClass.lastIndexOf('$'); + if (lastInnerClass != -1) { + String parent = loadedClass.substring(0, lastInnerClass); + if (excludedClasses.contains(parent) && ncdf.getMessage().contains(parent)) { + excludedClasses.add(loadedClass); + return true; + } + } + } + + return isFailureExcluded(loadedClass, thrown.getCause()); + } +} diff --git a/src/test/java/org/mockitointegration/NoByteCodeDependenciesTest.java b/src/test/java/org/mockitointegration/NoByteCodeDependenciesTest.java index 3a2908dc1c..3db1395066 100644 --- a/src/test/java/org/mockitointegration/NoByteCodeDependenciesTest.java +++ b/src/test/java/org/mockitointegration/NoByteCodeDependenciesTest.java @@ -43,21 +43,11 @@ public void pure_mockito_should_not_depend_bytecode_libraries() throws Exception pureMockitoAPIClasses.remove( "org.mockito.internal.util.reflection.InstrumentationMemberAccessor"); + ClassLoadabilityChecker checker = + new ClassLoadabilityChecker( + classLoader_without_bytecode_libraries, "ByteBuddy or Objenesis"); for (String pureMockitoAPIClass : pureMockitoAPIClasses) { - checkDependency(classLoader_without_bytecode_libraries, pureMockitoAPIClass); - } - } - - private void checkDependency(ClassLoader classLoader, String pureMockitoAPIClass) - throws ClassNotFoundException { - try { - Class.forName(pureMockitoAPIClass, true, classLoader); - } catch (Throwable e) { - e.printStackTrace(); - throw new AssertionError( - String.format( - "'%s' has some dependency to Byte Buddy or Objenesis", - pureMockitoAPIClass)); + checker.checkLoadability(pureMockitoAPIClass); } } } diff --git a/src/test/java/org/mockitointegration/NoJUnitDependenciesTest.java b/src/test/java/org/mockitointegration/NoJUnitDependenciesTest.java index 7b156f0aa3..503d859617 100644 --- a/src/test/java/org/mockitointegration/NoJUnitDependenciesTest.java +++ b/src/test/java/org/mockitointegration/NoJUnitDependenciesTest.java @@ -42,27 +42,18 @@ public void pure_mockito_should_not_depend_JUnit___ByteBuddy() throws Exception .omit("runners", "junit", "JUnit", "opentest4j") .listOwnedClasses(); + ClassLoadabilityChecker checker = + new ClassLoadabilityChecker(classLoader_without_JUnit, "JUnit"); + // The later class is required to be initialized before any inline mock maker classes can be // loaded. - checkDependency( - classLoader_without_JUnit, + checker.checkLoadability( "org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker"); pureMockitoAPIClasses.remove( "org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker"); for (String pureMockitoAPIClass : pureMockitoAPIClasses) { - checkDependency(classLoader_without_JUnit, pureMockitoAPIClass); - } - } - - private void checkDependency(ClassLoader classLoader_without_JUnit, String pureMockitoAPIClass) - throws ClassNotFoundException { - try { - Class.forName(pureMockitoAPIClass, true, classLoader_without_JUnit); - } catch (Throwable e) { - e.printStackTrace(); - throw new AssertionError( - String.format("'%s' has some dependency to JUnit", pureMockitoAPIClass)); + checker.checkLoadability(pureMockitoAPIClass); } } } diff --git a/src/test/java/org/mockitousage/internal/debugging/LocationFactoryTest.java b/src/test/java/org/mockitousage/internal/debugging/LocationFactoryTest.java new file mode 100644 index 0000000000..072c13fc02 --- /dev/null +++ b/src/test/java/org/mockitousage/internal/debugging/LocationFactoryTest.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitousage.internal.debugging; + +import org.junit.Test; +import org.mockito.internal.debugging.LocationFactory; +import org.mockitoutil.TestBase; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; + +public class LocationFactoryTest extends TestBase { + + @Test + public void shouldLocationNotContainGetStackTraceMethod() { + assertThat(LocationFactory.create().toString()) + .contains("shouldLocationNotContainGetStackTraceMethod"); + } + + @Test + public void provides_location_class() { + // when + final List files = new ArrayList(); + new Runnable() { // anonymous inner class adds stress to the check + public void run() { + files.add(LocationFactory.create().getSourceFile()); + } + }.run(); + + // then + assertEquals("LocationFactoryTest.java", files.get(0)); + } +} diff --git a/src/test/java/org/mockitousage/internal/debugging/LocationImplTest.java b/src/test/java/org/mockitousage/internal/debugging/LocationImplTest.java deleted file mode 100644 index d20bfce02a..0000000000 --- a/src/test/java/org/mockitousage/internal/debugging/LocationImplTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2007 Mockito contributors - * This program is made available under the terms of the MIT License. - */ -package org.mockitousage.internal.debugging; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Test; -import org.mockito.internal.debugging.LocationImpl; -import org.mockito.internal.exceptions.stacktrace.StackTraceFilter; -import org.mockitoutil.TestBase; - -@SuppressWarnings("serial") -public class LocationImplTest extends TestBase { - - @Test - public void shouldLocationNotContainGetStackTraceMethod() { - assertThat(new LocationImpl().toString()) - .contains("shouldLocationNotContainGetStackTraceMethod"); - } - - @Test - public void shouldBeSafeInCaseForSomeReasonFilteredStackTraceIsEmpty() { - // given - StackTraceFilter filterReturningEmptyArray = - new StackTraceFilter() { - @Override - public StackTraceElement[] filter(StackTraceElement[] target, boolean keepTop) { - return new StackTraceElement[0]; - } - - @Override - public StackTraceElement filterFirst(Throwable target, boolean isInline) { - return null; - } - }; - - // when - String loc = new LocationImpl(filterReturningEmptyArray).toString(); - - // then - assertEquals("-> at <>", loc); - } - - @Test - public void provides_location_class() { - // when - final List files = new ArrayList(); - new Runnable() { // anonymous inner class adds stress to the check - public void run() { - files.add(new LocationImpl().getSourceFile()); - } - }.run(); - - // then - assertEquals("LocationImplTest.java", files.get(0)); - } -} diff --git a/src/test/java/org/mockitoutil/TestBase.java b/src/test/java/org/mockitoutil/TestBase.java index 2fe89504e4..3f772d020b 100644 --- a/src/test/java/org/mockitoutil/TestBase.java +++ b/src/test/java/org/mockitoutil/TestBase.java @@ -17,7 +17,7 @@ import org.mockito.StateMaster; import org.mockito.internal.MockitoCore; import org.mockito.internal.configuration.ConfigurationAccess; -import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.debugging.LocationFactory; import org.mockito.internal.invocation.InterceptedInvocation; import org.mockito.internal.invocation.InvocationBuilder; import org.mockito.internal.invocation.InvocationMatcher; @@ -84,7 +84,7 @@ protected static Invocation invocationOf(Class type, String methodName, Objec new SerializableMethod(type.getMethod(methodName, types)), args, InterceptedInvocation.NO_OP, - new LocationImpl(), + LocationFactory.create(), 1); } diff --git a/subprojects/memory-test/src/test/java/org/mockito/memorytest/LocationFactoryAllocationRateTest.java b/subprojects/memory-test/src/test/java/org/mockito/memorytest/LocationFactoryAllocationRateTest.java new file mode 100644 index 0000000000..26b36ca9af --- /dev/null +++ b/subprojects/memory-test/src/test/java/org/mockito/memorytest/LocationFactoryAllocationRateTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.memorytest; + +import com.sun.management.ThreadMXBean; +import org.junit.Test; +import org.mockito.internal.debugging.LocationFactory; + +import java.lang.management.ManagementFactory; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LocationFactoryAllocationRateTest { + private static final int REPEAT = 1000; + private static final int RECURSION_LIMIT = 1000; + private static final double EXPECTED_IMPROVEMENT = expectedImprovement(); + + private static final ThreadMXBean memoryBean = + (ThreadMXBean) ManagementFactory.getThreadMXBean(); + + + @Test + public void shouldAllocateMuchLessMemoryThanThrowable() { + // On Java 8, this will use the internal approach. On Java 9, the StackWalker approach will + // be used. + new Throwable().fillInStackTrace(); + LocationFactory.create(); + long baseline = + countMemoryAllocations( + () -> + recurseAndThen( + RECURSION_LIMIT, + repeat(() -> new Throwable().fillInStackTrace()))); + long actual = + countMemoryAllocations( + () -> + recurseAndThen( + RECURSION_LIMIT, + repeat(() -> LocationFactory.create(false)))); + assertThat(actual * EXPECTED_IMPROVEMENT) + .as( + "stack walker approach (%d) expected to be at least %fx better than exception approach (%d)", + actual, EXPECTED_IMPROVEMENT, baseline) + .isLessThan(baseline); + } + + private static long countMemoryAllocations(Runnable someTask) { + long threadId = Thread.currentThread().getId(); + long atStart = memoryBean.getThreadAllocatedBytes(threadId); + someTask.run(); + return memoryBean.getThreadAllocatedBytes(threadId) - atStart; + } + + private static void recurseAndThen(int count, Runnable runnable) { + if (count <= 0) { + runnable.run(); + } else { + recurseAndThen(count - 1, runnable); + } + } + + private static Runnable repeat(Runnable task) { + return () -> IntStream.range(0, REPEAT).forEach(index -> task.run()); + } + + private static double expectedImprovement() { + try { + Class.forName("java.lang.StackWalker"); + return 20; + } catch (ClassNotFoundException e) { + return 1.5; + } + } +} diff --git a/subprojects/memory-test/src/test/java/org/mockito/memorytest/ShouldNotStarveMemoryOnLargeStackTraceInvocationsTest.java b/subprojects/memory-test/src/test/java/org/mockito/memorytest/ShouldNotStarveMemoryOnLargeStackTraceInvocationsTest.java index d3a6903e21..5c8bac2630 100644 --- a/subprojects/memory-test/src/test/java/org/mockito/memorytest/ShouldNotStarveMemoryOnLargeStackTraceInvocationsTest.java +++ b/subprojects/memory-test/src/test/java/org/mockito/memorytest/ShouldNotStarveMemoryOnLargeStackTraceInvocationsTest.java @@ -10,10 +10,8 @@ import static org.mockito.Mockito.when; import org.junit.Assume; -import org.junit.Ignore; import org.junit.Test; -@Ignore("https://github.com/mockito/mockito/issues/2478") public class ShouldNotStarveMemoryOnLargeStackTraceInvocationsTest { private static final int STACK_TRACE_DEPTH = 1000; @@ -23,12 +21,7 @@ public class ShouldNotStarveMemoryOnLargeStackTraceInvocationsTest { static { try { - Class.forName("sun.misc.SharedSecrets") - .getMethod("getJavaLangAccess") - .invoke(null); - Class.forName("sun.misc.JavaLangAccess") - .getMethod("getStackTraceElement", Throwable.class, int.class); - + Class.forName("java.lang.StackWalker"); supported = true; } catch (Exception ignored) { } From 2ded10ec704f2c93648d976031c2520a5a9b84aa Mon Sep 17 00:00:00 2001 From: Thibault Helsmoortel Date: Mon, 15 Aug 2022 21:29:58 +0200 Subject: [PATCH 13/17] Remove useless thrown exception from constructor (#2732) --- src/main/java/org/mockito/junit/MockitoJUnitRunner.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/mockito/junit/MockitoJUnitRunner.java b/src/main/java/org/mockito/junit/MockitoJUnitRunner.java index f7f4e1b928..413e8b8563 100644 --- a/src/main/java/org/mockito/junit/MockitoJUnitRunner.java +++ b/src/main/java/org/mockito/junit/MockitoJUnitRunner.java @@ -68,7 +68,7 @@ * } * * - * If you would like to take advantage of Mockito JUnit runner features + * If you would like to take advantage of Mockito JUnit runner features, * but you cannot use the runner there is a solution! * {@link MockitoSession} API is intended to offer cleaner tests and improved debuggability * to users that cannot use Mockito's built-in JUnit support (runner or the rule). @@ -154,7 +154,7 @@ public MockitoJUnitRunner(Class klass) throws InvocationTargetException { this(new StrictRunner(new RunnerFactory().createStrict(klass), klass)); } - MockitoJUnitRunner(InternalRunner runner) throws InvocationTargetException { + MockitoJUnitRunner(InternalRunner runner) { this.runner = runner; } From e123c2cdf7e4688bae7e4ecc145eb960626894db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 20:19:22 +0200 Subject: [PATCH 14/17] Bump versions.bytebuddy from 1.12.13 to 1.12.14 (#2734) Bumps `versions.bytebuddy` from 1.12.13 to 1.12.14. Updates `byte-buddy` from 1.12.13 to 1.12.14 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.12.13...byte-buddy-1.12.14) Updates `byte-buddy-agent` from 1.12.13 to 1.12.14 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.12.13...byte-buddy-1.12.14) Updates `byte-buddy-android` from 1.12.13 to 1.12.14 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.12.13...byte-buddy-1.12.14) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: net.bytebuddy:byte-buddy-agent dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: net.bytebuddy:byte-buddy-android dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index aebe3791cb..772fe4b6c2 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.12.13' +versions.bytebuddy = '1.12.14' versions.junitJupiter = '5.9.0' versions.errorprone = '2.14.0' From 160e3daf446e508eb17c6fc21a0932246848206b Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Mon, 22 Aug 2022 22:30:03 +0200 Subject: [PATCH 15/17] Drop varargs collector before invoking a user method. --- .../InstrumentationMemberAccessor.java | 3 +++ .../java/org/mockitoinline/StaticMockTest.java | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java b/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java index 0a972d8019..13d8416586 100644 --- a/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java +++ b/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java @@ -235,6 +235,9 @@ public Object invoke(Method method, Object target, Object... arguments) if (!Modifier.isStatic(method.getModifiers())) { handle = handle.bindTo(target); } + if (handle.isVarargsCollector()) { + handle = handle.asFixedArity(); + } try { return DISPATCHER.invokeWithArguments(handle, arguments); } catch (Throwable t) { diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StaticMockTest.java b/subprojects/inline/src/test/java/org/mockitoinline/StaticMockTest.java index eaa3ed1970..c9f7a58cce 100644 --- a/subprojects/inline/src/test/java/org/mockitoinline/StaticMockTest.java +++ b/subprojects/inline/src/test/java/org/mockitoinline/StaticMockTest.java @@ -212,6 +212,15 @@ public void testStaticMockMustUseValidMatchers() { } } + @Test + public void testStaticMockVarargs() { + assertEquals("foobar", Dummy.fooVarargs("foo", "bar")); + try (MockedStatic ignored = Mockito.mockStatic(Dummy.class)) { + assertNull(Dummy.fooVarargs("foo", "bar")); + } + assertEquals("foobar", Dummy.fooVarargs("foo", "bar")); + } + static class Dummy { static String var1 = null; @@ -227,5 +236,13 @@ static void fooVoid(String var2) { static void fooVoid(String var2, String var3) { var1 = var2; } + + static String fooVarargs(String... args) { + StringBuilder sb = new StringBuilder(); + for (String arg : args) { + sb.append(arg); + } + return sb.toString(); + } } } From 4b8042e24e4d2009eb46c5319673fd9755c86e45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Aug 2022 22:14:58 +0200 Subject: [PATCH 16/17] Bump com.diffplug.spotless from 6.9.1 to 6.10.0 (#2738) Bumps com.diffplug.spotless from 6.9.1 to 6.10.0. --- updated-dependencies: - dependency-name: com.diffplug.spotless dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f3f9a1dd26..5f520bfe27 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.9.1' + id 'com.diffplug.spotless' version '6.10.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.3.1' From 530558ae3c721cb310f3410a66ff4061414cf836 Mon Sep 17 00:00:00 2001 From: Kurt Alfred Kluever Date: Tue, 30 Aug 2022 15:33:41 -0400 Subject: [PATCH 17/17] Assign GlobalConfiguration initializer to unused variable (#2742) (This silences some errorprone static analysis about calling constructors w/o using the new object.) --- .../org/mockito/internal/configuration/GlobalConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/internal/configuration/GlobalConfiguration.java b/src/main/java/org/mockito/internal/configuration/GlobalConfiguration.java index 92f5a54eb4..d5ad75c93b 100644 --- a/src/main/java/org/mockito/internal/configuration/GlobalConfiguration.java +++ b/src/main/java/org/mockito/internal/configuration/GlobalConfiguration.java @@ -43,7 +43,7 @@ private IMockitoConfiguration createConfig() { } public static void validate() { - new GlobalConfiguration(); + GlobalConfiguration unused = new GlobalConfiguration(); } public org.mockito.plugins.AnnotationEngine tryGetPluginAnnotationEngine() {