Merge "Fixing b/141252815 by extracting splits from apks before parsing the file and installing them."
diff --git a/atest/test_data/test_commands.json b/atest/test_data/test_commands.json
index c87a51b..f8251aa 100644
--- a/atest/test_data/test_commands.json
+++ b/atest/test_data/test_commands.json
@@ -1,39 +1,39 @@
{
"hello_world_test": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter hello_world_test --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter hello_world_test --log-level WARN --skip-loading-config-jar --logcat-on-failure"
],
"packages/apps/Car/Messenger/tests/robotests/src/com/android/car/messenger/MessengerDelegateTest.java": [
"./build/soong/soong_ui.bash --make-mode RunCarMessengerRoboTests"
],
"CtsAnimationTestCases:AnimatorTest": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsAnimationTestCases --atest-include-filter CtsAnimationTestCases:android.animation.cts.AnimatorTest --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsAnimationTestCases --atest-include-filter CtsAnimationTestCases:android.animation.cts.AnimatorTest --log-level WARN --skip-loading-config-jar --logcat-on-failure"
],
"CtsSampleDeviceTestCases:android.sample.cts.SampleDeviceReportLogTest": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsSampleDeviceTestCases --atest-include-filter CtsSampleDeviceTestCases:android.sample.cts.SampleDeviceReportLogTest --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsSampleDeviceTestCases --atest-include-filter CtsSampleDeviceTestCases:android.sample.cts.SampleDeviceReportLogTest --log-level WARN --skip-loading-config-jar --logcat-on-failure"
],
"CtsAnimationTestCases CtsSampleDeviceTestCases": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsAnimationTestCases --include-filter CtsSampleDeviceTestCases --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsAnimationTestCases --include-filter CtsSampleDeviceTestCases --log-level WARN --skip-loading-config-jar --logcat-on-failure"
],
"AnimatorTest": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsAnimationTestCases --atest-include-filter CtsAnimationTestCases:android.animation.cts.AnimatorTest --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsAnimationTestCases --atest-include-filter CtsAnimationTestCases:android.animation.cts.AnimatorTest --log-level WARN --skip-loading-config-jar --logcat-on-failure"
],
"PacketFragmenterTest": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter net_test_hci --atest-include-filter net_test_hci:PacketFragmenterTest.* --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter net_test_hci --atest-include-filter net_test_hci:PacketFragmenterTest.* --log-level WARN --skip-loading-config-jar --logcat-on-failure"
],
"android.animation.cts": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsAnimationTestCases --atest-include-filter CtsAnimationTestCases:android.animation.cts --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsAnimationTestCases --atest-include-filter CtsAnimationTestCases:android.animation.cts --log-level WARN --skip-loading-config-jar --logcat-on-failure"
],
"platform_testing/tests/example/native/Android.bp": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter hello_world_test --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter hello_world_test --log-level WARN --skip-loading-config-jar --logcat-on-failure"
],
"tools/tradefederation/core/res/config/native-benchmark.xml": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter native-benchmark --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter native-benchmark --log-level WARN --logcat-on-failure"
],
"native-benchmark": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter native-benchmark --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter native-benchmark --log-level WARN --logcat-on-failure"
],
"platform_testing/tests/example/native": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter hello_world_test --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter hello_world_test --log-level WARN --skip-loading-config-jar --logcat-on-failure"
],
"VtsCodelabHelloWorldTest": [
"vts-tradefed run commandAndExit vts-staging-default -m VtsCodelabHelloWorldTest --skip-all-system-status-check --skip-preconditions --primary-abi-only"
@@ -42,16 +42,16 @@
"atest_tradefed.sh template/atest_local_min --template:map test=atest --atest-log-file-path=/tmp/atest_run_1568627341_v33kdA/log --include-filter aidegen_unittests --log-level WARN"
],
"HelloWorldTests": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter HelloWorldTests --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter HelloWorldTests --log-level WARN --skip-loading-config-jar --logcat-on-failure"
],
"CtsSampleDeviceTestCases:SampleDeviceTest#testSharedPreferences": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsSampleDeviceTestCases --atest-include-filter CtsSampleDeviceTestCases:android.sample.cts.SampleDeviceTest#testSharedPreferences --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsSampleDeviceTestCases --atest-include-filter CtsSampleDeviceTestCases:android.sample.cts.SampleDeviceTest#testSharedPreferences --log-level WARN --skip-loading-config-jar --logcat-on-failure"
],
"CtsSampleDeviceTestCases:android.sample.cts": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsSampleDeviceTestCases --atest-include-filter CtsSampleDeviceTestCases:android.sample.cts --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter CtsSampleDeviceTestCases --atest-include-filter CtsSampleDeviceTestCases:android.sample.cts --log-level WARN --skip-loading-config-jar --logcat-on-failure"
],
"PacketFragmenterTest#test_no_fragment_necessary,test_ble_fragment_necessary": [
-"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter net_test_hci --atest-include-filter net_test_hci:PacketFragmenterTest.test_ble_fragment_necessary:PacketFragmenterTest.test_no_fragment_necessary --log-level WARN"
+"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter net_test_hci --atest-include-filter net_test_hci:PacketFragmenterTest.test_ble_fragment_necessary:PacketFragmenterTest.test_no_fragment_necessary --log-level WARN --skip-loading-config-jar --logcat-on-failure"
],
"CarMessengerRoboTests": [
"./build/soong/soong_ui.bash --make-mode RunCarMessengerRoboTests"
diff --git a/common_util/com/android/tradefed/util/FileUtil.java b/common_util/com/android/tradefed/util/FileUtil.java
index 2034972..70ee95c 100644
--- a/common_util/com/android/tradefed/util/FileUtil.java
+++ b/common_util/com/android/tradefed/util/FileUtil.java
@@ -565,13 +565,30 @@
* @throws FileNotFoundException
*/
public static String readStringFromFile(File sourceFile) throws IOException {
- FileInputStream is = null;
- try {
- // no need to buffer since StreamUtil does
- is = new FileInputStream(sourceFile);
- return StreamUtil.getStringFromStream(is);
- } finally {
- StreamUtil.close(is);
+ return readStringFromFile(sourceFile, 0, 0);
+ }
+
+ /**
+ * A helper method for reading partial string data from a file
+ *
+ * @param sourceFile the file to read from
+ * @param startOffset the start offset to read from the file.
+ * @param length the number of bytes to read of the file.
+ * @throws IOException
+ * @throws FileNotFoundException
+ */
+ public static String readStringFromFile(File sourceFile, long startOffset, long length)
+ throws IOException {
+ try (FileInputStream is = new FileInputStream(sourceFile)) {
+ if (startOffset < 0) {
+ startOffset = 0;
+ }
+ long fileLength = sourceFile.length();
+ is.skip(startOffset);
+ if (length <= 0 || fileLength <= startOffset + length) {
+ return StreamUtil.getStringFromStream(is);
+ }
+ return StreamUtil.getStringFromStream(is, length);
}
}
diff --git a/common_util/com/android/tradefed/util/StreamUtil.java b/common_util/com/android/tradefed/util/StreamUtil.java
index f6fce21..a8d84bb 100644
--- a/common_util/com/android/tradefed/util/StreamUtil.java
+++ b/common_util/com/android/tradefed/util/StreamUtil.java
@@ -114,11 +114,28 @@
* @throws IOException if failure occurred reading the stream
*/
public static String getStringFromStream(InputStream stream) throws IOException {
+ return getStringFromStream(stream, 0);
+ }
+
+ /**
+ * Retrieves a {@link String} from a character stream.
+ *
+ * @param stream the {@link InputStream}
+ * @param length the size of the content to read, set to 0 to read all contents
+ * @return a {@link String} containing the stream contents
+ * @throws IOException if failure occurred reading the stream
+ */
+ public static String getStringFromStream(InputStream stream, long length) throws IOException {
int irChar = -1;
StringBuilder builder = new StringBuilder();
try (Reader ir = new BufferedReader(new InputStreamReader(stream))) {
+ long count = 0;
while ((irChar = ir.read()) != -1) {
builder.append((char) irChar);
+ count++;
+ if (length > 0 && count >= length) {
+ break;
+ }
}
}
return builder.toString();
diff --git a/device_build_interfaces/com/android/tradefed/device/contentprovider/ContentProviderHandler.java b/device_build_interfaces/com/android/tradefed/device/contentprovider/ContentProviderHandler.java
index b6babf9..33420fc 100644
--- a/device_build_interfaces/com/android/tradefed/device/contentprovider/ContentProviderHandler.java
+++ b/device_build_interfaces/com/android/tradefed/device/contentprovider/ContentProviderHandler.java
@@ -312,12 +312,17 @@
return true;
}
+ CLog.d("Received from content provider:\n%s", listCommandResult);
String[] listResult = listCommandResult.split("[\\r\\n]+");
for (String row : listResult) {
HashMap<String, String> columnValues = parseQueryResultRow(row);
boolean isDirectory = Boolean.valueOf(columnValues.get(COLUMN_DIRECTORY));
String name = columnValues.get(COLUMN_NAME);
+ if (name == null) {
+ CLog.w("Output from the content provider doesn't seem well formatted:\n%s", row);
+ return false;
+ }
String path = columnValues.get(COLUMN_ABSOLUTE_PATH);
File localChild = new File(localDir, name);
diff --git a/res/config/atest.xml b/res/config/atest.xml
index e2afcc6..2dbfed5 100644
--- a/res/config/atest.xml
+++ b/res/config/atest.xml
@@ -14,5 +14,9 @@
limitations under the License.
-->
<configuration description="Points to the TF entry point runner for atest.">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="force-root" value="false" />
+ </target_preparer>
+
<test class="com.android.tradefed.testtype.suite.AtestRunner" />
</configuration>
diff --git a/res/config/suite/test_mapping_suite.xml b/res/config/suite/test_mapping_suite.xml
index e22fb7d..823f00f 100644
--- a/res/config/suite/test_mapping_suite.xml
+++ b/res/config/suite/test_mapping_suite.xml
@@ -20,6 +20,10 @@
<system_checker class="com.android.tradefed.suite.checker.SystemServerStatusChecker" />
<system_checker class="com.android.tradefed.suite.checker.SystemServerFileDescriptorChecker" />
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="force-root" value="false" />
+ </target_preparer>
+
<test class="com.android.tradefed.testtype.suite.TestMappingSuiteRunner"/>
<!-- Tell all AndroidJUnitTests to exclude certain annotations -->
diff --git a/src/com/android/tradefed/command/CommandOptions.java b/src/com/android/tradefed/command/CommandOptions.java
index ee4daff..c5c6a33 100644
--- a/src/com/android/tradefed/command/CommandOptions.java
+++ b/src/com/android/tradefed/command/CommandOptions.java
@@ -22,7 +22,6 @@
import com.android.tradefed.config.OptionUpdateRule;
import com.android.tradefed.device.metric.AutoLogCollector;
import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.retry.RetryStrategy;
import com.android.tradefed.util.UniqueMultiMap;
import java.util.LinkedHashSet;
@@ -208,33 +207,6 @@
)
private String mHostLogSuffix = null;
- // [Options related to auto-retry]
- @Deprecated
- @Option(
- name = "max-testcase-run-count",
- description =
- "If the IRemoteTest can have its testcases run multiple times, "
- + "the max number of runs for each testcase."
- )
- private int mMaxRunLimit = 1;
-
- @Deprecated
- @Option(
- name = "retry-strategy",
- description =
- "The retry strategy to be used when re-running some tests with "
- + "--max-testcase-run-count"
- )
- private RetryStrategy mRetryStrategy = RetryStrategy.NO_RETRY;
-
- @Deprecated
- @Option(
- name = "auto-retry",
- description =
- "Whether or not to enable the new auto-retry. This is a feature flag for testing."
- )
- private boolean mEnableAutoRetry = false;
-
/**
* Set the help mode for the config.
* <p/>
diff --git a/src/com/android/tradefed/device/metric/FilePullerDeviceMetricCollector.java b/src/com/android/tradefed/device/metric/FilePullerDeviceMetricCollector.java
index 96b5f34..5fe4e1e 100644
--- a/src/com/android/tradefed/device/metric/FilePullerDeviceMetricCollector.java
+++ b/src/com/android/tradefed/device/metric/FilePullerDeviceMetricCollector.java
@@ -137,6 +137,7 @@
}
+
private Entry<String, File> pullMetricFile(
String pattern, final Map<String, String> currentMetrics) {
Pattern p = Pattern.compile(pattern);
@@ -148,7 +149,7 @@
continue;
}
try {
- File attemptPull = device.pullFile(entry.getValue());
+ File attemptPull = retrieveFile(device, entry.getValue());
if (attemptPull != null) {
if (mCleanUp) {
device.deleteFile(entry.getValue());
@@ -171,6 +172,19 @@
}
/**
+ * Pull the file from the specified path in the device.
+ *
+ * @param device which has the file.
+ * @param remoteFilePath location in the device.
+ * @return File retrieved from the given path in the device.
+ * @throws DeviceNotAvailableException
+ */
+ protected File retrieveFile(ITestDevice device, String remoteFilePath)
+ throws DeviceNotAvailableException {
+ return device.pullFile(remoteFilePath);
+ }
+
+ /**
* Pulls the directory and all its content from the device and save it in the
* host under the host_tmp folder.
*
diff --git a/src/com/android/tradefed/invoker/TestInvocation.java b/src/com/android/tradefed/invoker/TestInvocation.java
index 36a1564..2bc4e4e 100644
--- a/src/com/android/tradefed/invoker/TestInvocation.java
+++ b/src/com/android/tradefed/invoker/TestInvocation.java
@@ -214,7 +214,8 @@
ITestInvocationListener listener,
boolean devicePreSetupDone)
throws Throwable {
-
+ ReportHostLog reportThread = new ReportHostLog(listener, config);
+ Runtime.getRuntime().addShutdownHook(reportThread);
boolean resumed = false;
String bugreportName = null;
long startTime = System.currentTimeMillis();
@@ -357,6 +358,8 @@
PrettyPrintDelimiter.printStageDelimiter(message);
}
reportHostLog(listener, config);
+ // If host_log is reported, remove the hook
+ Runtime.getRuntime().removeShutdownHook(reportThread);
elapsedTime = System.currentTimeMillis() - startTime;
if (!resumed) {
@@ -966,4 +969,22 @@
}
return testTag;
}
+
+ /** Helper Thread that ensures host_log is reported in case of killed JVM */
+ private class ReportHostLog extends Thread {
+
+ private ITestInvocationListener mListener;
+ private IConfiguration mConfiguration;
+
+ public ReportHostLog(ITestInvocationListener listener, IConfiguration config) {
+ mListener = listener;
+ mConfiguration = config;
+ }
+
+ @Override
+ public void run() {
+ // Report all the logs that always be reported anyway.
+ reportHostLog(mListener, mConfiguration);
+ }
+ }
}
diff --git a/src/com/android/tradefed/result/suite/SuiteResultHolder.java b/src/com/android/tradefed/result/suite/SuiteResultHolder.java
index 63b80e2..8c6f1dc 100644
--- a/src/com/android/tradefed/result/suite/SuiteResultHolder.java
+++ b/src/com/android/tradefed/result/suite/SuiteResultHolder.java
@@ -38,4 +38,5 @@
public long failedTests;
public long startTime;
public long endTime;
+ public String hostName;
}
diff --git a/src/com/android/tradefed/result/suite/XmlSuiteResultFormatter.java b/src/com/android/tradefed/result/suite/XmlSuiteResultFormatter.java
index 1aa9997..67df6bf 100644
--- a/src/com/android/tradefed/result/suite/XmlSuiteResultFormatter.java
+++ b/src/com/android/tradefed/result/suite/XmlSuiteResultFormatter.java
@@ -127,6 +127,10 @@
public static final class RunHistory {
public long startTime;
public long endTime;
+ public long passedTests;
+ public long failedTests;
+ public String commandLineArgs;
+ public String hostName;
}
/**
@@ -256,6 +260,10 @@
serializer.startTag(NS, RUN_TAG);
serializer.attribute(NS, START_TIME_ATTR, String.valueOf(runHistory.startTime));
serializer.attribute(NS, END_TIME_ATTR, String.valueOf(runHistory.endTime));
+ serializer.attribute(NS, PASS_ATTR, Long.toString(runHistory.passedTests));
+ serializer.attribute(NS, FAILED_ATTR, Long.toString(runHistory.failedTests));
+ serializer.attribute(NS, COMMAND_LINE_ARGS, runHistory.commandLineArgs);
+ serializer.attribute(NS, HOST_NAME_ATTR, runHistory.hostName);
serializer.endTag(NS, RUN_TAG);
}
serializer.endTag(NS, RUN_HISTORY_TAG);
@@ -458,6 +466,7 @@
parser.require(XmlPullParser.START_TAG, NS, RESULT_TAG);
invocation.startTime = (Long.valueOf(parser.getAttributeValue(NS, START_TIME_ATTR)));
invocation.endTime = (Long.valueOf(parser.getAttributeValue(NS, END_TIME_ATTR)));
+ invocation.hostName = parser.getAttributeValue(NS, HOST_NAME_ATTR);
context.addInvocationAttribute(
COMMAND_LINE_ARGS, parser.getAttributeValue(NS, COMMAND_LINE_ARGS));
parseSuiteAttributes(parser, context);
diff --git a/src/com/android/tradefed/retry/BaseRetryDecision.java b/src/com/android/tradefed/retry/BaseRetryDecision.java
index c0187bc..7513ab5 100644
--- a/src/com/android/tradefed/retry/BaseRetryDecision.java
+++ b/src/com/android/tradefed/retry/BaseRetryDecision.java
@@ -39,6 +39,8 @@
*/
public class BaseRetryDecision implements IRetryDecision {
+ private static final int ABORT_MAX_FAILURES = 50;
+
@Option(
name = "reboot-at-last-retry",
description = "Reboot the device at the last retry attempt."
@@ -195,7 +197,16 @@
Set<TestDescription> previousFailedTests = getFailedTestCases(previousResults);
if (!mPreviouslyFailing.isEmpty()) {
previousFailedTests.retainAll(mPreviouslyFailing);
+ mPreviouslyFailing.retainAll(previousFailedTests);
}
+ // Abort if number of failures is high for a given one test
+ if (previousFailedTests.size() > ABORT_MAX_FAILURES) {
+ CLog.d(
+ "Found %s failures, skipping auto-retry to avoid large overhead.",
+ previousFailedTests.size());
+ return false;
+ }
+
if (!previousFailedTests.isEmpty()) {
CLog.d("Retrying the test case failure.");
addRetriedTestsToIncludeFilters(test, previousFailedTests);
diff --git a/src/com/android/tradefed/testtype/SubprocessTfLauncher.java b/src/com/android/tradefed/testtype/SubprocessTfLauncher.java
index b64a017..80dd8f6 100644
--- a/src/com/android/tradefed/testtype/SubprocessTfLauncher.java
+++ b/src/com/android/tradefed/testtype/SubprocessTfLauncher.java
@@ -142,7 +142,10 @@
"google-tradefed.jar",
"google-tradefed-tests.jar",
// Google contrib jars
- "google-tradefed-contrib.jar"));
+ "google-tradefed-contrib.jar",
+ // Older jar required for coverage tests
+ "jack-jacoco-reporter.jar",
+ "emmalib.jar"));
/** Timeout to wait for the events received from subprocess to finish being processed.*/
private static final long EVENT_THREAD_JOIN_TIMEOUT_MS = 30 * 1000;
diff --git a/src/com/android/tradefed/testtype/suite/BaseTestSuite.java b/src/com/android/tradefed/testtype/suite/BaseTestSuite.java
index b283546..f43c7af 100644
--- a/src/com/android/tradefed/testtype/suite/BaseTestSuite.java
+++ b/src/com/android/tradefed/testtype/suite/BaseTestSuite.java
@@ -184,6 +184,7 @@
private SuiteModuleLoader mModuleRepo;
private Map<String, List<SuiteTestFilter>> mIncludeFiltersParsed = new HashMap<>();
private Map<String, List<SuiteTestFilter>> mExcludeFiltersParsed = new HashMap<>();
+ private List<File> mConfigPaths = new ArrayList<>();
/** {@inheritDoc} */
@Override
@@ -304,6 +305,12 @@
public LinkedHashMap<String, IConfiguration> loadingStrategy(
Set<IAbi> abis, List<File> testsDirs, String suitePrefix, String suiteTag) {
LinkedHashMap<String, IConfiguration> loadedConfigs = new LinkedHashMap<>();
+ // Load and return directly the specific config files.
+ if (!mConfigPaths.isEmpty()) {
+ CLog.d("Loading the specified configs and skip loading from the resources.");
+ return getModuleLoader().loadConfigsFromSpecifiedPaths(mConfigPaths, abis, suiteTag);
+ }
+
// Load configs that are part of the resources
if (!mSkipJarLoading) {
loadedConfigs.putAll(
@@ -360,6 +367,11 @@
mModuleArgs.addAll(moduleArgs);
}
+ /** Clear the stored module args out */
+ void clearModuleArgs() {
+ mModuleArgs.clear();
+ }
+
/** Add config patterns */
public void addConfigPatterns(List<String> patterns) {
mConfigPatterns.addAll(patterns);
@@ -463,6 +475,21 @@
mExcludeFiltersParsed.clear();
}
+ /**
+ * Add the config path for {@link SuiteModuleLoader} to limit the search loading
+ * configurations.
+ *
+ * @param configPath A {@code File} with the absolute path of the configuration.
+ */
+ void addConfigPaths(File configPath) {
+ mConfigPaths.add(configPath);
+ }
+
+ /** Clear the stored config paths out. */
+ void clearConfigPaths() {
+ mConfigPaths.clear();
+ }
+
/* Helper method designed to remove filters in a list not applicable to the given module */
private static void checkFilters(Set<String> filters, String moduleName) {
Set<String> cleanedFilters = new HashSet<String>();
diff --git a/src/com/android/tradefed/testtype/suite/ITestSuite.java b/src/com/android/tradefed/testtype/suite/ITestSuite.java
index f5a20ac..7ff6fab 100644
--- a/src/com/android/tradefed/testtype/suite/ITestSuite.java
+++ b/src/com/android/tradefed/testtype/suite/ITestSuite.java
@@ -337,6 +337,7 @@
// Current modules to run, null if not started to run yet.
private List<ModuleDefinition> mRunModules = null;
+ private ModuleDefinition mModuleInProgress = null;
// Logger to be used to files.
private ITestLogger mCurrentLogger = null;
// Whether or not we are currently in split
@@ -656,6 +657,7 @@
.addDeviceBuildInfo(deviceName, mContext.getBuildInfo(deviceName));
}
listener.testModuleStarted(module.getModuleInvocationContext());
+ mModuleInProgress = module;
// Trigger module start on module level listener too
new ResultForwarder(moduleListeners)
.testModuleStarted(module.getModuleInvocationContext());
@@ -667,6 +669,7 @@
// clear out module invocation context since we are now done with module
// execution
listener.testModuleEnded();
+ mModuleInProgress = null;
}
// Module isolation routine
moduleIsolation(mContext, listener);
@@ -1113,6 +1116,16 @@
runModules = createExecutionList();
}
+ if (mModuleInProgress != null) {
+ // TODO: Ensure in-progress data make sense
+ String inProgressMessage =
+ String.format(
+ "Module %s was interrupted after starting. Results might not be "
+ + "accurate or complete.",
+ mModuleInProgress.getId());
+ mModuleInProgress.reportNotExecuted(listener, inProgressMessage);
+ }
+
while (!runModules.isEmpty()) {
ModuleDefinition module = runModules.remove(0);
module.reportNotExecuted(listener, message);
@@ -1376,4 +1389,9 @@
void disableAutoRetryTimeReporting() {
mDisableAutoRetryTimeReporting = true;
}
+
+ @VisibleForTesting
+ void setModuleInProgress(ModuleDefinition moduleInProgress) {
+ mModuleInProgress = moduleInProgress;
+ }
}
diff --git a/src/com/android/tradefed/testtype/suite/ModuleDefinition.java b/src/com/android/tradefed/testtype/suite/ModuleDefinition.java
index 385e06a..f80e2d1 100644
--- a/src/com/android/tradefed/testtype/suite/ModuleDefinition.java
+++ b/src/com/android/tradefed/testtype/suite/ModuleDefinition.java
@@ -136,6 +136,7 @@
private long mElapsedTearDown = 0l;
private long mStartTestTime = 0l;
+ private Long mStartModuleRunDate = null;
// Tracking of retry performance
private List<RetryStatistics> mRetryStats = new ArrayList<>();
@@ -326,6 +327,7 @@
TestFailureListener failureListener,
int maxRunLimit)
throws DeviceNotAvailableException {
+ mStartModuleRunDate = System.currentTimeMillis();
// Load extra configuration for the module from module_controller
// TODO: make module_controller a full TF object
boolean skipTestCases = false;
@@ -929,7 +931,9 @@
/** Report completely not executed modules. */
public final void reportNotExecuted(ITestInvocationListener listener, String message) {
- listener.testModuleStarted(getModuleInvocationContext());
+ if (mStartModuleRunDate == null) {
+ listener.testModuleStarted(getModuleInvocationContext());
+ }
listener.testRunStarted(getId(), 0, 0, System.currentTimeMillis());
listener.testRunFailed(message);
listener.testRunEnded(0, new HashMap<String, Metric>());
diff --git a/src/com/android/tradefed/testtype/suite/SuiteModuleLoader.java b/src/com/android/tradefed/testtype/suite/SuiteModuleLoader.java
index 08dd626..1884504 100644
--- a/src/com/android/tradefed/testtype/suite/SuiteModuleLoader.java
+++ b/src/com/android/tradefed/testtype/suite/SuiteModuleLoader.java
@@ -117,6 +117,20 @@
mExcludedModuleParameters = excludedParams;
}
+ /** Main loading of configurations, looking into the specified files */
+ public LinkedHashMap<String, IConfiguration> loadConfigsFromSpecifiedPaths(
+ List<File> listConfigFiles,
+ Set<IAbi> abis,
+ String suiteTag) {
+ LinkedHashMap<String, IConfiguration> toRun = new LinkedHashMap<>();
+ for (File configFile : listConfigFiles) {
+ toRun.putAll(
+ loadOneConfig(
+ configFile.getName(), configFile.getAbsolutePath(), abis, suiteTag));
+ }
+ return toRun;
+ }
+
/** Main loading of configurations, looking into a folder */
public LinkedHashMap<String, IConfiguration> loadConfigsFromDirectory(
List<File> testsDirs,
@@ -130,11 +144,7 @@
ConfigurationUtil.getConfigNamesFileFromDirs(suitePrefix, testsDirs, patterns));
// Ensure stable initial order of configurations.
Collections.sort(listConfigFiles);
- for (File configFile : listConfigFiles) {
- toRun.putAll(
- loadOneConfig(
- configFile.getName(), configFile.getAbsolutePath(), abis, suiteTag));
- }
+ toRun.putAll(loadConfigsFromSpecifiedPaths(listConfigFiles, abis, suiteTag));
return toRun;
}
diff --git a/src/com/android/tradefed/testtype/suite/TestMappingSuiteRunner.java b/src/com/android/tradefed/testtype/suite/TestMappingSuiteRunner.java
index b13ffa7..89d4545 100644
--- a/src/com/android/tradefed/testtype/suite/TestMappingSuiteRunner.java
+++ b/src/com/android/tradefed/testtype/suite/TestMappingSuiteRunner.java
@@ -19,12 +19,14 @@
import com.android.tradefed.config.IConfiguration;
import com.android.tradefed.config.Option;
import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.IRemoteTest;
import com.android.tradefed.util.testmapping.TestInfo;
import com.android.tradefed.util.testmapping.TestMapping;
import com.android.tradefed.util.testmapping.TestOption;
+import com.google.common.annotations.VisibleForTesting;
+import java.io.File;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@@ -89,10 +91,10 @@
*/
@Override
public LinkedHashMap<String, IConfiguration> loadTests() {
- // Map between test names and a list of test sources for each test.
- Map<String, List<String>> testsInTestMapping = new HashMap<>();
-
Set<String> includeFilter = getIncludeFilter();
+ // Name of the tests
+ Set<String> testNames = new HashSet<>();
+ Set<TestInfo> testInfosToRun = new HashSet<>();
if (mTestGroup == null && includeFilter.isEmpty()) {
throw new RuntimeException(
"At least one of the options, --test-mapping-test-group or --include-filter, "
@@ -114,96 +116,184 @@
}
if (mTestGroup != null) {
- Set<TestInfo> testsToRun =
+ testInfosToRun =
TestMapping.getTests(
getBuildInfo(), mTestGroup, getPrioritizeHostConfig(), mKeywords);
if (!mTestModulesForced.isEmpty()) {
CLog.i("Filtering tests for the given names: %s", mTestModulesForced);
- testsToRun =
- testsToRun
+ testInfosToRun =
+ testInfosToRun
.stream()
.filter(testInfo -> mTestModulesForced.contains(testInfo.getName()))
.collect(Collectors.toSet());
}
- if (testsToRun.isEmpty()) {
+ if (testInfosToRun.isEmpty()) {
throw new RuntimeException(
String.format("No test found for the given group: %s.", mTestGroup));
}
-
- // Name of the tests
- Set<String> testNames = new HashSet<>();
-
- Set<String> mappingIncludeFilters = new HashSet<>();
- Set<String> mappingExcludeFilters = new HashSet<>();
-
- // module-arg options compiled from test options for each test.
- Set<String> moduleArgs = new HashSet<>();
- for (TestInfo test : testsToRun) {
- boolean hasIncludeFilters = false;
- for (TestOption option : test.getOptions()) {
- switch (option.getName()) {
- // Handle include and exclude filter at the suite level to hide each
- // test runner specific implementation and option names related to filtering
- case TEST_MAPPING_INCLUDE_FILTER:
- hasIncludeFilters = true;
- mappingIncludeFilters.add(
- String.format("%s %s", test.getName(), option.getValue()));
- break;
- case TEST_MAPPING_EXCLUDE_FILTER:
- mappingExcludeFilters.add(
- String.format("%s %s", test.getName(), option.getValue()));
- break;
- default:
- String moduleArg =
- String.format("%s:%s", test.getName(), option.getName());
- if (option.getValue() != null && !option.getValue().isEmpty()) {
- moduleArg = String.format("%s:%s", moduleArg, option.getValue());
- }
- moduleArgs.add(moduleArg);
- break;
- }
- }
- if (!hasIncludeFilters) {
- testNames.add(test.getName());
- }
+ for (TestInfo testInfo : testInfosToRun) {
+ testNames.add(testInfo.getName());
}
-
- if (mappingIncludeFilters.isEmpty()) {
- setIncludeFilter(testNames);
- } else {
- mappingIncludeFilters.addAll(testNames);
- setIncludeFilter(mappingIncludeFilters);
- }
- if (!mappingExcludeFilters.isEmpty()) {
- setExcludeFilter(mappingExcludeFilters);
- }
- addModuleArgs(moduleArgs);
-
- for (TestInfo test : testsToRun) {
- List<String> testSources = null;
- // TODO(b/117880789): tests may not be grouped by name once that bug is fixed.
- // Update the dictionary with better keys.
- if (testsInTestMapping.containsKey(test.getName())) {
- testSources = testsInTestMapping.get(test.toString());
- } else {
- testSources = new ArrayList<String>();
- testsInTestMapping.put(test.getName(), testSources);
- }
- testSources.addAll(test.getSources());
- }
+ setIncludeFilter(testNames);
}
+ // load all the configurations with include-filter injected.
LinkedHashMap<String, IConfiguration> testConfigs = super.loadTests();
+
+ // Create and inject individual tests by calling super.loadTests() with each test info.
for (Map.Entry<String, IConfiguration> entry : testConfigs.entrySet()) {
+ List<IRemoteTest> allTests = new ArrayList<>();
+ IConfiguration moduleConfig = entry.getValue();
ConfigurationDescriptor configDescriptor =
- entry.getValue().getConfigurationDescription();
- if (testsInTestMapping.containsKey(configDescriptor.getModuleName())) {
- configDescriptor.addMetaData(
- TestMapping.TEST_SOURCES,
- testsInTestMapping.get(configDescriptor.getModuleName()));
+ moduleConfig.getConfigurationDescription();
+ String moduleName = configDescriptor.getModuleName();
+ String configPath = moduleConfig.getName();
+ Set<TestInfo> testInfos = getTestInfos(testInfosToRun, moduleName);
+ allTests.addAll(
+ createIndividualTests(
+ testInfos, configPath));
+ if (!allTests.isEmpty()) {
+ // Set back to IConfiguration only if IRemoteTests are created.
+ moduleConfig.setTests(allTests);
+ // Set test sources to ConfigurationDescriptor.
+ List<String> testSources = getTestSources(testInfos);
+ configDescriptor.addMetaData(TestMapping.TEST_SOURCES, testSources);
+ }
+ }
+ return testConfigs;
+ }
+
+ /**
+ * Create individual tests with test infos for a module.
+ *
+ * @param testInfos A {@code Set<TestInfo>} containing multiple test options.
+ * @param configPath A {@code String} of configuration path.
+ * @return The {@link List<IRemoteTest>} that are injected with the test options.
+ */
+ @VisibleForTesting
+ List<IRemoteTest> createIndividualTests(Set<TestInfo> testInfos, String configPath) {
+ List<IRemoteTest> tests = new ArrayList<>();
+ if (configPath == null) {
+ throw new RuntimeException(String.format("Configuration path is null."));
+ }
+ File configFie = new File(configPath);
+ if (!configFie.exists()) {
+ throw new RuntimeException(
+ String.format("Configuration path: %s doesn't exits.", configPath));
+ }
+ // De-duplicate test infos so that there won't be duplicate test options.
+ testInfos = dedupTestInfos(testInfos);
+ for (TestInfo testInfo : testInfos) {
+ // Clean up all the test options injected in SuiteModuleLoader.
+ super.cleanUpSuiteSetup();
+ super.clearModuleArgs();
+ clearConfigPaths();
+ // Set config path to BaseTestSuite to limit the search.
+ addConfigPaths(configFie);
+ // Inject the test options from each test info to SuiteModuleLoader.
+ parseOptions(testInfo);
+ LinkedHashMap<String, IConfiguration> config = super.loadTests();
+ for (Map.Entry<String, IConfiguration> entry : config.entrySet()) {
+ tests.addAll(entry.getValue().getTests());
+ }
+ }
+ return tests;
+ }
+
+ /**
+ * Get a list of path of TEST_MAPPING for a module.
+ *
+ * @param testInfos A {@code Set<TestInfo>} containing multiple test options.
+ * @return A {@code List<String>} of TEST_MAPPING path.
+ */
+ @VisibleForTesting
+ List<String> getTestSources(Set<TestInfo> testInfos) {
+ List<String> testSources = new ArrayList<>();
+ for (TestInfo testInfo : testInfos) {
+ testSources.addAll(testInfo.getSources());
+ }
+ return testSources;
+ }
+
+ /**
+ * Parse the test options for the test info.
+ *
+ * @param testInfo A {@code Set<TestInfo>} containing multiple test options.
+ */
+ @VisibleForTesting
+ void parseOptions(TestInfo testInfo) {
+ Set<String> mappingIncludeFilters = new HashSet<>();
+ Set<String> mappingExcludeFilters = new HashSet<>();
+ // module-arg options compiled from test options for each test.
+ Set<String> moduleArgs = new HashSet<>();
+ Set<String> testNames = new HashSet<>();
+ for (TestOption option : testInfo.getOptions()) {
+ switch (option.getName()) {
+ // Handle include and exclude filter at the suite level to hide each
+ // test runner specific implementation and option names related to filtering
+ case TEST_MAPPING_INCLUDE_FILTER:
+ mappingIncludeFilters.add(
+ String.format("%s %s", testInfo.getName(), option.getValue()));
+ break;
+ case TEST_MAPPING_EXCLUDE_FILTER:
+ mappingExcludeFilters.add(
+ String.format("%s %s", testInfo.getName(), option.getValue()));
+ break;
+ default:
+ String moduleArg =
+ String.format("%s:%s", testInfo.getName(), option.getName());
+ if (option.getValue() != null && !option.getValue().isEmpty()) {
+ moduleArg = String.format("%s:%s", moduleArg, option.getValue());
+ }
+ moduleArgs.add(moduleArg);
+ break;
}
}
- return testConfigs;
+ if (mappingIncludeFilters.isEmpty()) {
+ testNames.add(testInfo.getName());
+ setIncludeFilter(testNames);
+ } else {
+ setIncludeFilter(mappingIncludeFilters);
+ }
+ if (!mappingExcludeFilters.isEmpty()) {
+ setExcludeFilter(mappingExcludeFilters);
+ }
+ addModuleArgs(moduleArgs);
+ }
+
+ /**
+ * De-duplicate test infos with the same test options.
+ *
+ * @param testInfos A {@code Set<TestInfo>} containing multiple test options.
+ * @return A {@code Set<TestInfo>} of tests without duplicated test options.
+ */
+ @VisibleForTesting
+ Set<TestInfo> dedupTestInfos(Set<TestInfo> testInfos) {
+ Set<String> nameOptions = new HashSet<>();
+ Set<TestInfo> dedupTestInfos = new HashSet<>();
+ for (TestInfo testInfo : testInfos) {
+ String nameOption = testInfo.getName() + testInfo.getOptions().toString();
+ if (!nameOptions.contains(nameOption)) {
+ dedupTestInfos.add(testInfo);
+ nameOptions.add(nameOption);
+ }
+ }
+ return dedupTestInfos;
+ }
+
+ /**
+ * Get the test infos for the given module name.
+ *
+ * @param testInfos A {@code Set<TestInfo>} containing multiple test options.
+ * @param moduleName A {@code String} name of a test module.
+ * @return A {@code Set<TestInfo>} of tests for a module.
+ */
+ @VisibleForTesting
+ Set<TestInfo> getTestInfos(Set<TestInfo> testInfos, String moduleName) {
+ return testInfos
+ .stream()
+ .filter(testInfo -> moduleName.equals(testInfo.getName()))
+ .collect(Collectors.toSet());
}
}
diff --git a/src/com/android/tradefed/util/JavaCodeCoverageFlusher.java b/src/com/android/tradefed/util/JavaCodeCoverageFlusher.java
index 9586e6e..5377163 100644
--- a/src/com/android/tradefed/util/JavaCodeCoverageFlusher.java
+++ b/src/com/android/tradefed/util/JavaCodeCoverageFlusher.java
@@ -32,7 +32,7 @@
* A utility class that resets and forces a flush of Java code coverage measurements from processes
* running on the device.
*/
-public final class JavaCodeCoverageFlusher {
+public class JavaCodeCoverageFlusher {
private static final String COVERAGE_RESET_FORMAT =
"am attach-agent %s /system/lib/libdumpcoverage.so=reset";
diff --git a/src/com/android/tradefed/util/NativeCodeCoverageFlusher.java b/src/com/android/tradefed/util/NativeCodeCoverageFlusher.java
index b53542a..7c0ae3b 100644
--- a/src/com/android/tradefed/util/NativeCodeCoverageFlusher.java
+++ b/src/com/android/tradefed/util/NativeCodeCoverageFlusher.java
@@ -35,18 +35,21 @@
private static final String CLEAR_NATIVE_COVERAGE_FILES = "rm -rf /data/misc/trace/*";
private final ITestDevice mDevice;
+ private final List<String> mProcessNames;
- public NativeCodeCoverageFlusher(ITestDevice device) {
+ public NativeCodeCoverageFlusher(ITestDevice device, List<String> processNames) {
mDevice = device;
+ mProcessNames = processNames;
}
/**
- * Clears coverage measurements from disk on the device. Device must be in adb root.
+ * Resets native coverage counters for processes running on the device and clears any existing
+ * coverage measurements from disk. Device must be in adb root.
*
* @throws DeviceNotAvailableException
*/
- public void clearCoverageMeasurements() throws DeviceNotAvailableException {
- checkState(mDevice.isAdbRoot(), "adb root is required to clear coverage files.");
+ public void resetCoverage() throws DeviceNotAvailableException {
+ forceCoverageFlush();
mDevice.executeShellCommand(CLEAR_NATIVE_COVERAGE_FILES);
}
@@ -54,20 +57,18 @@
* Forces a flush of native coverage data from processes running on the device. Device must be
* in adb root.
*
- * @param processNames the name of processes to target for flushing; if empty, flushes from all
- * running native processes on the device.
* @throws DeviceNotAvailableException
*/
- public void forceCoverageFlush(List<String> processNames) throws DeviceNotAvailableException {
+ public void forceCoverageFlush() throws DeviceNotAvailableException {
checkState(mDevice.isAdbRoot(), "adb root is required to flush native coverage data.");
- if ((processNames == null) || processNames.isEmpty()) {
+ if ((mProcessNames == null) || mProcessNames.isEmpty()) {
// Use the special pid -1 to trigger a coverage flush of all running processes.
mDevice.executeShellCommand(String.format(COVERAGE_FLUSH_COMMAND_FORMAT, "-1"));
} else {
// Look up the pid of the processes to send them the coverage flush signal.
StringJoiner pidString = new StringJoiner(" ");
- for (String processName : processNames) {
+ for (String processName : mProcessNames) {
String pid = mDevice.getProcessPid(processName);
if (pid == null) {
CLog.w("Did not find pid for process \"%s\".", processName);
diff --git a/src/com/android/tradefed/util/SubprocessEventHelper.java b/src/com/android/tradefed/util/SubprocessEventHelper.java
index 50028d9..be182b1 100644
--- a/src/com/android/tradefed/util/SubprocessEventHelper.java
+++ b/src/com/android/tradefed/util/SubprocessEventHelper.java
@@ -546,7 +546,7 @@
mModuleContext
.getAttributes()
.getUniqueMap()
- .get(ModuleDefinition.MODULE_NAME);
+ .get(ModuleDefinition.MODULE_ID);
if (moduleName != null) {
tags.put(MODULE_NAME, moduleName);
}
diff --git a/src/com/android/tradefed/util/testmapping/TestMapping.java b/src/com/android/tradefed/util/testmapping/TestMapping.java
index 8221d1e..1b02e29 100644
--- a/src/com/android/tradefed/util/testmapping/TestMapping.java
+++ b/src/com/android/tradefed/util/testmapping/TestMapping.java
@@ -281,7 +281,7 @@
FileUtil.recursiveDelete(testMappingsDir);
}
- return TestMapping.mergeTests(tests);
+ return tests;
}
/**
diff --git a/test_framework/com/android/tradefed/device/metric/PerfettoPullerMetricCollector.java b/test_framework/com/android/tradefed/device/metric/PerfettoPullerMetricCollector.java
index 0f03c0f..1086d9e 100644
--- a/test_framework/com/android/tradefed/device/metric/PerfettoPullerMetricCollector.java
+++ b/test_framework/com/android/tradefed/device/metric/PerfettoPullerMetricCollector.java
@@ -19,6 +19,9 @@
import com.android.annotations.VisibleForTesting;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.LargeOutputReceiver;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.metrics.proto.MetricMeasurement.DataType;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
@@ -30,10 +33,17 @@
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.Pair;
import com.android.tradefed.util.RunUtil;
+import com.android.tradefed.util.StreamUtil;
import com.google.common.base.Joiner;
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -53,15 +63,38 @@
private static final String EXTRACTOR_FAILURE = "0";
private static final String EXTRACTOR_RUNTIME = "trace_extractor_runtime";
+ @Option(name = "compress-perfetto",
+ description = "If enabled retrieves the perfetto compressed content,"
+ + "decompress for processing and upload the compressed file. If"
+ + "this flag is not enabled uncompressed version of perfetto file is"
+ + "pulled, processed and uploaded.")
+ private boolean mCompressPerfetto = false;
+
+ @Option(name = "max-compressed-file-size", description = "Max size of the compressed"
+ + " perfetto file. If the compressed file size exceeds the max then"
+ + " post processing and uploading the compressed file will not happen.")
+ private long mMaxCompressedFileSize = 10000L * 1024 * 1024;
+
+ @Option(
+ name = "compressed-trace-shell-timeout",
+ description = "Timeout for retrieving compressed trace content through shell",
+ isTimeVal = true)
+ private long mCompressedTimeoutMs = TimeUnit.MINUTES.toMillis(20);
+
+ @Option(
+ name = "decompress-perfetto-timeout",
+ description = "Timeout to decompress perfetto compressed file.",
+ isTimeVal = true)
+ private long mDecompressTimeoutMs = TimeUnit.MINUTES.toMillis(20);
+
@Option(
name = "perfetto-binary-path",
description = "Path to the script files used to analyze the trace files.")
private List<File> mScriptFiles = new ArrayList<>();
@Option(
- name = "perfetto-binary-args",
- description = "Extra arguments to be passed to the binaries."
- )
+ name = "perfetto-binary-args",
+ description = "Extra arguments to be passed to the binaries.")
private List<String> mPerfettoBinaryArgs = new ArrayList<>();
@Option(
@@ -71,24 +104,23 @@
// List of process names passed to perfetto binary.
@Option(
- name = "process-name",
- description =
- "Process names to be passed in perfetto script."
- )
+ name = "process-name",
+ description =
+ "Process names to be passed in perfetto script.")
private Collection<String> mProcessNames = new ArrayList<String>();
// Timeout for the script to process the trace files.
// The default is arbitarily chosen to be 5 mins to prevent the test spending more time in
// processing the files.
@Option(
- name = "perfetto-script-timeout",
- description = "Timeout for the perfetto script.",
- isTimeVal = true
- )
+ name = "perfetto-script-timeout",
+ description = "Timeout for the perfetto script.",
+ isTimeVal = true)
private long mScriptTimeoutMs = TimeUnit.MINUTES.toMillis(5);
/**
* Process the perfetto trace file for the additional metrics and add it to final metrics.
+ * Decompress the perfetto file for processing if the compression was enabled.
*
* @param key the option key associated to the file that was pulled from the device.
* @param metricFile the {@link File} pulled from the device matching the option key.
@@ -97,72 +129,206 @@
@Override
public void processMetricFile(String key, File metricFile,
DeviceMetricData data) {
- // Extract the metrics from the trace file.
- for (File scriptFile : mScriptFiles) {
- // Apply necessary execute permissions to the script.
- FileUtil.chmodGroupRWX(scriptFile);
+ File processSrcFile = metricFile;
+ if (mCompressPerfetto) {
+ processSrcFile = decompressFile(metricFile);
+ }
- List<String> commandArgsList = new ArrayList<String>();
- commandArgsList.add(scriptFile.getAbsolutePath());
- commandArgsList.addAll(mPerfettoBinaryArgs);
- commandArgsList.add("-trace_file");
- commandArgsList.add(metricFile.getAbsolutePath());
+ if (processSrcFile != null) {
+ // Extract the metrics from the trace file.
+ for (File scriptFile : mScriptFiles) {
+ // Apply necessary execute permissions to the script.
+ FileUtil.chmodGroupRWX(scriptFile);
- if (!mProcessNames.isEmpty()) {
- commandArgsList.add("-process_names");
- commandArgsList.add(Joiner.on(",").join(mProcessNames));
- }
+ List<String> commandArgsList = new ArrayList<String>();
+ commandArgsList.add(scriptFile.getAbsolutePath());
+ commandArgsList.addAll(mPerfettoBinaryArgs);
+ commandArgsList.add("-trace_file");
+ commandArgsList.add(processSrcFile.getAbsolutePath());
- String traceExtractorStatus = EXTRACTOR_SUCCESS;
-
- double scriptDuration = 0;
- double scriptStartTime = System.currentTimeMillis();
- CommandResult cr = runHostCommand(commandArgsList.toArray(new String[commandArgsList
- .size()]));
- scriptDuration = System.currentTimeMillis() - scriptStartTime;
-
- // Update the script duration metrics.
- Metric.Builder metricDurationBuilder = Metric.newBuilder();
- metricDurationBuilder.getMeasurementsBuilder().setSingleDouble(scriptDuration);
- data.addMetric(
- String.format("%s_%s", mMetricPrefix, EXTRACTOR_RUNTIME),
- metricDurationBuilder.setType(DataType.RAW));
-
- if (CommandStatus.SUCCESS.equals(cr.getStatus())) {
- String[] metrics = cr.getStdout().split(LINE_SEPARATOR);
- for (String metric : metrics) {
- Pair<String, String> kv = splitKeyValue(metric);
-
- if (kv != null) {
- Metric.Builder metricBuilder = Metric.newBuilder();
- metricBuilder.getMeasurementsBuilder().setSingleString(kv.second);
- data.addMetric(
- String.format("%s_%s", mMetricPrefix, kv.first),
- metricBuilder.setType(DataType.RAW));
- } else {
- CLog.e("Output %s not in the expected format.", metric);
- }
+ if (!mProcessNames.isEmpty()) {
+ commandArgsList.add("-process_names");
+ commandArgsList.add(Joiner.on(",").join(mProcessNames));
}
- CLog.i(cr.getStdout());
- } else {
- traceExtractorStatus = EXTRACTOR_FAILURE;
- CLog.e("Unable to parse the trace file %s due to %s - Status - %s ",
- metricFile.getName(), cr.getStderr(), cr.getStatus());
- }
- Metric.Builder metricStatusBuilder = Metric.newBuilder();
- metricStatusBuilder.getMeasurementsBuilder().setSingleString(traceExtractorStatus);
- data.addMetric(
- String.format("%s_%s", mMetricPrefix, EXTRACTOR_STATUS),
- metricStatusBuilder.setType(DataType.RAW));
+ String traceExtractorStatus = EXTRACTOR_SUCCESS;
+
+ double scriptDuration = 0;
+ double scriptStartTime = System.currentTimeMillis();
+ CommandResult cr = runHostCommand(mScriptTimeoutMs,
+ commandArgsList.toArray(new String[commandArgsList.size()]), null, null);
+ scriptDuration = System.currentTimeMillis() - scriptStartTime;
+
+ // Update the script duration metrics.
+ Metric.Builder metricDurationBuilder = Metric.newBuilder();
+ metricDurationBuilder.getMeasurementsBuilder().setSingleDouble(scriptDuration);
+ data.addMetric(
+ String.format("%s_%s", mMetricPrefix, EXTRACTOR_RUNTIME),
+ metricDurationBuilder.setType(DataType.RAW));
+
+ if (CommandStatus.SUCCESS.equals(cr.getStatus())) {
+ String[] metrics = cr.getStdout().split(LINE_SEPARATOR);
+ for (String metric : metrics) {
+ Pair<String, String> kv = splitKeyValue(metric);
+
+ if (kv != null) {
+ Metric.Builder metricBuilder = Metric.newBuilder();
+ metricBuilder.getMeasurementsBuilder().setSingleString(kv.second);
+ data.addMetric(
+ String.format("%s_%s", mMetricPrefix, kv.first),
+ metricBuilder.setType(DataType.RAW));
+ } else {
+ CLog.e("Output %s not in the expected format.", metric);
+ }
+ }
+ CLog.i(cr.getStdout());
+ } else {
+ traceExtractorStatus = EXTRACTOR_FAILURE;
+ CLog.e("Unable to parse the trace file %s due to %s - Status - %s ",
+ processSrcFile.getName(), cr.getStderr(), cr.getStatus());
+ }
+
+ if (mCompressPerfetto) {
+ processSrcFile.delete();
+ }
+ Metric.Builder metricStatusBuilder = Metric.newBuilder();
+ metricStatusBuilder.getMeasurementsBuilder().setSingleString(traceExtractorStatus);
+ data.addMetric(
+ String.format("%s_%s", mMetricPrefix, EXTRACTOR_STATUS),
+ metricStatusBuilder.setType(DataType.RAW));
+ }
}
// Upload and delete the host trace file.
try (InputStreamSource source = new FileInputStreamSource(metricFile, true)) {
- testLog(metricFile.getName(), LogDataType.PB, source);
+ if (mCompressPerfetto) {
+ if (processSrcFile != null) {
+ testLog(metricFile.getName(), LogDataType.GZIP, source);
+ } else {
+ metricFile.delete();
+ }
+
+ } else {
+ testLog(metricFile.getName(), LogDataType.PB, source);
+ }
}
}
+ /**
+ * Pull the file from the specified path in the device. Pull the compressed content of the
+ * perfetto file if the compress perfetto option is enabled.
+ *
+ * @param device which has the file.
+ * @param remoteFilePath location in the device.
+ * @return compressed or decompressed version of perfetto file based on mCompressPerfetto
+ * option is set or not.
+ * @throws DeviceNotAvailableException
+ */
+ @Override
+ protected File retrieveFile(ITestDevice device, String remoteFilePath)
+ throws DeviceNotAvailableException {
+ if (!mCompressPerfetto) {
+ return super.retrieveFile(device, remoteFilePath);
+ }
+ File perfettoCompressedFile = null;
+ try {
+ String filePathInDevice = remoteFilePath;
+ CLog.i("Retrieving the compressed perfetto trace content from device.");
+ LargeOutputReceiver compressedOutputReceiver = new LargeOutputReceiver(
+ "perfetto_compressed_temp",
+ device.getSerialNumber(), mMaxCompressedFileSize);
+ device.executeShellCommand(
+ String.format("gzip -c %s", filePathInDevice),
+ compressedOutputReceiver,
+ mCompressedTimeoutMs, 10000, TimeUnit.MILLISECONDS, 1);
+ compressedOutputReceiver.flush();
+ compressedOutputReceiver.cancel();
+
+ // Copy to temp file which will be used for decompression, perfetto
+ // metrics extraction and uploading the file later.
+ try (InputStreamSource largeStreamSrc = compressedOutputReceiver.getData();
+ InputStream inputStream = largeStreamSrc.createInputStream()) {
+ perfettoCompressedFile = FileUtil.createTempFile(
+ "perfetto_compressed", ".gz");
+ FileOutputStream outStream = new FileOutputStream(
+ perfettoCompressedFile);
+ byte[] buffer = new byte[4096];
+ int bytesRead = -1;
+ while ((bytesRead = inputStream.read(buffer)) > -1) {
+ outStream.write(buffer, 0, bytesRead);
+ }
+ StreamUtil.close(outStream);
+ CLog.i("Successfully copied the compressed content from device to"
+ + " host.");
+ } catch (IOException e) {
+ if (perfettoCompressedFile != null) {
+ perfettoCompressedFile.delete();
+ }
+ CLog.e("Failed to copy compressed perfetto to temporary file.");
+ CLog.e(e);
+ } finally {
+ compressedOutputReceiver.delete();
+ }
+ } catch (DeviceNotAvailableException e) {
+ CLog.e(
+ "Exception when retrieveing compressed perfetto trace file '%s' "
+ + "from %s", remoteFilePath, device.getSerialNumber());
+ CLog.e(e);
+ }
+ return perfettoCompressedFile;
+ }
+
+ /**
+ * Decompress the file to a temporary file in the host.
+ *
+ * @param compressedFile file to be decompressed.
+ * @return decompressed file used for postprocessing.
+ */
+ private File decompressFile(File compressedFile) {
+ File decompressedFile = null;
+ try {
+ decompressedFile = FileUtil.createTempFile("perfetto_decompressed", ".pb");
+ } catch (IOException e) {
+ CLog.e("Not able to create decompressed perfetto file.");
+ CLog.e(e);
+ return null;
+ }
+ // Keep the original file for uploading.
+ List<String> decompressArgsList = new ArrayList<String>();
+ decompressArgsList.add("gzip");
+ decompressArgsList.add("-k");
+ decompressArgsList.add("-c");
+ decompressArgsList.add("-d");
+ decompressArgsList.add(compressedFile.getAbsolutePath());
+
+ // Decompress perfetto trace file.
+ CLog.i("Start decompressing the perfetto trace file.");
+ try (FileOutputStream outStream = new FileOutputStream(decompressedFile);
+ ByteArrayOutputStream errStream = new ByteArrayOutputStream()) {
+ CommandResult decompressResult = runHostCommand(mDecompressTimeoutMs,
+ decompressArgsList.toArray(new String[decompressArgsList
+ .size()]), outStream, errStream);
+
+ if (!CommandStatus.SUCCESS.equals(decompressResult.getStatus())) {
+ CLog.e("Unable decompress the metric file %s due to %s - Status - %s ",
+ compressedFile.getName(), errStream.toString(),
+ decompressResult.getStatus());
+ decompressedFile.delete();
+ return null;
+ }
+ } catch (FileNotFoundException e) {
+ CLog.e("Not able to find the decompressed file to copy the"
+ + " decompressed contents.");
+ CLog.e(e);
+ return null;
+ } catch (IOException e1) {
+ CLog.e("Unable to close the streams.");
+ CLog.e(e1);
+ }
+ CLog.i("Successfully decompressed the perfetto trace file.");
+ return decompressedFile;
+ }
+
@Override
public void processMetricDirectory(String key, File metricDirectory, DeviceMetricData runData) {
// Implement if all the files under specific directory have to be post processed.
@@ -172,11 +338,17 @@
* Run a host command with the given array of command args.
*
* @param commandArgs args to be used to construct the host command.
+ * @param stdout output of the command.
+ * @param stderr error message if any from the command.
* @return return the command results.
*/
@VisibleForTesting
- CommandResult runHostCommand(String[] commandArgs) {
- return RunUtil.getDefault().runTimedCmd(mScriptTimeoutMs, commandArgs);
+ CommandResult runHostCommand(long timeOut, String[] commandArgs, OutputStream stdout,
+ OutputStream stderr) {
+ if (stdout != null && stderr != null) {
+ return RunUtil.getDefault().runTimedCmd(timeOut, stdout, stderr, commandArgs);
+ }
+ return RunUtil.getDefault().runTimedCmd(timeOut, commandArgs);
}
@VisibleForTesting
diff --git a/test_framework/com/android/tradefed/targetprep/multi/MixImageZipPreparer.java b/test_framework/com/android/tradefed/targetprep/multi/MixImageZipPreparer.java
index e3b7195..4074e04 100644
--- a/test_framework/com/android/tradefed/targetprep/multi/MixImageZipPreparer.java
+++ b/test_framework/com/android/tradefed/targetprep/multi/MixImageZipPreparer.java
@@ -157,8 +157,17 @@
Map<String, InputStreamFactory> dummyFiles =
createDummyInputStreamFactories(mDummyFileNames);
- dummyFiles = replaceExistingEntries(dummyFiles, files);
- filesNotInDeviceBuild.putAll(dummyFiles);
+ Map<String, InputStreamFactory> dummyFilesNotInDeviceBuild =
+ replaceExistingEntries(dummyFiles, files);
+ // The purpose of the dummy files is to make fastboot shrink product partition.
+ // Some devices don't have product partition and image. If the dummy file names are not
+ // found in device build, they are ignored so that devices with and without product
+ // partition can share configurations.
+ if (!dummyFilesNotInDeviceBuild.isEmpty()) {
+ CLog.w(
+ "Skip creating dummy images: %s",
+ String.join(",", dummyFilesNotInDeviceBuild.keySet()));
+ }
if (resourceBuildInfo != null) {
// Get specified files from resource build and replace those in device build.
diff --git a/test_framework/com/android/tradefed/testtype/GTest.java b/test_framework/com/android/tradefed/testtype/GTest.java
index 642dbe9..7668e78 100644
--- a/test_framework/com/android/tradefed/testtype/GTest.java
+++ b/test_framework/com/android/tradefed/testtype/GTest.java
@@ -394,17 +394,18 @@
}
// Insert the coverage listener if code coverage collection is enabled.
listener = addNativeCoverageListenerIfEnabled(listener);
- NativeCodeCoverageFlusher flusher = new NativeCodeCoverageFlusher(mDevice);
+ NativeCodeCoverageFlusher flusher =
+ new NativeCodeCoverageFlusher(mDevice, getCoverageOptions().getCoverageProcesses());
Throwable throwable = null;
try {
if (getCoverageOptions().isCoverageEnabled()) {
+ flusher.resetCoverage();
+
// Clang will no longer create directories that are part of the GCOV_PREFIX
// environment variable. Force create the /data/misc/trace/testcoverage dir to
// prevent "No such file or directory" errors when writing test coverage to disk.
mDevice.executeShellCommand("mkdir /data/misc/trace/testcoverage");
- flusher.forceCoverageFlush(getCoverageOptions().getCoverageProcesses());
- flusher.clearCoverageMeasurements();
}
doRunAllTestsInSubdirectory(testPath, mDevice, listener);
} catch (Throwable t) {
diff --git a/test_framework/com/android/tradefed/testtype/InstrumentationTest.java b/test_framework/com/android/tradefed/testtype/InstrumentationTest.java
index e97fb90..3596d20 100644
--- a/test_framework/com/android/tradefed/testtype/InstrumentationTest.java
+++ b/test_framework/com/android/tradefed/testtype/InstrumentationTest.java
@@ -50,10 +50,14 @@
import com.android.tradefed.result.ddmlib.DefaultRemoteAndroidTestRunner;
import com.android.tradefed.retry.IRetryDecision;
import com.android.tradefed.retry.RetryStrategy;
+import com.android.tradefed.testtype.coverage.CoverageOptions;
+import com.android.tradefed.testtype.coverage.CoverageOptions.Toolchain;
import com.android.tradefed.util.AbiFormatter;
import com.android.tradefed.util.ArrayUtil;
+import com.android.tradefed.util.JavaCodeCoverageFlusher;
import com.android.tradefed.util.ListInstrumentationParser;
import com.android.tradefed.util.ListInstrumentationParser.InstrumentationTarget;
+import com.android.tradefed.util.NativeCodeCoverageFlusher;
import com.android.tradefed.util.StringEscapeUtils;
import com.google.common.annotations.VisibleForTesting;
@@ -913,6 +917,24 @@
mRunner.addInstrumentationArg("listener", ArrayUtil.join(",", mExtraDeviceListener));
}
+ // Clear coverage measurements on the device before running.
+ if (mConfiguration != null
+ && mConfiguration.getCoverageOptions().isCoverageFlushEnabled()) {
+ CoverageOptions options = mConfiguration.getCoverageOptions();
+
+ if (options.getCoverageToolchains().contains(Toolchain.GCOV)) {
+ NativeCodeCoverageFlusher flusher =
+ new NativeCodeCoverageFlusher(mDevice, options.getCoverageProcesses());
+ flusher.resetCoverage();
+ }
+
+ if (options.getCoverageToolchains().contains(Toolchain.JACOCO)) {
+ JavaCodeCoverageFlusher flusher =
+ new JavaCodeCoverageFlusher(mDevice, options.getCoverageProcesses());
+ flusher.resetCoverage();
+ }
+ }
+
if (testsToRun == null) {
// Failed to collect the tests or collection is off. Just try to run them all.
mDevice.runInstrumentationTests(mRunner, listener);
@@ -958,8 +980,11 @@
}
if (mConfiguration.getCoverageOptions().isCoverageEnabled()
&& mConfiguration.getCoverageOptions().getCoverageToolchains().contains(JACOCO)) {
- // TODO(b/137857876): Pass cross-process coverage options to the Java listener.
- return new JavaCodeCoverageListener(getDevice(), mMergeCoverageMeasurements, listener);
+ return new JavaCodeCoverageListener(
+ getDevice(),
+ mConfiguration.getCoverageOptions(),
+ mMergeCoverageMeasurements,
+ listener);
}
return listener;
}
diff --git a/test_framework/com/android/tradefed/testtype/JavaCodeCoverageListener.java b/test_framework/com/android/tradefed/testtype/JavaCodeCoverageListener.java
index c58e3c6..bc9929d 100644
--- a/test_framework/com/android/tradefed/testtype/JavaCodeCoverageListener.java
+++ b/test_framework/com/android/tradefed/testtype/JavaCodeCoverageListener.java
@@ -17,6 +17,7 @@
package com.android.tradefed.testtype;
import static com.google.common.base.Verify.verifyNotNull;
+import static com.google.common.io.Files.getNameWithoutExtension;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
@@ -25,7 +26,12 @@
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.LogDataType;
import com.android.tradefed.result.ResultForwarder;
+import com.android.tradefed.testtype.coverage.CoverageOptions;
import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.JavaCodeCoverageFlusher;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
import org.jacoco.core.tools.ExecFileLoader;
@@ -43,18 +49,31 @@
public static final String COVERAGE_MEASUREMENT_KEY = "coverageFilePath";
private final ITestDevice mDevice;
+ private final CoverageOptions mCoverageOptions;
private final boolean mMergeCoverageMeasurements;
private final ExecFileLoader mExecFileLoader = new ExecFileLoader();
+ private JavaCodeCoverageFlusher mFlusher;
private String mCurrentRunName;
public JavaCodeCoverageListener(
- ITestDevice device, boolean mergeMeasurements, ITestInvocationListener... listeners) {
+ ITestDevice device,
+ CoverageOptions options,
+ boolean mergeMeasurements,
+ ITestInvocationListener... listeners) {
super(listeners);
mDevice = device;
+ mCoverageOptions = options;
mMergeCoverageMeasurements = mergeMeasurements;
+
+ mFlusher = new JavaCodeCoverageFlusher(device, options.getCoverageProcesses());
+ }
+
+ @VisibleForTesting
+ public void setCoverageFlusher(JavaCodeCoverageFlusher flusher) {
+ mFlusher = flusher;
}
@Override
@@ -76,10 +95,7 @@
mExecFileLoader.save(mergedMeasurements, false);
// Save the merged measurement as a test log.
- try (FileInputStreamSource source =
- new FileInputStreamSource(mergedMeasurements, true)) {
- testLog("merged_runtime_coverage", LogDataType.COVERAGE, source);
- }
+ logCoverageMeasurement("merged_runtime_coverage", mergedMeasurements);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
@@ -93,37 +109,55 @@
super.testRunEnded(elapsedTime, runMetrics);
return;
}
- String devicePath = devicePathMetric.getMeasurements().getSingleString();
- if (devicePath == null) {
+ String testCoveragePath = devicePathMetric.getMeasurements().getSingleString();
+ if (testCoveragePath == null) {
super.testRunFailed("No coverage measurement.");
super.testRunEnded(elapsedTime, runMetrics);
return;
}
+ ImmutableList.Builder<String> devicePaths = ImmutableList.builder();
+ devicePaths.add(testCoveragePath);
+
File coverageFile = null;
try {
- coverageFile = mDevice.pullFile(devicePath);
- verifyNotNull(coverageFile, "Failed to pull the coverage file from %s", devicePath);
+ if (mCoverageOptions.isCoverageFlushEnabled()) {
+ devicePaths.addAll(mFlusher.forceCoverageFlush());
+ }
- // When merging, load the measurement data. Otherwise log the measurement
- // immediately.
- if (mMergeCoverageMeasurements) {
- mExecFileLoader.load(coverageFile);
- } else {
- try (FileInputStreamSource source =
- new FileInputStreamSource(coverageFile, true)) {
- testLog(
- mCurrentRunName + "_runtime_coverage",
- LogDataType.COVERAGE,
- source);
+ for (String devicePath : devicePaths.build()) {
+ coverageFile = mDevice.pullFile(devicePath);
+ verifyNotNull(
+ coverageFile, "Failed to pull the coverage file from %s", devicePath);
+
+ // When merging, load the measurement data. Otherwise log the measurement
+ // immediately.
+ try {
+ if (mMergeCoverageMeasurements) {
+ mExecFileLoader.load(coverageFile);
+ } else {
+ logCoverageMeasurement(
+ mCurrentRunName
+ + "_"
+ + getNameWithoutExtension(devicePath)
+ + "_runtime_coverage",
+ coverageFile);
+ }
+ } finally {
+ FileUtil.deleteFile(coverageFile);
}
}
} catch (DeviceNotAvailableException | IOException e) {
throw new RuntimeException(e);
} finally {
- FileUtil.deleteFile(coverageFile);
super.testRunEnded(elapsedTime, runMetrics);
}
}
}
+
+ private void logCoverageMeasurement(String name, File coverageFile) throws IOException {
+ try (FileInputStreamSource source = new FileInputStreamSource(coverageFile, true)) {
+ testLog(name, LogDataType.COVERAGE, source);
+ }
+ }
}
diff --git a/test_framework/com/android/tradefed/testtype/NativeCodeCoverageListener.java b/test_framework/com/android/tradefed/testtype/NativeCodeCoverageListener.java
index 6b045ae..37fed05 100644
--- a/test_framework/com/android/tradefed/testtype/NativeCodeCoverageListener.java
+++ b/test_framework/com/android/tradefed/testtype/NativeCodeCoverageListener.java
@@ -40,7 +40,6 @@
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.List;
/**
* A {@link ResultForwarder} that will pull native coverage measurements off of the device and log
@@ -53,7 +52,6 @@
String.format("find %s -name '*.gcda'", NATIVE_COVERAGE_DEVICE_PATH);
private final boolean mFlushCoverage;
- private final List<String> mCoverageProcesses;
private final ITestDevice mDevice;
private final NativeCodeCoverageFlusher mFlusher;
@@ -63,8 +61,7 @@
super(listeners);
mDevice = device;
mFlushCoverage = false;
- mCoverageProcesses = ImmutableList.of();
- mFlusher = new NativeCodeCoverageFlusher(mDevice);
+ mFlusher = new NativeCodeCoverageFlusher(mDevice, ImmutableList.of());
}
public NativeCodeCoverageListener(
@@ -74,8 +71,7 @@
super(listeners);
mDevice = device;
mFlushCoverage = coverageOptions.isCoverageFlushEnabled();
- mCoverageProcesses = coverageOptions.getCoverageProcesses();
- mFlusher = new NativeCodeCoverageFlusher(mDevice);
+ mFlusher = new NativeCodeCoverageFlusher(mDevice, coverageOptions.getCoverageProcesses());
}
@Override
@@ -98,7 +94,7 @@
// Flush cross-process coverage.
if (mFlushCoverage) {
- mFlusher.forceCoverageFlush(mCoverageProcesses);
+ mFlusher.forceCoverageFlush();
}
// List native coverage files on the device and pull them.
diff --git a/tests/src/com/android/tradefed/config/ConfigurationTest.java b/tests/src/com/android/tradefed/config/ConfigurationTest.java
index 747cf03..c5785c0 100644
--- a/tests/src/com/android/tradefed/config/ConfigurationTest.java
+++ b/tests/src/com/android/tradefed/config/ConfigurationTest.java
@@ -807,12 +807,12 @@
"<cmd_options class=\"com.android.tradefed.command.CommandOptions\" />"));
OptionSetter setter = new OptionSetter(options1);
- setter.setOptionValue("retry-strategy", "ITERATIONS");
+ setter.setOptionValue("test-tag", "tag-value");
sw = new StringWriter();
pw = new PrintWriter(sw);
one.dumpXml(pw, new ArrayList<>(), true, false);
String withOption = sw.toString();
- assertTrue(withOption.contains("<option name=\"retry-strategy\" value=\"ITERATIONS\" />"));
+ assertTrue(withOption.contains("<option name=\"test-tag\" value=\"tag-value\" />"));
CommandOptions options2 = new CommandOptions();
one.setCommandOptions(options2);
diff --git a/tests/src/com/android/tradefed/device/contentprovider/ContentProviderHandlerTest.java b/tests/src/com/android/tradefed/device/contentprovider/ContentProviderHandlerTest.java
index 88b0d45..1ab0c71 100644
--- a/tests/src/com/android/tradefed/device/contentprovider/ContentProviderHandlerTest.java
+++ b/tests/src/com/android/tradefed/device/contentprovider/ContentProviderHandlerTest.java
@@ -239,6 +239,19 @@
}
}
+ @Test
+ public void testPullDir_failedDevice() throws Exception {
+ File pullTo = FileUtil.createTempDir("content-provider-test");
+
+ doReturn("Something crashed").when(mMockDevice).executeShellCommand(anyString());
+
+ try {
+ assertFalse(mProvider.pullDir("path/somewhere", pullTo));
+ } finally {
+ FileUtil.recursiveDelete(pullTo);
+ }
+ }
+
/**
* Test {@link ContentProviderHandler#pullDir(String, File)} to pull a directory that contains
* one text file.
diff --git a/tests/src/com/android/tradefed/device/metric/PerfettoPullerMetricCollectorTest.java b/tests/src/com/android/tradefed/device/metric/PerfettoPullerMetricCollectorTest.java
index 98ca711..164039d 100644
--- a/tests/src/com/android/tradefed/device/metric/PerfettoPullerMetricCollectorTest.java
+++ b/tests/src/com/android/tradefed/device/metric/PerfettoPullerMetricCollectorTest.java
@@ -17,6 +17,7 @@
package com.android.tradefed.device.metric;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.ITestDevice;
@@ -30,10 +31,7 @@
import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.Pair;
import com.android.tradefed.util.proto.TfMetricProtoUtil;
-import java.io.File;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
+
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -44,6 +42,11 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
/** Unit tests for {@link PerfettoPullerMetricCollector}. */
@RunWith(JUnit4.class)
public class PerfettoPullerMetricCollectorTest {
@@ -55,7 +58,6 @@
private ITestDevice mMockDevice;
private IInvocationContext mContext;
-
@Before
public void setUp() {
@@ -100,12 +102,14 @@
cr.setStatus(CommandStatus.SUCCESS);
cr.setStdout("abc:efg");
- Mockito.doReturn(cr).when(mPerfettoMetricCollector).runHostCommand(Mockito.any());
+ Mockito.doReturn(cr).when(mPerfettoMetricCollector).runHostCommand(Mockito.anyLong(),
+ Mockito.any(), Mockito.any(), Mockito.any());
mPerfettoMetricCollector.testStarted(testDesc);
mPerfettoMetricCollector.testEnded(testDesc, currentMetrics);
- Mockito.verify(mPerfettoMetricCollector).runHostCommand(Mockito.any());
+ Mockito.verify(mPerfettoMetricCollector).runHostCommand(Mockito.anyLong(),
+ Mockito.any(), Mockito.any(), Mockito.any());
Mockito.verify(mMockListener)
.testLog(Mockito.eq("trace"), Mockito.eq(LogDataType.PB), Mockito.any());
assertTrue("Expected two metrics that includes success status",
@@ -117,6 +121,41 @@
}
@Test
+ public void testCompressedProcessingFlow() throws Exception {
+ OptionSetter setter = new OptionSetter(mPerfettoMetricCollector);
+ setter.setOptionValue("pull-pattern-keys", "perfettofile");
+ setter.setOptionValue("perfetto-binary-path", "trx");
+ setter.setOptionValue("compress-perfetto", "true");
+ HashMap<String, Metric> currentMetrics = new HashMap<>();
+ currentMetrics.put("perfettofile", TfMetricProtoUtil.stringToMetric("/data/trace.pb"));
+ Mockito.when(mMockDevice.pullFile(Mockito.eq("/data/trace.pb")))
+ .thenReturn(null);
+
+ TestDescription testDesc = new TestDescription("xyz", "abc");
+ CommandResult cr = new CommandResult();
+ cr.setStatus(CommandStatus.SUCCESS);
+ cr.setStdout("abc:efg");
+
+ Mockito.doReturn(cr).when(mPerfettoMetricCollector).runHostCommand(Mockito.anyLong(),
+ Mockito.any(), Mockito.any(), Mockito.any());
+
+ mPerfettoMetricCollector.testStarted(testDesc);
+ mPerfettoMetricCollector.testEnded(testDesc, currentMetrics);
+
+ Mockito.verify(mMockDevice, times(0)).pullFile(Mockito.eq("/data/trace.pb"));
+ Mockito.verify(mPerfettoMetricCollector, times(2)).runHostCommand(Mockito.anyLong(),
+ Mockito.any(), Mockito.any(), Mockito.any());
+ Mockito.verify(mMockListener).testLog(Mockito.contains("compressed"),
+ Mockito.eq(LogDataType.GZIP), Mockito.any());
+ assertTrue("Expected two metrics that includes success status",
+ currentMetrics.get("perfetto_trace_extractor_status").getMeasurements()
+ .getSingleString().equals("1"));
+ assertTrue("Trace duration metrics not available but expected.",
+ currentMetrics.get("perfetto_trace_extractor_runtime").getMeasurements()
+ .getSingleDouble() >= 0);
+ }
+
+ @Test
public void testScriptFailureStatus() throws Exception {
OptionSetter setter = new OptionSetter(mPerfettoMetricCollector);
@@ -132,12 +171,14 @@
cr.setStatus(CommandStatus.FAILED);
cr.setStdout("abc:efg");
- Mockito.doReturn(cr).when(mPerfettoMetricCollector).runHostCommand(Mockito.any());
+ Mockito.doReturn(cr).when(mPerfettoMetricCollector).runHostCommand(Mockito.anyLong(),
+ Mockito.any(), Mockito.any(), Mockito.any());
mPerfettoMetricCollector.testStarted(testDesc);
mPerfettoMetricCollector.testEnded(testDesc, currentMetrics);
- Mockito.verify(mPerfettoMetricCollector).runHostCommand(Mockito.any());
+ Mockito.verify(mPerfettoMetricCollector).runHostCommand(Mockito.anyLong(),
+ Mockito.any(), Mockito.any(), Mockito.any());
Mockito.verify(mMockListener)
.testLog(Mockito.eq("trace"), Mockito.eq(LogDataType.PB), Mockito.any());
assertTrue("Expected two metrics that includes failure status",
@@ -166,13 +207,15 @@
cr.setStatus(CommandStatus.SUCCESS);
cr.setStdout("abc:efg");
- Mockito.doReturn(cr).when(mPerfettoMetricCollector).runHostCommand(Mockito.any());
+ Mockito.doReturn(cr).when(mPerfettoMetricCollector).runHostCommand(Mockito.anyLong(),
+ Mockito.any(), Mockito.any(), Mockito.any());
mPerfettoMetricCollector.testStarted(testDesc);
mPerfettoMetricCollector.testEnded(testDesc, currentMetrics);
ArgumentCaptor<String[]> captor = ArgumentCaptor.forClass(String[].class);
- Mockito.verify(mPerfettoMetricCollector).runHostCommand(captor.capture());
+ Mockito.verify(mPerfettoMetricCollector).runHostCommand(Mockito.anyLong(),
+ captor.capture(), Mockito.any(), Mockito.any());
List<String> args = Arrays.asList(captor.getValue());
Assert.assertTrue(args.contains("--uno"));
Assert.assertTrue(args.contains("--dos"));
diff --git a/tests/src/com/android/tradefed/result/suite/XmlSuiteResultFormatterTest.java b/tests/src/com/android/tradefed/result/suite/XmlSuiteResultFormatterTest.java
index 8f61d29..e73813d 100644
--- a/tests/src/com/android/tradefed/result/suite/XmlSuiteResultFormatterTest.java
+++ b/tests/src/com/android/tradefed/result/suite/XmlSuiteResultFormatterTest.java
@@ -506,8 +506,13 @@
@Test
public void testRunHistoryReporting() throws Exception {
final String RUN_HISTORY =
- "[{\"startTime\":10000000000000,\"endTime\":10000000100000},"
- + "{\"startTime\":10000000200000,\"endTime\":10000000300000}]";
+ "[{\"startTime\":10000000000000,\"endTime\":10000000100000,\"passedTests\":10,"
+ + "\"failedTests\":5,\"commandLineArgs\":\"cts\","
+ + "\"hostName\":\"user.android.com\"},"
+ + "{\"startTime\":10000000200000,\"endTime\":10000000300000,"
+ + "\"passedTests\":3,\"failedTests\":2,"
+ + "\"commandLineArgs\":\"cts\","
+ + "\"hostName\":\"user.android.com\"}]";
mResultHolder.context = mContext;
mResultHolder.context.addInvocationAttribute("run_history", RUN_HISTORY);
@@ -532,8 +537,18 @@
assertXmlContainsNode(content, "Result/RunHistory");
assertXmlContainsAttribute(content, "Result/RunHistory/Run", "start", "10000000000000");
assertXmlContainsAttribute(content, "Result/RunHistory/Run", "end", "10000000100000");
+ assertXmlContainsAttribute(content, "Result/RunHistory/Run", "pass", "10");
+ assertXmlContainsAttribute(content, "Result/RunHistory/Run", "failed", "5");
+ assertXmlContainsAttribute(content, "Result/RunHistory/Run", "command_line_args", "cts");
+ assertXmlContainsAttribute(
+ content, "Result/RunHistory/Run", "host_name", "user.android.com");
assertXmlContainsAttribute(content, "Result/RunHistory/Run", "start", "10000000200000");
assertXmlContainsAttribute(content, "Result/RunHistory/Run", "end", "10000000300000");
+ assertXmlContainsAttribute(content, "Result/RunHistory/Run", "pass", "3");
+ assertXmlContainsAttribute(content, "Result/RunHistory/Run", "failed", "2");
+ assertXmlContainsAttribute(content, "Result/RunHistory/Run", "command_line_args", "cts");
+ assertXmlContainsAttribute(
+ content, "Result/RunHistory/Run", "host_name", "user.android.com");
// Test that we can read back the information.
SuiteResultHolder holder = mFormatter.parseResults(mResultDir, false);
assertEquals(RUN_HISTORY, holder.context.getAttributes().getUniqueMap().get("run_history"));
diff --git a/tests/src/com/android/tradefed/retry/BaseRetryDecisionTest.java b/tests/src/com/android/tradefed/retry/BaseRetryDecisionTest.java
index 617a56e..2c91df1 100644
--- a/tests/src/com/android/tradefed/retry/BaseRetryDecisionTest.java
+++ b/tests/src/com/android/tradefed/retry/BaseRetryDecisionTest.java
@@ -168,6 +168,22 @@
assertFalse(res3);
}
+ /** Ensure we abort the retry if there are too many failed test cases. */
+ @Test
+ public void testAbortTooManyFailures() throws Exception {
+ TestRunResult result = new TestRunResult();
+ result.testRunStarted("TEST", 60);
+ for (int i = 0; i < 60; i++) {
+ TestDescription test = new TestDescription("class", "method" + i);
+ result.testStarted(test);
+ result.testFailed(test, "failure" + i);
+ result.testEnded(test, new HashMap<String, Metric>());
+ }
+ result.testRunEnded(500, new HashMap<String, Metric>());
+ boolean res = mRetryDecision.shouldRetry(mTestClass, 0, Arrays.asList(result));
+ assertFalse(res);
+ }
+
private TestRunResult createResult(boolean failure1, boolean failure2) {
TestRunResult result = new TestRunResult();
result.testRunStarted("TEST", 2);
diff --git a/tests/src/com/android/tradefed/targetprep/multi/MixImageZipPreparerTest.java b/tests/src/com/android/tradefed/targetprep/multi/MixImageZipPreparerTest.java
index de542dc..2fa5601 100644
--- a/tests/src/com/android/tradefed/targetprep/multi/MixImageZipPreparerTest.java
+++ b/tests/src/com/android/tradefed/targetprep/multi/MixImageZipPreparerTest.java
@@ -134,6 +134,7 @@
mPreparer = new MixImageZipPreparer();
mPreparer.addSystemFileName(SYSTEM_IMAGE_NAME);
mPreparer.addDummyFileName(PRODUCT_IMAGE_NAME);
+ mPreparer.addDummyFileName("not_in_device_build.img");
}
private void setUpResource() throws IOException {
diff --git a/tests/src/com/android/tradefed/testtype/GTestTest.java b/tests/src/com/android/tradefed/testtype/GTestTest.java
index ebd1f4d..a47d6d7 100644
--- a/tests/src/com/android/tradefed/testtype/GTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/GTestTest.java
@@ -466,7 +466,6 @@
.andReturn("");
EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
EasyMock.expect(mMockITestDevice.executeShellCommand("kill -37 -1")).andReturn("");
- EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
EasyMock.expect(mMockITestDevice.executeShellCommand("rm -rf /data/misc/trace/*"))
.andReturn("");
EasyMock.expect(mMockITestDevice.doesFileExist(nativeTestPath)).andReturn(true);
@@ -529,7 +528,6 @@
EasyMock.expect(mMockITestDevice.executeShellCommand("kill -37 1 1000")).andReturn("");
// Clear the coverage data.
- EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
EasyMock.expect(mMockITestDevice.executeShellCommand("rm -rf /data/misc/trace/*"))
.andReturn("");
EasyMock.expect(mMockITestDevice.doesFileExist(nativeTestPath)).andReturn(true);
diff --git a/tests/src/com/android/tradefed/testtype/JavaCodeCoverageListenerTest.java b/tests/src/com/android/tradefed/testtype/JavaCodeCoverageListenerTest.java
index 68f7a3b..58aef9b 100644
--- a/tests/src/com/android/tradefed/testtype/JavaCodeCoverageListenerTest.java
+++ b/tests/src/com/android/tradefed/testtype/JavaCodeCoverageListenerTest.java
@@ -28,15 +28,20 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.result.LogDataType;
+import com.android.tradefed.testtype.coverage.CoverageOptions;
+import com.android.tradefed.util.JavaCodeCoverageFlusher;
import com.android.tradefed.util.proto.TfMetricProtoUtil;
import com.google.common.base.VerifyException;
+import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
import org.jacoco.core.tools.ExecFileLoader;
@@ -63,6 +68,7 @@
import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/** Unit tests for {@link JavaCodeCoverageListener}. */
@@ -82,17 +88,25 @@
@Rule public TemporaryFolder folder = new TemporaryFolder();
@Mock ITestDevice mMockDevice;
+ @Mock JavaCodeCoverageFlusher mMockFlusher;
@Spy LogFileReader mFakeListener = new LogFileReader();
/** Object under test. */
JavaCodeCoverageListener mCodeCoverageListener;
+ CoverageOptions mCoverageOptions = null;
+ OptionSetter mCoverageOptionsSetter = null;
+
@Before
- public void setUp() {
+ public void setUp() throws ConfigurationException {
MockitoAnnotations.initMocks(this);
- mCodeCoverageListener = new JavaCodeCoverageListener(mMockDevice, false, mFakeListener);
+ mCoverageOptions = new CoverageOptions();
+ mCoverageOptionsSetter = new OptionSetter(mCoverageOptions);
+
+ mCodeCoverageListener =
+ new JavaCodeCoverageListener(mMockDevice, mCoverageOptions, false, mFakeListener);
}
@Test
@@ -170,7 +184,8 @@
measurement.writeTo(out);
}
- mCodeCoverageListener = new JavaCodeCoverageListener(mMockDevice, true, mFakeListener);
+ mCodeCoverageListener =
+ new JavaCodeCoverageListener(mMockDevice, mCoverageOptions, true, mFakeListener);
Map<String, String> metric = new HashMap<>();
metric.put("coverageFilePath", DEVICE_PATH);
@@ -207,6 +222,42 @@
.isEqualTo(partiallyCovered);
}
+ @Test
+ public void testCoverageFlush_producesMultipleMeasurements() throws Exception {
+ List<String> coverageFileList =
+ ImmutableList.of(
+ "/data/misc/trace/com.android.test1.ec",
+ "/data/misc/trace/com.android.test2.ec",
+ "/data/misc/trace/com.google.test3.ec");
+
+ mCoverageOptionsSetter.setOptionValue("coverage-flush", "true");
+
+ // Setup mocks.
+ File coverageFile = folder.newFile("coverage.ec");
+ try (OutputStream out = new FileOutputStream(coverageFile)) {
+ COVERAGE_MEASUREMENT.writeTo(out);
+ }
+ doReturn(coverageFile).when(mMockDevice).pullFile(DEVICE_PATH);
+
+ for (String additionalFile : coverageFileList) {
+ File coverage = folder.newFile();
+ try (OutputStream out = new FileOutputStream(coverage)) {
+ COVERAGE_MEASUREMENT.writeTo(out);
+ }
+ doReturn(coverage).when(mMockDevice).pullFile(additionalFile);
+ }
+
+ doReturn(coverageFileList).when(mMockFlusher).forceCoverageFlush();
+
+ mCodeCoverageListener.setCoverageFlusher(mMockFlusher);
+
+ // Simulate a test run.
+ mCodeCoverageListener.testRunStarted(RUN_NAME, TEST_COUNT);
+ Map<String, String> metric = new HashMap<>();
+ metric.put("coverageFilePath", DEVICE_PATH);
+ mCodeCoverageListener.testRunEnded(ELAPSED_TIME, TfMetricProtoUtil.upgradeConvert(metric));
+ }
+
private static <T> String vmName(Class<T> clazz) {
return clazz.getName().replace('.', '/');
}
diff --git a/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java b/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java
index 13df991..05326a5 100644
--- a/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java
+++ b/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java
@@ -1804,4 +1804,57 @@
mTestSuite.run(mMockListener);
EasyMock.verify(mockBuildInfo);
}
+
+ /** Test for {@link ITestSuite#reportNotExecuted(ITestInvocationListener, String)}. */
+ @Test
+ public void testReportNotExecuted() {
+ mMockListener.testModuleStarted(EasyMock.anyObject());
+ mMockListener.testRunStarted(
+ EasyMock.eq(TEST_CONFIG_NAME), EasyMock.eq(0), EasyMock.eq(0), EasyMock.anyLong());
+ mMockListener.testRunFailed("Injected message");
+ mMockListener.testRunEnded(
+ EasyMock.anyLong(), EasyMock.<HashMap<String, Metric>>anyObject());
+ mMockListener.testModuleEnded();
+
+ EasyMock.replay(mMockListener);
+ mTestSuite.reportNotExecuted(mMockListener, "Injected message");
+ EasyMock.verify(mMockListener);
+ }
+
+ /**
+ * Test for {@link ITestSuite#reportNotExecuted(ITestInvocationListener, String)} with a module
+ * in progress.
+ */
+ @Test
+ public void testReportNotExecuted_moduleInProgress() {
+ ModuleDefinition m =
+ new ModuleDefinition(
+ "in-progress",
+ new ArrayList<>(),
+ new HashMap<>(),
+ new ArrayList<>(),
+ new Configuration("", ""));
+ mTestSuite.setModuleInProgress(m);
+ mMockListener.testModuleStarted(EasyMock.anyObject());
+ mMockListener.testRunStarted(
+ EasyMock.eq("in-progress"), EasyMock.eq(0), EasyMock.eq(0), EasyMock.anyLong());
+ mMockListener.testRunFailed(
+ "Module in-progress was interrupted after starting. Results might not be "
+ + "accurate or complete.");
+ mMockListener.testRunEnded(
+ EasyMock.anyLong(), EasyMock.<HashMap<String, Metric>>anyObject());
+ mMockListener.testModuleEnded();
+ // The non-executed module gets reported too.
+ mMockListener.testModuleStarted(EasyMock.anyObject());
+ mMockListener.testRunStarted(
+ EasyMock.eq(TEST_CONFIG_NAME), EasyMock.eq(0), EasyMock.eq(0), EasyMock.anyLong());
+ mMockListener.testRunFailed("Injected message");
+ mMockListener.testRunEnded(
+ EasyMock.anyLong(), EasyMock.<HashMap<String, Metric>>anyObject());
+ mMockListener.testModuleEnded();
+
+ EasyMock.replay(mMockListener);
+ mTestSuite.reportNotExecuted(mMockListener, "Injected message");
+ EasyMock.verify(mMockListener);
+ }
}
diff --git a/tests/src/com/android/tradefed/testtype/suite/SuiteModuleLoaderTest.java b/tests/src/com/android/tradefed/testtype/suite/SuiteModuleLoaderTest.java
index b5bbd81..e2a23ae 100644
--- a/tests/src/com/android/tradefed/testtype/suite/SuiteModuleLoaderTest.java
+++ b/tests/src/com/android/tradefed/testtype/suite/SuiteModuleLoaderTest.java
@@ -17,6 +17,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import com.android.tradefed.config.IConfiguration;
@@ -287,4 +288,83 @@
assertEquals(
"NativeDnsAsyncTest#Async_Cancel", stubTest.getExcludeFilters().iterator().next());
}
+
+ /**
+ * Test that the configuration can be found if specifying specific path.
+ */
+ @Test
+ public void testLoadConfigsFromSpecifiedPaths_OneModule() throws Exception {
+ createModuleConfig("module1");
+ File module1 = new File(mTestsDir, "module1" + SuiteModuleLoader.CONFIG_EXT);
+
+ mRepo =
+ new SuiteModuleLoader(
+ new LinkedHashMap<String, List<SuiteTestFilter>>(),
+ new LinkedHashMap<String, List<SuiteTestFilter>>(),
+ new ArrayList<>(),
+ new ArrayList<>());
+
+ LinkedHashMap<String, IConfiguration> res =
+ mRepo.loadConfigsFromSpecifiedPaths(
+ Arrays.asList(module1), mAbis, null);
+ assertEquals(1, res.size());
+ assertNotNull(res.get("armeabi-v7a module1"));
+ }
+
+ /**
+ * Test that multiple configurations can be found if specifying specific paths.
+ */
+ @Test
+ public void testLoadConfigsFromSpecifiedPaths_MultipleModules() throws Exception {
+ createModuleConfig("module1");
+ File module1 = new File(mTestsDir, "module1" + SuiteModuleLoader.CONFIG_EXT);
+ createModuleConfig("module2");
+ File module2 = new File(mTestsDir, "module2" + SuiteModuleLoader.CONFIG_EXT);
+
+ mRepo =
+ new SuiteModuleLoader(
+ new LinkedHashMap<String, List<SuiteTestFilter>>(),
+ new LinkedHashMap<String, List<SuiteTestFilter>>(),
+ new ArrayList<>(),
+ new ArrayList<>());
+
+ LinkedHashMap<String, IConfiguration> res =
+ mRepo.loadConfigsFromSpecifiedPaths(
+ Arrays.asList(module1, module2), mAbis, null);
+ assertEquals(2, res.size());
+ assertNotNull(res.get("armeabi-v7a module1"));
+ assertNotNull(res.get("armeabi-v7a module2"));
+ }
+
+ /**
+ * Test that configuration can be found correctly if specifying specific paths but someone is
+ * excluded.
+ */
+ @Test
+ public void testLoadConfigsFromSpecifiedPaths_WithExcludeFilter() throws Exception {
+ createModuleConfig("module1");
+ File module1 = new File(mTestsDir, "module1" + SuiteModuleLoader.CONFIG_EXT);
+ createModuleConfig("module2");
+ File module2 = new File(mTestsDir, "module2" + SuiteModuleLoader.CONFIG_EXT);
+
+ Map<String, List<SuiteTestFilter>> excludeFilters = new LinkedHashMap<>();
+ SuiteTestFilter filter =
+ SuiteTestFilter.createFrom(
+ "armeabi-v7a module2");
+ excludeFilters.put("armeabi-v7a module2", Arrays.asList(filter));
+
+ mRepo =
+ new SuiteModuleLoader(
+ new LinkedHashMap<String, List<SuiteTestFilter>>(),
+ excludeFilters,
+ new ArrayList<>(),
+ new ArrayList<>());
+
+ LinkedHashMap<String, IConfiguration> res =
+ mRepo.loadConfigsFromSpecifiedPaths(
+ Arrays.asList(module1, module2), mAbis, null);
+ assertEquals(1, res.size());
+ assertNotNull(res.get("armeabi-v7a module1"));
+ assertNull(res.get("armeabi-v7a module2"));
+ }
}
diff --git a/tests/src/com/android/tradefed/testtype/suite/TestMappingSuiteRunnerTest.java b/tests/src/com/android/tradefed/testtype/suite/TestMappingSuiteRunnerTest.java
index b2028d8..48b6600 100644
--- a/tests/src/com/android/tradefed/testtype/suite/TestMappingSuiteRunnerTest.java
+++ b/tests/src/com/android/tradefed/testtype/suite/TestMappingSuiteRunnerTest.java
@@ -20,6 +20,8 @@
import com.android.tradefed.build.BuildInfoKey.BuildInfoFileKey;
import com.android.tradefed.build.IDeviceBuildInfo;
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.ConfigurationFactory;
import com.android.tradefed.config.IConfiguration;
import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.DeviceNotAvailableException;
@@ -31,8 +33,11 @@
import com.android.tradefed.util.AbiUtils;
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.ZipUtil;
+import com.android.tradefed.util.testmapping.TestInfo;
import com.android.tradefed.util.testmapping.TestMapping;
+import com.android.tradefed.util.testmapping.TestOption;
+import java.util.ArrayList;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
@@ -57,14 +62,17 @@
private static final String ABI_1 = "arm64-v8a";
private static final String ABI_2 = "armeabi-v7a";
+ private static final String DISABLED_PRESUBMIT_TESTS = "disabled-presubmit-tests";
+ private static final String EMPTY_CONFIG = "empty";
private static final String NON_EXISTING_DIR = "non-existing-dir";
+ private static final String TEST_CONFIG_NAME = "test";
private static final String TEST_DATA_DIR = "testdata";
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";
private TestMappingSuiteRunner mRunner;
private OptionSetter mOptionSetter;
+ private TestMappingSuiteRunner mRunner2;
private IDeviceBuildInfo mBuildInfo;
private ITestDevice mMockDevice;
@@ -79,8 +87,13 @@
mOptionSetter = new OptionSetter(mRunner);
mOptionSetter.setOptionValue("suite-config-prefix", "suite");
- EasyMock.expect(mBuildInfo.getFile(BuildInfoFileKey.TARGET_LINKED_DIR)).andReturn(null);
- EasyMock.expect(mBuildInfo.getTestsDir()).andReturn(new File(NON_EXISTING_DIR));
+ mRunner2 = new FakeTestMappingSuiteRunner();
+ mRunner2.setBuild(mBuildInfo);
+ mRunner2.setDevice(mMockDevice);
+
+ EasyMock.expect(mBuildInfo.getFile(BuildInfoFileKey.TARGET_LINKED_DIR)).andReturn(null)
+ .anyTimes();
+ EasyMock.expect(mBuildInfo.getTestsDir()).andReturn(new File(NON_EXISTING_DIR)).anyTimes();
EasyMock.expect(mMockDevice.getProperty(EasyMock.anyObject())).andReturn(ABI_1);
EasyMock.expect(mMockDevice.getProperty(EasyMock.anyObject())).andReturn(ABI_2);
EasyMock.replay(mBuildInfo, mMockDevice);
@@ -98,6 +111,43 @@
abis.add(new Abi(ABI_2, AbiUtils.getBitness(ABI_2)));
return abis;
}
+
+ @Override
+ List<IRemoteTest> createIndividualTests(Set<TestInfo> testInfos, String configPath) {
+ IRemoteTest fakeTest = EasyMock.createMock(IRemoteTest.class);
+ return new ArrayList<>(Arrays.asList(fakeTest));
+ }
+ }
+
+ /**
+ * Test TestMappingSuiteRunner that create a fake IConfiguration with fake a test object.
+ */
+ public static class FakeTestMappingSuiteRunner extends TestMappingSuiteRunner {
+ @Override
+ public Set<IAbi> getAbis(ITestDevice device) throws DeviceNotAvailableException {
+ Set<IAbi> abis = new HashSet<>();
+ abis.add(new Abi(ABI_1, AbiUtils.getBitness(ABI_1)));
+ abis.add(new Abi(ABI_2, AbiUtils.getBitness(ABI_2)));
+ return abis;
+ }
+
+ @Override
+ public LinkedHashMap<String, IConfiguration> loadingStrategy(Set<IAbi> abis,
+ List<File> testsDirs, String suitePrefix, String suiteTag) {
+ LinkedHashMap<String, IConfiguration> testConfig = new LinkedHashMap<>();
+ try {
+ IConfiguration config =
+ ConfigurationFactory.getInstance()
+ .createConfigurationFromArgs(new String[] {EMPTY_CONFIG});
+ config.setTest(new StubTest());
+ config.getConfigurationDescription().setModuleName(TEST_CONFIG_NAME);
+ testConfig.put(TEST_CONFIG_NAME, config);
+
+ } catch (ConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ return testConfig;
+ }
}
/**
@@ -174,13 +224,7 @@
assertTrue(mRunner.getIncludeFilter().contains("test2"));
assertTrue(mRunner.getIncludeFilter().contains("instrument"));
assertTrue(mRunner.getIncludeFilter().contains("suite/stub1"));
- // Filters are applied directly
- assertTrue(mRunner.getExcludeFilter().contains("suite/stub1 filter.com"));
- assertTrue(mRunner.getIncludeFilter().contains("suite/stub2 filter.com"));
-
- // Check module-arg work as expected.
- StubTest test = (StubTest) configMap.get("arm64-v8a suite/stub2").getTests().get(0);
- assertTrue(test.getRunTest());
+ assertTrue(mRunner.getIncludeFilter().contains("suite/stub2"));
assertEquals(4, configMap.size());
assertTrue(configMap.containsKey(ABI_1 + " suite/stub1"));
@@ -575,4 +619,141 @@
FileUtil.recursiveDelete(tempDir);
}
}
+
+ /**
+ * Test for {@link TestMappingSuiteRunner#getTestInfos(Set, String)} that when a module is
+ * specified, tests would be still found correctly.
+ */
+ @Test
+ public void testGetTestInfos() throws Exception {
+ Set<TestInfo> testInfos = new HashSet<>();
+ testInfos.add(createTestInfo("test", "path"));
+ testInfos.add(createTestInfo("test", "path2"));
+ testInfos.add(createTestInfo("test2", "path2"));
+
+ assertEquals(2, mRunner.getTestInfos(testInfos, "test").size());
+ assertEquals(1, mRunner.getTestInfos(testInfos, "test2").size());
+ }
+
+ /**
+ * Test for {@link TestMappingSuiteRunner#dedupTestInfos(Set)} that tests with the same test
+ * options would be filtered out.
+ */
+ @Test
+ public void testDedupTestInfos() throws Exception {
+ Set<TestInfo> testInfos = new HashSet<>();
+ testInfos.add(createTestInfo("test", "path"));
+ testInfos.add(createTestInfo("test", "path2"));
+ assertEquals(1, mRunner.dedupTestInfos(testInfos).size());
+
+ TestInfo anotherInfo = new TestInfo("test", "folder3", false);
+ anotherInfo.addOption(new TestOption("include-filter", "value1"));
+ testInfos.add(anotherInfo);
+ assertEquals(2, mRunner.dedupTestInfos(testInfos).size());
+ }
+
+ /**
+ * Test for {@link TestMappingSuiteRunner#getTestSources(Set)} that test sources would be found
+ * correctly.
+ */
+ @Test
+ public void testGetTestSources() throws Exception {
+ Set<TestInfo> testInfos = new HashSet<>();
+ testInfos.add(createTestInfo("test", "path"));
+ testInfos.add(createTestInfo("test", "path2"));
+ List<String> results = mRunner.getTestSources(testInfos);
+ assertEquals(2, results.size());
+ }
+
+ /**
+ * Test for {@link TestMappingSuiteRunner#parseOptions(TestInfo)} that the test options are
+ * injected correctly.
+ */
+ @Test
+ public void testParseOptions() throws Exception {
+ TestInfo info = createTestInfo("test", "path");
+ mRunner.parseOptions(info);
+ assertEquals(1, mRunner.getIncludeFilter().size());
+ assertEquals(1, mRunner.getExcludeFilter().size());
+ }
+
+ /**
+ * Test for {@link TestMappingSuiteRunner#createIndividualTests(Set, String)} that IRemoteTest
+ * object are created according to the test infos with different test options.
+ */
+ @Test
+ public void testCreateIndividualTestsWithDifferentTestInfos() throws Exception {
+ File tempDir = null;
+ try {
+ tempDir = FileUtil.createTempDir("tmp");
+ File moduleConfig = new File(tempDir, "module_name.config");
+ moduleConfig.createNewFile();
+ Set<TestInfo> testInfos = new HashSet<>();
+ testInfos.add(createTestInfo("test", "path"));
+ testInfos.add(createTestInfo("test2", "path"));
+ String configPath = moduleConfig.getAbsolutePath();
+ assertEquals(2, mRunner2.createIndividualTests(testInfos, configPath).size());
+ assertEquals(1, mRunner2.getIncludeFilter().size());
+ assertEquals(1, mRunner2.getExcludeFilter().size());
+ } finally {
+ FileUtil.recursiveDelete(tempDir);
+ }
+ }
+
+ /**
+ * Test for {@link TestMappingSuiteRunner#createIndividualTests(Set, String)} that IRemoteTest
+ * object are created according to the test infos with multiple test options.
+ */
+ @Test
+ public void testCreateIndividualTestsWithDifferentTestOptions() throws Exception {
+ File tempDir = null;
+ try {
+ tempDir = FileUtil.createTempDir("tmp");
+ File moduleConfig = new File(tempDir, "module_name.config");
+ moduleConfig.createNewFile();
+ Set<TestInfo> testInfos = new HashSet<>();
+ testInfos.add(createTestInfo("test", "path"));
+ TestInfo info = new TestInfo("test", "path", false);
+ info.addOption(new TestOption("include-filter", "include-filter"));
+ testInfos.add(info);
+ String configPath = moduleConfig.getAbsolutePath();
+ assertEquals(2, mRunner2.createIndividualTests(testInfos, configPath).size());
+ assertEquals(1, mRunner2.getIncludeFilter().size());
+ assertEquals(0, mRunner2.getExcludeFilter().size());
+ } finally {
+ FileUtil.recursiveDelete(tempDir);
+ }
+ }
+
+ /**
+ * Test for {@link TestMappingSuiteRunner#createIndividualTests(Set, String)} that IRemoteTest
+ * object are created according to the test infos with the same test options and name.
+ */
+ @Test
+ public void testCreateIndividualTestsWithSameTestInfos() throws Exception {
+ File tempDir = null;
+ try {
+ tempDir = FileUtil.createTempDir("tmp");
+ File moduleConfig = new File(tempDir, "module_name.config");
+ moduleConfig.createNewFile();
+ String configPath = moduleConfig.getAbsolutePath();
+ Set<TestInfo> testInfos = new HashSet<>();
+ testInfos.add(createTestInfo("test", "path"));
+ testInfos.add(createTestInfo("test", "path"));
+ assertEquals(1, mRunner2.createIndividualTests(testInfos, configPath).size());
+ assertEquals(1, mRunner2.getIncludeFilter().size());
+ assertEquals(1, mRunner2.getExcludeFilter().size());
+ } finally {
+ FileUtil.recursiveDelete(tempDir);
+ }
+ }
+
+ /** Helper to create specific test infos. */
+ private TestInfo createTestInfo(String name, String source) {
+ TestInfo info = new TestInfo(name, source, false);
+ info.addOption(new TestOption("include-filter", name));
+ info.addOption(new TestOption("exclude-filter", name));
+ info.addOption(new TestOption("other", name));
+ return info;
+ }
}
diff --git a/tests/src/com/android/tradefed/util/FileUtilTest.java b/tests/src/com/android/tradefed/util/FileUtilTest.java
index e0f8ff6..687e783 100644
--- a/tests/src/com/android/tradefed/util/FileUtilTest.java
+++ b/tests/src/com/android/tradefed/util/FileUtilTest.java
@@ -535,4 +535,28 @@
FileUtil.recursiveDelete(tmpDir);
}
}
+
+ @Test
+ public void testReadPartialStringFromFile() throws IOException {
+ File tmpFile = FileUtil.createTempFile("test", ".txt");
+ try {
+ StringBuilder content = new StringBuilder();
+ StringBuilder partialContent = new StringBuilder();
+ for (int i = 0; i < 1024; i++) {
+ content.append('A');
+ }
+ for (int i = 0; i < 1024; i++) {
+ content.append('C');
+ partialContent.append('C');
+ }
+ FileUtil.writeToFile(content.toString(), tmpFile);
+ // Test to read the whole file with `length` greater than the file length.
+ assertEquals(content.toString(), FileUtil.readStringFromFile(tmpFile, -1024, 4096 * 2));
+ // Test to read the tailing part of the file.
+ assertEquals(
+ partialContent.toString(), FileUtil.readStringFromFile(tmpFile, 1024, 4096));
+ } finally {
+ FileUtil.deleteFile(tmpFile);
+ }
+ }
}
diff --git a/tests/src/com/android/tradefed/util/NativeCodeCoverageFlusherTest.java b/tests/src/com/android/tradefed/util/NativeCodeCoverageFlusherTest.java
index 523655c..d5f627f 100644
--- a/tests/src/com/android/tradefed/util/NativeCodeCoverageFlusherTest.java
+++ b/tests/src/com/android/tradefed/util/NativeCodeCoverageFlusherTest.java
@@ -34,7 +34,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.lang.IllegalStateException;
import java.util.List;
@RunWith(JUnit4.class)
@@ -48,15 +47,14 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
-
- mFlusher = new NativeCodeCoverageFlusher(mMockDevice);
}
@Test
public void testClearCoverageMeasurements_rmCommandCalled() throws DeviceNotAvailableException {
doReturn(true).when(mMockDevice).isAdbRoot();
- mFlusher.clearCoverageMeasurements();
+ mFlusher = new NativeCodeCoverageFlusher(mMockDevice, ImmutableList.of());
+ mFlusher.resetCoverage();
// Verify that the rm command was executed.
verify(mMockDevice).executeShellCommand("rm -rf /data/misc/trace/*");
@@ -67,7 +65,8 @@
doReturn(false).when(mMockDevice).isAdbRoot();
try {
- mFlusher.clearCoverageMeasurements();
+ mFlusher = new NativeCodeCoverageFlusher(mMockDevice, ImmutableList.of());
+ mFlusher.resetCoverage();
fail("Should have thrown an exception");
} catch (IllegalStateException e) {
// Expected
@@ -82,7 +81,8 @@
throws DeviceNotAvailableException {
doReturn(true).when(mMockDevice).isAdbRoot();
- mFlusher.forceCoverageFlush(ImmutableList.of());
+ mFlusher = new NativeCodeCoverageFlusher(mMockDevice, ImmutableList.of());
+ mFlusher.forceCoverageFlush();
// Verify that the flush command for all processes was called.
verify(mMockDevice).executeShellCommand("kill -37 -1");
@@ -97,7 +97,8 @@
doReturn("12").when(mMockDevice).getProcessPid(processes.get(0));
doReturn("789").when(mMockDevice).getProcessPid(processes.get(1));
- mFlusher.forceCoverageFlush(processes);
+ mFlusher = new NativeCodeCoverageFlusher(mMockDevice, processes);
+ mFlusher.forceCoverageFlush();
// Verify that the flush command for the specific processes was called.
verify(mMockDevice).executeShellCommand("kill -37 12 789");
@@ -108,7 +109,8 @@
doReturn(false).when(mMockDevice).isAdbRoot();
try {
- mFlusher.forceCoverageFlush(ImmutableList.of("mediaserver"));
+ mFlusher = new NativeCodeCoverageFlusher(mMockDevice, ImmutableList.of("mediaserver"));
+ mFlusher.forceCoverageFlush();
fail("Should have thrown an exception");
} catch (IllegalStateException e) {
// Expected
diff --git a/tests/src/com/android/tradefed/util/StreamUtilTest.java b/tests/src/com/android/tradefed/util/StreamUtilTest.java
index f2e774c..312c45d 100644
--- a/tests/src/com/android/tradefed/util/StreamUtilTest.java
+++ b/tests/src/com/android/tradefed/util/StreamUtilTest.java
@@ -110,8 +110,8 @@
}
/**
- * Verify that {@link com.android.tradefed.util.StreamUtil#getStringFromStream} works as
- * expected.
+ * Verify that {@link com.android.tradefed.util.StreamUtil#getStringFromStream(InputStream)}
+ * works as expected.
*/
public void testGetStringFromStream() throws Exception {
final String contents = "this is a string";
@@ -121,6 +121,17 @@
}
/**
+ * Verify that {@link com.android.tradefed.util.StreamUtil#getStringFromStream(InputStream,
+ * long)} works as expected.
+ */
+ public void testGetStringFromStream_withLength() throws Exception {
+ final String contents = "this is a string";
+ final String output =
+ StreamUtil.getStringFromStream(new ByteArrayInputStream(contents.getBytes()), 5);
+ assertEquals("this ", output);
+ }
+
+ /**
* Verify that {@link com.android.tradefed.util.StreamUtil#calculateCrc32(InputStream)} works as
* expected.
*
diff --git a/tests/src/com/android/tradefed/util/testmapping/TestMappingTest.java b/tests/src/com/android/tradefed/util/testmapping/TestMappingTest.java
index 8c53c17..0d69264 100644
--- a/tests/src/com/android/tradefed/util/testmapping/TestMappingTest.java
+++ b/tests/src/com/android/tradefed/util/testmapping/TestMappingTest.java
@@ -169,12 +169,10 @@
assertEquals(0, tests.size());
tests = TestMapping.getTests(mockBuildInfo, "presubmit", true, null);
- assertEquals(1, tests.size());
+ assertEquals(2, tests.size());
Set<String> names = new HashSet<String>();
for (TestInfo test : tests) {
names.add(test.getName());
- // Make sure the tests for `test1` are merged and no option is kept.
- assertTrue(test.getOptions().isEmpty());
if (test.getName().equals("test1")) {
assertTrue(test.getHostOnly());
} else {
diff --git a/tests/test-apps/Android.mk b/tests/test-apps/Android.mk
deleted file mode 100644
index e25764f..0000000
--- a/tests/test-apps/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (C) 2010 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/tests/test-apps/NativeTestSampleApp/Android.bp b/tests/test-apps/NativeTestSampleApp/Android.bp
new file mode 100644
index 0000000..12fa9c6
--- /dev/null
+++ b/tests/test-apps/NativeTestSampleApp/Android.bp
@@ -0,0 +1,22 @@
+// Copyright (C) 2010 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.
+
+// Makefile to build sample static library that has native tests.
+
+cc_library_static {
+ name: "tfnativetestsamplelib",
+ // All source files for the library
+ srcs: ["src/TradeFedNativeTestSampleLib.cpp"],
+ local_include_dirs: ["include"],
+}
diff --git a/tests/test-apps/NativeTestSampleApp/Android.mk b/tests/test-apps/NativeTestSampleApp/Android.mk
deleted file mode 100644
index f1ef111..0000000
--- a/tests/test-apps/NativeTestSampleApp/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (C) 2010 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.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/tests/test-apps/NativeTestSampleApp/src/Android.mk b/tests/test-apps/NativeTestSampleApp/src/Android.mk
deleted file mode 100644
index 6100eb5..0000000
--- a/tests/test-apps/NativeTestSampleApp/src/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2010 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.
-
-# Makefile to build sample static library that has native tests.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-#All source files for the library
-LOCAL_SRC_FILES := TradeFedNativeTestSampleLib.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/../include
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MODULE := tfnativetestsamplelib
-
-include $(BUILD_STATIC_LIBRARY)
diff --git a/tests/test-apps/TradeFedNativeTestApp/Android.bp b/tests/test-apps/TradeFedNativeTestApp/Android.bp
new file mode 100644
index 0000000..badb9b9
--- /dev/null
+++ b/tests/test-apps/TradeFedNativeTestApp/Android.bp
@@ -0,0 +1,21 @@
+// Copyright (C) 2010 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.
+
+// Makefile to build device-based native tests.
+
+cc_test {
+ name: "tfnativetests",
+ // All source files will be bundled into one test module
+ srcs: ["TradeFedNativeTestApp_test.cpp"],
+}
diff --git a/tests/test-apps/TradeFedNativeTestApp/Android.mk b/tests/test-apps/TradeFedNativeTestApp/Android.mk
deleted file mode 100644
index 551b9e2..0000000
--- a/tests/test-apps/TradeFedNativeTestApp/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (C) 2010 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.
-
-# Makefile to build device-based native tests.
-
-# GTest does not build on the simulator because it depends on STLport.
-ifneq ($(TARGET_SIMULATOR),true)
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-# All source files will be bundled into one test module
-LOCAL_SRC_FILES := TradeFedNativeTestApp_test.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-# All gtests in all files should be compiled into one binary
-# The standard naming should conform to: <module_being_tested>tests
-# For example, for libjingle, use libjingletests
-LOCAL_MODULE := tfnativetests
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_NATIVE_TEST)
-
-endif
diff --git a/tests/test-apps/TradeFedTestApp/Android.bp b/tests/test-apps/TradeFedTestApp/Android.bp
new file mode 100644
index 0000000..1611186
--- /dev/null
+++ b/tests/test-apps/TradeFedTestApp/Android.bp
@@ -0,0 +1,19 @@
+// Copyright (C) 2010 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.
+
+android_test {
+ name: "TradeFedTestApp",
+ srcs: ["src/**/*.java"],
+ sdk_version: "4",
+}
diff --git a/tests/test-apps/TradeFedTestApp/Android.mk b/tests/test-apps/TradeFedTestApp/Android.mk
deleted file mode 100644
index 657e2b0..0000000
--- a/tests/test-apps/TradeFedTestApp/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2010 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 4
-
-LOCAL_PACKAGE_NAME := TradeFedTestApp
-
-include $(BUILD_PACKAGE)
diff --git a/tests/test-apps/UITestApp/Android.bp b/tests/test-apps/UITestApp/Android.bp
new file mode 100644
index 0000000..d359c36
--- /dev/null
+++ b/tests/test-apps/UITestApp/Android.bp
@@ -0,0 +1,19 @@
+// Copyright (C) 2010 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.
+
+android_test {
+ name: "TradeFedUiTestApp",
+ srcs: ["src/**/*.java"],
+ sdk_version: "8",
+}
diff --git a/tests/test-apps/UITestApp/Android.mk b/tests/test-apps/UITestApp/Android.mk
deleted file mode 100644
index 137c129..0000000
--- a/tests/test-apps/UITestApp/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2010 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 8
-
-LOCAL_PACKAGE_NAME := TradeFedUiTestApp
-
-include $(BUILD_PACKAGE)
diff --git a/util-apps/Android.mk b/util-apps/Android.mk
deleted file mode 100644
index a799cb0..0000000
--- a/util-apps/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (C) 2012 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/util-apps/DeviceSetupUtil/Android.bp b/util-apps/DeviceSetupUtil/Android.bp
new file mode 100644
index 0000000..05884de
--- /dev/null
+++ b/util-apps/DeviceSetupUtil/Android.bp
@@ -0,0 +1,19 @@
+// Copyright (C) 2012 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.
+
+android_test_helper_app {
+ name: "DeviceSetupUtil",
+ srcs: ["src/**/*.java"],
+ sdk_version: "8",
+}
diff --git a/util-apps/DeviceSetupUtil/Android.mk b/util-apps/DeviceSetupUtil/Android.mk
deleted file mode 100644
index 79b6a4c..0000000
--- a/util-apps/DeviceSetupUtil/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (C) 2012 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := 8
-LOCAL_PACKAGE_NAME := DeviceSetupUtil
-
-include $(BUILD_PACKAGE)
-
diff --git a/util-apps/WifiUtil/Android.bp b/util-apps/WifiUtil/Android.bp
new file mode 100644
index 0000000..7345a0b
--- /dev/null
+++ b/util-apps/WifiUtil/Android.bp
@@ -0,0 +1,19 @@
+// Copyright (C) 2012 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.
+
+android_test_helper_app {
+ name: "WifiUtil",
+ srcs: ["src/**/*.java"],
+ min_sdk_version: "7",
+}
diff --git a/util-apps/WifiUtil/Android.mk b/util-apps/WifiUtil/Android.mk
deleted file mode 100644
index 98db113..0000000
--- a/util-apps/WifiUtil/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (C) 2012 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_MIN_SDK_VERSION := 7
-LOCAL_SDK_VERSION := current
-LOCAL_PACKAGE_NAME := WifiUtil
-
-include $(BUILD_PACKAGE)
-