# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import glob, logging, os
from autotest_lib.client.bin import test, utils
from autotest_lib.client.common_lib import base_utils, error
from autotest_lib.client.cros.graphics import graphics_utils
def get_percent_difference(file1, file2):
"""
Performs byte-by-byte comparison of two files, given by their paths |file1|
and |file2|. Returns difference as a percentage of the total file size. If
one file is larger than the other, the difference is a percentage of
|file1|.
"""
files = (file1, file2)
sizes = {}
for filename in files:
if not os.path.exists(filename):
raise error.TestFail('Could not find file \'%s\'.' % filename)
sizes[filename] = os.path.getsize(filename)
if sizes[filename] == 0:
raise error.TestFail('File \'%s\' has zero size.' % filename)
diff_bytes = int(utils.system_output('cmp -l %s %s | wc -l' % files))
return round(100. * diff_bytes / sizes[file1])
class graphics_VTSwitch(test.test):
"""
Verify that VT switching works.
"""
version = 1
GSC = None
# TODO(crosbug.com/36417): Need to handle more than one display screen.
def setup(self):
self.job.setup_dep(['gfxtest'])
def initialize(self):
self.GSC = graphics_utils.GraphicsStateChecker()
def cleanup(self):
# Return to VT1 when done. Ideally, the screen should already be in VT1
# but the test might fail and terminate while in VT2.
self._switch_to_vt(1)
if self.GSC:
self.GSC.finalize()
def run_once(self,
num_iterations=2,
similarity_percent_threshold=95,
difference_percent_threshold=5):
# TODO(ihf): Remove this once VTSwitch works on freon.
if utils.is_freon():
raise error.TestNAError(
'Test needs work on Freon. See crbug.com/413088.')
self._num_errors = 0
keyvals = {}
# Make sure we start in VT1
if not self._switch_to_vt(1):
raise error.TestFail('Could not switch to VT1')
# Take screenshot of sign-in screen.
logged_out_screenshot = self._take_current_vt_screenshot()
keyvals['num_iterations'] = num_iterations
# Go to VT2 and take a screenshot.
if not self._switch_to_vt(2):
raise error.TestFail('Could not switch to VT2')
vt2_screenshot = self._take_current_vt_screenshot()
# Make sure VT1 and VT2 are sufficiently different.
diff = get_percent_difference(logged_out_screenshot, vt2_screenshot)
keyvals['percent_initial_VT1_VT2_difference'] = diff
if not diff >= difference_percent_threshold:
self._num_errors += 1
logging.error('VT1 and VT2 screenshots only differ by ' + \
'%d %%: %s vs %s' %
(diff, logged_out_screenshot, vt2_screenshot))
num_identical_vt1_screenshots = 0
num_identical_vt2_screenshots = 0
max_vt1_difference_percent = 0
max_vt2_difference_percent = 0
# Repeatedly switch between VT1 and VT2.
for iteration in xrange(num_iterations):
logging.info('Iteration #%d', iteration)
# Go to VT1 and take a screenshot.
self._switch_to_vt(1)
current_vt1_screenshot = self._take_current_vt_screenshot()
# Make sure the current VT1 screenshot is the same as (or similar
# to) the original login screen screenshot.
diff = get_percent_difference(logged_out_screenshot,
current_vt1_screenshot)
if not diff < similarity_percent_threshold:
max_vt1_difference_percent = \
max(diff, max_vt1_difference_percent)
self._num_errors += 1
logging.error('VT1 screenshots differ by %d %%: %s vs %s',
diff, logged_out_screenshot,
current_vt1_screenshot)
else:
num_identical_vt1_screenshots += 1
# Go to VT2 and take a screenshot.
self._switch_to_vt(2)
current_vt2_screenshot = self._take_current_vt_screenshot()
# Make sure the current VT2 screenshot is the same as (or similar
# to) the first VT2 screenshot.
diff = get_percent_difference(vt2_screenshot,
current_vt2_screenshot)
if not diff <= similarity_percent_threshold:
max_vt2_difference_percent = \
max(diff, max_vt2_difference_percent)
self._num_errors += 1
logging.error(
'VT2 screenshots differ by %d %%: %s vs %s',
diff, vt2_screenshot, current_vt2_screenshot)
else:
num_identical_vt2_screenshots += 1
self._switch_to_vt(1)
keyvals['percent_VT1_screenshot_max_difference'] = \
max_vt1_difference_percent
keyvals['percent_VT2_screenshot_max_difference'] = \
max_vt2_difference_percent
keyvals['num_identical_vt1_screenshots'] = num_identical_vt1_screenshots
keyvals['num_identical_vt2_screenshots'] = num_identical_vt2_screenshots
self.write_perf_keyval(keyvals)
if self._num_errors > 0:
raise error.TestError('Test failed with %d errors' %
self._num_errors)
def _take_current_vt_screenshot(self):
"""
Captures a screenshot of the current VT screen in BMP format.
Returns the path of the screenshot file.
"""
current_vt = int(utils.system_output('fgconsole'))
extension = 'bmp'
# In VT1, X is running so use that screenshot function.
if current_vt == 1:
return graphics_utils.take_screenshot(self.resultsdir,
'graphics_VTSwitch_VT1',
extension)
# Otherwise, grab the framebuffer using DRM.
prefix = 'graphics_VTSwitch_VT2'
next_index = len(glob.glob(
os.path.join(self.resultsdir, '%s-*.%s' % (prefix, extension))))
filename = '%s-%d.%s' % (prefix, next_index, extension)
output_path = os.path.join(self.resultsdir, filename)
return self._take_drm_screenshot(output_path)
def _take_drm_screenshot(self, output_path):
"""
Takes drm screenshot.
"""
autotest_deps_path = os.path.join(self.autodir, 'deps')
getfb_path = os.path.join(autotest_deps_path, 'gfxtest', 'getfb')
output = utils.system_output('%s %s.rgba' % (getfb_path, output_path))
for line in output.split('\n'):
# Parse the getfb output for info about framebuffer size. The line
# should looks omething like:
# Framebuffer info: 1024x768, 32bpp
if line.startswith('Framebuffer info:'):
size = line.split(':')[1].split(',')[0].strip()
break
utils.system('convert -depth 8 -size %s %s.rgba %s' %
(size, output_path, output_path))
logging.info('Saving screenshot to %s', output_path)
return output_path
def _switch_to_vt(self, vt):
"""
Switches to virtual terminal given by |vt| (1, 2, etc) and checks that
the switch was successful by calling fgconsole.
Returns True if fgconsole returned the new vt number, False otherwise.
"""
utils.system_output('chvt %d' % vt)
# Verify that the VT switch was successful.
current_vt = base_utils.wait_for_value(
lambda: int(utils.system_output('fgconsole')),
expected_value=vt)
if vt != current_vt:
self._num_errors += 1
logging.error('Current VT %d does not match expected VT %d',
current_vt, vt)
return False
logging.info('Switched to VT%d', vt)
return True