Add support for "large" tagged tests to have non-default timeout

Bug: 19657861
Change-Id: Ieba63fa36a139629572eb8b288771b48980d2ef6
diff --git a/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java
index d0a3a37..efb53d5 100644
--- a/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java
+++ b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java
@@ -22,9 +22,9 @@
 import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
 
-import vogar.Expectation;
 import vogar.ExpectationStore;
 import vogar.ModeId;
+import vogar.Result;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -161,7 +161,7 @@
     }
 
     public static boolean isKnownFailure(ExpectationStore store, String fullname) {
-        return store != null && store.get(fullname) != Expectation.SUCCESS;
+        return store != null && store.get(fullname).getResult() != Result.SUCCESS;
     }
 
     public static void main(String[] args) throws Exception {
diff --git a/libs/vogar-expect/src/vogar/Expectation.java b/libs/vogar-expect/src/vogar/Expectation.java
index f065f42..ddbc233 100644
--- a/libs/vogar-expect/src/vogar/Expectation.java
+++ b/libs/vogar-expect/src/vogar/Expectation.java
@@ -16,7 +16,6 @@
 
 package vogar;
 
-import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.Set;
 import java.util.regex.Pattern;
@@ -37,14 +36,6 @@
  */
 public final class Expectation {
 
-    /** The pattern to use when no expected output is specified */
-    public static final Pattern MATCH_ALL_PATTERN
-            = Pattern.compile(".*", Pattern.MULTILINE | Pattern.DOTALL);
-
-    /** The expectation of a general successful run. */
-    public static final Expectation SUCCESS = new Expectation(Result.SUCCESS, MATCH_ALL_PATTERN,
-            Collections.<String>emptySet(), "", -1);
-
     /** Justification for this expectation */
     private final String description;
 
diff --git a/libs/vogar-expect/src/vogar/ExpectationStore.java b/libs/vogar-expect/src/vogar/ExpectationStore.java
index cfa20e9..1818889 100644
--- a/libs/vogar-expect/src/vogar/ExpectationStore.java
+++ b/libs/vogar-expect/src/vogar/ExpectationStore.java
@@ -26,6 +26,7 @@
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -50,7 +51,17 @@
  * expectation, the outcome expectation will be returned.
  */
 public final class ExpectationStore {
+
+    /** The pattern to use when no expected output is specified */
+    private static final Pattern MATCH_ALL_PATTERN
+            = Pattern.compile(".*", Pattern.MULTILINE | Pattern.DOTALL);
+
+    /** The expectation of a general successful run. */
+    private static final Expectation SUCCESS = new Expectation(Result.SUCCESS, MATCH_ALL_PATTERN,
+            Collections.<String>emptySet(), "", -1);
+
     private static final int PATTERN_FLAGS = Pattern.MULTILINE | Pattern.DOTALL;
+
     private final Map<String, Expectation> outcomes = new LinkedHashMap<String, Expectation>();
     private final Map<String, Expectation> failures = new LinkedHashMap<String, Expectation>();
 
@@ -62,7 +73,7 @@
      */
     public Expectation get(String name) {
         Expectation byName = getByNameOrPackage(name);
-        return byName != null ? byName : Expectation.SUCCESS;
+        return byName != null ? byName : SUCCESS;
     }
 
     /**
@@ -87,7 +98,7 @@
         }
 
         Expectation byName = getByNameOrPackage(outcome.getName());
-        return byName != null ? byName : Expectation.SUCCESS;
+        return byName != null ? byName : SUCCESS;
     }
 
     private Expectation getByNameOrPackage(String name) {
@@ -142,7 +153,7 @@
     private void readExpectation(JsonReader reader, ModeId mode) throws IOException {
         boolean isFailure = false;
         Result result = Result.SUCCESS;
-        Pattern pattern = Expectation.MATCH_ALL_PATTERN;
+        Pattern pattern = MATCH_ALL_PATTERN;
         Set<String> names = new LinkedHashSet<String>();
         Set<String> tags = new LinkedHashSet<String>();
         Set<ModeId> modes = null;
diff --git a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
index 833bf69..328b855 100644
--- a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
+++ b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
@@ -20,6 +20,7 @@
 
 import vogar.Expectation;
 import vogar.ExpectationStore;
+import vogar.Result;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -228,7 +229,8 @@
     }
 
     public static boolean isKnownFailure(ExpectationStore expectationStore, String testName) {
-        return expectationStore != null && expectationStore.get(testName) != Expectation.SUCCESS;
+        return expectationStore != null
+            && expectationStore.get(testName).getResult() != Result.SUCCESS;
     }
 
     // Returns the list of ABIs supported by this TestCase on this architecture.
diff --git a/tools/utils/CollectAllTests.java b/tools/utils/CollectAllTests.java
index 95b77f2..83c451e 100644
--- a/tools/utils/CollectAllTests.java
+++ b/tools/utils/CollectAllTests.java
@@ -447,6 +447,9 @@
                                                                     expectations,
                                                                     testClassName,
                                                                     testName);
+        int timeoutInMinutes = VogarUtils.timeoutInMinutes(expectations,
+                                                           testClassName,
+                                                           testName);
         TestClass testClass;
         if (testCases.containsKey(testClassName)) {
             testClass = testCases.get(testClassName);
@@ -456,7 +459,7 @@
         }
 
         testClass.mCases.add(new TestMethod(testName, "", "", supportedAbis,
-              knownFailure, false, false));
+              knownFailure, false, false, timeoutInMinutes));
     }
 
     private static boolean isJunit3Test(Class<?> klass) {
diff --git a/tools/utils/DescriptionGenerator.java b/tools/utils/DescriptionGenerator.java
index 09e1118..1e2542f 100644
--- a/tools/utils/DescriptionGenerator.java
+++ b/tools/utils/DescriptionGenerator.java
@@ -86,6 +86,7 @@
     static final String ATTRIBUTE_NAME = "name";
     static final String ATTRIBUTE_ABIS = "abis";
     static final String ATTRIBUTE_HOST_CONTROLLER = "HostController";
+    static final String ATTRIBUTE_TIMEOUT = "timeout";
 
     static final String XML_OUTPUT_PATH = "./description.xml";
 
@@ -438,6 +439,10 @@
                     if ((caze.mController != null) && (caze.mController.length() != 0)) {
                         setAttribute(caseNode, ATTRIBUTE_HOST_CONTROLLER, caze.mController);
                     }
+                    if (caze.mTimeoutInMinutes != 0) {
+                        setAttribute(caseNode, ATTRIBUTE_TIMEOUT,
+                                     Integer.toString(caze.mTimeoutInMinutes));
+                    }
 
                     if (caze.mDescription != null && !caze.mDescription.equals("")) {
                         caseNode.appendChild(mDoc.createElement(TAG_DESCRIPTION))
@@ -573,9 +578,10 @@
                             VogarUtils.buildFullTestName(clazz.toString(), name));
                     Set<String> supportedAbis =
                             VogarUtils.extractSupportedAbis(architecture, expectation);
+                    int timeoutInMinutes = VogarUtils.timeoutInMinutes(expectation);
                     cases.add(new TestMethod(
                             name, method.commentText(), controller, supportedAbis,
-                                    knownFailure, isBroken, isSuppressed));
+                                    knownFailure, isBroken, isSuppressed, timeoutInMinutes));
                 }
             }
 
@@ -635,6 +641,7 @@
         String mKnownFailure;
         boolean mIsBroken;
         boolean mIsSuppressed;
+        int mTimeoutInMinutes;  // zero to use default timeout.
 
         /**
          * Construct an test case object.
@@ -644,7 +651,10 @@
          * @param knownFailure The reason of known failure.
          */
         TestMethod(String name, String description, String controller, Set<String> abis,
-                String knownFailure, boolean isBroken, boolean isSuppressed) {
+                String knownFailure, boolean isBroken, boolean isSuppressed, int timeoutInMinutes) {
+            if (timeoutInMinutes < 0) {
+                throw new IllegalArgumentException("timeoutInMinutes < 0: " + timeoutInMinutes);
+            }
             mName = name;
             mDescription = description;
             mController = controller;
@@ -652,6 +662,7 @@
             mKnownFailure = knownFailure;
             mIsBroken = isBroken;
             mIsSuppressed = isSuppressed;
+            mTimeoutInMinutes = timeoutInMinutes;
         }
     }
 }
diff --git a/tools/utils/VogarUtils.java b/tools/utils/VogarUtils.java
index 5e8b944..8e77e7c 100644
--- a/tools/utils/VogarUtils.java
+++ b/tools/utils/VogarUtils.java
@@ -19,6 +19,7 @@
 import vogar.Expectation;
 import vogar.ExpectationStore;
 import vogar.ModeId;
+import vogar.Result;
 
 import java.io.File;
 import java.io.FilenameFilter;
@@ -52,14 +53,14 @@
         }
         String fullTestName = buildFullTestName(testClassName, testMethodName);
         Expectation expectation = expectationStore.get(fullTestName);
