Update interface for TestRecord loading

Add a reporter to dump the results as a proto too.

Test: See other CL in topic
run cts-unit-tests
test a retry
Bug: 110265525

Change-Id: I9afb7a1530c70018708037911ed9e856ea706d6a
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/suite/CompatibilityProtoResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/suite/CompatibilityProtoResultReporter.java
new file mode 100644
index 0000000..17da43a
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/suite/CompatibilityProtoResultReporter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.compatibility.common.tradefed.result.suite;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.proto.ProtoResultReporter;
+import com.android.tradefed.result.proto.TestRecordProto.TestRecord;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Proto reporter that will drop a {@link TestRecord} protobuf in the result directory.
+ */
+public class CompatibilityProtoResultReporter extends ProtoResultReporter {
+
+    public static final String PROTO_FILE_NAME = "test-record.pb";
+
+    private CompatibilityBuildHelper mBuildHelper;
+
+    /** The directory containing the results */
+    private File mResultDir = null;
+
+    @Override
+    public void processStartInvocation(
+            TestRecord invocationStartRecord, IInvocationContext invocationContext) {
+
+        if (mBuildHelper == null) {
+            mBuildHelper = new CompatibilityBuildHelper(invocationContext.getBuildInfos().get(0));
+        }
+        if (mResultDir == null) {
+            initializeResultDirectories();
+        }
+    }
+
+    @Override
+    public void processFinalProto(TestRecord finalRecord) {
+        super.processFinalProto(finalRecord);
+
+        File protoFile = new File(mResultDir, PROTO_FILE_NAME);
+        try {
+            finalRecord.writeDelimitedTo(new FileOutputStream(protoFile));
+        } catch (IOException e) {
+            CLog.e(e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void initializeResultDirectories() {
+        CLog.d("Initializing result directory");
+
+        try {
+            mResultDir = mBuildHelper.getResultDir();
+            if (mResultDir != null) {
+                mResultDir.mkdirs();
+            }
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+
+        if (mResultDir == null) {
+            throw new RuntimeException("Result Directory was not created");
+        }
+        if (!mResultDir.exists()) {
+            throw new RuntimeException("Result Directory was not created: " +
+                    mResultDir.getAbsolutePath());
+        }
+
+        CLog.d("Results Directory: %s", mResultDir.getAbsolutePath());
+    }
+}
\ No newline at end of file
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/suite/PreviousResultLoader.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/suite/PreviousResultLoader.java
index 1e4289a..2059109 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/suite/PreviousResultLoader.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/suite/PreviousResultLoader.java
@@ -21,11 +21,18 @@
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.invoker.InvocationContext;
 import com.android.tradefed.invoker.TestInvocation;
-import com.android.tradefed.result.suite.SuiteResultHolder;
+import com.android.tradefed.invoker.proto.InvocationContext.Context;
+import com.android.tradefed.result.proto.TestRecordProto.TestRecord;
 import com.android.tradefed.testtype.suite.retry.ITestSuiteResultLoader;
 
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.List;
 
 /**
@@ -41,27 +48,35 @@
             mandatory = true)
     private Integer mRetrySessionId = null;
 
-    private SuiteResultHolder mPreviousResults;
+    private TestRecord mTestRecord;
 
     @Override
     public void init(IInvocationContext context) {
         IBuildInfo info = context.getBuildInfos().get(0);
         CompatibilityBuildHelper helperBuild = new CompatibilityBuildHelper(info);
-        CertificationResultXml loader = new CertificationResultXml();
         try {
-            mPreviousResults = loader.parseResults(
-                    ResultHandler.getResultDirectory(helperBuild.getResultsDir(), mRetrySessionId));
+            File resultDir = ResultHandler.getResultDirectory(
+                    helperBuild.getResultsDir(), mRetrySessionId);
+            try (InputStream stream = new FileInputStream(
+                    new File(resultDir, CompatibilityProtoResultReporter.PROTO_FILE_NAME))) {
+                mTestRecord = TestRecord.parseDelimitedFrom(stream);
+            }
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
-        if (mPreviousResults == null) {
-            throw new RuntimeException("Failed to load the previous results.");
-        }
     }
 
     @Override
     public String getCommandLine() {
-        List<String> command = mPreviousResults.context.getAttributes().get(
+        Context context = null;
+        try {
+            context = mTestRecord.getDescription().unpack(Context.class);
+        } catch (InvalidProtocolBufferException e) {
+            throw new RuntimeException(e);
+        }
+        IInvocationContext invocContext = InvocationContext.fromProto(context);
+        
+        List<String> command = invocContext.getAttributes().get(
                 TestInvocation.COMMAND_ARGS_KEY);
         if (command == null) {
             throw new RuntimeException("Couldn't find the command line arg.");
@@ -70,7 +85,7 @@
     }
 
     @Override
-    public SuiteResultHolder loadPreviousResults() {
-        return mPreviousResults;
+    public TestRecord loadPreviousRecord() {
+        return mTestRecord;
     }
 }
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/suite/PreviousResultLoaderTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/suite/PreviousResultLoaderTest.java
index 8bb9dd5..f5c6587 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/suite/PreviousResultLoaderTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/suite/PreviousResultLoaderTest.java
@@ -22,12 +22,16 @@
 import com.android.tradefed.build.DeviceBuildInfo;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.ConfigurationDef;
+import com.android.tradefed.config.ConfigurationDescriptor;
 import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.invoker.IInvocationContext;
 import com.android.tradefed.invoker.InvocationContext;
-import com.android.tradefed.result.suite.SuiteResultHolder;
+import com.android.tradefed.invoker.TestInvocation;
+import com.android.tradefed.result.proto.TestRecordProto.TestRecord;
 import com.android.tradefed.util.FileUtil;
 
+import com.google.protobuf.Any;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -35,6 +39,7 @@
 import org.junit.runners.JUnit4;
 
 import java.io.File;
+import java.io.FileOutputStream;
 
 /**
  * Unit tests for {@link PreviousResultLoader}.
@@ -45,6 +50,7 @@
     private PreviousResultLoader mLoader;
     private IInvocationContext mContext;
     private File mRootDir;
+    private File mProtoFile;
 
     @Before
     public void setUp() throws Exception {
@@ -52,6 +58,9 @@
         OptionSetter setter = new OptionSetter(mLoader);
         setter.setOptionValue("retry", "0");
         mContext = new InvocationContext();
+        mContext.setConfigurationDescriptor(new ConfigurationDescriptor());
+        mContext.addInvocationAttribute(TestInvocation.COMMAND_ARGS_KEY,
+                "cts -m CtsGesture --skip-all-system-status-check");
     }
 
     @After
@@ -65,12 +74,16 @@
     @Test
     public void testReloadTests_failed() throws Exception {
         mContext.addDeviceBuildInfo(ConfigurationDef.DEFAULT_DEVICE_NAME, createFakeBuild(""));
+        // Delete the proto file
+        mProtoFile.delete();
         try {
             mLoader.init(mContext);
             fail("Should have thrown an exception.");
         } catch (RuntimeException expected) {
             // expected
-            assertEquals("Failed to load the previous results.", expected.getMessage());
+            assertEquals(
+                    String.format("java.io.FileNotFoundException: %s (No such file or directory)",
+                            mProtoFile.getAbsolutePath()), expected.getMessage());
         }
     }
 
@@ -83,8 +96,7 @@
                 createFakeBuild(createBasicResults()));
         mLoader.init(mContext);
         assertEquals("cts -m CtsGesture --skip-all-system-status-check", mLoader.getCommandLine());
-        SuiteResultHolder results = mLoader.loadPreviousResults();
-        assertEquals(2, results.totalModules);
+        mLoader.loadPreviousRecord();
     }
 
     private IBuildInfo createFakeBuild(String resultContent) throws Exception {
@@ -101,6 +113,12 @@
         File testResult = new File(new CompatibilityBuildHelper(build).getResultDir(),
                 "test_result.xml");
         testResult.createNewFile();
+        // Populate a proto result
+        mProtoFile = new File(new CompatibilityBuildHelper(build).getResultDir(),
+                CompatibilityProtoResultReporter.PROTO_FILE_NAME);
+        TestRecord.Builder builder = TestRecord.newBuilder();
+        builder.setDescription(Any.pack(mContext.toProto()));
+        builder.build().writeDelimitedTo(new FileOutputStream(mProtoFile));
         FileUtil.writeToFile(resultContent, testResult);
         return build;
     }
diff --git a/tools/cts-tradefed/res/config/cts-suite.xml b/tools/cts-tradefed/res/config/cts-suite.xml
index ca2cb2c..080f8f1 100644
--- a/tools/cts-tradefed/res/config/cts-suite.xml
+++ b/tools/cts-tradefed/res/config/cts-suite.xml
@@ -65,5 +65,6 @@
     <template-include name="metadata-reporters" default="empty" />
 
     <result_reporter class="com.android.compatibility.common.tradefed.result.ConsoleReporter" />
+    <result_reporter class="com.android.compatibility.common.tradefed.result.suite.CompatibilityProtoResultReporter" />
     <result_reporter class="com.android.compatibility.common.tradefed.result.suite.CertificationSuiteResultReporter" />
 </configuration>