blob: 4c5520292b1039770c6287305ec8d0e8f27afe45 [file] [log] [blame]
# Copyright 2017 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 time
import common
from common import TestDriver
from common import IntegrationTest
from common import ParseFlags
from decorators import Slow
from selenium.webdriver.common.by import By
class Video(IntegrationTest):
# Check videos are proxied.
def testCheckVideoHasViaHeader(self):
with TestDriver() as t:
t.AddChromeArg('--enable-spdy-proxy-auth')
t.LoadURL(
'http://check.googlezip.net/cacheable/video/buck_bunny_tiny.html')
responses = t.GetHTTPResponses()
self.assertEquals(2, len(responses))
for response in responses:
self.assertHasChromeProxyViaHeader(response)
# Videos fetched via an XHR request should not be proxied.
def testNoCompressionOnXHR(self):
with TestDriver() as t:
t.AddChromeArg('--enable-spdy-proxy-auth')
# The test will actually use Javascript, so use a site that won't have any
# resources on it that could interfere.
t.LoadURL('http://check.googlezip.net/connect')
t.ExecuteJavascript(
'var xhr = new XMLHttpRequest();'
'xhr.open("GET", "/cacheable/video/data/buck_bunny_tiny.mp4", false);'
'xhr.send();'
'return;'
)
saw_video_response = False
for response in t.GetHTTPResponses():
if 'video' in response.response_headers['content-type']:
self.assertNotHasChromeProxyViaHeader(response)
saw_video_response = True
else:
self.assertHasChromeProxyViaHeader(response)
self.assertTrue(saw_video_response, 'No video request seen in test!')
# Check the compressed video has the same frame count, width, height, and
# duration as uncompressed.
@Slow
def testVideoMetrics(self):
expected = {
'duration': 3.128,
'webkitDecodedFrameCount': 54.0,
'videoWidth': 1280.0,
'videoHeight': 720.0
}
with TestDriver() as t:
t.AddChromeArg('--enable-spdy-proxy-auth')
t.LoadURL(
'http://check.googlezip.net/cacheable/video/buck_bunny_tiny.html')
# Check request was proxied and we got a compressed video back.
for response in t.GetHTTPResponses():
self.assertHasChromeProxyViaHeader(response)
if ('content-type' in response.response_headers
and 'video' in response.response_headers['content-type']):
self.assertEqual('video/webm',
response.response_headers['content-type'])
if ParseFlags().android:
t.FindElement(By.TAG_NAME, "video").click()
else:
t.ExecuteJavascriptStatement(
'document.querySelectorAll("video")[0].play()')
# Wait for the video to finish playing, plus some headroom.
time.sleep(5)
# Check each metric against its expected value.
for metric in expected:
actual = float(t.ExecuteJavascriptStatement(
'document.querySelectorAll("video")[0].%s' % metric))
self.assertAlmostEqual(expected[metric], actual, msg="Compressed video "
"metric doesn't match expected! Metric=%s Expected=%f Actual=%f"
% (metric, expected[metric], actual), places=None, delta=0.01)
# Check that the compressed video can be seeked. Use a slow network to ensure
# the entire video isn't downloaded before we have a chance to seek.
@Slow
def testVideoSeeking(self):
with TestDriver(control_network_connection=True) as t:
t.SetNetworkConnection("2G")
t.AddChromeArg('--enable-spdy-proxy-auth')
t.LoadURL(
'http://check.googlezip.net/cacheable/video/'+
'buck_bunny_640x360_24fps.html')
# Play, pause, seek to 1s before the end, play again.
t.ExecuteJavascript(
'''
window.testDone = false;
const v = document.getElementsByTagName("video")[0];
let first = true;
v.onplaying = function() {
if (first) {
v.pause();
first = false;
} else {
window.testDone = true;
}
};
v.onpause = function() {
if (v.currentTime < v.duration) {
v.currentTime = v.duration-1;
v.play();
}
};
v.play();
''')
if ParseFlags().android:
# v.play() won't work on Android, so give it a click instead.
t.FindElement(By.TAG_NAME, "video").click()
t.WaitForJavascriptExpression('window.testDone', 15)
# Check request was proxied and we got a compressed video back.
# We expect to make multiple requests for the video: ensure they
# all have the same ETag.
video_etag = None
num_partial_requests = 0
for response in t.GetHTTPResponses():
self.assertHasChromeProxyViaHeader(response)
rh = response.response_headers
if ('content-type' in rh and 'video' in rh['content-type']):
self.assertTrue('etag' in rh),
self.assertEqual('video/webm', rh['content-type'])
if video_etag == None:
video_etag = rh['etag']
else:
self.assertEqual(video_etag, rh['etag'])
if ('range' in response.request_headers and
response.request_headers['range'] != 'bytes=0-'):
num_partial_requests += 1
# Also make sure that we had at least one partial Range request.
self.assertGreaterEqual(num_partial_requests, 1)
# Check the frames of a compressed video.
@Slow
def testVideoFrames(self):
self.instrumentedVideoTest('http://check.googlezip.net/cacheable/video/buck_bunny_640x360_24fps_video.html')
# Check the audio volume of a compressed video.
#
# This test makes some assumptions about the way audio is decoded and
# processed in JavaScript on different platforms. Despite getting the same
# video bytes from the proxy across all platforms, different data is generated
# out of the window.AudioContext object. As of May 2017, there were only two
# known datasets, the second occuring on all tested Android devices. If this
# test fails on a new or different platform, examine whether the expected data
# is drastically different. See crbug.com/723031 for more information.
@Slow
def testVideoAudio(self):
alt_data = None
is_android = ParseFlags().android
if is_android:
alt_data = 'data/buck_bunny_640x360_24fps.mp4.expected_volume_alt.json'
self.instrumentedVideoTest('http://check.googlezip.net/cacheable/video/buck_bunny_640x360_24fps_audio.html',
alt_data=alt_data, needs_click=is_android)
def instrumentedVideoTest(self, url, alt_data=None, needs_click=False):
"""Run an instrumented video test. The given page is reloaded up to some
maximum number of times until a compressed video is seen by ChromeDriver by
inspecting the network logs. Once that happens, test.ready is set and that
will signal the Javascript test on the page to begin. Once it is complete,
check the results.
"""
# The maximum number of times to attempt to reload the page for a compressed
# video.
max_attempts = 10
with TestDriver() as t:
t.AddChromeArg('--enable-spdy-proxy-auth')
loaded_compressed_video = False
attempts = 0
while not loaded_compressed_video and attempts < max_attempts:
t.LoadURL(url)
attempts += 1
for resp in t.GetHTTPResponses():
if ('content-type' in resp.response_headers
and resp.response_headers['content-type'] == 'video/webm'):
loaded_compressed_video = True
self.assertHasChromeProxyViaHeader(resp)
else:
# Take a breath before requesting again.
time.sleep(1)
if attempts >= max_attempts:
self.fail('Could not get a compressed video after %d tries' % attempts)
if alt_data != None:
t.ExecuteJavascriptStatement('test.expectedVolumeSrc = "%s"' % alt_data)
t.ExecuteJavascriptStatement('test.ready = true')
if needs_click:
t.FindElement(By.ID, 'video').click()
waitTimeQuery = 'test.waitTime'
if ParseFlags().android:
waitTimeQuery = 'test.androidWaitTime'
wait_time = int(t.ExecuteJavascriptStatement(waitTimeQuery))
t.WaitForJavascriptExpression('test.metrics.complete', wait_time)
metrics = t.ExecuteJavascriptStatement('test.metrics')
if not metrics['complete']:
self.fail('Test not complete after %d seconds.' % wait_time)
if metrics['failed']:
self.fail('Test failed! ' + metrics['detailedStatus'])
# Make sure YouTube autoplays.
def testYoutube(self):
with TestDriver() as t:
t.AddChromeArg('--enable-spdy-proxy-auth')
t.LoadURL('http://data-saver-test.appspot.com/youtube')
if ParseFlags().android:
# Video won't auto play on Android, so give it a click.
t.FindElement(By.ID, 'player').click()
t.WaitForJavascriptExpression(
'window.playerState == YT.PlayerState.PLAYING', 30)
for response in t.GetHTTPResponses():
if not response.url.startswith('https'):
self.assertHasChromeProxyViaHeader(response)
if __name__ == '__main__':
IntegrationTest.RunAllTests()