1 /* 2 * Copyright (C) 2024 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.shared.testing; 17 18 import android.annotation.CallSuper; 19 import android.content.Context; 20 import android.platform.test.ravenwood.RavenwoodRule; 21 22 import androidx.test.platform.app.InstrumentationRegistry; 23 24 import com.android.adservices.shared.testing.Logger.LogLevel; 25 26 import org.junit.Before; 27 import org.junit.BeforeClass; 28 import org.junit.ClassRule; 29 import org.junit.Rule; 30 import org.junit.Test; 31 32 /** 33 * Superclass for all device-side tests, it contains just the bare minimum features used by all 34 * tests. 35 */ 36 public abstract class DeviceSideTestCase extends SidelessTestCase { 37 38 private static final String TAG = DeviceSideTestCase.class.getSimpleName(); 39 40 private static final String REASON_SESSION_MANAGED_BY_RULE = 41 "mockito session is automatically managed by a @Rule"; 42 private static final String REASON_NO_TARGET_CONTEXT = 43 "tests should use mContext instead - if it needs the target context, please add to" 44 + " DeviceSideTestCase instead"; 45 46 // TODO(b/335935200): This (and RavenwoodConfig) should be removed once Ravenwood starts 47 // using the package name from the build file. 48 private static final String RAVENWOOD_PACKAGE_NAME = "com.android.adservices.shared.tests"; 49 50 /** {@code logcat} tag. */ 51 protected final String mTag = getClass().getSimpleName(); 52 53 // NOTE: references below CANNOT be set when declared as the call to InstrumentationRegistry 54 // would fail when running on host / under Ravenwood 55 56 /** 57 * @deprecated use {@link #mContext} 58 */ 59 @Deprecated protected static Context sContext; 60 61 /** 62 * Package name of the app being instrumented. 63 * 64 * @deprecated use {@link #mPackageName} instead. 65 */ 66 @Deprecated protected static String sPackageName; 67 68 /** 69 * Reference to the context of this test's instrumentation package (as defined by {@link 70 * android.app.Instrumentation#getContext()}) 71 */ 72 protected Context mContext; 73 74 /** 75 * Package name of this test's instrumentation package (as defined by {@link 76 * android.app.Instrumentation#getContext()}) 77 */ 78 protected String mPackageName; 79 80 @ClassRule 81 public static final RavenwoodRule sRavenwood = 82 new RavenwoodRule.Builder() 83 .setProvideMainThread(true) 84 .setPackageName(RAVENWOOD_PACKAGE_NAME) 85 .build(); 86 87 // TODO(b/342639109): make sure it's the right order 88 @Rule(order = 0) 89 public final SdkLevelSupportRule sdkLevel = SdkLevelSupportRule.forAnyLevel(); 90 91 // TODO(b/342639109): set order 92 @Rule 93 public final ProcessLifeguardRule processLifeguard = 94 new ProcessLifeguardRule(ProcessLifeguardRule.Mode.IGNORE); 95 96 @BeforeClass setStaticFixtures()97 public static void setStaticFixtures() { 98 if (sContext != null) { 99 // TODO(b/335935200): remove this check once the static initialization is gone 100 return; 101 } 102 try { 103 sContext = InstrumentationRegistry.getInstrumentation().getContext(); 104 sPackageName = sContext.getPackageName(); 105 } catch (Exception e) { 106 DynamicLogger.getInstance() 107 .log( 108 LogLevel.ERROR, 109 TAG, 110 e, 111 "setStaticFixtures() failed (usually happens under Ravenwood). Setting" 112 + " sContext=%s, sPackageName=%s", 113 sContext, 114 sPackageName); 115 } 116 } 117 118 @Before setInstanceFixtures()119 public final void setInstanceFixtures() { 120 mContext = sContext; 121 mPackageName = sPackageName; 122 } 123 124 // TODO(b/361555631): merge 2 classes below into testDeviceSideTestCaseFixtures() and annotate 125 // it with @MetaTest 126 @Test 127 @Override testValidTestCaseFixtures()128 public final void testValidTestCaseFixtures() throws Exception { 129 assertValidTestCaseFixtures(); 130 } 131 132 @CallSuper 133 @Override assertValidTestCaseFixtures()134 protected void assertValidTestCaseFixtures() throws Exception { 135 super.assertValidTestCaseFixtures(); 136 137 assertTestClassHasNoFieldsFromSuperclass( 138 DeviceSideTestCase.class, 139 "mContext", 140 "mPackageName", 141 "mTag", 142 "ravenwood", 143 "sdkLevel", 144 "processLifeGuard", 145 "sContext", 146 "sPackageName", 147 "sRavenWood", 148 "RAVENWOOD_PACKAGE_NAME"); 149 assertTestClassHasNoSuchField( 150 "CONTEXT", 151 "should use existing mContext (or sContext when that's not possible) instead"); 152 assertTestClassHasNoSuchField( 153 "APPLICATION_CONTEXT", 154 "should use existing mContext (or sContext when that's not possible) instead"); 155 assertTestClassHasNoSuchField("context", "should use existing mContext instead"); 156 assertTestClassHasNoSuchField("mTargetContext", REASON_NO_TARGET_CONTEXT); 157 assertTestClassHasNoSuchField("mTargetPackageName", REASON_NO_TARGET_CONTEXT); 158 assertTestClassHasNoSuchField("sTargetContext", REASON_NO_TARGET_CONTEXT); 159 assertTestClassHasNoSuchField("sTargetPackageName", REASON_NO_TARGET_CONTEXT); 160 } 161 162 // NOTE: it's static so it can be used by other mockito-related superclasses, as often test 163 // cases are converted to use AdServicesMockitoTestCase and still defined the ExtendedMockito 164 // session - they should migrate to AdServicesExtendedMockitoTestCase instead. checkProhibitedMockitoFields( Class<T> superclass, T testInstance)165 protected static <T extends DeviceSideTestCase> void checkProhibitedMockitoFields( 166 Class<T> superclass, T testInstance) throws Exception { 167 // NOTE: same fields below are not defined (yet?) SharedExtendedMockitoTestCase or 168 // SharedMockitoTestCase, but they might; and even if they don't, this method is also used 169 // by the classes on AdServices (AdServicesMockitoTestCase / 170 // AdServicesExtendedMockitoTestCase) 171 testInstance.assertTestClassHasNoFieldsFromSuperclass( 172 superclass, 173 "mMockContext", 174 "mSpyContext", 175 "extendedMockito", 176 "errorLogUtilUsageRule", 177 "mocker", 178 "sInlineCleaner", 179 "sSpyContext", 180 "mMockFlags", 181 "mMockDebugFlags"); 182 testInstance.assertTestClassHasNoSuchField( 183 "mContextMock", "should use existing mMockContext instead"); 184 testInstance.assertTestClassHasNoSuchField( 185 "mContextSpy", "should use existing mSpyContext instead"); 186 testInstance.assertTestClassHasNoSuchField("mockito", "already taken care by @Rule"); 187 testInstance.assertTestClassHasNoSuchField( 188 "mFlagsMock", "should use existing mMockFlags instead"); 189 testInstance.assertTestClassHasNoSuchField( 190 "sMockFlags", "should use existing mMockFlags instead"); 191 testInstance.assertTestClassHasNoSuchField( 192 "mFlags", 193 superclass.getSimpleName() 194 + " already define a mMockFlags, and often subclasses define a @Mock" 195 + " mFlags; to avoid confusion, either use the existing mMockFlags, or" 196 + " create a non-mock instance like mFakeFlags"); 197 198 // Listed below are existing names for the extended mockito session on test classes that 199 // don't use the rule / superclass: 200 // TODO(b/368153625): should check for type instead 201 testInstance.assertTestClassHasNoSuchField( 202 "mStaticMockSession", REASON_SESSION_MANAGED_BY_RULE); 203 testInstance.assertTestClassHasNoSuchField( 204 "mMockitoSession", REASON_SESSION_MANAGED_BY_RULE); 205 testInstance.assertTestClassHasNoSuchField( 206 "mockitoSession", REASON_SESSION_MANAGED_BY_RULE); 207 testInstance.assertTestClassHasNoSuchField("session", REASON_SESSION_MANAGED_BY_RULE); 208 testInstance.assertTestClassHasNoSuchField( 209 "sStaticMockitoSession", REASON_SESSION_MANAGED_BY_RULE); 210 testInstance.assertTestClassHasNoSuchField( 211 "staticMockitoSession", REASON_SESSION_MANAGED_BY_RULE); 212 testInstance.assertTestClassHasNoSuchField( 213 "staticMockSession", REASON_SESSION_MANAGED_BY_RULE); 214 } 215 216 // TODO(b/335935200): temporary hac^H^H^Hworkaround to set context references before subclasses 217 // when the class or instance is initialized. 218 // In the long term, these tests must be refactored to use them "inside" the test (otherwise 219 // it would not work when running on host-side / ravenwood), then they can be removed. 220 static { setStaticFixtures()221 setStaticFixtures(); 222 } 223 DeviceSideTestCase()224 protected DeviceSideTestCase() { 225 setInstanceFixtures(); 226 } 227 } 228