Merge "Fix some of the offending loggers"
diff --git a/src/com/android/tradefed/device/cloud/MultiUserSetupUtil.java b/src/com/android/tradefed/device/cloud/MultiUserSetupUtil.java
index 9c2e577..3a69667 100644
--- a/src/com/android/tradefed/device/cloud/MultiUserSetupUtil.java
+++ b/src/com/android/tradefed/device/cloud/MultiUserSetupUtil.java
@@ -28,13 +28,7 @@
 
     /** Files that must be copied between users to avoid conflicting ownership */
     private static final List<String> FILE_TO_BE_COPIED =
-            Arrays.asList(
-                    "android-info.txt",
-                    "boot.img",
-                    "cache.img",
-                    "product.img",
-                    "system.img",
-                    "vendor.img");
+            Arrays.asList("android-info.txt", "*.img");
 
     /** Files that can simply be shared between the different users */
     private static final List<String> FILE_TO_BE_LINKED = Arrays.asList("bin", "config", "lib64");
diff --git a/src/com/android/tradefed/result/JUnit4ResultForwarder.java b/src/com/android/tradefed/result/JUnit4ResultForwarder.java
index 34d183a..e2fc3b6 100644
--- a/src/com/android/tradefed/result/JUnit4ResultForwarder.java
+++ b/src/com/android/tradefed/result/JUnit4ResultForwarder.java
@@ -19,6 +19,7 @@
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.LogAnnotation;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.MetricAnnotation;
 import com.android.tradefed.testtype.MetricTestCase.LogHolder;
+import com.android.tradefed.testtype.junit4.CarryDnaeError;
 import com.android.tradefed.util.StreamUtil;
 
 import org.junit.AssumptionViolatedException;
@@ -51,6 +52,10 @@
         if (description.getMethodName() == null) {
             // In case of exception in @BeforeClass, the method name will be null
             mListener.testRunFailed(String.format("Failed with trace: %s", failure.getTrace()));
+            // If the exception is ours thrown from before, rethrow it
+            if (failure.getException() instanceof CarryDnaeError) {
+                throw ((CarryDnaeError) failure.getException()).getDeviceNotAvailableException();
+            }
             return;
         }
         mTestCaseFailures.add(failure.getException());
@@ -62,7 +67,7 @@
     }
 
     @Override
