summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkgui <kgui@google.com>2024-01-04 03:09:48 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2024-01-04 03:09:48 +0000
commit507b4520dcf97ebcfd8caf73ebacdae27e97e60f (patch)
treead6976c0ef7af61a039a6015a3807b78fdb5546d
parent80801f48686f5781c8f18d93a863e1ade1964b1c (diff)
parentbb7dc840badd31240c62ea71282d125873b85617 (diff)
downloadsuite_harness-507b4520dcf97ebcfd8caf73ebacdae27e97e60f.tar.gz
Add InteractiveResultCollector to pull xTS-Interactive result files from the device to the host. am: a433ddbc3e am: bb7dc840ba
Original change: https://android-review.googlesource.com/c/platform/test/suite_harness/+/2891634 Change-Id: Ia324ab20d8e35275013768c2fd3288a23d46bef4 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/InteractiveResultCollector.java124
-rw-r--r--common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/targetprep/InteractiveResultCollectorTest.java144
2 files changed, 268 insertions, 0 deletions
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/InteractiveResultCollector.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/InteractiveResultCollector.java
new file mode 100644
index 00000000..67de0e44
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/InteractiveResultCollector.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2023 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.targetprep;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.build.IDeviceBuildInfo;
+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.invoker.TestInformation;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.error.InfraErrorIdentifier;
+import com.android.tradefed.targetprep.BaseTargetPreparer;
+import com.android.tradefed.targetprep.TargetSetupError;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A {@link ITargetPreparer} that attempts to pull any number of files from device path(s) to any
+ * xTS report.
+ *
+ * <p>Should be performed *before* a new build is flashed, and *after* DeviceSetup is run (if
+ * enabled).
+ */
+@OptionClass(alias = "interactive-result-collector")
+public class InteractiveResultCollector extends BaseTargetPreparer {
+
+ @Option(
+ name = "host-path",
+ description =
+ "The host-side relative path to the directory where results should be"
+ + " collected. If not specified, defaults to the 'screenshots' directory.")
+ private String hostPath = "screenshots";
+
+ @Option(
+ name = "device-cleanup",
+ description =
+ "Whether all files in the device folers should be cleaned up before test. "
+ + "Note that the preparer does not verify that files/directories have"
+ + "been deleted successfully.")
+ private boolean mCleanup = true;
+
+ @Option(
+ name = "device-paths",
+ description = "The list of paths to the files stored on the device.")
+ private List<String> devicePaths = new ArrayList<>();
+
+ @Override
+ public void setUp(TestInformation testInfo)
+ throws TargetSetupError, DeviceNotAvailableException {
+ ITestDevice mDevice = testInfo.getDevice();
+ if (!(testInfo.getBuildInfo() instanceof IDeviceBuildInfo)) {
+ throw new TargetSetupError(
+ "Invalid buildInfo, expecting an IDeviceBuildInfo",
+ mDevice.getDeviceDescriptor(),
+ InfraErrorIdentifier.UNDETERMINED);
+ }
+ if (mCleanup && !devicePaths.isEmpty()) {
+ for (String devicePath : devicePaths) {
+ if (!devicePath.isEmpty()) {
+ CLog.d("Start clean up path: %s", devicePath);
+ mDevice.executeAdbCommand("shell", "rm", "-rf", devicePath);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException {
+ if (e != null && (e instanceof DeviceNotAvailableException)) {
+ CLog.e("Invocation finished with DeviceNotAvailable, skip collecting results.");
+ return;
+ }
+
+ File hostResultDir = null;
+ if (!devicePaths.isEmpty()) {
+ try {
+ hostResultDir =
+ new File(
+ new CompatibilityBuildHelper(testInfo.getBuildInfo())
+ .getResultDir(),
+ hostPath);
+ if (!hostResultDir.exists()) {
+ hostResultDir.mkdir();
+ }
+ } catch (FileNotFoundException exception) {
+ CLog.e(exception);
+ }
+ }
+ if (hostResultDir == null) {
+ // No host result directory, either no device paths, or fail to create it.
+ return;
+ }
+
+ ITestDevice testDevice = testInfo.getDevice();
+ for (String devicePath : devicePaths) {
+ if (!devicePath.isEmpty()) {
+ if (testDevice.pullDir(devicePath, hostResultDir)) {
+ CLog.d(
+ String.format(
+ "Successfully pulled %s to %s.",
+ devicePath, hostResultDir.getAbsolutePath()));
+ }
+ }
+ }
+ }
+}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/targetprep/InteractiveResultCollectorTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/targetprep/InteractiveResultCollectorTest.java
new file mode 100644
index 00000000..e66bf65e
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/targetprep/InteractiveResultCollectorTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 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.targetprep;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.build.BuildInfo;
+import com.android.tradefed.build.DeviceBuildInfo;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.build.IDeviceBuildInfo;
+import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.NativeDevice;
+import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.invoker.InvocationContext;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.targetprep.TargetSetupError;
+import com.android.tradefed.util.FileUtil;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+
+/** Unit tests for {@link InteractiveResultCollector}. */
+@RunWith(JUnit4.class)
+public final class InteractiveResultCollectorTest {
+
+ private static final String DEVICE_PATH = "/sdcard/documents/xts/screenshot";
+
+ private final InteractiveResultCollector mCollector = new InteractiveResultCollector();
+ private OptionSetter mOptionSetter;
+ private TestInformation mTestInfo;
+
+ @Test
+ public void setUp_nonDeviceBuildInfo_throwException() throws Exception {
+ initTestInfo(new BuildInfo(), mock(NativeDevice.class));
+
+ assertThrows(TargetSetupError.class, () -> mCollector.setUp(mTestInfo));
+ }
+
+ @Test
+ public void setUp_deviceCleanup_emptyDevicePaths_doNothing() throws Exception {
+ ITestDevice testDevice = mock(ITestDevice.class);
+ initTestInfo(new DeviceBuildInfo("0", ""), testDevice);
+
+ mCollector.setUp(mTestInfo);
+
+ verify(testDevice, never())
+ .executeAdbCommand(anyString(), anyString(), anyString(), anyString());
+ }
+
+ @Test
+ public void setUp_deviceClenup_emptyDevicePathSkipped() throws Exception {
+ ITestDevice testDevice = mock(ITestDevice.class);
+ initTestInfo(new DeviceBuildInfo("0", ""), testDevice);
+ mOptionSetter.setOptionValue("device-paths", "");
+ mOptionSetter.setOptionValue("device-paths", DEVICE_PATH);
+ mOptionSetter.setOptionValue("device-paths", "");
+
+ mCollector.setUp(mTestInfo);
+
+ // Only one execution for DEVICE_PATH.
+ verify(testDevice).executeAdbCommand(anyString(), anyString(), anyString(), anyString());
+ verify(testDevice).executeAdbCommand("shell", "rm", "-rf", DEVICE_PATH);
+ }
+
+ @Test
+ public void tearDown_deviceNotAvailableException_doNothing() throws Exception {
+ mCollector.tearDown(/* testInfo= */ null, new DeviceNotAvailableException("", ""));
+ }
+
+ @Test
+ public void tearDown_emptyDevicePaths_doNothing() throws Exception {
+ mCollector.tearDown(/* testInfo= */ null, /* e= */ null);
+ }
+
+ @Test
+ public void tearDown_failToGetResultDir_doNothing() throws Exception {
+ initTestInfo(new DeviceBuildInfo("0", ""), mock(NativeDevice.class));
+ mOptionSetter.setOptionValue("device-paths", DEVICE_PATH);
+
+ mCollector.tearDown(mTestInfo, /* e= */ null);
+ }
+
+ @Test
+ public void tearDown_pullDir_emptyDevicePathSkipped() throws Exception {
+ ITestDevice testDevice = mock(ITestDevice.class);
+ IDeviceBuildInfo buildInfo = new DeviceBuildInfo("0", "");
+ // Init the resultDir.
+ File rootDirForTesting = FileUtil.createTempDir("InteractiveResultCollectorTest");
+ buildInfo.addBuildAttribute(
+ CompatibilityBuildHelper.ROOT_DIR, rootDirForTesting.getAbsolutePath());
+ buildInfo.addBuildAttribute(CompatibilityBuildHelper.SUITE_NAME, "cts");
+ buildInfo.addBuildAttribute(CompatibilityBuildHelper.START_TIME_MS, "1000000");
+ new File(rootDirForTesting, "android-cts").mkdir();
+
+ initTestInfo(buildInfo, testDevice);
+ mOptionSetter.setOptionValue("device-paths", "");
+ mOptionSetter.setOptionValue("device-paths", DEVICE_PATH);
+ mOptionSetter.setOptionValue("device-paths", "");
+
+ mCollector.tearDown(mTestInfo, /* e= */ null);
+
+ // Only one execution for DEVICE_PATH.
+ verify(testDevice).pullDir(anyString(), any(File.class));
+ verify(testDevice).pullDir(eq(DEVICE_PATH), any(File.class));
+ }
+
+ /**
+ * Initializes the {@link TestInformation} for tests by the given {@link IBuildInfo} and {@link
+ * ITestDevice}.
+ */
+ private void initTestInfo(IBuildInfo buildInfo, ITestDevice testDevice) throws Exception {
+ IInvocationContext context = new InvocationContext();
+ context.addDeviceBuildInfo("device", buildInfo);
+ context.addAllocatedDevice("device", testDevice);
+
+ mOptionSetter = new OptionSetter(mCollector);
+ mTestInfo = TestInformation.newBuilder().setInvocationContext(context).build();
+ }
+}