comparison env/lib/python3.9/site-packages/planemo/galaxy/test/structures.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
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 typing 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 = "./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 class TestId(NamedTuple):
206 name: str
207 num: int
208 id: str
209
210 @property
211 def label(self):
212 if self.num is not None:
213 return "{0}[{1}]".format(self.name, self.num)
214 else:
215 return self.id