mustrd 0.3.3__tar.gz → 0.3.4a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mustrd-0.3.3 → mustrd-0.3.4a1}/PKG-INFO +1 -1
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/model/mustrdShapes.ttl +1 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/mustrd.py +25 -20
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/mustrdTestPlugin.py +22 -70
- {mustrd-0.3.3 → mustrd-0.3.4a1}/pyproject.toml +1 -1
- {mustrd-0.3.3 → mustrd-0.3.4a1}/LICENSE +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/README.md +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/README.adoc +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/README.md +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/TestResult.py +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/__init__.py +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/anzo_utils.py +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/logger_setup.py +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/model/catalog-v001.xml +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/model/mustrdTestOntology.ttl +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/model/mustrdTestShapes.ttl +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/model/ontology.ttl +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/model/test-resources/resources.ttl +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/model/triplestoreOntology.ttl +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/model/triplestoreshapes.ttl +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/mustrdAnzo.py +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/mustrdGraphDb.py +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/mustrdRdfLib.py +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/namespace.py +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/run.py +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/spec_component.py +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/steprunner.py +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/templates/md_ResultList_leaf_template.jinja +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/templates/md_ResultList_template.jinja +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/templates/md_stats_template.jinja +0 -0
- {mustrd-0.3.3 → mustrd-0.3.4a1}/mustrd/utils.py +0 -0
@@ -134,7 +134,7 @@ class TripleStoreConnectionError(SpecResult):
|
|
134
134
|
|
135
135
|
|
136
136
|
@dataclass
|
137
|
-
class
|
137
|
+
class SpecInvalid(SpecResult):
|
138
138
|
message: str
|
139
139
|
spec_file_name: str = "default.mustrd.ttl"
|
140
140
|
spec_source_file: Path = Path("default.mustrd.ttl")
|
@@ -204,6 +204,12 @@ def validate_specs(
|
|
204
204
|
f"Could not extract spec from {file} due to exception of type "
|
205
205
|
f"{type(e).__name__} when parsing file"
|
206
206
|
]
|
207
|
+
invalid_specs += [
|
208
|
+
SpecInvalid(
|
209
|
+
"urn:invalid_spec_file", triple_store["type"], message, file.name, file
|
210
|
+
)
|
211
|
+
for triple_store in triple_stores
|
212
|
+
]
|
207
213
|
continue
|
208
214
|
|
209
215
|
# run shacl validation
|
@@ -310,7 +316,7 @@ def add_spec_validation(
|
|
310
316
|
error_messages.sort()
|
311
317
|
error_message = "\n".join(msg for msg in error_messages)
|
312
318
|
invalid_specs += [
|
313
|
-
|
319
|
+
SpecInvalid(
|
314
320
|
subject_uri, triple_store["type"], error_message, file.name, file
|
315
321
|
)
|
316
322
|
for triple_store in triple_stores
|
@@ -324,15 +330,15 @@ def get_specs(
|
|
324
330
|
run_config: dict,
|
325
331
|
):
|
326
332
|
specs = []
|
327
|
-
|
333
|
+
invalid_spec = []
|
328
334
|
try:
|
329
335
|
for triple_store in triple_stores:
|
330
336
|
if "error" in triple_store:
|
331
337
|
log.error(
|
332
338
|
f"{triple_store['error']}. No specs run for this triple store."
|
333
339
|
)
|
334
|
-
|
335
|
-
|
340
|
+
invalid_spec += [
|
341
|
+
SpecInvalid(
|
336
342
|
spec_uri,
|
337
343
|
triple_store["type"],
|
338
344
|
triple_store["error"],
|
@@ -360,8 +366,8 @@ def get_specs(
|
|
360
366
|
)
|
361
367
|
or "unknown"
|
362
368
|
)
|
363
|
-
|
364
|
-
|
369
|
+
invalid_spec += [
|
370
|
+
SpecInvalid(
|
365
371
|
spec_uri,
|
366
372
|
triple_store["type"],
|
367
373
|
str(e),
|
@@ -380,7 +386,7 @@ def get_specs(
|
|
380
386
|
log.error("No specifications will be run.")
|
381
387
|
|
382
388
|
log.info(f"Extracted {len(specs)} specifications that will be run")
|
383
|
-
return specs,
|
389
|
+
return specs, invalid_spec
|
384
390
|
|
385
391
|
|
386
392
|
def run_specs(specs) -> List[SpecResult]:
|
@@ -505,7 +511,7 @@ def run_spec(spec: Specification) -> SpecResult:
|
|
505
511
|
upload_given(triple_store, spec.given)
|
506
512
|
else:
|
507
513
|
if triple_store["type"] == TRIPLESTORE.RdfLib:
|
508
|
-
return
|
514
|
+
return SpecInvalid(
|
509
515
|
spec_uri,
|
510
516
|
triple_store["type"],
|
511
517
|
"Unable to run Inherited State tests on Rdflib",
|
@@ -526,7 +532,6 @@ def run_spec(spec: Specification) -> SpecResult:
|
|
526
532
|
except NotImplementedError as ex:
|
527
533
|
log.error(f"NotImplementedError {ex}")
|
528
534
|
raise ex
|
529
|
-
# return SpecSkipped(spec_uri, triple_store["type"], ex.args[0])
|
530
535
|
return check_result(spec, result)
|
531
536
|
except (ConnectionError, TimeoutError, HTTPError, ConnectTimeout, OSError) as e:
|
532
537
|
# close_connection = False
|
@@ -948,8 +953,8 @@ def write_result_diff_to_log(res, info):
|
|
948
953
|
):
|
949
954
|
info(f"{Fore.RED}Failed {res.spec_uri} {res.triple_store}")
|
950
955
|
info(res.exception)
|
951
|
-
if isinstance(res,
|
952
|
-
info(f"{Fore.
|
956
|
+
if isinstance(res, SpecInvalid):
|
957
|
+
info(f"{Fore.RED} Invalid {res.spec_uri} {res.triple_store}")
|
953
958
|
info(res.message)
|
954
959
|
|
955
960
|
|
@@ -1046,7 +1051,7 @@ def review_results(results: List[SpecResult], verbose: bool) -> None:
|
|
1046
1051
|
colours = {
|
1047
1052
|
SpecPassed: Fore.GREEN,
|
1048
1053
|
SpecPassedWithWarning: Fore.YELLOW,
|
1049
|
-
|
1054
|
+
SpecInvalid: Fore.RED,
|
1050
1055
|
}
|
1051
1056
|
# Populate dictionaries from results
|
1052
1057
|
for result in results:
|
@@ -1103,12 +1108,12 @@ def review_results(results: List[SpecResult], verbose: bool) -> None:
|
|
1103
1108
|
|
1104
1109
|
pass_count = statuses.count(SpecPassed)
|
1105
1110
|
warning_count = statuses.count(SpecPassedWithWarning)
|
1106
|
-
|
1111
|
+
invalid_count = statuses.count(SpecInvalid)
|
1107
1112
|
fail_count = len(
|
1108
1113
|
list(
|
1109
1114
|
filter(
|
1110
1115
|
lambda status: status
|
1111
|
-
not in [SpecPassed, SpecPassedWithWarning,
|
1116
|
+
not in [SpecPassed, SpecPassedWithWarning, SpecInvalid],
|
1112
1117
|
statuses,
|
1113
1118
|
)
|
1114
1119
|
)
|
@@ -1116,18 +1121,18 @@ def review_results(results: List[SpecResult], verbose: bool) -> None:
|
|
1116
1121
|
|
1117
1122
|
if fail_count:
|
1118
1123
|
overview_colour = Fore.RED
|
1119
|
-
elif warning_count or
|
1124
|
+
elif warning_count or invalid_count:
|
1120
1125
|
overview_colour = Fore.YELLOW
|
1121
1126
|
else:
|
1122
1127
|
overview_colour = Fore.GREEN
|
1123
1128
|
|
1124
1129
|
logger_setup.flush()
|
1125
1130
|
log.info(
|
1126
|
-
f"{overview_colour}===== {fail_count} failures, {
|
1131
|
+
f"{overview_colour}===== {fail_count} failures, {invalid_count} invalid, {Fore.GREEN}{pass_count} passed, "
|
1127
1132
|
f"{overview_colour}{warning_count} passed with warnings ====="
|
1128
1133
|
)
|
1129
1134
|
|
1130
|
-
if verbose and (fail_count or warning_count or
|
1135
|
+
if verbose and (fail_count or warning_count or invalid_count):
|
1131
1136
|
display_verbose(results)
|
1132
1137
|
|
1133
1138
|
|
@@ -1165,8 +1170,8 @@ def display_verbose(results: List[SpecResult]):
|
|
1165
1170
|
):
|
1166
1171
|
log.info(f"{Fore.RED}Failed {res.spec_uri} {res.triple_store}")
|
1167
1172
|
log.info(res.exception)
|
1168
|
-
if isinstance(res,
|
1169
|
-
log.info(f"{Fore.YELLOW}
|
1173
|
+
if isinstance(res, SpecInvalid):
|
1174
|
+
log.info(f"{Fore.YELLOW}Invalid {res.spec_uri} {res.triple_store}")
|
1170
1175
|
log.info(res.message)
|
1171
1176
|
|
1172
1177
|
|
@@ -2,7 +2,7 @@ import logging
|
|
2
2
|
from dataclasses import dataclass
|
3
3
|
import pytest
|
4
4
|
import os
|
5
|
-
from pathlib import Path
|
5
|
+
from pathlib import Path
|
6
6
|
from rdflib.namespace import Namespace
|
7
7
|
from rdflib import Graph, RDF
|
8
8
|
from pytest import Session
|
@@ -11,20 +11,16 @@ from mustrd import logger_setup
|
|
11
11
|
from mustrd.TestResult import ResultList, TestResult, get_result_list
|
12
12
|
from mustrd.utils import get_mustrd_root
|
13
13
|
from mustrd.mustrd import (
|
14
|
-
write_result_diff_to_log,
|
15
|
-
get_triple_store_graph,
|
16
|
-
get_triple_stores,
|
17
|
-
)
|
18
|
-
from mustrd.mustrd import (
|
19
|
-
Specification,
|
20
|
-
SpecSkipped,
|
21
14
|
validate_specs,
|
22
15
|
get_specs,
|
23
16
|
SpecPassed,
|
24
17
|
run_spec,
|
18
|
+
write_result_diff_to_log,
|
19
|
+
get_triple_store_graph,
|
20
|
+
get_triple_stores,
|
21
|
+
SpecInvalid
|
25
22
|
)
|
26
23
|
from mustrd.namespace import MUST, TRIPLESTORE, MUSTRDTEST
|
27
|
-
from typing import Union
|
28
24
|
from pyshacl import validate
|
29
25
|
|
30
26
|
import pathlib
|
@@ -171,13 +167,6 @@ class TestConfig:
|
|
171
167
|
filter_on_tripleStore: str = None
|
172
168
|
|
173
169
|
|
174
|
-
@dataclass(frozen=True)
|
175
|
-
class TestParamWrapper:
|
176
|
-
id: str
|
177
|
-
test_config: TestConfig
|
178
|
-
unit_test: Union[Specification, SpecSkipped]
|
179
|
-
|
180
|
-
|
181
170
|
# Configure logging
|
182
171
|
logger = logger_setup.setup_logger(__name__)
|
183
172
|
|
@@ -187,7 +176,6 @@ class MustrdTestPlugin:
|
|
187
176
|
test_config_file: Path
|
188
177
|
selected_tests: list
|
189
178
|
secrets: str
|
190
|
-
unit_tests: Union[Specification, SpecSkipped]
|
191
179
|
items: list
|
192
180
|
path_filter: str
|
193
181
|
collect_error: BaseException
|
@@ -201,18 +189,17 @@ class MustrdTestPlugin:
|
|
201
189
|
@pytest.hookimpl(tryfirst=True)
|
202
190
|
def pytest_collection(self, session):
|
203
191
|
logger.info("Starting test collection")
|
204
|
-
|
205
|
-
self.unit_tests = []
|
192
|
+
|
206
193
|
args = session.config.args
|
207
|
-
|
194
|
+
|
208
195
|
# Split args into mustrd and regular pytest args
|
209
196
|
mustrd_args = [arg for arg in args if ".mustrd.ttl" in arg]
|
210
197
|
pytest_args = [arg for arg in args if arg != os.getcwd() and ".mustrd.ttl" not in arg]
|
211
|
-
|
198
|
+
|
212
199
|
self.selected_tests = list(
|
213
200
|
map(
|
214
201
|
lambda arg: Path(arg.split("::")[0]),
|
215
|
-
mustrd_args
|
202
|
+
mustrd_args
|
216
203
|
)
|
217
204
|
)
|
218
205
|
logger.info(f"selected_tests is: {self.selected_tests}")
|
@@ -237,7 +224,7 @@ class MustrdTestPlugin:
|
|
237
224
|
|
238
225
|
def get_file_name_from_arg(self, arg):
|
239
226
|
if arg and len(arg) > 0 and "[" in arg and ".mustrd.ttl " in arg:
|
240
|
-
return arg[arg.index("[") + 1
|
227
|
+
return arg[arg.index("[") + 1: arg.index(".mustrd.ttl ")]
|
241
228
|
return None
|
242
229
|
|
243
230
|
@pytest.hookimpl
|
@@ -247,9 +234,9 @@ class MustrdTestPlugin:
|
|
247
234
|
if not str(path).endswith('.ttl'):
|
248
235
|
return None
|
249
236
|
if Path(path).resolve() != Path(self.test_config_file).resolve():
|
250
|
-
|
251
|
-
|
252
|
-
|
237
|
+
logger.debug(f"{self.test_config_file}: Skipping non-matching-config file: {path}")
|
238
|
+
return None
|
239
|
+
|
253
240
|
mustrd_file = MustrdFile.from_parent(parent, path=pathlib.Path(path), mustrd_plugin=self)
|
254
241
|
mustrd_file.mustrd_plugin = self
|
255
242
|
return mustrd_file
|
@@ -264,7 +251,7 @@ class MustrdTestPlugin:
|
|
264
251
|
logger.debug("Generating tests for config: " + str(config))
|
265
252
|
logger.debug(f"selected_tests {self.selected_tests}")
|
266
253
|
|
267
|
-
valid_spec_uris, spec_graph,
|
254
|
+
valid_spec_uris, spec_graph, invalid_specs = validate_specs(
|
268
255
|
config,
|
269
256
|
triple_stores,
|
270
257
|
shacl_graph,
|
@@ -272,17 +259,6 @@ class MustrdTestPlugin:
|
|
272
259
|
file_name or "*",
|
273
260
|
selected_test_files=self.selected_tests,
|
274
261
|
)
|
275
|
-
# Convert invalid specs to SpecInvalid instead of SpecSkipped
|
276
|
-
invalid_specs = [
|
277
|
-
SpecInvalid(
|
278
|
-
spec.spec_uri,
|
279
|
-
spec.triple_store,
|
280
|
-
spec.message,
|
281
|
-
spec.spec_file_name,
|
282
|
-
spec.spec_source_file
|
283
|
-
) for spec in invalid_spec_results
|
284
|
-
]
|
285
|
-
|
286
262
|
|
287
263
|
specs, skipped_spec_results = get_specs(
|
288
264
|
valid_spec_uris, spec_graph, triple_stores, config
|
@@ -291,18 +267,6 @@ class MustrdTestPlugin:
|
|
291
267
|
# Return normal specs + skipped results
|
292
268
|
return specs + skipped_spec_results + invalid_specs
|
293
269
|
|
294
|
-
# Function called to generate the name of the test
|
295
|
-
def get_test_name(self, spec):
|
296
|
-
# FIXME: SpecSkipped should have the same structure?
|
297
|
-
if isinstance(spec, SpecSkipped):
|
298
|
-
triple_store = spec.triple_store
|
299
|
-
else:
|
300
|
-
triple_store = spec.triple_store["type"]
|
301
|
-
|
302
|
-
triple_store_name = triple_store.replace("https://mustrd.com/model/", "")
|
303
|
-
test_name = spec.spec_uri.replace(spnamespace, "").replace("_", " ")
|
304
|
-
return spec.spec_file_name + " : " + triple_store_name + ": " + test_name
|
305
|
-
|
306
270
|
# Get triple store configuration or default
|
307
271
|
def get_triple_stores_from_file(self, test_config):
|
308
272
|
if test_config.triplestore_spec_path:
|
@@ -397,13 +361,6 @@ class MustrdTestPlugin:
|
|
397
361
|
with open(self.md_path, "w") as file:
|
398
362
|
file.write(md)
|
399
363
|
|
400
|
-
@dataclass(frozen=True)
|
401
|
-
class SpecInvalid:
|
402
|
-
spec_uri: str
|
403
|
-
triple_store: str
|
404
|
-
message: str
|
405
|
-
spec_file_name: str = None
|
406
|
-
spec_source_file: Path = None
|
407
364
|
|
408
365
|
class MustrdFile(pytest.File):
|
409
366
|
mustrd_plugin: MustrdTestPlugin
|
@@ -417,14 +374,14 @@ class MustrdFile(pytest.File):
|
|
417
374
|
try:
|
418
375
|
logger.info(f"{self.mustrd_plugin.test_config_file}: Collecting tests from file: {self.path=}")
|
419
376
|
# Only process the specific mustrd config file we were given
|
420
|
-
|
377
|
+
|
421
378
|
# if not str(self.fspath).endswith(".ttl"):
|
422
379
|
# return []
|
423
380
|
# Only process the specific mustrd config file we were given
|
424
381
|
# if str(self.fspath) != str(self.mustrd_plugin.test_config_file):
|
425
382
|
# logger.info(f"Skipping non-config file: {self.fspath}")
|
426
383
|
# return []
|
427
|
-
|
384
|
+
|
428
385
|
test_configs = parse_config(self.path)
|
429
386
|
from collections import defaultdict
|
430
387
|
pytest_path_grouped = defaultdict(list)
|
@@ -435,7 +392,7 @@ class MustrdFile(pytest.File):
|
|
435
392
|
):
|
436
393
|
logger.info(f"Skipping test config due to path filter: {test_config.pytest_path=} {self.mustrd_plugin.path_filter=}")
|
437
394
|
continue
|
438
|
-
|
395
|
+
|
439
396
|
triple_stores = self.mustrd_plugin.get_triple_stores_from_file(test_config)
|
440
397
|
try:
|
441
398
|
specs = self.mustrd_plugin.generate_tests_for_config(
|
@@ -521,7 +478,7 @@ class MustrdItem(pytest.Item):
|
|
521
478
|
f"Error: \n{excinfo.value}\n"
|
522
479
|
f"Traceback:\n{tb_str}"
|
523
480
|
)
|
524
|
-
|
481
|
+
|
525
482
|
def reportinfo(self):
|
526
483
|
r = "", 0, f"mustrd test: {self.name}"
|
527
484
|
return r
|
@@ -531,9 +488,6 @@ class MustrdItem(pytest.Item):
|
|
531
488
|
def run_test_spec(test_spec):
|
532
489
|
logger = logging.getLogger("mustrd.test")
|
533
490
|
logger.info(f"Running test spec: {getattr(test_spec, 'spec_uri', test_spec)}")
|
534
|
-
if isinstance(test_spec, SpecSkipped):
|
535
|
-
logger.warning(f"Test skipped: {test_spec.message}")
|
536
|
-
pytest.skip(f"Invalid configuration, error : {test_spec.message}")
|
537
491
|
try:
|
538
492
|
result = run_spec(test_spec)
|
539
493
|
logger.info(f"Result type: {type(result)} for spec: {getattr(test_spec, 'spec_uri', test_spec)}")
|
@@ -544,13 +498,11 @@ def run_test_spec(test_spec):
|
|
544
498
|
|
545
499
|
if isinstance(test_spec, SpecInvalid):
|
546
500
|
logger.error(f"Invalid test specification: {test_spec.message} {test_spec}")
|
547
|
-
|
548
|
-
if
|
549
|
-
logger.warning("Test skipped due to unsupported configuration")
|
550
|
-
pytest.skip("Unsupported configuration")
|
551
|
-
if type(result) != SpecPassed:
|
501
|
+
pytest.fail(f"Invalid test specification: {test_spec.message} {test_spec}")
|
502
|
+
if not isinstance(result, SpecPassed):
|
552
503
|
write_result_diff_to_log(result, logger.info)
|
553
504
|
log_lines = []
|
505
|
+
|
554
506
|
def log_to_string(message):
|
555
507
|
log_lines.append(message)
|
556
508
|
try:
|
@@ -562,4 +514,4 @@ def run_test_spec(test_spec):
|
|
562
514
|
raise AssertionError("Test failed: " + "\n".join(log_lines))
|
563
515
|
|
564
516
|
logger.info(f"Test PASSED: {getattr(test_spec, 'spec_uri', test_spec)}")
|
565
|
-
return
|
517
|
+
return isinstance(result, SpecPassed)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|