Chromium Code Reviews| Index: tracing/tracing/metrics/estimated_input_latency_metric_test.html |
| diff --git a/tracing/tracing/metrics/estimated_input_latency_metric_test.html b/tracing/tracing/metrics/estimated_input_latency_metric_test.html |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f9d2b400bc797ae670cd641b0f815f2cd94fedbe |
| --- /dev/null |
| +++ b/tracing/tracing/metrics/estimated_input_latency_metric_test.html |
| @@ -0,0 +1,385 @@ |
| +<!DOCTYPE html> |
| +<!-- |
| +Copyright (c) 2016 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/metrics/estimated_input_latency_metric.html"> |
| +<link rel="import" href="/tracing/metrics/system_health/loading_metric.html"> |
| +<link rel="import" href="/tracing/value/value_set.html"> |
| + |
| +<script> |
| +'use strict'; |
| + |
| +tr.b.unittest.testSuite(function() { |
| + var FPTOLERANCE = 0.1; |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
no need for this constant. Just about everywhere e
dproy
2016/09/20 01:11:43
Done.
|
| + var MAIN_THREAD_ID = 2; |
| + var INTERACTIVE_WINDOW_SIZE_MS = tr.metrics.sh.INTERACTIVE_WINDOW_SIZE_MS; |
| + var calculateEILRiskPercentiles = tr.metrics.calculateEILRiskPercentiles; |
| + |
| + function newSchedulerTask(startTime, duration) { |
| + return tr.c.TestUtils.newSliceEx({ |
| + cat: 'toplevel', |
| + title: 'TaskQueueManager::ProcessTaskFromWorkQueue', |
| + start: startTime, |
| + duration: duration |
| + }); |
| + } |
| + |
| + function assertTasksNotTooLong(tti, taskList) { |
| + // Need to make sure the tasks in the interactive window are less that |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
nit: comments in tracing/ are complete sentences t
|
| + // responsiveness threshold because otherwise it will push back TTI. |
| + // If we change RESPONSIVENESS_THRESHOLD_MS or INTERACTIVE_WINDOW_SIZE_MS in |
| + // TTI we may have to change the task durations for this test. |
| + for (var task of taskList) { |
| + if (task.start < tti + INTERACTIVE_WINDOW_SIZE_MS) { |
| + assert.isBelow( |
| + task.duration, tr.metrics.sh.RESPONSIVENESS_THRESHOLD_MS); |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
nit: line continuations
dproy
2016/09/20 01:11:43
Done.
|
| + } |
| + } |
| + } |
| + |
| + function addTasksToThread(mainThread, tti, taskList) { |
| + assertTasksNotTooLong(tti, taskList); |
| + for (var task of taskList) { |
| + mainThread.sliceGroup.pushSlice( |
| + newSchedulerTask(task.start, task.duration)); |
| + } |
| + } |
| + |
| + function createTestModel(rendererCallback) { |
| + var model = tr.c.TestUtils.newModel(function(model) { |
| + var rendererProcess = model.getOrCreateProcess(1); |
| + var mainThread = rendererProcess.getOrCreateThread(MAIN_THREAD_ID); |
| + mainThread.name = 'CrRendererMain'; |
| + rendererCallback(rendererProcess); |
| + }); |
| + return model; |
| + } |
| + |
| + function addTestFrame(rendererProcess) { |
| + // We need FMP for TTI, and FMP needs to reference a frame. |
| + rendererProcess.objects.addSnapshot( |
| + 'ptr', 'loading', 'FrameLoader', 300, |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
nit: line continuations
dproy
2016/09/20 01:11:43
Done.
|
| + {isLoadingMainFrame: true, frame: {id_ref: '0xdeadbeef'}, |
| + documentLoaderURL: 'http://example.com'}); |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
nit: wrong indent here
dproy
2016/09/20 01:11:43
What's the correct indent here? Line 67 shifted fo
|
| + } |
| + |
| + function addInteractiveWindow( |
| + rendererProcess, startNavigationTime, ttiTime) { |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
nit: line continuations
dproy
2016/09/20 01:11:43
Done.
|
| + // To get to TTI, we need a firstMeaningfulPaint and a sufficiently large |
| + // window of no long tasks. |
| + var mainThread = rendererProcess.getOrCreateThread(MAIN_THREAD_ID); |
| + mainThread.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({ |
| + cat: 'blink.user_timing', |
| + title: 'navigationStart', |
| + start: startNavigationTime, |
| + duration: 0.0, |
| + args: {frame: '0xdeadbeef'} |
| + })); |
| + |
| + mainThread.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({ |
| + cat: 'loading', |
| + title: 'firstMeaningfulPaintCandidate', |
| + start: ttiTime, |
| + duration: 0.0, |
| + args: {frame: '0xdeadbeef'} |
| + })); |
| + |
| + // Add a 0 duration task at the end so we hit the required window size. |
| + mainThread.sliceGroup.pushSlice( |
| + newSchedulerTask(ttiTime + INTERACTIVE_WINDOW_SIZE_MS, 0)); |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
nit: line continuations
dproy
2016/09/20 01:11:43
Done.
|
| + } |
| + |
| + function prepareModelForSingleWindowTest(tti, postTTITasks) { |
| + var model = createTestModel(rendererProcess => { |
| + var startNavTime = 0; |
| + addTestFrame(rendererProcess); |
| + addInteractiveWindow(rendererProcess, startNavTime, tti); |
| + var mainThread = rendererProcess.getOrCreateThread(MAIN_THREAD_ID); |
| + addTasksToThread(mainThread, tti, postTTITasks); |
| + }); |
| + return model; |
| + } |
| + |
| + function computeEQT90Numeric(model) { |
| + var values = new tr.v.ValueSet(); |
| + tr.metrics.estimatedInputLatencyMetric(values, model); |
| + var numeric = tr.b.getOnlyElement(values.getValuesNamed( |
| + 'Expected Queueing Time 90th Percentile')); |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
same
dproy
2016/09/20 01:11:43
Done.
|
| + return numeric; |
| + } |
| + |
| + test('eqt90thPercentileTest40msFull', function() { |
| + var tti = 1000; |
| + var postTTITasks = []; |
| + var taskDur = 40; |
| + var windowEnd = tti + INTERACTIVE_WINDOW_SIZE_MS; |
| + for (var startTime = tti; startTime < windowEnd; startTime += taskDur) { |
| + postTTITasks.push({start: startTime, duration: taskDur}); |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
nit: no need for braces in one line branch
|
| + } |
| + |
| + var model = prepareModelForSingleWindowTest(tti, postTTITasks); |
| + var numeric = computeEQT90Numeric(model); |
| + assert.equal(numeric.running.count, 1); |
| + assert.closeTo(numeric.running.mean, 36, FPTOLERANCE); |
| + }); |
| + |
| + test('eqt90thPercentileTestZeroEQT', function() { |
| + var tti = 1000; |
| + var postTTITasks = []; // No tasks after TTI |
|
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: comments in TV are a complete sentence that e
|
| + var model = prepareModelForSingleWindowTest(tti, postTTITasks); |
| + var numeric = computeEQT90Numeric(model); |
| + assert.equal(numeric.running.count, 1); |
| + assert.equal(numeric.running.mean, 0); |
| + }); |
| + |
| + test('eqt90thPercentileTestLargePostTTITask', function() { |
| + var tti = 1000; |
| + var postTTITasks = [ |
| + {start: tti + INTERACTIVE_WINDOW_SIZE_MS, duration: 5000} |
| + ]; |
| + |
| + var model = prepareModelForSingleWindowTest(tti, postTTITasks); |
| + var numeric = computeEQT90Numeric(model); |
| + assert.equal(numeric.running.count, 1); |
| + assert.closeTo(numeric.running.mean, 4000, FPTOLERANCE); |
| + }); |
| + |
| + test('eqt90thPercentileTestAlternate40ms', function() { |
| + var tti = 1000; |
| + var postTTITasks = []; |
| + var taskDur = 40; |
| + var windowEnd = tti + INTERACTIVE_WINDOW_SIZE_MS; |
| + for (var startTime = tti; startTime < windowEnd; startTime += 2 * taskDur) { |
| + postTTITasks.push({start: startTime, duration: taskDur}); |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
nit: one line branch
|
| + } |
| + var model = prepareModelForSingleWindowTest(tti, postTTITasks); |
| + var numeric = computeEQT90Numeric(model); |
| + assert.equal(numeric.running.count, 1); |
| + assert.closeTo(numeric.running.mean, 32, FPTOLERANCE); |
| + }); |
| + |
| + test('eqt90thPercentileMultipleWindows', function() { |
| + // There are five interactive windows: |
| + // window 1 - 3 have zero tasks |
| + // window 4 is full of back to back 40 ms tasks |
| + // window 5 has alternating 40ms task and 40ms idleTime |
| + |
|
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: no need for blank line
dproy
2016/09/20 01:11:43
Done.
|
| + var model = createTestModel(rendererProcess => { |
| + addTestFrame(rendererProcess); |
| + var mainThread = rendererProcess.getOrCreateThread(MAIN_THREAD_ID); |
| + |
| + for (var i = 0; i < 5; i++) { |
| + var startNavigationTime = i * 10000; |
| + var ttiTime = startNavigationTime + 1000; |
| + var windowEnd = (i + 1) * 10000; |
| + addInteractiveWindow( |
| + rendererProcess, startNavigationTime, ttiTime, windowEnd); |
| + } |
| + |
| + var taskDur = 40; |
| + var win4Tasks = []; |
| + var win4Interactive = 31000; |
| + var win4End = 40000; |
| + for (var startTime = win4Interactive; |
| + startTime < win4End; startTime += taskDur) { |
| + win4Tasks.push({start: startTime, duration: taskDur}); |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
nit: no need for braces
|
| + } |
| + addTasksToThread(mainThread, win4Interactive, win4Tasks); |
| + |
| + var win5Tasks = []; |
| + var win5Interactive = 41000; |
| + var win5End = 50000; |
| + for (var startTime = win5Interactive; |
| + startTime < win5End; startTime += 2 * taskDur) { |
| + win5Tasks.push({start: startTime, duration: taskDur}); |
| + } |
| + addTasksToThread(mainThread, win5Interactive, win5Tasks); |
| + }); |
| + |
| + var numeric = computeEQT90Numeric(model); |
| + assert.equal(numeric.running.count, 5); |
| + numeric.sampleValues.slice(0, 3).forEach(v => assert.equal(v, 0)); |
| + assert.closeTo(numeric.sampleValues[3], 36, FPTOLERANCE); |
| + assert.closeTo(numeric.sampleValues[4], 32, FPTOLERANCE); |
| + }); |
| + |
| + test('properlyIterateSchedulerTasks', function() { |
| + // Test that we handle both top level message loop tasks and nested renderer |
| + // scheduler tasks. We add 2000ms worth of back to back 40ms |
| + // MessageLoop::RunTask slices with no nested scheduler task, and then one |
| + // big 3000ms MessageLoop::RunTask with 2000ms worth of back to back 40ms |
| + // nested scheduler tasks inside. |
| + |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
nit: no need for blank line
dproy
2016/09/20 01:11:43
Done.
|
| + var messageLoopTaskTitle = 'MessageLoop::RunTask'; |
| + var schedulerTaskTitle = 'TaskQueueManager::ProcessTaskFromWorkQueue'; |
| + var model = createTestModel(rendererProcess => { |
| + var startNavTime = 0; |
| + var tti = 1000; |
| + var taskDur = 40; |
| + assert.isBelow(taskDur, tr.metrics.sh.RESPONSIVENESS_THRESHOLD_MS); |
| + |
| + addTestFrame(rendererProcess); |
| + addInteractiveWindow(rendererProcess, startNavTime, tti); |
| + var mainThread = rendererProcess.getOrCreateThread(MAIN_THREAD_ID); |
| + |
| + for (var start = tti; start < tti + 2000; start += taskDur) { |
| + mainThread.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({ |
| + cat: 'toplevel', |
| + title: messageLoopTaskTitle, |
| + start: start, |
| + duration: taskDur |
| + })); |
| + } |
| + |
| + // A huge ML task with back to back 40ms tasks in scheduler |
| + var giantMessageLoopSlice = tr.c.TestUtils.newSliceEx({ |
| + cat: 'toplevel', |
| + title: messageLoopTaskTitle, |
| + start: 3000, |
| + duration: 3000 |
| + }); |
| + |
| + // back to back for 3000 |
| + var schedulerDoWork = tr.c.TestUtils.newSliceEx({ |
| + cat: 'not-toplevel', |
| + title: 'TaskQueueManager::DoWork', |
| + start: 3500, |
| + duration: 2000 |
| + }); |
| + |
| + for (var start = 3500; start < 3500 + 2000; start += taskDur) { |
| + schedulerDoWork.subSlices.push(newSchedulerTask(start, taskDur)); |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
nit: no need for braces
|
| + } |
| + |
| + giantMessageLoopSlice.subSlices.push(schedulerDoWork); |
| + mainThread.sliceGroup.pushSlice(giantMessageLoopSlice); |
| + }) |
| + |
| + var numeric = computeEQT90Numeric(model); |
| + assert.equal(numeric.running.count, 1); |
| + assert.closeTo(numeric.running.mean, 30, FPTOLERANCE); |
| + }); |
| + |
| + |
|
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: extra blank line
dproy
2016/09/20 01:11:43
Done.
|
| + // riskPercentiles tests ported from Lighthouse |
| + var defaultPercentiles = [0, .25, .50, .75, .90, .99, 1]; |
| + |
| + function verifyAllResults(results, expected) { |
| + for (var i = 0; i < defaultPercentiles.length; i++) { |
| + var computedEqt = results.get(defaultPercentiles[i]); |
| + var expectedEqt = expected[i]; |
| + console.log(results, expected, i); |
| + assert.closeTo(computedEqt, expectedEqt, FPTOLERANCE); |
| + } |
| + } |
| + |
| + test('riskPercentiles.PercentileOfNoTasks', () => { |
| + var results = calculateEILRiskPercentiles([], 100, defaultPercentiles); |
| + var expected = [0, 0, 0, 0, 0, 0, 0]; |
| + verifyAllResults(results, expected); |
| + }); |
| + |
| + test('riskPercentiles.singleTaskWithIdleTime', () => { |
| + var results = calculateEILRiskPercentiles([50], 100, defaultPercentiles); |
| + var expected = [0, 0, 0, 25, 40, 49, 50]; |
| + verifyAllResults(results, expected); |
| + }); |
| + |
| + test('riskPercentiles.singleTaskNoIdleTime', () => { |
| + var results = calculateEILRiskPercentiles([50], 50, defaultPercentiles); |
| + var expected = [0, 12.5, 25, 37.5, 45, 49.5, 50]; |
| + verifyAllResults(results, expected); |
| + }); |
| + |
| + test('riskPercentiles.severalEqualLengthTasks', () => { |
| + var results = calculateEILRiskPercentiles( |
| + [50, 50, 50, 50], 400, defaultPercentiles); |
| + var expected = [0, 0, 0, 25, 40, 49, 50]; |
| + verifyAllResults(results, expected); |
| + }); |
| + |
| + test('riskPercentiles.zeroDurationTask', () => { |
| + var results = calculateEILRiskPercentiles( |
| + [0, 0, 0, 10, 20, 20, 30, 30, 120], 320, defaultPercentiles); |
| + var expected = [0, 0, 12, 40, 88, 116.8, 120]; |
| + verifyAllResults(results, expected); |
| + }); |
| + |
| + test('riskPercentiles.threeOneSecondInFiveSecondWindow', () => { |
| + // Three tasks of one second each, all within a five-second window. |
| + // Mean Queueing Time of 300ms. |
| + var results = calculateEILRiskPercentiles( |
| + [1000, 1000, 1000], 5000, defaultPercentiles, 0); |
| + var expected = [0, 0, 166.67, 583.33, 833.33, 983.33, 1000]; |
| + verifyAllResults(results, expected); |
| + }); |
| + |
| + test('riskPercentiles.clippedTask', () => { |
| + var results = calculateEILRiskPercentiles( |
| + [10, 20, 50, 60, 90, 100], 300, defaultPercentiles, 30); |
| + var expected = [0, 16.25, 37.5, 58.33, 80, 97, 100]; |
| + verifyAllResults(results, expected); |
| + |
| + }); |
| + |
| + test('riskPercentiles.singleTaskOverMultipleWindows', () => { |
| + // One 20 second long task over three five-second windows. |
| + |
| + // Starts 3 seconds into the first window. Mean Queueing Time = 7600ms. |
| + var TASK_LENGTH = 20000; |
| + let results1 = calculateEILRiskPercentiles( |
| + [TASK_LENGTH], 5000, defaultPercentiles, TASK_LENGTH - 2000); |
| + var expected1 = [0, 0, 0, 18750, 19500, 19950, 20000]; |
| + verifyAllResults(results1, expected1); |
| + |
| + // Starts 2 seconds before and ends 13 seconds after. |
| + // Mean Queueing Time = 15500ms. |
| + var results2 = calculateEILRiskPercentiles( |
| + [TASK_LENGTH - 2000], 5000, defaultPercentiles, TASK_LENGTH - 7000); |
| + var expected2 = [0, 14250, 15500, 16750, 17500, 17950, 18000]; |
| + verifyAllResults(results2, expected2); |
| + |
| + // Starts 17 seconds before and ends 3 seconds into the window. |
| + // Mean Queueing Time = 900ms. |
| + var results3 = calculateEILRiskPercentiles( |
| + [TASK_LENGTH - 17000], 5000, defaultPercentiles, 0); |
| + var expected3 = [0, 0, 500, 1750, 2500, 2950, 3000]; |
| + verifyAllResults(results3, expected3); |
| + }); |
| + |
| + test('riskPercentiles.oneTaskShorterThanClippedLengthOfAnother', () => { |
| + var results = calculateEILRiskPercentiles( |
| + [40, 100], 100, defaultPercentiles, 50); |
| + var expected = [0, 15, 40, 75, 90, 99, 100]; |
| + verifyAllResults(results, expected); |
| + }); |
| + |
| + test('riskPercentiles.completelyClippedTask', () => { |
| + var results = calculateEILRiskPercentiles( |
| + [40, 100], 100, defaultPercentiles, 100); |
| + var expected = [0, 0, 0, 15, 30, 39, 40]; |
| + verifyAllResults(results, expected); |
| + }); |
| + |
| + test('riskPercentiles.doesNotDivideByZeroWhenSumLessThanWhole', () => { |
| + // Durations chosen such that, due to floating point error: |
| + // var idleTime = totalTime - (duration1 + duration2); |
| + // (idleTime + duration1 + duration2) < totalTime |
| + // Note(dproy): Since we now supply the percentiles as ints instead of |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
Please make this NOTE: and not Note(dproy). We usu
dproy
2016/09/20 01:11:43
This note is obselete because I changed percentile
|
| + // floats it's not clear if this test is still effective. If a divide by |
| + // zero bug resurfaces, this test should be fixed. |
| + var duration1 = 67 / 107; |
| + var duration2 = 67 / 53; |
| + var totalTime = 10; |
| + var results = calculateEILRiskPercentiles( |
| + [duration1, duration2], totalTime, [1], 0); |
|
charliea (OOO until 10-5)
2016/09/19 14:35:34
nit: line continuations (here and elsewhere)
dproy
2016/09/20 01:11:44
Done (here and elsewhere)
|
| + var expected = duration2; |
| + assert.closeTo(results.get(1), expected, FPTOLERANCE); |
| + }); |
| + |
| +}); |
| +</script> |