Home | History | Annotate | Download | only in android
      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