Merge "Give tests more control of crash parser behavior"
diff --git a/common/util/src/com/android/compatibility/common/util/CrashUtils.java b/common/util/src/com/android/compatibility/common/util/CrashUtils.java
index c5e94f6..657708d 100644
--- a/common/util/src/com/android/compatibility/common/util/CrashUtils.java
+++ b/common/util/src/com/android/compatibility/common/util/CrashUtils.java
@@ -17,6 +17,10 @@
 package com.android.compatibility.common.util;
 
 import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.math.BigInteger;
@@ -60,12 +64,9 @@
     private static Pattern sAbortMessageCheckPattern =
             Pattern.compile("(?i)Abort message.*?CHECK_");
 
-    /**
-     * returns true if the signal is a segmentation fault or bus error.
-     */
-    public static boolean isSecuritySignal(JSONObject crash) throws JSONException {
-        return crash.getString(SIGNAL).toLowerCase().matches("sig(segv|bus)");
-    }
+    public static final String SIGSEGV = "SIGSEGV";
+    public static final String SIGBUS = "SIGBUS";
+    public static final String SIGABRT = "SIGABRT";
 
     /**
      * returns the filename of the process.
@@ -79,14 +80,12 @@
      * Determines if the given input has a {@link com.android.compatibility.common.util.Crash} that
      * should fail an sts test
      *
-     * @param processPatterns list of patterns that match applicable process names
-     * @param checkMinAddr if the minimum fault address should be respected
      * @param crashes list of crashes to check
+     * @param config crash detection configuration object
      * @return if a crash is serious enough to fail an sts test
      */
