Commit 040db4f4 authored by jdduke's avatar jdduke Committed by Commit bot

Add key_idle_power_cases for ensuring idle activity on Android

Add a key_idle_power_cases pageset for monitoring activity for static
pages in the foreground and dynamic pages in the background. All such
cases should incur minimal recurring activity, monitored using a
new thread_times.key_power_cases benchmark.

BUG=466213,470147
CQ_EXTRA_TRYBOTS=tryserver.chromium.perf:linux_perf_bisect;tryserver.chromium.perf:mac_perf_bisect;tryserver.chromium.perf:win_perf_bisect;tryserver.chromium.perf:android_nexus5_perf_bisect

Review URL: https://codereview.chromium.org/1097463004

Cr-Commit-Position: refs/heads/master@{#330132}
parent 8959c37c
...@@ -94,3 +94,17 @@ class ThreadTimesPolymer(_ThreadTimes): ...@@ -94,3 +94,17 @@ class ThreadTimesPolymer(_ThreadTimes):
def Name(cls): def Name(cls):
return 'thread_times.polymer' return 'thread_times.polymer'
@benchmark.Enabled('android')
class ThreadTimesKeyIdlePowerCases(_ThreadTimes):
"""Measures timeline metrics for sites that should be idle in foreground
and background scenarios. The metrics are per-second rather than per-frame."""
page_set = page_sets.KeyIdlePowerCasesPageSet
@classmethod
def Name(cls):
return 'thread_times.key_idle_power_cases'
@classmethod
def ValueCanBeAddedPredicate(cls, value, _):
# Only report per-second metrics.
return 'per_frame' not in value.name and 'mean_frame' not in value.name
# Copyright 2015 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
from telemetry.page import shared_page_state
class AndroidScreenRestorationSharedState(shared_page_state.SharedPageState):
""" Ensures the screen is on before and after each user story is run. """
def WillRunUserStory(self, page):
super(AndroidScreenRestorationSharedState, self).WillRunUserStory(page)
self._EnsureScreenOn()
def DidRunUserStory(self, results):
try:
super(AndroidScreenRestorationSharedState, self).DidRunUserStory(results)
finally:
self._EnsureScreenOn()
def CanRunOnBrowser(self, browser_info):
if not browser_info.browser_type.startswith('android'):
logging.warning('Browser is non-Android, skipping test')
return False
return True
def _EnsureScreenOn(self):
self.platform.android_action_runner.EnsureScreenOn()
# Copyright 2015 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.
from page_sets import android_screen_restoration_shared_state
from telemetry.page import page as page_module
from telemetry.page import page_set as page_set_module
class KeyIdlePowerPage(page_module.Page):
def __init__(self, url, page_set, turn_screen_off):
super(KeyIdlePowerPage, self).__init__(
url=url,
page_set=page_set,
shared_page_state_class=(android_screen_restoration_shared_state
.AndroidScreenRestorationSharedState))
self.user_agent_type = 'mobile'
self._turn_screen_off = turn_screen_off
def RunNavigateSteps(self, action_runner):
super(KeyIdlePowerPage, self).RunNavigateSteps(action_runner)
action_runner.Wait(2)
if self._turn_screen_off:
# TODO(jdduke): Remove this API violation after the shared page state is
# exposed here, crbug.com/470147.
# pylint: disable=protected-access
action_runner._tab.browser.platform.android_action_runner.TurnScreenOff()
# We're not interested in tracking activity that occurs immediately after
# the screen is turned off. Several seconds should be enough time for the
# browser to "settle down" into an idle state.
action_runner.Wait(2)
def RunPageInteractions(self, action_runner):
# The page interaction is simply waiting in an idle state.
action_runner.Wait(20)
class KeyIdlePowerCasesPageSet(page_set_module.PageSet):
""" Key idle power cases """
def __init__(self):
super(KeyIdlePowerCasesPageSet, self).__init__(user_agent_type='mobile')
foreground_urls_list = [
# Why: Ensure minimal activity for static, empty pages in the foreground.
'file://key_idle_power_cases/blank.html',
]
for url in foreground_urls_list:
self.AddUserStory(KeyIdlePowerPage(url, self, False))
background_urls_list = [
# Why: Ensure animated GIFs aren't processed when Chrome is backgrounded.
'file://key_idle_power_cases/animated-gif.html',
# Why: Ensure CSS animations aren't processed when Chrome is backgrounded.
'file://key_idle_power_cases/css-animation.html',
# Why: Ensure rAF is suppressed when Chrome is backgrounded.
'file://key_idle_power_cases/request-animation-frame.html',
# Why: Ensure setTimeout is throttled when Chrome is backgrounded.
'file://key_idle_power_cases/set-timeout.html',
]
for url in background_urls_list:
self.AddUserStory(KeyIdlePowerPage(url, self, True))
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Animated GIF Test</title>
</head>
<body>
<img src="animated-10color.gif">
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Blank Test</title>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>CSS Animation Test</title>
<meta name="viewport" content="initial-scale=0.60, minimum-scale=0.60, maximum-scale=0.60">
<style type="text/css">
#stage {
margin: 150px auto;
width: 600px;
height: 400px;
/*
Setting the perspective of the contents of the stage
but not the stage itself
*/
-webkit-perspective: 800;
}
#rotate {
margin: 0 auto;
width: 600px;
height: 400px;
/* Ensure that we're in 3D space */
-webkit-transform-style: preserve-3d;
/*
Make the whole set of rows use the x-axis spin animation
for a duration of 7 seconds, running infinitely and linearly
*/
-webkit-animation-name: x-spin;
-webkit-animation-duration: 7s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
}
.ring {
margin: 0 auto;
height: 110px;
width: 600px;
-webkit-transform-style: preserve-3d;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
}
.ring > :nth-child(odd) {
background-color: #995C7F;
}
.ring > :nth-child(even) {
background-color: #835A99;
}
.poster {
position: absolute;
left: 250px;
width: 100px;
height: 100px;
opacity: 0.7;
color: rgba(0,0,0,0.9);
-webkit-border-radius: 10px;
}
.poster > p {
font-family: 'Georgia', serif;
font-size: 36px;
font-weight: bold;
text-align: center;
margin-top: 28px;
}
/*
Set up each row to have a different animation duration
and alternating y-axis rotation directions.
*/
#ring-1 {
-webkit-animation-name: y-spin;
-webkit-animation-duration: 5s;
}
#ring-2 {
-webkit-animation-name: back-y-spin;
-webkit-animation-duration: 4s;
}
#ring-3 {
-webkit-animation-name: y-spin;
-webkit-animation-duration: 3s;
}
/*
Here we define each of the three individual animations that
we will be using to have our 3D rotation effect. The first
animation will perform a full rotation on the x-axis, we'll
use that on the whole set of objects. The second and third
animations will perform a full rotation on the y-axis in
opposite directions, alternating directions between rows.
Note that you currently have to specify an intermediate step
for rotations even when you are using individual transformation
constructs.
*/
@-webkit-keyframes x-spin {
0% { -webkit-transform: rotateX(0deg); }
50% { -webkit-transform: rotateX(180deg); }
100% { -webkit-transform: rotateX(360deg); }
}
@-webkit-keyframes y-spin {
0% { -webkit-transform: rotateY(0deg); }
50% { -webkit-transform: rotateY(180deg); }
100% { -webkit-transform: rotateY(360deg); }
}
@-webkit-keyframes back-y-spin {
0% { -webkit-transform: rotateY(360deg); }
50% { -webkit-transform: rotateY(180deg); }
100% { -webkit-transform: rotateY(0deg); }
}
</style>
<script type="text/javascript">
const POSTERS_PER_ROW = 12;
const RING_RADIUS = 200;
function setup_posters (row)
{
var posterAngle = 360 / POSTERS_PER_ROW;
for (var i = 0; i < POSTERS_PER_ROW; i ++) {
var poster = document.createElement('div');
poster.className = 'poster';
// compute and assign the transform for this poster
var transform = 'rotateY(' + (posterAngle * i) + 'deg) translateZ(' + RING_RADIUS + 'px)';
poster.style.webkitTransform = transform;
// setup the number to show inside the poster
var content = poster.appendChild(document.createElement('p'));
content.textContent = i;
// add the poster to the row
row.appendChild(poster);
}
}
function init ()
{
setup_posters(document.getElementById('ring-1'));
setup_posters(document.getElementById('ring-2'));
setup_posters(document.getElementById('ring-3'));
}
// call init once the document is fully loaded
window.addEventListener('load', init, false);
</script>
</head>
<body>
<div id="stage">
<div id="rotate">
<div id="ring-1" class="ring"></div>
<div id="ring-2" class="ring"></div>
<div id="ring-3" class="ring"></div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
<title>requestAnimationFrame Test</title>
<style type="text/css">
html, body {
height: 100%;
padding: 0px;
margin: 0px;
}
#container {
width: 100%;
height: 100%;
background: rgb(128, 128, 128);
}
</style>
</head>
<body>
<div id="container"></div>
<script type="text/javascript">
var container = document.getElementById('container');
var c = 128;
tick();
function tick() {
++c;
c %= 256;
container.style.backgroundColor = "rgb("+c+","+c+","+c+")";
requestAnimationFrame(tick);
};
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
<title>setTimeout Test</title>
<style type="text/css">
html, body {
height: 100%;
padding: 0px;
margin: 0px;
}
#container {
width: 100%;
height: 100%;
background: rgb(128, 128, 128);
}
</style>
</head>
<body>
<div id="container"></div>
<script type="text/javascript">
var container = document.getElementById('container');
var c = 128;
tick();
function tick() {
++c;
c %= 256;
container.style.backgroundColor = "rgb("+c+","+c+","+c+")";
setTimeout(tick, 16.66666);
};
</script>
</body>
</html>
...@@ -53,3 +53,7 @@ class BrowserInfo(object): ...@@ -53,3 +53,7 @@ class BrowserInfo(object):
branch_num = ( branch_num = (
self._browser._browser_backend.devtools_client.GetChromeBranchNumber()) self._browser._browser_backend.devtools_client.GetChromeBranchNumber())
return branch_num >= 2332 return branch_num >= 2332
@property
def browser_type(self):
return self._browser.browser_type
...@@ -116,15 +116,28 @@ class AndroidActionRunner(object): ...@@ -116,15 +116,28 @@ class AndroidActionRunner(object):
""" """
self._platform_backend.adb.RunShellCommand('input roll %s %s' % (dx, dy)) self._platform_backend.adb.RunShellCommand('input roll %s %s' % (dx, dy))
def EnsureScreenOn(self):
"""If device screen is off, turn screen on.
If the screen is already on, return immediately.
Raises:
Timeout: If the screen is off and device fails to turn screen on.
"""
if self._platform_backend.IsScreenOn():
return
self._ToggleScreenOn()
util.WaitFor(self._platform_backend.IsScreenOn, 5)
def TurnScreenOn(self): def TurnScreenOn(self):
"""If device screen is off, turn screen on. """If device screen is off, turn screen on.
If the screen is already on, this method returns immediately. If the screen is already on, log a warning and return immediately.
Raises: Raises:
Timeout: If the screen is off and device fails to turn screen on. Timeout: If the screen is off and device fails to turn screen on.
""" """
if not self._platform_backend.IsScreenOn(): if not self._platform_backend.IsScreenOn():
self._platform_backend.adb.RunShellCommand('input keyevent 26') self._ToggleScreenOn()
else: else:
logging.warning('Screen on when expected off.') logging.warning('Screen on when expected off.')
return return
...@@ -133,7 +146,7 @@ class AndroidActionRunner(object): ...@@ -133,7 +146,7 @@ class AndroidActionRunner(object):
def TurnScreenOff(self): def TurnScreenOff(self):
"""If device screen is on, turn screen off. """If device screen is on, turn screen off.
If the screen is already off, this method returns immediately. If the screen is already off, log a warning and return immediately.
Raises: Raises:
Timeout: If the screen is on and device fails to turn screen off. Timeout: If the screen is on and device fails to turn screen off.
...@@ -142,8 +155,7 @@ class AndroidActionRunner(object): ...@@ -142,8 +155,7 @@ class AndroidActionRunner(object):
return not self._platform_backend.IsScreenOn() return not self._platform_backend.IsScreenOn()
if self._platform_backend.IsScreenOn(): if self._platform_backend.IsScreenOn():
self._platform_backend.adb.RunShellCommand( self._ToggleScreenOn()
'input keyevent 26')
else: else:
logging.warning('Screen off when expected on.') logging.warning('Screen off when expected on.')
return return
...@@ -152,7 +164,7 @@ class AndroidActionRunner(object): ...@@ -152,7 +164,7 @@ class AndroidActionRunner(object):
def UnlockScreen(self): def UnlockScreen(self):
"""If device screen is locked, unlocks it. """If device screen is locked, unlocks it.
If the device is not locked, this method returns immediately. If the device is not locked, log a warning and return immediately.
Raises: Raises:
Timeout: If device fails to unlock screen. Timeout: If device fails to unlock screen.
...@@ -167,3 +179,6 @@ class AndroidActionRunner(object): ...@@ -167,3 +179,6 @@ class AndroidActionRunner(object):
return return
util.WaitFor(is_screen_unlocked, 5) util.WaitFor(is_screen_unlocked, 5)
def _ToggleScreenOn(self):
self._platform_backend.adb.RunShellCommand('input keyevent 26')
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