| # Copyright 2015 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 collections |
| import logging |
| import re |
| |
| from autotest_lib.client.bin import utils |
| from autotest_lib.client.common_lib import error |
| |
| |
| def get_histogram_text(tab, histogram_name): |
| """ |
| This returns contents of the given histogram. |
| |
| @param tab: object, Chrome tab instance |
| @param histogram_name: string, name of the histogram |
| @returns string: contents of the histogram |
| """ |
| docEle = 'document.documentElement' |
| tab.Navigate('chrome://histograms/%s' % histogram_name) |
| tab.WaitForDocumentReadyStateToBeComplete() |
| raw_text = tab.EvaluateJavaScript( |
| '{0} && {0}.innerText'.format(docEle)) |
| # extract the contents of the histogram |
| histogram = raw_text[raw_text.find('Histogram:'):].strip() |
| if histogram: |
| logging.debug('chrome://histograms/%s:\n%s', histogram_name, |
| histogram) |
| else: |
| logging.debug('No histogram is shown in chrome://histograms/%s', |
| histogram_name) |
| return histogram |
| |
| |
| def loaded(tab, histogram_name, pattern): |
| """ |
| Checks if the histogram page has been fully loaded. |
| |
| @param tab: object, Chrome tab instance |
| @param histogram_name: string, name of the histogram |
| @param pattern: string, required text to look for |
| @returns re.MatchObject if the given pattern is found in the text |
| None otherwise |
| |
| """ |
| return re.search(pattern, get_histogram_text(tab, histogram_name)) |
| |
| |
| def verify(cr, histogram_name, histogram_bucket_value): |
| """ |
| Verifies histogram string and success rate in a parsed histogram bucket. |
| The histogram buckets are outputted in debug log regardless of the |
| verification result. |
| |
| Full histogram URL is used to load histogram. Example Histogram URL is : |
| chrome://histograms/Media.GpuVideoDecoderInitializeStatus |
| |
| @param cr: object, the Chrome instance |
| @param histogram_name: string, name of the histogram |
| @param histogram_bucket_value: int, required bucket number to look for |
| @raises error.TestError if histogram is not successful |
| |
| """ |
| bucket_pattern = '\n'+ str(histogram_bucket_value) +'.*100\.0%.*' |
| error_msg_format = ('{} not loaded or histogram bucket not found ' |
| 'or histogram bucket found at < 100%') |
| tab = cr.browser.tabs.New() |
| msg = error_msg_format.format(histogram_name) |
| utils.poll_for_condition(lambda : loaded(tab, histogram_name, |
| bucket_pattern), |
| exception=error.TestError(msg), |
| sleep_interval=1) |
| |
| |
| def is_bucket_present(cr,histogram_name, histogram_bucket_value): |
| """ |
| This returns histogram succes or fail to called function |
| |
| @param cr: object, the Chrome instance |
| @param histogram_name: string, name of the histogram |
| @param histogram_bucket_value: int, required bucket number to look for |
| @returns True if histogram page was loaded and the bucket was found. |
| False otherwise |
| |
| """ |
| try: |
| verify(cr,histogram_name, histogram_bucket_value) |
| except error.TestError: |
| return False |
| else: |
| return True |
| |
| |
| def is_histogram_present(cr, histogram_name): |
| """ |
| This checks if the given histogram is present and non-zero. |
| |
| @param cr: object, the Chrome instance |
| @param histogram_name: string, name of the histogram |
| @returns True if histogram page was loaded and the histogram is present |
| False otherwise |
| |
| """ |
| histogram_pattern = 'Histogram: '+ histogram_name + ' recorded ' + \ |
| r'[1-9][0-9]*' + ' samples' |
| tab = cr.browser.tabs.New() |
| try: |
| utils.poll_for_condition(lambda : loaded(tab, histogram_name, |
| histogram_pattern), |
| timeout=2, |
| sleep_interval=0.1) |
| return True |
| except utils.TimeoutError: |
| # the histogram is not present, and then returns false |
| return False |
| |
| |
| def get_histogram(cr, histogram_name): |
| """ |
| This returns contents of the given histogram. |
| |
| @param cr: object, the Chrome instance |
| @param histogram_name: string, name of the histogram |
| @returns string: contents of the histogram |
| |
| """ |
| tab = cr.browser.tabs.New() |
| return get_histogram_text(tab, histogram_name) |
| |
| |
| def parse_histogram(histogram_text): |
| """ |
| Parses histogram text into bucket structure. |
| |
| @param histogram_text: histogram raw text. |
| @returns dict(bucket_value, bucket_count) |
| """ |
| # Match separator line, e.g. "1 ..." |
| RE_SEPEARTOR = re.compile(r'\d+\s+\.\.\.') |
| # Match bucket line, e.g. "2 --O (46 = 1.5%) {46.1%}" |
| RE_BUCKET = re.compile( |
| r'(\d+)\s+\-*O\s+\((\d+) = (\d+\.\d+)%\).*') |
| result = {} |
| for line in histogram_text.splitlines(): |
| if RE_SEPEARTOR.match(line): |
| continue |
| m = RE_BUCKET.match(line) |
| if m: |
| result[int(m.group(1))] = int(m.group(2)) |
| return result |
| |
| |
| def subtract_histogram(minuend, subtrahend): |
| """ |
| Subtracts histogram: minuend - subtrahend |
| |
| @param minuend: histogram bucket dict from which another is to be |
| subtracted. |
| @param subtrahend: histogram bucket dict to be subtracted from another. |
| @result difference of the two histograms in bucket dict. Note that |
| zero-counted buckets are removed. |
| """ |
| result = collections.defaultdict(int, minuend) |
| for k, v in subtrahend.iteritems(): |
| result[k] -= v |
| |
| # Remove zero counted buckets. |
| return {k: v for k, v in result.iteritems() if v} |
| |
| |
| def expect_sole_bucket(histogram_differ, bucket, bucket_name, timeout=10, |
| sleep_interval=1): |
| """ |
| Returns true if the given bucket solely exists in histogram differ. |
| |
| @param histogram_differ: a HistogramDiffer instance used to get histogram |
| name and histogram diff multiple times. |
| @param bucket: bucket value. |
| @param bucket_name: bucket name to be shown on error message. |
| @param timeout: timeout in seconds. |
| @param sleep_interval: interval in seconds between getting diff. |
| @returns True if the given bucket solely exists in histogram. |
| @raises TestError if bucket doesn't exist or other buckets exist. |
| """ |
| timer = utils.Timer(timeout) |
| histogram = {} |
| histogram_name = histogram_differ.histogram_name |
| while timer.sleep(sleep_interval): |
| histogram = histogram_differ.end() |
| if histogram: |
| break |
| |
| if bucket not in histogram: |
| raise error.TestError('Expect %s has %s. Histogram: %r' % |
| (histogram_name, bucket_name, histogram)) |
| if len(histogram) > 1: |
| raise error.TestError('%s has bucket other than %s. Histogram: %r' % |
| (histogram_name, bucket_name, histogram)) |
| return True |
| |
| |
| def poll_histogram_grow(histogram_differ, timeout=2, sleep_interval=0.1): |
| """ |
| Polls histogram to see if it grows within |timeout| seconds. |
| |
| @param histogram_differ: a HistogramDiffer instance used to get histogram |
| name and histogram diff multiple times. |
| @param timeout: observation timeout in seconds. |
| @param sleep_interval: interval in seconds between getting diff. |
| @returns (True, histogram_diff) if the histogram grows. |
| (False, {}) if it does not grow in |timeout| seconds. |
| """ |
| timer = utils.Timer(timeout) |
| while timer.sleep(sleep_interval): |
| histogram_diff = histogram_differ.end() |
| if histogram_diff: |
| return (True, histogram_diff) |
| return (False, {}) |
| |
| |
| class HistogramDiffer(object): |
| """ |
| Calculates a histogram's progress between begin() and end(). |
| |
| Usage: |
| differ = HistogramDiffer(cr, 'Media.GpuVideoDecoderError') |
| .... |
| diff_gvd_error = differ.end() |
| """ |
| def __init__(self, cr, histogram_name, begin=True): |
| """ |
| Constructor. |
| |
| @param: cr: object, the Chrome instance |
| @param: histogram_name: string, name of the histogram |
| @param: begin: if set, calls begin(). |
| """ |
| self.cr = cr |
| self.histogram_name = histogram_name |
| self.begin_histogram_text = '' |
| self.end_histogram_text = '' |
| self.begin_histogram = {} |
| self.end_histogram = {} |
| if begin: |
| self.begin() |
| |
| def _get_histogram(self): |
| """ |
| Gets current histogram bucket. |
| |
| @returns (dict(bucket_value, bucket_count), histogram_text) |
| """ |
| tab = self.cr.browser.tabs.New() |
| text = get_histogram_text(tab, self.histogram_name) |
| tab.Close() |
| return (parse_histogram(text), text) |
| |
| def begin(self): |
| """ |
| Takes a histogram snapshot as begin_histogram. |
| """ |
| (self.begin_histogram, |
| self.begin_histogram_text) = self._get_histogram() |
| logging.debug('begin histograms/%s: %r\nraw_text: %s', |
| self.histogram_name, self.begin_histogram, |
| self.begin_histogram_text) |
| |
| def end(self): |
| """ |
| Takes a histogram snapshot as end_histogram. |
| |
| @returns self.diff() |
| """ |
| self.end_histogram, self.end_histogram_text = self._get_histogram() |
| logging.debug('end histograms/%s: %r\nraw_text: %s', |
| self.histogram_name, self.end_histogram, |
| self.end_histogram_text) |
| diff = subtract_histogram(self.end_histogram, self.begin_histogram) |
| logging.debug('histogram diff: %r', diff) |
| return diff |