Merge "Add the reason for done=false in XML"
diff --git a/src/com/android/tradefed/result/suite/XmlSuiteResultFormatter.java b/src/com/android/tradefed/result/suite/XmlSuiteResultFormatter.java
index d6a4be6..772075b 100644
--- a/src/com/android/tradefed/result/suite/XmlSuiteResultFormatter.java
+++ b/src/com/android/tradefed/result/suite/XmlSuiteResultFormatter.java
@@ -98,6 +98,7 @@
     private static final String MODULE_TAG = "Module";
     private static final String MODULES_DONE_ATTR = "modules_done";
     private static final String MODULES_TOTAL_ATTR = "modules_total";
+    private static final String MODULES_NOT_DONE_REASON = "Reason";
     private static final String NAME_ATTR = "name";
     private static final String OS_ARCH_ATTR = "os_arch";
     private static final String OS_NAME_ATTR = "os_name";
@@ -297,6 +298,15 @@
                     NS, PASS_ATTR, Integer.toString(module.getNumTestsInState(TestStatus.PASSED)));
             serializer.attribute(NS, TOTAL_TESTS_ATTR, Integer.toString(module.getNumTests()));
 
+            if (!isDone) {
+                String message = module.getRunFailureMessage();
+                if (message == null) {
+                    message = "Run was incomplete. Some tests might not have finished.";
+                }
+                serializer.startTag(NS, MODULES_NOT_DONE_REASON);
+                serializer.attribute(NS, MESSAGE_ATTR, message);
+                serializer.endTag(NS, MODULES_NOT_DONE_REASON);
+            }
             serializeTestCases(serializer, module.getTestResults());
             serializer.endTag(NS, MODULE_TAG);
         }
@@ -603,6 +613,13 @@
             module.testRunStarted(moduleId, totalTests);
             // TestCase level information parsing
             while (parser.nextTag() == XmlPullParser.START_TAG) {
+                // If a reason for not done exists, handle it.
+                if (parser.getName().equals(MODULES_NOT_DONE_REASON)) {
+                    parser.require(XmlPullParser.START_TAG, NS, MODULES_NOT_DONE_REASON);
+                    parser.nextTag();
+                    parser.require(XmlPullParser.END_TAG, NS, MODULES_NOT_DONE_REASON);
+                    continue;
+                }
                 parser.require(XmlPullParser.START_TAG, NS, CASE_TAG);
                 String className = parser.getAttributeValue(NS, NAME_ATTR);
                 // Test level information parsing
diff --git a/tests/src/com/android/tradefed/result/suite/XmlSuiteResultFormatterTest.java b/tests/src/com/android/tradefed/result/suite/XmlSuiteResultFormatterTest.java
index 7eb74c6..647db0b 100644
--- a/tests/src/com/android/tradefed/result/suite/XmlSuiteResultFormatterTest.java
+++ b/tests/src/com/android/tradefed/result/suite/XmlSuiteResultFormatterTest.java
@@ -16,6 +16,7 @@
 package com.android.tradefed.result.suite;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -184,6 +185,62 @@
         assertEquals(holder.runResults.size(), mResultHolder.runResults.size());
     }
 
+    @Test
+    public void testFailuresReporting_notDone() throws Exception {
+        mResultHolder.context = mContext;
+
+        List<TestRunResult> runResults = new ArrayList<>();
+        runResults.add(createFakeResult("module1", 2, 1, 0, 0));
+        runResults.get(0).testRunFailed("Failed module");
+        mResultHolder.runResults = runResults;
+
+        Map<String, IAbi> modulesAbi = new HashMap<>();
+        modulesAbi.put("module1", new Abi("armeabi-v7a", "32"));
+        mResultHolder.modulesAbi = modulesAbi;
+
+        mResultHolder.completeModules = 2;
+        mResultHolder.totalModules = 1;
+        mResultHolder.passedTests = 2;
+        mResultHolder.failedTests = 1;
+        mResultHolder.startTime = 0L;
+        mResultHolder.endTime = 10L;
+        File res = mFormatter.writeResults(mResultHolder, mResultDir);
+        String content = FileUtil.readStringFromFile(res);
+
+        assertXmlContainsNode(content, "Result/Module");
+        assertXmlContainsAttribute(content, "Result/Module", "done", "false");
+        assertXmlContainsNode(content, "Result/Module/Reason");
+        assertXmlContainsAttribute(content, "Result/Module/TestCase", "name", "com.class.module1");
+        assertXmlContainsAttribute(
+                content, "Result/Module/TestCase/Test", "name", "module1.method0");
+        assertXmlContainsAttribute(
+                content, "Result/Module/TestCase/Test", "name", "module1.method1");
+        // Check that failures are showing in the xml for the test cases
+        assertXmlContainsAttribute(
+                content, "Result/Module/TestCase/Test", "name", "module1.failed0");
+        assertXmlContainsAttribute(content, "Result/Module/TestCase/Test", "result", "fail");
+        assertXmlContainsAttribute(
+                content, "Result/Module/TestCase/Test/Failure", "message", "module1 failed.");
+        assertXmlContainsValue(
+                content,
+                "Result/Module/TestCase/Test/Failure/StackTrace",
+                XmlSuiteResultFormatter.sanitizeXmlContent("module1 failed.\nstack\nstack\0"));
+        // Test that we can read back the informations
+        SuiteResultHolder holder = mFormatter.parseResults(mResultDir, false);
+        assertEquals(holder.completeModules, mResultHolder.completeModules);
+        assertEquals(holder.totalModules, mResultHolder.totalModules);
+        assertEquals(holder.passedTests, mResultHolder.passedTests);
+        assertEquals(holder.failedTests, mResultHolder.failedTests);
+        assertEquals(holder.startTime, mResultHolder.startTime);
+        assertEquals(holder.endTime, mResultHolder.endTime);
+        assertEquals(
+                holder.modulesAbi.get("armeabi-v7a module1"),
+                mResultHolder.modulesAbi.get("module1"));
+        assertEquals(1, holder.runResults.size());
+        TestRunResult result = new ArrayList<>(holder.runResults).get(0);
+        assertFalse(result.isRunComplete());
+    }
+
     /** Test that assumption failures and ignored tests are correctly reported in the xml. */
     @Test
     public void testAssumptionFailures_Ignore_Reporting() throws Exception {