-        if (expectation == Expectation.SUCCESS) {
+        if (expectation.getResult() == Result.SUCCESS) {
             return false;
         }
 
         String description = expectation.getDescription();
         boolean foundAbi = AbiUtils.parseAbiList(description).size() > 0;
 
-        return expectation != Expectation.SUCCESS && !foundAbi;
+        return expectation.getResult() != Result.SUCCESS && !foundAbi;
     }
 
     public static ExpectationStore provideExpectationStore(String dir) throws IOException {
@@ -119,7 +120,7 @@
                                                    String className,
                                                    String testName) {
 
-        String fullTestName = VogarUtils.buildFullTestName(className, testName);
+        String fullTestName = buildFullTestName(className, testName);
         Set<String> supportedAbiSet = AbiUtils.getAbisForArch(architecture);
         for (ExpectationStore expectationStore : expectationStores) {
             Expectation expectation = expectationStore.get(fullTestName);
@@ -128,4 +129,44 @@
 
         return supportedAbiSet;
     }
+
+    /**
+     * Returns the greatest timeout in minutes for the test in all
+     * expectation stores, or 0 if no timeout was found.
+     */
+    public static int timeoutInMinutes(ExpectationStore[] expectationStores,
+            final String testClassName,
+            final String testMethodName) {
+        int timeoutInMinutes = 0;
+        for (ExpectationStore expectationStore : expectationStores) {
+            timeoutInMinutes = Math.max(timeoutInMinutes,
+                                        timeoutInMinutes(expectationStore,
+                                                         testClassName,
+                                                         testMethodName));
+        }
+        return timeoutInMinutes;
+    }
+
+    /**
+     * Returns the timeout in minutes for the test in the expectation
+     * stores, or 0 if no timeout was found.
+     */
+    public static int timeoutInMinutes(ExpectationStore expectationStore,
+            final String testClassName,
+            final String testMethodName) {
+        if (expectationStore == null) {
+            return 0;
+        }
+        String fullTestName = buildFullTestName(testClassName, testMethodName);
+        return timeoutInMinutes(expectationStore.get(fullTestName));
+    }
+
+    /**
+     * Returns the timeout in minutes for the expectation. Currently a
+     * tag of large results in a 60 minute timeout, otherwise 0 is
+     * returned to indicate a default timeout should be used.
+     */
+    public static int timeoutInMinutes(Expectation expectation) {
+        return expectation.getTags().contains("large") ? 60 : 0;
+    }
 }