Snap for 5285806 from ed72d74ae314877e1f19537d4351ed222003322a to pi-qpr3-release

Change-Id: I2443db6ef0272871fd4fb520eb7a75b4adb9b8b9
diff --git a/Android.mk b/Android.mk
index a5b52c7..9461ce2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -129,7 +129,7 @@
 # Zip up the built files and dist it as tradefed.zip
 ifneq (,$(filter tradefed tradefed-all, $(TARGET_BUILD_APPS)))
 
-tradefed_dist_host_jars := tradefed tradefed-tests tf-prod-tests tf-prod-metatests emmalib jack-jacoco-reporter loganalysis loganalysis-tests tf-remote-client tradefed-contrib
+tradefed_dist_host_jars := tradefed tradefed-tests tf-prod-tests tf-prod-metatests emmalib jack-jacoco-reporter loganalysis loganalysis-tests tf-remote-client tradefed-contrib tools-common-prebuilt
 tradefed_dist_host_jar_files := $(foreach m, $(tradefed_dist_host_jars), $(HOST_OUT_JAVA_LIBRARIES)/$(m).jar)
 
 tradefed_dist_host_exes := tradefed.sh tradefed_win.bat script_help.sh verify.sh run_tf_cmd.sh atest_tradefed.sh
diff --git a/src/com/android/tradefed/result/ddmlib/TestRunToTestInvocationForwarder.java b/src/com/android/tradefed/result/ddmlib/TestRunToTestInvocationForwarder.java
index 42ca817..bf26e07 100644
--- a/src/com/android/tradefed/result/ddmlib/TestRunToTestInvocationForwarder.java
+++ b/src/com/android/tradefed/result/ddmlib/TestRunToTestInvocationForwarder.java
@@ -34,7 +34,17 @@
  */
 public class TestRunToTestInvocationForwarder implements ITestRunListener {
 
+    private static final String NULL_STRING = "null";
+    public static final String ERROR_MESSAGE_FORMAT =
+            "Runner reported an invalid method 'null' (%s). Something went wrong, Skipping "
+                    + "its reporting.";
+
     private Collection<ITestLifeCycleReceiver> mListeners;
+    private Long mStartTime;
+    // Sometimes the instrumentation runner (Android JUnit Runner / AJUR) fails to load some class
+    // and report a "null" as a test method. This creates a lot of issues in the reporting pipeline
+    // so catch it, and avoid it at the root.
+    private TestIdentifier mNullMethod = null;
 
     public TestRunToTestInvocationForwarder(Collection<ITestLifeCycleReceiver> listeners) {
         mListeners = listeners;
@@ -46,6 +56,11 @@
 
     @Override
     public void testStarted(TestIdentifier testId) {
+        if (NULL_STRING.equals(testId.getTestName())) {
+            mNullMethod = testId;
+            return;
+        }
+        mNullMethod = null;
         for (ITestLifeCycleReceiver listener : mListeners) {
             try {
                 listener.testStarted(TestDescription.createFromTestIdentifier(testId));
@@ -60,6 +75,11 @@
 
     @Override
     public void testStarted(TestIdentifier testId, long startTime) {
+        if (NULL_STRING.equals(testId.getTestName())) {
+            mNullMethod = testId;
+            return;
+        }
+        mNullMethod = null;
         for (ITestLifeCycleReceiver listener : mListeners) {
             try {
                 listener.testStarted(TestDescription.createFromTestIdentifier(testId), startTime);
@@ -74,6 +94,9 @@
 
     @Override
     public void testAssumptionFailure(TestIdentifier testId, String trace) {
+        if (mNullMethod != null && mNullMethod.equals(testId)) {
+            return;
+        }
         for (ITestLifeCycleReceiver listener : mListeners) {
             try {
                 listener.testAssumptionFailure(
@@ -89,6 +112,9 @@
 
     @Override
     public void testFailed(TestIdentifier testId, String trace) {
+        if (mNullMethod != null && mNullMethod.equals(testId)) {
+            return;
+        }
         for (ITestLifeCycleReceiver listener : mListeners) {
             try {
                 listener.testFailed(TestDescription.createFromTestIdentifier(testId), trace);
@@ -103,6 +129,9 @@
 
     @Override
     public void testIgnored(TestIdentifier testId) {
+        if (mNullMethod != null && mNullMethod.equals(testId)) {
+            return;
+        }
         for (ITestLifeCycleReceiver listener : mListeners) {
             try {
                 listener.testIgnored(TestDescription.createFromTestIdentifier(testId));
@@ -118,6 +147,10 @@
     @Override
     public void testEnded(TestIdentifier testId, Map<String, String> testMetrics) {
         for (ITestLifeCycleReceiver listener : mListeners) {
+            if (mNullMethod != null && mNullMethod.equals(testId)) {
+                listener.testRunFailed(String.format(ERROR_MESSAGE_FORMAT, mNullMethod));
+                continue;
+            }
             try {
                 listener.testEnded(
                         TestDescription.createFromTestIdentifier(testId),
@@ -134,6 +167,10 @@
     @Override
     public void testEnded(TestIdentifier testId, long endTime, Map<String, String> testMetrics) {
         for (ITestLifeCycleReceiver listener : mListeners) {
+            if (mNullMethod != null && mNullMethod.equals(testId)) {
+                listener.testRunFailed(String.format(ERROR_MESSAGE_FORMAT, mNullMethod));
+                continue;
+            }
             try {
                 listener.testEnded(
                         TestDescription.createFromTestIdentifier(testId),
diff --git a/src/com/android/tradefed/targetprep/SwitchUserTargetPreparer.java b/src/com/android/tradefed/targetprep/SwitchUserTargetPreparer.java
new file mode 100644
index 0000000..9b4fe49
--- /dev/null
+++ b/src/com/android/tradefed/targetprep/SwitchUserTargetPreparer.java
@@ -0,0 +1,98 @@
+/*
+ * 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.targetprep;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+
+/**
+ * A {@link ITargetPreparer} that switches to the specified user kind in setUp. By default it
+ * remains in the current user, and no switching is performed.
+ *
+ * <p>Tries to restore device user state by switching back to the pre-execution current user.
+ */
+@OptionClass(alias = "switch-user-target-preparer")
+public class SwitchUserTargetPreparer extends BaseTargetPreparer implements ITargetCleaner {
+    private static final int USER_SYSTEM = 0; // From the UserHandle class.
+
+    /** Parameters that specify which user to run the test module as. */
+    public enum UserType {
+        // TODO:(b/123077733) Add support for guest and secondary.
+
+        /** current foreground user of the device */
+        CURRENT,
+        /** user flagged as primary on the device; most often primary = system user = user 0 */
+        PRIMARY,
+        /** system user = user 0 */
+        SYSTEM
+    }
+
+    @Option(
+        name = "user-type",
+        description = "The type of user to switch to before the module run."
+    )
+    private UserType mUserToSwitchTo = UserType.CURRENT;
+
+    private int mPreExecutionCurrentUser;
+
+    @Override
+    public void setUp(ITestDevice device, IBuildInfo buildInfo)
+            throws TargetSetupError, DeviceNotAvailableException {
+
+        mPreExecutionCurrentUser = device.getCurrentUser();
+
+        switch (mUserToSwitchTo) {
+            case SYSTEM:
+                switchToUser(USER_SYSTEM, device);
+                break;
+            case PRIMARY:
+                switchToUser(device.getPrimaryUserId(), device);
+                break;
+        }
+    }
+
+    private static void switchToUser(int userId, ITestDevice device)
+            throws TargetSetupError, DeviceNotAvailableException {
+        if (device.getCurrentUser() == userId) {
+            return;
+        }
+
+        // Otherwise, switch to user with userId.
+        if (device.switchUser(userId)) {
+            // Successful switch.
+            CLog.i("Switched to user %d.", userId);
+        } else {
+            // Couldn't switch, throw.
+            throw new TargetSetupError(
+                    String.format("Failed switch to user %d.", userId),
+                    device.getDeviceDescriptor());
+        }
+    }
+
+    @Override
+    public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
+            throws DeviceNotAvailableException {
+        // Restore the previous user as the foreground.
+        if (!device.switchUser(mPreExecutionCurrentUser)) {
+            CLog.w("Could not switch back to the user id: %d", mPreExecutionCurrentUser);
+        }
+    }
+}
diff --git a/src/com/android/tradefed/testtype/GTestListTestParser.java b/src/com/android/tradefed/testtype/GTestListTestParser.java
index 894010e..216f4e0 100644
--- a/src/com/android/tradefed/testtype/GTestListTestParser.java
+++ b/src/com/android/tradefed/testtype/GTestListTestParser.java
@@ -45,7 +45,7 @@
     // example: <line start>  emptyPlayback<line end>
     // example parameterized: <line start>  emptyPlayback/0 # GetParam() = (object 1)<line end>
     private static final Pattern TEST_METHOD =
-            Pattern.compile("\\s+([a-zA-Z]+[\\S]*)(.*)?(\\s+.*)?$");
+            Pattern.compile("\\s+([a-zA-Z_]+[\\S]*)(.*)?(\\s+.*)?$");
 
     // exposed for unit testing
     protected List<TestDescription> mTests = new ArrayList<>();
diff --git a/tests/res/testtype/gtest_list1.txt b/tests/res/testtype/gtest_list1.txt
index 0cce19e..1ef5287 100644
--- a/tests/res/testtype/gtest_list1.txt
+++ b/tests/res/testtype/gtest_list1.txt
@@ -8,7 +8,7 @@
   drawText
   drawText_strikeThruAndUnderline
   drawText_forceAlignLeft
-  drawColor
+  __drawColor
   backgroundAndImage
   saveLayer_simple
   saveLayer_missingRestore
diff --git a/tests/src/com/android/tradefed/UnitTests.java b/tests/src/com/android/tradefed/UnitTests.java
index f55bf9e..2cd4e0b 100644
--- a/tests/src/com/android/tradefed/UnitTests.java
+++ b/tests/src/com/android/tradefed/UnitTests.java
@@ -122,6 +122,7 @@
 import com.android.tradefed.result.TestRunResultTest;
 import com.android.tradefed.result.TestSummaryTest;
 import com.android.tradefed.result.XmlResultReporterTest;
+import com.android.tradefed.result.ddmlib.TestRunToTestInvocationForwarderTest;
 import com.android.tradefed.result.suite.FormattedGeneratorReporterTest;
 import com.android.tradefed.result.suite.XmlSuiteResultFormatterTest;
 import com.android.tradefed.sandbox.SandboxConfigDumpTest;
@@ -158,6 +159,7 @@
 import com.android.tradefed.targetprep.RunHostCommandTargetPreparerTest;
 import com.android.tradefed.targetprep.SdkAvdPreparerTest;
 import com.android.tradefed.targetprep.StopServicesSetupTest;
+import com.android.tradefed.targetprep.SwitchUserTargetPreparerTest;
 import com.android.tradefed.targetprep.SystemUpdaterDeviceFlasherTest;
 import com.android.tradefed.targetprep.TestAppInstallSetupTest;
 import com.android.tradefed.targetprep.TestFilePushSetupTest;
@@ -429,6 +431,9 @@
     TestSummaryTest.class,
     XmlResultReporterTest.class,
 
+    // result.ddmlib
+    TestRunToTestInvocationForwarderTest.class,
+
     // result.suite
     FormattedGeneratorReporterTest.class,
     XmlSuiteResultFormatterTest.class,
@@ -462,6 +467,7 @@
     TestAppInstallSetupTest.class,
     TestFilePushSetupTest.class,
     TimeSetterTargetPreparerTest.class,
+    SwitchUserTargetPreparerTest.class,
 
     // targetprep.multi
     MergeMultiBuildTargetPreparerTest.class,
diff --git a/tests/src/com/android/tradefed/result/ddmlib/TestRunToTestInvocationForwarderTest.java b/tests/src/com/android/tradefed/result/ddmlib/TestRunToTestInvocationForwarderTest.java
new file mode 100644
index 0000000..56dab4c
--- /dev/null
+++ b/tests/src/com/android/tradefed/result/ddmlib/TestRunToTestInvocationForwarderTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.result.ddmlib;
+
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.result.ITestLifeCycleReceiver;
+import com.android.tradefed.result.TestDescription;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.HashMap;
+
+/** Run unit tests {@link TestRunToTestInvocationForwarder}. */
+@RunWith(JUnit4.class)
+public class TestRunToTestInvocationForwarderTest {
+
+    private static final String RUN_NAME = "run";
+
+    private TestRunToTestInvocationForwarder mForwarder;
+    private ITestLifeCycleReceiver mMockListener;
+
+    @Before
+    public void setUp() {
+        mMockListener = EasyMock.createMock(ITestInvocationListener.class);
+        mForwarder = new TestRunToTestInvocationForwarder(mMockListener);
+    }
+
+    @Test
+    public void testForwarding() {
+        TestIdentifier tid1 = new TestIdentifier("class", "test1");
+        TestDescription td1 = new TestDescription(tid1.getClassName(), tid1.getTestName());
+        TestIdentifier tid2 = new TestIdentifier("class", "test2");
+        TestDescription td2 = new TestDescription(tid2.getClassName(), tid2.getTestName());
+        mMockListener.testRunStarted(RUN_NAME, 2);
+
+        mMockListener.testStarted(td1);
+        mMockListener.testFailed(td1, "I failed");
+        mMockListener.testEnded(td1, new HashMap<String, Metric>());
+
+        mMockListener.testStarted(td2);
+        mMockListener.testFailed(td2, "I failed");
+        mMockListener.testEnded(td2, new HashMap<String, Metric>());
+
+        mMockListener.testRunEnded(
+                EasyMock.anyLong(), EasyMock.<HashMap<String, Metric>>anyObject());
+
+        EasyMock.replay(mMockListener);
+        mForwarder.testRunStarted(RUN_NAME, 2);
+
+        mForwarder.testStarted(tid1);
+        mForwarder.testFailed(tid1, "I failed");
+        mForwarder.testEnded(tid1, new HashMap<>());
+
+        mForwarder.testStarted(tid2);
+        mForwarder.testFailed(tid2, "I failed");
+        mForwarder.testEnded(tid2, new HashMap<>());
+
+        mForwarder.testRunEnded(500L, new HashMap<>());
+        EasyMock.verify(mMockListener);
+    }
+
+    @Test
+    public void testForwarding_null() {
+        TestIdentifier tid1 = new TestIdentifier("class", "test1");
+        TestDescription td1 = new TestDescription(tid1.getClassName(), tid1.getTestName());
+        TestIdentifier tid2 = new TestIdentifier("class", "null");
+        mMockListener.testRunStarted(RUN_NAME, 2);
+
+        mMockListener.testStarted(td1);
+        mMockListener.testFailed(td1, "I failed");
+        mMockListener.testEnded(td1, new HashMap<String, Metric>());
+        // Second bad method is not propagated, instead we fail the run
+        mMockListener.testRunFailed(
+                String.format(TestRunToTestInvocationForwarder.ERROR_MESSAGE_FORMAT, tid2));
+
+        mMockListener.testRunEnded(
+                EasyMock.anyLong(), EasyMock.<HashMap<String, Metric>>anyObject());
+
+        EasyMock.replay(mMockListener);
+        mForwarder.testRunStarted(RUN_NAME, 2);
+
+        mForwarder.testStarted(tid1);
+        mForwarder.testFailed(tid1, "I failed");
+        mForwarder.testEnded(tid1, new HashMap<>());
+
+        mForwarder.testStarted(tid2);
+        mForwarder.testFailed(tid2, "I failed");
+        mForwarder.testEnded(tid2, new HashMap<>());
+
+        mForwarder.testRunEnded(500L, new HashMap<>());
+        EasyMock.verify(mMockListener);
+    }
+}
diff --git a/tests/src/com/android/tradefed/targetprep/SwitchUserTargetPreparerTest.java b/tests/src/com/android/tradefed/targetprep/SwitchUserTargetPreparerTest.java
new file mode 100644
index 0000000..6b55093
--- /dev/null
+++ b/tests/src/com/android/tradefed/targetprep/SwitchUserTargetPreparerTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.targetprep;
+
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link SwitchUserTargetPreparer}. */
+@RunWith(JUnit4.class)
+public class SwitchUserTargetPreparerTest {
+    private static final int USER_SYSTEM = 0; // From the UserHandle class.
+
+    @Mock private ITestDevice mMockDevice;
+
+    private SwitchUserTargetPreparer mSwitchUserTargetPreparer;
+    private OptionSetter mOptionSetter;
+
+    @Before
+    public void setUp() throws ConfigurationException {
+        MockitoAnnotations.initMocks(this);
+        mSwitchUserTargetPreparer = new SwitchUserTargetPreparer();
+        mOptionSetter = new OptionSetter(mSwitchUserTargetPreparer);
+    }
+
+    @Test
+    public void testSetUpRunAsPrimary_ifAlreadyInPrimary_noUserSwitch()
+            throws DeviceNotAvailableException, TargetSetupError, ConfigurationException {
+        // setup
+        mockUsers(/* primaryUserId= */ 11, /* currentUserId= */ 11);
+        mOptionSetter.setOptionValue("user-type", "primary");
+
+        // act
+        mSwitchUserTargetPreparer.setUp(mMockDevice, /* buildInfo= */ null);
+
+        // assert
+        verify(mMockDevice, never()).switchUser(anyInt());
+    }
+
+    @Test
+    public void testSetUpRunAsSystem_ifAlreadyInSystem_noUserSwitch()
+            throws DeviceNotAvailableException, TargetSetupError, ConfigurationException {
+        // setup
+        mockUsers(/* primaryUserId= */ 11, /* currentUserId= */ USER_SYSTEM);
+        mOptionSetter.setOptionValue("user-type", "system");
+
+        // act
+        mSwitchUserTargetPreparer.setUp(mMockDevice, /* buildInfo= */ null);
+
+        // assert
+        verify(mMockDevice, never()).switchUser(anyInt());
+    }
+
+    @Test
+    public void testSetUpRunAsPrimary_ifNotInPrimary_switchToPrimary()
+            throws DeviceNotAvailableException, TargetSetupError, ConfigurationException {
+        // setup
+        mockUsers(/* primaryUserId= */ 10, /* currentUserId= */ 11);
+        mOptionSetter.setOptionValue("user-type", "primary");
+
+        // act
+        mSwitchUserTargetPreparer.setUp(mMockDevice, /* buildInfo= */ null);
+
+        // assert it switches to primary in setUp
+        verify(mMockDevice, times(1)).switchUser(10);
+    }
+
+    @Test
+    public void testSetUpRunAsSystem_ifNotInSystem_switchToSystem()
+            throws DeviceNotAvailableException, TargetSetupError, ConfigurationException {
+        // setup
+        mockUsers(/* primaryUserId= */ 10, /* currentUserId= */ 11);
+        mOptionSetter.setOptionValue("user-type", "system");
+
+        // act
+        mSwitchUserTargetPreparer.setUp(mMockDevice, /* buildInfo= */ null);
+
+        // assert it switches to primary in setUp
+        verify(mMockDevice, times(1)).switchUser(USER_SYSTEM);
+    }
+
+    @Test
+    public void testTearDown_ifStartedInSecondary_switchesBackToSecondary()
+            throws DeviceNotAvailableException, TargetSetupError, ConfigurationException {
+        // setup
+        mockUsers(/* primaryUserId= */ 0, /* currentUserId= */ 10);
+        mOptionSetter.setOptionValue("user-type", "primary");
+
+        // first switches to primary
+        mSwitchUserTargetPreparer.setUp(mMockDevice, /* buildInfo= */ null);
+        verify(mMockDevice, times(1)).switchUser(0);
+
+        // then switches back to secondary
+        mSwitchUserTargetPreparer.tearDown(mMockDevice, /* buildInfo= */ null, null);
+        verify(mMockDevice, times(1)).switchUser(10);
+    }
+
+    @Test
+    public void testSetUp_ifNoSwitchToSpecified_noUserSwitch()
+            throws DeviceNotAvailableException, TargetSetupError {
+        // setup
+        mockUsers(/* primaryUserId= */ 0, /* currentUserId= */ 10);
+
+        // act
+        mSwitchUserTargetPreparer.setUp(mMockDevice, /* buildInfo= */ null);
+
+        // assert
+        verify(mMockDevice, never()).switchUser(anyInt());
+    }
+
+    @Test
+    public void testSetUp_ifSwitchFails_throwsTargetSetupError()
+            throws DeviceNotAvailableException, ConfigurationException {
+        // setup
+        mockUsers(/* primaryUserId= */ 0, /* currentUserId= */ 11);
+        mOptionSetter.setOptionValue("user-type", "primary");
+        when(mMockDevice.switchUser(0)).thenReturn(false);
+
+        // act
+        try {
+            mSwitchUserTargetPreparer.setUp(mMockDevice, /* buildInfo= */ null);
+            fail("Should have thrown TargetSetupError exception.");
+        } catch (TargetSetupError e) {
+            // do nothing
+        }
+    }
+
+    private void mockUsers(int primaryUserId, int currentUserId)
+            throws DeviceNotAvailableException {
+        when(mMockDevice.getCurrentUser()).thenReturn(currentUserId);
+        when(mMockDevice.getPrimaryUserId()).thenReturn(primaryUserId);
+        when(mMockDevice.switchUser(anyInt())).thenReturn(true);
+    }
+}