Merge lp:~sylvain-pineau/checkbox/show_reports into lp:checkbox

Proposed by Sylvain Pineau
Status: Merged
Merged at revision: 1273
Proposed branch: lp:~sylvain-pineau/checkbox/show_reports
Merge into: lp:checkbox
Diff against target: 776 lines (+472/-86)
6 files modified
checkbox_cli/cli_interface.py (+97/-1)
checkbox_gtk/gtk_interface.py (+1/-1)
checkbox_urwid/urwid_interface.py (+323/-84)
debian/changelog (+1/-0)
plugins/launchpad_report.py (+48/-0)
plugins/report_prompt.py (+2/-0)
To merge this branch: bzr merge lp:~sylvain-pineau/checkbox/show_reports
Reviewer Review Type Date Requested Status
Marc Tardif (community) Approve
Sylvain Pineau Needs Resubmitting
Ara Pulido (community) Needs Fixing
Review via email: mp+93856@code.launchpad.net

Description of the change

This branch provides additional screens for the gtk/cli and urwid ui to display test results.
Used in conjunction with checkbox-oem, the gtk ui also provides a bug report screen.

These changes come from the "patchy" checkbox maintained by oem-qa and worked well with checkbox-oem.
Once this merge proposal is accepted we could get rid of the oem patches branches for precise.

To post a comment you must log in.
Revision history for this message
Ara Pulido (ara) wrote :

There is some code (about the bug report) that needs to be removed as it won't be accepted FFe

review: Needs Fixing
1272. By Sylvain Pineau

Remove the gtk code of the bug report screen, useless without the corresponding plugin

Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

I've removed the bug report code making the change only related to the test report for all non qt interfaces

review: Needs Resubmitting
Revision history for this message
Marc Tardif (cr3) wrote :

I'm still looking at the code but, in the meanwhile, I'd appreciate if you could report a bug describing the problem this merge request fixes. Then, add the corresponding changelog entry in your branch and feel free to resubmit. Finally, link the bug to your branch. Thanks!

Revision history for this message
Marc Tardif (cr3) wrote :

On line 558 of checkbox_gtk/gtk_interface.py, you have the following diff:

- message_dialog.add_button(option, index)
+ button = getattr(Gtk, "STOCK_%s" % option.upper())
+ message_dialog.add_buttons(button, index)

The problem is that the message dialog is also used for prompting the user to restart or recover when running checkbox after the first time. However, there is not STOCK_RESTART nor STOCK_RECOVER attribute in Gtk. If you just revert those two lines to the previous single line, that should work.

review: Needs Fixing
Revision history for this message
Marc Tardif (cr3) wrote :

On line 728 of checkbox_urwid/urwid_interface.py, you have the following diff:

- self._collapse_children()
+ #self._collapse_children()

This gives the uneasy feeling that maybe the code was commented out by mistake. I'm not a big fan of commenting out code in general so, while you're making changes, I'd appreciate if you could remove the line completely. Thanks!

1273. By Sylvain Pineau

Updated changelog and fix after reviews

Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

Thanks for your comment Marc, I've fixed the branch.

review: Needs Resubmitting
Revision history for this message
Marc Tardif (cr3) wrote :

First, it seems that your last commit replaced some symlinks by accident:

bzr status -r 1272..
kind changed:
  configs@ (symlink => directory)
  data/websites/PNG_Color_Image_Ubuntu.png@ (symlink => file)
modified:
  checkbox_gtk/gtk_interface.py
  checkbox_urwid/urwid_interface.py
  debian/changelog

Second, I'm not seeing the show_report method in the user interface called anywhere. And, when I run the GTK interface, the view report button still shows the old report. Is the purpose that this code will only be called by checkbox-oem rather than checkbox?

review: Needs Fixing
1274. By Sylvain Pineau

Update plugins to take advantage of the report screen

Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

1) Even after rebranching the code, i don't see the symlink errors you mentioned.
2) It was an error, the report code is intended to be rendered by checkbox, not only checkbox-oem. I've updated the plugins to enable it.

review: Needs Resubmitting
1275. By Sylvain Pineau

