Include Audio Quality Details in XML Report

Bug 5352891

This will add a <details>Log from test...</details> for tests that
pass an optional string when setting the test result or from
overriding the getTestResult method.

This is implemented by adding another column to the test results
database. I modified the backup agent to backup this column as well.
I removed a line in the backup agent that marked the test to fail
due to a mismatched backup key. I kept getting the key
"../databases/results.db" and I think that is from some older
backup that I have... I confirmed backup still works fine though.

Change-Id: Iffec1853d8e1dcbcf9feda821716d071860be069
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
index b31d6c6..9991b9d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
@@ -83,6 +83,9 @@
          * Returns a unique identifier for the test.  Usually, this is just the class name.
          */
         String getTestId();
+
+        /** @return null or details about the test run. */
+        String getTestDetails();
     }
 
     public static class Activity extends android.app.Activity implements PassFailActivity {
@@ -111,6 +114,11 @@
         public String getTestId() {
             return getClass().getName();
         }
+
+        @Override
+        public String getTestDetails() {
+            return null;
+        }
     }
 
     public static class ListActivity extends android.app.ListActivity implements PassFailActivity {
@@ -139,6 +147,11 @@
         public String getTestId() {
             return getClass().getName();
         }
+
+        @Override
+        public String getTestDetails() {
+            return null;
+        }
     }
 
     public static class TestListActivity extends AbstractTestListActivity
@@ -168,6 +181,11 @@
         public String getTestId() {
             return getClass().getName();
         }
+
+        @Override
+        public String getTestDetails() {
+            return null;
+        }
     }
 
     private static <T extends android.app.Activity & PassFailActivity>
@@ -175,7 +193,8 @@
         View.OnClickListener clickListener = new View.OnClickListener() {
             @Override
             public void onClick(View target) {
-                setTestResultAndFinish(activity, activity.getTestId(), target);
+                setTestResultAndFinish(activity, activity.getTestId(), activity.getTestDetails(),
+                        target);
             }
         };
 
@@ -278,7 +297,7 @@
 
     /** Set the test result corresponding to the button clicked and finish the activity. */
     private static void setTestResultAndFinish(android.app.Activity activity, String testId,
-            View target) {
+            String testDetails, View target) {
         boolean passed;
         switch (target.getId()) {
             case R.id.pass_button:
@@ -290,16 +309,16 @@
             default:
                 throw new IllegalArgumentException("Unknown id: " + target.getId());
         }
-        setTestResultAndFinish(activity, testId, passed);
+        setTestResultAndFinish(activity, testId, testDetails, passed);
     }
 
     /** Set the test result and finish the activity. */
     public static void setTestResultAndFinish(android.app.Activity activity, String testId,
-            boolean passed) {
+            String testDetails, boolean passed) {
         if (passed) {
-            TestResult.setPassedResult(activity, testId);
+            TestResult.setPassedResult(activity, testId, testDetails);
         } else {
-            TestResult.setFailedResult(activity, testId);
+            TestResult.setFailedResult(activity, testId, testDetails);
         }
 
         activity.finish();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
index 3f84f51..52d9b32 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
@@ -63,6 +63,9 @@
     /** Mutable test results that will change as each test activity finishes. */
     private final Map<String, Integer> mTestResults = new HashMap<String, Integer>();
 
+    /** Map from test name to test details. */
+    private final Map<String, String> mTestDetails = new HashMap<String, String>();
+
     private final LayoutInflater mLayoutInflater;
 
     /** {@link ListView} row that is either a test category header or a test. */
@@ -121,15 +124,15 @@
     }
 
     public void setTestResult(TestResult testResult) {
-        new SetTestResultTask(testResult.getName(), testResult.getResult()).execute();
+        new SetTestResultTask(testResult.getName(), testResult.getResult(),
+                testResult.getDetails()).execute();
     }
 
     class RefreshTestResultsTask extends AsyncTask<Void, Void, RefreshResult> {
         @Override
         protected RefreshResult doInBackground(Void... params) {
             List<TestListItem> rows = getRows();
-            Map<String, Integer> results = getTestResults();
-            return new RefreshResult(rows, results);
+            return getRefreshResults(rows);
         }
 
         @Override
@@ -139,6 +142,8 @@
             mRows.addAll(result.mItems);
             mTestResults.clear();
             mTestResults.putAll(result.mResults);
+            mTestDetails.clear();
+            mTestDetails.putAll(result.mDetails);
             notifyDataSetChanged();
         }
     }
