Merge "Add support for TF to discover test cases specified in env vars"
diff --git a/src/com/android/tradefed/config/ConfigurationFactory.java b/src/com/android/tradefed/config/ConfigurationFactory.java
index 506af37..6c9acb3 100644
--- a/src/com/android/tradefed/config/ConfigurationFactory.java
+++ b/src/com/android/tradefed/config/ConfigurationFactory.java
@@ -22,7 +22,9 @@
import com.android.tradefed.util.ClassPathScanner;
import com.android.tradefed.util.ClassPathScanner.IClassPathFilter;
import com.android.tradefed.util.DirectedGraph;
+import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.StreamUtil;
+import com.android.tradefed.util.SystemUtil;
import com.android.tradefed.util.keystore.DryRunKeyStore;
import com.android.tradefed.util.keystore.IKeyStoreClient;
@@ -40,6 +42,7 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
@@ -183,9 +186,51 @@
}
/**
- * Implementation of {@link IConfigDefLoader} that tracks the included
- * configurations from one root config, and throws an exception on circular
- * includes.
+ * Get a list of {@link File} of the test cases directories
+ *
+ * <p>The wrapper function is for unit test to mock the system calls.
+ *
+ * @return a list of {@link File} of directories of the test cases folder of build output, based
+ * on the value of environment variables.
+ */
+ @VisibleForTesting
+ List<File> getTestCasesDirs() {
+ return SystemUtil.getTestCasesDirs();
+ }
+
+ /**
+ * Get the path to the config file for a test case.
+ *
+ * <p>The given name in a test config can be the name of a test case located in an out directory
+ * defined in the following environment variables:
+ *
+ * <p>ANDROID_TARGET_OUT_TESTCASES
+ *
+ * <p>ANDROID_HOST_OUT_TESTCASES
+ *
+ * <p>This method tries to locate the test config name in these directories. If no config is
+ * found, return null.
+ *
+ * @param name Name of a config file.
+ * @return A File object of the config file for the given test case.
+ */
+ @VisibleForTesting
+ File getTestCaseConfigPath(String name) {
+ String[] possibleConfigFileNames = {name + ".xml", name + ".config", name + ".dynamic"};
+ for (File testCasesDir : getTestCasesDirs()) {
+ for (String configFileName : possibleConfigFileNames) {
+ File config = FileUtil.findFile(testCasesDir, configFileName);
+ if (config != null) {
+ return config;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Implementation of {@link IConfigDefLoader} that tracks the included configurations from one
+ * root config, and throws an exception on circular includes.
*/
class ConfigLoader implements IConfigDefLoader {
@@ -207,6 +252,15 @@
String configName = name;
if (!isBundledConfig(name)) {
configName = getAbsolutePath(null, name);
+ // If the config file does not exist in the default location, try to locate it from
+ // test cases directories defined by environment variables.
+ File configFile = new File(configName);
+ if (!configFile.exists()) {
+ configFile = getTestCaseConfigPath(name);
+ if (configFile != null) {
+ configName = configFile.getAbsolutePath();
+ }
+ }
}
final ConfigId configId = new ConfigId(name, templateMap);
@@ -281,6 +335,9 @@
// check that 'name' maps to a local file that exists
File localConfig = new File(name);
if (!localConfig.exists()) {
+ localConfig = getTestCaseConfigPath(name);
+ }
+ if (localConfig == null) {
throw new ConfigurationException(String.format(
"Bundled config '%s' is including a config '%s' that's neither "
+ "local nor bundled.",
@@ -558,11 +615,29 @@
return cpScanner.getClassPathEntries(new ConfigClasspathFilter(subPath));
}
+ /** Helper to get the test config files from test cases directories from build output. */
+ @VisibleForTesting
+ Set<String> getConfigNamesFromTestCases() {
+
+ Set<String> configNames = new HashSet<String>();
+ for (File testCasesDir : getTestCasesDirs()) {
+ try {
+ configNames.addAll(FileUtil.findFiles(testCasesDir, ".*.config"));
+ configNames.addAll(FileUtil.findFiles(testCasesDir, ".*.xml"));
+ configNames.addAll(FileUtil.findFiles(testCasesDir, ".*.dynamic"));
+ } catch (IOException e) {
+ CLog.w(
+ "Failed to get test config files from directory %s",
+ testCasesDir.getAbsolutePath());
+ }
+ }
+ return configNames;
+ }
+
/**
- * Loads all configurations found in classpath.
+ * Loads all configurations found in classpath and test cases directories.
*
- * @param discardExceptions true if any ConfigurationException should be
- * ignored.
+ * @param discardExceptions true if any ConfigurationException should be ignored.
* @throws ConfigurationException
*/
public void loadAllConfigs(boolean discardExceptions) throws ConfigurationException {
@@ -570,6 +645,9 @@
PrintStream ps = new PrintStream(baos);
boolean failed = false;
Set<String> configNames = getConfigSetFromClasspath(null);
+ // TODO: split the the configs into two lists, one from the jar packages and one from test
+ // cases directories.
+ configNames.addAll(getConfigNamesFromTestCases());
for (String configName : configNames) {
final ConfigId configId = new ConfigId(configName);
try {
diff --git a/src/com/android/tradefed/targetprep/PushFilePreparer.java b/src/com/android/tradefed/targetprep/PushFilePreparer.java
index 95c06ce..edf774c 100644
--- a/src/com/android/tradefed/targetprep/PushFilePreparer.java
+++ b/src/com/android/tradefed/targetprep/PushFilePreparer.java
@@ -23,11 +23,14 @@
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.SystemUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
/**
* A {@link ITargetPreparer} that attempts to push any number of files from any host path to any
@@ -73,27 +76,6 @@
private Collection<String> mFilesPushed = null;
/**
- * Set abort on failure. Exposed for testing.
- */
- void setAbortOnFailure(boolean value) {
- mAbortOnFailure = value;
- }
-
- /**
- * Set pushspecs. Exposed for testing.
- */
- void setPushSpecs(Collection<String> pushspecs) {
- mPushSpecs = pushspecs;
- }
-
- /**
- * Set post-push commands. Exposed for testing.
- */
- void setPostPushCommands(Collection<String> commands) {
- mPostPushCommands = commands;
- }
-
- /**
* Helper method to only throw if mAbortOnFailure is enabled. Callers should behave as if this
* method may return.
*/
@@ -107,14 +89,36 @@
}
/**
- * Resolve relative file path via {@link IBuildInfo}
+ * Get a list of {@link File} of the test cases directories
+ *
+ * <p>The wrapper function is for unit test to mock the system calls.
+ *
+ * @return a list of {@link File} of directories of the test cases folder of build output, based
+ * on the value of environment variables.
+ */
+ List<File> getTestCasesDirs() {
+ return SystemUtil.getTestCasesDirs();
+ }
+
+ /**
+ * Resolve relative file path via {@link IBuildInfo} and test cases directories.
*
* @param buildInfo the build artifact information
* @param fileName relative file path to be resolved
- * @return the file from the build info
+ * @return the file from the build info or test cases directories
*/
public File resolveRelativeFilePath(IBuildInfo buildInfo, String fileName) {
- return buildInfo.getFile(fileName);
+ File src = buildInfo.getFile(fileName);
+ if (src != null && src.exists()) {
+ return src;
+ }
+ for (File dir : getTestCasesDirs()) {
+ src = FileUtil.getFileForPath(dir, fileName);
+ if (src != null && src.exists()) {
+ return src;
+ }
+ }
+ return null;
}
/**
diff --git a/src/com/android/tradefed/targetprep/TestAppInstallSetup.java b/src/com/android/tradefed/targetprep/TestAppInstallSetup.java
index 1d31b21..59651fa 100644
--- a/src/com/android/tradefed/targetprep/TestAppInstallSetup.java
+++ b/src/com/android/tradefed/targetprep/TestAppInstallSetup.java
@@ -29,6 +29,7 @@
import com.android.tradefed.util.AaptParser;
import com.android.tradefed.util.AbiFormatter;
import com.android.tradefed.util.BuildTestsZipUtils;
+import com.android.tradefed.util.SystemUtil;
import java.io.File;
import java.io.IOException;
@@ -120,11 +121,21 @@
}
/**
- * {@inheritDoc}
+ * Get a list of {@link File} of the test cases directories
+ *
+ * <p>The wrapper function is for unit test to mock the system calls.
+ *
+ * @return a list of {@link File} of directories of the test cases folder of build output, based
+ * on the value of environment variables.
*/
+ List<File> getTestCasesDirs() {
+ return SystemUtil.getTestCasesDirs();
+ }
+
+ /** {@inheritDoc} */
@Override
- public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
- DeviceNotAvailableException {
+ public void setUp(ITestDevice device, IBuildInfo buildInfo)
+ throws TargetSetupError, DeviceNotAvailableException {
if (mTestFileNames == null || mTestFileNames.size() == 0) {
CLog.i("No test apps to install, skipping");
return;
@@ -132,6 +143,12 @@
if (mCleanup) {
mPackagesInstalled = new ArrayList<>();
}
+
+ // Force to look for apk files in build ouput's test cases directory.
+ for (File testCasesDir : getTestCasesDirs()) {
+ setAltDir(testCasesDir);
+ }
+
for (String testAppName : mTestFileNames) {
if (testAppName == null || testAppName.trim().isEmpty()) {
continue;
diff --git a/src/com/android/tradefed/util/BuildTestsZipUtils.java b/src/com/android/tradefed/util/BuildTestsZipUtils.java
index a344157..310a9b9 100644
--- a/src/com/android/tradefed/util/BuildTestsZipUtils.java
+++ b/src/com/android/tradefed/util/BuildTestsZipUtils.java
@@ -61,6 +61,8 @@
dirs.add(FileUtil.getFileForPath(dir, "DATA", "priv-app", apkBase));
// Files in out dir will be in data/app/apk_name
dirs.add(FileUtil.getFileForPath(dir, "data", "app", apkBase));
+ // Files in testcases directory will be in //apkBase
+ dirs.add(FileUtil.getFileForPath(dir, apkBase));
}
}
// reverse the order so ones provided via command line last can be searched first
diff --git a/src/com/android/tradefed/util/FileUtil.java b/src/com/android/tradefed/util/FileUtil.java
index 14eb17e..34d48e6 100644
--- a/src/com/android/tradefed/util/FileUtil.java
+++ b/src/com/android/tradefed/util/FileUtil.java
@@ -32,6 +32,8 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
@@ -944,4 +946,19 @@
}
return result;
}
+
+ /*
+ * Get all file paths of files in the given directory with name matching the given filter
+ *
+ * @param dir {@link File} object of the directory to search for files recursively
+ * @param filter {@link String} of the regex to match file names
+ * @return a set of {@link String} of the file paths
+ */
+ public static Set<String> findFiles(File dir, String filter) throws IOException {
+ Set<String> files = new HashSet<String>();
+ Files.walk(Paths.get(dir.getAbsolutePath()))
+ .filter(path -> new File(path.toString()).getName().matches(filter))
+ .forEach(path -> files.add(path.toString()));
+ return files;
+ }
}
diff --git a/src/com/android/tradefed/util/SystemUtil.java b/src/com/android/tradefed/util/SystemUtil.java
new file mode 100644
index 0000000..ed50023
--- /dev/null
+++ b/src/com/android/tradefed/util/SystemUtil.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 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.util;
+
+import com.android.tradefed.log.LogUtil.CLog;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/** Utility class for making system calls. */
+public class SystemUtil {
+
+ @VisibleForTesting static SystemUtil singleton = new SystemUtil();
+
+ // Environment variables for the test cases directory in target out directory and host out
+ // directory.
+ @VisibleForTesting
+ static final String ENV_ANDROID_TARGET_OUT_TESTCASES = "ANDROID_TARGET_OUT_TESTCASES";
+
+ @VisibleForTesting
+ static final String ENV_ANDROID_HOST_OUT_TESTCASES = "ANDROID_HOST_OUT_TESTCASES";
+
+ /**
+ * Get the value of an environment variable.
+ *
+ * <p>The wrapper function is created for mock in unit test.
+ *
+ * @param name the name of the environment variable.
+ * @return {@link String} value of the given environment variable.
+ */
+ @VisibleForTesting
+ String getEnv(String name) {
+ return System.getenv(name);
+ }
+
+ /**
+ * Get a list of {@link File} of the test cases directories
+ *
+ * @return a list of {@link File} of directories of the test cases folder of build output, based
+ * on the value of environment variables.
+ */
+ public static List<File> getTestCasesDirs() {
+ List<File> testCasesDirs = new ArrayList<File>();
+ Set<String> testCasesDirNames =
+ new HashSet<String>(
+ Arrays.asList(
+ singleton.getEnv(ENV_ANDROID_TARGET_OUT_TESTCASES),
+ singleton.getEnv(ENV_ANDROID_HOST_OUT_TESTCASES)));
+ for (String testCasesDirName : testCasesDirNames) {
+ if (testCasesDirName != null) {
+ File dir = new File(testCasesDirName);
+ if (dir.exists() && dir.isDirectory()) {
+ testCasesDirs.add(dir);
+ } else {
+ CLog.w(
+ "Path %s for test cases directory does not exist or it's not a "
+ + "directory.",
+ testCasesDirName);
+ }
+ }
+ }
+ return testCasesDirs;
+ }
+}
diff --git a/tests/src/com/android/tradefed/config/ConfigurationFactoryTest.java b/tests/src/com/android/tradefed/config/ConfigurationFactoryTest.java
index 77ad103..d061645 100644
--- a/tests/src/com/android/tradefed/config/ConfigurationFactoryTest.java
+++ b/tests/src/com/android/tradefed/config/ConfigurationFactoryTest.java
@@ -24,6 +24,8 @@
import junit.framework.TestCase;
+import org.mockito.Mockito;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
@@ -32,6 +34,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -68,18 +71,26 @@
* Sanity test to ensure all config names on classpath are loadable
*/
public void testLoadAllConfigs() throws ConfigurationException {
+ ConfigurationFactory spyFactory = Mockito.spy(mRealFactory);
+ Mockito.doReturn(new HashSet<String>()).when(spyFactory).getConfigNamesFromTestCases();
+
// we dry-run the templates otherwise it will always fail.
- mRealFactory.loadAllConfigs(false);
- assertTrue(mRealFactory.getMapConfig().size() > 0);
+ spyFactory.loadAllConfigs(false);
+ assertTrue(spyFactory.getMapConfig().size() > 0);
+ Mockito.verify(spyFactory, Mockito.times(1)).getConfigNamesFromTestCases();
}
/**
* Sanity test to ensure all configs on classpath can be fully loaded and parsed
*/
public void testLoadAndPrintAllConfigs() throws ConfigurationException {
+ ConfigurationFactory spyFactory = Mockito.spy(mRealFactory);
+ Mockito.doReturn(new HashSet<String>()).when(spyFactory).getConfigNamesFromTestCases();
+
// Printing the help involves more checks since it tries to resolve the config objects.
- mRealFactory.loadAndPrintAllConfigs();
- assertTrue(mRealFactory.getMapConfig().size() > 0);
+ spyFactory.loadAndPrintAllConfigs();
+ assertTrue(spyFactory.getMapConfig().size() > 0);
+ Mockito.verify(spyFactory, Mockito.times(1)).getConfigNamesFromTestCases();
}
/**
@@ -1251,4 +1262,39 @@
// Check that mandatory option was properly set, otherwise it will throw.
res.validateOptions();
}
+
+ /**
+ * This unit test ensures that the code will search for missing test configs in directories
+ * specified in certain environment variables.
+ */
+ public void testSearchConfigFromEnvVar() throws ConfigurationException, IOException {
+ File externalConfig = FileUtil.createTempFile("external-config", ".config");
+ String configName = FileUtil.getBaseName(externalConfig.getName());
+ File tmpDir = externalConfig.getParentFile();
+
+ ConfigurationFactory spyFactory = Mockito.spy(mFactory);
+ Mockito.doReturn(Arrays.asList(tmpDir)).when(spyFactory).getTestCasesDirs();
+
+ try {
+ File config = spyFactory.getTestCaseConfigPath(configName);
+ assertEquals(config.getAbsolutePath(), externalConfig.getAbsolutePath());
+ } finally {
+ FileUtil.deleteFile(externalConfig);
+ }
+ }
+
+ /**
+ * This unit test ensures that the code will search for missing test configs in directories
+ * specified in certain environment variables, and fail as the test config still can't be found.
+ */
+ public void testSearchConfigFromEnvVarFailed() throws ConfigurationException, IOException {
+ File tmpDir = new File(System.getProperty("java.io.tmpdir"));
+
+ ConfigurationFactory spyFactory = Mockito.spy(mFactory);
+ Mockito.doReturn(Arrays.asList(tmpDir)).when(spyFactory).getTestCasesDirs();
+
+ File config = spyFactory.getTestCaseConfigPath("non-exist");
+ assertNull(config);
+ Mockito.verify(spyFactory, Mockito.times(1)).getTestCasesDirs();
+ }
}
diff --git a/tests/src/com/android/tradefed/targetprep/PushFilePreparerTest.java b/tests/src/com/android/tradefed/targetprep/PushFilePreparerTest.java
index 74693fd..36f7b90 100644
--- a/tests/src/com/android/tradefed/targetprep/PushFilePreparerTest.java
+++ b/tests/src/com/android/tradefed/targetprep/PushFilePreparerTest.java
@@ -16,11 +16,13 @@
package com.android.tradefed.targetprep;
+import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.ITestDevice;
import junit.framework.TestCase;
import org.easymock.EasyMock;
+import org.mockito.Mockito;
import java.io.File;
import java.util.Arrays;
@@ -32,6 +34,7 @@
private PushFilePreparer mPreparer = null;
private ITestDevice mMockDevice = null;
+ private OptionSetter mOptionSetter = null;
/**
* {@inheritDoc}
@@ -43,6 +46,7 @@
EasyMock.expect(mMockDevice.getDeviceDescriptor()).andStubReturn(null);
EasyMock.expect(mMockDevice.getSerialNumber()).andStubReturn("SERIAL");
mPreparer = new PushFilePreparer();
+ mOptionSetter = new OptionSetter(mPreparer);
}
/**
@@ -54,8 +58,8 @@
}
public void testLocalNoExist() throws Exception {
- mPreparer.setPushSpecs(Arrays.asList("/noexist->/data/"));
- mPreparer.setPostPushCommands(Arrays.asList("ls /"));
+ mOptionSetter.setOptionValue("push", "/noexist->/data/");
+ mOptionSetter.setOptionValue("post-push", "ls /");
EasyMock.replay(mMockDevice);
try {
// Should throw TargetSetupError and _not_ run any post-push command
@@ -67,8 +71,8 @@
}
public void testRemoteNoExist() throws Exception {
- mPreparer.setPushSpecs(Arrays.asList("/bin/sh->/noexist/"));
- mPreparer.setPostPushCommands(Arrays.asList("ls /"));
+ mOptionSetter.setOptionValue("push", "/bin/sh->/noexist/");
+ mOptionSetter.setOptionValue("post-push", "ls /");
// expect a pushFile() call and return false (failed)
EasyMock.expect(
mMockDevice.pushFile((File)EasyMock.anyObject(), EasyMock.eq("/noexist/")))
@@ -84,9 +88,10 @@
}
public void testWarnOnFailure() throws Exception {
- mPreparer.setPushSpecs(Arrays.asList("/noexist->/data/", "/bin/sh->/noexist/"));
- mPreparer.setPostPushCommands(Arrays.asList("ls /"));
- mPreparer.setAbortOnFailure(false);
+ mOptionSetter.setOptionValue("push", "/bin/sh->/noexist/");
+ mOptionSetter.setOptionValue("push", "/noexist->/data/");
+ mOptionSetter.setOptionValue("post-push", "ls /");
+ mOptionSetter.setOptionValue("abort-on-push-failure", "false");
// expect a pushFile() call and return false (failed)
EasyMock.expect(
@@ -99,5 +104,18 @@
// Don't expect any exceptions to be thrown
mPreparer.setUp(mMockDevice, null);
}
+
+ public void testPushFromTestCasesDir() throws Exception {
+ mOptionSetter.setOptionValue("push", "sh->/noexist/");
+ mOptionSetter.setOptionValue("post-push", "ls /");
+
+ PushFilePreparer spyPreparer = Mockito.spy(mPreparer);
+ Mockito.doReturn(Arrays.asList(new File("/bin"))).when(spyPreparer).getTestCasesDirs();
+
+ // expect a pushFile() call as /bin/sh should exist and return false (failed)
+ EasyMock.expect(mMockDevice.pushFile((File) EasyMock.anyObject(), EasyMock.eq("/noexist/")))
+ .andReturn(Boolean.FALSE);
+ EasyMock.replay(mMockDevice);
+ }
}
diff --git a/tests/src/com/android/tradefed/util/FileUtilTest.java b/tests/src/com/android/tradefed/util/FileUtilTest.java
index f4de549..1b325a2 100644
--- a/tests/src/com/android/tradefed/util/FileUtilTest.java
+++ b/tests/src/com/android/tradefed/util/FileUtilTest.java
@@ -327,4 +327,37 @@
perms.remove(PosixFilePermission.OTHERS_EXECUTE) &&
perms.isEmpty());
}
+
+ /** Test {@link FileUtil#findFiles()} can find files successfully. */
+ public void testFindFilesSuccess() throws IOException {
+ File tmpDir = FileUtil.createTempDir("find_files_test");
+ try {
+ File matchFile1 = FileUtil.createTempFile("test", ".config", tmpDir);
+ File subDir = new File(tmpDir, "subfolder");
+ subDir.mkdirs();
+ File matchFile2 = FileUtil.createTempFile("test", ".config", subDir);
+ File unmatchFile = FileUtil.createTempFile("test", ".xml", subDir);
+ Set<String> matchFiles = FileUtil.findFiles(tmpDir, ".*.config");
+ assertTrue(matchFiles.contains(matchFile1.getAbsolutePath()));
+ assertTrue(matchFiles.contains(matchFile2.getAbsolutePath()));
+ assertEquals(matchFiles.size(), 2);
+ } finally {
+ FileUtil.recursiveDelete(tmpDir);
+ }
+ }
+
+ /** Test {@link FileUtil#findFiles()} returns empty set if no file matches filter. */
+ public void testFindFilesFail() throws IOException {
+ File tmpDir = FileUtil.createTempDir("find_files_test");
+ try {
+ File file1 = FileUtil.createTempFile("test", ".config", tmpDir);
+ File subDir = new File(tmpDir, "subfolder");
+ subDir.mkdirs();
+ File file2 = FileUtil.createTempFile("test", ".config", subDir);
+ Set<String> matchFiles = FileUtil.findFiles(tmpDir, ".*.config_x");
+ assertEquals(matchFiles.size(), 0);
+ } finally {
+ FileUtil.recursiveDelete(tmpDir);
+ }
+ }
}
diff --git a/tests/src/com/android/tradefed/util/SystemUtilTest.java b/tests/src/com/android/tradefed/util/SystemUtilTest.java
new file mode 100644
index 0000000..fe9cd98
--- /dev/null
+++ b/tests/src/com/android/tradefed/util/SystemUtilTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Test;
+
+/** Unit tests for {@link SystemUtil} */
+@RunWith(JUnit4.class)
+public class SystemUtilTest {
+
+ /**
+ * test {@link SystemUtil#getTestCasesDirs()} to make sure it gets directories from environment
+ * variables.
+ */
+ @Test
+ public void testGetTestCasesDirs() throws IOException {
+ File targetOutDir = null;
+ File hostOutDir = null;
+ try {
+ targetOutDir = FileUtil.createTempDir("target_out_dir");
+ hostOutDir = FileUtil.createTempDir("host_out_dir");
+
+ SystemUtil.singleton = Mockito.mock(SystemUtil.class);
+ Mockito.when(SystemUtil.singleton.getEnv(SystemUtil.ENV_ANDROID_TARGET_OUT_TESTCASES))
+ .thenReturn(targetOutDir.getAbsolutePath());
+ Mockito.when(SystemUtil.singleton.getEnv(SystemUtil.ENV_ANDROID_HOST_OUT_TESTCASES))
+ .thenReturn(hostOutDir.getAbsolutePath());
+
+ Set<File> testCasesDirs = new HashSet<File>(SystemUtil.getTestCasesDirs());
+ assertTrue(testCasesDirs.contains(targetOutDir));
+ assertTrue(testCasesDirs.contains(hostOutDir));
+ } finally {
+ FileUtil.recursiveDelete(targetOutDir);
+ FileUtil.recursiveDelete(hostOutDir);
+ }
+ }
+
+ /**
+ * test {@link SystemUtil#getTestCasesDirs()} to make sure no exception thrown if no environment
+ * variable is set or the directory does not exist.
+ */
+ @Test
+ public void testGetTestCasesDirsNoDir() throws IOException {
+ File targetOutDir = new File("/path/not/exist_1");
+
+ SystemUtil.singleton = Mockito.mock(SystemUtil.class);
+ Mockito.when(SystemUtil.singleton.getEnv(SystemUtil.ENV_ANDROID_TARGET_OUT_TESTCASES))
+ .thenReturn(targetOutDir.getAbsolutePath());
+ Mockito.when(SystemUtil.singleton.getEnv(SystemUtil.ENV_ANDROID_HOST_OUT_TESTCASES))
+ .thenReturn(null);
+
+ Set<File> testCasesDirs = new HashSet<File>(SystemUtil.getTestCasesDirs());
+ assertEquals(testCasesDirs.size(), 0);
+ }
+}