hardpy 0.6.1__py3-none-any.whl → 0.8.0__py3-none-any.whl
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/__init__.py +49 -49
- hardpy/cli/cli.py +8 -9
- hardpy/cli/template.py +6 -6
- hardpy/common/config.py +19 -18
- hardpy/hardpy_panel/api.py +9 -9
- hardpy/hardpy_panel/frontend/dist/asset-manifest.json +3 -3
- hardpy/hardpy_panel/frontend/dist/index.html +1 -1
- hardpy/hardpy_panel/frontend/dist/logo192.png +0 -0
- hardpy/hardpy_panel/frontend/dist/static/css/main.e8a862f1.css.map +1 -1
- hardpy/hardpy_panel/frontend/dist/static/js/main.6f09d61a.js +3 -0
- hardpy/hardpy_panel/frontend/dist/static/js/{main.7c954faf.js.map → main.6f09d61a.js.map} +1 -1
- hardpy/pytest_hardpy/db/__init__.py +4 -5
- hardpy/pytest_hardpy/db/base_connector.py +6 -5
- hardpy/pytest_hardpy/db/base_server.py +1 -1
- hardpy/pytest_hardpy/db/base_store.py +23 -9
- hardpy/pytest_hardpy/db/const.py +3 -1
- hardpy/pytest_hardpy/db/runstore.py +13 -15
- hardpy/pytest_hardpy/db/schema/__init__.py +9 -0
- hardpy/pytest_hardpy/db/{schema.py → schema/v1.py} +120 -79
- hardpy/pytest_hardpy/db/statestore.py +7 -20
- hardpy/pytest_hardpy/plugin.py +128 -85
- hardpy/pytest_hardpy/pytest_call.py +80 -32
- hardpy/pytest_hardpy/pytest_wrapper.py +8 -8
- hardpy/pytest_hardpy/reporter/__init__.py +2 -2
- hardpy/pytest_hardpy/reporter/base.py +32 -7
- hardpy/pytest_hardpy/reporter/hook_reporter.py +66 -37
- hardpy/pytest_hardpy/reporter/runner_reporter.py +6 -8
- hardpy/pytest_hardpy/result/__init__.py +2 -2
- hardpy/pytest_hardpy/result/couchdb_config.py +20 -16
- hardpy/pytest_hardpy/result/report_loader/couchdb_loader.py +2 -2
- hardpy/pytest_hardpy/result/report_reader/couchdb_reader.py +36 -20
- hardpy/pytest_hardpy/utils/__init__.py +34 -29
- hardpy/pytest_hardpy/utils/connection_data.py +6 -8
- hardpy/pytest_hardpy/utils/const.py +1 -1
- hardpy/pytest_hardpy/utils/dialog_box.py +105 -66
- hardpy/pytest_hardpy/utils/exception.py +14 -8
- hardpy/pytest_hardpy/utils/machineid.py +15 -0
- hardpy/pytest_hardpy/utils/node_info.py +45 -16
- hardpy/pytest_hardpy/utils/progress_calculator.py +4 -3
- hardpy/pytest_hardpy/utils/singleton.py +23 -16
- {hardpy-0.6.1.dist-info → hardpy-0.8.0.dist-info}/METADATA +26 -33
- {hardpy-0.6.1.dist-info → hardpy-0.8.0.dist-info}/RECORD +46 -43
- hardpy/hardpy_panel/frontend/dist/static/js/main.7c954faf.js +0 -3
- /hardpy/hardpy_panel/frontend/dist/static/js/{main.7c954faf.js.LICENSE.txt → main.6f09d61a.js.LICENSE.txt} +0 -0
- {hardpy-0.6.1.dist-info → hardpy-0.8.0.dist-info}/WHEEL +0 -0
- {hardpy-0.6.1.dist-info → hardpy-0.8.0.dist-info}/entry_points.txt +0 -0
- {hardpy-0.6.1.dist-info → hardpy-0.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Copyright (c) 2024 Everypin
|
|
2
2
|
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
|
+
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import signal
|
|
5
|
-
import subprocess
|
|
6
|
+
import subprocess
|
|
6
7
|
import sys
|
|
7
8
|
from platform import system
|
|
8
9
|
from socket import socket
|
|
@@ -13,7 +14,7 @@ from hardpy.common.config import ConfigManager
|
|
|
13
14
|
class PyTestWrapper:
|
|
14
15
|
"""Wrapper for pytest subprocess."""
|
|
15
16
|
|
|
16
|
-
def __init__(self):
|
|
17
|
+
def __init__(self) -> None:
|
|
17
18
|
self._proc = None
|
|
18
19
|
self.python_executable = sys.executable
|
|
19
20
|
|
|
@@ -65,7 +66,7 @@ class PyTestWrapper:
|
|
|
65
66
|
"--hardpy-pt",
|
|
66
67
|
],
|
|
67
68
|
cwd=ConfigManager().get_tests_path(),
|
|
68
|
-
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP,
|
|
69
|
+
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP, # type: ignore
|
|
69
70
|
)
|
|
70
71
|
|
|
71
72
|
return True
|
|
@@ -80,11 +81,11 @@ class PyTestWrapper:
|
|
|
80
81
|
if system() == "Linux":
|
|
81
82
|
self._proc.terminate()
|
|
82
83
|
elif system() == "Windows":
|
|
83
|
-
self._proc.send_signal(signal.CTRL_BREAK_EVENT)
|
|
84
|
+
self._proc.send_signal(signal.CTRL_BREAK_EVENT) # type: ignore
|
|
84
85
|
return True
|
|
85
86
|
return False
|
|
86
87
|
|
|
87
|
-
def collect(self, is_clear_database: bool = False) -> bool:
|
|
88
|
+
def collect(self, *, is_clear_database: bool = False) -> bool:
|
|
88
89
|
"""Perform pytest collection.
|
|
89
90
|
|
|
90
91
|
Args:
|
|
@@ -115,7 +116,6 @@ class PyTestWrapper:
|
|
|
115
116
|
|
|
116
117
|
if is_clear_database:
|
|
117
118
|
args.append("--hardpy-clear-database")
|
|
118
|
-
args.append(str(is_clear_database))
|
|
119
119
|
|
|
120
120
|
subprocess.Popen( # noqa: S603
|
|
121
121
|
[self.python_executable, *args],
|
|
@@ -123,7 +123,7 @@ class PyTestWrapper:
|
|
|
123
123
|
)
|
|
124
124
|
return True
|
|
125
125
|
|
|
126
|
-
def send_data(self, data: str):
|
|
126
|
+
def send_data(self, data: str) -> bool:
|
|
127
127
|
"""Send data to pytest subprocess.
|
|
128
128
|
|
|
129
129
|
Args:
|
|
@@ -138,7 +138,7 @@ class PyTestWrapper:
|
|
|
138
138
|
client.connect((self.config.socket.host, self.config.socket.port))
|
|
139
139
|
client.sendall(data.encode("utf-8"))
|
|
140
140
|
client.close()
|
|
141
|
-
except Exception:
|
|
141
|
+
except Exception: # noqa: BLE001
|
|
142
142
|
return False
|
|
143
143
|
return True
|
|
144
144
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Copyright (c) 2024 Everypin
|
|
2
2
|
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
3
|
|
|
4
|
-
from hardpy.pytest_hardpy.reporter.runner_reporter import RunnerReporter
|
|
5
4
|
from hardpy.pytest_hardpy.reporter.hook_reporter import HookReporter
|
|
5
|
+
from hardpy.pytest_hardpy.reporter.runner_reporter import RunnerReporter
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
|
-
"RunnerReporter",
|
|
9
8
|
"HookReporter",
|
|
9
|
+
"RunnerReporter",
|
|
10
10
|
]
|
|
@@ -2,21 +2,30 @@
|
|
|
2
2
|
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
3
|
|
|
4
4
|
from logging import getLogger
|
|
5
|
+
from typing import Any
|
|
5
6
|
|
|
6
|
-
from hardpy.pytest_hardpy.db import
|
|
7
|
+
from hardpy.pytest_hardpy.db import (
|
|
8
|
+
DatabaseField as DF, # noqa: N817
|
|
9
|
+
RunStore,
|
|
10
|
+
StateStore,
|
|
11
|
+
)
|
|
7
12
|
|
|
8
13
|
|
|
9
14
|
class BaseReporter:
|
|
10
15
|
"""Base class for test reporter."""
|
|
11
16
|
|
|
12
|
-
def __init__(self):
|
|
17
|
+
def __init__(self) -> None:
|
|
13
18
|
self._statestore = StateStore()
|
|
14
19
|
self._runstore = RunStore()
|
|
15
20
|
self._log = getLogger(__name__)
|
|
16
21
|
|
|
17
22
|
def set_doc_value(
|
|
18
|
-
self,
|
|
19
|
-
|
|
23
|
+
self,
|
|
24
|
+
key: str,
|
|
25
|
+
value: Any, # noqa: ANN401
|
|
26
|
+
runstore_only: bool = False,
|
|
27
|
+
statestore_only: bool = False,
|
|
28
|
+
) -> None:
|
|
20
29
|
"""Set value to the document.
|
|
21
30
|
|
|
22
31
|
Update a document without writing to the database.
|
|
@@ -33,7 +42,8 @@ class BaseReporter:
|
|
|
33
42
|
ValueError: if both runstore_only and statestore_only are True
|
|
34
43
|
"""
|
|
35
44
|
if runstore_only and statestore_only:
|
|
36
|
-
|
|
45
|
+
msg = "Both runstore_only and statestore_only cannot be True"
|
|
46
|
+
raise ValueError(msg)
|
|
37
47
|
if runstore_only:
|
|
38
48
|
self._runstore.update_doc(key, value)
|
|
39
49
|
return
|
|
@@ -43,12 +53,12 @@ class BaseReporter:
|
|
|
43
53
|
self._runstore.update_doc(key, value)
|
|
44
54
|
self._statestore.update_doc(key, value)
|
|
45
55
|
|
|
46
|
-
def update_db_by_doc(self):
|
|
56
|
+
def update_db_by_doc(self) -> None:
|
|
47
57
|
"""Update database by current document."""
|
|
48
58
|
self._statestore.update_db()
|
|
49
59
|
self._runstore.update_db()
|
|
50
60
|
|
|
51
|
-
def generate_key(self, *args) -> str:
|
|
61
|
+
def generate_key(self, *args: Any) -> str: # noqa: ANN401
|
|
52
62
|
"""Generate key for database.
|
|
53
63
|
|
|
54
64
|
Args:
|
|
@@ -58,3 +68,18 @@ class BaseReporter:
|
|
|
58
68
|
str: database key
|
|
59
69
|
"""
|
|
60
70
|
return ".".join(args)
|
|
71
|
+
|
|
72
|
+
def get_current_attempt(self, module_id: str, case_id: str) -> int:
|
|
73
|
+
"""Get current attempt.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
int: current attempt
|
|
77
|
+
"""
|
|
78
|
+
key = self.generate_key(
|
|
79
|
+
DF.MODULES,
|
|
80
|
+
module_id,
|
|
81
|
+
DF.CASES,
|
|
82
|
+
case_id,
|
|
83
|
+
DF.ATTEMPT,
|
|
84
|
+
)
|
|
85
|
+
return self._statestore.get_field(key)
|
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
# Copyright (c) 2024 Everypin
|
|
2
2
|
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
|
+
from __future__ import annotations
|
|
3
4
|
|
|
4
|
-
from time import time, tzname
|
|
5
|
-
from logging import getLogger
|
|
6
5
|
from copy import deepcopy
|
|
6
|
+
from logging import getLogger
|
|
7
|
+
from time import time
|
|
7
8
|
|
|
8
9
|
from natsort import natsorted
|
|
10
|
+
from tzlocal import get_localzone
|
|
9
11
|
|
|
10
|
-
from hardpy.pytest_hardpy.db import DatabaseField as DF
|
|
12
|
+
from hardpy.pytest_hardpy.db import DatabaseField as DF # noqa: N817
|
|
11
13
|
from hardpy.pytest_hardpy.reporter.base import BaseReporter
|
|
12
|
-
from hardpy.pytest_hardpy.utils import TestStatus,
|
|
14
|
+
from hardpy.pytest_hardpy.utils import NodeInfo, TestStatus, machine_id
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
class HookReporter(BaseReporter):
|
|
16
18
|
"""Reporter for using in the hook HardPy plugin's hooks."""
|
|
17
19
|
|
|
18
|
-
def __init__(self, is_clear_database: bool = False)
|
|
20
|
+
def __init__(self, is_clear_database: bool = False) -> None:
|
|
19
21
|
super().__init__()
|
|
20
22
|
if is_clear_database:
|
|
21
23
|
self._statestore.clear()
|
|
24
|
+
self._runstore.clear()
|
|
22
25
|
self._log = getLogger(__name__)
|
|
23
26
|
|
|
24
|
-
def init_doc(self, doc_name: str):
|
|
27
|
+
def init_doc(self, doc_name: str) -> None:
|
|
25
28
|
"""Initialize document.
|
|
26
29
|
|
|
27
30
|
Args:
|
|
@@ -30,23 +33,26 @@ class HookReporter(BaseReporter):
|
|
|
30
33
|
self.set_doc_value(DF.NAME, doc_name)
|
|
31
34
|
self.set_doc_value(DF.STATUS, TestStatus.READY)
|
|
32
35
|
self.set_doc_value(DF.START_TIME, None)
|
|
33
|
-
self.set_doc_value(DF.TIMEZONE, None)
|
|
34
36
|
self.set_doc_value(DF.STOP_TIME, None)
|
|
35
|
-
self.set_doc_value(DF.PROGRESS, 0)
|
|
36
|
-
self.set_doc_value(DF.DRIVERS, {})
|
|
37
|
+
self.set_doc_value(DF.PROGRESS, 0, statestore_only=True)
|
|
37
38
|
self.set_doc_value(DF.ARTIFACT, {}, runstore_only=True)
|
|
38
39
|
self.set_doc_value(DF.OPERATOR_MSG, {}, statestore_only=True)
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
test_stand_tz = self.generate_key(DF.TEST_STAND, DF.TIMEZONE)
|
|
42
|
+
self.set_doc_value(test_stand_tz, str(get_localzone().key))
|
|
43
|
+
|
|
44
|
+
test_stand_id_key = self.generate_key(DF.TEST_STAND, DF.HW_ID)
|
|
45
|
+
self.set_doc_value(test_stand_id_key, machine_id())
|
|
46
|
+
|
|
47
|
+
def start(self) -> None:
|
|
41
48
|
"""Start test."""
|
|
42
49
|
self._log.debug("Starting test run.")
|
|
43
50
|
start_time = int(time())
|
|
44
51
|
self.set_doc_value(DF.START_TIME, start_time)
|
|
45
52
|
self.set_doc_value(DF.STATUS, TestStatus.RUN)
|
|
46
|
-
self.set_doc_value(DF.
|
|
47
|
-
self.set_doc_value(DF.PROGRESS, 0)
|
|
53
|
+
self.set_doc_value(DF.PROGRESS, 0, statestore_only=True)
|
|
48
54
|
|
|
49
|
-
def finish(self, status: TestStatus):
|
|
55
|
+
def finish(self, status: TestStatus) -> None:
|
|
50
56
|
"""Finish test.
|
|
51
57
|
|
|
52
58
|
This method must be called at the end of test run.
|
|
@@ -56,20 +62,20 @@ class HookReporter(BaseReporter):
|
|
|
56
62
|
self.set_doc_value(DF.STOP_TIME, stop_time)
|
|
57
63
|
self.set_doc_value(DF.STATUS, status)
|
|
58
64
|
|
|
59
|
-
def compact_all(self):
|
|
60
|
-
"""Compact all databases"""
|
|
65
|
+
def compact_all(self) -> None:
|
|
66
|
+
"""Compact all databases."""
|
|
61
67
|
self._statestore.compact()
|
|
62
68
|
self._runstore.compact()
|
|
63
69
|
|
|
64
|
-
def set_progress(self, progress: int):
|
|
70
|
+
def set_progress(self, progress: int) -> None:
|
|
65
71
|
"""Set test progress.
|
|
66
72
|
|
|
67
73
|
Args:
|
|
68
74
|
progress (int): test progress
|
|
69
75
|
"""
|
|
70
|
-
self.set_doc_value(DF.PROGRESS, progress)
|
|
76
|
+
self.set_doc_value(DF.PROGRESS, progress, statestore_only=True)
|
|
71
77
|
|
|
72
|
-
def set_assertion_msg(self, module_id: str, case_id: str, msg: str | None):
|
|
78
|
+
def set_assertion_msg(self, module_id: str, case_id: str, msg: str | None) -> None:
|
|
73
79
|
"""Set case assertion message.
|
|
74
80
|
|
|
75
81
|
Args:
|
|
@@ -78,11 +84,15 @@ class HookReporter(BaseReporter):
|
|
|
78
84
|
msg (str): assertion message
|
|
79
85
|
"""
|
|
80
86
|
key = self.generate_key(
|
|
81
|
-
DF.MODULES,
|
|
87
|
+
DF.MODULES,
|
|
88
|
+
module_id,
|
|
89
|
+
DF.CASES,
|
|
90
|
+
case_id,
|
|
91
|
+
DF.ASSERTION_MSG,
|
|
82
92
|
)
|
|
83
93
|
self.set_doc_value(key, msg)
|
|
84
94
|
|
|
85
|
-
def add_case(self, node_info: NodeInfo):
|
|
95
|
+
def add_case(self, node_info: NodeInfo) -> None:
|
|
86
96
|
"""Add test case to document.
|
|
87
97
|
|
|
88
98
|
Args:
|
|
@@ -99,7 +109,7 @@ class HookReporter(BaseReporter):
|
|
|
99
109
|
self.set_doc_value(key, item_statestore, statestore_only=True)
|
|
100
110
|
self.set_doc_value(key, item_runstore, runstore_only=True)
|
|
101
111
|
|
|
102
|
-
def set_case_status(self, module_id: str, case_id: str, status: TestStatus):
|
|
112
|
+
def set_case_status(self, module_id: str, case_id: str, status: TestStatus) -> None:
|
|
103
113
|
"""Set test case status.
|
|
104
114
|
|
|
105
115
|
Args:
|
|
@@ -110,7 +120,7 @@ class HookReporter(BaseReporter):
|
|
|
110
120
|
key = self.generate_key(DF.MODULES, module_id, DF.CASES, case_id, DF.STATUS)
|
|
111
121
|
self.set_doc_value(key, status)
|
|
112
122
|
|
|
113
|
-
def set_case_start_time(self, module_id: str, case_id: str):
|
|
123
|
+
def set_case_start_time(self, module_id: str, case_id: str) -> None:
|
|
114
124
|
"""Set test case start_time.
|
|
115
125
|
|
|
116
126
|
Args:
|
|
@@ -120,7 +130,7 @@ class HookReporter(BaseReporter):
|
|
|
120
130
|
key = self.generate_key(DF.MODULES, module_id, DF.CASES, case_id, DF.START_TIME)
|
|
121
131
|
self._set_time(key)
|
|
122
132
|
|
|
123
|
-
def set_case_stop_time(self, module_id: str, case_id: str):
|
|
133
|
+
def set_case_stop_time(self, module_id: str, case_id: str) -> None:
|
|
124
134
|
"""Set test case start_time.
|
|
125
135
|
|
|
126
136
|
Args:
|
|
@@ -130,7 +140,7 @@ class HookReporter(BaseReporter):
|
|
|
130
140
|
key = self.generate_key(DF.MODULES, module_id, DF.CASES, case_id, DF.STOP_TIME)
|
|
131
141
|
self._set_time(key)
|
|
132
142
|
|
|
133
|
-
def set_module_status(self, module_id: str, status: TestStatus):
|
|
143
|
+
def set_module_status(self, module_id: str, status: TestStatus) -> None:
|
|
134
144
|
"""Set test module status.
|
|
135
145
|
|
|
136
146
|
Args:
|
|
@@ -140,7 +150,7 @@ class HookReporter(BaseReporter):
|
|
|
140
150
|
key = self.generate_key(DF.MODULES, module_id, DF.STATUS)
|
|
141
151
|
self.set_doc_value(key, status)
|
|
142
152
|
|
|
143
|
-
def set_module_start_time(self, module_id: str):
|
|
153
|
+
def set_module_start_time(self, module_id: str) -> None:
|
|
144
154
|
"""Set test module status.
|
|
145
155
|
|
|
146
156
|
Args:
|
|
@@ -149,7 +159,7 @@ class HookReporter(BaseReporter):
|
|
|
149
159
|
key = self.generate_key(DF.MODULES, module_id, DF.START_TIME)
|
|
150
160
|
self._set_time(key)
|
|
151
161
|
|
|
152
|
-
def set_module_stop_time(self, module_id: str):
|
|
162
|
+
def set_module_stop_time(self, module_id: str) -> None:
|
|
153
163
|
"""Set test module status.
|
|
154
164
|
|
|
155
165
|
Args:
|
|
@@ -158,6 +168,23 @@ class HookReporter(BaseReporter):
|
|
|
158
168
|
key = self.generate_key(DF.MODULES, module_id, DF.STOP_TIME)
|
|
159
169
|
self._set_time(key)
|
|
160
170
|
|
|
171
|
+
def set_case_attempt(self, module_id: str, case_id: str, attempt: int) -> None:
|
|
172
|
+
"""Set test case current attempt.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
module_id (str): module id
|
|
176
|
+
case_id (str): case id
|
|
177
|
+
attempt (int): test case current attempt
|
|
178
|
+
"""
|
|
179
|
+
key = self.generate_key(
|
|
180
|
+
DF.MODULES,
|
|
181
|
+
module_id,
|
|
182
|
+
DF.CASES,
|
|
183
|
+
case_id,
|
|
184
|
+
DF.ATTEMPT,
|
|
185
|
+
)
|
|
186
|
+
self.set_doc_value(key, attempt, statestore_only=True)
|
|
187
|
+
|
|
161
188
|
def update_node_order(self, nodes: dict) -> None:
|
|
162
189
|
"""Update node order.
|
|
163
190
|
|
|
@@ -173,7 +200,7 @@ class HookReporter(BaseReporter):
|
|
|
173
200
|
updated_module_order = self._update_module_order(updated_case_order)
|
|
174
201
|
self.set_doc_value(key, updated_module_order, statestore_only=True)
|
|
175
202
|
|
|
176
|
-
def _set_time(self, key: str):
|
|
203
|
+
def _set_time(self, key: str) -> None:
|
|
177
204
|
current_time = self._statestore.get_field(key)
|
|
178
205
|
if current_time is None:
|
|
179
206
|
self.set_doc_value(key, int(time()))
|
|
@@ -184,8 +211,8 @@ class HookReporter(BaseReporter):
|
|
|
184
211
|
node_info: NodeInfo,
|
|
185
212
|
is_only_runstore: bool = False,
|
|
186
213
|
is_only_statestore: bool = False,
|
|
187
|
-
):
|
|
188
|
-
module_default = {
|
|
214
|
+
) -> None:
|
|
215
|
+
module_default = {
|
|
189
216
|
DF.STATUS: TestStatus.READY,
|
|
190
217
|
DF.NAME: self._get_module_name(node_info),
|
|
191
218
|
DF.START_TIME: None,
|
|
@@ -199,13 +226,12 @@ class HookReporter(BaseReporter):
|
|
|
199
226
|
DF.STOP_TIME: None,
|
|
200
227
|
DF.ASSERTION_MSG: None,
|
|
201
228
|
DF.MSG: None,
|
|
202
|
-
DF.ATTEMPT: 0,
|
|
203
229
|
}
|
|
204
230
|
|
|
205
|
-
if item.get(node_info.module_id) is None:
|
|
231
|
+
if item.get(node_info.module_id) is None:
|
|
206
232
|
if is_only_runstore:
|
|
207
233
|
module_default[DF.ARTIFACT] = {}
|
|
208
|
-
item[node_info.module_id] = module_default
|
|
234
|
+
item[node_info.module_id] = module_default
|
|
209
235
|
else:
|
|
210
236
|
item[node_info.module_id][DF.STATUS] = TestStatus.READY
|
|
211
237
|
item[node_info.module_id][DF.NAME] = self._get_module_name(node_info)
|
|
@@ -218,10 +244,14 @@ class HookReporter(BaseReporter):
|
|
|
218
244
|
|
|
219
245
|
if is_only_statestore:
|
|
220
246
|
case_default[DF.DIALOG_BOX] = {}
|
|
247
|
+
case_default[DF.ATTEMPT] = 0
|
|
221
248
|
item[node_info.module_id][DF.CASES][node_info.case_id] = case_default
|
|
222
249
|
|
|
223
250
|
def _remove_outdate_node(
|
|
224
|
-
self,
|
|
251
|
+
self,
|
|
252
|
+
old_modules: dict,
|
|
253
|
+
new_modules: dict,
|
|
254
|
+
nodes: dict,
|
|
225
255
|
) -> dict:
|
|
226
256
|
"""Remove outdated nodes from StateStore database.
|
|
227
257
|
|
|
@@ -277,16 +307,15 @@ class HookReporter(BaseReporter):
|
|
|
277
307
|
Returns:
|
|
278
308
|
dict: list of modules and cases.
|
|
279
309
|
"""
|
|
280
|
-
|
|
281
310
|
sorted_modules = natsorted(modules.items(), key=lambda item: item[0])
|
|
282
311
|
|
|
283
312
|
new_modules = {}
|
|
284
313
|
for module_id, module in sorted_modules:
|
|
285
|
-
new_modules[module_id] = module
|
|
314
|
+
new_modules[module_id] = module # noqa: PERF403
|
|
286
315
|
|
|
287
316
|
return new_modules
|
|
288
317
|
|
|
289
|
-
def _get_module_name(self, node_info) -> str:
|
|
318
|
+
def _get_module_name(self, node_info: NodeInfo) -> str:
|
|
290
319
|
"""Get module name from markers or use default.
|
|
291
320
|
|
|
292
321
|
Args:
|
|
@@ -297,7 +326,7 @@ class HookReporter(BaseReporter):
|
|
|
297
326
|
"""
|
|
298
327
|
return node_info.module_name if node_info.module_name else node_info.module_id
|
|
299
328
|
|
|
300
|
-
def _get_case_name(self, node_info) -> str:
|
|
329
|
+
def _get_case_name(self, node_info: NodeInfo) -> str:
|
|
301
330
|
"""Get case name from markers or use default.
|
|
302
331
|
|
|
303
332
|
Args:
|
|
@@ -5,19 +5,17 @@ from logging import getLogger
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
from hardpy.pytest_hardpy.reporter.base import BaseReporter
|
|
8
|
-
from hardpy.pytest_hardpy.utils import
|
|
8
|
+
from hardpy.pytest_hardpy.utils import SingletonMeta
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class RunnerReporter(
|
|
11
|
+
class RunnerReporter(BaseReporter, metaclass=SingletonMeta):
|
|
12
12
|
"""Reporter for using in direct call from test runner with HardPy plugin."""
|
|
13
13
|
|
|
14
|
-
def __init__(self):
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
self._log = getLogger(__name__)
|
|
18
|
-
self._initialized = True
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
super().__init__()
|
|
16
|
+
self._log = getLogger(__name__)
|
|
19
17
|
|
|
20
|
-
def get_field(self, key: str) -> Any:
|
|
18
|
+
def get_field(self, key: str) -> Any: # noqa: ANN401
|
|
21
19
|
"""Get field from the statestore.
|
|
22
20
|
|
|
23
21
|
Args:
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Copyright (c) 2024 Everypin
|
|
2
2
|
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
3
|
|
|
4
|
-
from hardpy.pytest_hardpy.result.report_reader.couchdb_reader import CouchdbReader
|
|
5
4
|
from hardpy.pytest_hardpy.result.report_loader.couchdb_loader import CouchdbLoader
|
|
5
|
+
from hardpy.pytest_hardpy.result.report_reader.couchdb_reader import CouchdbReader
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
|
-
"CouchdbReader",
|
|
9
8
|
"CouchdbLoader",
|
|
9
|
+
"CouchdbReader",
|
|
10
10
|
]
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
# Copyright (c) 2024 Everypin
|
|
2
2
|
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
|
+
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import ast
|
|
5
|
-
import requests
|
|
6
6
|
import socket
|
|
7
|
-
|
|
8
7
|
from dataclasses import dataclass
|
|
8
|
+
|
|
9
|
+
import requests
|
|
9
10
|
from urllib3 import disable_warnings
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
@dataclass
|
|
13
|
-
class CouchdbConfig:
|
|
14
|
+
class CouchdbConfig:
|
|
14
15
|
"""CouchDB loader config.
|
|
15
16
|
|
|
16
17
|
If `connection_str` arg is not set, it will be created from other args.
|
|
@@ -23,7 +24,7 @@ class CouchdbConfig: # noqa: WPS306
|
|
|
23
24
|
port: int = 5984
|
|
24
25
|
connection_str: str | None = None
|
|
25
26
|
|
|
26
|
-
def __post_init__(self):
|
|
27
|
+
def __post_init__(self) -> None:
|
|
27
28
|
"""Disable urllib3 warnings.
|
|
28
29
|
|
|
29
30
|
More info: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
|
|
@@ -43,14 +44,14 @@ class CouchdbConfig: # noqa: WPS306
|
|
|
43
44
|
if self.connection_str:
|
|
44
45
|
return self.connection_str
|
|
45
46
|
|
|
46
|
-
# TODO: Modify connection string creating based on protocol.
|
|
47
|
+
# TODO(xorialexandrov): Modify connection string creating based on protocol.
|
|
47
48
|
# Some problems with http and https, different ports, local
|
|
48
49
|
# and cloud databases.
|
|
49
50
|
protocol = self._get_protocol()
|
|
50
51
|
|
|
51
52
|
if protocol == "http":
|
|
52
53
|
host_url = f"http://{self.host}:{self.port}"
|
|
53
|
-
uri = f"{self.host}:{
|
|
54
|
+
uri = f"{self.host}:{self.port!s}"
|
|
54
55
|
elif protocol == "https":
|
|
55
56
|
host_url = f"https://{self.host}"
|
|
56
57
|
uri = f"{self.host}"
|
|
@@ -58,20 +59,22 @@ class CouchdbConfig: # noqa: WPS306
|
|
|
58
59
|
try:
|
|
59
60
|
response = requests.get(host_url, timeout=5)
|
|
60
61
|
except requests.exceptions.RequestException:
|
|
61
|
-
|
|
62
|
+
msg = f"Error CouchDB connecting to {host_url}."
|
|
63
|
+
raise RuntimeError(msg) # noqa: B904
|
|
62
64
|
|
|
63
65
|
# fmt: off
|
|
64
66
|
try:
|
|
65
|
-
couchdb_dict = ast.literal_eval(response._content.decode("utf-8")) # noqa:
|
|
67
|
+
couchdb_dict = ast.literal_eval(response._content.decode("utf-8")) # type: ignore # noqa: SLF001
|
|
66
68
|
couchdb_dict.get("couchdb", False)
|
|
67
|
-
except Exception:
|
|
68
|
-
|
|
69
|
+
except Exception: # noqa: BLE001
|
|
70
|
+
msg = f"Address {host_url} does not provide CouchDB attributes."
|
|
71
|
+
raise RuntimeError(msg) # noqa: B904
|
|
69
72
|
# fmt: on
|
|
70
73
|
|
|
71
74
|
credentials = f"{self.user}:{self.password}"
|
|
72
75
|
return f"{protocol}://{credentials}@{uri}/"
|
|
73
76
|
|
|
74
|
-
def _get_protocol(self) -> str:
|
|
77
|
+
def _get_protocol(self) -> str:
|
|
75
78
|
success = 200
|
|
76
79
|
try:
|
|
77
80
|
# HTTPS attempt
|
|
@@ -80,17 +83,18 @@ class CouchdbConfig: # noqa: WPS306
|
|
|
80
83
|
request = f"https://{self.host}"
|
|
81
84
|
if requests.get(request, timeout=5).status_code == success:
|
|
82
85
|
return "https"
|
|
83
|
-
raise OSError
|
|
86
|
+
raise OSError # noqa: TRY301
|
|
84
87
|
except OSError:
|
|
85
|
-
try:
|
|
88
|
+
try:
|
|
86
89
|
# HTTP attempt
|
|
87
90
|
sock = socket.create_connection((self.host, self.port))
|
|
88
91
|
sock.close()
|
|
89
92
|
request = f"http://{self.host}:{self.port}"
|
|
90
93
|
if requests.get(request, timeout=5).status_code == success:
|
|
91
94
|
return "http"
|
|
92
|
-
raise OSError
|
|
95
|
+
raise OSError # noqa: TRY301
|
|
93
96
|
except OSError:
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
msg = f"Error connecting to couchdb server {self.host}:{self.port}."
|
|
98
|
+
raise RuntimeError( # noqa: B904
|
|
99
|
+
msg,
|
|
96
100
|
)
|
|
@@ -5,8 +5,8 @@ from logging import getLogger
|
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
7
7
|
from pycouchdb import Server as DbServer
|
|
8
|
-
from pycouchdb.exceptions import Conflict
|
|
9
8
|
from pycouchdb.client import Database
|
|
9
|
+
from pycouchdb.exceptions import Conflict
|
|
10
10
|
|
|
11
11
|
from hardpy.pytest_hardpy.db.schema import ResultRunStore
|
|
12
12
|
from hardpy.pytest_hardpy.result.couchdb_config import CouchdbConfig
|
|
@@ -15,7 +15,7 @@ from hardpy.pytest_hardpy.result.couchdb_config import CouchdbConfig
|
|
|
15
15
|
class CouchdbLoader:
|
|
16
16
|
"""CouchDB report generator."""
|
|
17
17
|
|
|
18
|
-
def __init__(self, config: CouchdbConfig):
|
|
18
|
+
def __init__(self, config: CouchdbConfig) -> None:
|
|
19
19
|
self._log = getLogger(__name__)
|
|
20
20
|
self._config: CouchdbConfig = config
|
|
21
21
|
self._db_srv = DbServer(config.connection_string)
|