From 2eb26cc5b061f8b44f65b62917fcee1d5d426846 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 23:52:34 +0100 Subject: [PATCH 001/160] Bump com.diffplug.spotless from 6.3.0 to 6.4.0 (#2605) Bumps com.diffplug.spotless from 6.3.0 to 6.4.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 0af3a73686..1b3cb88fbf 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.3.0' + id 'com.diffplug.spotless' version '6.4.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.2.0' From ab3b1c2acaef29e52e37b0cfd41e7beec11f9f82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Mar 2022 19:43:55 +0100 Subject: [PATCH 002/160] Bump com.diffplug.spotless from 6.4.0 to 6.4.1 (#2606) Bumps com.diffplug.spotless from 6.4.0 to 6.4.1. --- updated-dependencies: - dependency-name: com.diffplug.spotless 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 1b3cb88fbf..6c54979298 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.4.0' + id 'com.diffplug.spotless' version '6.4.1' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.2.0' From 0dd95d7840a2d1650a237107ced916f0364c9b14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 21:04:18 +0100 Subject: [PATCH 003/160] Bump kotlinVersion from 1.6.10 to 1.6.20 (#2607) Bumps `kotlinVersion` from 1.6.10 to 1.6.20. Updates `kotlin-gradle-plugin` from 1.6.10 to 1.6.20 - [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.6.10 to 1.6.20 - [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 6c54979298..2034f2d0a6 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.6.10" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.20" } } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index d22748facd..d3aec48ce3 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -34,7 +34,7 @@ libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.2.0' libraries.groovy = 'org.codehaus.groovy:groovy:3.0.10' -def kotlinVersion = '1.6.10' +def kotlinVersion = '1.6.20' libraries.kotlin = [ version: kotlinVersion, From 331ff01c5f840ff8a3c5b0859185ba78f9446cf0 Mon Sep 17 00:00:00 2001 From: Rick Ossendrijver Date: Wed, 6 Apr 2022 08:55:57 +0200 Subject: [PATCH 004/160] Bump versions.errorprone from 2.10.0 to 2.12.1 (#2608) * Bump versions.errorprone from 2.10.0 to 2.11.0 Bumps `versions.errorprone` from 2.10.0 to 2.11.0. Updates `error_prone_core` from 2.10.0 to 2.11.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.10.0...v2.11.0) Updates `error_prone_test_helpers` from 2.10.0 to 2.11.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.10.0...v2.11.0) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.google.errorprone:error_prone_test_helpers dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Fix compilation for JDK 8 This also fixes #2554, by using `.withNoParameters()` instead. * Extract method for JDK 8 For some reason, Java 8 chooses the wrong subclass for this specific use case. All other assertions are fine and this doesn't happen on JDK 11. Therefore, let's extract it into a method to force the correct type. * Fixes #2554: Upgrade Error Prone 2.11 -> 2.12 See: - https://github.com/google/error-prone/releases/tag/v2.12.0 - https://github.com/google/error-prone/compare/v2.11.0...v2.12.0 * Upgrade Error Prone 2.12.0 -> 2.12.1 See: - https://github.com/google/error-prone/releases/tag/v2.12.1 - https://github.com/google/error-prone/compare/v2.12.0...v2.12.1 Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tim van der Lippe --- build.gradle | 6 ++- gradle/dependencies.gradle | 2 +- settings.gradle.kts | 7 ++- .../internal/matchers/EqualsWithDelta.java | 3 +- .../matchers/text/ValuePrinterTest.java | 38 +++++++--------- .../defaultanswers/ReturnsSmartNullsTest.java | 23 ++++------ .../mockito/internal/util/PlatformTest.java | 45 ++++++++----------- .../stubbing/StubbingWithThrowablesTest.java | 37 +++++++-------- .../MockitoAnyIncorrectPrimitiveType.java | 2 +- 9 files changed, 75 insertions(+), 88 deletions(-) diff --git a/build.gradle b/build.gradle index 2034f2d0a6..d251749b64 100644 --- a/build.gradle +++ b/build.gradle @@ -52,8 +52,10 @@ allprojects { proj -> mavenCentral() google() } - plugins.withId('java') { - proj.apply from: "$rootDir/gradle/errorprone.gradle" + if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_11)) { + plugins.withId('java') { + proj.apply from: "$rootDir/gradle/errorprone.gradle" + } } tasks.withType(JavaCompile) { //I don't believe those warnings add value given modern IDEs diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index d3aec48ce3..108aeb3481 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -6,7 +6,7 @@ def versions = [:] versions.bytebuddy = '1.12.8' versions.junitJupiter = '5.8.2' -versions.errorprone = '2.10.0' +versions.errorprone = '2.12.1' libraries.junit4 = 'junit:junit:4.13.2' libraries.junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}" diff --git a/settings.gradle.kts b/settings.gradle.kts index f5a786dfdc..5aad31c670 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,11 +14,16 @@ include("inline", "junitJupiterInlineMockMakerExtensionTest", "module-test", "memory-test", - "errorprone", "junitJupiterParallelTest", "osgi-test", "bom") +if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_11)) { + include("errorprone") +} else { + logger.info("Not including errorprone, which requires minimum JDK 11+") +} + if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17) && (System.getenv("ANDROID_SDK_ROOT") != null || File(".local.properties").exists())) { include("androidTest") } else { diff --git a/src/main/java/org/mockito/internal/matchers/EqualsWithDelta.java b/src/main/java/org/mockito/internal/matchers/EqualsWithDelta.java index 46fd4d6f01..e417ec2c5d 100644 --- a/src/main/java/org/mockito/internal/matchers/EqualsWithDelta.java +++ b/src/main/java/org/mockito/internal/matchers/EqualsWithDelta.java @@ -6,6 +6,7 @@ import java.io.Serializable; +import java.util.Objects; import org.mockito.ArgumentMatcher; public class EqualsWithDelta implements ArgumentMatcher, Serializable { @@ -24,7 +25,7 @@ public boolean matches(Number actual) { return false; } - if (wanted == actual) { + if (Objects.equals(wanted, actual)) { return true; } diff --git a/src/test/java/org/mockito/internal/matchers/text/ValuePrinterTest.java b/src/test/java/org/mockito/internal/matchers/text/ValuePrinterTest.java index 1ece06ca1e..56b5e5fad0 100644 --- a/src/test/java/org/mockito/internal/matchers/text/ValuePrinterTest.java +++ b/src/test/java/org/mockito/internal/matchers/text/ValuePrinterTest.java @@ -9,7 +9,7 @@ import static org.mockito.internal.matchers.text.ValuePrinter.print; import java.util.LinkedHashMap; - +import java.util.Map; import org.junit.Test; public class ValuePrinterTest { @@ -24,29 +24,23 @@ public void prints_values() { assertThat(print(3.14d)).isEqualTo("3.14d"); assertThat(print(3.14f)).isEqualTo("3.14f"); assertThat(print(new int[] {1, 2})).isEqualTo("[1, 2]"); - assertThat( - print( - new LinkedHashMap() { - { - put("foo", 2L); - } - })) - .isEqualTo("{\"foo\" = 2L}"); - assertThat( - print( - new LinkedHashMap() { - { - put("int passed as hex", 0x01); - put("byte", (byte) 0x01); - put("short", (short) 2); - put("int", 3); - put("long", 4L); - put("float", 2.71f); - put("double", 3.14d); - } - })) + + Map map1 = new LinkedHashMap<>(); + map1.put("foo", 2L); + assertThat(print(map1)).isEqualTo("{\"foo\" = 2L}"); + + Map map2 = new LinkedHashMap<>(); + map2.put("int passed as hex", 0x01); + map2.put("byte", (byte) 0x01); + map2.put("short", (short) 2); + map2.put("int", 3); + map2.put("long", 4L); + map2.put("float", 2.71f); + map2.put("double", 3.14d); + assertThat(print(map2)) .isEqualTo( "{\"int passed as hex\" = 1, \"byte\" = (byte) 0x01, \"short\" = (short) 2, \"int\" = 3, \"long\" = 4L, \"float\" = 2.71f, \"double\" = 3.14d}"); + assertTrue(print(new UnsafeToString()).contains("UnsafeToString")); assertThat(print(new ToString())).isEqualTo("ToString"); assertThat(print(new FormattedText("formatted"))).isEqualTo("formatted"); 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 870f0cee31..984e07da2a 100644 --- a/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNullsTest.java +++ b/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNullsTest.java @@ -16,7 +16,6 @@ import java.util.List; import java.util.Map; import java.util.Set; - import org.assertj.core.api.Assertions; import org.assertj.core.api.ThrowableAssert; import org.junit.Test; @@ -307,20 +306,14 @@ private static InterceptedInvocation invocationMethodWithVarArgs(final T[] o should_return_a_empty_map_that_has_been_defined_with_method_generic_and_provided_in_var_args() throws Throwable { - final Map map1 = - new HashMap() { - { - put("key-1", "value-1"); - put("key-2", "value-2"); - } - }; - final Map map2 = - new HashMap() { - { - put("key-3", "value-1"); - put("key-4", "value-2"); - } - }; + final Map map1 = new HashMap<>(); + map1.put("key-1", "value-1"); + map1.put("key-2", "value-2"); + + final Map map2 = new HashMap<>(); + map2.put("key-3", "value-1"); + map2.put("key-4", "value-2"); + Answer answer = new ReturnsSmartNulls(); Object smartNull = answer.answer(invocationMethodWithVarArgs(new Map[] {map1, map2})); diff --git a/src/test/java/org/mockito/internal/util/PlatformTest.java b/src/test/java/org/mockito/internal/util/PlatformTest.java index b72c71d398..d4c453386d 100644 --- a/src/test/java/org/mockito/internal/util/PlatformTest.java +++ b/src/test/java/org/mockito/internal/util/PlatformTest.java @@ -8,7 +8,6 @@ import java.util.HashMap; import java.util.Map; - import org.junit.Test; // Possible description on a IBM J9 VM (see #801) @@ -75,23 +74,19 @@ public void should_parse_open_jdk_string_and_report_wether_below_or_nut_update_4 // https://stackoverflow.com/questions/35844985/how-do-we-get-sr-and-fp-of-ibm-jre-using-java // - // https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.80.doc/user/build_number.html - Map versions = - new HashMap() { - { - put("1.8.0_92-b14", false); - put("1.8.0-b24", true); - put("1.8.0_5", true); - put("1.8.0b5_u44", true); - put("1.8.0b5_u92", false); - put("1.7.0_4", false); - put("1.4.0_03-b04", false); - put("1.4.0_03-ea-b01", false); - put("pxi3270_27sr4-20160303_03 (SR4)", false); - put("pwi3260sr11-20120412_01 (SR11)", false); - put("pwa6480sr1fp10-20150711_01 (SR1 FP10)", false); - put("null", false); - } - }; + Map versions = new HashMap<>(); + versions.put("1.8.0_92-b14", false); + versions.put("1.8.0-b24", true); + versions.put("1.8.0_5", true); + versions.put("1.8.0b5_u44", true); + versions.put("1.8.0b5_u92", false); + versions.put("1.7.0_4", false); + versions.put("1.4.0_03-b04", false); + versions.put("1.4.0_03-ea-b01", false); + versions.put("pxi3270_27sr4-20160303_03 (SR4)", false); + versions.put("pwi3260sr11-20120412_01 (SR11)", false); + versions.put("pwa6480sr1fp10-20150711_01 (SR1 FP10)", false); + versions.put("null", false); assertPlatformParsesCorrectlyVariousVersionScheme(versions); } @@ -134,15 +129,11 @@ public void should_parse_open_jdk9_string() { // java.specification.version 1.9 9 // java.vm.specification.version 1.9 9 // - Map versions = - new HashMap() { - { - put("9-ea+73", false); - put("9+100", false); - put("9.1.2+62", false); - put("9.0.1+20", false); - } - }; + Map versions = new HashMap<>(); + versions.put("9-ea+73", false); + versions.put("9+100", false); + versions.put("9.1.2+62", false); + versions.put("9.0.1+20", false); assertPlatformParsesCorrectlyVariousVersionScheme(versions); } diff --git a/src/test/java/org/mockitousage/stubbing/StubbingWithThrowablesTest.java b/src/test/java/org/mockitousage/stubbing/StubbingWithThrowablesTest.java index 045a8b6599..efc3e8f33a 100644 --- a/src/test/java/org/mockitousage/stubbing/StubbingWithThrowablesTest.java +++ b/src/test/java/org/mockitousage/stubbing/StubbingWithThrowablesTest.java @@ -24,6 +24,7 @@ import java.util.Map; import org.assertj.core.api.Assertions; +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.junit.Before; import org.junit.Test; import org.mockito.exceptions.base.MockitoException; @@ -248,34 +249,34 @@ public void shouldNotAllowSettingNullThrowableArray() { .hasMessageContaining("Cannot stub with null throwable"); } - @Test - public void shouldNotAllowSettingNullThrowableClass() { - assertThatThrownBy( - () -> { - when(mock.isEmpty()).thenThrow((Class) null); - }) + private void assertExceptionTypeCanNotBeNull(ThrowingCallable throwingCallable) { + assertThatThrownBy(throwingCallable) .isInstanceOf(MockitoException.class) .hasMessageContaining("Exception type cannot be null"); } + @Test + public void shouldNotAllowSettingNullThrowableClass() { + assertExceptionTypeCanNotBeNull( + () -> { + when(mock.isEmpty()).thenThrow((Class) null); + }); + } + @Test public void shouldNotAllowSettingNullThrowableClasses() { - assertThatThrownBy( - () -> { - when(mock.isEmpty()).thenThrow(RuntimeException.class, (Class[]) null); - }) - .isInstanceOf(MockitoException.class) - .hasMessageContaining("Exception type cannot be null"); + assertExceptionTypeCanNotBeNull( + () -> { + when(mock.isEmpty()).thenThrow(RuntimeException.class, (Class[]) null); + }); } @Test public void shouldNotAllowSettingNullVarArgThrowableClass() { - assertThatThrownBy( - () -> { - when(mock.isEmpty()).thenThrow(RuntimeException.class, (Class) null); - }) - .isInstanceOf(MockitoException.class) - .hasMessageContaining("Exception type cannot be null"); + assertExceptionTypeCanNotBeNull( + () -> { + when(mock.isEmpty()).thenThrow(RuntimeException.class, (Class) null); + }); } @Test diff --git a/subprojects/errorprone/src/main/java/org/mockito/errorprone/bugpatterns/MockitoAnyIncorrectPrimitiveType.java b/subprojects/errorprone/src/main/java/org/mockito/errorprone/bugpatterns/MockitoAnyIncorrectPrimitiveType.java index 48b7ee11e1..b73610b45d 100644 --- a/subprojects/errorprone/src/main/java/org/mockito/errorprone/bugpatterns/MockitoAnyIncorrectPrimitiveType.java +++ b/subprojects/errorprone/src/main/java/org/mockito/errorprone/bugpatterns/MockitoAnyIncorrectPrimitiveType.java @@ -49,7 +49,7 @@ public class MockitoAnyIncorrectPrimitiveType extends AbstractMockitoAnyForPrimi }; private static final Matcher METHOD_MATCHER = - staticMethod().onClassAny(CLASS_NAMES).withNameMatching(METHOD_NAME_PATTERN).withParameters(); + staticMethod().onClassAny(CLASS_NAMES).withNameMatching(METHOD_NAME_PATTERN).withNoParameters(); @Override protected Matcher matcher() { From e2fa1c95de728ef7e67f9be1e9a09f65fdac8d6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Apr 2022 23:01:42 +0200 Subject: [PATCH 005/160] Bump com.diffplug.spotless from 6.4.1 to 6.4.2 (#2611) Bumps com.diffplug.spotless from 6.4.1 to 6.4.2. --- updated-dependencies: - dependency-name: com.diffplug.spotless 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 d251749b64..3930e187cd 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.4.1' + id 'com.diffplug.spotless' version '6.4.2' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.2.0' From 98b5ff9f4df143eb5bf91aae2ad51a1f91624b0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Apr 2022 08:14:46 +0200 Subject: [PATCH 006/160] Bump versions.bytebuddy from 1.12.8 to 1.12.9 (#2614) Bumps `versions.bytebuddy` from 1.12.8 to 1.12.9. Updates `byte-buddy` from 1.12.8 to 1.12.9 - [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.8...byte-buddy-1.12.9) Updates `byte-buddy-agent` from 1.12.8 to 1.12.9 - [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.8...byte-buddy-1.12.9) Updates `byte-buddy-android` from 1.12.8 to 1.12.9 - [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.8...byte-buddy-1.12.9) --- 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 108aeb3481..8c6a8b69fa 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.12.8' +versions.bytebuddy = '1.12.9' versions.junitJupiter = '5.8.2' versions.errorprone = '2.12.1' From 09ba42033e5d81385286fc8bb651bf3bb9865ec2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Apr 2022 20:22:24 +0200 Subject: [PATCH 007/160] Bump actions/setup-java from 2 to 3 (#2615) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2 to 3. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28f68e3085..bf878a9b4d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: fetch-depth: '0' # https://github.com/shipkit/shipkit-changelog#fetch-depth-on-ci - name: 2. Set up Java ${{ matrix.java }} - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: ${{ matrix.java }} @@ -97,7 +97,7 @@ jobs: fetch-depth: '0' # https://github.com/shipkit/shipkit-changelog#fetch-depth-on-ci - name: Set up Java 8 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'adopt' java-version: 8 From 2947e9daf76a97fe179606248d5f1f5e040129a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Apr 2022 20:24:32 +0200 Subject: [PATCH 008/160] Bump versions.errorprone from 2.12.1 to 2.13.0 (#2619) Bumps `versions.errorprone` from 2.12.1 to 2.13.0. Updates `error_prone_core` from 2.12.1 to 2.13.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.12.1...v2.13.0) Updates `error_prone_test_helpers` from 2.12.1 to 2.13.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.12.1...v2.13.0) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.google.errorprone:error_prone_test_helpers 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 8c6a8b69fa..7971b3933f 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -6,7 +6,7 @@ def versions = [:] versions.bytebuddy = '1.12.9' versions.junitJupiter = '5.8.2' -versions.errorprone = '2.12.1' +versions.errorprone = '2.13.0' libraries.junit4 = 'junit:junit:4.13.2' libraries.junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}" From d539af2d7afdcfaf1eda4a57911d7f9df107db2a Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Mon, 18 Apr 2022 11:54:37 +0200 Subject: [PATCH 009/160] Fix mockito-inline for Groovy projects (#2618) Add ignore matcher for Groovy meta methods for inline Byte Buddy mock maker. Add a new test subproject to verify fixes between mockito-inline and Groovy Fixes #2522 Co-authored-by: Steve Green --- settings.gradle.kts | 1 + .../creation/bytebuddy/BytecodeGenerator.java | 18 ++++++++ .../bytebuddy/InlineBytecodeGenerator.java | 12 +++-- .../bytebuddy/SubclassBytecodeGenerator.java | 23 +--------- .../groovyInlineTest/groovyInlineTest.gradle | 11 +++++ .../mockito/groovy/GroovyMockitoTest.groovy | 46 +++++++++++++++++++ 6 files changed, 85 insertions(+), 26 deletions(-) create mode 100644 subprojects/groovyInlineTest/groovyInlineTest.gradle create mode 100644 subprojects/groovyInlineTest/src/test/groovy/org/mockito/groovy/GroovyMockitoTest.groovy diff --git a/settings.gradle.kts b/settings.gradle.kts index 5aad31c670..313149df98 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,6 +6,7 @@ include("inline", "proxy", "extTest", "groovyTest", + "groovyInlineTest", "kotlinTest", "kotlinReleaseCoroutinesTest", "android", diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/BytecodeGenerator.java b/src/main/java/org/mockito/internal/creation/bytebuddy/BytecodeGenerator.java index 392392d2d9..5040c17573 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/BytecodeGenerator.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/BytecodeGenerator.java @@ -4,6 +4,12 @@ */ package org.mockito.internal.creation.bytebuddy; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import static net.bytebuddy.matcher.ElementMatchers.*; +import static net.bytebuddy.matcher.ElementMatchers.named; + public interface BytecodeGenerator { Class mockClass(MockFeatures features); @@ -13,4 +19,16 @@ public interface BytecodeGenerator { void mockClassStatic(Class type); default void clearAllCaches() {} + + static ElementMatcher isGroovyMethod(boolean inline) { + ElementMatcher.Junction matcher = + isDeclaredBy(named("groovy.lang.GroovyObjectSupport")) + .or(isAnnotatedWith(named("groovy.transform.Internal"))); + if (inline) { + return matcher.or( + named("$getStaticMetaClass").and(returns(named("groovy.lang.MetaClass")))); + } else { + return matcher; + } + } } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java index 10b73c81a2..72ab81adbd 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java @@ -117,15 +117,19 @@ public InlineBytecodeGenerator( .or(isEquals()) .or(isDefaultFinalizer()))) .and( - not( - isDeclaredBy(nameStartsWith("java.")) + not(isDeclaredBy(nameStartsWith("java.")) .and( - isPackagePrivate()))), + isPackagePrivate())) + .and( + not( + BytecodeGenerator + .isGroovyMethod( + true)))), Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.class)) .method( - isStatic(), + isStatic().and(not(BytecodeGenerator.isGroovyMethod(true))), Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForStatic.class)) diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java index f67fdfc5a6..d27b5f82c3 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java @@ -12,12 +12,9 @@ import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.hasParameters; import static net.bytebuddy.matcher.ElementMatchers.hasType; -import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; -import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; import static net.bytebuddy.matcher.ElementMatchers.isEquals; import static net.bytebuddy.matcher.ElementMatchers.isHashCode; import static net.bytebuddy.matcher.ElementMatchers.isPackagePrivate; -import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.whereAny; import static org.mockito.internal.util.StringUtil.join; @@ -29,9 +26,7 @@ import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.Collection; import java.util.Iterator; -import java.util.LinkedList; import java.util.Random; import net.bytebuddy.ByteBuddy; import net.bytebuddy.description.method.MethodDescription; @@ -224,7 +219,7 @@ public Class mockClass(MockFeatures features) { byteBuddy .subclass(features.mockedType) .name(name) - .ignoreAlso(isGroovyMethod()) + .ignoreAlso(BytecodeGenerator.isGroovyMethod(false)) .annotateType( features.stripAnnotations ? new Annotation[0] @@ -282,22 +277,6 @@ public void mockClassConstruction(Class type) { "The subclass byte code generator cannot create construction mocks"); } - private Collection> getAllTypes(Class type) { - Collection> supertypes = new LinkedList<>(); - supertypes.add(type); - Class superType = type; - while (superType != null) { - supertypes.add(superType); - superType = superType.getSuperclass(); - } - return supertypes; - } - - private static ElementMatcher isGroovyMethod() { - return isDeclaredBy(named("groovy.lang.GroovyObjectSupport")) - .or(isAnnotatedWith(named("groovy.transform.Internal"))); - } - private boolean isComingFromJDK(Class type) { // Comes from the manifest entry : // Implementation-Title: Java Runtime Environment diff --git a/subprojects/groovyInlineTest/groovyInlineTest.gradle b/subprojects/groovyInlineTest/groovyInlineTest.gradle new file mode 100644 index 0000000000..1618ac6009 --- /dev/null +++ b/subprojects/groovyInlineTest/groovyInlineTest.gradle @@ -0,0 +1,11 @@ +apply plugin: 'groovy' + +description = "Integration test for using mockito-inline with Groovy." + +apply from: "$rootDir/gradle/dependencies.gradle" + +dependencies { + testImplementation project(":inline") + testImplementation libraries.groovy + testImplementation libraries.junit4 +} diff --git a/subprojects/groovyInlineTest/src/test/groovy/org/mockito/groovy/GroovyMockitoTest.groovy b/subprojects/groovyInlineTest/src/test/groovy/org/mockito/groovy/GroovyMockitoTest.groovy new file mode 100644 index 0000000000..c327d0e1bb --- /dev/null +++ b/subprojects/groovyInlineTest/src/test/groovy/org/mockito/groovy/GroovyMockitoTest.groovy @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.groovy + +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.junit.MockitoJUnitRunner + +import static org.mockito.Mockito.verify + +@RunWith(MockitoJUnitRunner) +class GroovyMockitoTest { + + @Mock Helper helper + @InjectMocks ClassUnderTest classUnderTest + + /** + * Test that the Groovy class under test can call methods on a mocked Groovy + * helper class. + */ + @Test + void testCallGroovyFromGroovy() { + classUnderTest.methodUnderTest() + verify(helper).helperMethod() + } + + static class ClassUnderTest { + private final Helper helper + + ClassUnderTest(Helper helper) { + this.helper = helper + } + + void methodUnderTest() { + helper.helperMethod() + } + } + + static class Helper { + void helperMethod() { } + } +} From 20f16278286b23aa8101cc646b4567560a432224 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Apr 2022 11:54:51 +0200 Subject: [PATCH 010/160] Bump kotlinx-coroutines-core from 1.6.0-native-mt to 1.6.1-native-mt (#2609) Bumps [kotlinx-coroutines-core](https://github.com/Kotlin/kotlinx.coroutines) from 1.6.0-native-mt to 1.6.1-native-mt. - [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases) - [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md) - [Commits](https://github.com/Kotlin/kotlinx.coroutines/commits) --- updated-dependencies: - dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-core 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 7971b3933f..b9563876a7 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -39,7 +39,7 @@ libraries.kotlin = [ version: kotlinVersion, stdlib: "org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}", - coroutines: 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0-native-mt', + coroutines: 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1-native-mt', gradlePlugin: "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}", ] libraries.android = [ From 8314824b2c83aff91db140b58de7d9e7d9be5ebf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Apr 2022 20:22:46 +0200 Subject: [PATCH 011/160] Bump versions.errorprone from 2.13.0 to 2.13.1 (#2621) Bumps `versions.errorprone` from 2.13.0 to 2.13.1. Updates `error_prone_core` from 2.13.0 to 2.13.1 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.13.0...v2.13.1) Updates `error_prone_test_helpers` from 2.13.0 to 2.13.1 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.13.0...v2.13.1) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.google.errorprone:error_prone_test_helpers 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 b9563876a7..f2cfad59c7 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -6,7 +6,7 @@ def versions = [:] versions.bytebuddy = '1.12.9' versions.junitJupiter = '5.8.2' -versions.errorprone = '2.13.0' +versions.errorprone = '2.13.1' libraries.junit4 = 'junit:junit:4.13.2' libraries.junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}" From d23dc0e57a8360c8d80f69096cee24c20e4c7c26 Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Thu, 3 Mar 2022 19:50:55 +0100 Subject: [PATCH 012/160] Support subclass mocks on Graal VM. --- .../creation/bytebuddy/MockMethodAdvice.java | 5 +- .../creation/bytebuddy/ModuleHandler.java | 44 ++++++----- .../bytebuddy/SubclassBytecodeGenerator.java | 75 ++++++++++++++++--- .../bytebuddy/SubclassInjectionLoader.java | 22 +++++- .../org/mockito/internal/util/Platform.java | 6 +- .../NoByteCodeDependenciesTest.java | 2 - 6 files changed, 117 insertions(+), 37 deletions(-) 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 580f22b309..fc92e49acf 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java @@ -33,6 +33,7 @@ import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.MethodList; import net.bytebuddy.description.method.ParameterDescription; +import net.bytebuddy.description.type.TypeDefinition; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.scaffold.MethodGraph; import net.bytebuddy.implementation.Implementation; @@ -188,7 +189,9 @@ public boolean isOverridden(Object instance, Method origin) { SoftReference reference = graphs.get(instance.getClass()); MethodGraph methodGraph = reference == null ? null : reference.get(); if (methodGraph == null) { - methodGraph = compiler.compile(new TypeDescription.ForLoadedType(instance.getClass())); + methodGraph = + compiler.compile( + (TypeDefinition) TypeDescription.ForLoadedType.of(instance.getClass())); graphs.put(instance.getClass(), new SoftReference<>(methodGraph)); } MethodGraph.Node node = diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/ModuleHandler.java b/src/main/java/org/mockito/internal/creation/bytebuddy/ModuleHandler.java index 38716d0776..6a716291ec 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/ModuleHandler.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/ModuleHandler.java @@ -4,13 +4,6 @@ */ package org.mockito.internal.creation.bytebuddy; -import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer; -import static org.mockito.internal.util.StringUtil.join; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Random; - import net.bytebuddy.ByteBuddy; import net.bytebuddy.description.modifier.Ownership; import net.bytebuddy.description.modifier.Visibility; @@ -18,9 +11,18 @@ import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.MethodCall; import net.bytebuddy.implementation.StubMethod; +import net.bytebuddy.utility.GraalImageCode; +import net.bytebuddy.utility.RandomString; +import org.mockito.Mockito; import org.mockito.codegen.InjectionBase; import org.mockito.exceptions.base.MockitoException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer; +import static org.mockito.internal.util.StringUtil.join; + abstract class ModuleHandler { abstract boolean isOpened(Class source, Class target); @@ -35,9 +37,9 @@ abstract class ModuleHandler { abstract void adjustModuleGraph(Class source, Class target, boolean export, boolean read); - static ModuleHandler make(ByteBuddy byteBuddy, SubclassLoader loader, Random random) { + static ModuleHandler make(ByteBuddy byteBuddy, SubclassLoader loader) { try { - return new ModuleSystemFound(byteBuddy, loader, random); + return new ModuleSystemFound(byteBuddy, loader); } catch (Exception ignored) { return new NoModuleSystemFound(); } @@ -47,7 +49,6 @@ private static class ModuleSystemFound extends ModuleHandler { private final ByteBuddy byteBuddy; private final SubclassLoader loader; - private final Random random; private final int injectonBaseSuffix; @@ -58,15 +59,15 @@ private static class ModuleSystemFound extends ModuleHandler { canRead, addExports, addReads, - addOpens, forName; - private ModuleSystemFound(ByteBuddy byteBuddy, SubclassLoader loader, Random random) - throws Exception { + private ModuleSystemFound(ByteBuddy byteBuddy, SubclassLoader loader) throws Exception { this.byteBuddy = byteBuddy; this.loader = loader; - this.random = random; - injectonBaseSuffix = Math.abs(random.nextInt()); + injectonBaseSuffix = + GraalImageCode.getCurrent().isDefined() + ? 0 + : Math.abs(Mockito.class.hashCode()); Class moduleType = Class.forName("java.lang.Module"); getModule = Class.class.getMethod("getModule"); isOpen = moduleType.getMethod("isOpen", String.class, moduleType); @@ -75,7 +76,6 @@ private ModuleSystemFound(ByteBuddy byteBuddy, SubclassLoader loader, Random ran canRead = moduleType.getMethod("canRead", moduleType); addExports = moduleType.getMethod("addExports", String.class, moduleType); addReads = moduleType.getMethod("addReads", moduleType); - addOpens = moduleType.getMethod("addOpens", String.class, moduleType); forName = Class.class.getMethod("forName", String.class); } @@ -207,9 +207,12 @@ void adjustModuleGraph(Class source, Class target, boolean export, boolean ConstructorStrategy.Default.NO_CONSTRUCTORS) .name( String.format( - "%s$%d", + "%s$%s%s", "org.mockito.codegen.MockitoTypeCarrier", - Math.abs(random.nextInt()))) + RandomString.hashOf( + source.getName().hashCode()), + RandomString.hashOf( + target.getName().hashCode()))) .defineField( "mockitoType", Class.class, @@ -262,10 +265,11 @@ void adjustModuleGraph(Class source, Class target, boolean export, boolean .subclass(Object.class) .name( String.format( - "%s$%s$%d", + "%s$%s$%s%s", source.getName(), "MockitoModuleProbe", - Math.abs(random.nextInt()))) + RandomString.hashOf(source.getName().hashCode()), + RandomString.hashOf(target.getName().hashCode()))) .invokable(isTypeInitializer()) .intercept(implementation) .make() diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java index d27b5f82c3..c9029924e0 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java @@ -21,13 +21,13 @@ import java.io.IOException; import java.io.ObjectInputStream; +import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Random; +import java.util.*; + import net.bytebuddy.ByteBuddy; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.modifier.SynchronizationState; @@ -39,6 +39,8 @@ import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.attribute.MethodAttributeAppender; import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.utility.GraalImageCode; +import net.bytebuddy.utility.RandomString; import org.mockito.codegen.InjectionBase; import org.mockito.exceptions.base.MockitoException; import org.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport.CrossClassLoaderSerializableMock; @@ -52,7 +54,6 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { private final SubclassLoader loader; private final ModuleHandler handler; private final ByteBuddy byteBuddy; - private final Random random; private final Implementation readReplace; private final ElementMatcher matcher; @@ -82,8 +83,7 @@ protected SubclassBytecodeGenerator( this.readReplace = readReplace; this.matcher = matcher; byteBuddy = new ByteBuddy().with(TypeValidation.DISABLED); - random = new Random(); - handler = ModuleHandler.make(byteBuddy, loader, random); + handler = ModuleHandler.make(byteBuddy, loader); } private static boolean needsSamePackageClassLoader(MockFeatures features) { @@ -167,7 +167,8 @@ public Class mockClass(MockFeatures features) { && features.serializableMode != SerializableMode.ACROSS_CLASSLOADERS && !isComingFromJDK(features.mockedType) && (loader.isDisrespectingOpenness() - || handler.isOpened(features.mockedType, MockAccess.class)); + || handler.isOpened(features.mockedType, MockAccess.class)) + && !GraalImageCode.getCurrent().isDefined(); String typeName; if (localMock || (loader instanceof MultipleParentClassLoader @@ -180,7 +181,13 @@ public Class mockClass(MockFeatures features) { + features.mockedType.getSimpleName(); } String name = - String.format("%s$%s$%d", typeName, "MockitoMock", Math.abs(random.nextInt())); + String.format( + "%s$%s$%s", + typeName, + "MockitoMock", + GraalImageCode.getCurrent().isDefined() + ? suffix(features) + : RandomString.make()); if (localMock) { handler.adjustModuleGraph(features.mockedType, MockAccess.class, false, true); @@ -214,17 +221,36 @@ public Class mockClass(MockFeatures features) { } } } - + // Graal requires that the byte code of classes is identical what requires that interfaces + // are always + // defined in the exact same order. Therefore, we add an interface to the interface set if + // not mocking + // a class when Graal is active. + @SuppressWarnings("unchecked") + Class target = + GraalImageCode.getCurrent().isDefined() && features.mockedType.isInterface() + ? (Class) Object.class + : features.mockedType; DynamicType.Builder builder = byteBuddy - .subclass(features.mockedType) + .subclass(target) .name(name) .ignoreAlso(BytecodeGenerator.isGroovyMethod(false)) .annotateType( - features.stripAnnotations + features.stripAnnotations || features.mockedType.isInterface() ? new Annotation[0] : features.mockedType.getAnnotations()) - .implement(new ArrayList(features.interfaces)) + .implement( + new ArrayList<>( + GraalImageCode.getCurrent().isDefined() + ? sortedSerializable( + features.interfaces, + GraalImageCode.getCurrent().isDefined() + && features.mockedType + .isInterface() + ? features.mockedType + : void.class) + : features.interfaces)) .method(matcher) .intercept(dispatcher) .transform(withModifiers(SynchronizationState.PLAIN)) @@ -266,6 +292,31 @@ public Class mockClass(MockFeatures features) { .getLoaded(); } + private static CharSequence suffix(MockFeatures features) { + // Constructs a deterministic suffix for this mock to assure that mocks always carry the + // same name. + StringBuilder sb = new StringBuilder(); + Set names = new TreeSet<>(); + names.add(features.mockedType.getName()); + for (Class type : features.interfaces) { + names.add(type.getName()); + } + return sb.append(RandomString.hashOf(names.hashCode())) + .append(RandomString.hashOf(features.serializableMode.name().hashCode())) + .append(features.stripAnnotations ? "S" : "N"); + } + + private static Collection sortedSerializable( + Collection> interfaces, Class mockedType) { + SortedSet> types = new TreeSet<>(Comparator.comparing(Class::getName)); + types.addAll(interfaces); + if (mockedType != void.class) { + types.add(mockedType); + } + types.add(Serializable.class); + return types; + } + @Override public void mockClassStatic(Class type) { throw new MockitoException("The subclass byte code generator cannot create static mocks"); diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassInjectionLoader.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassInjectionLoader.java index c70de01355..b5013891d7 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassInjectionLoader.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassInjectionLoader.java @@ -11,6 +11,7 @@ import net.bytebuddy.dynamic.loading.ClassInjector; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import net.bytebuddy.utility.GraalImageCode; import org.mockito.codegen.InjectionBase; import org.mockito.exceptions.base.MockitoException; import org.mockito.internal.util.Platform; @@ -27,9 +28,14 @@ class SubclassInjectionLoader implements SubclassLoader { private final SubclassLoader loader; SubclassInjectionLoader() { - if (!Boolean.getBoolean("org.mockito.internal.noUnsafeInjection") + if (!Boolean.parseBoolean( + System.getProperty( + "org.mockito.internal.noUnsafeInjection", + Boolean.toString(GraalImageCode.getCurrent().isDefined()))) && ClassInjector.UsingReflection.isAvailable()) { this.loader = new WithReflection(); + } else if (GraalImageCode.getCurrent().isDefined()) { + this.loader = new WithIsolatedLoader(); } else if (ClassInjector.UsingLookup.isAvailable()) { this.loader = tryLookup(); } else { @@ -70,6 +76,20 @@ public ClassLoadingStrategy resolveStrategy( } } + private static class WithIsolatedLoader implements SubclassLoader { + + @Override + public boolean isDisrespectingOpenness() { + return false; + } + + @Override + public ClassLoadingStrategy resolveStrategy( + Class mockedType, ClassLoader classLoader, boolean localMock) { + return ClassLoadingStrategy.Default.WRAPPER; + } + } + private static class WithLookup implements SubclassLoader { private final Object lookup; diff --git a/src/main/java/org/mockito/internal/util/Platform.java b/src/main/java/org/mockito/internal/util/Platform.java index 946e4ff3be..fde59fbbe9 100644 --- a/src/main/java/org/mockito/internal/util/Platform.java +++ b/src/main/java/org/mockito/internal/util/Platform.java @@ -69,7 +69,11 @@ public static String describe() { } public static boolean isJava8BelowUpdate45() { - return isJava8BelowUpdate45(JVM_VERSION); + if (JVM_VERSION == null) { + return false; + } else { + return isJava8BelowUpdate45(JVM_VERSION); + } } static boolean isJava8BelowUpdate45(String jvmVersion) { diff --git a/src/test/java/org/mockitointegration/NoByteCodeDependenciesTest.java b/src/test/java/org/mockitointegration/NoByteCodeDependenciesTest.java index 2363fe6f35..3a2908dc1c 100644 --- a/src/test/java/org/mockitointegration/NoByteCodeDependenciesTest.java +++ b/src/test/java/org/mockitointegration/NoByteCodeDependenciesTest.java @@ -15,8 +15,6 @@ public class NoByteCodeDependenciesTest { - private ClassLoader contextClassLoader; - @Test public void pure_mockito_should_not_depend_bytecode_libraries() throws Exception { From f48d794ad14982a134fd14dd2aef03477b699dc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Apr 2022 20:16:57 +0200 Subject: [PATCH 013/160] Bump kotlinVersion from 1.6.20 to 1.6.21 (#2622) Bumps `kotlinVersion` from 1.6.20 to 1.6.21. Updates `kotlin-gradle-plugin` from 1.6.20 to 1.6.21 - [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.6.20 to 1.6.21 - [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 3930e187cd..7cad0ba8c5 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.6.20" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21" } } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index f2cfad59c7..54dc32090c 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -34,7 +34,7 @@ libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.2.0' libraries.groovy = 'org.codehaus.groovy:groovy:3.0.10' -def kotlinVersion = '1.6.20' +def kotlinVersion = '1.6.21' libraries.kotlin = [ version: kotlinVersion, From 737f0a3517256af6ade37fd0e776a2bb230de553 Mon Sep 17 00:00:00 2001 From: Jeremy Landis Date: Thu, 21 Apr 2022 07:01:53 -0400 Subject: [PATCH 014/160] Fixes #2623: Use zulu distribution and java 11 for release GHA job (#2624) --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf878a9b4d..7a6d99b687 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,11 +96,11 @@ jobs: with: fetch-depth: '0' # https://github.com/shipkit/shipkit-changelog#fetch-depth-on-ci - - name: Set up Java 8 + - name: Set up Java 11 uses: actions/setup-java@v3 with: - distribution: 'adopt' - java-version: 8 + distribution: 'zulu' + java-version: 11 - name: Build and release run: ./gradlew githubRelease publishToSonatype closeAndReleaseStagingRepository releaseSummary From 5c9267d2f9c54a7d2e165b5dc14e168e9d761de2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Apr 2022 20:14:12 +0200 Subject: [PATCH 015/160] Bump com.diffplug.spotless from 6.4.2 to 6.5.0 (#2631) Bumps com.diffplug.spotless from 6.4.2 to 6.5.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 7cad0ba8c5..7f87db8340 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.4.2' + id 'com.diffplug.spotless' version '6.5.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.2.0' From 1e6b3e019c71d68bd0fe5bb2f49616430e3edbaa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Apr 2022 00:43:10 +0200 Subject: [PATCH 016/160] Bump com.diffplug.spotless from 6.5.0 to 6.5.1 (#2632) Bumps com.diffplug.spotless from 6.5.0 to 6.5.1. --- updated-dependencies: - dependency-name: com.diffplug.spotless 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 7f87db8340..e135e3ff16 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.5.0' + id 'com.diffplug.spotless' version '6.5.1' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.2.0' From 57632913f4d160e7024421980c0e87f03fea5567 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 May 2022 20:16:18 +0200 Subject: [PATCH 017/160] Bump versions.bytebuddy from 1.12.9 to 1.12.10 (#2635) Bumps `versions.bytebuddy` from 1.12.9 to 1.12.10. Updates `byte-buddy` from 1.12.9 to 1.12.10 - [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.9...byte-buddy-1.12.10) Updates `byte-buddy-agent` from 1.12.9 to 1.12.10 - [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.9...byte-buddy-1.12.10) Updates `byte-buddy-android` from 1.12.9 to 1.12.10 - [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.9...byte-buddy-1.12.10) --- 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 54dc32090c..e5d20ac862 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.12.9' +versions.bytebuddy = '1.12.10' versions.junitJupiter = '5.8.2' versions.errorprone = '2.13.1' From 0ae788ff3997e80c5d8f87381396923a118d2203 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 May 2022 22:19:31 +0200 Subject: [PATCH 018/160] Bump com.diffplug.spotless from 6.5.1 to 6.5.2 (#2636) Bumps com.diffplug.spotless from 6.5.1 to 6.5.2. --- updated-dependencies: - dependency-name: com.diffplug.spotless 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 e135e3ff16..c67bf86c77 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.5.1' + id 'com.diffplug.spotless' version '6.5.2' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.2.0' From f59bf37187ec974a5cec94121158603fef1f597d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 May 2022 08:11:00 +0200 Subject: [PATCH 019/160] Bump material from 1.5.0 to 1.6.0 (#2637) Bumps [material](https://github.com/material-components/material-components-android) from 1.5.0 to 1.6.0. - [Release notes](https://github.com/material-components/material-components-android/releases) - [Commits](https://github.com/material-components/material-components-android/compare/1.5.0...1.6.0) --- updated-dependencies: - dependency-name: com.google.android.material:material 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 e5d20ac862..fb20b9938e 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -45,7 +45,7 @@ libraries.kotlin = [ libraries.android = [ ktx: 'androidx.core:core-ktx:1.7.0', compat: 'androidx.appcompat:appcompat:1.4.1', - material: 'com.google.android.material:material:1.5.0', + material: 'com.google.android.material:material:1.6.0', junit: 'androidx.test.ext:junit:1.1.3', espresso: 'androidx.test.espresso:espresso-core:3.4.0', ] From 4629ed801868d9fd12117359a5084784003f582c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 May 2022 12:06:39 +0200 Subject: [PATCH 020/160] Bump com.diffplug.spotless from 6.5.2 to 6.6.0 (#2641) Bumps com.diffplug.spotless from 6.5.2 to 6.6.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 c67bf86c77..8ed13e9484 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.5.2' + id 'com.diffplug.spotless' version '6.6.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.2.0' From 0a7fb0468620db42fa2b57078d61b5af33da26ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Boutemy?= Date: Mon, 16 May 2022 07:35:48 +0100 Subject: [PATCH 021/160] Fix reproducible build (#2642) --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index 8ed13e9484..e4e9822354 100644 --- a/build.gradle +++ b/build.gradle @@ -72,6 +72,8 @@ allprojects { proj -> tasks.withType(AbstractArchiveTask) { preserveFileTimestamps = false reproducibleFileOrder = true + dirMode = Integer.parseInt("0755", 8) + fileMode = Integer.parseInt("0644", 8) } apply plugin: 'checkstyle' From 94e9797ba0b4c911a3e246bef68c2cb8ac2ab429 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 23:15:49 +0200 Subject: [PATCH 022/160] Bump com.diffplug.spotless from 6.6.0 to 6.6.1 (#2643) Bumps com.diffplug.spotless from 6.6.0 to 6.6.1. --- updated-dependencies: - dependency-name: com.diffplug.spotless 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 e4e9822354..1e72e8eb04 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.6.0' + id 'com.diffplug.spotless' version '6.6.1' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.2.0' From a3d57fd73ac3dc4288cc93b4e2677975d0820a98 Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Thu, 19 May 2022 23:46:43 +0200 Subject: [PATCH 023/160] Reintroduce inheriting type annotations from interfaces if only one interface is mocked, including additional interfaces. Without this restriction, the first presented interface might determine the interfaces that are inherited by a subsequent mock that presents the interfaces in a different order. Also, it does not make semantic sense to decide on a particular interface to inherit annotations from. Fixes #2640. --- .../bytebuddy/SubclassBytecodeGenerator.java | 24 ++++-- .../SubclassByteBuddyMockMakerTest.java | 86 +++++++++++++++++-- 2 files changed, 97 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java index c9029924e0..717de9de39 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java @@ -222,24 +222,32 @@ public Class mockClass(MockFeatures features) { } } // Graal requires that the byte code of classes is identical what requires that interfaces - // are always - // defined in the exact same order. Therefore, we add an interface to the interface set if - // not mocking - // a class when Graal is active. + // are always defined in the exact same order. Therefore, we add an interface to the + // interface set if not mocking a class when Graal is active. @SuppressWarnings("unchecked") Class target = GraalImageCode.getCurrent().isDefined() && features.mockedType.isInterface() ? (Class) Object.class : features.mockedType; + // If we create a mock for an interface with additional interfaces implemented, we do not + // want to preserve the annotations of either interface. The caching mechanism does not + // consider the order of these interfaces and the same mock class might be reused for + // either order. Also, it does not have clean semantics as annotations are not normally + // preserved for interfaces in Java. + Annotation[] annotationsOnType; + if (features.stripAnnotations) { + annotationsOnType = new Annotation[0]; + } else if (!features.mockedType.isInterface() || features.interfaces.isEmpty()) { + annotationsOnType = features.mockedType.getAnnotations(); + } else { + annotationsOnType = new Annotation[0]; + } DynamicType.Builder builder = byteBuddy .subclass(target) .name(name) .ignoreAlso(BytecodeGenerator.isGroovyMethod(false)) - .annotateType( - features.stripAnnotations || features.mockedType.isInterface() - ? new Annotation[0] - : features.mockedType.getAnnotations()) + .annotateType(annotationsOnType) .implement( new ArrayList<>( GraalImageCode.getCurrent().isDefined() diff --git a/src/test/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMakerTest.java b/src/test/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMakerTest.java index 6e082287d1..e6a0086c9f 100644 --- a/src/test/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMakerTest.java +++ b/src/test/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMakerTest.java @@ -12,6 +12,7 @@ import org.mockito.internal.creation.MockSettingsImpl; import org.mockito.plugins.MockMaker; +import java.io.Serializable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Observable; @@ -79,9 +80,8 @@ public void is_type_mockable_give_empty_reason_if_type_is_mockable() { } @Test - public void mock_type_with_annotations() throws Exception { - MockSettingsImpl mockSettings = - new MockSettingsImpl(); + public void mock_class_with_annotations() throws Exception { + MockSettingsImpl mockSettings = new MockSettingsImpl<>(); mockSettings.setTypeToMock(ClassWithAnnotation.class); ClassWithAnnotation proxy = mockMaker.createMock(mockSettings, dummyHandler()); @@ -102,10 +102,79 @@ public void mock_type_with_annotations() throws Exception { .isEqualTo("bar"); } + @Test + public void mock_class_with_annotations_with_additional_interface() throws Exception { + MockSettingsImpl mockSettings = new MockSettingsImpl<>(); + mockSettings.setTypeToMock(ClassWithAnnotation.class); + mockSettings.extraInterfaces(Serializable.class); + + ClassWithAnnotation proxy = mockMaker.createMock(mockSettings, dummyHandler()); + + assertThat(proxy.getClass().isAnnotationPresent(SampleAnnotation.class)).isTrue(); + assertThat(proxy.getClass().getAnnotation(SampleAnnotation.class).value()).isEqualTo("foo"); + + assertThat( + proxy.getClass() + .getMethod("sampleMethod") + .isAnnotationPresent(SampleAnnotation.class)) + .isTrue(); + assertThat( + proxy.getClass() + .getMethod("sampleMethod") + .getAnnotation(SampleAnnotation.class) + .value()) + .isEqualTo("bar"); + } + + @Test + public void mock_interface_with_annotations() throws Exception { + MockSettingsImpl mockSettings = new MockSettingsImpl<>(); + mockSettings.setTypeToMock(InterfaceWithAnnotation.class); + + InterfaceWithAnnotation proxy = mockMaker.createMock(mockSettings, dummyHandler()); + + assertThat(proxy.getClass().isAnnotationPresent(SampleAnnotation.class)).isTrue(); + assertThat(proxy.getClass().getAnnotation(SampleAnnotation.class).value()).isEqualTo("foo"); + + assertThat( + proxy.getClass() + .getMethod("sampleMethod") + .isAnnotationPresent(SampleAnnotation.class)) + .isTrue(); + assertThat( + proxy.getClass() + .getMethod("sampleMethod") + .getAnnotation(SampleAnnotation.class) + .value()) + .isEqualTo("bar"); + } + + @Test + public void mock_interface_with_annotations_with_additional_interface() throws Exception { + MockSettingsImpl mockSettings = new MockSettingsImpl<>(); + mockSettings.setTypeToMock(InterfaceWithAnnotation.class); + mockSettings.extraInterfaces(Serializable.class); + + InterfaceWithAnnotation proxy = mockMaker.createMock(mockSettings, dummyHandler()); + + assertThat(proxy.getClass().isAnnotationPresent(SampleAnnotation.class)).isFalse(); + + assertThat( + proxy.getClass() + .getMethod("sampleMethod") + .isAnnotationPresent(SampleAnnotation.class)) + .isTrue(); + assertThat( + proxy.getClass() + .getMethod("sampleMethod") + .getAnnotation(SampleAnnotation.class) + .value()) + .isEqualTo("bar"); + } + @Test public void mock_type_without_annotations() throws Exception { - MockSettingsImpl mockSettings = - new MockSettingsImpl(); + MockSettingsImpl mockSettings = new MockSettingsImpl<>(); mockSettings.setTypeToMock(ClassWithAnnotation.class); mockSettings.withoutAnnotations(); @@ -138,4 +207,11 @@ public void sampleMethod() { throw new UnsupportedOperationException(); } } + + @SampleAnnotation("foo") + public interface InterfaceWithAnnotation { + + @SampleAnnotation("bar") + void sampleMethod(); + } } From 7d2e4cce07a7842eaab3e15351d1f1a89f59e122 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 May 2022 20:20:34 +0200 Subject: [PATCH 024/160] Bump shipkit-auto-version from 1.1.20 to 1.2.0 (#2651) Bumps shipkit-auto-version from 1.1.20 to 1.2.0. --- updated-dependencies: - dependency-name: org.shipkit:shipkit-auto-version 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 1e72e8eb04..fc14697ad0 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.1.15' - classpath 'org.shipkit:shipkit-auto-version:1.1.20' + classpath 'org.shipkit:shipkit-auto-version:1.2.0' classpath 'com.google.googlejavaformat:google-java-format:1.15.0' classpath 'com.android.tools.build:gradle:4.2.0' From 6ce278b47367c18373572009e836056c64ac9e97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 May 2022 20:37:14 +0200 Subject: [PATCH 025/160] Bump versions.errorprone from 2.13.1 to 2.14.0 (#2653) Bumps `versions.errorprone` from 2.13.1 to 2.14.0. Updates `error_prone_core` from 2.13.1 to 2.14.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.13.1...v2.14.0) Updates `error_prone_test_helpers` from 2.13.1 to 2.14.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.13.1...v2.14.0) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.google.errorprone:error_prone_test_helpers 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 fb20b9938e..070154bf26 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -6,7 +6,7 @@ def versions = [:] versions.bytebuddy = '1.12.10' versions.junitJupiter = '5.8.2' -versions.errorprone = '2.13.1' +versions.errorprone = '2.14.0' libraries.junit4 = 'junit:junit:4.13.2' libraries.junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}" From 70cf2d2f48506fca24f16963e9c3d2223898ae5a Mon Sep 17 00:00:00 2001 From: "K. Siva Prasad Reddy" Date: Thu, 26 May 2022 22:10:37 +0530 Subject: [PATCH 026/160] Add support for customizing strictness via `@Mock` annotation and `MockSettings` (#2650) Fixes #2648 --- src/main/java/org/mockito/Mock.java | 12 ++++ src/main/java/org/mockito/MockSettings.java | 20 +++++++ src/main/java/org/mockito/Mockito.java | 16 ++++++ .../MockAnnotationProcessor.java | 1 + .../internal/creation/MockSettingsImpl.java | 13 ++++- .../creation/settings/CreationSettings.java | 12 +++- .../mockito/internal/exceptions/Reporter.java | 4 ++ .../stubbing/InvocationContainerImpl.java | 2 +- .../internal/stubbing/StrictnessSelector.java | 4 +- .../mockito/mock/MockCreationSettings.java | 11 ++++ .../creation/MockSettingsImplTest.java | 6 ++ .../StrictnessMockAnnotationTest.java | 43 ++++++++++++++ .../StrictnessWithSettingsTest.java | 57 +++++++++++++++++++ 13 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 src/test/java/org/mockitousage/strictness/StrictnessMockAnnotationTest.java create mode 100644 src/test/java/org/mockitousage/strictness/StrictnessWithSettingsTest.java diff --git a/src/main/java/org/mockito/Mock.java b/src/main/java/org/mockito/Mock.java index 62c4faa2df..7c3e036083 100644 --- a/src/main/java/org/mockito/Mock.java +++ b/src/main/java/org/mockito/Mock.java @@ -13,6 +13,7 @@ import java.lang.annotation.Target; import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; /** @@ -105,10 +106,21 @@ boolean serializable() default false; /** + * @deprecated Use {@link Mock#strictness()} instead. + * * Mock will be lenient, see {@link MockSettings#lenient()}. * For examples how to use 'Mock' annotation and parameters see {@link Mock}. * * @since 2.23.3 */ + @Deprecated boolean lenient() default false; + + /** + * Mock will have custom strictness, see {@link MockSettings#strictness(Strictness)}. + * For examples how to use 'Mock' annotation and parameters see {@link Mock}. + * + * @since 4.6.0 + */ + Strictness strictness() default Strictness.STRICT_STUBS; } diff --git a/src/main/java/org/mockito/MockSettings.java b/src/main/java/org/mockito/MockSettings.java index fb6554070f..f838a8d121 100644 --- a/src/main/java/org/mockito/MockSettings.java +++ b/src/main/java/org/mockito/MockSettings.java @@ -350,6 +350,8 @@ public interface MockSettings extends Serializable { MockCreationSettings buildStatic(Class classToMock); /** + * @deprecated Use {@link MockSettings#strictness(Strictness)} instead. + * * Lenient mocks bypass "strict stubbing" validation (see {@link Strictness#STRICT_STUBS}). * When mock is declared as lenient none of its stubbings will be checked for potential stubbing problems such as * 'unnecessary stubbing' ({@link UnnecessaryStubbingException}) or for 'stubbing argument mismatch' {@link PotentialStubbingProblem}. @@ -360,5 +362,23 @@ public interface MockSettings extends Serializable { * * For more information and an elaborate example, see {@link Mockito#lenient()}. */ + @Deprecated MockSettings lenient(); + + /** + * Specifies strictness level for the mock. + * The default strictness level is determined by the rule/runner used. + * If you are using no rule/runner, the default strictness level is LENIENT + * + *

+     *   Foo defaultStrictMock = mock(Foo.class);
+     *   Foo explicitStrictMock = mock(Foo.class, withSettings().strictness(Strictness.STRICT_STUBS));
+     *   Foo lenientMock = mock(Foo.class, withSettings().strictness(Strictness.LENIENT));
+     * 
+ * + * @param strictness the strictness level to set on mock + * @return settings instance so that you can fluently specify other settings + * @since 4.6.0 + */ + MockSettings strictness(Strictness strictness); } diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 8c1a0cff9b..8b7b92776c 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -105,6 +105,7 @@ * 49. New API for mocking object construction (Since 3.5.0)
* 50. Avoiding code generation when restricting mocks to interfaces (Since 3.12.2)
* 51. New API for marking classes as unmockable (Since 4.1.0)
+ * 52. New strictness attribute for @Mock annotation and MockSettings.strictness() methods (Since 4.6.0)
* * *

0. Migrating to Mockito 2

@@ -1606,6 +1607,21 @@ * For any class/interface you own that is problematic to mock, you can now mark the class with {@link org.mockito.DoNotMock @DoNotMock}. For usage * of the annotation and how to ship your own (to avoid a compile time dependency on a test artifact), please see its JavaDoc. *

+ * + *

52. + * New strictness attribute for @Mock annotation and MockSettings.strictness() methods (Since 4.6.0)

+ * + * You can now customize the strictness level for a single mock, either using `@Mock` annotation strictness attribute or + * using `MockSettings.strictness()`. This can be useful if you want all of your mocks to be strict, + * but one of the mocks to be lenient. + * + *

+ *   @Mock(strictness = Strictness.LENIENT)
+ *   Foo mock;
+ *   // using MockSettings.withSettings()
+ *   Foo mock = Mockito.mock(Foo.class, withSettings().strictness(Strictness.WARN));
+ * 
+ * */ @CheckReturnValue @SuppressWarnings("unchecked") diff --git a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java index e48291e5d5..484c9102e4 100644 --- a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java +++ b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java @@ -45,6 +45,7 @@ public static Object processAnnotationForMock( if (annotation.stubOnly()) { mockSettings.stubOnly(); } + mockSettings.strictness(annotation.strictness()); if (annotation.lenient()) { mockSettings.lenient(); } diff --git a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java index 3aa746c065..e1dcd48f9e 100644 --- a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java +++ b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java @@ -12,6 +12,7 @@ import static org.mockito.internal.exceptions.Reporter.extraInterfacesRequiresAtLeastOneInterface; import static org.mockito.internal.exceptions.Reporter.methodDoesNotAcceptParameter; import static org.mockito.internal.exceptions.Reporter.requiresAtLeastOneListener; +import static org.mockito.internal.exceptions.Reporter.strictnessDoesNotAcceptNullParameter; import static org.mockito.internal.util.collections.Sets.newSet; import java.io.Serializable; @@ -33,6 +34,7 @@ import org.mockito.mock.MockCreationSettings; import org.mockito.mock.MockName; import org.mockito.mock.SerializableMode; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; @SuppressWarnings("unchecked") @@ -239,7 +241,16 @@ public MockCreationSettings buildStatic(Class classToMock) { @Override public MockSettings lenient() { - this.lenient = true; + this.strictness = Strictness.LENIENT; + return this; + } + + @Override + public MockSettings strictness(Strictness strictness) { + this.strictness = strictness; + if (strictness == null) { + throw strictnessDoesNotAcceptNullParameter(); + } return this; } diff --git a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java index 9da7311c87..50bb937696 100644 --- a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java +++ b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java @@ -18,6 +18,7 @@ import org.mockito.mock.MockCreationSettings; import org.mockito.mock.MockName; import org.mockito.mock.SerializableMode; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; public class CreationSettings implements MockCreationSettings, Serializable { @@ -44,7 +45,7 @@ public class CreationSettings implements MockCreationSettings, Serializabl private boolean useConstructor; private Object outerClassInstance; private Object[] constructorArgs; - protected boolean lenient; + protected Strictness strictness = Strictness.STRICT_STUBS; public CreationSettings() {} @@ -65,7 +66,7 @@ public CreationSettings(CreationSettings copy) { this.useConstructor = copy.isUsingConstructor(); this.outerClassInstance = copy.getOuterClassInstance(); this.constructorArgs = copy.getConstructorArgs(); - this.lenient = copy.lenient; + this.strictness = copy.strictness; this.stripAnnotations = copy.stripAnnotations; } @@ -170,6 +171,11 @@ public boolean isStubOnly() { @Override public boolean isLenient() { - return lenient; + return strictness == Strictness.LENIENT; + } + + @Override + public Strictness getStrictness() { + return strictness; } } diff --git a/src/main/java/org/mockito/internal/exceptions/Reporter.java b/src/main/java/org/mockito/internal/exceptions/Reporter.java index f91c6e1f0d..ea670407e7 100644 --- a/src/main/java/org/mockito/internal/exceptions/Reporter.java +++ b/src/main/java/org/mockito/internal/exceptions/Reporter.java @@ -964,6 +964,10 @@ public static MockitoException defaultAnswerDoesNotAcceptNullParameter() { return new MockitoException("defaultAnswer() does not accept null parameter"); } + public static MockitoException strictnessDoesNotAcceptNullParameter() { + return new MockitoException("strictness() does not accept null parameter"); + } + public static MockitoException serializableWontWorkForObjectsThatDontImplementSerializable( Class classToMock) { return new MockitoException( diff --git a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java index 3091670fb1..5aedbb912e 100644 --- a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java @@ -38,7 +38,7 @@ public class InvocationContainerImpl implements InvocationContainer, Serializabl public InvocationContainerImpl(MockCreationSettings mockSettings) { this.registeredInvocations = createRegisteredInvocations(mockSettings); - this.mockStrictness = mockSettings.isLenient() ? Strictness.LENIENT : null; + this.mockStrictness = mockSettings.getStrictness(); this.doAnswerStyleStubbing = new DoAnswerStyleStubbing(); } diff --git a/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java b/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java index 6422fd53ae..c8e7e44400 100644 --- a/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java +++ b/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java @@ -31,8 +31,8 @@ public static Strictness determineStrictness( return stubbing.getStrictness(); } - if (mockSettings.isLenient()) { - return Strictness.LENIENT; + if (mockSettings.getStrictness() != null) { + return mockSettings.getStrictness(); } return testLevelStrictness; diff --git a/src/main/java/org/mockito/mock/MockCreationSettings.java b/src/main/java/org/mockito/mock/MockCreationSettings.java index 23d5418a14..f7f0b96028 100644 --- a/src/main/java/org/mockito/mock/MockCreationSettings.java +++ b/src/main/java/org/mockito/mock/MockCreationSettings.java @@ -118,10 +118,21 @@ public interface MockCreationSettings { Object getOuterClassInstance(); /** + * @deprecated Use {@link MockCreationSettings#getStrictness()} instead. + * * Informs if the mock was created with "lenient" strictness, e.g. having {@link Strictness#LENIENT} characteristic. * For more information about using mocks with lenient strictness, see {@link MockSettings#lenient()}. * * @since 2.20.0 */ + @Deprecated boolean isLenient(); + + /** + * Sets strictness level for the mock, e.g. having {@link Strictness#STRICT_STUBS} characteristic. + * For more information about using mocks with custom strictness, see {@link MockSettings#strictness(Strictness)}. + * + * @since 4.6.0 + */ + Strictness getStrictness(); } diff --git a/src/test/java/org/mockito/internal/creation/MockSettingsImplTest.java b/src/test/java/org/mockito/internal/creation/MockSettingsImplTest.java index 9e8d68d5d8..dc8af35423 100644 --- a/src/test/java/org/mockito/internal/creation/MockSettingsImplTest.java +++ b/src/test/java/org/mockito/internal/creation/MockSettingsImplTest.java @@ -260,4 +260,10 @@ public void addListeners_canAddDuplicateMockObjectListeners_ItsNotOurBusinessThe assertThat(mockSettingsImpl.getStubbingLookupListeners()) .containsSequence(stubbingLookupListener, stubbingLookupListener); } + + @Test + public void validates_strictness() { + assertThatThrownBy(() -> mockSettingsImpl.strictness(null)) + .hasMessageContaining("strictness() does not accept null parameter"); + } } diff --git a/src/test/java/org/mockitousage/strictness/StrictnessMockAnnotationTest.java b/src/test/java/org/mockitousage/strictness/StrictnessMockAnnotationTest.java new file mode 100644 index 0000000000..c035b3d708 --- /dev/null +++ b/src/test/java/org/mockitousage/strictness/StrictnessMockAnnotationTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitousage.strictness; + +import org.assertj.core.api.Assertions; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.exceptions.misusing.PotentialStubbingProblem; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.quality.Strictness; +import org.mockitousage.IMethods; + +import static org.mockito.Mockito.when; + +public class StrictnessMockAnnotationTest { + + public @Rule MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); + + @Mock(strictness = Strictness.LENIENT) + IMethods lenientMock; + + @Mock IMethods regularMock; + + @Test + public void mock_is_lenient() { + when(lenientMock.simpleMethod("1")).thenReturn("1"); + + // then lenient mock does not throw: + ProductionCode.simpleMethod(lenientMock, "3"); + } + + @Test + public void mock_is_strict() { + when(regularMock.simpleMethod("2")).thenReturn("2"); + + Assertions.assertThatThrownBy(() -> ProductionCode.simpleMethod(regularMock, "4")) + .isInstanceOf(PotentialStubbingProblem.class); + } +} diff --git a/src/test/java/org/mockitousage/strictness/StrictnessWithSettingsTest.java b/src/test/java/org/mockitousage/strictness/StrictnessWithSettingsTest.java new file mode 100644 index 0000000000..854792cd50 --- /dev/null +++ b/src/test/java/org/mockitousage/strictness/StrictnessWithSettingsTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitousage.strictness; + +import org.assertj.core.api.Assertions; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.exceptions.misusing.PotentialStubbingProblem; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.quality.Strictness; +import org.mockitousage.IMethods; + +import static org.mockito.Mockito.*; + +public class StrictnessWithSettingsTest { + + public @Rule MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); + + IMethods lenientMock; + IMethods regularMock; + IMethods strictMock; + + @Before + public void before() { + lenientMock = mock(IMethods.class, withSettings().strictness(Strictness.LENIENT)); + regularMock = mock(IMethods.class); + strictMock = mock(IMethods.class, withSettings().strictness(Strictness.STRICT_STUBS)); + } + + @Test + public void mock_is_lenient() { + when(lenientMock.simpleMethod("1")).thenReturn("1"); + + // lenient mock does not throw + ProductionCode.simpleMethod(lenientMock, "3"); + } + + @Test + public void mock_is_strict_with_default_settings() { + when(regularMock.simpleMethod("3")).thenReturn("3"); + + Assertions.assertThatThrownBy(() -> ProductionCode.simpleMethod(regularMock, "4")) + .isInstanceOf(PotentialStubbingProblem.class); + } + + @Test + public void mock_is_strict_with_explicit_settings() { + when(strictMock.simpleMethod("2")).thenReturn("2"); + + Assertions.assertThatThrownBy(() -> ProductionCode.simpleMethod(strictMock, "5")) + .isInstanceOf(PotentialStubbingProblem.class); + } +} From 93bba1171c7f41585ea8cc9c7ecc140929772124 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 May 2022 20:48:00 +0200 Subject: [PATCH 027/160] Bump shipkit-changelog from 1.1.15 to 1.2.0 (#2654) Bumps shipkit-changelog from 1.1.15 to 1.2.0. --- updated-dependencies: - dependency-name: org.shipkit:shipkit-changelog 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 fc14697ad0..7cc3dc209c 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { classpath 'net.ltgt.gradle:gradle-errorprone-plugin:2.0.2' classpath "io.github.gradle-nexus:publish-plugin:1.1.0" - classpath 'org.shipkit:shipkit-changelog:1.1.15' + classpath 'org.shipkit:shipkit-changelog:1.2.0' classpath 'org.shipkit:shipkit-auto-version:1.2.0' classpath 'com.google.googlejavaformat:google-java-format:1.15.0' From e5a06c5dbae4fecdbb8054d542d3bc5ba5c8ee00 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Sun, 29 May 2022 20:10:16 +0800 Subject: [PATCH 028/160] Fix typo (#2655) --- .../internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java index 533e2872df..da8de10de9 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java @@ -22,7 +22,7 @@ * the type to mock, extra interfaces, and serialization support. * *

- * The type to mock has to be not final and not part of the JDK. THe created mock will implement extra interfaces + * The type to mock has to be not final and not part of the JDK. The created mock will implement extra interfaces * if any. And will implement Serializable if this settings is explicitly set. */ public class SubclassByteBuddyMockMaker implements ClassCreatingMockMaker { From 4f41700cfbd00afeff7b9b48e1cdf777cf248ed5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 May 2022 21:05:40 +0200 Subject: [PATCH 029/160] Bump assertj-core from 3.22.0 to 3.23.1 (#2658) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1) --- updated-dependencies: - dependency-name: org.assertj:assertj-core 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 070154bf26..2083162117 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -13,7 +13,7 @@ libraries.junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${versions.juni libraries.junitPlatformLauncher = 'org.junit.platform:junit-platform-launcher:1.8.2' 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.22.0' +libraries.assertj = 'org.assertj:assertj-core:3.23.1' libraries.hamcrest = 'org.hamcrest:hamcrest-core:2.2' libraries.opentest4j = 'org.opentest4j:opentest4j:1.2.0' From 2bbf7963b17ea83c24c6925e0bd7ddc99e222825 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jun 2022 21:46:54 +0200 Subject: [PATCH 030/160] Bump material from 1.6.0 to 1.6.1 (#2662) Bumps [material](https://github.com/material-components/material-components-android) from 1.6.0 to 1.6.1. - [Release notes](https://github.com/material-components/material-components-android/releases) - [Commits](https://github.com/material-components/material-components-android/compare/1.6.0...1.6.1) --- updated-dependencies: - dependency-name: com.google.android.material:material 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 2083162117..08d8f4ef7b 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -45,7 +45,7 @@ libraries.kotlin = [ libraries.android = [ ktx: 'androidx.core:core-ktx:1.7.0', compat: 'androidx.appcompat:appcompat:1.4.1', - material: 'com.google.android.material:material:1.6.0', + material: 'com.google.android.material:material:1.6.1', junit: 'androidx.test.ext:junit:1.1.3', espresso: 'androidx.test.espresso:espresso-core:3.4.0', ] From c47c7ecedd66824325be0e62b883d78c97444521 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jun 2022 21:47:12 +0200 Subject: [PATCH 031/160] Bump core-ktx from 1.7.0 to 1.8.0 (#2661) Bumps core-ktx from 1.7.0 to 1.8.0. --- updated-dependencies: - dependency-name: androidx.core:core-ktx 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 08d8f4ef7b..1a07375860 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -43,7 +43,7 @@ libraries.kotlin = [ gradlePlugin: "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}", ] libraries.android = [ - ktx: 'androidx.core:core-ktx:1.7.0', + ktx: 'androidx.core:core-ktx:1.8.0', compat: 'androidx.appcompat:appcompat:1.4.1', material: 'com.google.android.material:material:1.6.1', junit: 'androidx.test.ext:junit:1.1.3', From 1fbef57f8a54ec6ff3ffc1551b493351df81ab9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jun 2022 21:47:22 +0200 Subject: [PATCH 032/160] Bump groovy from 3.0.10 to 3.0.11 (#2660) Bumps [groovy](https://github.com/apache/groovy) from 3.0.10 to 3.0.11. - [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 1a07375860..4fe2088485 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.17.200' libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.2.0' -libraries.groovy = 'org.codehaus.groovy:groovy:3.0.10' +libraries.groovy = 'org.codehaus.groovy:groovy:3.0.11' def kotlinVersion = '1.6.21' libraries.kotlin = [ From ff98622a8f4bbe96ef5405434b5d788fcd118bb4 Mon Sep 17 00:00:00 2001 From: Andy Coates <8012398+big-andy-coates@users.noreply.github.com> Date: Thu, 2 Jun 2022 17:07:34 +0100 Subject: [PATCH 033/160] Restore behavior for mocks configured without strictness (#2659) Fixes #2656 --- src/main/java/org/mockito/Mock.java | 35 +++++++++-- .../MockAnnotationProcessor.java | 6 +- .../internal/creation/MockSettingsImpl.java | 2 +- .../creation/settings/CreationSettings.java | 2 +- src/test/java/org/mockito/MockTest.java | 58 +++++++++++++++++++ .../StrictnessMockAnnotationTest.java | 58 ++++++++++++++----- .../java/org/mockitousage/ProductionCode.java | 15 +++++ .../java/org/mockitousage/StrictnessTest.java | 25 ++++++++ 8 files changed, 179 insertions(+), 22 deletions(-) create mode 100644 src/test/java/org/mockito/MockTest.java create mode 100644 subprojects/junit-jupiter/src/test/java/org/mockitousage/ProductionCode.java diff --git a/src/main/java/org/mockito/Mock.java b/src/main/java/org/mockito/Mock.java index 7c3e036083..e8469b830d 100644 --- a/src/main/java/org/mockito/Mock.java +++ b/src/main/java/org/mockito/Mock.java @@ -13,7 +13,6 @@ import java.lang.annotation.Target; import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; /** @@ -34,12 +33,13 @@ * @Mock(name = "database") private ArticleDatabase dbMock; * @Mock(answer = RETURNS_MOCKS) private UserProvider userProvider; * @Mock(extraInterfaces = {Queue.class, Observer.class}) private ArticleMonitor articleMonitor; + * @Mock(strictness = Mock.Strictness.LENIENT) private ArticleConsumer articleConsumer; * @Mock(stubOnly = true) private Logger logger; * * private ArticleManager manager; * * @Before public void setup() { - * manager = new ArticleManager(userProvider, database, calculator, articleMonitor, logger); + * manager = new ArticleManager(userProvider, database, calculator, articleMonitor, articleConsumer, logger); * } * } * @@ -117,10 +117,35 @@ boolean lenient() default false; /** - * Mock will have custom strictness, see {@link MockSettings#strictness(Strictness)}. + * Mock will have custom strictness, see {@link MockSettings#strictness(org.mockito.quality.Strictness)}. * For examples how to use 'Mock' annotation and parameters see {@link Mock}. * - * @since 4.6.0 + * @since 4.6.1 */ - Strictness strictness() default Strictness.STRICT_STUBS; + Strictness strictness() default Strictness.TEST_LEVEL_DEFAULT; + + enum Strictness { + + /** + * Default value used to indicate the mock does not override the test level strictness. + * + * @since 4.6.1 + */ + TEST_LEVEL_DEFAULT, + + /** + * See {@link org.mockito.quality.Strictness#LENIENT} + */ + LENIENT, + + /** + * See {@link org.mockito.quality.Strictness#WARN} + */ + WARN, + + /** + * See {@link org.mockito.quality.Strictness#STRICT_STUBS} + */ + STRICT_STUBS + } } diff --git a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java index 484c9102e4..66c3f31d12 100644 --- a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java +++ b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java @@ -17,6 +17,7 @@ import org.mockito.Mockito; import org.mockito.exceptions.base.MockitoException; import org.mockito.internal.util.Supplier; +import org.mockito.quality.Strictness; /** * Instantiates a mock on a field annotated by {@link Mock} @@ -28,6 +29,7 @@ public Object process(Mock annotation, Field field) { annotation, field.getType(), field::getGenericType, field.getName()); } + @SuppressWarnings("deprecation") public static Object processAnnotationForMock( Mock annotation, Class type, Supplier genericType, String name) { MockSettings mockSettings = Mockito.withSettings(); @@ -45,10 +47,12 @@ public static Object processAnnotationForMock( if (annotation.stubOnly()) { mockSettings.stubOnly(); } - mockSettings.strictness(annotation.strictness()); if (annotation.lenient()) { mockSettings.lenient(); } + if (annotation.strictness() != Mock.Strictness.TEST_LEVEL_DEFAULT) { + mockSettings.strictness(Strictness.valueOf(annotation.strictness().toString())); + } // see @Mock answer default value mockSettings.defaultAnswer(annotation.answer()); diff --git a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java index e1dcd48f9e..f73a718298 100644 --- a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java +++ b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java @@ -247,10 +247,10 @@ public MockSettings lenient() { @Override public MockSettings strictness(Strictness strictness) { - this.strictness = strictness; if (strictness == null) { throw strictnessDoesNotAcceptNullParameter(); } + this.strictness = strictness; return this; } diff --git a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java index 50bb937696..13939f6fbd 100644 --- a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java +++ b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java @@ -45,7 +45,7 @@ public class CreationSettings implements MockCreationSettings, Serializabl private boolean useConstructor; private Object outerClassInstance; private Object[] constructorArgs; - protected Strictness strictness = Strictness.STRICT_STUBS; + protected Strictness strictness = null; public CreationSettings() {} diff --git a/src/test/java/org/mockito/MockTest.java b/src/test/java/org/mockito/MockTest.java new file mode 100644 index 0000000000..e904ec5d45 --- /dev/null +++ b/src/test/java/org/mockito/MockTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito; + +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import static org.hamcrest.Matchers.not; +import static org.junit.Assume.assumeThat; + +@RunWith(Enclosed.class) +public class MockTest { + + @RunWith(value = Parameterized.class) + public static class StrictnessToMockStrictnessTest { + + public org.mockito.quality.Strictness strictness; + + public StrictnessToMockStrictnessTest(org.mockito.quality.Strictness strictness) { + this.strictness = strictness; + } + + @Test + public void should_have_matching_enum_in_mock_strictness_enum() { + Mock.Strictness.valueOf(strictness.name()); + } + + @Parameterized.Parameters(name = "{0}") + public static org.mockito.quality.Strictness[] data() { + return org.mockito.quality.Strictness.values(); + } + } + + @RunWith(value = Parameterized.class) + public static class MockStrictnessToStrictnessTest { + + public Mock.Strictness strictness; + + public MockStrictnessToStrictnessTest(Mock.Strictness strictness) { + this.strictness = strictness; + } + + @Test + public void should_have_matching_enum_in_strictness_enum() { + assumeThat("Ignore NOT_SET", strictness, not(Mock.Strictness.TEST_LEVEL_DEFAULT)); + org.mockito.quality.Strictness.valueOf(strictness.name()); + } + + @Parameterized.Parameters(name = "{0}") + public static Mock.Strictness[] data() { + return Mock.Strictness.values(); + } + } +} diff --git a/src/test/java/org/mockitousage/strictness/StrictnessMockAnnotationTest.java b/src/test/java/org/mockitousage/strictness/StrictnessMockAnnotationTest.java index c035b3d708..7250555880 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessMockAnnotationTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessMockAnnotationTest.java @@ -7,6 +7,8 @@ import org.assertj.core.api.Assertions; import org.junit.Rule; import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.exceptions.misusing.PotentialStubbingProblem; import org.mockito.junit.MockitoJUnit; @@ -16,28 +18,56 @@ import static org.mockito.Mockito.when; +@RunWith(Enclosed.class) public class StrictnessMockAnnotationTest { - public @Rule MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); + public static class StrictStubsTest { + public @Rule MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); - @Mock(strictness = Strictness.LENIENT) - IMethods lenientMock; + @Mock(strictness = Mock.Strictness.LENIENT) + IMethods lenientMock; - @Mock IMethods regularMock; + @Mock IMethods regularMock; - @Test - public void mock_is_lenient() { - when(lenientMock.simpleMethod("1")).thenReturn("1"); + @Test + public void mock_is_lenient() { + when(lenientMock.simpleMethod("1")).thenReturn("1"); - // then lenient mock does not throw: - ProductionCode.simpleMethod(lenientMock, "3"); + // then lenient mock does not throw: + ProductionCode.simpleMethod(lenientMock, "3"); + } + + @Test + public void mock_is_strict() { + when(regularMock.simpleMethod("2")).thenReturn("2"); + + Assertions.assertThatThrownBy(() -> ProductionCode.simpleMethod(regularMock, "4")) + .isInstanceOf(PotentialStubbingProblem.class); + } } - @Test - public void mock_is_strict() { - when(regularMock.simpleMethod("2")).thenReturn("2"); + public static class LenientStubsTest { + public @Rule MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.LENIENT); + + @Mock IMethods lenientMock; + + @Mock(strictness = Mock.Strictness.STRICT_STUBS) + IMethods regularMock; + + @Test + public void mock_is_lenient() { + when(lenientMock.simpleMethod("1")).thenReturn("1"); + + // then lenient mock does not throw: + ProductionCode.simpleMethod(lenientMock, "3"); + } + + @Test + public void mock_is_strict() { + when(regularMock.simpleMethod("2")).thenReturn("2"); - Assertions.assertThatThrownBy(() -> ProductionCode.simpleMethod(regularMock, "4")) - .isInstanceOf(PotentialStubbingProblem.class); + Assertions.assertThatThrownBy(() -> ProductionCode.simpleMethod(regularMock, "4")) + .isInstanceOf(PotentialStubbingProblem.class); + } } } diff --git a/subprojects/junit-jupiter/src/test/java/org/mockitousage/ProductionCode.java b/subprojects/junit-jupiter/src/test/java/org/mockitousage/ProductionCode.java new file mode 100644 index 0000000000..f2953c7bc6 --- /dev/null +++ b/subprojects/junit-jupiter/src/test/java/org/mockitousage/ProductionCode.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitousage; + +import java.util.function.Predicate; + +public class ProductionCode { + + @SuppressWarnings("ReturnValueIgnored") + public static void simpleMethod(Predicate mock, String argument) { + mock.test(argument); + } +} diff --git a/subprojects/junit-jupiter/src/test/java/org/mockitousage/StrictnessTest.java b/subprojects/junit-jupiter/src/test/java/org/mockitousage/StrictnessTest.java index 31a5d6a649..1b63d66cc4 100644 --- a/subprojects/junit-jupiter/src/test/java/org/mockitousage/StrictnessTest.java +++ b/subprojects/junit-jupiter/src/test/java/org/mockitousage/StrictnessTest.java @@ -21,7 +21,9 @@ import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; +import java.util.Optional; import java.util.function.Function; +import java.util.function.Predicate; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; @@ -162,6 +164,29 @@ void inherits_strictness_from_base_class() { assertThat(result.getStatus()).isEqualTo(TestExecutionResult.Status.SUCCESSFUL); } + @ExtendWith(MockitoExtension.class) + @MockitoSettings(strictness = Strictness.LENIENT) + static class LenientMockitoSettings { + + @Mock + private Predicate rootMock; + + @Test + void should_not_throw_on_potential_stubbing_issue() { + Mockito.doReturn(true).when(rootMock).test("Foo"); + + ProductionCode.simpleMethod(rootMock, "Bar"); + } + } + + @Test + void use_strictness_from_settings_annotation() { + TestExecutionResult result = invokeTestClassAndRetrieveMethodResult(LenientMockitoSettings.class); + + assertThat(result.getThrowable()).isEqualTo(Optional.empty()); + assertThat(result.getStatus()).isEqualTo(TestExecutionResult.Status.SUCCESSFUL); + } + private TestExecutionResult invokeTestClassAndRetrieveMethodResult(Class clazz) { LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request() .selectors( From 3175e37725e0cf87b6b02591fe31e6da6fea9687 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Jun 2022 21:51:03 +0200 Subject: [PATCH 034/160] Bump biz.aQute.bnd.builder from 6.2.0 to 6.3.0 (#2665) Bumps biz.aQute.bnd.builder from 6.2.0 to 6.3.0. --- updated-dependencies: - dependency-name: biz.aQute.bnd.builder 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 7cc3dc209c..97da16410d 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ plugins { id 'com.diffplug.spotless' version '6.6.1' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' - id 'biz.aQute.bnd.builder' version '6.2.0' + id 'biz.aQute.bnd.builder' version '6.3.0' id 'ru.vyarus.animalsniffer' version '1.5.2' } From b8274e93e0eb53b1ffd82bd4a353752d93f8d0fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Jun 2022 21:51:14 +0200 Subject: [PATCH 035/160] Bump biz.aQute.bnd.gradle from 6.2.0 to 6.3.0 (#2666) Bumps [biz.aQute.bnd.gradle](https://github.com/bndtools/bnd) from 6.2.0 to 6.3.0. - [Release notes](https://github.com/bndtools/bnd/releases) - [Changelog](https://github.com/bndtools/bnd/blob/master/docs/ADDING_RELEASE_DOCS.md) - [Commits](https://github.com/bndtools/bnd/compare/6.2.0...6.3.0) --- updated-dependencies: - dependency-name: biz.aQute.bnd:biz.aQute.bnd.gradle 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 4fe2088485..874c86a9e8 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -30,7 +30,7 @@ libraries.objenesis = 'org.objenesis:objenesis:3.2' libraries.osgi = 'org.osgi:osgi.core:8.0.0' libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.17.200' -libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.2.0' +libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.3.0' libraries.groovy = 'org.codehaus.groovy:groovy:3.0.11' From 4ed991942560fcbd878e3a638550a0f6ddf3fc6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Jun 2022 23:16:39 +0200 Subject: [PATCH 036/160] Bump appcompat from 1.4.1 to 1.4.2 (#2663) Bumps appcompat from 1.4.1 to 1.4.2. --- updated-dependencies: - dependency-name: androidx.appcompat:appcompat 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 874c86a9e8..51cc429a97 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -44,7 +44,7 @@ libraries.kotlin = [ ] libraries.android = [ ktx: 'androidx.core:core-ktx:1.8.0', - compat: 'androidx.appcompat:appcompat:1.4.1', + compat: 'androidx.appcompat:appcompat:1.4.2', material: 'com.google.android.material:material:1.6.1', junit: 'androidx.test.ext:junit:1.1.3', espresso: 'androidx.test.espresso:espresso-core:3.4.0', From ce6c84ccf56ec79fe215709539c80ed51b42b907 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Mon, 6 Jun 2022 03:16:29 +0800 Subject: [PATCH 037/160] Fix typos (#2669) --- .../bytebuddy/InlineDelegateByteBuddyMockMaker.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java index 9da65605a5..bf6cf4b735 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java @@ -44,9 +44,9 @@ /** * Agent and subclass based mock maker. *

- * This mock maker which uses a combination of the Java instrumentation API and sub-classing rather than creating + * This mock maker uses a combination of the Java instrumentation API and sub-classing rather than creating * a new sub-class to create a mock. This way, it becomes possible to mock final types and methods. This mock - * maker must to be activated explicitly for supporting mocking final types and methods: + * maker must be activated explicitly for supporting mocking final types and methods: *

*

* This mock maker can be activated by creating the file /mockito-extensions/org.mockito.plugins.MockMaker @@ -62,7 +62,7 @@ * assert mock(Foo.class).getClass() == Foo.class; * *

- * unless any of the following conditions is met, in such case the mock maker fall backs to the + * unless any of the following conditions is met, in such case the mock maker falls back to * the creation of a subclass. *

*

    @@ -87,7 +87,7 @@ * include private types in a protected package. *
  • Mockito can no longer mock native methods. Inline mocks require byte code manipulation of a * method where native methods do not offer any byte code to manipulate.
  • - *
  • Mockito cannot longer strip synchronized modifiers from mocked instances.
  • + *
  • Mockito can no longer strip synchronized modifiers from mocked instances.
  • *
*

*

From 770d95a578c4aee1085cf926f5879838f5ddd493 Mon Sep 17 00:00:00 2001 From: "Nikita Koselev. Developer Advocate, Open Source Ally" <6383738+nikitakoselev@users.noreply.github.com> Date: Sun, 5 Jun 2022 20:25:17 +0100 Subject: [PATCH 038/160] Update mentions to stackoverflow (#2671) Description for [Stack Overflow] has been changed to [Stack Overflow / Questions tagged [mockito]] . Hyperlink for Stack Overflow was added. Closes #2670 --- .github/CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3e35d36df3..8609981c54 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -6,9 +6,9 @@ **If looking for support** -* Search / Ask a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/mockito) +* Search / Ask a question on [Stack Overflow / Questions tagged [mockito]](https://stackoverflow.com/questions/tagged/mockito) * Go to the [mockito mailing-list](https://groups.google.com/group/mockito) (moderated) -* Issues should always have a [Short, Self Contained, Correct (Compilable), Example](http://sscce.org) (same as any question on stackoverflow.com) +* Issues should always have a [Short, Self Contained, Correct (Compilable), Example](http://sscce.org) (same as any question on [Stack Overflow](https://stackoverflow.com)) # Contributing to Mockito From 073f2f1d9b95cf06799d2a3ad9968152d0f71aa1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jun 2022 21:05:58 +0200 Subject: [PATCH 039/160] Bump com.diffplug.spotless from 6.6.1 to 6.7.0 (#2672) Bumps com.diffplug.spotless from 6.6.1 to 6.7.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 97da16410d..eaaabb46a1 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.6.1' + id 'com.diffplug.spotless' version '6.7.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.3.0' From dff5e93b0d82f0ab93a99de3c60571f824a58a95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jun 2022 23:06:42 +0200 Subject: [PATCH 040/160] Bump biz.aQute.bnd.gradle from 6.3.0 to 6.3.1 (#2674) Bumps [biz.aQute.bnd.gradle](https://github.com/bndtools/bnd) from 6.3.0 to 6.3.1. - [Release notes](https://github.com/bndtools/bnd/releases) - [Changelog](https://github.com/bndtools/bnd/blob/master/docs/ADDING_RELEASE_DOCS.md) - [Commits](https://github.com/bndtools/bnd/compare/6.3.0...6.3.1) --- updated-dependencies: - dependency-name: biz.aQute.bnd:biz.aQute.bnd.gradle 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 51cc429a97..861a5d9b21 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -30,7 +30,7 @@ libraries.objenesis = 'org.objenesis:objenesis:3.2' libraries.osgi = 'org.osgi:osgi.core:8.0.0' libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.17.200' -libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.3.0' +libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.3.1' libraries.groovy = 'org.codehaus.groovy:groovy:3.0.11' From 7d8e5ca4faa4bbcfa7551c2099d7ac3b720faf75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jun 2022 23:06:56 +0200 Subject: [PATCH 041/160] Bump biz.aQute.bnd.builder from 6.3.0 to 6.3.1 (#2675) Bumps biz.aQute.bnd.builder from 6.3.0 to 6.3.1. --- updated-dependencies: - dependency-name: biz.aQute.bnd.builder 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 eaaabb46a1..d05b9ae404 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ plugins { id 'com.diffplug.spotless' version '6.7.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' - id 'biz.aQute.bnd.builder' version '6.3.0' + id 'biz.aQute.bnd.builder' version '6.3.1' id 'ru.vyarus.animalsniffer' version '1.5.2' } From c4cce85decc1639db41209696131d41e6d722d2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jun 2022 23:51:56 +0200 Subject: [PATCH 042/160] Bump kotlinVersion from 1.6.21 to 1.7.0 (#2677) Bumps `kotlinVersion` from 1.6.21 to 1.7.0. Updates `kotlin-gradle-plugin` from 1.6.21 to 1.7.0 - [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.6.21 to 1.7.0 - [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-minor - dependency-name: org.jetbrains.kotlin:kotlin-stdlib 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 +- gradle/dependencies.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d05b9ae404..b9713fcd4b 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.6.21" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.0" } } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 861a5d9b21..cfea56954f 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.6.21' +def kotlinVersion = '1.7.0' libraries.kotlin = [ version: kotlinVersion, From 5df5fbe32d9a2dae36c2fb2ec18bbd399a472de4 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Sat, 11 Jun 2022 21:10:11 +0800 Subject: [PATCH 043/160] Fix typo (#2681) --- src/main/java/org/mockito/junit/MockitoJUnitRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/junit/MockitoJUnitRunner.java b/src/main/java/org/mockito/junit/MockitoJUnitRunner.java index c59f9720c9..f7f4e1b928 100644 --- a/src/main/java/org/mockito/junit/MockitoJUnitRunner.java +++ b/src/main/java/org/mockito/junit/MockitoJUnitRunner.java @@ -113,7 +113,7 @@ public Silent(Class klass) throws InvocationTargetException { * Detects unused stubs and reports them as failures. Default behavior in Mockito 2.x. * To improve productivity and quality of tests please consider newer API, the {@link StrictStubs}. *

- * For more information on detecting unusued stubs, see {@link UnnecessaryStubbingException}. + * For more information on detecting unused stubs, see {@link UnnecessaryStubbingException}. * For more information on stubbing argument mismatch warnings see {@link MockitoHint}. * * @since 2.1.0 From ce4e64dc782a132e4f32faf56a2630187a703a00 Mon Sep 17 00:00:00 2001 From: Andy Coates <8012398+big-andy-coates@users.noreply.github.com> Date: Sat, 11 Jun 2022 20:10:00 +0100 Subject: [PATCH 044/160] Improve Varargs handling in AdditionalAnswers (#2664) Fixes issues around vararg handling for the following methods in `AdditionalAnswers`: * `returnsFirstArg` * `returnsSecondArg` * `returnsLastArg` * `returnsArgAt` * `answer` * `answerVoid` These methods were not correctly handling varargs. For example, ```java doAnswer(answerVoid( (VoidAnswer2) logger::info )).when(mock) .info(any(), (Object[]) any()); mock.info("Some message with {} {} {}", "three", "parameters", ""); ``` Would previously have resulted in a `ClassCastException` being thrown from the `mock.info` call. This was because the `answerVoid` method was not taking into account that the second parameter was a varargs parameter and was attempting to pass the second actual argument `"three"`, rather than the second _raw_ argument `["three", "parameters", ""]`. Fixes #2644 --- .../answers/AnswerFunctionalInterfaces.java | 75 +++- .../stubbing/answers/ReturnsArgumentAt.java | 50 ++- .../defaultanswers/ForwardsInvocations.java | 3 +- .../org/mockito/invocation/Invocation.java | 9 +- .../mockito/invocation/InvocationOnMock.java | 9 + src/test/java/org/mockitousage/IMethods.java | 20 + .../java/org/mockitousage/MethodsImpl.java | 42 ++ .../StubbingWithAdditionalAnswersTest.java | 374 ++++++++++++++++++ 8 files changed, 542 insertions(+), 40 deletions(-) diff --git a/src/main/java/org/mockito/internal/stubbing/answers/AnswerFunctionalInterfaces.java b/src/main/java/org/mockito/internal/stubbing/answers/AnswerFunctionalInterfaces.java index e4692f26d9..d9b3271d91 100644 --- a/src/main/java/org/mockito/internal/stubbing/answers/AnswerFunctionalInterfaces.java +++ b/src/main/java/org/mockito/internal/stubbing/answers/AnswerFunctionalInterfaces.java @@ -19,6 +19,8 @@ import org.mockito.stubbing.VoidAnswer5; import org.mockito.stubbing.VoidAnswer6; +import java.lang.reflect.Method; + /** * Functional interfaces to make it easy to implement answers in Java 8 * @@ -38,11 +40,11 @@ private AnswerFunctionalInterfaces() {} * @return a new answer object */ public static Answer toAnswer(final Answer1 answer) { + final Method answerMethod = findAnswerMethod(answer.getClass(), 1); return new Answer() { @Override - @SuppressWarnings("unchecked") public T answer(InvocationOnMock invocation) throws Throwable { - return answer.answer((A) invocation.getArgument(0)); + return answer.answer(lastParameter(invocation, answerMethod, 0)); } }; } @@ -54,11 +56,11 @@ public T answer(InvocationOnMock invocation) throws Throwable { * @return a new answer object */ public static Answer toAnswer(final VoidAnswer1 answer) { + final Method answerMethod = findAnswerMethod(answer.getClass(), 1); return new Answer() { @Override - @SuppressWarnings("unchecked") public Void answer(InvocationOnMock invocation) throws Throwable { - answer.answer((A) invocation.getArgument(0)); + answer.answer(lastParameter(invocation, answerMethod, 0)); return null; } }; @@ -73,11 +75,13 @@ public Void answer(InvocationOnMock invocation) throws Throwable { * @return a new answer object */ public static Answer toAnswer(final Answer2 answer) { + final Method answerMethod = findAnswerMethod(answer.getClass(), 2); return new Answer() { @Override @SuppressWarnings("unchecked") public T answer(InvocationOnMock invocation) throws Throwable { - return answer.answer((A) invocation.getArgument(0), (B) invocation.getArgument(1)); + return answer.answer( + (A) invocation.getArgument(0), lastParameter(invocation, answerMethod, 1)); } }; } @@ -90,11 +94,13 @@ public T answer(InvocationOnMock invocation) throws Throwable { * @return a new answer object */ public static Answer toAnswer(final VoidAnswer2 answer) { + final Method answerMethod = findAnswerMethod(answer.getClass(), 2); return new Answer() { @Override @SuppressWarnings("unchecked") public Void answer(InvocationOnMock invocation) throws Throwable { - answer.answer((A) invocation.getArgument(0), (B) invocation.getArgument(1)); + answer.answer( + (A) invocation.getArgument(0), lastParameter(invocation, answerMethod, 1)); return null; } }; @@ -110,6 +116,7 @@ public Void answer(InvocationOnMock invocation) throws Throwable { * @return a new answer object */ public static Answer toAnswer(final Answer3 answer) { + final Method answerMethod = findAnswerMethod(answer.getClass(), 3); return new Answer() { @Override @SuppressWarnings("unchecked") @@ -117,7 +124,7 @@ public T answer(InvocationOnMock invocation) throws Throwable { return answer.answer( (A) invocation.getArgument(0), (B) invocation.getArgument(1), - (C) invocation.getArgument(2)); + lastParameter(invocation, answerMethod, 2)); } }; } @@ -131,6 +138,7 @@ public T answer(InvocationOnMock invocation) throws Throwable { * @return a new answer object */ public static Answer toAnswer(final VoidAnswer3 answer) { + final Method answerMethod = findAnswerMethod(answer.getClass(), 3); return new Answer() { @Override @SuppressWarnings("unchecked") @@ -138,7 +146,7 @@ public Void answer(InvocationOnMock invocation) throws Throwable { answer.answer( (A) invocation.getArgument(0), (B) invocation.getArgument(1), - (C) invocation.getArgument(2)); + lastParameter(invocation, answerMethod, 2)); return null; } }; @@ -155,6 +163,7 @@ public Void answer(InvocationOnMock invocation) throws Throwable { * @return a new answer object */ public static Answer toAnswer(final Answer4 answer) { + final Method answerMethod = findAnswerMethod(answer.getClass(), 4); return new Answer() { @Override @SuppressWarnings("unchecked") @@ -163,7 +172,7 @@ public T answer(InvocationOnMock invocation) throws Throwable { (A) invocation.getArgument(0), (B) invocation.getArgument(1), (C) invocation.getArgument(2), - (D) invocation.getArgument(3)); + lastParameter(invocation, answerMethod, 3)); } }; } @@ -178,6 +187,7 @@ public T answer(InvocationOnMock invocation) throws Throwable { * @return a new answer object */ public static Answer toAnswer(final VoidAnswer4 answer) { + final Method answerMethod = findAnswerMethod(answer.getClass(), 4); return new Answer() { @Override @SuppressWarnings("unchecked") @@ -186,7 +196,7 @@ public Void answer(InvocationOnMock invocation) throws Throwable { (A) invocation.getArgument(0), (B) invocation.getArgument(1), (C) invocation.getArgument(2), - (D) invocation.getArgument(3)); + lastParameter(invocation, answerMethod, 3)); return null; } }; @@ -204,6 +214,7 @@ public Void answer(InvocationOnMock invocation) throws Throwable { * @return a new answer object */ public static Answer toAnswer(final Answer5 answer) { + final Method answerMethod = findAnswerMethod(answer.getClass(), 5); return new Answer() { @Override @SuppressWarnings("unchecked") @@ -213,7 +224,7 @@ public T answer(InvocationOnMock invocation) throws Throwable { (B) invocation.getArgument(1), (C) invocation.getArgument(2), (D) invocation.getArgument(3), - (E) invocation.getArgument(4)); + lastParameter(invocation, answerMethod, 4)); } }; } @@ -229,6 +240,7 @@ public T answer(InvocationOnMock invocation) throws Throwable { * @return a new answer object */ public static Answer toAnswer(final VoidAnswer5 answer) { + final Method answerMethod = findAnswerMethod(answer.getClass(), 5); return new Answer() { @Override @SuppressWarnings("unchecked") @@ -238,7 +250,7 @@ public Void answer(InvocationOnMock invocation) throws Throwable { (B) invocation.getArgument(1), (C) invocation.getArgument(2), (D) invocation.getArgument(3), - (E) invocation.getArgument(4)); + lastParameter(invocation, answerMethod, 4)); return null; } }; @@ -259,6 +271,7 @@ public Void answer(InvocationOnMock invocation) throws Throwable { */ public static Answer toAnswer( final Answer6 answer) { + final Method answerMethod = findAnswerMethod(answer.getClass(), 6); return new Answer() { @Override @SuppressWarnings("unchecked") @@ -269,7 +282,7 @@ public T answer(InvocationOnMock invocation) throws Throwable { (C) invocation.getArgument(2), (D) invocation.getArgument(3), (E) invocation.getArgument(4), - (F) invocation.getArgument(5)); + lastParameter(invocation, answerMethod, 5)); } }; } @@ -288,6 +301,7 @@ public T answer(InvocationOnMock invocation) throws Throwable { */ public static Answer toAnswer( final VoidAnswer6 answer) { + final Method answerMethod = findAnswerMethod(answer.getClass(), 6); return new Answer() { @Override @SuppressWarnings("unchecked") @@ -298,9 +312,42 @@ public Void answer(InvocationOnMock invocation) throws Throwable { (C) invocation.getArgument(2), (D) invocation.getArgument(3), (E) invocation.getArgument(4), - (F) invocation.getArgument(5)); + lastParameter(invocation, answerMethod, 5)); return null; } }; } + + private static Method findAnswerMethod(final Class type, final int numberOfParameters) { + for (final Method m : type.getDeclaredMethods()) { + if (!m.isBridge() + && m.getName().equals("answer") + && m.getParameterTypes().length == numberOfParameters) { + return m; + } + } + throw new IllegalStateException( + "Failed to find answer() method on the supplied class: " + + type.getName() + + ", with the supplied number of parameters: " + + numberOfParameters); + } + + @SuppressWarnings("unchecked") + private static A lastParameter( + InvocationOnMock invocation, Method answerMethod, int argumentIndex) { + final Method invocationMethod = invocation.getMethod(); + + if (invocationMethod.isVarArgs() + && invocationMethod.getParameterTypes().length == (argumentIndex + 1)) { + final Class invocationRawArgType = + invocationMethod.getParameterTypes()[argumentIndex]; + final Class answerRawArgType = answerMethod.getParameterTypes()[argumentIndex]; + if (answerRawArgType.isAssignableFrom(invocationRawArgType)) { + return (A) invocation.getRawArguments()[argumentIndex]; + } + } + + return invocation.getArgument(argumentIndex); + } } diff --git a/src/main/java/org/mockito/internal/stubbing/answers/ReturnsArgumentAt.java b/src/main/java/org/mockito/internal/stubbing/answers/ReturnsArgumentAt.java index cd661152c3..c3cabc1420 100644 --- a/src/main/java/org/mockito/internal/stubbing/answers/ReturnsArgumentAt.java +++ b/src/main/java/org/mockito/internal/stubbing/answers/ReturnsArgumentAt.java @@ -52,24 +52,24 @@ public ReturnsArgumentAt(int wantedArgumentPosition) { @Override public Object answer(InvocationOnMock invocation) throws Throwable { - int argumentPosition = inferWantedArgumentPosition(invocation); - validateIndexWithinInvocationRange(invocation, argumentPosition); - - if (wantedArgIndexIsVarargAndSameTypeAsReturnType( - invocation.getMethod(), argumentPosition)) { + if (wantedArgIndexIsVarargAndSameTypeAsReturnType(invocation)) { // answer raw vararg array argument - return ((Invocation) invocation).getRawArguments()[argumentPosition]; + return invocation.getRawArguments()[invocation.getRawArguments().length - 1]; } + int argumentPosition = inferWantedArgumentPosition(invocation); + validateIndexWithinInvocationRange(invocation, argumentPosition); + // answer expanded argument at wanted position return invocation.getArgument(argumentPosition); } @Override - public void validateFor(InvocationOnMock invocation) { + public void validateFor(InvocationOnMock invocationOnMock) { + Invocation invocation = (Invocation) invocationOnMock; int argumentPosition = inferWantedArgumentPosition(invocation); - validateIndexWithinInvocationRange(invocation, argumentPosition); - validateArgumentTypeCompatibility((Invocation) invocation, argumentPosition); + validateIndexWithinTheoreticalInvocationRange(invocation, argumentPosition); + validateArgumentTypeCompatibility(invocation, argumentPosition); } private int inferWantedArgumentPosition(InvocationOnMock invocation) { @@ -80,9 +80,26 @@ private int inferWantedArgumentPosition(InvocationOnMock invocation) { return wantedArgumentPosition; } + private int inferWantedRawArgumentPosition(InvocationOnMock invocation) { + if (wantedArgumentPosition == LAST_ARGUMENT) { + return invocation.getRawArguments().length - 1; + } + + return wantedArgumentPosition; + } + private void validateIndexWithinInvocationRange( InvocationOnMock invocation, int argumentPosition) { - if (!wantedArgumentPositionIsValidForInvocation(invocation, argumentPosition)) { + + if (invocation.getArguments().length <= argumentPosition) { + throw invalidArgumentPositionRangeAtInvocationTime( + invocation, wantedArgumentPosition == LAST_ARGUMENT, wantedArgumentPosition); + } + } + + private void validateIndexWithinTheoreticalInvocationRange( + InvocationOnMock invocation, int argumentPosition) { + if (!wantedArgumentPositionIsValidForTheoreticalInvocation(invocation, argumentPosition)) { throw invalidArgumentPositionRangeAtInvocationTime( invocation, wantedArgumentPosition == LAST_ARGUMENT, wantedArgumentPosition); } @@ -102,15 +119,16 @@ private void validateArgumentTypeCompatibility(Invocation invocation, int argume } } - private boolean wantedArgIndexIsVarargAndSameTypeAsReturnType( - Method method, int argumentPosition) { + private boolean wantedArgIndexIsVarargAndSameTypeAsReturnType(InvocationOnMock invocation) { + int rawArgumentPosition = inferWantedRawArgumentPosition(invocation); + Method method = invocation.getMethod(); Class[] parameterTypes = method.getParameterTypes(); return method.isVarArgs() - && argumentPosition == /* vararg index */ parameterTypes.length - 1 - && method.getReturnType().isAssignableFrom(parameterTypes[argumentPosition]); + && rawArgumentPosition == /* vararg index */ parameterTypes.length - 1 + && method.getReturnType().isAssignableFrom(parameterTypes[rawArgumentPosition]); } - private boolean wantedArgumentPositionIsValidForInvocation( + private boolean wantedArgumentPositionIsValidForTheoreticalInvocation( InvocationOnMock invocation, int argumentPosition) { if (argumentPosition < 0) { return false; @@ -145,7 +163,7 @@ private Class inferArgumentType(Invocation invocation, int argumentIndex) { return parameterTypes[argumentIndex]; } // if wanted argument is vararg - if (wantedArgIndexIsVarargAndSameTypeAsReturnType(invocation.getMethod(), argumentIndex)) { + if (wantedArgIndexIsVarargAndSameTypeAsReturnType(invocation)) { // return the vararg array if return type is compatible // because the user probably want to return the array itself if the return type is // compatible diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ForwardsInvocations.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ForwardsInvocations.java index 5f596b7da8..5d12ca03e6 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ForwardsInvocations.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ForwardsInvocations.java @@ -12,7 +12,6 @@ import java.lang.reflect.Method; import org.mockito.internal.configuration.plugins.Plugins; -import org.mockito.invocation.Invocation; import org.mockito.invocation.InvocationOnMock; import org.mockito.plugins.MemberAccessor; import org.mockito.stubbing.Answer; @@ -45,7 +44,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } MemberAccessor accessor = Plugins.getMemberAccessor(); - Object[] rawArguments = ((Invocation) invocation).getRawArguments(); + Object[] rawArguments = invocation.getRawArguments(); return accessor.invoke(delegateMethod, delegatedObject, rawArguments); } catch (NoSuchMethodException e) { throw delegatedMethodDoesNotExistOnDelegate( diff --git a/src/main/java/org/mockito/invocation/Invocation.java b/src/main/java/org/mockito/invocation/Invocation.java index 7aeecce1c3..4742b34150 100644 --- a/src/main/java/org/mockito/invocation/Invocation.java +++ b/src/main/java/org/mockito/invocation/Invocation.java @@ -41,14 +41,6 @@ public interface Invocation extends InvocationOnMock, DescribedInvocation { @Override Location getLocation(); - /** - * Returns unprocessed arguments whereas {@link #getArguments()} returns - * arguments already processed (e.g. varargs expended, etc.). - * - * @return unprocessed arguments, exactly as provided to this invocation. - */ - Object[] getRawArguments(); - /** * Wraps each argument using {@link org.mockito.ArgumentMatchers#eq(Object)} or * {@link org.mockito.AdditionalMatchers#aryEq(Object[])} @@ -64,6 +56,7 @@ public interface Invocation extends InvocationOnMock, DescribedInvocation { * arguments already processed (e.g. varargs expended, etc.). * * @return unprocessed arguments, exactly as provided to this invocation. + * @since 4.7.0 */ Class getRawReturnType(); diff --git a/src/main/java/org/mockito/invocation/InvocationOnMock.java b/src/main/java/org/mockito/invocation/InvocationOnMock.java index 269926ea6b..126c2b5999 100644 --- a/src/main/java/org/mockito/invocation/InvocationOnMock.java +++ b/src/main/java/org/mockito/invocation/InvocationOnMock.java @@ -32,6 +32,15 @@ public interface InvocationOnMock extends Serializable { */ Method getMethod(); + /** + * Returns unprocessed arguments whereas {@link #getArguments()} returns + * arguments already processed (e.g. varargs expended, etc.). + * + * @return unprocessed arguments, exactly as provided to this invocation. + * @since 4.7.0 + */ + Object[] getRawArguments(); + /** * Returns arguments passed to the method. * diff --git a/src/test/java/org/mockitousage/IMethods.java b/src/test/java/org/mockitousage/IMethods.java index 12897422da..06492f09cb 100644 --- a/src/test/java/org/mockitousage/IMethods.java +++ b/src/test/java/org/mockitousage/IMethods.java @@ -133,8 +133,26 @@ String simpleMethod( String threeArgumentMethodWithStrings(int valueOne, String valueTwo, String valueThree); + String threeArgumentVarArgsMethod(int valueOne, String valueTwo, String... valueThree); + String fourArgumentMethod(int valueOne, String valueTwo, String valueThree, boolean[] array); + String fourArgumentVarArgsMethod( + int valueOne, String valueTwo, int valueThree, String... valueFour); + + String fiveArgumentVarArgsMethod( + int valueOne, String valueTwo, int valueThree, String valueFour, String... valueFive); + + String sixArgumentVarArgsMethod( + int valueOne, + String valueTwo, + int valueThree, + String valueFour, + String valueFive, + String... valueSix); + + int arrayVarargsMethod(String[]... arrayVarArgs); + void twoArgumentMethod(int one, int two); void arrayMethod(String[] strings); @@ -235,6 +253,8 @@ String simpleMethod( Integer toIntWrapper(int i); + Integer toIntWrapperVarArgs(int i, Object... varargs); + String forObject(Object object); String genericToString(T arg); diff --git a/src/test/java/org/mockitousage/MethodsImpl.java b/src/test/java/org/mockitousage/MethodsImpl.java index bb69656712..e2d53ba7ba 100644 --- a/src/test/java/org/mockitousage/MethodsImpl.java +++ b/src/test/java/org/mockitousage/MethodsImpl.java @@ -254,11 +254,48 @@ public String threeArgumentMethodWithStrings(int valueOne, String valueTwo, Stri return null; } + public String threeArgumentVarArgsMethod( + final int valueOne, final String valueTwo, final String... valueThree) { + return null; + } + public String fourArgumentMethod( int valueOne, String valueTwo, String valueThree, boolean[] array) { return null; } + public String fourArgumentVarArgsMethod( + final int valueOne, + final String valueTwo, + final int valueThree, + final String... valueFour) { + return null; + } + + public String fiveArgumentVarArgsMethod( + final int valueOne, + final String valueTwo, + final int valueThree, + final String valueFour, + final String... valueFive) { + return null; + } + + public String sixArgumentVarArgsMethod( + final int valueOne, + final String valueTwo, + final int valueThree, + final String valueFour, + final String valueFive, + final String... valueSix) { + return null; + } + + @Override + public int arrayVarargsMethod(final String[]... arrayVarArgs) { + return 0; + } + public void twoArgumentMethod(int one, int two) {} public void arrayMethod(String[] strings) {} @@ -435,6 +472,11 @@ public Integer toIntWrapper(int i) { return null; } + @Override + public Integer toIntWrapperVarArgs(final int i, final Object... varargs) { + return null; + } + public String forObject(Object object) { return null; } diff --git a/src/test/java/org/mockitousage/stubbing/StubbingWithAdditionalAnswersTest.java b/src/test/java/org/mockitousage/stubbing/StubbingWithAdditionalAnswersTest.java index 7dfed447a9..d037cfc0a0 100644 --- a/src/test/java/org/mockitousage/stubbing/StubbingWithAdditionalAnswersTest.java +++ b/src/test/java/org/mockitousage/stubbing/StubbingWithAdditionalAnswersTest.java @@ -5,6 +5,7 @@ package org.mockitousage.stubbing; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.within; import static org.mockito.AdditionalAnswers.answer; import static org.mockito.AdditionalAnswers.answerVoid; @@ -22,11 +23,13 @@ import static org.mockito.BDDMockito.times; import static org.mockito.BDDMockito.verify; +import java.util.Arrays; import java.util.Date; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.exceptions.base.MockitoException; import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer1; import org.mockito.stubbing.Answer2; @@ -42,6 +45,7 @@ import org.mockito.stubbing.VoidAnswer6; import org.mockitousage.IMethods; +@SuppressWarnings({"Convert2Lambda", "Anonymous2MethodRef", "RedundantThrows"}) @RunWith(MockitoJUnitRunner.class) public class StubbingWithAdditionalAnswersTest { @@ -52,10 +56,42 @@ public void can_return_arguments_of_invocation() throws Exception { given(iMethods.objectArgMethod(any())).will(returnsFirstArg()); given(iMethods.threeArgumentMethod(eq(0), any(), anyString())).will(returnsSecondArg()); given(iMethods.threeArgumentMethod(eq(1), any(), anyString())).will(returnsLastArg()); + given(iMethods.mixedVarargsReturningString(eq(1), any())).will(returnsArgAt(2)); assertThat(iMethods.objectArgMethod("first")).isEqualTo("first"); assertThat(iMethods.threeArgumentMethod(0, "second", "whatever")).isEqualTo("second"); assertThat(iMethods.threeArgumentMethod(1, "whatever", "last")).isEqualTo("last"); + assertThat(iMethods.mixedVarargsReturningString(1, "a", "b")).isEqualTo("b"); + } + + @Test + public void can_return_var_arguments_of_invocation() throws Exception { + given(iMethods.mixedVarargsReturningStringArray(eq(1), any())).will(returnsLastArg()); + given(iMethods.mixedVarargsReturningObjectArray(eq(1), any())).will(returnsArgAt(1)); + + assertThat(iMethods.mixedVarargsReturningStringArray(1, "the", "var", "args")) + .containsExactlyInAnyOrder("the", "var", "args"); + assertThat(iMethods.mixedVarargsReturningObjectArray(1, "the", "var", "args")) + .containsExactlyInAnyOrder("the", "var", "args"); + } + + @Test + public void returns_arg_at_throws_on_out_of_range_var_args() throws Exception { + given(iMethods.mixedVarargsReturningString(eq(1), any())).will(returnsArgAt(3)); + + assertThatThrownBy(() -> iMethods.mixedVarargsReturningString(1, "a", "b")) + .isInstanceOf(MockitoException.class) + .hasMessageContaining("Invalid argument index"); + } + + @Test + public void returns_arg_at_throws_on_out_of_range_array_var_args() throws Exception { + assertThatThrownBy( + () -> + given(iMethods.mixedVarargsReturningStringArray(eq(1), any())) + .will(returnsArgAt(3))) + .isInstanceOf(MockitoException.class) + .hasMessageContaining("The argument of type 'String' cannot be returned"); } @Test @@ -85,9 +121,11 @@ public void can_return_expanded_arguments_of_invocation() throws Exception { public void can_return_primitives_or_wrappers() throws Exception { given(iMethods.toIntPrimitive(anyInt())).will(returnsFirstArg()); given(iMethods.toIntWrapper(anyInt())).will(returnsFirstArg()); + given(iMethods.toIntWrapperVarArgs(anyInt(), any())).will(returnsFirstArg()); assertThat(iMethods.toIntPrimitive(1)).isEqualTo(1); assertThat(iMethods.toIntWrapper(1)).isEqualTo(1); + assertThat(iMethods.toIntWrapperVarArgs(1, 10)).isEqualTo(1); } @Test @@ -347,4 +385,340 @@ public void answer( // expect the answer to write correctly to "target" verify(target, times(1)).simpleMethod("hello", 1, 2, 3, 4, 5); } + + @Test + public void can_return_based_on_strongly_types_one_parameter_var_args_function() + throws Exception { + given(iMethods.varargs(any())) + .will( + answer( + new Answer1() { + public Integer answer(String[] strings) { + return strings.length; + } + })); + + assertThat(iMethods.varargs("some", "args")).isEqualTo(2); + } + + @Test + public void will_execute_a_void_based_on_strongly_typed_one_parameter_var_args_function() + throws Exception { + final IMethods target = mock(IMethods.class); + + given(iMethods.varargs(any())) + .will( + answerVoid( + new VoidAnswer1() { + public void answer(String[] s) { + target.varargs(s); + } + })); + + // invoke on iMethods + iMethods.varargs("some", "args"); + + // expect the answer to write correctly to "target" + verify(target, times(1)).varargs("some", "args"); + } + + @Test + public void can_return_based_on_strongly_typed_two_parameter_var_args_function() + throws Exception { + given(iMethods.mixedVarargsReturningString(any(), any())) + .will( + answer( + new Answer2() { + public String answer(Object o, String[] s) { + return String.join("-", s); + } + })); + + assertThat(iMethods.mixedVarargsReturningString(1, "var", "args")).isEqualTo("var-args"); + } + + @Test + public void will_execute_a_void_based_on_strongly_typed_two_parameter_var_args_function() + throws Exception { + final IMethods target = mock(IMethods.class); + + given(iMethods.mixedVarargsReturningString(any(), any())) + .will( + answerVoid( + new VoidAnswer2() { + public void answer(Object o, String[] s) { + target.mixedVarargsReturningString(o, s); + } + })); + + // invoke on iMethods + iMethods.mixedVarargsReturningString(1, "var", "args"); + + // expect the answer to write correctly to "target" + verify(target).mixedVarargsReturningString(1, "var", "args"); + } + + @Test + public void can_return_based_on_strongly_typed_three_parameter_var_args_function() + throws Exception { + final IMethods target = mock(IMethods.class); + + given(iMethods.threeArgumentVarArgsMethod(anyInt(), any(), any())) + .will( + answer( + new Answer3() { + public String answer(Integer i, String s1, String[] s2) { + target.threeArgumentVarArgsMethod(i, s1, s2); + return String.join("-", s2); + } + })); + + // invoke on iMethods + assertThat(iMethods.threeArgumentVarArgsMethod(1, "string1", "var", "args")) + .isEqualTo("var-args"); + + // expect the answer to write correctly to "target" + verify(target).threeArgumentVarArgsMethod(1, "string1", "var", "args"); + } + + @Test + public void will_execute_a_void_based_on_strongly_typed_three_parameter_var_args_function() + throws Exception { + final IMethods target = mock(IMethods.class); + + given(iMethods.threeArgumentVarArgsMethod(anyInt(), any(), any())) + .will( + answerVoid( + new VoidAnswer3() { + public void answer(Integer i, String s1, String[] s2) { + target.threeArgumentVarArgsMethod(i, s1, s2); + } + })); + + // invoke on iMethods + iMethods.threeArgumentVarArgsMethod(1, "string1", "var", "args"); + + // expect the answer to write correctly to "target" + verify(target, times(1)).threeArgumentVarArgsMethod(1, "string1", "var", "args"); + } + + @Test + public void can_return_based_on_strongly_typed_four_parameter_var_args_function() + throws Exception { + final IMethods target = mock(IMethods.class); + given(iMethods.fourArgumentVarArgsMethod(anyInt(), any(), anyInt(), any())) + .will( + answer( + new Answer4() { + public String answer( + Integer i1, String s2, Integer i3, String[] s4) { + target.fourArgumentVarArgsMethod(i1, s2, i3, s4); + return String.join("-", s4); + } + })); + + // invoke on iMethods + String[] varargs = {"var", "args"}; + assertThat(iMethods.fourArgumentVarArgsMethod(1, "string1", 3, varargs)) + .isEqualTo("var-args"); + + // expect the answer to write correctly to "target" + verify(target, times(1)).fourArgumentVarArgsMethod(1, "string1", 3, varargs); + } + + @Test + public void will_execute_a_void_based_on_strongly_typed_four_parameter_var_args_function() + throws Exception { + final IMethods target = mock(IMethods.class); + + given(iMethods.fourArgumentVarArgsMethod(anyInt(), any(), anyInt(), any())) + .will( + answerVoid( + new VoidAnswer4() { + public void answer( + Integer i, String s2, Integer i3, String[] s4) { + target.fourArgumentVarArgsMethod(i, s2, i3, s4); + } + })); + + // invoke on iMethods + iMethods.fourArgumentVarArgsMethod(1, "string1", 3, "var", "args"); + + // expect the answer to write correctly to "target" + verify(target, times(1)).fourArgumentVarArgsMethod(1, "string1", 3, "var", "args"); + } + + @Test + public void can_return_based_on_strongly_typed_five_parameter_var_args_function() + throws Exception { + final IMethods target = mock(IMethods.class); + given(iMethods.fiveArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(), any())) + .will( + answer( + new Answer5() { + public String answer( + Integer i1, + String s2, + Integer i3, + String s4, + String[] s5) { + target.fiveArgumentVarArgsMethod(i1, s2, i3, s4, s5); + return String.join("-", s5); + } + })); + + // invoke on iMethods + assertThat(iMethods.fiveArgumentVarArgsMethod(1, "two", 3, "four", "var", "args")) + .isEqualTo("var-args"); + + // expect the answer to write correctly to "target" + verify(target).fiveArgumentVarArgsMethod(1, "two", 3, "four", "var", "args"); + } + + @Test + public void will_execute_a_void_based_on_strongly_typed_five_parameter_var_args_function() + throws Exception { + final IMethods target = mock(IMethods.class); + + given(iMethods.fiveArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(), any())) + .will( + answerVoid( + new VoidAnswer5() { + public void answer( + Integer i1, + String s2, + Integer i3, + String s4, + String[] s5) { + target.fiveArgumentVarArgsMethod(i1, s2, i3, s4, s5); + } + })); + + // invoke on iMethods + iMethods.fiveArgumentVarArgsMethod(1, "two", 3, "four", "var", "args"); + + // expect the answer to write correctly to "target" + verify(target).fiveArgumentVarArgsMethod(1, "two", 3, "four", "var", "args"); + } + + @Test + public void can_return_based_on_strongly_typed_six_parameter_var_args_function() + throws Exception { + final IMethods target = mock(IMethods.class); + given(iMethods.sixArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(), any(), any())) + .will( + answer( + new Answer6< + String, + Integer, + String, + Integer, + String, + String, + String[]>() { + public String answer( + Integer i1, + String s2, + Integer i3, + String s4, + String s5, + String[] s6) { + target.sixArgumentVarArgsMethod(i1, s2, i3, s4, s5, s6); + return "answered"; + } + })); + + // invoke on iMethods + assertThat(iMethods.sixArgumentVarArgsMethod(1, "two", 3, "four", "five", "var", "args")) + .isEqualTo("answered"); + + // expect the answer to write correctly to "target" + verify(target, times(1)) + .sixArgumentVarArgsMethod(1, "two", 3, "four", "five", "var", "args"); + } + + @Test + public void will_execute_a_void_returning_strongly_typed_six_parameter_var_args_function() + throws Exception { + final IMethods target = mock(IMethods.class); + given(iMethods.sixArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(), any(), any())) + .will( + answerVoid( + new VoidAnswer6< + Integer, String, Integer, String, String, String[]>() { + public void answer( + Integer i1, + String s2, + Integer i3, + String s4, + String s5, + String[] s6) { + target.sixArgumentVarArgsMethod(i1, s2, i3, s4, s5, s6); + } + })); + + // invoke on iMethods + iMethods.sixArgumentVarArgsMethod(1, "two", 3, "four", "five", "var", "args"); + + // expect the answer to write correctly to "target" + verify(target, times(1)) + .sixArgumentVarArgsMethod(1, "two", 3, "four", "five", "var", "args"); + } + + @Test + public void can_accept_array_supertype_for_strongly_typed_var_args_function() throws Exception { + given(iMethods.varargs(any())) + .will( + answer( + new Answer1() { + public Integer answer(Object[] s) { + return s.length; + } + })); + + assertThat(iMethods.varargs("var", "args")).isEqualTo(2); + } + + @Test + public void can_accept_non_vararg_answer_on_var_args_function() throws Exception { + given(iMethods.varargs(any())) + .will( + answer( + new Answer2() { + public Integer answer(String s1, String s2) { + return s1.length() + s2.length(); + } + })); + + assertThat(iMethods.varargs("var", "args")).isEqualTo(7); + } + + @Test + public void should_work_with_var_args_with_no_elements() throws Exception { + given(iMethods.varargs(any())) + .will( + answer( + new Answer1() { + public Integer answer(String[] s) { + return s.length; + } + })); + + assertThat(iMethods.varargs()).isEqualTo(0); + } + + @Test + public void should_work_with_array_var_args() throws Exception { + given(iMethods.arrayVarargsMethod(any())) + .will( + answer( + new Answer1() { + public Integer answer(String[][] s) { + return Arrays.stream(s).mapToInt(e -> e.length).sum(); + } + })); + + String[][] varArgs = {{}, {""}, {"", ""}}; + assertThat(iMethods.arrayVarargsMethod(varArgs)).isEqualTo(3); + } } From 02615ffb8e1977296ef66e445a09d7e6611898ea Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Sat, 11 Jun 2022 23:02:35 +0200 Subject: [PATCH 045/160] Avoids starting mocks "half-way" if a superclass constructor is mocked but an unmocked subclass is initiated. --- .../InlineDelegateByteBuddyMockMaker.java | 6 ++ .../creation/bytebuddy/StackTraceChecker.java | 36 ++++++++ .../bytebuddy/StackWalkerChecker.java | 91 +++++++++++++++++++ .../mockitoinline/SubconstructorMockTest.java | 47 ++++++++++ 4 files changed, 180 insertions(+) create mode 100644 src/main/java/org/mockito/internal/creation/bytebuddy/StackTraceChecker.java create mode 100644 src/main/java/org/mockito/internal/creation/bytebuddy/StackWalkerChecker.java create mode 100644 subprojects/inline/src/test/java/org/mockitoinline/SubconstructorMockTest.java diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java index bf6cf4b735..6fcb6a500e 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java @@ -251,6 +251,7 @@ class InlineDelegateByteBuddyMockMaker ThreadLocal> currentConstruction = new ThreadLocal<>(); ThreadLocal isSuspended = ThreadLocal.withInitial(() -> false); + Predicate> isCallFromSubclassConstructor = StackWalkerChecker.orFallback(); Predicate> isMockConstruction = type -> { if (isSuspended.get()) { @@ -260,6 +261,11 @@ class InlineDelegateByteBuddyMockMaker } Map, ?> interceptors = mockedConstruction.get(); if (interceptors != null && interceptors.containsKey(type)) { + // We only initiate a construction mock, if the call originates from an + // un-mocked (as suppression is not enabled) subclass constructor. + if (isCallFromSubclassConstructor.test(type)) { + return false; + } currentConstruction.set(type); return true; } else { diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/StackTraceChecker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/StackTraceChecker.java new file mode 100644 index 0000000000..18f92ba690 --- /dev/null +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/StackTraceChecker.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.creation.bytebuddy; + +import java.util.function.Predicate; + +class StackTraceChecker implements Predicate> { + + @Override + public boolean test(Class type) { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + for (int index = 1; index < stackTrace.length - 1; index++) { + if (!stackTrace[index].getClassName().startsWith("org.mockito.internal.")) { + if (stackTrace[index + 1].getMethodName().startsWith("")) { + try { + if (!stackTrace[index + 1].getClassName().equals(type.getName()) + && type.isAssignableFrom( + Class.forName( + stackTrace[index + 1].getClassName(), + false, + type.getClassLoader()))) { + return true; + } else { + break; + } + } catch (ClassNotFoundException ignored) { + break; + } + } + } + } + return false; + } +} diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/StackWalkerChecker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/StackWalkerChecker.java new file mode 100644 index 0000000000..c16439dbcb --- /dev/null +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/StackWalkerChecker.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.creation.bytebuddy; + +import java.lang.reflect.Method; +import java.util.Iterator; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + +class StackWalkerChecker implements Predicate> { + + private final Method stackWalkerGetInstance; + private final Method stackWalkerWalk; + private final Method stackWalkerStackFrameGetDeclaringClass; + private final Enum stackWalkerOptionRetainClassReference; + + StackWalkerChecker() throws Exception { + Class stackWalker = Class.forName("java.lang.StackWalker"); + @SuppressWarnings({"unchecked", "rawtypes"}) + Class stackWalkerOption = + (Class>) Class.forName("java.lang.StackWalker$Option"); + stackWalkerGetInstance = stackWalker.getMethod("getInstance", stackWalkerOption); + stackWalkerWalk = stackWalker.getMethod("walk", Function.class); + Class stackWalkerStackFrame = Class.forName("java.lang.StackWalker$StackFrame"); + stackWalkerStackFrameGetDeclaringClass = + stackWalkerStackFrame.getMethod("getDeclaringClass"); + @SuppressWarnings("unchecked") + Enum stackWalkerOptionRetainClassReference = + Enum.valueOf(stackWalkerOption, "RETAIN_CLASS_REFERENCE"); + this.stackWalkerOptionRetainClassReference = stackWalkerOptionRetainClassReference; + } + + static Predicate> orFallback() { + try { + return new StackWalkerChecker(); + } catch (Exception e) { + return new StackTraceChecker(); + } + } + + @Override + public boolean test(Class type) { + try { + Object walker = + stackWalkerGetInstance.invoke(null, stackWalkerOptionRetainClassReference); + return (Boolean) + stackWalkerWalk.invoke( + walker, + (Function) + stream -> { + Iterator iterator = ((Stream) stream).iterator(); + while (iterator.hasNext()) { + try { + Object frame = iterator.next(); + if (((Class) + stackWalkerStackFrameGetDeclaringClass + .invoke(frame)) + .getName() + .startsWith("org.mockito.internal.")) { + continue; + } + if (iterator.hasNext()) { + Object next = iterator.next(); + Class declaringClass = + (Class) + stackWalkerStackFrameGetDeclaringClass + .invoke(next); + if (type != declaringClass + && type.isAssignableFrom( + declaringClass)) { + return true; + } else { + break; + } + } else { + break; + } + } catch (Exception ignored) { + return false; + } + } + return false; + }); + } catch (Exception ignored) { + return false; + } + } +} diff --git a/subprojects/inline/src/test/java/org/mockitoinline/SubconstructorMockTest.java b/subprojects/inline/src/test/java/org/mockitoinline/SubconstructorMockTest.java new file mode 100644 index 0000000000..0dd65cb6f7 --- /dev/null +++ b/subprojects/inline/src/test/java/org/mockitoinline/SubconstructorMockTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitoinline; + +import org.junit.Test; +import org.mockito.MockedConstruction; +import org.mockito.Mockito; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class SubconstructorMockTest { + + @Test + public void does_not_mock_subclass_constructor_for_superclass_mock() { + try (MockedConstruction mocked = Mockito.mockConstruction(SubClass.class)) { } + try (MockedConstruction mocked = Mockito.mockConstruction(SuperClass.class)) { + SubClass value = new SubClass(); + assertTrue(value.sup()); + assertTrue(value.sub()); + } + } + + @Test + public void does_mock_superclass_constructor_for_subclass_mock() { + try (MockedConstruction mocked = Mockito.mockConstruction(SuperClass.class)) { } + try (MockedConstruction mocked = Mockito.mockConstruction(SubClass.class)) { + SubClass value = new SubClass(); + assertFalse(value.sup()); + assertFalse(value.sub()); + } + } + + public static class SuperClass { + public boolean sup() { + return true; + } + } + + public static class SubClass extends SuperClass { + public boolean sub() { + return true; + } + } +} From ec278300c6d66e65ca58998f323502657bca6703 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jun 2022 20:30:29 +0200 Subject: [PATCH 046/160] Bump com.diffplug.spotless from 6.7.0 to 6.7.2 (#2684) Bumps com.diffplug.spotless from 6.7.0 to 6.7.2. --- updated-dependencies: - dependency-name: com.diffplug.spotless 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 b9713fcd4b..28f9f07dad 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.7.0' + id 'com.diffplug.spotless' version '6.7.2' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.3.1' From 14a1bb42babcf1519d45d7c6b3ba5b0627d5cafd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jun 2022 21:59:52 +0200 Subject: [PATCH 047/160] Bump org.eclipse.osgi from 3.17.200 to 3.18.0 (#2688) Bumps [org.eclipse.osgi](https://github.com/eclipse-equinox/equinox.binaries) from 3.17.200 to 3.18.0. - [Release notes](https://github.com/eclipse-equinox/equinox.binaries/releases) - [Commits](https://github.com/eclipse-equinox/equinox.binaries/commits) --- updated-dependencies: - dependency-name: org.eclipse.platform:org.eclipse.osgi 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 cfea56954f..756997364e 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -29,7 +29,7 @@ libraries.autoservice = "com.google.auto.service:auto-service:1.0.1" libraries.objenesis = 'org.objenesis:objenesis:3.2' libraries.osgi = 'org.osgi:osgi.core:8.0.0' -libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.17.200' +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' From 512ee3949484e4765038a0410cd7a7f1b73cc655 Mon Sep 17 00:00:00 2001 From: Marius Lichtblau Date: Tue, 14 Jun 2022 23:24:12 +0200 Subject: [PATCH 048/160] Fix RETURNS_SELF for methods with generic return type (#2687) Fixes #2686 --- .../stubbing/defaultanswers/TriesToReturnSelf.java | 2 +- .../stubbing/StubbingReturnsSelfTest.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/TriesToReturnSelf.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/TriesToReturnSelf.java index 58a64d722e..f5643c565b 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/TriesToReturnSelf.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/TriesToReturnSelf.java @@ -20,7 +20,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { Object mock = invocation.getMock(); Class mockType = MockUtil.getMockHandler(mock).getMockSettings().getTypeToMock(); - if (methodReturnType.isAssignableFrom(mockType)) { + if (methodReturnType.isAssignableFrom(mockType) && methodReturnType != Object.class) { return invocation.getMock(); } diff --git a/src/test/java/org/mockitousage/stubbing/StubbingReturnsSelfTest.java b/src/test/java/org/mockitousage/stubbing/StubbingReturnsSelfTest.java index 4e75dfb5c6..28b9cea895 100644 --- a/src/test/java/org/mockitousage/stubbing/StubbingReturnsSelfTest.java +++ b/src/test/java/org/mockitousage/stubbing/StubbingReturnsSelfTest.java @@ -73,6 +73,13 @@ public void should_not_fail_when_calling_primitive_returning_method() { assertThat(builder.returnInt()).isEqualTo(0); } + @Test + public void should_not_fail_when_calling_method_with_generic_return_type() { + Builder builder = mock(Builder.class, RETURNS_SELF); + + assertThat(builder.returnGeneric("Generic Result")).isEqualTo(null); + } + @Test public void use_full_builder_with_terminating_method() { HttpBuilder builder = mock(HttpBuilder.class, RETURNS_SELF); @@ -99,6 +106,10 @@ public void returnNothing() {} public int returnInt() { return 1; } + + public T returnGeneric(T result) { + return result; + } } private static class BuilderSubClass extends Builder { From 807b121cc9b677e22a3497509ac594525f0bc6a2 Mon Sep 17 00:00:00 2001 From: fishautumn Date: Sat, 18 Jun 2022 17:41:01 +0800 Subject: [PATCH 049/160] Fix nested static mock stubs (#2685) This also fixes the `WrongTypeOfReturnValue` issue for spy stubs. Fixes #2616 Co-authored-by: yushouqiu --- .../internal/handler/MockHandlerImpl.java | 31 ++++++----- .../SpyAsDefaultMockUsageTest.java} | 19 ++----- .../bugs/OngoingStubShiftTest.java | 54 +++++++++++++++++++ 3 files changed, 78 insertions(+), 26 deletions(-) rename src/test/java/org/mockitousage/{misuse/SpyStubbingMisuseTest.java => spies/SpyAsDefaultMockUsageTest.java} (61%) create mode 100644 subprojects/inline/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java diff --git a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java index 17b967efcf..e58659a166 100644 --- a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java +++ b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java @@ -107,18 +107,25 @@ public Object handle(Invocation invocation) throws Throwable { mockingProgress().reportOngoingStubbing(ongoingStubbing); } } else { - Object ret = mockSettings.getDefaultAnswer().answer(invocation); - DefaultAnswerValidator.validateReturnValueFor(invocation, ret); - - // Mockito uses it to redo setting invocation for potential stubbing in case of partial - // mocks / spies. - // Without it, the real method inside 'when' might have delegated to other self method - // and overwrite the intended stubbed method with a different one. - // This means we would be stubbing a wrong method. - // Typically this would led to runtime exception that validates return type with stubbed - // method signature. - invocationContainer.resetInvocationForPotentialStubbing(invocationMatcher); - return ret; + try { + Object ret = mockSettings.getDefaultAnswer().answer(invocation); + DefaultAnswerValidator.validateReturnValueFor(invocation, ret); + + return ret; + } finally { + // Mockito uses it to redo setting invocation for potential stubbing in case of + // partial + // mocks / spies. + // Without it, the real method inside 'when' might have delegated to other self + // method + // and overwrite the intended stubbed method with a different one. + // This means we would be stubbing a wrong method. + // Typically this would led to runtime exception that validates return type with + // stubbed + // method signature. + invocationContainer.resetInvocationForPotentialStubbing(invocationMatcher); + mockingProgress().reportOngoingStubbing(ongoingStubbing); + } } } diff --git a/src/test/java/org/mockitousage/misuse/SpyStubbingMisuseTest.java b/src/test/java/org/mockitousage/spies/SpyAsDefaultMockUsageTest.java similarity index 61% rename from src/test/java/org/mockitousage/misuse/SpyStubbingMisuseTest.java rename to src/test/java/org/mockitousage/spies/SpyAsDefaultMockUsageTest.java index 9f0acfee4e..463b9370d7 100644 --- a/src/test/java/org/mockitousage/misuse/SpyStubbingMisuseTest.java +++ b/src/test/java/org/mockitousage/spies/SpyAsDefaultMockUsageTest.java @@ -2,16 +2,14 @@ * Copyright (c) 2007 Mockito contributors * This program is made available under the terms of the MIT License. */ -package org.mockitousage.misuse; +package org.mockitousage.spies; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertSame; import static org.mockito.Mockito.*; import org.junit.Test; -import org.mockito.exceptions.misusing.WrongTypeOfReturnValue; -public class SpyStubbingMisuseTest { +public class SpyAsDefaultMockUsageTest { @Test public void nestedWhenTest() { @@ -19,15 +17,8 @@ public void nestedWhenTest() { Sampler mpoo = mock(Sampler.class); Producer out = spy(new Producer(mfoo)); - try { - when(out.produce()).thenReturn(mpoo); - fail(); - } catch (WrongTypeOfReturnValue e) { - assertThat(e.getMessage()) - .contains("spy") - .contains("syntax") - .contains("doReturn|Throw"); - } + when(out.produce()).thenReturn(mpoo); + assertSame(mpoo, out.produce()); } public class Sample {} diff --git a/subprojects/inline/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java b/subprojects/inline/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java new file mode 100644 index 0000000000..47062fedfd --- /dev/null +++ b/subprojects/inline/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitoinline.bugs; + +import static org.junit.Assert.assertEquals; +import org.junit.Test; +import org.mockito.MockedStatic; +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.mockStatic; + +public class OngoingStubShiftTest { + + private static class StaticInt { + static int getInt() { + return 1; + } + } + + private static class StaticStr { + static String getStr() { + return Integer.toString(StaticInt.getInt()); + } + } + + @Test + public void keep_ongoing_stub_when_spy() { + try (MockedStatic mockInt = mockStatic(StaticInt.class); + MockedStatic mockStr = mockStatic(StaticStr.class, CALLS_REAL_METHODS)) { + + mockStr.when(StaticStr::getStr).thenReturn("1"); + assertEquals("1", StaticStr.getStr()); + } + } + + private static class StaticWithException { + static String getString() { + return Integer.toString(getInt()); + } + + static int getInt() { + throw new NullPointerException(); + } + } + + @Test + public void keep_ongoing_stub_when_exception() { + try (MockedStatic mock = mockStatic(StaticWithException.class, CALLS_REAL_METHODS)) { + mock.when(StaticWithException::getString).thenReturn("1"); + assertEquals("1", StaticWithException.getString()); + } + } +} From b984aac2810a1cb488c7ccb44941becec553ff1f Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Sun, 19 Jun 2022 19:11:39 +0800 Subject: [PATCH 050/160] Fixes #2679 : Update Javadoc (#2689) --- src/main/java/org/mockito/quality/Strictness.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/mockito/quality/Strictness.java b/src/main/java/org/mockito/quality/Strictness.java index b25cab33e6..76cd552b38 100644 --- a/src/main/java/org/mockito/quality/Strictness.java +++ b/src/main/java/org/mockito/quality/Strictness.java @@ -65,7 +65,6 @@ public enum Strictness { * Ensures clean tests, reduces test code duplication, improves debuggability. * Offers best combination of flexibility and productivity. * Highly recommended. - * Planned as default for Mockito v4. * Enable it via our JUnit support ({@link MockitoJUnit}) or {@link MockitoSession}. *

* Adds following behavior: From 34cbd23ee985ec70787181fe35926e8ad7b92c4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jun 2022 08:05:27 +0200 Subject: [PATCH 051/160] Bump versions.bytebuddy from 1.12.10 to 1.12.11 (#2690) Bumps `versions.bytebuddy` from 1.12.10 to 1.12.11. Updates `byte-buddy` from 1.12.10 to 1.12.11 - [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.10...byte-buddy-1.12.11) Updates `byte-buddy-agent` from 1.12.10 to 1.12.11 - [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.10...byte-buddy-1.12.11) Updates `byte-buddy-android` from 1.12.10 to 1.12.11 - [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.10...byte-buddy-1.12.11) --- 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 756997364e..bc8fdbf537 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.12.10' +versions.bytebuddy = '1.12.11' versions.junitJupiter = '5.8.2' versions.errorprone = '2.14.0' From 70e16dca0daff6df6222b5c1832a57732114dd05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jun 2022 00:14:30 +0200 Subject: [PATCH 052/160] Bump kotlinx-coroutines-core from 1.6.1-native-mt to 1.6.3-native-mt (#2691) Bumps [kotlinx-coroutines-core](https://github.com/Kotlin/kotlinx.coroutines) from 1.6.1-native-mt to 1.6.3-native-mt. - [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases) - [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md) - [Commits](https://github.com/Kotlin/kotlinx.coroutines/commits) --- updated-dependencies: - dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-core 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 bc8fdbf537..fb8e6556d1 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -39,7 +39,7 @@ libraries.kotlin = [ version: kotlinVersion, stdlib: "org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}", - coroutines: 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1-native-mt', + coroutines: 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3-native-mt', gradlePlugin: "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}", ] libraries.android = [ From 3f9ea84ad056376dee9ad7cee2a46e9fa8cfdb69 Mon Sep 17 00:00:00 2001 From: dstango Date: Sun, 26 Jun 2022 00:03:52 +0200 Subject: [PATCH 053/160] Clarify mocking error message and point to GitHub (#2693) Fixes #2692 --- .../ByteBuddyCrossClassLoaderSerializationSupport.java | 4 ++-- .../creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java | 2 +- .../creation/bytebuddy/SubclassByteBuddyMockMaker.java | 5 +++-- .../java/org/mockito/internal/runners/RunnerFactory.java | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/ByteBuddyCrossClassLoaderSerializationSupport.java b/src/main/java/org/mockito/internal/creation/bytebuddy/ByteBuddyCrossClassLoaderSerializationSupport.java index 02e8961385..a1eed21e77 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/ByteBuddyCrossClassLoaderSerializationSupport.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/ByteBuddyCrossClassLoaderSerializationSupport.java @@ -219,14 +219,14 @@ private Object readResolve() throws ObjectStreamException { + typeToMock.getCanonicalName() + "'. The error was :", " " + ioe.getMessage(), - "If you are unsure what is the reason of this exception, feel free to contact us on the mailing list."), + "If you are unsure what is the reason of this exception, feel free to open an issue on GitHub."), ioe); } catch (ClassNotFoundException cce) { throw new MockitoSerializationIssue( join( "A class couldn't be found while deserializing a Mockito mock, you should check your classpath. The error was :", " " + cce.getMessage(), - "If you are still unsure what is the reason of this exception, feel free to contact us on the mailing list."), + "If you are still unsure what is the reason of this exception, feel free to open an issue on GitHub."), cce); } } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java index bf6cf4b735..4c8c3fadd7 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java @@ -434,7 +434,7 @@ private RuntimeException prettifyFailure( join( "Mockito cannot mock this class: " + mockFeatures.getTypeToMock() + ".", "", - "If you're not sure why you're getting this error, please report to the mailing list.", + "If you're not sure why you're getting this error, please open an issue on GitHub.", "", Platform.warnForVM( "IBM J9 VM", diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java index da8de10de9..e8dcce229a 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java @@ -114,8 +114,9 @@ private RuntimeException prettifyFailure( join( "Mockito cannot mock this class: " + mockFeatures.getTypeToMock() + ".", "", - "Mockito can only mock non-private & non-final classes.", - "If you're not sure why you're getting this error, please report to the mailing list.", + "Mockito can only mock non-private & non-final classes, but the root cause of this error might be different.", + "Please check the full stacktrace to understand what the issue is.", + "If you're still not sure why you're getting this error, please open an issue on GitHub.", "", Platform.warnForVM( "IBM J9 VM", diff --git a/src/main/java/org/mockito/internal/runners/RunnerFactory.java b/src/main/java/org/mockito/internal/runners/RunnerFactory.java index 369dc8ba82..68ef16cd1a 100644 --- a/src/main/java/org/mockito/internal/runners/RunnerFactory.java +++ b/src/main/java/org/mockito/internal/runners/RunnerFactory.java @@ -93,7 +93,7 @@ public InternalRunner create(Class klass, Supplier liste + "MockitoRunner can only be used with JUnit 4.5 or higher.\n" + "You can upgrade your JUnit version or write your own Runner (please consider contributing your runner to the Mockito community).\n" + "Bear in mind that you can still enjoy all features of the framework without using runners (they are completely optional).\n" - + "If you get this error despite using JUnit 4.5 or higher then please report this error to the mockito mailing list.\n", + + "If you get this error despite using JUnit 4.5 or higher, then please open an issue on GitHub.\n", t); } } From 2a66c6366a515273bbb25fb36b5915f1925833a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jun 2022 20:22:17 +0200 Subject: [PATCH 054/160] Bump versions.bytebuddy from 1.12.11 to 1.12.12 (#2695) Bumps `versions.bytebuddy` from 1.12.11 to 1.12.12. Updates `byte-buddy` from 1.12.11 to 1.12.12 - [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.11...byte-buddy-1.12.12) Updates `byte-buddy-agent` from 1.12.11 to 1.12.12 - [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.11...byte-buddy-1.12.12) Updates `byte-buddy-android` from 1.12.11 to 1.12.12 - [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.11...byte-buddy-1.12.12) --- 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 fb8e6556d1..c568bdfb5e 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.12.11' +versions.bytebuddy = '1.12.12' versions.junitJupiter = '5.8.2' versions.errorprone = '2.14.0' From e962176734e8b103fcce2b45c1c269b84a466ff0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 21:16:51 +0200 Subject: [PATCH 055/160] Bump com.diffplug.spotless from 6.7.2 to 6.8.0 (#2699) Bumps com.diffplug.spotless from 6.7.2 to 6.8.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 28f9f07dad..f9415563a3 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.7.2' + id 'com.diffplug.spotless' version '6.8.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.3.1' 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 056/160] 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 057/160] 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 058/160] 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 059/160] 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 060/160] 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 061/160] 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 062/160] 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 063/160] 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 064/160] 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 065/160] 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 066/160] 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 067/160] 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 068/160] 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 069/160] 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 070/160] 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 071/160] 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 072/160] 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() { From 0753d48161bc6376108f38146c01c80274a712e3 Mon Sep 17 00:00:00 2001 From: Alex <93376818+sashashura@users.noreply.github.com> Date: Mon, 5 Sep 2022 15:48:23 +0200 Subject: [PATCH 073/160] Explicitly add permissions to GitHub actions (#2744) Signed-off-by: sashashura <93376818+sashashura@users.noreply.github.com> --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a6d99b687..23975b8d29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,9 @@ on: pull_request: branches: ['**'] +permissions: + contents: read + jobs: # @@ -81,6 +84,8 @@ jobs: # Release job, only for pushes to the main development branch # release: + permissions: + contents: write runs-on: ubuntu-latest needs: [build] # build job must pass before we can release From 3e910ea990e61b29ec76d8b47a6d1b1607d9da3f Mon Sep 17 00:00:00 2001 From: Johannes Spangenberg Date: Wed, 7 Sep 2022 18:01:14 +0200 Subject: [PATCH 074/160] Fixes #2626 : Introduce MockSettings.mockMaker (#2701) --- src/main/java/org/mockito/Mock.java | 8 + src/main/java/org/mockito/MockMakers.java | 40 ++++ src/main/java/org/mockito/MockSettings.java | 22 ++ src/main/java/org/mockito/Mockito.java | 23 +- .../org/mockito/internal/MockitoCore.java | 18 +- .../MockAnnotationProcessor.java | 3 + .../configuration/SpyAnnotationEngine.java | 2 + .../injection/SpyOnInjectedFieldsHandler.java | 1 + .../plugins/DefaultMockitoPlugins.java | 20 +- .../plugins/PluginInitializer.java | 8 +- .../configuration/plugins/PluginLoader.java | 8 +- .../configuration/plugins/PluginRegistry.java | 3 +- .../internal/creation/MockSettingsImpl.java | 8 +- .../creation/settings/CreationSettings.java | 7 + .../RetrieveGenericsForDefaultAnswers.java | 7 +- .../defaultanswers/ReturnsDeepStubs.java | 9 +- .../stubbing/defaultanswers/ReturnsMocks.java | 9 +- .../defaultanswers/ReturnsSmartNulls.java | 12 +- .../internal/util/MockCreationValidator.java | 4 +- .../org/mockito/internal/util/MockUtil.java | 85 ++++++-- .../util/reflection/FieldInitializer.java | 6 +- .../mockito/mock/MockCreationSettings.java | 10 + .../java/org/mockito/plugins/MockMaker.java | 14 ++ .../mockito/ProgrammaticMockMakerTest.java | 201 ++++++++++++++++++ .../plugins/DefaultMockitoPluginsTest.java | 8 +- .../util/MockCreationValidatorTest.java | 2 +- .../mockito/internal/util/MockUtilTest.java | 8 +- .../DeferMockMakersClassLoadingTest.java | 96 +++++++++ .../ProgrammaticMockMakerAnnotationTest.java | 42 ++++ .../mockitoinline/ConstructionMockTest.java | 17 ++ 30 files changed, 638 insertions(+), 63 deletions(-) create mode 100644 src/main/java/org/mockito/MockMakers.java create mode 100644 src/test/java/org/mockito/ProgrammaticMockMakerTest.java create mode 100644 src/test/java/org/mockitointegration/DeferMockMakersClassLoadingTest.java create mode 100644 src/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java diff --git a/src/main/java/org/mockito/Mock.java b/src/main/java/org/mockito/Mock.java index e8469b830d..2e4f515035 100644 --- a/src/main/java/org/mockito/Mock.java +++ b/src/main/java/org/mockito/Mock.java @@ -13,6 +13,7 @@ import java.lang.annotation.Target; import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.plugins.MockMaker; import org.mockito.stubbing.Answer; /** @@ -124,6 +125,13 @@ */ Strictness strictness() default Strictness.TEST_LEVEL_DEFAULT; + /** + * Mock will be created by the given {@link MockMaker}, see {@link MockSettings#mockMaker(String)}. + * + * @since 4.8.0 + */ + String mockMaker() default ""; + enum Strictness { /** diff --git a/src/main/java/org/mockito/MockMakers.java b/src/main/java/org/mockito/MockMakers.java new file mode 100644 index 0000000000..2f5459d222 --- /dev/null +++ b/src/main/java/org/mockito/MockMakers.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito; + +import org.mockito.plugins.MockMaker; + +/** + * Constants for built-in implementations of {@code MockMaker}. + * You may use the constants of this class for {@link MockSettings#mockMaker(String)} or {@link Mock#mockMaker()}. + * The string values of these constants may also be used in the resource file mockito-extensions/org.mockito.plugins.MockMaker + * as described in the class documentation of {@link MockMaker}. + * + * @since 4.8.0 + */ +public final class MockMakers { + /** + * Inline mock maker which can mock final types, enums and final methods. + * This mock maker cannot mock native methods, + * and it does not support {@link MockSettings#extraInterfaces(Class[]) extra interfaces}. + * + * @see Mocking final types, enums and final methods + */ + public static final String INLINE = "mock-maker-inline"; + /** + * Proxy mock maker which avoids code generation, but can only mock interfaces. + * + * @see Avoiding code generation when restricting mocks to interfaces + */ + public static final String PROXY = "mock-maker-proxy"; + /** + * Subclass mock maker which mocks types by creating subclasses. + * This is the first built-in mock maker which has been provided by Mockito. + * Since this mock maker relies on subclasses, it cannot mock final classes and methods. + */ + public static final String SUBCLASS = "mock-maker-subclass"; + + private MockMakers() {} +} diff --git a/src/main/java/org/mockito/MockSettings.java b/src/main/java/org/mockito/MockSettings.java index f838a8d121..8d8a093e25 100644 --- a/src/main/java/org/mockito/MockSettings.java +++ b/src/main/java/org/mockito/MockSettings.java @@ -15,6 +15,7 @@ import org.mockito.listeners.VerificationStartedListener; import org.mockito.mock.MockCreationSettings; import org.mockito.mock.SerializableMode; +import org.mockito.plugins.MockMaker; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; @@ -381,4 +382,25 @@ public interface MockSettings extends Serializable { * @since 4.6.0 */ MockSettings strictness(Strictness strictness); + + /** + * Specifies the {@code MockMaker} for the mock. + * The default depends on your project as described in the class documentation of {@link MockMaker}. + * You should usually use the default, this option primarily exists to ease migrations. + * You may specify either one of the constants from {@link MockMakers}, + *

+     *     Object mock = Mockito.mock(Object.class, Mockito.withSettings()
+     *             .mockMaker(MockMakers.INLINE));
+     * 
+ * or the {@link Class#getName() binary name} of a class which implements the {@code MockMaker} interface. + *
+     *     Object mock = Mockito.mock(Object.class, Mockito.withSettings()
+     *             .mockMaker("org.awesome.mockito.AwesomeMockMaker"));
+     * 
+ * + * @param mockMaker the {@code MockMaker} to use for the mock + * @return settings instance so that you can fluently specify other settings + * @since 4.8.0 + */ + MockSettings mockMaker(String mockMaker); } diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 9f7383f232..a221376287 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -105,7 +105,8 @@ * 49. New API for mocking object construction (Since 3.5.0)
* 50. Avoiding code generation when restricting mocks to interfaces (Since 3.12.2)
* 51. New API for marking classes as unmockable (Since 4.1.0)
- * 52. New strictness attribute for @Mock annotation and MockSettings.strictness() methods (Since 4.6.0)
+ * 52. New strictness attribute for @Mock annotation and MockSettings.strictness() methods (Since 4.6.0)
+ * 53. Specifying mock maker for individual mocks (Since 4.8.0)
* * *

0. Migrating to Mockito 2

@@ -1586,7 +1587,7 @@ * released. To define mock behavior and to verify method invocations, use the MockedConstruction that is returned. *

* - *

50. Avoiding code generation when only interfaces are mocked (since 3.12.2)

+ *

50. Avoiding code generation when only interfaces are mocked (since 3.12.2)

* * The JVM offers the {@link java.lang.reflect.Proxy} facility for creating dynamic proxies of interface types. For most applications, Mockito * must be capable of mocking classes as supported by the default mock maker, or even final classes, as supported by the inline mock maker. To @@ -1609,7 +1610,7 @@ *

* *

52. - * New strictness attribute for @Mock annotation and MockSettings.strictness() methods (Since 4.6.0)

+ * New strictness attribute for @Mock annotation and MockSettings.strictness() methods (Since 4.6.0) * * You can now customize the strictness level for a single mock, either using `@Mock` annotation strictness attribute or * using `MockSettings.strictness()`. This can be useful if you want all of your mocks to be strict, @@ -1622,6 +1623,22 @@ * Foo mock = Mockito.mock(Foo.class, withSettings().strictness(Strictness.WARN)); * * + *

53. + * Specifying mock maker for individual mocks (Since 4.8.0)

+ * + * You may encounter situations where you want to use a different mock maker for a specific test only. + * For example, you might want to migrate to the inline mock maker, but a few test do not work right away. + * In such case, you can (temporarily) use {@link MockSettings#mockMaker(String)} and {@link Mock#mockMaker()} + * to specify the mock maker for a specific mock which is causing the problem. + * + *

+ *   // using annotation
+ *   @Mock(mockMaker = MockMakers.SUBCLASS)
+ *   Foo mock;
+ *   // using MockSettings.withSettings()
+ *   Foo mock = Mockito.mock(Foo.class, withSettings().mockMaker(MockMakers.SUBCLASS));
+ * 
+ * */ @CheckReturnValue @SuppressWarnings("unchecked") diff --git a/src/main/java/org/mockito/internal/MockitoCore.java b/src/main/java/org/mockito/internal/MockitoCore.java index fff3b7667e..fd39f6a4a2 100644 --- a/src/main/java/org/mockito/internal/MockitoCore.java +++ b/src/main/java/org/mockito/internal/MockitoCore.java @@ -22,7 +22,6 @@ import static org.mockito.internal.util.MockUtil.getMockHandler; import static org.mockito.internal.util.MockUtil.isMock; import static org.mockito.internal.util.MockUtil.resetMock; -import static org.mockito.internal.util.MockUtil.typeMockabilityOf; import static org.mockito.internal.verification.VerificationModeFactory.noInteractions; import static org.mockito.internal.verification.VerificationModeFactory.noMoreInteractions; @@ -31,6 +30,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.Function; import org.mockito.InOrder; import org.mockito.MockSettings; @@ -67,10 +67,6 @@ import org.mockito.stubbing.Stubber; import org.mockito.verification.VerificationMode; -import java.util.Arrays; -import java.util.List; -import java.util.function.Function; - @SuppressWarnings("unchecked") public class MockitoCore { @@ -78,10 +74,6 @@ public class MockitoCore { private static final Set> MOCKABLE_CLASSES = Collections.synchronizedSet(new HashSet<>()); - public boolean isTypeMockable(Class typeToMock) { - return typeMockabilityOf(typeToMock).mockable(); - } - public T mock(Class typeToMock, MockSettings settings) { if (!(settings instanceof MockSettingsImpl)) { throw new IllegalArgumentException( @@ -160,6 +152,14 @@ public MockedConstruction mockConstruction( + "At the moment, you cannot provide your own implementations of that class."); } MockSettingsImpl impl = MockSettingsImpl.class.cast(value); + String mockMaker = impl.getMockMaker(); + if (mockMaker != null) { + throw new IllegalArgumentException( + "Unexpected MockMaker '" + + mockMaker + + "'\n" + + "At the moment, you cannot override the MockMaker for construction mocks."); + } return impl.build(typeToMock); }; MockMaker.ConstructionMockControl control = diff --git a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java index 66c3f31d12..95356f5c9a 100644 --- a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java +++ b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java @@ -53,6 +53,9 @@ public static Object processAnnotationForMock( if (annotation.strictness() != Mock.Strictness.TEST_LEVEL_DEFAULT) { mockSettings.strictness(Strictness.valueOf(annotation.strictness().toString())); } + if (!annotation.mockMaker().isEmpty()) { + mockSettings.mockMaker(annotation.mockMaker()); + } // see @Mock answer default value mockSettings.defaultAnswer(annotation.answer()); diff --git a/src/main/java/org/mockito/internal/configuration/SpyAnnotationEngine.java b/src/main/java/org/mockito/internal/configuration/SpyAnnotationEngine.java index a88ad63b84..fa9af798f2 100644 --- a/src/main/java/org/mockito/internal/configuration/SpyAnnotationEngine.java +++ b/src/main/java/org/mockito/internal/configuration/SpyAnnotationEngine.java @@ -83,6 +83,7 @@ public AutoCloseable process(Class context, Object testInstance) { } private static Object spyInstance(Field field, Object instance) { + // TODO: Add mockMaker option for @Spy annotation (#2740) return Mockito.mock( instance.getClass(), withSettings() @@ -93,6 +94,7 @@ private static Object spyInstance(Field field, Object instance) { private static Object spyNewInstance(Object testInstance, Field field) throws InstantiationException, IllegalAccessException, InvocationTargetException { + // TODO: Add mockMaker option for @Spy annotation (#2740) MockSettings settings = withSettings().defaultAnswer(CALLS_REAL_METHODS).name(field.getName()); Class type = field.getType(); diff --git a/src/main/java/org/mockito/internal/configuration/injection/SpyOnInjectedFieldsHandler.java b/src/main/java/org/mockito/internal/configuration/injection/SpyOnInjectedFieldsHandler.java index 73f3004c19..bbfe88b85a 100644 --- a/src/main/java/org/mockito/internal/configuration/injection/SpyOnInjectedFieldsHandler.java +++ b/src/main/java/org/mockito/internal/configuration/injection/SpyOnInjectedFieldsHandler.java @@ -42,6 +42,7 @@ protected boolean processInjection(Field field, Object fieldOwner, Set m // B. protect against multiple use of MockitoAnnotations.openMocks() Mockito.reset(instance); } else { + // TODO: Add mockMaker option for @Spy annotation (#2740) Object mock = Mockito.mock( instance.getClass(), diff --git a/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java b/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java index 592f79a81e..14f52f3705 100644 --- a/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java +++ b/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java @@ -5,7 +5,11 @@ package org.mockito.internal.configuration.plugins; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; + +import org.mockito.MockMakers; import org.mockito.plugins.AnnotationEngine; import org.mockito.plugins.DoNotMockEnforcer; import org.mockito.plugins.InstantiatorProvider2; @@ -16,11 +20,13 @@ import org.mockito.plugins.PluginSwitch; import org.mockito.plugins.StackTraceCleanerProvider; -class DefaultMockitoPlugins implements MockitoPlugins { +public class DefaultMockitoPlugins implements MockitoPlugins { private static final Map DEFAULT_PLUGINS = new HashMap<>(); - static final String INLINE_ALIAS = "mock-maker-inline"; - static final String PROXY_ALIAS = "mock-maker-proxy"; + static final String INLINE_ALIAS = MockMakers.INLINE; + static final String PROXY_ALIAS = MockMakers.PROXY; + static final String SUBCLASS_ALIAS = MockMakers.SUBCLASS; + public static final Set MOCK_MAKER_ALIASES = new HashSet<>(); static final String MODULE_ALIAS = "member-accessor-module"; static { @@ -41,6 +47,8 @@ class DefaultMockitoPlugins implements MockitoPlugins { DEFAULT_PLUGINS.put( INLINE_ALIAS, "org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker"); DEFAULT_PLUGINS.put(PROXY_ALIAS, "org.mockito.internal.creation.proxy.ProxyMockMaker"); + DEFAULT_PLUGINS.put( + SUBCLASS_ALIAS, "org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker"); DEFAULT_PLUGINS.put( MockitoLogger.class.getName(), "org.mockito.internal.util.ConsoleMockitoLogger"); DEFAULT_PLUGINS.put( @@ -51,6 +59,10 @@ class DefaultMockitoPlugins implements MockitoPlugins { DEFAULT_PLUGINS.put( DoNotMockEnforcer.class.getName(), "org.mockito.internal.configuration.DefaultDoNotMockEnforcer"); + + MOCK_MAKER_ALIASES.add(INLINE_ALIAS); + MOCK_MAKER_ALIASES.add(PROXY_ALIAS); + MOCK_MAKER_ALIASES.add(SUBCLASS_ALIAS); } @Override @@ -59,7 +71,7 @@ public T getDefaultPlugin(Class pluginType) { return create(pluginType, className); } - String getDefaultPluginClass(String classOrAlias) { + public static String getDefaultPluginClass(String classOrAlias) { return DEFAULT_PLUGINS.get(classOrAlias); } diff --git a/src/main/java/org/mockito/internal/configuration/plugins/PluginInitializer.java b/src/main/java/org/mockito/internal/configuration/plugins/PluginInitializer.java index a88f0cc19f..b042a84396 100644 --- a/src/main/java/org/mockito/internal/configuration/plugins/PluginInitializer.java +++ b/src/main/java/org/mockito/internal/configuration/plugins/PluginInitializer.java @@ -18,12 +18,10 @@ class PluginInitializer { private final PluginSwitch pluginSwitch; private final Set alias; - private final DefaultMockitoPlugins plugins; - PluginInitializer(PluginSwitch pluginSwitch, Set alias, DefaultMockitoPlugins plugins) { + PluginInitializer(PluginSwitch pluginSwitch, Set alias) { this.pluginSwitch = pluginSwitch; this.alias = alias; - this.plugins = plugins; } /** @@ -47,7 +45,7 @@ public T loadImpl(Class service) { new PluginFinder(pluginSwitch).findPluginClass(Iterables.toIterable(resources)); if (classOrAlias != null) { if (alias.contains(classOrAlias)) { - classOrAlias = plugins.getDefaultPluginClass(classOrAlias); + classOrAlias = DefaultMockitoPlugins.getDefaultPluginClass(classOrAlias); } Class pluginClass = loader.loadClass(classOrAlias); Object plugin = pluginClass.getDeclaredConstructor().newInstance(); @@ -79,7 +77,7 @@ public List loadImpls(Class service) { List impls = new ArrayList<>(); for (String classOrAlias : classesOrAliases) { if (alias.contains(classOrAlias)) { - classOrAlias = plugins.getDefaultPluginClass(classOrAlias); + classOrAlias = DefaultMockitoPlugins.getDefaultPluginClass(classOrAlias); } Class pluginClass = loader.loadClass(classOrAlias); Object plugin = pluginClass.getDeclaredConstructor().newInstance(); diff --git a/src/main/java/org/mockito/internal/configuration/plugins/PluginLoader.java b/src/main/java/org/mockito/internal/configuration/plugins/PluginLoader.java index 3f33dffdc6..e554173326 100644 --- a/src/main/java/org/mockito/internal/configuration/plugins/PluginLoader.java +++ b/src/main/java/org/mockito/internal/configuration/plugins/PluginLoader.java @@ -27,8 +27,7 @@ class PluginLoader { PluginLoader(PluginSwitch pluginSwitch) { this( new DefaultMockitoPlugins(), - new PluginInitializer( - pluginSwitch, Collections.emptySet(), new DefaultMockitoPlugins())); + new PluginInitializer(pluginSwitch, Collections.emptySet())); } /** @@ -40,10 +39,7 @@ class PluginLoader { PluginLoader(PluginSwitch pluginSwitch, String... alias) { this( new DefaultMockitoPlugins(), - new PluginInitializer( - pluginSwitch, - new HashSet<>(Arrays.asList(alias)), - new DefaultMockitoPlugins())); + new PluginInitializer(pluginSwitch, new HashSet<>(Arrays.asList(alias)))); } /** diff --git a/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java b/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java index 01e89d2014..6abaedbaef 100644 --- a/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java +++ b/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java @@ -23,8 +23,7 @@ class PluginRegistry { private final MockMaker mockMaker = new PluginLoader( pluginSwitch, - DefaultMockitoPlugins.INLINE_ALIAS, - DefaultMockitoPlugins.PROXY_ALIAS) + DefaultMockitoPlugins.MOCK_MAKER_ALIASES.toArray(new String[0])) .loadPlugin(MockMaker.class); private final MemberAccessor memberAccessor = diff --git a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java index f73a718298..a25299722c 100644 --- a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java +++ b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java @@ -254,11 +254,17 @@ public MockSettings strictness(Strictness strictness) { return this; } + @Override + public MockSettings mockMaker(String mockMaker) { + this.mockMaker = mockMaker; + return this; + } + private static CreationSettings validatedSettings( Class typeToMock, CreationSettings source) { MockCreationValidator validator = new MockCreationValidator(); - validator.validateType(typeToMock); + validator.validateType(typeToMock, source.getMockMaker()); validator.validateExtraInterfaces(typeToMock, source.getExtraInterfaces()); validator.validateMockedType(typeToMock, source.getSpiedInstance()); diff --git a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java index 13939f6fbd..4b50781833 100644 --- a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java +++ b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java @@ -46,6 +46,7 @@ public class CreationSettings implements MockCreationSettings, Serializabl private Object outerClassInstance; private Object[] constructorArgs; protected Strictness strictness = null; + protected String mockMaker; public CreationSettings() {} @@ -68,6 +69,7 @@ public CreationSettings(CreationSettings copy) { this.constructorArgs = copy.getConstructorArgs(); this.strictness = copy.strictness; this.stripAnnotations = copy.stripAnnotations; + this.mockMaker = copy.mockMaker; } @Override @@ -178,4 +180,9 @@ public boolean isLenient() { public Strictness getStrictness() { return strictness; } + + @Override + public String getMockMaker() { + return mockMaker; + } } diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java index fa6a72f53d..8b64a16916 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java @@ -8,7 +8,6 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; -import org.mockito.internal.MockitoCore; import org.mockito.internal.util.MockUtil; import org.mockito.internal.util.reflection.GenericMetadataSupport; import org.mockito.invocation.InvocationOnMock; @@ -16,8 +15,6 @@ final class RetrieveGenericsForDefaultAnswers { - private static final MockitoCore MOCKITO_CORE = new MockitoCore(); - static Object returnTypeForMockWithCorrectGenerics( InvocationOnMock invocation, AnswerCallback answerCallback) { Class type = invocation.getMethod().getReturnType(); @@ -38,7 +35,9 @@ static Object returnTypeForMockWithCorrectGenerics( } if (type != null) { - if (!MOCKITO_CORE.isTypeMockable(type)) { + final MockCreationSettings mockSettings = + MockUtil.getMockSettings(invocation.getMock()); + if (!MockUtil.typeMockabilityOf(type, mockSettings.getMockMaker()).mockable()) { return null; } diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java index 097b11b6e2..8356c1b79b 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java @@ -5,6 +5,7 @@ package org.mockito.internal.stubbing.defaultanswers; import static org.mockito.Mockito.withSettings; +import static org.mockito.internal.util.MockUtil.typeMockabilityOf; import java.io.IOException; import java.io.Serializable; @@ -50,9 +51,10 @@ public Object answer(InvocationOnMock invocation) throws Throwable { GenericMetadataSupport returnTypeGenericMetadata = actualParameterizedType(invocation.getMock()) .resolveGenericReturnType(invocation.getMethod()); + MockCreationSettings mockSettings = MockUtil.getMockSettings(invocation.getMock()); Class rawType = returnTypeGenericMetadata.rawType(); - if (!mockitoCore().isTypeMockable(rawType)) { + if (!typeMockabilityOf(rawType, mockSettings.getMockMaker()).mockable()) { if (invocation.getMethod().getReturnType().equals(rawType)) { return delegate().answer(invocation); } else { @@ -119,7 +121,7 @@ private Object newDeepStubMock( private MockSettings withSettingsUsing( GenericMetadataSupport returnTypeGenericMetadata, - MockCreationSettings parentMockSettings) { + MockCreationSettings parentMockSettings) { MockSettings mockSettings = returnTypeGenericMetadata.hasRawExtraInterfaces() ? withSettings() @@ -127,7 +129,8 @@ private MockSettings withSettingsUsing( : withSettings(); return propagateSerializationSettings(mockSettings, parentMockSettings) - .defaultAnswer(returnsDeepStubsAnswerUsing(returnTypeGenericMetadata)); + .defaultAnswer(returnsDeepStubsAnswerUsing(returnTypeGenericMetadata)) + .mockMaker(parentMockSettings.getMockMaker()); } private MockSettings propagateSerializationSettings( diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java index 0ef6f0d954..c155780916 100755 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java @@ -8,7 +8,9 @@ import org.mockito.Mockito; import org.mockito.internal.creation.MockSettingsImpl; +import org.mockito.internal.util.MockUtil; import org.mockito.invocation.InvocationOnMock; +import org.mockito.mock.MockCreationSettings; import org.mockito.stubbing.Answer; public class ReturnsMocks implements Answer, Serializable { @@ -33,9 +35,14 @@ public Object apply(Class type) { return null; } + MockCreationSettings mockSettings = + MockUtil.getMockSettings(invocation.getMock()); + return Mockito.mock( type, - new MockSettingsImpl().defaultAnswer(ReturnsMocks.this)); + new MockSettingsImpl<>() + .defaultAnswer(ReturnsMocks.this) + .mockMaker(mockSettings.getMockMaker())); } }); } 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 bcca2ef52a..a6e0b2c138 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java @@ -10,9 +10,12 @@ import java.io.Serializable; import org.mockito.Mockito; +import org.mockito.internal.creation.MockSettingsImpl; import org.mockito.internal.debugging.LocationFactory; +import org.mockito.internal.util.MockUtil; import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.Location; +import org.mockito.mock.MockCreationSettings; import org.mockito.stubbing.Answer; /** @@ -56,9 +59,16 @@ public Object apply(Class type) { return null; } + MockCreationSettings mockSettings = + MockUtil.getMockSettings(invocation.getMock()); + Answer defaultAnswer = + new ThrowsSmartNullPointer(invocation, LocationFactory.create()); + return Mockito.mock( type, - new ThrowsSmartNullPointer(invocation, LocationFactory.create())); + new MockSettingsImpl<>() + .defaultAnswer(defaultAnswer) + .mockMaker(mockSettings.getMockMaker())); } }); } diff --git a/src/main/java/org/mockito/internal/util/MockCreationValidator.java b/src/main/java/org/mockito/internal/util/MockCreationValidator.java index 7baa4e252f..9db6b36ea3 100644 --- a/src/main/java/org/mockito/internal/util/MockCreationValidator.java +++ b/src/main/java/org/mockito/internal/util/MockCreationValidator.java @@ -18,8 +18,8 @@ @SuppressWarnings("unchecked") public class MockCreationValidator { - public void validateType(Class classToMock) { - TypeMockability typeMockability = MockUtil.typeMockabilityOf(classToMock); + public void validateType(Class classToMock, String mockMaker) { + TypeMockability typeMockability = MockUtil.typeMockabilityOf(classToMock, mockMaker); if (!typeMockability.mockable()) { throw cannotMockClass(classToMock, typeMockability.nonMockableReason()); } diff --git a/src/main/java/org/mockito/internal/util/MockUtil.java b/src/main/java/org/mockito/internal/util/MockUtil.java index 2159608623..0d80f6e195 100644 --- a/src/main/java/org/mockito/internal/util/MockUtil.java +++ b/src/main/java/org/mockito/internal/util/MockUtil.java @@ -7,6 +7,7 @@ import org.mockito.MockedConstruction; import org.mockito.Mockito; import org.mockito.exceptions.misusing.NotAMockException; +import org.mockito.internal.configuration.plugins.DefaultMockitoPlugins; import org.mockito.internal.configuration.plugins.Plugins; import org.mockito.internal.creation.settings.CreationSettings; import org.mockito.internal.stubbing.InvocationContainerImpl; @@ -18,6 +19,9 @@ import org.mockito.plugins.MockMaker.TypeMockability; import org.mockito.plugins.MockResolver; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import static org.mockito.internal.handler.MockHandlerFactory.createMockHandler; @@ -25,15 +29,57 @@ @SuppressWarnings("unchecked") public class MockUtil { - private static final MockMaker mockMaker = Plugins.getMockMaker(); + private static final MockMaker defaultMockMaker = Plugins.getMockMaker(); + private static final Map, MockMaker> mockMakers = + new ConcurrentHashMap<>( + Collections.singletonMap(defaultMockMaker.getClass(), defaultMockMaker)); private MockUtil() {} - public static TypeMockability typeMockabilityOf(Class type) { - return mockMaker.isTypeMockable(type); + private static MockMaker getMockMaker(String mockMaker) { + if (mockMaker == null) { + return defaultMockMaker; + } + + String typeName; + if (DefaultMockitoPlugins.MOCK_MAKER_ALIASES.contains(mockMaker)) { + typeName = DefaultMockitoPlugins.getDefaultPluginClass(mockMaker); + } else { + typeName = mockMaker; + } + + Class type; + // Using the context class loader because PluginInitializer.loadImpl is using it as well. + // Personally, I am suspicious whether the context class loader is a good choice in either + // of these cases. + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + if (loader == null) { + loader = ClassLoader.getSystemClassLoader(); + } + try { + type = loader.loadClass(typeName).asSubclass(MockMaker.class); + } catch (Exception e) { + throw new IllegalStateException("Failed to load MockMaker: " + mockMaker, e); + } + + return mockMakers.computeIfAbsent( + type, + t -> { + try { + return t.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new IllegalStateException( + "Failed to construct MockMaker: " + t.getName(), e); + } + }); + } + + public static TypeMockability typeMockabilityOf(Class type, String mockMaker) { + return getMockMaker(mockMaker).isTypeMockable(type); } public static T createMock(MockCreationSettings settings) { + MockMaker mockMaker = getMockMaker(settings.getMockMaker()); MockHandler mockHandler = createMockHandler(settings); Object spiedInstance = settings.getSpiedInstance(); @@ -62,17 +108,11 @@ public static void resetMock(Object mock) { MockHandler newHandler = createMockHandler(settings); mock = resolve(mock); - mockMaker.resetMock(mock, newHandler, settings); + getMockMaker(settings.getMockMaker()).resetMock(mock, newHandler, settings); } public static MockHandler getMockHandler(Object mock) { - if (mock == null) { - throw new NotAMockException("Argument should be a mock, but is null!"); - } - - mock = resolve(mock); - - MockHandler handler = mockMaker.getHandler(mock); + MockHandler handler = getMockHandlerOrNull(mock); if (handler != null) { return handler; } else { @@ -104,10 +144,24 @@ public static boolean isMock(Object mock) { if (mock == null) { return false; } + return getMockHandlerOrNull(mock) != null; + } + + private static MockHandler getMockHandlerOrNull(Object mock) { + if (mock == null) { + throw new NotAMockException("Argument should be a mock, but is null!"); + } mock = resolve(mock); - return mockMaker.getHandler(mock) != null; + for (MockMaker mockMaker : mockMakers.values()) { + MockHandler handler = mockMaker.getHandler(mock); + if (handler != null) { + assert getMockMaker(handler.getMockSettings().getMockMaker()) == mockMaker; + return handler; + } + } + return null; } private static Object resolve(Object mock) { @@ -143,6 +197,7 @@ public static MockCreationSettings getMockSettings(Object mock) { public static MockMaker.StaticMockControl createStaticMock( Class type, MockCreationSettings settings) { + MockMaker mockMaker = getMockMaker(settings.getMockMaker()); MockHandler handler = createMockHandler(settings); return mockMaker.createStaticMock(type, settings, handler); } @@ -153,11 +208,13 @@ public static MockMaker.ConstructionMockControl createConstructionMock( MockedConstruction.MockInitializer mockInitializer) { Function> handlerFactory = context -> createMockHandler(settingsFactory.apply(context)); - return mockMaker.createConstructionMock( + return defaultMockMaker.createConstructionMock( type, settingsFactory, handlerFactory, mockInitializer); } public static void clearAllCaches() { - mockMaker.clearAllCaches(); + for (MockMaker mockMaker : mockMakers.values()) { + mockMaker.clearAllCaches(); + } } } diff --git a/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java b/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java index 3f104d1f02..7d51a6bb1b 100644 --- a/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java +++ b/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java @@ -259,7 +259,11 @@ public int compare(Constructor constructorA, Constructor constructorB) { private int countMockableParams(Constructor constructor) { int constructorMockableParamsSize = 0; for (Class aClass : constructor.getParameterTypes()) { - if (MockUtil.typeMockabilityOf(aClass).mockable()) { + // The argResolver already knows the concrete types it can provide. + // Instead of checking for mockability, I think it would be better to + // ask the argResolver whether it can resolve this type. + // Anyway, I keep it for now to avoid breaking any existing code. + if (MockUtil.typeMockabilityOf(aClass, null).mockable()) { constructorMockableParamsSize++; } } diff --git a/src/main/java/org/mockito/mock/MockCreationSettings.java b/src/main/java/org/mockito/mock/MockCreationSettings.java index f7f0b96028..94c74558ee 100644 --- a/src/main/java/org/mockito/mock/MockCreationSettings.java +++ b/src/main/java/org/mockito/mock/MockCreationSettings.java @@ -12,6 +12,7 @@ import org.mockito.listeners.InvocationListener; import org.mockito.listeners.StubbingLookupListener; import org.mockito.listeners.VerificationStartedListener; +import org.mockito.plugins.MockMaker; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; @@ -135,4 +136,13 @@ public interface MockCreationSettings { * @since 4.6.0 */ Strictness getStrictness(); + + /** + * Returns the {@link MockMaker} which shall be used to create the mock. + * When the return value is {@code null}, the default shall be used. + * + * @see MockSettings#mockMaker(String) + * @since 4.8.0 + */ + String getMockMaker(); } diff --git a/src/main/java/org/mockito/plugins/MockMaker.java b/src/main/java/org/mockito/plugins/MockMaker.java index 93a87ef0a5..c0b1cbcd2d 100644 --- a/src/main/java/org/mockito/plugins/MockMaker.java +++ b/src/main/java/org/mockito/plugins/MockMaker.java @@ -4,6 +4,7 @@ */ package org.mockito.plugins; +import org.mockito.MockSettings; import org.mockito.MockedConstruction; import org.mockito.exceptions.base.MockitoException; import org.mockito.invocation.MockHandler; @@ -45,6 +46,19 @@ *

Note that if several mockito-extensions/org.mockito.plugins.MockMaker files exists in the classpath * Mockito will only use the first returned by the standard {@link ClassLoader#getResource} mechanism. * + *

Using the MockSettings of individual mocks

+ * + *

If you want to use a {@code MockMaker} only for a specific mock, + * you can specify it using {@link MockSettings#mockMaker(String)}.

+ *
+ *     // Use a built-in mock maker
+ *     Object mock = Mockito.mock(Object.class, Mockito.withSettings()
+ *             .mockMaker(MockMakers.INLINE));
+ *     // Or load a mock maker using a fully qualified class name
+ *     Object mock = Mockito.mock(Object.class, Mockito.withSettings()
+ *             .mockMaker("org.awesome.mockito.AwesomeMockMaker"));
+ * 
+ * * @see org.mockito.mock.MockCreationSettings * @see org.mockito.invocation.MockHandler * @since 1.9.5 diff --git a/src/test/java/org/mockito/ProgrammaticMockMakerTest.java b/src/test/java/org/mockito/ProgrammaticMockMakerTest.java new file mode 100644 index 0000000000..f03555d0e5 --- /dev/null +++ b/src/test/java/org/mockito/ProgrammaticMockMakerTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.withSettings; + +import org.junit.Test; +import org.mockito.exceptions.base.MockitoException; +import org.mockito.exceptions.verification.SmartNullPointerException; +import org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker; +import org.mockito.invocation.MockHandler; +import org.mockito.mock.MockCreationSettings; + +public final class ProgrammaticMockMakerTest { + @Test + public void test_normal_mock_uses_given_mock_maker() { + ClassWithFinalMethod inlineMock = + Mockito.mock( + ClassWithFinalMethod.class, withSettings().mockMaker(MockMakers.INLINE)); + ClassWithFinalMethod subclassMock = + Mockito.mock( + ClassWithFinalMethod.class, withSettings().mockMaker(MockMakers.SUBCLASS)); + + Mockito.when(inlineMock.finalMethodCallingNonFinal()).thenReturn("MOCKED"); + Mockito.when(subclassMock.finalMethodCallingNonFinal()).thenReturn("MOCKED"); + + assertEquals("MOCKED", inlineMock.finalMethodCallingNonFinal()); + assertEquals("ORIGINAL", subclassMock.finalMethodCallingNonFinal()); + assertEquals("MOCKED", subclassMock.nonFinal()); + } + + @Test + public void test_mockability_check_uses_given_mock_maker() { + assertNotNull(Mockito.mock(FinalClass.class, withSettings().mockMaker(MockMakers.INLINE))); + assertThrows( + MockitoException.class, + () -> + Mockito.mock( + FinalClass.class, withSettings().mockMaker(MockMakers.SUBCLASS))); + } + + @Test + public void test_deep_stups_inherit_mock_maker() { + Container inlineMock = + Mockito.mock( + Container.class, + withSettings() + .mockMaker(MockMakers.INLINE) + .defaultAnswer(Answers.RETURNS_DEEP_STUBS)); + Container subclassMock = + Mockito.mock( + Container.class, + withSettings() + .mockMaker(MockMakers.SUBCLASS) + .defaultAnswer(Answers.RETURNS_DEEP_STUBS)); + + assertNotNull(inlineMock.finalClass()); + assertNotNull(inlineMock.subContainer().finalClass()); + assertNull(inlineMock.finalClass().someMethod()); + assertNull(inlineMock.subContainer().finalClass().someMethod()); + assertNull(inlineMock.classWithFinalMethod().finalMethod()); + assertNull(inlineMock.subContainer().classWithFinalMethod().finalMethod()); + + assertNull(subclassMock.finalClass()); + assertNull(subclassMock.subContainer().finalClass()); + assertEquals("ORIGINAL", subclassMock.classWithFinalMethod().finalMethod()); + assertEquals("ORIGINAL", subclassMock.subContainer().classWithFinalMethod().finalMethod()); + } + + @Test + public void test_returned_mocks_inherit_mock_maker() { + Container inlineMock = + Mockito.mock( + Container.class, + withSettings() + .mockMaker(MockMakers.INLINE) + .defaultAnswer(Answers.RETURNS_MOCKS)); + Container subclassMock = + Mockito.mock( + Container.class, + withSettings() + .mockMaker(MockMakers.SUBCLASS) + .defaultAnswer(Answers.RETURNS_MOCKS)); + + assertNotNull(inlineMock.finalClass()); + assertNotNull(inlineMock.subContainer().finalClass()); + assertEquals("", inlineMock.finalClass().someMethod()); + assertEquals("", inlineMock.subContainer().finalClass().someMethod()); + assertEquals("", inlineMock.classWithFinalMethod().finalMethod()); + assertEquals("", inlineMock.subContainer().classWithFinalMethod().finalMethod()); + + assertNull(subclassMock.finalClass()); + assertNull(subclassMock.subContainer().finalClass()); + assertEquals("ORIGINAL", subclassMock.classWithFinalMethod().finalMethod()); + assertEquals("ORIGINAL", subclassMock.subContainer().classWithFinalMethod().finalMethod()); + } + + @Test + public void test_smart_nulls_inherit_mock_maker() { + Container inlineMock = + Mockito.mock( + Container.class, + withSettings() + .mockMaker(MockMakers.INLINE) + .defaultAnswer(Answers.RETURNS_SMART_NULLS)); + Container subclassMock = + Mockito.mock( + Container.class, + withSettings() + .mockMaker(MockMakers.SUBCLASS) + .defaultAnswer(Answers.RETURNS_SMART_NULLS)); + + assertNotNull(inlineMock.finalClass()); + assertNotNull(inlineMock.classWithFinalMethod()); + assertThrows(SmartNullPointerException.class, () -> inlineMock.finalClass().someMethod()); + assertThrows( + SmartNullPointerException.class, + () -> inlineMock.classWithFinalMethod().finalMethod()); + + assertNull(subclassMock.finalClass()); + assertNotNull(subclassMock.classWithFinalMethod()); + assertEquals("ORIGINAL", subclassMock.classWithFinalMethod().finalMethod()); + } + + @Test + public void test_custom_mock_maker() { + assertThatThrownBy( + () -> { + Mockito.mock( + Container.class, + withSettings().mockMaker(CustomMockMaker.class.getName())); + }) + .hasMessage("CUSTOM MOCK MAKER"); + } + + @Test + public void test_exception_when_mock_maker_cannot_be_instantiated() { + class InvalidMockMaker extends SubclassByteBuddyMockMaker { + // Local classes have an implicit constructor parameter, + // which makes them an invalid mock maker. + } + assertThatThrownBy( + () -> { + Mockito.mock( + Container.class, + withSettings().mockMaker(InvalidMockMaker.class.getName())); + }) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Failed to construct MockMaker") + .hasMessageContaining(InvalidMockMaker.class.getName()); + } + + private static final class FinalClass { + String someMethod() { + return "ORIGINAL"; + } + } + + private static class ClassWithFinalMethod { + final String finalMethod() { + return "ORIGINAL"; + } + + final String finalMethodCallingNonFinal() { + nonFinal(); + return "ORIGINAL"; + } + + String nonFinal() { + return "ORIGINAL"; + } + } + + private static class Container { + FinalClass finalClass() { + return new FinalClass(); + } + + ClassWithFinalMethod classWithFinalMethod() { + return new ClassWithFinalMethod(); + } + + Container subContainer() { + return new Container(); + } + } + + public static class CustomMockMaker extends SubclassByteBuddyMockMaker { + @Override + public T createMock(MockCreationSettings settings, MockHandler handler) { + throw new RuntimeException("CUSTOM MOCK MAKER"); + } + } +} diff --git a/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java b/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java index 44afe0b6a9..aa1835696e 100644 --- a/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java +++ b/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java @@ -7,6 +7,7 @@ import static org.junit.Assert.*; import static org.mockito.internal.configuration.plugins.DefaultMockitoPlugins.INLINE_ALIAS; import static org.mockito.internal.configuration.plugins.DefaultMockitoPlugins.PROXY_ALIAS; +import static org.mockito.internal.configuration.plugins.DefaultMockitoPlugins.SUBCLASS_ALIAS; import org.junit.Test; import org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker; @@ -25,11 +26,14 @@ public class DefaultMockitoPluginsTest extends TestBase { public void provides_plugins() throws Exception { assertEquals( "org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker", - plugins.getDefaultPluginClass(INLINE_ALIAS)); + DefaultMockitoPlugins.getDefaultPluginClass(INLINE_ALIAS)); assertEquals(InlineByteBuddyMockMaker.class, plugins.getInlineMockMaker().getClass()); assertEquals( "org.mockito.internal.creation.proxy.ProxyMockMaker", - plugins.getDefaultPluginClass(PROXY_ALIAS)); + DefaultMockitoPlugins.getDefaultPluginClass(PROXY_ALIAS)); + assertEquals( + "org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker", + DefaultMockitoPlugins.getDefaultPluginClass(SUBCLASS_ALIAS)); assertEquals( ByteBuddyMockMaker.class, plugins.getDefaultPlugin(MockMaker.class).getClass()); assertNotNull(plugins.getDefaultPlugin(InstantiatorProvider2.class)); diff --git a/src/test/java/org/mockito/internal/util/MockCreationValidatorTest.java b/src/test/java/org/mockito/internal/util/MockCreationValidatorTest.java index 7efffcf6a2..7491a3f070 100644 --- a/src/test/java/org/mockito/internal/util/MockCreationValidatorTest.java +++ b/src/test/java/org/mockito/internal/util/MockCreationValidatorTest.java @@ -66,7 +66,7 @@ public void should_validation_be_safe_when_nulls_passed() { @Test public void should_fail_when_type_not_mockable() { try { - validator.validateType(long.class); + validator.validateType(long.class, null); } catch (MockitoException ex) { assertThat(ex.getMessage()).contains("primitive"); } diff --git a/src/test/java/org/mockito/internal/util/MockUtilTest.java b/src/test/java/org/mockito/internal/util/MockUtilTest.java index 50cadb4c0c..834178cdec 100644 --- a/src/test/java/org/mockito/internal/util/MockUtilTest.java +++ b/src/test/java/org/mockito/internal/util/MockUtilTest.java @@ -100,12 +100,12 @@ interface SomeInterface {} @Test public void should_know_if_type_is_mockable() throws Exception { - Assertions.assertThat(MockUtil.typeMockabilityOf(FinalClass.class).mockable()) + Assertions.assertThat(MockUtil.typeMockabilityOf(FinalClass.class, null).mockable()) .isEqualTo(Plugins.getMockMaker().isTypeMockable(FinalClass.class).mockable()); - assertFalse(MockUtil.typeMockabilityOf(int.class).mockable()); + assertFalse(MockUtil.typeMockabilityOf(int.class, null).mockable()); - assertTrue(MockUtil.typeMockabilityOf(SomeClass.class).mockable()); - assertTrue(MockUtil.typeMockabilityOf(SomeInterface.class).mockable()); + assertTrue(MockUtil.typeMockabilityOf(SomeClass.class, null).mockable()); + assertTrue(MockUtil.typeMockabilityOf(SomeInterface.class, null).mockable()); } } diff --git a/src/test/java/org/mockitointegration/DeferMockMakersClassLoadingTest.java b/src/test/java/org/mockitointegration/DeferMockMakersClassLoadingTest.java new file mode 100644 index 0000000000..63c0d761bd --- /dev/null +++ b/src/test/java/org/mockitointegration/DeferMockMakersClassLoadingTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitointegration; + +import static org.mockito.Mockito.withSettings; +import static org.mockitoutil.ClassLoaders.coverageTool; + +import java.lang.reflect.Method; + +import org.assertj.core.api.Assertions; +import org.hamcrest.Matcher; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker; +import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker; +import org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker; +import org.mockito.internal.creation.proxy.ProxyMockMaker; +import org.mockito.invocation.MockHandler; +import org.mockito.mock.MockCreationSettings; +import org.mockito.plugins.MockMaker; +import org.mockitoutil.ClassLoaders; + +public class DeferMockMakersClassLoadingTest { + private static final Object MY_MOCK = new Object(); + + @Test + public void mockito_should_not_load_mock_makers_it_does_not_need() throws Exception { + ClassLoader classLoader_without_mockMakers = + ClassLoaders.excludingClassLoader() + .withCodeSourceUrlOf( + Mockito.class, + Matcher.class, + CustomMockMaker.class, + Assertions.class) + .withCodeSourceUrlOf(coverageTool()) + .without( + ByteBuddyMockMaker.class.getName(), + SubclassByteBuddyMockMaker.class.getName(), + InlineByteBuddyMockMaker.class.getName(), + ProxyMockMaker.class.getName()) + .build(); + + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(classLoader_without_mockMakers); + try { + Class self = classLoader_without_mockMakers.loadClass(getClass().getName()); + Method createMock = self.getMethod("createMock"); + createMock.invoke(null); + } finally { + Thread.currentThread().setContextClassLoader(contextClassLoader); + } + } + + // Called by reflection from the test method + public static void createMock() { + Assertions.assertThat( + Mockito.mock( + Object.class, + withSettings().mockMaker(CustomMockMaker.class.getName()))) + .isSameAs(MY_MOCK); + } + + public static class CustomMockMaker implements MockMaker { + @Override + public T createMock(MockCreationSettings settings, MockHandler handler) { + return settings.getTypeToMock().cast(MY_MOCK); + } + + @Override + public MockHandler getHandler(Object mock) { + throw new UnsupportedOperationException(); + } + + @Override + public void resetMock(Object mock, MockHandler newHandler, MockCreationSettings settings) { + throw new UnsupportedOperationException(); + } + + @Override + public TypeMockability isTypeMockable(Class type) { + return new TypeMockability() { + @Override + public boolean mockable() { + return type.equals(Object.class); + } + + @Override + public String nonMockableReason() { + return mockable() ? "" : "type != Object.class"; + } + }; + } + } +} diff --git a/src/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java b/src/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java new file mode 100644 index 0000000000..ad5f2a50ae --- /dev/null +++ b/src/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitousage.annotation; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockMakers; +import org.mockito.Mockito; +import org.mockitoutil.TestBase; + +public class ProgrammaticMockMakerAnnotationTest extends TestBase { + @Mock(mockMaker = MockMakers.INLINE) + ClassWithFinalMethod inlineMock; + + @Mock(mockMaker = MockMakers.SUBCLASS) + ClassWithFinalMethod subclassMock; + + @Test + public void test_mock_uses_given_mock_maker() { + Mockito.when(inlineMock.finalMethodCallingNonFinal()).thenReturn("MOCKED"); + Mockito.when(subclassMock.finalMethodCallingNonFinal()).thenReturn("MOCKED"); + + assertEquals("MOCKED", inlineMock.finalMethodCallingNonFinal()); + assertEquals("ORIGINAL", subclassMock.finalMethodCallingNonFinal()); + assertEquals("MOCKED", subclassMock.nonFinal()); + } + + private static class ClassWithFinalMethod { + final String finalMethodCallingNonFinal() { + nonFinal(); + return "ORIGINAL"; + } + + String nonFinal() { + return "ORIGINAL"; + } + } +} diff --git a/subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockTest.java b/subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockTest.java index 7eb87c5702..adbe3fe3ee 100644 --- a/subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockTest.java +++ b/subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockTest.java @@ -11,11 +11,13 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import java.util.Collections; import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; +import org.mockito.MockMakers; import org.mockito.MockedConstruction; import org.mockito.Mockito; import org.mockito.exceptions.base.MockitoException; @@ -154,6 +156,21 @@ public void testConstructionMockMustNotTargetAbstractClass() { .hasMessageContaining("It is not possible to construct primitive types or abstract types"); } + @Test + public void testConstructionMocksMustNotUseCustomMockMaker() { + assertThatThrownBy( + () -> { + try (MockedConstruction ignored = Mockito.mockConstruction( + Dummy.class, + withSettings().mockMaker(MockMakers.INLINE)) + ) { + new Dummy(); + } + }) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("you cannot override the MockMaker for construction mocks"); + } + static class Dummy { From 488d4b13c9d8a49ac9c30ca17eb6c7e004300440 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Sep 2022 20:54:36 +0200 Subject: [PATCH 075/160] Bump versions.bytebuddy from 1.12.14 to 1.12.16 (#2747) Bumps `versions.bytebuddy` from 1.12.14 to 1.12.16. Updates `byte-buddy` from 1.12.14 to 1.12.16 - [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.14...byte-buddy-1.12.16) Updates `byte-buddy-agent` from 1.12.14 to 1.12.16 - [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.14...byte-buddy-1.12.16) Updates `byte-buddy-android` from 1.12.14 to 1.12.16 - [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.14...byte-buddy-1.12.16) --- 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 772fe4b6c2..2c230c730c 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.12.14' +versions.bytebuddy = '1.12.16' versions.junitJupiter = '5.9.0' versions.errorprone = '2.14.0' From 185ea11ae46d814f12988176819d7557f3a1f78e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Sep 2022 20:23:42 +0200 Subject: [PATCH 076/160] Bump org.eclipse.osgi from 3.18.0 to 3.18.100 (#2751) Bumps [org.eclipse.osgi](https://github.com/eclipse-equinox/equinox) from 3.18.0 to 3.18.100. - [Release notes](https://github.com/eclipse-equinox/equinox/releases) - [Commits](https://github.com/eclipse-equinox/equinox/commits) --- updated-dependencies: - dependency-name: org.eclipse.platform:org.eclipse.osgi 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 2c230c730c..69c947524c 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -29,7 +29,7 @@ libraries.autoservice = "com.google.auto.service:auto-service:1.0.1" libraries.objenesis = 'org.objenesis:objenesis:3.2' libraries.osgi = 'org.osgi:osgi.core:8.0.0' -libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.18.0' +libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.18.100' libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.3.1' libraries.groovy = 'org.codehaus.groovy:groovy:3.0.12' From ee3679bc032e83675a1dd56651ee13248cedeb54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Sep 2022 20:34:05 +0200 Subject: [PATCH 077/160] Bump com.diffplug.spotless from 6.10.0 to 6.11.0 (#2753) Bumps com.diffplug.spotless from 6.10.0 to 6.11.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 5f520bfe27..d1ef4dc1be 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.10.0' + id 'com.diffplug.spotless' version '6.11.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.42.0' id 'biz.aQute.bnd.builder' version '6.3.1' From 0a9aa2697860096427cab0fb2f74aa940dff600f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Sep 2022 20:37:08 +0200 Subject: [PATCH 078/160] Bump groovy from 3.0.12 to 3.0.13 (#2756) Bumps [groovy](https://github.com/apache/groovy) from 3.0.12 to 3.0.13. - [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] 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 69c947524c..4f2445bd87 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.100' libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.3.1' -libraries.groovy = 'org.codehaus.groovy:groovy:3.0.12' +libraries.groovy = 'org.codehaus.groovy:groovy:3.0.13' def kotlinVersion = '1.7.10' libraries.kotlin = [ From 3507ce328f77b41a1792732e27747fb427240060 Mon Sep 17 00:00:00 2001 From: andrepaschoal Date: Mon, 17 Oct 2022 20:29:58 +0200 Subject: [PATCH 079/160] Use downloaded package-list file from Oracle for JavaDoc generation (#2766) Fixes #2765 --- gradle/mockito-core/java-8-docs/element-list | 217 +++++++++++++++++++ gradle/mockito-core/java-8-docs/package-list | 217 +++++++++++++++++++ gradle/mockito-core/javadoc.gradle | 10 +- 3 files changed, 442 insertions(+), 2 deletions(-) create mode 100644 gradle/mockito-core/java-8-docs/element-list create mode 100644 gradle/mockito-core/java-8-docs/package-list diff --git a/gradle/mockito-core/java-8-docs/element-list b/gradle/mockito-core/java-8-docs/element-list new file mode 100644 index 0000000000..351c186855 --- /dev/null +++ b/gradle/mockito-core/java-8-docs/element-list @@ -0,0 +1,217 @@ +java.applet +java.awt +java.awt.color +java.awt.datatransfer +java.awt.dnd +java.awt.event +java.awt.font +java.awt.geom +java.awt.im +java.awt.im.spi +java.awt.image +java.awt.image.renderable +java.awt.print +java.beans +java.beans.beancontext +java.io +java.lang +java.lang.annotation +java.lang.instrument +java.lang.invoke +java.lang.management +java.lang.ref +java.lang.reflect +java.math +java.net +java.nio +java.nio.channels +java.nio.channels.spi +java.nio.charset +java.nio.charset.spi +java.nio.file +java.nio.file.attribute +java.nio.file.spi +java.rmi +java.rmi.activation +java.rmi.dgc +java.rmi.registry +java.rmi.server +java.security +java.security.acl +java.security.cert +java.security.interfaces +java.security.spec +java.sql +java.text +java.text.spi +java.time +java.time.chrono +java.time.format +java.time.temporal +java.time.zone +java.util +java.util.concurrent +java.util.concurrent.atomic +java.util.concurrent.locks +java.util.function +java.util.jar +java.util.logging +java.util.prefs +java.util.regex +java.util.spi +java.util.stream +java.util.zip +javax.accessibility +javax.activation +javax.activity +javax.annotation +javax.annotation.processing +javax.crypto +javax.crypto.interfaces +javax.crypto.spec +javax.imageio +javax.imageio.event +javax.imageio.metadata +javax.imageio.plugins.bmp +javax.imageio.plugins.jpeg +javax.imageio.spi +javax.imageio.stream +javax.jws +javax.jws.soap +javax.lang.model +javax.lang.model.element +javax.lang.model.type +javax.lang.model.util +javax.management +javax.management.loading +javax.management.modelmbean +javax.management.monitor +javax.management.openmbean +javax.management.relation +javax.management.remote +javax.management.remote.rmi +javax.management.timer +javax.naming +javax.naming.directory +javax.naming.event +javax.naming.ldap +javax.naming.spi +javax.net +javax.net.ssl +javax.print +javax.print.attribute +javax.print.attribute.standard +javax.print.event +javax.rmi +javax.rmi.CORBA +javax.rmi.ssl +javax.script +javax.security.auth +javax.security.auth.callback +javax.security.auth.kerberos +javax.security.auth.login +javax.security.auth.spi +javax.security.auth.x500 +javax.security.cert +javax.security.sasl +javax.sound.midi +javax.sound.midi.spi +javax.sound.sampled +javax.sound.sampled.spi +javax.sql +javax.sql.rowset +javax.sql.rowset.serial +javax.sql.rowset.spi +javax.swing +javax.swing.border +javax.swing.colorchooser +javax.swing.event +javax.swing.filechooser +javax.swing.plaf +javax.swing.plaf.basic +javax.swing.plaf.metal +javax.swing.plaf.multi +javax.swing.plaf.nimbus +javax.swing.plaf.synth +javax.swing.table +javax.swing.text +javax.swing.text.html +javax.swing.text.html.parser +javax.swing.text.rtf +javax.swing.tree +javax.swing.undo +javax.tools +javax.transaction +javax.transaction.xa +javax.xml +javax.xml.bind +javax.xml.bind.annotation +javax.xml.bind.annotation.adapters +javax.xml.bind.attachment +javax.xml.bind.helpers +javax.xml.bind.util +javax.xml.crypto +javax.xml.crypto.dom +javax.xml.crypto.dsig +javax.xml.crypto.dsig.dom +javax.xml.crypto.dsig.keyinfo +javax.xml.crypto.dsig.spec +javax.xml.datatype +javax.xml.namespace +javax.xml.parsers +javax.xml.soap +javax.xml.stream +javax.xml.stream.events +javax.xml.stream.util +javax.xml.transform +javax.xml.transform.dom +javax.xml.transform.sax +javax.xml.transform.stax +javax.xml.transform.stream +javax.xml.validation +javax.xml.ws +javax.xml.ws.handler +javax.xml.ws.handler.soap +javax.xml.ws.http +javax.xml.ws.soap +javax.xml.ws.spi +javax.xml.ws.spi.http +javax.xml.ws.wsaddressing +javax.xml.xpath +org.ietf.jgss +org.omg.CORBA +org.omg.CORBA.DynAnyPackage +org.omg.CORBA.ORBPackage +org.omg.CORBA.TypeCodePackage +org.omg.CORBA.portable +org.omg.CORBA_2_3 +org.omg.CORBA_2_3.portable +org.omg.CosNaming +org.omg.CosNaming.NamingContextExtPackage +org.omg.CosNaming.NamingContextPackage +org.omg.Dynamic +org.omg.DynamicAny +org.omg.DynamicAny.DynAnyFactoryPackage +org.omg.DynamicAny.DynAnyPackage +org.omg.IOP +org.omg.IOP.CodecFactoryPackage +org.omg.IOP.CodecPackage +org.omg.Messaging +org.omg.PortableInterceptor +org.omg.PortableInterceptor.ORBInitInfoPackage +org.omg.PortableServer +org.omg.PortableServer.CurrentPackage +org.omg.PortableServer.POAManagerPackage +org.omg.PortableServer.POAPackage +org.omg.PortableServer.ServantLocatorPackage +org.omg.PortableServer.portable +org.omg.SendingContext +org.omg.stub.java.rmi +org.w3c.dom +org.w3c.dom.bootstrap +org.w3c.dom.events +org.w3c.dom.ls +org.w3c.dom.views +org.xml.sax +org.xml.sax.ext +org.xml.sax.helpers diff --git a/gradle/mockito-core/java-8-docs/package-list b/gradle/mockito-core/java-8-docs/package-list new file mode 100644 index 0000000000..351c186855 --- /dev/null +++ b/gradle/mockito-core/java-8-docs/package-list @@ -0,0 +1,217 @@ +java.applet +java.awt +java.awt.color +java.awt.datatransfer +java.awt.dnd +java.awt.event +java.awt.font +java.awt.geom +java.awt.im +java.awt.im.spi +java.awt.image +java.awt.image.renderable +java.awt.print +java.beans +java.beans.beancontext +java.io +java.lang +java.lang.annotation +java.lang.instrument +java.lang.invoke +java.lang.management +java.lang.ref +java.lang.reflect +java.math +java.net +java.nio +java.nio.channels +java.nio.channels.spi +java.nio.charset +java.nio.charset.spi +java.nio.file +java.nio.file.attribute +java.nio.file.spi +java.rmi +java.rmi.activation +java.rmi.dgc +java.rmi.registry +java.rmi.server +java.security +java.security.acl +java.security.cert +java.security.interfaces +java.security.spec +java.sql +java.text +java.text.spi +java.time +java.time.chrono +java.time.format +java.time.temporal +java.time.zone +java.util +java.util.concurrent +java.util.concurrent.atomic +java.util.concurrent.locks +java.util.function +java.util.jar +java.util.logging +java.util.prefs +java.util.regex +java.util.spi +java.util.stream +java.util.zip +javax.accessibility +javax.activation +javax.activity +javax.annotation +javax.annotation.processing +javax.crypto +javax.crypto.interfaces +javax.crypto.spec +javax.imageio +javax.imageio.event +javax.imageio.metadata +javax.imageio.plugins.bmp +javax.imageio.plugins.jpeg +javax.imageio.spi +javax.imageio.stream +javax.jws +javax.jws.soap +javax.lang.model +javax.lang.model.element +javax.lang.model.type +javax.lang.model.util +javax.management +javax.management.loading +javax.management.modelmbean +javax.management.monitor +javax.management.openmbean +javax.management.relation +javax.management.remote +javax.management.remote.rmi +javax.management.timer +javax.naming +javax.naming.directory +javax.naming.event +javax.naming.ldap +javax.naming.spi +javax.net +javax.net.ssl +javax.print +javax.print.attribute +javax.print.attribute.standard +javax.print.event +javax.rmi +javax.rmi.CORBA +javax.rmi.ssl +javax.script +javax.security.auth +javax.security.auth.callback +javax.security.auth.kerberos +javax.security.auth.login +javax.security.auth.spi +javax.security.auth.x500 +javax.security.cert +javax.security.sasl +javax.sound.midi +javax.sound.midi.spi +javax.sound.sampled +javax.sound.sampled.spi +javax.sql +javax.sql.rowset +javax.sql.rowset.serial +javax.sql.rowset.spi +javax.swing +javax.swing.border +javax.swing.colorchooser +javax.swing.event +javax.swing.filechooser +javax.swing.plaf +javax.swing.plaf.basic +javax.swing.plaf.metal +javax.swing.plaf.multi +javax.swing.plaf.nimbus +javax.swing.plaf.synth +javax.swing.table +javax.swing.text +javax.swing.text.html +javax.swing.text.html.parser +javax.swing.text.rtf +javax.swing.tree +javax.swing.undo +javax.tools +javax.transaction +javax.transaction.xa +javax.xml +javax.xml.bind +javax.xml.bind.annotation +javax.xml.bind.annotation.adapters +javax.xml.bind.attachment +javax.xml.bind.helpers +javax.xml.bind.util +javax.xml.crypto +javax.xml.crypto.dom +javax.xml.crypto.dsig +javax.xml.crypto.dsig.dom +javax.xml.crypto.dsig.keyinfo +javax.xml.crypto.dsig.spec +javax.xml.datatype +javax.xml.namespace +javax.xml.parsers +javax.xml.soap +javax.xml.stream +javax.xml.stream.events +javax.xml.stream.util +javax.xml.transform +javax.xml.transform.dom +javax.xml.transform.sax +javax.xml.transform.stax +javax.xml.transform.stream +javax.xml.validation +javax.xml.ws +javax.xml.ws.handler +javax.xml.ws.handler.soap +javax.xml.ws.http +javax.xml.ws.soap +javax.xml.ws.spi +javax.xml.ws.spi.http +javax.xml.ws.wsaddressing +javax.xml.xpath +org.ietf.jgss +org.omg.CORBA +org.omg.CORBA.DynAnyPackage +org.omg.CORBA.ORBPackage +org.omg.CORBA.TypeCodePackage +org.omg.CORBA.portable +org.omg.CORBA_2_3 +org.omg.CORBA_2_3.portable +org.omg.CosNaming +org.omg.CosNaming.NamingContextExtPackage +org.omg.CosNaming.NamingContextPackage +org.omg.Dynamic +org.omg.DynamicAny +org.omg.DynamicAny.DynAnyFactoryPackage +org.omg.DynamicAny.DynAnyPackage +org.omg.IOP +org.omg.IOP.CodecFactoryPackage +org.omg.IOP.CodecPackage +org.omg.Messaging +org.omg.PortableInterceptor +org.omg.PortableInterceptor.ORBInitInfoPackage +org.omg.PortableServer +org.omg.PortableServer.CurrentPackage +org.omg.PortableServer.POAManagerPackage +org.omg.PortableServer.POAPackage +org.omg.PortableServer.ServantLocatorPackage +org.omg.PortableServer.portable +org.omg.SendingContext +org.omg.stub.java.rmi +org.w3c.dom +org.w3c.dom.bootstrap +org.w3c.dom.events +org.w3c.dom.ls +org.w3c.dom.views +org.xml.sax +org.xml.sax.ext +org.xml.sax.helpers diff --git a/gradle/mockito-core/javadoc.gradle b/gradle/mockito-core/javadoc.gradle index 3d42d21e1f..b4334b1b2a 100644 --- a/gradle/mockito-core/javadoc.gradle +++ b/gradle/mockito-core/javadoc.gradle @@ -1,5 +1,11 @@ //It seems the gradle javadoc task works file by file and as such disable some features of javadoc tool //such as link to packages, https://groups.google.com/d/msg/gradle-dev/R83dy_6PHMc/bgw0cUTMFAAJ + +// In this directory we keep two copies of the `package-list` file for java 8 api docs: +// - as `package-list` for Java 9+ +// - as `element-list` for java 8 +def java8DocsDir = 'gradle/mockito-core/java-8-docs' + javadoc { description "Creates javadoc html for Mockito API." @@ -38,8 +44,8 @@ javadoc { options.noIndex = false options.noNavBar = false options.noTree = false - options.links = ['https://docs.oracle.com/javase/6/docs/api/', - 'https://junit.org/junit4/javadoc/4.12/'] + options.links('https://junit.org/junit4/javadoc/4.12/') + options.linksOffline('https://docs.oracle.com/javase/8/docs/api/', java8DocsDir) options.bottom(""" From 4d14d975cea51bc90d9d93c94e01cefdbaefeb67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 21:30:29 +0200 Subject: [PATCH 080/160] Bump versions.junitJupiter from 5.9.0 to 5.9.1 (#2758) Bumps `versions.junitJupiter` from 5.9.0 to 5.9.1. Updates `junit-jupiter-api` from 5.9.0 to 5.9.1 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) Updates `junit-jupiter-engine` from 5.9.0 to 5.9.1 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) Updates `junit-vintage-engine` from 5.9.0 to 5.9.1 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.vintage:junit-vintage-engine 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 4f2445bd87..fc9a324ea2 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -5,7 +5,7 @@ ext { def versions = [:] versions.bytebuddy = '1.12.16' -versions.junitJupiter = '5.9.0' +versions.junitJupiter = '5.9.1' versions.errorprone = '2.14.0' libraries.junit4 = 'junit:junit:4.13.2' From fe7dca265190a6ae3040e4938fb25600a09c669f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Oct 2022 09:24:35 +0200 Subject: [PATCH 081/160] Bump junit-platform-launcher from 1.9.0 to 1.9.1 (#2768) Bumps [junit-platform-launcher](https://github.com/junit-team/junit5) from 1.9.0 to 1.9.1. - [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-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 fc9a324ea2..a982cfd10f 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.9.0' +libraries.junitPlatformLauncher = 'org.junit.platform:junit-platform-launcher:1.9.1' 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 f512a7694097b46b18394d89173506cf59d071ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Oct 2022 20:50:28 +0200 Subject: [PATCH 082/160] Bump gradle-errorprone-plugin from 2.0.2 to 3.0.1 (#2770) Bumps gradle-errorprone-plugin from 2.0.2 to 3.0.1. --- updated-dependencies: - dependency-name: net.ltgt.gradle:gradle-errorprone-plugin dependency-type: direct:production update-type: version-update:semver-major ... 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 d1ef4dc1be..8262fb77b6 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { dependencies { classpath 'gradle.plugin.com.hierynomus.gradle.plugins:license-gradle-plugin:0.16.1' - classpath 'net.ltgt.gradle:gradle-errorprone-plugin:2.0.2' + classpath 'net.ltgt.gradle:gradle-errorprone-plugin:3.0.1' classpath "io.github.gradle-nexus:publish-plugin:1.1.0" classpath 'org.shipkit:shipkit-changelog:1.2.0' From fcb4cf7bf3f38b0135b50bdc930d7532c7168356 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 10:07:29 +0200 Subject: [PATCH 083/160] Bump gradle/wrapper-validation-action from 1.0.4 to 1.0.5 (#2775) Bumps [gradle/wrapper-validation-action](https://github.com/gradle/wrapper-validation-action) from 1.0.4 to 1.0.5. - [Release notes](https://github.com/gradle/wrapper-validation-action/releases) - [Commits](https://github.com/gradle/wrapper-validation-action/compare/v1.0.4...v1.0.5) --- updated-dependencies: - dependency-name: gradle/wrapper-validation-action 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> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23975b8d29..bc0694a891 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: - name: 3. Validate Gradle wrapper if: matrix.java == 8 && matrix.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB - uses: gradle/wrapper-validation-action@v1.0.4 # https://github.com/gradle/wrapper-validation-action + uses: gradle/wrapper-validation-action@v1.0.5 # https://github.com/gradle/wrapper-validation-action - name: 4. Build and check reproducibility of artifacts (single job only) if: matrix.java == 8 && matrix.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB From eb85518cef362e39788a7e030bab8ac78eca76c9 Mon Sep 17 00:00:00 2001 From: Andrei Solntsev Date: Fri, 28 Oct 2022 09:04:53 +0300 Subject: [PATCH 084/160] Update gradle to 7.5.1 (#2776) --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 93a3251ccf..b916c04dbb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=9afb3ca688fc12c761a0e9e4321e4d24e977a4a8916c8a768b1fe05ddb4d6b66 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip +distributionSha256Sum=f6b8596b10cce501591e92f229816aa4046424f3b24d771751b06779d58c8ec4 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 47045cbdfe606d8616f7e0a814af6e7803d4c33f Mon Sep 17 00:00:00 2001 From: Rick Ossendrijver Date: Thu, 3 Nov 2022 23:40:58 +0100 Subject: [PATCH 085/160] Upgrade objenesis 3.2 -> 3.3 (#2784) See: - http://objenesis.org/notes.html - https://github.com/easymock/objenesis/releases/tag/3.3 - https://github.com/easymock/objenesis/compare/3.2...3.3 Fixes #2783 --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index a982cfd10f..6928e86a3c 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -26,7 +26,7 @@ libraries.errorproneTestApi = "com.google.errorprone:error_prone_test_helpers:${ libraries.autoservice = "com.google.auto.service:auto-service:1.0.1" -libraries.objenesis = 'org.objenesis:objenesis:3.2' +libraries.objenesis = 'org.objenesis:objenesis:3.3' libraries.osgi = 'org.osgi:osgi.core:8.0.0' libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.18.100' From 0052e2f5f78ca2eda4593df158e1c2366639e5db Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Fri, 11 Nov 2022 22:45:40 +0100 Subject: [PATCH 086/160] Avoid clearing stale weak entries from critical code segments (#2780) This reduces the probability of checking for mocked from from the mock-checking code what can lead to infinitive loops. --- .../creation/bytebuddy/InlineBytecodeGenerator.java | 7 +++++-- .../bytebuddy/InlineDelegateByteBuddyMockMaker.java | 10 +++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java index 72ab81adbd..ed12d2ffe2 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java @@ -94,8 +94,8 @@ public InlineBytecodeGenerator( .with(Implementation.Context.Disabled.Factory.INSTANCE) .with(MethodGraph.Compiler.ForDeclaredMethods.INSTANCE) .ignore(isSynthetic().and(not(isConstructor())).or(isDefaultFinalizer())); - mocked = new WeakConcurrentSet<>(WeakConcurrentSet.Cleaner.INLINE); - flatMocked = new WeakConcurrentSet<>(WeakConcurrentSet.Cleaner.INLINE); + mocked = new WeakConcurrentSet<>(WeakConcurrentSet.Cleaner.MANUAL); + flatMocked = new WeakConcurrentSet<>(WeakConcurrentSet.Cleaner.MANUAL); String identifier = RandomString.make(); subclassEngine = new TypeCachingBytecodeGenerator( @@ -299,6 +299,9 @@ private void triggerRetransformation(Set> types, boolean flat) { lastException = null; } } + + mocked.expungeStaleEntries(); + flatMocked.expungeStaleEntries(); } private void assureCanReadMockito(Set> types) { diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java index 1a19a92732..05512b8409 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java @@ -192,13 +192,13 @@ class InlineDelegateByteBuddyMockMaker private final BytecodeGenerator bytecodeGenerator; private final WeakConcurrentMap mocks = - new WeakConcurrentMap.WithInlinedExpunction(); + new WeakConcurrentMap<>(false); private final DetachedThreadLocal, MockMethodInterceptor>> mockedStatics = - new DetachedThreadLocal<>(DetachedThreadLocal.Cleaner.INLINE); + new DetachedThreadLocal<>(DetachedThreadLocal.Cleaner.MANUAL); private final DetachedThreadLocal, BiConsumer>> - mockedConstruction = new DetachedThreadLocal<>(DetachedThreadLocal.Cleaner.INLINE); + mockedConstruction = new DetachedThreadLocal<>(DetachedThreadLocal.Cleaner.MANUAL); private final ThreadLocal mockitoConstruction = ThreadLocal.withInitial(() -> false); @@ -382,6 +382,7 @@ private T doCreateMock( if (instance instanceof MockAccess) { ((MockAccess) instance).setMockitoInterceptor(mockMethodInterceptor); } + mocks.expungeStaleEntries(); return instance; } catch (InstantiationException e) { throw new MockitoException( @@ -496,6 +497,7 @@ public void resetMock(Object mock, MockHandler newHandler, MockCreationSettings if (mock instanceof MockAccess) { ((MockAccess) mock).setMockitoInterceptor(mockMethodInterceptor); } + mocks.expungeStaleEntries(); } } @@ -570,6 +572,7 @@ public StaticMockControl createStaticMock( interceptors = new WeakHashMap<>(); mockedStatics.set(interceptors); } + mockedStatics.getBackingMap().expungeStaleEntries(); return new InlineStaticMockControl<>(type, interceptors, settings, handler); } @@ -598,6 +601,7 @@ public ConstructionMockControl createConstructionMock( interceptors = new WeakHashMap<>(); mockedConstruction.set(interceptors); } + mockedConstruction.getBackingMap().expungeStaleEntries(); return new InlineConstructionMockControl<>( type, settingsFactory, handlerFactory, mockInitializer, interceptors); From 3faa00299a8c0793cf17ce072229f2e8ffea4579 Mon Sep 17 00:00:00 2001 From: Andrei Solntsev Date: Mon, 14 Nov 2022 22:47:35 +0200 Subject: [PATCH 087/160] Add `Mockito.{mock,spy}` that take no `Class` argument (#2779) This commit adds method `Mockito.mock()` as a shorter alternative for `Mockito.mock(Class)`. When the result of this call is assigned to a variable/field with an explicit type, java will detect the needed class automatically. Co-authored-by: Tim van der Lippe --- src/main/java/org/mockito/Mockito.java | 60 ++++++++++++++++++- src/test/java/org/mockito/MockitoTest.java | 40 +++++++++++++ .../org/mockito/kotlin/InlineClassTest.kt | 10 ++++ 3 files changed, 109 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index a221376287..31eed046cf 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -33,7 +33,11 @@ import org.mockito.stubbing.OngoingStubbing; import org.mockito.stubbing.Stubber; import org.mockito.stubbing.VoidAnswer1; -import org.mockito.verification.*; +import org.mockito.verification.After; +import org.mockito.verification.Timeout; +import org.mockito.verification.VerificationAfterDelay; +import org.mockito.verification.VerificationMode; +import org.mockito.verification.VerificationWithTimeout; import java.util.function.Function; @@ -107,6 +111,7 @@ * 51. New API for marking classes as unmockable (Since 4.1.0)
* 52. New strictness attribute for @Mock annotation and MockSettings.strictness() methods (Since 4.6.0)
* 53. Specifying mock maker for individual mocks (Since 4.8.0)
+ * 54. Mocking/spying without specifying class (Since 4.9.0)
* * *

0. Migrating to Mockito 2

@@ -1639,6 +1644,23 @@ * Foo mock = Mockito.mock(Foo.class, withSettings().mockMaker(MockMakers.SUBCLASS)); * * + *

54. + * Mocking/spying without specifying class (Since 4.9.0)

+ * + * Instead of calling method {@link Mockito#mock(Class)} or {@link Mockito#spy(Class)} with Class parameter, you can now + * now call method {@code mock()} or {@code spy()} without parameters: + * + *

+ *   Foo foo = Mockito.mock();
+ *   Bar bar = Mockito.spy();
+ * 
+ * + * Mockito will automatically detect the needed class. + *

+ * It works only if you assign result of {@code mock()} or {@code spy()} to a variable or field with an explicit type. + * With an implicit type, the Java compiler is unable to automatically determine the type of a mock and you need + * to pass in the {@code Class} explicitly. + *

*/ @CheckReturnValue @SuppressWarnings("unchecked") @@ -1901,6 +1923,23 @@ public class Mockito extends ArgumentMatchers { */ public static final Answer RETURNS_SELF = Answers.RETURNS_SELF; + /** + * Creates mock object of requested class or interface. + *

+ * See examples in javadoc for {@link Mockito} class + * + * @param reified don't pass any values to it. It's a trick to detect the class/interface you want to mock. + * @return mock object + * @since 4.9.0 + */ + public static T mock(T... reified) { + if (reified.length > 0) { + throw new IllegalArgumentException( + "Please don't pass any values here. Java will detect class automagically."); + } + return mock(getClassOf(reified), withSettings()); + } + /** * Creates mock object of given class or interface. *

@@ -2115,6 +2154,25 @@ public static T spy(Class classToSpy) { classToSpy, withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS)); } + /** + * Please refer to the documentation of {@link #spy(Class)}. + * + * @param reified don't pass any values to it. It's a trick to detect the class/interface you want to mock. + * @return spy object + * @since 4.9.0 + */ + public static T spy(T... reified) { + if (reified.length > 0) { + throw new IllegalArgumentException( + "Please don't pass any values here. Java will detect class automagically."); + } + return spy(getClassOf(reified)); + } + + private static Class getClassOf(T[] array) { + return (Class) array.getClass().getComponentType(); + } + /** * Creates a thread-local mock controller for all static methods of the given class or interface. * The returned object's {@link MockedStatic#close()} method must be called upon completing the diff --git a/src/test/java/org/mockito/MockitoTest.java b/src/test/java/org/mockito/MockitoTest.java index 8455011bb2..168acb8f40 100644 --- a/src/test/java/org/mockito/MockitoTest.java +++ b/src/test/java/org/mockito/MockitoTest.java @@ -4,6 +4,7 @@ */ package org.mockito; +import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.times; @@ -133,4 +134,43 @@ public void shouldStartingMockSettingsContainDefaultBehavior() { // when / then assertThat(settings.getDefaultAnswer()).isEqualTo(Mockito.RETURNS_DEFAULTS); } + + @Test + @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"}) + public void automaticallyDetectsClassToMock() { + List mock = Mockito.mock(); + Mockito.when(mock.size()).thenReturn(42); + assertThat(mock.size()).isEqualTo(42); + } + + @Test + @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"}) + public void newMockMethod_shouldNotBeCalledWithParameters() { + assertThatThrownBy( + () -> { + Mockito.mock(asList("1", "2"), asList("3", "4")); + }) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("Please don't pass any values here"); + } + + @Test + @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"}) + public void automaticallyDetectsClassToSpy() { + List mock = Mockito.spy(); + Mockito.when(mock.size()).thenReturn(42); + assertThat(mock.size()).isEqualTo(42); + assertThat(mock.get(0)).isNull(); + } + + @Test + @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"}) + public void newSpyMethod_shouldNotBeCalledWithParameters() { + assertThatThrownBy( + () -> { + Mockito.spy(asList("1", "2"), asList("3", "4")); + }) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("Please don't pass any values here"); + } } diff --git a/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt index a1a64ce0b0..daa60a9a9a 100644 --- a/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt +++ b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt @@ -339,4 +339,14 @@ class InlineClassTest { verify(mock).returnsResult() } + + @Test + @SuppressWarnings("DoNotMock", "DoNotMockAutoValue") + fun automaticallyDetectsClassToMock() { + val mock: WithResult = mock() + + `when`(mock.returnsResult()).thenReturn(Result.success("OK")) + + assertEquals("OK", mock.returnsResult().getOrNull()) + } } From 544d0b6d90e1f89a52ad503b16ee797b5b55975f Mon Sep 17 00:00:00 2001 From: Christopher Lambert Date: Tue, 22 Nov 2022 20:32:23 +0100 Subject: [PATCH 088/160] Upgrade errorprone from 2.14.0 to 2.16 (#2794) --- gradle/dependencies.gradle | 2 +- .../mockitousage/stubbing/StubbingWithCustomAnswerTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 6928e86a3c..7acca469c5 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -6,7 +6,7 @@ def versions = [:] versions.bytebuddy = '1.12.16' versions.junitJupiter = '5.9.1' -versions.errorprone = '2.14.0' +versions.errorprone = '2.16' libraries.junit4 = 'junit:junit:4.13.2' libraries.junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}" diff --git a/src/test/java/org/mockitousage/stubbing/StubbingWithCustomAnswerTest.java b/src/test/java/org/mockitousage/stubbing/StubbingWithCustomAnswerTest.java index 5591bc47b5..dada3d5710 100644 --- a/src/test/java/org/mockitousage/stubbing/StubbingWithCustomAnswerTest.java +++ b/src/test/java/org/mockitousage/stubbing/StubbingWithCustomAnswerTest.java @@ -7,7 +7,6 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; -import java.lang.reflect.Method; import java.util.Set; import org.junit.Test; @@ -110,7 +109,8 @@ public void shouldMakeSureTheInterfaceDoesNotChange() throws Exception { new Answer() { public String answer(InvocationOnMock invocation) throws Throwable { assertTrue(invocation.getArguments().getClass().isArray()); - assertEquals(Method.class, invocation.getMethod().getClass()); + assertEquals( + IMethods.class, invocation.getMethod().getDeclaringClass()); return "assertions passed"; } From 408697046c02caf69e4cd173058e867e66ff4672 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Nov 2022 19:32:26 +0100 Subject: [PATCH 089/160] Bump versions.bytebuddy from 1.12.16 to 1.12.19 (#2799) Bumps `versions.bytebuddy` from 1.12.16 to 1.12.19. Updates `byte-buddy` from 1.12.16 to 1.12.19 - [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.16...byte-buddy-1.12.19) Updates `byte-buddy-agent` from 1.12.16 to 1.12.19 - [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.16...byte-buddy-1.12.19) Updates `byte-buddy-android` from 1.12.16 to 1.12.19 - [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.16...byte-buddy-1.12.19) --- 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 7acca469c5..56207681d9 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.12.16' +versions.bytebuddy = '1.12.19' versions.junitJupiter = '5.9.1' versions.errorprone = '2.16' From a8af0426e5e09a6d5ba8786af185f71dc7591bfc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Nov 2022 21:50:07 +0100 Subject: [PATCH 090/160] Bump espresso-core from 3.4.0 to 3.5.0 (#2800) Bumps espresso-core from 3.4.0 to 3.5.0. --- updated-dependencies: - dependency-name: androidx.test.espresso:espresso-core 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> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 56207681d9..8e37256bb2 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -47,5 +47,5 @@ libraries.android = [ compat: 'androidx.appcompat:appcompat:1.4.2', material: 'com.google.android.material:material:1.6.1', junit: 'androidx.test.ext:junit:1.1.3', - espresso: 'androidx.test.espresso:espresso-core:3.4.0', + espresso: 'androidx.test.espresso:espresso-core:3.5.0', ] From 0ce902af967cca91cb9ff2beaac95e44406bd175 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Nov 2022 21:37:46 +0100 Subject: [PATCH 091/160] Bump kotlinVersion from 1.7.10 to 1.7.21 (#2801) Bumps `kotlinVersion` from 1.7.10 to 1.7.21. Updates `kotlin-gradle-plugin` from 1.7.10 to 1.7.21 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.7.10...v1.7.21) Updates `kotlin-stdlib` from 1.7.10 to 1.7.21 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.7.10...v1.7.21) --- 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] 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 8262fb77b6..d55818690a 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.10" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.21" } } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 8e37256bb2..8060abe937 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.13' -def kotlinVersion = '1.7.10' +def kotlinVersion = '1.7.21' libraries.kotlin = [ version: kotlinVersion, From 8f4af189a9f98190c1ead3aff630e05cda721a89 Mon Sep 17 00:00:00 2001 From: Andy Coates <8012398+big-andy-coates@users.noreply.github.com> Date: Mon, 28 Nov 2022 12:45:37 +0000 Subject: [PATCH 092/160] Simplify `MatcherApplicationStrategy` (#2803) Prep work for https://github.com/mockito/mockito/issues/2796 The class is overly complex, with the precomputed `matchingType` adding to value. --- .../MatcherApplicationStrategy.java | 80 +++++++------------ .../MatcherApplicationStrategyTest.java | 11 +-- 2 files changed, 35 insertions(+), 56 deletions(-) diff --git a/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java b/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java index acc7382e2c..5e16f27040 100644 --- a/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java +++ b/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java @@ -4,10 +4,6 @@ */ package org.mockito.internal.invocation; -import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS; -import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.MATCH_EACH_VARARGS_WITH_LAST_MATCHER; -import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ONE_MATCHER_PER_ARGUMENT; - import java.util.ArrayList; import java.util.List; @@ -20,22 +16,12 @@ public class MatcherApplicationStrategy { private final Invocation invocation; - private final List> matchers; - private final MatcherApplicationType matchingType; + private final List> matchers; private MatcherApplicationStrategy( - Invocation invocation, - List> matchers, - MatcherApplicationType matchingType) { + Invocation invocation, List> matchers) { this.invocation = invocation; - if (matchingType == MATCH_EACH_VARARGS_WITH_LAST_MATCHER) { - int times = varargLength(invocation); - this.matchers = appendLastMatcherNTimes(matchers, times); - } else { - this.matchers = matchers; - } - - this.matchingType = matchingType; + this.matchers = matchers; } /** @@ -51,10 +37,8 @@ private MatcherApplicationStrategy( * @return never null */ public static MatcherApplicationStrategy getMatcherApplicationStrategyFor( - Invocation invocation, List> matchers) { - - MatcherApplicationType type = getMatcherApplicationType(invocation, matchers); - return new MatcherApplicationStrategy(invocation, matchers, type); + Invocation invocation, List> matchers) { + return new MatcherApplicationStrategy(invocation, matchers); } /** @@ -74,11 +58,28 @@ public static MatcherApplicationStrategy getMatcherApplicationStrategyFor( * */ public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) { - if (matchingType == ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS) { - return false; + if (invocation.getArguments().length == matchers.size()) { + return argsMatch(invocation.getArguments(), matchers, action); } - Object[] arguments = invocation.getArguments(); + final boolean isVararg = + invocation.getMethod().isVarArgs() + && invocation.getRawArguments().length == matchers.size() + && isLastMatcherVarargMatcher(matchers); + + if (isVararg) { + int times = varargLength(invocation); + final List> matchers = appendLastMatcherNTimes(times); + return argsMatch(invocation.getArguments(), matchers, action); + } + + return false; + } + + private boolean argsMatch( + Object[] arguments, + List> matchers, + ArgumentMatcherAction action) { for (int i = 0; i < arguments.length; i++) { ArgumentMatcher matcher = matchers.get(i); Object argument = arguments[i]; @@ -90,24 +91,7 @@ public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) { return true; } - private static MatcherApplicationType getMatcherApplicationType( - Invocation invocation, List> matchers) { - final int rawArguments = invocation.getRawArguments().length; - final int expandedArguments = invocation.getArguments().length; - final int matcherCount = matchers.size(); - - if (expandedArguments == matcherCount) { - return ONE_MATCHER_PER_ARGUMENT; - } - - if (rawArguments == matcherCount && isLastMatcherVarargMatcher(matchers)) { - return MATCH_EACH_VARARGS_WITH_LAST_MATCHER; - } - - return ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS; - } - - private static boolean isLastMatcherVarargMatcher(final List> matchers) { + private static boolean isLastMatcherVarargMatcher(List> matchers) { ArgumentMatcher argumentMatcher = lastMatcher(matchers); if (argumentMatcher instanceof HamcrestArgumentMatcher) { return ((HamcrestArgumentMatcher) argumentMatcher).isVarargMatcher(); @@ -115,8 +99,8 @@ private static boolean isLastMatcherVarargMatcher(final List> return argumentMatcher instanceof VarargMatcher; } - private static List> appendLastMatcherNTimes( - List> matchers, int timesToAppendLastMatcher) { + private List> appendLastMatcherNTimes( + int timesToAppendLastMatcher) { ArgumentMatcher lastMatcher = lastMatcher(matchers); List> expandedMatchers = new ArrayList>(matchers); @@ -132,13 +116,7 @@ private static int varargLength(Invocation invocation) { return expandedArgumentCount - rawArgumentCount; } - private static ArgumentMatcher lastMatcher(List> matchers) { + private static ArgumentMatcher lastMatcher(List> matchers) { return matchers.get(matchers.size() - 1); } - - enum MatcherApplicationType { - ONE_MATCHER_PER_ARGUMENT, - MATCH_EACH_VARARGS_WITH_LAST_MATCHER, - ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS; - } } diff --git a/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java b/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java index 285bfecc66..baf7a5ad5a 100644 --- a/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java +++ b/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java @@ -35,7 +35,7 @@ public class MatcherApplicationStrategyTest extends TestBase { @Mock IMethods mock; private Invocation invocation; - private List matchers; + private List> matchers; private RecordingAction recordAction; @@ -213,7 +213,8 @@ public void shouldMatchAnyEvenIfMatcherIsDecorated() { public void shouldMatchAnyEvenIfMatcherIsWrappedInHamcrestMatcher() { // given invocation = varargs("1", "2"); - HamcrestArgumentMatcher argumentMatcher = new HamcrestArgumentMatcher(new IntMatcher()); + HamcrestArgumentMatcher argumentMatcher = + new HamcrestArgumentMatcher<>(new IntMatcher()); matchers = asList(argumentMatcher); // when @@ -224,7 +225,7 @@ public void shouldMatchAnyEvenIfMatcherIsWrappedInHamcrestMatcher() { recordAction.assertContainsExactly(argumentMatcher, argumentMatcher); } - class IntMatcher extends BaseMatcher implements VarargMatcher { + private static class IntMatcher extends BaseMatcher implements VarargMatcher { public boolean matches(Object o) { return true; } @@ -242,8 +243,8 @@ private Invocation varargs(String... s) { return getLastInvocation(); } - private class RecordingAction implements ArgumentMatcherAction { - private List> matchers = new ArrayList>(); + private static class RecordingAction implements ArgumentMatcherAction { + private final List> matchers = new ArrayList>(); @Override public boolean apply(ArgumentMatcher matcher, Object argument) { From 83a41fdff122b0f82b8754571d3a9fa2d37dece6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 00:35:26 +0400 Subject: [PATCH 093/160] Bump junit from 1.1.3 to 1.1.4 (#2806) Bumps junit from 1.1.3 to 1.1.4. --- updated-dependencies: - dependency-name: androidx.test.ext:junit 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 8060abe937..bc71b3675f 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -46,6 +46,6 @@ libraries.android = [ ktx: 'androidx.core:core-ktx:1.8.0', compat: 'androidx.appcompat:appcompat:1.4.2', material: 'com.google.android.material:material:1.6.1', - junit: 'androidx.test.ext:junit:1.1.3', + junit: 'androidx.test.ext:junit:1.1.4', espresso: 'androidx.test.espresso:espresso-core:3.5.0', ] From 34b8a8db64798538d66d88fd7fc572990ca0c908 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Nov 2022 02:17:55 +0530 Subject: [PATCH 094/160] Bump kotlinVersion from 1.7.21 to 1.7.22 (#2809) Bumps `kotlinVersion` from 1.7.21 to 1.7.22. Updates `kotlin-gradle-plugin` from 1.7.21 to 1.7.22 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.7.21...v1.7.22) Updates `kotlin-stdlib` from 1.7.21 to 1.7.22 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.7.21...v1.7.22) --- 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] 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 d55818690a..d0eecb34bb 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.21" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.22" } } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index bc71b3675f..916bc4a45c 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.13' -def kotlinVersion = '1.7.21' +def kotlinVersion = '1.7.22' libraries.kotlin = [ version: kotlinVersion, From bdcb31e34c301a45845cfc743438c7aeaeb8b096 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Nov 2022 02:18:11 +0530 Subject: [PATCH 095/160] Bump com.github.ben-manes.versions from 0.42.0 to 0.44.0 (#2810) Bumps com.github.ben-manes.versions from 0.42.0 to 0.44.0. --- updated-dependencies: - dependency-name: com.github.ben-manes.versions 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 d0eecb34bb..8ec866b947 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ buildscript { plugins { id 'com.diffplug.spotless' version '6.11.0' id 'eclipse' - id 'com.github.ben-manes.versions' version '0.42.0' + id 'com.github.ben-manes.versions' version '0.44.0' id 'biz.aQute.bnd.builder' version '6.3.1' id 'ru.vyarus.animalsniffer' version '1.5.2' } From 2567f050f1e48da5acad2c5d616e565650f64b9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Nov 2022 23:41:03 +0530 Subject: [PATCH 096/160] Bump shipkit-auto-version from 1.2.1 to 1.2.2 (#2811) Bumps shipkit-auto-version from 1.2.1 to 1.2.2. --- updated-dependencies: - dependency-name: org.shipkit:shipkit-auto-version 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 8ec866b947..0a19ea239e 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.1' + classpath 'org.shipkit:shipkit-auto-version:1.2.2' classpath 'com.google.googlejavaformat:google-java-format:1.15.0' classpath 'com.android.tools.build:gradle:4.2.0' From ee9ba749fee1bf9c48429ce9083cd171709441e3 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sun, 4 Dec 2022 03:34:16 +0100 Subject: [PATCH 097/160] Fix incorrect Javadoc inline tag for MockitoJUnitRunner (#2816) --- src/main/java/org/mockito/junit/MockitoJUnitRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/junit/MockitoJUnitRunner.java b/src/main/java/org/mockito/junit/MockitoJUnitRunner.java index 413e8b8563..bfc938e2dc 100644 --- a/src/main/java/org/mockito/junit/MockitoJUnitRunner.java +++ b/src/main/java/org/mockito/junit/MockitoJUnitRunner.java @@ -34,7 +34,7 @@ * See {@link UnnecessaryStubbingException}. * Similar to JUnit rules, the runner also reports stubbing argument mismatches as console warnings * (see {@link MockitoHint}). - * To opt-out from this feature, use {@code}@RunWith(MockitoJUnitRunner.Silent.class){@code} + * To opt-out from this feature, use {@code @RunWith(MockitoJUnitRunner.Silent.class)} *

  • * Initializes mocks annotated with {@link Mock}, * so that explicit usage of {@link MockitoAnnotations#openMocks(Object)} is not necessary. From e2e628918518b73de7381692f28556ac6caa4710 Mon Sep 17 00:00:00 2001 From: Vladimir Glinskikh Date: Sun, 4 Dec 2022 14:07:40 +0300 Subject: [PATCH 098/160] Bump gradle from 7.5.1 to 7.6 (#2817) --- gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 60756 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 6 ++++++ gradlew.bat | 14 ++++++++------ 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..249e5832f090a2944b7473328c07c9755baa3196 100644 GIT binary patch delta 10158 zcmaKSbyOWsmn~e}-QC?axCPf>!2<-jxI0|j{UX8L-QC?axDz};a7}ppGBe+Nv*x{5 zy?WI?=j^WT(_Md5*V*xNP>X9&wM>xUvNiMuKDK=Xg!N%oM>Yru2rh7#yD-sW0Ov#$ zCKBSOD3>TM%&1T5t&#FK@|@1f)Ze+EE6(7`}J(Ek4})CD@I+W;L{ zO>K;wokKMA)EC6C|D@nz%D2L3U=Nm(qc>e4GM3WsHGu-T?l^PV6m-T-(igun?PZ8U z{qbiLDMcGSF1`FiKhlsV@qPMRm~h9@z3DZmWp;Suh%5BdP6jqHn}$-gu`_xNg|j{PSJ0n$ zbE;Azwq8z6IBlgKIEKc4V?*##hGW#t*rh=f<;~RFWotXS$vr;Mqz>A99PMH3N5BMi zWLNRjc57*z`2)gBV0o4rcGM(u*EG8_H5(|kThAnp|}u2xz>>X6tN zv)$|P2Nr1D*fk4wvqf(7;NmdRV3eL{!>DO-B98(s*-4$g{)EnRYAw+DP-C`=k)B!* zHU7!ejcbavGCYuz9k@$aZQaU%#K%6`D}=N_m?~^)IcmQZun+K)fSIoS>Ws zwvZ%Rfmw>%c!kCd~Pmf$E%LCj2r>+FzKGDm+%u88|hHprot{*OIVpi`Vd^^aumtx2L}h} zPu$v~zdHaWPF<`LVQX4i7bk82h#RwRyORx*z3I}o&>>eBDCif%s7&*vF6kU%1` zf(bvILch^~>cQ{=Y#?nx(8C-Uuv7!2_YeCfo?zkP;FK zX+KdjKS;HQ+7 zj>MCBI=d$~9KDJ1I2sb_3=T6D+Mu9{O&vcTnDA(I#<=L8csjEqsOe=&`=QBc7~>u2 zfdcO44PUOST%PcN+8PzKFYoR0;KJ$-Nwu#MgSM{_!?r&%rVM}acp>53if|vpH)q=O z;6uAi__am8g$EjZ33?PmCrg@(M!V_@(^+#wAWNu&e3*pGlfhF2<3NobAC zlusz>wMV--3ytd@S047g)-J@eOD;DMnC~@zvS=Gnw3=LnRzkeV`LH4#JGPklE4!Q3 zq&;|yGR0FiuE-|&1p2g{MG!Z3)oO9Jf4@0h*3!+RHv=SiEf*oGQCSRQf=LqT5~sajcJ8XjE>E*@q$n z!4|Rz%Lv8TgI23JV6%)N&`Otk6&RBdS|lCe7+#yAfdyEWNTfFb&*S6-;Q}d`de!}*3vM(z71&3 z37B%@GWjeQ_$lr%`m-8B&Zl4Gv^X{+N{GCsQGr!LLU4SHmLt3{B*z-HP{73G8u>nK zHxNQ4eduv>lARQfULUtIlLx#7ea+O;w?LH}FF28c9pg#*M`pB~{jQmPB*gA;Hik#e zZpz&X#O}}r#O_#oSr4f`zN^wedt>ST791bAZ5(=g<Oj)m9X8J^>Th}fznPY0T zsD9ayM7Hrlb6?jHXL<{kdA*Q#UPCYce0p`fHxoZ7_P`cF-$1YY9Pi;0QFt{CCf%C# zuF60A_NTstTQeFR3)O*ThlWKk08}7Nshh}J-sGY=gzE!?(_ZI4ovF6oZ$)&Zt~WZi z_0@Bk!~R4+<&b6CjI{nGj+P{*+9}6;{RwZ7^?H)xjhiRi;?A|wb0UxjPr?L@$^v|0= z@6d3+eU|&re3+G*XgFS}tih3;>2-R1x>`2hmUb5+Z~eM4P|$ zAxvE$l@sIhf_#YLnF|Wcfp(Gh@@dJ-yh|FhKqsyQp_>7j1)w|~5OKETx2P$~`}5huK;{gw_~HXP6=RsG)FKSZ=VYkt+0z&D zr?`R3bqVV?Zmqj&PQ`G3b^PIrd{_K|Hhqt zAUS#|*WpEOeZ{@h*j6%wYsrL`oHNV=z*^}yT1NCTgk1-Gl(&+TqZhODTKb9|0$3;| z;{UUq7X9Oz`*gwbi|?&USWH?Fr;6=@Be4w=8zu>DLUsrwf+7A`)lpdGykP`^SA8{ok{KE3sM$N@l}kB2GDe7MEN? zWcQ2I0fJ1ZK%s-YKk?QbEBO6`C{bg$%le0FTgfmSan-Kih0A7)rGy|2gd)_gRH7qp z*bNlP0u|S^5<)kFcd&wQg*6QP5;y(3ZgI%vUgWk#`g!sMf`02>@xz{Ie9_-fXllyw zh>P%cK+-HkQ;D$Jh=ig(ASN^zJ7|q*#m;}2M*T#s0a^nF_>jI(L(|*}#|$O&B^t!W zv-^-vP)kuu+b%(o3j)B@do)n*Y0x%YNy`sYj*-z2ncYoggD6l z6{1LndTQUh+GCX;7rCrT z@=vy&^1zyl{#7vRPv;R^PZPaIks8okq)To8!Cks0&`Y^Xy5iOWC+MmCg0Jl?1ufXO zaK8Q5IO~J&E|<;MnF_oXLc=LU#m{6yeomA^Ood;)fEqGPeD|fJiz(`OHF_f*{oWJq z1_$NF&Mo7@GKae#f4AD|KIkGVi~ubOj1C>>WCpQq>MeDTR_2xL01^+K1+ zr$}J>d=fW{65hi2bz&zqRKs8zpDln z*7+Gtfz6rkgfj~#{MB=49FRP;ge*e0=x#czw5N{@T1{EAl;G&@tpS!+&2&Stf<%<+55R18u2%+}`?PZo8xg|Y9Xli(fSQyC7 z+O5{;ZyW$!eYR~gy>;l6cA+e`oXN6a6t(&kUkWus*Kf<m$W7L)w5uXYF)->OeWMSUVXi;N#sY zvz4c?GkBU{D;FaQ)9|HU7$?BX8DFH%hC11a@6s4lI}y{XrB~jd{w1x&6bD?gemdlV z-+ZnCcldFanu`P=S0S7XzwXO(7N9KV?AkgZzm|J&f{l-Dp<)|-S7?*@HBIfRxmo1% zcB4`;Al{w-OFD08g=Qochf9=gb56_FPc{C9N5UAjTcJ(`$>)wVhW=A<8i#!bmKD#6~wMBak^2(p56d2vs&O6s4>#NB0UVr24K z%cw|-Yv}g5`_zcEqrZBaRSoBm;BuXJM^+W$yUVS9?u(`87t)IokPgC_bQ3g_#@0Yg zywb?u{Di7zd3XQ$y!m^c`6~t-7@g-hwnTppbOXckS-^N?w1`kRMpC!mfMY?K#^Ldm zYL>771%d{+iqh4a&4RdLNt3_(^^*{U2!A>u^b{7e@}Azd_PiZ>d~(@(Q@EYElLAx3LgQ5(ZUf*I%EbGiBTG!g#=t zXbmPhWH`*B;aZI)$+PWX+W)z?3kTOi{2UY9*b9bpSU!GWcVu+)!^b4MJhf=U9c?jj z%V)EOF8X3qC5~+!Pmmmd@gXzbycd5Jdn!N#i^50a$4u}8^O}DG2$w-U|8QkR-WU1mk4pF z#_imS#~c2~Z{>!oE?wfYc+T+g=eJL`{bL6=Gf_lat2s=|RxgP!e#L|6XA8w{#(Po(xk1~rNQ4UiG``U`eKy7`ot;xv4 zdv54BHMXIq;#^B%W(b8xt%JRueW5PZsB2eW=s3k^Pe1C$-NN8~UA~)=Oy->22yJ%e zu=(XD^5s{MkmWB)AF_qCFf&SDH%ytqpt-jgs35XK8Ez5FUj?uD3++@2%*9+-65LGQ zvu1eopeQoFW98@kzU{+He9$Yj#`vaQkqu%?1wCoBd%G=)TROYl2trZa{AZ@#^LARR zdzg-?EUnt9dK2;W=zCcVj18RTj-%w^#pREbgpD0aL@_v-XV2&Cd@JB^(}GRBU}9gV z6sWmVZmFZ9qrBN%4b?seOcOdOZ+6cx8-#R(+LYKJu~Y%pF5#85aF9$MnP7r^Bu%D? zT{b-KBujiy>7_*9{8u0|mTJ(atnnnS%qBDM_Gx5>3V+2~Wt=EeT4cXOdud$+weM(>wdBg+cV$}6%(ccP;`!~CzW{0O2aLY z?rQtBB6`ZztPP@_&`kzDzxc==?a{PUPUbbX31Vy?_(;c+>3q*!df!K(LQYZNrZ>$A*8<4M%e8vj1`%(x9)d~);ym4p zoo518$>9Pe| zZaFGj);h?khh*kgUI-Xvj+Dr#r&~FhU=eQ--$ZcOY9;x%&3U(&)q}eJs=)K5kUgi5 zNaI-m&4?wlwFO^`5l-B?17w4RFk(IKy5fpS0K%txp0qOj$e=+1EUJbLd-u>TYNna~ z+m?gU0~xlcnP>J>%m_y_*7hVMj3d&)2xV8>F%J;6ncm)ILGzF2sPAV|uYk5!-F%jL(53^51BKr zc3g7+v^w<4WIhk7a#{N6Ku_u{F`eo;X+u!C(lIaiY#*V5!sMed39%-AgV*`(nI)Im zemHE^2foBMPyIP<*yuD21{6I?Co?_{pqp-*#N6sZRQAzEBV4HQheOyZT5UBd)>G85 zw^xHvCEP4AJk<{v2kQQ;g;C)rCY=X!c8rNpNJ4mHETN}t1rwSe7=s8u&LzW-+6AEB z)LX0o7`EqC94HM{4p}d2wOwj2EB|O;?&^FeG9ZrT%c!J&x`Z3D2!cm(UZbFBb`+h ztfhjq75yuSn2~|Pc)p$Ul6=)}7cfXtBsvc15f&(K{jnEsw5Gh0GM^O=JC+X-~@r1kI$=FH=yBzsO#PxR1xU9+T{KuPx7sMe~GX zSP>AT3%(Xs@Ez**e@GAn{-GvB^oa6}5^2s+Mg~Gw?#$u&ZP;u~mP|FXsVtr>3k9O?%v>`Ha-3QsOG<7KdXlqKrsN25R|K<<;- z8kFY!&J&Yrqx3ptevOHiqPxKo_wwAPD)$DWMz{0>{T5qM%>rMqGZ!dJdK(&tP1#89 zVcu}I1I-&3%nMyF62m%MDpl~p)PM(%YoR zD)=W)E7kjwzAr!?^P*`?=fMHd1q4yjLGTTRUidem^Ocjrfgk2Jp|6SabEVHKC3c>RX@tNx=&Z7gC z0ztZoZx+#o36xH8mv6;^e{vU;G{JW17kn(RO&0L%q^fpWSYSkr1Cb92@bV->VO5P z;=V{hS5wcROQfbah6ND{2a$zFnj>@yuOcw}X~E20g7)5=Z#(y)RC878{_rObmGQ;9 zUy>&`YT^2R@jqR1z9Fx&x)WBstIE#*UhAa>WrMm<10={@$UN@Cog+#pxq{W@l0DOf zJGs^Jv?t8HgIXk(;NFHXun$J{{p})cJ^BWn4BeQo6dMNp%JO@$9z{(}qqEHuZOUQP zZiwo70Oa@lMYL(W*R4(!oj`)9kRggJns-A|w+XL=P07>QBMTEbG^gPS)H zu^@MFTFZtsKGFHgj|hupbK({r>PX3_kc@|4Jdqr@gyyKrHw8Tu<#0&32Hh?S zsVm_kQ2K`4+=gjw1mVhdOz7dI7V!Iu8J1LgI+_rF`Wgx5-XwU~$h>b$%#$U3wWC-ea0P(At2SjPAm57kd;!W5k{do1}X681o}`!c*(w!kCjtGTh7`=!M)$9 zWjTns{<-WX+Xi;&d!lyV&1KT9dKL??8)fu2(?Ox<^?EAzt_(#5bp4wAfgIADYgLU` z;J7f8g%-tfmTI1ZHjgufKcAT4SO(vx?xSo4pdWh`3#Yk;DqPGQE0GD?!_CfXb(E8WoJt6*Yutnkvmb?7H9B zVICAYowwxK;VM4(#~|}~Ooyzm*1ddU_Yg%Ax*_FcZm^AzYc$<+9bv;Eucr(SSF}*JsjTfb*DY>qmmkt z;dRkB#~SylP~Jcmr&Bl9TxHf^DcGUelG%rA{&s)5*$|-ww}Kwx-lWnNeghVm@z zqi3@-oJnN%r2O4t9`5I5Zfc;^ROHmY6C9 z1VRRX*1+aBlbO_p>B+50f1p&%?_A*16R0n+l}HKWI$yIH3oq2`k4O?tEVd~a4~>iI zo{d}b8tr+$q<%%K%Ett*i|RAJEMnk9hU7LtL!lxOB45xO1g)ycDBd=NbpaE3j?Gw& z0M&xx13EkCgNHu%Z8rBLo93XH-zQUfF3{Iy>65-KSPniqIzF+?x$3>`L?oBOBeEsv zs_y7@7>IbS&w2Vju^#vBpPWQuUv=dDRGm(-MH|l+8T?vfgD;{nE_*-h?@D;GN>4hA z9{!G@ANfHZOxMq5kkoh4h*p3+zE7z$13ocDJR$XA*7uKtG5Cn_-ibn%2h{ z;J0m5aCjg(@_!G>i2FDAvcn5-Aby8b;J0u%u)!`PK#%0FS-C3(cq9J{V`DJEbbE|| zYpTDd+ulcjEd5`&v!?=hVgz&S0|C^We?2|>9|2T6?~nn^_CpLn&kuI|VG7_E{Ofu9 zAqe0Reuq5Zunlx@zyTqEL+ssT15X|Z0LUfZAr-i$1_SJ{j}BHmBm}s8{OgK3lm%4F zzC%jz!y!8WUJo2FLkU(mVh7-uzC+gcbkV^bM}&Y6=HTTca{!7ZSoB!)l|v<(3ly!jq&P5A2q(U5~h)))aj-`-6&aM~LBySnAy zA0{Z{FHiUb8rW|Yo%kQwi`Kh>EEE$0g7UxeeeVkcY%~87yCmSjYyxoqq(%Jib*lH; zz`t5y094U`k_o{-*U^dFH~+1I@GsgwqmGsQC9-Vr0X94TLhlV;Kt#`9h-N?oKHqpx zzVAOxltd%gzb_Qu{NHnE8vPp=G$#S)Y%&6drobF_#NeY%VLzeod delta 9041 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b916c04dbb..03ca076c8a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=f6b8596b10cce501591e92f229816aa4046424f3b24d771751b06779d58c8ec4 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionSha256Sum=7ba68c54029790ab444b39d7e293d3236b2632631fb5f2e012bb28b4ff669e4b +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787337..a69d9cb6c2 100755 --- a/gradlew +++ b/gradlew @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd32c4..f127cfd49d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal From a4e2e48507424d7efc32be53f5bd23bcfd3b4ddb Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Wed, 14 Dec 2022 16:18:19 -0500 Subject: [PATCH 099/160] Add `mockito-subclass` artifact (#2821) Signed-off-by: Andriy Redko --- .github/workflows/ci.yml | 2 +- settings.gradle.kts | 3 ++- .../org.mockito.plugins.MemberAccessor | 1 + .../org.mockito.plugins.MockMaker | 1 + .../java/org/mockitosubclass/PluginTest.java | 26 +++++++++++++++++++ subprojects/subclass/subclass.gradle | 11 ++++++++ 6 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 subprojects/subclass/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor create mode 100644 subprojects/subclass/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker create mode 100644 subprojects/subclass/src/test/java/org/mockitosubclass/PluginTest.java create mode 100644 subprojects/subclass/subclass.gradle diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc0694a891..16a021c2ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: strategy: matrix: java: [8, 11, 17] - mock-maker: ['mock-maker-default', 'mock-maker-inline'] + mock-maker: ['mock-maker-default', 'mock-maker-inline', 'mock-maker-subclass'] # All build steps # SINGLE-MATRIX-JOB means that the step does not need to be executed on every job in the matrix diff --git a/settings.gradle.kts b/settings.gradle.kts index 313149df98..7c3c09c74d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,7 +2,8 @@ plugins { id("com.gradle.enterprise").version("3.3.4") } -include("inline", +include("subclass", + "inline", "proxy", "extTest", "groovyTest", diff --git a/subprojects/subclass/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor b/subprojects/subclass/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor new file mode 100644 index 0000000000..1422f9900b --- /dev/null +++ b/subprojects/subclass/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor @@ -0,0 +1 @@ +member-accessor-module diff --git a/subprojects/subclass/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker b/subprojects/subclass/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..fdbd0b1579 --- /dev/null +++ b/subprojects/subclass/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-subclass diff --git a/subprojects/subclass/src/test/java/org/mockitosubclass/PluginTest.java b/subprojects/subclass/src/test/java/org/mockitosubclass/PluginTest.java new file mode 100644 index 0000000000..4acd77899f --- /dev/null +++ b/subprojects/subclass/src/test/java/org/mockitosubclass/PluginTest.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitosubclass; + +import org.junit.Test; +import org.mockito.internal.configuration.plugins.Plugins; +import org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker; +import org.mockito.internal.util.reflection.ModuleMemberAccessor; + +import static org.junit.Assert.*; + +public class PluginTest { + + @Test + public void mock_maker_should_be_inline() throws Exception { + assertTrue(Plugins.getMockMaker() instanceof ByteBuddyMockMaker); + } + + @Test + public void member_accessor_should_be_module() throws Exception { + assertTrue(Plugins.getMemberAccessor() instanceof ModuleMemberAccessor); + } + +} diff --git a/subprojects/subclass/subclass.gradle b/subprojects/subclass/subclass.gradle new file mode 100644 index 0000000000..8ef7ae1ae5 --- /dev/null +++ b/subprojects/subclass/subclass.gradle @@ -0,0 +1,11 @@ +description = "Mockito preconfigured subclass mock maker" + +apply from: "$rootDir/gradle/java-library.gradle" + +dependencies { + api project.rootProject + testImplementation libraries.junit4 + testImplementation libraries.assertj +} + +tasks.javadoc.enabled = false From 386323820ff181d1e11113e9f539c9b8f49643dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 22:20:12 +0100 Subject: [PATCH 100/160] Bump biz.aQute.bnd.builder from 6.3.1 to 6.4.0 (#2813) Bumps biz.aQute.bnd.builder from 6.3.1 to 6.4.0. --- updated-dependencies: - dependency-name: biz.aQute.bnd.builder 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 0a19ea239e..2bc803c7b4 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ plugins { id 'com.diffplug.spotless' version '6.11.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.44.0' - id 'biz.aQute.bnd.builder' version '6.3.1' + id 'biz.aQute.bnd.builder' version '6.4.0' id 'ru.vyarus.animalsniffer' version '1.5.2' } From 96eb5e0cbff80a1545b1093db1baf4e33a2041e7 Mon Sep 17 00:00:00 2001 From: Andy Coates <8012398+big-andy-coates@users.noreply.github.com> Date: Thu, 15 Dec 2022 07:38:55 +0000 Subject: [PATCH 101/160] Fix missing sync block in `InvocationContainerImpl` (#2812) The missing `synchronized` block while accessing `stubbed` was causing `ReturnsDeepStubsConcurrentTest` to periodically fail, and likely multithreaded user tests too. Fixes #2808 --- .../stubbing/InvocationContainerImpl.java | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java index 5aedbb912e..832b1b155b 100644 --- a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java @@ -25,7 +25,6 @@ import org.mockito.stubbing.Stubbing; import org.mockito.stubbing.ValidableAnswer; -@SuppressWarnings("unchecked") public class InvocationContainerImpl implements InvocationContainer, Serializable { private static final long serialVersionUID = -5334301962749537177L; @@ -36,7 +35,7 @@ public class InvocationContainerImpl implements InvocationContainer, Serializabl private MatchableInvocation invocationForStubbing; - public InvocationContainerImpl(MockCreationSettings mockSettings) { + public InvocationContainerImpl(MockCreationSettings mockSettings) { this.registeredInvocations = createRegisteredInvocations(mockSettings); this.mockStrictness = mockSettings.getStrictness(); this.doAnswerStyleStubbing = new DoAnswerStyleStubbing(); @@ -51,14 +50,14 @@ public void resetInvocationForPotentialStubbing(MatchableInvocation invocationMa this.invocationForStubbing = invocationMatcher; } - public void addAnswer(Answer answer, Strictness stubbingStrictness) { + public void addAnswer(Answer answer, Strictness stubbingStrictness) { registeredInvocations.removeLast(); addAnswer(answer, false, stubbingStrictness); } /** Adds new stubbed answer and returns the invocation matcher the answer was added to. */ public StubbedInvocationMatcher addAnswer( - Answer answer, boolean isConsecutive, Strictness stubbingStrictness) { + Answer answer, boolean isConsecutive, Strictness stubbingStrictness) { Invocation invocation = invocationForStubbing.getInvocation(); mockingProgress().stubbingCompleted(); if (answer instanceof ValidableAnswer) { @@ -79,7 +78,7 @@ public StubbedInvocationMatcher addAnswer( } } - public void addConsecutiveAnswer(Answer answer) { + public void addConsecutiveAnswer(Answer answer) { addAnswer(answer, true, null); } @@ -143,18 +142,14 @@ public void clearInvocations() { registeredInvocations.clear(); } - /** - * Stubbings in descending order, most recent first - */ - public List getStubbingsDescending() { - return (List) stubbed; - } - /** * Stubbings in ascending order, most recent last */ public Collection getStubbingsAscending() { - List result = new LinkedList<>(stubbed); + List result; + synchronized (stubbed) { + result = new LinkedList<>(stubbed); + } Collections.reverse(result); return result; } @@ -163,13 +158,14 @@ public Object invokedMock() { return invocationForStubbing.getInvocation().getMock(); } - private RegisteredInvocations createRegisteredInvocations(MockCreationSettings mockSettings) { + private RegisteredInvocations createRegisteredInvocations( + MockCreationSettings mockSettings) { return mockSettings.isStubOnly() ? new SingleRegisteredInvocation() : new DefaultRegisteredInvocations(); } - public Answer findStubbedAnswer() { + public Answer findStubbedAnswer() { synchronized (stubbed) { for (StubbedInvocationMatcher s : stubbed) { if (invocationForStubbing.matches(s.getInvocation())) { From 676519f0da1c89231700dd51025fedbca44ff033 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Dec 2022 22:01:32 +0100 Subject: [PATCH 102/160] Bump versions.bytebuddy from 1.12.19 to 1.12.20 (#2824) Bumps `versions.bytebuddy` from 1.12.19 to 1.12.20. Updates `byte-buddy` from 1.12.19 to 1.12.20 - [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.19...byte-buddy-1.12.20) Updates `byte-buddy-agent` from 1.12.19 to 1.12.20 - [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.19...byte-buddy-1.12.20) Updates `byte-buddy-android` from 1.12.19 to 1.12.20 - [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.19...byte-buddy-1.12.20) --- 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 916bc4a45c..12cfaa4112 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.12.19' +versions.bytebuddy = '1.12.20' versions.junitJupiter = '5.9.1' versions.errorprone = '2.16' From e2f26d80806fc3a3b8eabb418145b70c695db877 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Dec 2022 19:38:08 +0100 Subject: [PATCH 103/160] Bump com.diffplug.spotless from 6.11.0 to 6.12.0 (#2825) Bumps com.diffplug.spotless from 6.11.0 to 6.12.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 2bc803c7b4..a74ce233ef 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.11.0' + id 'com.diffplug.spotless' version '6.12.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.44.0' id 'biz.aQute.bnd.builder' version '6.4.0' From a7c7fdb4f972d7fb6736f692e5ee208034f214f3 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Sun, 18 Dec 2022 14:52:29 -0500 Subject: [PATCH 104/160] Update minimum support Java version from 8 to 11 (#2804) Fixes #2798 Signed-off-by: Andriy Redko --- .github/workflows/ci.yml | 6 +- build.gradle | 19 +- gradle.properties | 1 + gradle/errorprone.gradle | 6 - gradle/java-library.gradle | 4 +- gradle/mockito-core/java-8-docs/package-list | 217 ------------- .../{java-8-docs => java-docs}/element-list | 305 +++++++++++------- gradle/mockito-core/javadoc.gradle | 10 +- gradle/root/coverage.gradle | 10 +- settings.gradle.kts | 9 +- .../internal/debugging/LocationImpl.java | 211 ++++++++++++ subprojects/androidTest/androidTest.gradle | 4 +- subprojects/errorprone/errorprone.gradle | 24 +- subprojects/inline/inline.gradle | 6 +- .../org/mockito/kotlin/CircularityTest.kt | 4 + .../org/mockito/kotlin/InlineClassTest.kt | 4 + subprojects/module-test/module-test.gradle | 8 +- 17 files changed, 455 insertions(+), 393 deletions(-) delete mode 100644 gradle/mockito-core/java-8-docs/package-list rename gradle/mockito-core/{java-8-docs => java-docs}/element-list (57%) create mode 100644 src/main/java/org/mockito/internal/debugging/LocationImpl.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16a021c2ce..58cb41b62c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: # Definition of the build matrix strategy: matrix: - java: [8, 11, 17] + java: [11, 17] mock-maker: ['mock-maker-default', 'mock-maker-inline', 'mock-maker-subclass'] # All build steps @@ -48,11 +48,11 @@ jobs: java-version: ${{ matrix.java }} - name: 3. Validate Gradle wrapper - if: matrix.java == 8 && matrix.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB + if: matrix.java == 11 && matrix.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB uses: gradle/wrapper-validation-action@v1.0.5 # https://github.com/gradle/wrapper-validation-action - name: 4. Build and check reproducibility of artifacts (single job only) - if: matrix.java == 8 && matrix.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB + if: matrix.java == 11 && matrix.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB run: ./check_reproducibility.sh - name: 5. Spotless check (single job only). Run './gradlew spotlessApply' locally if this job fails. diff --git a/build.gradle b/build.gradle index a74ce233ef..351bfa8266 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ plugins { id 'eclipse' id 'com.github.ben-manes.versions' version '0.44.0' id 'biz.aQute.bnd.builder' version '6.4.0' - id 'ru.vyarus.animalsniffer' version '1.5.2' + id 'ru.vyarus.animalsniffer' version '1.6.0' } description = 'Mockito mock objects library core API and implementation' @@ -52,10 +52,8 @@ allprojects { proj -> mavenCentral() google() } - if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_11)) { - plugins.withId('java') { - proj.apply from: "$rootDir/gradle/errorprone.gradle" - } + plugins.withId('java') { + proj.apply from: "$rootDir/gradle/errorprone.gradle" } tasks.withType(JavaCompile) { //I don't believe those warnings add value given modern IDEs @@ -66,7 +64,7 @@ allprojects { proj -> options.addStringOption('Xdoclint:none', '-quiet') options.addStringOption('encoding', 'UTF-8') options.addStringOption('charSet', 'UTF-8') - options.setSource('8') + options.setSource('11') } tasks.withType(AbstractArchiveTask) { @@ -107,6 +105,15 @@ dependencies { animalsniffer { sourceSets = [sourceSets.main] annotation = 'org.mockito.internal.SuppressSignatureCheck' + // See please https://github.com/mojohaus/animal-sniffer/issues/172 + ignore += [ + 'java.lang.instrument.Instrumentation', + 'java.lang.invoke.MethodHandle', + 'java.lang.invoke.MethodHandles$Lookup', + 'java.lang.StackWalker', + 'java.lang.StackWalker$StackFrame', + 'java.lang.StackWalker$Option' + ] } spotless { diff --git a/gradle.properties b/gradle.properties index 3ccf4c9a66..ad11ad8da0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,6 +4,7 @@ org.gradle.caching=true org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 \ -XX:+IgnoreUnrecognizedVMOptions \ --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ + --add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \ --add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ --add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ --add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ diff --git a/gradle/errorprone.gradle b/gradle/errorprone.gradle index 1d9cf7428e..d7d551432f 100644 --- a/gradle/errorprone.gradle +++ b/gradle/errorprone.gradle @@ -1,12 +1,6 @@ apply plugin: "net.ltgt.errorprone" -if (JavaVersion.current() == JavaVersion.VERSION_1_8) { - dependencies { - errorproneJavac("com.google.errorprone:javac:9+181-r4173-1") - } -} - dependencies { errorprone libraries.errorprone } diff --git a/gradle/java-library.gradle b/gradle/java-library.gradle index b5949b76fe..5e91b74443 100644 --- a/gradle/java-library.gradle +++ b/gradle/java-library.gradle @@ -14,8 +14,8 @@ generatePomFileForJavaLibraryPublication.doLast { assert pom.name == archivesBaseName } -sourceCompatibility = 1.8 -targetCompatibility = 1.8 +sourceCompatibility = 11 +targetCompatibility = 11 test { include "**/*Test.class" diff --git a/gradle/mockito-core/java-8-docs/package-list b/gradle/mockito-core/java-8-docs/package-list deleted file mode 100644 index 351c186855..0000000000 --- a/gradle/mockito-core/java-8-docs/package-list +++ /dev/null @@ -1,217 +0,0 @@ -java.applet -java.awt -java.awt.color -java.awt.datatransfer -java.awt.dnd -java.awt.event -java.awt.font -java.awt.geom -java.awt.im -java.awt.im.spi -java.awt.image -java.awt.image.renderable -java.awt.print -java.beans -java.beans.beancontext -java.io -java.lang -java.lang.annotation -java.lang.instrument -java.lang.invoke -java.lang.management -java.lang.ref -java.lang.reflect -java.math -java.net -java.nio -java.nio.channels -java.nio.channels.spi -java.nio.charset -java.nio.charset.spi -java.nio.file -java.nio.file.attribute -java.nio.file.spi -java.rmi -java.rmi.activation -java.rmi.dgc -java.rmi.registry -java.rmi.server -java.security -java.security.acl -java.security.cert -java.security.interfaces -java.security.spec -java.sql -java.text -java.text.spi -java.time -java.time.chrono -java.time.format -java.time.temporal -java.time.zone -java.util -java.util.concurrent -java.util.concurrent.atomic -java.util.concurrent.locks -java.util.function -java.util.jar -java.util.logging -java.util.prefs -java.util.regex -java.util.spi -java.util.stream -java.util.zip -javax.accessibility -javax.activation -javax.activity -javax.annotation -javax.annotation.processing -javax.crypto -javax.crypto.interfaces -javax.crypto.spec -javax.imageio -javax.imageio.event -javax.imageio.metadata -javax.imageio.plugins.bmp -javax.imageio.plugins.jpeg -javax.imageio.spi -javax.imageio.stream -javax.jws -javax.jws.soap -javax.lang.model -javax.lang.model.element -javax.lang.model.type -javax.lang.model.util -javax.management -javax.management.loading -javax.management.modelmbean -javax.management.monitor -javax.management.openmbean -javax.management.relation -javax.management.remote -javax.management.remote.rmi -javax.management.timer -javax.naming -javax.naming.directory -javax.naming.event -javax.naming.ldap -javax.naming.spi -javax.net -javax.net.ssl -javax.print -javax.print.attribute -javax.print.attribute.standard -javax.print.event -javax.rmi -javax.rmi.CORBA -javax.rmi.ssl -javax.script -javax.security.auth -javax.security.auth.callback -javax.security.auth.kerberos -javax.security.auth.login -javax.security.auth.spi -javax.security.auth.x500 -javax.security.cert -javax.security.sasl -javax.sound.midi -javax.sound.midi.spi -javax.sound.sampled -javax.sound.sampled.spi -javax.sql -javax.sql.rowset -javax.sql.rowset.serial -javax.sql.rowset.spi -javax.swing -javax.swing.border -javax.swing.colorchooser -javax.swing.event -javax.swing.filechooser -javax.swing.plaf -javax.swing.plaf.basic -javax.swing.plaf.metal -javax.swing.plaf.multi -javax.swing.plaf.nimbus -javax.swing.plaf.synth -javax.swing.table -javax.swing.text -javax.swing.text.html -javax.swing.text.html.parser -javax.swing.text.rtf -javax.swing.tree -javax.swing.undo -javax.tools -javax.transaction -javax.transaction.xa -javax.xml -javax.xml.bind -javax.xml.bind.annotation -javax.xml.bind.annotation.adapters -javax.xml.bind.attachment -javax.xml.bind.helpers -javax.xml.bind.util -javax.xml.crypto -javax.xml.crypto.dom -javax.xml.crypto.dsig -javax.xml.crypto.dsig.dom -javax.xml.crypto.dsig.keyinfo -javax.xml.crypto.dsig.spec -javax.xml.datatype -javax.xml.namespace -javax.xml.parsers -javax.xml.soap -javax.xml.stream -javax.xml.stream.events -javax.xml.stream.util -javax.xml.transform -javax.xml.transform.dom -javax.xml.transform.sax -javax.xml.transform.stax -javax.xml.transform.stream -javax.xml.validation -javax.xml.ws -javax.xml.ws.handler -javax.xml.ws.handler.soap -javax.xml.ws.http -javax.xml.ws.soap -javax.xml.ws.spi -javax.xml.ws.spi.http -javax.xml.ws.wsaddressing -javax.xml.xpath -org.ietf.jgss -org.omg.CORBA -org.omg.CORBA.DynAnyPackage -org.omg.CORBA.ORBPackage -org.omg.CORBA.TypeCodePackage -org.omg.CORBA.portable -org.omg.CORBA_2_3 -org.omg.CORBA_2_3.portable -org.omg.CosNaming -org.omg.CosNaming.NamingContextExtPackage -org.omg.CosNaming.NamingContextPackage -org.omg.Dynamic -org.omg.DynamicAny -org.omg.DynamicAny.DynAnyFactoryPackage -org.omg.DynamicAny.DynAnyPackage -org.omg.IOP -org.omg.IOP.CodecFactoryPackage -org.omg.IOP.CodecPackage -org.omg.Messaging -org.omg.PortableInterceptor -org.omg.PortableInterceptor.ORBInitInfoPackage -org.omg.PortableServer -org.omg.PortableServer.CurrentPackage -org.omg.PortableServer.POAManagerPackage -org.omg.PortableServer.POAPackage -org.omg.PortableServer.ServantLocatorPackage -org.omg.PortableServer.portable -org.omg.SendingContext -org.omg.stub.java.rmi -org.w3c.dom -org.w3c.dom.bootstrap -org.w3c.dom.events -org.w3c.dom.ls -org.w3c.dom.views -org.xml.sax -org.xml.sax.ext -org.xml.sax.helpers diff --git a/gradle/mockito-core/java-8-docs/element-list b/gradle/mockito-core/java-docs/element-list similarity index 57% rename from gradle/mockito-core/java-8-docs/element-list rename to gradle/mockito-core/java-docs/element-list index 351c186855..4cfabf8fdf 100644 --- a/gradle/mockito-core/java-8-docs/element-list +++ b/gradle/mockito-core/java-docs/element-list @@ -1,28 +1,14 @@ -java.applet -java.awt -java.awt.color -java.awt.datatransfer -java.awt.dnd -java.awt.event -java.awt.font -java.awt.geom -java.awt.im -java.awt.im.spi -java.awt.image -java.awt.image.renderable -java.awt.print -java.beans -java.beans.beancontext +module:java.base java.io java.lang java.lang.annotation -java.lang.instrument java.lang.invoke -java.lang.management +java.lang.module java.lang.ref java.lang.reflect java.math java.net +java.net.spi java.nio java.nio.channels java.nio.channels.spi @@ -31,17 +17,11 @@ java.nio.charset.spi java.nio.file java.nio.file.attribute java.nio.file.spi -java.rmi -java.rmi.activation -java.rmi.dgc -java.rmi.registry -java.rmi.server java.security java.security.acl java.security.cert java.security.interfaces java.security.spec -java.sql java.text java.text.spi java.time @@ -55,73 +35,63 @@ java.util.concurrent.atomic java.util.concurrent.locks java.util.function java.util.jar -java.util.logging -java.util.prefs java.util.regex java.util.spi java.util.stream java.util.zip -javax.accessibility -javax.activation -javax.activity -javax.annotation -javax.annotation.processing javax.crypto javax.crypto.interfaces javax.crypto.spec +javax.net +javax.net.ssl +javax.security.auth +javax.security.auth.callback +javax.security.auth.login +javax.security.auth.spi +javax.security.auth.x500 +javax.security.cert +module:java.compiler +javax.annotation.processing +javax.lang.model +javax.lang.model.element +javax.lang.model.type +javax.lang.model.util +javax.tools +module:java.datatransfer +java.awt.datatransfer +module:java.desktop +java.applet +java.awt +java.awt.color +java.awt.desktop +java.awt.dnd +java.awt.event +java.awt.font +java.awt.geom +java.awt.im +java.awt.im.spi +java.awt.image +java.awt.image.renderable +java.awt.print +java.beans +java.beans.beancontext +javax.accessibility javax.imageio javax.imageio.event javax.imageio.metadata javax.imageio.plugins.bmp javax.imageio.plugins.jpeg +javax.imageio.plugins.tiff javax.imageio.spi javax.imageio.stream -javax.jws -javax.jws.soap -javax.lang.model -javax.lang.model.element -javax.lang.model.type -javax.lang.model.util -javax.management -javax.management.loading -javax.management.modelmbean -javax.management.monitor -javax.management.openmbean -javax.management.relation -javax.management.remote -javax.management.remote.rmi -javax.management.timer -javax.naming -javax.naming.directory -javax.naming.event -javax.naming.ldap -javax.naming.spi -javax.net -javax.net.ssl javax.print javax.print.attribute javax.print.attribute.standard javax.print.event -javax.rmi -javax.rmi.CORBA -javax.rmi.ssl -javax.script -javax.security.auth -javax.security.auth.callback -javax.security.auth.kerberos -javax.security.auth.login -javax.security.auth.spi -javax.security.auth.x500 -javax.security.cert -javax.security.sasl javax.sound.midi javax.sound.midi.spi javax.sound.sampled javax.sound.sampled.spi -javax.sql -javax.sql.rowset -javax.sql.rowset.serial -javax.sql.rowset.spi javax.swing javax.swing.border javax.swing.colorchooser @@ -140,26 +110,64 @@ javax.swing.text.html.parser javax.swing.text.rtf javax.swing.tree javax.swing.undo -javax.tools -javax.transaction +module:java.instrument +java.lang.instrument +module:java.logging +java.util.logging +module:java.management +java.lang.management +javax.management +javax.management.loading +javax.management.modelmbean +javax.management.monitor +javax.management.openmbean +javax.management.relation +javax.management.remote +javax.management.timer +module:java.management.rmi +javax.management.remote.rmi +module:java.naming +javax.naming +javax.naming.directory +javax.naming.event +javax.naming.ldap +javax.naming.spi +module:java.net.http +java.net.http +module:java.prefs +java.util.prefs +module:java.rmi +java.rmi +java.rmi.activation +java.rmi.dgc +java.rmi.registry +java.rmi.server +javax.rmi.ssl +module:java.scripting +javax.script +module:java.se +module:java.security.jgss +javax.security.auth.kerberos +org.ietf.jgss +module:java.security.sasl +javax.security.sasl +module:java.smartcardio +javax.smartcardio +module:java.sql +java.sql +javax.sql +module:java.sql.rowset +javax.sql.rowset +javax.sql.rowset.serial +javax.sql.rowset.spi +module:java.transaction.xa javax.transaction.xa +module:java.xml javax.xml -javax.xml.bind -javax.xml.bind.annotation -javax.xml.bind.annotation.adapters -javax.xml.bind.attachment -javax.xml.bind.helpers -javax.xml.bind.util -javax.xml.crypto -javax.xml.crypto.dom -javax.xml.crypto.dsig -javax.xml.crypto.dsig.dom -javax.xml.crypto.dsig.keyinfo -javax.xml.crypto.dsig.spec +javax.xml.catalog javax.xml.datatype javax.xml.namespace javax.xml.parsers -javax.xml.soap javax.xml.stream javax.xml.stream.events javax.xml.stream.util @@ -169,49 +177,106 @@ javax.xml.transform.sax javax.xml.transform.stax javax.xml.transform.stream javax.xml.validation -javax.xml.ws -javax.xml.ws.handler -javax.xml.ws.handler.soap -javax.xml.ws.http -javax.xml.ws.soap -javax.xml.ws.spi -javax.xml.ws.spi.http -javax.xml.ws.wsaddressing javax.xml.xpath -org.ietf.jgss -org.omg.CORBA -org.omg.CORBA.DynAnyPackage -org.omg.CORBA.ORBPackage -org.omg.CORBA.TypeCodePackage -org.omg.CORBA.portable -org.omg.CORBA_2_3 -org.omg.CORBA_2_3.portable -org.omg.CosNaming -org.omg.CosNaming.NamingContextExtPackage -org.omg.CosNaming.NamingContextPackage -org.omg.Dynamic -org.omg.DynamicAny -org.omg.DynamicAny.DynAnyFactoryPackage -org.omg.DynamicAny.DynAnyPackage -org.omg.IOP -org.omg.IOP.CodecFactoryPackage -org.omg.IOP.CodecPackage -org.omg.Messaging -org.omg.PortableInterceptor -org.omg.PortableInterceptor.ORBInitInfoPackage -org.omg.PortableServer -org.omg.PortableServer.CurrentPackage -org.omg.PortableServer.POAManagerPackage -org.omg.PortableServer.POAPackage -org.omg.PortableServer.ServantLocatorPackage -org.omg.PortableServer.portable -org.omg.SendingContext -org.omg.stub.java.rmi org.w3c.dom org.w3c.dom.bootstrap org.w3c.dom.events org.w3c.dom.ls +org.w3c.dom.ranges +org.w3c.dom.traversal org.w3c.dom.views org.xml.sax org.xml.sax.ext org.xml.sax.helpers +module:java.xml.crypto +javax.xml.crypto +javax.xml.crypto.dom +javax.xml.crypto.dsig +javax.xml.crypto.dsig.dom +javax.xml.crypto.dsig.keyinfo +javax.xml.crypto.dsig.spec +module:jdk.accessibility +com.sun.java.accessibility.util +module:jdk.attach +com.sun.tools.attach +com.sun.tools.attach.spi +module:jdk.charsets +module:jdk.compiler +com.sun.source.doctree +com.sun.source.tree +com.sun.source.util +com.sun.tools.javac +module:jdk.crypto.cryptoki +module:jdk.crypto.ec +module:jdk.dynalink +jdk.dynalink +jdk.dynalink.beans +jdk.dynalink.linker +jdk.dynalink.linker.support +jdk.dynalink.support +module:jdk.editpad +module:jdk.hotspot.agent +module:jdk.httpserver +com.sun.net.httpserver +com.sun.net.httpserver.spi +module:jdk.jartool +com.sun.jarsigner +jdk.security.jarsigner +module:jdk.javadoc +com.sun.javadoc +com.sun.tools.javadoc +jdk.javadoc.doclet +module:jdk.jcmd +module:jdk.jconsole +com.sun.tools.jconsole +module:jdk.jdeps +module:jdk.jdi +com.sun.jdi +com.sun.jdi.connect +com.sun.jdi.connect.spi +com.sun.jdi.event +com.sun.jdi.request +module:jdk.jdwp.agent +module:jdk.jfr +jdk.jfr +jdk.jfr.consumer +module:jdk.jlink +module:jdk.jshell +jdk.jshell +jdk.jshell.execution +jdk.jshell.spi +jdk.jshell.tool +module:jdk.jsobject +netscape.javascript +module:jdk.jstatd +module:jdk.localedata +module:jdk.management +com.sun.management +module:jdk.management.agent +module:jdk.management.jfr +jdk.management.jfr +module:jdk.naming.dns +module:jdk.naming.rmi +module:jdk.net +jdk.net +jdk.nio +module:jdk.pack +module:jdk.rmic +module:jdk.scripting.nashorn +jdk.nashorn.api.scripting +jdk.nashorn.api.tree +module:jdk.sctp +com.sun.nio.sctp +module:jdk.security.auth +com.sun.security.auth +com.sun.security.auth.callback +com.sun.security.auth.login +com.sun.security.auth.module +module:jdk.security.jgss +com.sun.security.jgss +module:jdk.xml.dom +org.w3c.dom.css +org.w3c.dom.html +org.w3c.dom.stylesheets +org.w3c.dom.xpath +module:jdk.zipfs diff --git a/gradle/mockito-core/javadoc.gradle b/gradle/mockito-core/javadoc.gradle index b4334b1b2a..9a995ec45d 100644 --- a/gradle/mockito-core/javadoc.gradle +++ b/gradle/mockito-core/javadoc.gradle @@ -1,10 +1,6 @@ //It seems the gradle javadoc task works file by file and as such disable some features of javadoc tool //such as link to packages, https://groups.google.com/d/msg/gradle-dev/R83dy_6PHMc/bgw0cUTMFAAJ - -// In this directory we keep two copies of the `package-list` file for java 8 api docs: -// - as `package-list` for Java 9+ -// - as `element-list` for java 8 -def java8DocsDir = 'gradle/mockito-core/java-8-docs' +def javaDocsDir = 'gradle/mockito-core/java-docs' javadoc { description "Creates javadoc html for Mockito API." @@ -44,8 +40,8 @@ javadoc { options.noIndex = false options.noNavBar = false options.noTree = false - options.links('https://junit.org/junit4/javadoc/4.12/') - options.linksOffline('https://docs.oracle.com/javase/8/docs/api/', java8DocsDir) + options.links('https://junit.org/junit4/javadoc/4.13.2/') + options.linksOffline('https://docs.oracle.com/en/java/javase/11/docs/api/', javaDocsDir) options.bottom(""" diff --git a/gradle/root/coverage.gradle b/gradle/root/coverage.gradle index 87d0d7da01..cd48775763 100644 --- a/gradle/root/coverage.gradle +++ b/gradle/root/coverage.gradle @@ -5,21 +5,13 @@ task mockitoCoverage(type: JacocoReport) { if (currentProject.name in ['android']) { return } - // We only run these tests on Java 9+ - if (currentProject.name in ['module-test'] && !JavaVersion.current().isJava9Compatible()) { - return - } - // We only run these tests on Java 8 - if (currentProject.name in ['errorprone'] && JavaVersion.current().isJava9Compatible()) { - return - } plugins.withId("java") { mockitoCoverage.sourceSets currentProject.sourceSets.main apply plugin: "jacoco" jacoco { - toolVersion = '0.8.7' + toolVersion = '0.8.8' if (currentProject != rootProject) { //already automatically enhanced in mockito main project as "java" plugin was applied before applyTo test diff --git a/settings.gradle.kts b/settings.gradle.kts index 7c3c09c74d..3fdc394e6a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,13 +18,8 @@ include("subclass", "memory-test", "junitJupiterParallelTest", "osgi-test", - "bom") - -if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_11)) { - include("errorprone") -} else { - logger.info("Not including errorprone, which requires minimum JDK 11+") -} + "bom", + "errorprone") if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17) && (System.getenv("ANDROID_SDK_ROOT") != null || File(".local.properties").exists())) { include("androidTest") diff --git a/src/main/java/org/mockito/internal/debugging/LocationImpl.java b/src/main/java/org/mockito/internal/debugging/LocationImpl.java new file mode 100644 index 0000000000..ea72eeb7fd --- /dev/null +++ b/src/main/java/org/mockito/internal/debugging/LocationImpl.java @@ -0,0 +1,211 @@ +/* + * 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.StackWalker.Option; +import java.lang.StackWalker.StackFrame; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class LocationImpl 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."; + + /** + * 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 StackWalker STACK_WALKER = stackWalker(); + + 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; + + LocationImpl(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(LocationImpl.class.getName()); + }); + } + + private static T stackWalk(Function, T> function) { + return (T) STACK_WALKER.walk(function); + } + + private static StackWalker stackWalker() { + return StackWalker.getInstance( + Collections.singleton(Option.SHOW_REFLECT_FRAMES), BUFFER_SIZE); + } + + private static final class MetadataShim implements StackFrameMetadata, Serializable { + private static final long serialVersionUID = 8491903719411428648L; + private final StackFrame stackFrame; + + private MetadataShim(StackFrame stackFrame) { + this.stackFrame = stackFrame; + } + + @Override + public String getClassName() { + return stackFrame.getClassName(); + } + + @Override + public String getMethodName() { + return stackFrame.getMethodName(); + } + + @Override + public String getFileName() { + return stackFrame.getFileName(); + } + + @Override + public int getLineNumber() { + return stackFrame.getLineNumber(); + } + + @Override + public String toString() { + return stackFrame.toString(); + } + + /** + * Ensure that this type remains serializable. + */ + private Object writeReplace() { + return new SerializableShim(stackFrame.toStackTraceElement()); + } + } + + 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/subprojects/androidTest/androidTest.gradle b/subprojects/androidTest/androidTest.gradle index b6c32495be..037b99cf72 100644 --- a/subprojects/androidTest/androidTest.gradle +++ b/subprojects/androidTest/androidTest.gradle @@ -26,8 +26,8 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { jvmTarget = '1.8' diff --git a/subprojects/errorprone/errorprone.gradle b/subprojects/errorprone/errorprone.gradle index e3ef7a7b77..effbb91d2c 100644 --- a/subprojects/errorprone/errorprone.gradle +++ b/subprojects/errorprone/errorprone.gradle @@ -10,14 +10,30 @@ dependencies { implementation project.rootProject implementation libraries.errorprone - testImplementation 'junit:junit:4.13-beta-1' + testImplementation 'junit:junit:4.13.2' testImplementation libraries.errorproneTestApi } test { inputs.files(configurations.errorproneJavac).withNormalizer(ClasspathNormalizer) - jvmArgs += "-Xbootclasspath/p:${configurations.errorproneJavac.asPath}" + jvmArgs += "-Xbootclasspath/a:${configurations.errorproneJavac.asPath}" + jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED" + jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.type=ALL-UNNAMED" + jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED" + jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" + jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED" + jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED" + jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED" + jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED" + jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED" +} + +tasks.withType(JavaCompile) { + options.compilerArgs << "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED" + options.compilerArgs << "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED" + options.compilerArgs << "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" +} - // ErrorProne can only run on JDK 8 - it.enabled = !JavaVersion.current().isJava9Compatible() +tasks.withType(Javadoc) { + options.addBooleanOption("-add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", true) } diff --git a/subprojects/inline/inline.gradle b/subprojects/inline/inline.gradle index f96cffce9f..ef421f0c02 100644 --- a/subprojects/inline/inline.gradle +++ b/subprojects/inline/inline.gradle @@ -14,8 +14,6 @@ tasks.javadoc.enabled = false test.maxHeapSize = "256m" retryTest.maxHeapSize = "256m" -if (JavaVersion.current().java9Compatible) { - test { - jvmArgs '--illegal-access=deny' - } +test { + jvmArgs '--illegal-access=deny' } diff --git a/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/CircularityTest.kt b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/CircularityTest.kt index 82fccea672..cd499897be 100644 --- a/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/CircularityTest.kt +++ b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/CircularityTest.kt @@ -1,3 +1,7 @@ +/** + * Copyright (c) 2017 Mockito contributors + * This program is made available under the terms of the MIT License. + */ package org.mockito.kotlin import org.junit.Before diff --git a/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt index daa60a9a9a..8100b1282d 100644 --- a/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt +++ b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt @@ -1,3 +1,7 @@ +/** + * Copyright (c) 2017 Mockito contributors + * This program is made available under the terms of the MIT License. + */ package org.mockito.kotlin import org.junit.Assert.assertEquals diff --git a/subprojects/module-test/module-test.gradle b/subprojects/module-test/module-test.gradle index 3be8730cc6..d38a32c7d1 100644 --- a/subprojects/module-test/module-test.gradle +++ b/subprojects/module-test/module-test.gradle @@ -2,10 +2,6 @@ plugins { id 'java' } -if (JavaVersion.current() == JavaVersion.VERSION_1_8) { - project.tasks.all { task -> task.enabled = false } -} - description = "Test suite for Java 9 modules with Mockito" apply from: "$rootDir/gradle/dependencies.gradle" @@ -18,5 +14,5 @@ dependencies { tasks.javadoc.enabled = false -sourceCompatibility = 1.9 -targetCompatibility = 1.9 +sourceCompatibility = 11 +targetCompatibility = 11 From a576e8d20392e66952dd931521b16a3c470d119e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 19:15:15 +0100 Subject: [PATCH 105/160] Bump biz.aQute.bnd.gradle from 6.3.1 to 6.4.0 (#2827) Bumps [biz.aQute.bnd.gradle](https://github.com/bndtools/bnd) from 6.3.1 to 6.4.0. - [Release notes](https://github.com/bndtools/bnd/releases) - [Changelog](https://github.com/bndtools/bnd/blob/master/docs/ADDING_RELEASE_DOCS.md) - [Commits](https://github.com/bndtools/bnd/compare/6.3.1...6.4.0) --- updated-dependencies: - dependency-name: biz.aQute.bnd:biz.aQute.bnd.gradle 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> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 12cfaa4112..a5869303d8 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -30,7 +30,7 @@ libraries.objenesis = 'org.objenesis:objenesis:3.3' libraries.osgi = 'org.osgi:osgi.core:8.0.0' libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.18.100' -libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.3.1' +libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.4.0' libraries.groovy = 'org.codehaus.groovy:groovy:3.0.13' From abbd2c86d9bf0af961e47927520e05f6c6726dea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 21:09:39 +0100 Subject: [PATCH 106/160] Bump org.eclipse.osgi from 3.18.100 to 3.18.200 (#2828) Bumps [org.eclipse.osgi](https://github.com/eclipse-equinox/equinox) from 3.18.100 to 3.18.200. - [Release notes](https://github.com/eclipse-equinox/equinox/releases) - [Commits](https://github.com/eclipse-equinox/equinox/commits) --- updated-dependencies: - dependency-name: org.eclipse.platform:org.eclipse.osgi 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 a5869303d8..edcdfb3e46 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -29,7 +29,7 @@ libraries.autoservice = "com.google.auto.service:auto-service:1.0.1" libraries.objenesis = 'org.objenesis:objenesis:3.3' libraries.osgi = 'org.osgi:osgi.core:8.0.0' -libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.18.100' +libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.18.200' libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.4.0' libraries.groovy = 'org.codehaus.groovy:groovy:3.0.13' From 048731e9fc955789fe90efba6828262ce6e4f5d6 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Wed, 21 Dec 2022 02:29:34 -0500 Subject: [PATCH 107/160] Move test to separate test suite to avoid assertion error (#2829) Fixes #2823 Signed-off-by: Andriy Redko --- settings.gradle.kts | 3 ++- .../programmatic-test/programmatic-test.gradle | 18 ++++++++++++++++++ .../org/mockito/ProgrammaticMockMakerTest.java | 0 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 subprojects/programmatic-test/programmatic-test.gradle rename {src => subprojects/programmatic-test/src}/test/java/org/mockito/ProgrammaticMockMakerTest.java (100%) diff --git a/settings.gradle.kts b/settings.gradle.kts index 3fdc394e6a..551d0fa05f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,7 +19,8 @@ include("subclass", "junitJupiterParallelTest", "osgi-test", "bom", - "errorprone") + "errorprone", + "programmatic-test") if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17) && (System.getenv("ANDROID_SDK_ROOT") != null || File(".local.properties").exists())) { include("androidTest") diff --git a/subprojects/programmatic-test/programmatic-test.gradle b/subprojects/programmatic-test/programmatic-test.gradle new file mode 100644 index 0000000000..365dad93e3 --- /dev/null +++ b/subprojects/programmatic-test/programmatic-test.gradle @@ -0,0 +1,18 @@ +plugins { + id 'java' +} + +description = "Test suite for excercising programmatic mock maker in Mockito" + +apply from: "$rootDir/gradle/dependencies.gradle" + +dependencies { + implementation project.rootProject + testImplementation libraries.junit4 + testImplementation libraries.assertj +} + +tasks.javadoc.enabled = false + +sourceCompatibility = 11 +targetCompatibility = 11 diff --git a/src/test/java/org/mockito/ProgrammaticMockMakerTest.java b/subprojects/programmatic-test/src/test/java/org/mockito/ProgrammaticMockMakerTest.java similarity index 100% rename from src/test/java/org/mockito/ProgrammaticMockMakerTest.java rename to subprojects/programmatic-test/src/test/java/org/mockito/ProgrammaticMockMakerTest.java From ca7ee71d2bf3ff28c41ba4ad7360d712f7581bbe Mon Sep 17 00:00:00 2001 From: Mathieu Fortin Date: Wed, 21 Dec 2022 15:45:13 -0500 Subject: [PATCH 108/160] Update .gitignore for VSCode (#2830) --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5267dfa417..17410c4d48 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ .settings/ bin/ +# VSCode +.vscode/ + # Gradle & builds build build-cache @@ -26,4 +29,4 @@ classes *.log # Used to check reproducibility of Mockito jars -checksums/ \ No newline at end of file +checksums/ From fe1cb2de0923e78bf7d7ae46cbab792dd4e94136 Mon Sep 17 00:00:00 2001 From: Andy Coates <8012398+big-andy-coates@users.noreply.github.com> Date: Thu, 22 Dec 2022 16:04:55 +0000 Subject: [PATCH 109/160] Add `type()` method to `ArgumentMatcher` (#2807) Using the new `type()`, we can differentiate between matching all varargs or only one argument of the varargs. # Benefits: Because this approach leaves `VarargsMatcher` untouched, it does not require additional existing matchers to implement `VarargsMatcher` to fix issues such as https://github.com/mockito/mockito/issues/567. Where as the first PR would require `Null` and `NotNull` to be marked `VarargsMatcher`. This PR creates new variants of `isNotNull` and `isNull` to address https://github.com/mockito/mockito/issues/567. Having `InstanceOf` override `type()` provides a workable solution to https://github.com/mockito/mockito/issues/1593. Having `equals` override `type` addresses https://github.com/mockito/mockito/issues/1222. # Downsides The obvious downside is that this changes the public `ArgumentMatcher` interface, though in a backwards compatible way. ## Known limitation The main limitation I'm aware of, is not a new limitation. It is that it is not possible to assert only a single parameter is passed to the vararg parameter, when using a `VarargMatcher`, e.g. `any()`. (ref: https://github.com/mockito/mockito/issues/1593). For example: ```java // Given method: int vararg(String... args); // I want to mock this invocation: mock.vararag("one param"); // ...but not these: mock.vararg(); mock.vararg("more than", "one param"); ``` There is no current way to do this. This is because in the following intuitive mocking: ```java given(mock.vararg(any(String.class))).willReturn(1); ``` ... matches zero or more vararg parameters, as the `any()` method is using `VarargMatcher`. It seems to me that `VarargMatcher` is... a little broken! This is maybe something that should be consider a candiate for fixing in the next major version bump. While it is not possible to fix any `VarargMatcher` based matchers in a backwards compatible way, this the approach in this PR it is possible to mock/verify exactly one vararg param using `isA`, rather than `any`: ```java @Test public void shouldMatchExactlyOnParam() { mock.varargs("one param"); verify(mock).varargs(isA(String.class)); } @Test public void shouldNotMatchMoreParams() { mock.varargs("two", "params"); verify(mock, never()).varargs(isA(String.class)); } @Test public void shouldMatchAnyNumberOfParams() { mock.varargs("two", "params"); verify(mock).varargs(isA(String[].class)); } ``` ... because `isA` does not implement `VarargsMatcher`, and so can work as expected once it implements `type()`. Fixes #2796 Fixes #567 Fixes #584 Fixes #1222 Fixes #1498 --- src/main/java/org/mockito/ArgumentCaptor.java | 3 +- .../java/org/mockito/ArgumentMatcher.java | 41 +++ .../java/org/mockito/ArgumentMatchers.java | 57 +++ .../hamcrest/HamcrestArgumentMatcher.java | 1 + .../MatcherApplicationStrategy.java | 36 +- .../org/mockito/internal/matchers/And.java | 7 + .../org/mockito/internal/matchers/Any.java | 5 + .../internal/matchers/CapturingMatcher.java | 11 + .../org/mockito/internal/matchers/Equals.java | 5 + .../mockito/internal/matchers/InstanceOf.java | 12 +- .../org/mockito/internal/matchers/Not.java | 5 + .../mockito/internal/matchers/NotNull.java | 16 +- .../org/mockito/internal/matchers/Null.java | 15 +- .../org/mockito/internal/matchers/Or.java | 7 + .../org/mockito/internal/matchers/Same.java | 5 + .../internal/matchers/VarargMatcher.java | 9 + .../invocation/InvocationMatcherTest.java | 4 +- .../MatcherApplicationStrategyTest.java | 16 + .../mockito/internal/matchers/AndTest.java | 62 ++++ .../matchers/CapturingMatcherTest.java | 8 +- .../mockito/internal/matchers/EqualsTest.java | 13 + .../org/mockito/internal/matchers/OrTest.java | 62 ++++ .../mockito/internal/matchers/SameTest.java | 25 ++ src/test/java/org/mockitousage/IMethods.java | 12 +- .../java/org/mockitousage/MethodsImpl.java | 23 +- .../mockitousage/matchers/VarargsTest.java | 330 ++++++++++++++++-- 26 files changed, 723 insertions(+), 67 deletions(-) create mode 100644 src/test/java/org/mockito/internal/matchers/AndTest.java create mode 100644 src/test/java/org/mockito/internal/matchers/OrTest.java create mode 100644 src/test/java/org/mockito/internal/matchers/SameTest.java diff --git a/src/main/java/org/mockito/ArgumentCaptor.java b/src/main/java/org/mockito/ArgumentCaptor.java index afb3add2ba..ed9e398f91 100644 --- a/src/main/java/org/mockito/ArgumentCaptor.java +++ b/src/main/java/org/mockito/ArgumentCaptor.java @@ -62,11 +62,12 @@ @CheckReturnValue public class ArgumentCaptor { - private final CapturingMatcher capturingMatcher = new CapturingMatcher(); + private final CapturingMatcher capturingMatcher; private final Class clazz; private ArgumentCaptor(Class clazz) { this.clazz = clazz; + this.capturingMatcher = new CapturingMatcher(clazz); } /** diff --git a/src/main/java/org/mockito/ArgumentMatcher.java b/src/main/java/org/mockito/ArgumentMatcher.java index d0324b6f3f..ad52e1ecae 100644 --- a/src/main/java/org/mockito/ArgumentMatcher.java +++ b/src/main/java/org/mockito/ArgumentMatcher.java @@ -110,6 +110,7 @@ * @param type of argument * @since 2.1.0 */ +@FunctionalInterface public interface ArgumentMatcher { /** @@ -125,4 +126,44 @@ public interface ArgumentMatcher { * @return true if this matcher accepts the given argument. */ boolean matches(T argument); + + /** + * The type of the argument this matcher matches. + * + *

    This method is used to differentiate between a matcher used to match a raw vararg array parameter + * from a matcher used to match a single value passed as a vararg parameter. + * + *

    Where the matcher: + *

      + *
    • is at the parameter index of a vararg parameter
    • + *
    • is the last matcher passed
    • + *
    • this method returns a type assignable to the vararg parameter's raw type, i.e. its array type.
    • + *
    + * + * ...then the matcher is matched against the raw vararg parameter, rather than the first element of the raw parameter. + * + *

    For example: + * + *

    
    +     *  // Given vararg method with signature:
    +     *  int someVarargMethod(String... args);
    +     *
    +     *  // The following will match invocations with any number of parameters, i.e. any number of elements in the raw array.
    +     *  mock.someVarargMethod(isA(String[].class));
    +     *
    +     *  // The following will match invocations with a single parameter, i.e. one string in the raw array.
    +     *  mock.someVarargMethod(isA(String.class));
    +     *
    +     *  // The following will match invocations with two parameters, i.e. two strings in the raw array
    +     *  mock.someVarargMethod(isA(String.class), isA(String.class));
    +     * 
    + * + *

    Only matcher implementations that can conceptually match a raw vararg parameter should override this method. + * + * @return the type this matcher handles. The default value of {@link Void} means the type is not known. + * @since 5.0.0 + */ + default Class type() { + return Void.class; + } } diff --git a/src/main/java/org/mockito/ArgumentMatchers.java b/src/main/java/org/mockito/ArgumentMatchers.java index d969bc79e7..28986e1648 100644 --- a/src/main/java/org/mockito/ArgumentMatchers.java +++ b/src/main/java/org/mockito/ArgumentMatchers.java @@ -699,6 +699,23 @@ public static T isNull() { return null; } + /** + * null argument. + * + *

    + * See examples in javadoc for {@link ArgumentMatchers} class + *

    + * + * @param type the type of the argument being matched. + * @return null. + * @see #isNotNull(Class) + * @since 5.0.0 + */ + public static T isNull(Class type) { + reportMatcher(new Null<>(type)); + return null; + } + /** * Not null argument. * @@ -717,6 +734,26 @@ public static T notNull() { return null; } + /** + * Not null argument. + * + *

    + * Alias to {@link ArgumentMatchers#isNotNull()} + *

    + * + *

    + * See examples in javadoc for {@link ArgumentMatchers} class + *

    + * + * @param type the type of the argument being matched. + * @return null. + * @since 5.0.0 + */ + public static T notNull(Class type) { + reportMatcher(new NotNull<>(type)); + return null; + } + /** * Not null argument. * @@ -735,6 +772,26 @@ public static T isNotNull() { return notNull(); } + /** + * Not null argument. + * + *

    + * Alias to {@link ArgumentMatchers#notNull(Class)} + *

    + * + *

    + * See examples in javadoc for {@link ArgumentMatchers} class + *

    + * + * @param type the type of the argument being matched. + * @return null. + * @see #isNull() + * @since 5.0.0 + */ + public static T isNotNull(Class type) { + return notNull(type); + } + /** * Argument that is either null or of the given type. * diff --git a/src/main/java/org/mockito/internal/hamcrest/HamcrestArgumentMatcher.java b/src/main/java/org/mockito/internal/hamcrest/HamcrestArgumentMatcher.java index 99869faf73..db526546eb 100644 --- a/src/main/java/org/mockito/internal/hamcrest/HamcrestArgumentMatcher.java +++ b/src/main/java/org/mockito/internal/hamcrest/HamcrestArgumentMatcher.java @@ -22,6 +22,7 @@ public boolean matches(Object argument) { return this.matcher.matches(argument); } + @SuppressWarnings("deprecation") public boolean isVarargMatcher() { return matcher instanceof VarargMatcher; } diff --git a/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java b/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java index 5e16f27040..f3c0111c64 100644 --- a/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java +++ b/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java @@ -58,17 +58,24 @@ public static MatcherApplicationStrategy getMatcherApplicationStrategyFor( * */ public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) { + final boolean maybeVararg = + invocation.getMethod().isVarArgs() + && invocation.getRawArguments().length == matchers.size(); + + if (maybeVararg) { + final Class matcherType = lastMatcher().type(); + final Class paramType = lastParameterType(); + if (paramType.isAssignableFrom(matcherType)) { + return argsMatch(invocation.getRawArguments(), matchers, action); + } + } + if (invocation.getArguments().length == matchers.size()) { return argsMatch(invocation.getArguments(), matchers, action); } - final boolean isVararg = - invocation.getMethod().isVarArgs() - && invocation.getRawArguments().length == matchers.size() - && isLastMatcherVarargMatcher(matchers); - - if (isVararg) { - int times = varargLength(invocation); + if (maybeVararg && isLastMatcherVarargMatcher()) { + int times = varargLength(); final List> matchers = appendLastMatcherNTimes(times); return argsMatch(invocation.getArguments(), matchers, action); } @@ -91,8 +98,8 @@ private boolean argsMatch( return true; } - private static boolean isLastMatcherVarargMatcher(List> matchers) { - ArgumentMatcher argumentMatcher = lastMatcher(matchers); + private boolean isLastMatcherVarargMatcher() { + ArgumentMatcher argumentMatcher = lastMatcher(); if (argumentMatcher instanceof HamcrestArgumentMatcher) { return ((HamcrestArgumentMatcher) argumentMatcher).isVarargMatcher(); } @@ -101,7 +108,7 @@ private static boolean isLastMatcherVarargMatcher(List> appendLastMatcherNTimes( int timesToAppendLastMatcher) { - ArgumentMatcher lastMatcher = lastMatcher(matchers); + ArgumentMatcher lastMatcher = lastMatcher(); List> expandedMatchers = new ArrayList>(matchers); for (int i = 0; i < timesToAppendLastMatcher; i++) { @@ -110,13 +117,18 @@ private List> appendLastMatcherNTimes( return expandedMatchers; } - private static int varargLength(Invocation invocation) { + private int varargLength() { int rawArgumentCount = invocation.getRawArguments().length; int expandedArgumentCount = invocation.getArguments().length; return expandedArgumentCount - rawArgumentCount; } - private static ArgumentMatcher lastMatcher(List> matchers) { + private ArgumentMatcher lastMatcher() { return matchers.get(matchers.size() - 1); } + + private Class lastParameterType() { + final Class[] parameterTypes = invocation.getMethod().getParameterTypes(); + return parameterTypes[parameterTypes.length - 1]; + } } diff --git a/src/main/java/org/mockito/internal/matchers/And.java b/src/main/java/org/mockito/internal/matchers/And.java index 417e88dbbe..cffed323f1 100644 --- a/src/main/java/org/mockito/internal/matchers/And.java +++ b/src/main/java/org/mockito/internal/matchers/And.java @@ -23,6 +23,13 @@ public boolean matches(Object actual) { return m1.matches(actual) && m2.matches(actual); } + @Override + public Class type() { + return m1.type().isAssignableFrom(m2.type()) + ? m1.type() + : m2.type().isAssignableFrom(m1.type()) ? m2.type() : ArgumentMatcher.super.type(); + } + @Override public String toString() { return "and(" + m1 + ", " + m2 + ")"; diff --git a/src/main/java/org/mockito/internal/matchers/Any.java b/src/main/java/org/mockito/internal/matchers/Any.java index 7ad113feed..1a71a7e2bc 100644 --- a/src/main/java/org/mockito/internal/matchers/Any.java +++ b/src/main/java/org/mockito/internal/matchers/Any.java @@ -21,4 +21,9 @@ public boolean matches(Object actual) { public String toString() { return ""; } + + @Override + public Class type() { + return Object.class; + } } diff --git a/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java b/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java index 5138839ddb..84da8f57d9 100644 --- a/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java +++ b/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java @@ -9,6 +9,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -19,12 +20,17 @@ public class CapturingMatcher implements ArgumentMatcher, CapturesArguments, VarargMatcher, Serializable { + private final Class clazz; private final List arguments = new ArrayList<>(); private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); + public CapturingMatcher(final Class clazz) { + this.clazz = Objects.requireNonNull(clazz); + } + @Override public boolean matches(Object argument) { return true; @@ -66,4 +72,9 @@ public void captureFrom(Object argument) { writeLock.unlock(); } } + + @Override + public Class type() { + return clazz; + } } diff --git a/src/main/java/org/mockito/internal/matchers/Equals.java b/src/main/java/org/mockito/internal/matchers/Equals.java index 8b07b2da75..bbfad627c4 100644 --- a/src/main/java/org/mockito/internal/matchers/Equals.java +++ b/src/main/java/org/mockito/internal/matchers/Equals.java @@ -22,6 +22,11 @@ public boolean matches(Object actual) { return Equality.areEqual(this.wanted, actual); } + @Override + public Class type() { + return wanted != null ? wanted.getClass() : ArgumentMatcher.super.type(); + } + @Override public String toString() { return describe(wanted); diff --git a/src/main/java/org/mockito/internal/matchers/InstanceOf.java b/src/main/java/org/mockito/internal/matchers/InstanceOf.java index 1b4b30e875..9ea762bb96 100644 --- a/src/main/java/org/mockito/internal/matchers/InstanceOf.java +++ b/src/main/java/org/mockito/internal/matchers/InstanceOf.java @@ -11,7 +11,7 @@ public class InstanceOf implements ArgumentMatcher, Serializable { - private final Class clazz; + final Class clazz; private final String description; public InstanceOf(Class clazz) { @@ -30,6 +30,11 @@ public boolean matches(Object actual) { || clazz.isAssignableFrom(actual.getClass())); } + @Override + public Class type() { + return clazz; + } + @Override public String toString() { return description; @@ -44,5 +49,10 @@ public VarArgAware(Class clazz) { public VarArgAware(Class clazz, String describedAs) { super(clazz, describedAs); } + + @Override + public Class type() { + return clazz; + } } } diff --git a/src/main/java/org/mockito/internal/matchers/Not.java b/src/main/java/org/mockito/internal/matchers/Not.java index bd35eafbad..62b91b5c64 100644 --- a/src/main/java/org/mockito/internal/matchers/Not.java +++ b/src/main/java/org/mockito/internal/matchers/Not.java @@ -22,6 +22,11 @@ public boolean matches(Object actual) { return !matcher.matches(actual); } + @Override + public Class type() { + return matcher.type(); + } + @Override public String toString() { return "not(" + matcher + ")"; diff --git a/src/main/java/org/mockito/internal/matchers/NotNull.java b/src/main/java/org/mockito/internal/matchers/NotNull.java index 2902f57061..f3f7fe7d05 100644 --- a/src/main/java/org/mockito/internal/matchers/NotNull.java +++ b/src/main/java/org/mockito/internal/matchers/NotNull.java @@ -5,20 +5,30 @@ package org.mockito.internal.matchers; import java.io.Serializable; +import java.util.Objects; import org.mockito.ArgumentMatcher; -public class NotNull implements ArgumentMatcher, Serializable { +public class NotNull implements ArgumentMatcher, Serializable { - public static final NotNull NOT_NULL = new NotNull(); + public static final NotNull NOT_NULL = new NotNull<>(Object.class); - private NotNull() {} + private final Class type; + + public NotNull(Class type) { + this.type = Objects.requireNonNull(type); + } @Override public boolean matches(Object actual) { return actual != null; } + @Override + public Class type() { + return type; + } + @Override public String toString() { return "notNull()"; diff --git a/src/main/java/org/mockito/internal/matchers/Null.java b/src/main/java/org/mockito/internal/matchers/Null.java index 69eee484a1..400170a5cc 100644 --- a/src/main/java/org/mockito/internal/matchers/Null.java +++ b/src/main/java/org/mockito/internal/matchers/Null.java @@ -5,20 +5,29 @@ package org.mockito.internal.matchers; import java.io.Serializable; +import java.util.Objects; import org.mockito.ArgumentMatcher; -public class Null implements ArgumentMatcher, Serializable { +public class Null implements ArgumentMatcher, Serializable { - public static final Null NULL = new Null(); + public static final Null NULL = new Null<>(Object.class); + private final Class type; - private Null() {} + public Null(Class type) { + this.type = Objects.requireNonNull(type); + } @Override public boolean matches(Object actual) { return actual == null; } + @Override + public Class type() { + return type; + } + @Override public String toString() { return "isNull()"; diff --git a/src/main/java/org/mockito/internal/matchers/Or.java b/src/main/java/org/mockito/internal/matchers/Or.java index ed7bbdeb4d..7354814323 100644 --- a/src/main/java/org/mockito/internal/matchers/Or.java +++ b/src/main/java/org/mockito/internal/matchers/Or.java @@ -23,6 +23,13 @@ public boolean matches(Object actual) { return m1.matches(actual) || m2.matches(actual); } + @Override + public Class type() { + return m1.type().isAssignableFrom(m2.type()) + ? m1.type() + : m2.type().isAssignableFrom(m1.type()) ? m2.type() : ArgumentMatcher.super.type(); + } + @Override public String toString() { return "or(" + m1 + ", " + m2 + ")"; diff --git a/src/main/java/org/mockito/internal/matchers/Same.java b/src/main/java/org/mockito/internal/matchers/Same.java index 0e23c5cd46..fa116b53f0 100644 --- a/src/main/java/org/mockito/internal/matchers/Same.java +++ b/src/main/java/org/mockito/internal/matchers/Same.java @@ -22,6 +22,11 @@ public boolean matches(Object actual) { return wanted == actual; } + @Override + public Class type() { + return wanted != null ? wanted.getClass() : ArgumentMatcher.super.type(); + } + @Override public String toString() { return "same(" + ValuePrinter.print(wanted) + ")"; diff --git a/src/main/java/org/mockito/internal/matchers/VarargMatcher.java b/src/main/java/org/mockito/internal/matchers/VarargMatcher.java index 43a27596f8..2dfd9b7549 100644 --- a/src/main/java/org/mockito/internal/matchers/VarargMatcher.java +++ b/src/main/java/org/mockito/internal/matchers/VarargMatcher.java @@ -9,5 +9,14 @@ /** * Internal interface that informs Mockito that the matcher is intended to capture varargs. * This information is needed when mockito collects the arguments. + * + * @deprecated use of this interface is deprecated as the behaviour it promotes has limitations. + * It is not recommended for new implementations to implement this method. + * + *

    Instead, matchers should implement the {@link org.mockito.ArgumentMatcher#type()} method. + * If this method returns the same raw type as a vararg parameter, then Mockito will treat the + * matcher as matching the entire vararg array parameter, otherwise it will be treated as matching a single element. + * For an example, see {@link org.mockito.ArgumentMatchers#isNull(Class)}. */ +@Deprecated public interface VarargMatcher extends Serializable {} diff --git a/src/test/java/org/mockito/internal/invocation/InvocationMatcherTest.java b/src/test/java/org/mockito/internal/invocation/InvocationMatcherTest.java index 0b3d09d5f1..ab53be545f 100644 --- a/src/test/java/org/mockito/internal/invocation/InvocationMatcherTest.java +++ b/src/test/java/org/mockito/internal/invocation/InvocationMatcherTest.java @@ -136,7 +136,7 @@ public void should_be_similar_if_is_overloaded_but_used_with_different_arg() thr public void should_capture_arguments_from_invocation() throws Exception { // given Invocation invocation = new InvocationBuilder().args("1", 100).toInvocation(); - CapturingMatcher capturingMatcher = new CapturingMatcher(); + CapturingMatcher capturingMatcher = new CapturingMatcher(List.class); InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, (List) asList(new Equals("1"), capturingMatcher)); @@ -167,7 +167,7 @@ public void should_capture_varargs_as_vararg() throws Exception { // given mock.mixedVarargs(1, "a", "b"); Invocation invocation = getLastInvocation(); - CapturingMatcher m = new CapturingMatcher(); + CapturingMatcher m = new CapturingMatcher(List.class); InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, Arrays.asList(new Equals(1), m)); diff --git a/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java b/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java index baf7a5ad5a..ddd4333905 100644 --- a/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java +++ b/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java @@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.internal.invocation.MatcherApplicationStrategy.getMatcherApplicationStrategyFor; import static org.mockito.internal.matchers.Any.ANY; @@ -225,6 +226,21 @@ public void shouldMatchAnyEvenIfMatcherIsWrappedInHamcrestMatcher() { recordAction.assertContainsExactly(argumentMatcher, argumentMatcher); } + @Test + public void shouldMatchAnyThatMatchesRawVarArgType() { + // given + invocation = varargs("1", "2"); + InstanceOf.VarArgAware any = new InstanceOf.VarArgAware(String[].class, ""); + matchers = asList(any); + + // when + getMatcherApplicationStrategyFor(invocation, matchers) + .forEachMatcherAndArgument(recordAction); + + // then + recordAction.assertContainsExactly(any); + } + private static class IntMatcher extends BaseMatcher implements VarargMatcher { public boolean matches(Object o) { return true; diff --git a/src/test/java/org/mockito/internal/matchers/AndTest.java b/src/test/java/org/mockito/internal/matchers/AndTest.java new file mode 100644 index 0000000000..3115576d9e --- /dev/null +++ b/src/test/java/org/mockito/internal/matchers/AndTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.matchers; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockitoutil.TestBase; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; + +public class AndTest extends TestBase { + + @Mock private ArgumentMatcher m1; + @Mock private ArgumentMatcher m2; + private And and; + + @Before + public void setUp() throws Exception { + and = new And(m1, m2); + } + + @Test + public void shouldReturnMatchingTypes() { + given(m1.type()).will(inv -> String.class); + given(m2.type()).will(inv -> String.class); + + assertThat(and.type()).isEqualTo(String.class); + } + + @Test + public void shouldDefaultMismatchingTypes() { + given(m1.type()).will(inv -> String.class); + given(m2.type()).will(inv -> Integer.class); + + assertThat(and.type()).isEqualTo(Void.class); + } + + @Test + public void shouldReturnLeftBaseType() { + given(m1.type()).will(inv -> BaseType.class); + given(m2.type()).will(inv -> SubType.class); + + assertThat(and.type()).isEqualTo(BaseType.class); + } + + @Test + public void shouldReturnRightBaseType() { + given(m1.type()).will(inv -> SubType.class); + given(m2.type()).will(inv -> BaseType.class); + + assertThat(and.type()).isEqualTo(BaseType.class); + } + + private interface BaseType {} + + private interface SubType extends BaseType {} +} diff --git a/src/test/java/org/mockito/internal/matchers/CapturingMatcherTest.java b/src/test/java/org/mockito/internal/matchers/CapturingMatcherTest.java index a4c6b59149..768d08d0b8 100644 --- a/src/test/java/org/mockito/internal/matchers/CapturingMatcherTest.java +++ b/src/test/java/org/mockito/internal/matchers/CapturingMatcherTest.java @@ -19,7 +19,7 @@ public class CapturingMatcherTest extends TestBase { @Test public void should_capture_arguments() throws Exception { // given - CapturingMatcher m = new CapturingMatcher(); + CapturingMatcher m = new CapturingMatcher(String.class); // when m.captureFrom("foo"); @@ -32,7 +32,7 @@ public void should_capture_arguments() throws Exception { @Test public void should_know_last_captured_value() throws Exception { // given - CapturingMatcher m = new CapturingMatcher(); + CapturingMatcher m = new CapturingMatcher(String.class); // when m.captureFrom("foo"); @@ -45,7 +45,7 @@ public void should_know_last_captured_value() throws Exception { @Test public void should_scream_when_nothing_yet_captured() throws Exception { // given - CapturingMatcher m = new CapturingMatcher(); + CapturingMatcher m = new CapturingMatcher(String.class); try { // when @@ -59,7 +59,7 @@ public void should_scream_when_nothing_yet_captured() throws Exception { @Test public void should_not_fail_when_used_in_concurrent_tests() throws Exception { // given - final CapturingMatcher m = new CapturingMatcher(); + final CapturingMatcher m = new CapturingMatcher(String.class); // when m.captureFrom("concurrent access"); diff --git a/src/test/java/org/mockito/internal/matchers/EqualsTest.java b/src/test/java/org/mockito/internal/matchers/EqualsTest.java index ba5c578b57..e56de2a9c2 100644 --- a/src/test/java/org/mockito/internal/matchers/EqualsTest.java +++ b/src/test/java/org/mockito/internal/matchers/EqualsTest.java @@ -4,9 +4,11 @@ */ package org.mockito.internal.matchers; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import org.junit.Test; +import org.mockito.ArgumentMatcher; import org.mockitoutil.TestBase; public class EqualsTest extends TestBase { @@ -102,4 +104,15 @@ public void shouldMatchTypesSafelyWhenGivenIsNull() throws Exception { // then assertFalse(equals.typeMatches(null)); } + + @Test + public void shouldInferType() { + assertThat(new Equals("String").type()).isEqualTo(String.class); + } + + @Test + public void shouldDefaultTypeOnNull() { + assertThat(new Equals(null).type()) + .isEqualTo(((ArgumentMatcher) argument -> false).type()); + } } diff --git a/src/test/java/org/mockito/internal/matchers/OrTest.java b/src/test/java/org/mockito/internal/matchers/OrTest.java new file mode 100644 index 0000000000..6738d73507 --- /dev/null +++ b/src/test/java/org/mockito/internal/matchers/OrTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.matchers; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockitoutil.TestBase; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; + +public class OrTest extends TestBase { + + @Mock private ArgumentMatcher m1; + @Mock private ArgumentMatcher m2; + private Or or; + + @Before + public void setUp() throws Exception { + or = new Or(m1, m2); + } + + @Test + public void shouldReturnMatchingTypes() { + given(m1.type()).will(inv -> String.class); + given(m2.type()).will(inv -> String.class); + + assertThat(or.type()).isEqualTo(String.class); + } + + @Test + public void shouldDefaultMismatchingTypes() { + given(m1.type()).will(inv -> String.class); + given(m2.type()).will(inv -> Integer.class); + + assertThat(or.type()).isEqualTo(Void.class); + } + + @Test + public void shouldReturnLeftBaseType() { + given(m1.type()).will(inv -> BaseType.class); + given(m2.type()).will(inv -> SubType.class); + + assertThat(or.type()).isEqualTo(BaseType.class); + } + + @Test + public void shouldReturnRightBaseType() { + given(m1.type()).will(inv -> SubType.class); + given(m2.type()).will(inv -> BaseType.class); + + assertThat(or.type()).isEqualTo(BaseType.class); + } + + private interface BaseType {} + + private interface SubType extends BaseType {} +} diff --git a/src/test/java/org/mockito/internal/matchers/SameTest.java b/src/test/java/org/mockito/internal/matchers/SameTest.java new file mode 100644 index 0000000000..d5c6a1f9c8 --- /dev/null +++ b/src/test/java/org/mockito/internal/matchers/SameTest.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.matchers; + +import org.mockitoutil.TestBase; +import org.junit.Test; +import org.mockito.ArgumentMatcher; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SameTest extends TestBase { + + @Test + public void shouldInferType() { + assertThat(new Same("String").type()).isEqualTo(String.class); + } + + @Test + public void shouldDefaultTypeOnNull() { + assertThat(new Same(null).type()) + .isEqualTo(((ArgumentMatcher) argument -> false).type()); + } +} diff --git a/src/test/java/org/mockitousage/IMethods.java b/src/test/java/org/mockitousage/IMethods.java index 06492f09cb..5c1d3d8e5d 100644 --- a/src/test/java/org/mockitousage/IMethods.java +++ b/src/test/java/org/mockitousage/IMethods.java @@ -187,10 +187,12 @@ String sixArgumentVarArgsMethod( int varargs(Object... object); - String varargsReturningString(Object... object); - int varargs(String... string); + int polyVararg(BaseType... args); + + String varargsReturningString(Object... object); + void mixedVarargs(Object i, String... string); String mixedVarargsReturningString(Object i, String... string); @@ -199,6 +201,10 @@ String sixArgumentVarArgsMethod( Object[] mixedVarargsReturningObjectArray(Object i, String... string); + String methodWithVarargAndNonVarargVariants(String string); + + String methodWithVarargAndNonVarargVariants(String... string); + List listReturningMethod(Object... objects); LinkedList linkedListReturningMethod(); @@ -306,4 +312,6 @@ public int hashCode() { return Objects.hash(value); } } + + interface BaseType {} } diff --git a/src/test/java/org/mockitousage/MethodsImpl.java b/src/test/java/org/mockitousage/MethodsImpl.java index e2d53ba7ba..678d3f7fe1 100644 --- a/src/test/java/org/mockitousage/MethodsImpl.java +++ b/src/test/java/org/mockitousage/MethodsImpl.java @@ -354,14 +354,19 @@ public int varargs(Object... object) { return -1; } - public String varargsReturningString(Object... object) { - return null; - } - public int varargs(String... string) { return -1; } + @Override + public int polyVararg(final BaseType... args) { + return 0; + } + + public String varargsReturningString(Object... object) { + return null; + } + public void mixedVarargs(Object i, String... string) {} public String mixedVarargsReturningString(Object i, String... string) { @@ -376,6 +381,16 @@ public Object[] mixedVarargsReturningObjectArray(Object i, String... string) { return null; } + @Override + public String methodWithVarargAndNonVarargVariants(String string) { + return "plain"; + } + + @Override + public String methodWithVarargAndNonVarargVariants(String... string) { + return "varargs"; + } + public void varargsbyte(byte... bytes) {} public List listReturningMethod(Object... objects) { diff --git a/src/test/java/org/mockitousage/matchers/VarargsTest.java b/src/test/java/org/mockitousage/matchers/VarargsTest.java index dfb726942b..0e5456fd2f 100644 --- a/src/test/java/org/mockitousage/matchers/VarargsTest.java +++ b/src/test/java/org/mockitousage/matchers/VarargsTest.java @@ -4,12 +4,18 @@ */ package org.mockitousage.matchers; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.fail; +import static org.mockito.AdditionalMatchers.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.ArgumentMatchers.isNotNull; import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -28,6 +34,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.mockitousage.IMethods; +import org.mockitousage.IMethods.BaseType; public class VarargsTest { @@ -52,27 +59,17 @@ public void shouldMatchVarArgs_noArgs() { } @Test - @Ignore("This test must succeed but is fails currently, see github issue #616") public void shouldMatchEmptyVarArgs_noArgsIsNotNull() { mock.varargs(); - verify(mock).varargs(isNotNull()); + verify(mock).varargs(isNotNull(String[].class)); } @Test - @Ignore("This test must succeed but is fails currently, see github issue #616") public void shouldMatchEmptyVarArgs_noArgsIsNull() { - mock.varargs(); + mock.varargs((String[]) null); - verify(mock).varargs(isNull()); - } - - @Test - @Ignore("This test must succeed but is fails currently, see github issue #616") - public void shouldMatchEmptyVarArgs_noArgsIsNotNullArray() { - mock.varargs(); - - verify(mock).varargs((String[]) isNotNull()); + verify(mock).varargs(isNull(String[].class)); } @Test @@ -178,11 +175,10 @@ public void shouldMatchVarArgs_emptyByteArray() { } @Test - @Ignore public void shouldMatchEmptyVarArgs_emptyArrayIsNotNull() { mock.varargsbyte(); - verify(mock).varargsbyte((byte[]) isNotNull()); + verify(mock).varargsbyte(isNotNull(byte[].class)); } @Test @@ -198,7 +194,7 @@ public void shouldCaptureVarArgs_noArgs() { verify(mock).varargs(captor.capture()); - assertThat(captor).isEmpty(); + assertThatCaptor(captor).isEmpty(); } @Test @@ -208,7 +204,7 @@ public void shouldCaptureVarArgs_oneNullArg_eqNull() { verify(mock).varargs(captor.capture()); - assertThat(captor).areExactly(1, NULL); + assertThatCaptor(captor).areExactly(1, NULL); } /** @@ -221,7 +217,7 @@ public void shouldCaptureVarArgs_nullArrayArg() { mock.varargs(argArray); verify(mock).varargs(captor.capture()); - assertThat(captor).areExactly(1, NULL); + assertThatCaptor(captor).areExactly(1, NULL); } @Test @@ -230,7 +226,7 @@ public void shouldCaptureVarArgs_twoArgsOneCapture() { verify(mock).varargs(captor.capture()); - assertThat(captor).contains("1", "2"); + assertThatCaptor(captor).contains("1", "2"); } @Test @@ -239,7 +235,7 @@ public void shouldCaptureVarArgs_twoArgsTwoCaptures() { verify(mock).varargs(captor.capture(), captor.capture()); - assertThat(captor).contains("1", "2"); + assertThatCaptor(captor).contains("1", "2"); } @Test @@ -248,7 +244,7 @@ public void shouldCaptureVarArgs_oneNullArgument() { verify(mock).varargs(captor.capture()); - assertThat(captor).contains("1", (String) null); + assertThatCaptor(captor).contains("1", (String) null); } @Test @@ -257,7 +253,7 @@ public void shouldCaptureVarArgs_oneNullArgument2() { verify(mock).varargs(captor.capture(), captor.capture()); - assertThat(captor).contains("1", (String) null); + assertThatCaptor(captor).contains("1", (String) null); } @Test @@ -277,7 +273,7 @@ public void shouldCaptureVarArgs_3argsCaptorMatcherMix() { verify(mock).varargs(captor.capture(), eq("2"), captor.capture()); - assertThat(captor).containsExactly("1", "3"); + assertThatCaptor(captor).containsExactly("1", "3"); } @Test @@ -290,7 +286,7 @@ public void shouldNotCaptureVarArgs_3argsCaptorMatcherMix() { } catch (ArgumentsAreDifferent expected) { } - assertThat(captor).isEmpty(); + assertThatCaptor(captor).isEmpty(); } @Test @@ -304,16 +300,7 @@ public void shouldNotCaptureVarArgs_1args2captures() { .isInstanceOf(ArgumentsAreDifferent.class); } - /** - * As of v2.0.0-beta.118 this test fails. Once the github issues: - *
      - *
    • '#584 ArgumentCaptor can't capture varargs-arrays - *
    • #565 ArgumentCaptor should be type aware' are fixed this test must - * succeed - *
    - */ @Test - @Ignore("Blocked by github issue: #584 & #565") public void shouldCaptureVarArgsAsArray() { mock.varargs("1", "2"); @@ -321,7 +308,7 @@ public void shouldCaptureVarArgsAsArray() { verify(mock).varargs(varargCaptor.capture()); - assertThat(varargCaptor).containsExactly(new String[] {"1", "2"}); + assertThatCaptor(varargCaptor).containsExactly(new String[] {"1", "2"}); } @Test @@ -342,8 +329,281 @@ public void shouldNotMatchVaraArgs() { Assertions.assertThat(mock.varargsObject(1)).isNull(); } - private static AbstractListAssert> assertThat( + @Test + public void shouldDifferentiateNonVarargVariant() { + given(mock.methodWithVarargAndNonVarargVariants(any(String.class))) + .willReturn("single arg method"); + + assertThat(mock.methodWithVarargAndNonVarargVariants("a")).isEqualTo("single arg method"); + assertThat(mock.methodWithVarargAndNonVarargVariants(new String[] {"a"})).isNull(); + assertThat(mock.methodWithVarargAndNonVarargVariants("a", "b")).isNull(); + } + + @Test + public void shouldMockVarargsInvocation_single_vararg_matcher() { + given(mock.methodWithVarargAndNonVarargVariants(any(String[].class))) + .willReturn("var arg method"); + + assertThat(mock.methodWithVarargAndNonVarargVariants("a")).isNull(); + assertThat(mock.methodWithVarargAndNonVarargVariants(new String[] {"a"})) + .isEqualTo("var arg method"); + assertThat(mock.methodWithVarargAndNonVarargVariants("a", "b")).isEqualTo("var arg method"); + } + + @Test + public void shouldMockVarargsInvocation_multiple_vararg_matcher() { + given(mock.methodWithVarargAndNonVarargVariants(any(String.class), any(String.class))) + .willReturn("var arg method"); + + assertThat(mock.methodWithVarargAndNonVarargVariants("a")).isNull(); + assertThat(mock.methodWithVarargAndNonVarargVariants(new String[] {"a"})).isNull(); + assertThat(mock.methodWithVarargAndNonVarargVariants("a", "b")).isEqualTo("var arg method"); + assertThat(mock.methodWithVarargAndNonVarargVariants(new String[] {"a", "b"})) + .isEqualTo("var arg method"); + assertThat(mock.methodWithVarargAndNonVarargVariants("a", "b", "c")).isNull(); + } + + @Test + public void shouldMockVarargsInvocationUsingCasts() { + given(mock.methodWithVarargAndNonVarargVariants((String) any())) + .willReturn("single arg method"); + given(mock.methodWithVarargAndNonVarargVariants((String[]) any())) + .willReturn("var arg method"); + + assertThat(mock.methodWithVarargAndNonVarargVariants("a")).isEqualTo("single arg method"); + assertThat(mock.methodWithVarargAndNonVarargVariants()).isEqualTo("var arg method"); + assertThat(mock.methodWithVarargAndNonVarargVariants(new String[] {"a"})) + .isEqualTo("var arg method"); + assertThat(mock.methodWithVarargAndNonVarargVariants("a", "b")).isEqualTo("var arg method"); + } + + @Test + public void shouldMockVarargsInvocationForSuperType() { + given(mock.varargsReturningString(any(Object[].class))).willReturn("a"); + + assertThat(mock.varargsReturningString("a", "b")).isEqualTo("a"); + } + + @Test + public void shouldHandleArrayVarargsMethods() { + given(mock.arrayVarargsMethod(any(String[][].class))).willReturn(1); + + assertThat(mock.arrayVarargsMethod(new String[] {})).isEqualTo(1); + } + + @Test + public void shouldCaptureVarArgs_NullArrayArg1() { + mock.varargs((String[]) null); + ArgumentCaptor captor = ArgumentCaptor.forClass(String[].class); + + verify(mock).varargs(captor.capture()); + + assertThat(captor.getValue()).isNull(); + } + + @Test + public void shouldCaptureVarArgs_NullArrayArg2() { + mock.varargs((String[]) null); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + + verify(mock).varargs(captor.capture()); + + assertThat(captor.getValue()).isNull(); + } + + @Test + public void shouldVerifyVarArgs_any_NullArrayArg1() { + mock.varargs((String[]) null); + + verify(mock).varargs(any()); + } + + @Test + public void shouldVerifyVarArgs_any_NullArrayArg2() { + mock.varargs((String) null); + + verify(mock).varargs(any()); + } + + @Test + public void shouldVerifyVarArgs_eq_NullArrayArg1() { + mock.varargs((String[]) null); + + verify(mock).varargs(eq(null)); + } + + @Test + public void shouldVerifyVarArgs_eq_NullArrayArg2() { + mock.varargs((String) null); + + verify(mock).varargs(eq(null)); + } + + @Test + public void shouldVerifyVarArgs_isNull_NullArrayArg() { + mock.varargs((String) null); + + verify(mock).varargs(isNull(String.class)); + } + + @Test + public void shouldVerifyVarArgs_isNull_NullArrayArg2() { + mock.varargs((String) null); + + verify(mock).varargs(isNull()); + } + + @Test + public void shouldVerifyExactlyOneVarArg_isA() { + mock.varargs("one param"); + + verify(mock).varargs(isA(String.class)); + } + + @Test + public void shouldNotVerifyExactlyOneVarArg_isA() { + mock.varargs("two", "params"); + + verify(mock, never()).varargs(isA(String.class)); + } + + @Test + public void shouldVerifyVarArgArray_isA() { + mock.varargs("one param"); + + verify(mock).varargs(isA(String[].class)); + } + + @Test + public void shouldVerifyVarArgArray_isA2() { + mock.varargs("two", "params"); + + verify(mock).varargs(isA(String[].class)); + } + + @Test + public void shouldVerifyExactlyOneVarArg_any() { + mock.varargs("one param"); + + verify(mock).varargs(any(String.class)); + } + + @Test + @Ignore("Fails due to https://github.com/mockito/mockito/issues/1593") + public void shouldNotVerifyExactlyOneVarArg_any() { + mock.varargs("two", "params"); + + verify(mock, never()).varargs(any(String.class)); + } + + @Test + public void shouldMockVarargInvocation_eq() { + given(mock.varargs(eq("one param"))).willReturn(1); + + assertThat(mock.varargs("one param")).isEqualTo(1); + assertThat(mock.varargs()).isEqualTo(0); + assertThat(mock.varargs("different")).isEqualTo(0); + assertThat(mock.varargs("one param", "another")).isEqualTo(0); + } + + @Test + public void shouldVerifyInvocation_eq() { + mock.varargs("one param"); + + verify(mock).varargs(eq("one param")); + verify(mock, never()).varargs(); + verify(mock, never()).varargs(eq("different")); + verify(mock, never()).varargs(eq("one param"), eq("another")); + } + + @Test + public void shouldMockVarargInvocation_eq_raw() { + given(mock.varargs(eq(new String[] {"one param"}))).willReturn(1); + + assertThat(mock.varargs("one param")).isEqualTo(1); + assertThat(mock.varargs()).isEqualTo(0); + assertThat(mock.varargs("different")).isEqualTo(0); + assertThat(mock.varargs("one param", "another")).isEqualTo(0); + } + + @Test + public void shouldVerifyInvocation_eq_raw() { + mock.varargs("one param"); + + verify(mock).varargs(eq(new String[] {"one param"})); + verify(mock, never()).varargs(eq(new String[] {})); + verify(mock, never()).varargs(eq(new String[] {"different"})); + verify(mock, never()).varargs(eq(new String[] {"one param", "another"})); + } + + @Test + public void shouldVerifyInvocation_not() { + mock.varargs("one param"); + + verify(mock).varargs(not(eq(new String[] {"diff"}))); + verify(mock, never()).varargs(not(eq(new String[] {"one param"}))); + } + + @Test + public void shouldVerifyInvocation_same() { + String[] args = {"two", "params"}; + + mock.varargs(args); + + verify(mock).varargs(same(args)); + verify(mock, never()).varargs(same(new String[] {"two", "params"})); + } + + @Test + public void shouldVerifySubTypes() { + mock.polyVararg(new SubType(), new SubType()); + + verify(mock).polyVararg(eq(new SubType()), eq(new SubType())); + verify(mock).polyVararg(eq(new SubType[] {new SubType(), new SubType()})); + verify(mock).polyVararg(eq(new BaseType[] {new SubType(), new SubType()})); + } + + @Test + public void shouldVerifyInvocation_or() { + mock.polyVararg(new SubType(), new SubType()); + + verify(mock) + .polyVararg( + or( + eq(new BaseType[] {new SubType()}), + eq(new SubType[] {new SubType(), new SubType()}))); + verify(mock) + .polyVararg( + or( + eq(new BaseType[] {new SubType(), new SubType()}), + eq(new SubType[] {new SubType()}))); + } + + @Test + public void shouldVerifyInvocation_and() { + mock.polyVararg(new SubType(), new SubType()); + + verify(mock) + .polyVararg( + and( + eq(new BaseType[] {new SubType(), new SubType()}), + eq(new SubType[] {new SubType(), new SubType()}))); + } + + private static AbstractListAssert> assertThatCaptor( ArgumentCaptor captor) { return Assertions.assertThat(captor.getAllValues()); } + + private static class SubType implements BaseType { + @Override + public boolean equals(final Object obj) { + return obj != null && obj.getClass().equals(getClass()); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + } } From f61d40ddbc483f697e35684fdec6b2b2f8115242 Mon Sep 17 00:00:00 2001 From: Mathieu Fortin Date: Thu, 22 Dec 2022 16:04:14 -0500 Subject: [PATCH 110/160] Fix tests relying on timezone to succeed (#2833) --- ...tiveMessagesWhenVerificationFailsTest.java | 111 ++++++++++-------- 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/src/test/java/org/mockitousage/verification/DescriptiveMessagesWhenVerificationFailsTest.java b/src/test/java/org/mockitousage/verification/DescriptiveMessagesWhenVerificationFailsTest.java index bae7c31df7..5e26386e17 100644 --- a/src/test/java/org/mockitousage/verification/DescriptiveMessagesWhenVerificationFailsTest.java +++ b/src/test/java/org/mockitousage/verification/DescriptiveMessagesWhenVerificationFailsTest.java @@ -370,37 +370,43 @@ public void should_never_break_method_string_when_no_args_in_method() throws Exc @Test public void should_print_fully_qualified_name_when_arguments_classes_have_same_simple_name() { + Date dateArg = new Date(0); + String stringifiedDateArg = dateArg.toString(); + try { - mock.overloadedMethodWithSameClassNameArguments(new Date(0), new IMethods.Date(0)); - verify(mock) - .overloadedMethodWithSameClassNameArguments(new IMethods.Date(0), new Date(0)); + mock.overloadedMethodWithSameClassNameArguments(dateArg, new IMethods.Date(0)); + verify(mock).overloadedMethodWithSameClassNameArguments(new IMethods.Date(0), dateArg); fail(); } catch (Throwable e) { String wanted = - "\n" - + "Argument(s) are different! Wanted:" - + "\n" - + "iMethods.overloadedMethodWithSameClassNameArguments(" - + "\n" - + " (org.mockitousage.IMethods.Date) 0," - + "\n" - + " (java.sql.Date) 1970-01-01" - + "\n" - + ");"; + String.format( + "\n" + + "Argument(s) are different! Wanted:" + + "\n" + + "iMethods.overloadedMethodWithSameClassNameArguments(" + + "\n" + + " (org.mockitousage.IMethods.Date) 0," + + "\n" + + " (java.sql.Date) %s" + + "\n" + + ");", + stringifiedDateArg); assertThat(e).hasMessageContaining(wanted); String actual = - "\n" - + "Actual invocations have different arguments:" - + "\n" - + "iMethods.overloadedMethodWithSameClassNameArguments(" - + "\n" - + " (java.sql.Date) 1970-01-01," - + "\n" - + " (org.mockitousage.IMethods.Date) 0" - + "\n" - + ");"; + String.format( + "\n" + + "Actual invocations have different arguments:" + + "\n" + + "iMethods.overloadedMethodWithSameClassNameArguments(" + + "\n" + + " (java.sql.Date) %s," + + "\n" + + " (org.mockitousage.IMethods.Date) 0" + + "\n" + + ");", + stringifiedDateArg); assertThat(e).hasMessageContaining(actual); } } @@ -445,43 +451,50 @@ public void should_print_fully_qualified_name_when_arguments_classes_have_same_s @Test public void should_print_fully_qualified_name_when_some_arguments_classes_have_same_simple_name() { + Date dateArg = new Date(0); + String stringifiedDateArg = dateArg.toString(); + try { mock.overloadedMethodWithSameClassNameArguments( - new Date(0), "string", new IMethods.Date(0)); + dateArg, "string", new IMethods.Date(0)); verify(mock) .overloadedMethodWithSameClassNameArguments( - new IMethods.Date(0), "string", new Date(0)); + new IMethods.Date(0), "string", dateArg); fail(); } catch (Throwable e) { String wanted = - "\n" - + "Argument(s) are different! Wanted:" - + "\n" - + "iMethods.overloadedMethodWithSameClassNameArguments(" - + "\n" - + " (org.mockitousage.IMethods.Date) 0," - + "\n" - + " \"string\"," - + "\n" - + " (java.sql.Date) 1970-01-01" - + "\n" - + ");"; + String.format( + "\n" + + "Argument(s) are different! Wanted:" + + "\n" + + "iMethods.overloadedMethodWithSameClassNameArguments(" + + "\n" + + " (org.mockitousage.IMethods.Date) 0," + + "\n" + + " \"string\"," + + "\n" + + " (java.sql.Date) %s" + + "\n" + + ");", + stringifiedDateArg); assertThat(e).hasMessageContaining(wanted); String actual = - "\n" - + "Actual invocations have different arguments:" - + "\n" - + "iMethods.overloadedMethodWithSameClassNameArguments(" - + "\n" - + " (java.sql.Date) 1970-01-01," - + "\n" - + " \"string\"," - + "\n" - + " (org.mockitousage.IMethods.Date) 0" - + "\n" - + ");"; + String.format( + "\n" + + "Actual invocations have different arguments:" + + "\n" + + "iMethods.overloadedMethodWithSameClassNameArguments(" + + "\n" + + " (java.sql.Date) %s," + + "\n" + + " \"string\"," + + "\n" + + " (org.mockitousage.IMethods.Date) 0" + + "\n" + + ");", + stringifiedDateArg); assertThat(e).hasMessageContaining(actual); } } From 40e4f6c92cd71cc71d3a1a9681fd4b7e697dc005 Mon Sep 17 00:00:00 2001 From: Mathieu Fortin Date: Thu, 22 Dec 2022 17:22:25 -0500 Subject: [PATCH 111/160] Add unequal arguments positions in failed verification message (#2832) Fixes #2786 --- .../mockito/internal/exceptions/Reporter.java | 60 ++++++++++++++- .../ArgumentMatchingTool.java | 26 ++++++- .../checkers/MissingInvocationChecker.java | 6 +- .../ArgumentMatchingToolTest.java | 48 +++++++++++- .../MissingInvocationCheckerTest.java | 77 +++++++++++++++++++ ...tiveMessagesWhenVerificationFailsTest.java | 8 +- 6 files changed, 213 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/mockito/internal/exceptions/Reporter.java b/src/main/java/org/mockito/internal/exceptions/Reporter.java index 9a5ea90d21..2101e72554 100644 --- a/src/main/java/org/mockito/internal/exceptions/Reporter.java +++ b/src/main/java/org/mockito/internal/exceptions/Reporter.java @@ -10,11 +10,29 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + import org.mockito.exceptions.base.MockitoAssertionError; import org.mockito.exceptions.base.MockitoException; import org.mockito.exceptions.base.MockitoInitializationException; -import org.mockito.exceptions.misusing.*; +import org.mockito.exceptions.misusing.CannotStubVoidMethodWithReturnValue; +import org.mockito.exceptions.misusing.CannotVerifyStubOnlyMock; +import org.mockito.exceptions.misusing.FriendlyReminderException; +import org.mockito.exceptions.misusing.InjectMocksException; +import org.mockito.exceptions.misusing.InvalidUseOfMatchersException; +import org.mockito.exceptions.misusing.MissingMethodInvocationException; +import org.mockito.exceptions.misusing.NotAMockException; +import org.mockito.exceptions.misusing.NullInsteadOfMockException; +import org.mockito.exceptions.misusing.PotentialStubbingProblem; +import org.mockito.exceptions.misusing.RedundantListenerException; +import org.mockito.exceptions.misusing.UnfinishedMockingSessionException; +import org.mockito.exceptions.misusing.UnfinishedStubbingException; +import org.mockito.exceptions.misusing.UnfinishedVerificationException; +import org.mockito.exceptions.misusing.UnnecessaryStubbingException; +import org.mockito.exceptions.misusing.WrongTypeOfReturnValue; import org.mockito.exceptions.verification.MoreThanAllowedActualInvocations; import org.mockito.exceptions.verification.NeverWantedButInvoked; import org.mockito.exceptions.verification.NoInteractionsWanted; @@ -28,10 +46,12 @@ import org.mockito.internal.junit.ExceptionFactory; import org.mockito.internal.matchers.LocalizedMatcher; import org.mockito.internal.util.MockUtil; +import org.mockito.internal.verification.argumentmatching.ArgumentMatchingTool; import org.mockito.invocation.DescribedInvocation; import org.mockito.invocation.Invocation; import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.Location; +import org.mockito.invocation.MatchableInvocation; import org.mockito.listeners.InvocationListener; import org.mockito.mock.SerializableMode; @@ -308,7 +328,11 @@ private static Object locationsOf(Collection matchers) { } public static AssertionError argumentsAreDifferent( - String wanted, List actualCalls, List actualLocations) { + Invocation actualInvocation, + MatchableInvocation matchableInvocation, + String wanted, + List actualCalls, + List actualLocations) { if (actualCalls == null || actualLocations == null || actualCalls.size() != actualLocations.size()) { @@ -324,7 +348,11 @@ public static AssertionError argumentsAreDifferent( .append("\n") .append(LocationFactory.create()) .append("\n") - .append("Actual invocations have different arguments:\n"); + .append("Actual invocations have different arguments"); + + appendNotMatchingPositions(actualInvocation, matchableInvocation, messageBuilder); + + messageBuilder.append(":\n"); for (int i = 0; i < actualCalls.size(); i++) { actualBuilder.append(actualCalls.get(i)).append("\n"); @@ -340,6 +368,30 @@ public static AssertionError argumentsAreDifferent( messageBuilder.toString(), wanted, actualBuilder.toString()); } + /* + * Will append the non matching positions only if there are more than 1 arguments to the method. + */ + private static void appendNotMatchingPositions( + Invocation actualInvocation, + MatchableInvocation matchableInvocation, + StringBuilder messageBuilder) { + Object[] args = actualInvocation.getArguments(); + if (args.length <= 1) { + return; + } + + List indexes = + ArgumentMatchingTool.getNotMatchingArgsIndexes( + matchableInvocation.getMatchers(), actualInvocation.getArguments()); + + if (!indexes.isEmpty()) { + messageBuilder + .append(" at ") + .append(indexes.size() == 1 ? "position " : "positions ") + .append(indexes); + } + } + public static MockitoAssertionError wantedButNotInvoked(DescribedInvocation wanted) { return new WantedButNotInvoked(createWantedButNotInvokedMessage(wanted)); } diff --git a/src/main/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingTool.java b/src/main/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingTool.java index 060ea0913e..1959147fc1 100644 --- a/src/main/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingTool.java +++ b/src/main/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingTool.java @@ -4,6 +4,8 @@ */ package org.mockito.internal.verification.argumentmatching; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -15,7 +17,7 @@ import org.mockito.ArgumentMatcher; import org.mockito.internal.matchers.ContainsExtraTypeInfo; -@SuppressWarnings("unchecked") +@SuppressWarnings("rawtypes") public class ArgumentMatchingTool { private ArgumentMatchingTool() {} @@ -42,6 +44,28 @@ && toStringEquals(m, arguments[i]) return suspicious.toArray(new Integer[0]); } + /** + * Returns indexes of arguments not matching the provided matchers. + */ + public static List getNotMatchingArgsIndexes( + List matchers, Object[] arguments) { + if (matchers.size() != arguments.length) { + return Collections.emptyList(); + } + + List nonMatching = new ArrayList<>(); + int i = 0; + for (ArgumentMatcher m : matchers) { + if (!safelyMatches(m, arguments[i])) { + nonMatching.add(i); + } + + i++; + } + + return nonMatching; + } + private static boolean safelyMatches(ArgumentMatcher m, Object arg) { try { return m.matches(arg); diff --git a/src/main/java/org/mockito/internal/verification/checkers/MissingInvocationChecker.java b/src/main/java/org/mockito/internal/verification/checkers/MissingInvocationChecker.java index 9ceb483008..03c3d8b462 100644 --- a/src/main/java/org/mockito/internal/verification/checkers/MissingInvocationChecker.java +++ b/src/main/java/org/mockito/internal/verification/checkers/MissingInvocationChecker.java @@ -52,7 +52,11 @@ public static void checkMissingInvocation( invocations.stream().map(Invocation::getLocation).collect(Collectors.toList()); throw argumentsAreDifferent( - smartPrinter.getWanted(), smartPrinter.getActuals(), actualLocations); + similar, + wanted, + smartPrinter.getWanted(), + smartPrinter.getActuals(), + actualLocations); } public static void checkMissingInvocation( diff --git a/src/test/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingToolTest.java b/src/test/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingToolTest.java index 5f55faf3b2..27c986f43f 100644 --- a/src/test/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingToolTest.java +++ b/src/test/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingToolTest.java @@ -17,7 +17,7 @@ import org.mockito.internal.matchers.Equals; import org.mockitoutil.TestBase; -@SuppressWarnings({"unchecked", "serial"}) +@SuppressWarnings({"rawtypes", "unchecked", "serial"}) public class ArgumentMatchingToolTest extends TestBase { @Test @@ -97,7 +97,6 @@ public void shouldWorkFineWhenGivenArgIsNull() { } @Test - @SuppressWarnings("rawtypes") public void shouldUseMatchersSafely() { // This matcher is evil cause typeMatches(Object) returns true for every passed type but // matches(T) @@ -137,4 +136,49 @@ public Object getWanted() { // then assertEquals(0, suspicious.length); } + + @Test + public void shouldNotFindNonMatchingIndexesWhenEveryArgsMatch() { + String arg1Value = "arg1"; + Integer arg2Value = 2222; + + Equals arg1 = new Equals(arg1Value); + Equals arg2 = new Equals(arg2Value); + + List indexes = + ArgumentMatchingTool.getNotMatchingArgsIndexes( + Arrays.asList(arg1, arg2), new Object[] {arg1Value, arg2Value}); + + assertEquals(Arrays.asList(), indexes); + } + + @Test + public void shouldFindNonMatchingIndexesWhenSingleArgDoesNotMatch() { + String arg1Value = "arg1"; + Integer arg2Value = 2222; + + Equals arg1 = new Equals(arg1Value); + Equals arg2 = new Equals(1111); + + List indexes = + ArgumentMatchingTool.getNotMatchingArgsIndexes( + Arrays.asList(arg1, arg2), new Object[] {arg1Value, arg2Value}); + + assertEquals(Arrays.asList(1), indexes); + } + + @Test + public void shouldFindNonMatchingIndexesWhenMultiArgsDoNotMatch() { + String arg1Value = "arg1"; + Integer arg2Value = 2222; + + Equals arg1 = new Equals("differs"); + Equals arg2 = new Equals(1111); + + List indexes = + ArgumentMatchingTool.getNotMatchingArgsIndexes( + Arrays.asList(arg1, arg2), new Object[] {arg1Value, arg2Value}); + + assertEquals(Arrays.asList(0, 1), indexes); + } } diff --git a/src/test/java/org/mockito/internal/verification/checkers/MissingInvocationCheckerTest.java b/src/test/java/org/mockito/internal/verification/checkers/MissingInvocationCheckerTest.java index 911a9a59ac..cd7da33ca2 100644 --- a/src/test/java/org/mockito/internal/verification/checkers/MissingInvocationCheckerTest.java +++ b/src/test/java/org/mockito/internal/verification/checkers/MissingInvocationCheckerTest.java @@ -95,6 +95,76 @@ public void shouldReportUsingInvocationDescription() { "mock.intArgumentMethod(MyCoolPrint(1111));"); } + @Test + public void shouldSpecifyPosition0WhenWantedInvocationDiffersFromActual() { + wanted = buildMultiArgsMethod().args("arg1", 2222).toInvocationMatcher(); + invocations = singletonList(buildMultiArgsMethod().args("differs", 2222).toInvocation()); + + assertThatThrownBy( + () -> { + MissingInvocationChecker.checkMissingInvocation(invocations, wanted); + }) + .isInstanceOf(ArgumentsAreDifferent.class) + .hasMessageContainingAll( + "Argument(s) are different! Wanted:", + "mock.simpleMethod(\"arg1\", 2222);", + "Actual invocations have different arguments at position [0]:", + "mock.simpleMethod(\"differs\", 2222);"); + } + + @Test + public void shouldSpecifyPosition1WhenWantedInvocationDiffersFromActual() { + wanted = buildMultiArgsMethod().args("arg1", 2222).toInvocationMatcher(); + invocations = singletonList(buildMultiArgsMethod().args("arg1", 1111).toInvocation()); + + assertThatThrownBy( + () -> { + MissingInvocationChecker.checkMissingInvocation(invocations, wanted); + }) + .isInstanceOf(ArgumentsAreDifferent.class) + .hasMessageContainingAll( + "Argument(s) are different! Wanted:", + "mock.simpleMethod(\"arg1\", 2222);", + "Actual invocations have different arguments at position [1]:", + "mock.simpleMethod(\"arg1\", 1111);"); + } + + @Test + public void shouldSpecifyPosition0And1WhenWantedInvocationDiffersFromActual() { + wanted = buildMultiArgsMethod().args("arg1", 2222).toInvocationMatcher(); + invocations = singletonList(buildMultiArgsMethod().args("differs", 1111).toInvocation()); + + assertThatThrownBy( + () -> { + MissingInvocationChecker.checkMissingInvocation(invocations, wanted); + }) + .isInstanceOf(ArgumentsAreDifferent.class) + .hasMessageContainingAll( + "Argument(s) are different! Wanted:", + "mock.simpleMethod(\"arg1\", 2222);", + "Actual invocations have different arguments at positions [0, 1]:", + "mock.simpleMethod(\"differs\", 1111);"); + } + + @Test + public void shouldNotSpecifyPositionWhenWantedSingleArgInvocationSiffersFromActual() { + wanted = buildIntArgMethod(new CustomInvocationBuilder()).arg(2222).toInvocationMatcher(); + invocations = + singletonList( + buildIntArgMethod(new CustomInvocationBuilder()).arg(1111).toInvocation()); + + assertThatThrownBy( + () -> { + MissingInvocationChecker.checkMissingInvocation(invocations, wanted); + }) + .isInstanceOf(ArgumentsAreDifferent.class) + .hasMessageContainingAll( + "Argument(s) are different! Wanted:", + "mock.intArgumentMethod(MyCoolPrint(2222));", + "Actual invocations have different arguments:", + "mock.intArgumentMethod(MyCoolPrint(1111));"); + } + private InvocationBuilder buildIntArgMethod(InvocationBuilder invocationBuilder) { return invocationBuilder.mock(mock).method("intArgumentMethod").argTypes(int.class); } @@ -107,6 +177,13 @@ private InvocationBuilder buildDifferentMethod() { return new InvocationBuilder().mock(mock).differentMethod(); } + private InvocationBuilder buildMultiArgsMethod() { + return new InvocationBuilder() + .mock(mock) + .method("simpleMethod") + .argTypes(String.class, Integer.class); + } + static class CustomInvocationBuilder extends InvocationBuilder { @Override protected Invocation createInvocation( diff --git a/src/test/java/org/mockitousage/verification/DescriptiveMessagesWhenVerificationFailsTest.java b/src/test/java/org/mockitousage/verification/DescriptiveMessagesWhenVerificationFailsTest.java index 5e26386e17..a1a29c1aad 100644 --- a/src/test/java/org/mockitousage/verification/DescriptiveMessagesWhenVerificationFailsTest.java +++ b/src/test/java/org/mockitousage/verification/DescriptiveMessagesWhenVerificationFailsTest.java @@ -83,7 +83,7 @@ public void should_print_actual_and_wanted_in_line() { String actual = "\n" - + "Actual invocations have different arguments:" + + "Actual invocations have different arguments at position [1]:" + "\n" + "iMethods.varargs(1, 2);"; @@ -397,7 +397,7 @@ public void should_print_fully_qualified_name_when_arguments_classes_have_same_s String actual = String.format( "\n" - + "Actual invocations have different arguments:" + + "Actual invocations have different arguments at positions [0, 1]:" + "\n" + "iMethods.overloadedMethodWithSameClassNameArguments(" + "\n" @@ -435,7 +435,7 @@ public void should_print_fully_qualified_name_when_arguments_classes_have_same_s String actual = "\n" - + "Actual invocations have different arguments:" + + "Actual invocations have different arguments at positions [0, 1]:" + "\n" + "iMethods.overloadedMethodWithDifferentClassNameArguments(" + "\n" @@ -483,7 +483,7 @@ public void should_print_fully_qualified_name_when_arguments_classes_have_same_s String actual = String.format( "\n" - + "Actual invocations have different arguments:" + + "Actual invocations have different arguments at positions [0, 2]:" + "\n" + "iMethods.overloadedMethodWithSameClassNameArguments(" + "\n" From aa6c33903b385b86a607fcd485b4190a38aeb458 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 19:33:47 +0100 Subject: [PATCH 112/160] Bump groovy from 3.0.13 to 3.0.14 (#2838) Bumps [groovy](https://github.com/apache/groovy) from 3.0.13 to 3.0.14. - [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] 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 edcdfb3e46..6fac4911e0 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.200' libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.4.0' -libraries.groovy = 'org.codehaus.groovy:groovy:3.0.13' +libraries.groovy = 'org.codehaus.groovy:groovy:3.0.14' def kotlinVersion = '1.7.22' libraries.kotlin = [ From bd9947f3e5d7adfa2b18d87fd4bac7790bb10413 Mon Sep 17 00:00:00 2001 From: Andy Coates <8012398+big-andy-coates@users.noreply.github.com> Date: Tue, 27 Dec 2022 08:38:45 +0000 Subject: [PATCH 113/160] Fix copyMockMethodDispatcher task (#2840) The task was incorrectly defining inputs and outputs, resulting in the task being marked 'UP-TO-DATE' and not re-running when the input changed or the output was removed. The task now copies the 'raw' file into a new output directory and includes that directory in the jar. This corrects the task inputs and outputs, while also avoiding warnings about shared output directories. --- gradle/mockito-core/inline-mock.gradle | 27 +++++++++++--------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/gradle/mockito-core/inline-mock.gradle b/gradle/mockito-core/inline-mock.gradle index d555ad6f09..70b5ed86f9 100644 --- a/gradle/mockito-core/inline-mock.gradle +++ b/gradle/mockito-core/inline-mock.gradle @@ -1,24 +1,19 @@ -task copyMockMethodDispatcher { +tasks.register('copyMockMethodDispatcher', Copy) { dependsOn compileJava - outputs.files files("${sourceSets.main.java.outputDir}/org/mockito/internal/creation/bytebuddy/inject") - .asFileTree - .matching { include "*.raw" } - .files - .collect { "${buildDir}/${it.name}" } - doLast { - copy { - from "${sourceSets.main.java.outputDir}/org/mockito/internal/creation/bytebuddy/inject" - into "${sourceSets.main.java.outputDir}/org/mockito/internal/creation/bytebuddy/inject" - include 'MockMethodDispatcher.class' - rename { String fileName -> - fileName.replace('.class', '.raw') - } - } - } + from "${sourceSets.main.java.classesDirectory.get()}/org/mockito/internal/creation/bytebuddy/inject/MockMethodDispatcher.class" + into layout.buildDirectory.dir("generated/resources/inline/org/mockito/internal/creation/bytebuddy/inject") + + rename '(.+)\\.class', '$1.raw' } classes.dependsOn(copyMockMethodDispatcher) +sourceSets.main { + resources { + output.dir(layout.buildDirectory.dir("generated/resources/inline")) + } +} + jar { exclude("org/mockito/internal/creation/bytebuddy/inject/package-info.class") exclude("org/mockito/internal/creation/bytebuddy/inject/MockMethodDispatcher.class") From 1eb47bcebde6dcf11ff9e4a43da81be0dbc2e638 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Dec 2022 00:07:58 +0100 Subject: [PATCH 114/160] Bump kotlinVersion from 1.7.22 to 1.8.0 (#2841) Bumps `kotlinVersion` from 1.7.22 to 1.8.0. Updates `kotlin-gradle-plugin` from 1.7.22 to 1.8.0 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.7.22...v1.8.0) Updates `kotlin-stdlib` from 1.7.22 to 1.8.0 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.7.22...v1.8.0) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin:kotlin-gradle-plugin dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.jetbrains.kotlin:kotlin-stdlib 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 +- gradle/dependencies.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 351bfa8266..7625492d08 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.22" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0" } } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 6fac4911e0..9d39a91ee4 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -34,7 +34,7 @@ libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.4.0' libraries.groovy = 'org.codehaus.groovy:groovy:3.0.14' -def kotlinVersion = '1.7.22' +def kotlinVersion = '1.8.0' libraries.kotlin = [ version: kotlinVersion, From 4ef3ce8563eb1800b0d68e47271d8ff1d95bc5f9 Mon Sep 17 00:00:00 2001 From: Andy Coates <8012398+big-andy-coates@users.noreply.github.com> Date: Thu, 29 Dec 2022 17:12:54 +0000 Subject: [PATCH 115/160] Make ArgumentCaptor type aware (#2842) Enhance argument captor to be more type aware. With this change it is now possible to create an argument captor that only captures certain subtypes of a parameter type. For example: ```java // Given: int simpleMethod(Collection arg); // When: mock.simpleMethod(Set.of()); mock.simpleMethod(List.of()); // Then: ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); verify(mock).simpleMethod(captor.capture()); assertThat(captor.getAllValues()).containsExactly(List.of()); ``` Fixes #565 --- .../internal/matchers/CapturingMatcher.java | 23 +++-- .../matchers/CapturingArgumentsTest.java | 84 +++++++++++++++---- 2 files changed, 84 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java b/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java index 84da8f57d9..2be6d9f566 100644 --- a/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java +++ b/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java @@ -15,13 +15,13 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import org.mockito.ArgumentMatcher; +import org.mockito.internal.util.Primitives; -@SuppressWarnings("unchecked") public class CapturingMatcher implements ArgumentMatcher, CapturesArguments, VarargMatcher, Serializable { private final Class clazz; - private final List arguments = new ArrayList<>(); + private final List arguments = new ArrayList<>(); private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); @@ -33,12 +33,20 @@ public CapturingMatcher(final Class clazz) { @Override public boolean matches(Object argument) { - return true; + if (argument == null) { + return true; + } + + if (Primitives.isPrimitiveOrWrapper(clazz)) { + return Primitives.isAssignableFromWrapper(clazz, argument.getClass()); + } + + return clazz.isAssignableFrom(argument.getClass()); } @Override public String toString() { - return ""; + return ""; } public T getLastValue() { @@ -48,7 +56,7 @@ public T getLastValue() { throw noArgumentValueWasCaptured(); } - return (T) arguments.get(arguments.size() - 1); + return arguments.get(arguments.size() - 1); } finally { readLock.unlock(); } @@ -57,17 +65,18 @@ public T getLastValue() { public List getAllValues() { readLock.lock(); try { - return new ArrayList((List) arguments); + return new ArrayList<>(arguments); } finally { readLock.unlock(); } } + @SuppressWarnings("unchecked") @Override public void captureFrom(Object argument) { writeLock.lock(); try { - this.arguments.add(argument); + this.arguments.add((T) argument); } finally { writeLock.unlock(); } diff --git a/src/test/java/org/mockitousage/matchers/CapturingArgumentsTest.java b/src/test/java/org/mockitousage/matchers/CapturingArgumentsTest.java index b4b6e1a335..34b8684883 100644 --- a/src/test/java/org/mockitousage/matchers/CapturingArgumentsTest.java +++ b/src/test/java/org/mockitousage/matchers/CapturingArgumentsTest.java @@ -9,11 +9,14 @@ import static org.mockito.Mockito.*; import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.Set; import org.assertj.core.api.Assertions; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.exceptions.base.MockitoException; import org.mockito.exceptions.verification.WantedButNotInvoked; import org.mockitousage.IMethods; @@ -54,9 +57,10 @@ interface EmailService { boolean sendEmailTo(Person person); } - EmailService emailService = mock(EmailService.class); - BulkEmailService bulkEmailService = new BulkEmailService(emailService); - IMethods mock = mock(IMethods.class); + private final EmailService emailService = mock(EmailService.class); + private final BulkEmailService bulkEmailService = new BulkEmailService(emailService); + private final IMethods mock = mock(IMethods.class); + @Captor private ArgumentCaptor> listCaptor; @SuppressWarnings("deprecation") @Test @@ -110,7 +114,7 @@ public void should_print_captor_matcher() { fail(); } catch (WantedButNotInvoked e) { // then - assertThat(e).hasMessageContaining(""); + assertThat(e).hasMessageContaining(""); } } @@ -124,7 +128,7 @@ public void should_allow_assertions_on_captured_null() { // then verify(emailService).sendEmailTo(argument.capture()); - assertEquals(null, argument.getValue()); + assertNull(argument.getValue()); } @Test @@ -135,6 +139,7 @@ public void should_allow_construction_of_captor_for_parameterized_type_in_a_conv assertNotNull(argument); } + @SuppressWarnings("unchecked") @Test public void should_allow_construction_of_captor_for_a_more_specific_type() { // the test passes if this expression compiles @@ -180,7 +185,7 @@ public void should_say_something_smart_when_misused() { } @Test - public void should_capture_when_full_arg_list_matches() throws Exception { + public void should_capture_when_full_arg_list_matches() { // given ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); @@ -208,7 +213,7 @@ public void should_capture_int_by_creating_captor_with_primitive_wrapper() { } @Test - public void should_capture_int_by_creating_captor_with_primitive() throws Exception { + public void should_capture_int_by_creating_captor_with_primitive() { // given ArgumentCaptor argument = ArgumentCaptor.forClass(int.class); @@ -221,7 +226,19 @@ public void should_capture_int_by_creating_captor_with_primitive() throws Except } @Test - public void should_capture_byte_vararg_by_creating_captor_with_primitive() throws Exception { + public void should_not_capture_int_by_creating_captor_with_primitive() { + // given + ArgumentCaptor argument = ArgumentCaptor.forClass(int.class); + + // when + mock.forObject(10L); + + // then + verify(mock, never()).forObject(argument.capture()); + } + + @Test + public void should_capture_byte_vararg_by_creating_captor_with_primitive() { // given ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(byte.class); @@ -235,8 +252,7 @@ public void should_capture_byte_vararg_by_creating_captor_with_primitive() throw } @Test - public void should_capture_byte_vararg_by_creating_captor_with_primitive_wrapper() - throws Exception { + public void should_capture_byte_vararg_by_creating_captor_with_primitive_wrapper() { // given ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Byte.class); @@ -250,7 +266,7 @@ public void should_capture_byte_vararg_by_creating_captor_with_primitive_wrapper } @Test - public void should_capture_vararg() throws Exception { + public void should_capture_vararg() { // given ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class); @@ -263,7 +279,7 @@ public void should_capture_vararg() throws Exception { } @Test - public void should_capture_all_vararg() throws Exception { + public void should_capture_all_vararg() { // given ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class); @@ -279,8 +295,7 @@ public void should_capture_all_vararg() throws Exception { } @Test - public void should_capture_one_arg_even_when_using_vararg_captor_on_nonvararg_method() - throws Exception { + public void should_capture_one_arg_even_when_using_vararg_captor_on_nonvararg_method() { // given ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class); @@ -293,7 +308,7 @@ public void should_capture_one_arg_even_when_using_vararg_captor_on_nonvararg_me } @Test - public void captures_correctly_when_captor_used_multiple_times() throws Exception { + public void captures_correctly_when_captor_used_multiple_times() { // given ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class); @@ -312,7 +327,7 @@ public void captures_correctly_when_captor_used_multiple_times() throws Exceptio } @Test - public void captures_correctly_when_captor_used_on_pure_vararg_method() throws Exception { + public void captures_correctly_when_captor_used_on_pure_vararg_method() { // given ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class); @@ -323,4 +338,41 @@ public void captures_correctly_when_captor_used_on_pure_vararg_method() throws E verify(mock).varargs(eq(42), argumentCaptor.capture()); Assertions.assertThat(argumentCaptor.getValue()).contains("capturedValue"); } + + @SuppressWarnings("unchecked") + @Test + public void should_capture_by_type() { + // When: + mock.simpleMethod(Set.of()); + mock.simpleMethod(new ArrayList<>(0)); + + // Then: + ArgumentCaptor> captor = ArgumentCaptor.forClass(ArrayList.class); + verify(mock).simpleMethod(captor.capture()); + assertThat(captor.getAllValues()).containsExactly(List.of()); + } + + @Test + public void should_capture_by_type_using_annotation() { + // When: + mock.simpleMethod(Set.of()); + mock.simpleMethod(new ArrayList<>(0)); + + // Then: + verify(mock).simpleMethod(listCaptor.capture()); + assertThat(listCaptor.getAllValues()).containsExactly(List.of()); + } + + @SuppressWarnings("unchecked") + @Test + public void should_always_capture_nulls() { + // When: + mock.simpleMethod((Set) null); + mock.simpleMethod((List) null); + + // Then: + ArgumentCaptor> captor = ArgumentCaptor.forClass(ArrayList.class); + verify(mock, times(2)).simpleMethod(captor.capture()); + assertThat(captor.getAllValues()).containsExactly(null, null); + } } From 064fe9028993a11ccf0ae12b5975b96baffcf305 Mon Sep 17 00:00:00 2001 From: Andy Coates <8012398+big-andy-coates@users.noreply.github.com> Date: Fri, 30 Dec 2022 15:25:10 +0000 Subject: [PATCH 116/160] Remove internal interface `VarargMatcher` (#2835) BREAKING CHANGE: This changes the default behaviour of the `any()` matcher, argument captors and `MockitoHamcrest` matchers when passed to a varargs parameter. Previously, these matcher would match each element in the varargs parameter, matching any number of elements 0...n. From Mockito v5 onwards, when passed to a varargs parameter, will match invocations where a single value is passed to the varargs parameter. To match any number of values passed to the varargs parameter, pass the type of the varargs parameter to the matcher. For example, given a `String...` varargs parameter, use `any(String[].class)`. Fixes #2836 Fixes #1593 Fixes #585 --- .../java/org/mockito/ArgumentMatcher.java | 2 +- .../java/org/mockito/ArgumentMatchers.java | 8 +- .../org/mockito/hamcrest/MockitoHamcrest.java | 50 ++++- .../hamcrest/HamcrestArgumentMatcher.java | 27 ++- .../MatcherApplicationStrategy.java | 40 +--- .../org/mockito/internal/matchers/Any.java | 2 +- .../internal/matchers/CapturingMatcher.java | 3 +- .../mockito/internal/matchers/InstanceOf.java | 16 -- .../internal/matchers/VarargMatcher.java | 22 -- .../invocation/InvocationMatcherTest.java | 12 +- .../MatcherApplicationStrategyTest.java | 26 ++- .../internal/matchers/InstanceOfTest.java | 7 +- ...ameterizedConstructorInstantiatorTest.java | 4 +- .../basicapi/UsingVarargsTest.java | 4 +- ...argsAndAnyPicksUpExtraInvocationsTest.java | 4 +- .../varargs/VarargsNotPlayingWithAnyTest.java | 53 ----- .../matchers/CapturingArgumentsTest.java | 192 ++++++++++++++++-- .../matchers/HamcrestMatchersTest.java | 42 +++- .../mockitousage/matchers/VarargsTest.java | 54 ++--- .../StubbingWithAdditionalAnswersTest.java | 54 +++-- 20 files changed, 368 insertions(+), 254 deletions(-) delete mode 100644 src/main/java/org/mockito/internal/matchers/VarargMatcher.java delete mode 100644 src/test/java/org/mockitousage/bugs/varargs/VarargsNotPlayingWithAnyTest.java diff --git a/src/main/java/org/mockito/ArgumentMatcher.java b/src/main/java/org/mockito/ArgumentMatcher.java index ad52e1ecae..5d68132d82 100644 --- a/src/main/java/org/mockito/ArgumentMatcher.java +++ b/src/main/java/org/mockito/ArgumentMatcher.java @@ -161,7 +161,7 @@ public interface ArgumentMatcher { *

    Only matcher implementations that can conceptually match a raw vararg parameter should override this method. * * @return the type this matcher handles. The default value of {@link Void} means the type is not known. - * @since 5.0.0 + * @since 4.11.0 */ default Class type() { return Void.class; diff --git a/src/main/java/org/mockito/ArgumentMatchers.java b/src/main/java/org/mockito/ArgumentMatchers.java index 28986e1648..1d3f11136f 100644 --- a/src/main/java/org/mockito/ArgumentMatchers.java +++ b/src/main/java/org/mockito/ArgumentMatchers.java @@ -174,7 +174,7 @@ public static T any() { * @see #isNull() */ public static T any(Class type) { - reportMatcher(new InstanceOf.VarArgAware(type, "")); + reportMatcher(new InstanceOf(type, "")); return defaultValue(type); } @@ -709,7 +709,7 @@ public static T isNull() { * @param type the type of the argument being matched. * @return null. * @see #isNotNull(Class) - * @since 5.0.0 + * @since 4.11.0 */ public static T isNull(Class type) { reportMatcher(new Null<>(type)); @@ -747,7 +747,7 @@ public static T notNull() { * * @param type the type of the argument being matched. * @return null. - * @since 5.0.0 + * @since 4.11.0 */ public static T notNull(Class type) { reportMatcher(new NotNull<>(type)); @@ -786,7 +786,7 @@ public static T isNotNull() { * @param type the type of the argument being matched. * @return null. * @see #isNull() - * @since 5.0.0 + * @since 4.11.0 */ public static T isNotNull(Class type) { return notNull(type); diff --git a/src/main/java/org/mockito/hamcrest/MockitoHamcrest.java b/src/main/java/org/mockito/hamcrest/MockitoHamcrest.java index b8ebf8f97d..3624ba20c4 100644 --- a/src/main/java/org/mockito/hamcrest/MockitoHamcrest.java +++ b/src/main/java/org/mockito/hamcrest/MockitoHamcrest.java @@ -42,6 +42,24 @@ * Due to how java works we don't really have a clean way of detecting this scenario and protecting the user from this problem. * Hopefully, the javadoc describes the problem and solution well. * If you have an idea how to fix the problem, let us know via the mailing list or the issue tracker. + *

    + * By default, a matcher passed to a varargs parameter will match against the first element in the varargs array. + * To match against the raw varargs array pass the type of the varargs parameter to {@link MockitoHamcrest#argThat(Matcher, Class)} + *

    + * For example, to match any number of {@code String} values: + *

    + *     import static org.hamcrest.CoreMatchers.isA;
    + *     import static org.mockito.hamcrest.MockitoHamcrest.argThat;
    + *
    + *     // Given:
    + *     void varargMethod(String... args);
    + *
    + *     //stubbing
    + *     when(mock.varargMethod(argThat(isA(String[].class), String[].class));
    + *
    + *     //verification
    + *     verify(mock).giveMe(argThat(isA(String[].class), String[].class));
    + * 
    * * @since 2.1.0 */ @@ -62,6 +80,26 @@ public static T argThat(Matcher matcher) { return (T) defaultValue(genericTypeOfMatcher(matcher.getClass())); } + /** + * Allows matching arguments with hamcrest matchers. + *

    + * This variant can be used to pass an explicit {@code type}, + * which can be useful to provide a matcher that matches against all + * elements in a varargs parameter. + *

    + * See examples in javadoc for {@link MockitoHamcrest} class + * + * @param matcher decides whether argument matches + * @param type the type the matcher matches. + * @return null or default value for primitive (0, false, etc.) + * @since 5.0.0 + */ + @SuppressWarnings("unchecked") + public static T argThat(Matcher matcher, Class type) { + reportMatcher(matcher, type); + return (T) defaultValue(genericTypeOfMatcher(matcher.getClass())); + } + /** * Enables integrating hamcrest matchers that match primitive char arguments. * Note that {@link #argThat} will not work with primitive char matchers due to NullPointerException auto-unboxing caveat. @@ -175,9 +213,15 @@ public static double doubleThat(Matcher matcher) { } private static void reportMatcher(Matcher matcher) { - mockingProgress() - .getArgumentMatcherStorage() - .reportMatcher(new HamcrestArgumentMatcher(matcher)); + reportMatcher(new HamcrestArgumentMatcher(matcher)); + } + + private static void reportMatcher(Matcher matcher, Class type) { + reportMatcher(new HamcrestArgumentMatcher(matcher, type)); + } + + private static void reportMatcher(final HamcrestArgumentMatcher matcher) { + mockingProgress().getArgumentMatcherStorage().reportMatcher(matcher); } private MockitoHamcrest() {} diff --git a/src/main/java/org/mockito/internal/hamcrest/HamcrestArgumentMatcher.java b/src/main/java/org/mockito/internal/hamcrest/HamcrestArgumentMatcher.java index db526546eb..f9ef55d528 100644 --- a/src/main/java/org/mockito/internal/hamcrest/HamcrestArgumentMatcher.java +++ b/src/main/java/org/mockito/internal/hamcrest/HamcrestArgumentMatcher.java @@ -7,14 +7,25 @@ import org.hamcrest.Matcher; import org.hamcrest.StringDescription; import org.mockito.ArgumentMatcher; -import org.mockito.internal.matchers.VarargMatcher; + +import static java.util.Objects.requireNonNull; public class HamcrestArgumentMatcher implements ArgumentMatcher { - private final Matcher matcher; + private final Matcher matcher; + private final Class type; public HamcrestArgumentMatcher(Matcher matcher) { - this.matcher = matcher; + this(Void.class, matcher); + } + + public HamcrestArgumentMatcher(Matcher matcher, Class type) { + this(type, matcher); + } + + private HamcrestArgumentMatcher(Class type, Matcher matcher) { + this.type = requireNonNull(type, "type"); + this.matcher = requireNonNull(matcher, "matcher"); } @Override @@ -22,14 +33,14 @@ public boolean matches(Object argument) { return this.matcher.matches(argument); } - @SuppressWarnings("deprecation") - public boolean isVarargMatcher() { - return matcher instanceof VarargMatcher; - } - @Override public String toString() { // TODO SF add unit tests and integ test coverage for toString() return StringDescription.toString(matcher); } + + @Override + public Class type() { + return type; + } } diff --git a/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java b/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java index f3c0111c64..8b50da635d 100644 --- a/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java +++ b/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java @@ -4,13 +4,10 @@ */ package org.mockito.internal.invocation; -import java.util.ArrayList; import java.util.List; import org.mockito.ArgumentMatcher; -import org.mockito.internal.hamcrest.HamcrestArgumentMatcher; import org.mockito.internal.matchers.CapturingMatcher; -import org.mockito.internal.matchers.VarargMatcher; import org.mockito.invocation.Invocation; public class MatcherApplicationStrategy { @@ -63,7 +60,7 @@ public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) { && invocation.getRawArguments().length == matchers.size(); if (maybeVararg) { - final Class matcherType = lastMatcher().type(); + final Class matcherType = lastMatcherType(); final Class paramType = lastParameterType(); if (paramType.isAssignableFrom(matcherType)) { return argsMatch(invocation.getRawArguments(), matchers, action); @@ -74,12 +71,6 @@ public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) { return argsMatch(invocation.getArguments(), matchers, action); } - if (maybeVararg && isLastMatcherVarargMatcher()) { - int times = varargLength(); - final List> matchers = appendLastMatcherNTimes(times); - return argsMatch(invocation.getArguments(), matchers, action); - } - return false; } @@ -98,33 +89,8 @@ private boolean argsMatch( return true; } - private boolean isLastMatcherVarargMatcher() { - ArgumentMatcher argumentMatcher = lastMatcher(); - if (argumentMatcher instanceof HamcrestArgumentMatcher) { - return ((HamcrestArgumentMatcher) argumentMatcher).isVarargMatcher(); - } - return argumentMatcher instanceof VarargMatcher; - } - - private List> appendLastMatcherNTimes( - int timesToAppendLastMatcher) { - ArgumentMatcher lastMatcher = lastMatcher(); - - List> expandedMatchers = new ArrayList>(matchers); - for (int i = 0; i < timesToAppendLastMatcher; i++) { - expandedMatchers.add(lastMatcher); - } - return expandedMatchers; - } - - private int varargLength() { - int rawArgumentCount = invocation.getRawArguments().length; - int expandedArgumentCount = invocation.getArguments().length; - return expandedArgumentCount - rawArgumentCount; - } - - private ArgumentMatcher lastMatcher() { - return matchers.get(matchers.size() - 1); + private Class lastMatcherType() { + return matchers.get(matchers.size() - 1).type(); } private Class lastParameterType() { diff --git a/src/main/java/org/mockito/internal/matchers/Any.java b/src/main/java/org/mockito/internal/matchers/Any.java index 1a71a7e2bc..2074f81abc 100644 --- a/src/main/java/org/mockito/internal/matchers/Any.java +++ b/src/main/java/org/mockito/internal/matchers/Any.java @@ -8,7 +8,7 @@ import org.mockito.ArgumentMatcher; -public class Any implements ArgumentMatcher, VarargMatcher, Serializable { +public class Any implements ArgumentMatcher, Serializable { public static final Any ANY = new Any(); diff --git a/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java b/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java index 2be6d9f566..7ce9133651 100644 --- a/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java +++ b/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java @@ -17,8 +17,7 @@ import org.mockito.ArgumentMatcher; import org.mockito.internal.util.Primitives; -public class CapturingMatcher - implements ArgumentMatcher, CapturesArguments, VarargMatcher, Serializable { +public class CapturingMatcher implements ArgumentMatcher, CapturesArguments, Serializable { private final Class clazz; private final List arguments = new ArrayList<>(); diff --git a/src/main/java/org/mockito/internal/matchers/InstanceOf.java b/src/main/java/org/mockito/internal/matchers/InstanceOf.java index 9ea762bb96..a53b1bda0f 100644 --- a/src/main/java/org/mockito/internal/matchers/InstanceOf.java +++ b/src/main/java/org/mockito/internal/matchers/InstanceOf.java @@ -39,20 +39,4 @@ public Class type() { public String toString() { return description; } - - public static class VarArgAware extends InstanceOf implements VarargMatcher { - - public VarArgAware(Class clazz) { - super(clazz); - } - - public VarArgAware(Class clazz, String describedAs) { - super(clazz, describedAs); - } - - @Override - public Class type() { - return clazz; - } - } } diff --git a/src/main/java/org/mockito/internal/matchers/VarargMatcher.java b/src/main/java/org/mockito/internal/matchers/VarargMatcher.java deleted file mode 100644 index 2dfd9b7549..0000000000 --- a/src/main/java/org/mockito/internal/matchers/VarargMatcher.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2007 Mockito contributors - * This program is made available under the terms of the MIT License. - */ -package org.mockito.internal.matchers; - -import java.io.Serializable; - -/** - * Internal interface that informs Mockito that the matcher is intended to capture varargs. - * This information is needed when mockito collects the arguments. - * - * @deprecated use of this interface is deprecated as the behaviour it promotes has limitations. - * It is not recommended for new implementations to implement this method. - * - *

    Instead, matchers should implement the {@link org.mockito.ArgumentMatcher#type()} method. - * If this method returns the same raw type as a vararg parameter, then Mockito will treat the - * matcher as matching the entire vararg array parameter, otherwise it will be treated as matching a single element. - * For an example, see {@link org.mockito.ArgumentMatchers#isNull(Class)}. - */ -@Deprecated -public interface VarargMatcher extends Serializable {} diff --git a/src/test/java/org/mockito/internal/invocation/InvocationMatcherTest.java b/src/test/java/org/mockito/internal/invocation/InvocationMatcherTest.java index ab53be545f..a217d4d8d3 100644 --- a/src/test/java/org/mockito/internal/invocation/InvocationMatcherTest.java +++ b/src/test/java/org/mockito/internal/invocation/InvocationMatcherTest.java @@ -149,11 +149,11 @@ public void should_capture_arguments_from_invocation() throws Exception { } @Test - public void should_match_varargs_using_any_varargs() throws Exception { + public void should_match_varargs_using_any_varargs() { // given mock.varargs("1", "2"); Invocation invocation = getLastInvocation(); - InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, (List) asList(ANY)); + InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, asList(ANY, ANY)); // when boolean match = invocationMatcher.matches(invocation); @@ -163,11 +163,11 @@ public void should_match_varargs_using_any_varargs() throws Exception { } @Test - public void should_capture_varargs_as_vararg() throws Exception { + public void should_capture_varargs_as_vararg() { // given mock.mixedVarargs(1, "a", "b"); Invocation invocation = getLastInvocation(); - CapturingMatcher m = new CapturingMatcher(List.class); + CapturingMatcher m = new CapturingMatcher(String[].class); InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, Arrays.asList(new Equals(1), m)); @@ -175,11 +175,11 @@ public void should_capture_varargs_as_vararg() throws Exception { invocationMatcher.captureArgumentsFrom(invocation); // then - Assertions.assertThat(m.getAllValues()).containsExactly("a", "b"); + Assertions.assertThat(m.getAllValues()).containsExactly(new String[] {"a", "b"}); } @Test // like using several time the captor in the vararg - public void should_capture_arguments_when_args_count_does_NOT_match() throws Exception { + public void should_capture_arguments_when_args_count_does_NOT_match() { // given mock.varargs(); Invocation invocation = getLastInvocation(); diff --git a/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java b/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java index ddd4333905..21c94b1525 100644 --- a/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java +++ b/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java @@ -9,7 +9,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.internal.invocation.MatcherApplicationStrategy.getMatcherApplicationStrategyFor; import static org.mockito.internal.matchers.Any.ANY; @@ -26,7 +25,6 @@ import org.mockito.internal.matchers.Any; import org.mockito.internal.matchers.Equals; import org.mockito.internal.matchers.InstanceOf; -import org.mockito.internal.matchers.VarargMatcher; import org.mockito.invocation.Invocation; import org.mockitousage.IMethods; import org.mockitoutil.TestBase; @@ -124,7 +122,7 @@ public void shouldKnowWhenVarargsMatch() { public void shouldAllowAnyMatchEntireVararg() { // given invocation = varargs("1", "2"); - matchers = asList(ANY); + matchers = asList(ANY, ANY); // when boolean match = @@ -151,10 +149,10 @@ public void shouldNotAllowAnyWithMixedVarargs() { } @Test - public void shouldAllowanyWithMixedVarargs() { + public void shouldAllowAnyWithMixedVarargs() { // given invocation = mixedVarargs(1, "1", "2"); - matchers = asList(new Equals(1), ANY); + matchers = asList(new Equals(1), ANY, ANY); // when boolean match = @@ -186,7 +184,7 @@ public void shouldAnyDealWithDifferentSizeOfArgs() { public void shouldMatchAnyEvenIfOneOfTheArgsIsNull() { // given invocation = mixedVarargs(null, null, "2"); - matchers = asList(new Equals(null), ANY); + matchers = asList(new Equals(null), ANY, ANY); // when getMatcherApplicationStrategyFor(invocation, matchers) @@ -200,7 +198,7 @@ public void shouldMatchAnyEvenIfOneOfTheArgsIsNull() { public void shouldMatchAnyEvenIfMatcherIsDecorated() { // given invocation = varargs("1", "2"); - matchers = asList(ANY); + matchers = asList(ANY, ANY); // when getMatcherApplicationStrategyFor(invocation, matchers) @@ -216,7 +214,7 @@ public void shouldMatchAnyEvenIfMatcherIsWrappedInHamcrestMatcher() { invocation = varargs("1", "2"); HamcrestArgumentMatcher argumentMatcher = new HamcrestArgumentMatcher<>(new IntMatcher()); - matchers = asList(argumentMatcher); + matchers = asList(argumentMatcher, argumentMatcher); // when getMatcherApplicationStrategyFor(invocation, matchers) @@ -230,7 +228,7 @@ public void shouldMatchAnyEvenIfMatcherIsWrappedInHamcrestMatcher() { public void shouldMatchAnyThatMatchesRawVarArgType() { // given invocation = varargs("1", "2"); - InstanceOf.VarArgAware any = new InstanceOf.VarArgAware(String[].class, ""); + InstanceOf any = new InstanceOf(String[].class, ""); matchers = asList(any); // when @@ -241,7 +239,15 @@ public void shouldMatchAnyThatMatchesRawVarArgType() { recordAction.assertContainsExactly(any); } - private static class IntMatcher extends BaseMatcher implements VarargMatcher { + private static class IntMatcher extends BaseMatcher { + public boolean matches(Object o) { + return true; + } + + public void describeTo(Description description) {} + } + + private static class IntArrayMatcher extends BaseMatcher { public boolean matches(Object o) { return true; } diff --git a/src/test/java/org/mockito/internal/matchers/InstanceOfTest.java b/src/test/java/org/mockito/internal/matchers/InstanceOfTest.java index 8addef8ca0..7740d0b77d 100644 --- a/src/test/java/org/mockito/internal/matchers/InstanceOfTest.java +++ b/src/test/java/org/mockito/internal/matchers/InstanceOfTest.java @@ -59,9 +59,8 @@ public void should_check_for_primitive_wrapper_types() { @Test public void can_be_vararg_aware() { - assertThat(new InstanceOf.VarArgAware(Number[].class)).isInstanceOf(VarargMatcher.class); - assertThat(new InstanceOf.VarArgAware(Number[].class).matches(new Integer[0])).isTrue(); - assertThat(new InstanceOf.VarArgAware(Number[].class).matches(new Number[0])).isTrue(); - assertThat(new InstanceOf.VarArgAware(Number[].class).matches(new Object[0])).isFalse(); + assertThat(new InstanceOf(Number[].class).matches(new Integer[0])).isTrue(); + assertThat(new InstanceOf(Number[].class).matches(new Number[0])).isTrue(); + assertThat(new InstanceOf(Number[].class).matches(new Object[0])).isFalse(); } } diff --git a/src/test/java/org/mockito/internal/util/reflection/ParameterizedConstructorInstantiatorTest.java b/src/test/java/org/mockito/internal/util/reflection/ParameterizedConstructorInstantiatorTest.java index 9e63e8df69..45ff5ea86f 100644 --- a/src/test/java/org/mockito/internal/util/reflection/ParameterizedConstructorInstantiatorTest.java +++ b/src/test/java/org/mockito/internal/util/reflection/ParameterizedConstructorInstantiatorTest.java @@ -73,7 +73,7 @@ public void should_be_created_with_an_argument_resolver() throws Exception { public void should_instantiate_type_if_resolver_provide_matching_types() throws Exception { Observer observer = mock(Observer.class); Map map = mock(Map.class); - given(resolver.resolveTypeInstances(ArgumentMatchers.[]>any())) + given(resolver.resolveTypeInstances(ArgumentMatchers.any(Class[].class))) .willReturn(new Object[] {observer, map}); new ParameterizedConstructorInstantiator(this, field("withMultipleConstructor"), resolver) @@ -120,7 +120,7 @@ this, field("withThrowingConstructor"), resolver) @Test public void should_instantiate_type_with_vararg_constructor() throws Exception { Observer[] vararg = new Observer[] {}; - given(resolver.resolveTypeInstances(ArgumentMatchers.[]>any())) + given(resolver.resolveTypeInstances(ArgumentMatchers.any(Class[].class))) .willReturn(new Object[] {"", vararg}); new ParameterizedConstructorInstantiator(this, field("withVarargConstructor"), resolver) diff --git a/src/test/java/org/mockitousage/basicapi/UsingVarargsTest.java b/src/test/java/org/mockitousage/basicapi/UsingVarargsTest.java index 551c63790a..8d69a2a52f 100644 --- a/src/test/java/org/mockitousage/basicapi/UsingVarargsTest.java +++ b/src/test/java/org/mockitousage/basicapi/UsingVarargsTest.java @@ -173,9 +173,9 @@ public void shouldStubCorrectlyWhenDoubleStringAndMixedVarargsUsed() { @Test // See bug #157 - public void shouldMatchEasilyEmptyVararg() throws Exception { + public void shouldMatchEasilyEmptyVararg() { // when - when(mock.foo(any())).thenReturn(-1); + when(mock.foo(any(Object[].class))).thenReturn(-1); // then assertEquals(-1, mock.foo()); diff --git a/src/test/java/org/mockitousage/bugs/varargs/VarargsAndAnyPicksUpExtraInvocationsTest.java b/src/test/java/org/mockitousage/bugs/varargs/VarargsAndAnyPicksUpExtraInvocationsTest.java index f6f4417c4a..c08bdf0643 100644 --- a/src/test/java/org/mockitousage/bugs/varargs/VarargsAndAnyPicksUpExtraInvocationsTest.java +++ b/src/test/java/org/mockitousage/bugs/varargs/VarargsAndAnyPicksUpExtraInvocationsTest.java @@ -26,7 +26,7 @@ public void shouldVerifyCorrectlyWithAny() { table.newRow("abc", "def"); // then - verify(table, times(2)).newRow(anyString(), (String[]) any()); + verify(table, times(2)).newRow(anyString(), any(String[].class)); } @Test @@ -36,7 +36,7 @@ public void shouldVerifyCorrectlyNumberOfInvocationsUsingAnyAndEqualArgument() { table.newRow("x", "def"); // then - verify(table, times(2)).newRow(eq("x"), (String[]) any()); + verify(table, times(2)).newRow(eq("x"), any(String[].class)); } @Test diff --git a/src/test/java/org/mockitousage/bugs/varargs/VarargsNotPlayingWithAnyTest.java b/src/test/java/org/mockitousage/bugs/varargs/VarargsNotPlayingWithAnyTest.java deleted file mode 100644 index 6e11a6a7fd..0000000000 --- a/src/test/java/org/mockitousage/bugs/varargs/VarargsNotPlayingWithAnyTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2007 Mockito contributors - * This program is made available under the terms of the MIT License. - */ -package org.mockitousage.bugs.varargs; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import org.junit.Test; -import org.mockito.Mock; -import org.mockitoutil.TestBase; - -// see issue 62 -public class VarargsNotPlayingWithAnyTest extends TestBase { - - interface VarargMethod { - Object run(String... args); - } - - @Mock VarargMethod mock; - - @Test - public void shouldMatchAny() { - mock.run("a", "b"); - - verify(mock).run(anyString(), anyString()); - verify(mock).run((String) any(), (String) any()); - - verify(mock).run((String[]) any()); - - verify(mock, never()).run(); - verify(mock, never()).run(anyString(), eq("f")); - } - - @Test - public void shouldAllowUsinganyForVarArgs() { - mock.run("a", "b"); - verify(mock).run((String[]) any()); - } - - @Test - public void shouldStubUsingAny() { - when(mock.run((String[]) any())).thenReturn("foo"); - - assertEquals("foo", mock.run("a", "b")); - } -} diff --git a/src/test/java/org/mockitousage/matchers/CapturingArgumentsTest.java b/src/test/java/org/mockitousage/matchers/CapturingArgumentsTest.java index 34b8684883..e0146de780 100644 --- a/src/test/java/org/mockitousage/matchers/CapturingArgumentsTest.java +++ b/src/test/java/org/mockitousage/matchers/CapturingArgumentsTest.java @@ -4,6 +4,7 @@ */ package org.mockitousage.matchers; +import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import static org.mockito.Mockito.*; @@ -13,7 +14,6 @@ import java.util.List; import java.util.Set; -import org.assertj.core.api.Assertions; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -24,7 +24,7 @@ public class CapturingArgumentsTest extends TestBase { - class Person { + private static class Person { private final Integer age; @@ -37,9 +37,9 @@ public int getAge() { } } - class BulkEmailService { + private static class BulkEmailService { - private EmailService service; + private final EmailService service; public BulkEmailService(EmailService service) { this.service = service; @@ -62,7 +62,6 @@ interface EmailService { private final IMethods mock = mock(IMethods.class); @Captor private ArgumentCaptor> listCaptor; - @SuppressWarnings("deprecation") @Test public void should_allow_assertions_on_captured_argument() { // given @@ -171,7 +170,7 @@ public void should_capture_when_stubbing_only_when_entire_invocation_matches() { mock.simpleMethod("bar", 2); // then - Assertions.assertThat(argument.getAllValues()).containsOnly("bar"); + assertThat(argument.getAllValues()).containsOnly("bar"); } @Test @@ -243,12 +242,41 @@ public void should_capture_byte_vararg_by_creating_captor_with_primitive() { ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(byte.class); // when - mock.varargsbyte((byte) 1, (byte) 2); + mock.varargsbyte((byte) 1); // then verify(mock).varargsbyte(argumentCaptor.capture()); + assertEquals((byte) 1, (byte) argumentCaptor.getValue()); + assertThat(argumentCaptor.getAllValues()).containsExactly((byte) 1); + } + + @Test + public void should_capture_byte_vararg_by_creating_captor_with_primitive_2_args() { + // given + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(byte.class); + + // when + mock.varargsbyte((byte) 1, (byte) 2); + + // then + verify(mock).varargsbyte(argumentCaptor.capture(), argumentCaptor.capture()); assertEquals((byte) 2, (byte) argumentCaptor.getValue()); - Assertions.assertThat(argumentCaptor.getAllValues()).containsExactly((byte) 1, (byte) 2); + assertThat(argumentCaptor.getAllValues()).containsExactly((byte) 1, (byte) 2); + } + + @Test + public void should_capture_byte_vararg_by_creating_captor_with_primitive_array() { + // given + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(byte[].class); + + // when + mock.varargsbyte(); + mock.varargsbyte((byte) 1, (byte) 2); + + // then + verify(mock, times(2)).varargsbyte(argumentCaptor.capture()); + assertThat(argumentCaptor.getValue()).containsExactly(new byte[] {1, 2}); + assertThat(argumentCaptor.getAllValues()).containsExactly(new byte[] {}, new byte[] {1, 2}); } @Test @@ -257,41 +285,163 @@ public void should_capture_byte_vararg_by_creating_captor_with_primitive_wrapper ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Byte.class); // when - mock.varargsbyte((byte) 1, (byte) 2); + mock.varargsbyte((byte) 1); // then verify(mock).varargsbyte(argumentCaptor.capture()); - assertEquals((byte) 2, (byte) argumentCaptor.getValue()); - Assertions.assertThat(argumentCaptor.getAllValues()).containsExactly((byte) 1, (byte) 2); + assertEquals((byte) 1, (byte) argumentCaptor.getValue()); + assertThat(argumentCaptor.getAllValues()).containsExactly((byte) 1); } @Test - public void should_capture_vararg() { + public void should_not_capture_empty_vararg_with_single_captor() { // given ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class); // when - mock.mixedVarargs(42, "a", "b", "c"); + mock.mixedVarargs(42); + + // then + verify(mock, never()).mixedVarargs(any(), argumentCaptor.capture()); + } + + @Test + public void should_capture_single_vararg_with_single_captor() { + // given + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class); + + // when + mock.mixedVarargs(42, "a"); // then verify(mock).mixedVarargs(any(), argumentCaptor.capture()); - Assertions.assertThat(argumentCaptor.getAllValues()).containsExactly("a", "b", "c"); + assertThat(argumentCaptor.getValue()).isEqualTo("a"); + } + + @Test + public void should_not_capture_multiple_vararg_with_single_captor() { + // given + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class); + + // when + mock.mixedVarargs(42, "a", "b"); + + // then + verify(mock, never()).mixedVarargs(any(), argumentCaptor.capture()); } @Test - public void should_capture_all_vararg() { + public void should_capture_multiple_vararg_with_multiple_captor() { // given ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class); + // when + mock.mixedVarargs(42, "a", "b"); + + // then + verify(mock).mixedVarargs(any(), argumentCaptor.capture(), argumentCaptor.capture()); + assertThat(argumentCaptor.getValue()).isEqualTo("b"); + assertThat(argumentCaptor.getAllValues()).isEqualTo(asList("a", "b")); + } + + @Test + public void should_not_capture_multiple_vararg_some_null_with_single_captor() { + // given + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class); + + // when + mock.mixedVarargs(42, "a", null); + + // then + verify(mock, never()).mixedVarargs(any(), argumentCaptor.capture()); + } + + @Test + public void should_capture_empty_vararg_with_array_captor() { + // given + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String[].class); + + // when + mock.mixedVarargs(42); + + // then + verify(mock).mixedVarargs(any(), argumentCaptor.capture()); + assertThat(argumentCaptor.getValue()).isEqualTo(new String[] {}); + assertThat(argumentCaptor.getAllValues()).containsExactly(new String[] {}); + } + + @Test + public void should_capture_single_vararg_with_array_captor() { + // given + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String[].class); + + // when + mock.mixedVarargs(42, "a"); + + // then + verify(mock).mixedVarargs(any(), argumentCaptor.capture()); + assertThat(argumentCaptor.getValue()).isEqualTo(new String[] {"a"}); + assertThat(argumentCaptor.getAllValues()).containsExactly(new String[] {"a"}); + } + + @Test + public void should_capture_multiple_vararg_with_array_captor() { + // given + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String[].class); + // when mock.mixedVarargs(42, "a", "b", "c"); - mock.mixedVarargs(42, "again ?!"); + + // then + verify(mock).mixedVarargs(any(), argumentCaptor.capture()); + assertThat(argumentCaptor.getAllValues()).containsExactly(new String[] {"a", "b", "c"}); + } + + @Test + public void should_capture_multiple_vararg_some_null_with_array_captor() { + // given + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String[].class); + + // when + mock.mixedVarargs(42, "a", null, "c"); + + // then + verify(mock).mixedVarargs(any(), argumentCaptor.capture()); + assertThat(argumentCaptor.getAllValues()).containsExactly(new String[] {"a", null, "c"}); + } + + @Test + public void should_capture_multiple_invocations_with_captor() { + // given + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class); + + // when + mock.mixedVarargs(42, "a", "b"); + mock.mixedVarargs(42, "c", "d"); + + // then + verify(mock, times(2)) + .mixedVarargs(any(), argumentCaptor.capture(), argumentCaptor.capture()); + + assertThat(argumentCaptor.getValue()).isEqualTo("d"); + assertThat(argumentCaptor.getAllValues()).containsExactly("a", "b", "c", "d"); + } + + @Test + public void should_capture_multiple_invocations_with_array_captor() { + // given + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String[].class); + + // when + mock.mixedVarargs(42, "a", "b"); + mock.mixedVarargs(42, "c", "d"); // then verify(mock, times(2)).mixedVarargs(any(), argumentCaptor.capture()); - Assertions.assertThat(argumentCaptor.getAllValues()) - .containsExactly("a", "b", "c", "again ?!"); + assertThat(argumentCaptor.getValue()).isEqualTo(new String[] {"c", "d"}); + assertThat(argumentCaptor.getAllValues()) + .containsExactly(new String[] {"a", "b"}, new String[] {"c", "d"}); } @Test @@ -304,7 +454,7 @@ public void should_capture_one_arg_even_when_using_vararg_captor_on_nonvararg_me // then verify(mock).simpleMethod(argumentCaptor.capture(), eq(2)); - Assertions.assertThat(argumentCaptor.getAllValues()).containsExactly("a"); + assertThat(argumentCaptor.getAllValues()).containsExactly("a"); } @Test @@ -323,7 +473,7 @@ public void captures_correctly_when_captor_used_multiple_times() { argumentCaptor.capture(), argumentCaptor.capture(), argumentCaptor.capture()); - Assertions.assertThat(argumentCaptor.getAllValues()).containsExactly("a", "b", "c"); + assertThat(argumentCaptor.getAllValues()).containsExactly("a", "b", "c"); } @Test @@ -336,7 +486,7 @@ public void captures_correctly_when_captor_used_on_pure_vararg_method() { // then verify(mock).varargs(eq(42), argumentCaptor.capture()); - Assertions.assertThat(argumentCaptor.getValue()).contains("capturedValue"); + assertThat(argumentCaptor.getValue()).contains("capturedValue"); } @SuppressWarnings("unchecked") diff --git a/src/test/java/org/mockitousage/matchers/HamcrestMatchersTest.java b/src/test/java/org/mockitousage/matchers/HamcrestMatchersTest.java index 3f16618c2f..261a3c3c96 100644 --- a/src/test/java/org/mockitousage/matchers/HamcrestMatchersTest.java +++ b/src/test/java/org/mockitousage/matchers/HamcrestMatchersTest.java @@ -6,7 +6,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.isA; import static org.junit.Assert.*; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.hamcrest.MockitoHamcrest.*; @@ -54,7 +57,44 @@ public void verifies_with_hamcrest_matcher() { } } - private class IntMatcher extends BaseMatcher { + @Test + public void does_not_verify_vararg_with_no_items() { + mock.varargs(); + + verify(mock, never()).varargs(argThat(isA(String.class))); + } + + @Test + public void verifies_vararg_with_single_item() { + mock.varargs("a"); + + verify(mock).varargs(argThat(isA(String.class))); + } + + @Test + public void does_not_verify_vararg_with_multiple_items() { + mock.varargs("a", "b"); + + verify(mock, never()).varargs(argThat(isA(String.class))); + } + + @Test + public void verify_vararg_with_multiple_item() { + mock.varargs("a", "b"); + + verify(mock).varargs(argThat(isA(String.class)), argThat(isA(String.class))); + } + + @Test + public void verifies_vararg_with_any_num_items() { + mock.varargs(); + mock.varargs("a"); + mock.varargs("a", "b"); + + verify(mock, times(3)).varargs(argThat(isA(String[].class), String[].class)); + } + + private final class IntMatcher extends BaseMatcher { public boolean matches(Object o) { return true; } diff --git a/src/test/java/org/mockitousage/matchers/VarargsTest.java b/src/test/java/org/mockitousage/matchers/VarargsTest.java index 0e5456fd2f..5daba370eb 100644 --- a/src/test/java/org/mockitousage/matchers/VarargsTest.java +++ b/src/test/java/org/mockitousage/matchers/VarargsTest.java @@ -40,6 +40,7 @@ public class VarargsTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Captor private ArgumentCaptor captor; + @Captor private ArgumentCaptor arrayCaptor; @Mock private IMethods mock; private static final Condition NULL = @@ -82,18 +83,18 @@ public void shouldMatchVarArgs_oneNullArg_eqNull() { @Test public void shouldMatchVarArgs_oneNullArg_isNull() { - Object arg = null; - mock.varargs(arg); + mock.varargs((Object) null); - verify(mock).varargs(ArgumentMatchers.isNull()); + verify(mock).varargs(ArgumentMatchers.isNull()); + verify(mock, never()).varargs(isNull(Object[].class)); } @Test public void shouldMatchVarArgs_nullArrayArg() { - Object[] argArray = null; - mock.varargs(argArray); + mock.varargs((Object[]) null); - verify(mock).varargs(ArgumentMatchers.isNull()); + verify(mock).varargs(isNull(Object[].class)); + verify(mock).varargs(ArgumentMatchers.isNull()); } @Test @@ -111,28 +112,28 @@ public void shouldnotMatchVarArgs_twoArgsOneMatcher() { public void shouldMatchVarArgs_emptyVarArgsOneAnyMatcher() { mock.varargs(); - verify(mock).varargs((String[]) any()); // any() -> VarargMatcher + verify(mock).varargs(any(String[].class)); } @Test public void shouldMatchVarArgs_oneArgsOneAnyMatcher() { mock.varargs(1); - verify(mock).varargs(ArgumentMatchers.any()); // any() -> VarargMatcher + verify(mock).varargs(any(Object[].class)); } @Test public void shouldMatchVarArgs_twoArgsOneAnyMatcher() { mock.varargs(1, 2); - verify(mock).varargs(ArgumentMatchers.any()); // any() -> VarargMatcher + verify(mock).varargs(any(Object[].class)); } @Test public void shouldMatchVarArgs_twoArgsTwoAnyMatcher() { mock.varargs(1, 2); - verify(mock).varargs(any(), ArgumentMatchers.any()); // any() -> VarargMatcher + verify(mock).varargs(any(), ArgumentMatchers.any()); } @Test @@ -141,7 +142,7 @@ public void shouldMatchVarArgs_twoArgsThreeAnyMatcher() { assertThatThrownBy( () -> { - verify(mock).varargs(any(), any(), any()); // any() -> VarargMatcher + verify(mock).varargs(any(), any(), any()); }) .hasMessageContaining("Argument(s) are different"); } @@ -192,9 +193,9 @@ public void shouldMatchVarArgs_oneArgIsNotNull() { public void shouldCaptureVarArgs_noArgs() { mock.varargs(); - verify(mock).varargs(captor.capture()); + verify(mock).varargs(arrayCaptor.capture()); - assertThatCaptor(captor).isEmpty(); + assertThatCaptor(arrayCaptor).contains(new String[] {}); } @Test @@ -224,9 +225,9 @@ public void shouldCaptureVarArgs_nullArrayArg() { public void shouldCaptureVarArgs_twoArgsOneCapture() { mock.varargs("1", "2"); - verify(mock).varargs(captor.capture()); + verify(mock).varargs(arrayCaptor.capture()); - assertThatCaptor(captor).contains("1", "2"); + assertThatCaptor(arrayCaptor).contains(new String[] {"1", "2"}); } @Test @@ -238,15 +239,6 @@ public void shouldCaptureVarArgs_twoArgsTwoCaptures() { assertThatCaptor(captor).contains("1", "2"); } - @Test - public void shouldCaptureVarArgs_oneNullArgument() { - mock.varargs("1", null); - - verify(mock).varargs(captor.capture()); - - assertThatCaptor(captor).contains("1", (String) null); - } - @Test public void shouldCaptureVarArgs_oneNullArgument2() { mock.varargs("1", null); @@ -363,20 +355,6 @@ public void shouldMockVarargsInvocation_multiple_vararg_matcher() { assertThat(mock.methodWithVarargAndNonVarargVariants("a", "b", "c")).isNull(); } - @Test - public void shouldMockVarargsInvocationUsingCasts() { - given(mock.methodWithVarargAndNonVarargVariants((String) any())) - .willReturn("single arg method"); - given(mock.methodWithVarargAndNonVarargVariants((String[]) any())) - .willReturn("var arg method"); - - assertThat(mock.methodWithVarargAndNonVarargVariants("a")).isEqualTo("single arg method"); - assertThat(mock.methodWithVarargAndNonVarargVariants()).isEqualTo("var arg method"); - assertThat(mock.methodWithVarargAndNonVarargVariants(new String[] {"a"})) - .isEqualTo("var arg method"); - assertThat(mock.methodWithVarargAndNonVarargVariants("a", "b")).isEqualTo("var arg method"); - } - @Test public void shouldMockVarargsInvocationForSuperType() { given(mock.varargsReturningString(any(Object[].class))).willReturn("a"); diff --git a/src/test/java/org/mockitousage/stubbing/StubbingWithAdditionalAnswersTest.java b/src/test/java/org/mockitousage/stubbing/StubbingWithAdditionalAnswersTest.java index d037cfc0a0..2b96bdf0a9 100644 --- a/src/test/java/org/mockitousage/stubbing/StubbingWithAdditionalAnswersTest.java +++ b/src/test/java/org/mockitousage/stubbing/StubbingWithAdditionalAnswersTest.java @@ -56,7 +56,8 @@ public void can_return_arguments_of_invocation() throws Exception { given(iMethods.objectArgMethod(any())).will(returnsFirstArg()); given(iMethods.threeArgumentMethod(eq(0), any(), anyString())).will(returnsSecondArg()); given(iMethods.threeArgumentMethod(eq(1), any(), anyString())).will(returnsLastArg()); - given(iMethods.mixedVarargsReturningString(eq(1), any())).will(returnsArgAt(2)); + given(iMethods.mixedVarargsReturningString(eq(1), any(String[].class))) + .will(returnsArgAt(2)); assertThat(iMethods.objectArgMethod("first")).isEqualTo("first"); assertThat(iMethods.threeArgumentMethod(0, "second", "whatever")).isEqualTo("second"); @@ -66,8 +67,10 @@ public void can_return_arguments_of_invocation() throws Exception { @Test public void can_return_var_arguments_of_invocation() throws Exception { - given(iMethods.mixedVarargsReturningStringArray(eq(1), any())).will(returnsLastArg()); - given(iMethods.mixedVarargsReturningObjectArray(eq(1), any())).will(returnsArgAt(1)); + given(iMethods.mixedVarargsReturningStringArray(eq(1), any(String[].class))) + .will(returnsLastArg()); + given(iMethods.mixedVarargsReturningObjectArray(eq(1), any(String[].class))) + .will(returnsArgAt(1)); assertThat(iMethods.mixedVarargsReturningStringArray(1, "the", "var", "args")) .containsExactlyInAnyOrder("the", "var", "args"); @@ -77,7 +80,8 @@ public void can_return_var_arguments_of_invocation() throws Exception { @Test public void returns_arg_at_throws_on_out_of_range_var_args() throws Exception { - given(iMethods.mixedVarargsReturningString(eq(1), any())).will(returnsArgAt(3)); + given(iMethods.mixedVarargsReturningString(eq(1), any(String[].class))) + .will(returnsArgAt(3)); assertThatThrownBy(() -> iMethods.mixedVarargsReturningString(1, "a", "b")) .isInstanceOf(MockitoException.class) @@ -111,7 +115,7 @@ public void can_return_after_delay() throws Exception { @Test public void can_return_expanded_arguments_of_invocation() throws Exception { - given(iMethods.varargsObject(eq(1), any())).will(returnsArgAt(3)); + given(iMethods.varargsObject(eq(1), any(Object[].class))).will(returnsArgAt(3)); assertThat(iMethods.varargsObject(1, "bob", "alexander", "alice", "carl")) .isEqualTo("alice"); @@ -389,7 +393,7 @@ public void answer( @Test public void can_return_based_on_strongly_types_one_parameter_var_args_function() throws Exception { - given(iMethods.varargs(any())) + given(iMethods.varargs(any(String[].class))) .will( answer( new Answer1() { @@ -406,7 +410,7 @@ public void will_execute_a_void_based_on_strongly_typed_one_parameter_var_args_f throws Exception { final IMethods target = mock(IMethods.class); - given(iMethods.varargs(any())) + given(iMethods.varargs(any(String[].class))) .will( answerVoid( new VoidAnswer1() { @@ -425,7 +429,7 @@ public void answer(String[] s) { @Test public void can_return_based_on_strongly_typed_two_parameter_var_args_function() throws Exception { - given(iMethods.mixedVarargsReturningString(any(), any())) + given(iMethods.mixedVarargsReturningString(any(), any(String[].class))) .will( answer( new Answer2() { @@ -442,7 +446,7 @@ public void will_execute_a_void_based_on_strongly_typed_two_parameter_var_args_f throws Exception { final IMethods target = mock(IMethods.class); - given(iMethods.mixedVarargsReturningString(any(), any())) + given(iMethods.mixedVarargsReturningString(any(), any(String[].class))) .will( answerVoid( new VoidAnswer2() { @@ -463,7 +467,7 @@ public void can_return_based_on_strongly_typed_three_parameter_var_args_function throws Exception { final IMethods target = mock(IMethods.class); - given(iMethods.threeArgumentVarArgsMethod(anyInt(), any(), any())) + given(iMethods.threeArgumentVarArgsMethod(anyInt(), any(), any(String[].class))) .will( answer( new Answer3() { @@ -486,7 +490,7 @@ public void will_execute_a_void_based_on_strongly_typed_three_parameter_var_args throws Exception { final IMethods target = mock(IMethods.class); - given(iMethods.threeArgumentVarArgsMethod(anyInt(), any(), any())) + given(iMethods.threeArgumentVarArgsMethod(anyInt(), any(), any(String[].class))) .will( answerVoid( new VoidAnswer3() { @@ -506,7 +510,7 @@ public void answer(Integer i, String s1, String[] s2) { public void can_return_based_on_strongly_typed_four_parameter_var_args_function() throws Exception { final IMethods target = mock(IMethods.class); - given(iMethods.fourArgumentVarArgsMethod(anyInt(), any(), anyInt(), any())) + given(iMethods.fourArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(String[].class))) .will( answer( new Answer4() { @@ -531,7 +535,7 @@ public void will_execute_a_void_based_on_strongly_typed_four_parameter_var_args_ throws Exception { final IMethods target = mock(IMethods.class); - given(iMethods.fourArgumentVarArgsMethod(anyInt(), any(), anyInt(), any())) + given(iMethods.fourArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(String[].class))) .will( answerVoid( new VoidAnswer4() { @@ -552,7 +556,9 @@ public void answer( public void can_return_based_on_strongly_typed_five_parameter_var_args_function() throws Exception { final IMethods target = mock(IMethods.class); - given(iMethods.fiveArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(), any())) + given( + iMethods.fiveArgumentVarArgsMethod( + anyInt(), any(), anyInt(), any(), any(String[].class))) .will( answer( new Answer5() { @@ -580,7 +586,9 @@ public void will_execute_a_void_based_on_strongly_typed_five_parameter_var_args_ throws Exception { final IMethods target = mock(IMethods.class); - given(iMethods.fiveArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(), any())) + given( + iMethods.fiveArgumentVarArgsMethod( + anyInt(), any(), anyInt(), any(), any(String[].class))) .will( answerVoid( new VoidAnswer5() { @@ -605,7 +613,9 @@ public void answer( public void can_return_based_on_strongly_typed_six_parameter_var_args_function() throws Exception { final IMethods target = mock(IMethods.class); - given(iMethods.sixArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(), any(), any())) + given( + iMethods.sixArgumentVarArgsMethod( + anyInt(), any(), anyInt(), any(), any(), any(String[].class))) .will( answer( new Answer6< @@ -641,7 +651,9 @@ public String answer( public void will_execute_a_void_returning_strongly_typed_six_parameter_var_args_function() throws Exception { final IMethods target = mock(IMethods.class); - given(iMethods.sixArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(), any(), any())) + given( + iMethods.sixArgumentVarArgsMethod( + anyInt(), any(), anyInt(), any(), any(), any(String[].class))) .will( answerVoid( new VoidAnswer6< @@ -667,7 +679,7 @@ public void answer( @Test public void can_accept_array_supertype_for_strongly_typed_var_args_function() throws Exception { - given(iMethods.varargs(any())) + given(iMethods.varargs(any(String[].class))) .will( answer( new Answer1() { @@ -681,7 +693,7 @@ public Integer answer(Object[] s) { @Test public void can_accept_non_vararg_answer_on_var_args_function() throws Exception { - given(iMethods.varargs(any())) + given(iMethods.varargs(any(String[].class))) .will( answer( new Answer2() { @@ -695,7 +707,7 @@ public Integer answer(String s1, String s2) { @Test public void should_work_with_var_args_with_no_elements() throws Exception { - given(iMethods.varargs(any())) + given(iMethods.varargs(any(String[].class))) .will( answer( new Answer1() { @@ -709,7 +721,7 @@ public Integer answer(String[] s) { @Test public void should_work_with_array_var_args() throws Exception { - given(iMethods.arrayVarargsMethod(any())) + given(iMethods.arrayVarargsMethod(any(String[][].class))) .will( answer( new Answer1() { From ffb294c0a11fef0184c77a4a3d64a9d729fe3a68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Dec 2022 19:41:17 +0100 Subject: [PATCH 117/160] Bump versions.errorprone from 2.16 to 2.17.0 (#2844) Bumps `versions.errorprone` from 2.16 to 2.17.0. Updates `error_prone_core` from 2.16 to 2.17.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.16...v2.17.0) Updates `error_prone_test_helpers` from 2.16 to 2.17.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.16...v2.17.0) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.google.errorprone:error_prone_test_helpers 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> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 9d39a91ee4..08894c7d3e 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -6,7 +6,7 @@ def versions = [:] versions.bytebuddy = '1.12.20' versions.junitJupiter = '5.9.1' -versions.errorprone = '2.16' +versions.errorprone = '2.17.0' libraries.junit4 = 'junit:junit:4.13.2' libraries.junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}" From e910bcc335b6e3f06637c71e7a3853ae84cb4bef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 08:28:47 +0100 Subject: [PATCH 118/160] Bump com.diffplug.spotless from 6.12.0 to 6.12.1 (#2846) Bumps com.diffplug.spotless from 6.12.0 to 6.12.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 7625492d08..9698007a75 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.12.0' + id 'com.diffplug.spotless' version '6.12.1' id 'eclipse' id 'com.github.ben-manes.versions' version '0.44.0' id 'biz.aQute.bnd.builder' version '6.4.0' From 6c929446f6fbb5790efb69d426f4857c312cc03e Mon Sep 17 00:00:00 2001 From: Siva Kumar Edupuganti Date: Tue, 3 Jan 2023 14:13:41 -0700 Subject: [PATCH 119/160] Fix grammar and Tidelift link (#2847) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0e36847557..f1f4b11530 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Still on Mockito 1.x? See [what's new](https://github.com/mockito/mockito/wiki/W ## Mockito for enterprise -Available as part of the Tidelift Subscription +Available as part of the [Tidelift](https://tidelift.com/subscription/pkg/maven-org-mockito-mockito-core) Subscription. The maintainers of org.mockito:mockito-core and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, @@ -73,7 +73,7 @@ Then, _open_ the generated *.ipr file in IDEA. ## How to release new version? -1. Every change on the main development branch is released as -SNAPSHOT version to Sonatype snapshot repo +1. Every change on the main development branch is released as `-SNAPSHOT` version to Sonatype snapshot repo at https://s01.oss.sonatype.org/content/repositories/snapshots/org/mockito/mockito-core. 2. In order to release a non-snapshot version to Maven Central push an annotated tag, for example: From 7b5b8dd8408173ce1ee6fd3485a8bd5d5aae9ed7 Mon Sep 17 00:00:00 2001 From: cedretaber Date: Wed, 4 Jan 2023 23:46:10 +0900 Subject: [PATCH 120/160] Remove use case for non-existent method VerificationWithTimeout#never (#2848) --- .../java/org/mockito/verification/VerificationWithTimeout.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/mockito/verification/VerificationWithTimeout.java b/src/main/java/org/mockito/verification/VerificationWithTimeout.java index ad110d0cce..3e70d116bb 100644 --- a/src/main/java/org/mockito/verification/VerificationWithTimeout.java +++ b/src/main/java/org/mockito/verification/VerificationWithTimeout.java @@ -12,8 +12,6 @@ *
    
      * verify(mock, timeout(100).times(5)).foo();
      *
    - * verify(mock, timeout(100).never()).bar();
    - *
      * verify(mock, timeout(200).atLeastOnce()).baz();
      * 
    * From b1b6d6afcbb6159b91c993b2d11f1498e9d25ad6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jan 2023 22:38:55 +0100 Subject: [PATCH 121/160] Bump espresso-core from 3.5.0 to 3.5.1 (#2849) Bumps espresso-core from 3.5.0 to 3.5.1. --- updated-dependencies: - dependency-name: androidx.test.espresso:espresso-core 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 08894c7d3e..e9811ea943 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -47,5 +47,5 @@ libraries.android = [ compat: 'androidx.appcompat:appcompat:1.4.2', material: 'com.google.android.material:material:1.6.1', junit: 'androidx.test.ext:junit:1.1.4', - espresso: 'androidx.test.espresso:espresso-core:3.5.0', + espresso: 'androidx.test.espresso:espresso-core:3.5.1', ] From 4d62fa75c731b63bec2b2e1ed22eadb472afb1a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jan 2023 23:11:42 +0100 Subject: [PATCH 122/160] Bump junit from 1.1.4 to 1.1.5 (#2850) Bumps junit from 1.1.4 to 1.1.5. --- updated-dependencies: - dependency-name: androidx.test.ext:junit 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 e9811ea943..3e4b9cd6f6 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -46,6 +46,6 @@ libraries.android = [ ktx: 'androidx.core:core-ktx:1.8.0', compat: 'androidx.appcompat:appcompat:1.4.2', material: 'com.google.android.material:material:1.6.1', - junit: 'androidx.test.ext:junit:1.1.4', + junit: 'androidx.test.ext:junit:1.1.5', espresso: 'androidx.test.espresso:espresso-core:3.5.1', ] From dbd7f2ff4aae294feb7c9f5506398c1d6e07dfac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Jan 2023 22:06:40 +0100 Subject: [PATCH 123/160] Bump versions.bytebuddy from 1.12.20 to 1.12.21 (#2852) Bumps `versions.bytebuddy` from 1.12.20 to 1.12.21. Updates `byte-buddy` from 1.12.20 to 1.12.21 - [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.20...byte-buddy-1.12.21) Updates `byte-buddy-agent` from 1.12.20 to 1.12.21 - [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.20...byte-buddy-1.12.21) Updates `byte-buddy-android` from 1.12.20 to 1.12.21 - [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.20...byte-buddy-1.12.21) --- 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 3e4b9cd6f6..19973a5034 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.12.20' +versions.bytebuddy = '1.12.21' versions.junitJupiter = '5.9.1' versions.errorprone = '2.17.0' From c5d7fbc532301d003293649b71c8a6f87283638e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Jan 2023 11:26:52 +0100 Subject: [PATCH 124/160] Bump assertj-core from 3.23.1 to 3.24.1 (#2854) Bumps assertj-core from 3.23.1 to 3.24.1. --- updated-dependencies: - dependency-name: org.assertj:assertj-core 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> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 19973a5034..e9e88327ec 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -13,7 +13,7 @@ libraries.junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${versions.juni libraries.junitPlatformLauncher = 'org.junit.platform:junit-platform-launcher:1.9.1' 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' +libraries.assertj = 'org.assertj:assertj-core:3.24.1' libraries.hamcrest = 'org.hamcrest:hamcrest-core:2.2' libraries.opentest4j = 'org.opentest4j:opentest4j:1.2.0' From a9595f559cf4acdaed92371537ced982e30f508d Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Sun, 8 Jan 2023 15:45:55 -0500 Subject: [PATCH 125/160] Switch the default mockmaker to the inline mockmaker on JDK 17+ (#2834) Fixes #2589 Signed-off-by: Andriy Redko --- .../plugins/DefaultMockitoPlugins.java | 7 +++-- .../bytebuddy/InlineBytecodeGenerator.java | 2 ++ .../stacktrace/DefaultStackTraceCleaner.java | 9 +++++- .../defaultanswers/ReturnsSmartNulls.java | 22 ++++++++++++++ .../InstrumentationMemberAccessor.java | 29 ++++++++++++++----- src/test/java/org/mockito/MockitoTest.java | 23 +++++++++++++++ .../plugins/DefaultMockitoPluginsTest.java | 4 +-- .../runners/DefaultInternalRunnerTest.java | 8 +++++ .../annotation/SpyAnnotationTest.java | 24 ++++++++++++++- .../ClassCacheVersusClassReloadingTest.java | 4 ++- .../mockitousage/misuse/InvalidUsageTest.java | 14 +++++++++ .../org.mockito.plugins.MemberAccessor | 1 + .../mockito/moduletest/ModuleAccessTest.java | 23 +++++++++++++++ .../moduletest/ReplicatingClassLoader.java | 5 ++++ subprojects/osgi-test/osgi-test.gradle | 1 + .../java/org/mockito/osgitest/OsgiTest.java | 26 +++++++++++++---- .../testbundle/MockNonPublicClassTest.java | 19 ++++++++++++ .../programmatic-test.gradle | 4 +++ .../ProgrammaticMockMakerAnnotationTest.java | 10 +++++-- .../org.mockito.plugins.MemberAccessor | 1 + 20 files changed, 215 insertions(+), 21 deletions(-) create mode 100644 subprojects/android/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor create mode 100644 subprojects/osgi-test/src/testBundle/java/org/mockito/osgitest/testbundle/MockNonPublicClassTest.java rename {src => subprojects/programmatic-test/src}/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java (85%) create mode 100644 subprojects/proxy/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor diff --git a/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java b/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java index 14f52f3705..780742c64a 100644 --- a/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java +++ b/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java @@ -28,13 +28,14 @@ public class DefaultMockitoPlugins implements MockitoPlugins { static final String SUBCLASS_ALIAS = MockMakers.SUBCLASS; public static final Set MOCK_MAKER_ALIASES = new HashSet<>(); static final String MODULE_ALIAS = "member-accessor-module"; + static final String REFLECTION_ALIAS = "member-accessor-reflection"; static { // Keep the mapping: plugin interface name -> plugin implementation class name DEFAULT_PLUGINS.put(PluginSwitch.class.getName(), DefaultPluginSwitch.class.getName()); DEFAULT_PLUGINS.put( MockMaker.class.getName(), - "org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker"); + "org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker"); DEFAULT_PLUGINS.put( StackTraceCleanerProvider.class.getName(), "org.mockito.internal.exceptions.stacktrace.DefaultStackTraceCleanerProvider"); @@ -53,9 +54,11 @@ public class DefaultMockitoPlugins implements MockitoPlugins { MockitoLogger.class.getName(), "org.mockito.internal.util.ConsoleMockitoLogger"); DEFAULT_PLUGINS.put( MemberAccessor.class.getName(), - "org.mockito.internal.util.reflection.ReflectionMemberAccessor"); + "org.mockito.internal.util.reflection.ModuleMemberAccessor"); DEFAULT_PLUGINS.put( MODULE_ALIAS, "org.mockito.internal.util.reflection.ModuleMemberAccessor"); + DEFAULT_PLUGINS.put( + REFLECTION_ALIAS, "org.mockito.internal.util.reflection.ReflectionMemberAccessor"); DEFAULT_PLUGINS.put( DoNotMockEnforcer.class.getName(), "org.mockito.internal.configuration.DefaultDoNotMockEnforcer"); diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java index ed12d2ffe2..009daa4ccb 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java @@ -205,6 +205,7 @@ public Class mockClass(MockFeatures features) { boolean subclassingRequired = !features.interfaces.isEmpty() || features.serializableMode != SerializableMode.NONE + || features.stripAnnotations || Modifier.isAbstract(features.mockedType.getModifiers()); checkSupportedCombination(subclassingRequired, features); @@ -416,6 +417,7 @@ public synchronized void clearAllCaches() { } mocked.clear(); flatMocked.clear(); + subclassEngine.clearAllCaches(); try { instrumentation.retransformClasses(types.toArray(new Class[0])); } catch (UnmodifiableClassException e) { 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 6437ae4aa4..797515dea1 100644 --- a/src/main/java/org/mockito/internal/exceptions/stacktrace/DefaultStackTraceCleaner.java +++ b/src/main/java/org/mockito/internal/exceptions/stacktrace/DefaultStackTraceCleaner.java @@ -24,13 +24,20 @@ public boolean isIn(StackFrameMetadata e) { private boolean isIn(String className) { if (isFromMockitoRunner(className) || isFromMockitoRule(className)) { return true; - } else if (isMockDispatcher(className) || isFromMockito(className)) { + } else if (isMockDispatcher(className) + || isFromMockito(className) + || isMethodHandle(className)) { return false; } else { return true; } } + /* Some mock makers (like inline) use java.lang.invoke.MethodHandle to dispatch calls */ + private static boolean isMethodHandle(String className) { + return className.startsWith("java.lang.invoke.MethodHandle"); + } + private static boolean isMockDispatcher(String className) { return (className.contains("$$EnhancerByMockitoWithCGLIB$$") || className.contains("$MockitoMock$")); 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 a6e0b2c138..6f6476d046 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java @@ -8,9 +8,12 @@ import static org.mockito.internal.util.ObjectMethodsGuru.isToStringMethod; import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.Arrays; import org.mockito.Mockito; import org.mockito.internal.creation.MockSettingsImpl; +import org.mockito.internal.creation.bytebuddy.MockAccess; import org.mockito.internal.debugging.LocationFactory; import org.mockito.internal.util.MockUtil; import org.mockito.invocation.InvocationOnMock; @@ -89,9 +92,28 @@ public Object answer(InvocationOnMock currentInvocation) throws Throwable { if (isToStringMethod(currentInvocation.getMethod())) { return "SmartNull returned by this unstubbed method call on a mock:\n" + unstubbedInvocation; + } else if (isMethodOf( + MockAccess.class, currentInvocation.getMock(), currentInvocation.getMethod())) { + /* The MockAccess methods should be called directly */ + return currentInvocation.callRealMethod(); } throw smartNullPointerException(unstubbedInvocation.toString(), location); } + + private static boolean isMethodOf(Class clazz, Object instance, Method method) { + if (!clazz.isInstance(instance)) { + return false; + } + + for (Method m : clazz.getDeclaredMethods()) { + if (m.getName().equalsIgnoreCase(method.getName()) + && Arrays.equals(m.getParameterTypes(), method.getParameterTypes())) { + return true; + } + } + + return false; + } } } 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 13d8416586..61b3f06c46 100644 --- a/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java +++ b/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java @@ -188,7 +188,16 @@ public Object newInstance( onConstruction.invoke( () -> { try { - return DISPATCHER.invokeWithArguments(handle, arguments); + // Use handle.asFixedArity() to handle varargs bindings properly + // (as by default Java will create method handle with + // asVarargsCollector), fe: + // + // private static class Varargs { + // Varargs(String whatever, Observer... observers) { + // } + // } + return DISPATCHER.invokeWithArguments( + handle.asFixedArity(), arguments); } catch (Throwable throwable) { thrown.set(true); return throwable; @@ -372,17 +381,23 @@ private static void assureArguments( throw new IllegalArgumentException("Cannot access " + target + " on " + owner); } } - if (types.length != values.length) { + + Object[] args = values; + if (args == null) { + args = new Object[0]; + } + + if (types.length != args.length) { throw new IllegalArgumentException( "Incorrect number of arguments for " + target + ": expected " + types.length + " but recevied " - + values.length); + + args.length); } - for (int index = 0; index < values.length; index++) { - if (values[index] == null) { + for (int index = 0; index < args.length; index++) { + if (args[index] == null) { if (types[index].isPrimitive()) { throw new IllegalArgumentException( "Cannot assign null to primitive type " @@ -394,10 +409,10 @@ private static void assureArguments( } } else { Class resolved = WRAPPERS.getOrDefault(types[index], types[index]); - if (!resolved.isAssignableFrom(values[index].getClass())) { + if (!resolved.isAssignableFrom(args[index].getClass())) { throw new IllegalArgumentException( "Cannot assign value of type " - + values[index].getClass() + + args[index].getClass() + " to " + resolved + " for " diff --git a/src/test/java/org/mockito/MockitoTest.java b/src/test/java/org/mockito/MockitoTest.java index 168acb8f40..1ebbb89b80 100644 --- a/src/test/java/org/mockito/MockitoTest.java +++ b/src/test/java/org/mockito/MockitoTest.java @@ -7,16 +7,21 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.not; import static org.mockito.Mockito.times; import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress; import java.util.List; +import org.junit.Assume; import org.junit.Test; import org.mockito.exceptions.base.MockitoException; import org.mockito.exceptions.misusing.NotAMockException; import org.mockito.exceptions.misusing.NullInsteadOfMockException; +import org.mockito.internal.configuration.plugins.Plugins; import org.mockito.internal.creation.MockSettingsImpl; +import org.mockito.plugins.InlineMockMaker; @SuppressWarnings("unchecked") public class MockitoTest { @@ -99,6 +104,8 @@ public void shouldValidateMockWhenCreatingInOrderObject() { @SuppressWarnings({"CheckReturnValue", "MockitoUsage"}) @Test public void shouldGiveExplanationOnStaticMockingWithoutInlineMockMaker() { + Assume.assumeThat(Plugins.getMockMaker(), not(instanceOf(InlineMockMaker.class))); + assertThatThrownBy( () -> { Mockito.mockStatic(Object.class); @@ -114,6 +121,8 @@ public void shouldGiveExplanationOnStaticMockingWithoutInlineMockMaker() { @SuppressWarnings({"CheckReturnValue", "MockitoUsage"}) @Test public void shouldGiveExplanationOnConstructionMockingWithoutInlineMockMaker() { + Assume.assumeThat(Plugins.getMockMaker(), not(instanceOf(InlineMockMaker.class))); + assertThatThrownBy( () -> { Mockito.mockConstruction(Object.class); @@ -126,6 +135,20 @@ public void shouldGiveExplanationOnConstructionMockingWithoutInlineMockMaker() { "Note that Mockito's inline mock maker is not supported on Android."); } + @SuppressWarnings({"CheckReturnValue", "MockitoUsage"}) + @Test + public void shouldGiveExplanationOnConstructionMockingWithInlineMockMaker() { + Assume.assumeThat(Plugins.getMockMaker(), instanceOf(InlineMockMaker.class)); + + assertThatThrownBy( + () -> { + Mockito.mockConstruction(Object.class); + }) + .isInstanceOf(MockitoException.class) + .hasMessageContainingAll( + "It is not possible to mock construction of the Object class to avoid inference with default object constructor chains"); + } + @Test public void shouldStartingMockSettingsContainDefaultBehavior() { // given diff --git a/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java b/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java index aa1835696e..61fc8e8ed1 100644 --- a/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java +++ b/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java @@ -10,7 +10,6 @@ import static org.mockito.internal.configuration.plugins.DefaultMockitoPlugins.SUBCLASS_ALIAS; import org.junit.Test; -import org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker; import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker; import org.mockito.internal.util.ConsoleMockitoLogger; import org.mockito.plugins.InstantiatorProvider2; @@ -35,7 +34,8 @@ public void provides_plugins() throws Exception { "org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker", DefaultMockitoPlugins.getDefaultPluginClass(SUBCLASS_ALIAS)); assertEquals( - ByteBuddyMockMaker.class, plugins.getDefaultPlugin(MockMaker.class).getClass()); + InlineByteBuddyMockMaker.class, + plugins.getDefaultPlugin(MockMaker.class).getClass()); assertNotNull(plugins.getDefaultPlugin(InstantiatorProvider2.class)); assertEquals( ConsoleMockitoLogger.class, diff --git a/src/test/java/org/mockito/internal/runners/DefaultInternalRunnerTest.java b/src/test/java/org/mockito/internal/runners/DefaultInternalRunnerTest.java index c8912ec223..0e4e40076e 100644 --- a/src/test/java/org/mockito/internal/runners/DefaultInternalRunnerTest.java +++ b/src/test/java/org/mockito/internal/runners/DefaultInternalRunnerTest.java @@ -4,11 +4,14 @@ */ package org.mockito.internal.runners; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; +import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; @@ -18,9 +21,11 @@ import org.junit.runner.notification.RunNotifier; import org.junit.runners.model.Statement; import org.mockito.Mock; +import org.mockito.internal.configuration.plugins.Plugins; import org.mockito.internal.junit.MockitoTestListener; import org.mockito.internal.junit.TestFinishedEvent; import org.mockito.internal.util.Supplier; +import org.mockito.plugins.InlineMockMaker; public class DefaultInternalRunnerTest { @@ -42,6 +47,9 @@ public void does_not_fail_when_tests_succeeds() throws Exception { @Test public void does_not_fail_second_test_when_first_test_fail() throws Exception { + // The TestFailOnInitialization is initialized properly by inline mock maker + Assume.assumeThat(Plugins.getMockMaker(), not(instanceOf(InlineMockMaker.class))); + new DefaultInternalRunner(TestFailOnInitialization.class, supplier) .run(newNotifier(runListener)); diff --git a/src/test/java/org/mockitousage/annotation/SpyAnnotationTest.java b/src/test/java/org/mockitousage/annotation/SpyAnnotationTest.java index 01edd3c687..daa71bed33 100644 --- a/src/test/java/org/mockitousage/annotation/SpyAnnotationTest.java +++ b/src/test/java/org/mockitousage/annotation/SpyAnnotationTest.java @@ -6,6 +6,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -22,6 +24,7 @@ import java.util.LinkedList; import java.util.List; +import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -30,6 +33,8 @@ import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.mockito.exceptions.base.MockitoException; +import org.mockito.internal.configuration.plugins.Plugins; +import org.mockito.plugins.InlineMockMaker; import org.mockitoutil.TestBase; @SuppressWarnings("unused") @@ -188,8 +193,21 @@ class WithSpy { } } + @Test + public void should_spy_private_inner() throws Exception { + Assume.assumeThat(Plugins.getMockMaker(), instanceOf(InlineMockMaker.class)); + + WithInnerPrivate inner = new WithInnerPrivate(); + MockitoAnnotations.openMocks(inner); + + when(inner.spy_field.lenght()).thenReturn(10); + assertEquals(10, inner.spy_field.lenght()); + } + @Test public void should_report_private_inner_not_supported() throws Exception { + Assume.assumeThat(Plugins.getMockMaker(), not(instanceOf(InlineMockMaker.class))); + try { MockitoAnnotations.openMocks(new WithInnerPrivate()); fail(); @@ -287,7 +305,11 @@ private class InnerPrivateConcrete extends InnerPrivateAbstract {} static class WithInnerPrivate { @Spy private InnerPrivate spy_field; - private class InnerPrivate {} + private class InnerPrivate { + int lenght() { + return 0; + } + } private class InnerPrivateSub extends InnerPrivate {} } diff --git a/src/test/java/org/mockitousage/configuration/ClassCacheVersusClassReloadingTest.java b/src/test/java/org/mockitousage/configuration/ClassCacheVersusClassReloadingTest.java index 7d9c95061f..6c08198864 100644 --- a/src/test/java/org/mockitousage/configuration/ClassCacheVersusClassReloadingTest.java +++ b/src/test/java/org/mockitousage/configuration/ClassCacheVersusClassReloadingTest.java @@ -74,7 +74,9 @@ private static SimplePerRealmReloadingClassLoader.ReloadClassPredicate reloadMoc return new SimplePerRealmReloadingClassLoader.ReloadClassPredicate() { public boolean acceptReloadOf(String qualifiedName) { return (!qualifiedName.contains("net.bytebuddy") - && qualifiedName.contains("org.mockito")); + && qualifiedName.contains("org.mockito") + && !qualifiedName.contains( + "org.mockito.internal.creation.bytebuddy.inject")); } }; } diff --git a/src/test/java/org/mockitousage/misuse/InvalidUsageTest.java b/src/test/java/org/mockitousage/misuse/InvalidUsageTest.java index 35c135a62e..e4a8275be9 100644 --- a/src/test/java/org/mockitousage/misuse/InvalidUsageTest.java +++ b/src/test/java/org/mockitousage/misuse/InvalidUsageTest.java @@ -4,7 +4,10 @@ */ package org.mockitousage.misuse; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.not; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoInteractions; @@ -12,11 +15,14 @@ import static org.mockito.Mockito.when; import org.junit.After; +import org.junit.Assume; import org.junit.Test; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.exceptions.base.MockitoException; import org.mockito.exceptions.misusing.MissingMethodInvocationException; +import org.mockito.internal.configuration.plugins.Plugins; +import org.mockito.plugins.InlineMockMaker; import org.mockitousage.IMethods; import org.mockitoutil.TestBase; @@ -155,6 +161,8 @@ final class FinalClass {} @Test public void shouldNotAllowMockingFinalClassesIfDisabled() { + Assume.assumeThat(Plugins.getMockMaker(), not(instanceOf(InlineMockMaker.class))); + assertThatThrownBy( () -> { mock(FinalClass.class); @@ -166,6 +174,12 @@ public void shouldNotAllowMockingFinalClassesIfDisabled() { " - final class"); } + @Test + public void shouldAllowMockingFinalClassesIfEnabled() { + Assume.assumeThat(Plugins.getMockMaker(), instanceOf(InlineMockMaker.class)); + assertThat(mock(FinalClass.class)).isInstanceOf(FinalClass.class); + } + @SuppressWarnings({"CheckReturnValue", "MockitoUsage"}) @Test public void shouldNotAllowMockingPrimitives() { diff --git a/subprojects/android/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor b/subprojects/android/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor new file mode 100644 index 0000000000..71111e3378 --- /dev/null +++ b/subprojects/android/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor @@ -0,0 +1 @@ +member-accessor-reflection diff --git a/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleAccessTest.java b/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleAccessTest.java index 55923e77b5..460dc906f0 100644 --- a/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleAccessTest.java +++ b/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleAccessTest.java @@ -4,9 +4,11 @@ */ package org.mockito.moduletest; +import org.junit.Assume; import org.junit.Test; import org.mockito.Mockito; import org.mockito.exceptions.base.MockitoException; +import org.mockito.internal.configuration.plugins.Plugins; import org.mockito.internal.util.reflection.ModuleMemberAccessor; import org.mockito.internal.util.reflection.ReflectionMemberAccessor; @@ -17,6 +19,8 @@ import static junit.framework.TestCase.fail; import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.not; import static org.mockito.moduletest.ModuleUtil.layer; import static org.mockito.moduletest.ModuleUtil.modularJar; @@ -73,6 +77,8 @@ public void cannot_access_non_opened_module_with_reflection_member_accessor() th @Test public void cannot_read_unopened_private_field_but_exception_includes_cause() throws Exception { + Assume.assumeThat(Plugins.getMemberAccessor(), not(instanceOf(ModuleMemberAccessor.class))); + Path jar = modularJar(true, true, false, true); ModuleLayer layer = layer(jar, true, true); @@ -91,4 +97,21 @@ public void cannot_read_unopened_private_field_but_exception_includes_cause() th } } + @Test + public void can_read_unopened_private_field_but_exception_includes_cause() throws Exception { + Assume.assumeThat(Plugins.getMemberAccessor(), instanceOf(ModuleMemberAccessor.class)); + + Path jar = modularJar(true, true, false, true); + ModuleLayer layer = layer(jar, true, true); + + ClassLoader loader = layer.findLoader("mockito.test"); + Class type = loader.loadClass("sample.MyCallable"); + + @SuppressWarnings("unchecked") + Callable testInstance = (Callable) type.getDeclaredConstructor().newInstance(); + + Mockito.mockitoSession() + .initMocks(testInstance) + .startMocking(); + } } diff --git a/subprojects/module-test/src/test/java/org/mockito/moduletest/ReplicatingClassLoader.java b/subprojects/module-test/src/test/java/org/mockito/moduletest/ReplicatingClassLoader.java index c9cf0a32d2..144ff98767 100644 --- a/subprojects/module-test/src/test/java/org/mockito/moduletest/ReplicatingClassLoader.java +++ b/subprojects/module-test/src/test/java/org/mockito/moduletest/ReplicatingClassLoader.java @@ -53,4 +53,9 @@ public Class loadClass(String name) throws ClassNotFoundException { protected URL findResource(String moduleName, String name) { return Mockito.class.getResource("/" + name); } + + @Override + public InputStream getResourceAsStream(String name) { + return Mockito.class.getResourceAsStream("/" + name); + } } diff --git a/subprojects/osgi-test/osgi-test.gradle b/subprojects/osgi-test/osgi-test.gradle index 3689333d1e..b1d985b1f6 100644 --- a/subprojects/osgi-test/osgi-test.gradle +++ b/subprojects/osgi-test/osgi-test.gradle @@ -8,6 +8,7 @@ apply from: "osgi-test-bundles.gradle" description = "Test suite for OSGi framework with Mockito" dependencies { + testImplementation project.rootProject testImplementation libraries.junit4 testImplementation libraries.osgi diff --git a/subprojects/osgi-test/src/test/java/org/mockito/osgitest/OsgiTest.java b/subprojects/osgi-test/src/test/java/org/mockito/osgitest/OsgiTest.java index 296e6cfd7f..00d12234bb 100644 --- a/subprojects/osgi-test/src/test/java/org/mockito/osgitest/OsgiTest.java +++ b/subprojects/osgi-test/src/test/java/org/mockito/osgitest/OsgiTest.java @@ -13,6 +13,8 @@ import org.osgi.framework.Constants; import org.osgi.framework.launch.Framework; import org.osgi.framework.launch.FrameworkFactory; +import org.mockito.internal.configuration.plugins.Plugins; +import org.mockito.plugins.InlineMockMaker; import java.io.File; import java.io.IOException; @@ -51,6 +53,10 @@ private static Class[] setUpClasses() throws Exception { Map configuration = new HashMap<>(); configuration.put(Constants.FRAMEWORK_STORAGE, frameworkStorage.toString()); configuration.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, String.join(",", EXTRA_SYSTEMPACKAGES)); + // When inline mock macker is used, the new 'mockitoboot.jar' is created on the fly + // and added to boot classloader (by instrumentation). As such, the following + // packages have to be explicitly delegated to boot classloader. + configuration.put(Constants.FRAMEWORK_BOOTDELEGATION, "org.mockito.internal.creation.bytebuddy.inject"); framework = frameworkFactory.newFramework(configuration); framework.init(); BundleContext bundleContext = framework.getBundleContext(); @@ -79,11 +85,21 @@ private static Class[] setUpClasses() throws Exception { } private static Class[] getTestClasses() throws Exception { - return new Class[] { - loadTestClass("SimpleMockTest"), - loadTestClass("MockNonPublicClassFailsTest"), - loadTestClass("MockClassInOtherBundleTest") - }; + // The tests could not use 'Plugins' since 'org.mockito.internal' package is not exported. + // Making the decision which tests to run depending on mock maker instance. + if (Plugins.getMockMaker() instanceof InlineMockMaker) { + return new Class[] { + loadTestClass("SimpleMockTest"), + loadTestClass("MockNonPublicClassTest"), + loadTestClass("MockClassInOtherBundleTest") + }; + } else { + return new Class[] { + loadTestClass("SimpleMockTest"), + loadTestClass("MockNonPublicClassFailsTest"), + loadTestClass("MockClassInOtherBundleTest") + }; + } } @AfterClass diff --git a/subprojects/osgi-test/src/testBundle/java/org/mockito/osgitest/testbundle/MockNonPublicClassTest.java b/subprojects/osgi-test/src/testBundle/java/org/mockito/osgitest/testbundle/MockNonPublicClassTest.java new file mode 100644 index 0000000000..fedd45dab1 --- /dev/null +++ b/subprojects/osgi-test/src/testBundle/java/org/mockito/osgitest/testbundle/MockNonPublicClassTest.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.osgitest.testbundle; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; + +public class MockNonPublicClassTest { + + static class NonPublicClass {} + + @Test + public void test_non_public_class() { + NonPublicClass nonPublicClass = mock(NonPublicClass.class); + } +} diff --git a/subprojects/programmatic-test/programmatic-test.gradle b/subprojects/programmatic-test/programmatic-test.gradle index 365dad93e3..39db6c6442 100644 --- a/subprojects/programmatic-test/programmatic-test.gradle +++ b/subprojects/programmatic-test/programmatic-test.gradle @@ -16,3 +16,7 @@ tasks.javadoc.enabled = false sourceCompatibility = 11 targetCompatibility = 11 + +test { + forkEvery = 1 +} diff --git a/src/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java b/subprojects/programmatic-test/src/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java similarity index 85% rename from src/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java rename to subprojects/programmatic-test/src/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java index ad5f2a50ae..2d495bed00 100644 --- a/src/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java +++ b/subprojects/programmatic-test/src/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java @@ -6,19 +6,25 @@ import static org.junit.Assert.assertEquals; +import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockMakers; import org.mockito.Mockito; -import org.mockitoutil.TestBase; +import org.mockito.MockitoAnnotations; -public class ProgrammaticMockMakerAnnotationTest extends TestBase { +public class ProgrammaticMockMakerAnnotationTest { @Mock(mockMaker = MockMakers.INLINE) ClassWithFinalMethod inlineMock; @Mock(mockMaker = MockMakers.SUBCLASS) ClassWithFinalMethod subclassMock; + @Before + public void init() { + MockitoAnnotations.openMocks(this); + } + @Test public void test_mock_uses_given_mock_maker() { Mockito.when(inlineMock.finalMethodCallingNonFinal()).thenReturn("MOCKED"); diff --git a/subprojects/proxy/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor b/subprojects/proxy/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor new file mode 100644 index 0000000000..71111e3378 --- /dev/null +++ b/subprojects/proxy/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor @@ -0,0 +1 @@ +member-accessor-reflection From 9bec8e3a1a0f57e4baa9b64825d67641e9eb2d5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jan 2023 22:48:56 +0100 Subject: [PATCH 126/160] Bump versions.errorprone from 2.17.0 to 2.18.0 (#2857) Bumps `versions.errorprone` from 2.17.0 to 2.18.0. Updates `error_prone_core` from 2.17.0 to 2.18.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.17.0...v2.18.0) Updates `error_prone_test_helpers` from 2.17.0 to 2.18.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.17.0...v2.18.0) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.google.errorprone:error_prone_test_helpers 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> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index e9e88327ec..145093dcc7 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -6,7 +6,7 @@ def versions = [:] versions.bytebuddy = '1.12.21' versions.junitJupiter = '5.9.1' -versions.errorprone = '2.17.0' +versions.errorprone = '2.18.0' libraries.junit4 = 'junit:junit:4.13.2' libraries.junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}" From 3d40cd51d3982e33f7c2a2670c65d28233ceb66e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 19:58:54 +0100 Subject: [PATCH 127/160] Bump junit-platform-launcher from 1.9.1 to 1.9.2 (#2859) Bumps [junit-platform-launcher](https://github.com/junit-team/junit5) from 1.9.1 to 1.9.2. - [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-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 145093dcc7..5080a405d8 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -10,7 +10,7 @@ versions.errorprone = '2.18.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.9.1' +libraries.junitPlatformLauncher = 'org.junit.platform:junit-platform-launcher:1.9.2' 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.24.1' From 2418419a1915bd234332eac2b4d5de85622d4699 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 19:59:04 +0100 Subject: [PATCH 128/160] Bump versions.junitJupiter from 5.9.1 to 5.9.2 (#2858) Bumps `versions.junitJupiter` from 5.9.1 to 5.9.2. Updates `junit-jupiter-api` from 5.9.1 to 5.9.2 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.1...r5.9.2) Updates `junit-jupiter-engine` from 5.9.1 to 5.9.2 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.1...r5.9.2) Updates `junit-vintage-engine` from 5.9.1 to 5.9.2 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.1...r5.9.2) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.vintage:junit-vintage-engine 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 5080a405d8..ad6b493838 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -5,7 +5,7 @@ ext { def versions = [:] versions.bytebuddy = '1.12.21' -versions.junitJupiter = '5.9.1' +versions.junitJupiter = '5.9.2' versions.errorprone = '2.18.0' libraries.junit4 = 'junit:junit:4.13.2' From adf528d173f8b763fcd4fedab245ed485b465211 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Jan 2023 19:32:22 +0100 Subject: [PATCH 129/160] Bump versions.bytebuddy from 1.12.21 to 1.12.22 (#2864) Bumps `versions.bytebuddy` from 1.12.21 to 1.12.22. Updates `byte-buddy` from 1.12.21 to 1.12.22 - [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.21...byte-buddy-1.12.22) Updates `byte-buddy-agent` from 1.12.21 to 1.12.22 - [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.21...byte-buddy-1.12.22) Updates `byte-buddy-android` from 1.12.21 to 1.12.22 - [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.21...byte-buddy-1.12.22) --- 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 ad6b493838..ce6c0fdbdd 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.12.21' +versions.bytebuddy = '1.12.22' versions.junitJupiter = '5.9.2' versions.errorprone = '2.18.0' From acddbbc00d1754d3c4da893f6322d03d782fb1a8 Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sat, 14 Jan 2023 23:17:48 +0100 Subject: [PATCH 130/160] Annotate `Mockito#{mock,spy}(T... reified)` with `@SafeVarargs` (#2866) This avoids "Unchecked generics array creation for varargs parameter" warnings at the call site. --- src/main/java/org/mockito/Mockito.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 31eed046cf..ff543e74b5 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -1932,6 +1932,7 @@ public class Mockito extends ArgumentMatchers { * @return mock object * @since 4.9.0 */ + @SafeVarargs public static T mock(T... reified) { if (reified.length > 0) { throw new IllegalArgumentException( @@ -2161,6 +2162,7 @@ public static T spy(Class classToSpy) { * @return spy object * @since 4.9.0 */ + @SafeVarargs public static T spy(T... reified) { if (reified.length > 0) { throw new IllegalArgumentException( From 0aba0d5b031b4282c9b6686add2b443bda761817 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 14 Jan 2023 23:46:48 +0100 Subject: [PATCH 131/160] Update current version in README (#2867) --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f1f4b11530..d523402135 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,11 @@ Most popular mocking framework for Java [![Javadoc](https://www.javadoc.io/badge/org.mockito/mockito-core.svg)](https://www.javadoc.io/doc/org.mockito/mockito-core) -## Current version is 4.x -Still on Mockito 1.x? See [what's new](https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2) in Mockito 2! [Mockito 3](https://github.com/mockito/mockito/releases/tag/v3.0.0) does not introduce any breaking API changes, but now requires Java 8 over Java 6 for Mockito 2. +## Current version is 5.x +Still on Mockito 1.x? See [what's new](https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2) in Mockito 2! +[Mockito 3](https://github.com/mockito/mockito/releases/tag/v3.0.0) does not introduce any breaking API changes, but now requires Java 8 over Java 6 for Mockito 2. [Mockito 4](https://github.com/mockito/mockito/releases/tag/v4.0.0) removes deprecated API. +[Mockito 5](https://github.com/mockito/mockito/releases/tag/v5.0.0) switches the default mockmaker to mockito-inline, and now requires Java 11. ## Mockito for enterprise From 484de454af8a12da8e2944ab889a262caac73ae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp?= Date: Sun, 15 Jan 2023 12:59:27 +0000 Subject: [PATCH 132/160] Update release badge to 5.x (#2869) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d523402135..0a34afc47b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Most popular mocking framework for Java [![Coverage Status](https://img.shields.io/codecov/c/github/mockito/mockito.svg)](https://codecov.io/github/mockito/mockito) [![MIT License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/mockito/mockito/blob/main/LICENSE) -[![Release Notes](https://img.shields.io/badge/release%20notes-3.x-yellow.svg)](https://github.com/mockito/mockito/releases/) +[![Release Notes](https://img.shields.io/badge/release%20notes-5.x-yellow.svg)](https://github.com/mockito/mockito/releases/) [![Maven Central](https://img.shields.io/maven-central/v/org.mockito/mockito-core.svg)](https://search.maven.org/artifact/org.mockito/mockito-core/) [![Javadoc](https://www.javadoc.io/badge/org.mockito/mockito-core.svg)](https://www.javadoc.io/doc/org.mockito/mockito-core) From f2a47c35b8b16a8b1c560b9d93bc3cce62a3e310 Mon Sep 17 00:00:00 2001 From: Ashley <73482956+ascopes@users.noreply.github.com> Date: Sun, 15 Jan 2023 17:25:39 +0000 Subject: [PATCH 133/160] Improve examples for InOrder (#2843) Include some context in InOrder examples and add an example that uses a static mock as well. --- src/main/java/org/mockito/InOrder.java | 30 +++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/InOrder.java b/src/main/java/org/mockito/InOrder.java index 3cb047a4c4..ee71ae197c 100644 --- a/src/main/java/org/mockito/InOrder.java +++ b/src/main/java/org/mockito/InOrder.java @@ -12,13 +12,41 @@ * Allows verification in order. E.g: * *
    
    + * // Given
    + * First firstMock = mock(First.class);
    + * Second secondMock = mock(Second.class);
      * InOrder inOrder = inOrder(firstMock, secondMock);
      *
    + * // When
    + * firstMock.add("was called first");
    + * secondMock.add("was called second");
    + *
    + * // Then
      * inOrder.verify(firstMock).add("was called first");
      * inOrder.verify(secondMock).add("was called second");
    + * inOrder.verifyNoMoreInteractions();
    + * 
    + * + * Static mocks can be verified alongside non-static mocks. E.g: + * + *
    
    + * // Given
    + * First firstMock = mock(First.class);
    + * MockedStatic staticSecondMock = mockStatic(StaticSecond.class);
    + * InOrder inOrder = inOrder(firstMock, StaticSecond.class);
    + *
    + * // When
    + * firstMock.add("was called first");
    + * StaticSecond.doSomething("foobar");
    + *
    + * // Then
    + * inOrder.verify(firstMock).add("was called first");
    + * inOrder.verify(staticSecondMock, () -> StaticSecond.doSomething("foobar"));
    + * inOrder.verifyNoMoreInteractions();
      * 
    * - * As of Mockito 1.8.4 you can verifyNoMoreInteractions() in order-sensitive way. Read more: {@link InOrder#verifyNoMoreInteractions()} + * As of Mockito 1.8.4 you can verifyNoMoreInteractions() in order-sensitive way. Read more: + * {@link InOrder#verifyNoMoreInteractions()}. *

    * * See examples in javadoc for {@link Mockito} class From 23e344ec7b7c11743331ace30f26c21da12767ba Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sun, 15 Jan 2023 18:31:27 +0100 Subject: [PATCH 134/160] Remove broken link from `CONTRIBUTING.md` (#2870) Fixes #2868 --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 8609981c54..5a49c782b4 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -54,7 +54,7 @@ Things we pay attention in a PR : * In the code, always test your feature / change, in unit tests and in our `acceptance test suite` located in `org.mockitousage`. Older tests will be migrated when a test is modified. * New test methods should follow a snake case convention (`ensure_that_stuff_is_doing_that`), this allows the test name to be fully expressive on intent while still readable. * When reporting errors to the users, if it's a user report report it gently and explain how a user should deal with it, see the `Reporter` class. However not all errors should go there, some unlikely technical errors don't need to be in the `Reporter` class. -* Documentation !!! Always document with love the public API. Internals could use some love too. In all cases the code should _auto-document_ itself like any [well designed API](rebased and squashed if necessary, so that each commit clearly changes one things and there are no extraneous fix-ups). +* Documentation !!! Always document with love the public API. Internals could use some love too. In all cases the code should _auto-document_ itself like any well-designed API. * We use (4) spaces instead of tabs. Make sure line ending is Unix style (LF). More on line ending on the [Github help](https://help.github.com/articles/dealing-with-line-endings/). From f4d7c346b1783c4a14aa8905ec96aa16f9f2fdd5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jan 2023 19:32:17 +0100 Subject: [PATCH 135/160] Bump com.diffplug.spotless from 6.12.1 to 6.13.0 (#2871) Bumps com.diffplug.spotless from 6.12.1 to 6.13.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 9698007a75..f05c97fa31 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.12.1' + id 'com.diffplug.spotless' version '6.13.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.44.0' id 'biz.aQute.bnd.builder' version '6.4.0' From 1418769a9f89060f7035a48094d0bb2eba1e0ec5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Jan 2023 23:23:20 +0100 Subject: [PATCH 136/160] Bump assertj-core from 3.24.1 to 3.24.2 (#2875) Bumps assertj-core from 3.24.1 to 3.24.2. --- updated-dependencies: - dependency-name: org.assertj:assertj-core 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 ce6c0fdbdd..45f0353e9b 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -13,7 +13,7 @@ libraries.junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${versions.juni libraries.junitPlatformLauncher = 'org.junit.platform:junit-platform-launcher:1.9.2' 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.24.1' +libraries.assertj = 'org.assertj:assertj-core:3.24.2' libraries.hamcrest = 'org.hamcrest:hamcrest-core:2.2' libraries.opentest4j = 'org.opentest4j:opentest4j:1.2.0' From 96452fa7fb1a297323dfd998953489deeca64e28 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Thu, 19 Jan 2023 03:41:28 -0500 Subject: [PATCH 137/160] Make sure the tests use mock maker with intended member accessor (#2872) Fixes #2855 Signed-off-by: Andriy Redko --- .github/workflows/ci.yml | 18 ++++-- gradle/mockito-core/testing.gradle | 43 ++++++++++---- .../plugins/DefaultMockitoPlugins.java | 4 ++ .../configuration/plugins/PluginRegistry.java | 4 +- .../junit/DefaultStubbingLookupListener.java | 15 +++-- src/test/java/org/mockito/MockitoEnvTest.java | 59 +++++++++++++++++++ .../spies/PartialMockingWithSpiesTest.java | 33 +++++++++++ .../plugins/switcher/MyPluginSwitch.java | 4 ++ 8 files changed, 153 insertions(+), 27 deletions(-) create mode 100644 src/test/java/org/mockito/MockitoEnvTest.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58cb41b62c..0c57e59086 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,12 @@ jobs: strategy: matrix: java: [11, 17] - mock-maker: ['mock-maker-default', 'mock-maker-inline', 'mock-maker-subclass'] + entry: + - { mock-maker: 'mock-maker-default', member-accessor: 'member-accessor-default' } + - { mock-maker: 'mock-maker-inline', member-accessor: 'member-accessor-module' } + - { mock-maker: 'mock-maker-subclass', member-accessor: 'member-accessor-module' } + - { mock-maker: 'mock-maker-subclass', member-accessor: 'member-accessor-reflection' } + - { mock-maker: 'mock-maker-inline', member-accessor: 'member-accessor-reflection' } # All build steps # SINGLE-MATRIX-JOB means that the step does not need to be executed on every job in the matrix @@ -48,21 +53,22 @@ jobs: java-version: ${{ matrix.java }} - name: 3. Validate Gradle wrapper - if: matrix.java == 11 && matrix.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB + if: matrix.java == 11 && matrix.entry.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB uses: gradle/wrapper-validation-action@v1.0.5 # https://github.com/gradle/wrapper-validation-action - name: 4. Build and check reproducibility of artifacts (single job only) - if: matrix.java == 11 && matrix.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB + if: matrix.java == 11 && matrix.entry.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB run: ./check_reproducibility.sh - name: 5. Spotless check (single job only). Run './gradlew spotlessApply' locally if this job fails. - if: matrix.java == 11 && matrix.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB + if: matrix.java == 11 && matrix.entry.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB run: ./gradlew spotlessCheck - - name: 6. Build on Java ${{ matrix.java }} with ${{ matrix.mock-maker }} + - name: 6. Build on Java ${{ matrix.java }} with ${{ matrix.entry.mock-maker }} and ${{ matrix.entry.member-accessor }} run: ./gradlew build idea --scan env: - MOCK_MAKER: ${{ matrix.mock-maker }} + MOCK_MAKER: ${{ matrix.entry.mock-maker }} + MEMBER_ACCESSOR: ${{ matrix.entry.member-accessor }} - name: 7. Upload coverage report run: | diff --git a/gradle/mockito-core/testing.gradle b/gradle/mockito-core/testing.gradle index a7c14c1f62..79e2996b6c 100644 --- a/gradle/mockito-core/testing.gradle +++ b/gradle/mockito-core/testing.gradle @@ -6,24 +6,43 @@ if (java11 != null) { } } -task(createTestResources).doLast { - def mockMakerFile = new File("$projectDir/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker") - if (System.env.MOCK_MAKER != null) { - logger.info("Using MockMaker ${System.env.MOCK_MAKER}") - mockMakerFile.parentFile.mkdirs() - mockMakerFile.createNewFile() - mockMakerFile.write(System.env.MOCK_MAKER) - } else { - logger.info("Using default MockMaker") +task(createTestResources) { + doLast { + // Configure MockMaker from environment (if specified), otherwise use default + def mockMakerFile = new File("$sourceSets.test.output.resourcesDir/mockito-extensions/org.mockito.plugins.MockMaker") + if (System.env.MOCK_MAKER != null && !System.env.MOCK_MAKER.endsWith("default")) { + logger.info("Using MockMaker ${System.env.MOCK_MAKER}") + mockMakerFile.parentFile.mkdirs() + mockMakerFile.createNewFile() + mockMakerFile.write(System.env.MOCK_MAKER) + } else { + logger.info("Using default MockMaker") + } + + // Configure MemberAccessor from environment (if specified), otherwise use default + def memberAccessorFile = new File("$sourceSets.test.output.resourcesDir/mockito-extensions/org.mockito.plugins.MemberAccessor") + if (System.env.MEMBER_ACCESSOR != null && !System.env.MEMBER_ACCESSOR.endsWith("default")) { + logger.info("Using MemberAccessor ${System.env.MEMBER_ACCESSOR}") + memberAccessorFile.parentFile.mkdirs() + memberAccessorFile.createNewFile() + memberAccessorFile.write(System.env.MEMBER_ACCESSOR) + } else { + logger.info("Using default MemberAccessor") + } } } task(removeTestResources).doLast { - def mockMakerFile = new File("$projectDir/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker") + def mockMakerFile = new File("$sourceSets.test.output.resourcesDir/mockito-extensions/org.mockito.plugins.MockMaker") if (mockMakerFile.exists()) { mockMakerFile.delete() } + + def memberAccessorFile = new File("$sourceSets.test.output.resourcesDir/mockito-extensions/org.mockito.plugins.MemberAccessor") + if (memberAccessorFile.exists()) { + memberAccessorFile.delete() + } } -compileTestJava.dependsOn createTestResources -compileTestJava.finalizedBy removeTestResources +processTestResources.finalizedBy(createTestResources) +test.finalizedBy(removeTestResources) diff --git a/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java b/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java index 780742c64a..365c350e93 100644 --- a/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java +++ b/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java @@ -29,6 +29,7 @@ public class DefaultMockitoPlugins implements MockitoPlugins { public static final Set MOCK_MAKER_ALIASES = new HashSet<>(); static final String MODULE_ALIAS = "member-accessor-module"; static final String REFLECTION_ALIAS = "member-accessor-reflection"; + public static final Set MEMBER_ACCESSOR_ALIASES = new HashSet<>(); static { // Keep the mapping: plugin interface name -> plugin implementation class name @@ -66,6 +67,9 @@ public class DefaultMockitoPlugins implements MockitoPlugins { MOCK_MAKER_ALIASES.add(INLINE_ALIAS); MOCK_MAKER_ALIASES.add(PROXY_ALIAS); MOCK_MAKER_ALIASES.add(SUBCLASS_ALIAS); + + MEMBER_ACCESSOR_ALIASES.add(MODULE_ALIAS); + MEMBER_ACCESSOR_ALIASES.add(REFLECTION_ALIAS); } @Override diff --git a/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java b/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java index 6abaedbaef..72f5d8e7d5 100644 --- a/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java +++ b/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java @@ -27,7 +27,9 @@ class PluginRegistry { .loadPlugin(MockMaker.class); private final MemberAccessor memberAccessor = - new PluginLoader(pluginSwitch, DefaultMockitoPlugins.MODULE_ALIAS) + new PluginLoader( + pluginSwitch, + DefaultMockitoPlugins.MEMBER_ACCESSOR_ALIASES.toArray(new String[0])) .loadPlugin(MemberAccessor.class); private final StackTraceCleanerProvider stackTraceCleanerProvider = diff --git a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java index c71386346e..48ba775100 100644 --- a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java +++ b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java @@ -10,6 +10,7 @@ import java.util.Collection; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import org.mockito.internal.exceptions.Reporter; import org.mockito.internal.stubbing.UnusedStubbingReporting; @@ -68,17 +69,15 @@ private static List potentialArgMismatches( List matchingStubbings = new LinkedList<>(); for (Stubbing s : stubbings) { if (UnusedStubbingReporting.shouldBeReported(s) - && s.getInvocation() - .getMethod() - .getName() - .equals(invocation.getMethod().getName()) + && Objects.equals( + s.getInvocation().getMethod().getName(), + invocation.getMethod().getName()) // If stubbing and invocation are in the same source file we assume they are in // the test code, // and we don't flag it as mismatch: - && !s.getInvocation() - .getLocation() - .getSourceFile() - .equals(invocation.getLocation().getSourceFile())) { + && !Objects.equals( + s.getInvocation().getLocation().getSourceFile(), + invocation.getLocation().getSourceFile())) { matchingStubbings.add(s.getInvocation()); } } diff --git a/src/test/java/org/mockito/MockitoEnvTest.java b/src/test/java/org/mockito/MockitoEnvTest.java new file mode 100644 index 0000000000..1a162e9390 --- /dev/null +++ b/src/test/java/org/mockito/MockitoEnvTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.endsWith; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.nullValue; + +import org.junit.Assume; +import org.junit.Test; +import org.mockito.internal.configuration.plugins.DefaultMockitoPlugins; +import org.mockito.internal.configuration.plugins.Plugins; +import org.mockito.plugins.MemberAccessor; +import org.mockito.plugins.MockMaker; + +public class MockitoEnvTest { + @Test + public void uses_default_mock_maker_from_env() { + final String mockMaker = System.getenv("MOCK_MAKER"); + Assume.assumeThat(mockMaker, not(nullValue())); + Assume.assumeThat(mockMaker, endsWith("default")); + + assertThat(DefaultMockitoPlugins.getDefaultPluginClass(MockMaker.class.getName())) + .isEqualTo(Plugins.getMockMaker().getClass().getName()); + } + + @Test + public void uses_mock_maker_from_env() { + final String mockMaker = System.getenv("MOCK_MAKER"); + Assume.assumeThat(mockMaker, not(nullValue())); + Assume.assumeThat(mockMaker, not(endsWith("default"))); + + assertThat(DefaultMockitoPlugins.getDefaultPluginClass(mockMaker)) + .isEqualTo(Plugins.getMockMaker().getClass().getName()); + } + + @Test + public void uses_default_member_accessor_from_env() { + final String memberAccessor = System.getenv("MEMBER_ACCESSOR"); + Assume.assumeThat(memberAccessor, not(nullValue())); + Assume.assumeThat(memberAccessor, endsWith("default")); + + assertThat(DefaultMockitoPlugins.getDefaultPluginClass(MemberAccessor.class.getName())) + .isEqualTo(Plugins.getMemberAccessor().getClass().getName()); + } + + @Test + public void uses_member_accessor_from_env() { + final String memberAccessor = System.getenv("MEMBER_ACCESSOR"); + Assume.assumeThat(memberAccessor, not(nullValue())); + Assume.assumeThat(memberAccessor, not(endsWith("default"))); + + assertThat(DefaultMockitoPlugins.getDefaultPluginClass(memberAccessor)) + .isEqualTo(Plugins.getMemberAccessor().getClass().getName()); + } +} diff --git a/src/test/java/org/mockitousage/spies/PartialMockingWithSpiesTest.java b/src/test/java/org/mockitousage/spies/PartialMockingWithSpiesTest.java index bf616f8d9f..4efcb2b275 100644 --- a/src/test/java/org/mockitousage/spies/PartialMockingWithSpiesTest.java +++ b/src/test/java/org/mockitousage/spies/PartialMockingWithSpiesTest.java @@ -4,6 +4,8 @@ */ package org.mockitousage.spies; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Mockito.doThrow; @@ -13,8 +15,12 @@ import static org.mockitoutil.Conditions.methodsInStackTrace; import org.assertj.core.api.Assertions; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; +import org.mockito.internal.configuration.plugins.Plugins; +import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker; +import org.mockito.internal.util.reflection.ReflectionMemberAccessor; import org.mockitoutil.TestBase; @SuppressWarnings("unchecked") @@ -104,6 +110,9 @@ public void shouldAllowStubbingWithThrowablesMethodsThatDelegateToOtherMethods() @Test public void shouldStackTraceGetFilteredOnUserExceptions() { + Assume.assumeThat( + Plugins.getMemberAccessor(), not(instanceOf(ReflectionMemberAccessor.class))); + try { // when spy.getNameButDelegateToMethodThatThrows(); @@ -119,6 +128,30 @@ public void shouldStackTraceGetFilteredOnUserExceptions() { } } + @Test + public void shouldStackTraceGetFilteredOnUserExceptionsReflection() { + Assume.assumeThat(Plugins.getMockMaker(), instanceOf(InlineByteBuddyMockMaker.class)); + Assume.assumeThat(Plugins.getMemberAccessor(), instanceOf(ReflectionMemberAccessor.class)); + + try { + // when + spy.getNameButDelegateToMethodThatThrows(); + fail(); + } catch (Throwable t) { + // then + Assertions.assertThat(t) + .has( + methodsInStackTrace( + "throwSomeException", + "invoke0", + "invoke", + "invoke", + "invoke", + "getNameButDelegateToMethodThatThrows", + "shouldStackTraceGetFilteredOnUserExceptionsReflection")); + } + } + // @Test //manual verification public void verifyTheStackTrace() { spy.getNameButDelegateToMethodThatThrows(); diff --git a/subprojects/extTest/src/test/java/org/mockitousage/plugins/switcher/MyPluginSwitch.java b/subprojects/extTest/src/test/java/org/mockitousage/plugins/switcher/MyPluginSwitch.java index eb4423f361..cf60ee63ec 100644 --- a/subprojects/extTest/src/test/java/org/mockitousage/plugins/switcher/MyPluginSwitch.java +++ b/subprojects/extTest/src/test/java/org/mockitousage/plugins/switcher/MyPluginSwitch.java @@ -14,6 +14,10 @@ public class MyPluginSwitch implements PluginSwitch { static List invokedFor = new LinkedList(); public boolean isEnabled(String pluginClassName) { + if (!pluginClassName.startsWith("org.mockitousage")) { + return false; + } + invokedFor.add(pluginClassName); return true; } From 91223f830552ce3bb5daf7095f4f30767e072b96 Mon Sep 17 00:00:00 2001 From: Ashley <73482956+ascopes@users.noreply.github.com> Date: Sat, 21 Jan 2023 21:08:22 +0000 Subject: [PATCH 138/160] Feat: reified mock overloads (#2882) Added reified mock overload methods to match the methods that consume an explicit Class object. The following methods have been added: - Mockito.mock(String, T...) - Sets the mock name. - Mockito.mock(Answer, T...) - Sets the default answer. - Mockito.mock(WithSettings, T...) - Provides custom settings. I have also added a case where passing null varargs in would result in a NullPointerException previously. Now, it will tell the user that they should not be providing these varargs at all. These overloads should not conflict with any existing usages of this API in the intended way, as the assumption is that a user probably isn't going to want to be mocking Mockito internals, Strings, or WithSettings objects anyway. If they do need to achieve this, then this is still accessible via the existing Mockito.mock(Class) methods anyway. --- src/main/java/org/mockito/Mockito.java | 59 +++++++++++++++++++--- src/test/java/org/mockito/MockitoTest.java | 44 ++++++++++++++++ 2 files changed, 97 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index ff543e74b5..db2ca1096b 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -1924,21 +1924,68 @@ public class Mockito extends ArgumentMatchers { public static final Answer RETURNS_SELF = Answers.RETURNS_SELF; /** - * Creates mock object of requested class or interface. + * Creates a mock object of the requested class or interface. *

    - * See examples in javadoc for {@link Mockito} class + * See examples in javadoc for the {@link Mockito} class. * - * @param reified don't pass any values to it. It's a trick to detect the class/interface you want to mock. - * @return mock object + * @param reified don't pass any values to it. It's a trick to detect the class/interface you + * want to mock. + * @return the mock object. * @since 4.9.0 */ @SafeVarargs public static T mock(T... reified) { - if (reified.length > 0) { + return mock(withSettings(), reified); + } + + /** + * Creates a mock object of the requested class or interface with the given default answer. + *

    + * See examples in javadoc for the {@link Mockito} class. + * + * @param defaultAnswer the default answer to use. + * @param reified don't pass any values to it. It's a trick to detect the class/interface you + * want to mock. + * @return the mock object. + */ + @SafeVarargs + public static T mock(@SuppressWarnings("rawtypes") Answer defaultAnswer, T... reified) { + return mock(withSettings().defaultAnswer(defaultAnswer), reified); + } + + /** + * Creates a mock object of the requested class or interface with the given name. + *

    + * See examples in javadoc for the {@link Mockito} class. + * + * @param name the mock name to use. + * @param reified don't pass any values to it. It's a trick to detect the class/interface you + * want to mock. + * @return the mock object. + */ + @SafeVarargs + public static T mock(String name, T... reified) { + return mock(withSettings().name(name).defaultAnswer(RETURNS_DEFAULTS), reified); + } + + /** + * Creates a mock object of the requested class or interface with the given settings. + *

    + * See examples in javadoc for the {@link Mockito} class. + * + * @param settings the mock settings to use. + * @param reified don't pass any values to it. It's a trick to detect the class/interface you + * want to mock. + * @return the mock object. + */ + @SafeVarargs + public static T mock(MockSettings settings, T... reified) { + if (reified == null || reified.length > 0) { throw new IllegalArgumentException( "Please don't pass any values here. Java will detect class automagically."); } - return mock(getClassOf(reified), withSettings()); + + return mock(getClassOf(reified), settings); } /** diff --git a/src/test/java/org/mockito/MockitoTest.java b/src/test/java/org/mockito/MockitoTest.java index 1ebbb89b80..1671280d0c 100644 --- a/src/test/java/org/mockito/MockitoTest.java +++ b/src/test/java/org/mockito/MockitoTest.java @@ -21,6 +21,7 @@ import org.mockito.exceptions.misusing.NullInsteadOfMockException; import org.mockito.internal.configuration.plugins.Plugins; import org.mockito.internal.creation.MockSettingsImpl; +import org.mockito.listeners.InvocationListener; import org.mockito.plugins.InlineMockMaker; @SuppressWarnings("unchecked") @@ -166,6 +167,49 @@ public void automaticallyDetectsClassToMock() { assertThat(mock.size()).isEqualTo(42); } + @Test + @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"}) + public void newMockMethod_shouldNotBeCalledWithNullParameters() { + assertThatThrownBy( + () -> { + Mockito.mock((Object[]) null); + }) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("Please don't pass any values here"); + } + + @Test + public void reifiedMockMethodWithNameSetsTheExpectedName() { + List mock = Mockito.mock("My super cool new mock"); + assertThat(mock).hasToString("My super cool new mock"); + } + + @Test + public void reifiedMockMethodWithDefaultAnswerSetsTheDefaultAnswer() { + abstract class Something { + abstract Something somethingElse(); + } + + Something something = Mockito.mock(Answers.RETURNS_SELF); + + assertThat(something.somethingElse()).isSameAs(something); + } + + @Test + public void reifiedMockMethodWithSettingsAppliesTheSettings() { + + InvocationListener invocationListener = Mockito.mock(InvocationListener.class); + + List mock = + Mockito.mock( + Mockito.withSettings() + .name("my name here") + .invocationListeners(invocationListener)); + + assertThat(mock).hasToString("my name here"); + Mockito.verify(invocationListener).reportInvocation(ArgumentMatchers.any()); + } + @Test @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"}) public void newMockMethod_shouldNotBeCalledWithParameters() { From 4775c67fe3569bbb735877e101b5f8a7889788f9 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Tue, 24 Jan 2023 01:05:42 -0500 Subject: [PATCH 139/160] Clean up JDK-8 related code (#2883) Fixes #2879 Signed-off-by: Andriy Redko --- build.gradle | 2 +- .../InlineDelegateByteBuddyMockMaker.java | 4 +- .../bytebuddy/SubclassByteBuddyMockMaker.java | 4 +- .../internal/debugging/Java8LocationImpl.java | 59 ---- .../debugging/Java9PlusLocationImpl.java | 304 ------------------ .../internal/debugging/LocationFactory.java | 32 +- .../defaultanswers/ReturnsEmptyValues.java | 52 +-- .../mockito/internal/util/JavaEightUtil.java | 228 ------------- .../org/mockito/internal/util/Platform.java | 31 -- .../mockito/internal/util/PlatformTest.java | 32 +- subprojects/androidTest/androidTest.gradle | 2 +- .../kotlinReleaseCoroutinesTest.gradle | 12 + subprojects/kotlinTest/kotlinTest.gradle | 12 + 13 files changed, 61 insertions(+), 713 deletions(-) delete mode 100644 src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java delete mode 100644 src/main/java/org/mockito/internal/debugging/Java9PlusLocationImpl.java delete mode 100644 src/main/java/org/mockito/internal/util/JavaEightUtil.java diff --git a/build.gradle b/build.gradle index f05c97fa31..92c56c1ed3 100644 --- a/build.gradle +++ b/build.gradle @@ -99,7 +99,7 @@ dependencies { testUtil sourceSets.test.output signature 'org.codehaus.mojo.signature:java18:1.0@signature' - signature 'net.sf.androidscents.signature:android-api-level-24:7.0_r2@signature' + signature 'net.sf.androidscents.signature:android-api-level-26:8.0.0_r2@signature' } animalsniffer { diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java index 05512b8409..d40967f94e 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java @@ -447,9 +447,7 @@ private RuntimeException prettifyFailure( "IBM J9 VM", "Early IBM virtual machine are known to have issues with Mockito, please upgrade to an up-to-date version.\n", "Hotspot", - Platform.isJava8BelowUpdate45() - ? "Java 8 early builds have bugs that were addressed in Java 1.8.0_45, please update your JDK!\n" - : ""), + ""), Platform.describe(), "", "You are seeing this disclaimer because Mockito is configured to create inlined mocks.", diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java index e8dcce229a..6bb74322b3 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java @@ -122,9 +122,7 @@ private RuntimeException prettifyFailure( "IBM J9 VM", "Early IBM virtual machine are known to have issues with Mockito, please upgrade to an up-to-date version.\n", "Hotspot", - Platform.isJava8BelowUpdate45() - ? "Java 8 early builds have bugs that were addressed in Java 1.8.0_45, please update your JDK!\n" - : ""), + ""), Platform.describe(), "", "Underlying exception : " + generationFailed), diff --git a/src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java b/src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java deleted file mode 100644 index e8ee387c0a..0000000000 --- a/src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2007 Mockito contributors - * This program is made available under the terms of the MIT License. - */ -package org.mockito.internal.debugging; - -import java.io.Serializable; - -import org.mockito.internal.exceptions.stacktrace.StackTraceFilter; -import org.mockito.invocation.Location; - -class Java8LocationImpl implements Location, Serializable { - - private static final long serialVersionUID = -9054861157390980624L; - // Limit the amount of objects being created, as this class is heavily instantiated: - private static final StackTraceFilter stackTraceFilter = new StackTraceFilter(); - - private String stackTraceLine; - private String sourceFile; - - public Java8LocationImpl(Throwable stackTraceHolder, boolean isInline) { - this(stackTraceFilter, stackTraceHolder, isInline); - } - - private Java8LocationImpl( - StackTraceFilter stackTraceFilter, Throwable stackTraceHolder, boolean isInline) { - computeStackTraceInformation(stackTraceFilter, stackTraceHolder, isInline); - } - - @Override - public String toString() { - return stackTraceLine; - } - - /** - * Eagerly compute the stacktrace line from the stackTraceHolder. Storing the Throwable is - * memory-intensive for tests that have large stacktraces and have a lot of invocations on - * mocks. - */ - private void computeStackTraceInformation( - StackTraceFilter stackTraceFilter, Throwable stackTraceHolder, boolean isInline) { - StackTraceElement filtered = stackTraceFilter.filterFirst(stackTraceHolder, isInline); - - // there are corner cases where exception can have a null or empty stack trace - // for example, a custom exception can override getStackTrace() method - if (filtered == null) { - this.stackTraceLine = "-> at <>"; - this.sourceFile = ""; - } else { - this.stackTraceLine = "-> at " + filtered; - this.sourceFile = filtered.getFileName(); - } - } - - @Override - public String getSourceFile() { - return sourceFile; - } -} diff --git a/src/main/java/org/mockito/internal/debugging/Java9PlusLocationImpl.java b/src/main/java/org/mockito/internal/debugging/Java9PlusLocationImpl.java deleted file mode 100644 index 219835513e..0000000000 --- a/src/main/java/org/mockito/internal/debugging/Java9PlusLocationImpl.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * 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/LocationFactory.java b/src/main/java/org/mockito/internal/debugging/LocationFactory.java index daafddedaf..08e441e87a 100644 --- a/src/main/java/org/mockito/internal/debugging/LocationFactory.java +++ b/src/main/java/org/mockito/internal/debugging/LocationFactory.java @@ -7,8 +7,6 @@ import org.mockito.invocation.Location; public final class LocationFactory { - private static final Factory factory = createLocationFactory(); - private LocationFactory() {} public static Location create() { @@ -16,34 +14,6 @@ public static Location create() { } 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); - } + return new LocationImpl(inline); } } diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsEmptyValues.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsEmptyValues.java index ad8e7e2466..20e9d85afe 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsEmptyValues.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsEmptyValues.java @@ -8,6 +8,8 @@ import static org.mockito.internal.util.ObjectMethodsGuru.isToStringMethod; import java.io.Serializable; +import java.time.Duration; +import java.time.Period; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -17,12 +19,20 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; -import org.mockito.internal.util.JavaEightUtil; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + import org.mockito.internal.util.MockUtil; import org.mockito.internal.util.Primitives; import org.mockito.invocation.InvocationOnMock; @@ -129,26 +139,26 @@ Object returnValueFor(Class type) { return new TreeMap<>(); } else if (type == LinkedHashMap.class) { return new LinkedHashMap<>(); - } else if ("java.util.Optional".equals(type.getName())) { - return JavaEightUtil.emptyOptional(); - } else if ("java.util.OptionalDouble".equals(type.getName())) { - return JavaEightUtil.emptyOptionalDouble(); - } else if ("java.util.OptionalInt".equals(type.getName())) { - return JavaEightUtil.emptyOptionalInt(); - } else if ("java.util.OptionalLong".equals(type.getName())) { - return JavaEightUtil.emptyOptionalLong(); - } else if ("java.util.stream.Stream".equals(type.getName())) { - return JavaEightUtil.emptyStream(); - } else if ("java.util.stream.DoubleStream".equals(type.getName())) { - return JavaEightUtil.emptyDoubleStream(); - } else if ("java.util.stream.IntStream".equals(type.getName())) { - return JavaEightUtil.emptyIntStream(); - } else if ("java.util.stream.LongStream".equals(type.getName())) { - return JavaEightUtil.emptyLongStream(); - } else if ("java.time.Duration".equals(type.getName())) { - return JavaEightUtil.emptyDuration(); - } else if ("java.time.Period".equals(type.getName())) { - return JavaEightUtil.emptyPeriod(); + } else if (type == Optional.class) { + return Optional.empty(); + } else if (type == OptionalDouble.class) { + return OptionalDouble.empty(); + } else if (type == OptionalInt.class) { + return OptionalInt.empty(); + } else if (type == OptionalLong.class) { + return OptionalLong.empty(); + } else if (type == Stream.class) { + return Stream.empty(); + } else if (type == DoubleStream.class) { + return DoubleStream.empty(); + } else if (type == IntStream.class) { + return IntStream.empty(); + } else if (type == LongStream.class) { + return LongStream.empty(); + } else if (type == Duration.class) { + return Duration.ZERO; + } else if (type == Period.class) { + return Period.ZERO; } // Let's not care about the rest of collections. diff --git a/src/main/java/org/mockito/internal/util/JavaEightUtil.java b/src/main/java/org/mockito/internal/util/JavaEightUtil.java deleted file mode 100644 index 20c6e80c3b..0000000000 --- a/src/main/java/org/mockito/internal/util/JavaEightUtil.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2016 Mockito contributors - * This program is made available under the terms of the MIT License. - */ -package org.mockito.internal.util; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -import org.mockito.creation.instance.InstantiationException; - -/** - * Helper class to work with features that were introduced in Java versions after 1.5. - * This class uses reflection in most places to avoid coupling with a newer JDK. - */ -public final class JavaEightUtil { - - // No need for volatile, these optionals are already safe singletons. - private static Object emptyOptional; - private static Object emptyOptionalDouble; - private static Object emptyOptionalInt; - private static Object emptyOptionalLong; - private static Object emptyDuration; - private static Object emptyPeriod; - - private JavaEightUtil() { - // utility class - } - - /** - * Creates an empty Optional using reflection to stay backwards-compatible with older JDKs. - * - * @return an empty Optional. - */ - public static Object emptyOptional() { - // no need for double-checked locking - if (emptyOptional != null) { - return emptyOptional; - } - - return emptyOptional = invokeNullaryFactoryMethod("java.util.Optional", "empty"); - } - - /** - * Creates an empty OptionalDouble using reflection to stay backwards-compatible with older JDKs. - * - * @return an empty OptionalDouble. - */ - public static Object emptyOptionalDouble() { - // no need for double-checked locking - if (emptyOptionalDouble != null) { - return emptyOptionalDouble; - } - - return emptyOptionalDouble = - invokeNullaryFactoryMethod("java.util.OptionalDouble", "empty"); - } - - /** - * Creates an empty OptionalInt using reflection to stay backwards-compatible with older JDKs. - * - * @return an empty OptionalInt. - */ - public static Object emptyOptionalInt() { - // no need for double-checked locking - if (emptyOptionalInt != null) { - return emptyOptionalInt; - } - - return emptyOptionalInt = invokeNullaryFactoryMethod("java.util.OptionalInt", "empty"); - } - - /** - * Creates an empty OptionalLong using reflection to stay backwards-compatible with older JDKs. - * - * @return an empty OptionalLong. - */ - public static Object emptyOptionalLong() { - // no need for double-checked locking - if (emptyOptionalLong != null) { - return emptyOptionalLong; - } - - return emptyOptionalLong = invokeNullaryFactoryMethod("java.util.OptionalLong", "empty"); - } - - /** - * Creates an empty Stream using reflection to stay backwards-compatible with older JDKs. - * - * @return an empty Stream. - */ - public static Object emptyStream() { - // note: the empty stream can not be stored as a singleton. - return invokeNullaryFactoryMethod("java.util.stream.Stream", "empty"); - } - - /** - * Creates an empty DoubleStream using reflection to stay backwards-compatible with older JDKs. - * - * @return an empty DoubleStream. - */ - public static Object emptyDoubleStream() { - // note: the empty stream can not be stored as a singleton. - return invokeNullaryFactoryMethod("java.util.stream.DoubleStream", "empty"); - } - - /** - * Creates an empty IntStream using reflection to stay backwards-compatible with older JDKs. - * - * @return an empty IntStream. - */ - public static Object emptyIntStream() { - // note: the empty stream can not be stored as a singleton. - return invokeNullaryFactoryMethod("java.util.stream.IntStream", "empty"); - } - - /** - * Creates an empty LongStream using reflection to stay backwards-compatible with older JDKs. - * - * @return an empty LongStream. - */ - public static Object emptyLongStream() { - // note: the empty stream can not be stored as a singleton. - return invokeNullaryFactoryMethod("java.util.stream.LongStream", "empty"); - } - - /** - * Creates an empty Duration using reflection to stay backwards-compatible with older JDKs. - * - * @return an empty (ZERO) Duration. - */ - public static Object emptyDuration() { - // no need for double-checked locking - if (emptyDuration != null) { - return emptyDuration; - } - - return emptyDuration = getStaticFieldValue("java.time.Duration", "ZERO"); - } - - /** - * Creates an empty Period using reflection to stay backwards-compatible with older JDKs. - * - * @return an empty (ZERO) Period. - */ - public static Object emptyPeriod() { - // no need for double-checked locking - if (emptyPeriod != null) { - return emptyPeriod; - } - - return emptyPeriod = getStaticFieldValue("java.time.Period", "ZERO"); - } - - /** - * Invokes a nullary static factory method using reflection to stay backwards-compatible with older JDKs. - * - * @param fqcn The fully qualified class name of the type to be produced. - * @param methodName The name of the factory method. - * @return the object produced. - */ - private static Object invokeNullaryFactoryMethod(final String fqcn, final String methodName) { - try { - final Method method = getMethod(fqcn, methodName); - return method.invoke(null); - // any exception is really unexpected since the type name has - // already been verified - } catch (final Exception e) { - throw new InstantiationException( - String.format("Could not create %s#%s(): %s", fqcn, methodName, e), e); - } - } - - /** - * Gets a value of the classes' field using reflection to stay backwards-compatible with older JDKs. - * - * @param fqcn The fully qualified class name of the type to be produced. - * @param fieldName The name of th classes' field which value is going to be returned. - * @return the restored value. - */ - private static Object getStaticFieldValue(final String fqcn, final String fieldName) { - try { - final Class type = getClass(fqcn); - final Field field = type.getField(fieldName); - return field.get(null); - // any exception is really unexpected since the type name has - // already been verified - } catch (Exception e) { - throw new InstantiationException( - String.format("Could not get %s#%s(): %s", fqcn, fieldName, e), e); - } - } - - /** - * Returns the {@code Class} object associated with the class or interface with the given string name. - * - * @param fqcn The fully qualified class name of the type to be produced. - * @return the Class object for the class with the specified name. - */ - private static Class getClass(String fqcn) { - try { - return Class.forName(fqcn); - // any exception is really unexpected since the type name has - // already been verified - } catch (ClassNotFoundException e) { - throw new InstantiationException(String.format("Could not find %s: %s", fqcn, e), e); - } - } - - /** - * Returns a Method object that reflects the specified public member method of the class or interface represented by the fully qualified class name. - * - * @param fqcn The fully qualified class name of the type to be produced. - * @param methodName The name of the method. - * @param parameterClasses The list of parameters. - * @return The Method object that matches the specified name and parameterTypes. - */ - private static Method getMethod( - final String fqcn, final String methodName, final Class... parameterClasses) { - try { - final Class type = getClass(fqcn); - return type.getMethod(methodName, parameterClasses); - } catch (Exception e) { - throw new InstantiationException( - String.format("Could not find %s#%s(): %s", fqcn, methodName, e), e); - } - } -} diff --git a/src/main/java/org/mockito/internal/util/Platform.java b/src/main/java/org/mockito/internal/util/Platform.java index fde59fbbe9..6ff0378280 100644 --- a/src/main/java/org/mockito/internal/util/Platform.java +++ b/src/main/java/org/mockito/internal/util/Platform.java @@ -7,15 +7,9 @@ import static org.mockito.internal.util.StringUtil.join; import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public abstract class Platform { - private static final Pattern JAVA_8_RELEASE_VERSION_SCHEME = - Pattern.compile("1\\.8\\.0_(\\d+)(?:-ea)?(?:-b\\d+)?"); - private static final Pattern JAVA_8_DEV_VERSION_SCHEME = - Pattern.compile("1\\.8\\.0b\\d+_u(\\d+)"); public static final String JAVA_VERSION = System.getProperty("java.specification.version"); public static final String JVM_VERSION = System.getProperty("java.runtime.version"); public static final String JVM_VENDOR = System.getProperty("java.vm.vendor"); @@ -68,31 +62,6 @@ public static String describe() { return description; } - public static boolean isJava8BelowUpdate45() { - if (JVM_VERSION == null) { - return false; - } else { - return isJava8BelowUpdate45(JVM_VERSION); - } - } - - static boolean isJava8BelowUpdate45(String jvmVersion) { - Matcher matcher = JAVA_8_RELEASE_VERSION_SCHEME.matcher(jvmVersion); - if (matcher.matches()) { - int update = Integer.parseInt(matcher.group(1)); - return update < 45; - } - - matcher = JAVA_8_DEV_VERSION_SCHEME.matcher(jvmVersion); - if (matcher.matches()) { - int update = Integer.parseInt(matcher.group(1)); - return update < 45; - } - - matcher = Pattern.compile("1\\.8\\.0-b\\d+").matcher(jvmVersion); - return matcher.matches(); - } - public static String warnForVM( String vmName1, String warnMessage1, String vmName2, String warnMessage2) { return warnForVM(JVM_NAME, vmName1, warnMessage1, vmName2, warnMessage2); diff --git a/src/test/java/org/mockito/internal/util/PlatformTest.java b/src/test/java/org/mockito/internal/util/PlatformTest.java index d4c453386d..42f59a1bc6 100644 --- a/src/test/java/org/mockito/internal/util/PlatformTest.java +++ b/src/test/java/org/mockito/internal/util/PlatformTest.java @@ -63,34 +63,6 @@ public void should_warn_for_jvm() throws Exception { .isEqualTo(""); } - @Test - public void should_parse_open_jdk_string_and_report_wether_below_or_nut_update_45() { - // Given - // Sources : - // - https://www.oracle.com/java/technologies/javase/versioning-naming.html - // - https://www.oracle.com/java/technologies/javase/jdk7-naming.html - // - https://www.oracle.com/java/technologies/javase/jdk8-naming.html - // - - // https://stackoverflow.com/questions/35844985/how-do-we-get-sr-and-fp-of-ibm-jre-using-java - // - - // https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.80.doc/user/build_number.html - Map versions = new HashMap<>(); - versions.put("1.8.0_92-b14", false); - versions.put("1.8.0-b24", true); - versions.put("1.8.0_5", true); - versions.put("1.8.0b5_u44", true); - versions.put("1.8.0b5_u92", false); - versions.put("1.7.0_4", false); - versions.put("1.4.0_03-b04", false); - versions.put("1.4.0_03-ea-b01", false); - versions.put("pxi3270_27sr4-20160303_03 (SR4)", false); - versions.put("pwi3260sr11-20120412_01 (SR11)", false); - versions.put("pwa6480sr1fp10-20150711_01 (SR1 FP10)", false); - versions.put("null", false); - - assertPlatformParsesCorrectlyVariousVersionScheme(versions); - } - @Test public void should_parse_open_jdk9_string() { // The tested method targets Java 8 but should be able to parse other Java version numbers @@ -140,9 +112,7 @@ public void should_parse_open_jdk9_string() { private void assertPlatformParsesCorrectlyVariousVersionScheme(Map versions) { for (Map.Entry version : versions.entrySet()) { - assertThat(Platform.isJava8BelowUpdate45(version.getKey())) - .describedAs(version.getKey()) - .isEqualTo(version.getValue()); + assertThat(version.getValue()).describedAs(version.getKey()).isEqualTo(false); } } } diff --git a/subprojects/androidTest/androidTest.gradle b/subprojects/androidTest/androidTest.gradle index 037b99cf72..2176b5052b 100644 --- a/subprojects/androidTest/androidTest.gradle +++ b/subprojects/androidTest/androidTest.gradle @@ -30,7 +30,7 @@ android { targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '11' } } diff --git a/subprojects/kotlinReleaseCoroutinesTest/kotlinReleaseCoroutinesTest.gradle b/subprojects/kotlinReleaseCoroutinesTest/kotlinReleaseCoroutinesTest.gradle index a8512591ce..284b3bdfb7 100644 --- a/subprojects/kotlinReleaseCoroutinesTest/kotlinReleaseCoroutinesTest.gradle +++ b/subprojects/kotlinReleaseCoroutinesTest/kotlinReleaseCoroutinesTest.gradle @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + buildscript { repositories { mavenCentral() @@ -17,6 +20,15 @@ repositories { maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } } +sourceCompatibility = 11 +targetCompatibility = 11 + +tasks.withType(KotlinCompile).configureEach { + compilerOptions { + jvmTarget = JvmTarget.JVM_11 + } +} + dependencies { testImplementation project(":") testImplementation libraries.junit4 diff --git a/subprojects/kotlinTest/kotlinTest.gradle b/subprojects/kotlinTest/kotlinTest.gradle index 55c8e8036f..99397def9f 100644 --- a/subprojects/kotlinTest/kotlinTest.gradle +++ b/subprojects/kotlinTest/kotlinTest.gradle @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + plugins { id 'org.jetbrains.kotlin.jvm' id 'java' @@ -7,6 +10,15 @@ description = "Kotlin tests for Mockito." apply from: "$rootDir/gradle/dependencies.gradle" +sourceCompatibility = 11 +targetCompatibility = 11 + +tasks.withType(KotlinCompile).configureEach { + compilerOptions { + jvmTarget = JvmTarget.JVM_11 + } +} + dependencies { testImplementation project(":") testImplementation libraries.junit4 From b47bab2970a7f9c782527ed90da2a6668f25409c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Jan 2023 20:11:54 +0100 Subject: [PATCH 140/160] Bump com.diffplug.spotless from 6.13.0 to 6.14.0 (#2888) Bumps com.diffplug.spotless from 6.13.0 to 6.14.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 92c56c1ed3..b08e20b591 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.13.0' + id 'com.diffplug.spotless' version '6.14.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.44.0' id 'biz.aQute.bnd.builder' version '6.4.0' From 19ab3ea8389d215621b256e055987df204f400e4 Mon Sep 17 00:00:00 2001 From: Ashley <73482956+ascopes@users.noreply.github.com> Date: Sun, 29 Jan 2023 10:11:26 +0000 Subject: [PATCH 141/160] Fixes some mistakes and missing details in documentation (#2889) - Makes static mock verification and construction initialisers into functional interfaces, and adds some missing documentation notes. - Fixes a number of grammar/spelling typos. - Replaces an instance of a JavaDoc comment suggesting to use a deprecated method with a reference to the suggested replacement. - Adds a note about supporting chaining of Spy with InjectMocks into the InjectMocks documentation --- .../java/org/mockito/AdditionalAnswers.java | 2 +- src/main/java/org/mockito/ArgumentCaptor.java | 7 +- .../java/org/mockito/ArgumentMatcher.java | 10 +-- .../java/org/mockito/ArgumentMatchers.java | 16 ++-- src/main/java/org/mockito/Incubating.java | 7 ++ src/main/java/org/mockito/InjectMocks.java | 5 ++ src/main/java/org/mockito/MockSettings.java | 20 ++--- .../java/org/mockito/MockedConstruction.java | 33 ++++++++ src/main/java/org/mockito/MockedStatic.java | 9 ++ src/main/java/org/mockito/MockingDetails.java | 2 +- src/main/java/org/mockito/Mockito.java | 83 ++++++++++--------- .../java/org/mockito/MockitoFramework.java | 8 +- src/main/java/org/mockito/MockitoSession.java | 4 +- src/main/java/org/mockito/NotExtensible.java | 2 +- src/main/java/org/mockito/Spy.java | 4 +- .../configuration/IMockitoConfiguration.java | 4 +- .../internal/debugging/LoggingListener.java | 2 +- .../stubbing/answers/CallsRealMethods.java | 4 +- .../defaultanswers/ReturnsSmartNulls.java | 8 +- .../debugging/LoggingListenerTest.java | 4 +- 20 files changed, 146 insertions(+), 88 deletions(-) diff --git a/src/main/java/org/mockito/AdditionalAnswers.java b/src/main/java/org/mockito/AdditionalAnswers.java index 1ba790f1c0..63ab9bb483 100644 --- a/src/main/java/org/mockito/AdditionalAnswers.java +++ b/src/main/java/org/mockito/AdditionalAnswers.java @@ -276,7 +276,7 @@ public static Answer returnsArgAt(int position) { * This feature suffers from the same drawback as the spy. * The mock will call the delegate if you use regular when().then() stubbing style. * Since the real implementation is called this might have some side effects. - * Therefore you should use the doReturn|Throw|Answer|CallRealMethod stubbing style. Example: + * Therefore, you should use the doReturn|Throw|Answer|CallRealMethod stubbing style. Example: * *
    
          *   List listWithDelegate = mock(List.class, AdditionalAnswers.delegatesTo(awesomeList));
    diff --git a/src/main/java/org/mockito/ArgumentCaptor.java b/src/main/java/org/mockito/ArgumentCaptor.java
    index ed9e398f91..e9b347bd2e 100644
    --- a/src/main/java/org/mockito/ArgumentCaptor.java
    +++ b/src/main/java/org/mockito/ArgumentCaptor.java
    @@ -35,12 +35,13 @@
      *
      * 

    * Warning: it is recommended to use ArgumentCaptor with verification but not with stubbing. - * Using ArgumentCaptor with stubbing may decrease test readability because captor is created outside of assert (aka verify or 'then') block. - * Also it may reduce defect localization because if stubbed method was not called then no argument is captured. + * Using ArgumentCaptor with stubbing may decrease test readability because captor is created + * outside of assertion (aka verify or 'then') blocks. + * It may also reduce defect localization because if the stubbed method was not called, then no argument is captured. * *

    * In a way ArgumentCaptor is related to custom argument matchers (see javadoc for {@link ArgumentMatcher} class). - * Both techniques can be used for making sure certain arguments were passed to mocks. + * Both techniques can be used for making sure certain arguments were passed to mock objects. * However, ArgumentCaptor may be a better fit if: *

      *
    • custom argument matcher is not likely to be reused
    • diff --git a/src/main/java/org/mockito/ArgumentMatcher.java b/src/main/java/org/mockito/ArgumentMatcher.java index 5d68132d82..3c8ccdea00 100644 --- a/src/main/java/org/mockito/ArgumentMatcher.java +++ b/src/main/java/org/mockito/ArgumentMatcher.java @@ -10,7 +10,7 @@ * and reduce the risk of version incompatibility. * Migration guide is included close to the bottom of this javadoc. *

      - * For non-trivial method arguments used in stubbing or verification, you have following options + * For non-trivial method arguments used in stubbing or verification, you have the following options * (in no particular order): *

        *
      • refactor the code so that the interactions with collaborators are easier to test with mocks. @@ -102,10 +102,10 @@ * *
      • *
      - * What option is right for you? If you don't mind compile dependency to hamcrest - * then option b) is probably right for you. - * Your choice should not have big impact and is fully reversible - - * you can choose different option in future (and refactor the code) + * What option is right for you? If you don't mind having a compile-time dependency for Hamcrest, + * then the second option is probably right for you. + * Your choice should not have a big impact and is fully reversible - + * you can choose different option in future (and refactor the code)! * * @param type of argument * @since 2.1.0 diff --git a/src/main/java/org/mockito/ArgumentMatchers.java b/src/main/java/org/mockito/ArgumentMatchers.java index 1d3f11136f..de87f298a1 100644 --- a/src/main/java/org/mockito/ArgumentMatchers.java +++ b/src/main/java/org/mockito/ArgumentMatchers.java @@ -914,7 +914,7 @@ public static T argThat(ArgumentMatcher matcher) { /** * Allows creating custom char argument matchers. - * + *

      * Note that {@link #argThat} will not work with primitive char matchers due to NullPointerException auto-unboxing caveat. *

      * See examples in javadoc for {@link ArgumentMatchers} class @@ -929,7 +929,7 @@ public static char charThat(ArgumentMatcher matcher) { /** * Allows creating custom boolean argument matchers. - * + *

      * Note that {@link #argThat} will not work with primitive boolean matchers due to NullPointerException auto-unboxing caveat. *

      * See examples in javadoc for {@link ArgumentMatchers} class @@ -944,7 +944,7 @@ public static boolean booleanThat(ArgumentMatcher matcher) { /** * Allows creating custom byte argument matchers. - * + *

      * Note that {@link #argThat} will not work with primitive byte matchers due to NullPointerException auto-unboxing caveat. *

      * See examples in javadoc for {@link ArgumentMatchers} class @@ -959,7 +959,7 @@ public static byte byteThat(ArgumentMatcher matcher) { /** * Allows creating custom short argument matchers. - * + *

      * Note that {@link #argThat} will not work with primitive short matchers due to NullPointerException auto-unboxing caveat. *

      * See examples in javadoc for {@link ArgumentMatchers} class @@ -974,7 +974,7 @@ public static short shortThat(ArgumentMatcher matcher) { /** * Allows creating custom int argument matchers. - * + *

      * Note that {@link #argThat} will not work with primitive int matchers due to NullPointerException auto-unboxing caveat. *

      * See examples in javadoc for {@link ArgumentMatchers} class @@ -989,7 +989,7 @@ public static int intThat(ArgumentMatcher matcher) { /** * Allows creating custom long argument matchers. - * + *

      * Note that {@link #argThat} will not work with primitive long matchers due to NullPointerException auto-unboxing caveat. *

      * See examples in javadoc for {@link ArgumentMatchers} class @@ -1004,7 +1004,7 @@ public static long longThat(ArgumentMatcher matcher) { /** * Allows creating custom float argument matchers. - * + *

      * Note that {@link #argThat} will not work with primitive float matchers due to NullPointerException auto-unboxing caveat. *

      * See examples in javadoc for {@link ArgumentMatchers} class @@ -1019,7 +1019,7 @@ public static float floatThat(ArgumentMatcher matcher) { /** * Allows creating custom double argument matchers. - * + *

      * Note that {@link #argThat} will not work with primitive double matchers due to NullPointerException auto-unboxing caveat. *

      * See examples in javadoc for {@link ArgumentMatchers} class diff --git a/src/main/java/org/mockito/Incubating.java b/src/main/java/org/mockito/Incubating.java index adf011abc7..ba209b3c41 100644 --- a/src/main/java/org/mockito/Incubating.java +++ b/src/main/java/org/mockito/Incubating.java @@ -5,6 +5,7 @@ package org.mockito; import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -22,7 +23,13 @@ * and can change before release. * *

    + * + *

    + * Any components extending a class or interface annotated with this annotation should also + * be considered to be incubating, as the underlying implementation may be subject to future change + * before it is finalized. */ @Retention(RetentionPolicy.RUNTIME) +@Inherited @Documented public @interface Incubating {} diff --git a/src/main/java/org/mockito/InjectMocks.java b/src/main/java/org/mockito/InjectMocks.java index ea8f188985..ff412b8df6 100644 --- a/src/main/java/org/mockito/InjectMocks.java +++ b/src/main/java/org/mockito/InjectMocks.java @@ -158,6 +158,11 @@ * be it mocks/spies or real objects. *

    * + *

    + * Elements annotated with this annotation can also be spied upon by also adding the {@link Spy} + * annotation to the element. + *

    + * * @see Mock * @see Spy * @see MockitoAnnotations#openMocks(Object) diff --git a/src/main/java/org/mockito/MockSettings.java b/src/main/java/org/mockito/MockSettings.java index 8d8a093e25..6958bcb347 100644 --- a/src/main/java/org/mockito/MockSettings.java +++ b/src/main/java/org/mockito/MockSettings.java @@ -43,7 +43,7 @@ *
    * {@link MockSettings} has been introduced for two reasons. * Firstly, to make it easy to add another mock setting when the demand comes. - * Secondly, to enable combining together different mock settings without introducing zillions of overloaded mock() methods. + * Secondly, to enable combining different mock settings without introducing zillions of overloaded mock() methods. */ @NotExtensible public interface MockSettings extends Serializable { @@ -64,7 +64,7 @@ public interface MockSettings extends Serializable { * Baz baz = (Baz) foo; * * - * @param interfaces extra interfaces the should implement. + * @param interfaces extra interfaces the mock should implement. * @return settings instance so that you can fluently specify other settings */ MockSettings extraInterfaces(Class... interfaces); @@ -94,7 +94,7 @@ public interface MockSettings extends Serializable { * * Sets the instance that will be spied. Actually copies the internal fields of the passed instance to the mock. *

    - * As usual you are going to read the partial mock warning: + * As usual, you are going to read the partial mock warning: * Object oriented programming is more or less about tackling complexity by dividing the complexity into separate, specific, SRPy objects. * How does partial mock fit into this paradigm? Well, it just doesn't... * Partial mock usually means that the complexity has been moved to a different method on the same object. @@ -133,10 +133,10 @@ public interface MockSettings extends Serializable { /** * Specifies default answers to interactions. - * It's quite advanced feature and typically you don't need it to write decent tests. - * However it can be helpful when working with legacy systems. + * It's quite advanced feature, and typically you don't need it to write decent tests. + * However, it can be helpful when working with legacy systems. *

    - * It is the default answer so it will be used only when you don't stub the method call. + * It is the default answer, so it will be used only when you don't stub the method call. * *

    
          *   Foo mock = mock(Foo.class, withSettings().defaultAnswer(RETURNS_SMART_NULLS));
    @@ -209,7 +209,7 @@ public interface MockSettings extends Serializable {
         /**
          * Add stubbing lookup listener to the mock object.
          *
    -     * Multiple listeners may be added and they will be notified orderly.
    +     * Multiple listeners may be added, and they will be notified in an orderly fashion.
          *
          * For use cases and more info see {@link StubbingLookupListener}.
          *
    @@ -228,7 +228,7 @@ public interface MockSettings extends Serializable {
          * Registers a listener for method invocations on this mock. The listener is
          * notified every time a method on this mock is called.
          * 

    - * Multiple listeners may be added and they will be notified in the order they were supplied. + * Multiple listeners may be added, and they will be notified in the order they were supplied. * * Example: *

    
    @@ -247,7 +247,7 @@ public interface MockSettings extends Serializable {
          * See {@link VerificationStartedListener} on how such listener can be useful.
          * 

    * When multiple listeners are added, they are notified in order they were supplied. - * There is no reason to supply multiple listeners but we wanted to keep the API + * There is no reason to supply multiple listeners, but we wanted to keep the API * simple and consistent with {@link #invocationListeners(InvocationListener...)}. *

    * Throws exception when any of the passed listeners is null or when the entire vararg array is null. @@ -313,7 +313,7 @@ public interface MockSettings extends Serializable { MockSettings outerInstance(Object outerClassInstance); /** - * By default, Mockito makes an attempt to preserve all annotation meta data on the mocked + * By default, Mockito makes an attempt to preserve all annotation metadata on the mocked * type and its methods to mirror the mocked type as closely as possible. If this is not * desired, this option can be used to disable this behavior. * diff --git a/src/main/java/org/mockito/MockedConstruction.java b/src/main/java/org/mockito/MockedConstruction.java index 5c2ec2128d..ccf78c73a2 100644 --- a/src/main/java/org/mockito/MockedConstruction.java +++ b/src/main/java/org/mockito/MockedConstruction.java @@ -21,19 +21,52 @@ */ public interface MockedConstruction extends ScopedMock { + /** + * Get the constructed mocks. + * + * @return the constructed mocks. + */ List constructed(); + /** + * The context for a construction mock. + */ interface Context { int getCount(); + /** + * Get the constructor that is invoked during the mock creation. + * + * @return the constructor. + */ Constructor constructor(); + /** + * Get the arguments that were passed to the constructor. + * + * @return the arguments passed to the constructor, as a list. + */ List arguments(); } + /** + * Functional interface that consumes a newly created mock and the mock context. + *

    + * Used to attach behaviours to new mocks. + * + * @param the mock type. + */ + @FunctionalInterface interface MockInitializer { + /** + * Configure the mock. + * + * @param mock the newly created mock. + * @param context the mock context. + * @throws Throwable any exception that may be thrown. + */ void prepare(T mock, Context context) throws Throwable; } } diff --git a/src/main/java/org/mockito/MockedStatic.java b/src/main/java/org/mockito/MockedStatic.java index 9095556fd7..113bee2e3a 100644 --- a/src/main/java/org/mockito/MockedStatic.java +++ b/src/main/java/org/mockito/MockedStatic.java @@ -62,8 +62,17 @@ default void verify(Verification verification) { */ void verifyNoInteractions(); + /** + * Functional interface for a verification operation on a static mock. + */ + @FunctionalInterface interface Verification { + /** + * Apply the verification operation. + * + * @throws Throwable any exception that may be thrown. + */ void apply() throws Throwable; } } diff --git a/src/main/java/org/mockito/MockingDetails.java b/src/main/java/org/mockito/MockingDetails.java index c32e04e5c9..37a149100a 100644 --- a/src/main/java/org/mockito/MockingDetails.java +++ b/src/main/java/org/mockito/MockingDetails.java @@ -73,7 +73,7 @@ public interface MockingDetails { * What is 'stubbing'? * Stubbing is your when(x).then(y) declaration, e.g. configuring the mock to behave in a specific way, * when specific method with specific arguments is invoked on a mock. - * Typically stubbing is configuring mock to return X when method Y is invoked. + * Typically, stubbing is configuring mock to return X when method Y is invoked. *

    * Why do you need to access stubbings of a mock? * In a normal workflow of creation clean tests, there is no need for this API. diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index db2ca1096b..428655e4a8 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -71,7 +71,7 @@ * 11. Stubbing with callbacks
    * 12. doReturn()|doThrow()|doAnswer()|doNothing()|doCallRealMethod() family of methods
    * 13. Spying on real objects
    - * 14. Changing default return values of unstubbed invocations (Since 1.7)
    + * 14. Changing default return values of un-stubbed invocations (Since 1.7)
    * 15. Capturing arguments for further assertions (Since 1.8.0)
    * 16. Real partial mocks (Since 1.8.0)
    * 17. Resetting mocks (Since 1.8.0)
    @@ -226,7 +226,7 @@ * *

  • Stubbing can be overridden: for example common stubbing can go to * fixture setup but the test methods can override it. - * Please note that overridding stubbing is a potential code smell that points out too much stubbing
  • + * Please note that overriding stubbing is a potential code smell that points out too much stubbing * *
  • Once stubbed, the method will always return a stubbed value, regardless * of how many times it is called.
  • @@ -652,7 +652,7 @@ *
  • Mockito *does not* delegate calls to the passed real instance, instead it actually creates a copy of it. * So if you keep the real instance and interact with it, don't expect the spied to be aware of those interaction * and their effect on real instance state. - * The corollary is that when an *unstubbed* method is called *on the spy* but *not on the real instance*, + * The corollary is that when an *un-stubbed* method is called *on the spy* but *not on the real instance*, * you won't see any effects on the real instance. *
  • * @@ -665,7 +665,7 @@ * * * - *

    14. Changing default return values of unstubbed invocations (Since 1.7)

    + *

    14. Changing default return values of un-stubbed invocations (Since 1.7)

    * * You can create a mock with specified strategy for its return values. * It's quite an advanced feature and typically you don't need it to write decent tests. @@ -1671,9 +1671,9 @@ public class Mockito extends ArgumentMatchers { /** * The default Answer of every mock if the mock was not stubbed. * - * Typically it just returns some empty value. + * Typically, it just returns some empty value. *

    - * {@link Answer} can be used to define the return values of unstubbed invocations. + * {@link Answer} can be used to define the return values of un-stubbed invocations. *

    * This implementation first tries the global configuration and if there is no global configuration then * it will use a default answer that returns zeros, empty collections, nulls, etc. @@ -1683,12 +1683,14 @@ public class Mockito extends ArgumentMatchers { /** * Optional Answer to be used with {@link Mockito#mock(Class, Answer)}. *

    - * {@link Answer} can be used to define the return values of unstubbed invocations. + * {@link Answer} can be used to define the return values of un-stubbed invocations. *

    * This implementation can be helpful when working with legacy code. - * Unstubbed methods often return null. If your code uses the object returned by an unstubbed call you get a NullPointerException. + * Un-stubbed methods often return null. If your code uses the object returned by an un-stubbed call, + * you get a NullPointerException. * This implementation of Answer returns SmartNull instead of null. - * SmartNull gives nicer exception message than NPE because it points out the line where unstubbed method was called. You just click on the stack trace. + * SmartNull gives nicer exception messages than NPEs, because it points out the + * line where the un-stubbed method was called. You just click on the stack trace. *

    * ReturnsSmartNulls first tries to return ordinary values (zeros, empty collections, empty string, etc.) * then it tries to return SmartNull. If the return type is final then plain null is returned. @@ -1697,15 +1699,15 @@ public class Mockito extends ArgumentMatchers { *

    
          *   Foo mock = mock(Foo.class, RETURNS_SMART_NULLS);
          *
    -     *   //calling unstubbed method here:
    +     *   //calling un-stubbed method here:
          *   Stuff stuff = mock.getStuff();
          *
    -     *   //using object returned by unstubbed call:
    +     *   //using object returned by un-stubbed call:
          *   stuff.doSomething();
          *
          *   //Above doesn't yield NullPointerException this time!
          *   //Instead, SmartNullPointerException is thrown.
    -     *   //Exception's cause links to unstubbed mock.getStuff() - just click on the stack trace.
    +     *   //Exception's cause links to un-stubbed mock.getStuff() - just click on the stack trace.
          * 
    */ public static final Answer RETURNS_SMART_NULLS = Answers.RETURNS_SMART_NULLS; @@ -1713,7 +1715,7 @@ public class Mockito extends ArgumentMatchers { /** * Optional Answer to be used with {@link Mockito#mock(Class, Answer)} *

    - * {@link Answer} can be used to define the return values of unstubbed invocations. + * {@link Answer} can be used to define the return values of un-stubbed invocations. *

    * This implementation can be helpful when working with legacy code. *

    @@ -1814,14 +1816,14 @@ public class Mockito extends ArgumentMatchers { * Optional Answer to be used with {@link Mockito#mock(Class, Answer)} * *

    - * {@link Answer} can be used to define the return values of unstubbed invocations. + * {@link Answer} can be used to define the return values of un-stubbed invocations. *

    * This implementation can be helpful when working with legacy code. - * When this implementation is used, unstubbed methods will delegate to the real implementation. + * When this implementation is used, un-stubbed methods will delegate to the real implementation. * This is a way to create a partial mock object that calls real methods by default. *

    - * As usual you are going to read the partial mock warning: - * Object oriented programming is more less tackling complexity by dividing the complexity into separate, specific, SRPy objects. + * As usual, you are going to read the partial mock warning: + * Object oriented programming is more-or-less tackling complexity by dividing the complexity into separate, specific, SRPy objects. * How does partial mock fit into this paradigm? Well, it just doesn't... * Partial mock usually means that the complexity has been moved to a different method on the same object. * In most cases, this is not the way you want to design your application. @@ -2050,7 +2052,7 @@ public static MockingDetails mockingDetails(Object toInspect) { *

    See examples in javadoc for {@link Mockito} class

    * * @param classToMock class or interface to mock - * @param defaultAnswer default answer for unstubbed methods + * @param defaultAnswer default answer for un-stubbed methods * * @return mock object */ @@ -2061,7 +2063,7 @@ public static T mock(Class classToMock, Answer defaultAnswer) { /** * Creates a mock with some non-standard settings. *

    - * The number of configuration points for a mock grows + * The number of configuration points for a mock will grow, * so we need a fluent way to introduce new configuration without adding more and more overloaded Mockito.mock() methods. * Hence {@link MockSettings}. *

    
    @@ -2071,7 +2073,7 @@ public static  T mock(Class classToMock, Answer defaultAnswer) {
          * 
    * Use it carefully and occasionally. What might be reason your test needs non-standard mocks? * Is the code under test so complicated that it requires non-standard mocks? - * Wouldn't you prefer to refactor the code under test so it is testable in a simple way? + * Wouldn't you prefer to refactor the code under test, so that it is testable in a simple way? *

    * See also {@link Mockito#withSettings()} *

    @@ -2090,7 +2092,7 @@ public static T mock(Class classToMock, MockSettings mockSettings) { *

    * Real spies should be used carefully and occasionally, for example when dealing with legacy code. *

    - * As usual you are going to read the partial mock warning: + * As usual, you are going to read the partial mock warning: * Object oriented programming tackles complexity by dividing the complexity into separate, specific, SRPy objects. * How does partial mock fit into this paradigm? Well, it just doesn't... * Partial mock usually means that the complexity has been moved to a different method on the same object. @@ -2145,7 +2147,7 @@ public static T mock(Class classToMock, MockSettings mockSettings) { *

  • Mockito *does not* delegate calls to the passed real instance, instead it actually creates a copy of it. * So if you keep the real instance and interact with it, don't expect the spied to be aware of those interaction * and their effect on real instance state. - * The corollary is that when an *unstubbed* method is called *on the spy* but *not on the real instance*, + * The corollary is that when an *un-stubbed* method is called *on the spy* but *not on the real instance*, * you won't see any effects on the real instance.
  • * *
  • Watch out for final methods. @@ -2228,7 +2230,7 @@ private static Class getClassOf(T[] array) { * test or the mock will remain active on the current thread. *

    * Note: We recommend against mocking static methods of classes in the standard library or - * classes used by custom class loaders used to executed the block with the mocked class. A mock + * classes used by custom class loaders used to execute the block with the mocked class. A mock * maker might forbid mocking static methods of know classes that are known to cause problems. * Also, if a static method is a JVM-intrinsic, it cannot typically be mocked even if not * explicitly forbidden. @@ -2248,7 +2250,7 @@ public static MockedStatic mockStatic(Class classToMock) { * test or the mock will remain active on the current thread. *

    * Note: We recommend against mocking static methods of classes in the standard library or - * classes used by custom class loaders used to executed the block with the mocked class. A mock + * classes used by custom class loaders used to execute the block with the mocked class. A mock * maker might forbid mocking static methods of know classes that are known to cause problems. * Also, if a static method is a JVM-intrinsic, it cannot typically be mocked even if not * explicitly forbidden. @@ -2269,7 +2271,7 @@ public static MockedStatic mockStatic(Class classToMock, Answer defaul * test or the mock will remain active on the current thread. *

    * Note: We recommend against mocking static methods of classes in the standard library or - * classes used by custom class loaders used to executed the block with the mocked class. A mock + * classes used by custom class loaders used to execute the block with the mocked class. A mock * maker might forbid mocking static methods of know classes that are known to cause problems. * Also, if a static method is a JVM-intrinsic, it cannot typically be mocked even if not * explicitly forbidden. @@ -2290,7 +2292,7 @@ public static MockedStatic mockStatic(Class classToMock, String name) * test or the mock will remain active on the current thread. *

    * Note: We recommend against mocking static methods of classes in the standard library or - * classes used by custom class loaders used to executed the block with the mocked class. A mock + * classes used by custom class loaders used to execute the block with the mocked class. A mock * maker might forbid mocking static methods of know classes that are known to cause problems. * Also, if a static method is a JVM-intrinsic, it cannot typically be mocked even if not * explicitly forbidden. @@ -2358,7 +2360,7 @@ public static MockedConstruction mockConstruction(Class classToMock) { * See examples in javadoc for {@link Mockito} class * * @param classToMock non-abstract class of which constructions should be mocked. - * @param mockInitializer a callback to prepare a mock's methods after its instantiation. + * @param mockInitializer a callback to prepare the methods on a mock after its instantiation. * @return mock controller */ public static MockedConstruction mockConstruction( @@ -2408,7 +2410,7 @@ public static MockedConstruction mockConstruction( * * @param classToMock non-abstract class of which constructions should be mocked. * @param mockSettings the settings to use. - * @param mockInitializer a callback to prepare a mock's methods after its instantiation. + * @param mockInitializer a callback to prepare the methods on a mock after its instantiation. * @return mock controller */ public static MockedConstruction mockConstruction( @@ -2427,7 +2429,7 @@ public static MockedConstruction mockConstruction( * * @param classToMock non-abstract class of which constructions should be mocked. * @param mockSettingsFactory a function to create settings to use. - * @param mockInitializer a callback to prepare a mock's methods after its instantiation. + * @param mockInitializer a callback to prepare the methods on a mock after its instantiation. * @return mock controller */ public static MockedConstruction mockConstruction( @@ -2478,7 +2480,7 @@ public static MockedConstruction mockConstruction( *

    * Stubbing can be overridden: for example common stubbing can go to fixture * setup but the test methods can override it. - * Please note that overridding stubbing is a potential code smell that points out too much stubbing. + * Please note that overriding stubbing is a potential code smell that points out too much stubbing. *

    * Once stubbed, the method will always return stubbed value regardless * of how many times it is called. @@ -2630,7 +2632,7 @@ public static void clearInvocations(T... mocks) { * Some users who did a lot of classic, expect-run-verify mocking tend to use verifyNoMoreInteractions() very often, even in every test method. * verifyNoMoreInteractions() is not recommended to use in every test method. * verifyNoMoreInteractions() is a handy assertion from the interaction testing toolkit. Use it only when it's relevant. - * Abusing it leads to overspecified, less maintainable tests. + * Abusing it leads to over-specified, less maintainable tests. *

    * This method will also detect unverified invocations that occurred before the test method, * for example: in setUp(), @Before method or in constructor. @@ -2723,7 +2725,8 @@ public static Stubber doThrow(Class toBeThrown) { /** * Same as {@link #doThrow(Class)} but sets consecutive exception classes to be thrown. Remember to use - * doThrow() when you want to stub the void method to throw several exception of specified class. + * doThrow() when you want to stub the void method to throw several exceptions + * that are instances of the specified class. *

    * A new exception instance will be created for each method invocation. *

    @@ -2752,8 +2755,8 @@ public static Stubber doThrow( /** * Use doCallRealMethod() when you want to call the real implementation of a method. *

    - * As usual you are going to read the partial mock warning: - * Object oriented programming is more less tackling complexity by dividing the complexity into separate, specific, SRPy objects. + * As usual, you are going to read the partial mock warning: + * Object oriented programming is more-or-less tackling complexity by dividing the complexity into separate, specific, SRPy objects. * How does partial mock fit into this paradigm? Well, it just doesn't... * Partial mock usually means that the complexity has been moved to a different method on the same object. * In most cases, this is not the way you want to design your application. @@ -2891,7 +2894,7 @@ public static Stubber doNothing() { * * Above scenarios shows a tradeoff of Mockito's elegant syntax. Note that the scenarios are very rare, though. * Spying should be sporadic and overriding exception-stubbing is very rare. Not to mention that in general - * overridding stubbing is a potential code smell that points out too much stubbing. + * overriding stubbing is a potential code smell that points out too much stubbing. *

    * See examples in javadoc for {@link Mockito} class * @@ -2942,7 +2945,7 @@ public static Stubber doReturn(Object toBeReturned) { * * Above scenarios shows a trade-off of Mockito's elegant syntax. Note that the scenarios are very rare, though. * Spying should be sporadic and overriding exception-stubbing is very rare. Not to mention that in general - * overridding stubbing is a potential code smell that points out too much stubbing. + * overriding stubbing is a potential code smell that points out too much stubbing. *

    * See examples in javadoc for {@link Mockito} class * @@ -2993,7 +2996,7 @@ public static InOrder inOrder(Object... mocks) { * and provides other benefits. *

    * ignoreStubs() is sometimes useful when coupled with verifyNoMoreInteractions() or verification inOrder(). - * Helps avoiding redundant verification of stubbed calls - typically we're not interested in verifying stubs. + * Helps to avoid redundant verification of stubbed calls - typically we're not interested in verifying stubs. *

    * Warning, ignoreStubs() might lead to overuse of verifyNoMoreInteractions(ignoreStubs(...)); * Bear in mind that Mockito does not recommend bombarding every test with verifyNoMoreInteractions() @@ -3240,7 +3243,7 @@ public static VerificationWithTimeout timeout(long millis) { /** * Verification will be triggered after given amount of millis, allowing testing of async code. - * Useful when interactions with the mock object did not happened yet. + * Useful when interactions with the mock object have yet to occur. * Extensive use of {@code after()} method can be a code smell - there are better ways of testing concurrent code. *

    * Not yet implemented to work with InOrder verification. @@ -3400,7 +3403,7 @@ public static MockitoFramework framework() { /** * {@code MockitoSession} is an optional, highly recommended feature - * that helps driving cleaner tests by eliminating boilerplate code and adding extra validation. + * that drives writing cleaner tests by eliminating boilerplate code and adding extra validation. *

    * For more information, including use cases and sample code, see the javadoc for {@link MockitoSession}. * @@ -3422,7 +3425,7 @@ public static MockitoSessionBuilder mockitoSession() { * Most mocks in most tests don't need leniency and should happily prosper with {@link Strictness#STRICT_STUBS}. *

      *
    • If a specific stubbing needs to be lenient - use this method
    • - *
    • If a specific mock need to have stubbings lenient - use {@link MockSettings#lenient()}
    • + *
    • If a specific mock need to have lenient stubbings - use {@link MockSettings#strictness(Strictness)}
    • *
    • If a specific test method / test class needs to have all stubbings lenient * - configure strictness using our JUnit support ({@link MockitoJUnit} or Mockito Session ({@link MockitoSession})
    • * diff --git a/src/main/java/org/mockito/MockitoFramework.java b/src/main/java/org/mockito/MockitoFramework.java index ba814e6029..020186f052 100644 --- a/src/main/java/org/mockito/MockitoFramework.java +++ b/src/main/java/org/mockito/MockitoFramework.java @@ -26,11 +26,11 @@ public interface MockitoFramework { * Adds listener to Mockito. * For a list of supported listeners, see the interfaces that extend {@link MockitoListener}. *

      - * Listeners can be useful for engs that extend Mockito framework. + * Listeners can be useful for components that extend Mockito framework. * They are used in the implementation of unused stubbings warnings ({@link org.mockito.quality.MockitoHint}). *

      * Make sure you remove the listener when the job is complete, see {@link #removeListener(MockitoListener)}. - * Currently the listeners list is thread local so you need to remove listener from the same thread otherwise + * Currently, the listeners list is thread local, so you need to remove listener from the same thread otherwise * remove is ineffectual. * In typical scenarios, it is not a problem, because adding and removing listeners typically happens in the same thread. *

      @@ -56,7 +56,7 @@ public interface MockitoFramework { /** * When you add listener using {@link #addListener(MockitoListener)} make sure to remove it. - * Currently the listeners list is thread local so you need to remove listener from the same thread otherwise + * Currently, the listeners list is thread local, so you need to remove listener from the same thread otherwise * remove is ineffectual. * In typical scenarios, it is not a problem, because adding and removing listeners typically happens in the same thread. *

      @@ -92,7 +92,7 @@ public interface MockitoFramework { /** * Clears up internal state of all inline mocks. * This method is only meaningful if inline mock maker is in use. - * Otherwise this method is a no-op and need not be used. + * For all other intents and purposes, this method is a no-op and need not be used. *

      * This method is useful to tackle subtle memory leaks that are possible due to the nature of inline mocking * (issue #1619). diff --git a/src/main/java/org/mockito/MockitoSession.java b/src/main/java/org/mockito/MockitoSession.java index d3ad832c67..f87e07dcd4 100644 --- a/src/main/java/org/mockito/MockitoSession.java +++ b/src/main/java/org/mockito/MockitoSession.java @@ -17,12 +17,12 @@ /** * {@code MockitoSession} is an optional, highly recommended feature - * that helps driving cleaner tests by eliminating boilerplate code and adding extra validation. + * that drives writing cleaner tests by eliminating boilerplate code and adding extra validation. * If you already use {@link MockitoJUnitRunner} or {@link MockitoRule} * *you don't need* {@code MockitoSession} because it is used by the runner/rule. *

      * {@code MockitoSession} is a session of mocking, during which the user creates and uses Mockito mocks. - * Typically the session is an execution of a single test method. + * Typically, the session is an execution of a single test method. * {@code MockitoSession} initializes mocks, validates usage and detects incorrect stubbing. * When the session is started it must be concluded with {@link #finishMocking()} * otherwise {@link UnfinishedMockingSessionException} is triggered when the next session is created. diff --git a/src/main/java/org/mockito/NotExtensible.java b/src/main/java/org/mockito/NotExtensible.java index 39191ac481..f5110f5d57 100644 --- a/src/main/java/org/mockito/NotExtensible.java +++ b/src/main/java/org/mockito/NotExtensible.java @@ -19,7 +19,7 @@ * Public types are all types that are *not* under "org.mockito.internal.*" package. *

      * Absence of {@code NotExtensible} annotation on a type *does not* mean it is intended to be extended. - * The annotation has been introduced late and therefore it is not used frequently in the codebase. + * The annotation has been introduced late, and therefore it is not used frequently in the codebase. * Many public types from Mockito API are not intended for extension, even though they do not have this annotation applied. * * @since 2.10.0 diff --git a/src/main/java/org/mockito/Spy.java b/src/main/java/org/mockito/Spy.java index aa485a355d..fa5e6e5ad0 100644 --- a/src/main/java/org/mockito/Spy.java +++ b/src/main/java/org/mockito/Spy.java @@ -14,7 +14,7 @@ import org.mockito.junit.MockitoJUnitRunner; /** - * Allows shorthand wrapping of field instances in an spy object. + * Allows shorthand wrapping of field instances in a spy object. * *

      * Example: @@ -79,7 +79,7 @@ *

    • Mockito *does not* delegate calls to the passed real instance, instead it actually creates a copy of it. * So if you keep the real instance and interact with it, don't expect the spied to be aware of those interaction * and their effect on real instance state. - * The corollary is that when an *unstubbed* method is called *on the spy* but *not on the real instance*, + * The corollary is that when an *un-stubbed* method is called *on the spy* but *not on the real instance*, * you won't see any effects on the real instance.
    • * *
    • Watch out for final methods. diff --git a/src/main/java/org/mockito/configuration/IMockitoConfiguration.java b/src/main/java/org/mockito/configuration/IMockitoConfiguration.java index 53664940ea..6e16d350f1 100644 --- a/src/main/java/org/mockito/configuration/IMockitoConfiguration.java +++ b/src/main/java/org/mockito/configuration/IMockitoConfiguration.java @@ -13,7 +13,7 @@ * In most cases you don't really need to configure Mockito. For example in case of working with legacy code, * when you might want to have different 'mocking style' this interface might be helpful. * A reason of configuring Mockito might be if you disagree with the {@link org.mockito.Answers#RETURNS_DEFAULTS} - * unstubbed mocks return. + * un-stubbed mocks return. * *

      * To configure Mockito create exactly org.mockito.configuration.MockitoConfiguration class @@ -38,7 +38,7 @@ public interface IMockitoConfiguration { /** - * Allows configuring the default answers of unstubbed invocations + * Allows configuring the default answers of un-stubbed invocations *

      * See javadoc for {@link IMockitoConfiguration} */ diff --git a/src/main/java/org/mockito/internal/debugging/LoggingListener.java b/src/main/java/org/mockito/internal/debugging/LoggingListener.java index 6e0a645ce1..c5ebee4aa5 100644 --- a/src/main/java/org/mockito/internal/debugging/LoggingListener.java +++ b/src/main/java/org/mockito/internal/debugging/LoggingListener.java @@ -82,7 +82,7 @@ public String getStubbingInfo() { if (!unstubbedCalls.isEmpty()) { lines.add("[Mockito]"); lines.add( - "[Mockito] Unstubbed method invocations (perhaps missing stubbing in the test?):"); + "[Mockito] Un-stubbed method invocations (perhaps missing stubbing in the test?):"); lines.add("[Mockito]"); addOrderedList(lines, unstubbedCalls); } diff --git a/src/main/java/org/mockito/internal/stubbing/answers/CallsRealMethods.java b/src/main/java/org/mockito/internal/stubbing/answers/CallsRealMethods.java index bab007bbb0..ae16991af8 100644 --- a/src/main/java/org/mockito/internal/stubbing/answers/CallsRealMethods.java +++ b/src/main/java/org/mockito/internal/stubbing/answers/CallsRealMethods.java @@ -17,10 +17,10 @@ /** * Optional Answer that adds partial mocking support *

      - * {@link Answer} can be used to define the return values of unstubbed invocations. + * {@link Answer} can be used to define the return values of un-stubbed invocations. *

      * This implementation can be helpful when working with legacy code. - * When this implementation is used, unstubbed methods will delegate to the real implementation. + * When this implementation is used, un-stubbed methods will delegate to the real implementation. * This is a way to create a partial mock object that calls real methods by default. *

      * As usual you are going to read the partial mock warning: 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 6f6476d046..d538784a94 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java @@ -25,12 +25,12 @@ * Optional Answer that can be used with * {@link Mockito#mock(Class, Answer)} *

      - * This implementation can be helpful when working with legacy code. Unstubbed + * This implementation can be helpful when working with legacy code. Un-stubbed * methods often return null. If your code uses the object returned by an - * unstubbed call you get a NullPointerException. This implementation of + * un-stubbed call, you get a NullPointerException. This implementation of * Answer returns SmartNulls instead of nulls. * SmartNull gives nicer exception message than NPE because it points out the - * line where unstubbed method was called. You just click on the stack trace. + * line where un-stubbed method was called. You just click on the stack trace. *

      * ReturnsSmartNulls first tries to return ordinary return values (see * {@link ReturnsMoreEmptyValues}) then it tries to return SmartNull. If the @@ -90,7 +90,7 @@ private static class ThrowsSmartNullPointer implements Answer { @Override public Object answer(InvocationOnMock currentInvocation) throws Throwable { if (isToStringMethod(currentInvocation.getMethod())) { - return "SmartNull returned by this unstubbed method call on a mock:\n" + return "SmartNull returned by this un-stubbed method call on a mock:\n" + unstubbedInvocation; } else if (isMethodOf( MockAccess.class, currentInvocation.getMock(), currentInvocation.getMethod())) { diff --git a/src/test/java/org/mockito/internal/debugging/LoggingListenerTest.java b/src/test/java/org/mockito/internal/debugging/LoggingListenerTest.java index 54c5bec09d..a81aa98d2f 100644 --- a/src/test/java/org/mockito/internal/debugging/LoggingListenerTest.java +++ b/src/test/java/org/mockito/internal/debugging/LoggingListenerTest.java @@ -97,7 +97,7 @@ public void informs_about_various_kinds_of_stubs() { + "[Mockito]\n" + "[Mockito] 1. at com.FooTest:30\n" + "[Mockito]\n" - + "[Mockito] Unstubbed method invocations (perhaps missing stubbing in the test?):\n" + + "[Mockito] Un-stubbed method invocations (perhaps missing stubbing in the test?):\n" + "[Mockito]\n" + "[Mockito] 1. at com.Foo:96", listener.getStubbingInfo()); @@ -127,7 +127,7 @@ public void informs_about_unstubbed() { assertEquals( "[Mockito] Additional stubbing information (see javadoc for StubbingInfo class):\n" + "[Mockito]\n" - + "[Mockito] Unstubbed method invocations (perhaps missing stubbing in the test?):\n" + + "[Mockito] Un-stubbed method invocations (perhaps missing stubbing in the test?):\n" + "[Mockito]\n" + "[Mockito] 1. com.Foo:20", listener.getStubbingInfo()); From 50b21cf68b400a29369de58b9286d29e368212a7 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Mon, 30 Jan 2023 17:22:40 -0500 Subject: [PATCH 142/160] StackWalker.Option not found on Mockito 5.1.0 (#2891) Fixes #2890 Signed-off-by: Andriy Redko --- .../internal/debugging/Java8LocationImpl.java | 59 +++++++++++++++++++ .../internal/debugging/LocationFactory.java | 34 ++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java diff --git a/src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java b/src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java new file mode 100644 index 0000000000..e8ee387c0a --- /dev/null +++ b/src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.debugging; + +import java.io.Serializable; + +import org.mockito.internal.exceptions.stacktrace.StackTraceFilter; +import org.mockito.invocation.Location; + +class Java8LocationImpl implements Location, Serializable { + + private static final long serialVersionUID = -9054861157390980624L; + // Limit the amount of objects being created, as this class is heavily instantiated: + private static final StackTraceFilter stackTraceFilter = new StackTraceFilter(); + + private String stackTraceLine; + private String sourceFile; + + public Java8LocationImpl(Throwable stackTraceHolder, boolean isInline) { + this(stackTraceFilter, stackTraceHolder, isInline); + } + + private Java8LocationImpl( + StackTraceFilter stackTraceFilter, Throwable stackTraceHolder, boolean isInline) { + computeStackTraceInformation(stackTraceFilter, stackTraceHolder, isInline); + } + + @Override + public String toString() { + return stackTraceLine; + } + + /** + * Eagerly compute the stacktrace line from the stackTraceHolder. Storing the Throwable is + * memory-intensive for tests that have large stacktraces and have a lot of invocations on + * mocks. + */ + private void computeStackTraceInformation( + StackTraceFilter stackTraceFilter, Throwable stackTraceHolder, boolean isInline) { + StackTraceElement filtered = stackTraceFilter.filterFirst(stackTraceHolder, isInline); + + // there are corner cases where exception can have a null or empty stack trace + // for example, a custom exception can override getStackTrace() method + if (filtered == null) { + this.stackTraceLine = "-> at <>"; + this.sourceFile = ""; + } else { + this.stackTraceLine = "-> at " + filtered; + this.sourceFile = filtered.getFileName(); + } + } + + @Override + public String getSourceFile() { + return sourceFile; + } +} diff --git a/src/main/java/org/mockito/internal/debugging/LocationFactory.java b/src/main/java/org/mockito/internal/debugging/LocationFactory.java index 08e441e87a..3d936ae761 100644 --- a/src/main/java/org/mockito/internal/debugging/LocationFactory.java +++ b/src/main/java/org/mockito/internal/debugging/LocationFactory.java @@ -7,6 +7,8 @@ import org.mockito.invocation.Location; public final class LocationFactory { + private static final Factory factory = createLocationFactory(); + private LocationFactory() {} public static Location create() { @@ -14,6 +16,36 @@ public static Location create() { } public static Location create(boolean inline) { - return new LocationImpl(inline); + return factory.create(inline); + } + + private interface Factory { + Location create(boolean inline); + } + + private static Factory createLocationFactory() { + try { + // On some platforms, like Android, the StackWalker APIs may not be + // available, in this case we have to fallback to Java 8 style of stack + // traversing. + Class.forName("java.lang.StackWalker"); + return new DefaultLocationFactory(); + } 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 DefaultLocationFactory implements Factory { + @Override + public Location create(boolean inline) { + return new LocationImpl(inline); + } } } From bbf381bddbe7f0451262163bd6687db6a85863fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Feb 2023 20:19:29 +0100 Subject: [PATCH 143/160] Bump kotlinVersion from 1.8.0 to 1.8.10 (#2897) Bumps `kotlinVersion` from 1.8.0 to 1.8.10. Updates `org.jetbrains.kotlin:kotlin-gradle-plugin` from 1.8.0 to 1.8.10 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/v1.8.10/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.8.0...v1.8.10) Updates `org.jetbrains.kotlin:kotlin-stdlib` from 1.8.0 to 1.8.10 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/v1.8.10/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.8.0...v1.8.10) --- 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 b08e20b591..4e5fd3aa8c 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.8.0" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10" } } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 45f0353e9b..70ddac0395 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -34,7 +34,7 @@ libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.4.0' libraries.groovy = 'org.codehaus.groovy:groovy:3.0.14' -def kotlinVersion = '1.8.0' +def kotlinVersion = '1.8.10' libraries.kotlin = [ version: kotlinVersion, From a2c85c5d7c611285e1abf512c81a0489ddf39830 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Feb 2023 20:19:45 +0100 Subject: [PATCH 144/160] Bump com.github.ben-manes.versions from 0.44.0 to 0.45.0 (#2895) Bumps com.github.ben-manes.versions from 0.44.0 to 0.45.0. --- updated-dependencies: - dependency-name: com.github.ben-manes.versions 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 4e5fd3aa8c..5a1a0c17cf 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ buildscript { plugins { id 'com.diffplug.spotless' version '6.14.0' id 'eclipse' - id 'com.github.ben-manes.versions' version '0.44.0' + id 'com.github.ben-manes.versions' version '0.45.0' id 'biz.aQute.bnd.builder' version '6.4.0' id 'ru.vyarus.animalsniffer' version '1.6.0' } From e9784550dc773c14efbddba8880f9d6750cb9f9b Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Thu, 2 Feb 2023 15:29:57 -0500 Subject: [PATCH 145/160] Set up Android Github Action pipeline (#2893) Fixes #2892 Signed-off-by: Andriy Redko --- .github/workflows/ci.yml | 32 +++++++++++++++++++ .../androidtest/BasicInstrumentedTests.kt | 1 - 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c57e59086..6ea135eac2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,6 +85,38 @@ jobs: chmod +x codecov ./codecov + # + # Android build job + # + android: + runs-on: macos-latest + if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')" + + # Definition of the build matrix + strategy: + matrix: + java: [ 11 ] + android-api: [ 26 ] + + # All build steps + steps: + - name: 1. Check out code + uses: actions/checkout@v3 + with: + fetch-depth: '0' # https://github.com/shipkit/shipkit-changelog#fetch-depth-on-ci + + - name: 2. Set up Java ${{ matrix.java }} + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: ${{ matrix.java }} + + - name: 3. Run Android tests on Java ${{ matrix.java }} with Android API level ${{ matrix.android-api }} + uses: reactivecircus/android-emulator-runner@v2 + with: + arch: x86_64 + api-level: ${{ matrix.android-api }} + script: ./gradlew :androidTest:connectedCheck --no-daemon --no-build-cache # # Release job, only for pushes to the main development branch diff --git a/subprojects/androidTest/src/androidTest/java/org/mockitousage/androidtest/BasicInstrumentedTests.kt b/subprojects/androidTest/src/androidTest/java/org/mockitousage/androidtest/BasicInstrumentedTests.kt index 2fa04c8bd6..07378171fc 100644 --- a/subprojects/androidTest/src/androidTest/java/org/mockitousage/androidtest/BasicInstrumentedTests.kt +++ b/subprojects/androidTest/src/androidTest/java/org/mockitousage/androidtest/BasicInstrumentedTests.kt @@ -35,7 +35,6 @@ class BasicInstrumentedTests { fun mockAndUseBasicClassUsingAnnotatedMock() { val basicClass = BasicOpenClassReceiver(mockedViaAnnotationBasicOpenClass) basicClass.callDependencyMethod() - throw Exception("java") } @Test From b31b95a967c5408a68266f1dac12ad73499f8466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp?= Date: Sat, 4 Feb 2023 12:30:29 +0000 Subject: [PATCH 146/160] Run Android tests on API level 33 for Java 11 compatibility testing (#2899) --- .github/workflows/ci.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ea135eac2..dc1edb0cb4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: if: matrix.java == 11 && matrix.entry.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB run: ./gradlew spotlessCheck - - name: 6. Build on Java ${{ matrix.java }} with ${{ matrix.entry.mock-maker }} and ${{ matrix.entry.member-accessor }} + - name: 6. Build on Java ${{ matrix.java }} with ${{ matrix.entry.mock-maker }} and ${{ matrix.entry.member-accessor }} run: ./gradlew build idea --scan env: MOCK_MAKER: ${{ matrix.entry.mock-maker }} @@ -95,8 +95,13 @@ jobs: # Definition of the build matrix strategy: matrix: - java: [ 11 ] - android-api: [ 26 ] + include: + # Minimum supported + - android-api: 26 + android-image-type: default + # Maximum available + - android-api: 33 + android-image-type: google_apis # All build steps steps: @@ -105,17 +110,18 @@ jobs: with: fetch-depth: '0' # https://github.com/shipkit/shipkit-changelog#fetch-depth-on-ci - - name: 2. Set up Java ${{ matrix.java }} + - name: 2. Set up Java 11 uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: ${{ matrix.java }} + java-version: 11 - - name: 3. Run Android tests on Java ${{ matrix.java }} with Android API level ${{ matrix.android-api }} + - name: 3. Run Android tests on Android API level ${{ matrix.android-api }} uses: reactivecircus/android-emulator-runner@v2 with: arch: x86_64 api-level: ${{ matrix.android-api }} + target: ${{ matrix.android-image-type }} script: ./gradlew :androidTest:connectedCheck --no-daemon --no-build-cache # From ebdca97dfbef6e8cf35944e9cb9244672d86e12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp?= Date: Sat, 4 Feb 2023 22:40:58 +0000 Subject: [PATCH 147/160] Simplify and modernize Android Test module. (#2894) --- build.gradle | 2 +- gradle/dependencies.gradle | 5 +- settings.gradle.kts | 4 +- subprojects/androidTest/androidTest.gradle | 30 ++-- subprojects/androidTest/proguard-rules.pro | 21 --- .../androidTest/src/main/AndroidManifest.xml | 9 +- .../drawable-v24/ic_launcher_foreground.xml | 30 ---- .../res/drawable/ic_launcher_background.xml | 170 ------------------ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3593 -> 0 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 5339 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2636 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 3388 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4926 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 7472 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 7909 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 11873 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 10652 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 16570 -> 0 bytes .../src/main/res/values-night/themes.xml | 16 -- .../src/main/res/values/colors.xml | 10 -- .../src/main/res/values/strings.xml | 6 + .../src/main/res/values/themes.xml | 16 -- 24 files changed, 27 insertions(+), 302 deletions(-) delete mode 100644 subprojects/androidTest/proguard-rules.pro delete mode 100644 subprojects/androidTest/src/main/res/drawable-v24/ic_launcher_foreground.xml delete mode 100644 subprojects/androidTest/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 subprojects/androidTest/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 subprojects/androidTest/src/main/res/mipmap-hdpi/ic_launcher_round.png delete mode 100644 subprojects/androidTest/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 subprojects/androidTest/src/main/res/mipmap-mdpi/ic_launcher_round.png delete mode 100644 subprojects/androidTest/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 subprojects/androidTest/src/main/res/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 subprojects/androidTest/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 subprojects/androidTest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 subprojects/androidTest/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 subprojects/androidTest/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png delete mode 100644 subprojects/androidTest/src/main/res/values-night/themes.xml delete mode 100644 subprojects/androidTest/src/main/res/values/colors.xml delete mode 100644 subprojects/androidTest/src/main/res/values/themes.xml diff --git a/build.gradle b/build.gradle index 5a1a0c17cf..3cdc35e1dc 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { classpath 'org.shipkit:shipkit-auto-version:1.2.2' classpath 'com.google.googlejavaformat:google-java-format:1.15.0' - classpath 'com.android.tools.build:gradle:4.2.0' + classpath 'com.android.tools.build:gradle:7.3.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10" } } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 70ddac0395..9bc1112947 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -43,9 +43,6 @@ libraries.kotlin = [ gradlePlugin: "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}", ] libraries.android = [ - ktx: 'androidx.core:core-ktx:1.8.0', - compat: 'androidx.appcompat:appcompat:1.4.2', - material: 'com.google.android.material:material:1.6.1', + runner: 'androidx.test:runner:1.5.2', junit: 'androidx.test.ext:junit:1.1.5', - espresso: 'androidx.test.espresso:espresso-core:3.5.1', ] diff --git a/settings.gradle.kts b/settings.gradle.kts index 551d0fa05f..5806de0315 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,7 +22,9 @@ include("subclass", "errorprone", "programmatic-test") -if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17) && (System.getenv("ANDROID_SDK_ROOT") != null || File(".local.properties").exists())) { +// https://developer.android.com/studio/command-line/variables#envar +// https://developer.android.com/studio/build#properties-files +if (System.getenv("ANDROID_HOME") != null || File("local.properties").exists()) { include("androidTest") } else { logger.info("Not including android test project due to missing SDK configuration") diff --git a/subprojects/androidTest/androidTest.gradle b/subprojects/androidTest/androidTest.gradle index 2176b5052b..941a52b9cc 100644 --- a/subprojects/androidTest/androidTest.gradle +++ b/subprojects/androidTest/androidTest.gradle @@ -6,25 +6,18 @@ plugins { } android { - compileSdkVersion 31 - buildToolsVersion "30.0.3" + namespace = "org.mockitousage.androidtest" + compileSdk = 33 defaultConfig { - applicationId "org.mockitousage.androidtest" - minSdkVersion 21 - targetSdkVersion 31 - versionCode 1 - versionName "1.0" + minSdk = 26 + targetSdk = 33 + versionCode = 1 + versionName = "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 @@ -34,20 +27,23 @@ android { } } +androidComponents { + beforeVariants(selector().withBuildType("release")) { + enable = false + } +} + apply from: "$rootDir/gradle/dependencies.gradle" dependencies { implementation libraries.kotlin.stdlib - implementation libraries.android.ktx - implementation libraries.android.compat - implementation libraries.android.material testImplementation project(":inline") testImplementation libraries.junit4 testImplementation libraries.junitJupiterApi testImplementation libraries.junitJupiterEngine + androidTestImplementation libraries.android.runner androidTestImplementation libraries.android.junit - androidTestImplementation libraries.android.espresso androidTestImplementation project(":android") } diff --git a/subprojects/androidTest/proguard-rules.pro b/subprojects/androidTest/proguard-rules.pro deleted file mode 100644 index f1b424510d..0000000000 --- a/subprojects/androidTest/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/subprojects/androidTest/src/main/AndroidManifest.xml b/subprojects/androidTest/src/main/AndroidManifest.xml index cc2901a874..955375b4e3 100644 --- a/subprojects/androidTest/src/main/AndroidManifest.xml +++ b/subprojects/androidTest/src/main/AndroidManifest.xml @@ -1,13 +1,10 @@ - + + android:supportsRtl="true" /> diff --git a/subprojects/androidTest/src/main/res/drawable-v24/ic_launcher_foreground.xml b/subprojects/androidTest/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 7706ab9e6d..0000000000 --- a/subprojects/androidTest/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - diff --git a/subprojects/androidTest/src/main/res/drawable/ic_launcher_background.xml b/subprojects/androidTest/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9cbf..0000000000 --- a/subprojects/androidTest/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 6b78462d61..0000000000 --- a/subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 6b78462d61..0000000000 --- a/subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/subprojects/androidTest/src/main/res/mipmap-hdpi/ic_launcher.png b/subprojects/androidTest/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index a571e60098c92c2baca8a5df62f2929cbff01b52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3593 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 P00000NkvXXu0mjfP=x?Y diff --git a/subprojects/androidTest/src/main/res/mipmap-hdpi/ic_launcher_round.png b/subprojects/androidTest/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 61da551c5594a1f9d26193983d2cd69189014603..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5339 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 diff --git a/subprojects/androidTest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/subprojects/androidTest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index b216f2d313cc673d8b8c4da591c174ebed52795c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11873 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 diff --git a/subprojects/androidTest/src/main/res/values-night/themes.xml b/subprojects/androidTest/src/main/res/values-night/themes.xml deleted file mode 100644 index 70198506d0..0000000000 --- a/subprojects/androidTest/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/subprojects/androidTest/src/main/res/values/colors.xml b/subprojects/androidTest/src/main/res/values/colors.xml deleted file mode 100644 index ca1931bca9..0000000000 --- a/subprojects/androidTest/src/main/res/values/colors.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF - diff --git a/subprojects/androidTest/src/main/res/values/strings.xml b/subprojects/androidTest/src/main/res/values/strings.xml index 9aab1293ac..33d20491a8 100644 --- a/subprojects/androidTest/src/main/res/values/strings.xml +++ b/subprojects/androidTest/src/main/res/values/strings.xml @@ -1,3 +1,9 @@ + + + Mockito Android Tests diff --git a/subprojects/androidTest/src/main/res/values/themes.xml b/subprojects/androidTest/src/main/res/values/themes.xml deleted file mode 100644 index d21572ebb6..0000000000 --- a/subprojects/androidTest/src/main/res/values/themes.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - From 67259f0ea04a2de003531cab2c2de12bbb612c06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 19:17:42 +0100 Subject: [PATCH 148/160] Bump versions.bytebuddy from 1.12.22 to 1.12.23 (#2901) Bumps `versions.bytebuddy` from 1.12.22 to 1.12.23. Updates `net.bytebuddy:byte-buddy` from 1.12.22 to 1.12.23 - [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.22...byte-buddy-1.12.23) Updates `net.bytebuddy:byte-buddy-agent` from 1.12.22 to 1.12.23 - [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.22...byte-buddy-1.12.23) Updates `net.bytebuddy:byte-buddy-android` from 1.12.22 to 1.12.23 - [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.22...byte-buddy-1.12.23) --- 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 9bc1112947..a6e00918c4 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.12.22' +versions.bytebuddy = '1.12.23' versions.junitJupiter = '5.9.2' versions.errorprone = '2.18.0' From 10515bcd940c405ec058d7fb82d729c57bab2384 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 22:55:15 +0100 Subject: [PATCH 149/160] Bump com.diffplug.spotless from 6.14.0 to 6.14.1 (#2902) Bumps com.diffplug.spotless from 6.14.0 to 6.14.1. --- updated-dependencies: - dependency-name: com.diffplug.spotless 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 3cdc35e1dc..06ee2f7255 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.14.0' + id 'com.diffplug.spotless' version '6.14.1' id 'eclipse' id 'com.github.ben-manes.versions' version '0.45.0' id 'biz.aQute.bnd.builder' version '6.4.0' From a8bac5e24f99302704c06a5e23ba281741055363 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Feb 2023 07:38:47 +0100 Subject: [PATCH 150/160] Bump ru.vyarus.animalsniffer from 1.6.0 to 1.7.0 (#2911) Bumps ru.vyarus.animalsniffer from 1.6.0 to 1.7.0. --- updated-dependencies: - dependency-name: ru.vyarus.animalsniffer 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 06ee2f7255..d4228df562 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ plugins { id 'eclipse' id 'com.github.ben-manes.versions' version '0.45.0' id 'biz.aQute.bnd.builder' version '6.4.0' - id 'ru.vyarus.animalsniffer' version '1.6.0' + id 'ru.vyarus.animalsniffer' version '1.7.0' } description = 'Mockito mock objects library core API and implementation' From 6b9fc8c8b6a9f61f4dd28a3fd22ba7ba1a44a389 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Feb 2023 08:04:39 +0100 Subject: [PATCH 151/160] Bump org.codehaus.groovy:groovy from 3.0.14 to 3.0.15 (#2910) Bumps [org.codehaus.groovy:groovy](https://github.com/apache/groovy) from 3.0.14 to 3.0.15. - [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 a6e00918c4..65bf957de4 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.200' libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.4.0' -libraries.groovy = 'org.codehaus.groovy:groovy:3.0.14' +libraries.groovy = 'org.codehaus.groovy:groovy:3.0.15' def kotlinVersion = '1.8.10' libraries.kotlin = [ From 38b1f4438be49c7fd1ab33dec1a5cdd4061735f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Feb 2023 21:53:24 +0100 Subject: [PATCH 152/160] Bump versions.bytebuddy from 1.12.23 to 1.13.0 (#2912) Bumps `versions.bytebuddy` from 1.12.23 to 1.13.0. Updates `net.bytebuddy:byte-buddy` from 1.12.23 to 1.13.0 - [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.23...byte-buddy-1.13.0) Updates `net.bytebuddy:byte-buddy-agent` from 1.12.23 to 1.13.0 - [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.23...byte-buddy-1.13.0) Updates `net.bytebuddy:byte-buddy-android` from 1.12.23 to 1.13.0 - [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.23...byte-buddy-1.13.0) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: net.bytebuddy:byte-buddy-agent dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: net.bytebuddy:byte-buddy-android 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 65bf957de4..991067a87e 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.12.23' +versions.bytebuddy = '1.13.0' versions.junitJupiter = '5.9.2' versions.errorprone = '2.18.0' From 87ee3875e2a457a5c0e1083b4cafbfe91243c260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp?= Date: Sat, 11 Feb 2023 10:35:58 +0000 Subject: [PATCH 153/160] Fix most Gradle warnings in build (#2904) --- build.gradle | 7 +++---- gradle/java-library.gradle | 4 ++-- gradle/java-publication.gradle | 2 +- gradle/mockito-core/osgi.gradle | 2 +- gradle/root/coverage.gradle | 4 ++-- subprojects/junit-jupiter/junit-jupiter.gradle | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index d4228df562..c4e8b1f956 100644 --- a/build.gradle +++ b/build.gradle @@ -82,6 +82,9 @@ allprojects { proj -> configurations { testUtil //TODO move to separate project + // Putting 'provided' dependencies on test compile and runtime classpath. + testCompileOnly.extendsFrom(compileOnly) + testRuntimeOnly.extendsFrom(compileOnly) } dependencies { @@ -92,10 +95,6 @@ dependencies { testImplementation libraries.assertj - //putting 'provided' dependencies on test compile and runtime classpath - testCompileOnly configurations.compileOnly - testRuntimeOnly configurations.compileOnly - testUtil sourceSets.test.output signature 'org.codehaus.mojo.signature:java18:1.0@signature' diff --git a/gradle/java-library.gradle b/gradle/java-library.gradle index 5e91b74443..d6a9c175b8 100644 --- a/gradle/java-library.gradle +++ b/gradle/java-library.gradle @@ -30,8 +30,8 @@ apply from: "$rootDir/gradle/retry-test.gradle" tasks.withType(Checkstyle) { reports { - xml.enabled false - html.enabled true + xml.required = false + html.required = true html.stylesheet resources.text.fromFile("$rootDir/config/checkstyle/checkstyle.xsl") } } diff --git a/gradle/java-publication.gradle b/gradle/java-publication.gradle index 8c0b255d7e..4fe0103dfb 100644 --- a/gradle/java-publication.gradle +++ b/gradle/java-publication.gradle @@ -58,7 +58,7 @@ publishing { //Gradle does not write 'jar' packaging to the pom (unlike other packaging types). //This is OK because 'jar' is implicit/default: // https://maven.apache.org/guides/introduction/introduction-to-the-pom.html#minimal-pom - packaging = project.tasks.jar.extension + packaging = project.tasks.jar.archiveExtension.get() } url = "https://github.com/mockito/mockito" diff --git a/gradle/mockito-core/osgi.gradle b/gradle/mockito-core/osgi.gradle index 8dbf3e9063..f950f98de2 100644 --- a/gradle/mockito-core/osgi.gradle +++ b/gradle/mockito-core/osgi.gradle @@ -8,7 +8,7 @@ jar { 'Bundle-SymbolicName': 'org.mockito.mockito-core', 'Bundle-Version': "\${version_cleanup;${project.version}}", '-versionpolicy': '[${version;==;${@}},${version;+;${@}})', - 'Export-Package': "org.mockito.internal.*;status=INTERNAL;mandatory:=status;version=${version},org.mockito.*;version=${version}", + 'Export-Package': "org.mockito.internal.*;status=INTERNAL;mandatory:=status;version=${archiveVersion.get()},org.mockito.*;version=${archiveVersion.get()}", 'Import-Package': [ 'net.bytebuddy.*;version="[1.6.0,2.0)"', 'junit.*;resolution:=optional', diff --git a/gradle/root/coverage.gradle b/gradle/root/coverage.gradle index cd48775763..4b9b041113 100644 --- a/gradle/root/coverage.gradle +++ b/gradle/root/coverage.gradle @@ -35,8 +35,8 @@ task mockitoCoverage(type: JacocoReport) { } reports { - xml.enabled true - html.enabled true + xml.required = true + html.required = true html.destination file("${buildDir}/jacocoHtml") } diff --git a/subprojects/junit-jupiter/junit-jupiter.gradle b/subprojects/junit-jupiter/junit-jupiter.gradle index 99f63e75d5..cbd25d34ab 100644 --- a/subprojects/junit-jupiter/junit-jupiter.gradle +++ b/subprojects/junit-jupiter/junit-jupiter.gradle @@ -25,7 +25,7 @@ jar { 'Bundle-SymbolicName': 'org.mockito.junit-jupiter', 'Bundle-Version': "\${version_cleanup;${project.version}}", '-versionpolicy': '[${version;==;${@}},${version;+;${@}})', - 'Export-Package': "org.mockito.junit.jupiter.*;version=${version}", + 'Export-Package': "org.mockito.junit.jupiter.*;version=${archiveVersion.get()}", '-removeheaders': 'Private-Package', 'Automatic-Module-Name': 'org.mockito.junit.jupiter', '-noextraheaders': 'true', From d6e39b8c1e48f4c23164ef65c5694657e65f3fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp?= Date: Sat, 11 Feb 2023 10:43:24 +0000 Subject: [PATCH 154/160] Improve Android 33 API CI job (#2903) Improvements for #2892 --- .github/workflows/ci.yml | 60 ++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc1edb0cb4..d04261f8ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,17 +91,13 @@ jobs: android: runs-on: macos-latest if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')" + timeout-minutes: 30 # Definition of the build matrix strategy: matrix: - include: - # Minimum supported - - android-api: 26 - android-image-type: default - # Maximum available - - android-api: 33 - android-image-type: google_apis + # Minimum supported and maximum available. + android-api: [ 26, 33 ] # All build steps steps: @@ -121,8 +117,54 @@ jobs: with: arch: x86_64 api-level: ${{ matrix.android-api }} - target: ${{ matrix.android-image-type }} - script: ./gradlew :androidTest:connectedCheck --no-daemon --no-build-cache + # Workaround for https://issuetracker.google.com/issues/267458959 + target: ${{ matrix.android-api >= 32 && 'google_apis' || 'default' }} + # Workaround for https://github.com/ReactiveCircus/android-emulator-runner/issues/160, but newer version. + emulator-build: 9322596 # 31.3.14.0 got it via `emulator -version` + # Override default "-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim" + # See emulator manual for reference: https://developer.android.com/studio/run/emulator-commandline + emulator-options: > + -no-window + -gpu swiftshader_indirect + -no-snapshot + -noaudio + -no-boot-anim + -camera-back none + -camera-front none + # See logcat manual for reference: https://developer.android.com/studio/command-line/logcat + script: | + # Capture logcat output from "Launch Emulator" to a file. + adb logcat -d > emulator-startup.log + # Shorten the logcat output, by truncating at this point, the relevant part is yet to come. + # Best effort, could fail with "failed to clear the 'main' log", + # because something is locking logcat, so try a few times, and ignore errors each time. + adb logcat --clear || true + adb logcat --clear || true + adb logcat --clear || true + # Capture full logcat output to a file. + adb logcat > emulator.log & echo $! > logcat_file.pid + # Output instrumentation test logs to the GitHub Actions output. + adb logcat "*:S MonitoringInstr:V AndroidJUnitRunner:V TestRequestBuilder:V TestExecutor:V TestRunner:V" --format=color & echo $! > logcat_console.pid + + echo 0 > gradle.exit # Set a default exit code. + # Run the actual tests (suppress build failures by saving the exit code). + ./gradlew :androidTest:connectedCheck --no-daemon --no-build-cache || echo $? > gradle.exit + + # Stop capturing logcat output. + kill $(cat logcat_file.pid) || echo "::warning file=.github/workflows/ci.yml::Logcat process $(cat logcat_file.pid) didn't exist." + kill $(cat logcat_console.pid) || echo "::warning file=.github/workflows/ci.yml::Logcat process $(cat logcat_console.pid) didn't exist." + # Make sure the step fails if the tests failed. + exit $(cat gradle.exit) + + - name: 4. Upload artifact "androidTest-results-${{ matrix.android-api }}" + if: success() || failure() + uses: actions/upload-artifact@v3 + with: + name: androidTest-results-${{ matrix.android-api }} + path: | + ${{ github.workspace }}/subprojects/androidTest/build/reports/androidTests/connected/** + ${{ github.workspace }}/emulator.log + ${{ github.workspace }}/emulator-startup.log # # Release job, only for pushes to the main development branch From 51535267e574cc80fac453aff25e56c7bc21e41b Mon Sep 17 00:00:00 2001 From: Roberto Trunfio Date: Sat, 11 Feb 2023 15:08:26 +0100 Subject: [PATCH 155/160] Fix mocking of ThreadLocal (#2908) Fixes #2905 Co-authored-by: Roberto Trunfio --- .../creation/bytebuddy/MockMethodAdvice.java | 2 +- .../mockitousage/bugs/ThreadLocalTest.java | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/mockitousage/bugs/ThreadLocalTest.java 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 1dd744171f..6074eef038 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java @@ -168,7 +168,7 @@ public boolean isMock(Object instance) { @Override public boolean isMocked(Object instance) { - return selfCallInfo.checkSelfCall(instance) && isMock(instance); + return isMock(instance) && selfCallInfo.checkSelfCall(instance); } @Override diff --git a/src/test/java/org/mockitousage/bugs/ThreadLocalTest.java b/src/test/java/org/mockitousage/bugs/ThreadLocalTest.java new file mode 100644 index 0000000000..8c113d90a6 --- /dev/null +++ b/src/test/java/org/mockitousage/bugs/ThreadLocalTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitousage.bugs; + +import static org.mockito.Mockito.RETURNS_MOCKS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.mockitoutil.TestBase; + +/** + * This was an issue reported in #2905. Mocking {@link ThreadLocal} or classes extending {@link ThreadLocal} was + * throwing a {@link StackOverflowError}. + */ +public class ThreadLocalTest extends TestBase { + + @Test + public void mock_ThreadLocal_does_not_raise_StackOverflowError() { + StackOverflowError stackOverflowError = + Assertions.catchThrowableOfType( + () -> { + mock(ThreadLocal.class, RETURNS_MOCKS); + }, + StackOverflowError.class); + Assertions.assertThat(stackOverflowError).isNull(); + } + + @Test + public void mock_class_extending_ThreadLocal_does_not_raise_StackOverflowError() { + StackOverflowError stackOverflowError = + Assertions.catchThrowableOfType( + () -> { + mock(SomeThreadLocal.class, RETURNS_MOCKS); + }, + StackOverflowError.class); + Assertions.assertThat(stackOverflowError).isNull(); + } + + @Test + public void spy_ThreadLocal_does_not_raise_StackOverflowError() { + StackOverflowError stackOverflowError = + Assertions.catchThrowableOfType( + () -> { + spy(ThreadLocal.class); + }, + StackOverflowError.class); + Assertions.assertThat(stackOverflowError).isNull(); + } + + @Test + public void spy_class_extending_ThreadLocal_does_not_raise_StackOverflowError() { + StackOverflowError stackOverflowError = + Assertions.catchThrowableOfType( + () -> { + spy(SomeThreadLocal.class); + }, + StackOverflowError.class); + Assertions.assertThat(stackOverflowError).isNull(); + } + + static class SomeThreadLocal extends ThreadLocal {} +} From c5ae36062282cbbafff4dd72f5aaf13f2304d697 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Feb 2023 23:53:47 +0100 Subject: [PATCH 156/160] Bump com.diffplug.spotless from 6.14.1 to 6.15.0 (#2913) Bumps com.diffplug.spotless from 6.14.1 to 6.15.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 c4e8b1f956..9d07e8d2b0 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.14.1' + id 'com.diffplug.spotless' version '6.15.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.45.0' id 'biz.aQute.bnd.builder' version '6.4.0' From 4c79e06e97f6c76b717cc4ff70a5835b573e83bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Feb 2023 22:27:42 +0100 Subject: [PATCH 157/160] Bump gradle/wrapper-validation-action from 1.0.5 to 1.0.6 (#2917) Bumps [gradle/wrapper-validation-action](https://github.com/gradle/wrapper-validation-action) from 1.0.5 to 1.0.6. - [Release notes](https://github.com/gradle/wrapper-validation-action/releases) - [Commits](https://github.com/gradle/wrapper-validation-action/compare/v1.0.5...v1.0.6) --- updated-dependencies: - dependency-name: gradle/wrapper-validation-action 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> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d04261f8ca..1bef031588 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,7 +54,7 @@ jobs: - name: 3. Validate Gradle wrapper if: matrix.java == 11 && matrix.entry.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB - uses: gradle/wrapper-validation-action@v1.0.5 # https://github.com/gradle/wrapper-validation-action + uses: gradle/wrapper-validation-action@v1.0.6 # https://github.com/gradle/wrapper-validation-action - name: 4. Build and check reproducibility of artifacts (single job only) if: matrix.java == 11 && matrix.entry.mock-maker == 'mock-maker-default' # SINGLE-MATRIX-JOB From 60b0e7148be08dc1ae208b73d4ff5cb07424bbbd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Feb 2023 22:28:02 +0100 Subject: [PATCH 158/160] Bump versions.bytebuddy from 1.13.0 to 1.14.0 (#2918) Bumps `versions.bytebuddy` from 1.13.0 to 1.14.0. Updates `net.bytebuddy:byte-buddy` from 1.13.0 to 1.14.0 - [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.13.0...byte-buddy-1.14.0) Updates `net.bytebuddy:byte-buddy-agent` from 1.13.0 to 1.14.0 - [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.13.0...byte-buddy-1.14.0) Updates `net.bytebuddy:byte-buddy-android` from 1.13.0 to 1.14.0 - [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.13.0...byte-buddy-1.14.0) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: net.bytebuddy:byte-buddy-agent dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: net.bytebuddy:byte-buddy-android 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 991067a87e..bf58198b3c 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.13.0' +versions.bytebuddy = '1.14.0' versions.junitJupiter = '5.9.2' versions.errorprone = '2.18.0' From d9373348aab69203f9b38ee7b072bc186da34910 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 21:18:04 +0100 Subject: [PATCH 159/160] Bump com.github.ben-manes.versions from 0.45.0 to 0.46.0 (#2922) Bumps com.github.ben-manes.versions from 0.45.0 to 0.46.0. --- updated-dependencies: - dependency-name: com.github.ben-manes.versions 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 9d07e8d2b0..ac1a7d5dd6 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ buildscript { plugins { id 'com.diffplug.spotless' version '6.15.0' id 'eclipse' - id 'com.github.ben-manes.versions' version '0.45.0' + id 'com.github.ben-manes.versions' version '0.46.0' id 'biz.aQute.bnd.builder' version '6.4.0' id 'ru.vyarus.animalsniffer' version '1.7.0' } From 4eb275c0dba9d933980e2d72c09f9429b74bacf6 Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Tue, 21 Feb 2023 22:05:06 +0100 Subject: [PATCH 160/160] Make project relocatable by using relative paths in the OSGi test task (#2920) Fixes #2919 Co-authored-by: Iulian Dragos Co-authored-by: Nelson Osacky --- subprojects/osgi-test/osgi-test.gradle | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/subprojects/osgi-test/osgi-test.gradle b/subprojects/osgi-test/osgi-test.gradle index b1d985b1f6..0861fd7f46 100644 --- a/subprojects/osgi-test/osgi-test.gradle +++ b/subprojects/osgi-test/osgi-test.gradle @@ -30,10 +30,31 @@ dependencies { } test { + jvmArgumentProviders.add( + new RuntimeBundlesProvider(files: configurations.testRuntimeBundles.asFileTree) + ) dependsOn configurations.testRuntimeBundles inputs.files(sourceSets.testBundle.allSource) + .withPathSensitivity(PathSensitivity.RELATIVE) + .withPropertyName('testBundleSources') inputs.files(sourceSets.otherBundle.allSource) - systemProperty 'testRuntimeBundles', configurations.testRuntimeBundles.asPath + .withPathSensitivity(PathSensitivity.RELATIVE) + .withPropertyName('otherBundleSources') useJUnit() } +/** + * A helper class to pass classpath elements as relative paths. This allows the build + * to be checked out in different locations on the file system and still hit the cache. + */ +class RuntimeBundlesProvider implements CommandLineArgumentProvider { + @InputFiles + @PathSensitive(PathSensitivity.RELATIVE) + FileTree files + + @Override + Iterable asArguments() { + String[] absolutePaths = files.stream().map {it.absolutePath}.toArray() + ["-DtestRuntimeBundles=${CollectionUtils.join(File.pathSeparator, absolutePaths)}".toString()] + } +}