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 import logging 18 import os 19 import re 20 import sys 21 22 from vts.proto import VtsReportMessage_pb2 as ReportMsg 23 from vts.runners.host import asserts 24 from vts.runners.host import const 25 from vts.runners.host import errors 26 from vts.runners.host import keys 27 from vts.runners.host import logger 28 from vts.runners.host import records 29 from vts.runners.host import signals 30 from vts.runners.host import utils 31 from vts.utils.python.controllers import android_device 32 from vts.utils.python.common import filter_utils 33 from vts.utils.python.common import list_utils 34 from vts.utils.python.coverage import coverage_utils 35 from vts.utils.python.coverage import sancov_utils 36 from vts.utils.python.precondition import precondition_utils 37 from vts.utils.python.profiling import profiling_utils 38 from vts.utils.python.reporting import log_uploading_utils 39 from vts.utils.python.systrace import systrace_utils 40 from vts.utils.python.web import feature_utils 41 from vts.utils.python.web import web_utils 42 43 from acts import signals as acts_signals 44 45 # Macro strings for test result reporting 46 TEST_CASE_TOKEN = "[Test Case]" 47 RESULT_LINE_TEMPLATE = TEST_CASE_TOKEN + " %s %s" 48 STR_TEST = "test" 49 STR_GENERATE = "generate" 50 _REPORT_MESSAGE_FILE_NAME = "report_proto.msg" 51 _BUG_REPORT_FILE_PREFIX = "bugreport" 52 _BUG_REPORT_FILE_EXTENSION = ".zip" 53 _LOGCAT_FILE_PREFIX = "logcat" 54 _LOGCAT_FILE_EXTENSION = ".txt" 55 _ANDROID_DEVICES = '_android_devices' 56 _REASON_TO_SKIP_ALL_TESTS = '_reason_to_skip_all_tests' 57 _SETUP_RETRY_NUMBER = 5 58 59 LOGCAT_BUFFERS = [ 60 'radio', 61 'events', 62 'main', 63 'system', 64 'crash' 65 ] 66 67 68 class BaseTestClass(object): 69 """Base class for all test classes to inherit from. 70 71 This class gets all the controller objects from test_runner and executes 72 the test cases requested within itself. 73 74 Most attributes of this class are set at runtime based on the configuration 75 provided. 76 77 Attributes: 78 android_devices: A list of AndroidDevice object, representing android 79 devices. 80 test_module_name: A string representing the test module name. 81 tests: A list of strings, each representing a test case name. 82 log: A logger object used for logging. 83 results: A records.TestResult object for aggregating test results from 84 the execution of test cases. 85 _current_record: A records.TestResultRecord object for the test case 86 currently being executed. If no test is running, this 87 should be None. 88 include_filer: A list of string, each representing a test case name to 89 include. 90 exclude_filer: A list of string, each representing a test case name to 91 exclude. Has no effect if include_filer is not empty. 92 abi_name: String, name of abi in use 93 abi_bitness: String, bitness of abi in use 94 web: WebFeature, object storing web feature util for test run 95 coverage: CoverageFeature, object storing coverage feature util for test run 96 sancov: SancovFeature, object storing sancov feature util for test run 97 profiling: ProfilingFeature, object storing profiling feature util for test run 98 _bug_report_on_failure: bool, whether to catch bug report at the end 99 of failed test cases. Default is False 100 _logcat_on_failure: bool, whether to dump logcat at the end 101 of failed test cases. Default is True 102 test_filter: Filter object to filter test names. 103 """ 104 105 def __init__(self, configs): 106 self.tests = [] 107 # Set all the controller objects and params. 108 for name, value in configs.items(): 109 setattr(self, name, value) 110 self.results = records.TestResult() 111 self.log = logger.LoggerProxy() 112 self._current_record = None 113 114 # Setup test filters 115 self.include_filter = self.getUserParam( 116 [ 117 keys.ConfigKeys.KEY_TEST_SUITE, 118 keys.ConfigKeys.KEY_INCLUDE_FILTER 119 ], 120 default_value=[]) 121 self.exclude_filter = self.getUserParam( 122 [ 123 keys.ConfigKeys.KEY_TEST_SUITE, 124 keys.ConfigKeys.KEY_EXCLUDE_FILTER 125 ], 126 default_value=[]) 127 128 # TODO(yuexima): remove include_filter and exclude_filter from class attributes 129 # after confirming all modules no longer have reference to them 130 self.include_filter = list_utils.ExpandItemDelimiters( 131 list_utils.ItemsToStr(self.include_filter), ',') 132 self.exclude_filter = list_utils.ExpandItemDelimiters( 133 list_utils.ItemsToStr(self.exclude_filter), ',') 134 exclude_over_include = self.getUserParam( 135 keys.ConfigKeys.KEY_EXCLUDE_OVER_INCLUDE, default_value=None) 136 self.test_module_name = self.getUserParam( 137 keys.ConfigKeys.KEY_TESTBED_NAME, 138 log_warning_and_continue_if_not_found=True, 139 default_value=self.__class__.__name__) 140 self.test_filter = filter_utils.Filter( 141 self.include_filter, 142 self.exclude_filter, 143 enable_regex=True, 144 exclude_over_include=exclude_over_include, 145 enable_negative_pattern=True, 146 enable_module_name_prefix_matching=True, 147 module_name=self.test_module_name, 148 expand_bitness=True) 149 logging.info('Test filter: %s' % self.test_filter) 150 151 # TODO: get abi information differently for multi-device support. 152 # Set other optional parameters 153 self.abi_name = self.getUserParam( 154 keys.ConfigKeys.IKEY_ABI_NAME, default_value=None) 155 self.abi_bitness = self.getUserParam( 156 keys.ConfigKeys.IKEY_ABI_BITNESS, default_value=None) 157 self.skip_on_32bit_abi = self.getUserParam( 158 keys.ConfigKeys.IKEY_SKIP_ON_32BIT_ABI, default_value=False) 159 self.skip_on_64bit_abi = self.getUserParam( 160 keys.ConfigKeys.IKEY_SKIP_ON_64BIT_ABI, default_value=False) 161 self.run_32bit_on_64bit_abi = self.getUserParam( 162 keys.ConfigKeys.IKEY_RUN_32BIT_ON_64BIT_ABI, default_value=False) 163 self.web = web_utils.WebFeature(self.user_params) 164 self.coverage = coverage_utils.CoverageFeature( 165 self.user_params, web=self.web) 166 self.sancov = sancov_utils.SancovFeature( 167 self.user_params, web=self.web) 168 self.profiling = profiling_utils.ProfilingFeature( 169 self.user_params, web=self.web) 170 self.systrace = systrace_utils.SystraceFeature( 171 self.user_params, web=self.web) 172 self.log_uploading = log_uploading_utils.LogUploadingFeature( 173 self.user_params, web=self.web) 174 self.collect_tests_only = self.getUserParam( 175 keys.ConfigKeys.IKEY_COLLECT_TESTS_ONLY, default_value=False) 176 self.run_as_vts_self_test = self.getUserParam( 177 keys.ConfigKeys.RUN_AS_VTS_SELFTEST, default_value=False) 178 self.run_as_compliance_test = self.getUserParam( 179 keys.ConfigKeys.RUN_AS_COMPLIANCE_TEST, default_value=False) 180 self._bug_report_on_failure = self.getUserParam( 181 keys.ConfigKeys.IKEY_BUG_REPORT_ON_FAILURE, default_value=False) 182 self._logcat_on_failure = self.getUserParam( 183 keys.ConfigKeys.IKEY_LOGCAT_ON_FAILURE, default_value=True) 184 185 @property 186 def android_devices(self): 187 """Returns a list of AndroidDevice objects""" 188 if not hasattr(self, _ANDROID_DEVICES): 189 setattr(self, _ANDROID_DEVICES, 190 self.registerController(android_device)) 191 return getattr(self, _ANDROID_DEVICES) 192 193 @android_devices.setter 194 def android_devices(self, devices): 195 """Set the list of AndroidDevice objects""" 196 setattr(self, _ANDROID_DEVICES, devices) 197 198 def __enter__(self): 199 return self 200 201 def __exit__(self, *args): 202 self._exec_func(self.cleanUp) 203 204 def unpack_userparams(self, req_param_names=[], opt_param_names=[], **kwargs): 205 """Wrapper for test cases using ACTS runner API.""" 206 return self.getUserParams(req_param_names, opt_param_names, **kwargs) 207 208 def getUserParams(self, req_param_names=[], opt_param_names=[], **kwargs): 209 """Unpacks user defined parameters in test config into individual 210 variables. 211 212 Instead of accessing the user param with self.user_params["xxx"], the 213 variable can be directly accessed with self.xxx. 214 215 A missing required param will raise an exception. If an optional param 216 is missing, an INFO line will be logged. 217 218 Args: 219 req_param_names: A list of names of the required user params. 220 opt_param_names: A list of names of the optional user params. 221 **kwargs: Arguments that provide default values. 222 e.g. getUserParams(required_list, opt_list, arg_a="hello") 223 self.arg_a will be "hello" unless it is specified again in 224 required_list or opt_list. 225 226 Raises: 227 BaseTestError is raised if a required user params is missing from 228 test config. 229 """ 230 for k, v in kwargs.items(): 231 setattr(self, k, v) 232 for name in req_param_names: 233 if name not in self.user_params: 234 raise errors.BaseTestError(("Missing required user param '%s' " 235 "in test configuration.") % name) 236 setattr(self, name, self.user_params[name]) 237 for name in opt_param_names: 238 if name not in self.user_params: 239 logging.info(("Missing optional user param '%s' in " 240 "configuration, continue."), name) 241 else: 242 setattr(self, name, self.user_params[name]) 243 244 def getUserParam(self, 245 param_name, 246 error_if_not_found=False, 247 log_warning_and_continue_if_not_found=False, 248 default_value=None, 249 to_str=False): 250 """Get the value of a single user parameter. 251 252 This method returns the value of specified user parameter. 253 Note: this method will not automatically set attribute using the parameter name and value. 254 255 Args: 256 param_name: string or list of string, denoting user parameter names. If provided 257 a single string, self.user_params["<param_name>"] will be accessed. 258 If provided multiple strings, 259 self.user_params["<param_name1>"]["<param_name2>"]["<param_name3>"]... 260 will be accessed. 261 error_if_not_found: bool, whether to raise error if parameter not exists. Default: 262 False 263 log_warning_and_continue_if_not_found: bool, log a warning message if parameter value 264 not found. 265 default_value: object, default value to return if not found. If error_if_not_found is 266 True, this parameter has no effect. Default: None 267 to_str: boolean, whether to convert the result object to string if not None. 268 Note, strings passing in from java json config are usually unicode. 269 270 Returns: 271 object, value of the specified parameter name chain if exists; 272 <default_value> if not exists. 273 """ 274 275 def ToStr(return_value): 276 """Check to_str option and convert to string if not None""" 277 if to_str and return_value is not None: 278 return str(return_value) 279 return return_value 280 281 if not param_name: 282 if error_if_not_found: 283 raise errors.BaseTestError("empty param_name provided") 284 logging.error("empty param_name") 285 return ToStr(default_value) 286 287 if not isinstance(param_name, list): 288 param_name = [param_name] 289 290 curr_obj = self.user_params 291 for param in param_name: 292 if param not in curr_obj: 293 msg = "Missing user param '%s' in test configuration." % param_name 294 if error_if_not_found: 295 raise errors.BaseTestError(msg) 296 elif log_warning_and_continue_if_not_found: 297 logging.warn(msg) 298 return ToStr(default_value) 299 curr_obj = curr_obj[param] 300 301 return ToStr(curr_obj) 302 303 def _setUpClass(self): 304 """Proxy function to guarantee the base implementation of setUpClass 305 is called. 306 """ 307 if not precondition_utils.MeetFirstApiLevelPrecondition(self): 308 self.skipAllTests("The device's first API level doesn't meet the " 309 "precondition.") 310 return self.setUpClass() 311 312 def setUpClass(self): 313 """Setup function that will be called before executing any test case in 314 the test class. 315 316 To signal setup failure, return False or raise an exception. If 317 exceptions were raised, the stack trace would appear in log, but the 318 exceptions would not propagate to upper levels. 319 320 Implementation is optional. 321 """ 322 pass 323 324 def _tearDownClass(self): 325 """Proxy function to guarantee the base implementation of tearDownClass 326 is called. 327 """ 328 ret = self.tearDownClass() 329 if self.log_uploading.enabled: 330 self.log_uploading.UploadLogs() 331 if self.web.enabled: 332 message_b = self.web.GenerateReportMessage(self.results.requested, 333 self.results.executed) 334 else: 335 message_b = '' 336 337 report_proto_path = os.path.join(logging.log_path, 338 _REPORT_MESSAGE_FILE_NAME) 339 340 if message_b: 341 logging.info('Result proto message path: %s', report_proto_path) 342 343 with open(report_proto_path, "wb") as f: 344 f.write(message_b) 345 346 return ret 347 348 def tearDownClass(self): 349 """Teardown function that will be called after all the selected test 350 cases in the test class have been executed. 351 352 Implementation is optional. 353 """ 354 pass 355 356 def _testEntry(self, test_record): 357 """Internal function to be called upon entry of a test case. 358 359 Args: 360 test_record: The TestResultRecord object for the test case going to 361 be executed. 362 """ 363 self._current_record = test_record 364 if self.web.enabled: 365 self.web.AddTestReport(test_record.test_name) 366 367 def _setUp(self, test_name): 368 """Proxy function to guarantee the base implementation of setUp is 369 called. 370 """ 371 if self.systrace.enabled: 372 self.systrace.StartSystrace() 373 return self.setUp() 374 375 def setUp(self): 376 """Setup function that will be called every time before executing each 377 test case in the test class. 378 379 To signal setup failure, return False or raise an exception. If 380 exceptions were raised, the stack trace would appear in log, but the 381 exceptions would not propagate to upper levels. 382 383 Implementation is optional. 384 """ 385 386 def _testExit(self): 387 """Internal function to be called upon exit of a test.""" 388 self._current_record = None 389 390 def _tearDown(self, test_name): 391 """Proxy function to guarantee the base implementation of tearDown 392 is called. 393 """ 394 if self.systrace.enabled: 395 self.systrace.ProcessAndUploadSystrace(test_name) 396 self.tearDown() 397 398 def tearDown(self): 399 """Teardown function that will be called every time a test case has 400 been executed. 401 402 Implementation is optional. 403 """ 404 405 def _onFail(self): 406 """Proxy function to guarantee the base implementation of onFail is 407 called. 408 """ 409 record = self._current_record 410 logging.error(record.details) 411 begin_time = logger.epochToLogLineTimestamp(record.begin_time) 412 logging.info(RESULT_LINE_TEMPLATE, record.test_name, record.result) 413 if self.web.enabled: 414 self.web.SetTestResult(ReportMsg.TEST_CASE_RESULT_FAIL) 415 self.onFail(record.test_name, begin_time) 416 if self._bug_report_on_failure: 417 self.DumpBugReport( 418 '%s-%s' % (self.test_module_name, record.test_name)) 419 if self._logcat_on_failure: 420 self.DumpLogcat('%s-%s' % (self.test_module_name, record.test_name)) 421 422 def onFail(self, test_name, begin_time): 423 """A function that is executed upon a test case failure. 424 425 User implementation is optional. 426 427 Args: 428 test_name: Name of the test that triggered this function. 429 begin_time: Logline format timestamp taken when the test started. 430 """ 431 432 def _onPass(self): 433 """Proxy function to guarantee the base implementation of onPass is 434 called. 435 """ 436 record = self._current_record 437 test_name = record.test_name 438 begin_time = logger.epochToLogLineTimestamp(record.begin_time) 439 msg = record.details 440 if msg: 441 logging.info(msg) 442 logging.info(RESULT_LINE_TEMPLATE, test_name, record.result) 443 if self.web.enabled: 444 self.web.SetTestResult(ReportMsg.TEST_CASE_RESULT_PASS) 445 self.onPass(test_name, begin_time) 446 447 def onPass(self, test_name, begin_time): 448 """A function that is executed upon a test case passing. 449 450 Implementation is optional. 451 452 Args: 453 test_name: Name of the test that triggered this function. 454 begin_time: Logline format timestamp taken when the test started. 455 """ 456 457 def _onSkip(self): 458 """Proxy function to guarantee the base implementation of onSkip is 459 called. 460 """ 461 record = self._current_record 462 test_name = record.test_name 463 begin_time = logger.epochToLogLineTimestamp(record.begin_time) 464 logging.info(RESULT_LINE_TEMPLATE, test_name, record.result) 465 logging.info("Reason to skip: %s", record.details) 466 if self.web.enabled: 467 self.web.SetTestResult(ReportMsg.TEST_CASE_RESULT_SKIP) 468 self.onSkip(test_name, begin_time) 469 470 def onSkip(self, test_name, begin_time): 471 """A function that is executed upon a test case being skipped. 472 473 Implementation is optional. 474 475 Args: 476 test_name: Name of the test that triggered this function. 477 begin_time: Logline format timestamp taken when the test started. 478 """ 479 480 def _onSilent(self): 481 """Proxy function to guarantee the base implementation of onSilent is 482 called. 483 """ 484 record = self._current_record 485 test_name = record.test_name 486 begin_time = logger.epochToLogLineTimestamp(record.begin_time) 487 if self.web.enabled: 488 self.web.SetTestResult(None) 489 self.onSilent(test_name, begin_time) 490 491 def onSilent(self, test_name, begin_time): 492 """A function that is executed upon a test case being marked as silent. 493 494 Implementation is optional. 495 496 Args: 497 test_name: Name of the test that triggered this function. 498 begin_time: Logline format timestamp taken when the test started. 499 """ 500 501 def _onException(self): 502 """Proxy function to guarantee the base implementation of onException 503 is called. 504 """ 505 record = self._current_record 506 test_name = record.test_name 507 logging.exception(record.details) 508 begin_time = logger.epochToLogLineTimestamp(record.begin_time) 509 if self.web.enabled: 510 self.web.SetTestResult(ReportMsg.TEST_CASE_RESULT_EXCEPTION) 511 self.onException(test_name, begin_time) 512 if self._bug_report_on_failure: 513 self.DumpBugReport( 514 '%s-%s' % (self.test_module_name, record.test_name)) 515 if self._logcat_on_failure: 516 self.DumpLogcat('%s-%s' % (self.test_module_name, record.test_name)) 517 518 def onException(self, test_name, begin_time): 519 """A function that is executed upon an unhandled exception from a test 520 case. 521 522 Implementation is optional. 523 524 Args: 525 test_name: Name of the test that triggered this function. 526 begin_time: Logline format timestamp taken when the test started. 527 """ 528 529 def _exec_procedure_func(self, func): 530 """Executes a procedure function like onPass, onFail etc. 531 532 This function will alternate the 'Result' of the test's record if 533 exceptions happened when executing the procedure function. 534 535 This will let signals.TestAbortAll through so abortAll works in all 536 procedure functions. 537 538 Args: 539 func: The procedure function to be executed. 540 """ 541 record = self._current_record 542 if record is None: 543 logging.error("Cannot execute %s. No record for current test.", 544 func.__name__) 545 return 546 try: 547 func() 548 except (signals.TestAbortAll, acts_signals.TestAbortAll) as e: 549 raise signals.TestAbortAll, e, sys.exc_info()[2] 550 except Exception as e: 551 logging.exception("Exception happened when executing %s for %s.", 552 func.__name__, record.test_name) 553 record.addError(func.__name__, e) 554 555 def addTableToResult(self, name, rows): 556 """Adds a table to current test record. 557 558 A subclass can call this method to add a table to _current_record when 559 running test cases. 560 561 Args: 562 name: String, the table name. 563 rows: A 2-dimensional list which contains the data. 564 """ 565 self._current_record.addTable(name, rows) 566 567 def filterOneTest(self, test_name, test_filter=None): 568 """Check test filters for a test name. 569 570 The first layer of filter is user defined test filters: 571 if a include filter is not empty, only tests in include filter will 572 be executed regardless whether they are also in exclude filter. Else 573 if include filter is empty, only tests not in exclude filter will be 574 executed. 575 576 The second layer of filter is checking whether skipAllTests method is 577 called. If the flag is set, this method raises signals.TestSkip. 578 579 The third layer of filter is checking abi bitness: 580 if a test has a suffix indicating the intended architecture bitness, 581 and the current abi bitness information is available, non matching tests 582 will be skipped. By our convention, this function will look for bitness in suffix 583 formated as "32bit", "32Bit", "32BIT", or 64 bit equivalents. 584 585 This method assumes const.SUFFIX_32BIT and const.SUFFIX_64BIT are in lower cases. 586 587 Args: 588 test_name: string, name of a test 589 test_filter: Filter object, test filter 590 591 Raises: 592 signals.TestSilent if a test should not be executed 593 signals.TestSkip if a test should be logged but not be executed 594 """ 595 self._filterOneTestThroughTestFilter(test_name, test_filter) 596 self._filterOneTestThroughAbiBitness(test_name) 597 598 def _filterOneTestThroughTestFilter(self, test_name, test_filter=None): 599 """Check test filter for the given test name. 600 601 Args: 602 test_name: string, name of a test 603 604 Raises: 605 signals.TestSilent if a test should not be executed 606 signals.TestSkip if a test should be logged but not be executed 607 """ 608 if not test_filter: 609 test_filter = self.test_filter 610 611 if not test_filter.Filter(test_name): 612 raise signals.TestSilent("Test case '%s' did not pass filters.") 613 614 if self.isSkipAllTests(): 615 raise signals.TestSkip(self.getSkipAllTestsReason()) 616 617 def _filterOneTestThroughAbiBitness(self, test_name): 618 """Check test filter for the given test name. 619 620 Args: 621 test_name: string, name of a test 622 623 Raises: 624 signals.TestSilent if a test should not be executed 625 """ 626 asserts.skipIf( 627 self.abi_bitness and 628 ((self.skip_on_32bit_abi is True) and self.abi_bitness == "32") or 629 ((self.skip_on_64bit_abi is True) and self.abi_bitness == "64") or 630 (test_name.lower().endswith(const.SUFFIX_32BIT) and 631 self.abi_bitness != "32") or 632 (test_name.lower().endswith(const.SUFFIX_64BIT) and 633 self.abi_bitness != "64" and not self.run_32bit_on_64bit_abi), 634 "Test case '{}' excluded as ABI bitness is {}.".format( 635 test_name, self.abi_bitness)) 636 637 def execOneTest(self, test_name, test_func, args, **kwargs): 638 """Executes one test case and update test results. 639 640 Executes one test case, create a records.TestResultRecord object with 641 the execution information, and add the record to the test class's test 642 results. 643 644 Args: 645 test_name: Name of the test. 646 test_func: The test function. 647 args: A tuple of params. 648 kwargs: Extra kwargs. 649 """ 650 is_silenced = False 651 tr_record = records.TestResultRecord(test_name, self.test_module_name) 652 tr_record.testBegin() 653 logging.info("%s %s", TEST_CASE_TOKEN, test_name) 654 verdict = None 655 finished = False 656 try: 657 ret = self._testEntry(tr_record) 658 asserts.assertTrue(ret is not False, 659 "Setup test entry for %s failed." % test_name) 660 self.filterOneTest(test_name) 661 if self.collect_tests_only: 662 asserts.explicitPass("Collect tests only.") 663 664 try: 665 ret = self._setUp(test_name) 666 asserts.assertTrue(ret is not False, 667 "Setup for %s failed." % test_name) 668 669 if args or kwargs: 670 verdict = test_func(*args, **kwargs) 671 else: 672 verdict = test_func() 673 finished = True 674 finally: 675 self._tearDown(test_name) 676 except (signals.TestFailure, acts_signals.TestFailure, AssertionError) as e: 677 tr_record.testFail(e) 678 self._exec_procedure_func(self._onFail) 679 finished = True 680 except (signals.TestSkip, acts_signals.TestSkip) as e: 681 # Test skipped. 682 tr_record.testSkip(e) 683 self._exec_procedure_func(self._onSkip) 684 finished = True 685 except (signals.TestAbortClass, acts_signals.TestAbortClass) as e: 686 # Abort signals, pass along. 687 tr_record.testFail(e) 688 finished = True 689 raise signals.TestAbortClass, e, sys.exc_info()[2] 690 except (signals.TestAbortAll, acts_signals.TestAbortAll) as e: 691 # Abort signals, pass along. 692 tr_record.testFail(e) 693 finished = True 694 raise signals.TestAbortAll, e, sys.exc_info()[2] 695 except (signals.TestPass, acts_signals.TestPass) as e: 696 # Explicit test pass. 697 tr_record.testPass(e) 698 self._exec_procedure_func(self._onPass) 699 finished = True 700 except (signals.TestSilent, acts_signals.TestSilent) as e: 701 # Suppress test reporting. 702 is_silenced = True 703 self._exec_procedure_func(self._onSilent) 704 self.results.removeRecord(tr_record) 705 finished = True 706 except Exception as e: 707 # Exception happened during test. 708 logging.exception(e) 709 tr_record.testError(e) 710 self._exec_procedure_func(self._onException) 711 self._exec_procedure_func(self._onFail) 712 finished = True 713 else: 714 # Keep supporting return False for now. 715 # TODO(angli): Deprecate return False support. 716 if verdict or (verdict is None): 717 # Test passed. 718 tr_record.testPass() 719 self._exec_procedure_func(self._onPass) 720 return 721 # Test failed because it didn't return True. 722 # This should be removed eventually. 723 tr_record.testFail() 724 self._exec_procedure_func(self._onFail) 725 finished = True 726 finally: 727 if not finished: 728 for device in self.android_devices: 729 # if shell has not been set up yet 730 if device.shell is not None: 731 device.shell.DisableShell() 732 733 logging.error('Test timed out.') 734 tr_record.testError() 735 self._exec_procedure_func(self._onException) 736 self._exec_procedure_func(self._onFail) 737 738 if not is_silenced: 739 self.results.addRecord(tr_record) 740 self._testExit() 741 742 def runGeneratedTests(self, 743 test_func, 744 settings, 745 args=None, 746 kwargs=None, 747 tag="", 748 name_func=None): 749 """Runs generated test cases. 750 751 Generated test cases are not written down as functions, but as a list 752 of parameter sets. This way we reduce code repetition and improve 753 test case scalability. 754 755 Args: 756 test_func: The common logic shared by all these generated test 757 cases. This function should take at least one argument, 758 which is a parameter set. 759 settings: A list of strings representing parameter sets. These are 760 usually json strings that get loaded in the test_func. 761 args: Iterable of additional position args to be passed to 762 test_func. 763 kwargs: Dict of additional keyword args to be passed to test_func 764 tag: Name of this group of generated test cases. Ignored if 765 name_func is provided and operates properly. 766 name_func: A function that takes a test setting and generates a 767 proper test name. The test name should be shorter than 768 utils.MAX_FILENAME_LEN. Names over the limit will be 769 truncated. 770 771 Returns: 772 A list of settings that did not pass. 773 """ 774 args = args or () 775 kwargs = kwargs or {} 776 failed_settings = [] 777 778 def GenerateTestName(setting): 779 test_name = "{} {}".format(tag, setting) 780 if name_func: 781 try: 782 test_name = name_func(setting, *args, **kwargs) 783 except: 784 logging.exception(("Failed to get test name from " 785 "test_func. Fall back to default %s"), 786 test_name) 787 788 if len(test_name) > utils.MAX_FILENAME_LEN: 789 test_name = test_name[:utils.MAX_FILENAME_LEN] 790 791 return test_name 792 793 for setting in settings: 794 test_name = GenerateTestName(setting) 795 796 tr_record = records.TestResultRecord(test_name, self.test_module_name) 797 self.results.requested.append(tr_record) 798 799 for setting in settings: 800 test_name = GenerateTestName(setting) 801 previous_success_cnt = len(self.results.passed) 802 803 self.execOneTest(test_name, test_func, (setting, ) + args, **kwargs) 804 if len(self.results.passed) - previous_success_cnt != 1: 805 failed_settings.append(setting) 806 807 return failed_settings 808 809 def _exec_func(self, func, *args): 810 """Executes a function with exception safeguard. 811 812 This will let signals.TestAbortAll through so abortAll works in all 813 procedure functions. 814 815 Args: 816 func: Function to be executed. 817 args: Arguments to be passed to the function. 818 819 Returns: 820 Whatever the function returns, or False if non-caught exception 821 occurred. 822 """ 823 try: 824 return func(*args) 825 except (signals.TestAbortAll, acts_signals.TestAbortAll) as e: 826 raise signals.TestAbortAll, e, sys.exc_info()[2] 827 except: 828 logging.exception("Exception happened when executing %s in %s.", 829 func.__name__, self.test_module_name) 830 return False 831 832 def _get_all_test_names(self): 833 """Finds all the function names that match the test case naming 834 convention in this class. 835 836 Returns: 837 A list of strings, each is a test case name. 838 """ 839 test_names = [] 840 for name in dir(self): 841 if name.startswith(STR_TEST) or name.startswith(STR_GENERATE): 842 attr_func = getattr(self, name) 843 if hasattr(attr_func, "__call__"): 844 test_names.append(name) 845 return test_names 846 847 def _get_test_funcs(self, test_names): 848 """Obtain the actual functions of test cases based on test names. 849 850 Args: 851 test_names: A list of strings, each string is a test case name. 852 853 Returns: 854 A list of tuples of (string, function). String is the test case 855 name, function is the actual test case function. 856 857 Raises: 858 errors.USERError is raised if the test name does not follow 859 naming convention "test_*". This can only be caused by user input 860 here. 861 """ 862 test_funcs = [] 863 for test_name in test_names: 864 if not hasattr(self, test_name): 865 logging.warning("%s does not have test case %s.", 866 self.test_module_name, test_name) 867 elif (test_name.startswith(STR_TEST) or 868 test_name.startswith(STR_GENERATE)): 869 test_funcs.append((test_name, getattr(self, test_name))) 870 else: 871 msg = ("Test case name %s does not follow naming convention " 872 "test*, abort.") % test_name 873 raise errors.USERError(msg) 874 875 return test_funcs 876 877 def getTests(self, test_names=None): 878 """Get the test cases within a test class. 879 880 Args: 881 test_names: A list of string that are test case names requested in 882 cmd line. 883 884 Returns: 885 A list of tuples of (string, function). String is the test case 886 name, function is the actual test case function. 887 """ 888 if not test_names: 889 if self.tests: 890 # Specified by run list in class. 891 test_names = list(self.tests) 892 else: 893 # No test case specified by user, execute all in the test class 894 test_names = self._get_all_test_names() 895 896 tests = self._get_test_funcs(test_names) 897 return tests 898 899 def runTests(self, tests): 900 """Run tests and collect test results. 901 902 Args: 903 tests: A list of tests to be run. 904 905 Returns: 906 The test results object of this class. 907 """ 908 # Setup for the class with retry. 909 for i in xrange(_SETUP_RETRY_NUMBER): 910 try: 911 if self._setUpClass() is False: 912 raise signals.TestFailure( 913 "Failed to setup %s." % self.test_module_name) 914 else: 915 break 916 except Exception as e: 917 logging.exception("Failed to setup %s.", self.test_module_name) 918 if i + 1 == _SETUP_RETRY_NUMBER: 919 self.results.failClass(self.test_module_name, e) 920 self._exec_func(self._tearDownClass) 921 return self.results 922 else: 923 # restart services before retry setup. 924 for device in self.android_devices: 925 logging.info("restarting service on device %s", device.serial) 926 device.stopServices() 927 device.startServices() 928 929 # Run tests in order. 930 try: 931 # Check if module is running in self test mode. 932 if self.run_as_vts_self_test: 933 logging.info('setUpClass function was executed successfully.') 934 self.results.passClass(self.test_module_name) 935 return self.results 936 937 for test_name, test_func in tests: 938 if test_name.startswith(STR_GENERATE): 939 logging.info( 940 "Executing generated test trigger function '%s'", 941 test_name) 942 test_func() 943 logging.info("Finished '%s'", test_name) 944 else: 945 self.execOneTest(test_name, test_func, None) 946 if self.isSkipAllTests() and not self.results.executed: 947 self.results.skipClass( 948 self.test_module_name, 949 "All test cases skipped; unable to find any test case.") 950 return self.results 951 except (signals.TestAbortClass, acts_signals.TestAbortClass): 952 logging.info("Received TestAbortClass signal") 953 return self.results 954 except (signals.TestAbortAll, acts_signals.TestAbortAll) as e: 955 logging.info("Received TestAbortAll signal") 956 # Piggy-back test results on this exception object so we don't lose 957 # results from this test class. 958 setattr(e, "results", self.results) 959 raise signals.TestAbortAll, e, sys.exc_info()[2] 960 except Exception as e: 961 # Exception happened during test. 962 logging.exception(e) 963 raise e 964 finally: 965 self._exec_func(self._tearDownClass) 966 if self.web.enabled: 967 name, timestamp = self.web.GetTestModuleKeys() 968 self.results.setTestModuleKeys(name, timestamp) 969 logging.info("Summary for test class %s: %s", 970 self.test_module_name, self.results.summary()) 971 972 def run(self, test_names=None): 973 """Runs test cases within a test class by the order they appear in the 974 execution list. 975 976 One of these test cases lists will be executed, shown here in priority 977 order: 978 1. The test_names list, which is passed from cmd line. Invalid names 979 are guarded by cmd line arg parsing. 980 2. The self.tests list defined in test class. Invalid names are 981 ignored. 982 3. All function that matches test case naming convention in the test 983 class. 984 985 Args: 986 test_names: A list of string that are test case names requested in 987 cmd line. 988 989 Returns: 990 The test results object of this class. 991 """ 992 logging.info("==========> %s <==========", self.test_module_name) 993 # Devise the actual test cases to run in the test class. 994 tests = self.getTests(test_names) 995 996 if not self.run_as_vts_self_test: 997 self.results.requested = [ 998 records.TestResultRecord(test_name, self.test_module_name) 999 for test_name,_ in tests if test_name.startswith(STR_TEST) 1000 ] 1001 return self.runTests(tests) 1002 1003 def cleanUp(self): 1004 """A function that is executed upon completion of all tests cases 1005 selected in the test class. 1006 1007 This function should clean up objects initialized in the constructor by 1008 user. 1009 """ 1010 1011 def DumpBugReport(self, prefix=''): 1012 """Get device bugreport through adb command. 1013 1014 Args: 1015 prefix: string, file name prefix. Usually in format of 1016 <test_module>-<test_case> 1017 """ 1018 if prefix: 1019 prefix = re.sub('[^\w\-_\. ]', '_', prefix) + '_' 1020 1021 for device in self.android_devices: 1022 file_name = (prefix 1023 + _BUG_REPORT_FILE_PREFIX 1024 + '_%s' % device.serial 1025 + _BUG_REPORT_FILE_EXTENSION) 1026 1027 file_path = os.path.join(logging.log_path, 1028 file_name) 1029 1030 logging.info('Catching bugreport %s...' % file_path) 1031 device.adb.bugreport(file_path) 1032 1033 def skipAllTests(self, msg): 1034 """Skip all test cases. 1035 1036 This method is usually called in setup functions when a precondition 1037 to the test module is not met. 1038 1039 Args: 1040 msg: string, reason why tests are skipped. If set to None or empty 1041 string, a default message will be used (not recommended) 1042 """ 1043 if not msg: 1044 msg = "No reason provided." 1045 1046 setattr(self, _REASON_TO_SKIP_ALL_TESTS, msg) 1047 1048 def isSkipAllTests(self): 1049 """Returns whether all tests are set to be skipped. 1050 1051 Note: If all tests are being skipped not due to skipAllTests 1052 being called, or there is no tests defined, this method will 1053 still return False (since skipAllTests is not called.) 1054 1055 Returns: 1056 bool, True if skipAllTests has been called; False otherwise. 1057 """ 1058 return self.getSkipAllTestsReason() is not None 1059 1060 def getSkipAllTestsReason(self): 1061 """Returns the reason why all tests are skipped. 1062 1063 Note: If all tests are being skipped not due to skipAllTests 1064 being called, or there is no tests defined, this method will 1065 still return None (since skipAllTests is not called.) 1066 1067 Returns: 1068 String, reason why tests are skipped. None if skipAllTests 1069 is not called. 1070 """ 1071 return getattr(self, _REASON_TO_SKIP_ALL_TESTS, None) 1072 1073 def DumpLogcat(self, prefix=''): 1074 """Dumps device logcat outputs to log directory. 1075 1076 Args: 1077 prefix: string, file name prefix. Usually in format of 1078 <test_module>-<test_case> 1079 """ 1080 if prefix: 1081 prefix = re.sub('[^\w\-_\. ]', '_', prefix) + '_' 1082 1083 for device in self.android_devices: 1084 for buffer in LOGCAT_BUFFERS: 1085 file_name = (prefix 1086 + _LOGCAT_FILE_PREFIX 1087 + '_%s_' % buffer 1088 + device.serial 1089 + _LOGCAT_FILE_EXTENSION) 1090 1091 file_path = os.path.join(logging.log_path, 1092 file_name) 1093 1094 logging.info('Dumping logcat %s...' % file_path) 1095 device.adb.logcat('-b', buffer, '-d', '>', file_path) 1096