TestMapping: Support comments in TEST_MAPPING file.
Reason: TEST_MAPPING file uses JSON format, which doesn't support
comments. There is a request that it would be helpful if support
comments in TEST_MAPPING file.
Bug: 118691442
Test: unittests.
Change-Id: I35eae804a88cd38a9caa63000da99db80d56135e
Merged-In: I35eae804a88cd38a9caa63000da99db80d56135e
diff --git a/src/com/android/tradefed/util/testmapping/TestMapping.java b/src/com/android/tradefed/util/testmapping/TestMapping.java
index 79da7c2..fdc019d 100644
--- a/src/com/android/tradefed/util/testmapping/TestMapping.java
+++ b/src/com/android/tradefed/util/testmapping/TestMapping.java
@@ -42,6 +42,8 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -63,6 +65,10 @@
private static final String DISABLED_PRESUBMIT_TESTS_FILE = "disabled-presubmit-tests";
private Map<String, Set<TestInfo>> mTestCollection = null;
+ // Pattern used to identify comments start with "//" or "#" in TEST_MAPPING.
+ private static final Pattern COMMENTS_REGEX = Pattern.compile(
+ "(?m)[\\s\\t]*(//|#).*|(\".*?\")");
+ private static final Set<String> COMMENTS = new HashSet<>(Arrays.asList("#", "//"));
/**
* Constructor to create a {@link TestMapping} object from a path to TEST_MAPPING file.
@@ -75,7 +81,8 @@
String relativePath = testMappingsDir.relativize(path.getParent()).toString();
String errorMessage = null;
try {
- String content = String.join("", Files.readAllLines(path, StandardCharsets.UTF_8));
+ String content = removeComments(
+ String.join("\n", Files.readAllLines(path, StandardCharsets.UTF_8)));
if (content != null) {
JSONTokener tokener = new JSONTokener(content);
JSONObject root = new JSONObject(tokener);
@@ -139,6 +146,26 @@
}
/**
+ * Helper to remove comments in a TEST_MAPPING file to valid format. Only "//" and "#" are
+ * regarded as comments.
+ *
+ * @param jsonContent A {@link String} of json which content is from a TEST_MAPPING file.
+ * @return A {@link String} of valid json without comments.
+ */
+ @VisibleForTesting
+ static String removeComments(String jsonContent) {
+ StringBuffer out = new StringBuffer();
+ Matcher matcher = COMMENTS_REGEX.matcher(jsonContent);
+ while (matcher.find()) {
+ if (COMMENTS.contains(matcher.group(1))) {
+ matcher.appendReplacement(out, "");
+ }
+ }
+ matcher.appendTail(out);
+ return out.toString();
+ }
+
+ /**
* Helper to get all tests set in a TEST_MAPPING file for a given group.
*
* @param testGroup A {@link String} of the test group.
diff --git a/tests/res/testdata/test_mapping_golden1 b/tests/res/testdata/test_mapping_golden1
new file mode 100644
index 0000000..db3998d
--- /dev/null
+++ b/tests/res/testdata/test_mapping_golden1
@@ -0,0 +1,14 @@
+{
+ "presubmit": [
+ {
+ "name": "test1",
+ "host": true,
+ "include-filter": "testClass#testMethod"
+ }
+ ],
+ "imports": [
+ {
+ "path": "path1//path2//path3"
+ }
+ ]
+}
diff --git a/tests/res/testdata/test_mapping_golden2 b/tests/res/testdata/test_mapping_golden2
new file mode 100644
index 0000000..07486c0
--- /dev/null
+++ b/tests/res/testdata/test_mapping_golden2
@@ -0,0 +1,48 @@
+{
+ "presubmit": [
+ {
+ "name": "test1",
+ "host": true
+ },
+ {
+ "name": "suite/stub1"
+ },
+ {
+ "name": "suite/stub2",
+ "keywords": ["key_1"]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "test2",
+ "options": [
+ {
+ "instrumentation-arg": "annotation=android.platform.test.annotations.Presubmit"
+ }
+ ]
+ },
+ {
+ "name": "instrument",
+ "options": [
+ {
+ "run-name": "some-name"
+ }
+ ]
+ }
+ ],
+ "othertype": [
+ {
+ "name": "test3",
+ "options": [
+ {
+ "just-an-option": ""
+ }
+ ]
+ }
+ ],
+ "imports": [
+ {
+ "path": "folder1//folder2//folder3"
+ }
+ ]
+}
diff --git a/tests/res/testdata/test_mapping_with_comments1 b/tests/res/testdata/test_mapping_with_comments1
new file mode 100644
index 0000000..3f4083f
--- /dev/null
+++ b/tests/res/testdata/test_mapping_with_comments1
@@ -0,0 +1,16 @@
+{#comments1
+ "presubmit": [//comments2 // comments3 # comment4
+ #comments3
+ { #comments4
+ "name": "test1",#comments5
+//comments6
+ "host": true,//comments7
+ "include-filter": "testClass#testMethod" #comment11 // another comments
+ }#comments8
+ ],#comments9 // another comments
+ "imports": [
+ {
+ "path": "path1//path2//path3"#comment12
+ }
+ ]
+}#comments10
diff --git a/tests/res/testdata/test_mapping_with_comments2 b/tests/res/testdata/test_mapping_with_comments2
new file mode 100644
index 0000000..da5a503
--- /dev/null
+++ b/tests/res/testdata/test_mapping_with_comments2
@@ -0,0 +1,49 @@
+{//comment..// another comment # another comment
+ "presubmit": [ #comment //another comment #// another comment
+ { # comment
+ "name": "test1",//comment
+ "host": true#comment
+ },#comment
+ {
+ "name": "suite/stub1" // comment # comment
+ },
+ {
+ "name": "suite/stub2",
+ "keywords": ["key_1"] # comment
+ } # comment
+ ], // comment
+ "postsubmit": [
+ {
+ "name": "test2",
+ "options": [
+ {
+ "instrumentation-arg": "annotation=android.platform.test.annotations.Presubmit" //comment
+ }
+ ]
+ },
+ {
+ "name": "instrument",
+ "options": [
+ {
+ "run-name": "some-name"
+ }
+ ]
+ }
+ ],
+ "othertype": [ // another comment
+ {
+ "name": "test3",
+ "options": [
+ {
+ "just-an-option": ""##comment
+ }////another comment
+ //// another comment...
+ ]
+ }
+ ],
+ "imports": [
+ {
+ "path": "folder1//folder2//folder3"//comment... # another comment // another comment
+ }
+ ]
+}
diff --git a/tests/src/com/android/tradefed/util/testmapping/TestMappingTest.java b/tests/src/com/android/tradefed/util/testmapping/TestMappingTest.java
index 56cfa8c..8c53c17 100644
--- a/tests/src/com/android/tradefed/util/testmapping/TestMappingTest.java
+++ b/tests/src/com/android/tradefed/util/testmapping/TestMappingTest.java
@@ -32,6 +32,8 @@
import java.io.File;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
@@ -504,4 +506,35 @@
FileUtil.recursiveDelete(tempDir);
}
}
+
+ /** Test for {@link TestMapping#removeComments()} for removing comments in TEST_MAPPING file. */
+ @Test
+ public void testRemoveComments() throws Exception {
+ String jsonString = getJsonStringByName("test_mapping_with_comments1");
+ String goldenString = getJsonStringByName("test_mapping_golden1");
+ assertEquals(TestMapping.removeComments(jsonString), goldenString);
+ }
+
+ /** Test for {@link TestMapping#removeComments()} for removing comments in TEST_MAPPING file. */
+ @Test
+ public void testRemoveComments2() throws Exception {
+ String jsonString = getJsonStringByName("test_mapping_with_comments2");
+ String goldenString = getJsonStringByName("test_mapping_golden2");
+ assertEquals(TestMapping.removeComments(jsonString), goldenString);
+ }
+
+ private String getJsonStringByName(String fileName) throws Exception {
+ File tempDir = null;
+ try {
+ tempDir = FileUtil.createTempDir("test_mapping");
+ File srcDir = FileUtil.createTempDir("src", tempDir);
+ String srcFile = File.separator + TEST_DATA_DIR + File.separator + fileName;
+ InputStream resourceStream = this.getClass().getResourceAsStream(srcFile);
+ FileUtil.saveResourceFile(resourceStream, srcDir, TEST_MAPPING);
+ Path file = Paths.get(srcDir.getAbsolutePath(), TEST_MAPPING);
+ return String.join("\n", Files.readAllLines(file, StandardCharsets.UTF_8));
+ } finally {
+ FileUtil.recursiveDelete(tempDir);
+ }
+ }
}