blob: 9d9de28ed46fe61d2415999eab7645888a920eac [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 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.
-->
<link rel="import" href="/tracing/core/test_utils.html">
<link rel="import" href="/tracing/extras/android/android_auditor.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/ftrace_importer.html">
<script>
'use strict';
tr.b.unittest.testSuite(function() {
var AndroidModelHelper = tr.model.helpers.AndroidModelHelper;
var newAsyncSliceNamed = tr.c.TestUtils.newAsyncSliceNamed;
var newSliceEx = tr.c.TestUtils.newSliceEx;
var newCounterNamed = tr.c.TestUtils.newCounterNamed;
var newCounterSeries = tr.c.TestUtils.newCounterSeries;
function createSurfaceFlinger(model, vsyncCallback) {
if (model.getProcess(2))
throw new Error('process already exists');
var sfProcess = model.getOrCreateProcess(2);
var sfThread = sfProcess.getOrCreateThread(2); // main thread, tid = pid
sfThread.name = '/system/bin/surfaceflinger';
// ensure slicegroup has data
sfThread.sliceGroup.pushSlice(newSliceEx({
title: 'doComposition',
start: 8,
duration: 2
}));
vsyncCallback(sfProcess);
}
function createSurfaceFlingerWithVsync(model) {
createSurfaceFlinger(model, function(sfProcess) {
var counter = sfProcess.getOrCreateCounter('android', 'VSYNC');
var series = newCounterSeries();
for (var i = 0; i <= 10; i++) {
series.addCounterSample(i * 10, i % 2);
}
counter.addSeries(series);
});
}
/*
* List of customizeModelCallbacks which produce different 80ms frames,
* each starting at 10ms, and with a single important slice
*/
var SINGLE_FRAME_CUSTOM_MODELS = [
function(model) {
// UI thread only
var uiThread = model.getOrCreateProcess(120).getOrCreateThread(120);
uiThread.sliceGroup.pushSlice(newSliceEx(
{title: 'performTraversals', start: 10, duration: 80}));
model.uiThread = uiThread;
},
function(model) {
// RenderThread only
var renderThread = model.getOrCreateProcess(120).getOrCreateThread(200);
renderThread.name = 'RenderThread';
renderThread.sliceGroup.pushSlice(newSliceEx(
{title: 'doFrame', start: 10, duration: 80}));
model.renderThread = renderThread;
},
function(model) {
var uiThread = model.getOrCreateProcess(120).getOrCreateThread(120);
// UI thread time - 19 (from 10 to 29, ignored after)
uiThread.asyncSliceGroup.push(
newAsyncSliceNamed('deliverInputEvent', 10, 9, uiThread, uiThread));
uiThread.sliceGroup.pushSlice(newSliceEx(
{title: 'performTraversals', start: 20, duration: 110}));
uiThread.sliceGroup.pushSlice(newSliceEx(
{title: 'draw', start: 20, duration: 108}));
uiThread.sliceGroup.pushSlice(newSliceEx(
{title: 'Record View#draw()', start: 20, duration: 8}));
// RenderThread time - 61 (from 29 to 90, ignored after)
var renderThread = model.getOrCreateProcess(120).getOrCreateThread(200);
renderThread.name = 'RenderThread';
renderThread.sliceGroup.pushSlice(newSliceEx(
{title: 'DrawFrame', start: 29, duration: 70}));
renderThread.sliceGroup.pushSlice(newSliceEx(
{title: 'syncFrameState', start: 29, duration: 1}));
renderThread.sliceGroup.pushSlice(newSliceEx(
{title: 'queueBuffer', start: 89, duration: 1}));
model.uiThread = uiThread;
model.renderThread = renderThread;
}
];
test('getThreads', function() {
SINGLE_FRAME_CUSTOM_MODELS.forEach(function(customizeModelCallback) {
var model = tr.c.TestUtils.newModel(customizeModelCallback);
var helper = model.getOrCreateHelper(AndroidModelHelper);
assert.equal(helper.apps[0].uiThread, model.uiThread);
assert.equal(helper.apps[0].renderThread, model.renderThread);
});
});
test('iterateImportantSlices', function() {
SINGLE_FRAME_CUSTOM_MODELS.forEach(function(customizeModelCallback) {
var model = tr.c.TestUtils.newModel(customizeModelCallback);
var helper = model.getOrCreateHelper(AndroidModelHelper);
var seen = 0;
helper.iterateImportantSlices(function(importantSlice) {
assert.isTrue(importantSlice instanceof tr.model.Slice);
seen++;
});
assert.equal(seen, 1);
});
});
test('getFrames', function() {
SINGLE_FRAME_CUSTOM_MODELS.forEach(function(customizeModelCallback) {
var model = tr.c.TestUtils.newModel(customizeModelCallback);
var helper = model.getOrCreateHelper(AndroidModelHelper);
assert.equal(helper.apps.length, 1);
var frames = helper.apps[0].getFrames();
assert.equal(frames.length, 1);
assert.closeTo(frames[0].totalDuration, 80, 1e-5);
assert.closeTo(frames[0].start, 10, 1e-5);
assert.closeTo(frames[0].end, 90, 1e-5);
});
});
test('surfaceFlingerVsyncs', function() {
var model = tr.c.TestUtils.newModel(createSurfaceFlingerWithVsync);
var helper = model.getOrCreateHelper(AndroidModelHelper);
assert.isTrue(helper.surfaceFlinger.hasVsyncs);
// test querying the vsyncs
assert.closeTo(helper.surfaceFlinger.getFrameKickoff(5), 0, 1e-5);
assert.closeTo(helper.surfaceFlinger.getFrameDeadline(95), 100, 1e-5);
assert.closeTo(helper.surfaceFlinger.getFrameKickoff(10), 10, 1e-5);
assert.closeTo(helper.surfaceFlinger.getFrameDeadline(90), 100, 1e-5);
// test undefined behavior outside of vsyncs.
assert.isUndefined(helper.surfaceFlinger.getFrameKickoff(-5));
assert.isUndefined(helper.surfaceFlinger.getFrameDeadline(105));
});
test('surfaceFlingerShiftedVsyncs', function() {
var model = tr.c.TestUtils.newModel(function(model) {
createSurfaceFlinger(model, function(sfProcess) {
var appSeries = newCounterSeries();
var sfSeries = newCounterSeries();
for (var i = 0; i <= 10; i++) {
// SF vsync is 4ms after app
appSeries.addCounterSample(i * 16, i % 2);
sfSeries.addCounterSample(i * 16 + 4, i % 2);
}
sfProcess.getOrCreateCounter('android', 'VSYNC-sf')
.addSeries(sfSeries);
sfProcess.getOrCreateCounter('android', 'VSYNC-app')
.addSeries(appSeries);
});
});
var helper = model.getOrCreateHelper(AndroidModelHelper);
assert.isTrue(helper.surfaceFlinger.hasVsyncs);
// test querying the vsyncs - Frames should have 20ms window
assert.closeTo(helper.surfaceFlinger.getFrameKickoff(0), 0, 1e-5);
assert.closeTo(helper.surfaceFlinger.getFrameDeadline(0), 20, 1e-5);
assert.closeTo(helper.surfaceFlinger.getFrameKickoff(16), 16, 1e-5);
assert.closeTo(helper.surfaceFlinger.getFrameDeadline(16), 36, 1e-5);
// test undefined behavior outside of vsyncs.
assert.isUndefined(helper.surfaceFlinger.getFrameKickoff(-5));
assert.isUndefined(helper.surfaceFlinger.getFrameDeadline(165));
});
test('frameVsyncInterop', function() {
var model = tr.c.TestUtils.newModel(function(model) {
// app - 3 good, 3 bad frames
var uiThread = model.getOrCreateProcess(1).getOrCreateThread(1);
uiThread.sliceGroup.pushSlice(newSliceEx(
{title: 'performTraversals', start: 1, duration: 8}));
uiThread.sliceGroup.pushSlice(newSliceEx(
{title: 'performTraversals', start: 10, duration: 8}));
uiThread.sliceGroup.pushSlice(newSliceEx(
{title: 'performTraversals', start: 20, duration: 8}));
uiThread.sliceGroup.pushSlice(newSliceEx(
{title: 'performTraversals', start: 31, duration: 11}));
uiThread.sliceGroup.pushSlice(newSliceEx(
{title: 'performTraversals', start: 45, duration: 6}));
uiThread.sliceGroup.pushSlice(newSliceEx(
{title: 'performTraversals', start: 60, duration: 20}));
// surface flinger - vsync every 10ms
createSurfaceFlingerWithVsync(model);
});
var helper = model.getOrCreateHelper(AndroidModelHelper);
var frames = helper.apps[0].getFrames();
assert.equal(frames.length, 6);
for (var i = 0; i < 6; i++) {
var shouldMissDeadline = i >= 3;
var missedDeadline = frames[i].args['deadline'] < frames[i].end;
assert.equal(shouldMissDeadline, missedDeadline);
}
});
test('appInputs', function() {
var model = tr.c.TestUtils.newModel(function(model) {
var process = model.getOrCreateProcess(120);
var uiThread = process.getOrCreateThread(120);
uiThread.sliceGroup.pushSlice(newSliceEx(
{title: 'performTraversals', start: 20, duration: 4}));
uiThread.sliceGroup.pushSlice(newSliceEx(
{title: 'performTraversals', start: 40, duration: 4}));
var counter = process.getOrCreateCounter('android', 'aq:pending:foo');
var series = newCounterSeries();
series.addCounterSample(10, 1);
series.addCounterSample(20, 0);
series.addCounterSample(30, 1);
series.addCounterSample(40, 2);
series.addCounterSample(50, 0);
counter.addSeries(series);
});
var helper = model.getOrCreateHelper(AndroidModelHelper);
assert.equal(helper.apps.length, 1);
var inputSamples = helper.apps[0].getInputSamples();
assert.equal(inputSamples.length, 3);
assert.equal(inputSamples[0].timestamp, 10);
assert.equal(inputSamples[1].timestamp, 30);
assert.equal(inputSamples[2].timestamp, 40);
});
test('appAnimations', function() {
var model = tr.c.TestUtils.newModel(function(model) {
var process = model.getOrCreateProcess(120);
var uiThread = process.getOrCreateThread(120);
uiThread.sliceGroup.pushSlice(newSliceEx(
{title: 'performTraversals', start: 10, duration: 10}));
uiThread.asyncSliceGroup.push(newAsyncSliceNamed('animator:foo', 0, 10,
uiThread, uiThread));
});
var helper = model.getOrCreateHelper(AndroidModelHelper);
assert.equal(helper.apps.length, 1);
var animations = helper.apps[0].getAnimationAsyncSlices();
assert.equal(animations.length, 1);
assert.equal(animations[0].start, 0);
assert.equal(animations[0].end, 10);
});
});
</script>