-    public static boolean securityCrashDetected(
-            JSONArray crashes, boolean checkMinAddr, Pattern... processPatterns) {
-        return matchSecurityCrashes(crashes, checkMinAddr, processPatterns).length() > 0;
+    public static boolean securityCrashDetected(JSONArray crashes, Config config) {
+        return matchSecurityCrashes(crashes, config).length() > 0;
     }
 
     public static BigInteger getBigInteger(JSONObject source, String name) throws JSONException {
@@ -105,27 +104,34 @@
      * Determines which given inputs have a {@link com.android.compatibility.common.util.Crash} that
      * should fail an sts test
      *
-     * @param processPatterns list of patterns that match applicable process names
-     * @param checkMinAddr if the minimum fault address should be respected
      * @param crashes list of crashes to check
+     * @param config crash detection configuration object
      * @return the list of crashes serious enough to fail an sts test
      */
-    public static JSONArray matchSecurityCrashes(
-            JSONArray crashes, boolean checkMinAddr, Pattern... processPatterns) {
+    public static JSONArray matchSecurityCrashes(JSONArray crashes, Config config) {
         JSONArray securityCrashes = new JSONArray();
         for (int i = 0; i < crashes.length(); i++) {
             try {
                 JSONObject crash = crashes.getJSONObject(i);
-                if (!matchesAny(getProcessFileName(crash), processPatterns)) {
+
+                // match process patterns
+                if (!matchesAny(getProcessFileName(crash), config.processPatterns)) {
                     continue;
                 }
-                if (!isSecuritySignal(crash)) {
+
+                // match signal
+                String crashSignal = crash.getString(SIGNAL);
+                if (!config.signals.contains(crashSignal)) {
                     continue;
                 }
-                BigInteger faultAddress = getBigInteger(crash, FAULT_ADDRESS);
-                if (checkMinAddr && faultAddress != null
-                        && faultAddress.compareTo(MIN_CRASH_ADDR) < 0) {
-                    continue;
+
+                // if check specified, reject crash if address is unlikely to be security-related
+                if (config.checkMinAddress) {
+                    BigInteger faultAddress = getBigInteger(crash, FAULT_ADDRESS);
+                    if (faultAddress != null
+                            && faultAddress.compareTo(config.minCrashAddress) < 0) {
+                        continue;
+                    }
                 }
                 securityCrashes.put(crash);
             } catch (JSONException e) {}
@@ -136,7 +142,7 @@
     /**
      * returns true if the input matches any of the patterns.
      */
-    private static boolean matchesAny(String input, Pattern... patterns) {
+    private static boolean matchesAny(String input, Collection<Pattern> patterns) {
         for (Pattern p : patterns) {
             if (p.matcher(input).matches()) {
                 return true;
@@ -195,4 +201,53 @@
         }
         return crashes;
     }
+
+    public static class Config {
+        private boolean checkMinAddress = true;
+        private BigInteger minCrashAddress = MIN_CRASH_ADDR;
+        private List<String> signals = Arrays.asList(SIGSEGV, SIGBUS);
+        private List<Pattern> processPatterns = Collections.emptyList();
+
+        public Config setMinAddress(BigInteger minCrashAddress) {
+            this.minCrashAddress = minCrashAddress;
+            return this;
+        }
+
+        public Config checkMinAddress(boolean checkMinAddress) {
+            this.checkMinAddress = checkMinAddress;
+            return this;
+        }
+
+        public Config setSignals(String... signals) {
+            this.signals = Arrays.asList(signals);
+            return this;
+        }
+
+        public Config appendSignals(String... signals) {
+            Collections.addAll(this.signals, signals);
+            return this;
+        }
+
+        public Config setProcessPatterns(String... processPatternStrings) {
+            Pattern[] processPatterns = new Pattern[processPatternStrings.length];
+            for (int i = 0; i < processPatternStrings.length; i++) {
+                processPatterns[i] = Pattern.compile(processPatternStrings[i]);
+            }
+            return setProcessPatterns(processPatterns);
+        }
+
+        public Config setProcessPatterns(Pattern... processPatterns) {
+            this.processPatterns = Arrays.asList(processPatterns);
+            return this;
+        }
+
+        public List<Pattern> getProcessPatterns() {
+            return Collections.unmodifiableList(processPatterns);
+        }
+
+        public Config appendProcessPatterns(Pattern... processPatterns) {
+            Collections.addAll(this.processPatterns, processPatterns);
+            return this;
+        }
+    }
 }
diff --git a/common/util/tests/src/com/android/compatibility/common/util/CrashUtilsTest.java b/common/util/tests/src/com/android/compatibility/common/util/CrashUtilsTest.java
index 2138b7b..071733f 100644
--- a/common/util/tests/src/com/android/compatibility/common/util/CrashUtilsTest.java
+++ b/common/util/tests/src/com/android/compatibility/common/util/CrashUtilsTest.java
@@ -91,51 +91,61 @@
 
     @Test
     public void testValidCrash() throws Exception {
-        Assert.assertTrue(CrashUtils.securityCrashDetected(mCrashes, true,
-                Pattern.compile("synthetic_process_0")));
+        Assert.assertTrue(CrashUtils.securityCrashDetected(mCrashes, new CrashUtils.Config()
+                .checkMinAddress(true)
+                .setProcessPatterns(Pattern.compile("synthetic_process_0"))));
     }
 
     @Test
     public void testMissingName() throws Exception {
-        Assert.assertFalse(CrashUtils.securityCrashDetected(mCrashes, true,
-                Pattern.compile("")));
+        Assert.assertFalse(CrashUtils.securityCrashDetected(mCrashes, new CrashUtils.Config()
+                .checkMinAddress(true)
+                .setProcessPatterns(Pattern.compile(""))));
     }
 
     @Test
     public void testSIGABRT() throws Exception {
-        Assert.assertFalse(CrashUtils.securityCrashDetected(mCrashes, true,
-                Pattern.compile("installd")));
+        Assert.assertFalse(CrashUtils.securityCrashDetected(mCrashes, new CrashUtils.Config()
+                .checkMinAddress(true)
+                .setProcessPatterns(Pattern.compile("installd"))));
     }
 
     @Test
     public void testFaultAddressBelowMin() throws Exception {
-        Assert.assertFalse(CrashUtils.securityCrashDetected(mCrashes, true,
-                Pattern.compile("synthetic_process_1")));
+        Assert.assertFalse(CrashUtils.securityCrashDetected(mCrashes, new CrashUtils.Config()
+                .checkMinAddress(true)
+                .setProcessPatterns(Pattern.compile("synthetic_process_1"))));
     }
 
     @Test
     public void testIgnoreMinAddressCheck() throws Exception {
-        Assert.assertTrue(CrashUtils.securityCrashDetected(mCrashes, false,
-                Pattern.compile("synthetic_process_1")));
+        Assert.assertTrue(CrashUtils.securityCrashDetected(mCrashes, new CrashUtils.Config()
+                .checkMinAddress(false)
+                .setProcessPatterns(Pattern.compile("synthetic_process_1"))));
     }
 
     @Test
     public void testBadAbortMessage() throws Exception {
-        Assert.assertFalse(CrashUtils.securityCrashDetected(mCrashes, true,
-                Pattern.compile("generic")));
+        Assert.assertFalse(CrashUtils.securityCrashDetected(mCrashes, new CrashUtils.Config()
+                .checkMinAddress(true)
+                .setProcessPatterns(Pattern.compile("generic"))));
     }
 
     @Test
     public void testGoodAndBadCrashes() throws Exception {
-        Assert.assertTrue(CrashUtils.securityCrashDetected(mCrashes, true,
-                Pattern.compile("synthetic_process_0"), Pattern.compile("generic")));
+        Assert.assertTrue(CrashUtils.securityCrashDetected(mCrashes, new CrashUtils.Config()
+                .checkMinAddress(true)
+                .setProcessPatterns(
+                        Pattern.compile("synthetic_process_0"),
+                        Pattern.compile("generic"))));
     }
 
     @Test
     public void testNullFaultAddress() throws Exception {
         JSONArray crashes = new JSONArray();
         crashes.put(createCrashJson(8373, 8414, "loo", "com.android.bluetooth", null, "SIGSEGV"));
-        Assert.assertTrue(CrashUtils.securityCrashDetected(crashes, true,
-                Pattern.compile("com\\.android\\.bluetooth")));
+        Assert.assertTrue(CrashUtils.securityCrashDetected(crashes, new CrashUtils.Config()
+                .checkMinAddress(true)
+                .setProcessPatterns(Pattern.compile("com\\.android\\.bluetooth"))));
     }
 }