hardpy 0.15.2__py3-none-any.whl → 0.16.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 +7 -5
- hardpy/cli/cli.py +16 -10
- hardpy/common/config.py +70 -50
- hardpy/{pytest_hardpy/utils → common}/singleton.py +1 -1
- hardpy/hardpy_panel/api.py +13 -2
- hardpy/hardpy_panel/frontend/dist/assets/{allPaths-CV5wjLMB.js → allPaths-Cy69sdSD.js} +1 -1
- hardpy/hardpy_panel/frontend/dist/assets/{allPathsLoader-JIzW_pSb.js → allPathsLoader-D993NqQ9.js} +2 -2
- hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-16-Bfs1BwbR.ttf → blueprint-icons-16-B2twAPZE.ttf} +0 -0
- hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-16-RCDSkC4W.eot → blueprint-icons-16-C0Unyq1d.eot} +0 -0
- hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-16-CzsyEoPG.svg → blueprint-icons-16-CVy9qFng.svg} +249 -3
- hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-Ck1ifK4A.woff +0 -0
- hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-DwWyHYRo.woff2 +0 -0
- hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-9zitLjlL.woff2 +0 -0
- hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-CjKGIKxE.woff +0 -0
- hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-20-DyVnGNfQ.svg → blueprint-icons-20-DQ09GSQq.svg} +249 -3
- hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-20-BGGGsqDJ.ttf → blueprint-icons-20-DmR755bS.ttf} +0 -0
- hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-20-Doom1bSH.eot → blueprint-icons-20-p9MhBXD8.eot} +0 -0
- hardpy/hardpy_panel/frontend/dist/assets/browser-ponyfill-DD76sq2d.js +2 -0
- hardpy/hardpy_panel/frontend/dist/assets/index-B-fsa5Ru.js +1 -0
- hardpy/hardpy_panel/frontend/dist/assets/index-B7T9xvaW.css +1 -0
- hardpy/hardpy_panel/frontend/dist/assets/index-C93zcGIi.js +4672 -0
- hardpy/hardpy_panel/frontend/dist/assets/index-DLOviMB1.js +1 -0
- hardpy/hardpy_panel/frontend/dist/assets/{splitPathsBySizeLoader-DkZadBcn.js → splitPathsBySizeLoader-D4hRORV6.js} +1 -1
- hardpy/hardpy_panel/frontend/dist/index.html +2 -2
- hardpy/hardpy_panel/frontend/dist/locales/de/translation.json +9 -0
- hardpy/hardpy_panel/frontend/dist/locales/en/translation.json +9 -0
- hardpy/hardpy_panel/frontend/dist/locales/es/translation.json +9 -0
- hardpy/hardpy_panel/frontend/dist/locales/fr/translation.json +9 -0
- hardpy/hardpy_panel/frontend/dist/locales/ja/translation.json +9 -0
- hardpy/hardpy_panel/frontend/dist/locales/ru/translation.json +9 -0
- hardpy/hardpy_panel/frontend/dist/locales/zh/translation.json +9 -0
- hardpy/pytest_hardpy/db/__init__.py +12 -0
- hardpy/pytest_hardpy/db/base_store.py +25 -4
- hardpy/pytest_hardpy/db/runstore.py +1 -1
- hardpy/pytest_hardpy/db/schema/v1.py +8 -6
- hardpy/pytest_hardpy/db/statestore.py +1 -1
- hardpy/pytest_hardpy/plugin.py +30 -22
- hardpy/pytest_hardpy/pytest_call.py +12 -23
- hardpy/pytest_hardpy/pytest_wrapper.py +9 -7
- hardpy/pytest_hardpy/reporter/base.py +21 -1
- hardpy/pytest_hardpy/reporter/runner_reporter.py +1 -1
- hardpy/pytest_hardpy/result/report_loader/stand_cloud_loader.py +35 -4
- hardpy/pytest_hardpy/utils/__init__.py +0 -16
- {hardpy-0.15.2.dist-info → hardpy-0.16.0.dist-info}/METADATA +2 -2
- hardpy-0.16.0.dist-info/RECORD +83 -0
- hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-Btb8d-Hu.woff +0 -0
- hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-DrH54W_x.woff2 +0 -0
- hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-D9WO2FSG.woff2 +0 -0
- hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-ZW-9JnPf.woff +0 -0
- hardpy/hardpy_panel/frontend/dist/assets/browser-ponyfill-CccdstaD.js +0 -2
- hardpy/hardpy_panel/frontend/dist/assets/index-6RIgWzcZ.js +0 -790
- hardpy/hardpy_panel/frontend/dist/assets/index-BMEat_ws.js +0 -1
- hardpy/hardpy_panel/frontend/dist/assets/index-BwCQzehg.css +0 -1
- hardpy/hardpy_panel/frontend/dist/assets/index-xb4M2ucX.js +0 -1
- hardpy/pytest_hardpy/db/base_connector.py +0 -31
- hardpy/pytest_hardpy/db/base_server.py +0 -14
- hardpy/pytest_hardpy/utils/connection_data.py +0 -13
- hardpy-0.15.2.dist-info/RECORD +0 -86
- /hardpy/pytest_hardpy/{utils → db}/stand_type.py +0 -0
- {hardpy-0.15.2.dist-info → hardpy-0.16.0.dist-info}/WHEEL +0 -0
- {hardpy-0.15.2.dist-info → hardpy-0.16.0.dist-info}/entry_points.txt +0 -0
- {hardpy-0.15.2.dist-info → hardpy-0.16.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
import{_ as o,a as _,b as i,p as c,I as u}from"./index-
|
|
1
|
+
import{_ as o,a as _,b as i,p as c,I as u}from"./index-C93zcGIi.js";var p=function(n,s){return o(void 0,void 0,void 0,function(){var a,r;return _(this,function(e){switch(e.label){case 0:return a=c(n),s!==u.STANDARD?[3,2]:[4,i(()=>import("./index-DLOviMB1.js").then(t=>t.I),[])];case 1:return r=e.sent(),[3,4];case 2:return[4,i(()=>import("./index-B-fsa5Ru.js").then(t=>t.I),[])];case 3:r=e.sent(),e.label=4;case 4:return[2,r[a]]}})})};export{p as splitPathsBySizeLoader};
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
Learn how to configure a non-root public URL by running `npm run build`.
|
|
26
26
|
-->
|
|
27
27
|
<title>HardPy Operator Panel</title>
|
|
28
|
-
<script type="module" crossorigin src="/assets/index-
|
|
29
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
28
|
+
<script type="module" crossorigin src="/assets/index-C93zcGIi.js"></script>
|
|
29
|
+
<link rel="stylesheet" crossorigin href="/assets/index-B7T9xvaW.css">
|
|
30
30
|
</head>
|
|
31
31
|
<body>
|
|
32
32
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
@@ -29,6 +29,15 @@
|
|
|
29
29
|
"dbConnectionTitle": "Datenbankverbindungsfehler",
|
|
30
30
|
"dbConnectionMessage": "Verbindung zur Datenbank konnte nicht hergestellt werden"
|
|
31
31
|
},
|
|
32
|
+
"chart": {
|
|
33
|
+
"dataChart": "Diagrammdaten",
|
|
34
|
+
"xAxis": "X-Achse",
|
|
35
|
+
"yAxis": "Y-Achse",
|
|
36
|
+
"chart": "Diagramm",
|
|
37
|
+
"showChart": "Diagramm anzeigen {{title}}",
|
|
38
|
+
"fullscreenButton": "Diagramm im Vollbildmodus öffnen",
|
|
39
|
+
"series": "Reihe {{number}}"
|
|
40
|
+
},
|
|
32
41
|
"operatorDialog": {
|
|
33
42
|
"defaultTitle": "Nachricht",
|
|
34
43
|
"imageAlt": "Operator-Nachrichtenbild",
|
|
@@ -29,6 +29,15 @@
|
|
|
29
29
|
"dbConnectionTitle": "Database Connection Error",
|
|
30
30
|
"dbConnectionMessage": "Failed to establish connection with the database"
|
|
31
31
|
},
|
|
32
|
+
"chart": {
|
|
33
|
+
"dataChart": "Chart Data",
|
|
34
|
+
"xAxis": "X Axis",
|
|
35
|
+
"yAxis": "Y Axis",
|
|
36
|
+
"chart": "Chart",
|
|
37
|
+
"showChart": "Show chart {{title}}",
|
|
38
|
+
"fullscreenButton": "Open chart in full screen",
|
|
39
|
+
"series": "Series {{number}}"
|
|
40
|
+
},
|
|
32
41
|
"operatorDialog": {
|
|
33
42
|
"defaultTitle": "Message",
|
|
34
43
|
"imageAlt": "Operator message image",
|
|
@@ -29,6 +29,15 @@
|
|
|
29
29
|
"dbConnectionTitle": "Error de conexión a la base de datos",
|
|
30
30
|
"dbConnectionMessage": "No se pudo establecer la conexión con la base de datos"
|
|
31
31
|
},
|
|
32
|
+
"chart": {
|
|
33
|
+
"dataChart": "Datos del gráfico",
|
|
34
|
+
"xAxis": "Eje X",
|
|
35
|
+
"yAxis": "Eje Y",
|
|
36
|
+
"chart": "Gráfico",
|
|
37
|
+
"showChart": "Mostrar gráfico {{title}}",
|
|
38
|
+
"fullscreenButton": "Abrir gráfico en pantalla completa",
|
|
39
|
+
"series": "Serie {{number}}"
|
|
40
|
+
},
|
|
32
41
|
"operatorDialog": {
|
|
33
42
|
"defaultTitle": "Mensaje",
|
|
34
43
|
"imageAlt": "Imagen del mensaje del operador",
|
|
@@ -29,6 +29,15 @@
|
|
|
29
29
|
"dbConnectionTitle": "Erreur de connexion à la base de données",
|
|
30
30
|
"dbConnectionMessage": "Échec de la connexion à la base de données"
|
|
31
31
|
},
|
|
32
|
+
"chart": {
|
|
33
|
+
"dataChart": "Données du graphique",
|
|
34
|
+
"xAxis": "Axe X",
|
|
35
|
+
"yAxis": "Axe Y",
|
|
36
|
+
"chart": "Graphique",
|
|
37
|
+
"showChart": "Afficher le graphique {{title}}",
|
|
38
|
+
"fullscreenButton": "Ouvrir le graphique en plein écran",
|
|
39
|
+
"series": "Série {{number}}"
|
|
40
|
+
},
|
|
32
41
|
"operatorDialog": {
|
|
33
42
|
"defaultTitle": "Message",
|
|
34
43
|
"imageAlt": "Image du message de l'opérateur",
|
|
@@ -29,6 +29,15 @@
|
|
|
29
29
|
"dbConnectionTitle": "データベース接続エラー",
|
|
30
30
|
"dbConnectionMessage": "データベースへの接続に失敗しました"
|
|
31
31
|
},
|
|
32
|
+
"chart": {
|
|
33
|
+
"dataChart": "チャートデータ",
|
|
34
|
+
"xAxis": "X軸",
|
|
35
|
+
"yAxis": "Y軸",
|
|
36
|
+
"chart": "グラフ",
|
|
37
|
+
"showChart": "グラフを表示 {{title}}",
|
|
38
|
+
"fullscreenButton": "グラフを全画面表示で開く",
|
|
39
|
+
"series": "シリーズ{{number}}"
|
|
40
|
+
},
|
|
32
41
|
"operatorDialog": {
|
|
33
42
|
"defaultTitle": "メッセージ",
|
|
34
43
|
"imageAlt": "オペレーターメッセージ画像",
|
|
@@ -29,6 +29,15 @@
|
|
|
29
29
|
"dbConnectionTitle": "Ошибка подключения к базе данных",
|
|
30
30
|
"dbConnectionMessage": "Не удалось установить соединение с базой данных"
|
|
31
31
|
},
|
|
32
|
+
"chart": {
|
|
33
|
+
"dataChart": "Данные графика",
|
|
34
|
+
"xAxis": "Ось X",
|
|
35
|
+
"yAxis": "Ось Y",
|
|
36
|
+
"chart": "График",
|
|
37
|
+
"showChart": "Показать график {{title}}",
|
|
38
|
+
"fullscreenButton": "Открыть график в полноэкранном режиме",
|
|
39
|
+
"series": "Серия {{number}}"
|
|
40
|
+
},
|
|
32
41
|
"operatorDialog": {
|
|
33
42
|
"defaultTitle": "Сообщение",
|
|
34
43
|
"imageAlt": "Изображение в сообщении оператора",
|
|
@@ -29,6 +29,15 @@
|
|
|
29
29
|
"dbConnectionTitle": "数据库连接错误",
|
|
30
30
|
"dbConnectionMessage": "无法建立数据库连接"
|
|
31
31
|
},
|
|
32
|
+
"chart": {
|
|
33
|
+
"dataChart": "图表数据",
|
|
34
|
+
"xAxis": "X轴",
|
|
35
|
+
"yAxis": "Y轴",
|
|
36
|
+
"chart": "图表",
|
|
37
|
+
"showChart": "显示图表 {{title}}",
|
|
38
|
+
"fullscreenButton": "全屏显示图表",
|
|
39
|
+
"series": "系列{{number}}"
|
|
40
|
+
},
|
|
32
41
|
"operatorDialog": {
|
|
33
42
|
"defaultTitle": "消息",
|
|
34
43
|
"imageAlt": "操作员消息图片",
|
|
@@ -5,13 +5,25 @@ from hardpy.pytest_hardpy.db.base_store import BaseStore
|
|
|
5
5
|
from hardpy.pytest_hardpy.db.const import DatabaseField
|
|
6
6
|
from hardpy.pytest_hardpy.db.runstore import RunStore
|
|
7
7
|
from hardpy.pytest_hardpy.db.schema import ResultRunStore, ResultStateStore
|
|
8
|
+
from hardpy.pytest_hardpy.db.stand_type import (
|
|
9
|
+
Chart,
|
|
10
|
+
Instrument,
|
|
11
|
+
NumericMeasurement,
|
|
12
|
+
StringMeasurement,
|
|
13
|
+
SubUnit,
|
|
14
|
+
)
|
|
8
15
|
from hardpy.pytest_hardpy.db.statestore import StateStore
|
|
9
16
|
|
|
10
17
|
__all__ = [
|
|
11
18
|
"BaseStore",
|
|
19
|
+
"Chart",
|
|
12
20
|
"DatabaseField",
|
|
21
|
+
"Instrument",
|
|
22
|
+
"NumericMeasurement",
|
|
13
23
|
"ResultRunStore",
|
|
14
24
|
"ResultStateStore",
|
|
15
25
|
"RunStore",
|
|
16
26
|
"StateStore",
|
|
27
|
+
"StringMeasurement",
|
|
28
|
+
"SubUnit",
|
|
17
29
|
]
|
|
@@ -5,18 +5,26 @@ from logging import getLogger
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
from glom import assign, glom
|
|
8
|
-
from pycouchdb
|
|
8
|
+
from pycouchdb import Server as DbServer
|
|
9
|
+
from pycouchdb.client import Database
|
|
10
|
+
from pycouchdb.exceptions import Conflict, GenericError, NotFound
|
|
9
11
|
from pydantic._internal._model_construction import ModelMetaclass
|
|
12
|
+
from requests.exceptions import ConnectionError # noqa: A004
|
|
10
13
|
|
|
11
|
-
from hardpy.
|
|
14
|
+
from hardpy.common.config import ConfigManager
|
|
12
15
|
from hardpy.pytest_hardpy.db.const import DatabaseField as DF # noqa: N817
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
class BaseStore
|
|
18
|
+
class BaseStore:
|
|
16
19
|
"""HardPy base storage interface for CouchDB."""
|
|
17
20
|
|
|
18
21
|
def __init__(self, db_name: str) -> None:
|
|
19
|
-
|
|
22
|
+
config_manager = ConfigManager()
|
|
23
|
+
config = config_manager.config
|
|
24
|
+
self._db_srv = DbServer(config.database.url)
|
|
25
|
+
self._db_name = db_name
|
|
26
|
+
self._db = self._init_db()
|
|
27
|
+
self._doc_id = config.database.doc_id
|
|
20
28
|
self._log = getLogger(__name__)
|
|
21
29
|
self._doc: dict = self._init_doc()
|
|
22
30
|
self._schema: ModelMetaclass
|
|
@@ -83,6 +91,19 @@ class BaseStore(BaseConnector):
|
|
|
83
91
|
self._log.debug("Database will be created for the first time")
|
|
84
92
|
self._doc: dict = self._init_doc()
|
|
85
93
|
|
|
94
|
+
def _init_db(self) -> Database:
|
|
95
|
+
try:
|
|
96
|
+
return self._db_srv.create(self._db_name) # type: ignore
|
|
97
|
+
except Conflict:
|
|
98
|
+
# database is already created
|
|
99
|
+
return self._db_srv.database(self._db_name)
|
|
100
|
+
except GenericError as exc:
|
|
101
|
+
msg = f"Error initializing database {exc}"
|
|
102
|
+
raise RuntimeError(msg) from exc
|
|
103
|
+
except ConnectionError as exc:
|
|
104
|
+
msg = f"Error initializing database: {exc}"
|
|
105
|
+
raise RuntimeError(msg) from exc
|
|
106
|
+
|
|
86
107
|
def _init_doc(self) -> dict:
|
|
87
108
|
try:
|
|
88
109
|
doc = self._db.get(self._doc_id)
|
|
@@ -5,9 +5,9 @@ from logging import getLogger
|
|
|
5
5
|
|
|
6
6
|
from pycouchdb.exceptions import Conflict, NotFound
|
|
7
7
|
|
|
8
|
+
from hardpy.common.singleton import SingletonMeta
|
|
8
9
|
from hardpy.pytest_hardpy.db.base_store import BaseStore
|
|
9
10
|
from hardpy.pytest_hardpy.db.schema import ResultRunStore
|
|
10
|
-
from hardpy.pytest_hardpy.utils import SingletonMeta
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class RunStore(BaseStore, metaclass=SingletonMeta):
|
|
@@ -8,7 +8,7 @@ from typing import ClassVar
|
|
|
8
8
|
|
|
9
9
|
from pydantic import BaseModel, ConfigDict, Field
|
|
10
10
|
|
|
11
|
-
from hardpy.pytest_hardpy.utils import (
|
|
11
|
+
from hardpy.pytest_hardpy.utils.const import (
|
|
12
12
|
ChartType,
|
|
13
13
|
ComparisonOperation as CompOp,
|
|
14
14
|
Group,
|
|
@@ -78,7 +78,7 @@ class Dut(BaseModel):
|
|
|
78
78
|
part_number: str | None = None
|
|
79
79
|
revision: str | None = None
|
|
80
80
|
sub_units: list[SubUnit] = []
|
|
81
|
-
info: Mapping[str, str | int | float] = {}
|
|
81
|
+
info: Mapping[str, str | int | float | None] = {}
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
class SubUnit(BaseModel):
|
|
@@ -91,7 +91,7 @@ class SubUnit(BaseModel):
|
|
|
91
91
|
serial_number: str | None = None
|
|
92
92
|
part_number: str | None = None
|
|
93
93
|
revision: str | None = None
|
|
94
|
-
info: Mapping[str, str | int | float] = {}
|
|
94
|
+
info: Mapping[str, str | int | float | None] = {}
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
class Instrument(BaseModel):
|
|
@@ -101,9 +101,11 @@ class Instrument(BaseModel):
|
|
|
101
101
|
|
|
102
102
|
name: str | None = None
|
|
103
103
|
revision: str | None = None
|
|
104
|
+
serial_number: str | None = None
|
|
105
|
+
part_number: str | None = None
|
|
104
106
|
number: int | None = None
|
|
105
107
|
comment: str | None = None
|
|
106
|
-
info: Mapping[str, str | int | float] = {}
|
|
108
|
+
info: Mapping[str, str | int | float | None] = {}
|
|
107
109
|
|
|
108
110
|
|
|
109
111
|
class TestStand(BaseModel):
|
|
@@ -119,7 +121,7 @@ class TestStand(BaseModel):
|
|
|
119
121
|
number: int | None = None
|
|
120
122
|
drivers: dict = {} # deprecated, remove in v2
|
|
121
123
|
instruments: list[Instrument] = []
|
|
122
|
-
info: Mapping[str, str | int | float] = {}
|
|
124
|
+
info: Mapping[str, str | int | float | None] = {}
|
|
123
125
|
|
|
124
126
|
|
|
125
127
|
class Process(BaseModel):
|
|
@@ -129,7 +131,7 @@ class Process(BaseModel):
|
|
|
129
131
|
|
|
130
132
|
name: str | None = None
|
|
131
133
|
number: int | None = None
|
|
132
|
-
info: Mapping[str, str | int | float] = {}
|
|
134
|
+
info: Mapping[str, str | int | float | None] = {}
|
|
133
135
|
|
|
134
136
|
|
|
135
137
|
class IBaseMeasurement(BaseModel, ABC):
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
from logging import getLogger
|
|
5
5
|
|
|
6
|
+
from hardpy.common.singleton import SingletonMeta
|
|
6
7
|
from hardpy.pytest_hardpy.db.base_store import BaseStore
|
|
7
8
|
from hardpy.pytest_hardpy.db.schema import ResultStateStore
|
|
8
|
-
from hardpy.pytest_hardpy.utils import SingletonMeta
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class StateStore(BaseStore, metaclass=SingletonMeta):
|
hardpy/pytest_hardpy/plugin.py
CHANGED
|
@@ -32,14 +32,10 @@ from pytest import (
|
|
|
32
32
|
skip,
|
|
33
33
|
)
|
|
34
34
|
|
|
35
|
+
from hardpy.common.config import ConfigManager, HardpyConfig
|
|
35
36
|
from hardpy.common.stand_cloud.connector import StandCloudConnector, StandCloudError
|
|
36
37
|
from hardpy.pytest_hardpy.reporter import HookReporter
|
|
37
|
-
from hardpy.pytest_hardpy.utils import
|
|
38
|
-
ConnectionData,
|
|
39
|
-
NodeInfo,
|
|
40
|
-
ProgressCalculator,
|
|
41
|
-
TestStatus,
|
|
42
|
-
)
|
|
38
|
+
from hardpy.pytest_hardpy.utils import NodeInfo, ProgressCalculator, TestStatus
|
|
43
39
|
from hardpy.pytest_hardpy.utils.node_info import TestDependencyInfo
|
|
44
40
|
|
|
45
41
|
if __debug__:
|
|
@@ -51,11 +47,11 @@ if __debug__:
|
|
|
51
47
|
|
|
52
48
|
def pytest_addoption(parser: Parser) -> None:
|
|
53
49
|
"""Register argparse-style options."""
|
|
54
|
-
|
|
50
|
+
default_config = HardpyConfig()
|
|
55
51
|
parser.addoption(
|
|
56
52
|
"--hardpy-db-url",
|
|
57
53
|
action="store",
|
|
58
|
-
default=
|
|
54
|
+
default=default_config.database.url,
|
|
59
55
|
help="database url",
|
|
60
56
|
)
|
|
61
57
|
parser.addoption(
|
|
@@ -90,13 +86,13 @@ def pytest_addoption(parser: Parser) -> None:
|
|
|
90
86
|
parser.addoption(
|
|
91
87
|
"--sc-address",
|
|
92
88
|
action="store",
|
|
93
|
-
default=
|
|
89
|
+
default=default_config.stand_cloud.address,
|
|
94
90
|
help="StandCloud address",
|
|
95
91
|
)
|
|
96
92
|
parser.addoption(
|
|
97
93
|
"--sc-connection-only",
|
|
98
94
|
action="store_true",
|
|
99
|
-
default=
|
|
95
|
+
default=default_config.stand_cloud.connection_only,
|
|
100
96
|
help="check StandCloud availability",
|
|
101
97
|
)
|
|
102
98
|
parser.addoption(
|
|
@@ -144,11 +140,15 @@ class HardpyPlugin:
|
|
|
144
140
|
|
|
145
141
|
def pytest_configure(self, config: Config) -> None:
|
|
146
142
|
"""Configure pytest."""
|
|
147
|
-
|
|
143
|
+
config_manager = ConfigManager()
|
|
144
|
+
hardpy_config = config_manager.read_config(Path(config.rootpath))
|
|
145
|
+
|
|
146
|
+
if not hardpy_config:
|
|
147
|
+
hardpy_config = HardpyConfig()
|
|
148
148
|
|
|
149
149
|
database_url = config.getoption("--hardpy-db-url")
|
|
150
150
|
if database_url:
|
|
151
|
-
|
|
151
|
+
hardpy_config.database.url = str(database_url) # type: ignore
|
|
152
152
|
|
|
153
153
|
tests_name = config.getoption("--hardpy-tests-name")
|
|
154
154
|
if tests_name:
|
|
@@ -160,11 +160,11 @@ class HardpyPlugin:
|
|
|
160
160
|
|
|
161
161
|
sc_address = config.getoption("--sc-address")
|
|
162
162
|
if sc_address:
|
|
163
|
-
|
|
163
|
+
hardpy_config.stand_cloud.address = str(sc_address) # type: ignore
|
|
164
164
|
|
|
165
165
|
sc_connection_only = config.getoption("--sc-connection-only")
|
|
166
166
|
if sc_connection_only:
|
|
167
|
-
|
|
167
|
+
hardpy_config.stand_cloud.connection_only = bool(sc_connection_only) # type: ignore
|
|
168
168
|
|
|
169
169
|
_args = config.getoption("--hardpy-start-arg") or []
|
|
170
170
|
if _args:
|
|
@@ -246,17 +246,24 @@ class HardpyPlugin:
|
|
|
246
246
|
|
|
247
247
|
def pytest_runtestloop(self, session: Session) -> bool | None:
|
|
248
248
|
"""Call at the start of test run."""
|
|
249
|
-
|
|
249
|
+
try:
|
|
250
|
+
self._progress.set_test_amount(session.testscollected)
|
|
251
|
+
except ValueError:
|
|
252
|
+
msg = "No tests collected"
|
|
253
|
+
self._reporter.set_alert(msg)
|
|
254
|
+
exit(msg, ExitCode.NO_TESTS_COLLECTED)
|
|
250
255
|
if session.config.option.collectonly:
|
|
251
256
|
# ignore collect only mode
|
|
252
257
|
return True
|
|
253
258
|
|
|
254
|
-
|
|
259
|
+
config_manager = ConfigManager()
|
|
255
260
|
|
|
256
261
|
# running tests depends on a connection to StandCloud
|
|
257
|
-
if
|
|
262
|
+
if config_manager.config.stand_cloud.connection_only:
|
|
258
263
|
try:
|
|
259
|
-
sc_connector = StandCloudConnector(
|
|
264
|
+
sc_connector = StandCloudConnector(
|
|
265
|
+
addr=config_manager.config.stand_cloud.address,
|
|
266
|
+
)
|
|
260
267
|
except StandCloudError as exc:
|
|
261
268
|
msg = str(exc)
|
|
262
269
|
self._reporter.set_alert(msg)
|
|
@@ -264,7 +271,7 @@ class HardpyPlugin:
|
|
|
264
271
|
try:
|
|
265
272
|
sc_connector.healthcheck()
|
|
266
273
|
except Exception: # noqa: BLE001
|
|
267
|
-
addr =
|
|
274
|
+
addr = config_manager.config.stand_cloud.address
|
|
268
275
|
msg = (
|
|
269
276
|
f"StandCloud service at the address {addr} "
|
|
270
277
|
"not available or HardPy user is not authorized"
|
|
@@ -337,15 +344,16 @@ class HardpyPlugin:
|
|
|
337
344
|
self._reporter.clear_case_data(module_id, case_id)
|
|
338
345
|
self._reporter.update_db_by_doc()
|
|
339
346
|
|
|
347
|
+
# clear the error code if there were no failed tests before
|
|
348
|
+
if caused_dut_failure_id is None:
|
|
349
|
+
self._reporter.clear_error_code()
|
|
350
|
+
|
|
340
351
|
try:
|
|
341
352
|
item.runtest()
|
|
342
353
|
call.excinfo = None
|
|
343
354
|
self._is_critical_not_passed = False
|
|
344
355
|
is_dut_failure = False
|
|
345
356
|
self._reporter.set_case_status(module_id, case_id, TestStatus.PASSED)
|
|
346
|
-
# clear the error code if there were no failed tests before
|
|
347
|
-
if caused_dut_failure_id is None:
|
|
348
|
-
self._reporter.clear_error_code()
|
|
349
357
|
break
|
|
350
358
|
except AssertionError:
|
|
351
359
|
self._reporter.set_case_status(module_id, case_id, TestStatus.FAILED)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2025 Everypin
|
|
2
2
|
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -9,25 +9,21 @@ from time import sleep
|
|
|
9
9
|
from typing import TYPE_CHECKING, Any
|
|
10
10
|
from uuid import uuid4
|
|
11
11
|
|
|
12
|
-
from pycouchdb.exceptions import NotFound
|
|
13
|
-
from pydantic import ValidationError
|
|
14
|
-
|
|
15
12
|
from hardpy.pytest_hardpy.db import (
|
|
13
|
+
Chart,
|
|
16
14
|
DatabaseField as DF, # noqa: N817
|
|
15
|
+
Instrument,
|
|
16
|
+
NumericMeasurement,
|
|
17
17
|
ResultRunStore,
|
|
18
|
-
|
|
18
|
+
StringMeasurement,
|
|
19
|
+
SubUnit,
|
|
19
20
|
)
|
|
20
21
|
from hardpy.pytest_hardpy.reporter import RunnerReporter
|
|
21
22
|
from hardpy.pytest_hardpy.utils import (
|
|
22
|
-
Chart,
|
|
23
23
|
DialogBox,
|
|
24
24
|
DuplicateParameterError,
|
|
25
25
|
HTMLComponent,
|
|
26
26
|
ImageComponent,
|
|
27
|
-
Instrument,
|
|
28
|
-
NumericMeasurement,
|
|
29
|
-
StringMeasurement,
|
|
30
|
-
SubUnit,
|
|
31
27
|
TestStandNumberError,
|
|
32
28
|
)
|
|
33
29
|
|
|
@@ -71,7 +67,7 @@ class ErrorCode:
|
|
|
71
67
|
if reporter.get_field(key) is None:
|
|
72
68
|
reporter.set_doc_value(key, code)
|
|
73
69
|
reporter.update_db_by_doc()
|
|
74
|
-
self._message = message
|
|
70
|
+
self._message = message if message else f"Error code = {code}"
|
|
75
71
|
|
|
76
72
|
def __repr__(self) -> str:
|
|
77
73
|
return self._message
|
|
@@ -86,15 +82,8 @@ def get_current_report() -> ResultRunStore | None:
|
|
|
86
82
|
Returns:
|
|
87
83
|
ResultRunStore | None: report, or None if not found or invalid
|
|
88
84
|
"""
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return runstore.get_document() # type: ignore
|
|
92
|
-
except NotFound:
|
|
93
|
-
return None
|
|
94
|
-
except ValidationError:
|
|
95
|
-
return None
|
|
96
|
-
except TypeError:
|
|
97
|
-
return None
|
|
85
|
+
reporter = RunnerReporter()
|
|
86
|
+
return reporter.get_report()
|
|
98
87
|
|
|
99
88
|
|
|
100
89
|
def set_user_name(name: str) -> None:
|
|
@@ -154,7 +143,7 @@ def set_dut_sub_unit(sub_unit: SubUnit) -> int:
|
|
|
154
143
|
return len(sub_units) - 1
|
|
155
144
|
|
|
156
145
|
|
|
157
|
-
def set_dut_info(info: Mapping[str, str | int | float]) -> None:
|
|
146
|
+
def set_dut_info(info: Mapping[str, str | int | float | None]) -> None:
|
|
158
147
|
"""Set DUT info to document.
|
|
159
148
|
|
|
160
149
|
Args:
|
|
@@ -278,7 +267,7 @@ def set_stand_name(name: str) -> None:
|
|
|
278
267
|
reporter.update_db_by_doc()
|
|
279
268
|
|
|
280
269
|
|
|
281
|
-
def set_stand_info(info: Mapping[str, str | int | float]) -> None:
|
|
270
|
+
def set_stand_info(info: Mapping[str, str | int | float | None]) -> None:
|
|
282
271
|
"""Add test stand info to document.
|
|
283
272
|
|
|
284
273
|
Args:
|
|
@@ -524,7 +513,7 @@ def set_process_number(number: int) -> None:
|
|
|
524
513
|
reporter.update_db_by_doc()
|
|
525
514
|
|
|
526
515
|
|
|
527
|
-
def set_process_info(info: Mapping[str, str | int | float]) -> None:
|
|
516
|
+
def set_process_info(info: Mapping[str, str | int | float | None]) -> None:
|
|
528
517
|
"""Set process info to document.
|
|
529
518
|
|
|
530
519
|
Args:
|
|
@@ -22,7 +22,8 @@ class PyTestWrapper:
|
|
|
22
22
|
|
|
23
23
|
# Make sure test structure is stored in DB
|
|
24
24
|
# before clients come in
|
|
25
|
-
self.
|
|
25
|
+
self._config_manager = ConfigManager()
|
|
26
|
+
self.config = self._config_manager.config
|
|
26
27
|
self.collect(is_clear_database=True)
|
|
27
28
|
|
|
28
29
|
def start(self, start_args: dict | None = None) -> bool:
|
|
@@ -42,7 +43,7 @@ class PyTestWrapper:
|
|
|
42
43
|
"-m",
|
|
43
44
|
"pytest",
|
|
44
45
|
"--hardpy-db-url",
|
|
45
|
-
self.config.database.
|
|
46
|
+
self.config.database.url,
|
|
46
47
|
"--hardpy-tests-name",
|
|
47
48
|
self.config.tests_name,
|
|
48
49
|
"--sc-address",
|
|
@@ -59,13 +60,13 @@ class PyTestWrapper:
|
|
|
59
60
|
if system() == "Windows":
|
|
60
61
|
self._proc = subprocess.Popen( # noqa: S603
|
|
61
62
|
cmd,
|
|
62
|
-
cwd=
|
|
63
|
+
cwd=self._config_manager.tests_path,
|
|
63
64
|
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP,
|
|
64
65
|
)
|
|
65
66
|
if system() == "Linux":
|
|
66
67
|
self._proc = subprocess.Popen( # noqa: S603
|
|
67
68
|
cmd,
|
|
68
|
-
cwd=
|
|
69
|
+
cwd=self._config_manager.tests_path,
|
|
69
70
|
)
|
|
70
71
|
|
|
71
72
|
return True
|
|
@@ -105,7 +106,7 @@ class PyTestWrapper:
|
|
|
105
106
|
"pytest",
|
|
106
107
|
"--collect-only",
|
|
107
108
|
"--hardpy-db-url",
|
|
108
|
-
self.config.database.
|
|
109
|
+
self.config.database.url,
|
|
109
110
|
"--hardpy-tests-name",
|
|
110
111
|
self.config.tests_name,
|
|
111
112
|
"--hardpy-pt",
|
|
@@ -116,7 +117,7 @@ class PyTestWrapper:
|
|
|
116
117
|
|
|
117
118
|
subprocess.Popen( # noqa: S603
|
|
118
119
|
[self.python_executable, *args],
|
|
119
|
-
cwd=
|
|
120
|
+
cwd=self._config_manager.tests_path,
|
|
120
121
|
)
|
|
121
122
|
return True
|
|
122
123
|
|
|
@@ -153,4 +154,5 @@ class PyTestWrapper:
|
|
|
153
154
|
Returns:
|
|
154
155
|
dict: HardPy configuration
|
|
155
156
|
"""
|
|
156
|
-
|
|
157
|
+
config_manager = ConfigManager()
|
|
158
|
+
return config_manager.config.model_dump()
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2025 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
|
from logging import getLogger
|
|
5
6
|
from typing import Any
|
|
6
7
|
|
|
8
|
+
from pycouchdb.exceptions import NotFound
|
|
9
|
+
from pydantic import ValidationError
|
|
10
|
+
|
|
7
11
|
from hardpy.pytest_hardpy.db import (
|
|
8
12
|
DatabaseField as DF, # noqa: N817
|
|
13
|
+
ResultRunStore,
|
|
9
14
|
RunStore,
|
|
10
15
|
StateStore,
|
|
11
16
|
)
|
|
@@ -82,6 +87,21 @@ class BaseReporter:
|
|
|
82
87
|
"""
|
|
83
88
|
return ".".join(args)
|
|
84
89
|
|
|
90
|
+
def get_report(self) -> ResultRunStore | None:
|
|
91
|
+
"""Get current report from runstore database.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
ResultRunStore | None: report, or None if not found or invalid
|
|
95
|
+
"""
|
|
96
|
+
try:
|
|
97
|
+
return self._runstore.get_document() # type: ignore
|
|
98
|
+
except NotFound:
|
|
99
|
+
return None
|
|
100
|
+
except ValidationError:
|
|
101
|
+
return None
|
|
102
|
+
except TypeError:
|
|
103
|
+
return None
|
|
104
|
+
|
|
85
105
|
def get_current_attempt(self, module_id: str, case_id: str) -> int:
|
|
86
106
|
"""Get current attempt.
|
|
87
107
|
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
from logging import getLogger
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
+
from hardpy.common.singleton import SingletonMeta
|
|
7
8
|
from hardpy.pytest_hardpy.reporter.base import BaseReporter
|
|
8
|
-
from hardpy.pytest_hardpy.utils import SingletonMeta
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class RunnerReporter(BaseReporter, metaclass=SingletonMeta):
|