2 The LLVM Compiler Infrastructure
4 This file is distributed under the University of Illinois Open Source
5 License. See LICENSE.TXT for details.
8 from __future__ import absolute_import
9 from __future__ import print_function
21 from lldbsuite.test import lldbcurses
23 from . import results_formatter
24 from ..event_builder import EventBuilder
27 class Curses(results_formatter.ResultsFormatter):
28 """Receives live results from tests that are running and reports them to the terminal in a curses GUI"""
30 def __init__(self, out_file, options, file_is_stream):
31 # Initialize the parent
32 super(Curses, self).__init__(out_file, options, file_is_stream)
33 self.using_terminal = True
34 self.have_curses = True
35 self.initialize_event = None
36 self.jobs = [None] * 64
37 self.job_tests = [None] * 64
40 self.main_window = lldbcurses.intialize_curses()
41 self.main_window.add_key_action(
43 self.main_window.select_next_first_responder,
44 "Switch between views that can respond to keyboard input")
45 self.main_window.refresh()
47 self.results_panel = None
48 self.status_panel = None
49 self.info_panel = None
50 self.hide_status_list = list()
51 self.start_time = time.time()
53 self.have_curses = False
54 lldbcurses.terminate_curses()
55 self.using_terminal = False
56 print("Unexpected error:", sys.exc_info()[0])
59 self.line_dict = dict()
60 # self.events_file = open("/tmp/events.txt", "w")
61 # self.formatters = list()
62 # if tee_results_formatter:
63 # self.formatters.append(tee_results_formatter)
65 def status_to_short_str(self, status, test_event):
66 if status == EventBuilder.STATUS_SUCCESS:
68 elif status == EventBuilder.STATUS_FAILURE:
70 elif status == EventBuilder.STATUS_UNEXPECTED_SUCCESS:
72 elif status == EventBuilder.STATUS_EXPECTED_FAILURE:
74 elif status == EventBuilder.STATUS_SKIP:
76 elif status == EventBuilder.STATUS_ERROR:
77 if test_event.get("issue_phase", None) == "build":
82 elif status == EventBuilder.STATUS_TIMEOUT:
84 elif status == EventBuilder.STATUS_EXPECTED_TIMEOUT:
89 def show_info_panel(self):
90 selected_idx = self.results_panel.get_selected_idx()
91 if selected_idx >= 0 and selected_idx < len(self.results):
92 if self.info_panel is None:
93 info_frame = self.results_panel.get_contained_rect(
94 top_inset=10, left_inset=10, right_inset=10, height=30)
95 self.info_panel = lldbcurses.BoxedPanel(
96 info_frame, "Result Details")
97 # Add a key action for any key that will hide this panel when
99 self.info_panel.add_key_action(-1,
100 self.hide_info_panel,
101 'Hide the info panel')
102 self.info_panel.top()
104 self.info_panel.show()
106 self.main_window.push_first_responder(self.info_panel)
107 test_start = self.results[selected_idx][0]
108 test_result = self.results[selected_idx][1]
109 self.info_panel.set_line(
111 (test_start['test_filename']))
112 self.info_panel.set_line(
114 (test_start['test_class'], test_start['test_name']))
115 self.info_panel.set_line(
117 (test_result['elapsed_time']))
118 self.info_panel.set_line(3, "Status: %s" % (test_result['status']))
120 def hide_info_panel(self):
121 self.main_window.pop_first_responder(self.info_panel)
122 self.info_panel.hide()
123 self.main_window.refresh()
125 def toggle_status(self, status):
127 # Toggle showing and hiding results whose status matches "status"
128 # in "Results" window
129 if status in self.hide_status_list:
130 self.hide_status_list.remove(status)
132 self.hide_status_list.append(status)
133 self.update_results()
135 def update_results(self, update=True):
136 '''Called after a category of test have been show/hidden to update the results list with
137 what the user desires to see.'''
138 self.results_panel.clear(update=False)
139 for result in self.results:
140 test_result = result[1]
141 status = test_result['status']
142 if status in self.hide_status_list:
144 name = test_result['test_class'] + '.' + test_result['test_name']
145 self.results_panel.append_line(
146 '%s (%6.2f sec) %s' %
147 (self.status_to_short_str(
150 test_result['elapsed_time'],
153 self.main_window.refresh()
155 def handle_event(self, test_event):
157 super(Curses, self).handle_event(test_event)
158 # for formatter in self.formatters:
159 # formatter.process_event(test_event)
162 if 'worker_index' in test_event:
163 worker_index = test_event['worker_index']
164 if 'event' in test_event:
165 check_for_one_key = True
166 #print(str(test_event), file=self.events_file)
167 event = test_event['event']
168 if self.status_panel:
169 self.status_panel.update_status(
173 time.time() - self.start_time))))
174 if event == 'test_start':
175 name = test_event['test_class'] + \
176 '.' + test_event['test_name']
177 self.job_tests[worker_index] = test_event
178 if 'pid' in test_event:
179 line = 'pid: %5d ' % (test_event['pid']) + name
182 self.job_panel.set_line(worker_index, line)
183 self.main_window.refresh()
184 elif event == 'test_result':
185 status = test_event['status']
186 self.status_panel.increment_status(status)
187 if 'pid' in test_event:
188 line = 'pid: %5d ' % (test_event['pid'])
191 self.job_panel.set_line(worker_index, line)
192 name = test_event['test_class'] + \
193 '.' + test_event['test_name']
194 elapsed_time = test_event[
195 'event_time'] - self.job_tests[worker_index]['event_time']
196 if status not in self.hide_status_list:
197 self.results_panel.append_line(
198 '%s (%6.2f sec) %s' %
199 (self.status_to_short_str(
200 status, test_event), elapsed_time, name))
201 self.main_window.refresh()
202 # Append the result pairs
203 test_event['elapsed_time'] = elapsed_time
205 [self.job_tests[worker_index], test_event])
206 self.job_tests[worker_index] = ''
207 elif event == 'job_begin':
208 self.jobs[worker_index] = test_event
209 if 'pid' in test_event:
210 line = 'pid: %5d ' % (test_event['pid'])
213 self.job_panel.set_line(worker_index, line)
214 elif event == 'job_end':
215 self.jobs[worker_index] = ''
216 self.job_panel.set_line(worker_index, '')
217 elif event == 'initialize':
218 self.initialize_event = test_event
219 num_jobs = test_event['worker_count']
220 job_frame = self.main_window.get_contained_rect(
222 results_frame = self.main_window.get_contained_rect(
223 top_inset=num_jobs + 2, bottom_inset=1)
224 status_frame = self.main_window.get_contained_rect(
225 height=1, top_inset=self.main_window.get_size().h - 1)
226 self.job_panel = lldbcurses.BoxedPanel(
227 frame=job_frame, title="Jobs")
228 self.results_panel = lldbcurses.BoxedPanel(
229 frame=results_frame, title="Results")
231 self.results_panel.add_key_action(
233 self.results_panel.select_prev,
234 "Select the previous list entry")
235 self.results_panel.add_key_action(
236 curses.KEY_DOWN, self.results_panel.select_next, "Select the next list entry")
237 self.results_panel.add_key_action(
239 self.results_panel.scroll_begin,
240 "Scroll to the start of the list")
241 self.results_panel.add_key_action(
242 curses.KEY_END, self.results_panel.scroll_end, "Scroll to the end of the list")
243 self.results_panel.add_key_action(
245 self.show_info_panel,
246 "Display info for the selected result item")
247 self.results_panel.add_key_action(
249 lambda: self.toggle_status(
250 EventBuilder.STATUS_SUCCESS),
251 "Toggle showing/hiding tests whose status is 'success'")
252 self.results_panel.add_key_action(
254 lambda: self.toggle_status(
255 EventBuilder.STATUS_ERROR),
256 "Toggle showing/hiding tests whose status is 'error'")
257 self.results_panel.add_key_action(
259 lambda: self.toggle_status(
260 EventBuilder.STATUS_FAILURE),
261 "Toggle showing/hiding tests whose status is 'failure'")
262 self.results_panel.add_key_action('s', lambda: self.toggle_status(
263 EventBuilder.STATUS_SKIP), "Toggle showing/hiding tests whose status is 'skip'")
264 self.results_panel.add_key_action(
266 lambda: self.toggle_status(
267 EventBuilder.STATUS_EXPECTED_FAILURE),
268 "Toggle showing/hiding tests whose status is 'expected_failure'")
269 self.results_panel.add_key_action(
271 lambda: self.toggle_status(
272 EventBuilder.STATUS_UNEXPECTED_SUCCESS),
273 "Toggle showing/hiding tests whose status is 'unexpected_success'")
274 self.status_panel = lldbcurses.StatusPanel(
277 self.main_window.add_child(self.job_panel)
278 self.main_window.add_child(self.results_panel)
279 self.main_window.add_child(self.status_panel)
280 self.main_window.set_first_responder(
283 self.status_panel.add_status_item(
290 self.status_panel.add_status_item(
291 name=EventBuilder.STATUS_SUCCESS,
297 self.status_panel.add_status_item(
298 name=EventBuilder.STATUS_FAILURE,
304 self.status_panel.add_status_item(
305 name=EventBuilder.STATUS_ERROR,
311 self.status_panel.add_status_item(
312 name=EventBuilder.STATUS_SKIP,
318 self.status_panel.add_status_item(
319 name=EventBuilder.STATUS_EXPECTED_FAILURE,
320 title="Expected Failure",
325 self.status_panel.add_status_item(
326 name=EventBuilder.STATUS_UNEXPECTED_SUCCESS,
327 title="Unexpected Success",
332 self.main_window.refresh()
333 elif event == 'terminate':
334 # self.main_window.key_event_loop()
335 lldbcurses.terminate_curses()
336 check_for_one_key = False
337 self.using_terminal = False
338 # Check for 1 keypress with no delay
340 # Check for 1 keypress with no delay
341 if check_for_one_key:
342 self.main_window.key_event_loop(0, 1)