1# Copyright 2017 Google Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import io
16import os
17import shutil
18import sys
19import tempfile
20import unittest
21from unittest import mock
22
23from mobly import base_suite
24from mobly import base_test
25from mobly import suite_runner
26from tests.lib import integration2_test
27from tests.lib import integration_test
28
29
30class FakeTest1(base_test.BaseTestClass):
31  pass
32
33
34class SuiteRunnerTest(unittest.TestCase):
35
36  def setUp(self):
37    self.tmp_dir = tempfile.mkdtemp()
38
39  def tearDown(self):
40    shutil.rmtree(self.tmp_dir)
41
42  def test_select_no_args(self):
43    identifiers = suite_runner.compute_selected_tests(
44        test_classes=[
45            integration_test.IntegrationTest,
46            integration2_test.Integration2Test,
47        ],
48        selected_tests=None,
49    )
50    self.assertEqual(
51        {
52            integration_test.IntegrationTest: None,
53            integration2_test.Integration2Test: None,
54        },
55        identifiers,
56    )
57
58  def test_select_by_class(self):
59    identifiers = suite_runner.compute_selected_tests(
60        test_classes=[
61            integration_test.IntegrationTest,
62            integration2_test.Integration2Test,
63        ],
64        selected_tests=['IntegrationTest'],
65    )
66    self.assertEqual({integration_test.IntegrationTest: None}, identifiers)
67
68  def test_select_by_method(self):
69    identifiers = suite_runner.compute_selected_tests(
70        test_classes=[
71            integration_test.IntegrationTest,
72            integration2_test.Integration2Test,
73        ],
74        selected_tests=['IntegrationTest.test_a', 'IntegrationTest.test_b'],
75    )
76    self.assertEqual(
77        {integration_test.IntegrationTest: ['test_a', 'test_b']}, identifiers
78    )
79
80  def test_select_all_clobbers_method(self):
81    identifiers = suite_runner.compute_selected_tests(
82        test_classes=[
83            integration_test.IntegrationTest,
84            integration2_test.Integration2Test,
85        ],
86        selected_tests=['IntegrationTest.test_a', 'IntegrationTest'],
87    )
88    self.assertEqual({integration_test.IntegrationTest: None}, identifiers)
89
90    identifiers = suite_runner.compute_selected_tests(
91        test_classes=[
92            integration_test.IntegrationTest,
93            integration2_test.Integration2Test,
94        ],
95        selected_tests=['IntegrationTest', 'IntegrationTest.test_a'],
96    )
97    self.assertEqual({integration_test.IntegrationTest: None}, identifiers)
98
99  @mock.patch('sys.exit')
100  def test_run_suite(self, mock_exit):
101    tmp_file_path = os.path.join(self.tmp_dir, 'config.yml')
102    with io.open(tmp_file_path, 'w', encoding='utf-8') as f:
103      f.write("""
104        TestBeds:
105          # A test bed where adb will find Android devices.
106          - Name: SampleTestBed
107            Controllers:
108              MagicDevice: '*'
109            TestParams:
110              icecream: 42
111              extra_param: 'haha'
112      """)
113    suite_runner.run_suite(
114        [integration_test.IntegrationTest], argv=['-c', tmp_file_path]
115    )
116    mock_exit.assert_not_called()
117
118  @mock.patch('sys.exit')
119  def test_run_suite_with_failures(self, mock_exit):
120    tmp_file_path = os.path.join(self.tmp_dir, 'config.yml')
121    with io.open(tmp_file_path, 'w', encoding='utf-8') as f:
122      f.write("""
123        TestBeds:
124          # A test bed where adb will find Android devices.
125          - Name: SampleTestBed
126            Controllers:
127              MagicDevice: '*'
128      """)
129    suite_runner.run_suite(
130        [integration_test.IntegrationTest], argv=['-c', tmp_file_path]
131    )
132    mock_exit.assert_called_once_with(1)
133
134  @mock.patch('sys.exit')
135  @mock.patch.object(suite_runner, '_find_suite_class', autospec=True)
136  def test_run_suite_class(self, mock_find_suite_class, mock_exit):
137    mock_called = mock.MagicMock()
138
139    class FakeTestSuite(base_suite.BaseSuite):
140
141      def setup_suite(self, config):
142        mock_called.setup_suite()
143        super().setup_suite(config)
144        self.add_test_class(FakeTest1)
145
146      def teardown_suite(self):
147        mock_called.teardown_suite()
148        super().teardown_suite()
149
150    mock_find_suite_class.return_value = FakeTestSuite
151
152    tmp_file_path = os.path.join(self.tmp_dir, 'config.yml')
153    with io.open(tmp_file_path, 'w', encoding='utf-8') as f:
154      f.write("""
155        TestBeds:
156          # A test bed where adb will find Android devices.
157          - Name: SampleTestBed
158            Controllers:
159              MagicDevice: '*'
160      """)
161
162    mock_cli_args = ['test_binary', f'--config={tmp_file_path}']
163
164    with mock.patch.object(sys, 'argv', new=mock_cli_args):
165      suite_runner.run_suite_class()
166
167    mock_find_suite_class.assert_called_once()
168    mock_called.setup_suite.assert_called_once_with()
169    mock_called.teardown_suite.assert_called_once_with()
170    mock_exit.assert_not_called()
171
172  def test_print_test_names(self):
173    mock_test_class = mock.MagicMock()
174    mock_cls_instance = mock.MagicMock()
175    mock_test_class.return_value = mock_cls_instance
176    suite_runner._print_test_names([mock_test_class])
177    mock_cls_instance._pre_run.assert_called_once()
178    mock_cls_instance._clean_up.assert_called_once()
179
180  def test_print_test_names_with_exception(self):
181    mock_test_class = mock.MagicMock()
182    mock_cls_instance = mock.MagicMock()
183    mock_test_class.return_value = mock_cls_instance
184    suite_runner._print_test_names([mock_test_class])
185    mock_cls_instance._pre_run.side_effect = Exception('Something went wrong.')
186    mock_cls_instance._clean_up.assert_called_once()
187
188
189if __name__ == '__main__':
190  unittest.main()
191