<!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>