1# Copyright (c) 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import operator 7import os 8import time 9 10from autotest_lib.client.bin import test 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.common_lib.cros import chrome 13from autotest_lib.client.cros.video import helper_logger 14 15 16class video_YouTubePage(test.test): 17 """The main test class of this test. 18 19 """ 20 21 22 version = 1 23 24 PSEUDO_RANDOM_TIME_1 = 20.25 25 PSEUDO_RANDOM_TIME_2 = 5.47 26 27 # Minimum number of timeupdates required to fire in the last second. 28 MIN_LAST_SECOND_UPDATES = 3 29 30 NO_DELAY = 0 31 MINIMAL_DELAY = 1 32 MAX_REBUFFER_DELAY = 10 33 34 PLAYING_STATE = 'playing' 35 PAUSED_STATE = 'paused' 36 ENDED_STATE = 'ended' 37 TEST_PAGE = 'http://web-release-qa.youtube.com/watch?v=zuzaxlddWbk&html5=1' 38 39 DISABLE_COOKIES = False 40 41 tab = None 42 43 44 def initialize_test(self, chrome, player_page): 45 """Initializes the test. 46 47 @param chrome: An Autotest Chrome instance. 48 @param player_page: The URL (string) of the YouTube player page to test. 49 50 """ 51 self.tab = chrome.browser.tabs[0] 52 53 self.tab.Navigate(player_page) 54 self.tab.WaitForDocumentReadyStateToBeComplete() 55 time.sleep(2) 56 57 with open( 58 os.path.join(os.path.dirname(__file__), 59 'files/video_YouTubePageCommon.js')) as f: 60 js = f.read() 61 if not self.tab.EvaluateJavaScript(js): 62 raise error.TestFail('YouTube page failed to load.') 63 logging.info('Loaded accompanying .js script.') 64 65 66 def get_player_state(self): 67 """Simple wrapper to get the JS player state. 68 69 @returns: The state of the player (string). 70 71 """ 72 return self.tab.EvaluateJavaScript('window.__getVideoState();') 73 74 75 def play_video(self): 76 """Simple wrapper to play the video. 77 78 """ 79 self.tab.ExecuteJavaScript('window.__playVideo();') 80 81 82 def pause_video(self): 83 """Simple wrapper to pause the video. 84 85 """ 86 self.tab.ExecuteJavaScript('window.__pauseVideo();') 87 88 89 def seek_video(self, new_time): 90 """Simple wrapper to seek the video to a new time. 91 92 @param new_time: Time to seek to. 93 94 """ 95 self.tab.ExecuteJavaScript('window.__seek(%f);' % new_time) 96 97 98 def seek_to_almost_end(self, seconds_before_end): 99 """Simple wrapper to seek to almost the end of the video. 100 101 @param seconds_before_end: How many seconds (a float, not integer) 102 before end of video. 103 104 """ 105 self.tab.ExecuteJavaScript( 106 'window.__seekToAlmostEnd(%f);' % seconds_before_end) 107 108 109 def get_current_time(self): 110 """Simple wrapper to get the current time in the video. 111 112 @returns: The current time (float). 113 114 """ 115 return self.tab.EvaluateJavaScript('window.__getCurrentTime();') 116 117 118 def assert_event_state(self, event, op, error_str): 119 """Simple wrapper to get the status of a state in the video. 120 121 @param event: A string denoting the event. Check the accompanying JS 122 file for the possible values. 123 @param op: truth or not_ operator from the standard Python operator 124 module. 125 @param error_str: A string for the error output. 126 127 @returns: Whether or not the input event has fired. 128 129 """ 130 result = self.tab.EvaluateJavaScript( 131 'window.__getEventHappened("%s");' % event) 132 if not op(result): 133 raise error.TestError(error) 134 135 136 def clear_event_state(self, event): 137 """Simple wrapper to clear the status of a state in the video. 138 139 @param event: A string denoting the event. Check the accompanying JS 140 file for the possible vlaues. 141 142 """ 143 self.tab.ExecuteJavaScript('window.__clearEventHappened("%s");' % event) 144 145 146 def verify_last_second_playback(self): 147 """Simple wrapper to check the playback of the last second. 148 149 """ 150 result = self.tab.EvaluateJavaScript( 151 'window.__getLastSecondTimeupdates()') 152 if result < self.MIN_LAST_SECOND_UPDATES: 153 raise error.TestError( 154 'Last second did not play back correctly (%d events).' % 155 result) 156 157 158 def assert_player_state(self, state, max_wait_secs): 159 """Simple wrapper to busy wait and test the current state of the player. 160 161 @param state: A string denoting the expected state of the player. 162 @param max_wait_secs: Maximum amount of time to wait before failing. 163 164 @raises: A error.TestError if the state is not as expected. 165 166 """ 167 start_time = time.time() 168 while True: 169 current_state = self.get_player_state() 170 if current_state == state: 171 return 172 elif time.time() < start_time + max_wait_secs: 173 time.sleep(0.5) 174 else: 175 raise error.TestError( 176 'Current player state "%s" is not the expected state ' 177 '"%s".' % (current_state, state)) 178 179 180 def perform_test(self): 181 """Base method for derived classes to run their test. 182 183 """ 184 raise error.TestFail('Derived class did not specify a perform_test.') 185 186 187 def perform_playing_test(self): 188 """Test to check if the YT page starts off playing. 189 190 """ 191 self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) 192 if self.get_current_time() <= 0.0: 193 raise error.TestError('perform_playing_test failed.') 194 195 196 def perform_pausing_test(self): 197 """Test to check if the video is in the 'paused' state. 198 199 """ 200 self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) 201 self.pause_video() 202 self.assert_player_state(self.PAUSED_STATE, self.MINIMAL_DELAY) 203 204 205 def perform_resuming_test(self): 206 """Test to check if the video responds to resumption. 207 208 """ 209 self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) 210 self.pause_video() 211 self.assert_player_state(self.PAUSED_STATE, self.MINIMAL_DELAY) 212 self.play_video() 213 self.assert_player_state(self.PLAYING_STATE, self.MINIMAL_DELAY) 214 215 216 def perform_seeking_test(self): 217 """Test to check if seeking works. 218 219 """ 220 # Test seeking while playing. 221 self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) 222 self.seek_video(self.PSEUDO_RANDOM_TIME_1) 223 time.sleep(self.MINIMAL_DELAY) 224 if not self.tab.EvaluateJavaScript( 225 'window.__getCurrentTime() >= %f;' % self.PSEUDO_RANDOM_TIME_1): 226 raise error.TestError( 227 'perform_seeking_test failed because player time is not ' 228 'the expected time during playing seeking.') 229 self.assert_event_state( 230 'seeking', operator.truth, 231 'perform_seeking_test failed: "seeking" state did not fire.') 232 self.assert_event_state( 233 'seeked', operator.truth, 234 'perform_seeking_test failed: "seeked" state did not fire.') 235 236 # Now make sure the video is still playing. 237 238 # Let it buffer/play for at most 10 seconds before continuing. 239 self.assert_player_state(self.PLAYING_STATE, self.MAX_REBUFFER_DELAY) 240 241 self.clear_event_state('seeking'); 242 self.clear_event_state('seeked'); 243 self.assert_event_state( 244 'seeking', operator.not_, 245 'perform_seeking_test failed: ' 246 '"seeking" state did not get cleared.') 247 self.assert_event_state( 248 'seeked', operator.not_, 249 'perform_seeking_test failed: ' 250 '"seeked" state did not get cleared.') 251 252 # Test seeking while paused. 253 self.pause_video() 254 self.assert_player_state(self.PAUSED_STATE, self.MINIMAL_DELAY) 255 256 self.seek_video(self.PSEUDO_RANDOM_TIME_2) 257 time.sleep(self.MINIMAL_DELAY) 258 if not self.tab.EvaluateJavaScript( 259 'window.__getCurrentTime() === %f;' % 260 self.PSEUDO_RANDOM_TIME_2): 261 raise error.TestError( 262 'perform_seeking_test failed because player time is not ' 263 'the expected time.') 264 self.assert_event_state( 265 'seeking', operator.truth, 266 'perform_seeking_test failed: "seeking" state did not fire ' 267 'again.') 268 self.assert_event_state( 269 'seeked', operator.truth, 270 'perform_seeking_test failed: "seeked" state did not fire ' 271 'again.') 272 273 # Make sure the video is paused. 274 self.assert_player_state(self.PAUSED_STATE, self.NO_DELAY) 275 276 277 def perform_frame_drop_test(self): 278 """Test to check if there are too many dropped frames. 279 280 """ 281 self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) 282 time.sleep(15) 283 dropped_frames_percentage = self.tab.EvaluateJavaScript( 284 'window.__videoElement.webkitDroppedFrameCount /' 285 'window.__videoElement.webkitDecodedFrameCount') 286 if dropped_frames_percentage > 0.01: 287 raise error.TestError(( 288 'perform_frame_drop_test failed due to too many dropped ' 289 'frames (%f%%)') % (dropped_frames_percentage * 100)) 290 291 292 def perform_ending_test(self): 293 """Test to check if the state is 'ended' at the end of a video. 294 295 """ 296 ALMOST_END = 0.1 297 self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) 298 self.seek_to_almost_end(ALMOST_END) 299 self.assert_player_state(self.ENDED_STATE, self.MAX_REBUFFER_DELAY) 300 301 302 def perform_last_second_test(self): 303 """Test to check if the last second is played. 304 305 """ 306 NEAR_END = 2.0 307 self.assert_player_state(self.PLAYING_STATE, self.NO_DELAY) 308 self.seek_to_almost_end(NEAR_END) 309 self.assert_player_state( 310 self.ENDED_STATE, self.MAX_REBUFFER_DELAY + NEAR_END) 311 self.verify_last_second_playback() 312 313 314 @helper_logger.video_log_wrapper 315 def run_once(self, subtest_name): 316 """Main runner for the test. 317 318 @param subtest_name: The name of the test to run, given below. 319 320 """ 321 extension_paths = [] 322 if self.DISABLE_COOKIES: 323 # To stop the system from erasing the previous profile, enable: 324 # options.dont_override_profile = True 325 extension_path = os.path.join( 326 os.path.dirname(__file__), 327 'files/cookie-disabler') 328 extension_paths.append(extension_path) 329 330 331 with chrome.Chrome( 332 extra_browser_args=helper_logger.chrome_vmodule_flag(), 333 extension_paths=extension_paths) as cr: 334 self.initialize_test(cr, self.TEST_PAGE) 335 336 if subtest_name is 'playing': 337 self.perform_playing_test() 338 elif subtest_name is 'pausing': 339 self.perform_pausing_test() 340 elif subtest_name is 'resuming': 341 self.perform_resuming_test() 342 elif subtest_name is 'seeking': 343 self.perform_seeking_test() 344 elif subtest_name is 'frame_drop': 345 self.perform_frame_drop_test() 346 elif subtest_name is 'ending': 347 self.perform_ending_test() 348 elif subtest_name is 'last_second': 349 self.perform_last_second_test() 350