blob: 2c39e62d645ad5c6f92875be0fdd1fb6e5d81ff1 [file] [log] [blame]
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001# -*- coding: utf-8 -*-
2#
3# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7
8# DESCRIPTION :
9#
10# This library provides convenience routines to launch factory tests.
11# This includes support for drawing the test widget in a window at the
12# proper location, grabbing control of the mouse, and making the mouse
13# cursor disappear.
14
15import gobject, gtk, pango
16from itertools import izip, product
17
18import factory_common
19from autotest_lib.client.cros import factory
20from autotest_lib.client.cros.factory import TestState
21from autotest_lib.client.cros.factory.event import Event, EventClient
22
23# For compatibility with tests before TestState existed
24ACTIVE = TestState.ACTIVE
25PASSED = TestState.PASSED
26FAILED = TestState.FAILED
27UNTESTED = TestState.UNTESTED
28
29BLACK = gtk.gdk.Color()
30RED = gtk.gdk.Color(0xFFFF, 0, 0)
31GREEN = gtk.gdk.Color(0, 0xFFFF, 0)
32BLUE = gtk.gdk.Color(0, 0, 0xFFFF)
33WHITE = gtk.gdk.Color(0xFFFF, 0xFFFF, 0xFFFF)
34
35LIGHT_GREEN = gtk.gdk.color_parse('light green')
36
37SEP_COLOR = gtk.gdk.color_parse('grey50')
38
39RGBA_GREEN_OVERLAY = (0, 0.5, 0, 0.6)
40RGBA_YELLOW_OVERLAY = (0.6, 0.6, 0, 0.6)
41
42LABEL_COLORS = {
43 TestState.ACTIVE: gtk.gdk.color_parse('light goldenrod'),
44 TestState.PASSED: gtk.gdk.color_parse('pale green'),
45 TestState.FAILED: gtk.gdk.color_parse('tomato'),
46 TestState.UNTESTED: gtk.gdk.color_parse('dark slate grey')}
47
48LABEL_FONT = pango.FontDescription('courier new condensed 16')
49
50FAIL_TIMEOUT = 30
51
52USER_PASS_FAIL_SELECT_STR = (
53 'hit TAB to fail and ENTER to pass\n' +
54 '錯誤請按 TAB,成功請按 ENTER')
55
56
57def make_label(message, font=LABEL_FONT, fg=LIGHT_GREEN,
58 size=None, alignment=None):
59 l = gtk.Label(message)
60 l.modify_font(font)
61 l.modify_fg(gtk.STATE_NORMAL, fg)
62 if size:
63 l.set_size_request(*size)
64 if alignment:
65 l.set_alignment(*alignment)
66 return l
67
68
69def make_hsep(width=1):
70 frame = gtk.EventBox()
71 frame.set_size_request(-1, width)
72 frame.modify_bg(gtk.STATE_NORMAL, SEP_COLOR)
73 return frame
74
75
76def make_vsep(width=1):
77 frame = gtk.EventBox()
78 frame.set_size_request(width, -1)
79 frame.modify_bg(gtk.STATE_NORMAL, SEP_COLOR)
80 return frame
81
82
83def make_countdown_widget():
84 title = make_label('time remaining / 剩餘時間: ', alignment=(1, 0.5))
85 countdown = make_label('%d' % FAIL_TIMEOUT, alignment=(0, 0.5))
86 hbox = gtk.HBox()
87 hbox.pack_start(title)
88 hbox.pack_start(countdown)
89 eb = gtk.EventBox()
90 eb.modify_bg(gtk.STATE_NORMAL, BLACK)
91 eb.add(hbox)
92 return eb, countdown
93
94
95def hide_cursor(gdk_window):
96 pixmap = gtk.gdk.Pixmap(None, 1, 1, 1)
97 color = gtk.gdk.Color()
98 cursor = gtk.gdk.Cursor(pixmap, pixmap, color, color, 0, 0)
99 gdk_window.set_cursor(cursor)
100
101
102def calc_scale(wanted_x, wanted_y):
103 (widget_size_x, widget_size_y) = factory.get_shared_data('test_widget_size')
104 scale_x = (0.9 * widget_size_x) / wanted_x
105 scale_y = (0.9 * widget_size_y) / wanted_y
106 scale = scale_y if scale_y < scale_x else scale_x
107 scale = 1 if scale > 1 else scale
108 factory.log('scale: %s' % scale)
109 return scale
110
111
112def trim(text, length):
113 if len(text) > length:
114 text = text[:length-3] + '...'
115 return text
116
117
118def make_summary_box(tests, state_map, rows=15):
119 '''
120 Creates a widget display status of a set of test.
121
122 @param tests: A list of FactoryTest nodes whose status (and children's
123 status) should be displayed.
124 @param state_map: The state map as provide by the state instance.
125 @param rows: The number of rows to display.
126 @return: A tuple (widget, label_map), where widget is the widget, and
127 label_map is a map from each test to the corresponding label.
128 '''
129 LABEL_EN_SIZE = (170, 35)
130 LABEL_EN_SIZE_2 = (450, 25)
131 LABEL_EN_FONT = pango.FontDescription('courier new extra-condensed 16')
132
133 all_tests = sum([list(t.walk(in_order=True)) for t in tests], [])
134 columns = len(all_tests) / rows + (len(all_tests) % rows != 0)
135
136 info_box = gtk.HBox()
137 info_box.set_spacing(20)
138 for status in (TestState.ACTIVE, TestState.PASSED,
139 TestState.FAILED, TestState.UNTESTED):
140 label = make_label(status,
141 size=LABEL_EN_SIZE,
142 font=LABEL_EN_FONT,
143 alignment=(0.5, 0.5),
144 fg=LABEL_COLORS[status])
145 info_box.pack_start(label, False, False)
146
147 vbox = gtk.VBox()
148 vbox.set_spacing(20)
149 vbox.pack_start(info_box, False, False)
150
151 label_map = {}
152
153 if all_tests:
154 status_table = gtk.Table(rows, columns, True)
155 for (j, i), t in izip(product(xrange(columns), xrange(rows)),
156 all_tests):
157 msg_en = ' ' * (t.depth() - 1) + t.label_en
158 msg_en = trim(msg_en, 12)
159 if t.label_zh:
160 msg = '{0:<12} ({1})'.format(msg_en, t.label_zh)
161 else:
162 msg = msg_en
163 status = state_map[t].status
164 status_label = make_label(msg,
165 size=LABEL_EN_SIZE_2,
166 font=LABEL_EN_FONT,
167 alignment=(0.0, 0.5),
168 fg=LABEL_COLORS[status])
169 label_map[t] = status_label
170 status_table.attach(status_label, j, j+1, i, i+1)
171 vbox.pack_start(status_table, False, False)
172
173 return vbox, label_map
174
175
176def run_test_widget(dummy_job, test_widget,
177 invisible_cursor=True,
178 window_registration_callback=None,
179 cleanup_callback=None):
180 test_widget_size = factory.get_shared_data('test_widget_size')
181
182 window = gtk.Window(gtk.WINDOW_TOPLEVEL)
183 window.modify_bg(gtk.STATE_NORMAL, BLACK)
184 window.set_size_request(*test_widget_size)
185
186 def show_window():
187 window.show()
188 window.window.raise_() # pylint: disable=E1101
189 gtk.gdk.pointer_grab(window.window, confine_to=window.window)
190 if invisible_cursor:
191 hide_cursor(window.window)
192
193 test_path = factory.get_current_test_path()
194
195 def handle_event(event):
196 if (event.type == Event.Type.STATE_CHANGE and
197 test_path and event.path == test_path and
198 event.state.visible):
199 show_window()
200
201 event_client = EventClient(
202 callback=handle_event,
203 event_loop=EventClient.EVENT_LOOP_GOBJECT_IO)
204
205 align = gtk.Alignment(xalign=0.5, yalign=0.5)
206 align.add(test_widget)
207
208 window.add(align)
209 for c in window.get_children():
210 # Show all children, but not the window itself yet.
211 c.show_all()
212
213 if window_registration_callback is not None:
214 window_registration_callback(window)
215
216 # Show the window if it is the visible test, or if the test_path is not
217 # available (e.g., run directly from the command line).
218 if (not test_path) or (
219 TestState.from_dict_or_object(
220 factory.get_state_instance().get_test_state(test_path)).visible):
221 show_window()
222 else:
223 window.hide()
224
225 gtk.main()
226
227 gtk.gdk.pointer_ungrab()
228
229 if cleanup_callback is not None:
230 cleanup_callback()
231
232 del event_client