blob: fa43d28da540b1915bddbc04fcef0ec8d7d35c65 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.compatibility.common.tradefed.testtype;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.tradefed.result.IInvocationResultRepo;
import com.android.compatibility.common.tradefed.result.InvocationResultRepo;
import com.android.compatibility.common.util.AbiUtils;
import com.android.compatibility.common.util.ICaseResult;
import com.android.compatibility.common.util.IInvocationResult;
import com.android.compatibility.common.util.IModuleResult;
import com.android.compatibility.common.util.ITestResult;
import com.android.compatibility.common.util.TestFilter;
import com.android.compatibility.common.util.TestStatus;
import com.android.ddmlib.Log.LogLevel;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.build.IFolderBuildInfo;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.Option.Importance;
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.config.OptionCopier;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.testtype.IDeviceTest;
import com.android.tradefed.testtype.IRemoteTest;
import com.android.tradefed.testtype.IShardableTest;
import com.android.tradefed.util.AbiFormatter;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* A Test for running Compatibility Suites
*/
@OptionClass(alias="compatibility-test")
public class CompatibilityTest implements IDeviceTest, IShardableTest, IBuildReceiver {
private static final String INCLUDE_FILTER_OPTION = "include-filter";
private static final String EXCLUDE_FILTER_OPTION = "exclude-filter";
private static final String PLAN_OPTION = "plan";
private static final String MODULE_OPTION = "module";
private static final String TEST_OPTION = "test";
public static final String RETRY_OPTION = "retry";
private static final String ABI_OPTION = "abi";
private static final String SHARD_OPTION = "shard";
private static final String URL = "dynamic-config-url";
private static final TestStatus[] RETRY_TEST_STATUS = new TestStatus[] {
TestStatus.FAIL,
TestStatus.NOT_EXECUTED
};
@Option(name = PLAN_OPTION,
description = "the test suite plan to run, such as \"everything\" or \"cts\"",
importance = Importance.ALWAYS)
private String mSuitePlan;
@Option(name = INCLUDE_FILTER_OPTION,
description = "the include module filters to apply.",
importance = Importance.ALWAYS)
private List<String> mIncludeFilters = new ArrayList<>();
@Option(name = EXCLUDE_FILTER_OPTION,
description = "the exclude module filters to apply.",
importance = Importance.ALWAYS)
private List<String> mExcludeFilters = new ArrayList<>();
@Option(name = MODULE_OPTION,
shortName = 'm',
description = "the test module to run.",
importance = Importance.IF_UNSET)
private String mModuleName = null;
@Option(name = TEST_OPTION,
shortName = 't',
description = "the test run.",
importance = Importance.IF_UNSET)
private String mTestName = null;
@Option(name = RETRY_OPTION,
shortName = 'r',
description = "retry a previous session.",
importance = Importance.IF_UNSET)
private Integer mRetrySessionId = null;
@Option(name = ABI_OPTION,
shortName = 'a',
description = "the abi to test.",
importance = Importance.IF_UNSET)
private String mAbiName = null;
@Option(name = SHARD_OPTION,
description = "split the modules up to run on multiple devices concurrently.")
private int mShards = 1;
@Option(name = URL,
description = "Specify the url for override config")
private String mURL;
private int mShardAssignment;
private int mTotalShards;
private ITestDevice mDevice;
private IFolderBuildInfo mBuild;
private CompatibilityBuildHelper mBuildHelper;
private List<IModuleDef> mModules = new ArrayList<>();
private int mLastModuleIndex = 0;
/**
* Create a new {@link CompatibilityTest} that will run the default list of modules.
*/
public CompatibilityTest() {
this(0 /*shardAssignment*/, 1 /*totalShards*/);
}
/**
* Create a new {@link CompatibilityTest} that will run a sublist of modules.
*/
public CompatibilityTest(int shardAssignment, int totalShards) {
if (shardAssignment < 0) {
throw new IllegalArgumentException(
"shardAssignment cannot be negative. found:" + shardAssignment);
}
if (totalShards < 1) {
throw new IllegalArgumentException(
"shardAssignment must be at least 1. found:" + totalShards);
}
mShardAssignment = shardAssignment;
mTotalShards = totalShards;
}
/**
* {@inheritDoc}
*/
@Override
public ITestDevice getDevice() {
return mDevice;
}
/**
* {@inheritDoc}
*/
@Override
public void setDevice(ITestDevice device) {
mDevice = device;
}
/**
* {@inheritDoc}
*/
@Override
public void setBuild(IBuildInfo buildInfo) {
mBuild = (IFolderBuildInfo) buildInfo;
mBuildHelper = new CompatibilityBuildHelper(mBuild);
mBuildHelper.init(mSuitePlan, mURL);
}
/**
* {@inheritDoc}
*/
@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
try {
Set<IAbi> abiSet = getAbis();
if (abiSet == null || abiSet.isEmpty()) {
if (mAbiName == null) {
throw new IllegalArgumentException("Could not get device's ABIs");
} else {
throw new IllegalArgumentException(String.format("Device %s does't support %s",
mDevice.getSerialNumber(), mAbiName));
}
}
CLog.logAndDisplay(LogLevel.INFO, "ABIs: %s", abiSet);
setupTestModules(abiSet);
// TODO(stuartscott): Enable skipping of deviceinfo
// if (mSkipDeviceInfo) {
// remove device info from modules
// }
int moduleCount = mModules.size();
CLog.logAndDisplay(LogLevel.INFO, "Start test run of %d module%s", moduleCount,
(moduleCount > 1) ? "s" : "");
for (int i = mLastModuleIndex; i < moduleCount; i++) {
IModuleDef module = mModules.get(i);
module.setBuild(mBuild);
module.setDevice(mDevice);
module.run(listener);
// Track of the last complete test package index for resume
mLastModuleIndex = i;
}
} catch (DeviceNotAvailableException e) {
// Pass up
throw e;
} catch (RuntimeException e) {
CLog.logAndDisplay(LogLevel.ERROR, "Exception: %s", e.getMessage());
CLog.e(e);
} catch (Error e) {
CLog.logAndDisplay(LogLevel.ERROR, "Error: %s", e.getMessage());
CLog.e(e);
}
}
/**
* Set {@code mModules} to the list of test modules to run.
* @param abis
*/
private void setupTestModules(Set<IAbi> abis) {
if (!mModules.isEmpty()) {
CLog.d("Resume tests using existing module list");
return;
}
if (mRetrySessionId != null) {
// We're retrying so clear the filters
mIncludeFilters.clear();
mExcludeFilters.clear();
mModuleName = null;
mTestName = null;
// Load the invocation result
IInvocationResultRepo repo;
IInvocationResult result = null;
try {
repo = new InvocationResultRepo(mBuildHelper.getResultsDir());
result = repo.getResult(mRetrySessionId);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if (result == null) {
throw new IllegalArgumentException(String.format(
"Could not find session with id %d", mRetrySessionId));
}
// Append each test that failed or was not executed to the filters
for (IModuleResult module : result.getModules()) {
for (ICaseResult cr : module.getResults()) {
for (TestStatus status : RETRY_TEST_STATUS) {
for (ITestResult r : cr.getResults(status)) {
// Create the filter for the test to be run.
mIncludeFilters.add(new TestFilter(module.getAbi(), module.getName(),
r.getFullName()).toString());
}
}
}
}
}
if (mModuleName != null) {
mIncludeFilters.clear();
mIncludeFilters.add(new TestFilter(mAbiName, mModuleName, mTestName).toString());
if (mTestName != null) {
// We're filtering it down to the lowest level, no need to give excludes
mExcludeFilters.clear();
} else {
// If we dont specify a test name, we only want to run this module with any
// exclusions defined by the plan as long as they dont exclude the whole module.
List<String> excludeFilters = new ArrayList<>();
for (String excludeFilter : mExcludeFilters) {
TestFilter filter = TestFilter.createFrom(excludeFilter);
String name = filter.getName();
// Add the filter if it applies to this module, and it has a test name
if ((mModuleName.equals(name) || mModuleName.matches(name) ||
name.matches(mModuleName)) && filter.getTest() != null) {
excludeFilters.add(excludeFilter);
}
}
mExcludeFilters = excludeFilters;
}
}
try {
// Collect ALL tests
IModuleRepo testRepo = new ModuleRepo(mBuildHelper.getTestsDir(), abis);
List<IModuleDef> modules = testRepo.getModules(mIncludeFilters, mExcludeFilters);
// Filter by shard
int numTestmodules = modules.size();
int totalShards = Math.min(mTotalShards, numTestmodules);
mModules.clear();
for (int i = mShardAssignment; i < numTestmodules; i += totalShards) {
mModules.add(modules.get(i));
}
} catch (FileNotFoundException e) {
CLog.e(e);
}
}
/**
* Gets the set of ABIs supported by both Compatibility and the device under test
* @return The set of ABIs to run the tests on
* @throws DeviceNotAvailableException
*/
Set<IAbi> getAbis() throws DeviceNotAvailableException {
Set<IAbi> abis = new HashSet<>();
for (String abi : AbiFormatter.getSupportedAbis(mDevice, "")) {
// Only test against ABIs supported by Compatibility, and if the --abi option was given,
// it must match.
if (AbiUtils.isAbiSupportedByCompatibility(abi)
&& (mAbiName == null || mAbiName.equals(abi))) {
abis.add(new Abi(abi, AbiUtils.getBitness(abi)));
}
}
return abis;
}
/**
* {@inheritDoc}
*/
@Override
public Collection<IRemoteTest> split() {
if (mShards <= 1) {
return null;
}
List<IRemoteTest> shardQueue = new LinkedList<>();
for (int shardAssignment = 0; shardAssignment < mShards; shardAssignment++) {
CompatibilityTest test = new CompatibilityTest(shardAssignment, mShards /* total */);
OptionCopier.copyOptionsNoThrow(this, test);
// Set the shard count because the copy option on the previous line copies
// over the mShard value
test.mShards = 0;
shardQueue.add(test);
}
return shardQueue;
}
}