pytest-embedded 1.2.5__tar.gz → 1.3.1__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.1}/PKG-INFO +1 -5
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/README.md +0 -4
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/pytest_embedded/dut.py +37 -13
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/pytest_embedded/log.py +8 -11
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/pytest_embedded/plugin.py +49 -7
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/pytest_embedded/utils.py +119 -20
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/pytest_embedded.egg-info/PKG-INFO +1 -5
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/pytest_embedded/__init__.py +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/pytest_embedded/app.py +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/pytest_embedded/unity.py +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/pytest_embedded.egg-info/SOURCES.txt +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/pytest_embedded.egg-info/dependency_links.txt +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/pytest_embedded.egg-info/entry_points.txt +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/pytest_embedded.egg-info/requires.txt +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/pytest_embedded.egg-info/top_level.txt +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/setup.cfg +0 -0
- {pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 1.2
|
|
2
2
|
Name: pytest-embedded
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.1
|
|
4
4
|
Summary: pytest embedded plugin
|
|
5
5
|
Home-page: https://docs.espressif.com/projects/pytest-embedded/en/latest/
|
|
6
6
|
Author: Fu Hanxi
|
|
@@ -10,10 +10,6 @@ Description: ### pytest-embedded
|
|
|
10
10
|
|
|
11
11
|
A pytest plugin for embedded systems. Could activate different services for extra functionalities.
|
|
12
12
|
|
|
13
|
-
Used CLI Options:
|
|
14
|
-
|
|
15
|
-
- `app_path`
|
|
16
|
-
|
|
17
13
|
Platform: UNKNOWN
|
|
18
14
|
Classifier: Framework :: Pytest
|
|
19
15
|
Classifier: Intended Audience :: Developers
|
|
@@ -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
|
|
|
@@ -101,7 +101,7 @@ class Dut:
|
|
|
101
101
|
@_pexpect_func # noqa
|
|
102
102
|
def expect(self, pattern, **kwargs) -> Match: # noqa
|
|
103
103
|
"""
|
|
104
|
-
Expect the `pattern` from the internal buffer. All the arguments
|
|
104
|
+
Expect the `pattern` from the internal buffer. All the arguments will be passed to `pexpect.expect()`.
|
|
105
105
|
|
|
106
106
|
Args:
|
|
107
107
|
pattern: string, or compiled regex, or a list of string and compiled regex.
|
|
@@ -112,16 +112,17 @@ class Dut:
|
|
|
112
112
|
Otherwise match any of them could pass
|
|
113
113
|
|
|
114
114
|
Returns:
|
|
115
|
-
|
|
115
|
+
`AnyStr` or `re.Match`
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
- `AnyStr`: if you're matching `pexpect.EOF` or `pexpect.TIMEOUT` to get all the current buffers.
|
|
118
|
+
- `re.Match`: if matched given string.
|
|
118
119
|
"""
|
|
119
120
|
return self.pexpect_proc.expect(pattern, **kwargs)
|
|
120
121
|
|
|
121
122
|
@_pexpect_func # noqa
|
|
122
123
|
def expect_exact(self, pattern, **kwargs) -> Match: # noqa
|
|
123
124
|
"""
|
|
124
|
-
Expect the `pattern` from the internal buffer. All the arguments
|
|
125
|
+
Expect the `pattern` from the internal buffer. All the arguments will be passed to `pexpect.expect_exact()`.
|
|
125
126
|
|
|
126
127
|
Args:
|
|
127
128
|
pattern: string, or a list of string
|
|
@@ -132,9 +133,10 @@ class Dut:
|
|
|
132
133
|
Otherwise match any of them could pass
|
|
133
134
|
|
|
134
135
|
Returns:
|
|
135
|
-
|
|
136
|
+
`AnyStr` or `re.Match`
|
|
136
137
|
|
|
137
|
-
|
|
138
|
+
- `AnyStr`: if you're matching `pexpect.EOF` or `pexpect.TIMEOUT` to get all the current buffers.
|
|
139
|
+
- `re.Match`: if matched given string.
|
|
138
140
|
"""
|
|
139
141
|
return self.pexpect_proc.expect_exact(pattern, **kwargs)
|
|
140
142
|
|
|
@@ -147,7 +149,7 @@ class Dut:
|
|
|
147
149
|
"""
|
|
148
150
|
Expect a unity test summary block and parse the output into junit report.
|
|
149
151
|
|
|
150
|
-
Would combine the junit report into the main one if you use
|
|
152
|
+
Would combine the junit report into the main one if you use ``pytest --junitxml`` feature.
|
|
151
153
|
|
|
152
154
|
Args:
|
|
153
155
|
remove_asci_escape_code: remove asci escape code in the message field. (default: True)
|
|
@@ -155,12 +157,12 @@ class Dut:
|
|
|
155
157
|
extra_before: would append before the expected bytes.
|
|
156
158
|
Use this argument when need to run `expect` functions between one unity test call.
|
|
157
159
|
|
|
158
|
-
|
|
160
|
+
Note:
|
|
159
161
|
- Would raise AssertionError at the end of the test if any unity test case result is "FAIL"
|
|
160
|
-
- Would raise TIMEOUT exception at the end of the test
|
|
161
|
-
than timeout value
|
|
162
|
+
- Would raise TIMEOUT exception at the end of the test \
|
|
163
|
+
if any unity test case execution took longer than timeout value
|
|
162
164
|
|
|
163
|
-
|
|
165
|
+
Warning:
|
|
164
166
|
- All unity test cases record would be missed if the final report block is uncaught.
|
|
165
167
|
"""
|
|
166
168
|
self.expect(UNITY_SUMMARY_LINE_REGEX, timeout=timeout)
|
|
@@ -174,3 +176,25 @@ class Dut:
|
|
|
174
176
|
log = remove_asci_color_code(log)
|
|
175
177
|
|
|
176
178
|
self.testsuite.add_unity_test_cases(log)
|
|
179
|
+
|
|
180
|
+
@_InjectMixinCls.require_services('idf')
|
|
181
|
+
def run_all_single_board_cases(
|
|
182
|
+
self,
|
|
183
|
+
group: Optional[str] = None,
|
|
184
|
+
reset: bool = False,
|
|
185
|
+
timeout: float = 30,
|
|
186
|
+
run_ignore_cases: bool = False,
|
|
187
|
+
) -> None:
|
|
188
|
+
"""
|
|
189
|
+
Run all multi_stage cases
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
group: test case group
|
|
193
|
+
reset: whether to perform a hardware reset before running a case
|
|
194
|
+
timeout: timeout. (Default: 30 seconds)
|
|
195
|
+
run_ignore_cases: run ignored test cases or not
|
|
196
|
+
|
|
197
|
+
Warning:
|
|
198
|
+
requires enable service ``idf``
|
|
199
|
+
"""
|
|
200
|
+
pass
|
|
@@ -55,17 +55,15 @@ class PexpectProcess(pexpect.fdpexpect.fdspawn):
|
|
|
55
55
|
placeholder=f'... (total {len(self.buffer)} bytes)',
|
|
56
56
|
)
|
|
57
57
|
|
|
58
|
-
def read_nonblocking(self, size=1, timeout
|
|
58
|
+
def read_nonblocking(self, size: int = 1, timeout: int = -1) -> bytes:
|
|
59
59
|
"""
|
|
60
60
|
Since we're using real file stream, here we only raise an EOF error when the file stream has been closed.
|
|
61
61
|
This could solve the `os.read()` blocked issue.
|
|
62
62
|
|
|
63
|
-
:return: String containing the bytes read
|
|
64
|
-
|
|
65
63
|
Args:
|
|
66
|
-
size
|
|
67
|
-
timeout
|
|
68
|
-
ready to read. When -1 (default), use self.timeout
|
|
64
|
+
size: Read at most *size* bytes.
|
|
65
|
+
timeout: Wait timeout seconds for file descriptor to be
|
|
66
|
+
ready to read. When -1 (default), use `self.timeout`. When 0, poll.
|
|
69
67
|
|
|
70
68
|
Returns:
|
|
71
69
|
String containing the bytes read
|
|
@@ -115,7 +113,7 @@ def live_print_call(*args, msg_queue: Optional[MessageQueue] = None, expect_retu
|
|
|
115
113
|
msg_queue: `MessageQueue` instance, would redirect to message queue instead of sys.stdout if specified
|
|
116
114
|
expect_returncode: expect return code. (Default 0). Would raise exception when return code is different
|
|
117
115
|
|
|
118
|
-
|
|
116
|
+
Note:
|
|
119
117
|
This function behaves the same as `subprocess.call()`, it would block your current process.
|
|
120
118
|
"""
|
|
121
119
|
default_kwargs = {
|
|
@@ -210,12 +208,11 @@ class DuplicateStdoutPopen(subprocess.Popen):
|
|
|
210
208
|
super().terminate()
|
|
211
209
|
|
|
212
210
|
def write(self, s: AnyStr) -> None:
|
|
213
|
-
"""
|
|
211
|
+
r"""
|
|
214
212
|
Write to `stdin` via `stdin.write`.
|
|
215
213
|
|
|
216
|
-
If the input is `str`, will encode to `bytes` and add a b'\\n' automatically in the end.
|
|
217
|
-
|
|
218
|
-
if the input is `bytes`, will pass this directly.
|
|
214
|
+
- If the input is `str`, will encode to `bytes` and add a b'\\n' automatically in the end.
|
|
215
|
+
- If the input is `bytes`, will pass this directly.
|
|
219
216
|
|
|
220
217
|
Args:
|
|
221
218
|
s: bytes or str
|
|
@@ -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
|
###########
|
|
@@ -332,7 +341,7 @@ def multi_dut_fixture(func) -> t.Callable[..., t.Union[t.Any, t.Tuple[t.Any]]]:
|
|
|
332
341
|
"""
|
|
333
342
|
Apply the multi-dut arguments to each fixture.
|
|
334
343
|
|
|
335
|
-
|
|
344
|
+
Note:
|
|
336
345
|
Run the `func(*args, **kwargs)` for multiple times by iterating all `kwargs` via `itemgetter`
|
|
337
346
|
|
|
338
347
|
For example:
|
|
@@ -378,7 +387,7 @@ def multi_dut_generator_fixture(
|
|
|
378
387
|
"""
|
|
379
388
|
Apply the multi-dut arguments to each fixture.
|
|
380
389
|
|
|
381
|
-
|
|
390
|
+
Note:
|
|
382
391
|
Run the `func()` for multiple times by iterating all `kwargs` via `itemgetter`. Auto call `close()` or
|
|
383
392
|
`terminate()` method of the object after it yield back.
|
|
384
393
|
|
|
@@ -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
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import dataclasses
|
|
2
|
+
import functools
|
|
2
3
|
import importlib
|
|
3
4
|
import logging
|
|
4
5
|
import os
|
|
@@ -33,6 +34,12 @@ FIXTURES_SERVICES = {
|
|
|
33
34
|
|
|
34
35
|
_T = t.TypeVar('_T')
|
|
35
36
|
|
|
37
|
+
_MIXIN_REQUIRED_SERVICES = {
|
|
38
|
+
'IdfUnityMixin': ['idf'],
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_MIXIN_REQUIRED_SERVICES_KEY = '_based_on_services'
|
|
42
|
+
|
|
36
43
|
|
|
37
44
|
#######################
|
|
38
45
|
# Errors and Warnings #
|
|
@@ -54,6 +61,16 @@ class PackageNotInstalledError(SystemExit):
|
|
|
54
61
|
)
|
|
55
62
|
|
|
56
63
|
|
|
64
|
+
class RequireServiceError(SystemExit):
|
|
65
|
+
def __init__(self, func_name: str, services: t.Union[str, t.List[str]]) -> None:
|
|
66
|
+
services_str = ','.join(to_list(services))
|
|
67
|
+
super().__init__(
|
|
68
|
+
f'function {func_name} requires enabling one of the service(s) {services_str}. '
|
|
69
|
+
f'Please enable by passing CLI options "--embedded-services {services_str}". '
|
|
70
|
+
f'For more details, please refer to "pytest --help".'
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
57
74
|
#####################
|
|
58
75
|
# Utility Functions #
|
|
59
76
|
#####################
|
|
@@ -101,10 +118,11 @@ def to_list(s: _T) -> t.List[_T]:
|
|
|
101
118
|
s: Anything
|
|
102
119
|
|
|
103
120
|
Returns:
|
|
104
|
-
List (list[_T])
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
121
|
+
List (list[_T])
|
|
122
|
+
|
|
123
|
+
- `list(s)` (List. If `s` is a tuple or a set.
|
|
124
|
+
- itself. If `s` is a list.
|
|
125
|
+
- `[s]`. If `s` is other types.
|
|
108
126
|
"""
|
|
109
127
|
if not s:
|
|
110
128
|
return s
|
|
@@ -215,22 +233,23 @@ def lazy_load(
|
|
|
215
233
|
Returns:
|
|
216
234
|
__getattr__ function
|
|
217
235
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
236
|
+
Example:
|
|
237
|
+
|
|
238
|
+
::
|
|
239
|
+
|
|
240
|
+
__getattr__ = lazy_load(
|
|
241
|
+
importlib.import_module(__name__),
|
|
242
|
+
{
|
|
243
|
+
'IdfApp': IdfApp,
|
|
244
|
+
'LinuxDut': LinuxDut,
|
|
245
|
+
'LinuxSerial': LinuxSerial,
|
|
246
|
+
'CaseTester': CaseTester,
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
'IdfSerial': '.serial',
|
|
250
|
+
'IdfDut': '.dut',
|
|
251
|
+
},
|
|
252
|
+
)
|
|
234
253
|
"""
|
|
235
254
|
|
|
236
255
|
def __getattr__(object_name):
|
|
@@ -245,3 +264,83 @@ def lazy_load(
|
|
|
245
264
|
raise AttributeError('Attribute %s not found in module %s', object_name, base_module.__name__)
|
|
246
265
|
|
|
247
266
|
return __getattr__
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class _InjectMixinMeta(type):
|
|
270
|
+
def __call__(cls, *args, **kwargs):
|
|
271
|
+
try:
|
|
272
|
+
mixins = kwargs.pop('mixins', None)
|
|
273
|
+
if mixins:
|
|
274
|
+
mixins = to_list(mixins)
|
|
275
|
+
name = cls.__name__ + 'With' + 'And'.join([m.__name__ for m in mixins])
|
|
276
|
+
cls = type(name, tuple([*mixins, cls]), {})
|
|
277
|
+
|
|
278
|
+
# users should only know concept "services", not mixins
|
|
279
|
+
_based_on_services = set()
|
|
280
|
+
for m in mixins:
|
|
281
|
+
if m.__name__ not in _MIXIN_REQUIRED_SERVICES:
|
|
282
|
+
continue
|
|
283
|
+
|
|
284
|
+
_based_on_services.update(to_list(_MIXIN_REQUIRED_SERVICES[m.__name__]))
|
|
285
|
+
kwargs[_MIXIN_REQUIRED_SERVICES_KEY] = sorted(_based_on_services)
|
|
286
|
+
except KeyError:
|
|
287
|
+
pass
|
|
288
|
+
return type.__call__(cls, *args, **kwargs)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class _InjectMixinCls(metaclass=_InjectMixinMeta):
|
|
292
|
+
"""
|
|
293
|
+
This class provide a check function `require_services()` to check if the function is injected by
|
|
294
|
+
enabling one of the required services.
|
|
295
|
+
|
|
296
|
+
The benefits are:
|
|
297
|
+
- provide the autocompletion for the functions provided by the mixins
|
|
298
|
+
- check the requirement at runtime
|
|
299
|
+
|
|
300
|
+
Example:
|
|
301
|
+
|
|
302
|
+
::
|
|
303
|
+
|
|
304
|
+
class IdfUnityMixin:
|
|
305
|
+
def foo(self):
|
|
306
|
+
print('foo from MixinOne')
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class Test(_InjectMixinCls):
|
|
310
|
+
def __init__(self, **kwargs):
|
|
311
|
+
for k, v in kwargs.items():
|
|
312
|
+
setattr(self, k, v)
|
|
313
|
+
|
|
314
|
+
@_InjectMixinCls.require_services('idf')
|
|
315
|
+
def foo(self):
|
|
316
|
+
pass
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
# mro(): TestWithIdfUnityMixin, IdfUnityMixin, Test, object
|
|
320
|
+
s1 = Test(mixins=IdfUnityMixin)
|
|
321
|
+
# mro(): Test, object
|
|
322
|
+
s2 = Test()
|
|
323
|
+
s1.foo() # foo from IdfUnityMixin
|
|
324
|
+
s2.foo() # function foo requires enabling one of the service(s) idf.
|
|
325
|
+
"""
|
|
326
|
+
|
|
327
|
+
def require_services(*services):
|
|
328
|
+
def decorator(func):
|
|
329
|
+
@functools.wraps(func)
|
|
330
|
+
def wrapped(self, *args, **kwargs):
|
|
331
|
+
based = False
|
|
332
|
+
for service in services:
|
|
333
|
+
if hasattr(self, _MIXIN_REQUIRED_SERVICES_KEY) and service in getattr(
|
|
334
|
+
self, _MIXIN_REQUIRED_SERVICES_KEY
|
|
335
|
+
):
|
|
336
|
+
based = True
|
|
337
|
+
break
|
|
338
|
+
|
|
339
|
+
if based:
|
|
340
|
+
return func(self, *args, **kwargs)
|
|
341
|
+
else:
|
|
342
|
+
raise RequireServiceError(func.__name__, services)
|
|
343
|
+
|
|
344
|
+
return wrapped
|
|
345
|
+
|
|
346
|
+
return decorator
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 1.2
|
|
2
2
|
Name: pytest-embedded
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.1
|
|
4
4
|
Summary: pytest embedded plugin
|
|
5
5
|
Home-page: https://docs.espressif.com/projects/pytest-embedded/en/latest/
|
|
6
6
|
Author: Fu Hanxi
|
|
@@ -10,10 +10,6 @@ Description: ### pytest-embedded
|
|
|
10
10
|
|
|
11
11
|
A pytest plugin for embedded systems. Could activate different services for extra functionalities.
|
|
12
12
|
|
|
13
|
-
Used CLI Options:
|
|
14
|
-
|
|
15
|
-
- `app_path`
|
|
16
|
-
|
|
17
13
|
Platform: UNKNOWN
|
|
18
14
|
Classifier: Framework :: Pytest
|
|
19
15
|
Classifier: Intended Audience :: Developers
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest-embedded-1.2.5 → pytest-embedded-1.3.1}/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
|