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 android.platform.test.ravenwood; 17 18 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING; 19 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertTrue; 23 24 import android.util.Log; 25 import android.util.Pair; 26 27 import com.android.ravenwood.RavenwoodRuntimeNative; 28 29 import org.junit.runner.Description; 30 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.Set; 34 35 /** 36 * Used to store various states associated with the current test runner that's only needed 37 * in junit-impl. 38 * 39 * We don't want to put it in junit-src to avoid having to recompile all the downstream 40 * dependencies after changing this class. 41 * 42 * All members must be called from the runner's main thread. 43 */ 44 public final class RavenwoodRunnerState { 45 private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG; 46 private static final String RAVENWOOD_RULE_ERROR = 47 "RavenwoodRule(s) are not executed in the correct order"; 48 49 private static final List<Pair<RavenwoodRule, RavenwoodPropertyState>> sActiveProperties = 50 new ArrayList<>(); 51 52 private final RavenwoodAwareTestRunner mRunner; 53 54 /** 55 * Ctor. 56 */ RavenwoodRunnerState(RavenwoodAwareTestRunner runner)57 public RavenwoodRunnerState(RavenwoodAwareTestRunner runner) { 58 mRunner = runner; 59 } 60 61 private Description mMethodDescription; 62 enterTestRunner()63 public void enterTestRunner() { 64 if (RAVENWOOD_VERBOSE_LOGGING) { 65 Log.v(TAG, "enterTestRunner: " + mRunner); 66 } 67 RavenwoodRuntimeEnvironmentController.initForRunner(); 68 } 69 enterTestClass()70 public void enterTestClass() { 71 if (RAVENWOOD_VERBOSE_LOGGING) { 72 Log.v(TAG, "enterTestClass: " + mRunner.mTestJavaClass.getName()); 73 } 74 } 75 exitTestClass()76 public void exitTestClass() { 77 if (RAVENWOOD_VERBOSE_LOGGING) { 78 Log.v(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName()); 79 } 80 assertTrue(RAVENWOOD_RULE_ERROR, sActiveProperties.isEmpty()); 81 RavenwoodRuntimeEnvironmentController.exitTestClass(); 82 } 83 84 /** Called when a test method is about to start */ enterTestMethod(Description description)85 public void enterTestMethod(Description description) { 86 mMethodDescription = description; 87 RavenwoodRuntimeEnvironmentController.enterTestMethod(description); 88 } 89 90 /** Called when a test method finishes */ exitTestMethod(Description description)91 public void exitTestMethod(Description description) { 92 RavenwoodRuntimeEnvironmentController.exitTestMethod(description); 93 mMethodDescription = null; 94 } 95 enterRavenwoodRule(RavenwoodRule rule)96 public void enterRavenwoodRule(RavenwoodRule rule) { 97 pushTestProperties(rule); 98 } 99 exitRavenwoodRule(RavenwoodRule rule)100 public void exitRavenwoodRule(RavenwoodRule rule) { 101 popTestProperties(rule); 102 } 103 104 static class RavenwoodPropertyState { 105 106 final List<Pair<String, String>> mBackup; 107 final Set<String> mKeyReadable; 108 final Set<String> mKeyWritable; 109 RavenwoodPropertyState(RavenwoodTestProperties props)110 RavenwoodPropertyState(RavenwoodTestProperties props) { 111 mBackup = props.mValues.keySet().stream() 112 .map(key -> Pair.create(key, RavenwoodRuntimeNative.getSystemProperty(key))) 113 .toList(); 114 mKeyReadable = Set.copyOf(props.mKeyReadable); 115 mKeyWritable = Set.copyOf(props.mKeyWritable); 116 } 117 isKeyAccessible(String key, boolean write)118 boolean isKeyAccessible(String key, boolean write) { 119 return write ? mKeyWritable.contains(key) : mKeyReadable.contains(key); 120 } 121 restore()122 void restore() { 123 mBackup.forEach(pair -> { 124 if (pair.second == null) { 125 RavenwoodRuntimeNative.removeSystemProperty(pair.first); 126 } else { 127 RavenwoodRuntimeNative.setSystemProperty(pair.first, pair.second); 128 } 129 }); 130 } 131 } 132 pushTestProperties(RavenwoodRule rule)133 private static void pushTestProperties(RavenwoodRule rule) { 134 sActiveProperties.add(Pair.create(rule, new RavenwoodPropertyState(rule.mProperties))); 135 rule.mProperties.mValues.forEach(RavenwoodRuntimeNative::setSystemProperty); 136 } 137 popTestProperties(RavenwoodRule rule)138 private static void popTestProperties(RavenwoodRule rule) { 139 var pair = sActiveProperties.removeLast(); 140 assertNotNull(RAVENWOOD_RULE_ERROR, pair); 141 assertEquals(RAVENWOOD_RULE_ERROR, rule, pair.first); 142 pair.second.restore(); 143 } 144 145 @SuppressWarnings("unused") // Called from native code (ravenwood_sysprop.cpp) checkSystemPropertyAccess(String key, boolean write)146 private static void checkSystemPropertyAccess(String key, boolean write) { 147 if (write && RavenwoodSystemProperties.sDefaultValues.containsKey(key)) { 148 // The default core values should never be modified 149 throw new IllegalArgumentException( 150 "Setting core system property '" + key + "' is not allowed"); 151 } 152 153 final boolean result = RavenwoodSystemProperties.isKeyAccessible(key, write) 154 || sActiveProperties.stream().anyMatch(p -> p.second.isKeyAccessible(key, write)); 155 156 if (!result) { 157 throw new IllegalArgumentException((write ? "Write" : "Read") 158 + " access to system property '" + key + "' denied via RavenwoodRule"); 159 } 160 } 161 } 162