Merge "Atest: Add 4 detailed events to metrics."
diff --git a/.gitignore b/.gitignore
index 81cd456..76080d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,7 @@
.idea
*.iml
*.pyc
+
+# python coverage generated files.
+.coverage
+**/htmlcov
diff --git a/Android.bp b/Android.bp
index 1bb84d9..dbe9b42 100644
--- a/Android.bp
+++ b/Android.bp
@@ -24,6 +24,7 @@
"-Xep:ConstantField:ERROR",
"-Xep:DeadException:ERROR",
"-Xep:EqualsIncompatibleType:ERROR",
+ "-Xep:ExtendingJUnitAssert:ERROR",
"-Xep:FormatString:ERROR",
"-Xep:GetClassOnClass:ERROR",
"-Xep:IdentityBinaryExpression:ERROR",
@@ -32,9 +33,11 @@
"-Xep:JUnitAmbiguousTestClass:ERROR",
"-Xep:MissingFail:ERROR",
"-Xep:MissingOverride:ERROR",
+ "-Xep:ModifiedButNotUsed:ERROR",
"-Xep:MustBeClosedChecker:ERROR",
"-Xep:Overrides:ERROR",
"-Xep:PackageLocation:ERROR",
+ "-Xep:ParameterName:ERROR",
"-Xep:ReferenceEquality:ERROR",
"-Xep:RemoveUnusedImports:ERROR",
"-Xep:ReturnValueIgnored:ERROR",
diff --git a/atest/OWNERS b/atest/OWNERS
index def5f76..e41a595 100644
--- a/atest/OWNERS
+++ b/atest/OWNERS
@@ -1,3 +1,3 @@
dshi@google.com
kevcheng@google.com
-mikehoran@google.com
+yangbill@google.com
diff --git a/atest/atest_integration_tests.py b/atest/atest_integration_tests.py
index 33fc767..1dcd12f 100755
--- a/atest/atest_integration_tests.py
+++ b/atest/atest_integration_tests.py
@@ -31,6 +31,7 @@
import os
import subprocess
+import sys
import tempfile
import time
import unittest
@@ -45,7 +46,8 @@
"""ATest Integration Test Class."""
NAME = 'ATestIntegrationTest'
EXECUTABLE = 'atest'
- _RUN_CMD = '{exe} {test}'
+ OPTIONS = ''
+ _RUN_CMD = '{exe} {options} {test}'
_PASSED_CRITERIAS = ['will be rescheduled', 'All tests passed']
def setUp(self):
@@ -64,7 +66,8 @@
Args:
testcase: A string of testcase name.
"""
- run_cmd_dict = {'exe': self.EXECUTABLE, 'test': testcase}
+ run_cmd_dict = {'exe': self.EXECUTABLE, 'options': self.OPTIONS,
+ 'test': testcase}
run_command = self._RUN_CMD.format(**run_cmd_dict)
try:
subprocess.check_output(run_command,
@@ -124,6 +127,10 @@
if __name__ == '__main__':
+ # TODO(b/129029189) Implement detail comparison check for dry-run mode.
+ ARGS = ' '.join(sys.argv[1:])
+ if ARGS:
+ ATestIntegrationTest.OPTIONS = ARGS
TEST_PLANS = os.path.join(os.path.dirname(__file__), _INTEGRATION_TESTS)
try:
LOG_PATH = os.path.join(create_test_run_dir(), _LOG_FILE)
diff --git a/atest/run_atest_unittests.sh b/atest/run_atest_unittests.sh
index b313d21..6759456 100755
--- a/atest/run_atest_unittests.sh
+++ b/atest/run_atest_unittests.sh
@@ -20,34 +20,53 @@
# 2. PREUPLOAD hook invokes this script.
ATEST_DIR=`dirname $0`/
+ATEST_REAL_PATH=`realpath $ATEST_DIR`
PREUPLOAD_FILES=$@
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
function set_pythonpath() {
- local path_to_check=`realpath $ATEST_DIR`
- if ! echo $PYTHONPATH | grep -q $path_to_check; then
- PYTHONPATH=$path_to_check:$PYTHONPATH
+ if ! echo $PYTHONPATH | grep -q $ATEST_REAL_PATH; then
+ PYTHONPATH=$ATEST_REAL_PATH:$PYTHONPATH
fi
}
+function print_summary() {
+ local test_results=$1
+ local coverage_run=$2
+ if [[ $coverage_run == "coverage" ]]; then
+ coverage report -m
+ coverage html
+ fi
+ if [[ $test_results -eq 0 ]]; then
+ echo -e "${GREEN}All unittests pass${NC}!"
+ else
+ echo -e "${RED}There was a unittest failure${NC}"
+ fi
+}
+
function run_atest_unittests() {
- set_pythonpath
+ echo "Running tests..."
+ local coverage_run=$1
+ local run_cmd="python"
local rc=0
- for test_file in $(find $ATEST_DIR -name "*_unittest.py");
- do
- if ! $test_file; then
+ set_pythonpath $coverage_run
+ if [[ $coverage_run == "coverage" ]]; then
+ # Clear previously coverage data.
+ python -m coverage erase
+ # Collected coverage data.
+ run_cmd="coverage run --source $ATEST_REAL_PATH --append"
+ fi
+
+ for test_file in $(find $ATEST_DIR -name "*_unittest.py"); do
+ if ! $run_cmd $test_file; then
rc=1
+ echo -e "${RED}$t failed${NC}"
fi
done
-
echo
- if [[ $rc -eq 0 ]]; then
- echo -e "${GREEN}All unittests pass${NC}!"
- else
- echo -e "${RED}There was a unittest failure${NC}"
- fi
+ print_summary $rc $coverage_run
return $rc
}
@@ -57,8 +76,7 @@
run_atest_unittests
exit $?
else
- for f in $PREUPLOAD_FILES;
- do
+ for f in $PREUPLOAD_FILES; do
# We only want to run this unittest if atest files have been touched.
if [[ $f == atest/* ]]; then
run_atest_unittests
@@ -66,3 +84,12 @@
fi
done
fi
+
+case "$1" in
+ 'coverage')
+ run_atest_unittests "coverage"
+ ;;
+ *)
+ run_atest_unittests
+ ;;
+esac
diff --git a/error_prone_rules.mk b/error_prone_rules.mk
index 621e19a..fcc99bc 100644
--- a/error_prone_rules.mk
+++ b/error_prone_rules.mk
@@ -20,6 +20,7 @@
-Xep:ConstantField:ERROR \
-Xep:DeadException:ERROR \
-Xep:EqualsIncompatibleType:ERROR \
+ -Xep:ExtendingJUnitAssert:ERROR \
-Xep:FormatString:ERROR \
-Xep:GetClassOnClass:ERROR \
-Xep:IdentityBinaryExpression:ERROR \
@@ -28,9 +29,11 @@
-Xep:JUnitAmbiguousTestClass:ERROR \
-Xep:MissingFail:ERROR \
-Xep:MissingOverride:ERROR \
+ -Xep:ModifiedButNotUsed:ERROR \
-Xep:MustBeClosedChecker:ERROR \
-Xep:Overrides:ERROR \
-Xep:PackageLocation:ERROR \
+ -Xep:ParameterName:ERROR \
-Xep:ReferenceEquality:ERROR \
-Xep:RemoveUnusedImports:ERROR \
-Xep:ReturnValueIgnored:ERROR \
diff --git a/res/config/suite/test_mapping_host_suite.xml b/res/config/suite/test_mapping_host_suite.xml
index 10dd50e..f2449f1 100644
--- a/res/config/suite/test_mapping_host_suite.xml
+++ b/res/config/suite/test_mapping_host_suite.xml
@@ -16,4 +16,8 @@
<configuration description="Android test suite config for deviceless tests defined in TEST_MAPPING files">
<option name="null-device" value="true" />
<test class="com.android.tradefed.testtype.suite.TestMappingSuiteRunner"/>
+
+ <!-- Force GTest to report binary name in results -->
+ <option name="test-arg" value="com.android.tradefed.testtype.GTest:prepend-filename:true" />
+
</configuration>
diff --git a/res/config/suite/test_mapping_suite.xml b/res/config/suite/test_mapping_suite.xml
index 4f2a5e1..e22fb7d 100644
--- a/res/config/suite/test_mapping_suite.xml
+++ b/res/config/suite/test_mapping_suite.xml
@@ -29,4 +29,7 @@
<!-- Tell all HostTests to exclude certain annotations -->
<option name="test-arg" value="com.android.tradefed.testtype.HostTest:exclude-annotation:android.platform.test.annotations.RestrictedBuildTest" />
<option name="test-arg" value="com.android.compatibility.common.tradefed.testtype.JarHostTest:exclude-annotation:android.platform.test.annotations.RestrictedBuildTest" />
+
+ <!-- Force GTest to report binary name in results -->
+ <option name="test-arg" value="com.android.tradefed.testtype.GTest:prepend-filename:true" />
</configuration>
diff --git a/src/com/android/tradefed/device/INativeDevice.java b/src/com/android/tradefed/device/INativeDevice.java
index 99c0ee1..0915e40 100644
--- a/src/com/android/tradefed/device/INativeDevice.java
+++ b/src/com/android/tradefed/device/INativeDevice.java
@@ -629,6 +629,14 @@
public boolean doesFileExist(String deviceFilePath) throws DeviceNotAvailableException;
/**
+ * Helper method to delete a file or directory on the device.
+ *
+ * @param deviceFilePath The absolute path of the file on the device.
+ * @throws DeviceNotAvailableException
+ */
+ public void deleteFile(String deviceFilePath) throws DeviceNotAvailableException;
+
+ /**
* Retrieve a reference to a remote file on device.
*
* @param path the file path to retrieve. Can be an absolute path or path relative to '/'. (ie
diff --git a/src/com/android/tradefed/device/ITestDevice.java b/src/com/android/tradefed/device/ITestDevice.java
index 78e38f7..8356fe7 100644
--- a/src/com/android/tradefed/device/ITestDevice.java
+++ b/src/com/android/tradefed/device/ITestDevice.java
@@ -876,4 +876,13 @@
* @throws DeviceNotAvailableException
*/
public File dumpHeap(String process, String devicePath) throws DeviceNotAvailableException;
+
+ /**
+ * Collect the list of available displays id on the device as reported by "dumpsys
+ * SurfaceFlinger".
+ *
+ * @return The list of displays. Default always returns the default display 0.
+ * @throws DeviceNotAvailableException
+ */
+ public Set<Integer> listDisplayIds() throws DeviceNotAvailableException;
}
diff --git a/src/com/android/tradefed/device/ManagedTestDeviceFactory.java b/src/com/android/tradefed/device/ManagedTestDeviceFactory.java
index 37d117f..284c53e 100644
--- a/src/com/android/tradefed/device/ManagedTestDeviceFactory.java
+++ b/src/com/android/tradefed/device/ManagedTestDeviceFactory.java
@@ -23,6 +23,7 @@
import com.android.ddmlib.TimeoutException;
import com.android.tradefed.device.DeviceManager.FastbootDevice;
import com.android.tradefed.device.cloud.ManagedRemoteDevice;
+import com.android.tradefed.device.cloud.NestedDeviceStateMonitor;
import com.android.tradefed.device.cloud.NestedRemoteDevice;
import com.android.tradefed.device.cloud.RemoteAndroidVirtualDevice;
import com.android.tradefed.device.cloud.VmRemoteDevice;
@@ -95,7 +96,8 @@
testDevice =
new NestedRemoteDevice(
idevice,
- new DeviceStateMonitor(mDeviceManager, idevice, mFastbootEnabled),
+ new NestedDeviceStateMonitor(
+ mDeviceManager, idevice, mFastbootEnabled),
mAllocationMonitor);
} else {
// Handle device connected via 'adb connect'
diff --git a/src/com/android/tradefed/device/NativeDevice.java b/src/com/android/tradefed/device/NativeDevice.java
index 2511d55..4d32f24 100644
--- a/src/com/android/tradefed/device/NativeDevice.java
+++ b/src/com/android/tradefed/device/NativeDevice.java
@@ -34,6 +34,7 @@
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.command.remote.DeviceDescriptor;
import com.android.tradefed.config.GlobalConfiguration;
+import com.android.tradefed.device.contentprovider.ContentProviderHandler;
import com.android.tradefed.host.IHostOptions;
import com.android.tradefed.log.ITestLogger;
import com.android.tradefed.log.LogUtil.CLog;
@@ -98,6 +99,7 @@
*/
public class NativeDevice implements IManagedTestDevice {
+ private static final String SD_CARD = "/sdcard/";
/**
* Allow pauses of up to 2 minutes while receiving bugreport.
* <p/>
@@ -210,6 +212,9 @@
private String mLastConnectedWifiPsk = null;
private boolean mNetworkMonitorEnabled = false;
+ private ContentProviderHandler mContentProvider = null;
+ private boolean mShouldSkipContentProviderSetup = false;
+
/**
* Interface for a generic device communication attempt.
*/
@@ -1039,6 +1044,14 @@
@Override
public boolean pushFile(final File localFile, final String remoteFilePath)
throws DeviceNotAvailableException {
+ if (remoteFilePath.startsWith(SD_CARD)) {
+ ContentProviderHandler handler = getContentProvider();
+ if (handler != null) {
+ mShouldSkipContentProviderSetup = true;
+ return handler.pushFile(localFile, remoteFilePath);
+ }
+ }
+
DeviceAction pushAction =
new DeviceAction() {
@Override
@@ -1104,15 +1117,19 @@
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
@Override
- public boolean doesFileExist(String destPath) throws DeviceNotAvailableException {
- String lsGrep = executeShellCommand(String.format("ls \"%s\"", destPath));
+ public boolean doesFileExist(String deviceFilePath) throws DeviceNotAvailableException {
+ String lsGrep = executeShellCommand(String.format("ls \"%s\"", deviceFilePath));
return !lsGrep.contains("No such file or directory");
}
+ /** {@inheritDoc} */
+ @Override
+ public void deleteFile(String deviceFilePath) throws DeviceNotAvailableException {
+ executeShellCommand(String.format("rm -rf \"%s\"", deviceFilePath));
+ }
+
/**
* {@inheritDoc}
*/
@@ -1840,7 +1857,11 @@
/** Builds the OS command for the given adb shell command session and args */
private String[] buildAdbShellCommand(String command) {
// TODO: implement the shell v2 support in ddmlib itself.
- String[] commandArgs = QuotationAwareTokenizer.tokenizeLine(command);
+ String[] commandArgs =
+ QuotationAwareTokenizer.tokenizeLine(
+ command,
+ /** No logging */
+ false);
return ArrayUtil.buildArray(
new String[] {"adb", "-s", getSerialNumber(), "shell"}, commandArgs);
}
@@ -2336,7 +2357,7 @@
remoteFilePath.substring(0, remoteFilePath.lastIndexOf('/'));
if (!bugreportDir.isEmpty()) {
// clean bugreport files directory on device
- executeShellCommand(String.format("rm %s/*", bugreportDir));
+ deleteFile(String.format("%s/*", bugreportDir));
}
return zipFile;
@@ -2770,11 +2791,8 @@
doReboot();
RecoveryMode cachedRecoveryMode = getRecoveryMode();
setRecoveryMode(RecoveryMode.ONLINE);
- if (mStateMonitor.waitForDeviceOnline() != null) {
- enableAdbRoot();
- } else {
- recoverDevice();
- }
+ waitForDeviceOnline();
+ enableAdbRoot();
setRecoveryMode(cachedRecoveryMode);
}
@@ -3966,7 +3984,22 @@
*/
@Override
public void postInvocationTearDown() {
- // Default implementation empty on purpose
+ // Default implementation
+ if (getIDevice() instanceof StubDevice) {
+ return;
+ }
+ try {
+ // If we never installed it, don't even bother checking for it during tear down.
+ if (mContentProvider == null) {
+ return;
+ }
+ ContentProviderHandler handler = getContentProvider();
+ if (handler != null) {
+ handler.tearDown();
+ }
+ } catch (DeviceNotAvailableException e) {
+ CLog.e(e);
+ }
}
/**
@@ -4215,6 +4248,12 @@
return null;
}
+ /** {@inheritDoc} */
+ @Override
+ public Set<Integer> listDisplayIds() throws DeviceNotAvailableException {
+ throw new UnsupportedOperationException("dumpsys SurfaceFlinger is not supported.");
+ }
+
/** Validate that pid is an integer and not empty. */
private boolean checkValidPid(String output) {
if (output.isEmpty()) {
@@ -4234,4 +4273,23 @@
IHostOptions getHostOptions() {
return GlobalConfiguration.getInstance().getHostOptions();
}
+
+ /** Returns the {@link ContentProviderHandler} or null if not available. */
+ @VisibleForTesting
+ ContentProviderHandler getContentProvider() throws DeviceNotAvailableException {
+ // Prevent usage of content provider before API 25 as it would not work well.
+ if (getApiLevel() < 25) {
+ return null;
+ }
+ if (mContentProvider == null) {
+ mContentProvider = new ContentProviderHandler(this);
+ }
+ if (!mShouldSkipContentProviderSetup) {
+ boolean res = mContentProvider.setUp();
+ if (!res) {
+ return null;
+ }
+ }
+ return mContentProvider;
+ }
}
diff --git a/src/com/android/tradefed/device/TestDevice.java b/src/com/android/tradefed/device/TestDevice.java
index 78a3f90..78b315e 100644
--- a/src/com/android/tradefed/device/TestDevice.java
+++ b/src/com/android/tradefed/device/TestDevice.java
@@ -26,6 +26,8 @@
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ByteArrayInputStreamSource;
import com.android.tradefed.result.InputStreamSource;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.KeyguardControllerState;
import com.android.tradefed.util.RunUtil;
import com.android.tradefed.util.StreamUtil;
@@ -62,7 +64,7 @@
/** the command used to dismiss a error dialog. Currently sends a DPAD_CENTER key event */
static final String DISMISS_DIALOG_CMD = "input keyevent 23";
/** Commands that can be used to dismiss the keyguard. */
- static final String DISMISS_KEYGUARD_CMD = "input keyevent 82";
+ public static final String DISMISS_KEYGUARD_CMD = "input keyevent 82";
/**
* Alternative command to dismiss the keyguard by requesting the Window Manager service to do
@@ -90,6 +92,8 @@
/** user pattern in the output of "pm list users" = TEXT{<id>:<name>:<flags>} TEXT * */
private static final String USER_PATTERN = "(.*?\\{)(\\d+)(:)(.*)(:)(\\d+)(\\}.*)";
+ /** Pattern to find the display ids of "dumpsys SurfaceFlinger" */
+ private static final String DISPLAY_ID_PATTERN = "(Display )(?<id>\\d+)( color modes:)";
private static final int API_LEVEL_GET_CURRENT_USER = 24;
/** Timeout to wait for a screenshot before giving up to avoid hanging forever */
@@ -343,9 +347,9 @@
/**
* Core implementation for installing application with split apk files {@link
- * IDevice#installPackages(String, boolean, String...)}
- * See "https://developer.android.com/studio/build/configure-apk-splits" on how to split
- * apk to several files.
+ * IDevice#installPackages(List, boolean, List)} See
+ * "https://developer.android.com/studio/build/configure-apk-splits" on how to split apk to
+ * several files.
*
* @param packageFiles the local apk files
* @param reinstall <code>true</code> if a reinstall should be performed
@@ -448,11 +452,10 @@
/**
* Core implementation for split apk remote installation {@link IDevice#installPackage(String,
- * boolean, String...)}
- * See "https://developer.android.com/studio/build/configure-apk-splits" on how to split
- * apk to several files.
+ * boolean, String...)} See "https://developer.android.com/studio/build/configure-apk-splits" on
+ * how to split apk to several files.
*
- * @param packageFiles the remote apk file paths
+ * @param remoteApkPaths the remote apk file paths
* @param reinstall <code>true</code> if a reinstall should be performed
* @param extraArgs optional extra arguments to pass. See 'adb shell pm install --help' for
* available options.
@@ -1670,4 +1673,26 @@
File dumpFile = pullFile(devicePath);
return dumpFile;
}
+
+ /** {@inheritDoc} */
+ @Override
+ public Set<Integer> listDisplayIds() throws DeviceNotAvailableException {
+ Set<Integer> displays = new HashSet<>();
+ // Zero is the default display
+ displays.add(0);
+ CommandResult res = executeShellV2Command("dumpsys SurfaceFlinger | grep 'color modes:'");
+ if (!CommandStatus.SUCCESS.equals(res.getStatus())) {
+ CLog.e("Something went wrong while listing displays: %s", res.getStderr());
+ return displays;
+ }
+ String output = res.getStdout();
+ Pattern p = Pattern.compile(DISPLAY_ID_PATTERN);
+ for (String line : output.split("\n")) {
+ Matcher m = p.matcher(line);
+ if (m.matches()) {
+ displays.add(Integer.parseInt(m.group("id")));
+ }
+ }
+ return displays;
+ }
}
diff --git a/src/com/android/tradefed/device/cloud/NestedDeviceStateMonitor.java b/src/com/android/tradefed/device/cloud/NestedDeviceStateMonitor.java
new file mode 100644
index 0000000..a746129
--- /dev/null
+++ b/src/com/android/tradefed/device/cloud/NestedDeviceStateMonitor.java
@@ -0,0 +1,79 @@
+/*
+ * 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.device.cloud;
+
+import com.android.ddmlib.IDevice;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.DeviceStateMonitor;
+import com.android.tradefed.device.IDeviceManager;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.TestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+import com.android.tradefed.util.RunUtil;
+
+/**
+ * Device state monitor that executes extra checks on nested device to accommodate the specifics of
+ * the virtualized environment.
+ */
+public class NestedDeviceStateMonitor extends DeviceStateMonitor {
+
+ private ITestDevice mDevice;
+
+ public NestedDeviceStateMonitor(IDeviceManager mgr, IDevice device, boolean fastbootEnabled) {
+ super(mgr, device, fastbootEnabled);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean postOnlineCheck(final long waitTime) {
+ long startTime = System.currentTimeMillis();
+ if (!super.postOnlineCheck(waitTime)) {
+ return false;
+ }
+ long elapsedTime = System.currentTimeMillis() - startTime;
+ // Check that the device is actually usable and services have registered.
+ // VM devices tend to show back very quickly in adb but sometimes are not quite ready.
+ return nestedWaitForDeviceOnline(waitTime - elapsedTime);
+ }
+
+ private boolean nestedWaitForDeviceOnline(long maxWaitTime) {
+ long maxTime = System.currentTimeMillis() + maxWaitTime;
+ CommandResult res = null;
+ while (maxTime > System.currentTimeMillis()) {
+ try {
+ // TODO: Use IDevice directly
+ res = mDevice.executeShellV2Command(TestDevice.DISMISS_KEYGUARD_CMD);
+ if (CommandStatus.SUCCESS.equals(res.getStatus())) {
+ return true;
+ }
+ } catch (DeviceNotAvailableException e) {
+ CLog.e(e);
+ }
+ RunUtil.getDefault().sleep(200L);
+ }
+ if (res != null) {
+ CLog.e("Error checking device ready: %s", res.getStderr());
+ }
+ return false;
+ }
+
+ /** Set the ITestDevice to call an adb command. */
+ final void setDevice(ITestDevice device) {
+ mDevice = device;
+ }
+}
diff --git a/src/com/android/tradefed/device/cloud/NestedRemoteDevice.java b/src/com/android/tradefed/device/cloud/NestedRemoteDevice.java
index 4d617f9..e1ef45f 100644
--- a/src/com/android/tradefed/device/cloud/NestedRemoteDevice.java
+++ b/src/com/android/tradefed/device/cloud/NestedRemoteDevice.java
@@ -38,5 +38,9 @@
public NestedRemoteDevice(
IDevice device, IDeviceStateMonitor stateMonitor, IDeviceMonitor allocationMonitor) {
super(device, stateMonitor, allocationMonitor);
+ // TODO: Use IDevice directly
+ if (stateMonitor instanceof NestedDeviceStateMonitor) {
+ ((NestedDeviceStateMonitor) stateMonitor).setDevice(this);
+ }
}
}
diff --git a/src/com/android/tradefed/device/contentprovider/ContentProviderHandler.java b/src/com/android/tradefed/device/contentprovider/ContentProviderHandler.java
index a245dd8..902690b 100644
--- a/src/com/android/tradefed/device/contentprovider/ContentProviderHandler.java
+++ b/src/com/android/tradefed/device/contentprovider/ContentProviderHandler.java
@@ -42,6 +42,7 @@
public static final String CONTENT_PROVIDER_URI = "content://android.tradefed.contentprovider";
private static final String APK_NAME = "TradefedContentProvider.apk";
private static final String CONTENT_PROVIDER_APK_RES = "/apks/contentprovider/" + APK_NAME;
+ private static final String PROPERTY_RESULT = "LEGACY_STORAGE: allow";
private ITestDevice mDevice;
private File mContentProviderApk = null;
@@ -56,20 +57,43 @@
*
* @return True if ready to be used, False otherwise.
*/
- public boolean setUp() throws DeviceNotAvailableException, IOException {
+ public boolean setUp() throws DeviceNotAvailableException {
Set<String> packageNames = mDevice.getInstalledPackageNames();
if (packageNames.contains(PACKAGE_NAME)) {
return true;
}
if (mContentProviderApk == null) {
- mContentProviderApk = extractResourceApk();
+ try {
+ mContentProviderApk = extractResourceApk();
+ } catch (IOException e) {
+ CLog.e(e);
+ return false;
+ }
}
// Install package for all users
- String output = mDevice.installPackage(mContentProviderApk, true, true);
- if (output == null) {
+ String output =
+ mDevice.installPackage(
+ mContentProviderApk,
+ /** reinstall */
+ true,
+ /** grant permission */
+ true);
+ if (output != null) {
+ CLog.e("Something went wrong while installing the content provider apk: %s", output);
+ FileUtil.deleteFile(mContentProviderApk);
+ return false;
+ }
+ // Enable appops legacy storage
+ mDevice.executeShellV2Command(
+ String.format("cmd appops set %s android:legacy_storage allow", PACKAGE_NAME));
+ // Check that it worked and set on the system
+ CommandResult appOpsResult =
+ mDevice.executeShellV2Command(String.format("cmd appops get %s", PACKAGE_NAME));
+ if (CommandStatus.SUCCESS.equals(appOpsResult.getStatus())
+ && appOpsResult.getStdout().contains(PROPERTY_RESULT)) {
return true;
}
- CLog.e("Something went wrong while installing the content provider apk: %s", output);
+ CLog.e("Failed to set legacy_storage: %s", appOpsResult.getStderr());
FileUtil.deleteFile(mContentProviderApk);
return false;
}
diff --git a/src/com/android/tradefed/device/metric/AtraceCollector.java b/src/com/android/tradefed/device/metric/AtraceCollector.java
index a49bd0c..628410b 100644
--- a/src/com/android/tradefed/device/metric/AtraceCollector.java
+++ b/src/com/android/tradefed/device/metric/AtraceCollector.java
@@ -272,7 +272,7 @@
}
if (!mPreserveOndeviceLog) {
- device.executeShellCommand("rm -f " + fullLogPath());
+ device.deleteFile(fullLogPath());
}
else {
CLog.w("preserving ondevice atrace log: %s", fullLogPath());
diff --git a/src/com/android/tradefed/invoker/RemoteInvocationExecution.java b/src/com/android/tradefed/invoker/RemoteInvocationExecution.java
index c8d663e..1c99346 100644
--- a/src/com/android/tradefed/invoker/RemoteInvocationExecution.java
+++ b/src/com/android/tradefed/invoker/RemoteInvocationExecution.java
@@ -352,8 +352,7 @@
StringBuilder tfCmdBuilder =
new StringBuilder("TF_GLOBAL_CONFIG=" + globalConfig.getName());
// Set an env variable to notify that this a remote environment.
- // TODO: Reenable, right now it causes issue
- // tfCmdBuilder.append(" " + REMOTE_VM_VARIABLE + "=1");
+ tfCmdBuilder.append(" " + REMOTE_VM_VARIABLE + "=1");
tfCmdBuilder.append(" ENTRY_CLASS=" + CommandRunner.class.getCanonicalName());
tfCmdBuilder.append(" ./tradefed.sh " + mRemoteTradefedDir + configFile.getName());
if (config.getCommandOptions().shouldUseRemoteSandboxMode()) {
diff --git a/src/com/android/tradefed/targetprep/DeviceSetup.java b/src/com/android/tradefed/targetprep/DeviceSetup.java
index 9ee0cc7..0ad80f3 100644
--- a/src/com/android/tradefed/targetprep/DeviceSetup.java
+++ b/src/com/android/tradefed/targetprep/DeviceSetup.java
@@ -488,7 +488,7 @@
if (mPreviousProperties != null) {
device.pushFile(mPreviousProperties, "/data/local.prop");
} else {
- device.executeShellCommand("rm -f /data/local.prop");
+ device.deleteFile("/data/local.prop");
}
device.reboot();
}
diff --git a/src/com/android/tradefed/targetprep/InstallApexModuleTargetPreparer.java b/src/com/android/tradefed/targetprep/InstallApexModuleTargetPreparer.java
index ae86527..3f2a58d 100644
--- a/src/com/android/tradefed/targetprep/InstallApexModuleTargetPreparer.java
+++ b/src/com/android/tradefed/targetprep/InstallApexModuleTargetPreparer.java
@@ -54,14 +54,14 @@
public void setUp(ITestDevice device, IBuildInfo buildInfo)
throws TargetSetupError, DeviceNotAvailableException {
+ mApkInstalled = new ArrayList<>();
+ mTestApexInfoList = new ArrayList<ApexInfo>();
+
if (getTestsFileName().isEmpty()) {
CLog.i("No apk/apex module file to install. Skipping.");
return;
}
- mApkInstalled = new ArrayList<>();
- mTestApexInfoList = new ArrayList<ApexInfo>();
-
// Clean up data/apex/active and data/app-staging.
cleanUpStagedAndActiveSession(device, buildInfo);
diff --git a/src/com/android/tradefed/testtype/AndroidJUnitTest.java b/src/com/android/tradefed/testtype/AndroidJUnitTest.java
index ae934da..d9602b0 100644
--- a/src/com/android/tradefed/testtype/AndroidJUnitTest.java
+++ b/src/com/android/tradefed/testtype/AndroidJUnitTest.java
@@ -471,8 +471,7 @@
}
private void removeTestFilterDir() throws DeviceNotAvailableException {
- ITestDevice device = getDevice();
- device.executeShellCommand(String.format("rm -r %s", mTestFilterDir));
+ getDevice().deleteFile(mTestFilterDir);
}
private void reportEarlyFailure(ITestInvocationListener listener, String errorMessage) {
diff --git a/src/com/android/tradefed/testtype/DeviceJUnit4ClassRunner.java b/src/com/android/tradefed/testtype/DeviceJUnit4ClassRunner.java
index 3ab6c76..778f36f 100644
--- a/src/com/android/tradefed/testtype/DeviceJUnit4ClassRunner.java
+++ b/src/com/android/tradefed/testtype/DeviceJUnit4ClassRunner.java
@@ -25,6 +25,7 @@
import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.result.LogDataType;
import com.android.tradefed.testtype.MetricTestCase.LogHolder;
+import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.proto.TfMetricProtoUtil;
import com.google.common.annotations.VisibleForTesting;
@@ -32,7 +33,9 @@
import org.junit.rules.ExternalResource;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
+import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
@@ -61,6 +64,9 @@
private IInvocationContext mContext;
private Map<ITestDevice, IBuildInfo> mDeviceInfos;
+ /** Keep track of the list of downloaded files. */
+ private List<File> mDownloadedFiles = new ArrayList<>();
+
@Option(name = HostTest.SET_OPTION_NAME, description = HostTest.SET_OPTION_DESC)
private List<String> mKeyValueOptions = new ArrayList<>();
@@ -98,11 +104,22 @@
}
// Set options of test object
HostTest.setOptionToLoadedObject(testObj, mKeyValueOptions);
- resolveRemoteFileForObject(testObj);
+ mDownloadedFiles.addAll(resolveRemoteFileForObject(testObj));
return testObj;
}
@Override
+ protected void runChild(FrameworkMethod method, RunNotifier notifier) {
+ try {
+ super.runChild(method, notifier);
+ } finally {
+ for (File f : mDownloadedFiles) {
+ FileUtil.recursiveDelete(f);
+ }
+ }
+ }
+
+ @Override
public void setDevice(ITestDevice device) {
mDevice = device;
}
diff --git a/src/com/android/tradefed/testtype/GTest.java b/src/com/android/tradefed/testtype/GTest.java
index a86c96d..c3eb307 100644
--- a/src/com/android/tradefed/testtype/GTest.java
+++ b/src/com/android/tradefed/testtype/GTest.java
@@ -206,7 +206,7 @@
getMaxTestTimeMs() /* maxTimeToShellOutputResponse */,
TimeUnit.MILLISECONDS,
0 /* retry attempts */);
- testDevice.executeShellCommand(String.format("rm %s", tmpFileDevice));
+ testDevice.deleteFile(tmpFileDevice);
}
@Override
diff --git a/src/com/android/tradefed/testtype/HostTest.java b/src/com/android/tradefed/testtype/HostTest.java
index ee05af3..0d3a55a 100644
--- a/src/com/android/tradefed/testtype/HostTest.java
+++ b/src/com/android/tradefed/testtype/HostTest.java
@@ -178,6 +178,9 @@
private static final String TEST_FULL_NAME_FORMAT = "%s#%s";
private static final String ROOT_DIR = "ROOT_DIR";
+ /** Track the downloaded files. */
+ private List<File> mDownloadedFiles = new ArrayList<>();
+
public HostTest() {
mFilterHelper = new TestFilterHelper(new ArrayList<String>(), new ArrayList<String>(),
mIncludeAnnotations, mExcludeAnnotations);
@@ -531,7 +534,19 @@
runRemoteTest(listener, test);
} else if (Test.class.isAssignableFrom(classObj)) {
TestSuite junitTest = collectTests(collectClasses(classObj));
- runJUnit3Tests(listener, junitTest, classObj.getName());
+ // Resolve dynamic files for the junit3 test objects
+ Enumeration<Test> allTest = junitTest.tests();
+ while (allTest.hasMoreElements()) {
+ Test testObj = allTest.nextElement();
+ mDownloadedFiles.addAll(resolveRemoteFileForObject(testObj));
+ }
+ try {
+ runJUnit3Tests(listener, junitTest, classObj.getName());
+ } finally {
+ for (File f : mDownloadedFiles) {
+ FileUtil.recursiveDelete(f);
+ }
+ }
} else if (hasJUnit4Annotation(classObj)) {
// Include the method name filtering
Set<String> includes = mFilterHelper.getIncludeFilters();
@@ -717,7 +732,6 @@
if (testObj instanceof TestCase) {
((TestCase)testObj).setName(method.getName());
}
- resolveRemoteFileForObject(testObj);
suite.addTest(testObj);
}
}
diff --git a/src/com/android/tradefed/testtype/binary/ExecutableBaseTest.java b/src/com/android/tradefed/testtype/binary/ExecutableBaseTest.java
index a15994a..b008ba8 100644
--- a/src/com/android/tradefed/testtype/binary/ExecutableBaseTest.java
+++ b/src/com/android/tradefed/testtype/binary/ExecutableBaseTest.java
@@ -20,6 +20,8 @@
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
import com.android.tradefed.testtype.IRemoteTest;
import com.android.tradefed.testtype.IRuntimeHintProvider;
import com.android.tradefed.testtype.IShardableTest;
@@ -33,11 +35,11 @@
/** Base class for executable style of tests. For example: binaries, shell scripts. */
public abstract class ExecutableBaseTest
- implements IRemoteTest, IRuntimeHintProvider, ITestCollector, IShardableTest {
+ implements IRemoteTest, IRuntimeHintProvider, ITestCollector, IShardableTest, IAbiReceiver {
- public static final String NO_BINARY_ERROR = "Binary %s does not exists.";
+ public static final String NO_BINARY_ERROR = "Binary %s does not exist.";
- @Option(name = "binary-path", description = "Path to the binary to be run. Can be repeated.")
+ @Option(name = "binary", description = "Path to the binary to be run. Can be repeated.")
private List<String> mBinaryPaths = new ArrayList<>();
@Option(
@@ -53,13 +55,15 @@
)
private long mRuntimeHintMs = 60000L; // 1 minute
+ private IAbi mAbi;
+
@Override
public final void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
- for (String path : mBinaryPaths) {
- // TODO: Improve the search logic to the usual places (ANDROID_TESTCASES, etc.)
- if (!checkBinaryExists(path)) {
- listener.testRunStarted(new File(path).getName(), 0);
- listener.testRunFailed(String.format(NO_BINARY_ERROR, path));
+ for (String binary : mBinaryPaths) {
+ String path = findBinary(binary);
+ if (path == null) {
+ listener.testRunStarted(new File(binary).getName(), 0);
+ listener.testRunFailed(String.format(NO_BINARY_ERROR, binary));
listener.testRunEnded(0L, new HashMap<String, Metric>());
} else {
listener.testRunStarted(new File(path).getName(), 1);
@@ -75,12 +79,12 @@
}
/**
- * Ensures that a binary exists.
+ * Search for the binary to be able to run it.
*
- * @param binaryPath the path of the binary.
- * @return True if the binary exists.
+ * @param binary the path of the binary or simply the binary name.
+ * @return The path to the binary, or null if not found.
*/
- public abstract boolean checkBinaryExists(String binaryPath);
+ public abstract String findBinary(String binary);
/**
* Actually run the binary at the given path.
@@ -104,6 +108,18 @@
/** {@inheritDoc} */
@Override
+ public final void setAbi(IAbi abi) {
+ mAbi = abi;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public IAbi getAbi() {
+ return mAbi;
+ }
+
+ /** {@inheritDoc} */
+ @Override
public final Collection<IRemoteTest> split() {
if (mBinaryPaths.size() <= 2) {
return null;
diff --git a/src/com/android/tradefed/testtype/binary/ExecutableHostTest.java b/src/com/android/tradefed/testtype/binary/ExecutableHostTest.java
index 69b0539..00e0cb4 100644
--- a/src/com/android/tradefed/testtype/binary/ExecutableHostTest.java
+++ b/src/com/android/tradefed/testtype/binary/ExecutableHostTest.java
@@ -16,10 +16,16 @@
package com.android.tradefed.testtype.binary;
import com.android.annotations.VisibleForTesting;
+import com.android.tradefed.build.BuildInfoKey.BuildInfoFileKey;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.build.IDeviceBuildInfo;
import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.StubDevice;
+import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.testtype.IDeviceTest;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
@@ -28,6 +34,7 @@
import com.android.tradefed.util.RunUtil;
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -36,7 +43,8 @@
* the host binary might communicate to a device. If the received device is not a {@link StubDevice}
* the serial will be passed to the binary to be used.
*/
-public class ExecutableHostTest extends ExecutableBaseTest implements IDeviceTest {
+@OptionClass(alias = "executable-host-test")
+public class ExecutableHostTest extends ExecutableBaseTest implements IDeviceTest, IBuildReceiver {
private static final String ANDROID_SERIAL = "ANDROID_SERIAL";
@@ -48,10 +56,40 @@
private long mTimeoutPerBinaryMs = 5 * 60 * 1000L;
private ITestDevice mDevice;
+ private IBuildInfo mBuild;
@Override
- public boolean checkBinaryExists(String binaryPath) {
- return new File(binaryPath).exists();
+ public String findBinary(String binary) {
+ File bin = new File(binary);
+ // If it's a local path or absolute path
+ if (bin.exists()) {
+ return bin.getAbsolutePath();
+ }
+ if (mBuild instanceof IDeviceBuildInfo) {
+ IDeviceBuildInfo deviceBuild = (IDeviceBuildInfo) mBuild;
+ File testsDir = deviceBuild.getTestsDir();
+
+ List<File> scanDirs = new ArrayList<>();
+ // If it exists, always look first in the ANDROID_HOST_OUT_TESTCASES
+ File targetTestCases = deviceBuild.getFile(BuildInfoFileKey.HOST_LINKED_DIR);
+ if (targetTestCases != null) {
+ scanDirs.add(targetTestCases);
+ }
+ if (testsDir != null) {
+ scanDirs.add(testsDir);
+ }
+
+ try {
+ // Search the full tests dir if no target dir is available.
+ File src = FileUtil.findFile(binary, getAbi(), scanDirs.toArray(new File[] {}));
+ if (src != null) {
+ return src.getAbsolutePath();
+ }
+ } catch (IOException e) {
+ CLog.e("Failed to find test files from directory.");
+ }
+ }
+ return null;
}
@Override
@@ -93,6 +131,11 @@
return mDevice;
}
+ @Override
+ public final void setBuild(IBuildInfo buildInfo) {
+ mBuild = buildInfo;
+ }
+
@VisibleForTesting
IRunUtil createRunUtil() {
return new RunUtil();
diff --git a/src/com/android/tradefed/util/QuotationAwareTokenizer.java b/src/com/android/tradefed/util/QuotationAwareTokenizer.java
index e72afa7..eaf7377 100644
--- a/src/com/android/tradefed/util/QuotationAwareTokenizer.java
+++ b/src/com/android/tradefed/util/QuotationAwareTokenizer.java
@@ -25,34 +25,40 @@
private static final String LOG_TAG = "TOKEN";
/**
- * Tokenizes the string, splitting on specified delimiter. Does not split between consecutive,
+ * Tokenizes the string, splitting on specified delimiter. Does not split between consecutive,
* unquoted double-quote marks.
- * <p/>
- * How the tokenizer works:
+ *
+ * <p>How the tokenizer works:
+ *
* <ol>
- * <li> Split the string into "characters" where each "character" is either an escaped
- * character like \" (that is, "\\\"") or a single real character like f (just "f").
- * <li> For each "character"
- * <ol>
+ * <li> Split the string into "characters" where each "character" is either an escaped
+ * character like \" (that is, "\\\"") or a single real character like f (just "f").
+ * <li> For each "character"
+ * <ol>
* <li> If it's a space, finish a token unless we're being quoted
* <li> If it's a quotation mark, flip the "we're being quoted" bit
* <li> Otherwise, add it to the token being built
- * </ol>
- * <li> At EOL, we typically haven't added the final token to the (tokens) {@link ArrayList}
- * <ol>
+ * </ol>
+ *
+ * <li> At EOL, we typically haven't added the final token to the (tokens) {@link ArrayList}
+ * <ol>
* <li> If the last "character" is an escape character, throw an exception; that's not
- * valid
+ * valid
* <li> If we're in the middle of a quotation, throw an exception; that's not valid
* <li> Otherwise, add the final token to (tokens)
- * </ol>
- * <li> Return a String[] version of (tokens)
+ * </ol>
+ *
+ * <li> Return a String[] version of (tokens)
* </ol>
*
* @param line A {@link String} to be tokenized
+ * @param delim the delimiter to split on
+ * @param logging whether or not to log operations
* @return A tokenized version of the string
* @throws IllegalArgumentException if the line cannot be parsed
*/
- public static String[] tokenizeLine(String line, String delim) throws IllegalArgumentException {
+ public static String[] tokenizeLine(String line, String delim, boolean logging)
+ throws IllegalArgumentException {
if (line == null) {
throw new IllegalArgumentException("line is null");
}
@@ -65,7 +71,7 @@
String aChar = "";
boolean quotation = false;
- Log.d(LOG_TAG, String.format("Trying to tokenize the line '%s'", line));
+ log(String.format("Trying to tokenize the line '%s'", line), logging);
while (charMatcher.find()) {
aChar = charMatcher.group();
@@ -77,7 +83,7 @@
if (token.length() > 0) {
// this is the end of a non-empty token; dump it in our list of tokens,
// clear our temp storage, and keep rolling
- Log.d(LOG_TAG, String.format("Finished token '%s'", token.toString()));
+ log(String.format("Finished token '%s'", token.toString()), logging);
tokens.add(token.toString());
token.delete(0, token.length());
}
@@ -85,7 +91,7 @@
}
} else if ("\"".equals(aChar)) {
// unescaped quotation mark; flip quotation state
- Log.v(LOG_TAG, "Flipped quotation state");
+ log("Flipped quotation state", logging);
quotation ^= true;
} else {
// default case: add the character to the token being built
@@ -101,7 +107,7 @@
// Add the final token to the tokens array.
if (token.length() > 0) {
- Log.v(LOG_TAG, String.format("Finished final token '%s'", token.toString()));
+ log(String.format("Finished final token '%s'", token.toString()), logging);
tokens.add(token.toString());
token.delete(0, token.length());
}
@@ -117,7 +123,22 @@
* See also {@link #tokenizeLine(String, String)}
*/
public static String[] tokenizeLine(String line) throws IllegalArgumentException {
- return tokenizeLine(line, " ");
+ return tokenizeLine(line, " ", true);
+ }
+
+ public static String[] tokenizeLine(String line, String delim) throws IllegalArgumentException {
+ return tokenizeLine(line, delim, true);
+ }
+
+ /**
+ * Tokenizes the string, splitting on spaces. Does not split between consecutive, unquoted
+ * double-quote marks.
+ *
+ * <p>See also {@link #tokenizeLine(String, String)}
+ */
+ public static String[] tokenizeLine(String line, boolean logging)
+ throws IllegalArgumentException {
+ return tokenizeLine(line, " ", logging);
}
/**
@@ -147,4 +168,10 @@
}
return sb.toString();
}
+
+ private static void log(String message, boolean display) {
+ if (display) {
+ Log.v(LOG_TAG, message);
+ }
+ }
}
diff --git a/src/com/android/tradefed/util/RunUtil.java b/src/com/android/tradefed/util/RunUtil.java
index 15b7d86..593559b 100644
--- a/src/com/android/tradefed/util/RunUtil.java
+++ b/src/com/android/tradefed/util/RunUtil.java
@@ -250,8 +250,8 @@
new RunnableResult(
/* input= */ null,
createProcessBuilder(command),
- /* stdout= */ null,
- /* stderr= */ null,
+ /* stdoutStream= */ null,
+ /* stderrStream= */ null,
inputRedirect);
CommandStatus status = runTimed(timeout, osRunnable, true);
CommandResult result = osRunnable.getResult();
diff --git a/src/com/android/tradefed/util/testmapping/TestInfo.java b/src/com/android/tradefed/util/testmapping/TestInfo.java
index ee14503..8795138 100644
--- a/src/com/android/tradefed/util/testmapping/TestInfo.java
+++ b/src/com/android/tradefed/util/testmapping/TestInfo.java
@@ -259,25 +259,54 @@
}
@Override
+ public boolean equals(Object o) {
+ return this.toString().equals(o.toString());
+ }
+
+ @Override
+ public int hashCode() {
+ return this.toString().hashCode();
+ }
+
+ @Override
public String toString() {
- String options = "";
- String keywords = "";
+ StringBuilder string = new StringBuilder();
+ string.append(mName);
if (!mOptions.isEmpty()) {
- options =
+ String options =
String.format(
- "; Options: %s",
+ "Options: %s",
Joiner.on(",")
.join(
mOptions.stream()
+ .sorted()
.map(TestOption::toString)
.collect(Collectors.toList())));
+ string.append("\n\t").append(options);
}
if (!mKeywords.isEmpty()) {
- keywords =
+ String keywords =
String.format(
- "; Keywords: %s",
- Joiner.on(",").join(mKeywords.stream().collect(Collectors.toList())));
+ "Keywords: %s",
+ Joiner.on(",")
+ .join(
+ mKeywords.stream()
+ .sorted()
+ .collect(Collectors.toList())));
+ string.append("\n\t").append(keywords);
}
- return String.format("%s%s%s", mName, options, keywords);
+ if (!mSources.isEmpty()) {
+ String sources =
+ String.format(
+ "Sources: %s",
+ Joiner.on(",")
+ .join(
+ mSources.stream()
+ .sorted()
+ .collect(Collectors.toList())));
+ string.append("\n\t").append(sources);
+ }
+ string.append("\n\tHost: ").append(mHostOnly);
+ return string.toString();
}
}
diff --git a/src/com/android/tradefed/util/testmapping/TestMapping.java b/src/com/android/tradefed/util/testmapping/TestMapping.java
index cf7ed92..79da7c2 100644
--- a/src/com/android/tradefed/util/testmapping/TestMapping.java
+++ b/src/com/android/tradefed/util/testmapping/TestMapping.java
@@ -20,6 +20,8 @@
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.ZipUtil2;
+import com.google.common.annotations.VisibleForTesting;
+
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -32,8 +34,8 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -57,9 +59,10 @@
private static final String KEY_OPTIONS = "options";
private static final String TEST_MAPPING = "TEST_MAPPING";
private static final String TEST_MAPPINGS_ZIP = "test_mappings.zip";
- private static final String DISABLED_PRESUBMIT_TESTS = "disabled-presubmit-tests";
+ // A file containing module names that are disabled in presubmit test runs.
+ private static final String DISABLED_PRESUBMIT_TESTS_FILE = "disabled-presubmit-tests";
- private Map<String, List<TestInfo>> mTestCollection = null;
+ private Map<String, Set<TestInfo>> mTestCollection = null;
/**
* Constructor to create a {@link TestMapping} object from a path to TEST_MAPPING file.
@@ -84,7 +87,7 @@
// need to be considered.
continue;
}
- List<TestInfo> testsForGroup = new ArrayList<TestInfo>();
+ Set<TestInfo> testsForGroup = new HashSet<>();
mTestCollection.put(group, testsForGroup);
JSONArray arr = root.getJSONArray(group);
for (int i = 0; i < arr.length(); i++) {
@@ -144,13 +147,13 @@
* returned. false to return tests that require device to run.
* @param keywords A set of {@link String} to be matched when filtering tests to run in a Test
* Mapping suite.
- * @return A {@code List<TestInfo>} of the test infos.
+ * @return A {@code Set<TestInfo>} of the test infos.
*/
- public List<TestInfo> getTests(
+ public Set<TestInfo> getTests(
String testGroup, Set<String> disabledTests, boolean hostOnly, Set<String> keywords) {
- List<TestInfo> tests = new ArrayList<TestInfo>();
+ Set<TestInfo> tests = new HashSet<TestInfo>();
- for (TestInfo test : mTestCollection.getOrDefault(testGroup, new ArrayList<TestInfo>())) {
+ for (TestInfo test : mTestCollection.getOrDefault(testGroup, new HashSet<>())) {
if (disabledTests != null && disabledTests.contains(test.getName())) {
CLog.d("Test is disabled: %s.", test);
continue;
@@ -228,23 +231,11 @@
public static Set<TestInfo> getTests(
IBuildInfo buildInfo, String testGroup, boolean hostOnly, Set<String> keywords) {
Set<TestInfo> tests = new HashSet<TestInfo>();
- Set<String> disabledTests = new HashSet<>();
-
- File testMappingsZip = buildInfo.getFile(TEST_MAPPINGS_ZIP);
- File testMappingsDir = null;
+ File testMappingsDir = extractTestMappingsZip(buildInfo.getFile(TEST_MAPPINGS_ZIP));
Stream<Path> stream = null;
try {
- testMappingsDir = ZipUtil2.extractZipToTemp(testMappingsZip, TEST_MAPPINGS_ZIP);
Path testMappingsRootPath = Paths.get(testMappingsDir.getAbsolutePath());
- if (testGroup.equals(PRESUBMIT)) {
- File disabledPresubmitTestsFile =
- new File(testMappingsRootPath.toString(), DISABLED_PRESUBMIT_TESTS);
- disabledTests.addAll(
- Arrays.asList(
- FileUtil.readStringFromFile(disabledPresubmitTestsFile)
- .split("\\r?\\n")));
- }
-
+ Set<String> disabledTests = getDisabledTests(testMappingsRootPath, testGroup);
stream = Files.walk(testMappingsRootPath, FileVisitOption.FOLLOW_LINKS);
stream.filter(path -> path.getFileName().toString().equals(TEST_MAPPING))
.forEach(
@@ -258,13 +249,10 @@
keywords)));
} catch (IOException e) {
- RuntimeException runtimeException =
- new RuntimeException(
- String.format(
- "IO error (%s) when reading tests from TEST_MAPPING files (%s)",
- e.getMessage(), testMappingsZip.getAbsolutePath()),
- e);
- throw runtimeException;
+ throw new RuntimeException(
+ String.format(
+ "IO exception (%s) when reading tests from TEST_MAPPING files (%s)",
+ e.getMessage(), testMappingsDir.getAbsolutePath()), e);
} finally {
if (stream != null) {
stream.close();
@@ -274,4 +262,111 @@
return TestMapping.mergeTests(tests);
}
+
+ /**
+ * Helper to find all tests in the TEST_MAPPING files from a given directory.
+ *
+ * @param testMappingsDir the {@link File} the directory containing all Test Mapping files.
+ * @return A {@code Map<String, Set<TestInfo>>} of tests in the given directory and its child
+ * directories.
+ */
+ public static Map<String, Set<TestInfo>> getAllTests(File testMappingsDir) {
+ Map<String, Set<TestInfo>> allTests = new HashMap<String, Set<TestInfo>>();
+ Stream<Path> stream = null;
+ try {
+ Path testMappingsRootPath = Paths.get(testMappingsDir.getAbsolutePath());
+ stream = Files.walk(testMappingsRootPath, FileVisitOption.FOLLOW_LINKS);
+ stream.filter(path -> path.getFileName().toString().equals(TEST_MAPPING))
+ .forEach(
+ path ->
+ getAllTests(allTests, path, testMappingsRootPath));
+
+ } catch (IOException e) {
+ throw new RuntimeException(
+ String.format(
+ "IO exception (%s) when reading tests from TEST_MAPPING files (%s)",
+ e.getMessage(), testMappingsDir.getAbsolutePath()), e);
+ } finally {
+ if (stream != null) {
+ stream.close();
+ }
+ }
+ return allTests;
+ }
+
+ /**
+ * Extract a zip file and return the directory that contains the content of unzipped files.
+ *
+ * @param testMappingsZip A {@link File} of the test mappings zip to extract.
+ * @return a {@link File} pointing to the temp directory for test mappings zip.
+ */
+ public static File extractTestMappingsZip(File testMappingsZip) {
+ File testMappingsDir = null;
+ try {
+ testMappingsDir = ZipUtil2.extractZipToTemp(testMappingsZip, TEST_MAPPINGS_ZIP);
+ } catch (IOException e) {
+ throw new RuntimeException(
+ String.format(
+ "IO exception (%s) when extracting test mappings zip (%s)",
+ e.getMessage(), testMappingsZip.getAbsolutePath()), e);
+ }
+ return testMappingsDir;
+ }
+
+ /**
+ * Get disabled tests from test mapping artifact.
+ *
+ * @param testMappingsRootPath The {@link Path} to a test mappings zip path.
+ * @param testGroup a {@link String} of the test group.
+ * @return a {@link Set<String>} containing all the disabled presubmit tests. No test is
+ * returned if the testGroup is not PRESUBMIT.
+ */
+ @VisibleForTesting
+ static Set<String> getDisabledTests(Path testMappingsRootPath, String testGroup) {
+ Set<String> disabledTests = new HashSet<>();
+ File disabledPresubmitTestsFile =
+ new File(testMappingsRootPath.toString(), DISABLED_PRESUBMIT_TESTS_FILE);
+ if (!(testGroup.equals(PRESUBMIT) && disabledPresubmitTestsFile.exists())) {
+ return disabledTests;
+ }
+ try {
+ disabledTests.addAll(
+ Arrays.asList(
+ FileUtil.readStringFromFile(disabledPresubmitTestsFile)
+ .split("\\r?\\n")));
+ } catch (IOException e) {
+ throw new RuntimeException(
+ String.format(
+ "IO exception (%s) when reading disabled tests from file (%s)",
+ e.getMessage(), disabledPresubmitTestsFile.getAbsolutePath()), e);
+ }
+ return disabledTests;
+ }
+
+ /**
+ * Helper to find all tests in the TEST_MAPPING files from a given directory.
+ *
+ * @param allTests the {@code HashMap<String, Set<TestInfo>>} containing the tests of each
+ * test group.
+ * @param path the {@link Path} to a TEST_MAPPING file.
+ * @param testMappingsRootPath the {@link Path} to a test mappings zip path.
+ */
+ private static void getAllTests(Map<String, Set<TestInfo>> allTests,
+ Path path, Path testMappingsRootPath) {
+ Map<String, Set<TestInfo>> testCollection =
+ new TestMapping(path, testMappingsRootPath).getTestCollection();
+ for (String group : testCollection.keySet()) {
+ allTests.computeIfAbsent(group, k -> new HashSet<>()).addAll(testCollection.get(group));
+ }
+ }
+
+ /**
+ * Helper to get the test collection in a TEST_MAPPING file.
+ *
+ * @return A {@code Map<String, Set<TestInfo>>} containing the test collection in a
+ * TEST_MAPPING file.
+ */
+ private Map<String, Set<TestInfo>> getTestCollection() {
+ return mTestCollection;
+ }
}
diff --git a/tests/src/com/android/tradefed/UnitTests.java b/tests/src/com/android/tradefed/UnitTests.java
index ea67a59..d87c5b9 100644
--- a/tests/src/com/android/tradefed/UnitTests.java
+++ b/tests/src/com/android/tradefed/UnitTests.java
@@ -214,6 +214,7 @@
import com.android.tradefed.testtype.AndroidJUnitTestTest;
import com.android.tradefed.testtype.CodeCoverageListenerTest;
import com.android.tradefed.testtype.DeviceBatteryLevelCheckerTest;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunnerTest;
import com.android.tradefed.testtype.DeviceSuiteTest;
import com.android.tradefed.testtype.DeviceTestCaseTest;
import com.android.tradefed.testtype.DeviceTestSuiteTest;
@@ -624,6 +625,7 @@
CodeCoverageListenerTest.class,
CoverageMeasurementForwarderTest.class,
DeviceBatteryLevelCheckerTest.class,
+ DeviceJUnit4ClassRunnerTest.class,
DeviceSuiteTest.class,
DeviceTestCaseTest.class,
DeviceTestSuiteTest.class,
diff --git a/tests/src/com/android/tradefed/config/ConfigurationFactoryTest.java b/tests/src/com/android/tradefed/config/ConfigurationFactoryTest.java
index 17c4133..87a1762 100644
--- a/tests/src/com/android/tradefed/config/ConfigurationFactoryTest.java
+++ b/tests/src/com/android/tradefed/config/ConfigurationFactoryTest.java
@@ -886,8 +886,6 @@
final String configName = "template-include-config-with-default";
final String targetName = "local-config";
final String nameTemplate = "target";
- Map<String, String> expected = new HashMap<String,String>();
- expected.put(nameTemplate, targetName);
IConfiguration tmp = null;
try {
tmp = mFactory.createConfigurationFromArgs(new String[]{configName,
diff --git a/tests/src/com/android/tradefed/device/NativeDeviceTest.java b/tests/src/com/android/tradefed/device/NativeDeviceTest.java
index f9f11d3..9074b85 100644
--- a/tests/src/com/android/tradefed/device/NativeDeviceTest.java
+++ b/tests/src/com/android/tradefed/device/NativeDeviceTest.java
@@ -1210,34 +1210,44 @@
/** Unit test for {@link NativeDevice#getBugreportz()}. */
@Test
public void testGetBugreportz() throws IOException {
- mTestDevice = new TestableAndroidNativeDevice() {
- @Override
- public void executeShellCommand(
- String command, IShellOutputReceiver receiver,
- long maxTimeToOutputShellResponse, TimeUnit timeUnit, int retryAttempts)
+ mTestDevice =
+ new TestableAndroidNativeDevice() {
+ @Override
+ public void executeShellCommand(
+ String command,
+ IShellOutputReceiver receiver,
+ long maxTimeToOutputShellResponse,
+ TimeUnit timeUnit,
+ int retryAttempts)
throws DeviceNotAvailableException {
- String fakeRep = "OK:/data/0/com.android.shell/bugreports/bugreport1970-10-27.zip";
- receiver.addOutput(fakeRep.getBytes(), 0, fakeRep.getBytes().length);
- }
- @Override
- public boolean doesFileExist(String destPath) throws DeviceNotAvailableException {
- return true;
- }
- @Override
- public boolean pullFile(String remoteFilePath, File localFile)
- throws DeviceNotAvailableException {
- return true;
- }
- @Override
- public String executeShellCommand(String command) throws DeviceNotAvailableException {
- assertEquals("rm /data/0/com.android.shell/bugreports/*", command);
- return null;
- }
- @Override
- public int getApiLevel() throws DeviceNotAvailableException {
- return 24;
- }
- };
+ String fakeRep =
+ "OK:/data/0/com.android.shell/bugreports/bugreport1970-10-27.zip";
+ receiver.addOutput(fakeRep.getBytes(), 0, fakeRep.getBytes().length);
+ }
+
+ @Override
+ public boolean doesFileExist(String destPath)
+ throws DeviceNotAvailableException {
+ return true;
+ }
+
+ @Override
+ public boolean pullFile(String remoteFilePath, File localFile)
+ throws DeviceNotAvailableException {
+ return true;
+ }
+
+ @Override
+ public void deleteFile(String deviceFilePath)
+ throws DeviceNotAvailableException {
+ assertEquals("/data/0/com.android.shell/bugreports/*", deviceFilePath);
+ }
+
+ @Override
+ public int getApiLevel() throws DeviceNotAvailableException {
+ return 24;
+ }
+ };
FileInputStreamSource f = null;
try {
f = (FileInputStreamSource) mTestDevice.getBugreportz();
diff --git a/tests/src/com/android/tradefed/device/TestDeviceFuncTest.java b/tests/src/com/android/tradefed/device/TestDeviceFuncTest.java
index e14cf50..4836cff 100644
--- a/tests/src/com/android/tradefed/device/TestDeviceFuncTest.java
+++ b/tests/src/com/android/tradefed/device/TestDeviceFuncTest.java
@@ -50,6 +50,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.Set;
import javax.imageio.ImageIO;
@@ -819,4 +820,11 @@
// restore initial value
mTestDevice.setSetting(0, "system", "screen_brightness", initValue);
}
+
+ /** Test for {@link TestDevice#listDisplayIds()}. */
+ @Test
+ public void testListDisplays() throws Exception {
+ Set<Integer> displays = mTestDevice.listDisplayIds();
+ assertEquals(1, displays.size());
+ }
}
diff --git a/tests/src/com/android/tradefed/device/TestDeviceTest.java b/tests/src/com/android/tradefed/device/TestDeviceTest.java
index c26cb17..9561275 100644
--- a/tests/src/com/android/tradefed/device/TestDeviceTest.java
+++ b/tests/src/com/android/tradefed/device/TestDeviceTest.java
@@ -31,6 +31,7 @@
import com.android.tradefed.device.ITestDevice.ApexInfo;
import com.android.tradefed.device.ITestDevice.MountPointInfo;
import com.android.tradefed.device.ITestDevice.RecoveryMode;
+import com.android.tradefed.device.contentprovider.ContentProviderHandler;
import com.android.tradefed.host.HostOptions;
import com.android.tradefed.host.IHostOptions;
import com.android.tradefed.log.LogUtil.CLog;
@@ -4210,6 +4211,11 @@
throws DeviceNotAvailableException {
return mMockWifi;
}
+
+ @Override
+ ContentProviderHandler getContentProvider() throws DeviceNotAvailableException {
+ return null;
+ }
};
mMockIDevice.executeShellCommand(
EasyMock.eq("dumpsys package com.android.tradefed.utils.wifi"),
@@ -4224,4 +4230,30 @@
mTestDevice.postInvocationTearDown();
verifyMocks();
}
+
+ /** Test that displays can be collected. */
+ public void testListDisplayId() throws Exception {
+ CommandResult res = new CommandResult(CommandStatus.SUCCESS);
+ res.setStdout("Display 0 color modes:\nDisplay 5 color modes:\n");
+ EasyMock.expect(
+ mMockRunUtil.runTimedCmd(
+ 100L,
+ "adb",
+ "-s",
+ "serial",
+ "shell",
+ "dumpsys",
+ "SurfaceFlinger",
+ "|",
+ "grep",
+ "'color",
+ "modes:'"))
+ .andReturn(res);
+ replayMocks();
+ Set<Integer> displays = mTestDevice.listDisplayIds();
+ assertEquals(2, displays.size());
+ assertTrue(displays.contains(0));
+ assertTrue(displays.contains(5));
+ verifyMocks();
+ }
}
diff --git a/tests/src/com/android/tradefed/device/contentprovider/ContentProviderHandlerTest.java b/tests/src/com/android/tradefed/device/contentprovider/ContentProviderHandlerTest.java
index 2c1395f..0ff4b1a 100644
--- a/tests/src/com/android/tradefed/device/contentprovider/ContentProviderHandlerTest.java
+++ b/tests/src/com/android/tradefed/device/contentprovider/ContentProviderHandlerTest.java
@@ -55,12 +55,25 @@
mProvider.tearDown();
}
+ /** Test the install flow. */
@Test
public void testSetUp_install() throws Exception {
Set<String> set = new HashSet<>();
doReturn(set).when(mMockDevice).getInstalledPackageNames();
doReturn(1).when(mMockDevice).getCurrentUser();
doReturn(null).when(mMockDevice).installPackage(any(), eq(true), eq(true));
+ doReturn(null)
+ .when(mMockDevice)
+ .executeShellV2Command(
+ String.format(
+ "cmd appops set %s android:legacy_storage allow",
+ ContentProviderHandler.PACKAGE_NAME));
+ CommandResult res = new CommandResult(CommandStatus.SUCCESS);
+ res.setStdout("LEGACY_STORAGE: allow");
+ doReturn(res)
+ .when(mMockDevice)
+ .executeShellV2Command(
+ String.format("cmd appops get %s", ContentProviderHandler.PACKAGE_NAME));
assertTrue(mProvider.setUp());
}
diff --git a/tests/src/com/android/tradefed/device/metric/AtraceCollectorTest.java b/tests/src/com/android/tradefed/device/metric/AtraceCollectorTest.java
index 330743e..d5c4a5f 100644
--- a/tests/src/com/android/tradefed/device/metric/AtraceCollectorTest.java
+++ b/tests/src/com/android/tradefed/device/metric/AtraceCollectorTest.java
@@ -236,9 +236,7 @@
EasyMock.expect(mMockDevice.pullFile(EasyMock.eq(M_DEFAULT_LOG_PATH)))
.andReturn(new File("/tmp/potato"))
.once();
- EasyMock.expect(mMockDevice.executeShellCommand(EasyMock.eq("rm -f " + M_DEFAULT_LOG_PATH)))
- .andReturn("")
- .times(1);
+ mMockDevice.deleteFile(M_DEFAULT_LOG_PATH);
EasyMock.replay(mMockDevice);
mAtrace.onTestEnd(
diff --git a/tests/src/com/android/tradefed/device/metric/AtraceRunMetricCollectorTest.java b/tests/src/com/android/tradefed/device/metric/AtraceRunMetricCollectorTest.java
index 6242789..c6a43a1 100644
--- a/tests/src/com/android/tradefed/device/metric/AtraceRunMetricCollectorTest.java
+++ b/tests/src/com/android/tradefed/device/metric/AtraceRunMetricCollectorTest.java
@@ -24,7 +24,6 @@
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.LogDataType;
import com.android.tradefed.util.FileUtil;
-import com.android.tradefed.util.proto.TfMetricProtoUtil;
import org.junit.Before;
import org.junit.Test;
@@ -83,8 +82,6 @@
OptionSetter setter = new OptionSetter(mAtraceRunMetricCollector);
setter.setOptionValue("directory-keys", "sdcard/srcdirectory");
- HashMap<String, Metric> currentMetrics = new HashMap<>();
- currentMetrics.put("srcdirectory", TfMetricProtoUtil.stringToMetric("sdcard/srcdirectory"));
Mockito.when(mMockDevice.pullDir(Mockito.eq("sdcard/srcdirectory"),
Mockito.any(File.class))).thenReturn(true);
diff --git a/tests/src/com/android/tradefed/suite/checker/UserCheckerTest.java b/tests/src/com/android/tradefed/suite/checker/UserCheckerTest.java
index 6c000ce..b013373 100644
--- a/tests/src/com/android/tradefed/suite/checker/UserCheckerTest.java
+++ b/tests/src/com/android/tradefed/suite/checker/UserCheckerTest.java
@@ -46,14 +46,14 @@
ITestDevice preDevice =
mockDeviceUserState(
- /* users= */ new Integer[] {0},
+ /* userIds= */ new Integer[] {0},
/* runningUsers= */ new Integer[] {0},
/* currentUser= */ 0);
assertEquals(CheckStatus.SUCCESS, checker.preExecutionCheck(preDevice).getStatus());
ITestDevice postDevice =
mockDeviceUserState(
- /* users= */ new Integer[] {0},
+ /* userIds= */ new Integer[] {0},
/* runningUsers= */ new Integer[] {0},
/* currentUser= */ 0);
assertEquals(CheckStatus.SUCCESS, checker.postExecutionCheck(postDevice).getStatus());
@@ -66,7 +66,7 @@
ITestDevice preDevice =
mockDeviceUserState(
- /* users= */ new Integer[] {0, 10, 11},
+ /* userIds= */ new Integer[] {0, 10, 11},
/* runningUsers= */ new Integer[] {0, 10},
/* currentUser= */ 10);
assertEquals(CheckStatus.SUCCESS, checker.preExecutionCheck(preDevice).getStatus());
@@ -74,7 +74,7 @@
// User12 created, User11 deleted, User10 stopped, currentUser changed
ITestDevice postDevice =
mockDeviceUserState(
- /* users= */ new Integer[] {0, 10, 12},
+ /* userIds= */ new Integer[] {0, 10, 12},
/* runningUsers= */ new Integer[] {0},
/* currentUser= */ 0);
assertEquals(CheckStatus.FAILED, checker.postExecutionCheck(postDevice).getStatus());
@@ -87,7 +87,7 @@
mOptionSetter.setOptionValue("user-type", "secondary");
ITestDevice device =
mockDeviceUserState(
- /* users= */ new Integer[] {0},
+ /* userIds= */ new Integer[] {0},
/* runningUsers= */ new Integer[] {0},
/* currentUser= */ 0);
@@ -111,7 +111,7 @@
mOptionSetter.setOptionValue("user-type", "secondary");
ITestDevice device =
mockDeviceUserState(
- /* users= */ new Integer[] {0},
+ /* userIds= */ new Integer[] {0},
/* runningUsers= */ new Integer[] {0},
/* currentUser= */ 0);
@@ -128,12 +128,12 @@
public void testFindRemovedUsers() throws Exception {
DeviceUserState preState =
getMockedUserState(
- /* users= */ new Integer[] {0, 10},
+ /* userIds= */ new Integer[] {0, 10},
/* runningUsers= */ new Integer[] {0, 10},
/* currentUser= */ 0);
DeviceUserState postState =
getMockedUserState(
- /* users= */ new Integer[] {0},
+ /* userIds= */ new Integer[] {0},
/* runningUsers= */ new Integer[] {0},
/* currentUser= */ 0);
@@ -144,12 +144,12 @@
public void testFindAddedUsers() throws Exception {
DeviceUserState preState =
getMockedUserState(
- /* users= */ new Integer[] {0},
+ /* userIds= */ new Integer[] {0},
/* runningUsers= */ new Integer[] {0},
/* currentUser= */ 0);
DeviceUserState postState =
getMockedUserState(
- /* users= */ new Integer[] {0, 10},
+ /* userIds= */ new Integer[] {0, 10},
/* runningUsers= */ new Integer[] {0},
/* currentUser= */ 0);
@@ -160,12 +160,12 @@
public void testCurrentUserChanged() throws Exception {
DeviceUserState preState =
getMockedUserState(
- /* users= */ new Integer[] {0, 10},
+ /* userIds= */ new Integer[] {0, 10},
/* runningUsers= */ new Integer[] {0, 10},
/* currentUser= */ 10);
DeviceUserState postState =
getMockedUserState(
- /* users= */ new Integer[] {0, 10},
+ /* userIds= */ new Integer[] {0, 10},
/* runningUsers= */ new Integer[] {0, 10},
/* currentUser= */ 0);
@@ -176,12 +176,12 @@
public void testfindStartedUsers() throws Exception {
DeviceUserState preState =
getMockedUserState(
- /* users= */ new Integer[] {0, 10},
+ /* userIds= */ new Integer[] {0, 10},
/* runningUsers= */ new Integer[] {0},
/* currentUser= */ 0);
DeviceUserState postState =
getMockedUserState(
- /* users= */ new Integer[] {0, 10},
+ /* userIds= */ new Integer[] {0, 10},
/* runningUsers= */ new Integer[] {0, 10},
/* currentUser= */ 0);
@@ -193,12 +193,12 @@
public void testFindStopedUsers() throws Exception {
DeviceUserState preState =
getMockedUserState(
- /* users= */ new Integer[] {0, 10},
+ /* userIds= */ new Integer[] {0, 10},
/* runningUsers= */ new Integer[] {0, 10},
/* currentUser= */ 0);
DeviceUserState postState =
getMockedUserState(
- /* users= */ new Integer[] {0, 10},
+ /* userIds= */ new Integer[] {0, 10},
/* runningUsers= */ new Integer[] {0},
/* currentUser= */ 0);
diff --git a/tests/src/com/android/tradefed/targetprep/DeviceSetupTest.java b/tests/src/com/android/tradefed/targetprep/DeviceSetupTest.java
index 745175f..a7426a8 100644
--- a/tests/src/com/android/tradefed/targetprep/DeviceSetupTest.java
+++ b/tests/src/com/android/tradefed/targetprep/DeviceSetupTest.java
@@ -1079,9 +1079,7 @@
doSetupExpectations();
doCheckExternalStoreSpaceExpectations();
EasyMock.expect(mMockDevice.pullFile("/data/local.prop")).andReturn(null).once();
- EasyMock.expect(mMockDevice.executeShellCommand("rm -f /data/local.prop"))
- .andReturn(null)
- .once();
+ mMockDevice.deleteFile("/data/local.prop");
mMockDevice.reboot();
EasyMock.expectLastCall().once();
diff --git a/tests/src/com/android/tradefed/testtype/AndroidJUnitTestTest.java b/tests/src/com/android/tradefed/testtype/AndroidJUnitTestTest.java
index 7e61ffd..11ea3cb 100644
--- a/tests/src/com/android/tradefed/testtype/AndroidJUnitTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/AndroidJUnitTestTest.java
@@ -223,7 +223,8 @@
EasyMock.<File>anyObject(), EasyMock.<String>anyObject())).andReturn(Boolean.TRUE);
EasyMock.expect(mMockTestDevice.executeShellCommand(EasyMock.<String>anyObject()))
.andReturn("")
- .times(2);
+ .times(1);
+ mMockTestDevice.deleteFile("/data/local/tmp/ajur");
EasyMock.replay(mMockRemoteRunner, mMockTestDevice);
File tmpFile = FileUtil.createTempFile("testFile", ".txt");
@@ -248,7 +249,8 @@
EasyMock.<File>anyObject(), EasyMock.<String>anyObject())).andReturn(Boolean.TRUE);
EasyMock.expect(mMockTestDevice.executeShellCommand(EasyMock.<String>anyObject()))
.andReturn("")
- .times(2);
+ .times(1);
+ mMockTestDevice.deleteFile("/data/local/tmp/ajur");
EasyMock.replay(mMockRemoteRunner, mMockTestDevice);
File tmpFile = FileUtil.createTempFile("notTestFile", ".txt");
@@ -279,7 +281,8 @@
EasyMock.<String>anyObject())).andReturn(Boolean.TRUE).times(2);
EasyMock.expect(mMockTestDevice.executeShellCommand(EasyMock.<String>anyObject()))
.andReturn("")
- .times(3);
+ .times(2);
+ mMockTestDevice.deleteFile("/data/local/tmp/ajur");
EasyMock.replay(mMockRemoteRunner, mMockTestDevice);
File tmpFileInclude = FileUtil.createTempFile("includeFile", ".txt");
@@ -351,7 +354,8 @@
.times(2);
EasyMock.expect(mMockTestDevice.executeShellCommand(EasyMock.<String>anyObject()))
.andReturn("")
- .times(3);
+ .times(2);
+ mMockTestDevice.deleteFile("/data/local/tmp/ajur");
EasyMock.replay(mMockRemoteRunner, mMockTestDevice);
File tmpFileInclude = FileUtil.createTempFile("includeFile", ".txt");
diff --git a/tests/src/com/android/tradefed/testtype/DeviceJUnit4ClassRunnerTest.java b/tests/src/com/android/tradefed/testtype/DeviceJUnit4ClassRunnerTest.java
new file mode 100644
index 0000000..82ef7c9
--- /dev/null
+++ b/tests/src/com/android/tradefed/testtype/DeviceJUnit4ClassRunnerTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.doReturn;
+
+import com.android.tradefed.build.BuildInfo;
+import com.android.tradefed.config.ConfigurationDef;
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.DynamicRemoteFileResolver;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.config.remote.GcsRemoteFileResolver;
+import com.android.tradefed.config.remote.IRemoteFileResolver;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.invoker.InvocationContext;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
+import com.android.tradefed.result.ITestInvocationListener;
+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 org.junit.runners.model.InitializationError;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.util.HashMap;
+
+/** Unit tests for {@link DeviceJUnit4ClassRunner}. */
+@RunWith(JUnit4.class)
+public class DeviceJUnit4ClassRunnerTest {
+
+ private static final File FAKE_REMOTE_FILE_PATH = new File("gs://bucket/test/file.txt");
+
+ /** Class that allow testing. */
+ public static class TestableRunner extends DeviceJUnit4ClassRunner {
+
+ public TestableRunner(Class<?> klass) throws InitializationError {
+ super(klass);
+ }
+
+ @Override
+ OptionSetter createOptionSetter(Object obj) throws ConfigurationException {
+ return new OptionSetter(obj) {
+ @Override
+ protected DynamicRemoteFileResolver createResolver() {
+ DynamicRemoteFileResolver mResolver =
+ new DynamicRemoteFileResolver() {
+ @Override
+ protected IRemoteFileResolver getResolver(String protocol) {
+ if (protocol.equals(GcsRemoteFileResolver.PROTOCOL)) {
+ IRemoteFileResolver mockResolver =
+ Mockito.mock(IRemoteFileResolver.class);
+ try {
+ doReturn(new File("/downloaded/somewhere"))
+ .when(mockResolver)
+ .resolveRemoteFiles(
+ Mockito.eq(FAKE_REMOTE_FILE_PATH),
+ Mockito.any());
+ return mockResolver;
+ } catch (ConfigurationException e) {
+ CLog.e(e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean updateProtocols() {
+ // Do not set the static variable
+ return false;
+ }
+ };
+ return mResolver;
+ }
+ };
+ }
+ }
+
+ @RunWith(TestableRunner.class)
+ public static class Junit4TestClass {
+
+ public Junit4TestClass() {}
+
+ @Option(name = "dynamic-option")
+ public File mOption = FAKE_REMOTE_FILE_PATH;
+
+ @Test
+ public void testPass() {
+ assertNotNull(mOption);
+ assertNotEquals(FAKE_REMOTE_FILE_PATH, mOption);
+ }
+ }
+
+ private ITestInvocationListener mListener;
+ private HostTest mHostTest;
+ private ITestDevice mMockDevice;
+
+ @Before
+ public void setUp() throws Exception {
+ mListener = EasyMock.createMock(ITestInvocationListener.class);
+ mMockDevice = EasyMock.createMock(ITestDevice.class);
+ mHostTest = new HostTest();
+ mHostTest.setBuild(new BuildInfo());
+ mHostTest.setDevice(mMockDevice);
+ IInvocationContext context = new InvocationContext();
+ context.addAllocatedDevice(ConfigurationDef.DEFAULT_DEVICE_NAME, mMockDevice);
+ OptionSetter setter = new OptionSetter(mHostTest);
+ // Disable pretty logging for testing
+ setter.setOptionValue("enable-pretty-logs", "false");
+ }
+
+ @Test
+ public void testDynamicDownload() throws Exception {
+ mHostTest.setClassName(Junit4TestClass.class.getName());
+ TestDescription test1 = new TestDescription(Junit4TestClass.class.getName(), "testPass");
+ mListener.testRunStarted((String) EasyMock.anyObject(), EasyMock.eq(1));
+ mListener.testStarted(EasyMock.eq(test1));
+ mListener.testEnded(EasyMock.eq(test1), EasyMock.<HashMap<String, Metric>>anyObject());
+ mListener.testRunEnded(EasyMock.anyLong(), EasyMock.<HashMap<String, Metric>>anyObject());
+ EasyMock.replay(mListener);
+ mHostTest.run(mListener);
+ EasyMock.verify(mListener);
+ }
+}
diff --git a/tests/src/com/android/tradefed/testtype/GTestTest.java b/tests/src/com/android/tradefed/testtype/GTestTest.java
index d85e193..c8441a6 100644
--- a/tests/src/com/android/tradefed/testtype/GTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/GTestTest.java
@@ -312,8 +312,7 @@
EasyMock.same(mMockReceiver), EasyMock.anyLong(), (TimeUnit)EasyMock.anyObject(),
EasyMock.anyInt());
// Expect deletion of file on device
- EasyMock.expect(mMockITestDevice.executeShellCommand(
- EasyMock.eq(String.format("rm %s", deviceScriptPath)))).andReturn("");
+ mMockITestDevice.deleteFile(deviceScriptPath);
replayMocks();
mGTest.run(mMockInvocationListener);
diff --git a/tests/src/com/android/tradefed/testtype/binary/ExecutableHostTestTest.java b/tests/src/com/android/tradefed/testtype/binary/ExecutableHostTestTest.java
index f0c0acd..a267e95 100644
--- a/tests/src/com/android/tradefed/testtype/binary/ExecutableHostTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/binary/ExecutableHostTestTest.java
@@ -22,6 +22,8 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import com.android.tradefed.build.DeviceBuildInfo;
+import com.android.tradefed.build.IDeviceBuildInfo;
import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
@@ -75,7 +77,7 @@
public void testRunHostExecutable_doesNotExists() throws Exception {
String path = "/does/not/exists/path/bin/test";
OptionSetter setter = new OptionSetter(mExecutableTest);
- setter.setOptionValue("binary-path", path);
+ setter.setOptionValue("binary", path);
mExecutableTest.run(mMockListener);
@@ -91,7 +93,7 @@
File tmpBinary = FileUtil.createTempFile("test-executable", "");
try {
OptionSetter setter = new OptionSetter(mExecutableTest);
- setter.setOptionValue("binary-path", tmpBinary.getAbsolutePath());
+ setter.setOptionValue("binary", tmpBinary.getAbsolutePath());
CommandResult result = new CommandResult(CommandStatus.SUCCESS);
doReturn(result)
@@ -109,12 +111,73 @@
}
}
+ /** If the binary is available from the tests directory we can find it and run it. */
+ @Test
+ public void testRunHostExecutable_search() throws Exception {
+ File testsDir = FileUtil.createTempDir("executable-tests-dir");
+ File tmpBinary = FileUtil.createTempFile("test-executable", "", testsDir);
+ try {
+ IDeviceBuildInfo info = new DeviceBuildInfo();
+ info.setTestsDir(testsDir, "testversion");
+ mExecutableTest.setBuild(info);
+ OptionSetter setter = new OptionSetter(mExecutableTest);
+ setter.setOptionValue("binary", tmpBinary.getName());
+
+ CommandResult result = new CommandResult(CommandStatus.SUCCESS);
+ doReturn(result)
+ .when(mMockRunUtil)
+ .runTimedCmd(Mockito.anyLong(), Mockito.eq(tmpBinary.getAbsolutePath()));
+
+ mExecutableTest.run(mMockListener);
+
+ verify(mMockListener, Mockito.times(1)).testRunStarted(eq(tmpBinary.getName()), eq(1));
+ verify(mMockListener, Mockito.times(0)).testRunFailed(any());
+ verify(mMockListener, Mockito.times(1))
+ .testRunEnded(Mockito.anyLong(), Mockito.<HashMap<String, Metric>>any());
+ } finally {
+ FileUtil.recursiveDelete(testsDir);
+ }
+ }
+
+ @Test
+ public void testRunHostExecutable_notFound() throws Exception {
+ File testsDir = FileUtil.createTempDir("executable-tests-dir");
+ File tmpBinary = FileUtil.createTempFile("test-executable", "", testsDir);
+ try {
+ IDeviceBuildInfo info = new DeviceBuildInfo();
+ info.setTestsDir(testsDir, "testversion");
+ mExecutableTest.setBuild(info);
+ OptionSetter setter = new OptionSetter(mExecutableTest);
+ setter.setOptionValue("binary", tmpBinary.getName());
+ tmpBinary.delete();
+
+ CommandResult result = new CommandResult(CommandStatus.SUCCESS);
+ doReturn(result)
+ .when(mMockRunUtil)
+ .runTimedCmd(Mockito.anyLong(), Mockito.eq(tmpBinary.getAbsolutePath()));
+
+ mExecutableTest.run(mMockListener);
+
+ verify(mMockListener, Mockito.times(1)).testRunStarted(eq(tmpBinary.getName()), eq(0));
+ verify(mMockListener, Mockito.times(1))
+ .testRunFailed(
+ eq(
+ String.format(
+ ExecutableBaseTest.NO_BINARY_ERROR,
+ tmpBinary.getName())));
+ verify(mMockListener, Mockito.times(1))
+ .testRunEnded(Mockito.anyLong(), Mockito.<HashMap<String, Metric>>any());
+ } finally {
+ FileUtil.recursiveDelete(testsDir);
+ }
+ }
+
@Test
public void testRunHostExecutable_failure() throws Exception {
File tmpBinary = FileUtil.createTempFile("test-executable", "");
try {
OptionSetter setter = new OptionSetter(mExecutableTest);
- setter.setOptionValue("binary-path", tmpBinary.getAbsolutePath());
+ setter.setOptionValue("binary", tmpBinary.getAbsolutePath());
CommandResult result = new CommandResult(CommandStatus.FAILED);
result.setExitCode(5);
diff --git a/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java b/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java
index 3ae5415..8e51bc8 100644
--- a/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java
+++ b/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java
@@ -1502,9 +1502,6 @@
public void testNoAbi() throws Exception {
EasyMock.reset(mMockDevice);
EasyMock.expect(mMockDevice.getIDevice()).andStubReturn(new TcpDevice("tcp-device-0"));
- Set<String> expectedAbis = new HashSet<>();
- expectedAbis.add("arm64-v8a");
- expectedAbis.add("armeabi-v7a");
EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(null);
EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abi")).andReturn(null);
diff --git a/tests/src/com/android/tradefed/util/testmapping/TestInfoTest.java b/tests/src/com/android/tradefed/util/testmapping/TestInfoTest.java
index cd06932..dbc97a5 100644
--- a/tests/src/com/android/tradefed/util/testmapping/TestInfoTest.java
+++ b/tests/src/com/android/tradefed/util/testmapping/TestInfoTest.java
@@ -43,7 +43,11 @@
assertEquals("option2", info.getOptions().get(1).getName());
assertEquals("value2", info.getOptions().get(1).getValue());
assertEquals(
- "test1; Options: option1:value1,option2:value2; Keywords: key1,key2",
+ "test1\n\t" +
+ "Options: option1:value1,option2:value2\n\t" +
+ "Keywords: key1,key2\n\t" +
+ "Sources: folder1\n\t" +
+ "Host: false",
info.toString());
assertEquals("test1 - false", info.getNameAndHostOnly());
}
diff --git a/tests/src/com/android/tradefed/util/testmapping/TestMappingTest.java b/tests/src/com/android/tradefed/util/testmapping/TestMappingTest.java
index efc5ce8..56cfa8c 100644
--- a/tests/src/com/android/tradefed/util/testmapping/TestMappingTest.java
+++ b/tests/src/com/android/tradefed/util/testmapping/TestMappingTest.java
@@ -32,10 +32,12 @@
import java.io.File;
import java.io.InputStream;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/** Unit tests for {@link TestMapping}. */
@@ -61,35 +63,56 @@
String rootDirName = testMappingRootDir.getName();
testMappingFile =
FileUtil.saveResourceFile(resourceStream, testMappingRootDir, TEST_MAPPING);
- List<TestInfo> tests =
+ Set<TestInfo> tests =
new TestMapping(testMappingFile.toPath(), Paths.get(tempDir.getAbsolutePath()))
.getTests("presubmit", null, true, null);
assertEquals(1, tests.size());
- assertEquals("test1", tests.get(0).getName());
-
+ Set<String> names = new HashSet<String>();
+ for (TestInfo test : tests) {
+ names.add(test.getName());
+ }
+ assertTrue(names.contains("test1"));
tests =
new TestMapping(testMappingFile.toPath(), Paths.get(tempDir.getAbsolutePath()))
.getTests("presubmit", null, false, null);
assertEquals(1, tests.size());
- assertEquals("suite/stub1", tests.get(0).getName());
-
+ names = new HashSet<String>();
+ for (TestInfo test : tests) {
+ names.add(test.getName());
+ }
+ assertTrue(names.contains("suite/stub1"));
tests =
new TestMapping(testMappingFile.toPath(), Paths.get(tempDir.getAbsolutePath()))
.getTests("postsubmit", null, false, null);
assertEquals(2, tests.size());
- assertEquals("test2", tests.get(0).getName());
- TestOption option = tests.get(0).getOptions().get(0);
- assertEquals("instrumentation-arg", option.getName());
- assertEquals(
- "annotation=android.platform.test.annotations.Presubmit", option.getValue());
- assertEquals("instrument", tests.get(1).getName());
+ TestOption testOption =
+ new TestOption(
+ "instrumentation-arg",
+ "annotation=android.platform.test.annotations.Presubmit");
+ names = new HashSet<String>();
+ Set<TestOption> testOptions = new HashSet<TestOption>();
+ for (TestInfo test : tests) {
+ names.add(test.getName());
+ testOptions.addAll(test.getOptions());
+ }
+ assertTrue(names.contains("test2"));
+ assertTrue(names.contains("instrument"));
+ assertTrue(testOptions.contains(testOption));
tests =
new TestMapping(testMappingFile.toPath(), Paths.get(tempDir.getAbsolutePath()))
.getTests("othertype", null, false, null);
assertEquals(1, tests.size());
- assertEquals("test3", tests.get(0).getName());
- assertEquals(1, tests.get(0).getSources().size());
- assertTrue(tests.get(0).getSources().contains(rootDirName));
+ names = new HashSet<String>();
+ testOptions = new HashSet<TestOption>();
+ Set<String> sources = new HashSet<String>();
+ for (TestInfo test : tests) {
+ names.add(test.getName());
+ testOptions.addAll(test.getOptions());
+ sources.addAll(test.getSources());
+ }
+ assertTrue(names.contains("test3"));
+ assertEquals(1, testOptions.size());
+ assertTrue(sources.contains(rootDirName));
} finally {
FileUtil.recursiveDelete(tempDir);
}
@@ -104,7 +127,7 @@
tempDir = FileUtil.createTempDir("test_mapping");
File testMappingFile = Paths.get(tempDir.getAbsolutePath(), TEST_MAPPING).toFile();
FileUtil.writeToFile("bad format json file", testMappingFile);
- List<TestInfo> tests =
+ Set<TestInfo> tests =
new TestMapping(testMappingFile.toPath(), Paths.get(tempDir.getAbsolutePath()))
.getTests("presubmit", null, false, null);
} finally {
@@ -374,4 +397,111 @@
assertTrue(new HashSet<TestOption>(test1.getOptions()).contains(optionExcludeAnnotation2));
assertTrue(new HashSet<TestOption>(test1.getOptions()).contains(option2));
}
+
+ /** Test for {@link TestMapping#getAllTests()} for loading tests from test_mappings directory. */
+ @Test
+ public void testGetAllTests() throws Exception {
+ File tempDir = null;
+ try {
+ tempDir = FileUtil.createTempDir("test_mapping");
+
+ File srcDir = FileUtil.createTempDir("src", tempDir);
+ String srcFile = File.separator + TEST_DATA_DIR + File.separator + "test_mapping_1";
+ InputStream resourceStream = this.getClass().getResourceAsStream(srcFile);
+ FileUtil.saveResourceFile(resourceStream, srcDir, TEST_MAPPING);
+ File subDir = FileUtil.createTempDir("sub_dir", srcDir);
+ srcFile = File.separator + TEST_DATA_DIR + File.separator + "test_mapping_2";
+ resourceStream = this.getClass().getResourceAsStream(srcFile);
+ FileUtil.saveResourceFile(resourceStream, subDir, TEST_MAPPING);
+ srcFile = File.separator + TEST_DATA_DIR + File.separator + DISABLED_PRESUBMIT_TESTS;
+ resourceStream = this.getClass().getResourceAsStream(srcFile);
+ FileUtil.saveResourceFile(resourceStream, tempDir, DISABLED_PRESUBMIT_TESTS);
+
+ Map<String, Set<TestInfo>> allTests = TestMapping.getAllTests(tempDir);
+ Set<TestInfo> tests = allTests.get("presubmit");
+ assertEquals(5, tests.size());
+
+ tests = allTests.get("postsubmit");
+ assertEquals(4, tests.size());
+
+ tests = allTests.get("othertype");
+ assertEquals(1, tests.size());
+ } finally {
+ FileUtil.recursiveDelete(tempDir);
+ }
+ }
+
+ /** Test for {@link TestMapping#extractTestMappingsZip()} for extracting test mappings zip. */
+ @Test
+ public void testExtractTestMappingsZip() throws Exception {
+ File tempDir = null;
+ File extractedFile = null;
+ try {
+ tempDir = FileUtil.createTempDir("test_mapping");
+
+ File srcDir = FileUtil.createTempDir("src", tempDir);
+ String srcFile = File.separator + TEST_DATA_DIR + File.separator + "test_mapping_1";
+ InputStream resourceStream = this.getClass().getResourceAsStream(srcFile);
+ FileUtil.saveResourceFile(resourceStream, srcDir, TEST_MAPPING);
+ File subDir = FileUtil.createTempDir("sub_dir", srcDir);
+ srcFile = File.separator + TEST_DATA_DIR + File.separator + "test_mapping_2";
+ resourceStream = this.getClass().getResourceAsStream(srcFile);
+ FileUtil.saveResourceFile(resourceStream, subDir, TEST_MAPPING);
+ srcFile = File.separator + TEST_DATA_DIR + File.separator + DISABLED_PRESUBMIT_TESTS;
+ resourceStream = this.getClass().getResourceAsStream(srcFile);
+ FileUtil.saveResourceFile(resourceStream, tempDir, DISABLED_PRESUBMIT_TESTS);
+ List<File> filesToZip =
+ Arrays.asList(srcDir, new File(tempDir, DISABLED_PRESUBMIT_TESTS));
+
+ File zipFile = Paths.get(tempDir.getAbsolutePath(), TEST_MAPPINGS_ZIP).toFile();
+ ZipUtil.createZip(filesToZip, zipFile);
+
+ extractedFile = TestMapping.extractTestMappingsZip(zipFile);
+ Map<String, Set<TestInfo>> allTests = TestMapping.getAllTests(tempDir);
+ Set<TestInfo> tests = allTests.get("presubmit");
+ assertEquals(5, tests.size());
+
+ tests = allTests.get("postsubmit");
+ assertEquals(4, tests.size());
+
+ tests = allTests.get("othertype");
+ assertEquals(1, tests.size());
+ } finally {
+ FileUtil.recursiveDelete(tempDir);
+ FileUtil.recursiveDelete(extractedFile);
+ }
+ }
+
+ /** Test for {@link TestMapping#extractTestMappingsZip()} for extracting test mappings zip. */
+ @Test
+ public void testGetDisabledTests() throws Exception {
+ File tempDir = null;
+ try {
+ tempDir = FileUtil.createTempDir("test_mapping");
+
+ File srcDir = FileUtil.createTempDir("src", tempDir);
+ String srcFile = File.separator + TEST_DATA_DIR + File.separator + "test_mapping_1";
+ InputStream resourceStream = this.getClass().getResourceAsStream(srcFile);
+ FileUtil.saveResourceFile(resourceStream, srcDir, TEST_MAPPING);
+ File subDir = FileUtil.createTempDir("sub_dir", srcDir);
+ srcFile = File.separator + TEST_DATA_DIR + File.separator + "test_mapping_2";
+ resourceStream = this.getClass().getResourceAsStream(srcFile);
+ FileUtil.saveResourceFile(resourceStream, subDir, TEST_MAPPING);
+ srcFile = File.separator + TEST_DATA_DIR + File.separator + DISABLED_PRESUBMIT_TESTS;
+ resourceStream = this.getClass().getResourceAsStream(srcFile);
+ FileUtil.saveResourceFile(resourceStream, tempDir, DISABLED_PRESUBMIT_TESTS);
+ Path tempDirPath = Paths.get(tempDir.getAbsolutePath());
+ Set<String> disabledTests = TestMapping.getDisabledTests(tempDirPath, "presubmit");
+ assertEquals(2, disabledTests.size());
+
+ disabledTests = TestMapping.getDisabledTests(tempDirPath, "postsubmit");
+ assertEquals(0, disabledTests.size());
+
+ disabledTests = TestMapping.getDisabledTests(tempDirPath, "othertype");
+ assertEquals(0, disabledTests.size());
+
+ } finally {
+ FileUtil.recursiveDelete(tempDir);
+ }
+ }
}