# 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. import logging import operator import os import time from autotest_lib.client.bin import test from autotest_lib.client.common_lib import error from autotest_lib.client.common_lib.cros import chrome class video_YouTubePage(test.test): """The main test class of this test. """ version = 1 PSEUDO_RANDOM_TIME_1 = 20.25 PSEUDO_RANDOM_TIME_2 = 5.47 # Minimum number of timeupdates required to fire in the last second. MIN_LAST_SECOND_UPDATES = 3 NO_DELAY = 0 MINIMAL_DELAY = 1 MAX_REBUFFER_DELAY = 10 PLAYING_STATE = 'playing' PAUSED_STATE = 'paused' ENDED_STATE = 'ended' TEST_PAGE = 'http://web-release-qa.youtube.com/watch?v=zuzaxlddWbk&html5=1' DISABLE_COOKIES = False tab = None def initialize_test(self, chrome, player_page): """Initializes the test. @param chrome: An Autotest Chrome instance. @param player_page: The URL (string) of the YouTube player page to test. """ self.tab = chrome.browser.tabs[0] self.tab.Navigate(player_page) self.tab.WaitForDocumentReadyStateToBeComplete() time.sleep(2) with open( os.path.join(os.path.dirname(__file__), 'files/video_YouTubePageCommon.js')) as f: js = f.read() if not self.tab.EvaluateJavaScript(js): raise error.TestFail('YouTube page failed to load.') logging.info('Loaded accompanying .js script.') def get_player_state(self): """Simple wrapper to get the JS player state. @returns: The state of the player (string). """ return self.tab.EvaluateJavaScript('window.__getVideoState();') def play_video(self): """Simple wrapper to play the video. """ self.tab.ExecuteJavaScript('window.__playVideo();') def pause_video(self): """Simple wrapper to pause the video. """ self.tab.ExecuteJavaScript('window.__pauseVideo();') def seek_video(self, new_time): """Simple wrapper to seek the video to a new time. @param new_time: Time to seek to. """ self.tab.ExecuteJavaScript('window.__seek(%f);' % new_time) def seek_to_almost_end(self, seconds_before_end): """Simple wrapper to seek to almost the end of the video. @param seconds_before_end: How many seconds (a float, not integer) before end of video. """ self.tab.ExecuteJavaScript( 'window.__seekToAlmostEnd(%f);' % seconds_before_end) def get_current_time(self): """Simple wrapper to get the current time in the video. @returns: The current time (float). """ return self.tab.EvaluateJavaScript('window.__getCurrentTime();') def assert_event_state(self, event, op, error_str): """Simple wrapper to get the status of a state in the video. @param event: A string denoting the event. Check the accompanying JS file for the possible values. @param op: truth or not_ operator from the standard Python operator module. @param error_str: A string for the error output. @returns: Whether or not the input event has fired. """ result = self.tab.EvaluateJavaScript( 'window.__getEventHappened("%s");' % event) if not op(result): raise error.TestError(error) def clear_event_state(self, event): """Simple wrapper to clear the status of a state in the video. @param event: A string denoting the event. Check the accompanying JS file for the possible vlaues. """ self.tab.ExecuteJavaScript('window.__clearEventHappened("%s");' % event) def verify_last_second_playback(self): """Simple wrapper to check the playback of the last second. """ result = self.tab.EvaluateJavaScript( 'window.__getLastSecondTimeupdates()') if result < self.MIN_LAST_SECOND_UPDATES: raise error.TestError( 'Last second did not play back correctly (%d events).' % result) def assert_player_state(self, state, max_wait_secs): """Simple wrapper to busy wait and test the current state of the player. @param state: A string denoting the expected state of the player. @param max_wait_secs: Maximum amount of time to wait before failing. @raises: A error.TestError if the state is not as expected. """ start_time = time.time() while True: current_state = self.get_player_state() if current_state == state: return elif time.time() < start_time + max_wait_secs: time.sleep(0.5) else: raise error.TestError( 'Current player state "%s" is not the expected state ' '"%s".' % (current_state, state)) def perform_test(self): """Base method for derived classes to run their test. """ raise error.TestFail('Derived class did not specify a perform_test.') def perform_playing_test(self): """Test to check if the YT page starts off playing. """ self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) if self.get_current_time() <= 0.0: raise error.TestError('perform_playing_test failed.') def perform_pausing_test(self): """Test to check if the video is in the 'paused' state. """ self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) self.pause_video() self.assert_player_state(self.PAUSED_STATE, self.MINIMAL_DELAY) def perform_resuming_test(self): """Test to check if the video responds to resumption. """ self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) self.pause_video() self.assert_player_state(self.PAUSED_STATE, self.MINIMAL_DELAY) self.play_video() self.assert_player_state(self.PLAYING_STATE, self.MINIMAL_DELAY) def perform_seeking_test(self): """Test to check if seeking works. """ # Test seeking while playing. self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) self.seek_video(self.PSEUDO_RANDOM_TIME_1) time.sleep(self.MINIMAL_DELAY) if not self.tab.EvaluateJavaScript( 'window.__getCurrentTime() >= %f;' % self.PSEUDO_RANDOM_TIME_1): raise error.TestError( 'perform_seeking_test failed because player time is not ' 'the expected time during playing seeking.') self.assert_event_state( 'seeking', operator.truth, 'perform_seeking_test failed: "seeking" state did not fire.') self.assert_event_state( 'seeked', operator.truth, 'perform_seeking_test failed: "seeked" state did not fire.') # Now make sure the video is still playing. # Let it buffer/play for at most 10 seconds before continuing. self.assert_player_state(self.PLAYING_STATE, self.MAX_REBUFFER_DELAY) self.clear_event_state('seeking'); self.clear_event_state('seeked'); self.assert_event_state( 'seeking', operator.not_, 'perform_seeking_test failed: ' '"seeking" state did not get cleared.') self.assert_event_state( 'seeked', operator.not_, 'perform_seeking_test failed: ' '"seeked" state did not get cleared.') # Test seeking while paused. self.pause_video() self.assert_player_state(self.PAUSED_STATE, self.MINIMAL_DELAY) self.seek_video(self.PSEUDO_RANDOM_TIME_2) time.sleep(self.MINIMAL_DELAY) if not self.tab.EvaluateJavaScript( 'window.__getCurrentTime() === %f;' % self.PSEUDO_RANDOM_TIME_2): raise error.TestError( 'perform_seeking_test failed because player time is not ' 'the expected time.') self.assert_event_state( 'seeking', operator.truth, 'perform_seeking_test failed: "seeking" state did not fire ' 'again.') self.assert_event_state( 'seeked', operator.truth, 'perform_seeking_test failed: "seeked" state did not fire ' 'again.') # Make sure the video is paused. self.assert_player_state(self.PAUSED_STATE, self.NO_DELAY) def perform_frame_drop_test(self): """Test to check if there are too many dropped frames. """ self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) time.sleep(15) dropped_frames_percentage = self.tab.EvaluateJavaScript( 'window.__videoElement.webkitDroppedFrameCount /' 'window.__videoElement.webkitDecodedFrameCount') if dropped_frames_percentage > 0.01: raise error.TestError(( 'perform_frame_drop_test failed due to too many dropped ' 'frames (%f%%)') % (dropped_frames_percentage * 100)) def perform_ending_test(self): """Test to check if the state is 'ended' at the end of a video. """ ALMOST_END = 0.1 self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) self.seek_to_almost_end(ALMOST_END) self.assert_player_state(self.ENDED_STATE, self.MAX_REBUFFER_DELAY) def perform_last_second_test(self): """Test to check if the last second is played. """ NEAR_END = 2.0 self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) self.seek_to_almost_end(NEAR_END) self.assert_player_state( self.ENDED_STATE, self.MAX_REBUFFER_DELAY + NEAR_END) self.verify_last_second_playback() def run_once(self, subtest_name): """Main runner for the test. @param subtest_name: The name of the test to run, given below. """ extension_paths = [] if self.DISABLE_COOKIES: # To stop the system from erasing the previous profile, enable: # options.dont_override_profile = True extension_path = os.path.join( os.path.dirname(__file__), 'files/cookie-disabler') extension_paths.append(extension_path) with chrome.Chrome(extension_paths=extension_paths) as cr: self.initialize_test(cr, self.TEST_PAGE) if subtest_name is 'playing': self.perform_playing_test() elif subtest_name is 'pausing': self.perform_pausing_test() elif subtest_name is 'resuming': self.perform_resuming_test() elif subtest_name is 'seeking': self.perform_seeking_test() elif subtest_name is 'frame_drop': self.perform_frame_drop_test() elif subtest_name is 'ending': self.perform_ending_test() elif subtest_name is 'last_second': self.perform_last_second_test()