Support exchanging files during invocation
Provide an official support outside BUildInfo to pass file
information between TF objects.
Use it for the adb preparer + test.
Test: unit tests
Bug: 146371544
Change-Id: I1f5d71617e6c31bf1a99f2c20f64e60942744be7
diff --git a/invocation_interfaces/com/android/tradefed/invoker/ExecutionFiles.java b/invocation_interfaces/com/android/tradefed/invoker/ExecutionFiles.java
new file mode 100644
index 0000000..391147b
--- /dev/null
+++ b/invocation_interfaces/com/android/tradefed/invoker/ExecutionFiles.java
@@ -0,0 +1,140 @@
+/*
+ * 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.invoker;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.io.File;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/** Files generated during the execution of a test or invocation that need to be carried */
+public class ExecutionFiles {
+
+ /** Enumeration of known standard key for the map. */
+ public static enum FilesKey {
+ ADB_BINARY
+ }
+
+ private final ConcurrentMap<String, File> mProperties = new ConcurrentHashMap<>();
+
+ // Package private to avoid new instantiation.
+ ExecutionFiles() {}
+
+ /**
+ * Associates the specified value with the specified key in this map.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with {@code key}, or {@code null} if there was no
+ * mapping for {@code key}.
+ * @see ConcurrentMap
+ */
+ public File put(String key, File value) {
+ return mProperties.put(key, value);
+ }
+
+ /**
+ * Variation of {@link #put(FilesKey, File)} with a known key.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with {@code key}, or {@code null} if there was no
+ * mapping for {@code key}.
+ */
+ public File put(FilesKey key, File value) {
+ return mProperties.put(key.toString(), value);
+ }
+
+ /**
+ * If the specified key is not already associated with a value, associates it with the given
+ * value.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with the specified key, or {@code null} if there was no
+ * mapping for the key.
+ */
+ public File putIfAbsent(String key, File value) {
+ return mProperties.putIfAbsent(key, value);
+ }
+
+ /**
+ * Copies all of the mappings from the specified map to this map.
+ *
+ * @param properties mappings to be stored in this map
+ * @return The final mapping
+ */
+ public ExecutionFiles putAll(Map<String, File> properties) {
+ mProperties.putAll(properties);
+ return this;
+ }
+
+ /** Returns whether or not the map of properties is empty. */
+ public boolean isEmpty() {
+ return mProperties.isEmpty();
+ }
+
+ /**
+ * Returns the value to which the specified key is mapped, or {@code null} if this map contains
+ * no mapping for the key.
+ *
+ * @param key the key whose associated value is to be returned
+ * @return the value to which the specified key is mapped, or {@code null} if this map contains
+ * no mapping for the key
+ */
+ public File get(String key) {
+ return mProperties.get(key);
+ }
+
+ /**
+ * Variation of {@link #get(String)} with a known key.
+ *
+ * @param key the key whose associated value is to be returned
+ * @return the value to which the specified key is mapped, or {@code null} if this map contains
+ * no mapping for the key
+ */
+ public File get(FilesKey key) {
+ return mProperties.get(key.toString());
+ }
+
+ /**
+ * Returns {@code true} if this map contains a mapping for the specified key.
+ *
+ * @param key key whose presence in this map is to be tested
+ * @return {@code true} if this map contains a mapping for the specified key
+ */
+ public boolean containsKey(String key) {
+ return mProperties.containsKey(key);
+ }
+
+ /** Returns all the properties in a copy of the map */
+ public ImmutableMap<String, File> getAll() {
+ return ImmutableMap.copyOf(mProperties);
+ }
+
+ /**
+ * Removes the mapping for a key from this map if it is present (optional operation).
+ *
+ * @param key key whose mapping is to be removed from the map
+ * @return the previous value associated with {@code key}, or {@code null} if there was no
+ * mapping for {@code key}.
+ */
+ public File remove(String key) {
+ return mProperties.remove(key);
+ }
+}
diff --git a/invocation_interfaces/com/android/tradefed/invoker/TestInformation.java b/invocation_interfaces/com/android/tradefed/invoker/TestInformation.java
index 1e818b0..4880260 100644
--- a/invocation_interfaces/com/android/tradefed/invoker/TestInformation.java
+++ b/invocation_interfaces/com/android/tradefed/invoker/TestInformation.java
@@ -19,9 +19,7 @@
import com.android.tradefed.device.ITestDevice;
import java.io.File;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
/**
* Holder object that contains all the information and dependencies a test runner or test might need
@@ -32,24 +30,27 @@
private final IInvocationContext mContext;
/** Properties generated during execution. */
private final ExecutionProperties mProperties;
+ /**
+ * Files generated during execution that needs to be carried, they will be deleted at the end of
+ * the invocation.
+ */
+ private final ExecutionFiles mExecutionFiles;
/** Main folder for all dependencies of tests */
private final File mDependenciesFolder;
- /** The complete dependencies resolved for the invocation, keyed by file name requested */
- private final Map<String, File> mDependencies;
private TestInformation(Builder builder) {
mContext = builder.mContext;
mProperties = builder.mProperties;
mDependenciesFolder = builder.mDependenciesFolder;
- mDependencies = builder.mDependencies;
+ mExecutionFiles = builder.mExecutionFiles;
}
private TestInformation(TestInformation invocationInfo, IInvocationContext moduleContext) {
mContext = moduleContext;
mProperties = invocationInfo.mProperties;
mDependenciesFolder = invocationInfo.mDependenciesFolder;
- mDependencies = invocationInfo.mDependencies;
+ mExecutionFiles = invocationInfo.mExecutionFiles;
}
/** Create a builder for creating {@link TestInformation} instances. */
@@ -92,6 +93,15 @@
return mProperties;
}
+ /**
+ * Returns the files generated during the invocation execution. Passing files through the {@link
+ * ExecutionFiles} is the recommended way to make a file available between target_preparers and
+ * tests.
+ */
+ public ExecutionFiles executionFiles() {
+ return mExecutionFiles;
+ }
+
/** Returns the folder where all the dependencies are stored for an invocation. */
public File dependenciesFolder() {
return mDependenciesFolder;
@@ -102,11 +112,11 @@
private IInvocationContext mContext;
private ExecutionProperties mProperties;
private File mDependenciesFolder;
- private final Map<String, File> mDependencies;
+ private ExecutionFiles mExecutionFiles;
private Builder() {
mProperties = new ExecutionProperties();
- mDependencies = new HashMap<>();
+ mExecutionFiles = new ExecutionFiles();
}
public TestInformation build() {
diff --git a/src/com/android/tradefed/invoker/TestInvocation.java b/src/com/android/tradefed/invoker/TestInvocation.java
index 3e6a837..5dc294a 100644
--- a/src/com/android/tradefed/invoker/TestInvocation.java
+++ b/src/com/android/tradefed/invoker/TestInvocation.java
@@ -889,6 +889,10 @@
config.cleanConfigurationData();
// Delete the invocation work directory at the end
FileUtil.recursiveDelete(info.dependenciesFolder());
+ // Delete all the execution files
+ for (File f : info.executionFiles().getAll().values()) {
+ FileUtil.recursiveDelete(f);
+ }
}
}
diff --git a/test_framework/com/android/tradefed/targetprep/adb/AdbStopServerPreparer.java b/test_framework/com/android/tradefed/targetprep/adb/AdbStopServerPreparer.java
index 0424612..a73a671 100644
--- a/test_framework/com/android/tradefed/targetprep/adb/AdbStopServerPreparer.java
+++ b/test_framework/com/android/tradefed/targetprep/adb/AdbStopServerPreparer.java
@@ -22,7 +22,8 @@
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.IDeviceManager;
-import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.ExecutionFiles.FilesKey;
+import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.targetprep.BaseTargetPreparer;
import com.android.tradefed.targetprep.BuildError;
@@ -63,9 +64,9 @@
/** {@inheritDoc} */
@Override
- public void setUp(ITestDevice device, IBuildInfo buildInfo)
+ public void setUp(TestInformation testInfo)
throws TargetSetupError, BuildError, DeviceNotAvailableException {
-
+ IBuildInfo buildInfo = testInfo.getBuildInfo();
getDeviceManager().stopAdbBridge();
// Kill the default adb server
@@ -93,7 +94,7 @@
adb = buildInfo.getFile("adb");
adb = renameAdbBinary(adb);
// Track the updated adb file.
- buildInfo.setFile(ADB_BINARY_KEY, adb, "adb");
+ testInfo.executionFiles().put(FilesKey.ADB_BINARY, adb);
}
if (adb != null) {
@@ -107,19 +108,19 @@
"Failed to restart adb with the build info one. stdout: %s.\n"
+ "sterr: %s",
result.getStdout(), result.getStderr()),
- device.getDeviceDescriptor());
+ testInfo.getDevice().getDeviceDescriptor());
}
} else {
getRunUtil().runTimedCmd(CMD_TIMEOUT, "adb", "start-server");
throw new TargetSetupError(
- "Could not find a new version of adb to tests.", device.getDeviceDescriptor());
+ "Could not find a new version of adb to tests.",
+ testInfo.getDevice().getDeviceDescriptor());
}
}
/** {@inheritDoc} */
@Override
- public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
- throws DeviceNotAvailableException {
+ public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException {
FileUtil.recursiveDelete(mTmpDir);
// Kill the test adb server
getRunUtil().runTimedCmd(CMD_TIMEOUT, "adb", "kill-server");
diff --git a/test_framework/com/android/tradefed/testtype/python/PythonBinaryHostTest.java b/test_framework/com/android/tradefed/testtype/python/PythonBinaryHostTest.java
index 71e087f..83d6a75 100644
--- a/test_framework/com/android/tradefed/testtype/python/PythonBinaryHostTest.java
+++ b/test_framework/com/android/tradefed/testtype/python/PythonBinaryHostTest.java
@@ -22,6 +22,7 @@
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.StubDevice;
+import com.android.tradefed.invoker.ExecutionFiles.FilesKey;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
@@ -29,7 +30,6 @@
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.LogDataType;
import com.android.tradefed.result.ResultForwarder;
-import com.android.tradefed.targetprep.adb.AdbStopServerPreparer;
import com.android.tradefed.testtype.IRemoteTest;
import com.android.tradefed.testtype.PythonUnitTestResultParser;
import com.android.tradefed.util.CommandResult;
@@ -105,7 +105,7 @@
continue;
}
pyFile.setExecutable(true);
- runSinglePythonFile(listener, pyFile);
+ runSinglePythonFile(listener, testInfo, pyFile);
}
}
@@ -133,7 +133,8 @@
return files;
}
- private void runSinglePythonFile(ITestInvocationListener listener, File pyFile) {
+ private void runSinglePythonFile(
+ ITestInvocationListener listener, TestInformation testInfo, File pyFile) {
List<String> commandLine = new ArrayList<>();
commandLine.add(pyFile.getAbsolutePath());
// If we have a physical device, pass it to the python test by serial
@@ -148,7 +149,7 @@
.setEnvVariable(ANDROID_SERIAL_VAR, mTestInfo.getDevice().getSerialNumber());
}
- File updatedAdb = mTestInfo.getBuildInfo().getFile(AdbStopServerPreparer.ADB_BINARY_KEY);
+ File updatedAdb = testInfo.executionFiles().get(FilesKey.ADB_BINARY);
if (updatedAdb == null) {
String adbPath = getAdbPath();
// Don't check if it's the adb on the $PATH
diff --git a/tests/src/com/android/tradefed/targetprep/adb/AdbStopServerPreparerTest.java b/tests/src/com/android/tradefed/targetprep/adb/AdbStopServerPreparerTest.java
index f2cf74b..dfc6b95 100644
--- a/tests/src/com/android/tradefed/targetprep/adb/AdbStopServerPreparerTest.java
+++ b/tests/src/com/android/tradefed/targetprep/adb/AdbStopServerPreparerTest.java
@@ -22,6 +22,8 @@
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.IDeviceManager;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.InvocationContext;
+import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.targetprep.TargetSetupError;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
@@ -45,6 +47,7 @@
private IRunUtil mMockRunUtil;
private IDeviceManager mMockManager;
+ private TestInformation mTestInfo;
private ITestDevice mMockDevice;
private IBuildInfo mMockBuild;
private File mFakeAdbFile;
@@ -79,6 +82,10 @@
mMockRunUtil.sleep(2000);
EasyMock.expect(mMockDevice.getDeviceDescriptor()).andStubReturn(null);
+ InvocationContext context = new InvocationContext();
+ context.addAllocatedDevice("device", mMockDevice);
+ context.addDeviceBuildInfo("device", mMockBuild);
+ mTestInfo = TestInformation.newBuilder().setInvocationContext(context).build();
}
@After
@@ -105,8 +112,8 @@
// tear down
mockTearDown();
EasyMock.replay(mMockRunUtil, mMockManager, mMockDevice);
- mPreparer.setUp(mMockDevice, mMockBuild);
- mPreparer.tearDown(mMockDevice, mMockBuild, null);
+ mPreparer.setUp(mTestInfo);
+ mPreparer.tearDown(mTestInfo, null);
EasyMock.verify(mMockRunUtil, mMockManager, mMockDevice);
}
@@ -133,12 +140,12 @@
mockTearDown();
EasyMock.replay(mMockRunUtil, mMockManager, mMockDevice);
try {
- mPreparer.setUp(mMockDevice, mMockBuild);
+ mPreparer.setUp(mTestInfo);
fail("Should have thrown an exception.");
} catch (TargetSetupError expected) {
// Expected
}
- mPreparer.tearDown(mMockDevice, mMockBuild, null);
+ mPreparer.tearDown(mTestInfo, null);
EasyMock.verify(mMockRunUtil, mMockManager, mMockDevice);
}
@@ -160,7 +167,11 @@
.andReturn(result);
EasyMock.replay(mMockRunUtil, mMockManager, mMockDevice);
try {
- mPreparer.setUp(mMockDevice, new BuildInfo());
+ InvocationContext context = new InvocationContext();
+ context.addAllocatedDevice("device", mMockDevice);
+ context.addDeviceBuildInfo("device", new BuildInfo());
+ mTestInfo = TestInformation.newBuilder().setInvocationContext(context).build();
+ mPreparer.setUp(mTestInfo);
fail("Should have thrown an exception.");
} catch (TargetSetupError expected) {
// Expected
@@ -198,8 +209,8 @@
// tear down
mockTearDown();
EasyMock.replay(mMockRunUtil, mMockManager, mMockDevice);
- mPreparer.setUp(mMockDevice, mMockBuild);
- mPreparer.tearDown(mMockDevice, mMockBuild, null);
+ mPreparer.setUp(mTestInfo);
+ mPreparer.tearDown(mTestInfo, null);
EasyMock.verify(mMockRunUtil, mMockManager, mMockDevice);
} finally {
FileUtil.recursiveDelete(tmpDir);
diff --git a/tests/src/com/android/tradefed/testtype/python/PythonBinaryHostTestTest.java b/tests/src/com/android/tradefed/testtype/python/PythonBinaryHostTestTest.java
index d590cdd..b285530 100644
--- a/tests/src/com/android/tradefed/testtype/python/PythonBinaryHostTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/python/PythonBinaryHostTestTest.java
@@ -19,13 +19,13 @@
import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.StubDevice;
+import com.android.tradefed.invoker.ExecutionFiles.FilesKey;
import com.android.tradefed.invoker.IInvocationContext;
import com.android.tradefed.invoker.InvocationContext;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.LogDataType;
-import com.android.tradefed.targetprep.adb.AdbStopServerPreparer;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.FileUtil;
@@ -71,8 +71,6 @@
return mFakeAdb.getAbsolutePath();
}
};
- EasyMock.expect(mMockBuildInfo.getFile(AdbStopServerPreparer.ADB_BINARY_KEY))
- .andReturn(null);
IInvocationContext context = new InvocationContext();
context.addAllocatedDevice("device", mMockDevice);
context.addDeviceBuildInfo("device", mMockBuildInfo);
@@ -128,11 +126,7 @@
*/
@Test
public void testRun_withAdbPath() throws Exception {
- mMockBuildInfo = EasyMock.createMock(IBuildInfo.class);
- EasyMock.expect(mMockBuildInfo.getFile(AdbStopServerPreparer.ADB_BINARY_KEY))
- .andReturn(new File("/test/adb"));
- mTestInfo.getContext().addDeviceBuildInfo("device", mMockBuildInfo);
-
+ mTestInfo.executionFiles().put(FilesKey.ADB_BINARY, new File("/test/adb"));
File binary = FileUtil.createTempFile("python-dir", "");
try {
OptionSetter setter = new OptionSetter(mTest);