1#!/usr/bin/env python3
2#
3#   Copyright 2016 - The Android Open Source Project
4#
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#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
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.
16
17import shutil
18import tempfile
19import unittest
20
21import mock
22import mock_controller
23
24from acts import asserts
25from acts import base_test
26from acts import signals
27
28from mobly import base_test as mobly_base_test
29import mobly.config_parser as mobly_config_parser
30
31MSG_EXPECTED_EXCEPTION = 'This is an expected exception.'
32MSG_EXPECTED_TEST_FAILURE = 'This is an expected test failure.'
33MSG_UNEXPECTED_EXCEPTION = 'Unexpected exception!'
34
35MOCK_EXTRA = {'key': 'value', 'answer_to_everything': 42}
36
37
38def never_call():
39    raise Exception(MSG_UNEXPECTED_EXCEPTION)
40
41
42class SomeError(Exception):
43    """A custom exception class used for tests in this module."""
44
45
46class ActsBaseClassTest(unittest.TestCase):
47    def setUp(self):
48        self.tmp_dir = tempfile.mkdtemp()
49        self.tb_key = 'testbed_configs'
50        self.test_run_config = mobly_config_parser.TestRunConfig()
51        self.test_run_config.testbed_name = 'SampleTestBed'
52        self.test_run_config.controller_configs = {
53            self.tb_key: {
54                'name': self.test_run_config.testbed_name,
55            },
56        }
57        self.test_run_config.log_path = self.tmp_dir
58        self.test_run_config.user_params = {'some_param': 'hahaha'}
59        self.test_run_config.summary_writer = mock.MagicMock()
60        self.mock_test_name = 'test_something'
61
62    def tearDown(self):
63        shutil.rmtree(self.tmp_dir)
64
65    def test_current_test_case_name(self):
66        class MockBaseTest(base_test.BaseTestClass):
67            def test_func(self):
68                asserts.assert_true(
69                    self.current_test_name == 'test_func',
70                    'Got unexpected test name %s.' % self.current_test_name)
71
72        bt_cls = MockBaseTest(self.test_run_config)
73        bt_cls.run(test_names=['test_func'])
74        actual_record = bt_cls.results.passed[0]
75        self.assertEqual(actual_record.test_name, 'test_func')
76        self.assertIsNone(actual_record.details)
77        self.assertIsNone(actual_record.extras)
78
79    def test_self_tests_list(self):
80        class MockBaseTest(base_test.BaseTestClass):
81            def __init__(self, controllers):
82                super(MockBaseTest, self).__init__(controllers)
83                self.tests = ('test_something', )
84
85            def test_something(self):
86                pass
87
88            def test_never(self):
89                # This should not execute it's not on default test list.
90                never_call()
91
92        bt_cls = MockBaseTest(self.test_run_config)
93        bt_cls.run()
94        actual_record = bt_cls.results.passed[0]
95        self.assertEqual(actual_record.test_name, 'test_something')
96
97    def test_cli_test_selection_match_self_tests_list(self):
98        class MockBaseTest(base_test.BaseTestClass):
99            def __init__(self, controllers):
100                super(MockBaseTest, self).__init__(controllers)
101                self.tests = ('test_star1', 'test_star2', 'test_question_mark',
102                              'test_char_seq', 'test_no_match')
103
104            def test_star1(self):
105                pass
106
107            def test_star2(self):
108                pass
109
110            def test_question_mark(self):
111                pass
112
113            def test_char_seq(self):
114                pass
115
116            def test_no_match(self):
117                # This should not execute because it does not match any regex
118                # in the cmd line input.
119                never_call()
120
121        bt_cls = MockBaseTest(self.test_run_config)
122        test_names = [
123            'test_st*r1', 'test_*2', 'test_?uestion_mark', 'test_c[fghi]ar_seq'
124        ]
125        bt_cls.run(test_names=test_names)
126        passed_names = [p.test_name for p in bt_cls.results.passed]
127        self.assertEqual(len(passed_names), len(test_names))
128        for test in [
129                'test_star1', 'test_star2', 'test_question_mark',
130                'test_char_seq'
131        ]:
132            self.assertIn(test, passed_names)
133
134    def test_default_execution_of_all_tests(self):
135        class MockBaseTest(base_test.BaseTestClass):
136            def test_something(self):
137                pass
138
139            def not_a_test(self):
140                # This should not execute its name doesn't follow test case
141                # naming convention.
142                never_call()
143
144        bt_cls = MockBaseTest(self.test_run_config)
145        bt_cls.run()
146        actual_record = bt_cls.results.passed[0]
147        self.assertEqual(actual_record.test_name, 'test_something')
148
149    def test_setup_class_fail_by_exception(self):
150        call_check = mock.MagicMock()
151
152        class MockBaseTest(base_test.BaseTestClass):
153            def setup_class(self):
154                raise Exception(MSG_EXPECTED_EXCEPTION)
155
156            def test_something(self):
157                # This should not execute because setup_class failed.
158                never_call()
159
160            def on_skip(self, test_name, begin_time):
161                call_check('haha')
162
163        bt_cls = MockBaseTest(self.test_run_config)
164        bt_cls.run()
165        actual_record = bt_cls.results.error[0]
166        self.assertEqual(actual_record.test_name, 'test_something')
167        expected_summary = {
168            'Error': 1,
169            'Executed': 1,
170            'Failed': 0,
171            'Passed': 0,
172            'Requested': 1,
173            'Skipped': 0
174        }
175        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
176        call_check.assert_called_once_with('haha')
177
178    def test_setup_test_fail_by_exception(self):
179        class MockBaseTest(base_test.BaseTestClass):
180            def setup_test(self):
181                raise Exception(MSG_EXPECTED_EXCEPTION)
182
183            def test_something(self):
184                # This should not execute because setup_test failed.
185                never_call()
186
187        bt_cls = MockBaseTest(self.test_run_config)
188        bt_cls.run(test_names=['test_something'])
189        actual_record = bt_cls.results.error[0]
190        self.assertEqual(actual_record.test_name, self.mock_test_name)
191        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
192        self.assertIsNone(actual_record.extras)
193        expected_summary = {
194            'Error': 1,
195            'Executed': 1,
196            'Failed': 0,
197            'Passed': 0,
198            'Requested': 1,
199            'Skipped': 0
200        }
201        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
202
203    def test_setup_test_fail_by_test_signal(self):
204        class MockBaseTest(base_test.BaseTestClass):
205            def setup_test(self):
206                raise signals.TestFailure(MSG_EXPECTED_EXCEPTION)
207
208            def test_something(self):
209                # This should not execute because setup_test failed.
210                never_call()
211
212        bt_cls = MockBaseTest(self.test_run_config)
213        bt_cls.run(test_names=['test_something'])
214        actual_record = bt_cls.results.failed[0]
215        self.assertEqual(actual_record.test_name, self.mock_test_name)
216        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
217        self.assertIsNone(actual_record.extras)
218        expected_summary = {
219            'Error': 0,
220            'Executed': 1,
221            'Failed': 1,
222            'Passed': 0,
223            'Requested': 1,
224            'Skipped': 0
225        }
226        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
227
228    def test_setup_test_fail_by_return_False(self):
229        class MockBaseTest(base_test.BaseTestClass):
230            def setup_test(self):
231                return False
232
233            def test_something(self):
234                # This should not execute because setup_test failed.
235                never_call()
236
237        bt_cls = MockBaseTest(self.test_run_config)
238        bt_cls.run(test_names=['test_something'])
239        actual_record = bt_cls.results.failed[0]
240        expected_msg = 'Setup for %s failed.' % self.mock_test_name
241        self.assertEqual(actual_record.test_name, self.mock_test_name)
242        self.assertEqual(actual_record.details, expected_msg)
243        self.assertIsNone(actual_record.extras)
244        expected_summary = {
245            'Error': 0,
246            'Executed': 1,
247            'Failed': 1,
248            'Passed': 0,
249            'Requested': 1,
250            'Skipped': 0
251        }
252        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
253
254    def test_teardown_test_assert_fail(self):
255        class MockBaseTest(base_test.BaseTestClass):
256            def teardown_test(self):
257                asserts.assert_true(False, MSG_EXPECTED_EXCEPTION)
258
259            def test_something(self):
260                pass
261
262        bt_cls = MockBaseTest(self.test_run_config)
263        bt_cls.run()
264        actual_record = bt_cls.results.error[0]
265        self.assertEqual(actual_record.test_name, self.mock_test_name)
266        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
267        self.assertIsNone(actual_record.extras)
268        expected_summary = {
269            'Error': 1,
270            'Executed': 1,
271            'Failed': 0,
272            'Passed': 0,
273            'Requested': 1,
274            'Skipped': 0
275        }
276        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
277
278    def test_teardown_test_raise_exception(self):
279        class MockBaseTest(base_test.BaseTestClass):
280            def teardown_test(self):
281                raise Exception(MSG_EXPECTED_EXCEPTION)
282
283            def test_something(self):
284                pass
285
286        bt_cls = MockBaseTest(self.test_run_config)
287        bt_cls.run()
288        actual_record = bt_cls.results.error[0]
289        self.assertEqual(actual_record.test_name, self.mock_test_name)
290        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
291        self.assertIsNone(actual_record.extras)
292        expected_summary = {
293            'Error': 1,
294            'Executed': 1,
295            'Failed': 0,
296            'Passed': 0,
297            'Requested': 1,
298            'Skipped': 0
299        }
300        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
301
302    def test_teardown_test_executed_if_test_pass(self):
303        my_mock = mock.MagicMock()
304
305        class MockBaseTest(base_test.BaseTestClass):
306            def teardown_test(self):
307                my_mock('teardown_test')
308
309            def test_something(self):
310                pass
311
312        bt_cls = MockBaseTest(self.test_run_config)
313        bt_cls.run()
314        actual_record = bt_cls.results.passed[0]
315        my_mock.assert_called_once_with('teardown_test')
316        self.assertEqual(actual_record.test_name, self.mock_test_name)
317        self.assertIsNone(actual_record.details)
318        self.assertIsNone(actual_record.extras)
319        expected_summary = {
320            'Error': 0,
321            'Executed': 1,
322            'Failed': 0,
323            'Passed': 1,
324            'Requested': 1,
325            'Skipped': 0
326        }
327        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
328
329    def test_teardown_test_executed_if_setup_test_fails(self):
330        my_mock = mock.MagicMock()
331
332        class MockBaseTest(base_test.BaseTestClass):
333            def setup_test(self):
334                raise Exception(MSG_EXPECTED_EXCEPTION)
335
336            def teardown_test(self):
337                my_mock('teardown_test')
338
339            def test_something(self):
340                pass
341
342        bt_cls = MockBaseTest(self.test_run_config)
343        bt_cls.run()
344        actual_record = bt_cls.results.error[0]
345        my_mock.assert_called_once_with('teardown_test')
346        self.assertEqual(actual_record.test_name, self.mock_test_name)
347        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
348        self.assertIsNone(actual_record.extras)
349        expected_summary = {
350            'Error': 1,
351            'Executed': 1,
352            'Failed': 0,
353            'Passed': 0,
354            'Requested': 1,
355            'Skipped': 0
356        }
357        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
358
359    def test_teardown_test_executed_if_test_fails(self):
360        my_mock = mock.MagicMock()
361
362        class MockBaseTest(base_test.BaseTestClass):
363            def teardown_test(self):
364                my_mock('teardown_test')
365
366            def test_something(self):
367                raise Exception(MSG_EXPECTED_EXCEPTION)
368
369        bt_cls = MockBaseTest(self.test_run_config)
370        bt_cls.run()
371        actual_record = bt_cls.results.error[0]
372        my_mock.assert_called_once_with('teardown_test')
373        self.assertEqual(actual_record.test_name, self.mock_test_name)
374        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
375        self.assertIsNone(actual_record.extras)
376        expected_summary = {
377            'Error': 1,
378            'Executed': 1,
379            'Failed': 0,
380            'Passed': 0,
381            'Requested': 1,
382            'Skipped': 0
383        }
384        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
385
386    def test_on_exception_executed_if_teardown_test_fails(self):
387        my_mock = mock.MagicMock()
388
389        class MockBaseTest(base_test.BaseTestClass):
390            def on_exception(self, test_name, begin_time):
391                my_mock('on_exception')
392
393            def teardown_test(self):
394                raise Exception(MSG_EXPECTED_EXCEPTION)
395
396            def test_something(self):
397                pass
398
399        bt_cls = MockBaseTest(self.test_run_config)
400        bt_cls.run()
401        my_mock.assert_called_once_with('on_exception')
402        actual_record = bt_cls.results.error[0]
403        self.assertEqual(actual_record.test_name, self.mock_test_name)
404        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
405        self.assertIsNone(actual_record.extras)
406        expected_summary = {
407            'Error': 1,
408            'Executed': 1,
409            'Failed': 0,
410            'Passed': 0,
411            'Requested': 1,
412            'Skipped': 0
413        }
414        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
415
416    def test_on_fail_executed_if_test_fails(self):
417        my_mock = mock.MagicMock()
418
419        class MockBaseTest(base_test.BaseTestClass):
420            def on_fail(self, test_name, begin_time):
421                my_mock('on_fail')
422
423            def test_something(self):
424                asserts.assert_true(False, MSG_EXPECTED_EXCEPTION)
425
426        bt_cls = MockBaseTest(self.test_run_config)
427        bt_cls.run()
428        my_mock.assert_called_once_with('on_fail')
429        actual_record = bt_cls.results.failed[0]
430        self.assertEqual(actual_record.test_name, self.mock_test_name)
431        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
432        self.assertIsNone(actual_record.extras)
433        expected_summary = {
434            'Error': 0,
435            'Executed': 1,
436            'Failed': 1,
437            'Passed': 0,
438            'Requested': 1,
439            'Skipped': 0
440        }
441        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
442
443    def test_on_fail_executed_if_test_setup_fails_by_exception(self):
444        my_mock = mock.MagicMock()
445
446        class MockBaseTest(base_test.BaseTestClass):
447            def setup_test(self):
448                raise Exception(MSG_EXPECTED_EXCEPTION)
449
450            def on_fail(self, test_name, begin_time):
451                my_mock('on_fail')
452
453            def test_something(self):
454                pass
455
456        bt_cls = MockBaseTest(self.test_run_config)
457        bt_cls.run()
458        my_mock.assert_called_once_with('on_fail')
459        actual_record = bt_cls.results.error[0]
460        self.assertEqual(actual_record.test_name, self.mock_test_name)
461        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
462        self.assertIsNone(actual_record.extras)
463        expected_summary = {
464            'Error': 1,
465            'Executed': 1,
466            'Failed': 0,
467            'Passed': 0,
468            'Requested': 1,
469            'Skipped': 0
470        }
471        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
472
473    def test_on_fail_executed_if_test_setup_fails_by_return_False(self):
474        my_mock = mock.MagicMock()
475
476        class MockBaseTest(base_test.BaseTestClass):
477            def setup_test(self):
478                return False
479
480            def on_fail(self, test_name, begin_time):
481                my_mock('on_fail')
482
483            def test_something(self):
484                pass
485
486        bt_cls = MockBaseTest(self.test_run_config)
487        bt_cls.run()
488        my_mock.assert_called_once_with('on_fail')
489        actual_record = bt_cls.results.failed[0]
490        self.assertEqual(actual_record.test_name, self.mock_test_name)
491        self.assertEqual(actual_record.details,
492                         'Setup for test_something failed.')
493        self.assertIsNone(actual_record.extras)
494        expected_summary = {
495            'Error': 0,
496            'Executed': 1,
497            'Failed': 1,
498            'Passed': 0,
499            'Requested': 1,
500            'Skipped': 0
501        }
502        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
503
504    def test_failure_to_call_procedure_function_is_recorded(self):
505        class MockBaseTest(base_test.BaseTestClass):
506            # Wrong method signature; will raise exception
507            def on_pass(self):
508                pass
509
510            def test_something(self):
511                asserts.explicit_pass(MSG_EXPECTED_EXCEPTION)
512
513        bt_cls = MockBaseTest(self.test_run_config)
514        bt_cls.run()
515        actual_record = bt_cls.results.error[0]
516        self.assertIn('_on_pass', actual_record.extra_errors)
517        self.assertEqual(actual_record.test_name, self.mock_test_name)
518        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
519        self.assertIsNone(actual_record.extras)
520        expected_summary = {
521            'Error': 1,
522            'Executed': 1,
523            'Failed': 0,
524            'Passed': 0,
525            'Requested': 1,
526            'Skipped': 0
527        }
528        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
529
530    def test_failure_in_procedure_functions_is_recorded(self):
531        expected_msg = 'Something failed in on_pass.'
532
533        class MockBaseTest(base_test.BaseTestClass):
534            def on_pass(self, test_name, begin_time):
535                raise Exception(expected_msg)
536
537            def test_something(self):
538                asserts.explicit_pass(MSG_EXPECTED_EXCEPTION)
539
540        bt_cls = MockBaseTest(self.test_run_config)
541        bt_cls.run()
542        actual_record = bt_cls.results.error[0]
543        self.assertEqual(actual_record.test_name, self.mock_test_name)
544        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
545        self.assertIsNone(actual_record.extras)
546        expected_summary = {
547            'Error': 1,
548            'Executed': 1,
549            'Failed': 0,
550            'Passed': 0,
551            'Requested': 1,
552            'Skipped': 0
553        }
554        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
555
556    def test_both_teardown_and_test_body_raise_exceptions(self):
557        class MockBaseTest(base_test.BaseTestClass):
558            def teardown_test(self):
559                asserts.assert_true(False, MSG_EXPECTED_EXCEPTION)
560
561            def test_something(self):
562                raise Exception('Test Body Exception.')
563
564        bt_cls = MockBaseTest(self.test_run_config)
565        bt_cls.run()
566        actual_record = bt_cls.results.error[0]
567        self.assertEqual(actual_record.test_name, self.mock_test_name)
568        self.assertEqual(actual_record.details, 'Test Body Exception.')
569        self.assertIsNone(actual_record.extras)
570        self.assertEqual(actual_record.extra_errors['teardown_test'].details,
571                         'This is an expected exception.')
572        expected_summary = {
573            'Error': 1,
574            'Executed': 1,
575            'Failed': 0,
576            'Passed': 0,
577            'Requested': 1,
578            'Skipped': 0
579        }
580        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
581
582    def test_explicit_pass_but_teardown_test_raises_an_exception(self):
583        """Test record result should be marked as UNKNOWN as opposed to PASS.
584        """
585        class MockBaseTest(base_test.BaseTestClass):
586            def teardown_test(self):
587                asserts.assert_true(False, MSG_EXPECTED_EXCEPTION)
588
589            def test_something(self):
590                asserts.explicit_pass('Test Passed!')
591
592        bt_cls = MockBaseTest(self.test_run_config)
593        bt_cls.run()
594        actual_record = bt_cls.results.error[0]
595        self.assertEqual(actual_record.test_name, self.mock_test_name)
596        self.assertEqual(actual_record.details, 'Test Passed!')
597        self.assertIsNone(actual_record.extras)
598        self.assertEqual(actual_record.extra_errors['teardown_test'].details,
599                         'This is an expected exception.')
600        expected_summary = {
601            'Error': 1,
602            'Executed': 1,
603            'Failed': 0,
604            'Passed': 0,
605            'Requested': 1,
606            'Skipped': 0
607        }
608        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
609
610    def test_on_pass_raise_exception(self):
611        class MockBaseTest(base_test.BaseTestClass):
612            def on_pass(self, test_name, begin_time):
613                raise Exception(MSG_EXPECTED_EXCEPTION)
614
615            def test_something(self):
616                asserts.explicit_pass(MSG_EXPECTED_EXCEPTION,
617                                      extras=MOCK_EXTRA)
618
619        bt_cls = MockBaseTest(self.test_run_config)
620        bt_cls.run()
621        actual_record = bt_cls.results.error[0]
622        self.assertEqual(actual_record.test_name, self.mock_test_name)
623        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
624        self.assertEqual(actual_record.extras, MOCK_EXTRA)
625        expected_summary = {
626            'Error': 1,
627            'Executed': 1,
628            'Failed': 0,
629            'Passed': 0,
630            'Requested': 1,
631            'Skipped': 0
632        }
633        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
634
635    def test_on_fail_raise_exception(self):
636        class MockBaseTest(base_test.BaseTestClass):
637            def on_fail(self, test_name, begin_time):
638                raise Exception(MSG_EXPECTED_EXCEPTION)
639
640            def test_something(self):
641                asserts.fail(MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
642
643        bt_cls = MockBaseTest(self.test_run_config)
644        bt_cls.run()
645        actual_record = bt_cls.results.failed[0]
646        self.assertEqual(bt_cls.results.error, [])
647        self.assertEqual(actual_record.test_name, self.mock_test_name)
648        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
649        self.assertEqual(actual_record.extras, MOCK_EXTRA)
650        expected_summary = {
651            'Error': 0,
652            'Executed': 1,
653            'Failed': 1,
654            'Passed': 0,
655            'Requested': 1,
656            'Skipped': 0
657        }
658        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
659
660    def test_abort_class(self):
661        class MockBaseTest(base_test.BaseTestClass):
662            def test_1(self):
663                pass
664
665            def test_2(self):
666                asserts.abort_class(MSG_EXPECTED_EXCEPTION)
667                never_call()
668
669            def test_3(self):
670                never_call()
671
672        bt_cls = MockBaseTest(self.test_run_config)
673        bt_cls.run(test_names=['test_1', 'test_2', 'test_3'])
674        self.assertEqual(bt_cls.results.passed[0].test_name, 'test_1')
675        self.assertEqual(bt_cls.results.failed[0].details,
676                         MSG_EXPECTED_EXCEPTION)
677        expected_summary = {
678            'Error': 0,
679            'Executed': 2,
680            'Failed': 1,
681            'Passed': 1,
682            'Requested': 3,
683            'Skipped': 0
684        }
685        self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
686
687    def test_uncaught_exception(self):
688        class MockBaseTest(base_test.BaseTestClass):
689            def test_func(self):
690                raise Exception(MSG_EXPECTED_EXCEPTION)
691                never_call()
692
693        bt_cls = MockBaseTest(self.test_run_config)
694        bt_cls.run(test_names=['test_func'])
695        actual_record = bt_cls.results.error[0]
696        self.assertEqual(actual_record.test_name, 'test_func')
697        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
698        self.assertIsNone(actual_record.extras)
699
700    def test_fail(self):
701        class MockBaseTest(base_test.BaseTestClass):
702            def test_func(self):
703                asserts.fail(MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
704                never_call()
705
706        bt_cls = MockBaseTest(self.test_run_config)
707        bt_cls.run(test_names=['test_func'])
708        actual_record = bt_cls.results.failed[0]
709        self.assertEqual(actual_record.test_name, 'test_func')
710        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
711        self.assertEqual(actual_record.extras, MOCK_EXTRA)
712
713    def test_assert_true(self):
714        class MockBaseTest(base_test.BaseTestClass):
715            def test_func(self):
716                asserts.assert_true(False,
717                                    MSG_EXPECTED_EXCEPTION,
718                                    extras=MOCK_EXTRA)
719                never_call()
720
721        bt_cls = MockBaseTest(self.test_run_config)
722        bt_cls.run(test_names=['test_func'])
723        actual_record = bt_cls.results.failed[0]
724        self.assertEqual(actual_record.test_name, 'test_func')
725        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
726        self.assertEqual(actual_record.extras, MOCK_EXTRA)
727
728    def test_assert_equal_pass(self):
729        class MockBaseTest(base_test.BaseTestClass):
730            def test_func(self):
731                asserts.assert_equal(1, 1, extras=MOCK_EXTRA)
732
733        bt_cls = MockBaseTest(self.test_run_config)
734        bt_cls.run()
735        actual_record = bt_cls.results.passed[0]
736        self.assertEqual(actual_record.test_name, 'test_func')
737        self.assertIsNone(actual_record.details)
738        self.assertIsNone(actual_record.extras)
739
740    def test_assert_equal_fail(self):
741        class MockBaseTest(base_test.BaseTestClass):
742            def test_func(self):
743                asserts.assert_equal(1, 2, extras=MOCK_EXTRA)
744
745        bt_cls = MockBaseTest(self.test_run_config)
746        bt_cls.run()
747        actual_record = bt_cls.results.failed[0]
748        self.assertEqual(actual_record.test_name, 'test_func')
749        self.assertIn('1 != 2', actual_record.details)
750        self.assertEqual(actual_record.extras, MOCK_EXTRA)
751
752    def test_assert_equal_fail_with_msg(self):
753        class MockBaseTest(base_test.BaseTestClass):
754            def test_func(self):
755                asserts.assert_equal(1,
756                                     2,
757                                     msg=MSG_EXPECTED_EXCEPTION,
758                                     extras=MOCK_EXTRA)
759
760        bt_cls = MockBaseTest(self.test_run_config)
761        bt_cls.run()
762        actual_record = bt_cls.results.failed[0]
763        self.assertEqual(actual_record.test_name, 'test_func')
764        expected_msg = '1 != 2 ' + MSG_EXPECTED_EXCEPTION
765        self.assertIn(expected_msg, actual_record.details)
766        self.assertEqual(actual_record.extras, MOCK_EXTRA)
767
768    def test_assert_raises_pass(self):
769        class MockBaseTest(base_test.BaseTestClass):
770            def test_func(self):
771                with asserts.assert_raises(SomeError, extras=MOCK_EXTRA):
772                    raise SomeError(MSG_EXPECTED_EXCEPTION)
773
774        bt_cls = MockBaseTest(self.test_run_config)
775        bt_cls.run()
776        actual_record = bt_cls.results.passed[0]
777        self.assertEqual(actual_record.test_name, 'test_func')
778        self.assertIsNone(actual_record.details)
779        self.assertIsNone(actual_record.extras)
780
781    def test_assert_raises_regex_pass(self):
782        class MockBaseTest(base_test.BaseTestClass):
783            def test_func(self):
784                with asserts.assert_raises_regex(
785                        SomeError,
786                        expected_regex=MSG_EXPECTED_EXCEPTION,
787                        extras=MOCK_EXTRA):
788                    raise SomeError(MSG_EXPECTED_EXCEPTION)
789
790        bt_cls = MockBaseTest(self.test_run_config)
791        bt_cls.run()
792        actual_record = bt_cls.results.passed[0]
793        self.assertEqual(actual_record.test_name, 'test_func')
794        self.assertIsNone(actual_record.details)
795        self.assertIsNone(actual_record.extras)
796
797    def test_assert_raises_fail_with_noop(self):
798        class MockBaseTest(base_test.BaseTestClass):
799            def test_func(self):
800                with asserts.assert_raises_regex(
801                        SomeError,
802                        expected_regex=MSG_EXPECTED_EXCEPTION,
803                        extras=MOCK_EXTRA):
804                    pass
805
806        bt_cls = MockBaseTest(self.test_run_config)
807        bt_cls.run()
808        actual_record = bt_cls.results.failed[0]
809        self.assertEqual(actual_record.test_name, 'test_func')
810        self.assertEqual(actual_record.details, 'SomeError not raised')
811        self.assertEqual(actual_record.extras, MOCK_EXTRA)
812
813    def test_assert_raises_fail_with_wrong_regex(self):
814        wrong_msg = 'ha'
815
816        class MockBaseTest(base_test.BaseTestClass):
817            def test_func(self):
818                with asserts.assert_raises_regex(
819                        SomeError,
820                        expected_regex=MSG_EXPECTED_EXCEPTION,
821                        extras=MOCK_EXTRA):
822                    raise SomeError(wrong_msg)
823
824        bt_cls = MockBaseTest(self.test_run_config)
825        bt_cls.run()
826        actual_record = bt_cls.results.failed[0]
827        self.assertEqual(actual_record.test_name, 'test_func')
828        expected_details = ('"This is an expected exception." does not match '
829                            '"%s"') % wrong_msg
830        self.assertEqual(actual_record.details, expected_details)
831        self.assertEqual(actual_record.extras, MOCK_EXTRA)
832
833    def test_assert_raises_fail_with_wrong_error(self):
834        class MockBaseTest(base_test.BaseTestClass):
835            def test_func(self):
836                with asserts.assert_raises_regex(
837                        SomeError,
838                        expected_regex=MSG_EXPECTED_EXCEPTION,
839                        extras=MOCK_EXTRA):
840                    raise AttributeError(MSG_UNEXPECTED_EXCEPTION)
841
842        bt_cls = MockBaseTest(self.test_run_config)
843        bt_cls.run()
844        actual_record = bt_cls.results.error[0]
845        self.assertEqual(actual_record.test_name, 'test_func')
846        self.assertEqual(actual_record.details, MSG_UNEXPECTED_EXCEPTION)
847        self.assertIsNone(actual_record.extras)
848
849    def test_explicit_pass(self):
850        class MockBaseTest(base_test.BaseTestClass):
851            def test_func(self):
852                asserts.explicit_pass(MSG_EXPECTED_EXCEPTION,
853                                      extras=MOCK_EXTRA)
854                never_call()
855
856        bt_cls = MockBaseTest(self.test_run_config)
857        bt_cls.run(test_names=['test_func'])
858        actual_record = bt_cls.results.passed[0]
859        self.assertEqual(actual_record.test_name, 'test_func')
860        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
861        self.assertEqual(actual_record.extras, MOCK_EXTRA)
862
863    def test_implicit_pass(self):
864        class MockBaseTest(base_test.BaseTestClass):
865            def test_func(self):
866                pass
867
868        bt_cls = MockBaseTest(self.test_run_config)
869        bt_cls.run(test_names=['test_func'])
870        actual_record = bt_cls.results.passed[0]
871        self.assertEqual(actual_record.test_name, 'test_func')
872        self.assertIsNone(actual_record.details)
873        self.assertIsNone(actual_record.extras)
874
875    def test_skip(self):
876        class MockBaseTest(base_test.BaseTestClass):
877            def test_func(self):
878                asserts.skip(MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
879                never_call()
880
881        bt_cls = MockBaseTest(self.test_run_config)
882        bt_cls.run(test_names=['test_func'])
883        actual_record = bt_cls.results.skipped[0]
884        self.assertEqual(actual_record.test_name, 'test_func')
885        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
886        self.assertEqual(actual_record.extras, MOCK_EXTRA)
887
888    def test_skip_if(self):
889        class MockBaseTest(base_test.BaseTestClass):
890            def test_func(self):
891                asserts.skip_if(False, MSG_UNEXPECTED_EXCEPTION)
892                asserts.skip_if(True,
893                                MSG_EXPECTED_EXCEPTION,
894                                extras=MOCK_EXTRA)
895                never_call()
896
897        bt_cls = MockBaseTest(self.test_run_config)
898        bt_cls.run(test_names=['test_func'])
899        actual_record = bt_cls.results.skipped[0]
900        self.assertEqual(actual_record.test_name, 'test_func')
901        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
902        self.assertEqual(actual_record.extras, MOCK_EXTRA)
903
904    def test_unpack_userparams_required(self):
905        """Missing a required param should raise an error."""
906        required = ['some_param']
907        bc = base_test.BaseTestClass(self.test_run_config)
908        bc.unpack_userparams(required)
909        expected_value = self.test_run_config.user_params['some_param']
910        self.assertEqual(bc.some_param, expected_value)
911
912    def test_unpack_userparams_required_missing(self):
913        """Missing a required param should raise an error."""
914        required = ['something']
915        bc = base_test.BaseTestClass(self.test_run_config)
916        expected_msg = ('Missing required user param "%s" in test '
917                        'configuration.') % required[0]
918        with self.assertRaises(mobly_base_test.Error, msg=expected_msg):
919            bc.unpack_userparams(required)
920
921    def test_unpack_userparams_optional(self):
922        """If an optional param is specified, the value should be what's in the
923        config.
924        """
925        opt = ['some_param']
926        bc = base_test.BaseTestClass(self.test_run_config)
927        bc.unpack_userparams(opt_param_names=opt)
928        expected_value = self.test_run_config.user_params['some_param']
929        self.assertEqual(bc.some_param, expected_value)
930
931    def test_unpack_userparams_optional_with_default(self):
932        """If an optional param is specified with a default value, and the
933        param is not in the config, the value should be the default value.
934        """
935        bc = base_test.BaseTestClass(self.test_run_config)
936        bc.unpack_userparams(optional_thing='whatever')
937        self.assertEqual(bc.optional_thing, 'whatever')
938
939    def test_unpack_userparams_default_overwrite_by_optional_param_list(self):
940        """If an optional param is specified in kwargs, and the param is in the
941        config, the value should be the one in the config.
942        """
943        bc = base_test.BaseTestClass(self.test_run_config)
944        bc.unpack_userparams(some_param='whatever')
945        expected_value = self.test_run_config.user_params['some_param']
946        self.assertEqual(bc.some_param, expected_value)
947
948    def test_unpack_userparams_default_overwrite_by_required_param_list(self):
949        """If an optional param is specified in kwargs, the param is in the
950        required param list, and the param is not specified in the config, the
951        param's alue should be the default value and there should be no error
952        thrown.
953        """
954        bc = base_test.BaseTestClass(self.test_run_config)
955        bc.unpack_userparams(req_param_names=['a_kwarg_param'],
956                             a_kwarg_param='whatever')
957        self.assertEqual(bc.a_kwarg_param, 'whatever')
958
959    def test_unpack_userparams_optional_missing(self):
960        """Missing an optional param should not raise an error."""
961        opt = ['something']
962        bc = base_test.BaseTestClass(self.test_run_config)
963        bc.unpack_userparams(opt_param_names=opt)
964
965    def test_unpack_userparams_basic(self):
966        """Required and optional params are unpacked properly."""
967        required = ['something']
968        optional = ['something_else']
969        configs = self.test_run_config.copy()
970        configs.user_params['something'] = 42
971        configs.user_params['something_else'] = 53
972        bc = base_test.BaseTestClass(configs)
973        bc.unpack_userparams(req_param_names=required,
974                             opt_param_names=optional)
975        self.assertEqual(bc.something, 42)
976        self.assertEqual(bc.something_else, 53)
977
978    def test_unpack_userparams_default_overwrite(self):
979        default_arg_val = 'haha'
980        actual_arg_val = 'wawa'
981        arg_name = 'arg1'
982        configs = self.test_run_config.copy()
983        configs.user_params[arg_name] = actual_arg_val
984        bc = base_test.BaseTestClass(configs)
985        bc.unpack_userparams(opt_param_names=[arg_name], arg1=default_arg_val)
986        self.assertEqual(bc.arg1, actual_arg_val)
987
988    def test_unpack_userparams_default_None(self):
989        bc = base_test.BaseTestClass(self.test_run_config)
990        bc.unpack_userparams(arg1='haha')
991        self.assertEqual(bc.arg1, 'haha')
992
993    def test_register_controller_no_config(self):
994        base_cls = base_test.BaseTestClass(self.test_run_config)
995        with self.assertRaisesRegexp(signals.ControllerError,
996                                     'No corresponding config found for'):
997            base_cls.register_controller(mock_controller)
998
999    def test_register_optional_controller_no_config(self):
1000        base_cls = base_test.BaseTestClass(self.test_run_config)
1001        self.assertIsNone(
1002            base_cls.register_controller(mock_controller, required=False))
1003
1004    def test_register_controller_third_party_dup_register(self):
1005        """Verifies correctness of registration, internal tally of controllers
1006        objects, and the right error happen when a controller module is
1007        registered twice.
1008        """
1009        mock_test_config = self.test_run_config.copy()
1010        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
1011        mock_test_config.controller_configs[mock_ctrlr_config_name] = [
1012            'magic1', 'magic2'
1013        ]
1014        base_cls = base_test.BaseTestClass(mock_test_config)
1015        base_cls.register_controller(mock_controller)
1016        registered_name = 'mock_controller'
1017        controller_objects = base_cls._controller_manager._controller_objects
1018        self.assertTrue(registered_name in controller_objects)
1019        mock_ctrlrs = controller_objects[registered_name]
1020        self.assertEqual(mock_ctrlrs[0].magic, 'magic1')
1021        self.assertEqual(mock_ctrlrs[1].magic, 'magic2')
1022        expected_msg = 'Controller module .* has already been registered.'
1023        with self.assertRaisesRegexp(signals.ControllerError, expected_msg):
1024            base_cls.register_controller(mock_controller)
1025
1026    def test_register_optional_controller_third_party_dup_register(self):
1027        """Verifies correctness of registration, internal tally of controllers
1028        objects, and the right error happen when an optional controller module
1029        is registered twice.
1030        """
1031        mock_test_config = self.test_run_config.copy()
1032        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
1033        mock_test_config.controller_configs[mock_ctrlr_config_name] = [
1034            'magic1', 'magic2'
1035        ]
1036        base_cls = base_test.BaseTestClass(mock_test_config)
1037        base_cls.register_controller(mock_controller, required=False)
1038        expected_msg = 'Controller module .* has already been registered.'
1039        with self.assertRaisesRegexp(signals.ControllerError, expected_msg):
1040            base_cls.register_controller(mock_controller, required=False)
1041
1042    def test_register_controller_builtin_dup_register(self):
1043        """Same as test_register_controller_third_party_dup_register, except
1044        this is for a builtin controller module.
1045        """
1046        mock_test_config = self.test_run_config.copy()
1047        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
1048        mock_ref_name = 'haha'
1049        setattr(mock_controller, 'ACTS_CONTROLLER_REFERENCE_NAME',
1050                mock_ref_name)
1051        try:
1052            mock_ctrlr_ref_name = mock_controller.ACTS_CONTROLLER_REFERENCE_NAME
1053            mock_test_config.controller_configs[mock_ctrlr_config_name] = [
1054                'magic1', 'magic2'
1055            ]
1056            base_cls = base_test.BaseTestClass(mock_test_config)
1057            base_cls.register_controller(mock_controller, builtin=True)
1058            self.assertTrue(hasattr(base_cls, mock_ref_name))
1059            self.assertTrue(mock_controller.__name__ in
1060                            base_cls._controller_manager._controller_objects)
1061            mock_ctrlrs = getattr(base_cls, mock_ctrlr_ref_name)
1062            self.assertEqual(mock_ctrlrs[0].magic, 'magic1')
1063            self.assertEqual(mock_ctrlrs[1].magic, 'magic2')
1064            expected_msg = 'Controller module .* has already been registered.'
1065            with self.assertRaisesRegexp(signals.ControllerError,
1066                                         expected_msg):
1067                base_cls.register_controller(mock_controller, builtin=True)
1068        finally:
1069            delattr(mock_controller, 'ACTS_CONTROLLER_REFERENCE_NAME')
1070
1071    def test_register_controller_no_get_info(self):
1072        mock_test_config = self.test_run_config.copy()
1073        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
1074        mock_ref_name = 'haha'
1075        get_info = getattr(mock_controller, 'get_info')
1076        delattr(mock_controller, 'get_info')
1077        try:
1078            mock_test_config.controller_configs[mock_ctrlr_config_name] = [
1079                'magic1', 'magic2'
1080            ]
1081            base_cls = base_test.BaseTestClass(mock_test_config)
1082            base_cls.register_controller(mock_controller)
1083            self.assertEqual(base_cls.results.controller_info, [])
1084        finally:
1085            setattr(mock_controller, 'get_info', get_info)
1086
1087    def test_register_controller_return_value(self):
1088        mock_test_config = self.test_run_config.copy()
1089        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
1090        mock_test_config.controller_configs[mock_ctrlr_config_name] = [
1091            'magic1', 'magic2'
1092        ]
1093        base_cls = base_test.BaseTestClass(mock_test_config)
1094        magic_devices = base_cls.register_controller(mock_controller)
1095        self.assertEqual(magic_devices[0].magic, 'magic1')
1096        self.assertEqual(magic_devices[1].magic, 'magic2')
1097
1098    def test_handle_file_user_params_does_not_overwrite_existing_params(self):
1099        test_run_config = self.test_run_config.copy()
1100        test_run_config.user_params = {
1101            'foo': ['good_value'],
1102            'local_files': {
1103                'foo': ['bad_value']
1104            }
1105        }
1106        test = base_test.BaseTestClass(test_run_config)
1107
1108        self.assertEqual(test.user_params['foo'], ['good_value'])
1109
1110    def test_handle_file_user_params_dumps_files_dict(self):
1111        test_run_config = self.test_run_config.copy()
1112        test_run_config.user_params = {
1113            'my_files': {
1114                'foo': ['good_value']
1115            }
1116        }
1117        test = base_test.BaseTestClass(test_run_config)
1118
1119        self.assertEqual(test.user_params['foo'], ['good_value'])
1120
1121    def test_handle_file_user_params_is_called_in_init(self):
1122        test_run_config = self.test_run_config.copy()
1123        test_run_config.user_params['files'] = {
1124            'file_a': ['/some/path']
1125        }
1126        test = base_test.BaseTestClass(test_run_config)
1127
1128        self.assertEqual(test.user_params['file_a'], ['/some/path'])
1129
1130
1131if __name__ == '__main__':
1132    unittest.main()
1133