-    public void testStarted(Description description) {
+    public void testStarted(Description description) throws Exception {
         mTestCaseFailures.clear();
         TestDescription testid =
                 new TestDescription(
@@ -73,32 +78,35 @@
     }
 
     @Override
-    public void testFinished(Description description) {
+    public void testFinished(Description description) throws Exception {
         TestDescription testid =
                 new TestDescription(
                         description.getClassName(),
                         description.getMethodName(),
                         description.getAnnotations());
-        handleFailures(testid);
-        // Explore the Description to see if we find any Annotation metrics carrier
-        HashMap<String, Metric> metrics = new HashMap<>();
-        for (Description child : description.getChildren()) {
-            for (Annotation a : child.getAnnotations()) {
-                if (a instanceof MetricAnnotation) {
-                    metrics.putAll(((MetricAnnotation) a).mMetrics);
-                }
-                if (a instanceof LogAnnotation) {
-                    // Log all the logs found.
-                    for (LogHolder log : ((LogAnnotation) a).mLogs) {
-                        mListener.testLog(log.mDataName, log.mDataType, log.mDataStream);
-                        StreamUtil.cancel(log.mDataStream);
+        try {
+            handleFailures(testid);
+        } finally {
+            // Explore the Description to see if we find any Annotation metrics carrier
+            HashMap<String, Metric> metrics = new HashMap<>();
+            for (Description child : description.getChildren()) {
+                for (Annotation a : child.getAnnotations()) {
+                    if (a instanceof MetricAnnotation) {
+                        metrics.putAll(((MetricAnnotation) a).mMetrics);
                     }
-                    ((LogAnnotation) a).mLogs.clear();
+                    if (a instanceof LogAnnotation) {
+                        // Log all the logs found.
+                        for (LogHolder log : ((LogAnnotation) a).mLogs) {
+                            mListener.testLog(log.mDataName, log.mDataType, log.mDataStream);
+                            StreamUtil.cancel(log.mDataStream);
+                        }
+                        ((LogAnnotation) a).mLogs.clear();
+                    }
                 }
             }
+            //description.
+            mListener.testEnded(testid, metrics);
         }
-        //description.
-        mListener.testEnded(testid, metrics);
     }
 
     @Override
diff --git a/src/com/android/tradefed/testtype/DeviceJUnit4ClassRunner.java b/src/com/android/tradefed/testtype/DeviceJUnit4ClassRunner.java
index 778f36f..b69cf75 100644
--- a/src/com/android/tradefed/testtype/DeviceJUnit4ClassRunner.java
+++ b/src/com/android/tradefed/testtype/DeviceJUnit4ClassRunner.java
@@ -25,6 +25,8 @@
 import com.android.tradefed.result.InputStreamSource;
 import com.android.tradefed.result.LogDataType;
 import com.android.tradefed.testtype.MetricTestCase.LogHolder;
+import com.android.tradefed.testtype.junit4.CarryDnaeError;
+import com.android.tradefed.testtype.junit4.RunNotifierWrapper;
 import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.proto.TfMetricProtoUtil;
 
@@ -110,13 +112,27 @@
 
     @Override
     protected void runChild(FrameworkMethod method, RunNotifier notifier) {
+        RunNotifierWrapper wrapper = new RunNotifierWrapper(notifier);
         try {
-            super.runChild(method, notifier);
+            super.runChild(method, wrapper);
         } finally {
             for (File f : mDownloadedFiles) {
                 FileUtil.recursiveDelete(f);
             }
         }
+        if (wrapper.getDeviceNotAvailableException() != null) {
+            throw new CarryDnaeError(wrapper.getDeviceNotAvailableException());
+        }
+    }
+
+    @Override
+    public void run(RunNotifier notifier) {
+        RunNotifierWrapper wrapper = new RunNotifierWrapper(notifier);
+        super.run(wrapper);
+
+        if (wrapper.getDeviceNotAvailableException() != null) {
+            throw new CarryDnaeError(wrapper.getDeviceNotAvailableException());
+        }
     }
 
     @Override
diff --git a/src/com/android/tradefed/testtype/HostTest.java b/src/com/android/tradefed/testtype/HostTest.java
index 0d3a55a..f6c9160 100644
--- a/src/com/android/tradefed/testtype/HostTest.java
+++ b/src/com/android/tradefed/testtype/HostTest.java
@@ -33,6 +33,7 @@
 import com.android.tradefed.result.ResultForwarder;
 import com.android.tradefed.result.TestDescription;
 import com.android.tradefed.testtype.host.PrettyTestEventLogger;
+import com.android.tradefed.testtype.junit4.CarryDnaeError;
 import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.JUnit4TestFilter;
 import com.android.tradefed.util.StreamUtil;
@@ -627,7 +628,8 @@
     }
 
     private void runJUnit4Tests(
-            ITestInvocationListener listener, Runner checkRunner, String className) {
+            ITestInvocationListener listener, Runner checkRunner, String className)
+            throws DeviceNotAvailableException {
         JUnitCore runnerCore = new JUnitCore();
         JUnit4ResultForwarder list = new JUnit4ResultForwarder(listener);
         runnerCore.addListener(list);
@@ -636,14 +638,19 @@
         if (!(checkRunner instanceof ErrorReportingRunner)) {
             long startTime = System.currentTimeMillis();
             listener.testRunStarted(className, checkRunner.testCount());
-            if (mCollectTestsOnly) {
-                fakeDescriptionExecution(checkRunner.getDescription(), list);
-            } else {
-                setTestObjectInformation(checkRunner);
-                runnerCore.run(checkRunner);
+            try {
+                if (mCollectTestsOnly) {
+                    fakeDescriptionExecution(checkRunner.getDescription(), list);
+                } else {
+                    setTestObjectInformation(checkRunner);
+                    runnerCore.run(checkRunner);
+                }
+            } catch (CarryDnaeError e) {
+                throw e.getDeviceNotAvailableException();
+            } finally {
+                listener.testRunEnded(
+                        System.currentTimeMillis() - startTime, new HashMap<String, Metric>());
             }
-            listener.testRunEnded(
-                    System.currentTimeMillis() - startTime, new HashMap<String, Metric>());
         } else {
             // Special case where filtering leaves no tests to run, we report no failure
             // in this case.
@@ -671,8 +678,13 @@
                 fakeDescriptionExecution(child, listener);
             }
         } else {
-            listener.testStarted(desc);
-            listener.testFinished(desc);
+            try {
+                listener.testStarted(desc);
+                listener.testFinished(desc);
+            } catch (Exception e) {
+                // Should never happen
+                CLog.e(e);
+            }
         }
     }
 
diff --git a/src/com/android/tradefed/testtype/junit4/CarryDnaeError.java b/src/com/android/tradefed/testtype/junit4/CarryDnaeError.java
new file mode 100644
index 0000000..9b714fa
--- /dev/null
+++ b/src/com/android/tradefed/testtype/junit4/CarryDnaeError.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 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.tradefed.testtype.junit4;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+/**
+ * Internal {@link RuntimeException} to carry {@link DeviceNotAvailableException} through the JUnit4
+ * framework.
+ */
+public class CarryDnaeError extends RuntimeException {
+    static final long serialVersionUID = 4980196508277280342L;
+
+    private DeviceNotAvailableException mException;
+
+    public CarryDnaeError(DeviceNotAvailableException e) {
+        mException = e;
+    }
+
+    /** Returns the {@link DeviceNotAvailableException} carried by this wrapper. */
+    public DeviceNotAvailableException getDeviceNotAvailableException() {
+        return mException;
+    }
+}
diff --git a/src/com/android/tradefed/testtype/junit4/RunNotifierWrapper.java b/src/com/android/tradefed/testtype/junit4/RunNotifierWrapper.java
new file mode 100644
index 0000000..1099e28
--- /dev/null
+++ b/src/com/android/tradefed/testtype/junit4/RunNotifierWrapper.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 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.tradefed.testtype.junit4;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+import org.junit.runner.Description;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunNotifier;
+
+/** Wrapper of {@link RunNotifier} so we can carry the {@link DeviceNotAvailableException}. */
+public class RunNotifierWrapper extends RunNotifier {
+
+    private DeviceNotAvailableException mDnae;
+    private RunNotifier notifier;
+
+    public RunNotifierWrapper(RunNotifier notifier) {
+        this.notifier = notifier;
+    }
+
+    @Override
+    public void fireTestFailure(Failure failure) {
+        notifier.fireTestFailure(failure);
+        if (failure.getException() instanceof DeviceNotAvailableException) {
+            mDnae = (DeviceNotAvailableException) failure.getException();
+        }
+    }
+
+    @Override
+    public void fireTestAssumptionFailed(Failure failure) {
+        notifier.fireTestAssumptionFailed(failure);
+    }
+
+    @Override
+    public void fireTestFinished(Description description) {
+        notifier.fireTestFinished(description);
+    }
+
+    @Override
+    public void fireTestStarted(Description description) {
+        notifier.fireTestStarted(description);
+    }
+
+    @Override
+    public void fireTestIgnored(Description description) {
+        notifier.fireTestIgnored(description);
+    }
+
+    /** Returns the {@link DeviceNotAvailableException} if any was thrown. */
+    public DeviceNotAvailableException getDeviceNotAvailableException() {
+        return mDnae;
+    }
+}
diff --git a/tests/src/com/android/tradefed/testtype/HostTestTest.java b/tests/src/com/android/tradefed/testtype/HostTestTest.java
index d1c39d7..a5d8387 100644
--- a/tests/src/com/android/tradefed/testtype/HostTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/HostTestTest.java
@@ -300,6 +300,20 @@
     }
 
     @RunWith(DeviceJUnit4ClassRunner.class)
+    public static class JUnit4TestClassMultiExceptionDnae {
+
+        @org.junit.Test
+        public void testPass5() {
+            Assume.assumeTrue(false);
+        }
+
+        @After
+        public void tearDown() throws Exception {
+            throw new DeviceNotAvailableException("dnae", "serial");
+        }
+    }
+
+    @RunWith(DeviceJUnit4ClassRunner.class)
     public static class Junit4TestClassMulti implements IMultiDeviceTest {
         private Map<ITestDevice, IBuildInfo> mDeviceMap;
 
@@ -1191,6 +1205,29 @@
         EasyMock.verify(mListener);
     }
 
+    public void testRun_junit4style_multiException_dnae() throws Exception {
+        mListener = EasyMock.createStrictMock(ITestInvocationListener.class);
+        mHostTest.setClassName(JUnit4TestClassMultiExceptionDnae.class.getName());
+        TestDescription test1 =
+                new TestDescription(JUnit4TestClassMultiExceptionDnae.class.getName(), "testPass5");
+        mListener.testRunStarted((String) EasyMock.anyObject(), EasyMock.eq(1));
+        mListener.testStarted(EasyMock.eq(test1));
+        mListener.testFailed(
+                EasyMock.eq(test1),
+                EasyMock.contains("MultipleFailureException: There were 2 errors:"));
+        mListener.testEnded(EasyMock.eq(test1), (HashMap<String, Metric>) EasyMock.anyObject());
+        mListener.testRunFailed(EasyMock.anyObject());
+        mListener.testRunEnded(EasyMock.anyLong(), (HashMap<String, Metric>) EasyMock.anyObject());
+        EasyMock.replay(mListener);
+        try {
+            mHostTest.run(mListener);
+            fail("Should have thrown an exception.");
+        } catch (DeviceNotAvailableException expected) {
+
+        }
+        EasyMock.verify(mListener);
+    }
+
     /**
      * Test for {@link HostTest#run(ITestInvocationListener)}, for test with Junit4 style properly
      * pass to the test the {@link IMultiDeviceTest} information.
@@ -1878,6 +1915,7 @@
         mListener.testRunStarted(
                 EasyMock.eq("com.android.tradefed.testtype.HostTestTest$Junit4RegularClass"),
                 EasyMock.eq(1));
+        mListener.testRunEnded(EasyMock.anyLong(), EasyMock.<HashMap<String, Metric>>anyObject());
         EasyMock.replay(mListener);
         try {
             mHostTest.run(mListener);