Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d65323c5 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "[Ravenwood] Update system property handling" into main am: 5c0d09f8

parents 3fbd3eb1 5c0d09f8
Loading
Loading
Loading
Loading
+78 −2
Original line number Diff line number Diff line
@@ -15,12 +15,23 @@
 */
package android.platform.test.ravenwood;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import android.util.Log;
import android.util.Pair;

import com.android.ravenwood.RavenwoodRuntimeNative;

import org.junit.runner.Description;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Used to store various states associated with the current test runner that's inly needed
 * Used to store various states associated with the current test runner that's only needed
 * in junit-impl.
 *
 * We don't want to put it in junit-src to avoid having to recompile all the downstream
@@ -30,6 +41,11 @@ import org.junit.runner.Description;
 */
public final class RavenwoodRunnerState {
    private static final String TAG = "RavenwoodRunnerState";
    private static final String RAVENWOOD_RULE_ERROR =
            "RavenwoodRule(s) are not executed in the correct order";

    private static final List<Pair<RavenwoodRule, RavenwoodPropertyState>> sActiveProperties =
            new ArrayList<>();

    private final RavenwoodAwareTestRunner mRunner;

@@ -53,6 +69,7 @@ public final class RavenwoodRunnerState {

    public void exitTestClass() {
        Log.i(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName());
        assertTrue(RAVENWOOD_RULE_ERROR, sActiveProperties.isEmpty());
        RavenwoodRuntimeEnvironmentController.exitTestClass();
    }

@@ -66,9 +83,68 @@ public final class RavenwoodRunnerState {
    }

    public void enterRavenwoodRule(RavenwoodRule rule) {
        RavenwoodRuntimeEnvironmentController.setSystemProperties(rule.mSystemProperties);
        pushTestProperties(rule);
    }

    public void exitRavenwoodRule(RavenwoodRule rule) {
        popTestProperties(rule);
    }

    static class RavenwoodPropertyState {

        final List<Pair<String, String>> mBackup;
        final Set<String> mKeyReadable;
        final Set<String> mKeyWritable;

        RavenwoodPropertyState(RavenwoodTestProperties props) {
            mBackup = props.mValues.keySet().stream()
                    .map(key -> Pair.create(key, RavenwoodRuntimeNative.getSystemProperty(key)))
                    .toList();
            mKeyReadable = Set.copyOf(props.mKeyReadable);
            mKeyWritable = Set.copyOf(props.mKeyWritable);
        }

        boolean isKeyAccessible(String key, boolean write) {
            return write ? mKeyWritable.contains(key) : mKeyReadable.contains(key);
        }

        void restore() {
            mBackup.forEach(pair -> {
                if (pair.second == null) {
                    RavenwoodRuntimeNative.removeSystemProperty(pair.first);
                } else {
                    RavenwoodRuntimeNative.setSystemProperty(pair.first, pair.second);
                }
            });
        }
    }

    private static void pushTestProperties(RavenwoodRule rule) {
        sActiveProperties.add(Pair.create(rule, new RavenwoodPropertyState(rule.mProperties)));
        rule.mProperties.mValues.forEach(RavenwoodRuntimeNative::setSystemProperty);
    }

    private static void popTestProperties(RavenwoodRule rule) {
        var pair = sActiveProperties.removeLast();
        assertNotNull(RAVENWOOD_RULE_ERROR, pair);
        assertEquals(RAVENWOOD_RULE_ERROR, rule, pair.first);
        pair.second.restore();
    }

    @SuppressWarnings("unused")  // Called from native code (ravenwood_sysprop.cpp)
    private static void checkSystemPropertyAccess(String key, boolean write) {
        if (write && RavenwoodSystemProperties.sDefaultValues.containsKey(key)) {
            // The default core values should never be modified
            throw new IllegalArgumentException(
                    "Setting core system property '" + key + "' is not allowed");
        }

        final boolean result = RavenwoodSystemProperties.isKeyAccessible(key, write)
                || sActiveProperties.stream().anyMatch(p -> p.second.isKeyAccessible(key, write));

        if (!result) {
            throw new IllegalArgumentException((write ? "Write" : "Read")
                    + " access to system property '" + key + "' denied via RavenwoodRule");
        }
    }
}
+3 −25
Original line number Diff line number Diff line
@@ -163,8 +163,6 @@ public class RavenwoodRuntimeEnvironmentController {
    @GuardedBy("sInitializationLock")
    private static Throwable sExceptionFromGlobalInit;

    private static RavenwoodSystemProperties sProps;

    private static final int DEFAULT_TARGET_SDK_LEVEL = VERSION_CODES.CUR_DEVELOPMENT;
    private static final String DEFAULT_PACKAGE_NAME = "com.android.ravenwoodtests.defaultname";

@@ -234,7 +232,6 @@ public class RavenwoodRuntimeEnvironmentController {

        // Do the basic set up for the android sysprops.
        RavenwoodSystemProperties.initialize();
        setSystemProperties(null);

        // Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()),
        // before loadFrameworkNativeCode() (which uses $ANDROID_LOG_TAGS).
@@ -356,10 +353,13 @@ public class RavenwoodRuntimeEnvironmentController {
        // will call Mockito.framework().clearInlineMocks() after execution.
        sInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation());

        // Reset some global state
        Process_ravenwood.reset();
        DeviceConfig_host.reset();
        Binder.restoreCallingIdentity(sCallingIdentity);

        SystemProperties.clearChangeCallbacksForTest();

        if (ENABLE_TIMEOUT_STACKS) {
            sPendingTimeout = sTimeoutExecutor.schedule(
                    RavenwoodRuntimeEnvironmentController::dumpStacks,
@@ -484,19 +484,6 @@ public class RavenwoodRuntimeEnvironmentController {
        }
    }

    /**
     * Set the current configuration to the actual SystemProperties.
     */
    public static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) {
        SystemProperties.clearChangeCallbacksForTest();
        RavenwoodRuntimeNative.clearSystemProperties();
        if (systemProperties == null) systemProperties = new RavenwoodSystemProperties();
        sProps = new RavenwoodSystemProperties(systemProperties, true);
        for (var entry : systemProperties.getValues().entrySet()) {
            RavenwoodRuntimeNative.setSystemProperty(entry.getKey(), entry.getValue());
        }
    }

    private static final String MOCKITO_ERROR = "FATAL: Unsupported Mockito detected!"
            + " Your test or its dependencies use one of the \"mockito-target-*\""
            + " modules as static library, which is unusable on host side."
@@ -546,15 +533,6 @@ public class RavenwoodRuntimeEnvironmentController {
        return mock;
    }

    @SuppressWarnings("unused")  // Called from native code (ravenwood_sysprop.cpp)
    private static void checkSystemPropertyAccess(String key, boolean write) {
        boolean result = write ? sProps.isKeyWritable(key) : sProps.isKeyReadable(key);
        if (!result) {
            throw new IllegalArgumentException((write ? "Write" : "Read")
                    + " access to system property '" + key + "' denied via RavenwoodConfig");
        }
    }

    private static void dumpCommandLineArgs() {
        Log.i(TAG, "JVM arguments:");

+17 −67
Original line number Diff line number Diff line
@@ -13,7 +13,6 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.platform.test.ravenwood;

import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
@@ -21,26 +20,30 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.getRavenwoodRunt

import android.util.Log;

import com.android.ravenwood.RavenwoodRuntimeNative;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * A class to manage the core default system properties of the Ravenwood environment.
 */
public class RavenwoodSystemProperties {
    private static final String TAG = "RavenwoodSystemProperties";

    /** We pull in propeties from this file. */
    /** We pull in properties from this file. */
    private static final String RAVENWOOD_BUILD_PROP = "ravenwood-data/ravenwood-build.prop";

    /** This is the actual build.prop we use to build the device (contents depends on lunch). */
    private static final String DEVICE_BUILD_PROP = "ravenwood-data/build.prop";

    /** The default values. */
    private static final Map<String, String> sDefaultValues = new HashMap<>();
    static final Map<String, String> sDefaultValues = new HashMap<>();

    private static final String[] PARTITIONS = {
            "bootimage",
@@ -115,6 +118,7 @@ public class RavenwoodSystemProperties {
                }
            }
        }

        if (RAVENWOOD_VERBOSE_LOGGING) {
            // Dump all properties for local debugging.
            Log.v(TAG, "All system properties:");
@@ -122,35 +126,12 @@ public class RavenwoodSystemProperties {
                Log.v(TAG, "" + key + "=" + sDefaultValues.get(key));
            }
        }
    }

    private volatile boolean mIsImmutable;

    private final Map<String, String> mValues = new HashMap<>();

    /** Set of additional keys that should be considered readable */
    private final Set<String> mKeyReadable = new HashSet<>();

    /** Set of additional keys that should be considered writable */
    private final Set<String> mKeyWritable = new HashSet<>();

    public RavenwoodSystemProperties() {
        mValues.putAll(sDefaultValues);
    }

    /** Copy constructor */
    public RavenwoodSystemProperties(RavenwoodSystemProperties source, boolean immutable) {
        mKeyReadable.addAll(source.mKeyReadable);
        mKeyWritable.addAll(source.mKeyWritable);
        mValues.putAll(source.mValues);
        mIsImmutable = immutable;
    }

    public Map<String, String> getValues() {
        return new HashMap<>(mValues);
        // Actually set the system properties
        sDefaultValues.forEach(RavenwoodRuntimeNative::setSystemProperty);
    }

    public boolean isKeyReadable(String key) {
    private static boolean isKeyReadable(String key) {
        final String root = getKeyRoot(key);

        if (root.startsWith("debug.")) return true;
@@ -183,10 +164,10 @@ public class RavenwoodSystemProperties {
                return true;
        }

        return mKeyReadable.contains(key);
        return false;
    }

    public boolean isKeyWritable(String key) {
    private static boolean isKeyWritable(String key) {
        final String root = getKeyRoot(key);

        if (root.startsWith("debug.")) return true;
@@ -194,42 +175,11 @@ public class RavenwoodSystemProperties {
        // For PropertyInvalidatedCache
        if (root.startsWith("cache_key.")) return true;

        return mKeyWritable.contains(key);
    }

    private void ensureNotImmutable() {
        if (mIsImmutable) {
            throw new RuntimeException("Unable to update immutable instance");
        }
    }

    public void setValue(String key, Object value) {
        ensureNotImmutable();

        final String valueString = (value == null) ? null : String.valueOf(value);
        if ((valueString == null) || valueString.isEmpty()) {
            mValues.remove(key);
        } else {
            mValues.put(key, valueString);
        }
    }

    public void setAccessNone(String key) {
        ensureNotImmutable();
        mKeyReadable.remove(key);
        mKeyWritable.remove(key);
    }

    public void setAccessReadOnly(String key) {
        ensureNotImmutable();
        mKeyReadable.add(key);
        mKeyWritable.remove(key);
        return false;
    }

    public void setAccessReadWrite(String key) {
        ensureNotImmutable();
        mKeyReadable.add(key);
        mKeyWritable.add(key);
    static boolean isKeyAccessible(String key, boolean write) {
        return write ? isKeyWritable(key) : isKeyReadable(key);
    }

    /**
+5 −5
Original line number Diff line number Diff line
@@ -92,7 +92,7 @@ public final class RavenwoodRule implements TestRule {
        }
    }

    final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties();
    final RavenwoodTestProperties mProperties = new RavenwoodTestProperties();

    public static class Builder {

@@ -144,8 +144,8 @@ public final class RavenwoodRule implements TestRule {
         * Has no effect on non-Ravenwood environments.
         */
        public Builder setSystemPropertyImmutable(@NonNull String key, @Nullable Object value) {
            mRule.mSystemProperties.setValue(key, value);
            mRule.mSystemProperties.setAccessReadOnly(key);
            mRule.mProperties.setValue(key, value);
            mRule.mProperties.setAccessReadOnly(key);
            return this;
        }

@@ -160,8 +160,8 @@ public final class RavenwoodRule implements TestRule {
         * Has no effect on non-Ravenwood environments.
         */
        public Builder setSystemPropertyMutable(@NonNull String key, @Nullable Object value) {
            mRule.mSystemProperties.setValue(key, value);
            mRule.mSystemProperties.setAccessReadWrite(key);
            mRule.mProperties.setValue(key, value);
            mRule.mProperties.setAccessReadWrite(key);
            return this;
        }

+59 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.platform.test.ravenwood;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * A class to store system properties defined by tests.
 */
public class RavenwoodTestProperties {
    final Map<String, String> mValues = new HashMap<>();

    /** Set of additional keys that should be considered readable */
    final Set<String> mKeyReadable = new HashSet<>();

    /** Set of additional keys that should be considered writable */
    final Set<String> mKeyWritable = new HashSet<>();

    public void setValue(String key, Object value) {
        final String valueString = (value == null) ? null : String.valueOf(value);
        if ((valueString == null) || valueString.isEmpty()) {
            mValues.remove(key);
        } else {
            mValues.put(key, valueString);
        }
    }

    public void setAccessNone(String key) {
        mKeyReadable.remove(key);
        mKeyWritable.remove(key);
    }

    public void setAccessReadOnly(String key) {
        mKeyReadable.add(key);
        mKeyWritable.remove(key);
    }

    public void setAccessReadWrite(String key) {
        mKeyReadable.add(key);
        mKeyWritable.add(key);
    }
}
Loading