1 // Copyright 2023 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base.test; 6 7 import androidx.test.core.app.ApplicationProvider; 8 9 import org.junit.rules.TestRule; 10 import org.junit.runner.Description; 11 import org.junit.runners.model.Statement; 12 13 import org.chromium.base.ApplicationStatus; 14 import org.chromium.base.BundleUtils; 15 import org.chromium.base.ContextUtils; 16 import org.chromium.base.Flag; 17 import org.chromium.base.LifetimeAssert; 18 import org.chromium.base.PathUtils; 19 import org.chromium.base.ResettersForTesting; 20 import org.chromium.base.ThreadUtils; 21 import org.chromium.base.library_loader.LibraryLoader; 22 import org.chromium.base.library_loader.LibraryProcessType; 23 import org.chromium.base.metrics.UmaRecorderHolder; 24 import org.chromium.base.task.PostTask; 25 import org.chromium.base.test.BaseRobolectricTestRunner.HelperTestRunner; 26 import org.chromium.base.test.util.CommandLineFlags; 27 import org.chromium.build.NativeLibraries; 28 29 import java.lang.reflect.Method; 30 import java.util.Locale; 31 import java.util.TimeZone; 32 33 /** 34 * The default Rule used by BaseRobolectricTestRunner. Include this directly when using 35 * ParameterizedRobolectricTestRunner. 36 * Use @Rule(order=-2) to ensure it runs before other rules. 37 */ 38 public class BaseRobolectricTestRule implements TestRule { 39 private static final Locale ORIG_LOCALE = Locale.getDefault(); 40 private static final TimeZone ORIG_TIMEZONE = TimeZone.getDefault(); 41 42 // Removes the API Level suffix. E.g. "testSomething[28]" -> "testSomething". stripBrackets(String methodName)43 private static String stripBrackets(String methodName) { 44 int idx = methodName.indexOf('['); 45 if (idx != -1) { 46 methodName = methodName.substring(0, idx); 47 } 48 return methodName; 49 } 50 51 @Override apply(Statement base, Description description)52 public Statement apply(Statement base, Description description) { 53 return new Statement() { 54 @Override 55 public void evaluate() throws Throwable { 56 setUp( 57 description 58 .getTestClass() 59 .getMethod(stripBrackets(description.getMethodName()))); 60 boolean testFailed = true; 61 try { 62 base.evaluate(); 63 testFailed = false; 64 } finally { 65 tearDown(testFailed); 66 // We cannot guarantee that this Rule will be evaluated first, so never 67 // call setMethodMode(), and reset class resetters after each method. 68 ResettersForTesting.onAfterClass(); 69 } 70 } 71 }; 72 } 73 74 static void setUp(Method method) { 75 UmaRecorderHolder.setUpNativeUmaRecorder(false); 76 ContextUtils.initApplicationContextForTests(ApplicationProvider.getApplicationContext()); 77 LibraryLoader.getInstance().setLibraryProcessType(LibraryProcessType.PROCESS_BROWSER); 78 // Whether or not native is loaded is a global one-way switch, so do it automatically so 79 // that it is always in the same state. 80 if (NativeLibraries.LIBRARIES.length > 0) { 81 LibraryLoader.getInstance().ensureMainDexInitialized(); 82 } 83 ApplicationStatus.initialize(ApplicationProvider.getApplicationContext()); 84 UmaRecorderHolder.resetForTesting(); 85 CommandLineFlags.setUpClass(method.getDeclaringClass()); 86 CommandLineFlags.setUpMethod(method); 87 BundleUtils.resetForTesting(); 88 Flag.resetAllInMemoryCachedValuesForTesting(); 89 } 90 91 static void tearDown(boolean testFailed) { 92 try { 93 // https://crbug.com/1392817 for context as to why we do this. 94 PostTask.flushJobsAndResetForTesting(); 95 } catch (InterruptedException e) { 96 HelperTestRunner.sTestFailed = true; 97 throw new RuntimeException(e); 98 } finally { 99 CommandLineFlags.tearDownMethod(); 100 CommandLineFlags.tearDownClass(); 101 ResettersForTesting.onAfterMethod(); 102 ApplicationStatus.destroyForJUnitTests(); 103 ContextUtils.clearApplicationContextForTests(); 104 PathUtils.resetForTesting(); 105 ThreadUtils.clearUiThreadForTesting(); 106 Locale.setDefault(ORIG_LOCALE); 107 TimeZone.setDefault(ORIG_TIMEZONE); 108 // Run assertions only when the test has not already failed so as to not mask 109 // failures. https://crbug.com/1466313 110 if (testFailed) { 111 LifetimeAssert.resetForTesting(); 112 } else { 113 LifetimeAssert.assertAllInstancesDestroyedForTesting(); 114 } 115 } 116 } 117 } 118