Expand foldable parameters for filters

In order to pass filters for foldable configuration
without knowing in advance their name.

Test: unit tests
Bug: 193448311
Change-Id: I9d5ea05f68e8b010fe9e80e8d2a3bb221c169777
diff --git a/javatests/com/android/tradefed/testtype/suite/SuiteModuleLoaderTest.java b/javatests/com/android/tradefed/testtype/suite/SuiteModuleLoaderTest.java
index 379ac1d..ac22254 100644
--- a/javatests/com/android/tradefed/testtype/suite/SuiteModuleLoaderTest.java
+++ b/javatests/com/android/tradefed/testtype/suite/SuiteModuleLoaderTest.java
@@ -58,6 +58,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -445,13 +446,20 @@
 
     @Test
     public void testLoad_foldable() throws Exception {
+        Set<DeviceFoldableState> foldableStates = new LinkedHashSet<>();
+        foldableStates.add(new DeviceFoldableState(0, "DEFAULT"));
+        foldableStates.add(new DeviceFoldableState(1, "CLOSED"));
+        foldableStates.add(new DeviceFoldableState(2, "OPEN"));
         Map<String, LinkedHashSet<SuiteTestFilter>> excludeFilters = new LinkedHashMap<>();
         createFoldableModuleConfig("basemodule");
         Set<String> excludeFilterString = new LinkedHashSet<>();
         excludeFilterString.add("armeabi-v7a basemodule");
         excludeFilterString.add(
                 "armeabi-v7a basemodule[foldable:1:CLOSED] NativeDnsAsyncTest#Async_Cancel");
-        SuiteModuleLoader.addFilters(excludeFilterString, excludeFilters, null);
+        // All foldable configs will get injected
+        excludeFilterString.add(
+                "armeabi-v7a basemodule[all_foldable_states] NativeDnsAsyncTest#test2");
+        SuiteModuleLoader.addFilters(excludeFilterString, excludeFilters, null, foldableStates);
 
         mRepo =
                 new SuiteModuleLoader(
@@ -460,10 +468,6 @@
                         new ArrayList<>(),
                         new ArrayList<>());
         mRepo.setParameterizedModules(true);
-        Set<DeviceFoldableState> foldableStates = new LinkedHashSet<>();
-        foldableStates.add(new DeviceFoldableState(0, "DEFAULT"));
-        foldableStates.add(new DeviceFoldableState(1, "CLOSED"));
-        foldableStates.add(new DeviceFoldableState(2, "OPEN"));
         mRepo.setFoldableStates(foldableStates);
 
         List<String> patterns = new ArrayList<>();
@@ -477,9 +481,10 @@
         IConfiguration foldable1 = res.get("armeabi-v7a basemodule[foldable:1:CLOSED]");
         assertNotNull(foldable1);
         TestSuiteStub stubTest = (TestSuiteStub) foldable1.getTests().get(0);
-        assertEquals(1, stubTest.getExcludeFilters().size());
-        assertEquals(
-                "NativeDnsAsyncTest#Async_Cancel", stubTest.getExcludeFilters().iterator().next());
+        assertEquals(2, stubTest.getExcludeFilters().size());
+        Iterator<String> iteFilters = stubTest.getExcludeFilters().iterator();
+        assertEquals("NativeDnsAsyncTest#Async_Cancel", iteFilters.next());
+        assertEquals("NativeDnsAsyncTest#test2", iteFilters.next());
         // Ensure that appropriate metadata are set on the module config descriptor
         ConfigurationDescriptor descriptor = foldable1.getConfigurationDescription();
         assertEquals(
@@ -498,6 +503,10 @@
 
         IConfiguration foldable2 = res.get("armeabi-v7a basemodule[foldable:2:OPEN]");
         assertNotNull(foldable2);
+        TestSuiteStub stubTest2 = (TestSuiteStub) foldable2.getTests().get(0);
+        assertEquals(1, stubTest2.getExcludeFilters().size());
+        Iterator<String> iteFilters2 = stubTest2.getExcludeFilters().iterator();
+        assertEquals("NativeDnsAsyncTest#test2", iteFilters2.next());
         ConfigurationDescriptor descriptor2 = foldable2.getConfigurationDescription();
         assertEquals(
                 1,
diff --git a/javatests/com/android/tradefed/testtype/suite/SuiteTestFilterTest.java b/javatests/com/android/tradefed/testtype/suite/SuiteTestFilterTest.java
index 8702405..a1d3736 100644
--- a/javatests/com/android/tradefed/testtype/suite/SuiteTestFilterTest.java
+++ b/javatests/com/android/tradefed/testtype/suite/SuiteTestFilterTest.java
@@ -56,6 +56,16 @@
     }
 
     @Test
+    public void testParseFilter_moduleParam() {
+        SuiteTestFilter filter = SuiteTestFilter.createFrom("x86_64 module[instant]");
+        assertEquals("x86_64", filter.getAbi());
+        assertEquals("module", filter.getBaseName());
+        assertEquals("instant", filter.getParameterName());
+        assertNull(filter.getTest());
+        assertEquals("x86_64 module[instant]", filter.toString());
+    }
+
+    @Test
     public void testEquals() {
         SuiteTestFilter filter1 = SuiteTestFilter.createFrom("x86 module class#method");
         SuiteTestFilter filter2 = SuiteTestFilter.createFrom("x86 module class#method");
diff --git a/javatests/com/android/tradefed/testtype/suite/TestSuiteStub.java b/javatests/com/android/tradefed/testtype/suite/TestSuiteStub.java
index 9df42c5..1e203ba 100644
--- a/javatests/com/android/tradefed/testtype/suite/TestSuiteStub.java
+++ b/javatests/com/android/tradefed/testtype/suite/TestSuiteStub.java
@@ -37,6 +37,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -80,14 +81,14 @@
     protected List<TestDescription> mShardedTestToRun;
     protected Integer mShardIndex = null;
 
-    private Set<String> mIncludeAnnotationFilter = new HashSet<>();
+    private Set<String> mIncludeAnnotationFilter = new LinkedHashSet<>();
 
     @Option(
             name = "exclude-annotation",
             description = "The notAnnotation class name of the test name to run, can be repeated")
-    private Set<String> mExcludeAnnotationFilter = new HashSet<>();
+    private Set<String> mExcludeAnnotationFilter = new LinkedHashSet<>();
 
-    private Set<String> mExcludeFilters = new HashSet<>();
+    private Set<String> mExcludeFilters = new LinkedHashSet<>();
 
     /** Tests attempt. */
     private void testAttempt(ITestInvocationListener listener) throws DeviceNotAvailableException {
diff --git a/src/com/android/tradefed/testtype/suite/BaseTestSuite.java b/src/com/android/tradefed/testtype/suite/BaseTestSuite.java
index 184287a..e2ed1b0 100644
--- a/src/com/android/tradefed/testtype/suite/BaseTestSuite.java
+++ b/src/com/android/tradefed/testtype/suite/BaseTestSuite.java
@@ -248,8 +248,10 @@
             }
 
             // Create and populate the filters here
-            SuiteModuleLoader.addFilters(mIncludeFilters, mIncludeFiltersParsed, mAbis);
-            SuiteModuleLoader.addFilters(mExcludeFilters, mExcludeFiltersParsed, mAbis);
+            SuiteModuleLoader.addFilters(
+                    mIncludeFilters, mIncludeFiltersParsed, mAbis, mFoldableStates);
+            SuiteModuleLoader.addFilters(
+                    mExcludeFilters, mExcludeFiltersParsed, mAbis, mFoldableStates);
 
             String includeFilters = "";
             if (mIncludeFiltersParsed.size() > MAX_FILTER_DISPLAY) {
@@ -446,8 +448,10 @@
     }
 
     public void reevaluateFilters() {
-        SuiteModuleLoader.addFilters(mIncludeFilters, mIncludeFiltersParsed, mAbis);
-        SuiteModuleLoader.addFilters(mExcludeFilters, mExcludeFiltersParsed, mAbis);
+        SuiteModuleLoader.addFilters(
+                mIncludeFilters, mIncludeFiltersParsed, mAbis, mFoldableStates);
+        SuiteModuleLoader.addFilters(
+                mExcludeFilters, mExcludeFiltersParsed, mAbis, mFoldableStates);
     }
 
     /** Adds module args */
diff --git a/src/com/android/tradefed/testtype/suite/SuiteModuleLoader.java b/src/com/android/tradefed/testtype/suite/SuiteModuleLoader.java
index 08744b8..5fc0839 100644
--- a/src/com/android/tradefed/testtype/suite/SuiteModuleLoader.java
+++ b/src/com/android/tradefed/testtype/suite/SuiteModuleLoader.java
@@ -474,20 +474,44 @@
      * @param abis The Abis to consider in the filtering.
      */
     public static void addFilters(
-            Set<String> stringFilters, Map<String, LinkedHashSet<SuiteTestFilter>> filters, Set<IAbi> abis) {
+            Set<String> stringFilters, Map<String, LinkedHashSet<SuiteTestFilter>> filters,
+            Set<IAbi> abis, Set<DeviceFoldableState> foldableStates) {
         for (String filterString : stringFilters) {
-            SuiteTestFilter filter = SuiteTestFilter.createFrom(filterString);
-            String abi = filter.getAbi();
-            if (abi == null) {
-                for (IAbi a : abis) {
-                    addFilter(a.getName(), filter, filters);
+            SuiteTestFilter parentFilter = SuiteTestFilter.createFrom(filterString);
+            List<SuiteTestFilter> expanded = expandFoldableFilters(parentFilter, foldableStates);
+            for (SuiteTestFilter filter : expanded) {
+                String abi = filter.getAbi();
+                if (abi == null) {
+                    for (IAbi a : abis) {
+                        addFilter(a.getName(), filter, filters);
+                    }
+                } else {
+                    addFilter(abi, filter, filters);
                 }
-            } else {
-                addFilter(abi, filter, filters);
             }
         }
     }
 
+    private static List<SuiteTestFilter> expandFoldableFilters(
+            SuiteTestFilter filter, Set<DeviceFoldableState> foldableStates) {
+        List<SuiteTestFilter> expandedFilters = new ArrayList<>();
+        if (foldableStates == null || foldableStates.isEmpty()) {
+            expandedFilters.add(filter);
+            return expandedFilters;
+        }
+        if (!ModuleParameters.ALL_FOLDABLE_STATES.toString().equals(filter.getParameterName())) {
+            expandedFilters.add(filter);
+            return expandedFilters;
+        }
+        for (DeviceFoldableState state : foldableStates) {
+            String name = filter.getBaseName() + "[" + state.toString() + "]";
+            expandedFilters.add(
+                    new SuiteTestFilter(
+                            filter.getShardIndex(), filter.getAbi(), name, filter.getTest()));
+        }
+        return expandedFilters;
+    }
+
     private static void addFilter(
             String abi, SuiteTestFilter filter, Map<String, LinkedHashSet<SuiteTestFilter>> filters) {
         getFilterList(filters, AbiUtils.createId(abi, filter.getName())).add(filter);
diff --git a/src/com/android/tradefed/testtype/suite/SuiteTestFilter.java b/src/com/android/tradefed/testtype/suite/SuiteTestFilter.java
index e6fabd0..323bf25 100644
--- a/src/com/android/tradefed/testtype/suite/SuiteTestFilter.java
+++ b/src/com/android/tradefed/testtype/suite/SuiteTestFilter.java
@@ -167,6 +167,18 @@
         return mName;
     }
 
+    /**
+     * If the module is parameterized, returns the parameter value. Null if not parameterized.
+     */
+    public String getParameterName() {
+        // If the module looks parameterized, return the parameter name.
+        Matcher m = PARAMETERIZED_TEST_REGEX.matcher(mName);
+        if (m.find()) {
+            return m.group(2);
+        }
+        return null;
+    }
+
     /** @return the test identifier of this filter, or null if not specified. */
     public String getTest() {
         return mTest;