Mercurial > repos > bcclaywell > argo_navis
comparison venv/lib/python2.7/site-packages/planemo/galaxy_test/structures.py @ 0:d67268158946 draft
planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
| author | bcclaywell |
|---|---|
| date | Mon, 12 Oct 2015 17:43:33 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:d67268158946 |
|---|---|
| 1 """ Utilities for reasoning about Galaxy test results. | |
| 2 """ | |
| 3 from __future__ import print_function | |
| 4 from __future__ import absolute_import | |
| 5 | |
| 6 from collections import namedtuple | |
| 7 import json | |
| 8 import xml.etree.ElementTree as ET | |
| 9 | |
| 10 RUN_TESTS_CMD = ( | |
| 11 "sh run_tests.sh --report_file %s %s %s %s" | |
| 12 ) | |
| 13 | |
| 14 | |
| 15 class GalaxyTestCommand(object): | |
| 16 | |
| 17 def __init__( | |
| 18 self, | |
| 19 html_report_file, | |
| 20 xunit_report_file, | |
| 21 structured_report_file, | |
| 22 failed=False, | |
| 23 installed=False, | |
| 24 ): | |
| 25 self.html_report_file = html_report_file | |
| 26 self.xunit_report_file = xunit_report_file | |
| 27 self.structured_report_file = structured_report_file | |
| 28 self.failed = failed | |
| 29 self.installed = installed | |
| 30 | |
| 31 def build(self): | |
| 32 xunit_report_file = self.xunit_report_file | |
| 33 sd_report_file = self.structured_report_file | |
| 34 html_report_file = self.html_report_file | |
| 35 if xunit_report_file: | |
| 36 xunit_arg = "--xunit_report_file %s" % xunit_report_file | |
| 37 else: | |
| 38 xunit_arg = "" | |
| 39 if sd_report_file: | |
| 40 sd_arg = "--structured_data_report_file %s" % sd_report_file | |
| 41 else: | |
| 42 sd_arg = "" | |
| 43 if self.installed: | |
| 44 tests = "-installed" | |
| 45 else: | |
| 46 tests = "functional.test_toolbox" | |
| 47 if self.failed: | |
| 48 sd = StructuredData(self.structured_report_file) | |
| 49 failed_ids = sd.failed_ids | |
| 50 tests = " ".join(failed_ids) | |
| 51 return RUN_TESTS_CMD % (html_report_file, xunit_arg, sd_arg, tests) | |
| 52 | |
| 53 | |
| 54 class StructuredData(object): | |
| 55 """ Abstraction around Galaxy's structured test data output. | |
| 56 """ | |
| 57 | |
| 58 def __init__(self, json_path): | |
| 59 self.json_path = json_path | |
| 60 try: | |
| 61 with open(json_path, "r") as output_json_f: | |
| 62 structured_data = json.load(output_json_f) | |
| 63 structured_data_tests = structured_data["tests"] | |
| 64 except Exception: | |
| 65 print("Warning: Targetting older Galaxy which did not " | |
| 66 "produce a structured test results files.") | |
| 67 structured_data = {} | |
| 68 structured_data_tests = {} | |
| 69 self.structured_data = structured_data | |
| 70 self.structured_data_tests = structured_data_tests | |
| 71 structured_data_by_id = {} | |
| 72 for test in self.structured_data_tests: | |
| 73 structured_data_by_id[test["id"]] = test["data"] | |
| 74 self.structured_data_by_id = structured_data_by_id | |
| 75 self.has_details = "summary" in structured_data | |
| 76 if self.has_details: | |
| 77 self._read_summary() | |
| 78 | |
| 79 def update(self): | |
| 80 with open(self.json_path, "w") as out_f: | |
| 81 json.dump(self.structured_data, out_f) | |
| 82 | |
| 83 def merge_xunit(self, xunit_root): | |
| 84 self.has_details = True | |
| 85 xunit_attrib = xunit_root.attrib | |
| 86 num_tests = int(xunit_attrib.get("tests", 0)) | |
| 87 num_failures = int(xunit_attrib.get("failures", 0)) | |
| 88 num_errors = int(xunit_attrib.get("errors", 0)) | |
| 89 num_skips = int(xunit_attrib.get("skips", 0)) | |
| 90 summary = dict( | |
| 91 num_tests=num_tests, | |
| 92 num_failures=num_failures, | |
| 93 num_errors=num_errors, | |
| 94 num_skips=num_skips, | |
| 95 ) | |
| 96 | |
| 97 self.structured_data["summary"] = summary | |
| 98 self.num_tests = num_tests | |
| 99 self.num_problems = num_skips + num_errors + num_failures | |
| 100 | |
| 101 for testcase_el in xunit_t_elements_from_root(xunit_root): | |
| 102 test = case_id(testcase_el) | |
| 103 test_data = self.structured_data_by_id.get(test.id) | |
| 104 if not test_data: | |
| 105 continue | |
| 106 problem_el = None | |
| 107 for problem_type in ["skip", "failure", "error"]: | |
| 108 problem_el = testcase_el.find(problem_type) | |
| 109 if problem_el is not None: | |
| 110 break | |
| 111 if problem_el is not None: | |
| 112 status = problem_el.tag | |
| 113 test_data["problem_type"] = problem_el.attrib["type"] | |
| 114 test_data["problem_log"] = problem_el.text | |
| 115 else: | |
| 116 status = "success" | |
| 117 test_data["status"] = status | |
| 118 | |
| 119 def _read_summary(self): | |
| 120 # TODO: read relevant data out of summary object. | |
| 121 pass | |
| 122 | |
| 123 @property | |
| 124 def failed_ids(self): | |
| 125 ids = set([]) | |
| 126 for test_data in self.structured_data_tests: | |
| 127 if test_data["data"]["status"] == "success": | |
| 128 continue | |
| 129 test_case = test_data["id"].replace(".test_toolbox.", ".test_toolbox:") | |
| 130 ids.add(test_case) | |
| 131 return ids | |
| 132 | |
| 133 | |
| 134 class GalaxyTestResults(object): | |
| 135 """ Class that combine the test-centric xunit output | |
| 136 with the Galaxy centric structured data output - and | |
| 137 abstracts away the difference (someday). | |
| 138 """ | |
| 139 | |
| 140 def __init__( | |
| 141 self, | |
| 142 output_json_path, | |
| 143 output_xml_path, | |
| 144 output_html_path, | |
| 145 exit_code, | |
| 146 ): | |
| 147 self.output_html_path = output_html_path | |
| 148 self.exit_code = exit_code | |
| 149 sd = StructuredData(output_json_path) | |
| 150 self.sd = sd | |
| 151 self.structured_data = sd.structured_data | |
| 152 self.structured_data_tests = sd.structured_data_tests | |
| 153 self.structured_data_by_id = sd.structured_data_by_id | |
| 154 | |
| 155 if output_xml_path: | |
| 156 self.xunit_tree = parse_xunit_report(output_xml_path) | |
| 157 sd.merge_xunit(self._xunit_root) | |
| 158 else: | |
| 159 self.xunit_tree = ET.fromstring("<testsuite />") | |
| 160 self.sd.update() | |
| 161 | |
| 162 @property | |
| 163 def has_details(self): | |
| 164 return self.sd.has_details | |
| 165 | |
| 166 @property | |
| 167 def num_tests(self): | |
| 168 return self.sd.num_tests | |
| 169 | |
| 170 @property | |
| 171 def num_problems(self): | |
| 172 return self.sd.num_problems | |
| 173 | |
| 174 @property | |
| 175 def _xunit_root(self): | |
| 176 return self.xunit_tree.getroot() | |
| 177 | |
| 178 @property | |
| 179 def all_tests_passed(self): | |
| 180 return self.sd.num_problems == 0 | |
| 181 | |
| 182 @property | |
| 183 def xunit_testcase_elements(self): | |
| 184 return xunit_t_elements_from_root(self._xunit_root) | |
| 185 | |
| 186 | |
| 187 def xunit_t_elements_from_root(xunit_root): | |
| 188 for testcase_el in find_cases(xunit_root): | |
| 189 yield testcase_el | |
| 190 | |
| 191 | |
| 192 def parse_xunit_report(xunit_report_path): | |
| 193 return ET.parse(xunit_report_path) | |
| 194 | |
| 195 | |
| 196 def find_cases(xunit_root): | |
| 197 return xunit_root.findall("testcase") | |
| 198 | |
| 199 | |
| 200 def case_id(testcase_el): | |
| 201 name_raw = testcase_el.attrib["name"] | |
| 202 if "TestForTool_" in name_raw: | |
| 203 raw_id = name_raw | |
| 204 else: | |
| 205 class_name = testcase_el.attrib["classname"] | |
| 206 raw_id = "{0}.{1}".format(class_name, name_raw) | |
| 207 | |
| 208 name = None | |
| 209 num = None | |
| 210 if "TestForTool_" in raw_id: | |
| 211 tool_and_num = raw_id.split("TestForTool_")[-1] | |
| 212 if ".test_tool_" in tool_and_num: | |
| 213 name, num_str = tool_and_num.split(".test_tool_", 1) | |
| 214 num = _parse_num(num_str) | |
| 215 # Tempted to but something human friendly in here like | |
| 216 # num + 1 - but then it doesn't match HTML report. | |
| 217 else: | |
| 218 name = tool_and_num | |
| 219 else: | |
| 220 name = raw_id | |
| 221 | |
| 222 return TestId(name, num, raw_id) | |
| 223 | |
| 224 | |
| 225 def _parse_num(num_str): | |
| 226 try: | |
| 227 num = int(num_str) | |
| 228 except ValueError: | |
| 229 num = None | |
| 230 return num | |
| 231 | |
| 232 | |
| 233 TestId = namedtuple("TestId", ["name", "num", "id"]) | |
| 234 | |
| 235 | |
| 236 @property | |
| 237 def _label(self): | |
| 238 if self.num is not None: | |
| 239 return "{0}[{1}]".format(self.name, self.num) | |
| 240 else: | |
| 241 return self.id | |
| 242 | |
| 243 | |
| 244 TestId.label = _label |
