1# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Some utility classes and functions.""" 6 7import glob 8import os 9import re 10import sys 11import time 12 13import common_util 14import test_conf as conf 15 16 17def get_display_name(): 18 """Return the display name.""" 19 return ':0' 20 21 22def get_screen_size(): 23 """Get the screen size using xwininfo.""" 24 cmd = 'DISPLAY=:0 xwininfo -root' 25 wininfo = common_util.simple_system_output(cmd) 26 # the geometry string looks like: 27 # " -geometry 3840x1200+0+0" 28 geometry_pattern = re.compile('\s*-geometry\s*(\d+)x(\d+)+.*', re.I) 29 for line in wininfo.splitlines(): 30 result = geometry_pattern.search(line) 31 if result: 32 width = int(result.group(1)) 33 height = int(result.group(2)) 34 return (width, height) 35 return None 36 37 38def get_current_time_str(): 39 """Get the string of current time.""" 40 time_format = '%Y%m%d_%H%M%S' 41 return time.strftime(time_format, time.gmtime()) 42 43 44def get_board(): 45 """Get board of the Chromebook machine.""" 46 with open('/etc/lsb-release') as lsb: 47 context = lsb.read() 48 board = None 49 if context is not None: 50 for line in context.splitlines(): 51 if line.startswith('CHROMEOS_RELEASE_BOARD'): 52 board_str = line.split('=')[1] 53 if '-' in board_str: 54 board = board_str.split('-')[1] 55 elif '_' in board_str: 56 board = board_str.split('_')[1] 57 else: 58 board = board_str 59 # Some boards, e.g. alex, may have board name as alex32 60 board = re.search('(\D+)\d*', board, re.I).group(1) 61 break 62 return board 63 64 65def get_board_from_filename(filename): 66 """Get the board name from a given string which is usually a log file. 67 68 A log file looks like: 69 touch_firmware_report-lumpy-fw_11.27-complete-20130610_150540.log 70 71 @param filename: the filename used to extract the board 72 """ 73 pieces = filename.split('-') 74 return pieces[1] if len(pieces) >= 2 else None 75 76def get_board_from_directory(directory): 77 """Get the board name from the log in the replay directory. 78 79 @param directory: the log directory 80 """ 81 log_files = glob.glob(os.path.join(conf.log_root_dir, directory, '*.log')) 82 for log_file in log_files: 83 board = get_board_from_filename(os.path.basename(log_file)) 84 if board: 85 return board 86 return None 87 88 89def get_cpu(): 90 """Get the processor of the machine.""" 91 return common_util.simple_system_output('uname -m') 92 93 94def install_pygtk(): 95 """A temporary dirty hack of installing pygtk related packages.""" 96 pygtk_dict = {'x86_64': ['pygtk_x86_64.tbz2', 'lib64'], 97 'i686': ['pygtk_x86_32.tbz2', 'lib'], 98 'armv7l': ['pygtk_armv7l.tbz2', 'lib'], 99 } 100 pygtk_info = pygtk_dict.get(get_cpu().lower()) 101 102 if get_board() is None: 103 print 'This does not look like a chromebook.' 104 elif pygtk_info: 105 cmd_remount = 'mount -o remount,rw /' 106 if common_util.simple_system(cmd_remount) == 0: 107 pygtk_tarball, lib_path = pygtk_info 108 cmd_untar = 'tar -jxf pygtk/%s -C /' % pygtk_tarball 109 if common_util.simple_system(cmd_untar) == 0: 110 # Need to add the gtk path manually. Otherwise, the path 111 # may not be in the sys.path for the first time when the 112 # tarball is extracted. 113 gtk_path = ('/usr/local/%s/python2.7/site-packages/gtk-2.0' % 114 lib_path) 115 sys.path.append(gtk_path) 116 print 'Successful installation of pygtk.' 117 return True 118 else: 119 print 'Error: Failed to install pygtk.' 120 else: 121 print 'Failed to remount. Have you removed the write protect?' 122 else: 123 print 'The pygtk is only supported for %s so far.' % pygtk_dict.keys() 124 print 'The other cpus will be supported on demand.' 125 print 'The plan is to remove gtk totally and upgrade to Chrome browser.' 126 return False 127 128 129def get_fw_and_date(filename): 130 """Get the firmware version and the test date from a log directory 131 or a log file. 132 133 An example html filename looks like 134 'touch_firmware_report-link-fw_1.0.170-manual-20130426_064849.log' 135 return (fw_1.0.170, 20130426_064849) 136 137 An example log directory looks like 138 '20130422_020631-fw_1.0.170-manual' 139 return (fw_1.0.170, 20130422_020631) 140 """ 141 # The firmware could be fw_1.0.170 or fw_1.0.AA which always comes with 142 # 'fw_' as its prefix. The character '-' is used to separate components 143 # in the filename. 144 result = re.search('-(%s[^-]+?)-' % conf.fw_prefix, filename) 145 fw = result.group(1) if result else None 146 147 result = re.search('(\d{8}_\d{6})[-.]', filename) 148 date = result.group(1) if result else None 149 150 return (fw, date) 151 152 153def create_log_dir(firmware_version, mode): 154 """Create a directory to save the report and device event files.""" 155 dir_basename = conf.filename.sep.join([get_current_time_str(), 156 conf.fw_prefix + firmware_version, 157 mode]) 158 log_root_dir = conf.log_root_dir 159 log_dir = os.path.join(log_root_dir, dir_basename) 160 latest_symlink = os.path.join(log_root_dir, 'latest') 161 162 # Create the log directory. 163 try: 164 os.makedirs(log_dir) 165 except OSError, e: 166 print 'Error in create the directory (%s): %s' % (log_dir, e) 167 sys.exit(1) 168 169 # Set up the latest symbolic link to the newly created log directory. 170 try: 171 if os.path.islink(latest_symlink): 172 os.remove(latest_symlink) 173 os.symlink(log_dir, latest_symlink) 174 except OSError, e: 175 print 'Error in setup latest symlink (%s): %s' % (latest_symlink, e) 176 sys.exit(1) 177 return log_dir 178 179 180def stop_power_management(): 181 """Stop the power daemon management.""" 182 ret_d = common_util.simple_system('stop -q powerd') 183 if ret_d: 184 print 'Error in stopping powerd.' 185 print 'The screen may dim during the test.' 186 187 188def start_power_management(): 189 """Start the power daemon management.""" 190 ret_d = common_util.simple_system('start -q powerd') 191 if ret_d: 192 print 'Error in starting powerd.' 193 print 'The screen may not go into suspend mode.' 194 print 'If this is a problem, you could reboot the machine.' 195 196 197class GestureList: 198 """A class defines the gesture list.""" 199 200 def __init__(self, gesture_names=None): 201 self.gesture_names = (gesture_names if gesture_names 202 else conf.gesture_names_complete) 203 204 def get_gesture_list(self, key=None): 205 """Get the list of Gesture objects based on the gesture names.""" 206 gesture_dict = conf.get_gesture_dict() 207 gesture_list = [] 208 for name in self.gesture_names: 209 gesture = gesture_dict.get(name) 210 if gesture is None: 211 msg = 'Error: the gesture "%s" is not defined in the config.' 212 print msg % name 213 return [] 214 gesture_list.append(gesture) 215 return sorted(gesture_list, key=key) if key else gesture_list 216 217 218class Output: 219 """A class to handle outputs to the window and to the report.""" 220 def __init__(self, log_dir, report_name, win, report_html): 221 self.log_dir = log_dir 222 self.report_name = report_name 223 self.report = open(report_name, 'w') 224 self.win = win 225 self.prefix_space = ' ' * 4 226 self.msg = None 227 self.report_html = report_html 228 229 def __del__(self): 230 self.stop() 231 232 def stop(self): 233 """Close the report file and print it on stdout.""" 234 self.report.close() 235 with open(self.report_name) as f: 236 for line in f.read().splitlines(): 237 print line 238 report_msg = '\n*** This test report is saved in the file: %s\n' 239 print report_msg % self.report_name 240 241 def get_prefix_space(self): 242 """Get the prefix space when printing the report.""" 243 return self.prefix_space 244 245 def print_report_line(self, msg): 246 """Print the line with proper indentation.""" 247 self.report.write(self.prefix_space + str(msg) + os.linesep) 248 249 def print_window(self, msg): 250 """Print the message to the result window.""" 251 if type(msg) is list: 252 msg = os.linesep.join(msg) 253 self.win.set_result(msg) 254 print msg 255 256 def _print_report(self, msg): 257 """Print the message to the report.""" 258 if type(msg) is list: 259 for line in msg: 260 self.print_report_line(line) 261 else: 262 self.print_report_line(msg) 263 264 def buffer_report(self, msg): 265 """Buffer the message and print it later if not over-written. 266 267 Usage of the method: the validator test result of a gesture may 268 be discarded because the user chooses to re-perform the gesture 269 again. So it should be able to over-write the message. 270 """ 271 self.msg = msg 272 273 def flush_report(self): 274 """Print the buffered message if any.""" 275 if self.msg: 276 self._print_report(self.msg) 277 self.msg = None 278 279 def print_report(self, msg): 280 """Print the message to the report.""" 281 # Print any buffered message first. 282 self.flush_report() 283 # Print this incoming message 284 self._print_report(msg) 285 286 def print_all(self, msg): 287 """Print the message to both report and to the window.""" 288 self.print_window(msg) 289 self.buffer_report(msg) 290 291 292class ScreenShot: 293 """Handle screen shot.""" 294 295 def __init__(self, geometry_str): 296 self.geometry_str = geometry_str 297 environment_str = 'DISPLAY=:0.0 XAUTHORITY=/home/chronos/.Xauthority ' 298 dump_util = '/usr/local/bin/import -quality 20' 299 self.dump_window_format = ' '.join([environment_str, dump_util, 300 '-window %s %s.png']) 301 self.dump_root_format = ' '.join([environment_str, dump_util, 302 '-window root -crop %s %s.png']) 303 self.get_id_cmd = 'DISPLAY=:0 xwininfo -root -tree' 304 305 def dump_window(self, filename): 306 """Dump the screenshot of a window to the specified file name.""" 307 win_id = self._get_win_id() 308 if win_id: 309 dump_cmd = self.dump_window_format % (win_id, filename) 310 common_util.simple_system(dump_cmd) 311 else: 312 print 'Warning: cannot get the window id.' 313 314 def dump_root(self, filename): 315 """Dump the screenshot of root to the specified file name.""" 316 dump_cmd = self.dump_root_format % (self.geometry_str, filename) 317 common_util.simple_system(dump_cmd) 318 319 def _get_win_id(self): 320 """Get the window ID based on the characteristic string.""" 321 result = common_util.simple_system_output(self.get_id_cmd) 322 for line in result.splitlines(): 323 if self.geometry_str in line: 324 return line.split()[0].strip() 325 return None 326