diff options
author | Garfield Tan <xutan@google.com> | 2019-09-10 18:22:17 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-09-10 18:22:17 -0700 |
commit | b6817da86933edf615bee1cbac2fdf5e04a6b4e5 (patch) | |
tree | 188df979a65f5480cf9ad22b6890b9c8e3484466 /src/main | |
parent | efeed0526d5f5bd76d05a510031d05599ab68abb (diff) | |
parent | 3a0e7c2c89c04a873833dc22da500194b5b6b337 (diff) | |
download | mockito-b6817da86933edf615bee1cbac2fdf5e04a6b4e5.tar.gz |
Merge changes from topic "mockito-dexmaker-update-revert" am: ae3172e5fc
am: 3a0e7c2c89
Change-Id: I2450d9ee7788af230d92b402408875fc74fbd422
Diffstat (limited to 'src/main')
5 files changed, 149 insertions, 2 deletions
diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 2d06e58..4058340 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -103,7 +103,8 @@ import org.mockito.verification.VerificationWithTimeout; * <a href="#43">43. New API for integrations: <code>MockitoSession</code> is usable by testing frameworks (Since 2.15.+)</a><br/> * <a href="#44">44. Deprecated <code>org.mockito.plugins.InstantiatorProvider</code> as it was leaking internal API. it was replaced by <code>org.mockito.plugins.InstantiatorProvider2 (Since 2.15.4)</code></a><br/> * <a href="#45">45. New JUnit Jupiter (JUnit5+) extension</a><br/> - * <a href="#46">46. New <code>Mockito.lenient()</code> and <code>MockSettings.lenient()</code> methods (Since 2.20.0</a><br/> + * <a href="#46">46. New <code>Mockito.lenient()</code> and <code>MockSettings.lenient()</code> methods (Since 2.20.0)</a><br/> + * <a href="#47">47. New API for clearing mock state in inline mocking (Since 2.25.0)</a><br/> * </b> * * <h3 id="0">0. <a class="meaningful_link" href="#mockito2" name="mockito2">Migrating to Mockito 2</a></h3> @@ -1532,6 +1533,15 @@ import org.mockito.verification.VerificationWithTimeout; * * For more information refer to {@link Mockito#lenient()}. * Let us know how do you find the new feature by opening a GitHub issue to discuss! + * + * <h3 id="47">47. <a class="meaningful_link" href="#clear_inline_mocks" name="clear_inline_mocks">New API for clearing mock state in inline mocking (Since 2.25.0)</a></h3> + * + * In certain specific, rare scenarios (issue <a href="https://github.com/mockito/mockito/pull/1619">#1619</a>) + * inline mocking causes memory leaks. + * There is no clean way to mitigate this problem completely. + * Hence, we introduced a new API to explicitly clear mock state (only make sense in inline mocking!). + * See example usage in {@link MockitoFramework#clearInlineMocks()}. + * If you have feedback or a better idea how to solve the problem please reach out. */ @SuppressWarnings("unchecked") public class Mockito extends ArgumentMatchers { diff --git a/src/main/java/org/mockito/MockitoFramework.java b/src/main/java/org/mockito/MockitoFramework.java index 5ffe272..58cd4b6 100644 --- a/src/main/java/org/mockito/MockitoFramework.java +++ b/src/main/java/org/mockito/MockitoFramework.java @@ -92,4 +92,55 @@ public interface MockitoFramework { */ @Incubating InvocationFactory getInvocationFactory(); + + /** + * 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. + * <p> + * This method is useful to tackle subtle memory leaks that are possible due to the nature of inline mocking + * (issue <a href="https://github.com/mockito/mockito/pull/1619">#1619</a>). + * If you are facing those problems, call this method at the end of the test (or in "@After" method). + * See examples of using "clearInlineMocks" in Mockito test code. + * To find out why inline mock maker keeps track of the mock objects see {@link org.mockito.plugins.InlineMockMaker}. + * <p> + * Mockito's "inline mocking" enables mocking final types, enums and final methods + * (read more in section 39 of {@link Mockito} javadoc). + * This method is only meaningful when {@link org.mockito.plugins.InlineMockMaker} is in use. + * If you're using a different {@link org.mockito.plugins.MockMaker} then this method is a no-op. + * + * <pre class="code"><code class="java"> + * public class ExampleTest { + * + * @After + * public void clearMocks() { + * Mockito.framework().clearInlineMocks(); + * } + * + * @Test + * public void someTest() { + * ... + * } + * } + * </pre> + * + * If you have feedback or a better idea how to solve the problem please reach out. + * + * @since 2.25.0 + * @see #clearInlineMock(Object) + */ + @Incubating + void clearInlineMocks(); + + /** + * Clears up internal state of specific inline mock. + * This method is a single-mock variant of {@link #clearInlineMocks()}. + * Please read javadoc for {@link #clearInlineMocks()}. + * + * @param mock to clear up + * @since 2.25.0 + * @see #clearInlineMocks() + */ + @Incubating + void clearInlineMock(Object mock); } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java index 42f10ce..dfe2061 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java @@ -14,6 +14,7 @@ import org.mockito.internal.util.Platform; import org.mockito.internal.util.concurrent.WeakConcurrentMap; import org.mockito.invocation.MockHandler; import org.mockito.mock.MockCreationSettings; +import org.mockito.plugins.InlineMockMaker; import java.io.File; import java.io.FileOutputStream; @@ -87,7 +88,7 @@ import static org.mockito.internal.util.StringUtil.join; * support this feature. */ @Incubating -public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker { +public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker, InlineMockMaker { private static final Instrumentation INSTRUMENTATION; @@ -276,6 +277,16 @@ public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker { } @Override + public void clearMock(Object mock) { + mocks.remove(mock); + } + + @Override + public void clearAllMocks() { + mocks.clear(); + } + + @Override public TypeMockability isTypeMockable(final Class<?> type) { return new TypeMockability() { @Override diff --git a/src/main/java/org/mockito/internal/framework/DefaultMockitoFramework.java b/src/main/java/org/mockito/internal/framework/DefaultMockitoFramework.java index 69a733c..d92fc28 100644 --- a/src/main/java/org/mockito/internal/framework/DefaultMockitoFramework.java +++ b/src/main/java/org/mockito/internal/framework/DefaultMockitoFramework.java @@ -10,6 +10,8 @@ import org.mockito.internal.invocation.DefaultInvocationFactory; import org.mockito.internal.util.Checks; import org.mockito.invocation.InvocationFactory; import org.mockito.listeners.MockitoListener; +import org.mockito.plugins.InlineMockMaker; +import org.mockito.plugins.MockMaker; import org.mockito.plugins.MockitoPlugins; import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress; @@ -37,4 +39,25 @@ public class DefaultMockitoFramework implements MockitoFramework { public InvocationFactory getInvocationFactory() { return new DefaultInvocationFactory(); } + + private InlineMockMaker getInlineMockMaker() { + MockMaker mockMaker = Plugins.getMockMaker(); + return (mockMaker instanceof InlineMockMaker) ? (InlineMockMaker) mockMaker : null; + } + + @Override + public void clearInlineMocks() { + InlineMockMaker mockMaker = getInlineMockMaker(); + if (mockMaker != null) { + mockMaker.clearAllMocks(); + } + } + + @Override + public void clearInlineMock(Object mock) { + InlineMockMaker mockMaker = getInlineMockMaker(); + if (mockMaker != null) { + mockMaker.clearMock(mock); + } + } } diff --git a/src/main/java/org/mockito/plugins/InlineMockMaker.java b/src/main/java/org/mockito/plugins/InlineMockMaker.java new file mode 100644 index 0000000..8771aa7 --- /dev/null +++ b/src/main/java/org/mockito/plugins/InlineMockMaker.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Mockito contributors + * This program is made available under the terms of the MIT License. + */ + +package org.mockito.plugins; + +import org.mockito.Incubating; +import org.mockito.MockitoFramework; + +/** + * Extension to {@link MockMaker} for mock makers that changes inline method implementations + * and need keep track of created mock objects. + * <p> + * Mockito's default inline mock maker keeps track of created mock objects via weak reference map. + * This poses a risk of memory leaks in certain scenarios + * (issue <a href="https://github.com/mockito/mockito/pull/1619">#1619</a>). + * There is no clean way to tackle those problems at the moment. + * Hence, {@code InlineMockMaker} interface exposes methods to explicitly clear mock references. + * Those methods are called by {@link MockitoFramework#clearInlineMocks()}. + * When the user encounters a leak, he can mitigate the problem with {@link MockitoFramework#clearInlineMocks()}. + * <p> + * {@code InlineMockMaker} is for expert users and framework integrators, when custom inline mock maker is in use. + * If you have a custom {@link MockMaker} that keeps track of mock objects, + * please have your mock maker implement {@code InlineMockMaker} interface. + * This way, it can participate in {@link MockitoFramework#clearInlineMocks()} API. + * + * @since 2.25.0 + */ +@Incubating +public interface InlineMockMaker extends MockMaker { + + /** + * Clean up internal state for specified {@code mock}. You may assume there won't be any interaction to the specific + * mock after this is called. + * + * @param mock the mock instance whose internal state is to be cleaned. + * @since 2.25.0 + */ + @Incubating + void clearMock(Object mock); + + /** + * Cleans up internal state for all existing mocks. You may assume there won't be any interaction to mocks created + * previously after this is called. + * + * @since 2.25.0 + */ + @Incubating + void clearAllMocks(); + +} |