Chromium Code Reviews| Index: chrome/test/functional/media/media_seek_perf.py |
| diff --git a/chrome/test/functional/media/media_seek_perf.py b/chrome/test/functional/media/media_seek_perf.py |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..af4386684de13f542c52582abba3d9d5e6b6759c |
| --- /dev/null |
| +++ b/chrome/test/functional/media/media_seek_perf.py |
| @@ -0,0 +1,197 @@ |
| +#!/usr/bin/env python |
| +# Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +"""Seek performance testing for <video>. |
| + |
| +Calculates the short and long seek times for different video formats on |
| +different network constraints. |
| +""" |
| + |
| +import itertools |
| +import logging |
| +import os |
| +import Queue |
| +import string |
| +import threading |
| + |
| +import pyauto_media |
| +import pyauto |
| +import pyauto_utils |
| + |
| +import cns_test_base |
| +from cns_test_base import CNSTestBase |
| + |
| +# Number of threads to use during testing. |
|
DaleCurtis
2012/04/10 23:00:06
Lots of duplicated defines here. Should refactor a
|
| +_TEST_THREADS = 3 |
| + |
| +# HTML test path; relative to src/chrome/test/data. |
| +_TEST_HTML_PATH = os.path.join('media', 'html', 'media_seek.html') |
| + |
| +# The media files used for testing. |
| +# Path under CNS root folder (pyauto_private/media). |
| +_TEST_VIDEOS = [os.path.join('crowd', name) for name in |
| + ['crowd1080.webm', 'crowd720.webm', 'crowd480.webm', 'crowd360.webm']] |
| + |
| +# Constraints to run tests on. |
| +_TESTS_TO_RUN = { |
| + '512kbps_105ms': [512, 105, 0], |
| + 'Wifi_1Mbps_60ms': [1024, 60, 0], |
| + 'DSL_1.5Mbps_50ms': [1541, 50, 0], |
| + 'Cable_5Mbps_28ms': [5120, 28, 0], |
| + 'NoConstraints': [0, 0, 0] |
| +} |
| + |
| + |
| +class TestWorker(threading.Thread): |
|
DaleCurtis
2012/04/10 23:00:06
This worker code should probably be refactored/abs
|
| + """Worker thread. For each queue entry: opens tab, runs test, closes tab.""" |
| + |
| + # Atomic, monotonically increasing task identifier. Used to ID tabs. |
| + _task_id = itertools.count() |
| + |
| + def __init__(self, pyauto_test, tasks, automation_lock, url): |
| + """Sets up TestWorker class variables. |
| + |
| + Args: |
| + pyauto_test: Reference to a pyauto.PyUITest instance. |
| + tasks: Queue containing (settings, name) tuples. |
| + automation_lock: Global automation lock for pyauto calls. |
| + url: File URL to HTML/JavaScript test code. |
| + """ |
| + threading.Thread.__init__(self) |
| + self._tasks = tasks |
| + self._automation_lock = automation_lock |
| + self._pyauto = pyauto_test |
| + self._url = url |
| + self._uncached_seeks = [] |
| + self._cached_seeks = [] |
| + self._error_msg = '' |
| + self.start() |
| + |
| + def _FindTabLocked(self, url): |
| + """Returns the tab index for the tab belonging to this url. |
| + |
| + self._automation_lock must be owned by caller. |
| + """ |
| + for tab in self._pyauto.GetBrowserInfo()['windows'][0]['tabs']: |
| + if tab['url'] == url: |
| + return tab['index'] |
| + |
| + def _EndTest(self, unique_url): |
| + """Checks if the page has variable value ready or if an error has occured. |
| + |
| + The varaible value must be set to < 0 pre-run. |
| + |
| + Args: |
| + var_name: The variable name to check the metric for. |
| + unique_url: The url of the page to check for the variable's metric. |
| + |
| + Returns: |
| + True is the var_name value is >=0 or if an error_msg exists. |
| + """ |
| + with self._automation_lock: |
| + tab = self._FindTabLocked(unique_url) |
| + self._error_msg = self._pyauto.GetDOMValue('errorMsg', tab_index=tab) |
| + end_test = self._pyauto.GetDOMValue('endTest', tab_index=tab) |
| + if end_test: |
| + self._uncached_seeks = [ |
| + float(value) for value in |
| + self._pyauto.GetDOMValue("longSeeks.join(',')", |
| + tab_index=tab).split(',')] |
| + self._cached_seeks = [ |
| + float(value) for value in |
| + self._pyauto.GetDOMValue("shortSeeks.join(',')", |
| + tab_index=tab).split(',')] |
| + return self._error_msg or end_test |
| + |
| + def run(self): |
| + """Opens tab, starts HTML test, and records metrics for each queue entry. |
| + |
| + No exception handling is done to make sure the main thread exits properly |
| + during Chrome crashes or other failures. Doing otherwise has the potential |
| + to leave the CNS server running in the background. |
| + |
| + For a clean shutdown, put the magic exit value (None, None) in the queue. |
| + """ |
| + # Make the test URL unique so we can figure out our tab index later. |
| + unique_url = '%s?%d' % (self._url, TestWorker._task_id.next()) |
| + with self._automation_lock: |
| + self._pyauto.AppendTab(pyauto.GURL(unique_url)) |
| + |
| + while True: |
| + series_name, settings, file_name = self._tasks.get() |
| + |
| + # Check for magic exit values. |
| + if (series_name, settings, file_name) == (None, None, None): |
| + break |
| + |
| + video_url = cns_test_base.GetFileURL( |
| + file_name, bandwidth=settings[0], latency=settings[1], |
| + loss=settings[2], new_port=True) |
| + |
| + self._uncached_seeks = [] |
| + self._cached_seeks = [] |
| + self._error_msg = '' |
| + # Start the test! |
| + with self._automation_lock: |
| + self._pyauto.CallJavascriptFunc( |
| + 'startTest', [video_url], |
| + tab_index=self._FindTabLocked(unique_url)) |
| + logging.debug('Running perf test for %s.', video_url) |
| + |
| + self._pyauto.WaitUntil( |
| + self._EndTest, args=[unique_url], retry_sleep=1, timeout=500, |
| + debug=False) |
| + |
| + if self._error_msg: |
| + logging.error('Error while running the test: %s' % self._error_msg) |
| + else: |
| + graph_name = series_name +'_' + os.path.basename(file_name) |
| + pyauto_utils.PrintPerfResult('seek', 'uncached_' + graph_name, |
| + self._uncached_seeks, 'ms') |
| + pyauto_utils.PrintPerfResult('seek', 'cached_' + graph_name, |
| + self._cached_seeks, 'ms') |
| + |
| + # Close the tab. |
| + with self._automation_lock: |
| + self._pyauto.GetBrowserWindow(0).GetTab( |
| + self._FindTabLocked(unique_url)).Close(True) |
| + |
| + self._tasks.task_done() |
| + |
| + |
| +class MediaSeekPerfTest(CNSTestBase, pyauto.PyUITest): |
| + """PyAuto test container. See file doc string for more information.""" |
| + |
| + def testMediaSeekPerformance(self): |
| + """Launches HTML test which plays each video and records seek stats.""" |
| + # Convert relative test path into an absolute path. |
| + test_url = self.GetFileURLForDataPath(_TEST_HTML_PATH) |
| + |
| + # PyAuto doesn't support threads, so we synchronize all automation calls. |
| + automation_lock = threading.Lock() |
| + |
| + # Spin up worker threads. |
|
DaleCurtis
2012/04/10 23:00:06
No dummy test necessary anymore?
|
| + tasks = Queue.Queue() |
| + for file_name in _TEST_VIDEOS: |
| + for series_name, settings in _TESTS_TO_RUN.iteritems(): |
| + logging.debug('Add test: %s\tSettings: %s\tMedia: %s', series_name, |
| + settings, file_name) |
| + tasks.put((series_name, settings, file_name)) |
| + |
| + # Add shutdown magic to end of queue. |
| + for thread in xrange(_TEST_THREADS): |
| + tasks.put((None, None, None)) |
| + |
| + threads = [] |
| + for _ in xrange(_TEST_THREADS): |
| + threads.append(TestWorker(self, tasks, automation_lock, test_url)) |
| + |
| + # Wait for threads to exit, gracefully or otherwise. |
| + for thread in threads: |
| + thread.join() |
| + |
| + |
| +if __name__ == '__main__': |
| + pyauto_media.Main() |