Add class to parse chrome histograms.
Existing hw decode video tests need to observe the output of
chrome://histograms/<some_histogram> to tell if hw was used.
Every test was repeating this logic. Moving this responsibility into one
simple
class.
BUG=None.
TEST=ran all modified video tests.
Change-Id: I3526706b79c7fda8e2129fe2c2707ed27b7b7583
Reviewed-on: https://chromium-review.googlesource.com/219875
Reviewed-by: Rohit Makasana <rohitbm@chromium.org>
Tested-by: Mussa Kiroga <mussa@chromium.org>
Commit-Queue: Mussa Kiroga <mussa@chromium.org>
diff --git a/client/cros/video/histogram_parser.py b/client/cros/video/histogram_parser.py
new file mode 100644
index 0000000..cfbf56c
--- /dev/null
+++ b/client/cros/video/histogram_parser.py
@@ -0,0 +1,187 @@
+# Copyright (c) 2013 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 re
+from collections import namedtuple
+
+from autotest_lib.client.bin import utils
+from autotest_lib.client.common_lib import error
+
+
+BucketStats = namedtuple('BucketStats', 'value percent')
+
+
+class HistogramParser(object):
+ """
+ Parses a chrome histogram page and provide access to its values.
+
+ Example usage:
+ parser = histogram_parser.HistogramParser('some_histogram_name')
+
+ # Later access amazing magical values:
+ buckets = parser.buckets
+
+ if buckets and buckets[1] == ??:
+ # do cool stuff
+
+ """
+
+ def __init__(self, chrome, histogram, time_out_s=10):
+ """
+ @param chrome: Chrome instance representing the browser in current test.
+ @param histogram: string, name of the histogram of interest.
+ @param time_out_s: int, max duration in secs to wait for specified
+ histogram to be loaded.
+
+ """
+ # This pattern was built by observing the chrome://histogram output
+ self._histogram_pattern = ('Histogram.*([0-9]+)'
+ 'samples.*average.*([0-9]+\.[0-9]+)')
+
+ self._bucket_pattern = '(^[0-9]+).*\(([0-9]+)'
+
+ """
+ Match counts are based on the text that needs to be parsed.
+ E.g: "0 ---------------------------O (9 = 16.4%)" is a typical entry
+ in the list of buckets. In this case we want to match 0 and 9,
+ therefore the match count is 2.
+
+ """
+
+ self._histogram_match_count = 2
+ self._bucket_match_count = 2
+
+ self._histogram = histogram
+ self._time_out_s = time_out_s
+ self._raw_text = None
+ self._sample_count = None
+ self._average = None
+ self._buckets = {}
+ self.tab = chrome.browser.tabs.New()
+ self.wait_for_histogram_loaded()
+ self.parse()
+
+
+ @property
+ def buckets(self):
+ """
+ @returns the dictionary containing buckets and their values.
+
+ """
+ return self._buckets
+
+
+ @property
+ def sample_count(self):
+ """
+ @returns the count of all samples in histogram as int.
+
+ """
+ return self._sample_count
+
+
+ @property
+ def average(self):
+ """
+ @returns the average of bucket values as float.
+
+ """
+ return self._average
+
+
+ def wait_for_histogram_loaded(self):
+ """
+ Uses js to poll doc content until valid content is retrieved.
+
+ """
+ def loaded():
+ """
+ Checks if the histogram page has been fully loaded.
+
+ """
+
+ self.tab.Navigate('chrome://histograms/%s' % self._histogram)
+ self.tab.WaitForDocumentReadyStateToBeComplete()
+ docEle = 'document.documentElement'
+ self._raw_text = self.tab.EvaluateJavaScript(
+ "{0} && {0}.innerText".format(docEle))
+ return self._histogram in self._raw_text
+
+ msg = "%s not loaded. Waited %ss" % (self._histogram, self._time_out_s)
+
+ utils.poll_for_condition(condition=loaded,
+ exception=error.TestError(msg),
+ sleep_interval=1)
+
+ def parse(self):
+ """
+ Parses histogram text to retrieve useful properties.
+
+ @raises whatever _check_match() raises.
+
+ """
+
+ histogram_entries = self._raw_text.split('\n')
+ found_hist_title = False
+
+ for entry in histogram_entries:
+ matches = self._check_match(self._histogram_pattern,
+ entry,
+ self._histogram_match_count)
+
+ if matches:
+ if not found_hist_title:
+ self._sample_count = int(matches[0])
+ self._average = matches[1]
+ found_hist_title = True
+
+ else: # this is another histogram, bail out
+ return
+
+ else:
+ matches = self._check_match(self._bucket_pattern,
+ entry,
+ self._bucket_match_count)
+ if matches:
+ self._buckets[int(matches[0])] = int(matches[1])
+
+ bucket_sum = sum(self._buckets.values())
+
+ for key, value in self._buckets.items():
+ percent = (float(value) / bucket_sum) * 100
+ percent = round(number=percent, ndigits=2)
+ self._buckets[key] = BucketStats(value, percent)
+
+
+ def _check_match(self, pattern, text, expected_match_count):
+ """
+ Checks if provided text contains a pattern and if so expected number of
+ matches is found.
+
+ @param pattern: string, regex pattern to search for.
+ @param text: string, text to search for patterns.
+ @param expected_match_count: int, number of matches expected.
+
+ @returns: tuple, match groups, none if no match was found.
+ @raises TestError if a match was found but number of matches is not
+ equal to expected count.
+
+ """
+ m = re.match(pattern, text)
+
+ if not m:
+ return m
+
+ ln = len(m.groups())
+ if ln != expected_match_count:
+ msg = ('Expected %d matches. Got %d. Pattern: %s. Text: %s'
+ % (expected_match_count, ln, pattern, text))
+ raise error.TestError(msg)
+
+ return m.groups()
+
+
+ def __str__(self):
+ return ("Histogram name: %s. Buckets: %s"
+ % (self._histogram, str(self._buckets)))
\ No newline at end of file
diff --git a/client/site_tests/video_ChromeHWDecodeUsed/video_ChromeHWDecodeUsed.py b/client/site_tests/video_ChromeHWDecodeUsed/video_ChromeHWDecodeUsed.py
index 415110c..a5929f7 100755
--- a/client/site_tests/video_ChromeHWDecodeUsed/video_ChromeHWDecodeUsed.py
+++ b/client/site_tests/video_ChromeHWDecodeUsed/video_ChromeHWDecodeUsed.py
@@ -2,12 +2,14 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-from autotest_lib.client.bin import test, utils
+from autotest_lib.client.bin import test
from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib.cros import chrome
+from autotest_lib.client.cros.video import histogram_parser
MEDIA_GVD_INIT_STATUS = 'Media.GpuVideoDecoderInitializeStatus'
+MEDIA_GVD_BUCKET = 0
class video_ChromeHWDecodeUsed(test.test):
@@ -26,25 +28,12 @@
tab1.WaitForDocumentReadyStateToBeComplete()
# Waits for histogram updated for the test video.
- tab2 = cr.browser.tabs.New()
+ parser = histogram_parser.HistogramParser(cr, MEDIA_GVD_INIT_STATUS)
- def search_histogram_text(text):
- """Searches the histogram text in the second tab.
+ buckets = parser.buckets
- @param text: Text to be searched in the histogram tab.
- """
- return tab2.EvaluateJavaScript('document.documentElement && '
- 'document.documentElement.innerText.search('
- '\'%s\') != -1' % text)
+ if (not buckets or not buckets[MEDIA_GVD_BUCKET]
+ or buckets[MEDIA_GVD_BUCKET].percent < 100.0):
- def gpu_histogram_loaded():
- """Loads the histogram in the second tab."""
- tab2.Navigate('chrome://histograms/%s' % MEDIA_GVD_INIT_STATUS)
- return search_histogram_text(MEDIA_GVD_INIT_STATUS)
-
- utils.poll_for_condition(gpu_histogram_loaded,
- exception=error.TestError(
- 'Histogram gpu status failed to load.'),
- sleep_interval=1)
- if not search_histogram_text('average = 0.0'):
- raise error.TestError('Video decode acceleration not working.')
+ raise error.TestError('%s not found or not at 100 percent. %s'
+ % MEDIA_GVD_BUCKET, str(parser))
\ No newline at end of file
diff --git a/client/site_tests/video_ChromeRTCHWDecodeUsed/video_ChromeRTCHWDecodeUsed.py b/client/site_tests/video_ChromeRTCHWDecodeUsed/video_ChromeRTCHWDecodeUsed.py
index 6c5903d..948f922 100755
--- a/client/site_tests/video_ChromeRTCHWDecodeUsed/video_ChromeRTCHWDecodeUsed.py
+++ b/client/site_tests/video_ChromeRTCHWDecodeUsed/video_ChromeRTCHWDecodeUsed.py
@@ -5,13 +5,12 @@
from contextlib import closing
import logging
import os
-import re
-import time
import urllib2
-from autotest_lib.client.bin import test, utils
+from autotest_lib.client.bin import test
from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib.cros import chrome
+from autotest_lib.client.cros.video import histogram_parser
# Chrome flags to use fake camera and skip camera permission.
@@ -25,6 +24,7 @@
RTC_VIDEO_DECODE_BUCKET = 1
HISTOGRAMS_URL = 'chrome://histograms/'
+
class video_ChromeRTCHWDecodeUsed(test.test):
"""The test verifies HW Encoding for WebRTC video."""
version = 1
@@ -38,7 +38,7 @@
"""
tab = cr.browser.tabs[0]
tab.Navigate(cr.browser.http_server.UrlOf(
- os.path.join(self.bindir, 'loopback.html')))
+ os.path.join(self.bindir, 'loopback.html')))
tab.WaitForDocumentReadyStateToBeComplete()
@@ -50,31 +50,15 @@
@raises error.TestError if decoding is not hardware accelerated.
"""
- tab = cr.browser.tabs.New()
- def histograms_loaded(histogram):
- """Returns true if histogram is loaded."""
- tab.Navigate(HISTOGRAMS_URL + histogram)
- tab.WaitForDocumentReadyStateToBeComplete()
- return tab.EvaluateJavaScript(
- 'document.documentElement.innerText.search("%s") != -1'
- % histogram)
+ parser = histogram_parser.HistogramParser(cr.browser.tabs.New(),
+ RTC_VIDEO_DECODE)
+ buckets = parser.buckets
- def histogram_sucess(histogram, bucket):
- lines = tab.EvaluateJavaScript('document.documentElement.innerText')
- logging.info(lines)
- re_string = '^'+ str(bucket) +'\s\s-(.*)100.0%(.*)'
- if not re.findall(re_string, lines, re.MULTILINE):
- raise error.TestError(
- '{0} didn\'t show up or is not 100%'
- ' successful.'.format(histogram))
+ if (not buckets or not buckets[RTC_VIDEO_DECODE_BUCKET]
+ or buckets[RTC_VIDEO_DECODE_BUCKET].percent < 100.0):
- utils.poll_for_condition(
- lambda: histograms_loaded(RTC_VIDEO_DECODE),
- timeout=5,
- exception=error.TestError('Cannot find %s histogram.' %
- RTC_VIDEO_DECODE),
- sleep_interval=1)
- histogram_sucess(RTC_VIDEO_DECODE, RTC_VIDEO_DECODE_BUCKET)
+ raise error.TestError('%s not found or not at 100 percent. %s'
+ % RTC_VIDEO_DECODE, str(parser))
def run_once(self):
diff --git a/client/site_tests/video_ChromeVidResChangeHWDecode/video_ChromeVidResChangeHWDecode.py b/client/site_tests/video_ChromeVidResChangeHWDecode/video_ChromeVidResChangeHWDecode.py
index 7192a74..c1864f2 100755
--- a/client/site_tests/video_ChromeVidResChangeHWDecode/video_ChromeVidResChangeHWDecode.py
+++ b/client/site_tests/video_ChromeVidResChangeHWDecode/video_ChromeVidResChangeHWDecode.py
@@ -8,9 +8,11 @@
from autotest_lib.client.bin import test, utils
from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib.cros import chrome
+from autotest_lib.client.cros.video import histogram_parser
MEDIA_GVD_INIT_STATUS = 'Media.GpuVideoDecoderInitializeStatus'
+MEDIA_GVD_BUCKET = 0
class video_ChromeVidResChangeHWDecode(test.test):
@@ -33,28 +35,15 @@
'loadVideo("%s")' % (video_file))
# Waits for histogram updated for the test video.
- tab2 = cr.browser.tabs.New()
+ parser = histogram_parser.HistogramParser(cr.browser.tabs.New(),
+ MEDIA_GVD_INIT_STATUS)
+ buckets = parser.buckets
- def search_histogram_text(text):
- """Searches the histogram text in the second tab.
+ if (not buckets or not buckets[MEDIA_GVD_BUCKET]
+ or buckets[MEDIA_GVD_BUCKET].percent < 100.0):
- @param text: Text to be searched in the histogram tab.
- """
- return tab2.EvaluateJavaScript('document.documentElement && '
- 'document.documentElement.innerText.search('
- '\'%s\') != -1' % text)
-
- def gpu_histogram_loaded():
- """Loads the histogram in the second tab."""
- tab2.Navigate('chrome://histograms/%s' % MEDIA_GVD_INIT_STATUS)
- return search_histogram_text(MEDIA_GVD_INIT_STATUS)
-
- utils.poll_for_condition(gpu_histogram_loaded,
- exception=error.TestError(
- 'Histogram gpu status failed to load.'),
- sleep_interval=1)
- if not search_histogram_text('average = 0.0'):
- raise error.TestError('Video decode acceleration not working.')
+ raise error.TestError('%s not found or not at 100 percent. %s'
+ % MEDIA_GVD_BUCKET, str(parser))
# Verify the video playback.
for i in range(1, video_len/2):
@@ -66,7 +55,6 @@
# Verify that video ends successfully.
utils.poll_for_condition(
lambda: tab1.EvaluateJavaScript('testvideo.ended'),
- timeout=video_len - video_len/2,
- exception=error.TestError(
- 'Video didn\'t end successfully.'),
+ timeout=video_len,
+ exception=error.TestError('Video did not end successfully'),
sleep_interval=1)