1 <!DOCTYPE html> 2 <!-- 3 Copyright (c) 2015 The Chromium Authors. All rights reserved. 4 Use of this source code is governed by a BSD-style license that can be 5 found in the LICENSE file. 6 --> 7 8 <link rel="import" href="/core/test_utils.html"> 9 <link rel="import" href="/extras/android/android_auditor.html"> 10 <link rel="import" href="/extras/importer/linux_perf/ftrace_importer.html"> 11 12 <script> 13 'use strict'; 14 15 tr.b.unittest.testSuite(function() { 16 var AndroidModelHelper = tr.e.audits.AndroidModelHelper; 17 var newAsyncSliceNamed = tr.c.test_utils.newAsyncSliceNamed; 18 var newSliceNamed = tr.c.test_utils.newSliceNamed; 19 var newSliceEx = tr.c.test_utils.newSliceEx; 20 var newCounterNamed = tr.c.test_utils.newCounterNamed; 21 var newCounterSeries = tr.c.test_utils.newCounterSeries; 22 23 function createSurfaceFlingerWithVsyncs(model) { 24 if (model.getProcess(2)) 25 throw new Error('process already exists'); 26 27 var sfProcess = model.getOrCreateProcess(2); 28 var sfThread = sfProcess.getOrCreateThread(2); // main thread, tid = pid 29 sfThread.name = '/system/bin/surfaceflinger'; 30 31 // ensure slicegroup has data 32 sfThread.sliceGroup.pushSlice(newSliceEx({ 33 title: 'doComposition', 34 start: 8, 35 duration: 2 36 })); 37 38 var counter = sfProcess.getOrCreateCounter('android', 'VSYNC'); 39 var series = newCounterSeries(); 40 for (var i = 0; i <= 10; i++) { 41 series.addCounterSample(i * 10, i % 2); 42 } 43 counter.addSeries(series); 44 } 45 46 /* 47 * List of customizeModelCallbacks which produce different 80ms frames, 48 * each starting at 10ms, and with a single important slice 49 */ 50 var SINGLE_FRAME_CUSTOM_MODELS = [ 51 function(model) { 52 // UI thread only 53 var uiThread = model.getOrCreateProcess(120).getOrCreateThread(120); 54 uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 10, 80)); 55 56 model.uiThread = uiThread; 57 }, 58 59 function(model) { 60 // RenderThread only 61 var renderThread = model.getOrCreateProcess(120).getOrCreateThread(200); 62 renderThread.name = 'RenderThread'; 63 renderThread.sliceGroup.pushSlice(newSliceNamed('doFrame', 10, 80)); 64 65 model.renderThread = renderThread; 66 }, 67 68 function(model) { 69 var uiThread = model.getOrCreateProcess(120).getOrCreateThread(120); 70 71 // UI thread time - 19 (from 10 to 29) 72 uiThread.asyncSliceGroup.push( 73 newAsyncSliceNamed('deliverInputEvent', 10, 9, uiThread, uiThread)); 74 uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 20, 10)); 75 uiThread.sliceGroup.pushSlice(newSliceNamed('draw', 20, 8)); 76 uiThread.sliceGroup.pushSlice(newSliceNamed('Record View#draw()', 20, 8)); 77 78 // RenderThread time - 61 (from 29 to 90) 79 var renderThread = model.getOrCreateProcess(120).getOrCreateThread(200); 80 renderThread.name = 'RenderThread'; 81 renderThread.sliceGroup.pushSlice(newSliceNamed('DrawFrame', 29, 61)); 82 renderThread.sliceGroup.pushSlice(newSliceNamed('syncFrameState', 29, 1)); 83 84 model.uiThread = uiThread; 85 model.renderThread = renderThread; 86 } 87 ]; 88 89 test('getThreads', function() { 90 SINGLE_FRAME_CUSTOM_MODELS.forEach(function(customizeModelCallback) { 91 var model = tr.c.test_utils.newModel(customizeModelCallback); 92 var helper = new AndroidModelHelper(model); 93 assert.equal(helper.apps[0].uiThread, model.uiThread); 94 assert.equal(helper.apps[0].renderThread, model.renderThread); 95 }); 96 }); 97 98 test('iterateImportantSlices', function() { 99 SINGLE_FRAME_CUSTOM_MODELS.forEach(function(customizeModelCallback) { 100 var model = tr.c.test_utils.newModel(customizeModelCallback); 101 var helper = new AndroidModelHelper(model); 102 103 var seen = 0; 104 helper.iterateImportantSlices(function(importantSlice) { 105 assert.isTrue(importantSlice instanceof tr.model.Slice); 106 seen++; 107 }); 108 assert.equal(seen, 1); 109 }); 110 }); 111 112 test('getFrames', function() { 113 SINGLE_FRAME_CUSTOM_MODELS.forEach(function(customizeModelCallback) { 114 var model = tr.c.test_utils.newModel(customizeModelCallback); 115 var helper = new AndroidModelHelper(model); 116 assert.equal(helper.apps.length, 1); 117 118 var frames = helper.apps[0].getFrames(); 119 assert.equal(frames.length, 1); 120 assert.closeTo(frames[0].totalDuration, 80, 1e-5); 121 122 assert.closeTo(frames[0].start, 10, 1e-5); 123 assert.closeTo(frames[0].end, 90, 1e-5); 124 }); 125 }); 126 127 test('surfaceFlingerVsyncs', function() { 128 var model = tr.c.test_utils.newModel(createSurfaceFlingerWithVsyncs); 129 var helper = new AndroidModelHelper(model); 130 assert.isTrue(helper.surfaceFlinger.hasVsyncs); 131 132 // test querying the vsyncs 133 assert.closeTo(helper.surfaceFlinger.getFrameKickoff(5), 0, 1e-5); 134 assert.closeTo(helper.surfaceFlinger.getFrameDeadline(95), 100, 1e-5); 135 136 assert.closeTo(helper.surfaceFlinger.getFrameKickoff(10), 10, 1e-5); 137 assert.closeTo(helper.surfaceFlinger.getFrameDeadline(90), 100, 1e-5); 138 139 // test undefined behavior outside of vsyncs. 140 assert.isUndefined(helper.surfaceFlinger.getFrameKickoff(-5)); 141 assert.isUndefined(helper.surfaceFlinger.getFrameDeadline(105)); 142 }); 143 144 test('frameVsyncInterop', function() { 145 var model = tr.c.test_utils.newModel(function(model) { 146 // app - 3 good, 3 bad frames 147 var uiThread = model.getOrCreateProcess(1).getOrCreateThread(1); 148 uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 1, 8)); 149 uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 10, 8)); 150 uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 20, 8)); 151 uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 31, 11)); 152 uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 45, 6)); 153 uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 60, 20)); 154 155 // surface flinger - vsync every 10ms 156 createSurfaceFlingerWithVsyncs(model); 157 }); 158 var helper = new AndroidModelHelper(model); 159 160 var frames = helper.apps[0].getFrames(); 161 assert.equal(frames.length, 6); 162 for (var i = 0; i < 6; i++) { 163 var shouldMissDeadline = i >= 3; 164 var missedDeadline = frames[i].args['deadline'] < frames[i].end; 165 assert.equal(shouldMissDeadline, missedDeadline); 166 } 167 }); 168 169 test('appInputs', function() { 170 var model = tr.c.test_utils.newModel(function(model) { 171 var process = model.getOrCreateProcess(120); 172 var uiThread = process.getOrCreateThread(120); 173 uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 20, 4)); 174 uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 40, 4)); 175 176 var counter = process.getOrCreateCounter('android', 'aq:pending:foo'); 177 var series = newCounterSeries(); 178 series.addCounterSample(10, 1); 179 series.addCounterSample(20, 0); 180 series.addCounterSample(30, 1); 181 series.addCounterSample(40, 2); 182 series.addCounterSample(50, 0); 183 counter.addSeries(series); 184 }); 185 var helper = new AndroidModelHelper(model); 186 assert.equal(helper.apps.length, 1); 187 188 var inputSamples = helper.apps[0].getInputSamples(); 189 assert.equal(inputSamples.length, 3); 190 assert.equal(inputSamples[0].timestamp, 10); 191 assert.equal(inputSamples[1].timestamp, 30); 192 assert.equal(inputSamples[2].timestamp, 40); 193 }); 194 195 test('appAnimations', function() { 196 var model = tr.c.test_utils.newModel(function(model) { 197 var process = model.getOrCreateProcess(120); 198 var uiThread = process.getOrCreateThread(120); 199 uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 10, 10)); 200 uiThread.asyncSliceGroup.push(newAsyncSliceNamed('animator:foo', 0, 10, 201 uiThread, uiThread)); 202 }); 203 var helper = new AndroidModelHelper(model); 204 assert.equal(helper.apps.length, 1); 205 206 var animations = helper.apps[0].getAnimationAsyncSlices(); 207 assert.equal(animations.length, 1); 208 assert.equal(animations[0].start, 0); 209 assert.equal(animations[0].end, 10); 210 }); 211 }); 212 </script> 213