1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.adservices.mockito; 17 18 import static com.android.adservices.shared.testing.TestHelper.getAnnotation; 19 import static com.android.adservices.shared.testing.TestHelper.getTestName; 20 21 import android.util.Log; 22 23 import org.junit.rules.TestRule; 24 import org.junit.runner.Description; 25 import org.junit.runners.model.Statement; 26 import org.mockito.Mockito; 27 28 import java.lang.annotation.ElementType; 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 import java.lang.annotation.Target; 32 import java.util.Objects; 33 34 // TODO(b/315339283): move to shared infra or module-utils (and in the case of the latter, 35 // refactor the ExtendedMockito.Builder.dontClearInlineMocks() to something like 36 // setClearInlineMocks(Mode)) 37 // TODO(b/315339283): add unit tests 38 /** 39 * Rule used to clear Mockito inline mocks after a test is run. 40 * 41 * <p>Typically used as a {@code ClassRule} in together with {@link AdServicesExtendedMockitoRule}. 42 */ 43 public final class ExtendedMockitoInlineCleanerRule implements TestRule { 44 45 private static final String TAG = ExtendedMockitoInlineCleanerRule.class.getSimpleName(); 46 47 private final Mode mDefaultMode; 48 ExtendedMockitoInlineCleanerRule()49 public ExtendedMockitoInlineCleanerRule() { 50 this(Mode.DONT_CLEAR_AT_ALL); 51 } 52 ExtendedMockitoInlineCleanerRule(Mode defaultMode)53 public ExtendedMockitoInlineCleanerRule(Mode defaultMode) { 54 mDefaultMode = Objects.requireNonNull(defaultMode); 55 } 56 57 @Override apply(Statement base, Description description)58 public Statement apply(Statement base, Description description) { 59 return new Statement() { 60 61 @Override 62 public void evaluate() throws Throwable { 63 try { 64 base.evaluate(); 65 } finally { 66 clearInlineMocksAfterTest(description); 67 } 68 } 69 }; 70 } 71 72 private Mode getMode(Description description) { 73 Mode mode = mDefaultMode; 74 String testName = getTestName(description); 75 ClearInlineMocksMode annotation = getAnnotation(description, ClearInlineMocksMode.class); 76 if (annotation != null) { 77 mode = annotation.value(); 78 Log.v(TAG, "getMode(" + testName + "): returning mode from annotation (" + mode + ")"); 79 } 80 Log.v(TAG, "getMode(" + testName + "): returning default mode (" + mode + ")"); 81 return mode; 82 } 83 84 private void clearInlineMocksAfterTest(Description description) { 85 Mode mode = getMode(description); 86 boolean shouldClear = shouldClearInlineMocksAfterTest(description, mode); 87 Log.d( 88 TAG, 89 "clearInlineMocksAfterTest(" 90 + getTestName(description) 91 + "): mode=" 92 + mode 93 + ", shouldClear=" 94 + shouldClear); 95 if (shouldClear) { 96 clearInlineMocks(); 97 } 98 } 99 100 private void clearInlineMocks() { 101 Log.i(TAG, "Calling Mockito.framework().clearInlineMocks()"); 102 Mockito.framework().clearInlineMocks(); 103 } 104 105 /** 106 * Gets whether the inline mocks should be cleared after the given test, based on whether the 107 * test represents a method or class and the given mode. 108 */ 109 public static boolean shouldClearInlineMocksAfterTest(Description description, Mode mode) { 110 switch (mode) { 111 case DONT_CLEAR_AT_ALL: 112 return false; 113 case CLEAR_AFTER_TEST_CLASS: 114 return description.isSuite(); 115 case CLEAR_AFTER_TEST_METHOD: 116 return !description.isSuite(); 117 default: 118 throw new IllegalArgumentException("unsupported mode: " + mode); 119 } 120 } 121 122 /** Defines when the inline mocks should be cleared. */ 123 public enum Mode { 124 DONT_CLEAR_AT_ALL, 125 CLEAR_AFTER_TEST_METHOD, 126 CLEAR_AFTER_TEST_CLASS 127 } 128 129 /** Annotation used to defines when the inline mocks should be cleared. */ 130 @Retention(RetentionPolicy.RUNTIME) 131 @Target({ElementType.METHOD, ElementType.TYPE}) 132 public @interface ClearInlineMocksMode { 133 Mode value(); 134 } 135 } 136