| <!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="/base/base.html"> |
| <link rel="import" href="/core/test_utils.html"> |
| <link rel="import" href="/core/trace_model/frame.html"> |
| <link rel="import" href="/extras/audits/android_auditor.html"> |
| <link rel="import" href="/extras/importer/linux_perf/linux_perf_importer.html"> |
| |
| <script> |
| 'use strict'; |
| |
| tv.b.unittest.testSuite(function() { |
| var SCHEDULING_STATE = tv.c.trace_model.SCHEDULING_STATE; |
| var newSliceNamed = tv.c.test_utils.newSliceNamed; |
| var FRAME_PERF_CLASS = tv.c.trace_model.FRAME_PERF_CLASS; |
| var newThreadSlice = tv.c.test_utils.newThreadSlice; |
| |
| test('scheduleAlerts', function() { |
| var model = tv.c.test_utils.newModelWithAuditor(function(model) { |
| var uiThread = model.getOrCreateProcess(1).getOrCreateThread(1); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 0, 10)); |
| uiThread.timeSlices = [ |
| newThreadSlice(uiThread, SCHEDULING_STATE.RUNNING, 0, 3, 0), |
| newThreadSlice(uiThread, SCHEDULING_STATE.RUNNABLE, 3, 5, 0), |
| newThreadSlice(uiThread, SCHEDULING_STATE.RUNNING, 8, 2, 0)]; |
| }, tv.e.audits.AndroidAuditor); |
| assert.equal(model.alerts.length, 1); |
| var alert = model.alerts[0]; |
| assert.equal(alert.type.title, 'Scheduling delay'); |
| assert.closeTo(alert.args.delay, 5, 1e-5); |
| |
| model = tv.c.test_utils.newModelWithAuditor(function(model) { |
| var uiThread = model.getOrCreateProcess(1).getOrCreateThread(1); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 0, 15)); |
| uiThread.timeSlices = [ |
| newThreadSlice(uiThread, SCHEDULING_STATE.RUNNING, 0, 5, 0), |
| newThreadSlice(uiThread, SCHEDULING_STATE.UNINTR_SLEEP, 5, 9, 0), |
| newThreadSlice(uiThread, SCHEDULING_STATE.RUNNING, 14, 1, 0)]; |
| }, tv.e.audits.AndroidAuditor); |
| assert.equal(model.alerts.length, 1); |
| var alert = model.alerts[0]; |
| assert.equal(alert.type.title, 'Blocking I/O delay'); |
| assert.closeTo(alert.args.delay, 9, 1e-5); |
| }); |
| |
| test('saveLayerAlert', function() { |
| var model = tv.c.test_utils.newModelWithAuditor(function(model) { |
| var renderThread = model.getOrCreateProcess(1).getOrCreateThread(2); |
| renderThread.name = 'RenderThread'; |
| renderThread.sliceGroup.pushSlice(newSliceNamed('DrawFrame', 200, 5)); |
| renderThread.sliceGroup.pushSlice(newSliceNamed( |
| 'BadAlphaView alpha caused saveLayer 480x320', 203, 1)); |
| }, tv.e.audits.AndroidAuditor); |
| |
| assert.equal(model.alerts.length, 1); |
| |
| var alert = model.alerts[0]; |
| assert.equal(alert.args['view name'], 'BadAlphaView'); |
| assert.equal(alert.args.width, 480); |
| assert.equal(alert.args.height, 320); |
| }); |
| |
| test('uploadAlert', function() { |
| var model = tv.c.test_utils.newModelWithAuditor(function(model) { |
| var renderThread = model.getOrCreateProcess(1).getOrCreateThread(2); |
| renderThread.name = 'RenderThread'; |
| renderThread.sliceGroup.pushSlice(newSliceNamed('doFrame', 0, 20)); |
| renderThread.sliceGroup.pushSlice(newSliceNamed( |
| 'Upload 1000x1000 Texture', 0, 15)); |
| }, tv.e.audits.AndroidAuditor); |
| |
| assert.equal(model.alerts.length, 1); |
| |
| var alert = model.alerts[0]; |
| assert.closeTo(alert.args['pixels uploaded (millions)'], 1, 1e-5); |
| assert.equal(alert.args['time spent'], 15); |
| }); |
| |
| test('listViewAlert', function() { |
| var model = tv.c.test_utils.newModelWithAuditor(function(model) { |
| var uiThread = model.getOrCreateProcess(1).getOrCreateThread(1); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('obtainView', 0, 5)); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('setupListItem', 5, 5)); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('obtainView', 10, 5)); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('setupListItem', 15, 5)); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 20, 10)); |
| |
| // short frame, so no alert should be generated |
| uiThread.sliceGroup.pushSlice(newSliceNamed('obtainView', 50, 5)); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('setupListItem', 55, 5)); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 60, 1)); |
| }, tv.e.audits.AndroidAuditor); |
| |
| assert.equal(model.alerts.length, 1); |
| var alert = model.alerts[0]; |
| assert.equal(alert.args['items inflated'], 2); |
| assert.closeTo(alert.args['time spent'], 20, 1e-5); |
| }); |
| |
| test('measureLayoutAlert', function() { |
| var model = tv.c.test_utils.newModelWithAuditor(function(model) { |
| var uiThread = model.getOrCreateProcess(1).getOrCreateThread(1); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 0, 20)); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('measure', 0, 5)); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('layout', 10, 5)); |
| }, tv.e.audits.AndroidAuditor); |
| |
| assert.equal(model.alerts.length, 1); |
| assert.closeTo(model.alerts[0].args['time spent'], 10, 1e-5); |
| }); |
| |
| test('viewDrawAlert', function() { |
| var model = tv.c.test_utils.newModelWithAuditor(function(model) { |
| var uiThread = model.getOrCreateProcess(1).getOrCreateThread(1); |
| // modern naming |
| uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 0, 20)); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('Record View#draw()', 0, 10)); |
| |
| // legacy naming |
| uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 40, 20)); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('getDisplayList', 40, 10)); |
| }, tv.e.audits.AndroidAuditor); |
| |
| assert.equal(model.alerts.length, 2); |
| assert.closeTo(model.alerts[0].args['time spent'], 10, 1e-5); |
| assert.closeTo(model.alerts[1].args['time spent'], 10, 1e-5); |
| }); |
| |
| test('addFrameToModel', function() { |
| var process; |
| var model = tv.c.test_utils.newModelWithAuditor(function(model) { |
| process = model.getOrCreateProcess(1); |
| var uiThread = process.getOrCreateThread(1); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 0, 8)); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 16, 20)); |
| uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 40, 90)); |
| }, tv.e.audits.AndroidAuditor); |
| |
| assert.equal(process.frames.length, 3); |
| assert.closeTo(process.frames[0].totalDuration, 8, 1e-5); |
| assert.closeTo(process.frames[1].totalDuration, 20, 1e-5); |
| assert.closeTo(process.frames[2].totalDuration, 90, 1e-5); |
| |
| assert.equal(process.frames[0].perfClass, |
| FRAME_PERF_CLASS.GOOD); |
| assert.equal(process.frames[1].perfClass, |
| FRAME_PERF_CLASS.BAD); |
| assert.equal(process.frames[2].perfClass, |
| FRAME_PERF_CLASS.TERRIBLE); |
| }); |
| |
| test('processRenameAndSort', function() { |
| var appProcess; |
| var sfProcess; |
| var model = tv.c.test_utils.newModelWithAuditor(function(model) { |
| appProcess = model.getOrCreateProcess(1); |
| var uiThread = appProcess.getOrCreateThread(1); |
| uiThread.name = 'ndroid.systemui'; |
| uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 0, 8)); |
| |
| sfProcess = model.getOrCreateProcess(2); |
| var sfThread = sfProcess.getOrCreateThread(2); |
| sfThread.name = '/system/bin/surfaceflinger'; |
| sfThread.sliceGroup.pushSlice(newSliceNamed('doComposition', 8, 2)); |
| |
| }, tv.e.audits.AndroidAuditor); |
| |
| // both processes should be renamed |
| assert.equal(appProcess.name, 'android.systemui'); |
| assert.equal(sfProcess.name, 'SurfaceFlinger'); |
| |
| assert.isTrue(sfProcess.sortIndex < appProcess.sortIndex); |
| }); |
| |
| test('drawingThreadPriorities', function() { |
| var uiThread; |
| var renderThread; |
| var workerThread; |
| var otherThread; |
| var model = tv.c.test_utils.newModelWithAuditor(function(model) { |
| var appProcess = model.getOrCreateProcess(1); |
| |
| uiThread = appProcess.getOrCreateThread(1); |
| uiThread.name = 'ndroid.systemui'; |
| uiThread.sliceGroup.pushSlice(newSliceNamed('performTraversals', 0, 4)); |
| |
| renderThread = appProcess.getOrCreateThread(2); |
| renderThread.name = 'RenderThread'; |
| renderThread.sliceGroup.pushSlice(newSliceNamed('DrawFrame', 3, 4)); |
| |
| workerThread = appProcess.getOrCreateThread(3); |
| workerThread.name = 'hwuiTask1'; |
| workerThread.sliceGroup.pushSlice(newSliceNamed('work', 4, 1)); |
| |
| otherThread = appProcess.getOrCreateThread(4); |
| otherThread.name = 'other'; |
| otherThread.sliceGroup.pushSlice(newSliceNamed('otherWork', 0, 2)); |
| }, tv.e.audits.AndroidAuditor); |
| |
| assert.isTrue(uiThread.sortIndex < renderThread.sortIndex); |
| assert.isTrue(renderThread.sortIndex < workerThread.sortIndex); |
| assert.isTrue(workerThread.sortIndex < otherThread.sortIndex); |
| }); |
| |
| test('favicon', function() { |
| var createTraceModelWithJank = function(percentageJank) { |
| |
| return tv.c.test_utils.newModelWithAuditor(function(model) { |
| var uiThread = model.getOrCreateProcess(1).getOrCreateThread(1); |
| for (var i = 0; i < 100; i++) { |
| var slice = newSliceNamed('performTraversals', |
| 30 * i, |
| i <= percentageJank ? 24 : 8); |
| uiThread.sliceGroup.pushSlice(slice); |
| } |
| }, tv.e.audits.AndroidAuditor); |
| }; |
| assert.equal(createTraceModelWithJank(3).faviconHue, 'green'); |
| assert.equal(createTraceModelWithJank(10).faviconHue, 'yellow'); |
| assert.equal(createTraceModelWithJank(50).faviconHue, 'red'); |
| }); |
| }); |
| </script> |