1 /* 2 * Copyright (C) 2017 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.testrunner.ITestRunListener; 20 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 21 import com.android.tradefed.build.IBuildInfo; 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.log.LogUtil.CLog; 27 import com.android.ddmlib.Log.LogLevel; 28 import com.android.tradefed.result.ITestInvocationListener; 29 import com.android.tradefed.util.ArrayUtil; 30 import com.android.tradefed.util.CommandResult; 31 import com.android.tradefed.util.CommandStatus; 32 import com.android.tradefed.util.FileUtil; 33 import com.android.tradefed.util.StreamUtil; 34 import com.android.tradefed.util.JsonUtil; 35 import com.android.tradefed.util.IRunUtil; 36 import com.android.tradefed.util.RunUtil; 37 import com.android.tradefed.util.VtsDashboardUtil; 38 import com.android.tradefed.util.VtsVendorConfigFileUtil; 39 import com.android.tradefed.testtype.IAbi; 40 41 import org.json.JSONArray; 42 import org.json.JSONObject; 43 import org.json.JSONException; 44 45 import java.io.FileReader; 46 import java.io.BufferedReader; 47 import java.io.BufferedWriter; 48 49 import java.io.File; 50 import java.io.FileNotFoundException; 51 import java.io.FileWriter; 52 import java.io.IOException; 53 import java.io.InputStream; 54 import java.io.PrintWriter; 55 import java.nio.file.Paths; 56 import java.util.TreeSet; 57 import java.util.Set; 58 import java.util.Collection; 59 import java.util.ArrayList; 60 61 /** 62 * A Test that runs a vts multi device test package (part of Vendor Test Suite, 63 * VTS) on given device. 64 */ 65 66 @OptionClass(alias = "vtsmultidevicetest") 67 public class VtsMultiDeviceTest implements IDeviceTest, IRemoteTest, ITestFilterReceiver, 68 IRuntimeHintProvider, ITestCollector, IBuildReceiver, IAbiReceiver { 69 70 static final String ANDROIDDEVICE = "AndroidDevice"; 71 static final String BUILD = "build"; 72 static final String BUILD_ID = "build_id"; 73 static final String BUILD_TARGET = "build_target"; 74 static final String COVERAGE_PROPERTY = "ro.vts.coverage"; 75 static final String DATA_FILE_PATH = "data_file_path"; 76 static final String LOG_PATH = "log_path"; 77 static final String NAME = "name"; 78 static final String OS_NAME = "os.name"; 79 static final String WINDOWS = "Windows"; 80 static final String PYTHONPATH = "PYTHONPATH"; 81 static final String SERIAL = "serial"; 82 static final String TESTMODULE = "TestModule"; 83 static final String TEST_PLAN_REPORT_FILE = "TEST_PLAN_REPORT_FILE"; 84 static final String TEST_SUITE = "test_suite"; 85 static final String TEST_MAX_TIMEOUT = "test_max_timeout"; 86 static final String VIRTUAL_ENV_PATH = "VIRTUALENVPATH"; 87 static final String ABI_NAME = "abi_name"; 88 static final String ABI_BITNESS = "abi_bitness"; 89 static final String SKIP_ON_32BIT_ABI = "skip_on_32bit_abi"; 90 static final String SKIP_ON_64BIT_ABI = "skip_on_64bit_abi"; 91 static final String SKIP_IF_THERMAL_THROTTLING = "skip_if_thermal_throttling"; 92 static final String RUN_32BIT_ON_64BIT_ABI = "run_32bit_on_64bit_abi"; 93 static final String VTS = "vts"; 94 static final String CONFIG_FILE_EXTENSION = ".config"; 95 static final String INCLUDE_FILTER = "include_filter"; 96 static final String EXCLUDE_FILTER = "exclude_filter"; 97 static final String EXCLUDE_OVER_INCLUDE = "exclude_over_include"; 98 static final String BINARY_TEST_SOURCE = "binary_test_source"; 99 static final String BINARY_TEST_WORKING_DIRECTORY = "binary_test_working_directory"; 100 static final String BINARY_TEST_ENVP = "binary_test_envp"; 101 static final String BINARY_TEST_ARGS = "binary_test_args"; 102 static final String BINARY_TEST_LD_LIBRARY_PATH = "binary_test_ld_library_path"; 103 static final String BINARY_TEST_PROFILING_LIBRARY_PATH = "binary_test_profiling_library_path"; 104 static final String BINARY_TEST_DISABLE_FRAMEWORK = "binary_test_disable_framework"; 105 static final String BINARY_TEST_STOP_NATIVE_SERVERS = "binary_test_stop_native_servers"; 106 static final String BINARY_TEST_TYPE_GTEST = "gtest"; 107 static final String BINARY_TEST_TYPE_LLVMFUZZER = "llvmfuzzer"; 108 static final String BINARY_TEST_TYPE_HAL_HIDL_GTEST = "hal_hidl_gtest"; 109 static final String BINARY_TEST_TYPE_HAL_HIDL_REPLAY_TEST = "hal_hidl_replay_test"; 110 static final String BINARY_TEST_TYPE_HOST_BINARY_TEST = "host_binary_test"; 111 static final String BUG_REPORT_ON_FAILURE = "bug_report_on_failure"; 112 static final String ENABLE_COVERAGE = "enable_coverage"; 113 static final String ENABLE_PROFILING = "enable_profiling"; 114 static final String GTEST_BATCH_MODE = "gtest_batch_mode"; 115 static final String SAVE_TRACE_FIEL_REMOTE = "save_trace_file_remote"; 116 static final String OUTPUT_COVERAGE_REPORT = "output_coverage_report"; 117 static final String GLOBAL_COVERAGE = "global_coverage"; 118 static final String LTP_NUMBER_OF_THREADS = "ltp_number_of_threads"; 119 static final String NATIVE_SERVER_PROCESS_NAME = "native_server_process_name"; 120 static final String PASSTHROUGH_MODE = "passthrough_mode"; 121 static final String PRECONDITION_HWBINDER_SERVICE = "precondition_hwbinder_service"; 122 static final String PRECONDITION_FEATURE = "precondition_feature"; 123 static final String PRECONDITION_FILE_PATH_PREFIX = "precondition_file_path_prefix"; 124 static final String PRECONDITION_LSHAL = "precondition_lshal"; 125 static final String PRECONDITION_VINTF = "precondition_vintf"; 126 static final String ENABLE_SYSTRACE = "enable_systrace"; 127 static final String HAL_HIDL_REPLAY_TEST_TRACE_PATHS = "hal_hidl_replay_test_trace_paths"; 128 static final String HAL_HIDL_PACKAGE_NAME = "hal_hidl_package_name"; 129 static final String REPORT_MESSAGE_FILE_NAME = "report_proto.msg"; 130 static final String SYSTRACE_PROCESS_NAME = "systrace_process_name"; 131 static final String TEMPLATE_BINARY_TEST_PATH = "vts/testcases/template/binary_test/binary_test"; 132 static final String TEMPLATE_GTEST_BINARY_TEST_PATH = "vts/testcases/template/gtest_binary_test/gtest_binary_test"; 133 static final String TEMPLATE_LLVMFUZZER_TEST_PATH = "vts/testcases/template/llvmfuzzer_test/llvmfuzzer_test"; 134 static final String TEMPLATE_HAL_HIDL_GTEST_PATH = "vts/testcases/template/hal_hidl_gtest/hal_hidl_gtest"; 135 static final String TEMPLATE_HAL_HIDL_REPLAY_TEST_PATH = "vts/testcases/template/hal_hidl_replay_test/hal_hidl_replay_test"; 136 static final String TEMPLATE_HOST_BINARY_TEST_PATH = "vts/testcases/template/host_binary_test/host_binary_test"; 137 static final long TEST_ABORT_TIMEOUT_MSECS = 1000 * 15; 138 static final String TEST_RUN_SUMMARY_FILE_NAME = "test_run_summary.json"; 139 static final float DEFAULT_TARGET_VERSION = -1; 140 static final String DEFAULT_TESTCASE_CONFIG_PATH = "vts/tools/vts-tradefed/res/default/DefaultTestCase.config"; 141 142 private ITestDevice mDevice = null; 143 private IAbi mAbi = null; 144 145 @Option(name = "test-timeout", 146 description = "The amount of time (in milliseconds) for a test invocation. " 147 + "If the test cannot finish before timeout, it should interrupt itself and " 148 + "clean up in " + TEST_ABORT_TIMEOUT_MSECS + "ms. Hence the actual timeout " 149 + "is the specified value + " + TEST_ABORT_TIMEOUT_MSECS + "ms.", 150 isTimeVal = true) 151 private long mTestTimeout = 1000 * 60 * 60 * 3; 152 153 @Option(name = "test-module-name", 154 description = "The name for a test module.") 155 private String mTestModuleName = null; 156 157 @Option(name = "test-case-path", 158 description = "The path for test case.") 159 private String mTestCasePath = null; 160 161 @Option(name = "test-config-path", 162 description = "The path for test case config file.") 163 private String mTestConfigPath = null; 164 165 @Option(name = "precondition-hwbinder-service", 166 description = "The name of a HW binder service needed to run the test.") 167 private String mPreconditionHwBinderServiceName = null; 168 169 @Option(name = "precondition-feature", 170 description = "The name of a `pm`-listable feature needed to run the test.") 171 private String mPreconditionFeature = null; 172 173 @Option(name = "precondition-file-path-prefix", 174 description = "The path prefix of a file (e.g., shared lib) needed to run the test.") 175 private String mPreconditionFilePathPrefix = null; 176 177 @Option(name = "precondition-lshal", 178 description = "The name of a `lshal`-listable feature needed to run the test.") 179 private String mPreconditionLshal = null; 180 181 @Option(name = "precondition-vintf", 182 description = "The full name of a HAL specified in vendor/manifest.xml and " 183 + "needed to run the test (e.g., android.hardware.graphics.mapper@2.0). " 184 + "this can override precondition-lshal option.") 185 private String mPreconditionVintf = null; 186 187 @Option(name = "precondition-vintf-override", 188 description = "If precondition-lshal is present and precondition-vintf is not, " 189 + "set precondition-vintf to the value of precondition-lshal. " 190 + "The test runner will find the HAL in manifest.xml instead of lshal.") 191 private boolean mPreconditionVintfOverride = false; 192 193 @Option(name = "use-stdout-logs", 194 description = "Flag that determines whether to use std:out to parse output.") 195 private boolean mUseStdoutLogs = false; 196 197 @Option(name = "include-filter", 198 description = "The positive filter of the test names to run.") 199 private Set<String> mIncludeFilters = new TreeSet<>(); 200 201 @Option(name = "exclude-filter", 202 description = "The negative filter of the test names to run.") 203 private Set<String> mExcludeFilters = new TreeSet<>(); 204 205 @Option(name = "exclude-over-include", 206 description = "The negative filter of the test names to run.") 207 private boolean mExcludeOverInclude = false; 208 209 @Option(name = "runtime-hint", description = "The hint about the test's runtime.", 210 isTimeVal = true) 211 private long mRuntimeHint = 60000; // 1 minute 212 213 @Option(name = "enable-profiling", description = "Enable profiling for the tests.") 214 private boolean mEnableProfiling = false; 215 216 @Option(name = "save-trace-file-remote", 217 description = "Whether to save the trace file in remote storage.") 218 private boolean mSaveTraceFileRemote = false; 219 220 @Option(name = "enable-systrace", description = "Enable systrace for the tests.") 221 private boolean mEnableSystrace = false; 222 223 @Option(name = "enable-coverage", 224 description = "Enable coverage for the tests. In order for coverage to be measured, " + 225 "ro.vts.coverage system must have value \"1\" to indicate the target " + 226 "build is coverage instrumented.") 227 private boolean mEnableCoverage = true; 228 229 @Option(name = "global-coverage", description = "True to measure coverage for entire test, " 230 + "measure coverage for each test case otherwise. Currently, only global " 231 + "coverage is supported for binary tests") 232 private boolean mGlobalCoverage = true; 233 234 @Option(name = "output-coverage-report", description = "Whether to store raw coverage report.") 235 private boolean mOutputCoverageReport = false; 236 237 // Another design option is to parse a string or use enum for host preference on BINDER, 238 // PASSTHROUGH and DEFAULT(which is BINDER). Also in the future, we might want to deal with 239 // the case of target preference on PASSTHROUGH (if host does not specify to use BINDER mode). 240 @Option(name = "passthrough-mode", description = "Set getStub to use passthrough mode. " 241 + "Value true means use passthrough mode if available; false for binderized mode if " 242 + "available. Default is false") 243 private boolean mPassthroughMode = false; 244 245 @Option(name = "ltp-number-of-threads", 246 description = "Number of threads to run the LTP test cases. " 247 + "0 means using number of avaiable CPU threads.") 248 private int mLtpNumberOfThreads = -1; 249 250 @Option(name = "skip-on-32bit-abi", 251 description = "Whether to skip tests on 32bit ABI.") 252 private boolean mSkipOn32BitAbi = false; 253 254 @Option(name = "skip-on-64bit-abi", 255 description = "Whether to skip tests on 64bit ABI.") 256 private boolean mSkipOn64BitAbi = false; 257 258 @Option(name = "skip-if-thermal-throttling", 259 description = "Whether to skip tests if target device suffers from thermal throttling.") 260 private boolean mSkipIfThermalThrottling = false; 261 262 @Option(name = "run-32bit-on-64bit-abi", 263 description = "Whether to run 32bit tests on 64bit ABI.") 264 private boolean mRun32bBitOn64BitAbi = false; 265 266 @Option(name = "binary-test-source", 267 description = "Binary test source paths relative to vts testcase directory on host." 268 + "Format of tags:" 269 + " <source>: source without tag." 270 + " <tag>::<source>: source with tag. Can be used to separate 32bit and 64" 271 + " bit tests with same file name." 272 + " <tag1>::<source1>, <tag1>::<source2>, <tag2>::<source3>: multiple" 273 + " sources connected by comma. White spaces in-between" 274 + " will be ignored." 275 + "Format of each source string:" 276 + " <source file>: push file and create test case." 277 + " Source is relative path of source file under vts's testcases" 278 + " folder. Source file will be pushed to working directory" 279 + " discarding original directory structure, and test cases will" 280 + " be created using the pushed file." 281 + " <source file>-><destination file>: push file and create test case." 282 + " Destination path is absolute path on device. Test cases will" 283 + " be created using the pushed file." 284 + " <source file>->: push file only." 285 + " Push the source file to its' tag's corresponding" 286 + " working directory. Test case will not be created on" 287 + " this file. This is equivalent to but simpler than specifying a" 288 + " working directory for the tag and use VtsFilePusher to push the" 289 + " file to the directory." 290 + " -><destination file>: create test case only." 291 + " Destination is absolute device side path." 292 + " Note: each path in source string can be a directory. However, the" 293 + " default binary test runner and gtest binary test runner does not" 294 + " support creating test cases from a directory. You will need to" 295 + " override the binary test runner's CreateTestCase method in python." 296 + " If you wish to push a source file to a specific destination and not" 297 + " create a test case from it, please use VtsFilePusher.") 298 private Collection<String> mBinaryTestSource = new ArrayList<>(); 299 300 @Option(name = "binary-test-working-directory", description = "Working directories for binary " 301 + "tests. Tags can be added to the front of each directory using '::' as delimiter. " 302 + "However, each tag should only has one working directory. This option is optional for " 303 + "binary tests. If not specified, different directories will be used for files with " 304 + "different tags.") 305 private Collection<String> mBinaryTestWorkingDirectory = new ArrayList<>(); 306 307 @Option(name = "binary-test-envp", description = "Additional environment path for binary " 308 + "tests. Tags can be added to the front of each directory using '::' as delimiter. " 309 + "There can be multiple instances of binary-test-envp for a same tag, which will " 310 + "later automatically be combined.") 311 private Collection<String> mBinaryTestEnvp = new ArrayList<>(); 312 313 @Option(name = "binary-test-args", description = "Additional args or flags for binary " 314 + "tests. Tags can be added to the front of each directory using '::' as delimiter. " 315 + "There can be multiple instances of binary-test-args for a same tag, which will " 316 + "later automatically be combined.") 317 private Collection<String> mBinaryTestArgs = new ArrayList<>(); 318 319 @Option(name = "binary-test-ld-library-path", description = "LD_LIBRARY_PATH for binary " 320 + "tests. Tags can be added to the front of each instance using '::' as delimiter. " 321 + "Multiple directories can be added under a same tag using ':' as delimiter. " 322 + "There can be multiple instances of ld-library-path for a same tag, which will " 323 + "later automatically be combined using ':' as delimiter. Paths without a tag " 324 + "will only used for binaries without tag. This option is optional for binary tests.") 325 private Collection<String> mBinaryTestLdLibraryPath = new ArrayList<>(); 326 327 @Option(name = "binary-test-profiling-library-path", description = "Path to lookup and load " 328 + "profiling libraries for tests with profiling enabled. Tags can be added to the " 329 + "front of each directory using '::' as delimiter. Only one directory could be " 330 + "specified for the same tag. This option is optional for binary tests. If not " 331 + "specified, default directories will be used for files with different tags.") 332 private Collection<String> mBinaryTestProfilingLibraryPath = new ArrayList<>(); 333 334 @Option(name = "binary-test-disable-framework", description = "Adb stop/start before/after test.") 335 private boolean mBinaryTestDisableFramework = false; 336 337 @Option(name = "binary-test-stop-native-servers", 338 description = "Set to stop all properly configured native servers during the testing.") 339 private boolean mBinaryTestStopNativeServers = false; 340 341 @Option(name = "bug-report-on-failure", 342 description = "To catch bugreport zip file at the end of failed test cases. " 343 + "If set to true, a report will be caught through adh shell command at the end of each failed " 344 + "test cases.") 345 private boolean mBugReportOnFailure = false; 346 347 @Option(name = "native-server-process-name", 348 description = "Name of a native server process. The runner checks to make sure " 349 + "each specified native server process is not running after the framework stop.") 350 private Collection<String> mNativeServerProcessName = new ArrayList<>(); 351 352 @Option(name = "binary-test-type", description = "Binary test type. Only specify this when " 353 + "running an extended binary test without a python test file. Available options: gtest") 354 private String mBinaryTestType = ""; 355 356 @Option(name = "hal-hidl-replay-test-trace-path", description = "The path of a trace file to replay.") 357 private Collection<String> mHalHidlReplayTestTracePaths = new ArrayList<>(); 358 359 @Option(name = "hal-hidl-package-name", description = "The name of a target HIDL HAL package " 360 + "e.g., 'android.hardware.light@2.0'.") 361 private String mHalHidlPackageName = null; 362 363 @Option(name = "systrace-process-name", description = "Process name for systrace.") 364 private String mSystraceProcessName = null; 365 366 @Option(name = "collect-tests-only", 367 description = "Only invoke the test binary to collect list of applicable test cases. " 368 + "All test run callbacks will be triggered, but test execution will " 369 + "not be actually carried out.") 370 private boolean mCollectTestsOnly = false; 371 372 @Option(name = "gtest-batch-mode", description = "Run Gtest binaries in batch mode.") 373 private boolean mGtestBatchMode = false; 374 375 // This variable is set in order to include the directory that contains the 376 // python test cases. This is set before calling the method. 377 // {@link #doRunTest(IRunUtil, String, String)}. 378 public String mPythonPath = null; 379 380 @Option(name = "python-binary", description = "python binary to use " 381 + "(optional)") 382 private String mPythonBin = null; 383 private IRunUtil mRunUtil = null; 384 private IBuildInfo mBuildInfo = null; 385 private String mRunName = "VtsHostDrivenTest"; 386 // the path of a dir which contains the test data files. 387 private String mTestCaseDataDir = "./"; 388 389 private VtsVendorConfigFileUtil configReader = null; 390 391 /** 392 * @return the mRunUtil 393 */ getRunUtil()394 public IRunUtil getRunUtil() { 395 return mRunUtil; 396 } 397 398 /** 399 * @param mRunUtil the mRunUtil to set 400 */ setRunUtil(IRunUtil mRunUtil)401 public void setRunUtil(IRunUtil mRunUtil) { 402 this.mRunUtil = mRunUtil; 403 } 404 405 /** 406 * {@inheritDoc} 407 */ 408 @Override setDevice(ITestDevice device)409 public void setDevice(ITestDevice device) { 410 mDevice = device; 411 } 412 413 /** 414 * {@inheritDoc} 415 */ 416 @Override getDevice()417 public ITestDevice getDevice() { 418 return mDevice; 419 } 420 setTestCasePath(String path)421 public void setTestCasePath(String path){ 422 mTestCasePath = path; 423 } 424 setTestConfigPath(String path)425 public void setTestConfigPath(String path){ 426 mTestConfigPath = path; 427 } 428 429 /** 430 * {@inheritDoc} 431 */ 432 @Override addIncludeFilter(String filter)433 public void addIncludeFilter(String filter) { 434 mIncludeFilters.add(cleanFilter(filter)); 435 } 436 437 /** 438 * {@inheritDoc} 439 */ 440 @Override addAllIncludeFilters(Set<String> filters)441 public void addAllIncludeFilters(Set<String> filters) { 442 for (String filter : filters) { 443 mIncludeFilters.add(cleanFilter(filter)); 444 } 445 } 446 447 /** 448 * {@inheritDoc} 449 */ 450 @Override addExcludeFilter(String filter)451 public void addExcludeFilter(String filter) { 452 mExcludeFilters.add(cleanFilter(filter)); 453 } 454 455 /** 456 * {@inheritDoc} 457 */ 458 @Override addAllExcludeFilters(Set<String> filters)459 public void addAllExcludeFilters(Set<String> filters) { 460 for (String filter : filters) { 461 mExcludeFilters.add(cleanFilter(filter)); 462 } 463 } 464 465 /* 466 * Conforms filters using a {@link com.android.ddmlib.testrunner.TestIdentifier} format 467 * to be recognized by the GTest executable. 468 */ cleanFilter(String filter)469 private String cleanFilter(String filter) { 470 return filter.replace('#', '.'); 471 } 472 473 /** 474 * {@inheritDoc} 475 */ 476 @Override getRuntimeHint()477 public long getRuntimeHint() { 478 return mRuntimeHint; 479 } 480 481 /** 482 * {@inheritDoc} 483 */ 484 @Override setCollectTestsOnly(boolean shouldCollectTest)485 public void setCollectTestsOnly(boolean shouldCollectTest) { 486 mCollectTestsOnly = shouldCollectTest; 487 } 488 489 /** 490 * {@inheritDoc} 491 */ 492 @SuppressWarnings("deprecation") 493 @Override run(ITestInvocationListener listener)494 public void run(ITestInvocationListener listener) 495 throws IllegalArgumentException, DeviceNotAvailableException { 496 if (mDevice == null) { 497 throw new DeviceNotAvailableException("Device has not been set"); 498 } 499 500 if (mTestCasePath == null) { 501 if (!mBinaryTestSource.isEmpty()) { 502 String template; 503 switch (mBinaryTestType) { 504 case BINARY_TEST_TYPE_GTEST: 505 template = TEMPLATE_GTEST_BINARY_TEST_PATH; 506 break; 507 case BINARY_TEST_TYPE_HAL_HIDL_GTEST: 508 template = TEMPLATE_HAL_HIDL_GTEST_PATH; 509 break; 510 case BINARY_TEST_TYPE_HOST_BINARY_TEST: 511 template = TEMPLATE_HOST_BINARY_TEST_PATH; 512 break; 513 default: 514 template = TEMPLATE_BINARY_TEST_PATH; 515 } 516 CLog.i("Using default test case template at %s.", template); 517 setTestCasePath(template); 518 if (mEnableCoverage && !mGlobalCoverage) { 519 CLog.e("Only global coverage is supported for test type %s.", mBinaryTestType); 520 throw new RuntimeException("Failed to produce VTS runner test config"); 521 } 522 } else if (mBinaryTestType.equals(BINARY_TEST_TYPE_HAL_HIDL_REPLAY_TEST)) { 523 setTestCasePath(TEMPLATE_HAL_HIDL_REPLAY_TEST_PATH); 524 } else if (mBinaryTestType.equals(BINARY_TEST_TYPE_LLVMFUZZER)) { 525 // Fuzz test don't need test-case-path. 526 setTestCasePath(TEMPLATE_LLVMFUZZER_TEST_PATH); 527 } else { 528 throw new IllegalArgumentException("test-case-path is not set."); 529 } 530 } 531 532 setPythonPath(); 533 534 if (mPythonBin == null) { 535 mPythonBin = getPythonBinary(); 536 } 537 538 if (mRunUtil == null){ 539 mRunUtil = new RunUtil(); 540 mRunUtil.setEnvVariable(PYTHONPATH, mPythonPath); 541 } 542 543 doRunTest(listener); 544 } 545 546 /** 547 * {@inheritDoc} 548 */ 549 @Override setBuild(IBuildInfo buildInfo)550 public void setBuild(IBuildInfo buildInfo) { 551 mBuildInfo = buildInfo; 552 } 553 554 /** 555 * Populate a jsonObject with default fields. 556 * This method uses deepMergeJsonObjects method from JsonUtil to merge the default config file with the target 557 * config file. Field already defined in target config file will not be overwritten. Also, JSONArray will not be 558 * deep merged. 559 * 560 * @param jsonObject the target json object to populate 561 * @param testCaseDataDir data file path 562 * @throws IOException 563 * @throws JSONException 564 */ populateDefaultJsonFields(JSONObject jsonObject, String testCaseDataDir)565 private void populateDefaultJsonFields(JSONObject jsonObject, String testCaseDataDir) 566 throws IOException, JSONException { 567 CLog.i("Populating default fields to json object from %s", DEFAULT_TESTCASE_CONFIG_PATH); 568 String content = FileUtil.readStringFromFile(new File(mTestCaseDataDir, DEFAULT_TESTCASE_CONFIG_PATH)); 569 JSONObject defaultJsonObject = new JSONObject(content); 570 571 JsonUtil.deepMergeJsonObjects(jsonObject, defaultJsonObject); 572 } 573 574 /** 575 * This method reads the provided VTS runner test json config, adds or updates some of its 576 * fields (e.g., to add build info and device serial IDs), and returns the updated json object. 577 * This method calls populateDefaultJsonFields to populate the config JSONObject if the config file is missing 578 * or some required fields is missing from the JSONObject. If test name is not specified, this method 579 * will use the config file's file name file without extension as test name. If config file is missing, this method 580 * will use the test case's class name as test name. 581 * 582 * @param log_path the path of a directory to store the VTS runner logs. 583 * @return the updated JSONObject as the new test config. 584 */ updateVtsRunnerTestConfig(JSONObject jsonObject)585 private void updateVtsRunnerTestConfig(JSONObject jsonObject) 586 throws IOException, JSONException, RuntimeException { 587 configReader = new VtsVendorConfigFileUtil(); 588 if (configReader.LoadVendorConfig(mBuildInfo)) { 589 JSONObject vendorConfigJson = configReader.GetVendorConfigJson(); 590 if (vendorConfigJson != null) { 591 JsonUtil.deepMergeJsonObjects(jsonObject, vendorConfigJson); 592 } 593 } 594 595 CLog.i("Load original test config %s %s", mTestCaseDataDir, mTestConfigPath); 596 String content = null; 597 598 if (mTestConfigPath != null) { 599 content = FileUtil.readStringFromFile( 600 new File(Paths.get(mTestCaseDataDir, mTestConfigPath).toString())); 601 CLog.i("Loaded original test config %s", content); 602 if (content != null) { 603 JsonUtil.deepMergeJsonObjects(jsonObject, new JSONObject(content)); 604 } 605 } 606 607 populateDefaultJsonFields(jsonObject, mTestCaseDataDir); 608 CLog.i("Built a Json object using the loaded original test config"); 609 610 JSONArray deviceArray = new JSONArray(); 611 JSONObject deviceItemObject = new JSONObject(); 612 deviceItemObject.put(SERIAL, mDevice.getSerialNumber()); 613 boolean coverageBuild = false; 614 try { 615 deviceItemObject.put("product_type", mDevice.getProductType()); 616 deviceItemObject.put("product_variant", mDevice.getProductVariant()); 617 deviceItemObject.put("build_alias", mDevice.getBuildAlias()); 618 deviceItemObject.put("build_id", mDevice.getBuildId()); 619 deviceItemObject.put("build_flavor", mDevice.getBuildFlavor()); 620 String coverageProperty = mDevice.getProperty(COVERAGE_PROPERTY); 621 coverageBuild = coverageProperty != null && coverageProperty.equals("1"); 622 } catch (DeviceNotAvailableException e) { 623 CLog.e("A device not available - continuing"); 624 throw new RuntimeException("Failed to get device information"); 625 } 626 deviceArray.put(deviceItemObject); 627 628 JSONArray testBedArray = (JSONArray) jsonObject.get("test_bed"); 629 if (testBedArray.length() == 0) { 630 JSONObject testBedItemObject = new JSONObject(); 631 String testName; 632 if (mTestModuleName != null) { 633 testName = mTestModuleName; 634 } else { 635 CLog.w("--test-module-name not set (not recommended); deriving automatically"); 636 if (mTestConfigPath != null) { 637 testName = new File(mTestConfigPath).getName(); 638 testName = testName.replace(CONFIG_FILE_EXTENSION, ""); 639 } else if (mTestCasePath != null) { 640 testName = new File(mTestCasePath).getName(); 641 } else { 642 throw new RuntimeException( 643 "Failed to derive test module name; use --test-module-name option"); 644 } 645 } 646 CLog.logAndDisplay(LogLevel.INFO, "Setting test name as %s", testName); 647 testBedItemObject.put(NAME, testName); 648 testBedItemObject.put(ANDROIDDEVICE, deviceArray); 649 testBedArray.put(testBedItemObject); 650 } else if (testBedArray.length() == 1) { 651 JSONObject testBedItemObject = (JSONObject) testBedArray.get(0); 652 testBedItemObject.put(ANDROIDDEVICE, deviceArray); 653 } else { 654 CLog.e("Multi-device not yet supported: %d devices requested", 655 testBedArray.length()); 656 throw new RuntimeException("Failed to produce VTS runner test config"); 657 } 658 jsonObject.put(DATA_FILE_PATH, mTestCaseDataDir); 659 CLog.i("Added %s = %s to the Json object", DATA_FILE_PATH, mTestCaseDataDir); 660 661 JSONObject build = new JSONObject(); 662 build.put(BUILD_ID, mBuildInfo.getBuildId()); 663 build.put(BUILD_TARGET, mBuildInfo.getBuildTargetName()); 664 jsonObject.put(BUILD, build); 665 CLog.i("Added %s to the Json object", BUILD); 666 667 JSONObject suite = new JSONObject(); 668 suite.put(NAME, mBuildInfo.getTestTag()); 669 suite.put(INCLUDE_FILTER, new JSONArray(mIncludeFilters)); 670 CLog.i("Added include filter to test suite: %s", mIncludeFilters); 671 suite.put(EXCLUDE_FILTER, new JSONArray(mExcludeFilters)); 672 CLog.i("Added exclude filter to test suite: %s", mExcludeFilters); 673 674 if (mExcludeOverInclude) { 675 jsonObject.put(EXCLUDE_OVER_INCLUDE, mExcludeOverInclude); 676 CLog.i("Added %s to the Json object", EXCLUDE_OVER_INCLUDE); 677 } 678 679 jsonObject.put(TEST_SUITE, suite); 680 CLog.i("Added %s to the Json object", TEST_SUITE); 681 682 jsonObject.put(TEST_MAX_TIMEOUT, mTestTimeout); 683 CLog.i("Added %s to the Json object: %d", TEST_MAX_TIMEOUT, mTestTimeout); 684 685 if (mAbi != null) { 686 jsonObject.put(ABI_NAME, mAbi.getName()); 687 CLog.i("Added %s to the Json object", ABI_NAME); 688 jsonObject.put(ABI_BITNESS, mAbi.getBitness()); 689 CLog.i("Added %s to the Json object", ABI_BITNESS); 690 } 691 692 if (mSkipOn32BitAbi) { 693 jsonObject.put(SKIP_ON_32BIT_ABI, mSkipOn32BitAbi); 694 CLog.i("Added %s to the Json object", SKIP_ON_32BIT_ABI); 695 } 696 697 if (mSkipOn64BitAbi) { 698 jsonObject.put(SKIP_ON_64BIT_ABI, mSkipOn64BitAbi); 699 CLog.i("Added %s to the Json object", SKIP_ON_64BIT_ABI); 700 } else if (mRun32bBitOn64BitAbi) { 701 jsonObject.put(RUN_32BIT_ON_64BIT_ABI, mRun32bBitOn64BitAbi); 702 CLog.i("Added %s to the Json object", RUN_32BIT_ON_64BIT_ABI); 703 } 704 705 if (mSkipIfThermalThrottling) { 706 jsonObject.put(SKIP_IF_THERMAL_THROTTLING, mSkipIfThermalThrottling); 707 CLog.i("Added %s to the Json object", SKIP_IF_THERMAL_THROTTLING); 708 } 709 710 if (!mBinaryTestSource.isEmpty()) { 711 jsonObject.put(BINARY_TEST_SOURCE, new JSONArray(mBinaryTestSource)); 712 CLog.i("Added %s to the Json object", BINARY_TEST_SOURCE); 713 } 714 715 if (!mBinaryTestWorkingDirectory.isEmpty()) { 716 jsonObject.put(BINARY_TEST_WORKING_DIRECTORY, 717 new JSONArray(mBinaryTestWorkingDirectory)); 718 CLog.i("Added %s to the Json object", BINARY_TEST_WORKING_DIRECTORY); 719 } 720 721 if (!mBinaryTestEnvp.isEmpty()) { 722 jsonObject.put(BINARY_TEST_ENVP, new JSONArray(mBinaryTestEnvp)); 723 CLog.i("Added %s to the Json object", BINARY_TEST_ENVP); 724 } 725 726 if (!mBinaryTestArgs.isEmpty()) { 727 jsonObject.put(BINARY_TEST_ARGS, new JSONArray(mBinaryTestArgs)); 728 CLog.i("Added %s to the Json object", BINARY_TEST_ARGS); 729 } 730 731 if (!mBinaryTestLdLibraryPath.isEmpty()) { 732 jsonObject.put(BINARY_TEST_LD_LIBRARY_PATH, 733 new JSONArray(mBinaryTestLdLibraryPath)); 734 CLog.i("Added %s to the Json object", BINARY_TEST_LD_LIBRARY_PATH); 735 } 736 737 if (mBugReportOnFailure) { 738 jsonObject.put(BUG_REPORT_ON_FAILURE, mBugReportOnFailure); 739 CLog.i("Added %s to the Json object", BUG_REPORT_ON_FAILURE); 740 } 741 742 if (mEnableProfiling) { 743 jsonObject.put(ENABLE_PROFILING, mEnableProfiling); 744 CLog.i("Added %s to the Json object", ENABLE_PROFILING); 745 } 746 747 if (mSaveTraceFileRemote) { 748 jsonObject.put(SAVE_TRACE_FIEL_REMOTE, mSaveTraceFileRemote); 749 CLog.i("Added %s to the Json object", SAVE_TRACE_FIEL_REMOTE); 750 } 751 752 if (mEnableSystrace) { 753 jsonObject.put(ENABLE_SYSTRACE, mEnableSystrace); 754 CLog.i("Added %s to the Json object", ENABLE_SYSTRACE); 755 } 756 757 if (mEnableCoverage) { 758 jsonObject.put(GLOBAL_COVERAGE, mGlobalCoverage); 759 if (coverageBuild) { 760 jsonObject.put(ENABLE_COVERAGE, mEnableCoverage); 761 CLog.i("Added %s to the Json object", ENABLE_COVERAGE); 762 } else { 763 CLog.i("Device build has coverage disabled"); 764 } 765 } 766 767 if (mOutputCoverageReport) { 768 jsonObject.put(OUTPUT_COVERAGE_REPORT, mOutputCoverageReport); 769 CLog.i("Added %s to the Json object", OUTPUT_COVERAGE_REPORT); 770 } 771 772 if (mPreconditionHwBinderServiceName != null) { 773 jsonObject.put(PRECONDITION_HWBINDER_SERVICE, mPreconditionHwBinderServiceName); 774 CLog.i("Added %s to the Json object", PRECONDITION_HWBINDER_SERVICE); 775 } 776 777 if (mPreconditionFeature != null) { 778 jsonObject.put(PRECONDITION_FEATURE, mPreconditionFeature); 779 CLog.i("Added %s to the Json object", PRECONDITION_FEATURE); 780 } 781 782 if (mPreconditionFilePathPrefix != null) { 783 jsonObject.put(PRECONDITION_FILE_PATH_PREFIX, mPreconditionFilePathPrefix); 784 CLog.i("Added %s to the Json object", PRECONDITION_FILE_PATH_PREFIX); 785 } 786 787 if (mPreconditionLshal != null) { 788 jsonObject.put(PRECONDITION_LSHAL, mPreconditionLshal); 789 CLog.i("Added %s to the Json object", PRECONDITION_LSHAL); 790 } 791 792 if (mPreconditionVintf != null) { 793 jsonObject.put(PRECONDITION_VINTF, mPreconditionVintf); 794 CLog.i("Added %s to the Json object", PRECONDITION_VINTF); 795 } 796 797 if (mPreconditionVintfOverride && mPreconditionLshal != null) { 798 if (mPreconditionVintf == null) { 799 jsonObject.put(PRECONDITION_VINTF, mPreconditionLshal); 800 CLog.i("Added %s to the Json object, overriding %s", PRECONDITION_VINTF, 801 PRECONDITION_LSHAL); 802 } else { 803 CLog.w("Ignored precondition-vintf-override as precondition-vintf is present"); 804 } 805 } 806 807 if (!mBinaryTestProfilingLibraryPath.isEmpty()) { 808 jsonObject.put(BINARY_TEST_PROFILING_LIBRARY_PATH, 809 new JSONArray(mBinaryTestProfilingLibraryPath)); 810 CLog.i("Added %s to the Json object", BINARY_TEST_PROFILING_LIBRARY_PATH); 811 } 812 813 if (mBinaryTestDisableFramework) { 814 jsonObject.put(BINARY_TEST_DISABLE_FRAMEWORK, mBinaryTestDisableFramework); 815 CLog.i("Added %s to the Json object", BINARY_TEST_DISABLE_FRAMEWORK); 816 } 817 818 if (mBinaryTestStopNativeServers) { 819 jsonObject.put(BINARY_TEST_STOP_NATIVE_SERVERS, mBinaryTestStopNativeServers); 820 CLog.i("Added %s to the Json object", BINARY_TEST_STOP_NATIVE_SERVERS); 821 } 822 823 if (!mNativeServerProcessName.isEmpty()) { 824 jsonObject.put(NATIVE_SERVER_PROCESS_NAME, new JSONArray(mNativeServerProcessName)); 825 CLog.i("Added %s to the Json object", NATIVE_SERVER_PROCESS_NAME); 826 } 827 828 if (!mHalHidlReplayTestTracePaths.isEmpty()) { 829 jsonObject.put(HAL_HIDL_REPLAY_TEST_TRACE_PATHS, 830 new JSONArray(mHalHidlReplayTestTracePaths)); 831 CLog.i("Added %s to the Json object", HAL_HIDL_REPLAY_TEST_TRACE_PATHS); 832 } 833 834 if (mHalHidlPackageName != null) { 835 jsonObject.put(HAL_HIDL_PACKAGE_NAME, mHalHidlPackageName); 836 CLog.i("Added %s to the Json object", SYSTRACE_PROCESS_NAME); 837 } 838 839 if (mSystraceProcessName != null) { 840 jsonObject.put(SYSTRACE_PROCESS_NAME, mSystraceProcessName); 841 CLog.i("Added %s to the Json object", SYSTRACE_PROCESS_NAME); 842 } 843 844 if (mPassthroughMode) { 845 jsonObject.put(PASSTHROUGH_MODE, mPassthroughMode); 846 CLog.i("Added %s to the Json object", PASSTHROUGH_MODE); 847 } 848 849 if (mGtestBatchMode) { 850 jsonObject.put(GTEST_BATCH_MODE, mGtestBatchMode); 851 CLog.i("Added %s to the Json object", GTEST_BATCH_MODE); 852 } 853 854 if (mLtpNumberOfThreads >= 0) { 855 jsonObject.put(LTP_NUMBER_OF_THREADS, mLtpNumberOfThreads); 856 CLog.i("Added %s to the Json object", LTP_NUMBER_OF_THREADS); 857 } 858 } 859 860 /** 861 * Log a test module execution status to device logcat. 862 * 863 * @param status 864 * @return true if succesful, false otherwise 865 */ printToDeviceLogcatAboutTestModuleStatus(String status)866 private boolean printToDeviceLogcatAboutTestModuleStatus(String status) { 867 try { 868 mDevice.executeShellCommand(String.format( 869 "log -p i -t \"VTS\" \"[Test Module] %s %s\"", mTestModuleName, status)); 870 } catch (DeviceNotAvailableException e) { 871 CLog.w("Device unavailable while trying to write a message to logcat."); 872 return false; 873 } 874 return true; 875 } 876 AddTestModuleKeys(String test_module_name, long test_module_timestamp)877 private boolean AddTestModuleKeys(String test_module_name, long test_module_timestamp) { 878 if (test_module_name.length() == 0 || test_module_timestamp == -1) { 879 CLog.e(String.format("Test module keys (%s,%d) are invalid.", test_module_name, 880 test_module_timestamp)); 881 return false; 882 } 883 File reportFile = mBuildInfo.getFile(TEST_PLAN_REPORT_FILE); 884 885 try (FileWriter fw = new FileWriter(reportFile.getAbsoluteFile(), true); 886 BufferedWriter bw = new BufferedWriter(fw); PrintWriter out = new PrintWriter(bw)) { 887 out.println(String.format("%s %s", test_module_name, test_module_timestamp)); 888 } catch (IOException e) { 889 CLog.e(String.format( 890 "Can't write to the test plan result file, %s", TEST_PLAN_REPORT_FILE)); 891 return false; 892 } 893 return true; 894 } 895 896 /** 897 * This method prepares a command for the test and runs the python file as 898 * given in the arguments. 899 * 900 * @param listener 901 * @param runUtil 902 * @param mTestCasePath 903 * @param mTestConfigPath 904 * @throws RuntimeException 905 * @throws IllegalArgumentException 906 */ doRunTest(ITestRunListener listener)907 private void doRunTest(ITestRunListener listener) throws RuntimeException, IllegalArgumentException { 908 CLog.i("Device serial number: " + mDevice.getSerialNumber()); 909 910 JSONObject jsonObject = new JSONObject(); 911 File vtsRunnerLogDir = null; 912 try { 913 vtsRunnerLogDir = FileUtil.createTempDir("vts-runner-log"); 914 updateVtsRunnerTestConfig(jsonObject); 915 916 jsonObject.put(LOG_PATH, vtsRunnerLogDir.getAbsolutePath()); 917 CLog.i("Added %s to the Json object", LOG_PATH); 918 } catch(IOException e) { 919 throw new RuntimeException("Failed to read test config json file"); 920 } catch(JSONException e) { 921 throw new RuntimeException("Failed to build updated test config json data"); 922 } 923 924 CLog.i("config json: %s", jsonObject.toString()); 925 926 String jsonFilePath = null; 927 try { 928 File tmpFile = FileUtil.createTempFile( 929 mBuildInfo.getTestTag() + "-config-" + mBuildInfo.getDeviceSerial(), ".json"); 930 jsonFilePath = tmpFile.getAbsolutePath(); 931 CLog.i("config json file path: %s", jsonFilePath); 932 FileWriter fw = new FileWriter(jsonFilePath); 933 fw.write(jsonObject.toString()); 934 fw.close(); 935 } catch(IOException e) { 936 throw new RuntimeException("Failed to create device config json file"); 937 } 938 939 if (mPythonBin == null){ 940 mPythonBin = getPythonBinary(); 941 } 942 String[] baseOpts = {mPythonBin, "-m"}; 943 String[] testModule = {mTestCasePath.replace("/", "."), jsonFilePath}; 944 String[] cmd; 945 cmd = ArrayUtil.buildArray(baseOpts, testModule); 946 947 printToDeviceLogcatAboutTestModuleStatus("BEGIN"); 948 CommandResult commandResult = 949 mRunUtil.runTimedCmd(mTestTimeout + TEST_ABORT_TIMEOUT_MSECS, cmd); 950 951 if (commandResult != null) { 952 CommandStatus commandStatus = commandResult.getStatus(); 953 if (commandStatus != CommandStatus.SUCCESS 954 && commandStatus != CommandStatus.TIMED_OUT) { 955 CLog.e("Python process failed"); 956 CLog.e("Python path: %s", mPythonPath); 957 CLog.e("Stderr: %s", commandResult.getStderr()); 958 CLog.e("Stdout: %s", commandResult.getStdout()); 959 printVtsLogs(vtsRunnerLogDir); 960 printToDeviceLogcatAboutTestModuleStatus("ERROR"); 961 throw new RuntimeException("Failed to run VTS test"); 962 } 963 CLog.i("Standard output is: %s", commandResult.getStdout()); 964 CLog.i("Parsing test result: %s", commandResult.getStderr()); 965 printToDeviceLogcatAboutTestModuleStatus("END"); 966 } else { 967 printToDeviceLogcatAboutTestModuleStatus("FRAMEWORK_ERROR"); 968 } 969 970 VtsMultiDeviceTestResultParser parser = 971 new VtsMultiDeviceTestResultParser(listener, mRunName); 972 973 if (mUseStdoutLogs) { 974 if (commandResult.getStdout() == null) { 975 CLog.e("The std:out is null for CommandResult."); 976 throw new RuntimeException("The std:out is null for CommandResult."); 977 } 978 parser.processNewLines(commandResult.getStdout().split("\n")); 979 } else { 980 // parse from test_run_summary.json instead of stdout 981 String jsonData = null; 982 JSONObject object = null; 983 File testRunSummary = getFileTestRunSummary(vtsRunnerLogDir); 984 if (testRunSummary == null) { 985 throw new RuntimeException("Couldn't locate the file : " + 986 TEST_RUN_SUMMARY_FILE_NAME); 987 } 988 try { 989 jsonData = FileUtil.readStringFromFile(testRunSummary); 990 CLog.i("Test Result Summary: %s", jsonData); 991 object = new JSONObject(jsonData); 992 } catch (IOException e) { 993 CLog.e("Error occurred in parsing Json file : %s", testRunSummary.toPath()); 994 } catch (JSONException e) { 995 CLog.e("Error occurred in parsing Json String : %s", jsonData); 996 } 997 if (object == null) { 998 CLog.e("Json object is null."); 999 throw new RuntimeException("Json object is null."); 1000 } 1001 parser.processJsonFile(object); 1002 1003 try { 1004 JSONObject planObject = object.getJSONObject(TESTMODULE); 1005 String test_module_name = planObject.getString("Name"); 1006 long test_module_timestamp = planObject.getLong("Timestamp"); 1007 AddTestModuleKeys(test_module_name, test_module_timestamp); 1008 } catch (JSONException e) { 1009 CLog.d("Key '%s' not found in result json summary", TESTMODULE); 1010 } 1011 } 1012 printVtsLogs(vtsRunnerLogDir); 1013 1014 File reportMsg; 1015 int waitCount = 0; 1016 // Wait python process to finish for 3 minutes at most 1017 while ((reportMsg = FileUtil.findFile(vtsRunnerLogDir, REPORT_MESSAGE_FILE_NAME)) == null 1018 && waitCount < 180) { 1019 try { 1020 Thread.sleep(1000); 1021 } catch (Exception e) { 1022 System.out.println(e); 1023 } 1024 waitCount++; 1025 } 1026 1027 CLog.i("Report message path: %s", reportMsg); 1028 1029 if (reportMsg == null) { 1030 CLog.e("Cannot find report message proto file."); 1031 } else if (reportMsg.length() > 0) { 1032 CLog.i("Uploading report message. File size: %s", reportMsg.length()); 1033 VtsDashboardUtil dashboardUtil = new VtsDashboardUtil(configReader); 1034 dashboardUtil.Upload(reportMsg.getAbsolutePath()); 1035 } else { 1036 CLog.i("Result uploading is not enabled."); 1037 } 1038 1039 FileUtil.recursiveDelete(vtsRunnerLogDir); 1040 CLog.i("Deleted the runner log dir, %s.", vtsRunnerLogDir); 1041 if (jsonFilePath != null) { 1042 FileUtil.deleteFile(new File(jsonFilePath)); 1043 CLog.i("Deleted the runner json config file, %s.", jsonFilePath); 1044 } 1045 } 1046 1047 /** 1048 * This method return the file test_run_details.txt which is then used to parse logs. 1049 * 1050 * @param logDir : The file that needs to be converted 1051 * @return the file named test_run_details.txt 1052 * @throws IllegalArgumentException 1053 */ getFileTestRunSummary(File logDir)1054 private File getFileTestRunSummary(File logDir) throws IllegalArgumentException { 1055 File[] children; 1056 if (logDir == null) { 1057 throw new IllegalArgumentException("Argument logDir is null."); 1058 } 1059 children = logDir.listFiles(); 1060 if (children != null) { 1061 for (File child : children) { 1062 if (!child.isDirectory()) { 1063 if (child.getName().equals(TEST_RUN_SUMMARY_FILE_NAME)) { 1064 return child; 1065 } 1066 } else { 1067 File file = getFileTestRunSummary(child); 1068 if (file != null) { 1069 return file; 1070 } 1071 } 1072 } 1073 } 1074 return null; 1075 } 1076 1077 /** 1078 * The method prints all VTS runner log files 1079 * 1080 * @param logDir the File instance of the base log dir. 1081 */ printVtsLogs(File logDir)1082 private void printVtsLogs(File logDir) { 1083 File[] children; 1084 if (logDir == null) { 1085 CLog.e("Scan VTS log dir: null\n"); 1086 return; 1087 } 1088 CLog.i("Scan VTS log dir %s\n", logDir.getAbsolutePath()); 1089 children = logDir.listFiles(); 1090 if (children != null) { 1091 for (File child : children) { 1092 if (child.isDirectory()) { 1093 printVtsLogs(child); 1094 } else { 1095 CLog.i("VTS log file %s\n", child.getAbsolutePath()); 1096 try { 1097 if (child.getName().startsWith("vts_agent") && 1098 child.getName().endsWith(".log")) { 1099 CLog.i("Content: %s\n", FileUtil.readStringFromFile(child)); 1100 } else { 1101 CLog.i("skip %s\n", child.getName()); 1102 } 1103 } catch (IOException e) { 1104 CLog.e("I/O error\n"); 1105 } 1106 } 1107 } 1108 } 1109 } 1110 1111 /** 1112 * This method returns whether the OS is Windows. 1113 */ isOnWindows()1114 private static boolean isOnWindows() { 1115 return System.getProperty(OS_NAME).contains(WINDOWS); 1116 } 1117 1118 /** 1119 * This method sets the python path. It's based on the 1120 * assumption that the environment variable $ANDROID_BUILD_TOP is set. 1121 */ setPythonPath()1122 private void setPythonPath() { 1123 StringBuilder sb = new StringBuilder(); 1124 String separator = File.pathSeparator; 1125 if (System.getenv(PYTHONPATH) != null) { 1126 sb.append(separator); 1127 sb.append(System.getenv(PYTHONPATH)); 1128 } 1129 1130 // to get the path for android-vts/testcases/ which keeps the VTS python code under vts. 1131 if (mBuildInfo != null) { 1132 CompatibilityBuildHelper mBuildHelper = new CompatibilityBuildHelper(mBuildInfo); 1133 1134 File testDir = null; 1135 try { 1136 testDir = mBuildHelper.getTestsDir(); 1137 } catch (FileNotFoundException e) { 1138 /* pass */ 1139 } 1140 if (testDir != null) { 1141 sb.append(separator); 1142 mTestCaseDataDir = testDir.getAbsolutePath(); 1143 sb.append(mTestCaseDataDir); 1144 } else if (mBuildInfo.getFile(VTS) != null) { 1145 sb.append(separator); 1146 sb.append(mBuildInfo.getFile(VTS).getAbsolutePath()).append("/.."); 1147 } 1148 } 1149 1150 // for when one uses PythonVirtualenvPreparer. 1151 if (mBuildInfo.getFile(PYTHONPATH) != null) { 1152 sb.append(separator); 1153 sb.append(mBuildInfo.getFile(PYTHONPATH).getAbsolutePath()); 1154 } 1155 if (System.getenv("ANDROID_BUILD_TOP") != null) { 1156 sb.append(separator); 1157 sb.append(System.getenv("ANDROID_BUILD_TOP")).append("/test"); 1158 } 1159 if (sb.length() == 0) { 1160 throw new RuntimeException("Could not find python path on host machine"); 1161 } 1162 mPythonPath = sb.substring(1); 1163 CLog.i("mPythonPath: %s", mPythonPath); 1164 } 1165 1166 /** 1167 * This method gets the python binary. 1168 */ getPythonBinary()1169 private String getPythonBinary() { 1170 boolean isWindows = isOnWindows(); 1171 String python = (isWindows? "python.exe": "python"); 1172 File venvDir = mBuildInfo.getFile(VIRTUAL_ENV_PATH); 1173 if (venvDir != null) { 1174 String binDir = (isWindows? "Scripts": "bin"); 1175 File pythonBinaryFile = new File(venvDir.getAbsolutePath(), 1176 binDir + File.separator + python); 1177 String pythonBinPath = pythonBinaryFile.getAbsolutePath(); 1178 if (pythonBinaryFile.exists()) { 1179 CLog.i("Python path " + pythonBinPath + ".\n"); 1180 return pythonBinPath; 1181 } 1182 CLog.e(python + " doesn't exist under the " + 1183 "created virtualenv dir (" + pythonBinPath + ").\n"); 1184 } else { 1185 CLog.e(VIRTUAL_ENV_PATH + " not available in BuildInfo. " + 1186 "Please use VtsPythonVirtualenvPreparer tartget preparer.\n"); 1187 } 1188 1189 IRunUtil runUtil = (mRunUtil == null ? RunUtil.getDefault() : mRunUtil); 1190 CommandResult c = runUtil.runTimedCmd(1000, 1191 (isWindows ? "where" : "which"), python); 1192 String pythonBin = c.getStdout().trim(); 1193 if (pythonBin.length() == 0) { 1194 throw new RuntimeException("Could not find python binary on host " 1195 + "machine"); 1196 } 1197 return pythonBin; 1198 } 1199 1200 /** 1201 * {@inheritDoc} 1202 */ 1203 @Override setAbi(IAbi abi)1204 public void setAbi(IAbi abi){ 1205 mAbi = abi; 1206 } 1207 } 1208