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 17 package com.android.adservices.common; 18 19 import static com.android.adservices.common.AbstractAdServicesDeviceSupportedRule.REQUIRES_ANDROID_SERVICE_ASSUMPTION_FAILED_ERROR_MSG; 20 21 import static org.junit.Assert.assertThrows; 22 import static org.mockito.Mockito.when; 23 24 import com.android.adservices.common.annotations.RequiresAndroidServiceAvailable; 25 import com.android.adservices.shared.meta_testing.SimpleStatement; 26 import com.android.adservices.shared.meta_testing.TestAnnotations; 27 import com.android.adservices.shared.testing.DeviceConditionsViolatedException; 28 import com.android.adservices.shared.testing.Logger; 29 import com.android.adservices.shared.testing.ScreenSize; 30 import com.android.adservices.shared.testing.StandardStreamsLogger; 31 32 import com.google.common.truth.Expect; 33 34 import org.junit.Before; 35 import org.junit.Rule; 36 import org.junit.Test; 37 import org.junit.runner.Description; 38 import org.mockito.Mock; 39 import org.mockito.junit.MockitoJUnit; 40 import org.mockito.junit.MockitoRule; 41 42 import java.lang.annotation.Annotation; 43 import java.util.Locale; 44 45 // TODO(b/315542995): provide host-side implementation 46 /** 47 * Test case for {@link AbstractAdServicesDeviceSupportedRule} implementations. 48 * 49 * <p>By default, it uses a {@link 50 * AbstractAdServicesDeviceSupportedRuleTest.FakeAdServicesDeviceSupportedRule bogus rule} so it can 51 * be run by IDEs. 52 */ 53 public class AbstractAdServicesDeviceSupportedRuleTest { 54 private static final String CLASS_SERVICE_NAME = "ClassService"; 55 private final Logger.RealLogger mRealLogger = StandardStreamsLogger.getInstance(); 56 private final SimpleStatement mBaseStatement = new SimpleStatement(); 57 58 private FakeAdServicesDeviceSupportedRule mAdServicesDeviceSupportedRule; 59 60 @Mock private AbstractDeviceSupportHelper mAbstractDeviceSupportHelper; 61 62 @Rule public final Expect expect = Expect.create(); 63 @Rule public final MockitoRule mockitoRule = MockitoJUnit.rule(); 64 65 @Before setup()66 public void setup() { 67 mAdServicesDeviceSupportedRule = 68 new FakeAdServicesDeviceSupportedRule(mRealLogger, mAbstractDeviceSupportHelper); 69 mockIsGoDevice(false); 70 mockIsLowRamDevice(false); 71 mockIsLargeScreenDevice(false); 72 } 73 74 @Test testIsAdServicesSupported_supported()75 public void testIsAdServicesSupported_supported() throws Exception { 76 when(mAbstractDeviceSupportHelper.isDeviceSupported()).thenReturn(true); 77 78 expect.that(mAdServicesDeviceSupportedRule.isAdServicesSupportedOnDevice()).isTrue(); 79 } 80 81 @Test testIsAdServicesSupportedOnDeviceTest_notSupported()82 public void testIsAdServicesSupportedOnDeviceTest_notSupported() throws Exception { 83 when(mAbstractDeviceSupportHelper.isDeviceSupported()).thenReturn(false); 84 85 expect.that(mAdServicesDeviceSupportedRule.isAdServicesSupportedOnDevice()).isFalse(); 86 } 87 88 @Test testIsLowRamDevice_returnsTrue()89 public void testIsLowRamDevice_returnsTrue() { 90 mockIsLowRamDevice(true); 91 92 expect.that(mAdServicesDeviceSupportedRule.isLowRamDevice()).isTrue(); 93 } 94 95 @Test testIsLowRamDevice_returnsFalse()96 public void testIsLowRamDevice_returnsFalse() { 97 mockIsLowRamDevice(false); 98 99 expect.that(mAdServicesDeviceSupportedRule.isLowRamDevice()).isFalse(); 100 } 101 102 @Test testIsLargeScreenevice_returnsTrue()103 public void testIsLargeScreenevice_returnsTrue() { 104 mockIsLargeScreenDevice(true); 105 106 expect.that(mAdServicesDeviceSupportedRule.isLargeScreenDevice()).isTrue(); 107 } 108 109 @Test testIsLargeScreenDevice_returnsFalse()110 public void testIsLargeScreenDevice_returnsFalse() { 111 mockIsLargeScreenDevice(false); 112 113 expect.that(mAdServicesDeviceSupportedRule.isLargeScreenDevice()).isFalse(); 114 } 115 116 @Test testIsGoDevice_returnsTrue()117 public void testIsGoDevice_returnsTrue() { 118 mockIsGoDevice(true); 119 120 expect.that(mAdServicesDeviceSupportedRule.isGoDevice()).isTrue(); 121 } 122 123 @Test testIsGoDevice_returnsFalse()124 public void testIsGoDevice_returnsFalse() { 125 mockIsGoDevice(false); 126 127 expect.that(mAdServicesDeviceSupportedRule.isGoDevice()).isFalse(); 128 } 129 130 @Test testAnnotatedWithLowRam_deviceNotLowRam()131 public void testAnnotatedWithLowRam_deviceNotLowRam() { 132 mockIsLowRamDevice(false); 133 Description description = createTestMethod(TestAnnotations.requiresLowRamDevice()); 134 135 assertTestThrowsAssumptionsViolatedException( 136 description, 137 AbstractAdServicesDeviceSupportedRule 138 .REQUIRES_LOW_RAM_ASSUMPTION_FAILED_ERROR_MESSAGE); 139 } 140 141 @Test testAnnotatedWithLowRam_deviceLowRam()142 public void testAnnotatedWithLowRam_deviceLowRam() throws Throwable { 143 mockIsLowRamDevice(true); 144 Description description = createTestMethod(TestAnnotations.requiresLowRamDevice()); 145 146 mAdServicesDeviceSupportedRule.apply(mBaseStatement, description).evaluate(); 147 148 mBaseStatement.assertEvaluated(); 149 } 150 151 @Test testAnnotatedWithLargeScreen_deviceSmallScreen()152 public void testAnnotatedWithLargeScreen_deviceSmallScreen() { 153 mockIsLargeScreenDevice(false); 154 Description description = 155 createTestMethod(TestAnnotations.requiresScreenSizeDevice(ScreenSize.LARGE_SCREEN)); 156 157 assertTestThrowsAssumptionsViolatedException( 158 description, 159 String.format( 160 AbstractAdServicesDeviceSupportedRule 161 .REQUIRES_SCREEN_SIZE_ASSUMPTION_FAILED_ERROR_MESSAGE, 162 ScreenSize.LARGE_SCREEN)); 163 } 164 165 @Test testAnnotatedWithLargeScreen_deviceLargeScreen()166 public void testAnnotatedWithLargeScreen_deviceLargeScreen() throws Throwable { 167 mockIsLargeScreenDevice(true); 168 Description description = 169 createTestMethod(TestAnnotations.requiresScreenSizeDevice(ScreenSize.LARGE_SCREEN)); 170 171 mAdServicesDeviceSupportedRule.apply(mBaseStatement, description).evaluate(); 172 173 mBaseStatement.assertEvaluated(); 174 } 175 176 @Test testAnnotatedWithSmallScreen_deviceLargeScreen()177 public void testAnnotatedWithSmallScreen_deviceLargeScreen() { 178 mockIsLargeScreenDevice(true); 179 Description description = 180 createTestMethod(TestAnnotations.requiresScreenSizeDevice(ScreenSize.SMALL_SCREEN)); 181 182 assertTestThrowsAssumptionsViolatedException( 183 description, 184 String.format( 185 AbstractAdServicesDeviceSupportedRule 186 .REQUIRES_SCREEN_SIZE_ASSUMPTION_FAILED_ERROR_MESSAGE, 187 ScreenSize.SMALL_SCREEN)); 188 } 189 190 @Test testAnnotatedWithSmallScreen_deviceSmallScreen()191 public void testAnnotatedWithSmallScreen_deviceSmallScreen() throws Throwable { 192 mockIsLargeScreenDevice(false); 193 Description description = 194 createTestMethod(TestAnnotations.requiresScreenSizeDevice(ScreenSize.SMALL_SCREEN)); 195 196 mAdServicesDeviceSupportedRule.apply(mBaseStatement, description).evaluate(); 197 198 mBaseStatement.assertEvaluated(); 199 } 200 201 @Test testAnnotatedWithRequiresGoDevice_deviceNotGoDevice()202 public void testAnnotatedWithRequiresGoDevice_deviceNotGoDevice() { 203 mockIsGoDevice(false); 204 Description description = createTestMethod(TestAnnotations.requiresGoDevice()); 205 206 assertTestThrowsAssumptionsViolatedException( 207 description, 208 AbstractAdServicesDeviceSupportedRule 209 .REQUIRES_GO_DEVICE_ASSUMPTION_FAILED_ERROR_MESSAGE); 210 } 211 212 @Test testAnnotatedWithRequiresGoDevice_deviceGoDevice()213 public void testAnnotatedWithRequiresGoDevice_deviceGoDevice() throws Throwable { 214 mockIsGoDevice(true); 215 Description description = createTestMethod(TestAnnotations.requiresGoDevice()); 216 217 mAdServicesDeviceSupportedRule.apply(mBaseStatement, description).evaluate(); 218 219 mBaseStatement.assertEvaluated(); 220 } 221 222 @Test testAnnotatedWithRequiresGoDeviceAndRequiresLowRamDevice_onlyGoDevice()223 public void testAnnotatedWithRequiresGoDeviceAndRequiresLowRamDevice_onlyGoDevice() { 224 mockIsGoDevice(true); 225 Description description = 226 createTestMethod( 227 TestAnnotations.requiresGoDevice(), TestAnnotations.requiresLowRamDevice()); 228 229 assertTestThrowsAssumptionsViolatedException( 230 description, 231 AbstractAdServicesDeviceSupportedRule 232 .REQUIRES_LOW_RAM_ASSUMPTION_FAILED_ERROR_MESSAGE); 233 } 234 235 @Test testAnnotatedWithRequiresGoDeviceAndRequiresLowMemoryDevice_onlyLowRamDevice()236 public void testAnnotatedWithRequiresGoDeviceAndRequiresLowMemoryDevice_onlyLowRamDevice() { 237 mockIsLowRamDevice(true); 238 Description description = 239 createTestMethod( 240 TestAnnotations.requiresGoDevice(), TestAnnotations.requiresLowRamDevice()); 241 242 assertTestThrowsAssumptionsViolatedException( 243 description, 244 AbstractAdServicesDeviceSupportedRule 245 .REQUIRES_GO_DEVICE_ASSUMPTION_FAILED_ERROR_MESSAGE); 246 } 247 248 @Test testAnnotatedWithRequiresGoDeviceAndRequiresLowMemoryDevice_deviceNone()249 public void testAnnotatedWithRequiresGoDeviceAndRequiresLowMemoryDevice_deviceNone() { 250 mockIsLowRamDevice(false); 251 mockIsGoDevice(false); 252 Description description = 253 createTestMethod( 254 TestAnnotations.requiresGoDevice(), TestAnnotations.requiresLowRamDevice()); 255 256 assertTestThrowsAssumptionsViolatedException( 257 description, 258 AbstractAdServicesDeviceSupportedRule 259 .REQUIRES_GO_DEVICE_ASSUMPTION_FAILED_ERROR_MESSAGE, 260 AbstractAdServicesDeviceSupportedRule 261 .REQUIRES_LOW_RAM_ASSUMPTION_FAILED_ERROR_MESSAGE); 262 } 263 264 @Test testAnnotatedWithRequiresGoDeviceAndRequiresLowMemoryDevice_deviceBoth()265 public void testAnnotatedWithRequiresGoDeviceAndRequiresLowMemoryDevice_deviceBoth() 266 throws Throwable { 267 mockIsLowRamDevice(true); 268 mockIsGoDevice(true); 269 Description description = 270 createTestMethod( 271 TestAnnotations.requiresGoDevice(), TestAnnotations.requiresLowRamDevice()); 272 273 mAdServicesDeviceSupportedRule.apply(mBaseStatement, description).evaluate(); 274 275 mBaseStatement.assertEvaluated(); 276 } 277 278 @Test testAnnotatedWithRequiresAndroidServiceAvailable_available()279 public void testAnnotatedWithRequiresAndroidServiceAvailable_available() throws Throwable { 280 String serviceName = "SomeService"; 281 mockGetResolveInfos(serviceName, /* isAdIdAvailable= */ true); 282 Description description = 283 createTestMethod( 284 com.android.adservices.common.TestAnnotations 285 .requiresAndroidServiceAvailable(serviceName)); 286 287 mAdServicesDeviceSupportedRule.apply(mBaseStatement, description).evaluate(); 288 289 mBaseStatement.assertEvaluated(); 290 } 291 292 @Test testAnnotatedWithRequiresAndroidServiceAvailable_unavailable()293 public void testAnnotatedWithRequiresAndroidServiceAvailable_unavailable() { 294 String serviceName = "SomeService"; 295 // Package Manager returns null for resolveInfos. 296 mockGetResolveInfos(serviceName, /* isAdIdAvailable= */ false); 297 Description description = 298 createTestMethod( 299 com.android.adservices.common.TestAnnotations 300 .requiresAndroidServiceAvailable(serviceName)); 301 302 assertTestThrowsAssumptionsViolatedException( 303 description, getRequiresAndroidServiceAssumptionFailureString(serviceName)); 304 } 305 306 @Test testAnnotatedWithRequiresAndroidServiceAvailable_mismatchedServiceName()307 public void testAnnotatedWithRequiresAndroidServiceAvailable_mismatchedServiceName() { 308 // Package Manager returns null for resolveInfos. 309 mockGetResolveInfos("SomeService", /* isAdIdAvailable= */ false); 310 String mismatchedServiceName = "MismatchedServiceName"; 311 Description description = 312 createTestMethod( 313 com.android.adservices.common.TestAnnotations 314 .requiresAndroidServiceAvailable(mismatchedServiceName)); 315 316 assertTestThrowsAssumptionsViolatedException( 317 description, 318 getRequiresAndroidServiceAssumptionFailureString(mismatchedServiceName)); 319 } 320 321 @Test testAnnotatedWithRequiresAndroidServiceAvailable_classAnnotation()322 public void testAnnotatedWithRequiresAndroidServiceAvailable_classAnnotation() { 323 String serviceName = "SomeService"; 324 // Package Manager returns null for resolveInfos. 325 mockGetResolveInfos(serviceName, /* isAdIdAvailable= */ false); 326 Description description = 327 createTestMethodUsingDefinedClass( 328 com.android.adservices.common.TestAnnotations 329 .requiresAndroidServiceAvailable(serviceName)); 330 331 assertTestThrowsAssumptionsViolatedException( 332 description, getRequiresAndroidServiceAssumptionFailureString(serviceName)); 333 } 334 335 @Test testAnnotatedWithRequiresAndroidServiceAvailable_methodOverridingClassAnnotation()336 public void testAnnotatedWithRequiresAndroidServiceAvailable_methodOverridingClassAnnotation() 337 throws Throwable { 338 String methodServiceName = "MethodService"; 339 // Mock that the device doesn't have a service named as CLASS_SERVICE_NAME but has a service 340 // named as methodServiceName. 341 mockGetResolveInfos(CLASS_SERVICE_NAME, /* isAdIdAvailable= */ false); 342 mockGetResolveInfos(methodServiceName, /* isAdIdAvailable= */ true); 343 344 // Create a description as a test method in the test class 345 // "TestRequiresAndroidServiceAvailable". This test method also has the annotation 346 // RequiresAndroidServiceAvailable but with a different intentAction. 347 Description methodDescription = 348 createTestMethodUsingDefinedClass( 349 com.android.adservices.common.TestAnnotations 350 .requiresAndroidServiceAvailable(methodServiceName)); 351 352 // It doesn't throw AssumptionsViolatedException though 1) class has the annotation 2) the 353 // corresponding intentAction for class is not available on the device. This is because the 354 // test method's annotation overrides the class's annotation. 355 mAdServicesDeviceSupportedRule.apply(mBaseStatement, methodDescription).evaluate(); 356 } 357 assertTestThrowsAssumptionsViolatedException( Description description, String... expectedReasons)358 private void assertTestThrowsAssumptionsViolatedException( 359 Description description, String... expectedReasons) { 360 DeviceConditionsViolatedException e = 361 assertThrows( 362 DeviceConditionsViolatedException.class, 363 () -> 364 mAdServicesDeviceSupportedRule 365 .apply(mBaseStatement, description) 366 .evaluate()); 367 368 expect.withMessage("exception message") 369 .that(e.getConditionsViolatedReasons()) 370 .containsExactlyElementsIn(expectedReasons); 371 mBaseStatement.assertNotEvaluated(); 372 } 373 374 /** Bogus implementation of {@link AbstractAdServicesDeviceSupportedRule}. */ 375 private static final class FakeAdServicesDeviceSupportedRule 376 extends AbstractAdServicesDeviceSupportedRule { 377 /** Default constructor. */ FakeAdServicesDeviceSupportedRule( Logger.RealLogger logger, AbstractDeviceSupportHelper deviceSupportHelper)378 private FakeAdServicesDeviceSupportedRule( 379 Logger.RealLogger logger, AbstractDeviceSupportHelper deviceSupportHelper) { 380 super(logger, deviceSupportHelper); 381 } 382 } 383 createTestMethod(Annotation... annotations)384 private Description createTestMethod(Annotation... annotations) { 385 return Description.createTestDescription( 386 AbstractAdServicesDeviceSupportedRuleTest.class, "method_name", annotations); 387 } 388 createTestMethodUsingDefinedClass(Annotation... annotations)389 private Description createTestMethodUsingDefinedClass(Annotation... annotations) { 390 return Description.createTestDescription( 391 TestRequiresAndroidServiceAvailable.class, "some_test", annotations); 392 } 393 mockIsLowRamDevice(boolean isLowRam)394 private void mockIsLowRamDevice(boolean isLowRam) { 395 when(mAbstractDeviceSupportHelper.isLowRamDevice()).thenReturn(isLowRam); 396 } 397 mockIsLargeScreenDevice(boolean isLargeScreen)398 private void mockIsLargeScreenDevice(boolean isLargeScreen) { 399 when(mAbstractDeviceSupportHelper.isLargeScreenDevice()).thenReturn(isLargeScreen); 400 } 401 mockIsGoDevice(boolean isGoDevice)402 private void mockIsGoDevice(boolean isGoDevice) { 403 when(mAbstractDeviceSupportHelper.isGoDevice()).thenReturn(isGoDevice); 404 } 405 mockGetResolveInfos(String serviceName, boolean isAdIdAvailable)406 private void mockGetResolveInfos(String serviceName, boolean isAdIdAvailable) { 407 // Bypass the device supported check 408 when(mAbstractDeviceSupportHelper.isDeviceSupported()).thenReturn(true); 409 410 when(mAbstractDeviceSupportHelper.isAndroidServiceAvailable(serviceName)) 411 .thenReturn(isAdIdAvailable); 412 } 413 getRequiresAndroidServiceAssumptionFailureString(String serviceName)414 private String getRequiresAndroidServiceAssumptionFailureString(String serviceName) { 415 return String.format( 416 Locale.ENGLISH, REQUIRES_ANDROID_SERVICE_ASSUMPTION_FAILED_ERROR_MSG, serviceName); 417 } 418 419 @RequiresAndroidServiceAvailable(intentAction = CLASS_SERVICE_NAME) 420 static final class TestRequiresAndroidServiceAvailable {} 421 } 422