Have ResultReporter check build info during invocationEnded

Instead of making ResultReporter IShardableListener, have it collect a
single "master" build info when sharded, then load the saved build info
during invocationEnded. The build info is populated by
DeviceInfoCollector.

Bug: 28883703
Bug: 28979002
Change-Id: I36b5b34a8116d021a5e8dfd4d05a5ae5a8ac978b
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/MasterBuildInfo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/MasterBuildInfo.java
new file mode 100644
index 0000000..fdcb634
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/MasterBuildInfo.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 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.build;
+
+import com.android.tradefed.build.IBuildInfo;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Singleton class containing a master {@link IBuildInfo} to be persisted across sharded
+ * invocations.
+ */
+public class MasterBuildInfo {
+
+    private static Map<String, String> mBuild = new HashMap<String, String>();
+
+    private MasterBuildInfo() { }
+
+    public static Map<String, String> getBuild() {
+        return mBuild;
+    }
+
+    /**
+     *
+     * @param buildInfo
+     */
+    public static void addBuildInfo(Map<String, String> buildInfo) {
+        mBuild.putAll(buildInfo);
+    }
+
+    public static void clear() {
+        mBuild.clear();
+    }
+}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
index a473c05..c609163 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
@@ -16,6 +16,7 @@
 package com.android.compatibility.common.tradefed.result;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.build.MasterBuildInfo;
 import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
 import com.android.compatibility.common.util.ICaseResult;
 import com.android.compatibility.common.util.IInvocationResult;
@@ -34,11 +35,9 @@
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.Option.Importance;
 import com.android.tradefed.config.OptionClass;
-import com.android.tradefed.config.OptionCopier;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.ILogSaver;
 import com.android.tradefed.result.ILogSaverListener;
-import com.android.tradefed.result.IShardableListener;
 import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.result.ITestSummaryListener;
 import com.android.tradefed.result.InputStreamSource;
@@ -69,7 +68,7 @@
  */
 @OptionClass(alias="result-reporter")
 public class ResultReporter implements ILogSaverListener, ITestInvocationListener,
-       ITestSummaryListener, IShardableListener {
+       ITestSummaryListener {
 
     private static final String RESULT_KEY = "COMPATIBILITY_TEST_RESULT";
     private static final String CTS_PREFIX = "cts:";
@@ -115,7 +114,7 @@
     private IBuildInfo mBuild;
     private CompatibilityBuildHelper mBuildHelper;
     private ILogSaver mLogSaver;
-    private int mResultDirIndex = 0;
+    private boolean mReloadBuildInfo = false;
 
     /**
      * {@inheritDoc}
@@ -127,6 +126,7 @@
         mDeviceSerial = buildInfo.getDeviceSerial();
         if (mDeviceSerial == null) {
             mDeviceSerial = "unknown_device";
+            mReloadBuildInfo = true;
         }
         long time = System.currentTimeMillis();
         String dirSuffix = getDirSuffix(time);
@@ -153,13 +153,8 @@
             } catch (FileNotFoundException e) {
                 e.printStackTrace();
             }
-            if (mResultDir != null) {
-                while (mResultDir.exists()) {
-                    mResultDir = new File(mResultDir.getAbsolutePath() + "_" + ++mResultDirIndex);
-                }
-            }
             if (mResultDir != null && mResultDir.mkdirs()) {
-                logResult("Created result dir %s", mResultDir.getAbsolutePath());
+                    logResult("Created result dir %s", mResultDir.getAbsolutePath());
             } else {
                 throw new IllegalArgumentException(String.format("Could not create result dir %s",
                         mResultDir.getAbsolutePath()));
@@ -173,11 +168,6 @@
         } catch (FileNotFoundException e) {
             e.printStackTrace();
         }
-        if (mLogDir != null) {
-            while (mLogDir.exists()) {
-                mLogDir = new File(mLogDir.getAbsolutePath() + "_" + mResultDirIndex);
-            }
-        }
         if (mLogDir != null && mLogDir.mkdirs()) {
             logResult("Created log dir %s", mLogDir.getAbsolutePath());
         } else {
@@ -200,7 +190,11 @@
     public void testRunStarted(String id, int numTests) {
         logResult("Starting %s with %d test%s", id, numTests, (numTests > 1) ? "s" : "");
         mCurrentModuleResult = mResult.getOrCreateModule(id);
-        mResult.addDeviceSerial(mDeviceSerial);
+        if (mDeviceSerial == null || mDeviceSerial.equals("unknown_device")) {
+            mResult.addDeviceSerial(mBuild.getDeviceSerial());
+        } else {
+            mResult.addDeviceSerial(mDeviceSerial);
+        }
     }
 
     /**
@@ -347,6 +341,13 @@
                 mResult.countResults(TestStatus.FAIL),
                 mResult.countResults(TestStatus.NOT_EXECUTED));
         try {
+            // invocationStarted picked up an unfinished IBuildInfo, so repopulate it now from
+            // the master
+            if (mReloadBuildInfo) {
+                for (Map.Entry<String, String> e : MasterBuildInfo.getBuild().entrySet()) {
+                    mResult.addBuildInfo(e.getKey(), e.getValue());
+                }
+            }
             File resultFile = ResultHandler.writeResults(mBuildHelper.getSuiteName(),
                     mBuildHelper.getSuiteVersion(), mBuildHelper.getSuitePlan(),
                     mBuildHelper.getSuiteBuild(), mResult, mResultDir, mStartTime,
@@ -391,6 +392,8 @@
         } catch (IOException | XmlPullParserException e) {
             CLog.e("[%s] Exception while saving result XML.", mDeviceSerial);
             CLog.e(e);
+        } finally {
+            MasterBuildInfo.clear();
         }
     }
 
@@ -525,11 +528,4 @@
             CLog.logAndDisplay(LogLevel.INFO, format, args);
         }
     }
-
-    @Override
-    public IShardableListener clone() {
-        ResultReporter clone = new ResultReporter();
-        OptionCopier.copyOptionsNoThrow(this, clone);
-        return clone;
-    }
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DeviceInfoCollector.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DeviceInfoCollector.java
index 651dc7b..bae4e02 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DeviceInfoCollector.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DeviceInfoCollector.java
@@ -17,6 +17,7 @@
 package com.android.compatibility.common.tradefed.targetprep;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.build.MasterBuildInfo;
 import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.Option;
@@ -80,13 +81,14 @@
     @Override
     public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
             BuildError, DeviceNotAvailableException {
-        if (mSkipDeviceInfo) {
-            return;
-        }
         for (Entry<String, String> entry : BUILD_KEYS.entrySet()) {
             buildInfo.addBuildAttribute(entry.getKey(),
                     ArrayUtil.join(",", device.getProperty(entry.getValue())));
         }
+        MasterBuildInfo.addBuildInfo(buildInfo.getBuildAttributes());
+        if (mSkipDeviceInfo) {
+            return;
+        }
         run(device, buildInfo);
         getDeviceInfoFiles(device, buildInfo);
     }