1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tradefed.testtype; 18 19 import com.android.ddmlib.IShellOutputReceiver; 20 import com.android.ddmlib.Log; 21 import com.android.ddmlib.testrunner.ITestRunListener; 22 import com.android.tradefed.config.Option; 23 import com.android.tradefed.config.OptionClass; 24 import com.android.tradefed.device.DeviceNotAvailableException; 25 import com.android.tradefed.device.ITestDevice; 26 import com.android.tradefed.result.ITestInvocationListener; 27 28 import java.util.concurrent.TimeUnit; 29 30 /** 31 * A Test that runs a system fuzz test package (part of Vendor Test Suite, VTS) on given device. 32 */ 33 @OptionClass(alias = "vtsfuzztest") 34 public class VtsFuzzTest implements IDeviceTest, IRemoteTest { 35 36 private static final String LOG_TAG = "VtsFuzzTest"; 37 static final String DEFAULT_FUZZER_BINARY_PATH = "/system/bin/fuzzer"; 38 39 static final float DEFAULT_TARGET_VERSION = -1; 40 static final int DEFAULT_EPOCH_COUNT = 10; 41 42 // fuzzer flags 43 private static final String VTS_FUZZ_TEST_FLAG_CLASS = "--class"; 44 private static final String VTS_FUZZ_TEST_FLAG_TYPE = "--type"; 45 private static final String VTS_FUZZ_TEST_FLAG_VERSION = "--version"; 46 private static final String VTS_FUZZ_TEST_FLAG_EPOCH_COUNT = "--epoch_count"; 47 48 private ITestDevice mDevice = null; 49 50 @Option(name = "fuzzer-binary-path", 51 description="The path on the device where fuzzer is located.") 52 private String mFuzzerBinaryPath = DEFAULT_FUZZER_BINARY_PATH; 53 54 @Option(name = "target-component-path", 55 description="The path of a target component (e.g., .so file path).") 56 private String mTargetComponentPath = null; 57 58 @Option(name = "target-class", 59 description="The target component class.") 60 private String mTargetClass = null; 61 62 @Option(name = "target-type", 63 description="The target component type.") 64 private String mTargetType = null; 65 66 @Option(name = "target-version", 67 description="The target component version.") 68 private float mTargetVersion = DEFAULT_TARGET_VERSION; 69 70 @Option(name = "epoch-count", 71 description="The epoch count.") 72 private int mEpochCount = DEFAULT_EPOCH_COUNT; 73 74 @Option(name = "test-timeout", description = 75 "The max time in ms for a VTS test to run. " + 76 "Test run will be aborted if any test takes longer.") 77 private int mMaxTestTimeMs = 15 * 60 * 1000; // 15 minutes 78 79 @Option(name = "runtime-hint", 80 isTimeVal=true, 81 description="The hint about the test's runtime.") 82 private long mRuntimeHint = 5 * 60 * 1000; // 5 minutes 83 84 /** 85 * {@inheritDoc} 86 */ 87 @Override 88 public void setDevice(ITestDevice device) { 89 mDevice = device; 90 } 91 92 /** 93 * {@inheritDoc} 94 */ 95 @Override 96 public ITestDevice getDevice() { 97 return mDevice; 98 } 99 100 /** 101 * Set the target component path variable. 102 * 103 * @param path The path to set. 104 */ 105 public void setTargetComponentPath(String path) { 106 mTargetComponentPath = path; 107 } 108 109 /** 110 * Get the target component path variable. 111 * 112 * @return The target component path. 113 */ 114 public String getTargetComponentPath() { 115 return mTargetComponentPath; 116 } 117 118 /** 119 * Set the target class variable. 120 * 121 * @param class_name The class name to set. 122 */ 123 public void setTargetClass(String class_name) { 124 mTargetClass = class_name; 125 } 126 127 /** 128 * Get the target class name variable. 129 * 130 * @return The target class name. 131 */ 132 public String getTargetClass() { 133 return mTargetClass; 134 } 135 136 /** 137 * Set the target type variable. 138 * 139 * @param type_name The type name to set. 140 */ 141 public void setTargetType(String type_name) { 142 mTargetType = type_name; 143 } 144 145 /** 146 * Get the target type name variable. 147 * 148 * @return The target type name. 149 */ 150 public String getTargetType() { 151 return mTargetType; 152 } 153 154 /** 155 * Set the target version variable. 156 * 157 * @param version The version to set. 158 */ 159 public void setTargetVersion(float version) { 160 mTargetVersion = version; 161 } 162 163 /** 164 * Get the target version variable. 165 * 166 * @return The version. 167 */ 168 public float getTargetVersion() { 169 return mTargetVersion; 170 } 171 172 /** 173 * Set the epoch count variable. 174 * 175 * @param count The epoch count to set. 176 */ 177 public void setEpochCount(int count) { 178 mEpochCount = count; 179 } 180 181 /** 182 * Get the epoch count variable. 183 * 184 * @return The epoch count. 185 */ 186 public float getEpochCount() { 187 return mEpochCount; 188 } 189 190 /** 191 * Helper to get all the VtsFuzzTest flags to pass into the adb shell command. 192 * 193 * @return the {@link String} of all the VtsFuzzTest flags that should be passed to the VtsFuzzTest 194 * @throws IllegalArgumentException 195 */ 196 private String getAllVtsFuzzTestFlags() { 197 String flags; 198 199 if (mTargetClass == null) { 200 throw new IllegalArgumentException(VTS_FUZZ_TEST_FLAG_CLASS + " must be set."); 201 } 202 flags = String.format("%s=%s", VTS_FUZZ_TEST_FLAG_CLASS, mTargetClass); 203 204 if (mTargetType == null) { 205 throw new IllegalArgumentException(VTS_FUZZ_TEST_FLAG_TYPE + " must be set."); 206 } 207 flags = String.format("%s %s=%s", flags, VTS_FUZZ_TEST_FLAG_TYPE, mTargetType); 208 209 if (mTargetVersion == DEFAULT_TARGET_VERSION) { 210 throw new IllegalArgumentException(VTS_FUZZ_TEST_FLAG_VERSION + " must be set."); 211 } 212 flags = String.format("%s %s=%s", flags, VTS_FUZZ_TEST_FLAG_VERSION, mTargetVersion); 213 214 flags = String.format("%s %s=%s", flags, VTS_FUZZ_TEST_FLAG_EPOCH_COUNT, mEpochCount); 215 216 flags = String.format("%s %s", flags, mTargetComponentPath); 217 return flags; 218 } 219 220 /** 221 * Run the given fuzzer binary using the given flags 222 * 223 * @param testDevice the {@link ITestDevice} 224 * @param resultParser the test run output parser 225 * @param fullPath absolute file system path to gtest binary on device 226 * @param flags fuzzer execution flags 227 * @throws DeviceNotAvailableException 228 */ 229 private void runTest(final ITestDevice testDevice, final IShellOutputReceiver resultParser, 230 final String fullPath, final String flags) throws DeviceNotAvailableException { 231 // TODO: add individual test timeout support, and rerun support 232 try { 233 String cmd = getVtsFuzzTestCmdLine(fullPath, flags); 234 testDevice.executeShellCommand(cmd, resultParser, 235 mMaxTestTimeMs, 236 TimeUnit.MILLISECONDS, 237 0 /* retryAttempts */); 238 } catch (DeviceNotAvailableException e) { 239 // TODO: consider moving the flush of parser data on exceptions to TestDevice or 240 // AdbHelper 241 resultParser.flush(); 242 throw e; 243 } catch (RuntimeException e) { 244 resultParser.flush(); 245 throw e; 246 } 247 } 248 249 /** 250 * Helper method to build the fuzzer command to run. 251 * 252 * @param fullPath absolute file system path to fuzzer binary on device 253 * @param flags fuzzer execution flags 254 * @return the shell command line to run for the fuzzer 255 */ 256 protected String getVtsFuzzTestCmdLine(String fullPath, String flags) { 257 StringBuilder cmdLine = new StringBuilder(); 258 cmdLine.append(String.format("%s %s", fullPath, flags)); 259 return cmdLine.toString(); 260 } 261 262 /** 263 * Factory method for creating a {@link IShellOutputReceiver} that parses test output and 264 * forwards results to the result listener. 265 * <p/> 266 * Exposed so unit tests can mock 267 * 268 * @param listener 269 * @param runName 270 * @return a {@link IShellOutputReceiver} 271 */ 272 IShellOutputReceiver createResultParser(String runName, ITestRunListener listener) { 273 VtsFuzzTestResultParser resultParser = new VtsFuzzTestResultParser(runName, listener); 274 return resultParser; 275 } 276 277 /** 278 * {@inheritDoc} 279 */ 280 @Override 281 public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 282 if (mDevice == null) { 283 throw new IllegalArgumentException("Device has not been set"); 284 } 285 286 if (!mDevice.doesFileExist(mFuzzerBinaryPath)) { 287 Log.w(LOG_TAG, String.format("Could not find fuzzer binary file %s in %s!", 288 mFuzzerBinaryPath, mDevice.getSerialNumber())); 289 return; 290 } 291 292 IShellOutputReceiver resultParser = createResultParser(mTargetComponentPath, listener); 293 String flags = getAllVtsFuzzTestFlags(); 294 Log.i(LOG_TAG, String.format("Running fuzzer '%s %s' on %s", 295 mFuzzerBinaryPath, flags, mDevice.getSerialNumber())); 296 runTest(mDevice, resultParser, mFuzzerBinaryPath, flags); 297 } 298 } 299