blob: 2c949ae8d7f4508443f4943980cba48f8af4bb6e [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.testtype;
import com.android.ddmlib.IShellOutputReceiver;
import com.android.ddmlib.Log;
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.result.ITestInvocationListener;
import com.android.tradefed.result.ITestLifeCycleReceiver;
import java.util.concurrent.TimeUnit;
/**
* A Test that runs a system fuzz test package (part of Vendor Test Suite, VTS) on given device.
*/
@OptionClass(alias = "vtsfuzztest")
public class VtsFuzzTest implements IDeviceTest, IRemoteTest {
private static final String LOG_TAG = "VtsFuzzTest";
static final String DEFAULT_FUZZER_BINARY_PATH = "/system/bin/fuzzer";
static final float DEFAULT_TARGET_VERSION = -1;
static final int DEFAULT_EPOCH_COUNT = 10;
// fuzzer flags
private static final String VTS_FUZZ_TEST_FLAG_CLASS = "--class";
private static final String VTS_FUZZ_TEST_FLAG_TYPE = "--type";
private static final String VTS_FUZZ_TEST_FLAG_VERSION = "--version";
private static final String VTS_FUZZ_TEST_FLAG_EPOCH_COUNT = "--epoch_count";
private ITestDevice mDevice = null;
@Option(name = "fuzzer-binary-path",
description="The path on the device where fuzzer is located.")
private String mFuzzerBinaryPath = DEFAULT_FUZZER_BINARY_PATH;
@Option(name = "target-component-path",
description="The path of a target component (e.g., .so file path).")
private String mTargetComponentPath = null;
@Option(name = "target-class",
description="The target component class.")
private String mTargetClass = null;
@Option(name = "target-type",
description="The target component type.")
private String mTargetType = null;
@Option(name = "target-version",
description="The target component version.")
private float mTargetVersion = DEFAULT_TARGET_VERSION;
@Option(name = "epoch-count",
description="The epoch count.")
private int mEpochCount = DEFAULT_EPOCH_COUNT;
@Option(name = "test-timeout", description =
"The max time in ms for a VTS test to run. " +
"Test run will be aborted if any test takes longer.")
private int mMaxTestTimeMs = 15 * 60 * 1000; // 15 minutes
@Option(name = "runtime-hint",
isTimeVal=true,
description="The hint about the test's runtime.")
private long mRuntimeHint = 5 * 60 * 1000; // 5 minutes
/**
* {@inheritDoc}
*/
@Override
public void setDevice(ITestDevice device) {
mDevice = device;
}
/**
* {@inheritDoc}
*/
@Override
public ITestDevice getDevice() {
return mDevice;
}
/**
* Set the target component path variable.
*
* @param path The path to set.
*/
public void setTargetComponentPath(String path) {
mTargetComponentPath = path;
}
/**
* Get the target component path variable.
*
* @return The target component path.
*/
public String getTargetComponentPath() {
return mTargetComponentPath;
}
/**
* Set the target class variable.
*
* @param class_name The class name to set.
*/
public void setTargetClass(String class_name) {
mTargetClass = class_name;
}
/**
* Get the target class name variable.
*
* @return The target class name.
*/
public String getTargetClass() {
return mTargetClass;
}
/**
* Set the target type variable.
*
* @param type_name The type name to set.
*/
public void setTargetType(String type_name) {
mTargetType = type_name;
}
/**
* Get the target type name variable.
*
* @return The target type name.
*/
public String getTargetType() {
return mTargetType;
}
/**
* Set the target version variable.
*
* @param version The version to set.
*/
public void setTargetVersion(float version) {
mTargetVersion = version;
}
/**
* Get the target version variable.
*
* @return The version.
*/
public float getTargetVersion() {
return mTargetVersion;
}
/**
* Set the epoch count variable.
*
* @param count The epoch count to set.
*/
public void setEpochCount(int count) {
mEpochCount = count;
}
/**
* Get the epoch count variable.
*
* @return The epoch count.
*/
public float getEpochCount() {
return mEpochCount;
}
/**
* Helper to get all the VtsFuzzTest flags to pass into the adb shell command.
*
* @return the {@link String} of all the VtsFuzzTest flags that should be passed to the VtsFuzzTest
* @throws IllegalArgumentException
*/
private String getAllVtsFuzzTestFlags() {
String flags;
if (mTargetClass == null) {
throw new IllegalArgumentException(VTS_FUZZ_TEST_FLAG_CLASS + " must be set.");
}
flags = String.format("%s=%s", VTS_FUZZ_TEST_FLAG_CLASS, mTargetClass);
if (mTargetType == null) {
throw new IllegalArgumentException(VTS_FUZZ_TEST_FLAG_TYPE + " must be set.");
}
flags = String.format("%s %s=%s", flags, VTS_FUZZ_TEST_FLAG_TYPE, mTargetType);
if (mTargetVersion == DEFAULT_TARGET_VERSION) {
throw new IllegalArgumentException(VTS_FUZZ_TEST_FLAG_VERSION + " must be set.");
}
flags = String.format("%s %s=%s", flags, VTS_FUZZ_TEST_FLAG_VERSION, mTargetVersion);
flags = String.format("%s %s=%s", flags, VTS_FUZZ_TEST_FLAG_EPOCH_COUNT, mEpochCount);
flags = String.format("%s %s", flags, mTargetComponentPath);
return flags;
}
/**
* Run the given fuzzer binary using the given flags
*
* @param testDevice the {@link ITestDevice}
* @param resultParser the test run output parser
* @param fullPath absolute file system path to gtest binary on device
* @param flags fuzzer execution flags
* @throws DeviceNotAvailableException
*/
private void runTest(final ITestDevice testDevice, final IShellOutputReceiver resultParser,
final String fullPath, final String flags) throws DeviceNotAvailableException {
// TODO: add individual test timeout support, and rerun support
try {
String cmd = getVtsFuzzTestCmdLine(fullPath, flags);
testDevice.executeShellCommand(cmd, resultParser,
mMaxTestTimeMs,
TimeUnit.MILLISECONDS,
0 /* retryAttempts */);
} catch (DeviceNotAvailableException e) {
// TODO: consider moving the flush of parser data on exceptions to TestDevice or
// AdbHelper
resultParser.flush();
throw e;
} catch (RuntimeException e) {
resultParser.flush();
throw e;
}
}
/**
* Helper method to build the fuzzer command to run.
*
* @param fullPath absolute file system path to fuzzer binary on device
* @param flags fuzzer execution flags
* @return the shell command line to run for the fuzzer
*/
protected String getVtsFuzzTestCmdLine(String fullPath, String flags) {
StringBuilder cmdLine = new StringBuilder();
cmdLine.append(String.format("%s %s", fullPath, flags));
return cmdLine.toString();
}
/**
* Factory method for creating a {@link IShellOutputReceiver} that parses test output and
* forwards results to the result listener.
* <p/>
* Exposed so unit tests can mock
*
* @param listener
* @param runName
* @return a {@link IShellOutputReceiver}
*/
IShellOutputReceiver createResultParser(String runName, ITestLifeCycleReceiver listener) {
VtsFuzzTestResultParser resultParser = new VtsFuzzTestResultParser(runName, listener);
return resultParser;
}
/**
* {@inheritDoc}
*/
@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
if (mDevice == null) {
throw new IllegalArgumentException("Device has not been set");
}
if (!mDevice.doesFileExist(mFuzzerBinaryPath)) {
Log.w(LOG_TAG, String.format("Could not find fuzzer binary file %s in %s!",
mFuzzerBinaryPath, mDevice.getSerialNumber()));
return;
}
IShellOutputReceiver resultParser = createResultParser(mTargetComponentPath, listener);
String flags = getAllVtsFuzzTestFlags();
Log.i(LOG_TAG, String.format("Running fuzzer '%s %s' on %s",
mFuzzerBinaryPath, flags, mDevice.getSerialNumber()));
runTest(mDevice, resultParser, mFuzzerBinaryPath, flags);
}
}