hardpy 0.1.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 +34 -0
- hardpy/hardpy_panel/__init__.py +0 -0
- hardpy/hardpy_panel/api.py +71 -0
- hardpy/hardpy_panel/frontend/dist/asset-manifest.json +36 -0
- hardpy/hardpy_panel/frontend/dist/favicon.ico +0 -0
- hardpy/hardpy_panel/frontend/dist/index.html +1 -0
- hardpy/hardpy_panel/frontend/dist/logo512.png +0 -0
- hardpy/hardpy_panel/frontend/dist/manifest.json +25 -0
- hardpy/hardpy_panel/frontend/dist/static/css/main.e8a862f1.css +2 -0
- hardpy/hardpy_panel/frontend/dist/static/css/main.e8a862f1.css.map +1 -0
- hardpy/hardpy_panel/frontend/dist/static/js/808.ce070002.chunk.js +2 -0
- hardpy/hardpy_panel/frontend/dist/static/js/808.ce070002.chunk.js.map +1 -0
- hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-16px-paths.d605910e.chunk.js +2 -0
- hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-16px-paths.d605910e.chunk.js.map +1 -0
- hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-20px-paths.7ee05cc8.chunk.js +2 -0
- hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-20px-paths.7ee05cc8.chunk.js.map +1 -0
- hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-all-paths-loader.0aa89747.chunk.js +2 -0
- hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-all-paths-loader.0aa89747.chunk.js.map +1 -0
- hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-all-paths.f63155c9.chunk.js +2 -0
- hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-all-paths.f63155c9.chunk.js.map +1 -0
- hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-split-paths-by-size-loader.52a072d3.chunk.js +2 -0
- hardpy/hardpy_panel/frontend/dist/static/js/blueprint-icons-split-paths-by-size-loader.52a072d3.chunk.js.map +1 -0
- hardpy/hardpy_panel/frontend/dist/static/js/main.8ef63e9b.js +3 -0
- hardpy/hardpy_panel/frontend/dist/static/js/main.8ef63e9b.js.LICENSE.txt +90 -0
- hardpy/hardpy_panel/frontend/dist/static/js/main.8ef63e9b.js.map +1 -0
- hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.520846c6beb41df528c8.eot +0 -0
- hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.5c52b39c697f2323ce8b.svg +1806 -0
- hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.84db1772f4bfb529f64f.woff +0 -0
- hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.b67ee1736e20e37a3225.woff2 +0 -0
- hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-16.e02ecf515378db143652.ttf +0 -0
- hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.429cacb8accf72488451.ttf +0 -0
- hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.6ae3791ee2d86fc228a6.svg +1806 -0
- hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.8cecf62de42997e4d82f.woff2 +0 -0
- hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.afbadb627d43b7857223.eot +0 -0
- hardpy/hardpy_panel/frontend/dist/static/media/blueprint-icons-20.e857f5a5132b8bfa71a1.woff +0 -0
- hardpy/hardpy_panel/frontend/dist/static/media/logo_smol.5b16f92447a4a9e80331.png +0 -0
- hardpy/hardpy_panel/runner.py +52 -0
- hardpy/pytest_hardpy/__init__.py +0 -0
- hardpy/pytest_hardpy/db/__init__.py +18 -0
- hardpy/pytest_hardpy/db/base_connector.py +24 -0
- hardpy/pytest_hardpy/db/base_server.py +14 -0
- hardpy/pytest_hardpy/db/base_store.py +88 -0
- hardpy/pytest_hardpy/db/const.py +25 -0
- hardpy/pytest_hardpy/db/runstore.py +30 -0
- hardpy/pytest_hardpy/db/schema.py +292 -0
- hardpy/pytest_hardpy/db/statestore.py +19 -0
- hardpy/pytest_hardpy/plugin.py +244 -0
- hardpy/pytest_hardpy/pytest_call.py +218 -0
- hardpy/pytest_hardpy/pytest_wrapper.py +117 -0
- hardpy/pytest_hardpy/reporter/__init__.py +10 -0
- hardpy/pytest_hardpy/reporter/base.py +42 -0
- hardpy/pytest_hardpy/reporter/hook_reporter.py +307 -0
- hardpy/pytest_hardpy/reporter/runner_reporter.py +29 -0
- hardpy/pytest_hardpy/result/__init__.py +10 -0
- hardpy/pytest_hardpy/result/couchdb_config.py +22 -0
- hardpy/pytest_hardpy/result/report_loader/__init__.py +10 -0
- hardpy/pytest_hardpy/result/report_loader/couchdb_loader.py +62 -0
- hardpy/pytest_hardpy/result/report_reader/__init__.py +0 -0
- hardpy/pytest_hardpy/result/report_reader/couchdb_reader.py +164 -0
- hardpy/pytest_hardpy/utils/__init__.py +19 -0
- hardpy/pytest_hardpy/utils/config_data.py +31 -0
- hardpy/pytest_hardpy/utils/const.py +29 -0
- hardpy/pytest_hardpy/utils/exception.py +16 -0
- hardpy/pytest_hardpy/utils/node_info.py +59 -0
- hardpy/pytest_hardpy/utils/progress_calculator.py +38 -0
- hardpy/pytest_hardpy/utils/singleton.py +23 -0
- hardpy-0.1.0.dist-info/METADATA +129 -0
- hardpy-0.1.0.dist-info/RECORD +71 -0
- hardpy-0.1.0.dist-info/WHEEL +4 -0
- hardpy-0.1.0.dist-info/entry_points.txt +5 -0
- hardpy-0.1.0.dist-info/licenses/LICENSE +674 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,52 @@
|
|
|
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 sys
|
|
5
|
+
from argparse import ArgumentParser
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from uvicorn import run as uvicorn_run
|
|
9
|
+
|
|
10
|
+
from hardpy.pytest_hardpy.utils import ConfigData
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def run():
|
|
14
|
+
"""Start server for frontend."""
|
|
15
|
+
config = ConfigData()
|
|
16
|
+
parser = ArgumentParser(description="Usage: hardpy-panel [OPTION]... [PATH]")
|
|
17
|
+
# fmt: off
|
|
18
|
+
parser.add_argument("-dbu", "--db_user", default=config.db_user, help="database user")
|
|
19
|
+
parser.add_argument("-dbpw", "--db_pswd", default=config.db_pswd, help="database user password") # noqa: E501
|
|
20
|
+
parser.add_argument("-dbp", "--db_port", type=int, default=config.db_port, help="database port number") # noqa: E501
|
|
21
|
+
parser.add_argument("-dbh", "--db_host", type=str, default=config.db_host, help="database hostname") # noqa: E501
|
|
22
|
+
parser.add_argument("-wh", "--web_host", type=str, default=config.web_host, help="web operator panel hostname") # noqa: E501
|
|
23
|
+
parser.add_argument("-wp", "--web_port", type=str, default=config.web_port, help="web operator panel port") # noqa: E501
|
|
24
|
+
parser.add_argument("path", type=str, nargs='?', help="path to test directory")
|
|
25
|
+
# fmt: on
|
|
26
|
+
|
|
27
|
+
args = parser.parse_args()
|
|
28
|
+
|
|
29
|
+
config.db_user = args.db_user
|
|
30
|
+
config.db_pswd = args.db_pswd
|
|
31
|
+
config.db_port = args.db_port
|
|
32
|
+
config.db_host = args.db_host
|
|
33
|
+
config.web_host = args.web_host
|
|
34
|
+
config.web_port = args.web_port
|
|
35
|
+
|
|
36
|
+
path = Path(args.path) if args.path else Path.cwd()
|
|
37
|
+
|
|
38
|
+
config.tests_dir = path
|
|
39
|
+
|
|
40
|
+
if not config.tests_dir.exists():
|
|
41
|
+
print(f"Directory not found: {path}")
|
|
42
|
+
sys.exit()
|
|
43
|
+
|
|
44
|
+
uvicorn_run(
|
|
45
|
+
"hardpy.hardpy_panel.api:app",
|
|
46
|
+
host=config.web_host,
|
|
47
|
+
port=config.web_port,
|
|
48
|
+
log_level="critical",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
run()
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
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 hardpy.pytest_hardpy.db.schema import ResultRunStore, ResultStateStore
|
|
5
|
+
from hardpy.pytest_hardpy.db.statestore import StateStore
|
|
6
|
+
from hardpy.pytest_hardpy.db.runstore import RunStore
|
|
7
|
+
from hardpy.pytest_hardpy.db.base_store import BaseStore
|
|
8
|
+
from hardpy.pytest_hardpy.db.const import DatabaseField
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"BaseStore",
|
|
13
|
+
"DatabaseField",
|
|
14
|
+
"ResultRunStore",
|
|
15
|
+
"ResultStateStore",
|
|
16
|
+
"StateStore",
|
|
17
|
+
"RunStore",
|
|
18
|
+
]
|
|
@@ -0,0 +1,24 @@
|
|
|
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 pycouchdb.client import Database
|
|
5
|
+
from pycouchdb.exceptions import Conflict
|
|
6
|
+
|
|
7
|
+
from hardpy.pytest_hardpy.db.base_server import BaseServer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BaseConnector(BaseServer):
|
|
11
|
+
"""Base class for CouchDB connector."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, db_name: str):
|
|
14
|
+
super().__init__()
|
|
15
|
+
self._db_name = db_name
|
|
16
|
+
self._db = self._init_db()
|
|
17
|
+
self._doc_id = "current"
|
|
18
|
+
|
|
19
|
+
def _init_db(self) -> Database:
|
|
20
|
+
try:
|
|
21
|
+
return self._db_srv.create(self._db_name) # type: ignore
|
|
22
|
+
except Conflict:
|
|
23
|
+
# database is already created
|
|
24
|
+
return self._db_srv.database(self._db_name)
|
|
@@ -0,0 +1,14 @@
|
|
|
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 pycouchdb import Server as DbServer
|
|
5
|
+
|
|
6
|
+
from hardpy.pytest_hardpy.utils import ConfigData
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseServer(object):
|
|
10
|
+
"""Base class for CouchDB server."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
config = ConfigData()
|
|
14
|
+
self._db_srv = DbServer(config.connection_string)
|
|
@@ -0,0 +1,88 @@
|
|
|
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 typing import Any
|
|
6
|
+
|
|
7
|
+
from glom import assign, glom
|
|
8
|
+
from pycouchdb.exceptions import Conflict, NotFound
|
|
9
|
+
from pydantic._internal._model_construction import ModelMetaclass
|
|
10
|
+
|
|
11
|
+
from hardpy.pytest_hardpy.db.base_connector import BaseConnector
|
|
12
|
+
from hardpy.pytest_hardpy.db.const import DatabaseField as DF
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BaseStore(BaseConnector):
|
|
16
|
+
"""HardPy base storage interface for CouchDB."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, db_name: str):
|
|
19
|
+
super().__init__(db_name)
|
|
20
|
+
self._log = getLogger(__name__)
|
|
21
|
+
self._doc: dict = self._init_doc()
|
|
22
|
+
self._schema: ModelMetaclass
|
|
23
|
+
|
|
24
|
+
def compact(self):
|
|
25
|
+
"""Compact database."""
|
|
26
|
+
self._db.compact()
|
|
27
|
+
|
|
28
|
+
def get_field(self, key: str) -> Any:
|
|
29
|
+
"""Get field from the state store.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
key (str): field name
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Any: field value
|
|
36
|
+
"""
|
|
37
|
+
return glom(self._doc, key)
|
|
38
|
+
|
|
39
|
+
def set_value(self, key: str, value):
|
|
40
|
+
"""Set a value in the state store."""
|
|
41
|
+
assign(self._doc, key, value)
|
|
42
|
+
try:
|
|
43
|
+
self._doc = self._db.save(self._doc)
|
|
44
|
+
except Conflict as exc:
|
|
45
|
+
self._log.error(
|
|
46
|
+
f"Error while saving runner document: {exc} "
|
|
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)
|
|
52
|
+
|
|
53
|
+
def get_document(self) -> ModelMetaclass:
|
|
54
|
+
"""Get document by schema.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
ModelMetaclass: document by schema
|
|
58
|
+
"""
|
|
59
|
+
self._doc = self._db.get(self._doc_id)
|
|
60
|
+
return self._schema(**self._doc)
|
|
61
|
+
|
|
62
|
+
def _init_doc(self) -> dict:
|
|
63
|
+
try:
|
|
64
|
+
doc = self._db.get(self._doc_id)
|
|
65
|
+
except NotFound:
|
|
66
|
+
return {
|
|
67
|
+
"_id": self._doc_id,
|
|
68
|
+
DF.MODULES: {},
|
|
69
|
+
DF.DUT: {
|
|
70
|
+
DF.SERIAL_NUMBER: None,
|
|
71
|
+
DF.INFO: {},
|
|
72
|
+
},
|
|
73
|
+
DF.TEST_STAND: {},
|
|
74
|
+
DF.DRIVERS: {},
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if DF.MODULES not in doc:
|
|
78
|
+
doc[DF.MODULES] = {}
|
|
79
|
+
|
|
80
|
+
for item in (DF.TEST_STAND, DF.DRIVERS):
|
|
81
|
+
doc[item] = {}
|
|
82
|
+
|
|
83
|
+
doc[DF.DUT] = {
|
|
84
|
+
DF.SERIAL_NUMBER: None,
|
|
85
|
+
DF.INFO: {},
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return doc
|
|
@@ -0,0 +1,25 @@
|
|
|
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 enum import Enum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DatabaseField(str, Enum): # noqa: WPS600
|
|
8
|
+
"""Database field."""
|
|
9
|
+
|
|
10
|
+
NAME = "name"
|
|
11
|
+
STATUS = "status"
|
|
12
|
+
START_TIME = "start_time"
|
|
13
|
+
STOP_TIME = "stop_time"
|
|
14
|
+
ASSERTION_MSG = "assertion_msg"
|
|
15
|
+
MSG = "msg"
|
|
16
|
+
MODULES = "modules"
|
|
17
|
+
CASES = "cases"
|
|
18
|
+
TIMEZONE = "timezone"
|
|
19
|
+
PROGRESS = "progress"
|
|
20
|
+
ARTIFACT = "artifact"
|
|
21
|
+
DUT = "dut"
|
|
22
|
+
INFO = "info"
|
|
23
|
+
TEST_STAND = "test_stand"
|
|
24
|
+
SERIAL_NUMBER = "serial_number"
|
|
25
|
+
DRIVERS = "drivers"
|
|
@@ -0,0 +1,30 @@
|
|
|
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 pycouchdb.exceptions import Conflict, NotFound
|
|
7
|
+
|
|
8
|
+
from hardpy.pytest_hardpy.db.base_store import BaseStore
|
|
9
|
+
from hardpy.pytest_hardpy.db import ResultRunStore
|
|
10
|
+
from hardpy.pytest_hardpy.utils import Singleton
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RunStore(Singleton, BaseStore):
|
|
14
|
+
"""HardPy run storage interface for CouchDB.
|
|
15
|
+
|
|
16
|
+
Save state and case artifact.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self):
|
|
20
|
+
if not self._initialized:
|
|
21
|
+
super().__init__("runstore")
|
|
22
|
+
self._log = getLogger(__name__)
|
|
23
|
+
try:
|
|
24
|
+
# Clear the runstore database before each launch
|
|
25
|
+
self._db.delete(self._doc_id)
|
|
26
|
+
except (Conflict, NotFound):
|
|
27
|
+
self._log.debug("Runstore database will be created for the first time")
|
|
28
|
+
self._doc: dict = self._init_doc()
|
|
29
|
+
self._schema = ResultRunStore
|
|
30
|
+
self._initialized = True
|
|
@@ -0,0 +1,292 @@
|
|
|
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 typing import Optional
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field, ConfigDict
|
|
7
|
+
|
|
8
|
+
from hardpy.pytest_hardpy.utils import TestStatus as Status
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class IBaseResult(BaseModel):
|
|
12
|
+
"""Base class for all result models."""
|
|
13
|
+
|
|
14
|
+
model_config = ConfigDict(extra="forbid")
|
|
15
|
+
|
|
16
|
+
status: Status
|
|
17
|
+
stop_time: int | None
|
|
18
|
+
start_time: int | None
|
|
19
|
+
name: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class CaseStateStore(IBaseResult):
|
|
23
|
+
"""Test case description.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
"test_one": {
|
|
27
|
+
"status": "passed",
|
|
28
|
+
"name": "Test 2",
|
|
29
|
+
"start_time": 1695817188,
|
|
30
|
+
"stop_time": 1695817189,
|
|
31
|
+
"assertion_msg": null,
|
|
32
|
+
"msg": null
|
|
33
|
+
}
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
assertion_msg: str | None = None
|
|
37
|
+
msg: dict | None = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class CaseRunStore(IBaseResult):
|
|
41
|
+
"""Test case description with artifact.
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
"test_one": {
|
|
45
|
+
"status": "passed",
|
|
46
|
+
"name": "Test 2",
|
|
47
|
+
"start_time": 1695817188,
|
|
48
|
+
"stop_time": 1695817189,
|
|
49
|
+
"assertion_msg": null,
|
|
50
|
+
"msg": null,
|
|
51
|
+
"artifact": {}
|
|
52
|
+
}
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
assertion_msg: str | None = None
|
|
56
|
+
msg: dict | None = None
|
|
57
|
+
artifact: Optional[dict] = {}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ModuleStateStore(IBaseResult):
|
|
61
|
+
"""Test module description.
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
"test_2_b": {
|
|
65
|
+
"status": "passed",
|
|
66
|
+
"name": "Module 2",
|
|
67
|
+
"start_time": 1695816886,
|
|
68
|
+
"stop_time": 1695817016,
|
|
69
|
+
"cases": {
|
|
70
|
+
"test_one": {
|
|
71
|
+
"status": "passed",
|
|
72
|
+
"name": "Test 1",
|
|
73
|
+
"start_time": 1695817015,
|
|
74
|
+
"stop_time": 1695817016,
|
|
75
|
+
"assertion_msg": null,
|
|
76
|
+
"msg": null
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
cases: dict[str, CaseStateStore] = {}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ModuleRunStore(IBaseResult):
|
|
86
|
+
"""Test module description.
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
"test_2_b": {
|
|
90
|
+
"status": "passed",
|
|
91
|
+
"name": "Module 2",
|
|
92
|
+
"start_time": 1695816886,
|
|
93
|
+
"stop_time": 1695817016,
|
|
94
|
+
"artifact": {},
|
|
95
|
+
"cases": {
|
|
96
|
+
"test_one": {
|
|
97
|
+
"status": "passed",
|
|
98
|
+
"name": "Test 1",
|
|
99
|
+
"start_time": 1695817015,
|
|
100
|
+
"stop_time": 1695817016,
|
|
101
|
+
"assertion_msg": null,
|
|
102
|
+
"msg": null,
|
|
103
|
+
"artifact": {}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
cases: dict[str, CaseRunStore] = {}
|
|
110
|
+
artifact: Optional[dict] = {}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class Dut(BaseModel):
|
|
114
|
+
"""Device under test description.
|
|
115
|
+
|
|
116
|
+
Example:
|
|
117
|
+
"dut": {
|
|
118
|
+
"serial_number": "a9ad8dca-2c64-4df8-a358-c21e832a32e4",
|
|
119
|
+
"info": {
|
|
120
|
+
"batch": "test_batch",
|
|
121
|
+
"board_rev": "rev_1"
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
model_config = ConfigDict(extra="forbid")
|
|
127
|
+
|
|
128
|
+
serial_number: str | None
|
|
129
|
+
info: dict = {}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class ResultStateStore(IBaseResult):
|
|
133
|
+
"""Test run description.
|
|
134
|
+
|
|
135
|
+
Example:
|
|
136
|
+
{
|
|
137
|
+
"_rev": "44867-3888ae85c19c428cc46685845953b483",
|
|
138
|
+
"_id": "current",
|
|
139
|
+
"progress": 100,
|
|
140
|
+
"stop_time": 1695817266,
|
|
141
|
+
"timezone": [
|
|
142
|
+
"CET",
|
|
143
|
+
"CET"
|
|
144
|
+
],
|
|
145
|
+
"start_time": 1695817263,
|
|
146
|
+
"status": "failed",
|
|
147
|
+
"name": "hardpy-stand",
|
|
148
|
+
"dut": {
|
|
149
|
+
"serial_number": "92c5a4bb-ecb0-42c5-89ac-e0caca0919fd",
|
|
150
|
+
"info": {
|
|
151
|
+
"batch": "test_batch",
|
|
152
|
+
"board_rev": "rev_1"
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
"test_stand": {
|
|
156
|
+
"name": "Test stand 1"
|
|
157
|
+
},
|
|
158
|
+
"drivers": {
|
|
159
|
+
"driver_1": "driver info",
|
|
160
|
+
"driver_2": {
|
|
161
|
+
"state": "active",
|
|
162
|
+
"port": 8000
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"modules": {
|
|
166
|
+
"test_1_a": {
|
|
167
|
+
"status": "failed",
|
|
168
|
+
"name": "Module 1",
|
|
169
|
+
"start_time": 1695816884,
|
|
170
|
+
"stop_time": 1695817265,
|
|
171
|
+
"cases": {
|
|
172
|
+
"test_dut_info": {
|
|
173
|
+
"status": "passed",
|
|
174
|
+
"name": "DUT info ",
|
|
175
|
+
"start_time": 1695817263,
|
|
176
|
+
"stop_time": 1695817264,
|
|
177
|
+
"assertion_msg": null,
|
|
178
|
+
"msg": null
|
|
179
|
+
},
|
|
180
|
+
"test_minute_parity": {
|
|
181
|
+
"status": "failed",
|
|
182
|
+
"name": "Test 1",
|
|
183
|
+
"start_time": 1695817264,
|
|
184
|
+
"stop_time": 1695817264,
|
|
185
|
+
"assertion_msg": "The test failed because minute 21 is odd! Try again!",
|
|
186
|
+
"msg": [
|
|
187
|
+
"Current minute 21"
|
|
188
|
+
]
|
|
189
|
+
},
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
model_config = ConfigDict(extra="forbid")
|
|
197
|
+
|
|
198
|
+
rev: str = Field(..., alias="_rev")
|
|
199
|
+
id: str = Field(..., alias="_id")
|
|
200
|
+
progress: int
|
|
201
|
+
timezone: tuple[str, str] | None = None
|
|
202
|
+
test_stand: dict = {}
|
|
203
|
+
dut: Dut
|
|
204
|
+
modules: dict[str, ModuleStateStore] = {}
|
|
205
|
+
drivers: Optional[dict] = {}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class ResultRunStore(IBaseResult):
|
|
209
|
+
"""Test run description.
|
|
210
|
+
|
|
211
|
+
Example:
|
|
212
|
+
{
|
|
213
|
+
"_rev": "44867-3888ae85c19c428cc46685845953b483",
|
|
214
|
+
"_id": "current",
|
|
215
|
+
"progress": 100,
|
|
216
|
+
"stop_time": 1695817266,
|
|
217
|
+
"timezone": [
|
|
218
|
+
"CET",
|
|
219
|
+
"CET"
|
|
220
|
+
],
|
|
221
|
+
"start_time": 1695817263,
|
|
222
|
+
"status": "failed",
|
|
223
|
+
"name": "hardpy-stand",
|
|
224
|
+
"dut": {
|
|
225
|
+
"serial_number": "92c5a4bb-ecb0-42c5-89ac-e0caca0919fd",
|
|
226
|
+
"info": {
|
|
227
|
+
"batch": "test_batch",
|
|
228
|
+
"board_rev": "rev_1"
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
"test_stand": {
|
|
232
|
+
"name": "Test stand 1"
|
|
233
|
+
},
|
|
234
|
+
"drivers": {
|
|
235
|
+
"driver_1": "driver info",
|
|
236
|
+
"driver_2": {
|
|
237
|
+
"state": "active",
|
|
238
|
+
"port": 8000
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
"artifact": {},
|
|
242
|
+
"modules": {
|
|
243
|
+
"test_1_a": {
|
|
244
|
+
"status": "failed",
|
|
245
|
+
"name": "Module 1",
|
|
246
|
+
"start_time": 1695816884,
|
|
247
|
+
"stop_time": 1695817265,
|
|
248
|
+
"artifact": {},
|
|
249
|
+
"cases": {
|
|
250
|
+
"test_dut_info": {
|
|
251
|
+
"status": "passed",
|
|
252
|
+
"name": "DUT info",
|
|
253
|
+
"start_time": 1695817263,
|
|
254
|
+
"stop_time": 1695817264,
|
|
255
|
+
"assertion_msg": null,
|
|
256
|
+
"msg": null,
|
|
257
|
+
"artifact": {}
|
|
258
|
+
},
|
|
259
|
+
"test_minute_parity": {
|
|
260
|
+
"status": "failed",
|
|
261
|
+
"name": "Test 1",
|
|
262
|
+
"start_time": 1695817264,
|
|
263
|
+
"stop_time": 1695817264,
|
|
264
|
+
"assertion_msg": "The test failed because minute 21 is odd! Try again!",
|
|
265
|
+
"msg": [
|
|
266
|
+
"Current minute 21"
|
|
267
|
+
],
|
|
268
|
+
"artifact": {
|
|
269
|
+
"data_str": "123DATA",
|
|
270
|
+
"data_int": 12345,
|
|
271
|
+
"data_dict": {
|
|
272
|
+
"test_key": "456DATA"
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
"""
|
|
281
|
+
|
|
282
|
+
model_config = ConfigDict(extra="forbid")
|
|
283
|
+
|
|
284
|
+
rev: str = Field(..., alias="_rev")
|
|
285
|
+
id: str = Field(..., alias="_id")
|
|
286
|
+
progress: int
|
|
287
|
+
timezone: tuple[str, str] | None = None
|
|
288
|
+
test_stand: dict = {}
|
|
289
|
+
dut: Dut
|
|
290
|
+
modules: dict[str, ModuleRunStore] = {}
|
|
291
|
+
drivers: Optional[dict] = {}
|
|
292
|
+
artifact: Optional[dict] = {}
|
|
@@ -0,0 +1,19 @@
|
|
|
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.base_store import BaseStore
|
|
7
|
+
from hardpy.pytest_hardpy.db import ResultStateStore
|
|
8
|
+
from hardpy.pytest_hardpy.utils import Singleton
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StateStore(Singleton, BaseStore):
|
|
12
|
+
"""HardPy state storage interface for CouchDB."""
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
if not self._initialized:
|
|
16
|
+
super().__init__("statestore")
|
|
17
|
+
self._log = getLogger(__name__)
|
|
18
|
+
self._schema = ResultStateStore
|
|
19
|
+
self._initialized = True
|