Home | History | Annotate | Download | only in graphics_VTSwitch
      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 import glob
      5 import logging
      6 import os
      7 import time
      8 
      9 from autotest_lib.client.bin import test
     10 from autotest_lib.client.bin import utils
     11 from autotest_lib.client.common_lib import error
     12 from autotest_lib.client.common_lib.cros import chrome
     13 from autotest_lib.client.cros.graphics import graphics_utils
     14 from autotest_lib.client.cros.image_comparison import pdiff_image_comparer
     15 from autotest_lib.client.cros.input_playback import input_playback
     16 
     17 def 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 
     37 class graphics_VTSwitch(graphics_utils.GraphicsTest):
     38     """
     39     Verify that VT switching works.
     40     """
     41     version = 2
     42     _WAIT = 5
     43     # TODO(crosbug.com/36417): Need to handle more than one display screen.
     44 
     45     def initialize(self):
     46         super(graphics_VTSwitch, self).initialize()
     47         self._player = input_playback.InputPlayback()
     48         self._player.emulate(input_type='keyboard')
     49         self._player.find_connected_inputs()
     50 
     51     def _open_vt2(self):
     52         """Use keyboard shortcut to open VT2."""
     53         self._player.blocking_playback_of_default_file(
     54             input_type='keyboard', filename='keyboard_ctrl+alt+f2')
     55         time.sleep(self._WAIT)
     56 
     57     def _open_vt1(self):
     58         """Use keyboard shortcut to close VT2."""
     59         self._player.blocking_playback_of_default_file(
     60             input_type='keyboard', filename='keyboard_ctrl+alt+f1')
     61         time.sleep(self._WAIT)
     62 
     63     @graphics_utils.GraphicsTest.failure_report_decorator('graphics_VTSwitch')
     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         self._player.close()
    206         super(graphics_VTSwitch, self).cleanup()
    207