pytest-embedded 1.2.5__tar.gz → 1.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/PKG-INFO +1 -1
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded/dut.py +25 -3
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded/plugin.py +47 -5
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded/utils.py +93 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded.egg-info/PKG-INFO +1 -1
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/README.md +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded/__init__.py +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded/app.py +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded/log.py +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded/unity.py +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded.egg-info/SOURCES.txt +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded.egg-info/dependency_links.txt +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded.egg-info/entry_points.txt +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded.egg-info/requires.txt +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded.egg-info/top_level.txt +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/setup.cfg +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/setup.py +0 -0
|
@@ -9,10 +9,10 @@ import pexpect
|
|
|
9
9
|
from .app import App
|
|
10
10
|
from .log import PexpectProcess
|
|
11
11
|
from .unity import UNITY_SUMMARY_LINE_REGEX, TestSuite
|
|
12
|
-
from .utils import Meta, remove_asci_color_code, to_bytes, to_list
|
|
12
|
+
from .utils import Meta, _InjectMixinCls, remove_asci_color_code, to_bytes, to_list
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
class Dut:
|
|
15
|
+
class Dut(_InjectMixinCls):
|
|
16
16
|
"""
|
|
17
17
|
Device under test (DUT) base class
|
|
18
18
|
|
|
@@ -160,7 +160,7 @@ class Dut:
|
|
|
160
160
|
- Would raise TIMEOUT exception at the end of the test if any unity test case execution took longer
|
|
161
161
|
than timeout value
|
|
162
162
|
|
|
163
|
-
|
|
163
|
+
Warning:
|
|
164
164
|
- All unity test cases record would be missed if the final report block is uncaught.
|
|
165
165
|
"""
|
|
166
166
|
self.expect(UNITY_SUMMARY_LINE_REGEX, timeout=timeout)
|
|
@@ -174,3 +174,25 @@ class Dut:
|
|
|
174
174
|
log = remove_asci_color_code(log)
|
|
175
175
|
|
|
176
176
|
self.testsuite.add_unity_test_cases(log)
|
|
177
|
+
|
|
178
|
+
@_InjectMixinCls.require_services('idf')
|
|
179
|
+
def run_all_single_board_cases(
|
|
180
|
+
self,
|
|
181
|
+
group: Optional[str] = None,
|
|
182
|
+
reset: bool = False,
|
|
183
|
+
timeout: float = 30,
|
|
184
|
+
run_ignore_cases: bool = False,
|
|
185
|
+
):
|
|
186
|
+
"""
|
|
187
|
+
Run all multi_stage cases
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
group: test case group
|
|
191
|
+
reset: whether to perform a hardware reset before running a case
|
|
192
|
+
timeout: timeout. (Default: 30 seconds)
|
|
193
|
+
run_ignore_cases: run ignored test cases or not
|
|
194
|
+
|
|
195
|
+
Warning:
|
|
196
|
+
requires enable service `idf`
|
|
197
|
+
"""
|
|
198
|
+
pass
|
|
@@ -216,7 +216,7 @@ def pytest_addoption(parser):
|
|
|
216
216
|
)
|
|
217
217
|
qemu_group.addoption(
|
|
218
218
|
'--qemu-cli-args',
|
|
219
|
-
help='QEMU cli default arguments. (Default: "-nographic -
|
|
219
|
+
help='QEMU cli default arguments. (Default: "-nographic -machine esp32")',
|
|
220
220
|
)
|
|
221
221
|
qemu_group.addoption(
|
|
222
222
|
'--qemu-extra-args',
|
|
@@ -227,6 +227,15 @@ def pytest_addoption(parser):
|
|
|
227
227
|
help='y/yes/true for True and n/no/false for False. '
|
|
228
228
|
'Set to True to disable auto regenerate image. (Default: False)',
|
|
229
229
|
)
|
|
230
|
+
qemu_group.addoption(
|
|
231
|
+
'--encrypt',
|
|
232
|
+
help='y/yes/true for True and n/no/false for False. '
|
|
233
|
+
'Set to True for pre-encryption workflow (Default: False)',
|
|
234
|
+
)
|
|
235
|
+
qemu_group.addoption(
|
|
236
|
+
'--keyfile',
|
|
237
|
+
help='Flash Encryption (pre-encrypted workflow) key path. (Default: None)',
|
|
238
|
+
)
|
|
230
239
|
|
|
231
240
|
|
|
232
241
|
###########
|
|
@@ -898,6 +907,20 @@ def skip_regenerate_image(request: FixtureRequest) -> t.Optional[str]:
|
|
|
898
907
|
return _request_param_or_config_option_or_default(request, 'skip_regenerate_image', None)
|
|
899
908
|
|
|
900
909
|
|
|
910
|
+
@pytest.fixture
|
|
911
|
+
@multi_dut_argument
|
|
912
|
+
def encrypt(request: FixtureRequest) -> t.Optional[str]:
|
|
913
|
+
"""Enable parametrization for the same cli option"""
|
|
914
|
+
return _request_param_or_config_option_or_default(request, 'encrypt', None)
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
@pytest.fixture
|
|
918
|
+
@multi_dut_argument
|
|
919
|
+
def keyfile(request: FixtureRequest) -> t.Optional[str]:
|
|
920
|
+
"""Enable parametrization for the same cli option"""
|
|
921
|
+
return _request_param_or_config_option_or_default(request, 'keyfile', None)
|
|
922
|
+
|
|
923
|
+
|
|
901
924
|
####################
|
|
902
925
|
# Private Fixtures #
|
|
903
926
|
####################
|
|
@@ -924,6 +947,7 @@ def _services(embedded_services: t.Optional[str]) -> t.List[str]:
|
|
|
924
947
|
@dataclass
|
|
925
948
|
class ClassCliOptions:
|
|
926
949
|
classes: t.Dict[str, type]
|
|
950
|
+
mixins: t.Dict[str, t.List[type]]
|
|
927
951
|
kwargs: t.Dict[str, t.Dict[str, t.Any]]
|
|
928
952
|
|
|
929
953
|
|
|
@@ -958,6 +982,8 @@ def _fixture_classes_and_options(
|
|
|
958
982
|
qemu_cli_args,
|
|
959
983
|
qemu_extra_args,
|
|
960
984
|
skip_regenerate_image,
|
|
985
|
+
encrypt,
|
|
986
|
+
keyfile,
|
|
961
987
|
# pre-initialized fixtures
|
|
962
988
|
dut_index,
|
|
963
989
|
_pexpect_logfile,
|
|
@@ -985,6 +1011,7 @@ def _fixture_classes_and_options(
|
|
|
985
1011
|
}
|
|
986
1012
|
"""
|
|
987
1013
|
classes: t.Dict[str, type] = {}
|
|
1014
|
+
mixins: t.Dict[str, t.List[type]] = defaultdict(list)
|
|
988
1015
|
kwargs: t.Dict[str, t.Dict[str, t.Any]] = defaultdict(dict)
|
|
989
1016
|
|
|
990
1017
|
for fixture in FIXTURES_SERVICES.keys():
|
|
@@ -1001,6 +1028,8 @@ def _fixture_classes_and_options(
|
|
|
1001
1028
|
'part_tool': part_tool,
|
|
1002
1029
|
'qemu_image_path': qemu_image_path,
|
|
1003
1030
|
'skip_regenerate_image': skip_regenerate_image,
|
|
1031
|
+
'encrypt': encrypt,
|
|
1032
|
+
'keyfile': keyfile,
|
|
1004
1033
|
}
|
|
1005
1034
|
)
|
|
1006
1035
|
else:
|
|
@@ -1099,16 +1128,23 @@ def _fixture_classes_and_options(
|
|
|
1099
1128
|
}
|
|
1100
1129
|
elif fixture == 'qemu':
|
|
1101
1130
|
if 'qemu' in _services:
|
|
1102
|
-
from pytest_embedded_qemu import
|
|
1131
|
+
from pytest_embedded_qemu import (
|
|
1132
|
+
DEFAULT_IMAGE_FN,
|
|
1133
|
+
ENCRYPTED_IMAGE_FN,
|
|
1134
|
+
Qemu,
|
|
1135
|
+
)
|
|
1103
1136
|
|
|
1104
1137
|
classes[fixture] = Qemu
|
|
1105
1138
|
kwargs[fixture] = {
|
|
1106
1139
|
'msg_queue': msg_queue,
|
|
1107
1140
|
'qemu_image_path': qemu_image_path
|
|
1108
|
-
or os.path.join(
|
|
1141
|
+
or os.path.join(
|
|
1142
|
+
app_path or '', build_dir or 'build', ENCRYPTED_IMAGE_FN if encrypt else DEFAULT_IMAGE_FN
|
|
1143
|
+
),
|
|
1109
1144
|
'qemu_prog_path': qemu_prog_path,
|
|
1110
1145
|
'qemu_cli_args': qemu_cli_args,
|
|
1111
1146
|
'qemu_extra_args': qemu_extra_args,
|
|
1147
|
+
'app': None,
|
|
1112
1148
|
'meta': _meta,
|
|
1113
1149
|
}
|
|
1114
1150
|
elif fixture == 'dut':
|
|
@@ -1130,6 +1166,11 @@ def _fixture_classes_and_options(
|
|
|
1130
1166
|
'qemu': None,
|
|
1131
1167
|
}
|
|
1132
1168
|
)
|
|
1169
|
+
|
|
1170
|
+
if 'idf' in _services:
|
|
1171
|
+
from pytest_embedded_idf.unity_tester import IdfUnityDutMixin
|
|
1172
|
+
|
|
1173
|
+
mixins[fixture].append(IdfUnityDutMixin)
|
|
1133
1174
|
elif 'jtag' in _services:
|
|
1134
1175
|
if 'idf' in _services:
|
|
1135
1176
|
from pytest_embedded_idf import IdfDut
|
|
@@ -1169,7 +1210,7 @@ def _fixture_classes_and_options(
|
|
|
1169
1210
|
}
|
|
1170
1211
|
)
|
|
1171
1212
|
|
|
1172
|
-
return ClassCliOptions(classes, kwargs)
|
|
1213
|
+
return ClassCliOptions(classes, mixins, kwargs)
|
|
1173
1214
|
|
|
1174
1215
|
|
|
1175
1216
|
####################
|
|
@@ -1268,6 +1309,7 @@ def dut(
|
|
|
1268
1309
|
return cls(**kwargs)
|
|
1269
1310
|
|
|
1270
1311
|
cls = _fixture_classes_and_options.classes['dut']
|
|
1312
|
+
mixins = _fixture_classes_and_options.mixins['dut']
|
|
1271
1313
|
kwargs = _fixture_classes_and_options.kwargs['dut']
|
|
1272
1314
|
|
|
1273
1315
|
for k, v in kwargs.items():
|
|
@@ -1283,7 +1325,7 @@ def dut(
|
|
|
1283
1325
|
elif k == 'qemu':
|
|
1284
1326
|
kwargs[k] = qemu
|
|
1285
1327
|
|
|
1286
|
-
return cls(**_drop_none_kwargs(kwargs))
|
|
1328
|
+
return cls(**_drop_none_kwargs(kwargs), mixins=mixins)
|
|
1287
1329
|
|
|
1288
1330
|
|
|
1289
1331
|
@pytest.fixture
|
|
@@ -33,6 +33,12 @@ FIXTURES_SERVICES = {
|
|
|
33
33
|
|
|
34
34
|
_T = t.TypeVar('_T')
|
|
35
35
|
|
|
36
|
+
_MIXIN_REQUIRED_SERVICES = {
|
|
37
|
+
'IdfUnityMixin': ['idf'],
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
_MIXIN_REQUIRED_SERVICES_KEY = '_based_on_services'
|
|
41
|
+
|
|
36
42
|
|
|
37
43
|
#######################
|
|
38
44
|
# Errors and Warnings #
|
|
@@ -54,6 +60,16 @@ class PackageNotInstalledError(SystemExit):
|
|
|
54
60
|
)
|
|
55
61
|
|
|
56
62
|
|
|
63
|
+
class RequireServiceError(SystemExit):
|
|
64
|
+
def __init__(self, func_name: str, services: t.Union[str, t.List[str]]) -> None:
|
|
65
|
+
services_str = ','.join(to_list(services))
|
|
66
|
+
super().__init__(
|
|
67
|
+
f'function {func_name} requires enabling one of the service(s) {services_str}. '
|
|
68
|
+
f'Please enable by passing CLI options "--embedded-services {services_str}". '
|
|
69
|
+
f'For more details, please refer to "pytest --help".'
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
57
73
|
#####################
|
|
58
74
|
# Utility Functions #
|
|
59
75
|
#####################
|
|
@@ -245,3 +261,80 @@ def lazy_load(
|
|
|
245
261
|
raise AttributeError('Attribute %s not found in module %s', object_name, base_module.__name__)
|
|
246
262
|
|
|
247
263
|
return __getattr__
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class _InjectMixinMeta(type):
|
|
267
|
+
def __call__(cls, *args, **kwargs):
|
|
268
|
+
try:
|
|
269
|
+
mixins = kwargs.pop('mixins', None)
|
|
270
|
+
if mixins:
|
|
271
|
+
mixins = to_list(mixins)
|
|
272
|
+
name = cls.__name__ + 'With' + 'And'.join([m.__name__ for m in mixins])
|
|
273
|
+
cls = type(name, tuple([*mixins, cls]), {})
|
|
274
|
+
|
|
275
|
+
# users should only know concept "services", not mixins
|
|
276
|
+
_based_on_services = set()
|
|
277
|
+
for m in mixins:
|
|
278
|
+
if m.__name__ not in _MIXIN_REQUIRED_SERVICES:
|
|
279
|
+
continue
|
|
280
|
+
|
|
281
|
+
_based_on_services.update(to_list(_MIXIN_REQUIRED_SERVICES[m.__name__]))
|
|
282
|
+
kwargs[_MIXIN_REQUIRED_SERVICES_KEY] = sorted(_based_on_services)
|
|
283
|
+
except KeyError:
|
|
284
|
+
pass
|
|
285
|
+
return type.__call__(cls, *args, **kwargs)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class _InjectMixinCls(metaclass=_InjectMixinMeta):
|
|
289
|
+
"""
|
|
290
|
+
This class provide a check function `require_services()` to check if the function is injected by
|
|
291
|
+
enabling one of the required services.
|
|
292
|
+
|
|
293
|
+
The benefits are:
|
|
294
|
+
- provide the autocompletion for the functions provided by the mixins
|
|
295
|
+
- check the requirement at runtime
|
|
296
|
+
|
|
297
|
+
Examples:
|
|
298
|
+
|
|
299
|
+
class IdfUnityMixin:
|
|
300
|
+
def foo(self):
|
|
301
|
+
print('foo from MixinOne')
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class Test(_InjectMixinCls):
|
|
305
|
+
def __init__(self, **kwargs):
|
|
306
|
+
for k, v in kwargs.items():
|
|
307
|
+
setattr(self, k, v)
|
|
308
|
+
|
|
309
|
+
@_InjectMixinCls.require_services('idf')
|
|
310
|
+
def foo(self):
|
|
311
|
+
pass
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
# mro(): TestWithIdfUnityMixin, IdfUnityMixin, Test, object
|
|
315
|
+
s1 = Test(mixins=IdfUnityMixin)
|
|
316
|
+
# mro(): Test, object
|
|
317
|
+
s2 = Test()
|
|
318
|
+
s1.foo() # foo from IdfUnityMixin
|
|
319
|
+
s2.foo() # function foo requires enabling one of the service(s) idf.
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
def require_services(*services):
|
|
323
|
+
def decorator(func):
|
|
324
|
+
def wrapped(self, *args, **kwargs):
|
|
325
|
+
based = False
|
|
326
|
+
for service in services:
|
|
327
|
+
if hasattr(self, _MIXIN_REQUIRED_SERVICES_KEY) and service in getattr(
|
|
328
|
+
self, _MIXIN_REQUIRED_SERVICES_KEY
|
|
329
|
+
):
|
|
330
|
+
based = True
|
|
331
|
+
break
|
|
332
|
+
|
|
333
|
+
if based:
|
|
334
|
+
return func(self, *args, **kwargs)
|
|
335
|
+
else:
|
|
336
|
+
raise RequireServiceError(func.__name__, services)
|
|
337
|
+
|
|
338
|
+
return wrapped
|
|
339
|
+
|
|
340
|
+
return decorator
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest-embedded-1.2.5 → pytest-embedded-1.3.0}/pytest_embedded.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|