• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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