Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/planemo/galaxy/test/structures.py @ 2:6af9afd405e9 draft
"planemo upload commit 0a63dd5f4d38a1f6944587f52a8cd79874177fc1"
| author | shellac |
|---|---|
| date | Thu, 14 May 2020 14:56:58 -0400 |
| parents | 26e78fe6e8c4 |
| children |
comparison
equal
deleted
inserted
replaced
| 1:75ca89e9b81c | 2:6af9afd405e9 |
|---|---|
| 1 """Utilities for reasoning about Galaxy test results.""" | |
| 2 from __future__ import absolute_import | |
| 3 from __future__ import print_function | |
| 4 | |
| 5 import os | |
| 6 from collections import namedtuple | |
| 7 from xml.etree import ElementTree as ET | |
| 8 | |
| 9 from six.moves import shlex_quote | |
| 10 | |
| 11 from planemo.io import error | |
| 12 from planemo.test.results import StructuredData as BaseStructuredData | |
| 13 | |
| 14 | |
| 15 NO_STRUCTURED_FILE = ( | |
| 16 "Warning: Problem with target Galaxy, it did not " | |
| 17 "produce a structured test results file [%s] - summary " | |
| 18 "information and planemo reports will be incorrect." | |
| 19 ) | |
| 20 | |
| 21 | |
| 22 class GalaxyTestCommand(object): | |
| 23 """Abstraction around building a ``run_tests.sh`` command for Galaxy tests.""" | |
| 24 | |
| 25 def __init__( | |
| 26 self, | |
| 27 html_report_file, | |
| 28 xunit_report_file, | |
| 29 structured_report_file, | |
| 30 failed=False, | |
| 31 installed=False, | |
| 32 ): | |
| 33 self.html_report_file = html_report_file | |
| 34 self.xunit_report_file = xunit_report_file | |
| 35 self.structured_report_file = structured_report_file | |
| 36 self.failed = failed | |
| 37 self.installed = installed | |
| 38 | |
| 39 def build(self): | |
| 40 xunit_report_file = self.xunit_report_file | |
| 41 sd_report_file = self.structured_report_file | |
| 42 cmd = "sh run_tests.sh $COMMON_STARTUP_ARGS --report_file %s" % shlex_quote(self.html_report_file) | |
| 43 if xunit_report_file: | |
| 44 cmd += " --xunit_report_file %s" % shlex_quote(xunit_report_file) | |
| 45 if sd_report_file: | |
| 46 cmd += " --structured_data_report_file %s" % shlex_quote(sd_report_file) | |
| 47 if self.installed: | |
| 48 cmd += ' -installed' | |
| 49 elif self.failed: | |
| 50 sd = StructuredData(sd_report_file) | |
| 51 tests = " ".join(sd.failed_ids) | |
| 52 cmd += " %s" % tests | |
| 53 else: | |
| 54 cmd += ' functional.test_toolbox' | |
| 55 return cmd | |
| 56 | |
| 57 | |
| 58 class StructuredData(BaseStructuredData): | |
| 59 """Abstraction around Galaxy's structured test data output.""" | |
| 60 | |
| 61 def __init__(self, json_path): | |
| 62 if not json_path or not os.path.exists(json_path): | |
| 63 error(NO_STRUCTURED_FILE % json_path) | |
| 64 super(StructuredData, self).__init__(json_path) | |
| 65 | |
| 66 def merge_xunit(self, xunit_root): | |
| 67 self.has_details = True | |
| 68 xunit_attrib = xunit_root.attrib | |
| 69 num_tests = int(xunit_attrib.get("tests", 0)) | |
| 70 num_failures = int(xunit_attrib.get("failures", 0)) | |
| 71 num_errors = int(xunit_attrib.get("errors", 0)) | |
| 72 num_skips = int(xunit_attrib.get("skips", 0)) | |
| 73 summary = dict( | |
| 74 num_tests=num_tests, | |
| 75 num_failures=num_failures, | |
| 76 num_errors=num_errors, | |
| 77 num_skips=num_skips, | |
| 78 ) | |
| 79 | |
| 80 self.structured_data["summary"] = summary | |
| 81 | |
| 82 for testcase_el in xunit_t_elements_from_root(xunit_root): | |
| 83 test = case_id(testcase_el) | |
| 84 test_data = self.structured_data_by_id.get(test.id) | |
| 85 if not test_data: | |
| 86 continue | |
| 87 problem_el = None | |
| 88 for problem_type in ["skip", "failure", "error"]: | |
| 89 problem_el = testcase_el.find(problem_type) | |
| 90 if problem_el is not None: | |
| 91 break | |
| 92 if problem_el is not None: | |
| 93 status = problem_el.tag | |
| 94 test_data["problem_type"] = problem_el.attrib["type"] | |
| 95 test_data["problem_log"] = problem_el.text | |
| 96 else: | |
| 97 status = "success" | |
| 98 test_data["status"] = status | |
| 99 | |
| 100 | |
| 101 class GalaxyTestResults(object): | |
| 102 """ Class that combine the test-centric xunit output | |
| 103 with the Galaxy centric structured data output - and | |
| 104 abstracts away the difference (someday). | |
| 105 """ | |
| 106 | |
| 107 def __init__( | |
| 108 self, | |
| 109 output_json_path, | |
| 110 output_xml_path, | |
| 111 output_html_path, | |
| 112 exit_code, | |
| 113 ): | |
| 114 self.output_html_path = output_html_path | |
| 115 sd = StructuredData(output_json_path) | |
| 116 self.sd = sd | |
| 117 self.structured_data = sd.structured_data | |
| 118 self.structured_data_tests = sd.structured_data_tests | |
| 119 self.structured_data_by_id = sd.structured_data_by_id | |
| 120 | |
| 121 self.xunit_tree = parse_xunit_report(output_xml_path) | |
| 122 sd.merge_xunit(self._xunit_root) | |
| 123 | |
| 124 self.sd.set_exit_code(exit_code) | |
| 125 self.sd.read_summary() | |
| 126 self.sd.update() | |
| 127 | |
| 128 @property | |
| 129 def exit_code(self): | |
| 130 return self.sd.exit_code | |
| 131 | |
| 132 @property | |
| 133 def has_details(self): | |
| 134 return self.sd.has_details | |
| 135 | |
| 136 @property | |
| 137 def num_tests(self): | |
| 138 return self.sd.num_tests | |
| 139 | |
| 140 @property | |
| 141 def num_problems(self): | |
| 142 return self.sd.num_problems | |
| 143 | |
| 144 @property | |
| 145 def _xunit_root(self): | |
| 146 return self.xunit_tree.getroot() | |
| 147 | |
| 148 @property | |
| 149 def all_tests_passed(self): | |
| 150 return self.sd.num_problems == 0 | |
| 151 | |
| 152 @property | |
| 153 def xunit_testcase_elements(self): | |
| 154 return xunit_t_elements_from_root(self._xunit_root) | |
| 155 | |
| 156 | |
| 157 def xunit_t_elements_from_root(xunit_root): | |
| 158 for testcase_el in find_cases(xunit_root): | |
| 159 yield testcase_el | |
| 160 | |
| 161 | |
| 162 def parse_xunit_report(xunit_report_path): | |
| 163 return ET.parse(xunit_report_path) | |
| 164 | |
| 165 | |
| 166 def find_cases(xunit_root): | |
| 167 return xunit_root.findall("testcase") | |
| 168 | |
| 169 | |
| 170 def case_id(testcase_el=None, raw_id=None): | |
| 171 if raw_id is None: | |
| 172 assert testcase_el is not None | |
| 173 name_raw = testcase_el.attrib["name"] | |
| 174 if "TestForTool_" in name_raw: | |
| 175 raw_id = name_raw | |
| 176 else: | |
| 177 class_name = testcase_el.attrib["classname"] | |
| 178 raw_id = "{0}.{1}".format(class_name, name_raw) | |
| 179 | |
| 180 name = None | |
| 181 num = None | |
| 182 if "TestForTool_" in raw_id: | |
| 183 tool_and_num = raw_id.split("TestForTool_")[-1] | |
| 184 if ".test_tool_" in tool_and_num: | |
| 185 name, num_str = tool_and_num.split(".test_tool_", 1) | |
| 186 num = _parse_num(num_str) | |
| 187 # Tempted to but something human friendly in here like | |
| 188 # num + 1 - but then it doesn't match HTML report. | |
| 189 else: | |
| 190 name = tool_and_num | |
| 191 else: | |
| 192 name = raw_id | |
| 193 | |
| 194 return TestId(name, num, raw_id) | |
| 195 | |
| 196 | |
| 197 def _parse_num(num_str): | |
| 198 try: | |
| 199 num = int(num_str) | |
| 200 except ValueError: | |
| 201 num = None | |
| 202 return num | |
| 203 | |
| 204 | |
| 205 TestId = namedtuple("TestId", ["name", "num", "id"]) | |
| 206 | |
| 207 | |
| 208 @property | |
| 209 def _label(self): | |
| 210 if self.num is not None: | |
| 211 return "{0}[{1}]".format(self.name, self.num) | |
| 212 else: | |
| 213 return self.id | |
| 214 | |
| 215 | |
| 216 TestId.label = _label |
