Commit 3aa872b6 authored by kbr's avatar kbr Committed by Commit bot

Port context_lost test to new GpuIntegrationTest harness.

The main trick here is to get hand-written tests to trampoline through
the _RunGpuTest harness, which is where test expectations are
implemented.

This is done by adopting a convention that the "real" test, like
"_GPUProcessCrashesExactlyOnce", is prefixed with an underscore. This
is implemented in self-contained fashion in
ContextLostIntegrationTest.GenerateGpuTests. If necessary, this will
be generalized further into a base class.

The waterfalls will be switched to run the new version of the test in
a follow-on CL.

BUG=352807
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel

Review-Url: https://codereview.chromium.org/2271543002
Cr-Commit-Position: refs/heads/master@{#414279}
parent e211f36d
# Copyright 2016 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 os
import time
from gpu_tests import gpu_integration_test
from gpu_tests import context_lost_expectations
from gpu_tests import path_util
from telemetry.core import exceptions
from telemetry.core import util
data_path = os.path.join(
path_util.GetChromiumSrcDir(), 'content', 'test', 'data', 'gpu')
wait_timeout = 60 # seconds
harness_script = r"""
var domAutomationController = {};
domAutomationController._loaded = false;
domAutomationController._succeeded = false;
domAutomationController._finished = false;
domAutomationController.setAutomationId = function(id) {}
domAutomationController.send = function(msg) {
msg = msg.toLowerCase()
if (msg == "loaded") {
domAutomationController._loaded = true;
} else if (msg == "success") {
/* Don't squelch earlier failures! */
if (!domAutomationController._finished) {
domAutomationController._succeeded = true;
}
domAutomationController._finished = true;
} else {
/* Always record failures. */
domAutomationController._succeeded = false;
domAutomationController._finished = true;
}
}
domAutomationController.reset = function() {
domAutomationController._succeeded = false;
domAutomationController._finished = false;
}
window.domAutomationController = domAutomationController;
console.log("Harness injected.");
"""
class ContextLostIntegrationTest(gpu_integration_test.GpuIntegrationTest):
@classmethod
def Name(cls):
return 'context_lost'
@classmethod
def CustomizeOptions(cls):
options = cls._finder_options.browser_options
options.AppendExtraBrowserArgs(
'--disable-domain-blocking-for-3d-apis')
options.AppendExtraBrowserArgs(
'--disable-gpu-process-crash-limit')
# Required for about:gpucrash handling from Telemetry.
options.AppendExtraBrowserArgs('--enable-gpu-benchmarking')
@classmethod
def GenerateGpuTests(cls, options):
tests = (('GPUProcessCrashesExactlyOncePerVisitToAboutGpuCrash',
'gpu_process_crash.html'),
('WebGLContextLostFromGPUProcessExit',
'webgl.html?query=kill_after_notification'),
('WebGLContextLostFromLoseContextExtension',
'webgl.html?query=WEBGL_lose_context'),
('WebGLContextLostFromQuantity',
'webgl.html?query=forced_quantity_loss'),
('WebGLContextLostFromSelectElement',
'webgl_with_select_element.html'),
('WebGLContextLostInHiddenTab',
'webgl.html?query=kill_after_notification'))
for t in tests:
yield (t[0], t[1], ('_' + t[0]))
def RunActualGpuTest(self, test_path, *args):
test_name = args[0]
tab = self.tab
if not tab.browser.supports_tab_control:
self.fail('Browser must support tab control')
getattr(self, test_name)(test_path)
@classmethod
def _CreateExpectations(cls):
return context_lost_expectations.ContextLostExpectations()
@classmethod
def setUpClass(cls):
super(cls, ContextLostIntegrationTest).setUpClass()
cls.CustomizeOptions()
cls.SetBrowserOptions(cls._finder_options)
cls.StartBrowser()
cls.SetStaticServerDir(data_path)
def _WaitForPageToFinish(self, tab):
try:
util.WaitFor(lambda: tab.EvaluateJavaScript(
'window.domAutomationController._finished'), wait_timeout)
return True
except exceptions.TimeoutException:
return False
def _KillGPUProcess(self, number_of_gpu_process_kills,
check_crash_count):
tab = self.tab
# Doing the GPU process kill operation cooperatively -- in the
# same page's context -- is much more stressful than restarting
# the browser every time.
for x in range(number_of_gpu_process_kills):
expected_kills = x + 1
# Reset the test's state.
tab.EvaluateJavaScript(
'window.domAutomationController.reset()')
# If we're running the GPU process crash test, we need the test
# to have fully reset before crashing the GPU process.
if check_crash_count:
util.WaitFor(lambda: tab.EvaluateJavaScript(
'window.domAutomationController._finished'), wait_timeout)
# Crash the GPU process.
gpucrash_tab = tab.browser.tabs.New()
# To access these debug URLs from Telemetry, they have to be
# written using the chrome:// scheme.
# The try/except is a workaround for crbug.com/368107.
try:
gpucrash_tab.Navigate('chrome://gpucrash')
except Exception:
print 'Tab crashed while navigating to chrome://gpucrash'
# Activate the original tab and wait for completion.
tab.Activate()
completed = self._WaitForPageToFinish(tab)
if check_crash_count:
self._CheckCrashCount(tab, expected_kills)
# The try/except is a workaround for crbug.com/368107.
try:
gpucrash_tab.Close()
except Exception:
print 'Tab crashed while closing chrome://gpucrash'
if not completed:
self.fail('Test didn\'t complete (no context lost event?)')
if not tab.EvaluateJavaScript(
'window.domAutomationController._succeeded'):
self.fail('Test failed (context not restored properly?)')
def _CheckCrashCount(self, tab, expected_kills):
if not tab.browser.supports_system_info:
self.fail('Browser must support system info')
if not tab.EvaluateJavaScript(
'window.domAutomationController._succeeded'):
self.fail('Test failed (didn\'t render content properly?)')
number_of_crashes = -1
# To allow time for a gpucrash to complete, wait up to 20s,
# polling repeatedly.
start_time = time.time()
current_time = time.time()
while current_time - start_time < 20:
system_info = tab.browser.GetSystemInfo()
number_of_crashes = \
system_info.gpu.aux_attributes[u'process_crash_count']
if number_of_crashes >= expected_kills:
break
time.sleep(1)
current_time = time.time()
# Wait 5 more seconds and re-read process_crash_count, in
# attempt to catch latent process crashes.
time.sleep(5)
system_info = tab.browser.GetSystemInfo()
number_of_crashes = \
system_info.gpu.aux_attributes[u'process_crash_count']
if number_of_crashes < expected_kills:
self.fail('Timed out waiting for a gpu process crash')
elif number_of_crashes != expected_kills:
self.fail('Expected %d gpu process crashes; got: %d' %
(expected_kills, number_of_crashes))
def _NavigateAndWaitForLoad(self, test_path):
url = self.UrlOfStaticFilePath(test_path)
tab = self.tab
tab.Navigate(url, script_to_evaluate_on_commit=harness_script)
tab.action_runner.WaitForJavaScriptCondition(
'window.domAutomationController._loaded')
def _WaitForTabAndCheckCompletion(self):
tab = self.tab
completed = self._WaitForPageToFinish(tab)
if not completed:
self.fail('Test didn\'t complete (no context restored event?)')
if not tab.EvaluateJavaScript('window.domAutomationController._succeeded'):
self.fail('Test failed (context not restored properly?)')
# The browser test runner synthesizes methods with the exact name
# given in GenerateGpuTests, so in order to hand-write our tests but
# also go through the _RunGpuTest trampoline, the test needs to be
# slightly differently named.
def _GPUProcessCrashesExactlyOncePerVisitToAboutGpuCrash(self, test_path):
self._NavigateAndWaitForLoad(test_path)
self._KillGPUProcess(2, True)
self._RestartBrowser('must restart after tests that kill the GPU process')
def _WebGLContextLostFromGPUProcessExit(self, test_path):
self._NavigateAndWaitForLoad(test_path)
self._KillGPUProcess(1, False)
self._RestartBrowser('must restart after tests that kill the GPU process')
def _WebGLContextLostFromLoseContextExtension(self, test_path):
url = self.UrlOfStaticFilePath(test_path)
tab = self.tab
tab.Navigate(url, script_to_evaluate_on_commit=harness_script)
tab.action_runner.WaitForJavaScriptCondition(
'window.domAutomationController._finished')
def _WebGLContextLostFromQuantity(self, test_path):
self._NavigateAndWaitForLoad(test_path)
# Try to coerce GC to clean up any contexts not attached to the page.
# This method seems unreliable, so the page will also attempt to
# force GC through excessive allocations.
self.tab.CollectGarbage()
self._WaitForTabAndCheckCompletion()
def _WebGLContextLostFromSelectElement(self, test_path):
self._NavigateAndWaitForLoad(test_path)
self._WaitForTabAndCheckCompletion()
def _WebGLContextLostInHiddenTab(self, test_path):
self._NavigateAndWaitForLoad(test_path)
# Test losing a context in a hidden tab. This test passes if the tab
# doesn't crash.
tab = self.tab
dummy_tab = tab.browser.tabs.New()
tab.EvaluateJavaScript('loseContextUsingExtension()')
tab.Activate()
self._WaitForTabAndCheckCompletion()
......@@ -58,7 +58,7 @@ class GpuIntegrationTest(
self.StartBrowser()
self.tab = self.browser.tabs[0]
def _RunGpuTest(self, url, test_name, args):
def _RunGpuTest(self, url, test_name, *args):
temp_page = _EmulatedPage(url, test_name)
expectations = self.__class__.GetExpectations()
expectation = expectations.GetExpectationForPage(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment