Add dynamic class rule injection to microbenchmarks.

Bug: 123281375
Test: atest MicrobenchmarkTest

Original change: https://android-review.googlesource.com/c/platform/platform_testing/+/1838193

Change-Id: I5deee3ef50f8eb0343428df3950072ba3c57cd5c
(cherry picked from commit 760129f9e3414a64b6ea1e52462274c01f2d2be9)
Merged-In: I5deee3ef50f8eb0343428df3950072ba3c57cd5c
Merged-In: I5796d5a0bd5c5fd3ad9d0f7e25c6ddf1a7e1b875
Merged-In: If77c96e34f74c37c0adc2264aa8daf035a885835
Merged-In: I96813a8a2c6a477d96178a0c6ec1b64ff5c575a5
diff --git a/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Microbenchmark.java b/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Microbenchmark.java
index b6c6f13..7ad17de 100644
--- a/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Microbenchmark.java
+++ b/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Microbenchmark.java
@@ -75,8 +75,10 @@
     @VisibleForTesting static final String MAX_BATTERY_DRAIN_OPTION = "max-battery-drain";
     // Use these options to inject rules at runtime via the command line. For details, please see
     // documentation for DynamicRuleChain.
-    @VisibleForTesting static final String DYNAMIC_OUTER_RULES_OPTION = "outer-rules";
-    @VisibleForTesting static final String DYNAMIC_INNER_RULES_OPTION = "inner-rules";
+    @VisibleForTesting static final String DYNAMIC_OUTER_CLASS_RULES_OPTION = "outer-class-rules";
+    @VisibleForTesting static final String DYNAMIC_INNER_CLASS_RULES_OPTION = "inner-class-rules";
+    @VisibleForTesting static final String DYNAMIC_OUTER_TEST_RULES_OPTION = "outer-test-rules";
+    @VisibleForTesting static final String DYNAMIC_INNER_TEST_RULES_OPTION = "inner-test-rules";
 
     // Options for aligning with the battery charge (coulomb) counter for power tests. We want to
     // start microbenchmarks just after the coulomb counter has decremented to account for the
@@ -283,9 +285,9 @@
         Statement result = statement;
         List<TestRule> testRules = new ArrayList<>();
         // Inner dynamic rules should be included first because RunRules applies rules inside-out.
-        testRules.add(new DynamicRuleChain(DYNAMIC_INNER_RULES_OPTION, mArguments));
+        testRules.add(new DynamicRuleChain(DYNAMIC_INNER_TEST_RULES_OPTION, mArguments));
         testRules.addAll(getTestRules(target));
