<!DOCTYPE HTML> <html> <!-- Copyright (c) 2012 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. --> <head i18n-values="dir:textdirection;"> <title>TimelineTrack tests</title> <script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"> </script> <script> goog.require('goog.testing.jsunit'); </script> <style> * { box-sizing: border-box; -webkit-user-select: none; } .timeline-container { border: 1px solid red; } </style> <link rel="stylesheet" href="timeline.css"> <script src="../shared/js/cr.js"></script> <script src="../shared/js/cr/event_target.js"></script> <script src="../shared/js/cr/ui.js"></script> <script src="../shared/js/util.js"></script> <script src="timeline_model.js"></script> <script src="sorted_array_utils.js"></script> <script src="measuring_stick.js"></script> <script src="timeline.js"></script> <script src="timeline_track.js"></script> <script src="fast_rect_renderer.js"></script> </head> <body> <script> </script> <script> 'use strict'; var TimelineAsyncSlice = tracing.TimelineAsyncSlice; var TimelineAsyncSliceGroup = tracing.TimelineAsyncSliceGroup; var TimelineCounter = tracing.TimelineCounter; var TimelineCounterTrack = tracing.TimelineCounterTrack; var TimelineCpu = tracing.TimelineCpu; var TimelineCpuTrack = tracing.TimelineCpuTrack; var TimelineProcess = tracing.TimelineProcess; var TimelineSelection = tracing.TimelineSelection; var TimelineSliceTrack = tracing.TimelineSliceTrack; var TimelineSlice = tracing.TimelineSlice; var TimelineThread = tracing.TimelineThread; var TimelineThreadSlice = tracing.TimelineThreadSlice; var TimelineThreadTrack = tracing.TimelineThreadTrack; var TimelineViewport = tracing.TimelineViewport; var testDivs = {}; // Helper function to create a slice. function newAsyncSlice(start, duration, startThread, endThread) { var s = new TimelineAsyncSlice('a', 0, start); s.duration = duration; s.startThread = startThread; s.endThread = endThread; return s; } function getTestDiv(name) { if (!testDivs[name]) { testDivs[name] = document.createElement('div'); document.body.appendChild(testDivs[name]); } testDivs[name].textContent = ''; return testDivs[name]; } function testBasicSlices() { var testEl = getTestDiv('testBasicSlices'); var track = TimelineSliceTrack(); testEl.appendChild(track); track.heading = 'testBasicSlices'; track.slices = [ new TimelineSlice('a', 0, 1, {}, 1), new TimelineSlice('b', 1, 2.1, {}, 4.8), new TimelineSlice('b', 1, 7, {}, 0.5), new TimelineSlice('c', 2, 7.6, {}, 0.4) ]; track.viewport = new TimelineViewport(testEl); track.viewport.xSetWorldRange(0, 8.8, track.clientWidth); } function testFindAllObjectsMatchingInSliceTrack() { var track = TimelineSliceTrack(); track.slices = [ new TimelineSlice('a', 0, 1, {}, 1), new TimelineSlice('b', 1, 2.1, {}, 4.8), new TimelineSlice('b', 1, 7, {}, 0.5), new TimelineSlice('c', 2, 7.6, {}, 0.4) ]; var selection = new TimelineSelection(); track.addAllObjectsMatchingFilterToSelection( new tracing.TimelineFilter("b"), selection); assertEquals(2, selection.length); assertEquals(track.slices[1], selection[0].slice); assertEquals(track.slices[2], selection[1].slice); } function testShrinkingSliceSizes() { var testEl = getTestDiv('testShrinkingSliceSizes'); var track = TimelineSliceTrack(); testEl.appendChild(track); track.heading = 'testShrinkingSliceSizes'; var x = 0; var widths = [10, 5, 4, 3, 2, 1, 0.5, 0.4, 0.3, 0.2, 0.1, 0.05]; var slices = []; for (var i = 0; i < widths.length; i++) { var s = new TimelineSlice('a', 1, x, {}, widths[i]); x += s.duration + 0.5; slices.push(s); } track.slices = slices; track.viewport = new TimelineViewport(testEl); track.viewport.xSetWorldRange(0, 1.1 * x, track.clientWidth); } function testSelectionHitTesting() { var testEl = getTestDiv('testSelectionHitTesting'); var track = new TimelineSliceTrack(); testEl.appendChild(track); track.heading = 'testSelectionHitTesting'; track.headingWidth = '100px'; track.slices = [ new TimelineSlice('a', 0, 1, {}, 1), new TimelineSlice('b', 1, 2.1, {}, 4.8) ]; track.style.width = '500px'; track.viewport = new TimelineViewport(testEl); track.viewport.xSetWorldRange(0, 7.6, track.clientWidth); var clientRect = track.getBoundingClientRect(); var selection = new TimelineSelection(); track.addIntersectingItemsToSelection(1.5, clientRect.top + 5, selection); assertEquals(track.slices[0], selection[0].slice); var selection = new TimelineSelection(); track.addIntersectingItemsToSelection(2, clientRect.top + 5, selection); assertEquals(0, selection.length); var selection = new TimelineSelection(); track.addIntersectingItemsToSelection(6.8, clientRect.top + 5, selection); assertEquals(track.slices[1], selection[0].slice); var selection = new TimelineSelection(); track.addIntersectingItemsToSelection(6.9, clientRect.top + 5, selection); assertEquals(0, selection.length); } function testSelectionHitTestingWithTimelineThreadTrack() { var model = new tracing.TimelineModel(); var p1 = model.getOrCreateProcess(1); var t1 = p1.getOrCreateThread(1); t1.subRows[0].push(new tracing.TimelineThreadSlice('a', 0, 1, {}, 5)); t1.subRows[0].push(new tracing.TimelineThreadSlice('b', 0, 5.1, {}, 4)); var testEl = getTestDiv('testSelectionHitTestingWithTimelineThreadTrack'); var track = new tracing.TimelineThreadTrack(); testEl.appendChild(track); track.heading = 'testSelectionHitTestingWithTimelineThreadTrack'; track.headingWidth = '100px'; track.thread = t1; track.style.width = '500px'; track.viewport = new TimelineViewport(testEl); track.viewport.xSetWorldRange(0, 10, track.clientWidth); var clientRect = track.getBoundingClientRect(); var selection = new TimelineSelection(); track.addIntersectingItemsToSelection(1.5, clientRect.top + 5, selection); assertEquals(t1.subRows[0][0], selection[0].slice); var selection = new TimelineSelection(); track.addIntersectingItemsInRangeToSelection(1.5, 1.8, clientRect.top + 5, clientRect.top + 7, selection); assertEquals(t1.subRows[0][0], selection[0].slice); } function testBasicCpu() { var testEl = getTestDiv('testBasicCpu'); var cpu = new TimelineCpu(7); cpu.slices = [ new TimelineSlice('a', 0, 1, {}, 1), new TimelineSlice('b', 1, 2.1, {}, 4.8) ]; cpu.updateBounds(); var track = TimelineCpuTrack(); testEl.appendChild(track); track.heading = 'CPU ' + cpu.cpuNumber; track.cpu = cpu; track.viewport = new TimelineViewport(testEl); track.viewport.xSetWorldRange(0, 11.1, track.clientWidth); } function testViewport() { var testEl = getTestDiv('testViewport'); var track = tracing.TimelineViewportTrack(); testEl.appendChild(track); track.viewport = new TimelineViewport(testEl); track.viewport.setPanAndScale(0, track.clientWidth / 1000); } function testBasicCounter() { var testEl = getTestDiv('testBasicCounter'); var ctr = new TimelineCounter(undefined, 'testBasicCounter', 'testBasicCounter'); ctr.seriesNames = ['value1', 'value2']; ctr.seriesColors = [tracing.getStringColorId('testBasicCounter.value1'), tracing.getStringColorId('testBasicCounter.value2')]; ctr.timestamps = [0, 1, 2, 3, 4, 5, 6, 7]; ctr.samples = [0, 5, 3, 3, 1, 1, 2, 1.1, 3, 0, 1, 7, 3, 0, 3.1, 0.5]; ctr.updateBounds(); var track = new TimelineCounterTrack(); testEl.appendChild(track); track.heading = ctr.name; track.counter = ctr; track.viewport = new TimelineViewport(testEl); track.viewport.xSetWorldRange(0, 7.7, track.clientWidth); } function runOffscreenCounterTest(timestamps, samples, testFn) { var testEl = document.createElement('div'); var ctr = new TimelineCounter(undefined, 'foo', 'foo'); var n = samples.length / timestamps.length; ctr.timestamps = timestamps; ctr.samples = samples; ctr.seriesNames = [] ctr.seriesColors = [] for (var i = 0; i < n; ++i) { ctr.seriesNames.push('value' + i); ctr.seriesColors.push(tracing.getStringColorId(ctr.seriesNames[i])); } ctr.updateBounds(); var track = new TimelineCounterTrack(); testEl.appendChild(track); document.body.appendChild(testEl); track.heading = ctr.name; track.counter = ctr; track.viewport = new TimelineViewport(testEl); track.viewport.xSetWorldRange(0, 10, track.clientWidth); try { testFn(ctr, track); } finally { document.body.removeChild(testEl); } } function testBasicCounterXPointPicking() { var timestamps = [0, 1, 2, 3, 4, 5, 6, 7]; var samples = [0, 5, 3, 3, 1, 1, 2, 1.1, 3, 0, 1, 7, 3, 0, 3.1, 0.5]; runOffscreenCounterTest(timestamps, samples, function(ctr, track) { var clientRect = track.getBoundingClientRect(); var y75 = clientRect.top + 0.75 * clientRect.height; var sel; // In bounds. sel = new tracing.TimelineSelection(); track.addIntersectingItemsToSelection(1.5, y75, sel); assertEquals(1, sel.length); assertEquals(track, sel[0].track); assertEquals(ctr, sel[0].counter); assertEquals(1, sel[0].sampleIndex); // Outside bouds. sel = new tracing.TimelineSelection(); track.addIntersectingItemsToSelection(-1, y75, sel); assertEquals(0, sel.length); sel = new tracing.TimelineSelection(); track.addIntersectingItemsToSelection(8, y75, sel); assertEquals(0, sel.length); }); } /* You'll need visual inspection to test eliding with this one. */ function testElideVisualInspection() { var optDicts = [{ trackName: 'elideOff', elide: false }, { trackName: 'elideOn', elide: true }]; for (var dictIndex in optDicts) { var dict = optDicts[dictIndex]; var testEl = getTestDiv(dict.trackName); var track = new TimelineSliceTrack(); if (dict.elide) { track.SHOULD_ELIDE_TEXT = true; } else { track.SHOULD_ELIDE_TEXT = false; } var tooLongTitle = 'Unless eliding this SHOULD NOT BE DISPLAYED. '; var bigTitle = 'Very big title name that goes on longer ' + 'than you may expect'; testEl.appendChild(track); track.heading = 'Visual: ' + dict.trackName; track.slices = [ // title, colorId, start, args, opt_duration new TimelineSlice('a ' + tooLongTitle + bigTitle, 0, 1, {}, 1), new TimelineSlice(bigTitle, 1, 2.1, {}, 4.8), new TimelineSlice('cccc cccc cccc', 1, 7, {}, 0.5), new TimelineSlice('d', 2, 7.6, {}, 1.0) ]; track.viewport = new TimelineViewport(testEl); track.viewport.xSetWorldRange(0, 9.5, track.clientWidth); } } function testElide() { var testEl = getTestDiv('testElide'); var track = new TimelineSliceTrack(); testEl.appendChild(track); var bigtitle = 'Super duper long long title ' + 'holy moly when did you get so verbose?'; var smalltitle = 'small'; track.viewport = new TimelineViewport(testEl); track.heading = 'testElide'; track.slices = [ // title, colorId, start, args, opt_duration new TimelineSlice(bigtitle, 0, 1, {}, 1), new TimelineSlice(smalltitle, 1, 2, {}, 1) ]; track.viewport = new TimelineViewport(testEl); track.viewport.xSetWorldRange(0, 3.3, track.clientWidth); var stringWidthPair = undefined; var pixWidth = track.viewport_.xViewVectorToWorld(1); // Small titles on big slices are not elided. stringWidthPair = track.elidedTitleCache.get(track, pixWidth, smalltitle, track.labelWidth(smalltitle), 1); assertEquals(smalltitle, stringWidthPair.string); // Keep shrinking the slice until eliding starts. var elidedWhenSmallEnough = false; for (var sliceLength = 1; sliceLength >= 0.00001; sliceLength /= 2.0) { stringWidthPair = track.elidedTitleCache.get(track, pixWidth, smalltitle, track.labelWidth(smalltitle), sliceLength); if (stringWidthPair.string.length < smalltitle.length) { elidedWhenSmallEnough = true; break; } } assertTrue(elidedWhenSmallEnough); // Big titles are elided immediately. var superBigTitle = ''; for (var x = 0; x < 10; x++) { superBigTitle += bigtitle; } stringWidthPair = track.elidedTitleCache.get(track, pixWidth, superBigTitle, track.labelWidth(superBigTitle), 1); assertTrue(stringWidthPair.string.length < superBigTitle.length); // And elided text ends with ... var len = stringWidthPair.string.length; assertEquals('...', stringWidthPair.string.substring(len - 3, len)); } function testTimelineThreadTrackWithRegularSlices() { var testEl = getTestDiv('testTimelineThreadTrackWithRegularSlices'); var track = TimelineThreadTrack(); testEl.appendChild(track); track.heading = 'testTimelineThreadTrackWithRegularSlices'; var thread = new TimelineThread(new TimelineProcess(7), 1); thread.subRows = [ [ new TimelineThreadSlice('a', 0, 1, {}, 1), new TimelineThreadSlice('b', 1, 2.1, {}, 4.8), new TimelineThreadSlice('b', 1, 7, {}, 0.5), new TimelineThreadSlice('c', 2, 7.6, {}, 0.4) ], [ new TimelineThreadSlice('d', 3, 1.1, {}, 0.8), new TimelineThreadSlice('e', 4, 7.1, {}, 0.3) ] ]; thread.updateBounds(); track.heading = 'thread regular'; track.headingWidth = '150px'; track.toolTip = thread.userFriendlyDetails + ':'; track.thread = thread; track.viewport = new TimelineViewport(testEl); track.viewport.xSetWorldRange(0, 8.2, track.clientWidth); } function testTimelineThreadTrackWithTallSlices() { var testEl = getTestDiv('testTimelineThreadTrackWithTallSlices'); var track = TimelineThreadTrack(); testEl.appendChild(track); track.heading = 'testTimelineThreadTrackWithTallSlices'; var thread = new TimelineThread(new TimelineProcess(7), 1); thread.subRows = [ [new TimelineThreadSlice('a', 1, 0, {}, 1)], [new TimelineThreadSlice('b', 2, 0.1, {}, 0.8)], [new TimelineThreadSlice('c', 3, 0.15, {}, 0.70)], [new TimelineThreadSlice('d', 4, 0.20, {}, 0.50)], [new TimelineThreadSlice('e', 5, 0.30, {}, 0.28)], [new TimelineThreadSlice('e', 6, 0.35, {}, 0.20)], [new TimelineThreadSlice('f', 7, 0.40, {}, 0.10)] ]; thread.updateBounds(); track.heading = 'thread tall'; track.headingWidth = '150px'; track.toolTip = thread.userFriendlyDetails + ':'; track.thread = thread; track.viewport = new TimelineViewport(testEl); track.viewport.xSetWorldRange(0, 1.1, track.clientWidth); } function testTimelineThreadTrackWithRegularAndAsyncSlices() { var testEl = getTestDiv('testTimelineThreadTrackWithAsyncSlices'); var track = TimelineThreadTrack(); testEl.appendChild(track); var thread = new TimelineThread(new TimelineProcess(7), 1); thread.subRows = [ [ new TimelineThreadSlice('a', 0, 1, {}, 1), new TimelineThreadSlice('b', 1, 2.1, {}, 4.8), new TimelineThreadSlice('b', 1, 7, {}, 0.5), new TimelineThreadSlice('c', 2, 7.6, {}, 0.4) ], [ new TimelineThreadSlice('d', 3, 1.1, {}, 0.8), new TimelineThreadSlice('e', 4, 7.1, {}, 0.3) ] ]; thread.asyncSlices.push(newAsyncSlice(1.2, 7.2 - 1.2, thread, thread)); thread.asyncSlices.push(newAsyncSlice(1.3, 7.3 - 1.3, thread, thread)); thread.updateBounds(); track.heading = 'thread regular + async'; track.headingWidth = '150px'; track.toolTip = thread.userFriendlyDetails + ':'; track.thread = thread; track.viewport = new TimelineViewport(testEl); track.viewport.xSetWorldRange(0, 8.15, track.clientWidth); } function testTimelineSliceTrackAddItemNearToProvidedHit() { var track = new TimelineSliceTrack(); track.slices = [ new TimelineSlice('a', 0, 1, {}, 1), new TimelineSlice('b', 1, 2.1, {}, 4.8), new TimelineSlice('b', 1, 7, {}, 0.5), new TimelineSlice('c', 2, 7.6, {}, 0.4) ]; var sel = new tracing.TimelineSelection(); track.addAllObjectsMatchingFilterToSelection(new tracing.TimelineFilter("b"), sel); var ret; // Select to the right of B. var selRight = new tracing.TimelineSelection(); ret = track.addItemNearToProvidedHitToSelection(sel[0], 1, selRight); assertTrue(ret); assertEquals(track.slices[2], selRight[0].slice); // Select to the right of the 2nd b. var selRight2 = new tracing.TimelineSelection(); ret = track.addItemNearToProvidedHitToSelection(sel[0], 2, selRight2); assertTrue(ret); assertEquals(track.slices[3], selRight2[0].slice); // Select to 2 to the right of the 2nd b. var selRightOfRight = new tracing.TimelineSelection(); ret = track.addItemNearToProvidedHitToSelection(selRight[0], 1, selRightOfRight); assertTrue(ret); assertEquals(track.slices[3], selRightOfRight[0].slice); // Select to the right of the rightmost slice. var selNone = new tracing.TimelineSelection(); ret = track.addItemNearToProvidedHitToSelection(selRightOfRight[0], 1, selNone); assertFalse(ret); assertEquals(0, selNone.length); // Select A and then select left. var sel = new tracing.TimelineSelection(); track.addAllObjectsMatchingFilterToSelection(new tracing.TimelineFilter("a"), sel); var ret; selNone = new tracing.TimelineSelection(); ret = track.addItemNearToProvidedHitToSelection(sel[0], -1, selNone); assertFalse(ret); assertEquals(0, selNone.length); } </script> </body> </html>