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 |