-        testRules.add(new DynamicRuleChain(DYNAMIC_OUTER_RULES_OPTION, mArguments));
+        testRules.add(new DynamicRuleChain(DYNAMIC_OUTER_TEST_RULES_OPTION, mArguments));
         // Apply legacy MethodRules, if they don't overlap with TestRules.
         for (org.junit.rules.MethodRule each : rules(target)) {
             if (!testRules.contains(each)) {
@@ -297,6 +299,18 @@
         return result;
     }
 
+    /** Add {@link DynamicRuleChain} to existing class rules. */
+    @Override
+    protected List<TestRule> classRules() {
+        List<TestRule> classRules = new ArrayList<>();
+        // Inner dynamic class rules should be included first because RunRules applies rules inside
+        // -out.
+        classRules.add(new DynamicRuleChain(DYNAMIC_INNER_CLASS_RULES_OPTION, mArguments));
+        classRules.addAll(super.classRules());
+        classRules.add(new DynamicRuleChain(DYNAMIC_OUTER_CLASS_RULES_OPTION, mArguments));
+        return classRules;
+    }
+
     /**
      * Combine the {@code #runChild}, {@code #methodBlock}, and final {@code #runLeaf} methods to
      * implement the specific {@code Microbenchmark} test behavior. In particular, (1) keep track of
diff --git a/libraries/health/runners/microbenchmark/tests/src/android/platform/test/microbenchmark/MicrobenchmarkTest.java b/libraries/health/runners/microbenchmark/tests/src/android/platform/test/microbenchmark/MicrobenchmarkTest.java
index 0b44d1a..fa8e64a 100644
--- a/libraries/health/runners/microbenchmark/tests/src/android/platform/test/microbenchmark/MicrobenchmarkTest.java
+++ b/libraries/health/runners/microbenchmark/tests/src/android/platform/test/microbenchmark/MicrobenchmarkTest.java
@@ -30,6 +30,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestRule;
@@ -418,53 +419,102 @@
                 .inOrder();
     }
 
-    /**
-     * Test successive iteration will be executed when the terminate on test fail option is
-     * disabled.
-     */
+    /** Test dynamic test rule injection. */
     @Test
-    public void testDynamicRuleInjection() throws InitializationError {
+    public void testDynamicTestRuleInjection() throws InitializationError {
         Bundle args = new Bundle();
         args.putString("iterations", "2");
         args.putString("rename-iterations", "false");
         args.putString("terminate-on-test-fail", "false");
-        args.putString(Microbenchmark.DYNAMIC_INNER_RULES_OPTION, LoggingRule1.class.getName());
-        args.putString(Microbenchmark.DYNAMIC_OUTER_RULES_OPTION, LoggingRule2.class.getName());
+        args.putString(
+                Microbenchmark.DYNAMIC_INNER_TEST_RULES_OPTION, LoggingRule1.class.getName());
+        args.putString(
+                Microbenchmark.DYNAMIC_OUTER_TEST_RULES_OPTION, LoggingRule2.class.getName());
         LoggingMicrobenchmark loggingRunner =
-                new LoggingMicrobenchmark(LoggingTestWithRule.class, args);
+                new LoggingMicrobenchmark(LoggingTestWithRules.class, args);
+        loggingRunner.setOperationLog(sLogs);
+        new JUnitCore().run(loggingRunner);
+        assertThat(sLogs)
+                .containsExactly(
+                        "hardcoded class rule starting",
+                        "logging rule 2 starting",
+                        "hardcoded test rule starting",
+                        "logging rule 1 starting",
+                        "before",
+                        "tight before",
+                        "begin: testMethod("
+                                + "android.platform.test.microbenchmark.MicrobenchmarkTest"
+                                + "$LoggingTestWithRules)",
+                        "test",
+                        "end",
+                        "tight after",
+                        "after",
+                        "logging rule 1 finished",
+                        "hardcoded test rule finished",
+                        "logging rule 2 finished",
+                        "logging rule 2 starting",
+                        "hardcoded test rule starting",
+                        "logging rule 1 starting",
+                        "before",
+                        "tight before",
+                        "begin: testMethod("
+                                + "android.platform.test.microbenchmark.MicrobenchmarkTest"
+                                + "$LoggingTestWithRules)",
+                        "test",
+                        "end",
+                        "tight after",
+                        "after",
+                        "logging rule 1 finished",
+                        "hardcoded test rule finished",
+                        "logging rule 2 finished",
+                        "hardcoded class rule finished")
+                .inOrder();
+    }
+
+    /** Test dynamic class rule injection. */
+    @Test
+    public void testDynamicClassRuleInjection() throws InitializationError {
+        Bundle args = new Bundle();
+        args.putString("iterations", "2");
+        args.putString("rename-iterations", "false");
+        args.putString("terminate-on-test-fail", "false");
+        args.putString(
+                Microbenchmark.DYNAMIC_INNER_CLASS_RULES_OPTION, LoggingRule1.class.getName());
+        args.putString(
+                Microbenchmark.DYNAMIC_OUTER_CLASS_RULES_OPTION, LoggingRule2.class.getName());
+        LoggingMicrobenchmark loggingRunner =
+                new LoggingMicrobenchmark(LoggingTestWithRules.class, args);
         loggingRunner.setOperationLog(sLogs);
         new JUnitCore().run(loggingRunner);
         assertThat(sLogs)
                 .containsExactly(
                         "logging rule 2 starting",
-                        "hardcoded rule starting",
+                        "hardcoded class rule starting",
                         "logging rule 1 starting",
+                        "hardcoded test rule starting",
                         "before",
                         "tight before",
                         "begin: testMethod("
                                 + "android.platform.test.microbenchmark.MicrobenchmarkTest"
-                                + "$LoggingTestWithRule)",
+                                + "$LoggingTestWithRules)",
                         "test",
                         "end",
                         "tight after",
                         "after",
-                        "logging rule 1 finished",
-                        "hardcoded rule finished",
-                        "logging rule 2 finished",
-                        "logging rule 2 starting",
-                        "hardcoded rule starting",
-                        "logging rule 1 starting",
+                        "hardcoded test rule finished",
+                        "hardcoded test rule starting",
                         "before",
                         "tight before",
                         "begin: testMethod("
                                 + "android.platform.test.microbenchmark.MicrobenchmarkTest"
-                                + "$LoggingTestWithRule)",
+                                + "$LoggingTestWithRules)",
                         "test",
                         "end",
                         "tight after",
                         "after",
+                        "hardcoded test rule finished",
                         "logging rule 1 finished",
-                        "hardcoded rule finished",
+                        "hardcoded class rule finished",
                         "logging rule 2 finished")
                 .inOrder();
     }
@@ -565,18 +615,32 @@
         }
     }
 
-    public static class LoggingTestWithRule extends LoggingTest {
+    public static class LoggingTestWithRules extends LoggingTest {
+        @ClassRule
+        public static TestRule hardCodedClassRule =
+                new TestWatcher() {
+                    @Override
+                    public void starting(Description description) {
+                        sLogs.add("hardcoded class rule starting");
+                    }
+
+                    @Override
+                    public void finished(Description description) {
+                        sLogs.add("hardcoded class rule finished");
+                    }
+                };
+
         @Rule
         public TestRule hardCodedRule =
                 new TestWatcher() {
                     @Override
                     public void starting(Description description) {
-                        sLogs.add("hardcoded rule starting");
+                        sLogs.add("hardcoded test rule starting");
                     }
 
                     @Override
                     public void finished(Description description) {
-                        sLogs.add("hardcoded rule finished");
+                        sLogs.add("hardcoded test rule finished");
                     }
                 };
     }