"""Describes results.
Is a JSON.
"""
import json
import os
from planemo.io import error
[docs]
class StructuredData:
"""Abstraction around a simple data structure describing test results."""
def __init__(self, json_path=None, data=None):
"""Create a :class:`StructuredData` from a JSON file."""
def data_error():
error(
"An invalid JSON for structured test result data - "
"summary information and planemo reports will be "
"incorrect."
)
self.json_path = json_path
structured_data = {}
structured_data_tests = {}
if json_path and os.path.exists(json_path) and data is None:
try:
with open(json_path) as output_json_f:
data = json.load(output_json_f)
except Exception:
data_error()
try:
structured_data = data
structured_data_tests = structured_data["tests"]
except Exception:
data_error()
self.structured_data = structured_data
self.structured_data_tests = structured_data_tests
structured_data_by_id = {}
for test in self.structured_data_tests:
structured_data_by_id[test["id"]] = test["data"]
self.structured_data_by_id = structured_data_by_id
self.has_details = "summary" in structured_data
if self.has_details:
self.read_summary()
[docs]
def update(self):
"""Write out an updated version of this data structure to supplied json path."""
with open(self.json_path, "w") as out_f:
json.dump(self.structured_data, out_f)
[docs]
def set_exit_code(self, exit_code):
"""Set the exit_code for the this test."""
self.structured_data["exit_code"] = exit_code
[docs]
def calculate_summary_data_if_needed(self):
if "summary" not in self.structured_data:
self.calculate_summary_data()
[docs]
def calculate_summary_data(self):
"""Use full details on individual test data to update structured data with summary info."""
num_tests = 0
num_failures = 0
num_skips = 0
num_errors = 0
for test in self.structured_data_tests:
test_data = get_dict_value("data", test)
status = get_dict_value("status", test_data)
num_tests += 1
if status == "skip":
num_skips += 1
elif status == "failure":
num_failures += 1
elif status == "error":
num_errors += 1
elif status != "success":
raise Exception("Unknown test status encountered [%s]" % status)
summary = {}
summary["num_tests"] = num_tests
summary["num_failures"] = num_failures
summary["num_skips"] = num_skips
summary["num_errors"] = num_errors
self.structured_data["summary"] = summary
self.read_summary()
[docs]
def read_summary(self):
"""Read summary data into properties on this class."""
summary = self.structured_data["summary"]
num_tests = summary["num_tests"]
num_failures = summary["num_failures"]
num_skips = summary["num_skips"]
num_errors = summary["num_errors"]
self.num_tests = num_tests
self.num_problems = num_skips + num_errors + num_failures
self.exit_code = self.structured_data.get("exit_code", None)
@property
def failed_ids(self):
"""Find set of IDs for failed tests."""
ids = set()
for test_data in self.structured_data_tests:
if test_data["data"]["status"] == "success":
continue
test_case = test_data["id"].replace(".test_toolbox.", ".test_toolbox:")
ids.add(test_case)
return ids
[docs]
def get_dict_value(key, data):
"""Return data[key] with improved KeyError."""
try:
return data[key]
except (KeyError, TypeError):
raise KeyError(f"No key [{key}] in [{data}]")
__all__ = (
"StructuredData",
"get_dict_value",
)