summaryrefslogtreecommitdiffstats
path: root/tools/telemetry
diff options
context:
space:
mode:
authorshadi@chromium.org <shadi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-23 23:43:21 +0000
committershadi@chromium.org <shadi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-23 23:43:21 +0000
commit8e0a395ff0dc584469813189c635040c34d18780 (patch)
tree3f1034ff35046ca1a2effaa64b39d5f3b5fa362d /tools/telemetry
parent5f37610ffe6c89e9efd19f95825d2c03507d5e34 (diff)
downloadchromium_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.js38
-rw-r--r--tools/telemetry/telemetry/page/actions/media_action.py46
-rw-r--r--tools/telemetry/telemetry/page/actions/play.js36
-rw-r--r--tools/telemetry/telemetry/page/actions/play.py24
-rw-r--r--tools/telemetry/telemetry/page/actions/play_unittest.py12
-rw-r--r--tools/telemetry/telemetry/page/actions/seek.js49
-rw-r--r--tools/telemetry/telemetry/page/actions/seek.py47
-rw-r--r--tools/telemetry/telemetry/page/actions/seek_unittest.py76
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)