@@ -146,27 +151,40 @@
     static class RefreshResult {
         List<TestListItem> mItems;
         Map<String, Integer> mResults;
+        Map<String, String> mDetails;
 
-        RefreshResult(List<TestListItem> items, Map<String, Integer> results) {
+        RefreshResult(List<TestListItem> items, Map<String, Integer> results,
+                Map<String, String> details) {
             mItems = items;
             mResults = results;
+            mDetails = details;
         }
     }
 
     protected abstract List<TestListItem> getRows();
 
-    Map<String, Integer> getTestResults() {
+    static final String[] REFRESH_PROJECTION = {
+        TestResultsProvider._ID,
+        TestResultsProvider.COLUMN_TEST_NAME,
+        TestResultsProvider.COLUMN_TEST_RESULT,
+        TestResultsProvider.COLUMN_TEST_DETAILS,
+    };
+
+    RefreshResult getRefreshResults(List<TestListItem> items) {
         Map<String, Integer> results = new HashMap<String, Integer>();
+        Map<String, String> details = new HashMap<String, String>();
         ContentResolver resolver = mContext.getContentResolver();
         Cursor cursor = null;
         try {
-            cursor = resolver.query(TestResultsProvider.RESULTS_CONTENT_URI,
-                    TestResultsProvider.ALL_COLUMNS, null, null, null);
+            cursor = resolver.query(TestResultsProvider.RESULTS_CONTENT_URI, REFRESH_PROJECTION,
+                    null, null, null);
             if (cursor.moveToFirst()) {
                 do {
                     String testName = cursor.getString(1);
                     int testResult = cursor.getInt(2);
+                    String testDetails = cursor.getString(3);
                     results.put(testName, testResult);
+                    details.put(testName, testDetails);
                 } while (cursor.moveToNext());
             }
         } finally {
@@ -174,7 +192,7 @@
                 cursor.close();
             }
         }
-        return results;
+        return new RefreshResult(items, results, details);
     }
 
     class ClearTestResultsTask extends AsyncTask<Void, Void, Void> {
@@ -193,14 +211,17 @@
 
         private final int mResult;
 
-        SetTestResultTask(String testName, int result) {
+        private final String mDetails;
+
+        SetTestResultTask(String testName, int result, String details) {
             mTestName = testName;
             mResult = result;
+            mDetails = details;
         }
 
         @Override
         protected Void doInBackground(Void... params) {
-            TestResultsProvider.setTestResult(mContext, mTestName, mResult);
+            TestResultsProvider.setTestResult(mContext, mTestName, mResult, mDetails);
             return null;
         }
     }
@@ -261,6 +282,13 @@
                 : TestResult.TEST_RESULT_NOT_EXECUTED;
     }
 
+    public String getTestDetails(int position) {
+        TestListItem item = getItem(position);
+        return mTestDetails.containsKey(item.testName)
+                ? mTestDetails.get(item.testName)
+                : null;
+    }
+
     public boolean allTestsPassed() {
         for (TestListItem item : mRows) {
             if (item.isTest() && (!mTestResults.containsKey(item.testName)
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResult.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResult.java
index 3b42c3b..68513ac 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResult.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResult.java
@@ -21,8 +21,9 @@
 
 /**
  * Object representing the result of a test activity like whether it succeeded or failed.
- * Use {@link #setPassedResult(Activity, String)} or {@link #setFailedResult(Activity, String)} from
- * a test activity like you would {@link Activity#setResult(int)} so that {@link TestListActivity}
+ * Use {@link #setPassedResult(Activity, String, String)} or
+ * {@link #setFailedResult(Activity, String, String)} from a test activity like you would
+ * {@link Activity#setResult(int)} so that {@link TestListActivity}
  * will persist the test result and update its adapter and thus the list view.
  */
 public class TestResult {
@@ -33,25 +34,30 @@
 
     private static final String TEST_NAME = "name";
     private static final String TEST_RESULT = "result";
+    private static final String TEST_DETAILS = "details";
 
     private final String mName;
-
     private final int mResult;
+    private final String mDetails;
 
     /** Sets the test activity's result to pass. */
-    public static void setPassedResult(Activity activity, String testId) {
-        activity.setResult(Activity.RESULT_OK, createResult(activity, TEST_RESULT_PASSED, testId));
+    public static void setPassedResult(Activity activity, String testId, String testDetails) {
+        activity.setResult(Activity.RESULT_OK, createResult(activity, TEST_RESULT_PASSED, testId,
+                testDetails));
     }
 
     /** Sets the test activity's result to failed. */
-    public static void setFailedResult(Activity activity, String testId) {
-        activity.setResult(Activity.RESULT_OK, createResult(activity, TEST_RESULT_FAILED, testId));
+    public static void setFailedResult(Activity activity, String testId, String testDetails) {
+        activity.setResult(Activity.RESULT_OK, createResult(activity, TEST_RESULT_FAILED, testId,
+                testDetails));
     }
 
-    private static Intent createResult(Activity activity, int testResult, String testName) {
+    private static Intent createResult(Activity activity, int testResult, String testName,
+            String testDetails) {
         Intent data = new Intent(activity, activity.getClass());
         data.putExtra(TEST_NAME, testName);
         data.putExtra(TEST_RESULT, testResult);
+        data.putExtra(TEST_DETAILS, testDetails);
         return data;
     }
 
@@ -59,15 +65,17 @@
      * Convert the test activity's result into a {@link TestResult}. Only meant to be used by
      * {@link TestListActivity}.
      */
-    public static TestResult fromActivityResult(int resultCode, Intent data) {
+    static TestResult fromActivityResult(int resultCode, Intent data) {
         String name = data.getStringExtra(TEST_NAME);
         int result = data.getIntExtra(TEST_RESULT, TEST_RESULT_NOT_EXECUTED);
-        return new TestResult(name, result);
+        String details = data.getStringExtra(TEST_DETAILS);
+        return new TestResult(name, result, details);
     }
 
-    private TestResult(String name, int result) {
+    private TestResult(String name, int result, String details) {
         this.mName = name;
         this.mResult = result;
+        this.mDetails = details;
     }
 
     /** Return the name of the test like "com.android.cts.verifier.foo.FooTest" */
@@ -79,4 +87,9 @@
     public int getResult() {
         return mResult;
     }
+
+    /** Return null or string containing test output. */
+    public String getDetails() {
+        return mDetails;
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsBackupHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsBackupHelper.java
index 0966de4..e4cd24a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsBackupHelper.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsBackupHelper.java
@@ -58,6 +58,7 @@
             int nameIndex = cursor.getColumnIndex(TestResultsProvider.COLUMN_TEST_NAME);
             int resultIndex = cursor.getColumnIndex(TestResultsProvider.COLUMN_TEST_RESULT);
             int infoSeenIndex = cursor.getColumnIndex(TestResultsProvider.COLUMN_TEST_INFO_SEEN);
+            int detailsIndex = cursor.getColumnIndex(TestResultsProvider.COLUMN_TEST_DETAILS);
 
             ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
             DataOutputStream dataOutput = new DataOutputStream(byteOutput);
@@ -67,10 +68,12 @@
                 String name = cursor.getString(nameIndex);
                 int result = cursor.getInt(resultIndex);
                 int infoSeen = cursor.getInt(infoSeenIndex);
+                String details = cursor.getString(detailsIndex);
 
                 dataOutput.writeUTF(name);
                 dataOutput.writeInt(result);
                 dataOutput.writeInt(infoSeen);
+                dataOutput.writeUTF(details != null ? details : "");
             }
 
             byte[] rawBytes = byteOutput.toByteArray();
@@ -102,17 +105,19 @@
                     String name = dataInput.readUTF();
                     int result = dataInput.readInt();
                     int infoSeen = dataInput.readInt();
+                    String details = dataInput.readUTF();
 
                     values[i] = new ContentValues();
                     values[i].put(TestResultsProvider.COLUMN_TEST_NAME, name);
                     values[i].put(TestResultsProvider.COLUMN_TEST_RESULT, result);
                     values[i].put(TestResultsProvider.COLUMN_TEST_INFO_SEEN, infoSeen);
+                    values[i].put(TestResultsProvider.COLUMN_TEST_DETAILS, details);
                 }
 
                 ContentResolver resolver = mContext.getContentResolver();
                 resolver.bulkInsert(TestResultsProvider.RESULTS_CONTENT_URI, values);
             } else {
-                failBackupTest();
+                Log.e(TAG, "Skipping key: " + data.getKey());
             }
         } catch (IOException e) {
             Log.e(TAG, "Couldn't restore test results...", e);
@@ -122,7 +127,7 @@
 
     private void failBackupTest() {
         TestResultsProvider.setTestResult(mContext, BackupTestActivity.class.getName(),
-                TestResult.TEST_RESULT_FAILED);
+                TestResult.TEST_RESULT_FAILED, null);
     }
 
     @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java
index cc9dc73..df05519 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java
@@ -53,12 +53,8 @@
     /** Boolean indicating whether the test info has been seen. */
     static final String COLUMN_TEST_INFO_SEEN = "testinfoseen";
 
-    static final String[] ALL_COLUMNS = {
-        _ID,
-        COLUMN_TEST_NAME,
-        COLUMN_TEST_RESULT,
-        COLUMN_TEST_INFO_SEEN,
-    };
+    /** String containing the test's details. */
+    static final String COLUMN_TEST_DETAILS = "testdetails";
 
     private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
     private static final int RESULTS_ALL = 1;
@@ -87,7 +83,7 @@
 
         private static final String DATABASE_NAME = "results.db";
 
-        private static final int DATABASE_VERSION = 5;
+        private static final int DATABASE_VERSION = 6;
 
         TestResultsOpenHelper(Context context) {
             super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -99,7 +95,8 @@
                     + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
                     + COLUMN_TEST_NAME + " TEXT, "
                     + COLUMN_TEST_RESULT + " INTEGER,"
-                    + COLUMN_TEST_INFO_SEEN + " INTEGER DEFAULT 0);");
+                    + COLUMN_TEST_INFO_SEEN + " INTEGER DEFAULT 0,"
+                    + COLUMN_TEST_DETAILS + " TEXT);");
         }
 
         @Override
@@ -204,10 +201,12 @@
         return null;
     }
 
-    static void setTestResult(Context context, String testName, int testResult) {
+    static void setTestResult(Context context, String testName, int testResult,
+            String testDetails) {
         ContentValues values = new ContentValues(2);
         values.put(TestResultsProvider.COLUMN_TEST_RESULT, testResult);
         values.put(TestResultsProvider.COLUMN_TEST_NAME, testName);
+        values.put(TestResultsProvider.COLUMN_TEST_DETAILS, testDetails);
 
         ContentResolver resolver = context.getContentResolver();
         int numUpdated = resolver.update(TestResultsProvider.RESULTS_CONTENT_URI, values,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
index efc9c85..e40b428 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
@@ -22,6 +22,7 @@
 
 import android.content.Context;
 import android.os.Build;
+import android.text.TextUtils;
 import android.util.Xml;
 
 import java.io.ByteArrayOutputStream;
@@ -55,7 +56,7 @@
 class TestResultsReport {
 
     /** Version of the test report. Increment whenever adding new tags and attributes. */
-    private static final int REPORT_VERSION = 1;
+    private static final int REPORT_VERSION = 2;
 
     /** Format of the report's creation time. Maintain the same format at CTS. */
     private static DateFormat DATE_FORMAT =
@@ -67,6 +68,7 @@
     private static final String BUILD_INFO_TAG = "build-info";
     private static final String TEST_RESULTS_TAG = "test-results";
     private static final String TEST_TAG = "test";
+    private static final String TEST_DETAILS_TAG = "details";
 
     private final Context mContext;
 
@@ -105,7 +107,7 @@
         xml.attribute(null, "model", Build.MODEL);
         xml.attribute(null, "product", Build.PRODUCT);
         xml.attribute(null, "release", Build.VERSION.RELEASE);
-        xml.attribute(null, "sdk", Build.VERSION.SDK);
+        xml.attribute(null, "sdk", Integer.toString(Build.VERSION.SDK_INT));
         xml.endTag(null, BUILD_INFO_TAG);
         xml.endTag(null, DEVICE_INFO_TAG);
 
@@ -118,6 +120,14 @@
                 xml.attribute(null, "title", item.title);
                 xml.attribute(null, "class-name", item.testName);
                 xml.attribute(null, "result", getTestResultString(mAdapter.getTestResult(i)));
+
+                String details = mAdapter.getTestDetails(i);
+                if (!TextUtils.isEmpty(details)) {
+                    xml.startTag(null, TEST_DETAILS_TAG);
+                    xml.text(details);
+                    xml.endTag(null, TEST_DETAILS_TAG);
+                }
+
                 xml.endTag(null, TEST_TAG);
             }
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioQualityVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioQualityVerifierActivity.java
index 7d7c16d..755b62d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioQualityVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioQualityVerifierActivity.java
@@ -280,6 +280,11 @@
     }
 
     @Override
+    public String getTestDetails() {
+        return genReport();
+    }
+
+    @Override
     protected void onDestroy() {
         super.onDestroy();
         unregisterReceiver(mReceiver);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/MessageTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/MessageTestActivity.java
index c730aed..2c6324b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/MessageTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/MessageTestActivity.java
@@ -342,7 +342,7 @@
                     new DialogInterface.OnClickListener() {
                 @Override
                 public void onClick(DialogInterface dialog, int which) {
-                    TestResult.setFailedResult(MessageTestActivity.this, getTestId());
+                    TestResult.setFailedResult(MessageTestActivity.this, getTestId(), null);
                     finish();
                 }
             })
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/PlayVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/PlayVideoActivity.java
index efd076d..2b65421 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/PlayVideoActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/PlayVideoActivity.java
@@ -121,7 +121,7 @@
                             @Override
                             public void onClick(DialogInterface dialog, int which) {
                                 PassFailButtons.setTestResultAndFinish(PlayVideoActivity.this,
-                                        getTestId(), false);
+                                        getTestId(), null, false);
                             }
                         })
                         .show();