Skip to content

Commit e1414ad

Browse files
Merge pull request #41 from bridadan/host_test_black_box_testing
Host test black box testing
2 parents 2f70241 + 3db942b commit e1414ad

File tree

11 files changed

+581
-0
lines changed

11 files changed

+581
-0
lines changed

mbed_os_tools/test/__init__.py

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
2222
"""
2323

24+
import imp
25+
import sys
26+
from optparse import OptionParser
27+
from optparse import SUPPRESS_HELP
2428
from . import host_tests_plugins
2529
from .host_tests_registry import HostRegistry
2630
from .host_tests import BaseHostTest, event_callback
@@ -40,3 +44,205 @@ def get_plugin_caps(methods=None):
4044
for method in methods:
4145
result[method] = host_tests_plugins.get_plugin_caps(method)
4246
return result
47+
48+
def init_host_test_cli_params():
49+
"""! Function creates CLI parser object and returns populated options object.
50+
@return Function returns 'options' object returned from OptionParser class
51+
@details Options object later can be used to populate host test selector script.
52+
"""
53+
parser = OptionParser()
54+
55+
parser.add_option("-m", "--micro",
56+
dest="micro",
57+
help="Target microcontroller name",
58+
metavar="MICRO")
59+
60+
parser.add_option("-p", "--port",
61+
dest="port",
62+
help="Serial port of the target",
63+
metavar="PORT")
64+
65+
parser.add_option("-d", "--disk",
66+
dest="disk",
67+
help="Target disk (mount point) path",
68+
metavar="DISK_PATH")
69+
70+
parser.add_option("-t", "--target-id",
71+
dest="target_id",
72+
help="Unique Target Id or mbed platform",
73+
metavar="TARGET_ID")
74+
75+
parser.add_option("", "--sync",
76+
dest="sync_behavior",
77+
default=2,
78+
type=int,
79+
help="Define how many times __sync packet will be sent to device: 0: none; -1: forever; 1,2,3... - number of times (Default 2 time)",
80+
metavar="SYNC_BEHAVIOR")
81+
82+
parser.add_option("", "--sync-timeout",
83+
dest="sync_timeout",
84+
default=5,
85+
type=int,
86+
help="Define delay in seconds between __sync packet (Default is 5 seconds)",
87+
metavar="SYNC_TIMEOUT")
88+
89+
parser.add_option("-f", "--image-path",
90+
dest="image_path",
91+
help="Path with target's binary image",
92+
metavar="IMAGE_PATH")
93+
94+
copy_methods_str = "Plugin support: " + ', '.join(host_tests_plugins.get_plugin_caps('CopyMethod'))
95+
96+
parser.add_option("-c", "--copy",
97+
dest="copy_method",
98+
help="Copy (flash the target) method selector. " + copy_methods_str,
99+
metavar="COPY_METHOD")
100+
101+
parser.add_option("", "--retry-copy",
102+
dest="retry_copy",
103+
default=3,
104+
type=int,
105+
help="Number of attempts to flash the target",
106+
metavar="RETRY_COPY")
107+
108+
parser.add_option("", "--tag-filters",
109+
dest="tag_filters",
110+
default="",
111+
type=str,
112+
help="Comma seperated list of device tags used when allocating a target to specify required hardware or attributes [--tag-filters tag1,tag2]",
113+
metavar="TAG_FILTERS")
114+
115+
reset_methods_str = "Plugin support: " + ', '.join(host_tests_plugins.get_plugin_caps('ResetMethod'))
116+
117+
parser.add_option("-r", "--reset",
118+
dest="forced_reset_type",
119+
help="Forces different type of reset. " + reset_methods_str)
120+
121+
parser.add_option("-C", "--program_cycle_s",
122+
dest="program_cycle_s",
123+
default=4,
124+
help="Program cycle sleep. Define how many seconds you want wait after copying binary onto target (Default is 4 second)",
125+
type="float",
126+
metavar="PROGRAM_CYCLE_S")
127+
128+
parser.add_option("-R", "--reset-timeout",
129+
dest="forced_reset_timeout",
130+
default=1,
131+
metavar="NUMBER",
132+
type="float",
133+
help="When forcing a reset using option -r you can set up after reset idle delay in seconds (Default is 1 second)")
134+
135+
parser.add_option("--process-start-timeout",
136+
dest="process_start_timeout",
137+
default=60,
138+
metavar="NUMBER",
139+
type="float",
140+
help="This sets the maximum time in seconds to wait for an internal process to start. This mostly only affects machines under heavy load (Default is 60 seconds)")
141+
142+
parser.add_option("-e", "--enum-host-tests",
143+
dest="enum_host_tests",
144+
action="append",
145+
default=["./test/host_tests"],
146+
help="Define directory with local host tests")
147+
148+
parser.add_option('', '--test-cfg',
149+
dest='json_test_configuration',
150+
help='Pass to host test class data about host test configuration')
151+
152+
parser.add_option('', '--list',
153+
dest='list_reg_hts',
154+
default=False,
155+
action="store_true",
156+
help='Prints registered host test and exits')
157+
158+
parser.add_option('', '--plugins',
159+
dest='list_plugins',
160+
default=False,
161+
action="store_true",
162+
help='Prints registered plugins and exits')
163+
164+
parser.add_option('-g', '--grm',
165+
dest='global_resource_mgr',
166+
help='[Experimental] Global resource manager service module name, IP and port, example remote_client:10.2.123.43:3334')
167+
168+
# Show --fm option only if "fm_agent" module installed
169+
try:
170+
imp.find_module('fm_agent')
171+
except ImportError:
172+
fm_help=SUPPRESS_HELP
173+
else:
174+
fm_help='Fast Model connection, This option requires mbed-fastmodel-agent module installed, list CONFIGs via "mbedfm"'
175+
parser.add_option('', '--fm',
176+
dest='fast_model_connection',
177+
metavar="CONFIG",
178+
default=None,
179+
help=fm_help)
180+
181+
parser.add_option('', '--run',
182+
dest='run_binary',
183+
default=False,
184+
action="store_true",
185+
help='Runs binary image on target (workflow: flash, reset, output console)')
186+
187+
parser.add_option('', '--skip-flashing',
188+
dest='skip_flashing',
189+
default=False,
190+
action="store_true",
191+
help='Skips use of copy/flash plugin. Note: target will not be reflashed')
192+
193+
parser.add_option('', '--skip-reset',
194+
dest='skip_reset',
195+
default=False,
196+
action="store_true",
197+
help='Skips use of reset plugin. Note: target will not be reset')
198+
199+
parser.add_option('-P', '--polling-timeout',
200+
dest='polling_timeout',
201+
default=60,
202+
metavar="NUMBER",
203+
type="int",
204+
help='Timeout in sec for readiness of mount point and serial port of local or remote device. Default 60 sec')
205+
206+
parser.add_option('-b', '--send-break',
207+
dest='send_break_cmd',
208+
default=False,
209+
action="store_true",
210+
help='Send reset signal to board on specified port (-p PORT) and print serial output. You can combine this with (-r RESET_TYPE) switch')
211+
212+
parser.add_option('', '--baud-rate',
213+
dest='baud_rate',
214+
help="Baud rate of target, overrides values from mbed-ls, disk/mount point (-d, --disk-path), and serial port -p <port>:<baud rate>",
215+
metavar="BAUD_RATE")
216+
217+
parser.add_option('-v', '--verbose',
218+
dest='verbose',
219+
default=False,
220+
action="store_true",
221+
help='More verbose mode')
222+
223+
parser.add_option('', '--serial-output-file',
224+
dest='serial_output_file',
225+
default=None,
226+
help='Save target serial output to this file.')
227+
228+
parser.add_option('', '--compare-log',
229+
dest='compare_log',
230+
default=None,
231+
help='Log file to compare with the serial output from target.')
232+
233+
parser.add_option('', '--version',
234+
dest='version',
235+
default=False,
236+
action="store_true",
237+
help='Prints package version and exits')
238+
239+
parser.description = """Flash, reset and perform host supervised tests on mbed platforms"""
240+
parser.epilog = """Example: mbedhtrun -d E: -p COM5 -f "test.bin" -C 4 -c shell -m K64F"""
241+
242+
(options, _) = parser.parse_args()
243+
244+
if len(sys.argv) == 1:
245+
parser.print_help()
246+
sys.exit()
247+
248+
return options

test/test/host_test_black_box.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env python
2+
# Copyright (c) 2018, Arm Limited and affiliates.
3+
# SPDX-License-Identifier: Apache-2.0
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+
17+
import unittest
18+
from copy import copy
19+
from mbed_os_tools.test import init_host_test_cli_params
20+
from mbed_os_tools.test.host_tests_runner.host_test_default import DefaultTestSelector
21+
22+
from .mocks.environment.linux import MockTestEnvironmentLinux
23+
from .mocks.environment.darwin import MockTestEnvironmentDarwin
24+
from .mocks.environment.windows import MockTestEnvironmentWindows
25+
26+
mock_platform_info = {
27+
"platform_name": "K64F",
28+
"target_id": "0240000031754e45000c0018948500156461000097969900",
29+
"mount_point": "/mnt/DAPLINK",
30+
"serial_port": "/dev/ttyACM0",
31+
}
32+
mock_image_path = "BUILD/tests/K64F/GCC_ARM/TESTS/network/interface/interface.bin"
33+
34+
class BlackBoxHostTestTestCase(unittest.TestCase):
35+
36+
def _run_host_test(self, environment):
37+
with environment as _env:
38+
test_selector = DefaultTestSelector(init_host_test_cli_params())
39+
result = test_selector.execute()
40+
test_selector.finish()
41+
42+
self.assertEqual(result, 0)
43+
44+
def test_host_test_linux(self):
45+
self._run_host_test(
46+
MockTestEnvironmentLinux(self, mock_platform_info, mock_image_path)
47+
)
48+
49+
def test_host_test_darwin(self):
50+
self._run_host_test(
51+
MockTestEnvironmentDarwin(self, mock_platform_info, mock_image_path)
52+
)
53+
54+
def test_host_test_windows(self):
55+
win_mock_platform_info = copy(mock_platform_info)
56+
win_mock_platform_info["serial_port"] = "COM5"
57+
58+
self._run_host_test(
59+
MockTestEnvironmentWindows(self, win_mock_platform_info, mock_image_path)
60+
)
61+
62+
if __name__ == '__main__':
63+
unittest.main()

test/test/mocks/__init__.py

Whitespace-only changes.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import tempfile
2+
import os
3+
import shutil
4+
5+
from mock import patch, MagicMock
6+
from copy import copy
7+
from ..serial import MockSerial
8+
from ..mbed_device import MockMbedDevice
9+
from ..process import MockProcess
10+
11+
class MockTestEnvironment(object):
12+
13+
def __init__(self, test_case, platform_info, image_path):
14+
self._test_case = test_case
15+
self._tempdir = tempfile.mkdtemp()
16+
self._patch_definitions = []
17+
self.patches = {}
18+
self._platform_info = copy(platform_info)
19+
20+
# Clean and retarget path to tempdir
21+
self._image_path = self._clean_path(image_path)
22+
self._platform_info['mount_point'] = self._clean_path(
23+
self._platform_info['mount_point']
24+
)
25+
26+
# Need to remove the drive letter in this case
27+
self._platform_info['serial_port'] = os.path.splitdrive(
28+
self._clean_path(self._platform_info['serial_port'])
29+
)[1]
30+
31+
args = (
32+
'mbedhtrun -m {} -p {}:9600 -f '
33+
'"{}" -e "TESTS/host_tests" -d {} -c default '
34+
'-t {} -r default '
35+
'-C 4 --sync 5 -P 60'
36+
).format(
37+
self._platform_info['platform_name'],
38+
self._platform_info['serial_port'],
39+
self._image_path,
40+
self._platform_info['mount_point'],
41+
self._platform_info['target_id']
42+
).split()
43+
self.patch('sys.argv', new=args)
44+
45+
# Mock detect
46+
detect_mock = MagicMock()
47+
detect_mock.return_value.list_mbeds.return_value = [
48+
self._platform_info
49+
]
50+
self.patch('mbed_os_tools.detect.create', new=detect_mock)
51+
52+
# Mock process calls and move them to threads to preserve mocks
53+
self.patch(
54+
'mbed_os_tools.test.host_tests_runner.host_test_default.Process',
55+
new=MagicMock(side_effect=self._process_side_effect)
56+
)
57+
self.patch(
58+
'mbed_os_tools.test.host_tests_plugins.host_test_plugins.call',
59+
new=MagicMock(return_value=0)
60+
)
61+
62+
mock_serial = MockSerial()
63+
mock_device = MockMbedDevice(mock_serial)
64+
self.patch(
65+
'mbed_os_tools.test.host_tests_conn_proxy.conn_primitive_serial.Serial',
66+
new=MagicMock(return_value=mock_serial)
67+
)
68+
69+
def _clean_path(self, path):
70+
# Remove the drive letter and ensure separators are consistent
71+
path = os.path.splitdrive(os.path.normpath(path))[1]
72+
return os.path.join(self._tempdir, path.lstrip(os.sep))
73+
74+
@staticmethod
75+
def _process_side_effect(target=None, args=None):
76+
return MockProcess(target=target, args=args)
77+
78+
def patch(self, path, **kwargs):
79+
self._patch_definitions.append((path, patch(path, **kwargs)))
80+
81+
def __enter__(self):
82+
os.makedirs(os.path.dirname(self._image_path))
83+
with open(self._image_path, 'w') as _:
84+
pass
85+
86+
os.makedirs(self._platform_info['mount_point'])
87+
88+
for path, patcher in self._patch_definitions:
89+
self.patches[path] = patcher.start()
90+
91+
def __exit__(self, type, value, traceback):
92+
for _, patcher in self._patch_definitions:
93+
patcher.stop()
94+
95+
shutil.rmtree(self._tempdir)

0 commit comments

Comments
 (0)