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