# Copyright (c) 2013 The Chromium OS 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, time
from autotest_lib.client.bin import utils
from autotest_lib.client.common_lib import error
from autotest_lib.client.cros.graphics import graphics_utils
PLAYBACK_TEST_TIME_S = 10
PLAYER_ENDED_STATE = 'Ended'
PLAYER_PAUSE_STATE = 'Paused'
PLAYER_PLAYING_STATE = 'Playing'
WAIT_TIMEOUT_S = 20
class YouTubeHelper(object):
"""A helper class contains YouTube related utility functions.
To use this class, please call wait_for_player_state(playing) as below
before calling set_video_duration. Please note that set_video_duration
must be called in order to access few functions which uses the video
length member variable.
yh = youtube_helper.YouTubeHelper(tab)
yh.wait_for_player_state(PLAYER_PLAYING_STATE)
yh.set_video_duration()
"""
def __init__(self, youtube_tab):
self._tab = youtube_tab
self._video_duration = 0
def set_video_duration(self):
"""Sets the video duration."""
self._video_duration = (int(self._tab.EvaluateJavaScript(
'player.getDuration()')))
def video_current_time(self):
"""Returns video's current playback time.
Returns:
returns the current playback location in seconds (int).
"""
return int(self._tab.EvaluateJavaScript('player.getCurrentTime()'))
def get_player_status(self):
"""Returns the player status."""
return self._tab.EvaluateJavaScript(
'(typeof playerStatus !== \'undefined\') && '
'playerStatus.innerHTML')
def set_playback_quality(self, quality):
"""Set the video quality to the quality passed in the arg.
@param quality: video quality to set.
"""
self._tab.ExecuteJavaScript(
'player.setPlaybackQuality("%s")' % quality)
def get_playback_quality(self):
"""Returns the playback quality."""
return self._tab.EvaluateJavaScript('player.getPlaybackQuality()')
def wait_for_player_state(self, expected_status):
"""Wait till the player status changes to expected_status.
If the status doesn't change for long, the test will time out after
WAIT_TIMEOUT_S and fails.
@param expected_status: status which is expected for the test
to continue.
"""
utils.poll_for_condition(
lambda: self.get_player_status() == expected_status,
exception=error.TestError(
'Video failed to load. Player expected status: %s'
' and current status: %s.'
% (expected_status, self.get_player_status())),
timeout=WAIT_TIMEOUT_S,
sleep_interval=1)
def verify_video_playback(self):
"""Verify the video playback."""
logging.info('Verifying the YouTube video playback.')
playback = 0 # seconds
prev_playback = 0
count = 0
while (self.video_current_time() < self._video_duration
and playback < PLAYBACK_TEST_TIME_S):
time.sleep(1)
if self.video_current_time() <= prev_playback:
if count < 2:
logging.info('Retrying to video playback test.')
self._tab.ExecuteJavaScript(
'player.seekTo(%d, true)'
% (self.video_current_time() + 2))
time.sleep(1)
count = count + 1
else:
player_status = self.get_player_status()
raise error.TestError(
'Video is not playing. Player status: %s.' %
player_status)
prev_playback = self.video_current_time()
playback = playback + 1
def wait_for_expected_resolution(self, expected_quality):
"""Wait for some time for getting expected resolution.
@param expected_quality: quality to be set.
"""
for _ in range(WAIT_TIMEOUT_S):
dummy_quality = self.get_playback_quality()
if dummy_quality == expected_quality:
logging.info('Expected resolution is set.')
break
else:
logging.info('Waiting for expected resolution.')
time.sleep(0.1)
def verify_video_resolutions(self):
"""Verify available video resolutions.
Video resolution should be 360p, 480p, 720p and 1080p.
"""
logging.info('Verifying the video resolutions.')
video_qualities = self._tab.EvaluateJavaScript(
'player.getAvailableQualityLevels()')
logging.info('Available video resolutions: %s', video_qualities)
if not video_qualities:
raise error.TestError(
'Player failed to return available video qualities.')
video_qualities.reverse()
# Removing 'auto' resolution since it tends to interfere with explicit
# bandwidth selection later.
video_qualities.remove('auto')
width, height = graphics_utils.get_internal_resolution()
logging.info('checking resolution: %d width %d height', width, height)
for quality in video_qualities:
logging.info('Playing video in %s quality.', quality)
self.set_playback_quality(quality)
self.wait_for_player_state(PLAYER_PLAYING_STATE)
self.wait_for_expected_resolution(quality)
current_quality = self.get_playback_quality()
if (quality not in ['auto', 'tiny', 'small'] and
quality != current_quality):
if current_quality in ['hd720', 'hd1080'] and width == 2560:
logging.info('HD devices starts playing YouTube video in '
'HD so skipping 480p test.')
continue
raise error.TestError(
'Expected video quality: %s. Current video quality: %s'
% (quality, current_quality))
time.sleep(1)
# setting the video resolution to 480p for rest of the tests.
self.set_playback_quality('large')
def verify_player_states(self):
"""Verify the player states like play, pause, ended and seek."""
logging.info('Verifying the player states.')
self._tab.ExecuteJavaScript('player.pauseVideo()')
self.wait_for_player_state(PLAYER_PAUSE_STATE)
self._tab.ExecuteJavaScript('player.playVideo()')
self.wait_for_player_state(PLAYER_PLAYING_STATE)
# We are seeking the player position to (video length - 2 seconds).
# Since the player waits for WAIT_TIMEOUT_S for the status change,
# the video should be ended before we hit the timeout.
video_end_test_duration = (self._video_duration -
self.video_current_time() - 2)
if video_end_test_duration >= WAIT_TIMEOUT_S:
self._tab.ExecuteJavaScript(
'player.seekTo(%d, true)' % (self._video_duration - 5))
self.wait_for_player_state(PLAYER_ENDED_STATE)
else:
raise error.TestError(
'Test video is not long enough for the video end test.')
# Verifying seek back from the end position.
self._tab.ExecuteJavaScript('player.seekTo(%d, true)'
% (self._video_duration / 2))
self.wait_for_player_state(PLAYER_PLAYING_STATE)
# So the playback doesn't stay at the mid.
seek_test = False
for _ in range(WAIT_TIMEOUT_S):
logging.info('Waiting for seek position to change.')
time.sleep(1)
seek_position = self.video_current_time()
if (seek_position > self._video_duration / 2
and seek_position < self._video_duration):
seek_test = True
break
if not seek_test:
raise error.TestError(
'Seek location is wrong. '
'Video length: %d, seek position: %d.' %
(self._video_duration, seek_position))