diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/media/test/external/external_media_tests | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/media/test/external/external_media_tests')
35 files changed, 1699 insertions, 0 deletions
diff --git a/dom/media/test/external/external_media_tests/__init__.py b/dom/media/test/external/external_media_tests/__init__.py new file mode 100644 index 000000000..bf7ceec47 --- /dev/null +++ b/dom/media/test/external/external_media_tests/__init__.py @@ -0,0 +1,10 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import os + +root = os.path.abspath(os.path.dirname(__file__)) +manifest = os.path.join(root, 'manifest.ini') +resources = os.path.join(root, 'resources') +urls = os.path.join(root, 'urls') diff --git a/dom/media/test/external/external_media_tests/manifest.ini b/dom/media/test/external/external_media_tests/manifest.ini new file mode 100644 index 000000000..e370fd679 --- /dev/null +++ b/dom/media/test/external/external_media_tests/manifest.ini @@ -0,0 +1 @@ +[include:playback/manifest.ini] diff --git a/dom/media/test/external/external_media_tests/media_utils/__init__.py b/dom/media/test/external/external_media_tests/media_utils/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/media/test/external/external_media_tests/media_utils/__init__.py diff --git a/dom/media/test/external/external_media_tests/media_utils/video_puppeteer.py b/dom/media/test/external/external_media_tests/media_utils/video_puppeteer.py new file mode 100644 index 000000000..b904267dd --- /dev/null +++ b/dom/media/test/external/external_media_tests/media_utils/video_puppeteer.py @@ -0,0 +1,448 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from collections import namedtuple +from time import clock, sleep + +from marionette_driver import By, expected, Wait +from marionette_harness import Marionette + +from external_media_tests.utils import verbose_until + + +# Adapted from +# https://github.com/gavinsharp/aboutmedia/blob/master/chrome/content/aboutmedia.xhtml +debug_script = """ +var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIWebNavigation) + .QueryInterface(Components.interfaces.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindow); +var tabbrowser = mainWindow.gBrowser; +for (var i=0; i < tabbrowser.browsers.length; ++i) { + var b = tabbrowser.getBrowserAtIndex(i); + var media = b.contentDocumentAsCPOW.getElementsByTagName('video'); + for (var j=0; j < media.length; ++j) { + var ms = media[j].mozMediaSourceObject; + if (ms) { + debugLines = ms.mozDebugReaderData.split(\"\\n\"); + return debugLines; + } + } +}""" + + +class VideoPuppeteer(object): + """ + Wrapper to control and introspect HTML5 video elements. + + A note about properties like current_time and duration: + These describe whatever stream is playing when the state is checked. + It is possible that many different streams are dynamically spliced + together, so the video stream that is currently playing might be the main + video or it might be something else, like an ad, for example. + + :param marionette: The marionette instance this runs in. + :param url: the URL of the page containing the video element. + :param video_selector: the selector of the element that we want to watch. + This is set by default to 'video', which is what most sites use, but + others should work. + :param interval: The polling interval that is used to check progress. + :param set_duration: When set to >0, the polling and checking will stop at + the number of seconds specified. Otherwise, this will stop at the end + of the video. + :param stall_wait_time: The amount of time to wait to see if a stall has + cleared. If 0, do not check for stalls. + :param timeout: The amount of time to wait until the video starts. + """ + + _video_var_script = ( + 'var video = arguments[0];' + 'var baseURI = arguments[0].baseURI;' + 'var currentTime = video.wrappedJSObject.currentTime;' + 'var duration = video.wrappedJSObject.duration;' + 'var buffered = video.wrappedJSObject.buffered;' + 'var bufferedRanges = [];' + 'for (var i = 0; i < buffered.length; i++) {' + 'bufferedRanges.push([buffered.start(i), buffered.end(i)]);' + '}' + 'var played = video.wrappedJSObject.played;' + 'var playedRanges = [];' + 'for (var i = 0; i < played.length; i++) {' + 'playedRanges.push([played.start(i), played.end(i)]);' + '}' + 'var totalFrames = ' + 'video.getVideoPlaybackQuality()["totalVideoFrames"];' + 'var droppedFrames = ' + 'video.getVideoPlaybackQuality()["droppedVideoFrames"];' + 'var corruptedFrames = ' + 'video.getVideoPlaybackQuality()["corruptedVideoFrames"];' + ) + """ + A string containing JS that assigns video state to variables. + The purpose of this string script is to be appended to by this and + any inheriting classes to return these and other variables. In the case + of an inheriting class the script can be added to in order to fetch + further relevant variables -- keep in mind we only want one script + execution to prevent races, so it wouldn't do to have child classes + run this script then their own, as there is potential for lag in + between. + + This script assigns a subset of the vars used later by the + `_video_state_named_tuple` function. Please see that function's + documentation for further information on these variables. + """ + + def __init__(self, marionette, url, video_selector='video', interval=1, + set_duration=0, stall_wait_time=0, timeout=60, + autostart=True): + self.marionette = marionette + self.test_url = url + self.interval = interval + self.stall_wait_time = stall_wait_time + self.timeout = timeout + self._set_duration = set_duration + self.video = None + self.expected_duration = 0 + self._first_seen_time = 0 + self._first_seen_wall_time = 0 + self._fetch_state_script_string = None + self._last_seen_video_state = None + wait = Wait(self.marionette, timeout=self.timeout) + with self.marionette.using_context(Marionette.CONTEXT_CONTENT): + self.marionette.navigate(self.test_url) + self.marionette.execute_script(""" + log('URL: {0}');""".format(self.test_url)) + verbose_until(wait, self, + expected.element_present(By.TAG_NAME, 'video')) + videos_found = self.marionette.find_elements(By.CSS_SELECTOR, + video_selector) + if len(videos_found) > 1: + self.marionette.log(type(self).__name__ + ': multiple video ' + 'elements found. ' + 'Using first.') + if len(videos_found) <= 0: + self.marionette.log(type(self).__name__ + ': no video ' + 'elements found.') + return + self.video = videos_found[0] + self.marionette.execute_script("log('video element obtained');") + if autostart: + self.start() + + def start(self): + # To get an accurate expected_duration, playback must have started + self._refresh_state() + wait = Wait(self, timeout=self.timeout) + verbose_until(wait, self, VideoPuppeteer.playback_started, + "Check if video has played some range") + self._first_seen_time = self._last_seen_video_state.current_time + self._first_seen_wall_time = clock() + self._update_expected_duration() + + def get_debug_lines(self): + """ + Get Firefox internal debugging for the video element. + + :return: A text string that has Firefox-internal debugging information. + """ + with self.marionette.using_context('chrome'): + debug_lines = self.marionette.execute_script(debug_script) + return debug_lines + + def play(self): + """ + Tell the video element to Play. + """ + self._execute_video_script('arguments[0].wrappedJSObject.play();') + + def pause(self): + """ + Tell the video element to Pause. + """ + self._execute_video_script('arguments[0].wrappedJSObject.pause();') + + def playback_started(self): + """ + Determine if video has started + + :param self: The VideoPuppeteer instance that we are interested in. + + :return: True if is playing; False otherwise + """ + self._refresh_state() + try: + played_ranges = self._last_seen_video_state.played + return ( + played_ranges.length > 0 and + played_ranges.start(0) < played_ranges.end(0) and + played_ranges.end(0) > 0.0 + ) + except Exception as e: + print ('Got exception {}'.format(e)) + return False + + def playback_done(self): + """ + If we are near the end and there is still a video element, then + we are essentially done. If this happens to be last time we are polled + before the video ends, we won't get another chance. + + :param self: The VideoPuppeteer instance that we are interested in. + + :return: True if we are close enough to the end of playback; False + otherwise. + """ + self._refresh_state() + + if self._last_seen_video_state.remaining_time < self.interval: + return True + + # Check to see if the video has stalled. Accumulate the amount of lag + # since the video started, and if it is too high, then raise. + if (self.stall_wait_time and + self._last_seen_video_state.lag > self.stall_wait_time): + raise VideoException('Video {} stalled.\n{}' + .format(self._last_seen_video_state.video_uri, + self)) + + # We are cruising, so we are not done. + return False + + def _update_expected_duration(self): + """ + Update the duration of the target video at self.test_url (in seconds). + This is based on the last seen state, so the state should be, + refreshed at least once before this is called. + + expected_duration represents the following: how long do we expect + playback to last before we consider the video to be 'complete'? + If we only want to play the first n seconds of the video, + expected_duration is set to n. + """ + + # self._last_seen_video_state.duration is the duration of whatever was + # playing when the state was checked. In this case, we assume the video + # element always shows the same stream throughout playback (i.e. the + # are no ads spliced into the main video, for example), so + # self._last_seen_video_state.duration is the duration of the main + # video. + video_duration = self._last_seen_video_state.duration + # Do our best to figure out where the video started playing + played_ranges = self._last_seen_video_state.played + if played_ranges.length > 0: + # If we have a range we should only have on continuous range + assert played_ranges.length == 1 + start_position = played_ranges.start(0) + else: + # If we don't have a range we should have a current time + start_position = self._first_seen_time + # In case video starts at t > 0, adjust target time partial playback + remaining_video = video_duration - start_position + if 0 < self._set_duration < remaining_video: + self.expected_duration = self._set_duration + else: + self.expected_duration = remaining_video + + @staticmethod + def _video_state_named_tuple(): + """ + Create a named tuple class that can be used to store state snapshots + of the wrapped element. The fields in the tuple should be used as + follows: + + base_uri: the baseURI attribute of the wrapped element. + current_time: the current time of the wrapped element. + duration: the duration of the wrapped element. + buffered: the buffered ranges of the wrapped element. In its raw form + this is as a list where the first element is the length and the second + element is a list of 2 item lists, where each two items are a buffered + range. Once assigned to the tuple this data should be wrapped in the + TimeRanges class. + played: the played ranges of the wrapped element. In its raw form this + is as a list where the first element is the length and the second + element is a list of 2 item lists, where each two items are a played + range. Once assigned to the tuple this data should be wrapped in the + TimeRanges class. + lag: the difference in real world time and wrapped element time. + Calculated as real world time passed - element time passed. + totalFrames: number of total frames for the wrapped element + droppedFrames: number of dropped frames for the wrapped element. + corruptedFrames: number of corrupted frames for the wrapped. + video_src: the src attribute of the wrapped element. + + :return: A 'video_state_info' named tuple class. + """ + return namedtuple('video_state_info', + ['base_uri', + 'current_time', + 'duration', + 'remaining_time', + 'buffered', + 'played', + 'lag', + 'total_frames', + 'dropped_frames', + 'corrupted_frames', + 'video_src']) + + def _create_video_state_info(self, **video_state_info_kwargs): + """ + Create an instance of the video_state_info named tuple. This function + expects a dictionary populated with the following keys: current_time, + duration, raw_played_ranges, total_frames, dropped_frames, and + corrupted_frames. + + Aside from raw_played_ranges, see `_video_state_named_tuple` for more + information on the above keys and values. For raw_played_ranges a + list is expected that can be consumed to make a TimeRanges object. + + :return: A named tuple 'video_state_info' derived from arguments and + state information from the puppeteer. + """ + raw_buffered_ranges = video_state_info_kwargs['raw_buffered_ranges'] + raw_played_ranges = video_state_info_kwargs['raw_played_ranges'] + # Remove raw ranges from dict as they are not used in the final named + # tuple and will provide an unexpected kwarg if kept. + del video_state_info_kwargs['raw_buffered_ranges'] + del video_state_info_kwargs['raw_played_ranges'] + # Create buffered ranges + video_state_info_kwargs['buffered'] = ( + TimeRanges(raw_buffered_ranges[0], raw_buffered_ranges[1])) + # Create played ranges + video_state_info_kwargs['played'] = ( + TimeRanges(raw_played_ranges[0], raw_played_ranges[1])) + # Calculate elapsed times + elapsed_current_time = (video_state_info_kwargs['current_time'] - + self._first_seen_time) + elapsed_wall_time = clock() - self._first_seen_wall_time + # Calculate lag + video_state_info_kwargs['lag'] = ( + elapsed_wall_time - elapsed_current_time) + # Calculate remaining time + if video_state_info_kwargs['played'].length > 0: + played_duration = (video_state_info_kwargs['played'].end(0) - + video_state_info_kwargs['played'].start(0)) + video_state_info_kwargs['remaining_time'] = ( + self.expected_duration - played_duration) + else: + # No playback has happened yet, remaining time is duration + video_state_info_kwargs['remaining_time'] = self.expected_duration + # Fetch non time critical source information + video_state_info_kwargs['video_src'] = self.video.get_attribute('src') + # Create video state snapshot + state_info = self._video_state_named_tuple() + return state_info(**video_state_info_kwargs) + + @property + def _fetch_state_script(self): + if not self._fetch_state_script_string: + self._fetch_state_script_string = ( + self._video_var_script + + 'return [' + 'baseURI,' + 'currentTime,' + 'duration,' + '[buffered.length, bufferedRanges],' + '[played.length, playedRanges],' + 'totalFrames,' + 'droppedFrames,' + 'corruptedFrames];') + return self._fetch_state_script_string + + def _refresh_state(self): + """ + Refresh the snapshot of the underlying video state. We do this all + in one so that the state doesn't change in between queries. + + We also store information that can be derived from the snapshotted + information, such as lag. This is stored in the last seen state to + stress that it's based on the snapshot. + """ + keys = ['base_uri', 'current_time', 'duration', 'raw_buffered_ranges', + 'raw_played_ranges', 'total_frames', 'dropped_frames', + 'corrupted_frames'] + values = self._execute_video_script(self._fetch_state_script) + self._last_seen_video_state = ( + self._create_video_state_info(**dict(zip(keys, values)))) + + def _measure_progress(self): + self._refresh_state() + initial = self._last_seen_video_state.current_time + sleep(1) + self._refresh_state() + return self._last_seen_video_state.current_time - initial + + def _execute_video_script(self, script): + """ + Execute JS script in content context with access to video element. + + :param script: script to be executed + :return: value returned by script + """ + with self.marionette.using_context(Marionette.CONTEXT_CONTENT): + return self.marionette.execute_script(script, + script_args=[self.video]) + + def __str__(self): + messages = ['{} - test url: {}: ' + .format(type(self).__name__, self.test_url)] + if not self.video: + messages += ['\tvideo: None'] + return '\n'.join(messages) + if not self._last_seen_video_state: + messages += ['\tvideo: No last seen state'] + return '\n'.join(messages) + # Have video and state info + messages += [ + '{', + '\t(video)' + ] + messages += ['\tinterval: {}'.format(self.interval)] + messages += ['\texpected duration: {}'.format(self.expected_duration)] + messages += ['\tstall wait time: {}'.format(self.stall_wait_time)] + messages += ['\ttimeout: {}'.format(self.timeout)] + # Print each field on its own line + for field in self._last_seen_video_state._fields: + # For compatibility with different test environments we force ascii + field_ascii = ( + unicode(getattr(self._last_seen_video_state, field)) + .encode('ascii','replace')) + messages += [('\t{}: {}'.format(field, field_ascii))] + messages += '}' + return '\n'.join(messages) + + +class VideoException(Exception): + """ + Exception class to use for video-specific error processing. + """ + pass + + +class TimeRanges: + """ + Class to represent the TimeRanges data returned by played(). Exposes a + similar interface to the JavaScript TimeRanges object. + """ + def __init__(self, length, ranges): + # These should be the same,. Theoretically we don't need the length, + # but since this should be used to consume data coming back from + # JS exec, this is a valid sanity check. + assert length == len(ranges) + self.length = length + self.ranges = [(pair[0], pair[1]) for pair in ranges] + + def __repr__(self): + return ( + 'TimeRanges: length: {}, ranges: {}' + .format(self.length, self.ranges) + ) + + def start(self, index): + return self.ranges[index][0] + + def end(self, index): + return self.ranges[index][1] diff --git a/dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py b/dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py new file mode 100644 index 000000000..e42bbcc87 --- /dev/null +++ b/dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py @@ -0,0 +1,496 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import re + +from collections import namedtuple +from json import loads +from time import sleep + +from marionette_driver import By, expected, Wait +from marionette_driver.errors import TimeoutException, NoSuchElementException +from marionette_harness import Marionette + +from video_puppeteer import VideoPuppeteer, VideoException +from external_media_tests.utils import verbose_until + + +class YouTubePuppeteer(VideoPuppeteer): + """ + Wrapper around a YouTube .html5-video-player element. + + Can be used with youtube videos or youtube videos at embedded URLS. E.g. + both https://www.youtube.com/watch?v=AbAACm1IQE0 and + https://www.youtube.com/embed/AbAACm1IQE0 should work. + + Using an embedded video has the advantage of not auto-playing more videos + while a test is running. + + Compared to video puppeteer, this class has the benefit of accessing the + youtube player object as well as the video element. The YT player will + report information for the underlying video even if an add is playing (as + opposed to the video element behaviour, which will report on whatever + is play at the time of query), and can also report if an ad is playing. + + Partial reference: https://developers.google.com/youtube/iframe_api_reference. + This reference is useful for site-specific features such as interacting + with ads, or accessing YouTube's debug data. + """ + + _player_var_script = ( + 'var player_duration = arguments[1].wrappedJSObject.getDuration();' + 'var player_current_time = ' + 'arguments[1].wrappedJSObject.getCurrentTime();' + 'var player_playback_quality = ' + 'arguments[1].wrappedJSObject.getPlaybackQuality();' + 'var player_movie_id = ' + 'arguments[1].wrappedJSObject.getVideoData()["video_id"];' + 'var player_movie_title = ' + 'arguments[1].wrappedJSObject.getVideoData()["title"];' + 'var player_url = ' + 'arguments[1].wrappedJSObject.getVideoUrl();' + 'var player_state = ' + 'arguments[1].wrappedJSObject.getPlayerState();' + 'var player_ad_state = arguments[1].wrappedJSObject.getAdState();' + 'var player_breaks_count = ' + 'arguments[1].wrappedJSObject.getOption("ad", "breakscount");' + ) + """ + A string containing JS that will assign player state to + variables. This is similar to `_video_var_script` from + `VideoPuppeteer`. See `_video_var_script` for more information on the + motivation for this method. + + This script assigns a subset of the vars used later by the + `_yt_state_named_tuple` function. Please see that functions + documentation for further information on these variables. + """ + + _yt_player_state = { + 'UNSTARTED': -1, + 'ENDED': 0, + 'PLAYING': 1, + 'PAUSED': 2, + 'BUFFERING': 3, + 'CUED': 5 + } + _yt_player_state_name = {v: k for k, v in _yt_player_state.items()} + _time_pattern = re.compile('(?P<minute>\d+):(?P<second>\d+)') + + def __init__(self, marionette, url, autostart=True, **kwargs): + self.player = None + self._last_seen_player_state = None + super(YouTubePuppeteer, + self).__init__(marionette, url, + video_selector='.html5-video-player video', + autostart=False, + **kwargs) + wait = Wait(self.marionette, timeout=30) + with self.marionette.using_context(Marionette.CONTEXT_CONTENT): + verbose_until(wait, self, + expected.element_present(By.CLASS_NAME, + 'html5-video-player')) + self.player = self.marionette.find_element(By.CLASS_NAME, + 'html5-video-player') + self.marionette.execute_script("log('.html5-video-player " + "element obtained');") + # When an ad is playing, self.player_duration indicates the duration + # of the spliced-in ad stream, not the duration of the main video, so + # we attempt to skip the ad first. + for attempt in range(5): + sleep(1) + self.process_ad() + if (self._last_seen_player_state.player_ad_inactive and + self._last_seen_video_state.duration and not + self._last_seen_player_state.player_buffering): + break + self._update_expected_duration() + if autostart: + self.start() + + def player_play(self): + """ + Play via YouTube API. + """ + self._execute_yt_script('arguments[1].wrappedJSObject.playVideo();') + + def player_pause(self): + """ + Pause via YouTube API. + """ + self._execute_yt_script('arguments[1].wrappedJSObject.pauseVideo();') + + def _player_measure_progress(self): + """ + Determine player progress. Refreshes state. + + :return: Playback progress in seconds via YouTube API with snapshots. + """ + self._refresh_state() + initial = self._last_seen_player_state.player_current_time + sleep(1) + self._refresh_state() + return self._last_seen_player_state.player_current_time - initial + + def _get_player_debug_dict(self): + text = self._execute_yt_script('return arguments[1].' + 'wrappedJSObject.getDebugText();') + if text: + try: + return loads(text) + except ValueError: + self.marionette.log('Error loading json: DebugText', + level='DEBUG') + + def _execute_yt_script(self, script): + """ + Execute JS script in content context with access to video element and + YouTube .html5-video-player element. + + :param script: script to be executed. + + :return: value returned by script + """ + with self.marionette.using_context(Marionette.CONTEXT_CONTENT): + return self.marionette.execute_script(script, + script_args=[self.video, + self.player]) + + def _check_if_ad_ended(self): + self._refresh_state() + return self._last_seen_player_state.player_ad_ended + + def process_ad(self): + """ + Wait for this ad to finish. Refreshes state. + """ + self._refresh_state() + if self._last_seen_player_state.player_ad_inactive: + return + ad_timeout = (self._search_ad_duration() or 30) + 5 + wait = Wait(self, timeout=ad_timeout, interval=1) + try: + self.marionette.log('process_ad: waiting {} s for ad' + .format(ad_timeout)) + verbose_until(wait, + self, + YouTubePuppeteer._check_if_ad_ended, + "Check if ad ended") + except TimeoutException: + self.marionette.log('Waiting for ad to end timed out', + level='WARNING') + + def _search_ad_duration(self): + """ + Try and determine ad duration. Refreshes state. + + :return: ad duration in seconds, if currently displayed in player + """ + self._refresh_state() + if not (self._last_seen_player_state.player_ad_playing or + self._player_measure_progress() == 0): + return None + if (self._last_seen_player_state.player_ad_playing and + self._last_seen_video_state.duration): + return self._last_seen_video_state.duration + selector = '.html5-video-player .videoAdUiAttribution' + wait = Wait(self.marionette, timeout=5) + try: + with self.marionette.using_context(Marionette.CONTEXT_CONTENT): + wait.until(expected.element_present(By.CSS_SELECTOR, + selector)) + countdown = self.marionette.find_element(By.CSS_SELECTOR, + selector) + ad_time = self._time_pattern.search(countdown.text) + if ad_time: + ad_minutes = int(ad_time.group('minute')) + ad_seconds = int(ad_time.group('second')) + return 60 * ad_minutes + ad_seconds + except (TimeoutException, NoSuchElementException): + self.marionette.log('Could not obtain ' + 'element: {}'.format(selector), + level='WARNING') + return None + + def _player_stalled(self): + """ + Checks if the player has stalled. Refreshes state. + + :return: True if playback is not making progress for 4-9 seconds. This + excludes ad breaks. Note that the player might just be busy with + buffering due to a slow network. + """ + + # `current_time` stands still while ad is playing + def condition(): + # no ad is playing and current_time stands still + return (not self._last_seen_player_state.player_ad_playing and + self._measure_progress() < 0.1 and + self._player_measure_progress() < 0.1 and + (self._last_seen_player_state.player_playing or + self._last_seen_player_state.player_buffering)) + + if condition(): + sleep(2) + self._refresh_state() + if self._last_seen_player_state.player_buffering: + sleep(5) + self._refresh_state() + return condition() + else: + return False + + @staticmethod + def _yt_state_named_tuple(): + """ + Create a named tuple class that can be used to store state snapshots + of the wrapped youtube player. The fields in the tuple should be used + as follows: + + player_duration: the duration as fetched from the wrapped player. + player_current_time: the current playback time as fetched from the + wrapped player. + player_remaining_time: the remaining time as calculated based on the + puppeteers expected time and the players current time. + player_playback_quality: the playback quality as fetched from the + wrapped player. See: + https://developers.google.com/youtube/js_api_reference#Playback_quality + player_movie_id: the movie id fetched from the wrapped player. + player_movie_title: the title fetched from the wrapped player. + player_url: the self reported url fetched from the wrapped player. + player_state: the current state of playback as fetch from the wrapped + player. See: + https://developers.google.com/youtube/js_api_reference#Playback_status + player_unstarted, player_ended, player_playing, player_paused, + player_buffering, and player_cued: these are all shortcuts to the + player state, only one should be true at any time. + player_ad_state: as player_state, but reports for the current ad. + player_ad_state, player_ad_inactive, player_ad_playing, and + player_ad_ended: these are all shortcuts to the ad state, only one + should be true at any time. + player_breaks_count: the number of ads as fetched from the wrapped + player. This includes both played and unplayed ads, and includes + streaming ads as well as pop up ads. + + :return: A 'player_state_info' named tuple class. + """ + return namedtuple('player_state_info', + ['player_duration', + 'player_current_time', + 'player_remaining_time', + 'player_playback_quality', + 'player_movie_id', + 'player_movie_title', + 'player_url', + 'player_state', + 'player_unstarted', + 'player_ended', + 'player_playing', + 'player_paused', + 'player_buffering', + 'player_cued', + 'player_ad_state', + 'player_ad_inactive', + 'player_ad_playing', + 'player_ad_ended', + 'player_breaks_count' + ]) + + def _create_player_state_info(self, **player_state_info_kwargs): + """ + Create an instance of the state info named tuple. This function + expects a dictionary containing the following keys: + player_duration, player_current_time, player_playback_quality, + player_movie_id, player_movie_title, player_url, player_state, + player_ad_state, and player_breaks_count. + + For more information on the above keys and their values see + `_yt_state_named_tuple`. + + :return: A named tuple 'yt_state_info', derived from arguments and + state information from the puppeteer. + """ + player_state_info_kwargs['player_remaining_time'] = ( + self.expected_duration - + player_state_info_kwargs['player_current_time']) + # Calculate player state convenience info + player_state = player_state_info_kwargs['player_state'] + player_state_info_kwargs['player_unstarted'] = ( + player_state == self._yt_player_state['UNSTARTED']) + player_state_info_kwargs['player_ended'] = ( + player_state == self._yt_player_state['ENDED']) + player_state_info_kwargs['player_playing'] = ( + player_state == self._yt_player_state['PLAYING']) + player_state_info_kwargs['player_paused'] = ( + player_state == self._yt_player_state['PAUSED']) + player_state_info_kwargs['player_buffering'] = ( + player_state == self._yt_player_state['BUFFERING']) + player_state_info_kwargs['player_cued'] = ( + player_state == self._yt_player_state['CUED']) + # Calculate ad state convenience info + player_ad_state = player_state_info_kwargs['player_ad_state'] + player_state_info_kwargs['player_ad_inactive'] = ( + player_ad_state == self._yt_player_state['UNSTARTED']) + player_state_info_kwargs['player_ad_playing'] = ( + player_ad_state == self._yt_player_state['PLAYING']) + player_state_info_kwargs['player_ad_ended'] = ( + player_ad_state == self._yt_player_state['ENDED']) + # Create player snapshot + state_info = self._yt_state_named_tuple() + return state_info(**player_state_info_kwargs) + + @property + def _fetch_state_script(self): + if not self._fetch_state_script_string: + self._fetch_state_script_string = ( + self._video_var_script + + self._player_var_script + + 'return [' + 'baseURI,' + 'currentTime,' + 'duration,' + '[buffered.length, bufferedRanges],' + '[played.length, playedRanges],' + 'totalFrames,' + 'droppedFrames,' + 'corruptedFrames,' + 'player_duration,' + 'player_current_time,' + 'player_playback_quality,' + 'player_movie_id,' + 'player_movie_title,' + 'player_url,' + 'player_state,' + 'player_ad_state,' + 'player_breaks_count];') + return self._fetch_state_script_string + + def _refresh_state(self): + """ + Refresh the snapshot of the underlying video and player state. We do + this allin one so that the state doesn't change in between queries. + + We also store information that can be derived from the snapshotted + information, such as lag. This is stored in the last seen state to + stress that it's based on the snapshot. + """ + values = self._execute_yt_script(self._fetch_state_script) + video_keys = ['base_uri', 'current_time', 'duration', + 'raw_buffered_ranges', 'raw_played_ranges', + 'total_frames', 'dropped_frames', 'corrupted_frames'] + player_keys = ['player_duration', 'player_current_time', + 'player_playback_quality', 'player_movie_id', + 'player_movie_title', 'player_url', 'player_state', + 'player_ad_state', 'player_breaks_count'] + # Get video state + self._last_seen_video_state = ( + self._create_video_state_info(**dict( + zip(video_keys, values[:len(video_keys)])))) + # Get player state + self._last_seen_player_state = ( + self._create_player_state_info(**dict( + zip(player_keys, values[-len(player_keys):])))) + + def mse_enabled(self): + """ + Check if the video source indicates mse usage for current video. + Refreshes state. + + :return: True if MSE is being used, False if not. + """ + self._refresh_state() + return self._last_seen_video_state.video_src.startswith('blob') + + def playback_started(self): + """ + Check whether playback has started. Refreshes state. + + :return: True if play back has started, False if not. + """ + self._refresh_state() + # usually, ad is playing during initial buffering + if (self._last_seen_player_state.player_playing or + self._last_seen_player_state.player_buffering): + return True + if (self._last_seen_video_state.current_time > 0 or + self._last_seen_player_state.player_current_time > 0): + return True + return False + + def playback_done(self): + """ + Check whether playback is done. Refreshes state. + + :return: True if play back has ended, False if not. + """ + # in case ad plays at end of video + self._refresh_state() + if self._last_seen_player_state.player_ad_playing: + return False + return (self._last_seen_player_state.player_ended or + self._last_seen_player_state.player_remaining_time < 1) + + def wait_for_almost_done(self, final_piece=120): + """ + Allow the given video to play until only `final_piece` seconds remain, + skipping ads mid-way as much as possible. + `final_piece` should be short enough to not be interrupted by an ad. + + Depending on the length of the video, check the ad status every 10-30 + seconds, skip an active ad if possible. + + This call refreshes state. + + :param final_piece: The length in seconds of the desired remaining time + to wait until. + """ + self._refresh_state() + rest = 10 + duration = remaining_time = self.expected_duration + if duration < final_piece: + # video is short so don't attempt to skip more ads + return duration + elif duration > 600: + # for videos that are longer than 10 minutes + # wait longer between checks + rest = duration / 50 + + while remaining_time > final_piece: + if self._player_stalled(): + if self._last_seen_player_state.player_buffering: + # fall back on timeout in 'wait' call that comes after this + # in test function + self.marionette.log('Buffering and no playback progress.') + break + else: + message = '\n'.join(['Playback stalled', str(self)]) + raise VideoException(message) + if self._last_seen_player_state.player_breaks_count > 0: + self.process_ad() + if remaining_time > 1.5 * rest: + sleep(rest) + else: + sleep(rest / 2) + # TODO during an ad, remaining_time will be based on ad's current_time + # rather than current_time of target video + remaining_time = self._last_seen_player_state.player_remaining_time + return remaining_time + + def __str__(self): + messages = [super(YouTubePuppeteer, self).__str__()] + if not self.player: + messages += ['\t.html5-media-player: None'] + return '\n'.join(messages) + if not self._last_seen_player_state: + messages += ['\t.html5-media-player: No last seen state'] + return '\n'.join(messages) + messages += ['.html5-media-player: {'] + for field in self._last_seen_player_state._fields: + # For compatibility with different test environments we force ascii + field_ascii = ( + unicode(getattr(self._last_seen_player_state, field)) + .encode('ascii', 'replace')) + messages += [('\t{}: {}'.format(field, field_ascii))] + messages += '}' + return '\n'.join(messages) diff --git a/dom/media/test/external/external_media_tests/playback/eme.ini b/dom/media/test/external/external_media_tests/playback/eme.ini new file mode 100644 index 000000000..6f08919bf --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/eme.ini @@ -0,0 +1 @@ +[test_eme_playback.py] diff --git a/dom/media/test/external/external_media_tests/playback/limiting_bandwidth.ini b/dom/media/test/external/external_media_tests/playback/limiting_bandwidth.ini new file mode 100644 index 000000000..77c144d80 --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/limiting_bandwidth.ini @@ -0,0 +1,2 @@ +[test_playback_limiting_bandwidth.py] +[test_ultra_low_bandwidth.py] diff --git a/dom/media/test/external/external_media_tests/playback/manifest.ini b/dom/media/test/external/external_media_tests/playback/manifest.ini new file mode 100644 index 000000000..f7271cbfb --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/manifest.ini @@ -0,0 +1 @@ +[test_video_playback.py] diff --git a/dom/media/test/external/external_media_tests/playback/netflix_limiting_bandwidth.ini b/dom/media/test/external/external_media_tests/playback/netflix_limiting_bandwidth.ini new file mode 100644 index 000000000..dd0ce3601 --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/netflix_limiting_bandwidth.ini @@ -0,0 +1 @@ +[test_eme_playback_limiting_bandwidth.py] diff --git a/dom/media/test/external/external_media_tests/playback/test_eme_playback.py b/dom/media/test/external/external_media_tests/playback/test_eme_playback.py new file mode 100644 index 000000000..9c7eb6725 --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/test_eme_playback.py @@ -0,0 +1,18 @@ +# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from external_media_harness.testcase import (
+ MediaTestCase,
+ VideoPlaybackTestsMixin,
+ EMESetupMixin
+)
+
+
+class TestEMEPlayback(MediaTestCase, VideoPlaybackTestsMixin, EMESetupMixin):
+
+ def setUp(self):
+ super(TestEMEPlayback, self).setUp()
+ self.check_eme_system()
+
+ # Tests are implemented in VideoPlaybackTestsMixin
diff --git a/dom/media/test/external/external_media_tests/playback/test_eme_playback_limiting_bandwidth.py b/dom/media/test/external/external_media_tests/playback/test_eme_playback_limiting_bandwidth.py new file mode 100644 index 000000000..44cbb44f1 --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/test_eme_playback_limiting_bandwidth.py @@ -0,0 +1,24 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from marionette_harness import BrowserMobProxyTestCaseMixin + +from external_media_harness.testcase import ( + EMESetupMixin, + NetworkBandwidthTestCase, + NetworkBandwidthTestsMixin, +) + + +class TestEMEPlaybackLimitingBandwidth(NetworkBandwidthTestCase, + BrowserMobProxyTestCaseMixin, + NetworkBandwidthTestsMixin, + EMESetupMixin): + + + def setUp(self): + super(TestEMEPlaybackLimitingBandwidth, self).setUp() + self.check_eme_system() + + # Tests in NetworkBandwidthTestsMixin diff --git a/dom/media/test/external/external_media_tests/playback/test_full_playback.py b/dom/media/test/external/external_media_tests/playback/test_full_playback.py new file mode 100644 index 000000000..0db504682 --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/test_full_playback.py @@ -0,0 +1,25 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from marionette_harness import Marionette + +from external_media_harness.testcase import MediaTestCase +from external_media_tests.media_utils.video_puppeteer import VideoPuppeteer + + +class TestFullPlayback(MediaTestCase): + """ Test MSE playback in HTML5 video element. + + These tests should pass on any site where a single video element plays + upon loading and is uninterrupted (by ads, for example). This will play + the full videos, so it could take a while depending on the videos playing. + It should be run much less frequently in automated systems. + """ + + def test_video_playback_full(self): + with self.marionette.using_context(Marionette.CONTEXT_CONTENT): + for url in self.video_urls: + video = VideoPuppeteer(self.marionette, url, + stall_wait_time=10) + self.run_playback(video) diff --git a/dom/media/test/external/external_media_tests/playback/test_playback_limiting_bandwidth.py b/dom/media/test/external/external_media_tests/playback/test_playback_limiting_bandwidth.py new file mode 100644 index 000000000..81f1e8a59 --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/test_playback_limiting_bandwidth.py @@ -0,0 +1,17 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from marionette_harness import BrowserMobProxyTestCaseMixin + +from external_media_harness.testcase import ( + NetworkBandwidthTestCase, NetworkBandwidthTestsMixin +) + + +class TestPlaybackLimitingBandwidth(NetworkBandwidthTestCase, + NetworkBandwidthTestsMixin, + BrowserMobProxyTestCaseMixin): + + # Tests are in NetworkBandwidthTestsMixin + pass diff --git a/dom/media/test/external/external_media_tests/playback/test_shaka_playback.py b/dom/media/test/external/external_media_tests/playback/test_shaka_playback.py new file mode 100644 index 000000000..a2ecb4a2c --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/test_shaka_playback.py @@ -0,0 +1,42 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from marionette_harness import Marionette + +from external_media_harness.testcase import MediaTestCase +from external_media_tests.media_utils.video_puppeteer import VideoPuppeteer + + +class TestShakaPlayback(MediaTestCase): + """ Test Widevine playback in shaka-player + + This test takes manifest URLs rather than URLs for pages with videos. These + manifests are loaded with shaka-player + """ + + def test_video_playback_partial(self): + """ Plays 60 seconds of the video from the manifest URLs given + """ + shakaUrl = "http://shaka-player-demo.appspot.com" + self.marionette.set_pref('media.mediasource.webm.enabled', True) + + with self.marionette.using_context(Marionette.CONTEXT_CONTENT): + for manifestUrl in self.video_urls: + vp = VideoPuppeteer(self.marionette, + shakaUrl, + stall_wait_time=10, + set_duration=60, + video_selector="video#video", + autostart=False) + + + manifestInput = self.marionette.find_element("id", + "manifestUrlInput") + manifestInput.clear() + manifestInput.send_keys(manifestUrl) + loadButton = self.marionette.find_element("id", "loadButton") + loadButton.click() + + vp.start() + self.run_playback(vp) diff --git a/dom/media/test/external/external_media_tests/playback/test_ultra_low_bandwidth.py b/dom/media/test/external/external_media_tests/playback/test_ultra_low_bandwidth.py new file mode 100644 index 000000000..d49ff7d94 --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/test_ultra_low_bandwidth.py @@ -0,0 +1,15 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from marionette_harness import BrowserMobProxyTestCaseMixin + +from external_media_harness.testcase import NetworkBandwidthTestCase + + +class TestUltraLowBandwidth(NetworkBandwidthTestCase, + BrowserMobProxyTestCaseMixin): + + def test_playback_limiting_bandwidth_160(self): + self.proxy.limits({'downstream_kbps': 160}) + self.run_videos(timeout=120) diff --git a/dom/media/test/external/external_media_tests/playback/test_video_playback.py b/dom/media/test/external/external_media_tests/playback/test_video_playback.py new file mode 100644 index 000000000..b841ec08c --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/test_video_playback.py @@ -0,0 +1,15 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from external_media_harness.testcase import ( + MediaTestCase, + VideoPlaybackTestsMixin +) + + +class TestVideoPlayback(MediaTestCase, VideoPlaybackTestsMixin): + + # Tests are actually implemented in VideoPlaybackTestsMixin. + + pass diff --git a/dom/media/test/external/external_media_tests/playback/youtube/manifest.ini b/dom/media/test/external/external_media_tests/playback/youtube/manifest.ini new file mode 100644 index 000000000..d9ad7eb19 --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/youtube/manifest.ini @@ -0,0 +1 @@ +[test_basic_playback.py ] diff --git a/dom/media/test/external/external_media_tests/playback/youtube/test_basic_playback.py b/dom/media/test/external/external_media_tests/playback/youtube/test_basic_playback.py new file mode 100644 index 000000000..edd0afc5e --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/youtube/test_basic_playback.py @@ -0,0 +1,74 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from marionette_driver import Wait +from marionette_driver.errors import TimeoutException +from marionette_harness import Marionette + +from external_media_tests.utils import verbose_until +from external_media_harness.testcase import MediaTestCase +from external_media_tests.media_utils.video_puppeteer import VideoException +from external_media_tests.media_utils.youtube_puppeteer import YouTubePuppeteer + + +class TestBasicYouTubePlayback(MediaTestCase): + def test_mse_is_enabled_by_default(self): + with self.marionette.using_context(Marionette.CONTEXT_CONTENT): + youtube = YouTubePuppeteer(self.marionette, self.video_urls[0], + timeout=60) + wait = Wait(youtube, + timeout=min(300, youtube.expected_duration * 1.3), + interval=1) + try: + verbose_until(wait, youtube, + YouTubePuppeteer.mse_enabled, + "Failed to find 'blob' in video src url.") + except TimeoutException as e: + raise self.failureException(e) + + def test_video_playing_in_one_tab(self): + with self.marionette.using_context(Marionette.CONTEXT_CONTENT): + for url in self.video_urls: + self.logger.info(url) + youtube = YouTubePuppeteer(self.marionette, url) + self.logger.info('Expected duration: {}' + .format(youtube.expected_duration)) + + final_piece = 60 + try: + time_left = youtube.wait_for_almost_done( + final_piece=final_piece) + except VideoException as e: + raise self.failureException(e) + duration = abs(youtube.expected_duration) + 1 + if duration > 1: + self.logger.info('Almost done: {} - {} seconds left.' + .format(url, time_left)) + if time_left > final_piece: + self.marionette.log('time_left greater than ' + 'final_piece - {}' + .format(time_left), + level='WARNING') + self.save_screenshot() + else: + self.marionette.log('Duration close to 0 - {}' + .format(youtube), + level='WARNING') + self.save_screenshot() + try: + verbose_until(Wait(youtube, + timeout=max(100, time_left) * 1.3, + interval=1), + youtube, + YouTubePuppeteer.playback_done) + except TimeoutException as e: + raise self.failureException(e) + + def test_playback_starts(self): + with self.marionette.using_context(Marionette.CONTEXT_CONTENT): + for url in self.video_urls: + try: + YouTubePuppeteer(self.marionette, url, timeout=60) + except TimeoutException as e: + raise self.failureException(e) diff --git a/dom/media/test/external/external_media_tests/playback/youtube/test_prefs.py b/dom/media/test/external/external_media_tests/playback/youtube/test_prefs.py new file mode 100644 index 000000000..4c07ca008 --- /dev/null +++ b/dom/media/test/external/external_media_tests/playback/youtube/test_prefs.py @@ -0,0 +1,46 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from external_media_harness.testcase import MediaTestCase +from marionette_driver import Wait + +from external_media_tests.utils import verbose_until +from external_media_tests.media_utils.youtube_puppeteer import YouTubePuppeteer + + +class TestMediaSourcePrefs(MediaTestCase): + def setUp(self): + MediaTestCase.setUp(self) + self.test_urls = self.video_urls[:2] + self.max_timeout = 60 + + def tearDown(self): + MediaTestCase.tearDown(self) + + def test_mse_prefs(self): + """ mediasource should only be used if MSE prefs are enabled.""" + self.set_mse_enabled_prefs(False) + self.check_mse_src(False, self.test_urls[0]) + + self.set_mse_enabled_prefs(True) + self.check_mse_src(True, self.test_urls[0]) + + def set_mse_enabled_prefs(self, value): + with self.marionette.using_context('chrome'): + self.marionette.set_pref('media.mediasource.enabled', value) + self.marionette.set_pref('media.mediasource.mp4.enabled', value) + self.marionette.set_pref('media.mediasource.webm.enabled', value) + + def check_mse_src(self, mse_expected, url): + with self.marionette.using_context('content'): + youtube = YouTubePuppeteer(self.marionette, url) + wait = Wait(youtube, + timeout=min(self.max_timeout, + youtube.expected_duration * 1.3), + interval=1) + + def cond(y): + return y.mse_enabled == mse_expected + + verbose_until(wait, youtube, cond) diff --git a/dom/media/test/external/external_media_tests/resources/mozilla.html b/dom/media/test/external/external_media_tests/resources/mozilla.html new file mode 100644 index 000000000..1cdf0fb4f --- /dev/null +++ b/dom/media/test/external/external_media_tests/resources/mozilla.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <title>Mozilla</title> + <link rel="shortcut icon" type="image/ico" href="../images/mozilla_favicon.ico" /> +</head> + +<body> + <a href="mozilla.html"> + <img id="mozilla_logo" src="../images/mozilla_logo.jpg" /> + </a> + + <a href="#community">RARARARARARA</a> | + <a href="#project">Project</a> | + <a href="#organization">Organization</a> + + <div id="content"> + <h1 id="page-title"> + <strong>RARARARARARA</strong> that the internet should be public, + open and accessible. + </h1> + + <h2><a name="community">RARARARARARA</a></h2> + <p id="community"> + We're a global community of thousands who believe in the power + of technology to enrich people's lives. + <a href="mozilla_community.html">More</a> + </p> + + <h2><a name="project">Project</a></h2> + <p id="project"> + We're an open source project whose code is used for some of the + Internet's most innovative applications. + <a href="mozilla_projects.html">More</a> + </p> + + <h2><a name="organization">Organization</a></h2> + <p id="organization"> + We're a public benefit organization dedicated to making the + Internet better for everyone. + <a href="mozilla_mission.html">More</a> + </p> + </div> +</body> +</html> diff --git a/dom/media/test/external/external_media_tests/test_example.py b/dom/media/test/external/external_media_tests/test_example.py new file mode 100644 index 000000000..99782014f --- /dev/null +++ b/dom/media/test/external/external_media_tests/test_example.py @@ -0,0 +1,21 @@ +from marionette_harness import Marionette + +from external_media_harness.testcase import MediaTestCase + + +class TestSomethingElse(MediaTestCase): + def setUp(self): + MediaTestCase.setUp(self) + self.test_urls = [ + 'mozilla.html', + ] + self.test_urls = [self.marionette.absolute_url(t) + for t in self.test_urls] + + def tearDown(self): + MediaTestCase.tearDown(self) + + def test_foo(self): + self.logger.info('foo!') + with self.marionette.using_context(Marionette.CONTEXT_CONTENT): + self.marionette.navigate(self.test_urls[0]) diff --git a/dom/media/test/external/external_media_tests/urls/default.ini b/dom/media/test/external/external_media_tests/urls/default.ini new file mode 100644 index 000000000..b1e26abbd --- /dev/null +++ b/dom/media/test/external/external_media_tests/urls/default.ini @@ -0,0 +1,9 @@ +# short videos; no ads; embedded; max 5 minutes +# 0:12 +[https://youtube.com/embed/AbAACm1IQE0?autoplay=1] +# 2:18 +[https://youtube.com/embed/yOQQCoxs8-k?autoplay=1] +# 0:08 +[https://youtube.com/embed/1visYpIREUM?autoplay=1] +# 2:09 +[https://youtube.com/embed/rjmuKV9BTkE?autoplay=1] diff --git a/dom/media/test/external/external_media_tests/urls/netflix/default.ini b/dom/media/test/external/external_media_tests/urls/netflix/default.ini new file mode 100644 index 000000000..ed14b69b8 --- /dev/null +++ b/dom/media/test/external/external_media_tests/urls/netflix/default.ini @@ -0,0 +1,8 @@ +# YouTube test +#[https://www.youtube.com/watch?v=AbAACm1IQE0] +# ClearKey - 11:07 +[http://www.netflix.com/watch/70136810] +# NoDRM - 2:24:xx +[http://www.netflix.com/watch/70304192] +# DRM - 24:47 +[http://www.netflix.com/watch/80015538] diff --git a/dom/media/test/external/external_media_tests/urls/shaka-player/default.ini b/dom/media/test/external/external_media_tests/urls/shaka-player/default.ini new file mode 100644 index 000000000..8a42d7603 --- /dev/null +++ b/dom/media/test/external/external_media_tests/urls/shaka-player/default.ini @@ -0,0 +1,30 @@ +# The Shaka-player tests take manifest URLs rather than URLs for a page that +# plays a video (since shaka-player is the page that plays the video) +# This file contains the manifest URLs that shaka-player provides in it's +# dropdown + +# "Angel One" (TNG clip) - multilingual, subtitles, VP8 +[http://shaka-player-demo.appspot.com/assets/angel_one.mpd] +# "Car" (YT DASH test) - MP4 +[http://shaka-player-demo.appspot.com/assets/car-20120827-manifest.mpd] +# "Car/CENC" (YT DASH EME test) - MP4, ClearKey +[http://shaka-player-demo.appspot.com/assets/car_cenc-20120827-manifest.mpd] +# "Feelings" (YT DASH test) - VP9 +[http://shaka-player-demo.appspot.com/assets/feelings_vp9-20130806-manifest.mpd] +# "Feelings" (YT DASH test) - Audio only +[http://shaka-player-demo.appspot.com/assets/feelings_audio_only-20130806-manifest.mpd] +# "Car/SegmentTemplate" (Chromecast test) - MP4 (no SIDX, video only), Widevine +[http://shaka-player-demo.appspot.com/assets/car_segmenttemplate.mpd] +# "GPAC/SegmentList" (conformance test) +[http://download.tsi.telecom-paristech.fr/gpac/DASH_CONFORMANCE/TelecomParisTech/mp4-main-multi/mp4-main-multi-mpd-AV-NBS.mpd] +# "Oops" (modified YT DASH EME test) - MP4, multi-DRM +[http://shaka-player-demo.appspot.com/assets/oops_cenc-20121114-signedlicenseurl-manifest.mpd] +# "Oops" (modified YT DASH EME test) - MP4, Widevine, PSSH in MPD +# This stream currently does not load +#[http://shaka-player-demo.appspot.com/assets/oops_cenc_pssh.mpd] +# "Sintel" (1080p high bitrate test) - MP4 +[http://storage.googleapis.com/widevine-demo-media/sintel-1080p/dash.mpd] +# "Sintel" (4k) - MP4, VP8, VP9 +[http://storage.googleapis.com/widevine-demo-media/sintel-multicodec-4k/dash.mpd] +# "Sintel" (4k) - MP4, Widevine +[http://storage.googleapis.com/widevine-demo-media/sintel-4k-widevine/sintel.mpd] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/archive/crash_videos.ini b/dom/media/test/external/external_media_tests/urls/youtube/archive/crash_videos.ini new file mode 100644 index 000000000..e7d420254 --- /dev/null +++ b/dom/media/test/external/external_media_tests/urls/youtube/archive/crash_videos.ini @@ -0,0 +1,25 @@ +[https://www.youtube.com/watch?v=2GfaRuIMdos] +[https://www.youtube.com/watch?v=9vKvcCNt40g] +[https://www.youtube.com/watch?v=SHLLHya2pNo] +[https://www.youtube.com/watch?v=isMEMDE2enU] +[https://www.youtube.com/watch?v=H81M_MebLsk] +[https://www.youtube.com/watch?v=yopNkcDzQQw] +[https://www.youtube.com/watch?v=r_bG5beSqw0] +[https://www.youtube.com/watch?v=Ki9sSZKClO0] +[https://www.youtube.com/watch?v=gNS04P8djk4] +[https://www.youtube.com/watch?v=DwC_6fIBW0w] +[https://www.youtube.com/watch?v=g1D3A14o0NA] +[https://www.youtube.com/watch?v=cs-XZ_dN4Hc] +[https://www.youtube.com/watch?v=ZEWZ3AAH98c] +[https://www.youtube.com/watch?v=hwbVGE4GBJI] +[https://www.youtube.com/watch?v=cvcMnbkasIs] +[https://www.youtube.com/watch?v=cHaBuoHwQ0Y] +[https://www.youtube.com/watch?v=VKIYoAG9MZ0] +[https://www.youtube.com/watch?v=WWDb2_unEJc] +[https://www.youtube.com/watch?v=ybw5zonQffE] +[https://www.youtube.com/watch?v=hS6ps2Xph_o] +[https://www.youtube.com/watch?v=Bjb3xhgIqv4] +[https://www.youtube.com/watch?v=fOzvEhX4Kvk] +[https://www.youtube.com/watch?v=_TNsUxp_BxM] +[https://www.youtube.com/watch?v=QRdwCSHF3oo] +[https://www.youtube.com/watch?v=VwaHFcKJSYA] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/archive/other_videos.ini b/dom/media/test/external/external_media_tests/urls/youtube/archive/other_videos.ini new file mode 100644 index 000000000..732d2405c --- /dev/null +++ b/dom/media/test/external/external_media_tests/urls/youtube/archive/other_videos.ini @@ -0,0 +1,19 @@ +# backlog of videos +[http://youtu.be/2iVAvSnofy8] + +# 300s <= duration <= 1200s (5-20min) +[http://youtu.be/9bZkp7q19f0] +[http://youtu.be/KQ6zr6kCPj8] + +# duration > 1200s (>20min) +[http://youtu.be/wZZ7oFKsKzY] +[http://youtu.be/eHUrC_UiZwY] +[http://youtu.be/FLX64H5FYa8] +[http://youtu.be/Fu2DcHzokew] + +#no_ad_tests_youtube +#[http://youtu.be/pWI8RB2dmfU] +#[http://youtu.be/6GBtEmtVObw] + +#playlist_tests_youtube +#[http://youtu.be/R6KJjPqlPz4?list=PL75_HhpYGJQ1Fzv9a46FlHfiy-fJusKBZ] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/archive/video_data.ini b/dom/media/test/external/external_media_tests/urls/youtube/archive/video_data.ini new file mode 100644 index 000000000..ff8f58866 --- /dev/null +++ b/dom/media/test/external/external_media_tests/urls/youtube/archive/video_data.ini @@ -0,0 +1,21 @@ +# duration < 300s (5min) +[http://youtu.be/065dlrJoHcw] +[http://youtu.be/1visYpIREUM] +[http://youtu.be/mDf7CR5QKcE] +[http://youtu.be/Aebs62bX0dA] +[http://youtu.be/6SFp1z7uA6g] +[http://youtu.be/tDDVAErOI5U] + +# ad testing +[https://www.youtube.com/watch?v=l5ODwR6FPRQ] +[https://www.youtube.com/watch?v=7RMQksXpQSk] + +# duration > 5 min +# video with ad in the middle +[https://www.youtube.com/watch?v=cht9Xq9suGg] + +# long video (>30 min), no ads +[https://www.youtube.com/watch?v=-qXxNPvqHtQ] + +# bug 1144172, duration ~ 1hr +#[https://www.youtube.com/watch?v=AYYDshv8C4g] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/archive/youtube.ini b/dom/media/test/external/external_media_tests/urls/youtube/archive/youtube.ini new file mode 100644 index 000000000..0676b9ef4 --- /dev/null +++ b/dom/media/test/external/external_media_tests/urls/youtube/archive/youtube.ini @@ -0,0 +1,38 @@ +# < 1 no ads +[https://youtu.be/AbAACm1IQE0] +[https://www.youtube.com/watch?v=KdHZwWQWNyM] +[https://www.youtube.com/watch?v=-hVmkA_I9EE] +[https://www.youtube.com/watch?v=1visYpIREUM] + +# 1 < t <= 5 no ads +[https://www.youtube.com/watch?v=rpYRAs6ePY8] +[https://www.youtube.com/watch?v=xcgUKzwg0Mo] +[https://youtu.be/sEAT2EFIJow] +[https://www.youtube.com/watch?v=SSgnbQ5UC48] +[https://youtu.be/4oQu26IhiaA] +[https://youtu.be/IbND63HOb0M] +[https://youtu.be/-9sJp9wrdAk] +[https://www.youtube.com/watch?v=yIQGH4aQWI0] + +# 1 < t <= 5 +[https://www.youtube.com/watch?v=-hVmkA_I9EE] +[https://www.youtube.com/watch?v=l5ODwR6FPRQ] +[https://www.youtube.com/watch?v=7RMQksXpQSk] +[https://www.youtube.com/watch?v=TsXMe8H6iyc] +[https://www.youtube.com/watch?v=tDDVAErOI5U] + +# 5 < t <= 10 +[https://youtu.be/Tl-hI2IsCo0] # no ad +[https://www.youtube.com/watch?v=IX_d_vMKswE] #no ad +[https://www.youtube.com/watch?v=YVQeTY-Ayko] #no ad +[https://www.youtube.com/watch?v=rE3j_RHkqJc] +[https://www.youtube.com/watch?v=l4bmZ1gRqCc] + +# 10 < t <= 30 +[https://www.youtube.com/watch?v=RvymAHt3nPc] # no ads +[https://www.youtube.com/watch?v=8XQ1onjXJK0] +[https://www.youtube.com/watch?v=6Lm9EHhbJAY] +[https://www.youtube.com/watch?v=cht9Xq9suGg] + +# long video (>30 min), no ads +[https://www.youtube.com/watch?v=-qXxNPvqHtQ] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/long1-720.ini b/dom/media/test/external/external_media_tests/urls/youtube/long1-720.ini new file mode 100644 index 000000000..cabb823b1 --- /dev/null +++ b/dom/media/test/external/external_media_tests/urls/youtube/long1-720.ini @@ -0,0 +1,5 @@ +# a couple of very long videos, < 12 hours total +# 6:00:00 - can't embed due to copyright +[https://www.youtube.com/watch?v=5N8sUccRiTA] +# 2:09:00 +[https://www.youtube.com/embed/b6q5N16dje4?autoplay=1] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/long2-crashes-720.ini b/dom/media/test/external/external_media_tests/urls/youtube/long2-crashes-720.ini new file mode 100644 index 000000000..de449f882 --- /dev/null +++ b/dom/media/test/external/external_media_tests/urls/youtube/long2-crashes-720.ini @@ -0,0 +1,39 @@ +# It appears these are not currently used by tests. They are left here as they +# reference failure scenarios. If tese are fixed that can be removed. + +# videos from crashes, < 12 hours + +# hang | NtUserMessageCall | SendMessageW +# 1:10:00 +[https://www.youtube.com/watch?v=Ztie4DqeOak] + +# nsPluginInstanceOwner::GetDocument(nsIDocument**) +# 22:40 +[https://www.youtube.com/watch?v=D4cLM_JRrAU] +# 16:47 +[https://www.youtube.com/watch?v=3C2r05Lxsrk] + +# F1398665248_____________________________ +# 1:06:00 +[https://www.youtube.com/watch?v=59gTMBss8o0] +# 50:58 +[https://www.youtube.com/watch?v=_7VFIZhR744] +# 44:54 +[https://www.youtube.com/watch?v=d6ro4Oq5msA] + +# hang | WaitForMultipleObjectsEx | RealMsgWaitForMultipleObjectsEx | MsgWaitForMultipleObjects | F_1152915508___________________________________ +#1:07:12 +[https://www.youtube.com/watch?v=Ffkf3tosmKw] +# 1:02:00 +[https://www.youtube.com/watch?v=dC3AHEao2MI] + +# hang | BaseGetNamedObjectDirectory | RealMsgWaitForMultipleObjectsEx | MsgWaitForMultipleObjects | F_1152915508___________________________________ +# 10:00 +[https://www.youtube.com/watch?v=fn3Qb56ujNQ] +# 5:00 +[https://www.youtube.com/watch?v=gBsh1bT8ltI] +# 03:50:12 +[https://www.youtube.com/watch?v=TdW4S8zbmJQ] + + + diff --git a/dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-900.ini b/dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-900.ini new file mode 100644 index 000000000..70081b986 --- /dev/null +++ b/dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-900.ini @@ -0,0 +1,86 @@ +# It appears these are not currently used by tests. They are left here as they +# reference failure scenarios. If tese are fixed that can be removed. + +# Total time: about 12-13 hours + unskippable ads +#Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=50&platform=Windows&version=37.0&date=%3E2015-03-26 + +#Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=5&platform=Windows&version=37.0&signature=%3Dhang+%7C+NtUserMessageCall+%7C+SendMessageW&date=%3E2015-03-26 + +#Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=5&platform=Windows&version=37.0&signature=%3DOOM+%7C+small&date=%3E2015-03-26 + +#Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=5&platform=Windows&version=37.0&signature=%3Dmozilla%3A%3Alayers%3A%3ACompositorD3D11%3A%3AHandleError%28long%2C+mozilla%3A%3Alayers%3A%3ACompositorD3D11%3A%3ASeverity%29+%7C+mozilla%3A%3Alayers%3A%3ACompositorD3D11%3A%3AFailed%28long%2C+mozilla%3A%3Alayers%3A%3ACompositorD3D11%3A%3ASeverity%29+%7C+mozilla%3A%3Alayers%3A%3ACompositorD3D11%3A%3AUpdateRenderTarget%28%29&date=%3E2015-03-26 + +#Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=5&platform=Windows&version=37.0&signature=%3DOOM+%7C+large+%7C+mozalloc_abort%28char+const%2A+const%29+%7C+mozalloc_handle_oom%28unsigned+int%29+%7C+moz_xmalloc+%7C+nsTArray_base%3CnsTArrayInfallibleAllocator%2C+nsTArray_CopyWithMemutils%3E%3A%3AEnsureCapacity%28unsigned+int%2C+unsigned+int%29+%7C+nsTArray_base%3CnsTArrayInfallibleAllo...&date=%3E2015-03-26 + +#Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=5&platform=Windows&version=37.0&signature=%3Dshutdownhang+%7C+WaitForSingleObjectEx+%7C+WaitForSingleObject+%7C+PR_Wait+%7C+nsThread%3A%3AProcessNextEvent%28bool%2C+bool%2A%29+%7C+NS_ProcessNextEvent%28nsIThread%2A%2C+bool%29+%7C+mozilla%3A%3AMediaShutdownManager%3A%3AShutdown%28%29&date=%3E2015-03-26 + +#Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=5&platform=Windows&version=37.0&signature=%3Dmozilla%3A%3Alayers%3A%3ACompositorD3D11%3A%3AUpdateConstantBuffers%28%29&date=%3E2015-03-26 + +#Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=5&platform=Windows&version=37.0&signature=%3Dmsvcr120.dll%400xf20c&date=%3E2015-03-26 + +#Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=5&platform=Windows&version=37.0&signature=%3Djs%3A%3AGCMarker%3A%3AprocessMarkStackTop%28js%3A%3ASliceBudget%26%29&date=%3E2015-03-26 + +#Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=5&platform=Windows&version=37.0&signature=%3Dshutdownhang+%7C+WaitForSingleObjectEx+%7C+WaitForSingleObject+%7C+PR_Wait+%7C+nsThread%3A%3AProcessNextEvent%28bool%2C+bool%2A%29+%7C+NS_ProcessNextEvent%28nsIThread%2A%2C+bool%29+%7C+mozilla%3A%3Alayers%3A%3ACompositorParent%3A%3AShutDown%28%29&date=%3E2015-03-26 + +#Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=5&platform=Windows&version=37.0&signature=%3Dshutdownhang+%7C+WaitForSingleObjectEx+%7C+WaitForSingleObject+%7C+PR_Wait+%7C+mozilla%3A%3AReentrantMonitor%3A%3AWait%28unsigned+int%29+%7C+mozilla%3A%3Alayers%3A%3AImageBridgeChild%3A%3AShutDown%28%29&date=%3E2015-03-26 + +# shutdownhang | WaitForSingleObjectEx | WaitForSingleObject | PR_Wait | nsThread::ProcessNextEvent(bool, bool*) | NS_ProcessNextEvent(nsIThread*, bool) | mozilla::MediaShutdownManager::Shutdown() +[https://www.youtube.com/watch?v=PnwS01Yu9bs] +[https://www.youtube.com/watch?v=6hNOMhEqI9g] +[https://www.youtube.com/watch?v=gK9eCjYEwH4] +#[https://www.youtube.com/watch?v=E9DFupLEV7c] Geographic restriction +[https://www.youtube.com/watch?v=sLEVm0OGImU] +# hang | NtUserMessageCall | SendMessageW +[https://www.youtube.com/watch?v=kt0g4dWxEBo] +[https://www.youtube.com/watch?v=cvwMS6UmesQ] +[https://www.youtube.com/watch?v=Bj3YSTu3jUs] +[https://www.youtube.com/watch?v=J9bgaoXLbFI] +[https://www.youtube.com/watch?v=d5GUd6IElIw] +# shutdownhang | WaitForSingleObjectEx | WaitForSingleObject | PR_Wait | mozilla::ReentrantMonitor::Wait(unsigned int) | mozilla::layers::ImageBridgeChild::ShutDown() +[https://www.youtube.com/watch?v=6FMNFvKEy4c] +[https://www.youtube.com/watch?v=w4RNIyJw9RI] +#[https://www.youtube.com/watch?v=tKB5S1yp5MA] Account terminated +[https://www.youtube.com/watch?v=Tct2Iv1QRUU] +[https://www.youtube.com/watch?v=zDHOW9PdQYE] +# shutdownhang | WaitForSingleObjectEx | WaitForSingleObject | PR_Wait | nsThread::ProcessNextEvent(bool, bool*) | NS_ProcessNextEvent(nsIThread*, bool) | mozilla::layers::CompositorParent::ShutDown() +[https://www.youtube.com/watch?v=AGo24nC3_HU] +[https://www.youtube.com/watch?v=GsVaCnud57U] +[https://www.youtube.com/watch?v=zFg55zva7ok] +#[https://www.youtube.com/watch?v=5VSk7bwPPOM] Policy violation +[https://www.youtube.com/watch?v=2OYa5kR5EQ4] +# OOM | large | mozalloc_abort(char const* const) | mozalloc_handle_oom(unsigned int) | moz_xmalloc | nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithMemutils>::EnsureCapacity(unsigned int, unsigned int) | nsTArray_base<nsTArrayInfallibleAllo... +#[https://www.youtube.com/watch?v=1g91CAubt1c] Policy violation +[https://www.youtube.com/watch?v=HE_7UFHPfQ0] +# [https://www.youtube.com/watch?v=vhrM1JXG8-k] Live stream, Flash only +[https://www.youtube.com/watch?v=ERWFf0JS94E] +#[https://www.youtube.com/watch?v=8tmiawwVreE] Age restriction +# mozilla::layers::CompositorD3D11::UpdateConstantBuffers() +[https://www.youtube.com/watch?v=7azYa518LvE] +[https://www.youtube.com/watch?v=Zg5JvdXHUqg] +[https://www.youtube.com/watch?v=Q_kcoEY2wNw] +[https://www.youtube.com/watch?v=eNzUJa0WjfU] +[https://www.youtube.com/watch?v=B5V12xYb7hE] +# OOM | small +[https://www.youtube.com/watch?v=TS9Z8dN4OPo] +[https://www.youtube.com/watch?v=EpngdStzhmQ] +[https://www.youtube.com/watch?v=dUiDCX3BnM0] +[https://www.youtube.com/watch?v=Ii4Su6Z8pCw] +[https://www.youtube.com/watch?v=vviBJS6WQno] +# msvcr120.dll@0xf20c +[https://www.youtube.com/watch?v=hRE2VO9oa_g] +[https://www.youtube.com/watch?v=qLL8VanC3zI] +[https://www.youtube.com/watch?v=YX2LIztg2EI] +[https://www.youtube.com/watch?v=-7Eh28eatBo] +[https://www.youtube.com/watch?v=a32AMX55sZM] +# js::GCMarker::processMarkStackTop(js::SliceBudget&) +[https://www.youtube.com/watch?v=f0L2RzygE5k] +[https://www.youtube.com/watch?v=-1RGIDgwHgM] +[https://www.youtube.com/watch?v=iL1CEn7SQfQ] +[https://www.youtube.com/watch?v=450p7goxZqg] +[https://www.youtube.com/watch?v=Eo8c2sZ2eOY] +# mozilla::layers::CompositorD3D11::HandleError(long, mozilla::layers::CompositorD3D11::Severity) | mozilla::layers::CompositorD3D11::Failed(long, mozilla::layers::CompositorD3D11::Severity) | mozilla::layers::CompositorD3D11::UpdateRenderTarget() +[https://www.youtube.com/watch?v=a79R7bPhVhw] +[https://www.youtube.com/watch?v=JRNCgvZs5v4] +[https://www.youtube.com/watch?v=q8y58dWKfY8] +[https://www.youtube.com/watch?v=Ns9M6sUvqxs] +[https://www.youtube.com/watch?v=Ii-PCeTgR-A] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/medium1-60.ini b/dom/media/test/external/external_media_tests/urls/youtube/medium1-60.ini new file mode 100644 index 000000000..65ccef11a --- /dev/null +++ b/dom/media/test/external/external_media_tests/urls/youtube/medium1-60.ini @@ -0,0 +1,18 @@ +# mix of shorter/longer videos with/without ads, < 60 min +# 4:59 - can't embed +[https://www.youtube.com/watch?v=pWI8RB2dmfU] +# 0:46 ad at start +[https://www.youtube.com/embed/6SFp1z7uA6g?autoplay=1] +# 0:58 ad at start +[https://www.youtube.com/embed/Aebs62bX0dA?autoplay=1] +# 1:43 ad +[https://www.youtube.com/embed/l5ODwR6FPRQ?autoplay=1] +# 8:00 ad - can't embed +[https://www.youtube.com/watch?v=KlyXNRrsk4A] +# video with ad in beginning and in the middle 20:00 +# https://bugzilla.mozilla.org/show_bug.cgi?id=1176815 +[https://www.youtube.com/embed/cht9Xq9suGg?autoplay=1] +# 1:35 ad +[https://www.youtube.com/embed/orybDrUj4vA?autoplay=1] +# 3:02 ad +[https://www.youtube.com/embed/tDDVAErOI5U?autoplay=1] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/short1-10.ini b/dom/media/test/external/external_media_tests/urls/youtube/short1-10.ini new file mode 100644 index 000000000..a8b4016dc --- /dev/null +++ b/dom/media/test/external/external_media_tests/urls/youtube/short1-10.ini @@ -0,0 +1,13 @@ +# short videos; no ads; max 10 minutes +# 0:12 +[https://youtu.be/AbAACm1IQE0] +# 0:30 +[https://www.youtube.com/watch?v=KdHZwWQWNyM] +# 0:08 +[https://www.youtube.com/watch?v=1visYpIREUM] +# 3:27 +[https://www.youtube.com/watch?v=xcgUKzwg0Mo] +# 1:21 +[https://youtu.be/sEAT2EFIJow] +# 1:23 +[https://www.youtube.com/watch?v=SSgnbQ5UC48] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/short2-crashes-15.ini b/dom/media/test/external/external_media_tests/urls/youtube/short2-crashes-15.ini new file mode 100644 index 000000000..bfcba4101 --- /dev/null +++ b/dom/media/test/external/external_media_tests/urls/youtube/short2-crashes-15.ini @@ -0,0 +1,17 @@ +# It appears these are not currently used by tests. They are left here as they +# reference failure scenarios. If tese are fixed that can be removed. + +# crash-data videos, < 15 minutes total + +# hang | NtUserMessageCall | SendMessageW +# 5:40 +[https://www.youtube.com/watch?v=UIobdRNLNek] + +# F1398665248_____________________________ +# 3:59 +[https://www.youtube.com/watch?v=XGotQYd-X6o] + +# hang | WaitForMultipleObjectsEx | RealMsgWaitForMultipleObjectsEx | MsgWaitForMultipleObjects | F_1152915508___________________________________ +# 4:07 +[https://www.youtube.com/watch?v=wQgppPHXJSs] + diff --git a/dom/media/test/external/external_media_tests/utils.py b/dom/media/test/external/external_media_tests/utils.py new file mode 100644 index 000000000..4ac0d5f62 --- /dev/null +++ b/dom/media/test/external/external_media_tests/utils.py @@ -0,0 +1,68 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import datetime +import time +import types + +from marionette_driver.errors import TimeoutException + + +def timestamp_now(): + return int(time.mktime(datetime.datetime.now().timetuple())) + + +def verbose_until(wait, target, condition, message=""): + """ + Performs a `wait`.until(condition)` and adds information about the state of + `target` to any resulting `TimeoutException`. + + :param wait: a `marionette.Wait` instance + :param target: the object you want verbose output about if a + `TimeoutException` is raised + This is usually the input value provided to the `condition` used by + `wait`. Ideally, `target` should implement `__str__` + :param condition: callable function used by `wait.until()` + :param message: optional message to log when exception occurs + + :return: the result of `wait.until(condition)` + """ + if isinstance(condition, types.FunctionType): + name = condition.__name__ + else: + name = str(condition) + err_message = '\n'.join([message, + 'condition: ' + name, + str(target)]) + + return wait.until(condition, message=err_message) + + + +def save_memory_report(marionette): + """ + Saves memory report (like about:memory) to current working directory. + + :param marionette: Marionette instance to use for executing. + """ + with marionette.using_context('chrome'): + marionette.execute_async_script(""" + Components.utils.import("resource://gre/modules/Services.jsm"); + let Cc = Components.classes; + let Ci = Components.interfaces; + let dumper = Cc["@mozilla.org/memory-info-dumper;1"]. + getService(Ci.nsIMemoryInfoDumper); + // Examples of dirs: "CurProcD" usually 'browser' dir in + // current FF dir; "DfltDwnld" default download dir + let file = Services.dirsvc.get("CurProcD", Ci.nsIFile); + file.append("media-memory-report"); + file.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0777); + file.append("media-memory-report.json.gz"); + dumper.dumpMemoryReportsToNamedFile(file.path, null, null, false); + log('Saved memory report to ' + file.path); + // for dmd-enabled build + dumper.dumpMemoryInfoToTempDir("media", false, false); + marionetteScriptFinished(true); + return; + """, script_timeout=30000) |