Home | History | Annotate | Download | only in video
      1 # Copyright 2015 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 import collections
      6 import logging
      7 import re
      8 
      9 from autotest_lib.client.bin import utils
     10 from autotest_lib.client.common_lib import error
     11 
     12 
     13 def get_histogram_text(tab, histogram_name):
     14      """
     15      This returns contents of the given histogram.
     16 
     17      @param tab: object, Chrome tab instance
     18      @param histogram_name: string, name of the histogram
     19      @returns string: contents of the histogram
     20      """
     21      docEle = 'document.documentElement'
     22      tab.Navigate('chrome://histograms/%s' % histogram_name)
     23      tab.WaitForDocumentReadyStateToBeComplete()
     24      raw_text = tab.EvaluateJavaScript(
     25           '{0} && {0}.innerText'.format(docEle))
     26      # extract the contents of the histogram
     27      histogram = raw_text[raw_text.find('Histogram:'):].strip()
     28      if histogram:
     29           logging.debug('chrome://histograms/%s:\n%s', histogram_name,
     30                         histogram)
     31      else:
     32           logging.debug('No histogram is shown in chrome://histograms/%s',
     33                         histogram_name)
     34      return histogram
     35 
     36 
     37 def loaded(tab, histogram_name, pattern):
     38      """
     39      Checks if the histogram page has been fully loaded.
     40 
     41      @param tab: object, Chrome tab instance
     42      @param histogram_name: string, name of the histogram
     43      @param pattern: string, required text to look for
     44      @returns re.MatchObject if the given pattern is found in the text
     45               None otherwise
     46 
     47      """
     48      return re.search(pattern, get_histogram_text(tab, histogram_name))
     49 
     50 
     51 def  verify(cr, histogram_name, histogram_bucket_value):
     52      """
     53      Verifies histogram string and success rate in a parsed histogram bucket.
     54      The histogram buckets are outputted in debug log regardless of the
     55      verification result.
     56 
     57      Full histogram URL is used to load histogram. Example Histogram URL is :
     58      chrome://histograms/Media.GpuVideoDecoderInitializeStatus
     59 
     60      @param cr: object, the Chrome instance
     61      @param histogram_name: string, name of the histogram
     62      @param histogram_bucket_value: int, required bucket number to look for
     63      @raises error.TestError if histogram is not successful
     64 
     65      """
     66      bucket_pattern = '\n'+ str(histogram_bucket_value) +'.*100\.0%.*'
     67      error_msg_format = ('{} not loaded or histogram bucket not found '
     68                          'or histogram bucket found at < 100%')
     69      tab = cr.browser.tabs.New()
     70      msg = error_msg_format.format(histogram_name)
     71      utils.poll_for_condition(lambda : loaded(tab, histogram_name,
     72                                               bucket_pattern),
     73                               exception=error.TestError(msg),
     74                               sleep_interval=1)
     75 
     76 
     77 def is_bucket_present(cr,histogram_name, histogram_bucket_value):
     78      """
     79      This returns histogram succes or fail to called function
     80 
     81      @param cr: object, the Chrome instance
     82      @param histogram_name: string, name of the histogram
     83      @param histogram_bucket_value: int, required bucket number to look for
     84      @returns True if histogram page was loaded and the bucket was found.
     85               False otherwise
     86 
     87      """
     88      try:
     89           verify(cr,histogram_name, histogram_bucket_value)
     90      except error.TestError:
     91           return False
     92      else:
     93           return True
     94 
     95 
     96 def is_histogram_present(cr, histogram_name):
     97      """
     98      This checks if the given histogram is present and non-zero.
     99 
    100      @param cr: object, the Chrome instance
    101      @param histogram_name: string, name of the histogram
    102      @returns True if histogram page was loaded and the histogram is present
    103               False otherwise
    104 
    105      """
    106      histogram_pattern = 'Histogram: '+ histogram_name + ' recorded ' + \
    107                          r'[1-9][0-9]*' + ' samples'
    108      tab = cr.browser.tabs.New()
    109      try:
    110           utils.poll_for_condition(lambda : loaded(tab, histogram_name,
    111                                                    histogram_pattern),
    112                                    timeout=2,
    113                                    sleep_interval=0.1)
    114           return True
    115      except utils.TimeoutError:
    116           # the histogram is not present, and then returns false
    117           return False
    118 
    119 
    120 def get_histogram(cr, histogram_name):
    121      """
    122      This returns contents of the given histogram.
    123 
    124      @param cr: object, the Chrome instance
    125      @param histogram_name: string, name of the histogram
    126      @returns string: contents of the histogram
    127 
    128      """
    129      tab = cr.browser.tabs.New()
    130      return get_histogram_text(tab, histogram_name)
    131 
    132 
    133 def parse_histogram(histogram_text):
    134      """
    135      Parses histogram text into bucket structure.
    136 
    137      @param histogram_text: histogram raw text.
    138      @returns dict(bucket_value, bucket_count)
    139      """
    140      # Match separator line, e.g. "1   ..."
    141      RE_SEPEARTOR = re.compile(r'\d+\s+\.\.\.')
    142      # Match bucket line, e.g. "2  --O  (46 = 1.5%) {46.1%}"
    143      RE_BUCKET = re.compile(
    144           r'(\d+)\s+\-*O\s+\((\d+) = (\d+\.\d+)%\).*')
    145      result = {}
    146      for line in histogram_text.splitlines():
    147           if RE_SEPEARTOR.match(line):
    148                continue
    149           m = RE_BUCKET.match(line)
    150           if m:
    151                result[int(m.group(1))] = int(m.group(2))
    152      return result
    153 
    154 
    155 def subtract_histogram(minuend, subtrahend):
    156      """
    157      Subtracts histogram: minuend - subtrahend
    158 
    159      @param minuend: histogram bucket dict from which another is to be
    160                      subtracted.
    161      @param subtrahend: histogram bucket dict to be subtracted from another.
    162      @result difference of the two histograms in bucket dict. Note that
    163              zero-counted buckets are removed.
    164      """
    165      result = collections.defaultdict(int, minuend)
    166      for k, v in subtrahend.iteritems():
    167           result[k] -= v
    168 
    169      # Remove zero counted buckets.
    170      return {k: v for k, v in result.iteritems() if v}
    171 
    172 
    173 def expect_sole_bucket(histogram_differ, bucket, bucket_name, timeout=10,
    174                        sleep_interval=1):
    175      """
    176      Returns true if the given bucket solely exists in histogram differ.
    177 
    178      @param histogram_differ: a HistogramDiffer instance used to get histogram
    179             name and histogram diff multiple times.
    180      @param bucket: bucket value.
    181      @param bucket_name: bucket name to be shown on error message.
    182      @param timeout: timeout in seconds.
    183      @param sleep_interval: interval in seconds between getting diff.
    184      @returns True if the given bucket solely exists in histogram.
    185      @raises TestError if bucket doesn't exist or other buckets exist.
    186      """
    187      timer = utils.Timer(timeout)
    188      histogram = {}
    189      histogram_name = histogram_differ.histogram_name
    190      while timer.sleep(sleep_interval):
    191           histogram = histogram_differ.end()
    192           if histogram:
    193                break
    194 
    195      if bucket not in histogram:
    196           raise error.TestError('Expect %s has %s. Histogram: %r' %
    197                                 (histogram_name, bucket_name, histogram))
    198      if len(histogram) > 1:
    199           raise error.TestError('%s has bucket other than %s. Histogram: %r' %
    200                                 (histogram_name, bucket_name, histogram))
    201      return True
    202 
    203 
    204 def poll_histogram_grow(histogram_differ, timeout=2, sleep_interval=0.1):
    205      """
    206      Polls histogram to see if it grows within |timeout| seconds.
    207 
    208      @param histogram_differ: a HistogramDiffer instance used to get histogram
    209             name and histogram diff multiple times.
    210      @param timeout: observation timeout in seconds.
    211      @param sleep_interval: interval in seconds between getting diff.
    212      @returns (True, histogram_diff) if the histogram grows.
    213               (False, {}) if it does not grow in |timeout| seconds.
    214      """
    215      timer = utils.Timer(timeout)
    216      while timer.sleep(sleep_interval):
    217           histogram_diff = histogram_differ.end()
    218           if histogram_diff:
    219                return (True, histogram_diff)
    220      return (False, {})
    221 
    222 
    223 class HistogramDiffer(object):
    224      """
    225      Calculates a histogram's progress between begin() and end().
    226 
    227      Usage:
    228        differ = HistogramDiffer(cr, 'Media.GpuVideoDecoderError')
    229        ....
    230        diff_gvd_error = differ.end()
    231      """
    232      def __init__(self, cr, histogram_name, begin=True):
    233           """
    234           Constructor.
    235 
    236           @param: cr: object, the Chrome instance
    237           @param: histogram_name: string, name of the histogram
    238           @param: begin: if set, calls begin().
    239           """
    240           self.cr = cr
    241           self.histogram_name = histogram_name
    242           self.begin_histogram_text = ''
    243           self.end_histogram_text = ''
    244           self.begin_histogram = {}
    245           self.end_histogram = {}
    246           if begin:
    247                self.begin()
    248 
    249      def _get_histogram(self):
    250           """
    251           Gets current histogram bucket.
    252 
    253           @returns (dict(bucket_value, bucket_count), histogram_text)
    254           """
    255           tab = self.cr.browser.tabs.New()
    256           text = get_histogram_text(tab, self.histogram_name)
    257           tab.Close()
    258           return (parse_histogram(text), text)
    259 
    260      def begin(self):
    261           """
    262           Takes a histogram snapshot as begin_histogram.
    263           """
    264           (self.begin_histogram,
    265            self.begin_histogram_text) = self._get_histogram()
    266           logging.debug('begin histograms/%s: %r\nraw_text: %s',
    267                         self.histogram_name, self.begin_histogram,
    268                         self.begin_histogram_text)
    269 
    270      def end(self):
    271           """
    272           Takes a histogram snapshot as end_histogram.
    273 
    274           @returns self.diff()
    275           """
    276           self.end_histogram, self.end_histogram_text = self._get_histogram()
    277           logging.debug('end histograms/%s: %r\nraw_text: %s',
    278                         self.histogram_name, self.end_histogram,
    279                         self.end_histogram_text)
    280           diff = subtract_histogram(self.end_histogram, self.begin_histogram)
    281           logging.debug('histogram diff: %r', diff)
    282           return diff
    283