hardpy 0.2.0__tar.gz → 0.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.
- {hardpy-0.2.0 → hardpy-0.3.0}/PKG-INFO +8 -1
- {hardpy-0.2.0 → hardpy-0.3.0}/README.md +7 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/db/base_store.py +21 -11
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/plugin.py +68 -4
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/pytest_call.py +16 -8
- hardpy-0.3.0/hardpy/pytest_hardpy/reporter/base.py +60 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/reporter/hook_reporter.py +28 -37
- hardpy-0.3.0/hardpy/pytest_hardpy/utils/node_info.py +125 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/pyproject.toml +1 -1
- hardpy-0.2.0/hardpy/pytest_hardpy/reporter/base.py +0 -42
- hardpy-0.2.0/hardpy/pytest_hardpy/utils/node_info.py +0 -59
- {hardpy-0.2.0 → hardpy-0.3.0}/.gitignore +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/LICENSE +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/__init__.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/__init__.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/api.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/asset-manifest.json +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/favicon.ico +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/index.html +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/logo512.png +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/manifest.json +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/css/main.e8a862f1.css +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/css/main.e8a862f1.css.map +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/808.ce070002.chunk.js +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/808.ce070002.chunk.js.map +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-16px-paths.d605910e.chunk.js +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-16px-paths.d605910e.chunk.js.map +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-20px-paths.7ee05cc8.chunk.js +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-20px-paths.7ee05cc8.chunk.js.map +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-all-paths-loader.0aa89747.chunk.js +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-all-paths-loader.0aa89747.chunk.js.map +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-all-paths.f63155c9.chunk.js +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-all-paths.f63155c9.chunk.js.map +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-split-paths-by-size-loader.52a072d3.chunk.js +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-split-paths-by-size-loader.52a072d3.chunk.js.map +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/main.8ef63e9b.js +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/main.8ef63e9b.js.LICENSE.txt +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/main.8ef63e9b.js.map +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.520846c6beb41df528c8.eot +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.5c52b39c697f2323ce8b.svg +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.84db1772f4bfb529f64f.woff +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.b67ee1736e20e37a3225.woff2 +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.e02ecf515378db143652.ttf +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.429cacb8accf72488451.ttf +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.6ae3791ee2d86fc228a6.svg +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.8cecf62de42997e4d82f.woff2 +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.afbadb627d43b7857223.eot +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.e857f5a5132b8bfa71a1.woff +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/media/logo_smol.5b16f92447a4a9e80331.png +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/runner.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/__init__.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/db/__init__.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/db/base_connector.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/db/base_server.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/db/const.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/db/runstore.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/db/schema.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/db/statestore.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/pytest_wrapper.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/reporter/__init__.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/reporter/runner_reporter.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/result/__init__.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/result/couchdb_config.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/result/report_loader/__init__.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/result/report_loader/couchdb_loader.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/result/report_reader/__init__.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/result/report_reader/couchdb_reader.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/utils/__init__.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/utils/config_data.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/utils/const.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/utils/exception.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/utils/progress_calculator.py +0 -0
- {hardpy-0.2.0 → hardpy-0.3.0}/hardpy/pytest_hardpy/utils/singleton.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hardpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: HardPy library for device testing
|
|
5
5
|
Project-URL: repository, https://github.com/everypindevices/hardpy
|
|
6
6
|
Author: Everypin
|
|
@@ -84,6 +84,7 @@ For Windows, follow the instructions from the
|
|
|
84
84
|
[documentation](https://everypinio.github.io/hardpy/documentation/database/#couchdb-instance).
|
|
85
85
|
|
|
86
86
|
Launch CouchDB with Docker.
|
|
87
|
+
The Docker version must be 24.0.0 or higher.
|
|
87
88
|
Create `couchdb.ini` file:
|
|
88
89
|
|
|
89
90
|
```ini
|
|
@@ -103,6 +104,12 @@ Run the Docker container from folder with couchdb.ini file:
|
|
|
103
104
|
docker run --rm --name couchdb -p 5984:5984 -e COUCHDB_USER=dev -e COUCHDB_PASSWORD=dev -v ./couchdb.ini:/opt/couchdb/etc/local.ini couchdb:3.3
|
|
104
105
|
```
|
|
105
106
|
|
|
107
|
+
Command for Windows:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
docker run --rm --name couchdb -p 5984:5984 -e COUCHDB_USER=dev -e COUCHDB_PASSWORD=dev -v .\couchdb.ini:/opt/couchdb/etc/local.ini couchdb:3.3.2
|
|
111
|
+
```
|
|
112
|
+
|
|
106
113
|
#### Test steps
|
|
107
114
|
|
|
108
115
|
Add simple test to `tests` folder
|
|
@@ -47,6 +47,7 @@ For Windows, follow the instructions from the
|
|
|
47
47
|
[documentation](https://everypinio.github.io/hardpy/documentation/database/#couchdb-instance).
|
|
48
48
|
|
|
49
49
|
Launch CouchDB with Docker.
|
|
50
|
+
The Docker version must be 24.0.0 or higher.
|
|
50
51
|
Create `couchdb.ini` file:
|
|
51
52
|
|
|
52
53
|
```ini
|
|
@@ -66,6 +67,12 @@ Run the Docker container from folder with couchdb.ini file:
|
|
|
66
67
|
docker run --rm --name couchdb -p 5984:5984 -e COUCHDB_USER=dev -e COUCHDB_PASSWORD=dev -v ./couchdb.ini:/opt/couchdb/etc/local.ini couchdb:3.3
|
|
67
68
|
```
|
|
68
69
|
|
|
70
|
+
Command for Windows:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
docker run --rm --name couchdb -p 5984:5984 -e COUCHDB_USER=dev -e COUCHDB_PASSWORD=dev -v .\couchdb.ini:/opt/couchdb/etc/local.ini couchdb:3.3.2
|
|
74
|
+
```
|
|
75
|
+
|
|
69
76
|
#### Test steps
|
|
70
77
|
|
|
71
78
|
Add simple test to `tests` folder
|
|
@@ -36,19 +36,29 @@ class BaseStore(BaseConnector):
|
|
|
36
36
|
"""
|
|
37
37
|
return glom(self._doc, key)
|
|
38
38
|
|
|
39
|
-
def
|
|
40
|
-
"""
|
|
41
|
-
|
|
39
|
+
def update_doc(self, key: str, value):
|
|
40
|
+
"""Update document.
|
|
41
|
+
|
|
42
|
+
HardPy collecting uses a simple key without dots.
|
|
43
|
+
Assign is used to update a document.
|
|
44
|
+
Assign is a longer function.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
key (str): document key
|
|
48
|
+
value: document value
|
|
49
|
+
"""
|
|
50
|
+
if "." in key:
|
|
51
|
+
assign(self._doc, key, value)
|
|
52
|
+
else:
|
|
53
|
+
self._doc[key] = value
|
|
54
|
+
|
|
55
|
+
def update_db(self):
|
|
56
|
+
"""Update database by current document."""
|
|
42
57
|
try:
|
|
43
58
|
self._doc = self._db.save(self._doc)
|
|
44
|
-
except Conflict
|
|
45
|
-
self.
|
|
46
|
-
|
|
47
|
-
f"when trying to save key={key}, value={value}. "
|
|
48
|
-
"Current document will be changed by "
|
|
49
|
-
"document from database."
|
|
50
|
-
)
|
|
51
|
-
self._doc = self._db.get(self._doc_id)
|
|
59
|
+
except Conflict:
|
|
60
|
+
self._doc["_rev"] = self._db.get(self._doc_id)["_rev"]
|
|
61
|
+
self._doc = self._db.save(self._doc)
|
|
52
62
|
|
|
53
63
|
def get_document(self) -> ModelMetaclass:
|
|
54
64
|
"""Get document by schema.
|
|
@@ -9,6 +9,7 @@ from platform import system
|
|
|
9
9
|
|
|
10
10
|
from natsort import natsorted
|
|
11
11
|
from pytest import (
|
|
12
|
+
skip,
|
|
12
13
|
exit,
|
|
13
14
|
TestReport,
|
|
14
15
|
Item,
|
|
@@ -27,6 +28,7 @@ from hardpy.pytest_hardpy.utils import (
|
|
|
27
28
|
ProgressCalculator,
|
|
28
29
|
ConfigData,
|
|
29
30
|
)
|
|
31
|
+
from hardpy.pytest_hardpy.utils.node_info import TestDependencyInfo
|
|
30
32
|
|
|
31
33
|
|
|
32
34
|
def pytest_addoption(parser: Parser):
|
|
@@ -40,6 +42,7 @@ def pytest_addoption(parser: Parser):
|
|
|
40
42
|
parser.addoption("--hardpy-pt", action="store_true", default=False, help="enable pytest-hardpy plugin") # noqa: E501
|
|
41
43
|
# fmt: on
|
|
42
44
|
|
|
45
|
+
|
|
43
46
|
# Bootstrapping hooks
|
|
44
47
|
def pytest_load_initial_conftests(early_config, parser, args):
|
|
45
48
|
if "--hardpy-pt" in args:
|
|
@@ -57,6 +60,7 @@ class HardpyPlugin(object):
|
|
|
57
60
|
self._progress = ProgressCalculator()
|
|
58
61
|
self._results = {}
|
|
59
62
|
self._post_run_functions: list[Callable] = []
|
|
63
|
+
self._dependencies = {}
|
|
60
64
|
|
|
61
65
|
if system() == "Linux":
|
|
62
66
|
signal.signal(signal.SIGTERM, self._stop_handler)
|
|
@@ -64,7 +68,6 @@ class HardpyPlugin(object):
|
|
|
64
68
|
signal.signal(signal.SIGBREAK, self._stop_handler)
|
|
65
69
|
self._log = getLogger(__name__)
|
|
66
70
|
|
|
67
|
-
|
|
68
71
|
# Initialization hooks
|
|
69
72
|
|
|
70
73
|
def pytest_configure(self, config: Config):
|
|
@@ -77,6 +80,7 @@ class HardpyPlugin(object):
|
|
|
77
80
|
|
|
78
81
|
config.addinivalue_line("markers", "case_name")
|
|
79
82
|
config.addinivalue_line("markers", "module_name")
|
|
83
|
+
config.addinivalue_line("markers", "dependency")
|
|
80
84
|
|
|
81
85
|
# must be init after config data is set
|
|
82
86
|
self._reporter = HookReporter()
|
|
@@ -92,6 +96,8 @@ class HardpyPlugin(object):
|
|
|
92
96
|
return
|
|
93
97
|
status = self._get_run_status(exitstatus)
|
|
94
98
|
self._reporter.finish(status)
|
|
99
|
+
self._reporter.update_db_by_doc()
|
|
100
|
+
self._reporter.compact_all()
|
|
95
101
|
|
|
96
102
|
# call post run methods
|
|
97
103
|
if self._post_run_functions:
|
|
@@ -107,6 +113,7 @@ class HardpyPlugin(object):
|
|
|
107
113
|
self._reporter.init_doc(str(PurePath(config.rootpath).name))
|
|
108
114
|
|
|
109
115
|
nodes = {}
|
|
116
|
+
modules = set()
|
|
110
117
|
|
|
111
118
|
session.items = natsorted(
|
|
112
119
|
session.items,
|
|
@@ -115,18 +122,26 @@ class HardpyPlugin(object):
|
|
|
115
122
|
for item in session.items:
|
|
116
123
|
if item.parent is None:
|
|
117
124
|
continue
|
|
118
|
-
|
|
125
|
+
try:
|
|
126
|
+
node_info = NodeInfo(item)
|
|
127
|
+
except ValueError:
|
|
128
|
+
error_msg = f"Error creating NodeInfo for item: {item}\n"
|
|
129
|
+
exit(error_msg, 1)
|
|
119
130
|
|
|
120
131
|
self._init_case_result(node_info.module_id, node_info.case_id)
|
|
121
|
-
|
|
122
132
|
if node_info.module_id not in nodes:
|
|
123
133
|
nodes[node_info.module_id] = [node_info.case_id]
|
|
124
134
|
else:
|
|
125
135
|
nodes[node_info.module_id].append(node_info.case_id)
|
|
126
136
|
|
|
127
137
|
self._reporter.add_case(node_info)
|
|
128
|
-
|
|
138
|
+
|
|
139
|
+
self._add_dependency(node_info, nodes)
|
|
140
|
+
modules.add(node_info.module_id)
|
|
141
|
+
for module_id in modules:
|
|
142
|
+
self._reporter.set_module_status(module_id, TestStatus.READY)
|
|
129
143
|
self._reporter.update_node_order(nodes)
|
|
144
|
+
self._reporter.update_db_by_doc()
|
|
130
145
|
|
|
131
146
|
# Test running (runtest) hooks
|
|
132
147
|
|
|
@@ -139,6 +154,7 @@ class HardpyPlugin(object):
|
|
|
139
154
|
|
|
140
155
|
# testrun entrypoint
|
|
141
156
|
self._reporter.start()
|
|
157
|
+
self._reporter.update_db_by_doc()
|
|
142
158
|
|
|
143
159
|
def pytest_runtest_setup(self, item: Item):
|
|
144
160
|
"""Call before each test setup phase."""
|
|
@@ -148,6 +164,8 @@ class HardpyPlugin(object):
|
|
|
148
164
|
|
|
149
165
|
node_info = NodeInfo(item)
|
|
150
166
|
|
|
167
|
+
self._handle_dependency(node_info)
|
|
168
|
+
|
|
151
169
|
self._reporter.set_module_status(node_info.module_id, TestStatus.RUN)
|
|
152
170
|
self._reporter.set_module_start_time(node_info.module_id)
|
|
153
171
|
self._reporter.set_case_status(
|
|
@@ -159,6 +177,7 @@ class HardpyPlugin(object):
|
|
|
159
177
|
node_info.module_id,
|
|
160
178
|
node_info.case_id,
|
|
161
179
|
)
|
|
180
|
+
self._reporter.update_db_by_doc()
|
|
162
181
|
|
|
163
182
|
# Reporting hooks
|
|
164
183
|
|
|
@@ -188,6 +207,7 @@ class HardpyPlugin(object):
|
|
|
188
207
|
|
|
189
208
|
if None not in self._results[module_id].values():
|
|
190
209
|
self._collect_module_result(module_id)
|
|
210
|
+
self._reporter.update_db_by_doc()
|
|
191
211
|
|
|
192
212
|
# Fixture
|
|
193
213
|
|
|
@@ -250,3 +270,47 @@ class HardpyPlugin(object):
|
|
|
250
270
|
index = report.find("\nE")
|
|
251
271
|
return report[:index]
|
|
252
272
|
return None
|
|
273
|
+
|
|
274
|
+
def _handle_dependency(self, node_info: NodeInfo):
|
|
275
|
+
dependency = self._dependencies.get(
|
|
276
|
+
TestDependencyInfo(
|
|
277
|
+
node_info.module_id,
|
|
278
|
+
node_info.case_id,
|
|
279
|
+
)
|
|
280
|
+
)
|
|
281
|
+
if dependency and self._is_dependency_failed(dependency):
|
|
282
|
+
self._log.debug(f"Skipping test due to dependency: {dependency}")
|
|
283
|
+
self._results[node_info.module_id][node_info.case_id] = TestStatus.SKIPPED
|
|
284
|
+
skip(f"Test {node_info.module_id}::{node_info.case_id} is skipped")
|
|
285
|
+
|
|
286
|
+
def _is_dependency_failed(self, dependency) -> bool:
|
|
287
|
+
if isinstance(dependency, TestDependencyInfo):
|
|
288
|
+
incorrect_status = {
|
|
289
|
+
TestStatus.FAILED,
|
|
290
|
+
TestStatus.SKIPPED,
|
|
291
|
+
TestStatus.ERROR,
|
|
292
|
+
}
|
|
293
|
+
module_id, case_id = dependency
|
|
294
|
+
if case_id is not None:
|
|
295
|
+
return self._results[module_id][case_id] in incorrect_status
|
|
296
|
+
return any(
|
|
297
|
+
status in incorrect_status
|
|
298
|
+
for status in set(self._results[module_id].values())
|
|
299
|
+
)
|
|
300
|
+
return False
|
|
301
|
+
|
|
302
|
+
def _add_dependency(self, node_info, nodes):
|
|
303
|
+
dependency = node_info.dependency
|
|
304
|
+
if dependency is None or dependency == "":
|
|
305
|
+
return
|
|
306
|
+
module_id, case_id = dependency
|
|
307
|
+
if module_id not in nodes:
|
|
308
|
+
error_message = f"Error: Module dependency '{dependency}' not found."
|
|
309
|
+
exit(error_message, 1)
|
|
310
|
+
elif case_id not in nodes[module_id] and case_id is not None:
|
|
311
|
+
error_message = f"Error: Case dependency '{dependency}' not found."
|
|
312
|
+
exit(error_message, 1)
|
|
313
|
+
|
|
314
|
+
self._dependencies[
|
|
315
|
+
TestDependencyInfo(node_info.module_id, node_info.case_id)
|
|
316
|
+
] = dependency
|
|
@@ -52,7 +52,8 @@ def set_dut_info(info: dict):
|
|
|
52
52
|
reporter = RunnerReporter()
|
|
53
53
|
for dut_key, dut_value in info.items():
|
|
54
54
|
key = reporter.generate_key(DF.DUT, DF.INFO, dut_key)
|
|
55
|
-
reporter.
|
|
55
|
+
reporter.set_doc_value(key, dut_value)
|
|
56
|
+
reporter.update_db_by_doc()
|
|
56
57
|
|
|
57
58
|
|
|
58
59
|
def set_dut_serial_number(serial_number: str):
|
|
@@ -68,7 +69,8 @@ def set_dut_serial_number(serial_number: str):
|
|
|
68
69
|
key = reporter.generate_key(DF.DUT, DF.SERIAL_NUMBER)
|
|
69
70
|
if reporter.get_field(key):
|
|
70
71
|
raise DuplicateSerialNumberError
|
|
71
|
-
reporter.
|
|
72
|
+
reporter.set_doc_value(key, serial_number)
|
|
73
|
+
reporter.update_db_by_doc()
|
|
72
74
|
|
|
73
75
|
|
|
74
76
|
def set_stand_info(info: dict):
|
|
@@ -80,7 +82,8 @@ def set_stand_info(info: dict):
|
|
|
80
82
|
reporter = RunnerReporter()
|
|
81
83
|
for stand_key, stand_value in info.items():
|
|
82
84
|
key = reporter.generate_key(DF.TEST_STAND, stand_key)
|
|
83
|
-
reporter.
|
|
85
|
+
reporter.set_doc_value(key, stand_value)
|
|
86
|
+
reporter.update_db_by_doc()
|
|
84
87
|
|
|
85
88
|
|
|
86
89
|
def set_message(msg: str, msg_key: Optional[str] = None) -> None:
|
|
@@ -111,7 +114,8 @@ def set_message(msg: str, msg_key: Optional[str] = None) -> None:
|
|
|
111
114
|
|
|
112
115
|
msgs[msg_key] = msg
|
|
113
116
|
|
|
114
|
-
reporter.
|
|
117
|
+
reporter.set_doc_value(key, msgs)
|
|
118
|
+
reporter.update_db_by_doc()
|
|
115
119
|
|
|
116
120
|
|
|
117
121
|
def set_case_artifact(data: dict):
|
|
@@ -134,7 +138,8 @@ def set_case_artifact(data: dict):
|
|
|
134
138
|
DF.ARTIFACT,
|
|
135
139
|
stand_key,
|
|
136
140
|
)
|
|
137
|
-
reporter.
|
|
141
|
+
reporter.set_doc_value(key, stand_value, runstore_only=True)
|
|
142
|
+
reporter.update_db_by_doc()
|
|
138
143
|
|
|
139
144
|
|
|
140
145
|
def set_module_artifact(data: dict):
|
|
@@ -155,7 +160,8 @@ def set_module_artifact(data: dict):
|
|
|
155
160
|
DF.ARTIFACT,
|
|
156
161
|
artifact_key,
|
|
157
162
|
)
|
|
158
|
-
reporter.
|
|
163
|
+
reporter.set_doc_value(key, artifact_value, runstore_only=True)
|
|
164
|
+
reporter.update_db_by_doc()
|
|
159
165
|
|
|
160
166
|
|
|
161
167
|
def set_run_artifact(data: dict):
|
|
@@ -173,7 +179,8 @@ def set_run_artifact(data: dict):
|
|
|
173
179
|
DF.ARTIFACT,
|
|
174
180
|
artifact_key,
|
|
175
181
|
)
|
|
176
|
-
reporter.
|
|
182
|
+
reporter.set_doc_value(key, artifact_value, runstore_only=True)
|
|
183
|
+
reporter.update_db_by_doc()
|
|
177
184
|
|
|
178
185
|
|
|
179
186
|
def set_driver_info(drivers: dict) -> None:
|
|
@@ -192,7 +199,8 @@ def set_driver_info(drivers: dict) -> None:
|
|
|
192
199
|
DF.DRIVERS,
|
|
193
200
|
driver_name,
|
|
194
201
|
)
|
|
195
|
-
reporter.
|
|
202
|
+
reporter.set_doc_value(key, driver_data)
|
|
203
|
+
reporter.update_db_by_doc()
|
|
196
204
|
|
|
197
205
|
|
|
198
206
|
def _get_current_test() -> CurrentTestInfo:
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Copyright (c) 2024 Everypin
|
|
2
|
+
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
|
+
|
|
4
|
+
from logging import getLogger
|
|
5
|
+
|
|
6
|
+
from hardpy.pytest_hardpy.db import StateStore, RunStore
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseReporter(object):
|
|
10
|
+
"""Base class for test reporter."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self._statestore = StateStore()
|
|
14
|
+
self._runstore = RunStore()
|
|
15
|
+
self._log = getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
def set_doc_value(
|
|
18
|
+
self, key: str, value, runstore_only=False, statestore_only=False
|
|
19
|
+
):
|
|
20
|
+
"""Set value to the document.
|
|
21
|
+
|
|
22
|
+
Update a document without writing to the database.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
key (str): document key
|
|
26
|
+
value: document value
|
|
27
|
+
runstore_only (bool, optional): indicates whether data should
|
|
28
|
+
be written to RunStore. Defaults to True.
|
|
29
|
+
statestore_only (bool, optional): indicates whether data should
|
|
30
|
+
be written to StateStore. Defaults to True.
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
ValueError: if both runstore_only and statestore_only are True
|
|
34
|
+
"""
|
|
35
|
+
if runstore_only and statestore_only:
|
|
36
|
+
raise ValueError("Both runstore_only and statestore_only cannot be True")
|
|
37
|
+
if runstore_only:
|
|
38
|
+
self._runstore.update_doc(key, value)
|
|
39
|
+
return
|
|
40
|
+
if statestore_only:
|
|
41
|
+
self._statestore.update_doc(key, value)
|
|
42
|
+
return
|
|
43
|
+
self._runstore.update_doc(key, value)
|
|
44
|
+
self._statestore.update_doc(key, value)
|
|
45
|
+
|
|
46
|
+
def update_db_by_doc(self):
|
|
47
|
+
"""Update database by current document."""
|
|
48
|
+
self._statestore.update_db()
|
|
49
|
+
self._runstore.update_db()
|
|
50
|
+
|
|
51
|
+
def generate_key(self, *args) -> str:
|
|
52
|
+
"""Generate key for database.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
args: list of database keys
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
str: database key
|
|
59
|
+
"""
|
|
60
|
+
return ".".join(args)
|
|
@@ -25,23 +25,23 @@ class HookReporter(BaseReporter):
|
|
|
25
25
|
Args:
|
|
26
26
|
doc_name (str): test run name
|
|
27
27
|
"""
|
|
28
|
-
self.
|
|
29
|
-
self.
|
|
30
|
-
self.
|
|
31
|
-
self.
|
|
32
|
-
self.
|
|
33
|
-
self.
|
|
34
|
-
self.
|
|
35
|
-
self.
|
|
28
|
+
self.set_doc_value(DF.NAME, doc_name)
|
|
29
|
+
self.set_doc_value(DF.STATUS, TestStatus.READY)
|
|
30
|
+
self.set_doc_value(DF.START_TIME, None)
|
|
31
|
+
self.set_doc_value(DF.TIMEZONE, None)
|
|
32
|
+
self.set_doc_value(DF.STOP_TIME, None)
|
|
33
|
+
self.set_doc_value(DF.PROGRESS, 0)
|
|
34
|
+
self.set_doc_value(DF.DRIVERS, {})
|
|
35
|
+
self.set_doc_value(DF.ARTIFACT, {}, runstore_only=True)
|
|
36
36
|
|
|
37
37
|
def start(self):
|
|
38
38
|
"""Start test."""
|
|
39
39
|
self._log.debug("Starting test run.")
|
|
40
40
|
start_time = int(time())
|
|
41
|
-
self.
|
|
42
|
-
self.
|
|
43
|
-
self.
|
|
44
|
-
self.
|
|
41
|
+
self.set_doc_value(DF.START_TIME, start_time)
|
|
42
|
+
self.set_doc_value(DF.STATUS, TestStatus.RUN)
|
|
43
|
+
self.set_doc_value(DF.TIMEZONE, tzname) # noqa: WPS432
|
|
44
|
+
self.set_doc_value(DF.PROGRESS, 0)
|
|
45
45
|
|
|
46
46
|
def finish(self, status: RunStatus):
|
|
47
47
|
"""Finish test.
|
|
@@ -50,15 +50,11 @@ class HookReporter(BaseReporter):
|
|
|
50
50
|
"""
|
|
51
51
|
self._log.debug("Finishing test run.")
|
|
52
52
|
stop_time = int(time())
|
|
53
|
-
self.
|
|
54
|
-
self.
|
|
55
|
-
|
|
56
|
-
if self._statestore.get_document():
|
|
57
|
-
self._log.debug("Report StateStore has been successfully validated.")
|
|
58
|
-
|
|
59
|
-
if self._runstore.get_document():
|
|
60
|
-
self._log.debug("Report RunStore has been successfully validated.")
|
|
53
|
+
self.set_doc_value(DF.STOP_TIME, stop_time)
|
|
54
|
+
self.set_doc_value(DF.STATUS, status)
|
|
61
55
|
|
|
56
|
+
def compact_all(self):
|
|
57
|
+
"""Compact all databases"""
|
|
62
58
|
self._statestore.compact()
|
|
63
59
|
self._runstore.compact()
|
|
64
60
|
|
|
@@ -68,7 +64,7 @@ class HookReporter(BaseReporter):
|
|
|
68
64
|
Args:
|
|
69
65
|
progress (int): test progress
|
|
70
66
|
"""
|
|
71
|
-
self.
|
|
67
|
+
self.set_doc_value(DF.PROGRESS, progress)
|
|
72
68
|
|
|
73
69
|
def set_assertion_msg(self, module_id: str, case_id: str, msg: str | None):
|
|
74
70
|
"""Set case assertion message.
|
|
@@ -81,7 +77,7 @@ class HookReporter(BaseReporter):
|
|
|
81
77
|
key = self.generate_key(
|
|
82
78
|
DF.MODULES, module_id, DF.CASES, case_id, DF.ASSERTION_MSG
|
|
83
79
|
)
|
|
84
|
-
self.
|
|
80
|
+
self.set_doc_value(key, msg)
|
|
85
81
|
|
|
86
82
|
def add_case(self, node_info: NodeInfo):
|
|
87
83
|
"""Add test case to document.
|
|
@@ -90,18 +86,15 @@ class HookReporter(BaseReporter):
|
|
|
90
86
|
node_info (NodeInfo): node info
|
|
91
87
|
"""
|
|
92
88
|
key = DF.MODULES
|
|
89
|
+
|
|
93
90
|
item_statestore = self._statestore.get_field(key)
|
|
94
91
|
item_runstore = self._runstore.get_field(key)
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
item_runstore,
|
|
99
|
-
node_info,
|
|
100
|
-
is_use_artifact=True,
|
|
101
|
-
)
|
|
93
|
+
self._init_case(item_statestore, node_info)
|
|
94
|
+
self._init_case(item_runstore, node_info, is_use_artifact=True)
|
|
102
95
|
|
|
103
|
-
self.
|
|
104
|
-
self.
|
|
96
|
+
self.set_doc_value(key, item_statestore, statestore_only=True)
|
|
97
|
+
self.set_doc_value(key, item_runstore, runstore_only=True)
|
|
105
98
|
|
|
106
99
|
def set_case_status(self, module_id: str, case_id: str, status: TestStatus):
|
|
107
100
|
"""Set test case status.
|
|
@@ -112,7 +105,7 @@ class HookReporter(BaseReporter):
|
|
|
112
105
|
status (TestStatus): test case status
|
|
113
106
|
"""
|
|
114
107
|
key = self.generate_key(DF.MODULES, module_id, DF.CASES, case_id, DF.STATUS)
|
|
115
|
-
self.
|
|
108
|
+
self.set_doc_value(key, status)
|
|
116
109
|
|
|
117
110
|
def set_case_start_time(self, module_id: str, case_id: str):
|
|
118
111
|
"""Set test case start_time.
|
|
@@ -142,7 +135,7 @@ class HookReporter(BaseReporter):
|
|
|
142
135
|
status (TestStatus): test module status
|
|
143
136
|
"""
|
|
144
137
|
key = self.generate_key(DF.MODULES, module_id, DF.STATUS)
|
|
145
|
-
self.
|
|
138
|
+
self.set_doc_value(key, status)
|
|
146
139
|
|
|
147
140
|
def set_module_start_time(self, module_id: str):
|
|
148
141
|
"""Set test module status.
|
|
@@ -175,16 +168,16 @@ class HookReporter(BaseReporter):
|
|
|
175
168
|
rm_outdated_nodes = self._remove_outdate_node(old_modules, modules_copy, nodes)
|
|
176
169
|
updated_case_order = self._update_case_order(rm_outdated_nodes, nodes)
|
|
177
170
|
updated_module_order = self._update_module_order(updated_case_order)
|
|
178
|
-
self.
|
|
171
|
+
self.set_doc_value(key, updated_module_order, statestore_only=True)
|
|
179
172
|
|
|
180
173
|
def _set_time(self, key: str):
|
|
181
174
|
current_time = self._statestore.get_field(key)
|
|
182
175
|
if current_time is None:
|
|
183
|
-
self.
|
|
176
|
+
self.set_doc_value(key, int(time()))
|
|
184
177
|
|
|
185
178
|
def _init_case(
|
|
186
179
|
self, item: dict, node_info: NodeInfo, is_use_artifact: bool = False
|
|
187
|
-
)
|
|
180
|
+
):
|
|
188
181
|
module_default = { # noqa: WPS204
|
|
189
182
|
DF.STATUS: TestStatus.READY,
|
|
190
183
|
DF.NAME: self._get_module_name(node_info),
|
|
@@ -216,8 +209,6 @@ class HookReporter(BaseReporter):
|
|
|
216
209
|
case_default[DF.ARTIFACT] = {}
|
|
217
210
|
item[node_info.module_id][DF.CASES][node_info.case_id] = case_default
|
|
218
211
|
|
|
219
|
-
return item
|
|
220
|
-
|
|
221
212
|
def _remove_outdate_node(
|
|
222
213
|
self, old_modules: dict, new_modules: dict, nodes: dict
|
|
223
214
|
) -> dict:
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Copyright (c) 2024 Everypin
|
|
2
|
+
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
from logging import getLogger
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import NamedTuple
|
|
8
|
+
|
|
9
|
+
from pytest import Item, Mark
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestDependencyInfo(NamedTuple):
|
|
13
|
+
"""Test info."""
|
|
14
|
+
|
|
15
|
+
def __repr__(self) -> str:
|
|
16
|
+
return f"Dependency: {self.module_id}::{self.case_id}"
|
|
17
|
+
|
|
18
|
+
module_id: str
|
|
19
|
+
case_id: str | None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class NodeInfo(object):
|
|
23
|
+
"""Test node info."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, item: Item):
|
|
26
|
+
self._item = item
|
|
27
|
+
self._log = getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
self._case_name = self._get_human_name(
|
|
30
|
+
item.own_markers,
|
|
31
|
+
"case_name",
|
|
32
|
+
)
|
|
33
|
+
self._module_name = self._get_human_name(
|
|
34
|
+
item.parent.own_markers,
|
|
35
|
+
"module_name",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
self._dependency = self._get_dependency_info(
|
|
39
|
+
item.own_markers + item.parent.own_markers
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
self._module_id = Path(item.parent.nodeid).stem
|
|
43
|
+
self._case_id = item.name
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def module_id(self):
|
|
47
|
+
"""Get module id.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
str: module id
|
|
51
|
+
"""
|
|
52
|
+
return self._module_id
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def case_id(self):
|
|
56
|
+
"""Get case id.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
str: case id
|
|
60
|
+
"""
|
|
61
|
+
return self._case_id
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def module_name(self):
|
|
65
|
+
"""Get module name.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
str: module name
|
|
69
|
+
"""
|
|
70
|
+
return self._module_name
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def case_name(self):
|
|
74
|
+
"""Get case name.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
str: case name
|
|
78
|
+
"""
|
|
79
|
+
return self._case_name
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def dependency(self):
|
|
83
|
+
"""Get dependency information.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
TestDependencyInfo | str: Parsed dependency information.
|
|
87
|
+
"""
|
|
88
|
+
return self._dependency
|
|
89
|
+
|
|
90
|
+
def _get_human_name(self, markers: list[Mark], marker_name: str) -> str:
|
|
91
|
+
"""Get human name from markers.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
markers (list[Mark]): item markers list
|
|
95
|
+
marker_name (str): marker name
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
str: human name by user
|
|
99
|
+
"""
|
|
100
|
+
for marker in markers:
|
|
101
|
+
if marker.name == marker_name:
|
|
102
|
+
if marker.args:
|
|
103
|
+
return marker.args[0]
|
|
104
|
+
|
|
105
|
+
return ""
|
|
106
|
+
|
|
107
|
+
def _get_dependency_info(self, markers: list[Mark]) -> TestDependencyInfo | str:
|
|
108
|
+
"""Extract and parse dependency information.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
markers (list[Mark]): item markers list
|
|
112
|
+
marker_name (str): marker name
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
TestDependencyInfo | str | None: Parsed dependency information.
|
|
116
|
+
"""
|
|
117
|
+
dependency_value = self._get_human_name(markers, "dependency")
|
|
118
|
+
dependency_data = re.search(r"(\w+)::(\w+)", dependency_value)
|
|
119
|
+
if dependency_data:
|
|
120
|
+
return TestDependencyInfo(*dependency_data.groups())
|
|
121
|
+
elif re.search(r"^\w+$", dependency_value):
|
|
122
|
+
return TestDependencyInfo(dependency_value, None)
|
|
123
|
+
elif dependency_data is None and dependency_value == "":
|
|
124
|
+
return ""
|
|
125
|
+
raise ValueError
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Everypin
|
|
2
|
-
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
|
-
|
|
4
|
-
from logging import getLogger
|
|
5
|
-
|
|
6
|
-
from hardpy.pytest_hardpy.db import StateStore, RunStore
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class BaseReporter(object):
|
|
10
|
-
"""Base class for test reporter."""
|
|
11
|
-
|
|
12
|
-
def __init__(self):
|
|
13
|
-
self._statestore = StateStore()
|
|
14
|
-
self._runstore = RunStore()
|
|
15
|
-
self._log = getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
def set_db_value(self, key: str, value, is_statestore=True, is_runstore=True):
|
|
18
|
-
"""Set value to database.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
key (str): database key
|
|
22
|
-
value (_type_): database value
|
|
23
|
-
is_statestore (bool, optional): indicates whether data should
|
|
24
|
-
be written to StateStore. Defaults to True.
|
|
25
|
-
is_runstore (bool, optional): indicates whether data should
|
|
26
|
-
be written to RunStore. Defaults to True.
|
|
27
|
-
"""
|
|
28
|
-
if is_statestore:
|
|
29
|
-
self._statestore.set_value(key, value)
|
|
30
|
-
if is_runstore:
|
|
31
|
-
self._runstore.set_value(key, value)
|
|
32
|
-
|
|
33
|
-
def generate_key(self, *args) -> str:
|
|
34
|
-
"""Generate key for database.
|
|
35
|
-
|
|
36
|
-
Args:
|
|
37
|
-
args: list of database keys
|
|
38
|
-
|
|
39
|
-
Returns:
|
|
40
|
-
str: database key
|
|
41
|
-
"""
|
|
42
|
-
return ".".join(args)
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Everypin
|
|
2
|
-
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
|
-
|
|
4
|
-
from logging import getLogger
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
from pytest import Item, Mark
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class NodeInfo(object):
|
|
11
|
-
"""Test node info."""
|
|
12
|
-
|
|
13
|
-
def __init__(self, item: Item):
|
|
14
|
-
self._item = item
|
|
15
|
-
self._log = getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
self._case_name = self._get_human_name(
|
|
18
|
-
item.own_markers,
|
|
19
|
-
"case_name",
|
|
20
|
-
)
|
|
21
|
-
self._module_name = self._get_human_name(
|
|
22
|
-
item.parent.own_markers,
|
|
23
|
-
"module_name",
|
|
24
|
-
)
|
|
25
|
-
self._module_id = Path(item.parent.nodeid).stem
|
|
26
|
-
self._case_id = item.name
|
|
27
|
-
|
|
28
|
-
@property
|
|
29
|
-
def module_id(self):
|
|
30
|
-
return self._module_id
|
|
31
|
-
|
|
32
|
-
@property
|
|
33
|
-
def case_id(self):
|
|
34
|
-
return self._case_id
|
|
35
|
-
|
|
36
|
-
@property
|
|
37
|
-
def module_name(self):
|
|
38
|
-
return self._module_name
|
|
39
|
-
|
|
40
|
-
@property
|
|
41
|
-
def case_name(self):
|
|
42
|
-
return self._case_name
|
|
43
|
-
|
|
44
|
-
def _get_human_name(self, markers: list[Mark], marker_name: str) -> str:
|
|
45
|
-
"""Get human name from markers.
|
|
46
|
-
|
|
47
|
-
Args:
|
|
48
|
-
markers (list[Mark]): item markers list
|
|
49
|
-
marker_name (str): marker name
|
|
50
|
-
|
|
51
|
-
Returns:
|
|
52
|
-
str: human name by user
|
|
53
|
-
"""
|
|
54
|
-
for marker in markers:
|
|
55
|
-
if marker.name == marker_name:
|
|
56
|
-
if marker.args:
|
|
57
|
-
return marker.args[0]
|
|
58
|
-
|
|
59
|
-
return ""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/css/main.e8a862f1.css
RENAMED
|
File without changes
|
{hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/css/main.e8a862f1.css.map
RENAMED
|
File without changes
|
{hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/808.ce070002.chunk.js
RENAMED
|
File without changes
|
{hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/808.ce070002.chunk.js.map
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hardpy-0.2.0 → hardpy-0.3.0}/hardpy/hardpy_panel/frontend/dist/static/js/main.8ef63e9b.js.map
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|