blob: dd8ff848ba30fb16355ac9f9e945927e87be618f [file] [log] [blame]
Ang Li93420002016-05-10 19:11:44 -07001#!/usr/bin/env python3.4
2#
Keun Soo Yimb05f84e2016-10-31 09:29:42 -07003# Copyright (C) 2016 The Android Open Source Project
Ang Li93420002016-05-10 19:11:44 -07004#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
Keun Soo Yimb05f84e2016-10-31 09:29:42 -07009# http://www.apache.org/licenses/LICENSE-2.0
Ang Li93420002016-05-10 19:11:44 -070010#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
Keun Soo Yimb05f84e2016-10-31 09:29:42 -070016#
Ang Li93420002016-05-10 19:11:44 -070017
Ang Li64920272016-05-26 18:37:43 -070018import logging
Ang Li93420002016-05-10 19:11:44 -070019import os
20
21from vts.runners.host import asserts
22from vts.runners.host import errors
23from vts.runners.host import keys
24from vts.runners.host import logger
25from vts.runners.host import records
26from vts.runners.host import signals
Ang Li93420002016-05-10 19:11:44 -070027from vts.runners.host import utils
Yuexi Mab34e8c72016-09-16 20:58:08 +000028from vts.runners.host import const
29from vts.utils.python.common import list_utils
Ang Li93420002016-05-10 19:11:44 -070030
Ang Li93420002016-05-10 19:11:44 -070031# Macro strings for test result reporting
32TEST_CASE_TOKEN = "[Test Case]"
33RESULT_LINE_TEMPLATE = TEST_CASE_TOKEN + " %s %s"
Yuexi Maf87f2c62016-09-14 00:47:30 -070034STR_TEST = "test"
35STR_GENERATE = "generate"
Ang Li93420002016-05-10 19:11:44 -070036
37
38class BaseTestClass(object):
39 """Base class for all test classes to inherit from.
40
41 This class gets all the controller objects from test_runner and executes
42 the test cases requested within itself.
43
44 Most attributes of this class are set at runtime based on the configuration
45 provided.
46
47 Attributes:
48 tests: A list of strings, each representing a test case name.
49 TAG: A string used to refer to a test class. Default is the test class
50 name.
Ang Li93420002016-05-10 19:11:44 -070051 results: A records.TestResult object for aggregating test results from
52 the execution of test cases.
53 currentTestName: A string that's the name of the test case currently
54 being executed. If no test is executing, this should
55 be None.
Yuexi Mab34e8c72016-09-16 20:58:08 +000056 include_filer: A list of string, each representing a test case name to
57 include.
58 exclude_filer: A list of string, each representing a test case name to
59 exclude. Has no effect if include_filer is not empty.
Yuexi Maf3628182016-11-18 17:03:54 -080060 abi_name: String, name of abi in use
61 abi_bitness: String, bitness of abi in use
Ang Li93420002016-05-10 19:11:44 -070062 """
63
64 TAG = None
65
66 def __init__(self, configs):
67 self.tests = []
68 if not self.TAG:
69 self.TAG = self.__class__.__name__
70 # Set all the controller objects and params.
71 for name, value in configs.items():
72 setattr(self, name, value)
73 self.results = records.TestResult()
74 self.currentTestName = None
75
Yuexi Maf3afb602016-09-30 16:18:23 -070076 # Setup test filters (optional)
77 if keys.ConfigKeys.KEY_TEST_SUITE in self.user_params:
78 test_suite = self.user_params[keys.ConfigKeys.KEY_TEST_SUITE]
79 filters = [keys.ConfigKeys.KEY_INCLUDE_FILTER,
80 keys.ConfigKeys.KEY_EXCLUDE_FILTER]
81 for filter in filters:
82 if filter in test_suite:
83 filter_expanded = list_utils.ExpandItemDelimiters(
84 test_suite[filter],
85 const.LIST_ITEM_DELIMITER,
86 strip=True)
87 setattr(self, filter, filter_expanded)
Yuexi Mab34e8c72016-09-16 20:58:08 +000088
Yuexi Maf3628182016-11-18 17:03:54 -080089 # TODO: get abi information differently for multi-device support.
90 # Set other optional parameters
91 opt_param_names = [keys.ConfigKeys.IKEY_ABI_NAME,
92 keys.ConfigKeys.IKEY_ABI_BITNESS,
93 keys.ConfigKeys.IKEY_RUN_32BIT_ON_64BIT_ABI]
94 self.getUserParams(opt_param_names=opt_param_names)
Yuexi Mab5132a42016-11-10 14:59:30 -080095
Ang Li93420002016-05-10 19:11:44 -070096 def __enter__(self):
97 return self
98
99 def __exit__(self, *args):
100 self._exec_func(self.cleanUp)
101
Ang Lie2139f12016-05-12 17:39:06 -0700102 def getUserParams(self, req_param_names=[], opt_param_names=[], **kwargs):
Ang Li93420002016-05-10 19:11:44 -0700103 """Unpacks user defined parameters in test config into individual
104 variables.
105
106 Instead of accessing the user param with self.user_params["xxx"], the
107 variable can be directly accessed with self.xxx.
108
109 A missing required param will raise an exception. If an optional param
110 is missing, an INFO line will be logged.
111
112 Args:
113 req_param_names: A list of names of the required user params.
114 opt_param_names: A list of names of the optional user params.
115 **kwargs: Arguments that provide default values.
116 e.g. getUserParams(required_list, opt_list, arg_a="hello")
117 self.arg_a will be "hello" unless it is specified again in
118 required_list or opt_list.
119
120 Raises:
121 BaseTestError is raised if a required user params is missing from
122 test config.
123 """
124 for k, v in kwargs.items():
125 setattr(self, k, v)
126 for name in req_param_names:
127 if name not in self.user_params:
Ang Li05f99ab2016-08-04 16:48:24 -0700128 raise errors.BaseTestError(("Missing required user param '%s' "
129 "in test configuration.") % name)
Ang Li93420002016-05-10 19:11:44 -0700130 setattr(self, name, self.user_params[name])
131 for name in opt_param_names:
132 if name not in self.user_params:
Yuexi Maf87f2c62016-09-14 00:47:30 -0700133 logging.info(("Missing optional user param '%s' in "
134 "configuration, continue."), name)
Ang Li93420002016-05-10 19:11:44 -0700135 else:
136 setattr(self, name, self.user_params[name])
137
Yuexi Mafb20bb32016-11-10 15:39:30 -0800138 def getUserParam(self,
139 param_name,
140 error_if_not_found=False,
141 default_value=None):
Yuexi Mac1396c02016-11-10 15:13:29 -0800142 """Get the value of a single user parameter.
143
144 This method returns the value of specified user parameter.
145 Note: this method will not automatically set attribute using the parameter name and value.
146
147 Args:
148 param_name: string or list of string, denoting user parameter names. If provided
149 a single string, self.user_params["<param_name>"] will be accessed.
150 If provided multiple strings,
151 self.user_params["<param_name1>"]["<param_name2>"]["<param_name3>"]...
152 will be accessed.
153 error_if_not_found: bool, whether to raise error if parameter not exists. Default:
154 False
155 default_value: object, default value to return if not found. If error_if_not_found is
156 True, this parameter has no effect. Default: None
157
158 Returns:
159 object, value of the specified parameter name chain if exists;
160 <default_value> if not exists.
161 """
162 if not param_name:
163 if error_if_not_found:
164 raise errors.BaseTestError("empty param_name provided")
165 logging.error("empty param_name")
166 return default_value
167
168 if not isinstance(param_name, list):
169 param_name = [param_name]
170
171 curr_obj = self.user_params
172 for param in param_name:
173 if param not in curr_obj:
174 if error_if_not_found:
175 raise errors.BaseTestError(
176 ("Missing user param '%s' "
177 "in test configuration.") % name)
178 return default_value
179 curr_obj = curr_obj[param]
180
181 return curr_obj
182
Ang Li93420002016-05-10 19:11:44 -0700183 def _setUpClass(self):
184 """Proxy function to guarantee the base implementation of setUpClass
185 is called.
186 """
187 return self.setUpClass()
188
189 def setUpClass(self):
190 """Setup function that will be called before executing any test case in
191 the test class.
192
193 To signal setup failure, return False or raise an exception. If
194 exceptions were raised, the stack trace would appear in log, but the
195 exceptions would not propagate to upper levels.
196
197 Implementation is optional.
198 """
Keun Soo Yimbae361d2016-07-23 10:39:54 -0700199 pass
200
201 def _tearDownClass(self):
202 """Proxy function to guarantee the base implementation of tearDownClass
203 is called.
204 """
205 return self.tearDownClass()
Ang Li93420002016-05-10 19:11:44 -0700206
207 def tearDownClass(self):
208 """Teardown function that will be called after all the selected test
209 cases in the test class have been executed.
210
211 Implementation is optional.
212 """
Keun Soo Yimbae361d2016-07-23 10:39:54 -0700213 pass
Ang Li93420002016-05-10 19:11:44 -0700214
Yuexi Ma4e228992016-11-15 22:48:57 -0800215 def _testEntry(self, test_name):
Yuexi Ma13077852016-11-17 18:37:02 -0800216 """Internal function to be called upon entry of a test case."""
Yuexi Ma4e228992016-11-15 22:48:57 -0800217 self.currentTestName = test_name
218
Ang Li93420002016-05-10 19:11:44 -0700219 def _setUpTest(self, test_name):
220 """Proxy function to guarantee the base implementation of setUpTest is
221 called.
222 """
Ang Li93420002016-05-10 19:11:44 -0700223 return self.setUpTest()
224
225 def setUpTest(self):
226 """Setup function that will be called every time before executing each
227 test case in the test class.
228
229 To signal setup failure, return False or raise an exception. If
230 exceptions were raised, the stack trace would appear in log, but the
231 exceptions would not propagate to upper levels.
232
233 Implementation is optional.
234 """
235
Yuexi Ma4e228992016-11-15 22:48:57 -0800236 def _testExit(self, test_name):
237 """Internal function to be called upon exit of a test."""
238 self.currentTestName = None
239
Ang Li93420002016-05-10 19:11:44 -0700240 def _tearDownTest(self, test_name):
241 """Proxy function to guarantee the base implementation of tearDownTest
242 is called.
243 """
Yuexi Ma4e228992016-11-15 22:48:57 -0800244 self.tearDownTest()
Ang Li93420002016-05-10 19:11:44 -0700245
246 def tearDownTest(self):
247 """Teardown function that will be called every time a test case has
248 been executed.
249
250 Implementation is optional.
251 """
252
253 def _onFail(self, record):
254 """Proxy function to guarantee the base implementation of onFail is
255 called.
256
257 Args:
258 record: The records.TestResultRecord object for the failed test
259 case.
260 """
261 test_name = record.test_name
Ang Li64920272016-05-26 18:37:43 -0700262 logging.error(record.details)
Ang Li93420002016-05-10 19:11:44 -0700263 begin_time = logger.epochToLogLineTimestamp(record.begin_time)
Ang Li64920272016-05-26 18:37:43 -0700264 logging.info(RESULT_LINE_TEMPLATE, test_name, record.result)
Ang Li93420002016-05-10 19:11:44 -0700265 self.onFail(test_name, begin_time)
266
267 def onFail(self, test_name, begin_time):
268 """A function that is executed upon a test case failure.
269
270 User implementation is optional.
271
272 Args:
273 test_name: Name of the test that triggered this function.
274 begin_time: Logline format timestamp taken when the test started.
275 """
276
277 def _onPass(self, record):
278 """Proxy function to guarantee the base implementation of onPass is
279 called.
280
281 Args:
282 record: The records.TestResultRecord object for the passed test
283 case.
284 """
285 test_name = record.test_name
286 begin_time = logger.epochToLogLineTimestamp(record.begin_time)
287 msg = record.details
288 if msg:
Ang Li64920272016-05-26 18:37:43 -0700289 logging.info(msg)
290 logging.info(RESULT_LINE_TEMPLATE, test_name, record.result)
Ang Li93420002016-05-10 19:11:44 -0700291 self.onPass(test_name, begin_time)
292
293 def onPass(self, test_name, begin_time):
294 """A function that is executed upon a test case passing.
295
296 Implementation is optional.
297
298 Args:
299 test_name: Name of the test that triggered this function.
300 begin_time: Logline format timestamp taken when the test started.
301 """
302
303 def _onSkip(self, record):
304 """Proxy function to guarantee the base implementation of onSkip is
305 called.
306
307 Args:
308 record: The records.TestResultRecord object for the skipped test
309 case.
310 """
311 test_name = record.test_name
312 begin_time = logger.epochToLogLineTimestamp(record.begin_time)
Ang Li64920272016-05-26 18:37:43 -0700313 logging.info(RESULT_LINE_TEMPLATE, test_name, record.result)
314 logging.info("Reason to skip: %s", record.details)
Ang Li93420002016-05-10 19:11:44 -0700315 self.onSkip(test_name, begin_time)
316
317 def onSkip(self, test_name, begin_time):
318 """A function that is executed upon a test case being skipped.
319
320 Implementation is optional.
321
322 Args:
323 test_name: Name of the test that triggered this function.
324 begin_time: Logline format timestamp taken when the test started.
325 """
326
Yuexi Ma13077852016-11-17 18:37:02 -0800327 def _onSilent(self, record):
328 """Proxy function to guarantee the base implementation of onSilent is
329 called.
330
331 Args:
332 record: The records.TestResultRecord object for the skipped test
333 case.
334 """
335 test_name = record.test_name
336 begin_time = logger.epochToLogLineTimestamp(record.begin_time)
337 self.onSilent(test_name, begin_time)
338
339 def onSilent(self, test_name, begin_time):
340 """A function that is executed upon a test case being marked as silent.
341
342 Implementation is optional.
343
344 Args:
345 test_name: Name of the test that triggered this function.
346 begin_time: Logline format timestamp taken when the test started.
347 """
348
Ang Li93420002016-05-10 19:11:44 -0700349 def _onException(self, record):
350 """Proxy function to guarantee the base implementation of onException
351 is called.
352
353 Args:
354 record: The records.TestResultRecord object for the failed test
355 case.
356 """
357 test_name = record.test_name
Ang Li64920272016-05-26 18:37:43 -0700358 logging.exception(record.details)
Ang Li93420002016-05-10 19:11:44 -0700359 begin_time = logger.epochToLogLineTimestamp(record.begin_time)
Ang Li93420002016-05-10 19:11:44 -0700360 self.onException(test_name, begin_time)
361
362 def onException(self, test_name, begin_time):
363 """A function that is executed upon an unhandled exception from a test
364 case.
365
366 Implementation is optional.
367
368 Args:
369 test_name: Name of the test that triggered this function.
370 begin_time: Logline format timestamp taken when the test started.
371 """
372
373 def _exec_procedure_func(self, func, tr_record):
374 """Executes a procedure function like onPass, onFail etc.
375
376 This function will alternate the 'Result' of the test's record if
377 exceptions happened when executing the procedure function.
378
379 This will let signals.TestAbortAll through so abortAll works in all
380 procedure functions.
381
382 Args:
383 func: The procedure function to be executed.
384 tr_record: The TestResultRecord object associated with the test
385 case executed.
386 """
387 try:
388 func(tr_record)
389 except signals.TestAbortAll:
390 raise
391 except Exception as e:
Ang Li64920272016-05-26 18:37:43 -0700392 logging.exception("Exception happened when executing %s for %s.",
393 func.__name__, self.currentTestName)
Ang Li93420002016-05-10 19:11:44 -0700394 tr_record.addError(func.__name__, e)
395
Yuexi Maf3afb602016-09-30 16:18:23 -0700396 def filterOneTest(self, test_name):
Yuexi Mab5132a42016-11-10 14:59:30 -0800397 """Check test filters for a test name.
Yuexi Maf3afb602016-09-30 16:18:23 -0700398
Yuexi Mab5132a42016-11-10 14:59:30 -0800399 The first layer of filter is user defined test filters:
400 if a include filter is not empty, only tests in include filter will
Yuexi Maf3afb602016-09-30 16:18:23 -0700401 be executed regardless whether they are also in exclude filter. Else
402 if include filter is empty, only tests not in exclude filter will be
403 executed.
404
Yuexi Mab5132a42016-11-10 14:59:30 -0800405 The second layer of filter is checking abi bitness:
406 if a test has a suffix indicating the intended architecture bitness,
407 and the current abi bitness information is available, non matching tests
408 will be skipped. By our convention, this function will look for bitness in suffix
409 formated as "32bit", "32Bit", "32BIT", or 64 bit equivalents.
410
Yuexi Mafb20bb32016-11-10 15:39:30 -0800411 This method assumes const.SUFFIX_32BIT and const.SUFFIX_64BIT are in lower cases.
412
Yuexi Maf3afb602016-09-30 16:18:23 -0700413 Args:
414 test_name: string, name of a test
415
416 Raises:
417 signals.TestSilent if a test should not be executed
418 """
419 if (hasattr(self, keys.ConfigKeys.KEY_INCLUDE_FILTER) and
420 getattr(self, keys.ConfigKeys.KEY_INCLUDE_FILTER)):
421 if test_name not in getattr(self,
422 keys.ConfigKeys.KEY_INCLUDE_FILTER):
423 logging.info("Test case '%s' not in include filter." %
424 test_name)
425 raise signals.TestSilent(
426 "Test case '%s' not in include filter." % test_name)
427 elif (hasattr(self, keys.ConfigKeys.KEY_EXCLUDE_FILTER) and
428 test_name in getattr(self, keys.ConfigKeys.KEY_EXCLUDE_FILTER)):
429 logging.info("Test case '%s' in exclude filter." % test_name)
430 raise signals.TestSilent("Test case '%s' in exclude filter." %
431 test_name)
432
Yuexi Maf3628182016-11-18 17:03:54 -0800433 if hasattr(self, keys.ConfigKeys.IKEY_ABI_BITNESS):
434 bitness = getattr(self, keys.ConfigKeys.IKEY_ABI_BITNESS)
Yuexi Maf97beaf2016-11-23 12:58:06 -0800435 run_32bit_on_64bit_abi = getattr(
Yuexi Maa4ba3932016-11-23 15:04:18 -0800436 self, keys.ConfigKeys.IKEY_RUN_32BIT_ON_64BIT_ABI, False)
Yuexi Mab5132a42016-11-10 14:59:30 -0800437 asserts.skipIf(
Yuexi Ma9c1ec632016-11-18 18:36:55 -0800438 (test_name.lower().endswith(const.SUFFIX_32BIT) and
Yuexi Maf97beaf2016-11-23 12:58:06 -0800439 bitness != "32") or
Yuexi Ma9c1ec632016-11-18 18:36:55 -0800440 (test_name.lower().endswith(const.SUFFIX_64BIT) and
Yuexi Maf97beaf2016-11-23 12:58:06 -0800441 bitness != "64" and not run_32bit_on_64bit_abi),
Yuexi Mab5132a42016-11-10 14:59:30 -0800442 "Test case '{}' excluded as abi bitness is {}.".format(
Yuexi Maf3628182016-11-18 17:03:54 -0800443 test_name, bitness))
Yuexi Mab5132a42016-11-10 14:59:30 -0800444
Ang Li93420002016-05-10 19:11:44 -0700445 def execOneTest(self, test_name, test_func, args, **kwargs):
446 """Executes one test case and update test results.
447
448 Executes one test case, create a records.TestResultRecord object with
449 the execution information, and add the record to the test class's test
450 results.
451
452 Args:
453 test_name: Name of the test.
454 test_func: The test function.
455 args: A tuple of params.
456 kwargs: Extra kwargs.
457 """
Yuexi Ma85941942016-09-14 00:49:24 -0700458 is_silenced = False
Ang Li93420002016-05-10 19:11:44 -0700459 tr_record = records.TestResultRecord(test_name, self.TAG)
460 tr_record.testBegin()
Ang Li64920272016-05-26 18:37:43 -0700461 logging.info("%s %s", TEST_CASE_TOKEN, test_name)
Ang Li93420002016-05-10 19:11:44 -0700462 verdict = None
463 try:
Yuexi Ma4e228992016-11-15 22:48:57 -0800464 ret = self._testEntry(test_name)
Ang Li93420002016-05-10 19:11:44 -0700465 asserts.assertTrue(ret is not False,
Yuexi Ma4e228992016-11-15 22:48:57 -0800466 "Setup test entry for %s failed." % test_name)
467 self.filterOneTest(test_name)
Ang Li93420002016-05-10 19:11:44 -0700468 try:
Yuexi Ma4e228992016-11-15 22:48:57 -0800469 ret = self._setUpTest(test_name)
470 asserts.assertTrue(ret is not False,
471 "Setup for %s failed." % test_name)
472
Ang Li93420002016-05-10 19:11:44 -0700473 if args or kwargs:
474 verdict = test_func(*args, **kwargs)
475 else:
476 verdict = test_func()
477 finally:
478 self._tearDownTest(test_name)
479 except (signals.TestFailure, AssertionError) as e:
480 tr_record.testFail(e)
481 self._exec_procedure_func(self._onFail, tr_record)
482 except signals.TestSkip as e:
483 # Test skipped.
484 tr_record.testSkip(e)
485 self._exec_procedure_func(self._onSkip, tr_record)
486 except (signals.TestAbortClass, signals.TestAbortAll) as e:
487 # Abort signals, pass along.
488 tr_record.testFail(e)
489 raise e
490 except signals.TestPass as e:
491 # Explicit test pass.
492 tr_record.testPass(e)
493 self._exec_procedure_func(self._onPass, tr_record)
494 except signals.TestSilent as e:
Yuexi Maaeaacbc2016-09-15 21:28:44 -0700495 # Suppress test reporting.
Yuexi Ma85941942016-09-14 00:49:24 -0700496 is_silenced = True
Yuexi Ma13077852016-11-17 18:37:02 -0800497 self._exec_procedure_func(self._onSilent, tr_record)
Ang Li93420002016-05-10 19:11:44 -0700498 self.results.requested.remove(test_name)
499 except Exception as e:
500 # Exception happened during test.
Yuexi Maaeaacbc2016-09-15 21:28:44 -0700501 logging.exception(e)
Ang Li93420002016-05-10 19:11:44 -0700502 tr_record.testError(e)
503 self._exec_procedure_func(self._onException, tr_record)
504 self._exec_procedure_func(self._onFail, tr_record)
505 else:
506 # Keep supporting return False for now.
507 # TODO(angli): Deprecate return False support.
508 if verdict or (verdict is None):
509 # Test passed.
510 tr_record.testPass()
511 self._exec_procedure_func(self._onPass, tr_record)
512 return
513 # Test failed because it didn't return True.
514 # This should be removed eventually.
515 tr_record.testFail()
516 self._exec_procedure_func(self._onFail, tr_record)
517 finally:
Yuexi Ma85941942016-09-14 00:49:24 -0700518 if not is_silenced:
Ang Li93420002016-05-10 19:11:44 -0700519 self.results.addRecord(tr_record)
Yuexi Ma13077852016-11-17 18:37:02 -0800520 self._testExit(test_name)
Ang Li93420002016-05-10 19:11:44 -0700521
Ang Lie2139f12016-05-12 17:39:06 -0700522 def runGeneratedTests(self,
523 test_func,
524 settings,
525 args=None,
526 kwargs=None,
527 tag="",
528 name_func=None):
Ang Li93420002016-05-10 19:11:44 -0700529 """Runs generated test cases.
530
531 Generated test cases are not written down as functions, but as a list
532 of parameter sets. This way we reduce code repetition and improve
533 test case scalability.
534
535 Args:
536 test_func: The common logic shared by all these generated test
537 cases. This function should take at least one argument,
538 which is a parameter set.
539 settings: A list of strings representing parameter sets. These are
540 usually json strings that get loaded in the test_func.
541 args: Iterable of additional position args to be passed to
542 test_func.
543 kwargs: Dict of additional keyword args to be passed to test_func
544 tag: Name of this group of generated test cases. Ignored if
545 name_func is provided and operates properly.
546 name_func: A function that takes a test setting and generates a
547 proper test name. The test name should be shorter than
548 utils.MAX_FILENAME_LEN. Names over the limit will be
549 truncated.
550
551 Returns:
552 A list of settings that did not pass.
553 """
554 args = args or ()
555 kwargs = kwargs or {}
556 failed_settings = []
557 for s in settings:
558 test_name = "{} {}".format(tag, s)
559 if name_func:
560 try:
561 test_name = name_func(s, *args, **kwargs)
562 except:
Yuexi Maf87f2c62016-09-14 00:47:30 -0700563 logging.exception(("Failed to get test name from "
564 "test_func. Fall back to default %s"),
565 test_name)
Ang Li93420002016-05-10 19:11:44 -0700566 self.results.requested.append(test_name)
567 if len(test_name) > utils.MAX_FILENAME_LEN:
568 test_name = test_name[:utils.MAX_FILENAME_LEN]
569 previous_success_cnt = len(self.results.passed)
Ang Lie2139f12016-05-12 17:39:06 -0700570 self.execOneTest(test_name, test_func, (s, ) + args, **kwargs)
Ang Li93420002016-05-10 19:11:44 -0700571 if len(self.results.passed) - previous_success_cnt != 1:
572 failed_settings.append(s)
573 return failed_settings
574
575 def _exec_func(self, func, *args):
576 """Executes a function with exception safeguard.
577
578 This will let signals.TestAbortAll through so abortAll works in all
579 procedure functions.
580
581 Args:
582 func: Function to be executed.
583 args: Arguments to be passed to the function.
584
585 Returns:
586 Whatever the function returns, or False if unhandled exception
587 occured.
588 """
589 try:
590 return func(*args)
591 except signals.TestAbortAll:
592 raise
593 except:
Ang Li64920272016-05-26 18:37:43 -0700594 logging.exception("Exception happened when executing %s in %s.",
595 func.__name__, self.TAG)
Ang Li93420002016-05-10 19:11:44 -0700596 return False
597
598 def _get_all_test_names(self):
599 """Finds all the function names that match the test case naming
600 convention in this class.
601
602 Returns:
603 A list of strings, each is a test case name.
604 """
605 test_names = []
606 for name in dir(self):
Yuexi Maf87f2c62016-09-14 00:47:30 -0700607 if name.startswith(STR_TEST) or name.startswith(STR_GENERATE):
Ang Li64920272016-05-26 18:37:43 -0700608 attr_func = getattr(self, name)
609 if hasattr(attr_func, "__call__"):
610 test_names.append(name)
Ang Li93420002016-05-10 19:11:44 -0700611 return test_names
612
613 def _get_test_funcs(self, test_names):
614 """Obtain the actual functions of test cases based on test names.
615
616 Args:
617 test_names: A list of strings, each string is a test case name.
618
619 Returns:
620 A list of tuples of (string, function). String is the test case
621 name, function is the actual test case function.
622
623 Raises:
624 errors.USERError is raised if the test name does not follow
625 naming convention "test_*". This can only be caused by user input
626 here.
627 """
628 test_funcs = []
629 for test_name in test_names:
Yuexi Maf87f2c62016-09-14 00:47:30 -0700630 if not hasattr(self, test_name):
631 logging.warning("%s does not have test case %s.", self.TAG,
632 test_name)
633 elif (test_name.startswith(STR_TEST) or
634 test_name.startswith(STR_GENERATE)):
635 test_funcs.append((test_name, getattr(self, test_name)))
636 else:
Ang Li93420002016-05-10 19:11:44 -0700637 msg = ("Test case name %s does not follow naming convention "
Ang Li64920272016-05-26 18:37:43 -0700638 "test*, abort.") % test_name
Ang Li93420002016-05-10 19:11:44 -0700639 raise errors.USERError(msg)
Yuexi Maf87f2c62016-09-14 00:47:30 -0700640
Ang Li93420002016-05-10 19:11:44 -0700641 return test_funcs
642
643 def run(self, test_names=None):
644 """Runs test cases within a test class by the order they appear in the
645 execution list.
646
647 One of these test cases lists will be executed, shown here in priority
648 order:
649 1. The test_names list, which is passed from cmd line. Invalid names
650 are guarded by cmd line arg parsing.
651 2. The self.tests list defined in test class. Invalid names are
652 ignored.
653 3. All function that matches test case naming convention in the test
654 class.
655
656 Args:
657 test_names: A list of string that are test case names requested in
658 cmd line.
659
660 Returns:
661 The test results object of this class.
662 """
Ang Li64920272016-05-26 18:37:43 -0700663 logging.info("==========> %s <==========", self.TAG)
Ang Li93420002016-05-10 19:11:44 -0700664 # Devise the actual test cases to run in the test class.
665 if not test_names:
666 if self.tests:
667 # Specified by run list in class.
668 test_names = list(self.tests)
669 else:
670 # No test case specified by user, execute all in the test class
671 test_names = self._get_all_test_names()
Yuexi Maf87f2c62016-09-14 00:47:30 -0700672 self.results.requested = [test_name for test_name in test_names
673 if test_name.startswith(STR_TEST)]
Ang Li93420002016-05-10 19:11:44 -0700674 tests = self._get_test_funcs(test_names)
Yuexi Maf87f2c62016-09-14 00:47:30 -0700675
Ang Li93420002016-05-10 19:11:44 -0700676 # Setup for the class.
677 try:
678 if self._setUpClass() is False:
679 raise signals.TestFailure("Failed to setup %s." % self.TAG)
680 except Exception as e:
Ang Li64920272016-05-26 18:37:43 -0700681 logging.exception("Failed to setup %s.", self.TAG)
Ang Li93420002016-05-10 19:11:44 -0700682 self.results.failClass(self.TAG, e)
Keun Soo Yimbae361d2016-07-23 10:39:54 -0700683 self._exec_func(self._tearDownClass)
Ang Li93420002016-05-10 19:11:44 -0700684 return self.results
685 # Run tests in order.
686 try:
687 for test_name, test_func in tests:
Yuexi Maf87f2c62016-09-14 00:47:30 -0700688 if test_name.startswith(STR_GENERATE):
Yuexi Maaeaacbc2016-09-15 21:28:44 -0700689 logging.info(
690 "Executing generated test trigger function '%s'",
691 test_name)
Yuexi Maf87f2c62016-09-14 00:47:30 -0700692 test_func()
Yuexi Maaeaacbc2016-09-15 21:28:44 -0700693 logging.info("Finished '%s'", test_name)
Yuexi Maf87f2c62016-09-14 00:47:30 -0700694 else:
695 self.execOneTest(test_name, test_func, None)
Ang Li93420002016-05-10 19:11:44 -0700696 return self.results
697 except signals.TestAbortClass:
Yuexi Maaeaacbc2016-09-15 21:28:44 -0700698 logging.info("Received TestAbortClass signal")
Ang Li93420002016-05-10 19:11:44 -0700699 return self.results
700 except signals.TestAbortAll as e:
Yuexi Maaeaacbc2016-09-15 21:28:44 -0700701 logging.info("Received TestAbortAll signal")
Ang Li93420002016-05-10 19:11:44 -0700702 # Piggy-back test results on this exception object so we don't lose
703 # results from this test class.
704 setattr(e, "results", self.results)
705 raise e
Yuexi Maf8d6e5b2016-10-12 21:57:53 -0700706 except Exception as e:
707 # Exception happened during test.
708 logging.exception(e)
709 raise e
Ang Li93420002016-05-10 19:11:44 -0700710 finally:
Keun Soo Yimbae361d2016-07-23 10:39:54 -0700711 self._exec_func(self._tearDownClass)
Ang Li64920272016-05-26 18:37:43 -0700712 logging.info("Summary for test class %s: %s", self.TAG,
713 self.results.summary())
Ang Li93420002016-05-10 19:11:44 -0700714
715 def cleanUp(self):
716 """A function that is executed upon completion of all tests cases
717 selected in the test class.
718
719 This function should clean up objects initialized in the constructor by
720 user.
721 """