Fix missing text parameter and use NotImplementedError to raise exception in show_report

Revision history for this message
Marc Tardif (cr3) wrote :

The first problem was my mistake, sorry about that. As for the second problem, I now see the report. However, do you think we should remove the "View report" link from the final screen? Otherwise, it seems strange to have two ways to view essentially the same information.

review: Needs Information
1276. By Sylvain Pineau

Remove the gtk ui implementation to just keep cli/urwid and avoid duplication with the submission.xml link

Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

The scope of the proposal is now limited to the cli/urwid interface, given that the gtk interface allows the tester to open the submission.xml to review the results.

review: Needs Resubmitting
Revision history for this message
Marc Tardif (cr3) wrote :

1. I unliked bug #937764 from this merge request which only seems to relate to bug #937657.

2. I would appreciate if you could rephrase bug #937657 as a problem rather than a feature request.

3. I updated the changelog entry to be less intimidating: Display results report in non-graphical interfaces.

4. I changed checking for the show_report attribute in plugins/report_prompt.py to catching the NotImplementError silently in plugins/launchpad_report.py instead.

Finally, I merged and pushed the rest of your changes.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'checkbox_cli/cli_interface.py'
2--- checkbox_cli/cli_interface.py 2012-02-09 16:03:54 +0000
3+++ checkbox_cli/cli_interface.py 2012-02-22 17:22:19 +0000
4@@ -160,6 +160,94 @@
5 self.options.append(option)
6
7
8+class CLIReportDialog(CLIDialog):
9+ """
10+ Display test results
11+ """
12+ STATUS = {'pass': '{0}',
13+ 'fail': '{0}'}
14+
15+ def __init__(self, text, results):
16+ super(CLIReportDialog, self).__init__(text)
17+ self.results = results
18+
19+ def run(self):
20+ """
21+ Show root of the tree
22+ and provide the ability to further display subtress
23+ """
24+ root = self.results
25+ title = self.text
26+ self._display(title, root)
27+
28+ def _is_suite(self, root):
29+ """
30+ Return True if root contains a suite
31+ that is, a job containing other jobs
32+ """
33+ return all(issubclass(type(value), dict)
34+ for value in root.itervalues())
35+
36+ def _display(self, title, root):
37+ """
38+ Display dialog until user decides to exit
39+ (recursively for subtrees)
40+ """
41+ while True:
42+ self.put_newline()
43+ self.put_newline()
44+ self.put_line(title)
45+ self.put_newline()
46+
47+ keys = []
48+ options = []
49+ def add_option(option, key=None):
50+ """
51+ Add option to list
52+ and generate automatic key value
53+ if not provided
54+ """
55+ if key is None:
56+ key = string.lowercase[len(keys)]
57+ keys.append(key)
58+ options.append(option)
59+
60+ for job_name, job_data in sorted(root.iteritems()):
61+ if self._is_suite(job_data):
62+ add_option(job_name)
63+ self.put_line('{key}: {option}'
64+ .format(key=keys[-1],
65+ option=options[-1]))
66+ else:
67+ job_status = job_data.get('status')
68+ status_string = (self.STATUS.get(job_status, '{0}')
69+ .format(job_status))
70+ self.put_line(' {name} [{status}]'
71+ .format(name=job_name,
72+ status=status_string))
73+
74+ add_option(_("Space when finished"), " ")
75+ self.put_line('{key}: {option}'
76+ .format(key=keys[-1],
77+ option=options[-1]))
78+
79+ response = self.get(_("Please choose (%s): ") % ("/".join(keys)))
80+
81+ if response != ' ':
82+ try:
83+ selected_option = options[keys.index(response)]
84+ except ValueError:
85+ # Display again menu
86+ continue
87+
88+ # Display new menu with the contents of the selected option
89+ self._display(selected_option, root[selected_option])
90+ else:
91+ # Exit from this menu display
92+ # (display again parent menu or exit)
93+ break
94+
95+
96 class CLITextDialog(CLIDialog):
97
98 limit = 255
99@@ -271,7 +359,6 @@
100
101 def show_tree(self, text, options={}, default={}):
102 keys = sorted(options.keys())
103- values = [options[k] for k in keys]
104
105 dialog = CLIChoiceDialog(text)
106 for option in keys:
107@@ -314,6 +401,15 @@
108
109 return results
110
111+
112+ def show_report(self, text, results):
113+ """
114+ Show test case results in a tree hierarchy
115+ """
116+ dialog = CLIReportDialog(text, results)
117+ dialog.run()
118+
119+
120 def show_test(self, test, runner):
121 options = list([ANSWER_TO_OPTION[a] for a in ALL_ANSWERS])
122 if "command" in test:
123
124=== modified file 'checkbox_gtk/gtk_interface.py'
125--- checkbox_gtk/gtk_interface.py 2012-02-09 16:03:54 +0000
126+++ checkbox_gtk/gtk_interface.py 2012-02-22 17:22:19 +0000
127@@ -116,7 +116,7 @@
128 for radio_button, value in map.items():
129 if self._get_widget(radio_button).get_active():
130 return value
131- raise Exception, "Failed to map radio_button."
132+ raise Exception("Failed to map radio_button.")
133
134 def _get_label(self, name):
135 widget = self._get_widget(name)
136
137=== modified file 'checkbox_urwid/urwid_interface.py'
138--- checkbox_urwid/urwid_interface.py 2012-02-09 16:03:54 +0000
139+++ checkbox_urwid/urwid_interface.py 2012-02-22 17:22:19 +0000
140@@ -19,6 +19,7 @@
141 import urwid
142
143 import re, string
144+from operator import itemgetter
145 from gettext import gettext as _
146
147 from checkbox.user_interface import (UserInterface, NEXT, PREV,
148@@ -37,7 +38,12 @@
149 ('button focused', 'white', 'dark blue'),
150 ('highlight', 'black', 'dark cyan'),
151 ('highlight focused', 'white', 'dark blue'),
152+ ('fail', 'light red', 'dark cyan'),
153+ ('pass', 'light green', 'dark cyan'),
154+ ('result', 'light gray', 'dark cyan'),
155 )
156+ PALETTE_MEMBERS = set(color_spec[0]
157+ for color_spec in PALETTE)
158 header = None
159 footer = None
160
161@@ -368,12 +374,12 @@
162 """
163 Create a tree node and all its children
164 """
165- widget = TreeNodeWidget(name, parent)
166+ widget = SelectableTreeNodeWidget(name, parent)
167 urwid.signals.connect_signal(widget, 'change',
168 widget.changed_cb, self.walker)
169
170 if isinstance(data, dict):
171- items = sorted(data.iteritems(), key=lambda item: item[0])
172+ items = sorted(data.iteritems(), key=itemgetter(0))
173 for children_name, children_data in items:
174 child_widget = self.create_tree(children_name, children_data, widget)
175 widget.append(child_widget)
176@@ -402,14 +408,15 @@
177
178 # Show tree
179 self.option_widgets = []
180- items = sorted(self.options.iteritems(), key=lambda item: item[0])
181+ items = sorted(self.options.iteritems(),
182+ key=itemgetter(0))
183 for name, data in items:
184 widget = self.create_tree(name, data)
185 self.option_widgets.append(widget)
186 self.walker.append(widget)
187
188 self._set_default([node for node in self.walker
189- if isinstance(node, TreeNodeWidget)],
190+ if isinstance(node, SelectableTreeNodeWidget)],
191 self.default)
192
193 # Show buttons
194@@ -422,9 +429,131 @@
195 self.walker.append(buttons_box)
196
197
198+class ReportDialog(ChoiceDialog):
199+ """
200+ Display test results dialog
201+ """
202+ footer = urwid.AttrMap(urwid.Columns((urwid.Text('Arrow keys/Page Up/Page Down: Move'),
203+ urwid.Text(''),
204+ urwid.Text('+/-/Enter/Space: Expand/Collapse'))),
205+ 'footer')
206+
207+ def __init__(self, text, results):
208+ Dialog.__init__(self, text)
209+ self.results = results
210+
211+
212+ def _get_tree_node(self, node):
213+ """
214+ Get tree node even if a column is wrapping it
215+ """
216+ if issubclass(type(node), TreeNodeWidget):
217+ return node
218+ elif issubclass(type(node), urwid.Columns):
219+ for widget in child.widget_list:
220+ if issubclass(type(widget), TreeNodeWidget):
221+ return widget
222+ return node
223+
224+
225+ def expand_all_clicked_cb(self, button):
226+ """
227+ Expand all elements in the tree to see results
228+ """
229+ for tree_node in [self._get_tree_node(node)
230+ for node in self.root_nodes]:
231+ tree_node.expand(expand_all=True)
232+
233+
234+ def collapse_all_clicked_cb(self, button):
235+ """
236+ Collapse all elements in the tree to see results
237+ """
238+ for tree_node in [self._get_tree_node(node)
239+ for node in self.root_nodes]:
240+ tree_node.collapse(collapse_all=True)
241+
242+
243+ def next_button_clicked_cb(self, button):
244+ """
245+ Set direction, response and exit
246+ """
247+ self.direction = NEXT
248+ raise urwid.ExitMainLoop
249+
250+
251+ def previous_button_clicked_cb(self, button):
252+ """
253+ Set direction, response and exit
254+ """
255+ self.direction = PREV
256+ raise urwid.ExitMainLoop
257+
258+
259+ def create_tree(self, name, data, parent=None):
260+ """
261+ Create a tree node and all its children
262+ """
263+ widget = TreeNodeWidget(name, parent)
264+ urwid.signals.connect_signal(widget, 'change',
265+ widget.changed_cb, self.walker)
266+
267+ items = sorted(data.iteritems(), key=itemgetter(0))
268+ for child_name, child_data in items:
269+ is_suite = all(issubclass(type(value), dict)
270+ for value in child_data.itervalues())
271+
272+ if is_suite:
273+ child_widget = self.create_tree(child_name,
274+ child_data,
275+ widget)
276+ else:
277+ result=child_data['status']
278+
279+ # Use color specification for result
280+ # if found or default one
281+ attr = (result
282+ if result in self.PALETTE_MEMBERS
283+ else 'result')
284+
285+ child_widget = urwid.Columns(
286+ (TreeNodeWidget(child_name, widget),
287+ urwid.AttrMap(urwid.Text((attr, result)),
288+ 'highlight', 'highlight focused')))
289+ widget.append(child_widget)
290+
291+ return widget
292+
293+
294+ def show(self):
295+ """
296+ Display dialog text, options tree and buttons
297+ """
298+ # Show text
299+ Dialog.show(self)
300+
301+ # Show tree
302+ items = sorted(self.results.iteritems(),
303+ key=itemgetter(0))
304+ for name, data in items:
305+ widget = self.create_tree(name, data)
306+ self.walker.append(widget)
307+
308+ self.root_nodes = [node for node in self.walker]
309+
310+ # Show buttons
311+ labels = ((_('Expand All'), self.expand_all_clicked_cb),
312+ (_('Collapse All'), self.collapse_all_clicked_cb),
313+ (_('Previous'), self.previous_button_clicked_cb),
314+ (_('Next'), self.next_button_clicked_cb))
315+ buttons_box = self.create_buttons(labels)
316+ self.walker.append(urwid.Divider())
317+ self.walker.append(buttons_box)
318+
319+
320 class TreeNodeWidget(urwid.WidgetWrap):
321 """
322- Implementation of a node in a tree that can be selected/deselected
323+ Implementation of a node in a tree that can be expanded/unexpanded
324 """
325 signals = ['change']
326
327@@ -436,17 +565,45 @@
328
329 self.expanded = False
330
331- # Use a checkbox as internal representation of the widget
332- self.checkbox = urwid.CheckBox(self._get_label())
333- w = urwid.AttrMap(self.checkbox, 'highlight', 'highlight focused')
334+ w = self._get_widget()
335 super(TreeNodeWidget, self).__init__(w)
336
337
338+ def _get_widget(self):
339+ """
340+ Create widget that is wrapped by this class
341+ """
342+ self.widget = urwid.Text(self._get_label())
343+ w = urwid.AttrMap(self.widget, 'highlight', 'highlight focused')
344+ return w
345+
346+
347+ def _update_label(self):
348+ """
349+ Update text label
350+ """
351+ self.widget.set_text(self._get_label())
352+
353+
354+ def _get_node(self, child):
355+ """
356+ Get TreeNode directly without traversing Columns
357+ """
358+ if issubclass(type(child), TreeNodeWidget):
359+ return child
360+ elif issubclass(type(child), urwid.Columns):
361+ for widget in child.widget_list:
362+ if issubclass(type(widget), TreeNodeWidget):
363+ return widget
364+ return child
365+
366+
367 def __iter__(self):
368 """
369 Iterate over children nodes
370 """
371- return iter(self.children)
372+ return iter([self._get_node(child)
373+ for child in self.children])
374
375
376 def __len__(self):
377@@ -476,69 +633,17 @@
378 return True
379
380
381- @property
382- def state(self):
383- """
384- Get state from checkbox widget
385- """
386- return self.checkbox.get_state()
387-
388-
389- @state.setter
390- def state(self, value):
391- """
392- Set state to checkbox widget
393- """
394- self.checkbox.set_state(value)
395-
396-
397- def set_ancestors_state(self, new_state):
398- """
399- Set the state of all ancestors consistently
400- """
401- # If child is set, then all ancestors must be set
402- if new_state:
403- parent = self.parent
404- while parent:
405- parent.state = new_state
406- parent = parent.parent
407- # If child is not set, then all ancestors mustn't be set
408- # unless another child of the ancestor is set
409- else:
410- parent = self.parent
411- while parent:
412- if any((child.state
413- for child in parent)):
414- break
415- parent.state = new_state
416- parent = parent.parent
417-
418-
419- def set_children_state(self, new_state):
420- """
421- Set the state of all children recursively
422- """
423- self.state = new_state
424- for child in self:
425- child.set_children_state(new_state)
426-
427-
428 def keypress(self, size, key):
429 """
430- Use key events to select checkbox and expand tree hierarchy
431+ Use key events to expand/collapse tree hierarchy
432 """
433-
434- if key == ' ':
435- new_state = not self.state
436- self.state = new_state
437- self.set_children_state(new_state)
438- self.set_ancestors_state(new_state)
439- return None
440- elif self.children:
441- if key in ('+', 'enter') and self.expanded == False:
442+ if self.children:
443+ if (key in ('+', 'enter', ' ')
444+ and self.expanded == False):
445 urwid.signals.emit_signal(self, 'change')
446 return None
447- elif key in ('-', 'enter') and self.expanded == True:
448+ elif (key in ('-', 'enter', ' ')
449+ and self.expanded == True):
450 urwid.signals.emit_signal(self, 'change')
451 return None
452
453@@ -547,16 +652,10 @@
454
455 def mouse_event(self, size, event, button, col, row, focus):
456 """
457- Use mouse events to select checkbox and expand tree hierarchy
458+ Use mouse events to expand/collapse tree hierarchy
459 """
460- # Left click event
461- if button == 1:
462- new_state = not self.state
463- self.state = new_state
464- self.set_children_state(new_state)
465- self.set_ancestors_state(new_state)
466 # Ignore button release event
467- elif button == 0:
468+ if button == 0:
469 pass
470 else:
471 urwid.signals.emit_signal(self, 'change')
472@@ -581,11 +680,15 @@
473 return label
474
475
476- def _update_label(self):
477- """
478- Update text label
479- """
480- self.checkbox.set_label(self._get_label())
481+ def collapse(self, collapse_all=False):
482+ """
483+ Collapse node
484+ """
485+ if self.expanded == True:
486+ urwid.signals.emit_signal(self, 'change')
487+
488+ if collapse_all:
489+ self._collapse_children()
490
491
492 def _collapse_children(self):
493@@ -593,12 +696,24 @@
494 Collapse all children
495 """
496 for child in self:
497+ child._collapse_children()
498 if child.expanded:
499- child._collapse_children()
500 child.expanded = False
501 child._update_label()
502
503
504+ def expand(self, expand_all=False):
505+ """
506+ Expand node
507+ """
508+ if self.expanded == False:
509+ urwid.signals.emit_signal(self, 'change')
510+
511+ if expand_all:
512+ for child in self:
513+ child.expand(expand_all)
514+
515+
516 def changed_cb(self, walker):
517 """
518 Handle node expansion in the tree
519@@ -610,18 +725,134 @@
520 del_end_position = (del_start_position +
521 len(self) - 1)
522 del walker[del_start_position:del_end_position]
523- self._collapse_children()
524 self.expanded = False
525 else:
526 insert_position = position + 1
527
528 # Append widgets to the list
529- walker[insert_position:insert_position] = self.children
530+ subtree = list(self._get_subtree())
531+ walker[insert_position:insert_position] = subtree
532 self.expanded = True
533
534 self._update_label()
535
536
537+ def _get_subtree(self):
538+ """
539+ Return subtree with expanded children
540+ """
541+ for child in self.children:
542+ yield child
543+
544+ child_node = self._get_node(child)
545+ if child_node.expanded:
546+ for descendant in child._get_subtree():
547+ yield descendant
548+
549+
550+class SelectableTreeNodeWidget(TreeNodeWidget):
551+ """
552+ Implementation of a node in a tree that can be selected/deselected
553+ """
554+ def __init__(self, name, parent=None):
555+ super(SelectableTreeNodeWidget, self).__init__(name, parent)
556+
557+
558+ def _get_widget(self):
559+ """
560+ Create widget that is wrapped by this class
561+ """
562+ # Use a checkbox to preserve widget selection stat
563+ self.widget = urwid.CheckBox(self._get_label())
564+ w = urwid.AttrMap(self.widget, 'highlight', 'highlight focused')
565+ return w
566+
567+
568+ def _update_label(self):
569+ """
570+ Update text label
571+ """
572+ self.widget.set_label(self._get_label())
573+
574+
575+ @property
576+ def state(self):
577+ """
578+ Get state from checkbox widget
579+ """
580+ return self.widget.get_state()
581+
582+
583+ @state.setter
584+ def state(self, value):
585+ """
586+ Set state to checkbox widget
587+ """
588+ self.widget.set_state(value)
589+
590+
591+ def set_ancestors_state(self, new_state):
592+ """
593+ Set the state of all ancestors consistently
594+ """
595+ # If child is set, then all ancestors must be set
596+ if new_state:
597+ parent = self.parent
598+ while parent:
599+ parent.state = new_state
600+ parent = parent.parent
601+ # If child is not set, then all ancestors mustn't be set
602+ # unless another child of the ancestor is set
603+ else:
604+ parent = self.parent
605+ while parent:
606+ if any((child.state
607+ for child in parent)):
608+ break
609+ parent.state = new_state
610+ parent = parent.parent
611+
612+
613+ def set_children_state(self, new_state):
614+ """
615+ Set the state of all children recursively
616+ """
617+ self.state = new_state
618+ for child in self:
619+ child.set_children_state(new_state)
620+
621+
622+ def keypress(self, size, key):
623+ """
624+ Use key events to select checkbox and expand tree hierarchy
625+ """
626+
627+ if key == ' ':
628+ new_state = not self.state
629+ self.state = new_state
630+ self.set_children_state(new_state)
631+ self.set_ancestors_state(new_state)
632+ return None
633+
634+ return super(SelectableTreeNodeWidget, self).keypress(size, key)
635+
636+
637+ def mouse_event(self, size, event, button, col, row, focus):
638+ """
639+ Use mouse events to select checkbox and expand tree hierarchy
640+ """
641+ # Left click event
642+ if button == 1:
643+ new_state = not self.state
644+ self.state = new_state
645+ self.set_children_state(new_state)
646+ self.set_ancestors_state(new_state)
647+ return True
648+
649+ return (super(SelectableTreeNodeWidget, self)
650+ .mouse_event(size, event, button, col, row, focus))
651+
652+
653 class ProgressDialog(Dialog):
654 """
655 Show progress through a bar
656@@ -753,6 +984,14 @@
657 return dialog.response
658
659
660+ def show_report(self, text, results):
661+ """
662+ Show test case results in a tree hierarchy
663+ """
664+ dialog = ReportDialog(text, results).run()
665+ self.direction = dialog.direction
666+
667+
668 def show_test(self, test, runner):
669 """
670 Show test description, radio buttons to set result
671@@ -792,7 +1031,7 @@
672 test['data'] = dialog.input
673 test['status'] = ANSWER_TO_STATUS[answer]
674 self.direction = dialog.direction
675- return self.response
676+ return dialog.response
677
678
679 def show_info(self, text, options=[], default=None):
680
681=== modified file 'debian/changelog'
682--- debian/changelog 2012-02-20 08:22:59 +0000
683+++ debian/changelog 2012-02-22 17:22:19 +0000
684@@ -5,6 +5,7 @@
685
686 [Sylvain Pineau]
687 * Fix depends fields in info and suspend test suites (LP: #934051)
688+ * Display results report before upload (LP: #937657)
689
690 [Brendan Donegan]
691 * Typo in command for for miscellanea/virtualization-check (LP: #934243)
692
693=== modified file 'plugins/launchpad_report.py'
694--- plugins/launchpad_report.py 2012-02-07 20:39:15 +0000
695+++ plugins/launchpad_report.py 2012-02-22 17:22:19 +0000
696@@ -66,6 +66,8 @@
697 ("report-package", self.report_package),
698 ("report-uname", self.report_uname),
699 ("report-system_id", self.report_system_id),
700+ ("report-suites", self.report_suites),
701+ ("report-review", self.report_review),
702 ("report-tests", self.report_tests)]:
703 self._manager.reactor.call_on(rt, rh)
704
705@@ -136,6 +138,7 @@
706 self._report["summary"]["system_id"] = system_id
707
708 def report_tests(self, tests):
709+ self.tests = tests
710 for test in tests:
711 question = {
712 "name": test["name"],
713@@ -167,5 +170,50 @@
714
715 self._manager.reactor.fire("launchpad-report", self.filename)
716
717+ def report_review(self, interface):
718+ """
719+ Show test report in the interface
720+ """
721+ report = {}
722+
723+ def add_job(job):
724+ is_suite = 'type' in job and job['type'] == 'suite'
725+ if 'suite' in job:
726+ suite_name = job['suite']
727+ parent_node = add_job(self.suites[suite_name])
728+
729+ if is_suite:
730+ if job['description'] in parent_node:
731+ return parent_node[job['description']]
732+
733+ node = {}
734+ parent_node[job['description']] = node
735+ return node
736+ parent_node[job['name']] = job
737+ else:
738+ if is_suite:
739+ field = 'description'
740+ else:
741+ field = 'name'
742+
743+ if job[field] in report:
744+ return report[job[field]]
745+
746+ node = {}
747+ report[job[field]] = node
748+ return node
749+
750+ for test in self.tests:
751+ add_job(test)
752+
753+ interface.show_report("Test case results report", report)
754+
755+ def report_suites(self, suites):
756+ """
757+ Get tests results and store it
758+ to display them later
759+ """
760+ self.suites = dict([(suite['name'], suite) for suite in suites])
761+
762
763 factory = LaunchpadReport
764
765=== modified file 'plugins/report_prompt.py'
766--- plugins/report_prompt.py 2011-09-08 14:52:33 +0000
767+++ plugins/report_prompt.py 2012-02-22 17:22:19 +0000
768@@ -40,6 +40,8 @@
769 self._manager.reactor.fire, "report")
770
771 self._manager.reactor.cancel_call(event_id)
772+ if hasattr(interface, 'show_report'):
773+ self._manager.reactor.fire("report-review", interface)
774
775
776 factory = ReportPrompt

Subscribers

People subscribed via source and target branches