140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que# Use of this source code is governed by a BSD-style license that can be 340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que# found in the LICENSE file. 43f219a2165c5233fa9a0e6d71e6dbdb3fe06d106Simon Queimport glob, logging, os 540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 63dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedelfrom autotest_lib.client.bin import test, utils 740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Quefrom autotest_lib.client.common_lib import base_utils, error 83dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedelfrom autotest_lib.client.cros.graphics import graphics_utils 940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 1040b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Quedef get_percent_difference(file1, file2): 1140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que """ 1240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que Performs byte-by-byte comparison of two files, given by their paths |file1| 1340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que and |file2|. Returns difference as a percentage of the total file size. If 1440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que one file is larger than the other, the difference is a percentage of 1540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que |file1|. 1640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que """ 1740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que files = (file1, file2) 1840b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que sizes = {} 1940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que for filename in files: 2040b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que if not os.path.exists(filename): 2140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que raise error.TestFail('Could not find file \'%s\'.' % filename) 2240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que sizes[filename] = os.path.getsize(filename) 2340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que if sizes[filename] == 0: 2440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que raise error.TestFail('File \'%s\' has zero size.' % filename) 2540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 2640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que diff_bytes = int(utils.system_output('cmp -l %s %s | wc -l' % files)) 2740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 2840b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que return round(100. * diff_bytes / sizes[file1]) 2940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 3040b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 313dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedelclass graphics_VTSwitch(test.test): 323dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel """ 333dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel Verify that VT switching works. 343dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel """ 3540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que version = 1 363dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel GSC = None 3740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # TODO(crosbug.com/36417): Need to handle more than one display screen. 3840b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 393f219a2165c5233fa9a0e6d71e6dbdb3fe06d106Simon Que def setup(self): 403f219a2165c5233fa9a0e6d71e6dbdb3fe06d106Simon Que self.job.setup_dep(['gfxtest']) 413f219a2165c5233fa9a0e6d71e6dbdb3fe06d106Simon Que 423dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel def initialize(self): 433dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel self.GSC = graphics_utils.GraphicsStateChecker() 443dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel 453dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel def cleanup(self): 463dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel # Return to VT1 when done. Ideally, the screen should already be in VT1 473dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel # but the test might fail and terminate while in VT2. 483dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel self._switch_to_vt(1) 493dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel if self.GSC: 503dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel self.GSC.finalize() 513f219a2165c5233fa9a0e6d71e6dbdb3fe06d106Simon Que 5240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que def run_once(self, 5336eff4686c6781384f184cdff65510a5d6234429Simon Que num_iterations=2, 5440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que similarity_percent_threshold=95, 5540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que difference_percent_threshold=5): 56fd45dc71cfed09edcd30a39ffbd6a4cd441b89ceIlja H. Friedel # TODO(ihf): Remove this once VTSwitch works on freon. 57fd45dc71cfed09edcd30a39ffbd6a4cd441b89ceIlja H. Friedel if utils.is_freon(): 58fd45dc71cfed09edcd30a39ffbd6a4cd441b89ceIlja H. Friedel raise error.TestNAError( 59fd45dc71cfed09edcd30a39ffbd6a4cd441b89ceIlja H. Friedel 'Test needs work on Freon. See crbug.com/413088.') 60fd45dc71cfed09edcd30a39ffbd6a4cd441b89ceIlja H. Friedel 6140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que self._num_errors = 0 6240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que keyvals = {} 6340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 6440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # Make sure we start in VT1 6540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que if not self._switch_to_vt(1): 6640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que raise error.TestFail('Could not switch to VT1') 6740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 6840b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # Take screenshot of sign-in screen. 6940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que logged_out_screenshot = self._take_current_vt_screenshot() 7040b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 7140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que keyvals['num_iterations'] = num_iterations 7240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 7340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # Go to VT2 and take a screenshot. 7440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que if not self._switch_to_vt(2): 7540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que raise error.TestFail('Could not switch to VT2') 7640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que vt2_screenshot = self._take_current_vt_screenshot() 7740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 7840b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # Make sure VT1 and VT2 are sufficiently different. 7940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que diff = get_percent_difference(logged_out_screenshot, vt2_screenshot) 8040b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que keyvals['percent_initial_VT1_VT2_difference'] = diff 8140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que if not diff >= difference_percent_threshold: 8240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que self._num_errors += 1 8340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que logging.error('VT1 and VT2 screenshots only differ by ' + \ 8440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que '%d %%: %s vs %s' % 8540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que (diff, logged_out_screenshot, vt2_screenshot)) 8640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 8740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que num_identical_vt1_screenshots = 0 8840b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que num_identical_vt2_screenshots = 0 8940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que max_vt1_difference_percent = 0 9040b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que max_vt2_difference_percent = 0 9140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 9240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # Repeatedly switch between VT1 and VT2. 9340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que for iteration in xrange(num_iterations): 943dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel logging.info('Iteration #%d', iteration) 9540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 9640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # Go to VT1 and take a screenshot. 9740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que self._switch_to_vt(1) 9840b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que current_vt1_screenshot = self._take_current_vt_screenshot() 9940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 10040b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # Make sure the current VT1 screenshot is the same as (or similar 10140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # to) the original login screen screenshot. 10240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que diff = get_percent_difference(logged_out_screenshot, 10340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que current_vt1_screenshot) 10440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que if not diff < similarity_percent_threshold: 10540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que max_vt1_difference_percent = \ 10640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que max(diff, max_vt1_difference_percent) 10740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que self._num_errors += 1 1083dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel logging.error('VT1 screenshots differ by %d %%: %s vs %s', 1093dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel diff, logged_out_screenshot, 1103dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel current_vt1_screenshot) 11140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que else: 11240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que num_identical_vt1_screenshots += 1 11340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 11440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # Go to VT2 and take a screenshot. 11540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que self._switch_to_vt(2) 11640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que current_vt2_screenshot = self._take_current_vt_screenshot() 11740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 11840b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # Make sure the current VT2 screenshot is the same as (or similar 11940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # to) the first VT2 screenshot. 12040b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que diff = get_percent_difference(vt2_screenshot, 12140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que current_vt2_screenshot) 12240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que if not diff <= similarity_percent_threshold: 12340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que max_vt2_difference_percent = \ 12440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que max(diff, max_vt2_difference_percent) 12540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que self._num_errors += 1 12640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que logging.error( 1273dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel 'VT2 screenshots differ by %d %%: %s vs %s', 1283dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel diff, vt2_screenshot, current_vt2_screenshot) 12940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que else: 13040b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que num_identical_vt2_screenshots += 1 13140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 13240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que self._switch_to_vt(1) 13340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 13440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que keyvals['percent_VT1_screenshot_max_difference'] = \ 13540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que max_vt1_difference_percent 13640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que keyvals['percent_VT2_screenshot_max_difference'] = \ 13740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que max_vt2_difference_percent 13840b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que keyvals['num_identical_vt1_screenshots'] = num_identical_vt1_screenshots 13940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que keyvals['num_identical_vt2_screenshots'] = num_identical_vt2_screenshots 14040b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 14140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que self.write_perf_keyval(keyvals) 14240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 14340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que if self._num_errors > 0: 14440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que raise error.TestError('Test failed with %d errors' % 14540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que self._num_errors) 14640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 14740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 14840b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que def _take_current_vt_screenshot(self): 14940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que """ 150aba1f72618fec4b790af63794f60062fe0c49cd9Simon Que Captures a screenshot of the current VT screen in BMP format. 15140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que Returns the path of the screenshot file. 15240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que """ 15340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que current_vt = int(utils.system_output('fgconsole')) 154aba1f72618fec4b790af63794f60062fe0c49cd9Simon Que extension = 'bmp' 15540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 1563dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel # In VT1, X is running so use that screenshot function. 15740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que if current_vt == 1: 1583dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel return graphics_utils.take_screenshot(self.resultsdir, 1593dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel 'graphics_VTSwitch_VT1', 1603dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel extension) 16140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 16240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # Otherwise, grab the framebuffer using DRM. 16340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que prefix = 'graphics_VTSwitch_VT2' 16440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que next_index = len(glob.glob( 16540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que os.path.join(self.resultsdir, '%s-*.%s' % (prefix, extension)))) 166aba1f72618fec4b790af63794f60062fe0c49cd9Simon Que filename = '%s-%d.%s' % (prefix, next_index, extension) 16740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que output_path = os.path.join(self.resultsdir, filename) 16840b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que return self._take_drm_screenshot(output_path) 16940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 17040b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 17140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que def _take_drm_screenshot(self, output_path): 1723dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel """ 1733dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel Takes drm screenshot. 1743dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel """ 17540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que autotest_deps_path = os.path.join(self.autodir, 'deps') 17640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que getfb_path = os.path.join(autotest_deps_path, 'gfxtest', 'getfb') 177aba1f72618fec4b790af63794f60062fe0c49cd9Simon Que output = utils.system_output('%s %s.rgba' % (getfb_path, output_path)) 1785de8c55bcd38b3317419098d9459e19f1d49a7f9Simon Que for line in output.split('\n'): 1795de8c55bcd38b3317419098d9459e19f1d49a7f9Simon Que # Parse the getfb output for info about framebuffer size. The line 1805de8c55bcd38b3317419098d9459e19f1d49a7f9Simon Que # should looks omething like: 1815de8c55bcd38b3317419098d9459e19f1d49a7f9Simon Que # Framebuffer info: 1024x768, 32bpp 1825de8c55bcd38b3317419098d9459e19f1d49a7f9Simon Que if line.startswith('Framebuffer info:'): 1835de8c55bcd38b3317419098d9459e19f1d49a7f9Simon Que size = line.split(':')[1].split(',')[0].strip() 1845de8c55bcd38b3317419098d9459e19f1d49a7f9Simon Que break 185aba1f72618fec4b790af63794f60062fe0c49cd9Simon Que utils.system('convert -depth 8 -size %s %s.rgba %s' % 186aba1f72618fec4b790af63794f60062fe0c49cd9Simon Que (size, output_path, output_path)) 18740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 1883dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel logging.info('Saving screenshot to %s', output_path) 189aba1f72618fec4b790af63794f60062fe0c49cd9Simon Que return output_path 19040b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 19140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 19240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que def _switch_to_vt(self, vt): 19340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que """ 19440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que Switches to virtual terminal given by |vt| (1, 2, etc) and checks that 19540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que the switch was successful by calling fgconsole. 19640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 19740b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que Returns True if fgconsole returned the new vt number, False otherwise. 19840b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que """ 19940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que utils.system_output('chvt %d' % vt) 20040b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que 20140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que # Verify that the VT switch was successful. 20240b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que current_vt = base_utils.wait_for_value( 20340b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que lambda: int(utils.system_output('fgconsole')), 20440b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que expected_value=vt) 20540b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que if vt != current_vt: 20640b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que self._num_errors += 1 2073dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel logging.error('Current VT %d does not match expected VT %d', 2083dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel current_vt, vt) 20940b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que return False 2103dab8e583cce64f14f728e656798c48ed2ea4143Ilja Friedel logging.info('Switched to VT%d', vt) 21140b289060ac9d8bc97299dc6a6f7515fc326a13cSimon Que return True 212