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. 4import glob 5import logging 6import os 7import time 8 9from autotest_lib.client.bin import test 10from autotest_lib.client.bin import utils 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.common_lib.cros import chrome 13from autotest_lib.client.cros.graphics import graphics_utils 14from autotest_lib.client.cros.image_comparison import pdiff_image_comparer 15from autotest_lib.client.cros.input_playback import input_playback 16 17def get_percent_difference(file1, file2): 18 """ 19 Performs pixel comparison of two files, given by their paths |file1| 20 and |file2| using terminal tool 'perceptualdiff' and returns percentage 21 difference of the total file size. 22 23 @param file1: path to image 24 @param file2: path to secondary image 25 @return: percentage difference of total file size. 26 @raise ValueError: if image dimensions are not the same 27 @raise OSError: if file does not exist or cannot be opened. 28 29 """ 30 # Using pdiff image comparer to compare the two images. This class 31 # invokes the terminal tool perceptualdiff. 32 pdi = pdiff_image_comparer.PdiffImageComparer() 33 diff_bytes = pdi.compare(file1, file2)[0] 34 return round(100. * diff_bytes / os.path.getsize(file1)) 35 36 37class graphics_VTSwitch(test.test): 38 """ 39 Verify that VT switching works. 40 """ 41 version = 2 42 GSC = None 43 _WAIT = 5 44 # TODO(crosbug.com/36417): Need to handle more than one display screen. 45 46 def initialize(self): 47 self.GSC = graphics_utils.GraphicsStateChecker() 48 self._player = input_playback.InputPlayback() 49 self._player.emulate(input_type='keyboard') 50 self._player.find_connected_inputs() 51 52 def _open_vt2(self): 53 """Use keyboard shortcut to open VT2.""" 54 self._player.blocking_playback_of_default_file( 55 input_type='keyboard', filename='keyboard_ctrl+alt+f2') 56 time.sleep(self._WAIT) 57 58 def _open_vt1(self): 59 """Use keyboard shortcut to close VT2.""" 60 self._player.blocking_playback_of_default_file( 61 input_type='keyboard', filename='keyboard_ctrl+alt+f1') 62 time.sleep(self._WAIT) 63 64 def run_once(self, 65 num_iterations=2, 66 similarity_percent_threshold=95, 67 difference_percent_threshold=5): 68 69 # Check for chromebook type devices 70 if not utils.get_board_type() == 'CHROMEBOOK': 71 raise error.TestNAError('DUT is not Chromebook. Test Skipped.') 72 73 self._num_errors = 0 74 keyvals = {} 75 76 # Make sure we start in VT1. 77 self._open_vt1() 78 79 with chrome.Chrome(): 80 81 # Take screenshot of browser. 82 vt1_screenshot = self._take_current_vt_screenshot(1) 83 84 keyvals['num_iterations'] = num_iterations 85 86 # Go to VT2 and take a screenshot. 87 self._open_vt2() 88 vt2_screenshot = self._take_current_vt_screenshot(2) 89 90 # Make sure VT1 and VT2 are sufficiently different. 91 diff = get_percent_difference(vt1_screenshot, vt2_screenshot) 92 keyvals['percent_initial_VT1_VT2_difference'] = diff 93 if not diff >= difference_percent_threshold: 94 self._num_errors += 1 95 logging.error('VT1 and VT2 screenshots only differ by ' + \ 96 '%d %%: %s vs %s' % 97 (diff, vt1_screenshot, vt2_screenshot)) 98 99 num_identical_vt1_screenshots = 0 100 num_identical_vt2_screenshots = 0 101 max_vt1_difference_percent = 0 102 max_vt2_difference_percent = 0 103 104 # Repeatedly switch between VT1 and VT2. 105 for iteration in xrange(num_iterations): 106 logging.info('Iteration #%d', iteration) 107 108 # Go to VT1 and take a screenshot. 109 self._open_vt1() 110 current_vt1_screenshot = self._take_current_vt_screenshot(1) 111 112 # Make sure the current VT1 screenshot is the same as (or similar 113 # to) the original login screen screenshot. 114 diff = get_percent_difference(vt1_screenshot, 115 current_vt1_screenshot) 116 if not diff < similarity_percent_threshold: 117 max_vt1_difference_percent = \ 118 max(diff, max_vt1_difference_percent) 119 self._num_errors += 1 120 logging.error('VT1 screenshots differ by %d %%: %s vs %s', 121 diff, vt1_screenshot, 122 current_vt1_screenshot) 123 else: 124 num_identical_vt1_screenshots += 1 125 126 # Go to VT2 and take a screenshot. 127 self._open_vt2() 128 current_vt2_screenshot = self._take_current_vt_screenshot(2) 129 130 # Make sure the current VT2 screenshot is the same as (or 131 # similar to) the first VT2 screenshot. 132 diff = get_percent_difference(vt2_screenshot, 133 current_vt2_screenshot) 134 if not diff <= similarity_percent_threshold: 135 max_vt2_difference_percent = \ 136 max(diff, max_vt2_difference_percent) 137 self._num_errors += 1 138 logging.error( 139 'VT2 screenshots differ by %d %%: %s vs %s', 140 diff, vt2_screenshot, current_vt2_screenshot) 141 else: 142 num_identical_vt2_screenshots += 1 143 144 self._open_vt1() 145 146 keyvals['percent_VT1_screenshot_max_difference'] = \ 147 max_vt1_difference_percent 148 keyvals['percent_VT2_screenshot_max_difference'] = \ 149 max_vt2_difference_percent 150 keyvals['num_identical_vt1_screenshots'] = num_identical_vt1_screenshots 151 keyvals['num_identical_vt2_screenshots'] = num_identical_vt2_screenshots 152 153 self.write_perf_keyval(keyvals) 154 155 if self._num_errors > 0: 156 raise error.TestFail('Failed: saw %d VT switching errors.' % 157 self._num_errors) 158 159 def _take_current_vt_screenshot(self, current_vt): 160 """ 161 Captures a screenshot of the current VT screen in BMP format. 162 163 @param current_vt: desired vt for screenshot. 164 165 @returns the path of the screenshot file. 166 167 """ 168 extension = 'bmp' 169 170 # In VT1, X is running so use that screenshot function. 171 if current_vt == 1: 172 return graphics_utils.take_screenshot(self.resultsdir, 173 'graphics_VTSwitch_VT1', 174 extension) 175 176 # Otherwise, take a screenshot using screenshot.py. 177 prefix = 'graphics_VTSwitch_VT2' 178 next_index = len(glob.glob( 179 os.path.join(self.resultsdir, '%s-*.%s' % (prefix, extension)))) 180 filename = '%s-%d.%s' % (prefix, next_index, extension) 181 output_path = os.path.join(self.resultsdir, filename) 182 return self._take_screenshot(output_path) 183 184 def _take_screenshot(self, output_path): 185 """ 186 Takes screenshot. 187 188 @param output_path: screenshot output path. 189 190 @returns output_path where screenshot was saved. 191 192 """ 193 screenshot_path = os.path.join(self.autodir, 'bin', 'screenshot.py') 194 utils.system('%s %s' % (screenshot_path, output_path), 195 ignore_status=True) 196 utils.system('sync', ignore_status=True) 197 time.sleep(self._WAIT) 198 logging.info('Saving screenshot to %s', output_path) 199 return output_path 200 201 def cleanup(self): 202 # Return to VT1 when done. Ideally, the screen should already be in VT1 203 # but the test might fail and terminate while in VT2. 204 self._open_vt1() 205 if self.GSC: 206 self.GSC.finalize() 207 self._player.close() 208