diff options
author | shadi@chromium.org <shadi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-23 23:43:21 +0000 |
---|---|---|
committer | shadi@chromium.org <shadi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-23 23:43:21 +0000 |
commit | 8e0a395ff0dc584469813189c635040c34d18780 (patch) | |
tree | 3f1034ff35046ca1a2effaa64b39d5f3b5fa362d /tools/telemetry | |
parent | 5f37610ffe6c89e9efd19f95825d2c03507d5e34 (diff) | |
download | chromium_src-8e0a395ff0dc584469813189c635040c34d18780.zip chromium_src-8e0a395ff0dc584469813189c635040c34d18780.tar.gz chromium_src-8e0a395ff0dc584469813189c635040c34d18780.tar.bz2 |
Telemetry media Seek action and metrics.
BUG=249435
Review URL: https://chromiumcodereview.appspot.com/19482009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@213275 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/telemetry')
-rw-r--r-- | tools/telemetry/telemetry/page/actions/media_action.js | 38 | ||||
-rw-r--r-- | tools/telemetry/telemetry/page/actions/media_action.py | 46 | ||||
-rw-r--r-- | tools/telemetry/telemetry/page/actions/play.js | 36 | ||||
-rw-r--r-- | tools/telemetry/telemetry/page/actions/play.py | 24 | ||||
-rw-r--r-- | tools/telemetry/telemetry/page/actions/play_unittest.py | 12 | ||||
-rw-r--r-- | tools/telemetry/telemetry/page/actions/seek.js | 49 | ||||
-rw-r--r-- | tools/telemetry/telemetry/page/actions/seek.py | 47 | ||||
-rw-r--r-- | tools/telemetry/telemetry/page/actions/seek_unittest.py | 76 |
8 files changed, 267 insertions, 61 deletions
diff --git a/tools/telemetry/telemetry/page/actions/media_action.js b/tools/telemetry/telemetry/page/actions/media_action.js new file mode 100644 index 0000000..96f6ffa --- /dev/null +++ b/tools/telemetry/telemetry/page/actions/media_action.js @@ -0,0 +1,38 @@ +// Copyright 2013 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. + + +// This file provides common functions for media actions. +window.__findMediaElements = function(selector) { + // Returns elements matching the selector, otherwise returns the first video + // or audio tag element that can be found. + // If selector == 'all', returns all media elements. + if (selector == 'all') { + return document.querySelectorAll('video, audio'); + } else if (selector) { + return document.querySelectorAll(selector); + } else { + var media = document.getElementsByTagName('video'); + if (media.length > 0) { + return [media[0]]; + } else { + media = document.getElementsByTagName('audio'); + if (media.length > 0) { + return [media[0]]; + } + } + } + console.error('Could not find any media elements matching: ' + selector); + return []; +}; + +window.__hasEventCompleted = function(selector, event_name) { + // Return true if the event_name fired for media satisfying the selector. + var mediaElements = window.__findMediaElements(selector); + for (var i = 0; i < mediaElements.length; i++) { + if (!mediaElements[i][event_name + '_completed']) + return false; + } + return true; +}; diff --git a/tools/telemetry/telemetry/page/actions/media_action.py b/tools/telemetry/telemetry/page/actions/media_action.py new file mode 100644 index 0000000..7f3829b --- /dev/null +++ b/tools/telemetry/telemetry/page/actions/media_action.py @@ -0,0 +1,46 @@ +# Copyright 2013 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. + +"""Common media action functions.""" + +import os + +from telemetry.core import util +from telemetry.page.actions import page_action + + +class MediaAction(page_action.PageAction): + def __init__(self, attributes=None): + super(MediaAction, self).__init__(attributes) + + def WillRunAction(self, page, tab): + """Loads the common media action JS code prior to running the action.""" + self.LoadJS(tab, 'media_action.js') + + def RunAction(self, page, tab, previous_action): + super(MediaAction, self).RunAction(page, tab, previous_action) + + def LoadJS(self, tab, js_file_name): + """Loads and executes a JS file in the tab.""" + with open(os.path.join(os.path.dirname(__file__), js_file_name)) as f: + js = f.read() + tab.ExecuteJavaScript(js) + + def WaitForEvent(self, tab, selector, event_name, timeout, + poll_interval=0.5): + """Halts media action until the selector's event is fired. + + Args: + tab: The tab to check for event on. + selector: Media element selector. + event_name: Name of the event to check if fired or not. + timeout: Timeout to check for event, throws an exception if not fired. + poll_interval: Interval to poll for event firing status. + """ + util.WaitFor(lambda: self.HasEventCompleted(tab, selector, event_name), + timeout=timeout, poll_interval=poll_interval) + + def HasEventCompleted(self, tab, selector, event_name): + return tab.EvaluateJavaScript( + 'window.__hasEventCompleted("%s", "%s");' % (selector, event_name))
\ No newline at end of file diff --git a/tools/telemetry/telemetry/page/actions/play.js b/tools/telemetry/telemetry/page/actions/play.js index eb17424..ef3cb1b 100644 --- a/tools/telemetry/telemetry/page/actions/play.js +++ b/tools/telemetry/telemetry/page/actions/play.js @@ -4,32 +4,9 @@ // This file performs actions on media elements. (function() { - function findMediaElements(selector) { - // Returns elements matching the selector, otherwise returns the first video - // or audio tag element that can be found. - // If selector == 'all', returns all media elements. - if (selector == 'all') { - return document.querySelectorAll('video, audio'); - } else if (selector) { - return document.querySelectorAll(selector); - } else { - var media = document.getElementsByTagName('video'); - if (media.length > 0) { - return [media[0]]; - } else { - media = document.getElementsByTagName('audio'); - if (media.length > 0) { - return [media[0]]; - } - } - } - console.error('Could not find any media elements matching: ' + selector); - return []; - } - function playMedia(selector) { // Performs the "Play" action on media satisfying selector. - var mediaElements = findMediaElements(selector); + var mediaElements = window.__findMediaElements(selector); for (var i = 0; i < mediaElements.length; i++) { console.log('Playing element: ' + mediaElements[i].src); play(mediaElements[i]); @@ -61,16 +38,5 @@ element.play(); } - function hasEventCompleted(selector, event_name) { - // Return true if the event_name fired for media satisfying the selector. - var mediaElements = findMediaElements(selector); - for (var i = 0; i < mediaElements.length; i++) { - if (!mediaElements[i][event_name + '_completed']) - return false; - } - return true; - } - window.__playMedia = playMedia; - window.__hasEventCompleted = hasEventCompleted; })(); diff --git a/tools/telemetry/telemetry/page/actions/play.py b/tools/telemetry/telemetry/page/actions/play.py index 26b657f..3f67371 100644 --- a/tools/telemetry/telemetry/page/actions/play.py +++ b/tools/telemetry/telemetry/page/actions/play.py @@ -1,4 +1,4 @@ -# Copyright 2013 The Chromium Authors. All rights reserved. +# Copyright (c) 2013 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. @@ -12,27 +12,24 @@ Other attributes to use are: wait_for_playing and wait_for_ended, which forces the action to wait until playing and ended events get fired respectively. """ -import os - +from telemetry.page.actions.media_action import MediaAction from telemetry.core import exceptions -from telemetry.core import util from telemetry.page.actions import page_action -class PlayAction(page_action.PageAction): +class PlayAction(MediaAction): def __init__(self, attributes=None): super(PlayAction, self).__init__(attributes) def WillRunAction(self, page, tab): """Load the media metrics JS code prior to running the action.""" - with open(os.path.join(os.path.dirname(__file__), 'play.js')) as f: - js = f.read() - tab.ExecuteJavaScript(js) + super(PlayAction, self).WillRunAction(page, tab) + self.LoadJS(tab, 'play.js') def RunAction(self, page, tab, previous_action): try: selector = self.selector if hasattr(self, 'selector') else '' - tab.ExecuteJavaScript('window.__playMedia(\'%s\');' % selector) + tab.ExecuteJavaScript('window.__playMedia("%s");' % selector) timeout = self.wait_timeout if hasattr(self, 'wait_timeout') else 60 # Check if we need to wait for 'playing' event to fire. if hasattr(self, 'wait_for_playing') and self.wait_for_playing: @@ -43,12 +40,3 @@ class PlayAction(page_action.PageAction): except exceptions.EvaluateException: raise page_action.PageActionFailed('Cannot play media element(s) with ' 'selector = %s.' % selector) - - def WaitForEvent(self, tab, selector, event_name, timeout): - """Halts play action until the selector's event is fired.""" - util.WaitFor(lambda: self.HasEventCompleted(tab, selector, event_name), - timeout=timeout, poll_interval=0.5) - - def HasEventCompleted(self, tab, selector, event_name): - return tab.EvaluateJavaScript( - 'window.__hasEventCompleted(\'%s\', \'%s\');' % (selector, event_name)) diff --git a/tools/telemetry/telemetry/page/actions/play_unittest.py b/tools/telemetry/telemetry/page/actions/play_unittest.py index cd5444e..b29f8f8 100644 --- a/tools/telemetry/telemetry/page/actions/play_unittest.py +++ b/tools/telemetry/telemetry/page/actions/play_unittest.py @@ -2,24 +2,20 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import os - from telemetry.core import util from telemetry.page.actions import play from telemetry.unittest import tab_test_case -AUDIO_1_PLAYING_CHECK = "window.__hasEventCompleted('#audio_1', 'playing');" -VIDEO_1_PLAYING_CHECK = "window.__hasEventCompleted('#video_1', 'playing');" -VIDEO_1_ENDED_CHECK = "window.__hasEventCompleted('#video_1', 'ended');" +AUDIO_1_PLAYING_CHECK = 'window.__hasEventCompleted("#audio_1", "playing");' +VIDEO_1_PLAYING_CHECK = 'window.__hasEventCompleted("#video_1", "playing");' +VIDEO_1_ENDED_CHECK = 'window.__hasEventCompleted("#video_1", "ended");' class PlayActionTest(tab_test_case.TabTestCase): def setUp(self): tab_test_case.TabTestCase.setUp(self) - unittest_data_dir = os.path.join(os.path.dirname(__file__), - '..', '..', '..', 'unittest_data') - self._browser.SetHTTPServerDirectories(unittest_data_dir) + self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir()) self._tab.Navigate(self._browser.http_server.UrlOf('video_test.html')) self._tab.WaitForDocumentReadyStateToBeComplete() diff --git a/tools/telemetry/telemetry/page/actions/seek.js b/tools/telemetry/telemetry/page/actions/seek.js new file mode 100644 index 0000000..64f0ef1 --- /dev/null +++ b/tools/telemetry/telemetry/page/actions/seek.js @@ -0,0 +1,49 @@ +// Copyright 2013 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. + +// This file performs actions on media elements. +(function() { + function seekMedia(selector, seekTime, logSeekTime) { + // Performs the "Seek" action on media satisfying selector. + var mediaElements = window.__findMediaElements(selector); + for (var i = 0; i < mediaElements.length; i++) { + seek(mediaElements[i], seekTime, logSeekTime); + } + } + + function seek(element, seekTime, logSeekTime) { + if (element instanceof HTMLMediaElement) + seekHTML5Element(element, seekTime, logSeekTime); + else + console.error('Can not seek non HTML5 media elements.'); + } + + function seekHTML5Element(element, seekTime, logSeekTime) { + element['seeked_completed'] = false; + var onSeeked = function(e) { + element[e.type + '_completed'] = true; + element.removeEventListener('seeked', onSeeked); + }; + function onError(e) { + console.error('Error playing media :' + e.type); + } + element.addEventListener('error', onError); + element.addEventListener('abort', onError); + + if (logSeekTime) { + var willSeekEvent = document.createEvent('Event'); + willSeekEvent.initEvent('willSeek', false, false); + willSeekEvent.seekLabel = seekTime; + element.dispatchEvent(willSeekEvent); + } + element.addEventListener('seeked', onSeeked); + try { + element.currentTime = seekTime; + } catch (err) { + console.error('Cannot seek with network state: ' + element.networkState); + } + } + + window.__seekMedia = seekMedia; +})(); diff --git a/tools/telemetry/telemetry/page/actions/seek.py b/tools/telemetry/telemetry/page/actions/seek.py new file mode 100644 index 0000000..af908a7 --- /dev/null +++ b/tools/telemetry/telemetry/page/actions/seek.py @@ -0,0 +1,47 @@ +# Copyright 2013 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. + +"""A Telemetry page_action that performs the "seek" action on media elements. + +Action attributes are: +- seek_time: The media time to seek to. Test fails if not provided. +- selector: If no selector is defined then the action attempts to seek the first + media element on the page. If 'all' then seek all media elements. +- log_seek_time: If true the seek time is recorded, otherwise media measurement + will not be aware of the seek action. Used to perform multiple + seeks. Default true. +- wait_for_seeked: If true forces the action to wait for seeked event to fire. + Default false. +- wait_timeout: Timeout to wait for seeked event. Only valid with + wait_for_seeked=true +""" + +from telemetry.page.actions.media_action import MediaAction +from telemetry.core import exceptions +from telemetry.page.actions import page_action + + +class SeekAction(MediaAction): + def __init__(self, attributes=None): + super(SeekAction, self).__init__(attributes) + + def WillRunAction(self, page, tab): + """Load the media metrics JS code prior to running the action.""" + super(SeekAction, self).WillRunAction(page, tab) + self.LoadJS(tab, 'seek.js') + + def RunAction(self, page, tab, previous_action): + try: + assert hasattr(self, 'seek_time') + selector = self.selector if hasattr(self, 'selector') else '' + log_seek = self.log_seek if hasattr(self, 'log_seek') else True + tab.ExecuteJavaScript('window.__seekMedia("%s", "%s", "%s");' % + (selector, self.seek_time, log_seek)) + timeout = self.wait_timeout if hasattr(self, 'wait_timeout') else 60 + # Check if we need to wait for 'seeked' event to fire. + if hasattr(self, 'wait_for_seeked') and self.wait_for_seeked: + self.WaitForEvent(tab, selector, 'seeked', timeout) + except exceptions.EvaluateException: + raise page_action.PageActionFailed('Cannot seek media element(s) with ' + 'selector = %s.' % selector) diff --git a/tools/telemetry/telemetry/page/actions/seek_unittest.py b/tools/telemetry/telemetry/page/actions/seek_unittest.py new file mode 100644 index 0000000..2f9d5a9 --- /dev/null +++ b/tools/telemetry/telemetry/page/actions/seek_unittest.py @@ -0,0 +1,76 @@ +# Copyright 2013 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. + +from telemetry.core import util +from telemetry.page.actions import seek +from telemetry.unittest import tab_test_case + +AUDIO_1_SEEKED_CHECK = 'window.__hasEventCompleted("#audio_1", "seeked");' +VIDEO_1_SEEKED_CHECK = 'window.__hasEventCompleted("#video_1", "seeked");' + + +class SeekActionTest(tab_test_case.TabTestCase): + + def setUp(self): + tab_test_case.TabTestCase.setUp(self) + self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir()) + self._tab.Navigate(self._browser.http_server.UrlOf('video_test.html')) + self._tab.WaitForDocumentReadyStateToBeComplete() + + def testSeekWithNoSelector(self): + """Tests that with no selector Seek action seeks first media element.""" + data = {'wait_for_seeked': True, 'seek_time': 1} + action = seek.SeekAction(data) + action.WillRunAction(None, self._tab) + action.RunAction(None, self._tab, None) + # Assert only first video has played. + self.assertTrue(self._tab.EvaluateJavaScript(VIDEO_1_SEEKED_CHECK)) + self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_SEEKED_CHECK)) + + def testSeekWithVideoSelector(self): + """Tests that Seek action seeks video element matching selector.""" + data = {'selector': '#video_1', 'wait_for_seeked': True, 'seek_time': 1} + action = seek.SeekAction(data) + action.WillRunAction(None, self._tab) + # Both videos not playing before running action. + self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_SEEKED_CHECK)) + self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_SEEKED_CHECK)) + action.RunAction(None, self._tab, None) + # Assert only video matching selector has played. + self.assertTrue(self._tab.EvaluateJavaScript(VIDEO_1_SEEKED_CHECK)) + self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_SEEKED_CHECK)) + + def testSeekWithAllSelector(self): + """Tests that Seek action seeks all video elements with selector='all'.""" + data = {'selector': 'all', 'wait_for_seeked': True, 'seek_time': 1} + action = seek.SeekAction(data) + action.WillRunAction(None, self._tab) + # Both videos not playing before running action. + self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_SEEKED_CHECK)) + self.assertFalse(self._tab.EvaluateJavaScript(AUDIO_1_SEEKED_CHECK)) + action.RunAction(None, self._tab, None) + # Assert all media elements played. + self.assertTrue(self._tab.EvaluateJavaScript(VIDEO_1_SEEKED_CHECK)) + self.assertTrue(self._tab.EvaluateJavaScript(AUDIO_1_SEEKED_CHECK)) + + def testSeekWaitForSeekTimeout(self): + """Tests that wait_for_seeked timeouts if video does not seek.""" + data = {'selector': '#video_1', + 'wait_for_seeked': True, + 'wait_timeout': 1, + 'seek_time': 1} + action = seek.SeekAction(data) + action.WillRunAction(None, self._tab) + self._tab.EvaluateJavaScript('document.getElementById("video_1").src = ""') + self.assertFalse(self._tab.EvaluateJavaScript(VIDEO_1_SEEKED_CHECK)) + self.assertRaises(util.TimeoutException, action.RunAction, None, self._tab, + None) + + def testSeekWithoutSeekTime(self): + """Tests that seek action fails with no seek time.""" + data = {'wait_for_seeked': True} + action = seek.SeekAction(data) + action.WillRunAction(None, self._tab) + self.assertRaises(AssertionError, action.RunAction, None, self._tab, + None) |