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

Commit e5725b32 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge "Ravenwood support for feature flags." into main

parents 672d0aac 9ac0521c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -179,8 +179,10 @@ android_ravenwood_test {
        "androidx.test.ext.junit",
        "mockito_ravenwood",
        "platform-test-annotations",
        "flag-junit",
    ],
    srcs: [
        "src/android/os/BuildTest.java",
        "src/android/os/FileUtilsTest.java",
        "src/android/util/**/*.java",
        "src/com/android/internal/util/**/*.java",
+35 −3
Original line number Diff line number Diff line
@@ -16,19 +16,37 @@

package android.os;

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

import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.ravenwood.RavenwoodRule;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import junit.framework.Assert;
import junit.framework.TestCase;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Provides test cases for android.os.Build and, in turn, many of the
 * system properties set by the build system.
 */
public class BuildTest extends TestCase {

@RunWith(AndroidJUnit4.class)
public class BuildTest {
    private static final String TAG = "BuildTest";

    @Rule
    public final RavenwoodRule mRavenwood = new RavenwoodRule();

    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    /**
     * Asserts that a String is non-null and non-empty.  If it is not,
     * an AssertionFailedError is thrown with the given message.
@@ -50,7 +68,9 @@ public class BuildTest extends TestCase {
    /**
     * Asserts that all android.os.Build fields are non-empty and/or in a valid range.
     */
    @Test
    @SmallTest
    @IgnoreUnderRavenwood(blockedBy = Build.class)
    public void testBuildFields() throws Exception {
        assertNotEmpty("ID", Build.ID);
        assertNotEmpty("DISPLAY", Build.DISPLAY);
@@ -72,4 +92,16 @@ public class BuildTest extends TestCase {
        // (e.g., must be a C identifier, must be a valid filename, must not contain any spaces)
        // add tests for them.
    }

    @Test
    public void testFlagEnabled() throws Exception {
        mSetFlagsRule.enableFlags(Flags.FLAG_ANDROID_OS_BUILD_VANILLA_ICE_CREAM);
        assertTrue(Flags.androidOsBuildVanillaIceCream());
    }

    @Test
    public void testFlagDisabled() throws Exception {
        mSetFlagsRule.disableFlags(Flags.FLAG_ANDROID_OS_BUILD_VANILLA_ICE_CREAM);
        assertFalse(Flags.androidOsBuildVanillaIceCream());
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -3,6 +3,9 @@
# Keep all AIDL interfaces
class :aidl stubclass

# Keep all feature flag implementations
class :feature_flags stubclass

# Collections
class android.util.ArrayMap stubclass
class android.util.ArraySet stubclass
+23 −2
Original line number Diff line number Diff line
@@ -71,10 +71,10 @@ public class MyCodeTest {
Once you’ve defined your test, you can use typical commands to execute it locally:

```
$ atest MyTestsRavenwood
$ atest --host MyTestsRavenwood
```

> **Note:** There's a known bug where `atest` currently requires a connected device to run Ravenwood tests, but that device isn't used for testing.
> **Note:** There's a known bug where `atest` currently requires a connected device to run Ravenwood tests, but that device isn't used for testing. Using the `--host` argument above is a way to bypass this requirement until bug #312525698 is fixed.

You can also run your new tests automatically via `TEST_MAPPING` rules like this:

@@ -89,6 +89,27 @@ You can also run your new tests automatically via `TEST_MAPPING` rules like this
}
```

## Strategies for feature flags

Ravenwood supports writing tests against logic that uses feature flags through the existing `SetFlagsRule` infrastructure maintained by the feature flagging team:

```
import android.platform.test.flag.junit.SetFlagsRule;

@RunWith(AndroidJUnit4.class)
public class MyCodeTest {
    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    @Test
    public void testEnabled() {
        mSetFlagsRule.enableFlags(Flags.FLAG_MY_FLAG);
        // verify test logic that depends on flag being enabled
    }
```

This naturally composes together well with any `RavenwoodRule` that your test may need.

## Strategies for migration/bivalent tests

Ravenwood aims to support tests that are written in a “bivalent” way, where the same test code can run on both a real Android device and under a Ravenwood environment.
+17 −1
Original line number Diff line number Diff line
@@ -23,12 +23,16 @@ import com.android.hoststubgen.asm.ClassNodes
class AndroidHeuristicsFilter(
        private val classes: ClassNodes,
        val aidlPolicy: FilterPolicyWithReason?,
        val featureFlagsPolicy: FilterPolicyWithReason?,
        fallback: OutputFilter
) : DelegatingFilter(fallback) {
    override fun getPolicyForClass(className: String): FilterPolicyWithReason {
        if (aidlPolicy != null && classes.isAidlClass(className)) {
            return aidlPolicy
        }
        if (featureFlagsPolicy != null && classes.isFeatureFlagsClass(className)) {
            return featureFlagsPolicy
        }
        return super.getPolicyForClass(className)
    }
}
@@ -41,3 +45,15 @@ private fun ClassNodes.isAidlClass(className: String): Boolean {
            hasClass("$className\$Stub") &&
            hasClass("$className\$Stub\$Proxy")
}

/**
 * @return if a given class "seems like" an feature flags class.
 */
private fun ClassNodes.isFeatureFlagsClass(className: String): Boolean {
    // Matches template classes defined here:
    // https://cs.android.com/android/platform/superproject/+/master:build/make/tools/aconfig/templates/
    return className.endsWith("/Flags")
            || className.endsWith("/FeatureFlags")
            || className.endsWith("/FeatureFlagsImpl")
            || className.endsWith("/FakeFeatureFlagsImpl");
}
Loading