|
17 | 17 | import unittest
|
18 | 18 | import os
|
19 | 19 | import re
|
20 |
| - |
21 |
| -import threading |
22 |
| -import shutil |
23 |
| -import tempfile |
24 | 20 | from builtins import super
|
25 | 21 | from copy import copy
|
26 | 22 | from mbed_os_tools.test import init_host_test_cli_params
|
27 | 23 | from mbed_os_tools.test.host_tests_runner.host_test_default import DefaultTestSelector
|
28 | 24 | from mock import patch, MagicMock
|
29 | 25 |
|
30 |
| - |
31 |
| -class MockThread(threading.Thread): |
32 |
| - def __init__(self, target=None, args=None): |
33 |
| - super().__init__(target=target, args=args) |
34 |
| - self._terminates = 0 |
35 |
| - self.exitcode = 0 # TODO maybe this needs to be setable? Mock sys.exit |
36 |
| - |
37 |
| - def terminate(self): |
38 |
| - self._terminates += 1 |
39 |
| - |
40 |
| -class MockSerial(object): |
41 |
| - def __init__(self, *args, **kwargs): |
42 |
| - super().__init__(*args, **kwargs) |
43 |
| - self._args = args |
44 |
| - self._kwargs = kwargs |
45 |
| - self._open = True |
46 |
| - self._rx_counter = 0 |
47 |
| - self._tx_buffer = b"" |
48 |
| - self._rx_buffer = b"" |
49 |
| - self._upstream_write_cb = None |
50 |
| - |
51 |
| - def read(self, count): |
52 |
| - contents = self._rx_buffer[self._rx_counter:count] |
53 |
| - self._rx_counter += len(contents) |
54 |
| - return contents |
55 |
| - |
56 |
| - def write(self, data): |
57 |
| - self._tx_buffer += data |
58 |
| - if self._upstream_write_cb: |
59 |
| - # TODO this may not work... |
60 |
| - self._upstream_write_cb(data) |
61 |
| - |
62 |
| - def close(self): |
63 |
| - self._open = False |
64 |
| - |
65 |
| - def downstream_write(self, data): |
66 |
| - self._rx_buffer += data.encode("utf-8") |
67 |
| - |
68 |
| - def downstream_write_bytes(self, data): |
69 |
| - self._rx_buffer += data |
70 |
| - |
71 |
| - def on_upstream_write(self, func): |
72 |
| - self._upstream_write_cb = func |
73 |
| - |
74 |
| -kv_regex = re.compile("\{\{([\w\d_-]+);([^\}]+)\}\}") |
75 |
| - |
76 |
| -class MockMbedDevice(object): |
77 |
| - def __init__(self, serial): |
78 |
| - self._synced = False |
79 |
| - self._kvs = [] |
80 |
| - self._serial = serial |
81 |
| - self._serial.on_upstream_write(self.on_write) |
82 |
| - |
83 |
| - def handle_kv(self, key, value): |
84 |
| - if not self._synced: |
85 |
| - if key == "__sync": |
86 |
| - self._synced = True |
87 |
| - self.send_kv(key, value) |
88 |
| - self.on_sync() |
89 |
| - else: |
90 |
| - pass |
91 |
| - |
92 |
| - def send_kv(self, key, value): |
93 |
| - self._serial.downstream_write("{{{{{};{}}}}}\r\n".format(key, value)) |
94 |
| - |
95 |
| - def on_write(self, data): |
96 |
| - kvs = kv_regex.findall(data.decode("utf-8")) |
97 |
| - |
98 |
| - for key, value in kvs: |
99 |
| - self.handle_kv(key, value) |
100 |
| - self._kvs.append((key, value)) |
101 |
| - |
102 |
| - def on_sync(self): |
103 |
| - self._serial.downstream_write( |
104 |
| - "{{__timeout;15}}\r\n" |
105 |
| - "{{__host_test_name;default_auto}}\r\n" |
106 |
| - "{{end;success}}\n" |
107 |
| - "{{__exit;0}}\r\n" |
108 |
| - ) |
109 |
| - |
110 |
| - |
111 |
| -def _process_side_effect(target=None, args=None): |
112 |
| - return MockThread(target=target, args=args) |
113 |
| - |
114 |
| -class MockTestEnvironment(object): |
115 |
| - |
116 |
| - def __init__(self, test_case, platform_info, image_path): |
117 |
| - self._test_case = test_case |
118 |
| - self._tempdir = tempfile.mkdtemp() |
119 |
| - self._platform_info = copy(platform_info) |
120 |
| - self._platform_info['mount_point'] = os.path.join(self._tempdir, self._platform_info['mount_point']) |
121 |
| - self._platform_info['serial_port'] = os.path.join(self._tempdir, self._platform_info['serial_port']) |
122 |
| - self._image_path = os.path.join(self._tempdir, image_path) |
123 |
| - |
124 |
| - self._patch_definitions = [] |
125 |
| - self.patches = {} |
126 |
| - |
127 |
| - args = ( |
128 |
| - 'mbedhtrun -m {} -p {}:9600 -f ' |
129 |
| - '"{}" -e "TESTS/host_tests" -d {} -c default ' |
130 |
| - '-t {} -r default ' |
131 |
| - '-C 4 --sync 5 -P 60' |
132 |
| - ).format( |
133 |
| - self._platform_info['platform_name'], |
134 |
| - self._platform_info['serial_port'], |
135 |
| - self._image_path, |
136 |
| - self._platform_info['mount_point'], |
137 |
| - self._platform_info['target_id'] |
138 |
| - ).split() |
139 |
| - self.patch('sys.argv', new=args) |
140 |
| - |
141 |
| - # Mock detect |
142 |
| - detect_mock = MagicMock() |
143 |
| - detect_mock.return_value.list_mbeds.return_value = [ |
144 |
| - self._platform_info |
145 |
| - ] |
146 |
| - self.patch('mbed_os_tools.detect.create', new=detect_mock) |
147 |
| - |
148 |
| - # Mock process |
149 |
| - self.patch( |
150 |
| - 'mbed_os_tools.test.host_tests_runner.host_test_default.Process', |
151 |
| - new=MagicMock(side_effect=_process_side_effect) |
152 |
| - ) |
153 |
| - self.patch( |
154 |
| - 'mbed_os_tools.test.host_tests_plugins.host_test_plugins.call', |
155 |
| - new=MagicMock(return_value=0) |
156 |
| - ) |
157 |
| - |
158 |
| - # Mock serial |
159 |
| - mock_serial = MockSerial() |
160 |
| - mock_device = MockMbedDevice(mock_serial) |
161 |
| - self.patch( |
162 |
| - 'mbed_os_tools.test.host_tests_conn_proxy.conn_primitive_serial.Serial', |
163 |
| - new=MagicMock(return_value=mock_serial) |
164 |
| - ) |
165 |
| - |
166 |
| - |
167 |
| - def patch(self, path, **kwargs): |
168 |
| - self._patch_definitions.append((path, patch(path, **kwargs))) |
169 |
| - |
170 |
| - def __enter__(self): |
171 |
| - os.makedirs(os.path.dirname(self._image_path)) |
172 |
| - with open(self._image_path, 'w') as _: |
173 |
| - pass |
174 |
| - |
175 |
| - os.makedirs(self._platform_info['mount_point']) |
176 |
| - |
177 |
| - for path, patcher in self._patch_definitions: |
178 |
| - self.patches[path] = patcher.start() |
179 |
| - |
180 |
| - def __exit__(self, type, value, traceback): |
181 |
| - for _, patcher in self._patch_definitions: |
182 |
| - patcher.stop() |
183 |
| - |
184 |
| - shutil.rmtree(self._tempdir) |
185 |
| - |
186 |
| -class MockTestEnvironmentPosix(MockTestEnvironment): |
187 |
| - |
188 |
| - def __init__(self, test_case, platform_info, image_path): |
189 |
| - super().__init__(test_case, platform_info, image_path) |
190 |
| - |
191 |
| - self.patch('os.name', new='posix') |
192 |
| - |
193 |
| - def __exit__(self, type, value, traceback): |
194 |
| - super().__exit__(type, value, traceback) |
195 |
| - |
196 |
| - # Assert for proper image copy |
197 |
| - mocked_call = self.patches[ |
198 |
| - 'mbed_os_tools.test.host_tests_plugins.host_test_plugins.call' |
199 |
| - ] |
200 |
| - |
201 |
| - first_call_args = mocked_call.call_args_list[0][0][0] |
202 |
| - self._test_case.assertEqual(first_call_args[0], "cp") |
203 |
| - self._test_case.assertEqual(first_call_args[1], self._image_path) |
204 |
| - self._test_case.assertTrue(first_call_args[2].startswith(self._platform_info["mount_point"])) |
205 |
| - self._test_case.assertTrue(first_call_args[2].endswith(os.path.splitext(self._image_path)[1])) |
206 |
| - |
207 |
| - |
208 |
| -class MockTestEnvironmentLinux(MockTestEnvironmentPosix): |
209 |
| - |
210 |
| - def __init__(self, test_case, platform_info, image_path): |
211 |
| - super().__init__(test_case, platform_info, image_path) |
212 |
| - |
213 |
| - self.patch( |
214 |
| - 'os.uname', |
215 |
| - new=MagicMock(return_value=('Linux',)), |
216 |
| - create=True |
217 |
| - ) |
218 |
| - |
219 |
| - def __exit__(self, type, value, traceback): |
220 |
| - super().__exit__(type, value, traceback) |
221 |
| - |
222 |
| - # Assert for proper image copy |
223 |
| - mocked_call = self.patches[ |
224 |
| - 'mbed_os_tools.test.host_tests_plugins.host_test_plugins.call' |
225 |
| - ] |
226 |
| - |
227 |
| - second_call_args = mocked_call.call_args_list[1][0][0] |
228 |
| - destination_path = os.path.normpath( |
229 |
| - os.path.join( |
230 |
| - self._platform_info["mount_point"], |
231 |
| - os.path.basename(self._image_path) |
232 |
| - ) |
233 |
| - ) |
234 |
| - |
235 |
| - self._test_case.assertEqual( |
236 |
| - second_call_args, |
237 |
| - ["sync", "-f", destination_path] |
238 |
| - ) |
239 |
| - |
240 |
| - # Ensure only two subprocesses were started |
241 |
| - self._test_case.assertEqual(len(mocked_call.call_args_list), 2) |
242 |
| - |
243 |
| -class MockTestEnvironmentDarwin(MockTestEnvironmentPosix): |
244 |
| - |
245 |
| - def __init__(self, test_case, platform_info, image_path): |
246 |
| - super().__init__(test_case, platform_info, image_path) |
247 |
| - |
248 |
| - self.patch( |
249 |
| - 'os.uname', |
250 |
| - new=MagicMock(return_value=('Darwin',)), |
251 |
| - create=True |
252 |
| - ) |
253 |
| - |
254 |
| - def __exit__(self, type, value, traceback): |
255 |
| - super().__exit__(type, value, traceback) |
256 |
| - |
257 |
| - # Assert for proper image copy |
258 |
| - mocked_call = self.patches[ |
259 |
| - 'mbed_os_tools.test.host_tests_plugins.host_test_plugins.call' |
260 |
| - ] |
261 |
| - |
262 |
| - second_call_args = mocked_call.call_args_list[1][0][0] |
263 |
| - self._test_case.assertEqual(second_call_args, ["sync"]) |
264 |
| - |
265 |
| - # Ensure only two subprocesses were started |
266 |
| - self._test_case.assertEqual(len(mocked_call.call_args_list), 2) |
267 |
| - |
268 |
| -class MockTestEnvironmentWindows(MockTestEnvironment): |
269 |
| - |
270 |
| - def __init__(self, test_case, platform_info, image_path): |
271 |
| - super().__init__(test_case, platform_info, image_path) |
272 |
| - |
273 |
| - self.patch('os.name', new='nt') |
274 |
| - |
275 |
| - def __exit__(self, type, value, traceback): |
276 |
| - super().__exit__(type, value, traceback) |
277 |
| - |
278 |
| - # Assert for proper image copy |
279 |
| - mocked_call = self.patches[ |
280 |
| - 'mbed_os_tools.test.host_tests_plugins.host_test_plugins.call' |
281 |
| - ] |
282 |
| - |
283 |
| - first_call_args = mocked_call.call_args_list[0][0][0] |
284 |
| - self._test_case.assertEqual(first_call_args[0], "copy") |
285 |
| - self._test_case.assertEqual(first_call_args[1], self._image_path) |
286 |
| - self._test_case.assertTrue(first_call_args[2].startswith(self._platform_info["mount_point"])) |
287 |
| - self._test_case.assertTrue(first_call_args[2].endswith(os.path.splitext(self._image_path)[1])) |
288 |
| - |
289 |
| - # Ensure only one subprocess was started |
290 |
| - self._test_case.assertEqual(len(mocked_call.call_args_list), 1) |
| 26 | +from .mocks.environment.linux import MockTestEnvironmentLinux |
| 27 | +from .mocks.environment.darwin import MockTestEnvironmentDarwin |
| 28 | +from .mocks.environment.windows import MockTestEnvironmentWindows |
291 | 29 |
|
292 | 30 | mock_platform_info = {
|
293 | 31 | "platform_name": "K64F",
|
|
0 commit comments