argus-alm 0.12.4b1__py3-none-any.whl → 0.12.5__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.
- argus/backend/controller/client_api.py +11 -0
- argus/backend/controller/testrun_api.py +11 -0
- argus/backend/models/result.py +41 -0
- argus/backend/models/web.py +4 -0
- argus/backend/plugins/core.py +4 -2
- argus/backend/plugins/driver_matrix_tests/controller.py +0 -39
- argus/backend/plugins/driver_matrix_tests/model.py +2 -248
- argus/backend/plugins/driver_matrix_tests/raw_types.py +0 -27
- argus/backend/plugins/driver_matrix_tests/service.py +0 -18
- argus/backend/plugins/driver_matrix_tests/udt.py +13 -14
- argus/backend/plugins/sct/testrun.py +7 -0
- argus/backend/service/client_service.py +18 -0
- argus/backend/service/testrun.py +19 -0
- argus/client/base.py +16 -0
- argus/client/driver_matrix_tests/client.py +193 -56
- argus/client/generic_result.py +99 -0
- argus/client/generic_result_old.py +143 -0
- {argus_alm-0.12.4b1.dist-info → argus_alm-0.12.5.dist-info}/METADATA +1 -1
- {argus_alm-0.12.4b1.dist-info → argus_alm-0.12.5.dist-info}/RECORD +22 -20
- {argus_alm-0.12.4b1.dist-info → argus_alm-0.12.5.dist-info}/entry_points.txt +0 -1
- argus/client/driver_matrix_tests/cli.py +0 -110
- {argus_alm-0.12.4b1.dist-info → argus_alm-0.12.5.dist-info}/LICENSE +0 -0
- {argus_alm-0.12.4b1.dist-info → argus_alm-0.12.5.dist-info}/WHEEL +0 -0
argus/client/base.py
CHANGED
|
@@ -7,6 +7,7 @@ from uuid import UUID
|
|
|
7
7
|
import requests
|
|
8
8
|
|
|
9
9
|
from argus.backend.util.enums import TestStatus
|
|
10
|
+
from argus.client.generic_result import GenericResultTable
|
|
10
11
|
from argus.client.sct.types import LogLink
|
|
11
12
|
|
|
12
13
|
JSON = dict[str, Any] | list[Any] | int | str | float | bool | Type[None]
|
|
@@ -28,6 +29,8 @@ class ArgusClient:
|
|
|
28
29
|
SET_STATUS = "/testrun/$type/$id/set_status"
|
|
29
30
|
SET_PRODUCT_VERSION = "/testrun/$type/$id/update_product_version"
|
|
30
31
|
SUBMIT_LOGS = "/testrun/$type/$id/logs/submit"
|
|
32
|
+
SUBMIT_RESULTS = "/testrun/$type/$id/submit_results"
|
|
33
|
+
FETCH_RESULTS = "/testrun/$type/$id/fetch_results"
|
|
31
34
|
FINALIZE = "/testrun/$type/$id/finalize"
|
|
32
35
|
|
|
33
36
|
def __init__(self, auth_token: str, base_url: str, api_version="v1") -> None:
|
|
@@ -190,3 +193,16 @@ class ArgusClient:
|
|
|
190
193
|
}
|
|
191
194
|
)
|
|
192
195
|
self.check_response(response)
|
|
196
|
+
|
|
197
|
+
def submit_results(self, result: GenericResultTable) -> None:
|
|
198
|
+
response = self.post(
|
|
199
|
+
endpoint=self.Routes.SUBMIT_RESULTS,
|
|
200
|
+
location_params={"type": self.test_type, "id": str(self.run_id)},
|
|
201
|
+
body={
|
|
202
|
+
**self.generic_body,
|
|
203
|
+
"run_id": str(self.run_id),
|
|
204
|
+
** result.as_dict(),
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
self.check_response(response)
|
|
208
|
+
|
|
@@ -1,77 +1,214 @@
|
|
|
1
|
+
from functools import reduce
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import TypedDict, Literal
|
|
4
|
+
import re
|
|
5
|
+
import logging
|
|
1
6
|
from uuid import UUID
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from pprint import pformat
|
|
9
|
+
from glob import glob
|
|
10
|
+
from xml.etree import ElementTree
|
|
11
|
+
|
|
2
12
|
from argus.backend.util.enums import TestStatus
|
|
3
13
|
from argus.client.base import ArgusClient
|
|
14
|
+
from argus.backend.plugins.driver_matrix_tests.raw_types import RawMatrixTestResult, RawMatrixTestCase
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
LOGGER = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
TestTypeType = Literal['java', 'cpp', 'python', 'gocql']
|
|
20
|
+
|
|
21
|
+
class AdaptedXUnitData(TypedDict):
|
|
22
|
+
timestamp: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def python_driver_matrix_adapter(xml: ElementTree.ElementTree) -> AdaptedXUnitData:
|
|
26
|
+
testsuites = list(xml.getroot().iter("testsuite"))
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
"timestamp": testsuites[0].attrib.get("timestamp"),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def java_driver_matrix_adapter(xml: ElementTree.ElementTree) -> AdaptedXUnitData:
|
|
34
|
+
testsuites = xml.getroot()
|
|
35
|
+
ts_now = datetime.utcnow().timestamp()
|
|
36
|
+
try:
|
|
37
|
+
time_taken = float(testsuites.attrib.get("time"))
|
|
38
|
+
except ValueError:
|
|
39
|
+
time_taken = 0.0
|
|
40
|
+
|
|
41
|
+
timestamp = datetime.utcfromtimestamp(ts_now - time_taken).isoformat()
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
"timestamp": timestamp,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def cpp_driver_matrix_adapter(xml: ElementTree.ElementTree) -> AdaptedXUnitData:
|
|
49
|
+
testsuites = xml.getroot()
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
"timestamp": testsuites.attrib.get("timestamp"),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def gocql_driver_matrix_adapter(xml: ElementTree.ElementTree) -> AdaptedXUnitData:
|
|
57
|
+
testsuites = list(xml.getroot().iter("testsuite"))
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
"timestamp": testsuites[0].attrib.get("timestamp"),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def generic_adapter(xml: ElementTree.ElementTree) -> AdaptedXUnitData:
|
|
65
|
+
return {
|
|
66
|
+
"timestamp": datetime.utcnow().isoformat()
|
|
67
|
+
}
|
|
4
68
|
|
|
5
69
|
|
|
6
70
|
class ArgusDriverMatrixClient(ArgusClient):
|
|
7
71
|
test_type = "driver-matrix-tests"
|
|
8
|
-
schema_version: None = "
|
|
72
|
+
schema_version: None = "v1"
|
|
9
73
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
74
|
+
TEST_ADAPTERS = {
|
|
75
|
+
"java": java_driver_matrix_adapter,
|
|
76
|
+
"cpp": cpp_driver_matrix_adapter,
|
|
77
|
+
"python": python_driver_matrix_adapter,
|
|
78
|
+
"gocql": gocql_driver_matrix_adapter,
|
|
79
|
+
}
|
|
14
80
|
|
|
15
81
|
def __init__(self, run_id: UUID, auth_token: str, base_url: str, api_version="v1") -> None:
|
|
16
82
|
super().__init__(auth_token, base_url, api_version)
|
|
17
83
|
self.run_id = run_id
|
|
18
84
|
|
|
19
|
-
def
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"job_name": job_name,
|
|
23
|
-
"job_url": job_url
|
|
24
|
-
})
|
|
85
|
+
def parse_build_environment(self, env_path: str) -> dict[str, str]:
|
|
86
|
+
with open(Path(env_path), mode="rt", encoding="utf-8") as env_file:
|
|
87
|
+
raw_env = env_file.read()
|
|
25
88
|
|
|
26
|
-
|
|
89
|
+
result = {}
|
|
90
|
+
for line in raw_env.split("\n"):
|
|
91
|
+
if not line:
|
|
92
|
+
continue
|
|
93
|
+
LOGGER.debug("ENV: %s", line)
|
|
94
|
+
key, val = line.split(": ")
|
|
95
|
+
result[key] = val.strip()
|
|
27
96
|
|
|
28
|
-
|
|
29
|
-
"""
|
|
30
|
-
Submit results of a single driver run
|
|
31
|
-
"""
|
|
32
|
-
response = self.post(
|
|
33
|
-
endpoint=self.Routes.SUBMIT_DRIVER_RESULT,
|
|
34
|
-
location_params={},
|
|
35
|
-
body={
|
|
36
|
-
**self.generic_body,
|
|
37
|
-
"run_id": str(self.run_id),
|
|
38
|
-
"driver_name": driver_name,
|
|
39
|
-
"driver_type": driver_type,
|
|
40
|
-
"raw_xml": str(raw_junit_data, encoding="utf-8"),
|
|
41
|
-
}
|
|
42
|
-
)
|
|
43
|
-
self.check_response(response)
|
|
97
|
+
return result
|
|
44
98
|
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
99
|
+
def get_test_cases(self, cases: list[ElementTree.Element]) -> list[RawMatrixTestCase]:
|
|
100
|
+
raw_cases = []
|
|
101
|
+
for case in cases:
|
|
102
|
+
children = list(case.findall("./*"))
|
|
103
|
+
if len(children) > 0:
|
|
104
|
+
status = children[0].tag
|
|
105
|
+
message = f"{children[0].attrib.get('message', 'no-message')} ({children[0].attrib.get('type', 'no-type')})"
|
|
106
|
+
else:
|
|
107
|
+
status = "passed"
|
|
108
|
+
message = ""
|
|
109
|
+
|
|
110
|
+
raw_cases.append({
|
|
111
|
+
"name": case.attrib["name"],
|
|
112
|
+
"status": status,
|
|
113
|
+
"time": float(case.attrib.get("time", 0.0)),
|
|
114
|
+
"classname": case.attrib.get("classname", ""),
|
|
115
|
+
"message": message,
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
return raw_cases
|
|
119
|
+
|
|
120
|
+
def get_driver_info(self, xml_name: str, test_type: TestTypeType) -> dict[str, str]:
|
|
121
|
+
if test_type == "cpp":
|
|
122
|
+
filename_re = r"TEST-(?P<driver_name>[\w]*)-(?P<version>[\d\.]*)\.xml"
|
|
123
|
+
else:
|
|
124
|
+
filename_re = r"(?P<name>[\w]*)\.(?P<driver_name>[\w]*)\.(?P<proto>v\d)\.(?P<version>[\d\.]*)\.xml"
|
|
125
|
+
|
|
126
|
+
match = re.match(filename_re, xml_name)
|
|
127
|
+
|
|
128
|
+
return match.groupdict() if match else {}
|
|
129
|
+
|
|
130
|
+
def get_passed_count(self, suite_attribs: dict[str, str]) -> int:
|
|
131
|
+
if (pass_count := suite_attribs.get("passed")):
|
|
132
|
+
return int(pass_count)
|
|
133
|
+
total = int(suite_attribs.get("tests", 0))
|
|
134
|
+
errors = int(suite_attribs.get("errors", 0))
|
|
135
|
+
skipped = int(suite_attribs.get("skipped", 0))
|
|
136
|
+
failures = int(suite_attribs.get("failures", 0))
|
|
137
|
+
|
|
138
|
+
return total - errors - skipped - failures
|
|
61
139
|
|
|
62
|
-
def
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
140
|
+
def parse_result_xml(self, xml_path: Path, test_type: TestTypeType) -> RawMatrixTestResult:
|
|
141
|
+
with xml_path.open(mode="rt", encoding="utf-8") as xml_file:
|
|
142
|
+
xml = ElementTree.parse(source=xml_file)
|
|
143
|
+
LOGGER.info("%s", pformat(xml))
|
|
144
|
+
testsuites = xml.getroot()
|
|
145
|
+
adapted_data = self.TEST_ADAPTERS.get(test_type, generic_adapter)(xml)
|
|
146
|
+
|
|
147
|
+
driver_info = self.get_driver_info(xml_path.name, test_type)
|
|
148
|
+
test_collection = {
|
|
149
|
+
"timestamp": adapted_data["timestamp"],
|
|
150
|
+
"name": xml_path.stem,
|
|
151
|
+
"driver_name": driver_info.get("driver_name"),
|
|
152
|
+
"tests": 0,
|
|
153
|
+
"failures": 0,
|
|
154
|
+
"errors": 0,
|
|
155
|
+
"disabled": 0,
|
|
156
|
+
"skipped": 0,
|
|
157
|
+
"passed": 0,
|
|
158
|
+
"time": 0.0,
|
|
159
|
+
}
|
|
160
|
+
all_suites = []
|
|
161
|
+
for suite in testsuites.iter("testsuite"):
|
|
162
|
+
raw_suite = {
|
|
163
|
+
"name": suite.attrib["name"],
|
|
164
|
+
"tests": int(suite.attrib.get("tests", 0)),
|
|
165
|
+
"failures": int(suite.attrib.get("failures", 0)),
|
|
166
|
+
"disabled": int(0),
|
|
167
|
+
"passed": self.get_passed_count(suite.attrib),
|
|
168
|
+
"skipped": int(suite.attrib.get("skipped", 0)),
|
|
169
|
+
"errors": int(suite.attrib.get("errors", 0)),
|
|
170
|
+
"time": float(suite.attrib["time"]),
|
|
171
|
+
"cases": self.get_test_cases(list(suite.iter("testcase")))
|
|
73
172
|
}
|
|
74
|
-
|
|
173
|
+
all_suites.append(raw_suite)
|
|
174
|
+
test_collection["tests"] += raw_suite["tests"]
|
|
175
|
+
test_collection["failures"] += raw_suite["failures"]
|
|
176
|
+
test_collection["errors"] += raw_suite["errors"]
|
|
177
|
+
test_collection["disabled"] += raw_suite["disabled"]
|
|
178
|
+
test_collection["skipped"] += raw_suite["skipped"]
|
|
179
|
+
test_collection["passed"] += raw_suite["passed"]
|
|
180
|
+
test_collection["time"] += raw_suite["time"]
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
**test_collection,
|
|
184
|
+
"suites": all_suites
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
def get_results(self, result_path: str, test_type: TestTypeType) -> list[RawMatrixTestResult]:
|
|
188
|
+
xmls = glob(f"{result_path}/**/*.xml", recursive=True)
|
|
189
|
+
LOGGER.info("Will use following XMLs: %s", pformat(xmls))
|
|
190
|
+
results = []
|
|
191
|
+
for xml in xmls:
|
|
192
|
+
result = self.parse_result_xml(Path(xml), test_type)
|
|
193
|
+
results.append(result)
|
|
194
|
+
return results
|
|
195
|
+
|
|
196
|
+
def submit(self, build_id: str, build_url: str, env_path: str, result_path: str, test_type: TestTypeType):
|
|
197
|
+
env = self.parse_build_environment(env_path)
|
|
198
|
+
results = self.get_results(result_path, test_type)
|
|
199
|
+
|
|
200
|
+
self.submit_driver_matrix_run(job_name=build_id, job_url=build_url, test_environment=env, results=results)
|
|
201
|
+
|
|
202
|
+
def submit_driver_matrix_run(self, job_name: str, job_url: str,
|
|
203
|
+
results: list[RawMatrixTestResult], test_environment: dict[str, str]) -> None:
|
|
204
|
+
response = super().submit_run(run_type=self.test_type, run_body={
|
|
205
|
+
"run_id": str(self.run_id),
|
|
206
|
+
"job_name": job_name,
|
|
207
|
+
"job_url": job_url,
|
|
208
|
+
"test_environment": test_environment,
|
|
209
|
+
"matrix_results": results
|
|
210
|
+
})
|
|
211
|
+
|
|
75
212
|
self.check_response(response)
|
|
76
213
|
|
|
77
214
|
def set_matrix_status(self, status: TestStatus):
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from enum import Enum, auto
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Status(Enum):
|
|
7
|
+
PASS = auto()
|
|
8
|
+
WARNING = auto()
|
|
9
|
+
ERROR = auto()
|
|
10
|
+
|
|
11
|
+
def __str__(self):
|
|
12
|
+
return self.name
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ResultType(Enum):
|
|
16
|
+
INTEGER = auto()
|
|
17
|
+
FLOAT = auto()
|
|
18
|
+
DURATION = auto()
|
|
19
|
+
|
|
20
|
+
def __str__(self):
|
|
21
|
+
return self.name
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class ColumnMetadata:
|
|
26
|
+
name: str
|
|
27
|
+
unit: str
|
|
28
|
+
type: ResultType
|
|
29
|
+
|
|
30
|
+
def as_dict(self) -> dict:
|
|
31
|
+
return {
|
|
32
|
+
"name": self.name,
|
|
33
|
+
"unit": self.unit,
|
|
34
|
+
"type": str(self.type)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ResultTableMeta(type):
|
|
39
|
+
def __new__(cls, name, bases, dct):
|
|
40
|
+
cls_instance = super().__new__(cls, name, bases, dct)
|
|
41
|
+
meta = dct.get('Meta')
|
|
42
|
+
|
|
43
|
+
if meta:
|
|
44
|
+
cls_instance.name = meta.name
|
|
45
|
+
cls_instance.description = meta.description
|
|
46
|
+
cls_instance.columns = meta.Columns
|
|
47
|
+
cls_instance.column_names = {column.name for column in cls_instance.columns}
|
|
48
|
+
cls_instance.rows = []
|
|
49
|
+
return cls_instance
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class Cell:
|
|
54
|
+
column: str
|
|
55
|
+
row: str
|
|
56
|
+
value: Union[int, float, str]
|
|
57
|
+
status: Status
|
|
58
|
+
|
|
59
|
+
def as_dict(self) -> dict:
|
|
60
|
+
return {
|
|
61
|
+
"column": self.column,
|
|
62
|
+
"row": self.row,
|
|
63
|
+
"value": self.value,
|
|
64
|
+
"status": str(self.status)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class GenericResultTable(metaclass=ResultTableMeta):
|
|
70
|
+
"""
|
|
71
|
+
Base class for all Generic Result Tables in Argus. Use it as a base class for your result table.
|
|
72
|
+
"""
|
|
73
|
+
sut_timestamp: int = 0 # automatic timestamp based on SUT version. Works only with SCT and refers to Scylla version.
|
|
74
|
+
sut_details: str = ""
|
|
75
|
+
results: list[Cell] = field(default_factory=list)
|
|
76
|
+
|
|
77
|
+
def as_dict(self) -> dict:
|
|
78
|
+
rows = []
|
|
79
|
+
for result in self.results:
|
|
80
|
+
if result.row not in rows:
|
|
81
|
+
rows.append(result.row)
|
|
82
|
+
|
|
83
|
+
meta_info = {
|
|
84
|
+
"name": self.name,
|
|
85
|
+
"description": self.description,
|
|
86
|
+
"columns_meta": [column.as_dict() for column in self.columns],
|
|
87
|
+
"rows_meta": rows
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
"meta": meta_info,
|
|
91
|
+
"sut_timestamp": self.sut_timestamp,
|
|
92
|
+
"sut_details": self.sut_details,
|
|
93
|
+
"results": [result.as_dict() for result in self.results]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
def add_result(self, column: str, row: str, value: Union[int, float, str], status: Status):
|
|
97
|
+
if column not in self.column_names:
|
|
98
|
+
raise ValueError(f"Column {column} not found in the table")
|
|
99
|
+
self.results.append(Cell(column=column, row=row, value=value, status=status))
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from dataclasses import dataclass, field, asdict
|
|
2
|
+
from typing import Dict, List, Tuple, Union, Type
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
from enum import Enum
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Status(Enum):
|
|
10
|
+
PASS = 1
|
|
11
|
+
WARNING = 2
|
|
12
|
+
ERROR = 3
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ResultType(Enum):
|
|
16
|
+
INTEGER = int
|
|
17
|
+
FLOAT = float
|
|
18
|
+
TEXT = str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class ColumnMetadata:
|
|
23
|
+
id: int
|
|
24
|
+
unit: str
|
|
25
|
+
type: ResultType
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class Result:
|
|
30
|
+
value: Union[int, float, str]
|
|
31
|
+
status: Status
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ResultTableMeta(type):
|
|
35
|
+
def __new__(cls, name, bases, dct):
|
|
36
|
+
cls_instance = super().__new__(cls, name, bases, dct)
|
|
37
|
+
meta = dct.get('Meta')
|
|
38
|
+
|
|
39
|
+
if meta:
|
|
40
|
+
cls_instance.table_name = meta.table_name
|
|
41
|
+
cls_instance.columns_map = {col_name: ColumnMetadata(id=col_id, unit=unit, type=result_type)
|
|
42
|
+
for col_name, (col_id, unit, result_type) in meta.Columns.items()}
|
|
43
|
+
cls_instance.rows_map = {row_name: row_id
|
|
44
|
+
for row_name, row_id in meta.Rows.items()}
|
|
45
|
+
return cls_instance
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Row:
|
|
49
|
+
def __init__(self, columns_map: Dict[str, ColumnMetadata]):
|
|
50
|
+
self.columns_map = columns_map
|
|
51
|
+
self.data: Dict[str, Result] = {}
|
|
52
|
+
|
|
53
|
+
def __getitem__(self, column_name: str) -> Result:
|
|
54
|
+
return self.data[column_name]
|
|
55
|
+
|
|
56
|
+
def __setitem__(self, column_name: str, result: Result):
|
|
57
|
+
if column_name not in self.columns_map:
|
|
58
|
+
raise ValueError(f"Column name '{column_name}' not found in columns_map.")
|
|
59
|
+
column_metadata = self.columns_map[column_name]
|
|
60
|
+
if not isinstance(result.value, column_metadata.type.value):
|
|
61
|
+
raise ValueError(f"Value {result.value} for column '{column_name}' is not of type {column_metadata.type.name}")
|
|
62
|
+
self.data[column_name] = result
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class BaseResultTable(metaclass=ResultTableMeta):
|
|
67
|
+
results: Dict[str, Row] = field(default_factory=dict)
|
|
68
|
+
|
|
69
|
+
def as_dict(self) -> dict:
|
|
70
|
+
results_list = []
|
|
71
|
+
for row_name, row in self.results.items():
|
|
72
|
+
row_id = self.rows_map.get(row_name)
|
|
73
|
+
for column_name, result in row.data.items():
|
|
74
|
+
column_metadata = self.columns_map.get(column_name)
|
|
75
|
+
results_list.append({
|
|
76
|
+
"column_id": column_metadata.id,
|
|
77
|
+
"row_id": row_id,
|
|
78
|
+
"result": result.value,
|
|
79
|
+
"status": result.status.value
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
meta_info = {
|
|
83
|
+
"table_name": self.table_name,
|
|
84
|
+
"columns": {name: {"id": meta.id, "unit": meta.unit, "type": meta.type.name} for name, meta in self.columns_map.items()},
|
|
85
|
+
"rows": self.rows_map
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
"meta": meta_info,
|
|
89
|
+
"results": results_list
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
def to_json(self) -> str:
|
|
93
|
+
dict_result = self.as_dict()
|
|
94
|
+
return json.dumps(dict_result, default=str)
|
|
95
|
+
|
|
96
|
+
def __getitem__(self, row_name: str) -> Row:
|
|
97
|
+
if row_name not in self.rows_map:
|
|
98
|
+
raise ValueError(f"Row name '{row_name}' not found in rows_map.")
|
|
99
|
+
if row_name not in self.results:
|
|
100
|
+
self.results[row_name] = Row(self.columns_map)
|
|
101
|
+
return self.results[row_name]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# Example of a specific result table with its own metadata
|
|
105
|
+
class LatencyResultTable(BaseResultTable):
|
|
106
|
+
class Meta:
|
|
107
|
+
table_name = "latency_percentile_write"
|
|
108
|
+
Columns = {
|
|
109
|
+
"latency": (1, "ms", ResultType.FLOAT),
|
|
110
|
+
"op_rate": (2, "ops", ResultType.INTEGER)
|
|
111
|
+
}
|
|
112
|
+
Rows = {
|
|
113
|
+
"mean": 1,
|
|
114
|
+
"p99": 2
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# Client code example
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
class LatencyResultTable(BaseResultTable):
|
|
121
|
+
class Meta:
|
|
122
|
+
table_name = "latency_percentile_write"
|
|
123
|
+
Columns = {
|
|
124
|
+
"latency": (1, "ms", ResultType.FLOAT),
|
|
125
|
+
"op_rate": (2, "ops", ResultType.INTEGER)
|
|
126
|
+
}
|
|
127
|
+
Rows = {
|
|
128
|
+
"mean": 1,
|
|
129
|
+
"p99": 2
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
result_table = LatencyResultTable()
|
|
133
|
+
|
|
134
|
+
result_table["mean"]["latency"] = Result(value=1.1, status=Status.WARNING)
|
|
135
|
+
result_table["mean"]["op_rate"] = Result(value=59988, status=Status.ERROR)
|
|
136
|
+
result_table["p99"]["latency"] = Result(value=2.7, status=Status.PASS)
|
|
137
|
+
result_table["p99"]["op_rate"] = Result(value=59988, status=Status.WARNING)
|
|
138
|
+
|
|
139
|
+
from argus.client.sct.client import ArgusSCTClient
|
|
140
|
+
|
|
141
|
+
run_id = UUID("24e09748-bba4-47fd-a615-bf7ea2c425eb")
|
|
142
|
+
client = ArgusSCTClient(run_id, auth_token="UO+2GXL9XqSgcVJijWk5WnbPXPit5ot5nfkLAHAr7SaqROfSCWycabpp/wxyY8+I", base_url="http://localhost:5000")
|
|
143
|
+
client.submit_results(result_table)
|
|
@@ -7,27 +7,28 @@ argus/backend/controller/admin.py,sha256=2z29RX7ZQO_VTklSKH9RrEj-Ag2SsvyOaIzWDKr
|
|
|
7
7
|
argus/backend/controller/admin_api.py,sha256=I2eQbU5f5rlQeL7eI4t9mRoxM3SJ5O-6fqLsHf0wcEU,6975
|
|
8
8
|
argus/backend/controller/api.py,sha256=HvjpR4ug5NXiTdBlrFTBeq57YQ5Vl_jca-IT35sAuas,14203
|
|
9
9
|
argus/backend/controller/auth.py,sha256=nwF_5uZLxPOCLm2ljLyw2OxGur6fmPM7WAAoGJLI_kk,2030
|
|
10
|
-
argus/backend/controller/client_api.py,sha256=
|
|
10
|
+
argus/backend/controller/client_api.py,sha256=AsPkZVjvogEVbNnhqVftvVX-1MACmzJuULAoUj_95bw,3366
|
|
11
11
|
argus/backend/controller/main.py,sha256=gYA0pqUBlLhpedkOetUX0V82qfe489frOEHPEtM-iHQ,8992
|
|
12
12
|
argus/backend/controller/notification_api.py,sha256=wz7V4nE6Mxclpq78P8gNnCyeQ7xA9BBJjZ-dPhLLd2I,1964
|
|
13
13
|
argus/backend/controller/notifications.py,sha256=zMSJln72BGU6Q_nQvJesMnuvJ57Ucbov4M2ZI-37Bxo,290
|
|
14
14
|
argus/backend/controller/team.py,sha256=G6LdIBaYgfG0Qr4RhNQ53MZVdh4wcuotsIIpFwhTJ3w,3101
|
|
15
15
|
argus/backend/controller/team_ui.py,sha256=B7N1_Kzl6Rac8BV3FbKj55pGAS_dht47rYhAi94PC8A,589
|
|
16
|
-
argus/backend/controller/testrun_api.py,sha256=
|
|
16
|
+
argus/backend/controller/testrun_api.py,sha256=nJZz2VhyNUWMyHdOmnK-BbxT7ifMLrMPZl8qh9tjVpM,12571
|
|
17
17
|
argus/backend/controller/view_api.py,sha256=rI7LwcS7keK37nYx76D9StFV_rLHcNkHan8OhFgBrhM,4106
|
|
18
18
|
argus/backend/db.py,sha256=bBiraYD05Qex28yZHjSP1bRlcMsc6oTYGt792zXmaHo,4101
|
|
19
19
|
argus/backend/error_handlers.py,sha256=IEjz7Vzfldv1PTOeHrpRWmRsgBrHtAW0PXHUJZDovAE,480
|
|
20
20
|
argus/backend/events/event_processors.py,sha256=bsmBayiXvlGn3aqiT2z9WgwnVBRtn2cRqkgn4pLodck,1291
|
|
21
21
|
argus/backend/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
-
argus/backend/models/
|
|
22
|
+
argus/backend/models/result.py,sha256=jkdwC7bP_KOysAnfB6YcgKIFX_9Tya9Hpe_Zw-YU3x8,1319
|
|
23
|
+
argus/backend/models/web.py,sha256=4K1Gj70nugmuW3sv0Sv5M_sVSmEhfJgxRE670qChGzo,13095
|
|
23
24
|
argus/backend/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
argus/backend/plugins/core.py,sha256=
|
|
25
|
-
argus/backend/plugins/driver_matrix_tests/controller.py,sha256=
|
|
26
|
-
argus/backend/plugins/driver_matrix_tests/model.py,sha256=
|
|
25
|
+
argus/backend/plugins/core.py,sha256=p5VZ3OJPWf3IUzNZbVKW7pG5iysrtTvOCFX3C1HMU0Y,8678
|
|
26
|
+
argus/backend/plugins/driver_matrix_tests/controller.py,sha256=9Q6QCripzM528SGsQnYupM6k7HW7hUzkNyjJFiXXfEw,739
|
|
27
|
+
argus/backend/plugins/driver_matrix_tests/model.py,sha256=8iq9YqHcSSur2N4nZtD6Fn-07F5NN7uVxAu5rauCzUA,7025
|
|
27
28
|
argus/backend/plugins/driver_matrix_tests/plugin.py,sha256=72ESU7s8C6ovVMfJTlYwtaokdvRp_HJF1_czm1UMhKg,745
|
|
28
|
-
argus/backend/plugins/driver_matrix_tests/raw_types.py,sha256=
|
|
29
|
-
argus/backend/plugins/driver_matrix_tests/service.py,sha256=
|
|
30
|
-
argus/backend/plugins/driver_matrix_tests/udt.py,sha256=
|
|
29
|
+
argus/backend/plugins/driver_matrix_tests/raw_types.py,sha256=A108HCnv5q0RHfNRhUJrTpRy3fG7sPxr9Sk4gfsbooU,600
|
|
30
|
+
argus/backend/plugins/driver_matrix_tests/service.py,sha256=dxb8VGTJLIyVqVrZ4RtzCXgnmS2qg2RAGhcp_SARt9I,1737
|
|
31
|
+
argus/backend/plugins/driver_matrix_tests/udt.py,sha256=6lydzF3AQHm3GR5bKEDu1xLPYsLaBL3o-wu9NpabbqA,1134
|
|
31
32
|
argus/backend/plugins/generic/model.py,sha256=QLVO7QhGr38Hz0VO-BlDYF7LhRX7Pl049vw4W_VMT8o,3302
|
|
32
33
|
argus/backend/plugins/generic/plugin.py,sha256=5URbQVUCizrk-KZqb6I0P_8nLUekjYh-Js7ZLKVoBAA,407
|
|
33
34
|
argus/backend/plugins/generic/types.py,sha256=jlZUcQ7r153ziyl3ZJmix7AzL2G1aX9N_z-4Kw9trWc,267
|
|
@@ -36,7 +37,7 @@ argus/backend/plugins/sct/controller.py,sha256=NF11JLoUJ13whghlxRrVex9rLMgFtlkcz
|
|
|
36
37
|
argus/backend/plugins/sct/plugin.py,sha256=_sOMcXLoFfeG9jwj_t48C4IFvY87juK8ApR6tfSw6q4,1007
|
|
37
38
|
argus/backend/plugins/sct/resource_setup.py,sha256=hwfAOu-oKOH42tjtzJhiqwq_MtUE9_HevoFyql8JKqY,10120
|
|
38
39
|
argus/backend/plugins/sct/service.py,sha256=ygAL85BkyyovJ1xHktlCQJdJS8CrerJZ_Tbr3EXqsg4,22021
|
|
39
|
-
argus/backend/plugins/sct/testrun.py,sha256=
|
|
40
|
+
argus/backend/plugins/sct/testrun.py,sha256=u0jDFVMo1v4w0_IXRp3BzIjgDIScre20fd3bCLzZl-c,10166
|
|
40
41
|
argus/backend/plugins/sct/types.py,sha256=Gw1y4iqYguqNqTh_GopLDFho8vuGaOGuK7fjaHYhAOQ,1326
|
|
41
42
|
argus/backend/plugins/sct/udt.py,sha256=V_x8_yw8rV7Q_QRBYayqtTNsPdZvjzOxWpRhXP1XAzs,3119
|
|
42
43
|
argus/backend/plugins/sirenada/model.py,sha256=KVnI75BacuBryc5lR_Aai-mEOs7CB9xxhb7J-YRU3bc,4705
|
|
@@ -45,14 +46,14 @@ argus/backend/plugins/sirenada/types.py,sha256=Gm3XMK9YJoozVaeM9XE7n8iRxA6PKBrS2
|
|
|
45
46
|
argus/backend/service/admin.py,sha256=_VnWl3CkZBOAie_pPbd9sbXZUpBf2SApyNoFZLfB_QI,637
|
|
46
47
|
argus/backend/service/argus_service.py,sha256=d1NrCQmcE_1NmkV761dheBL43Vk0n-oP0S4IZtQzJUQ,28832
|
|
47
48
|
argus/backend/service/build_system_monitor.py,sha256=E8Ro1HBMfktxqcCHLPuXwzohkdgrls7pYro-VSG_CMs,8248
|
|
48
|
-
argus/backend/service/client_service.py,sha256=
|
|
49
|
+
argus/backend/service/client_service.py,sha256=shcDLlCxHNUBuYxE8sjkAPfzqLict-BDllCVlUm8mFs,3273
|
|
49
50
|
argus/backend/service/event_service.py,sha256=iYeqxN2QCYTjYB1WPPv4BEFLXG0Oz3TvskkaK4v9pVY,654
|
|
50
51
|
argus/backend/service/jenkins_service.py,sha256=PKmu44kAEWl-Dw7f8gXkKhwRkV4yWy-B4CmFxv7c6-8,9685
|
|
51
52
|
argus/backend/service/notification_manager.py,sha256=h00Ej_-hH9H7pq0wah_1TH8dnpPyPNsgVJNO1rwJi7o,7011
|
|
52
53
|
argus/backend/service/release_manager.py,sha256=d1J6llBb4aKgFPrsPTPYpV9NnGx772jeORZjs-ojYGE,7771
|
|
53
54
|
argus/backend/service/stats.py,sha256=-V94A8EUlQBvwG53oJTL4U1EzR4vciEF7Niu-efTL6Y,22713
|
|
54
55
|
argus/backend/service/team_manager_service.py,sha256=zY5dvy3ffvQbJuXBvlWKE5dS5LQ3ss6tkFE-cwFZsdw,3010
|
|
55
|
-
argus/backend/service/testrun.py,sha256=
|
|
56
|
+
argus/backend/service/testrun.py,sha256=RmkjdcaKiFguYwIw3g6tFZHIIw5NdVefqXkhdYc4vDw,22310
|
|
56
57
|
argus/backend/service/user.py,sha256=N3t43rgKMnSsPXU5R9bigEEGbPjYrc6MsJtof3z7kDE,9027
|
|
57
58
|
argus/backend/service/views.py,sha256=amkyOQgHSP3YEDqiFHAhOiNzNCLk2sLkruTB2_IagDQ,11244
|
|
58
59
|
argus/backend/template_filters.py,sha256=04PHl0DiN4PBHQ82HMAmTfww09fGMXcYy-I5BU_b1s4,682
|
|
@@ -64,11 +65,12 @@ argus/backend/util/logsetup.py,sha256=XWyFLC1_J5G8NcR7oC9l-Pf02ybAvEZR95y5LoY4W4
|
|
|
64
65
|
argus/backend/util/module_loaders.py,sha256=AcIlX-VRmUQ2THFKT8DLefLSE62Eub2hCxIosg3WgE0,698
|
|
65
66
|
argus/backend/util/send_email.py,sha256=Bb2Hta7WlBCvDKga0_WPFWgxWJEfKpectOGypgf9xzo,3217
|
|
66
67
|
argus/client/__init__.py,sha256=bO9_j5_jK5kvTHR46KEZ0Y-p0li7CBW8QSd-K5Ez4vA,42
|
|
67
|
-
argus/client/base.py,sha256
|
|
68
|
-
argus/client/driver_matrix_tests/
|
|
69
|
-
argus/client/driver_matrix_tests/client.py,sha256=UPryBku2rg6IV2wKKDkclXHnH3r6EYwWdds65wLC-KU,2748
|
|
68
|
+
argus/client/base.py,sha256=etjUxHIlbGNTrvPtvFQYKFYGazuo7avnP8Z__NI6ZyE,7358
|
|
69
|
+
argus/client/driver_matrix_tests/client.py,sha256=ekuTkEZbcVqhg6DAC6PvGuC9bXuu4DSOjk96vcLzmuQ,7817
|
|
70
70
|
argus/client/generic/cli.py,sha256=IJkgEZ5VOAeqp5SlLM13Y5m8e34Cqnyz8WkfeKoN7so,2208
|
|
71
71
|
argus/client/generic/client.py,sha256=l4PDjDy65Mm2OI9ZLSnyd8_2i4Ei1Pp9yRt3bRX8s2Y,1114
|
|
72
|
+
argus/client/generic_result.py,sha256=Xg3MwwulHH7dGk_pwwbVOm4cHzV4qbfHaFD0VteFVHI,2666
|
|
73
|
+
argus/client/generic_result_old.py,sha256=Oi15Gu8WbXK_WruF0IU-Fokr-I1k8mzg1MpHbmpt50M,4662
|
|
72
74
|
argus/client/sct/client.py,sha256=DtRA0Ra3ycUcedDYfZZW1jER0nc8vdYHaY6DT0te4x0,11341
|
|
73
75
|
argus/client/sct/types.py,sha256=VLgVe7qPmJtCLqtPnuX8N8kMKZq-iY3SKz68nvU6nJ4,371
|
|
74
76
|
argus/client/sirenada/client.py,sha256=ilcyLXJb-0gKbmb9WSPr-Yvldh73joGBhRDoilQoSJ4,6220
|
|
@@ -80,8 +82,8 @@ argus/db/db_types.py,sha256=iLbmrUaDzrBw0kDCnvW0FSZ9-kNc3uQY-fsbIPymV4E,3612
|
|
|
80
82
|
argus/db/interface.py,sha256=HroyA1Yijz5cXLdYbxorHCEu0GH9VeMMqB36IHTlcew,17146
|
|
81
83
|
argus/db/testrun.py,sha256=0YG7FIH5FLQeNlYULxC6rhhyru2rziSMe3qKtYzTBnc,26014
|
|
82
84
|
argus/db/utils.py,sha256=YAWsuLjUScSgKgdaL5aF4Sgr13gqH29Mb5cLctX4V_w,337
|
|
83
|
-
argus_alm-0.12.
|
|
84
|
-
argus_alm-0.12.
|
|
85
|
-
argus_alm-0.12.
|
|
86
|
-
argus_alm-0.12.
|
|
87
|
-
argus_alm-0.12.
|
|
85
|
+
argus_alm-0.12.5.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
86
|
+
argus_alm-0.12.5.dist-info/METADATA,sha256=IaMdyKUclP6Zdzy58_4wulhMbGQIOblEXaMnGAKcEa8,3508
|
|
87
|
+
argus_alm-0.12.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
88
|
+
argus_alm-0.12.5.dist-info/entry_points.txt,sha256=zEqrAK95P8AAhKbwO4lgrQzKBWqCzHH9zlUPCaCVHoQ,69
|
|
89
|
+
argus_alm-0.12.5.dist-info/RECORD,,
|