Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / trace_viewer / tracing / importer / trace_event_importer_test.html
1 <!DOCTYPE html>
2 <!--
3 Copyright (c) 2013 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="/tracing/test_utils.html">
9 <link rel="import" href="/tracing/importer/trace_event_importer.html">
10
11 <script>
12 'use strict';
13
14 tvcm.unittest.testSuite(function() { // @suppress longLineCheck
15   var findSliceNamed = tracing.test_utils.findSliceNamed;
16
17   test('canImportEmpty', function() {
18     self.assertFalse(tracing.importer.TraceEventImporter.canImport([]));
19     self.assertFalse(tracing.importer.TraceEventImporter.canImport(''));
20   });
21
22   test('basicSingleThreadNonnestedParsing', function() {
23     var events = [
24       {name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
25       {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'},
26       {name: 'b', args: {}, pid: 52, ts: 629, cat: 'bar', tid: 53, ph: 'B'},
27       {name: 'b', args: {}, pid: 52, ts: 631, cat: 'bar', tid: 53, ph: 'E'}
28     ];
29
30     var m = new tracing.TraceModel(events);
31     assertEquals(1, m.numProcesses);
32     var p = m.processes[52];
33     assertNotUndefined(p);
34
35     assertEquals(1, p.numThreads);
36     var t = p.threads[53];
37     assertNotUndefined(t);
38     assertEquals(2, t.sliceGroup.length);
39     assertEquals(53, t.tid);
40     var slice = t.sliceGroup.slices[0];
41     assertEquals('a', slice.title);
42     assertEquals('foo', slice.category);
43     assertEquals(0, slice.start);
44     assertAlmostEquals((560 - 520) / 1000, slice.duration);
45     assertEquals(0, slice.subSlices.length);
46
47     slice = t.sliceGroup.slices[1];
48     assertEquals('b', slice.title);
49     assertEquals('bar', slice.category);
50     assertAlmostEquals((629 - 520) / 1000, slice.start);
51     assertAlmostEquals((631 - 629) / 1000, slice.duration);
52     assertEquals(0, slice.subSlices.length);
53   });
54
55   test('basicSingleThreadNonnestedParsingWithCpuDuration', function() {
56     var events = [
57       {name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B', tts: 221}, // @suppress longLineCheck
58       {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E', tts: 259}, // @suppress longLineCheck
59       {name: 'b', args: {}, pid: 52, ts: 629, cat: 'bar', tid: 53, ph: 'B', tts: 329}, // @suppress longLineCheck
60       {name: 'b', args: {}, pid: 52, ts: 631, cat: 'bar', tid: 53, ph: 'E', tts: 331}  // @suppress longLineCheck
61     ];
62
63     var m = new tracing.TraceModel(events);
64     assertEquals(1, m.numProcesses);
65     var p = m.processes[52];
66     assertNotUndefined(p);
67
68     assertEquals(1, p.numThreads);
69     var t = p.threads[53];
70     assertNotUndefined(t);
71     assertEquals(2, t.sliceGroup.length);
72     assertEquals(53, t.tid);
73     var slice = t.sliceGroup.slices[0];
74     assertEquals('a', slice.title);
75     assertEquals('foo', slice.category);
76     assertEquals(0, slice.start);
77     assertAlmostEquals((560 - 520) / 1000, slice.duration);
78     assertAlmostEquals((259 - 221) / 1000, slice.cpuDuration);
79     assertEquals(0, slice.subSlices.length);
80
81     slice = t.sliceGroup.slices[1];
82     assertEquals('b', slice.title);
83     assertEquals('bar', slice.category);
84     assertAlmostEquals((629 - 520) / 1000, slice.start);
85     assertAlmostEquals((631 - 629) / 1000, slice.duration);
86     assertAlmostEquals((331 - 329) / 1000, slice.cpuDuration);
87     assertEquals(0, slice.subSlices.length);
88   });
89
90   test('argumentDupeCreatesNonFailingImportError', function() {
91     var events = [
92       {name: 'a',
93         args: {'x': 1},
94         pid: 1,
95         ts: 520,
96         cat: 'foo',
97         tid: 1,
98         ph: 'B'},
99       {name: 'a',
100         args: {'x': 2},
101         pid: 1,
102         ts: 560,
103         cat: 'foo',
104         tid: 1,
105         ph: 'E'}
106     ];
107
108     var m = new tracing.TraceModel(events);
109     var t = m.processes[1].threads[1];
110     var sA = findSliceNamed(t.sliceGroup, 'a');
111
112     assertEquals(2, sA.args.x);
113     assertTrue(m.hasImportWarnings);
114     assertEquals(m.importWarnings.length, 1);
115   });
116
117   test('importMissingArgs', function() {
118     var events = [
119       {name: 'a', pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
120       {name: 'a', pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'},
121       {name: 'b', pid: 52, ts: 629, cat: 'bar', tid: 53, ph: 'I'}
122     ];
123
124     // This should not throw an exception.
125     new tracing.TraceModel(events);
126   });
127
128   test('importDoesNotChokeOnNulls', function() {
129     var events = [
130       {name: 'a', args: { foo: null }, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'}, // @suppress longLineCheck
131       {name: 'a', pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
132     ];
133
134     // This should not throw an exception.
135     new tracing.TraceModel(events);
136   });
137
138   test('categoryBeginEndMismatchPrefersBegin', function() {
139     var events = [
140       {name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
141       {name: 'a', args: {}, pid: 52, ts: 560, cat: 'bar', tid: 53, ph: 'E'}
142     ];
143
144     var m = new tracing.TraceModel(events);
145     assertEquals(1, m.numProcesses);
146     var p = m.processes[52];
147     assertNotUndefined(p);
148
149     assertEquals(1, p.numThreads);
150     var t = p.threads[53];
151     assertNotUndefined(t);
152     assertEquals(1, t.sliceGroup.length);
153     assertEquals(53, t.tid);
154     var slice = t.sliceGroup.slices[0];
155     assertEquals('a', slice.title);
156     assertEquals('foo', slice.category);
157   });
158
159   test('beginEndNameMismatch', function() {
160     var events = [
161       {name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
162       {name: 'b', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
163     ];
164
165     var m = new tracing.TraceModel(events);
166     assertTrue(m.hasImportWarnings);
167     assertEquals(1, m.importWarnings.length);
168   });
169
170   test('nestedParsing', function() {
171     var events = [
172       {name: 'a', args: {}, pid: 1, ts: 1, tts: 1, cat: 'foo', tid: 1, ph: 'B'},
173       {name: 'b', args: {}, pid: 1, ts: 2, tts: 2, cat: 'bar', tid: 1, ph: 'B'},
174       {name: 'b', args: {}, pid: 1, ts: 3, tts: 3, cat: 'bar', tid: 1, ph: 'E'},
175       {name: 'a', args: {}, pid: 1, ts: 4, tts: 3, cat: 'foo', tid: 1, ph: 'E'}
176     ];
177     var m = new tracing.TraceModel(events, false);
178     var t = m.processes[1].threads[1];
179
180     var sA = findSliceNamed(t.sliceGroup, 'a');
181     var sB = findSliceNamed(t.sliceGroup, 'b');
182
183     assertEquals('a', sA.title);
184     assertEquals('foo', sA.category);
185     assertEquals(0.001, sA.start);
186     assertEquals(0.003, sA.duration);
187     assertEquals(0.002, sA.selfTime);
188     assertEquals(0.001, sA.cpuSelfTime);
189
190     assertEquals('b', sB.title);
191     assertEquals('bar', sB.category);
192     assertEquals(0.002, sB.start);
193     assertEquals(0.001, sB.duration);
194
195     assertTrue(sA.subSlices.length == 1);
196     assertTrue(sA.subSlices[0] == sB);
197     assertTrue(sB.parentSlice == sA);
198   });
199
200   test('nestedParsingWithTwoSubSlices', function() {
201     var events = [
202       {name: 'a', args: {}, pid: 1, ts: 1, tts: 1, cat: 'foo', tid: 1, ph: 'B'},
203       {name: 'b', args: {}, pid: 1, ts: 2, tts: 2, cat: 'bar', tid: 1, ph: 'B'},
204       {name: 'b', args: {}, pid: 1, ts: 3, tts: 3, cat: 'bar', tid: 1, ph: 'E'},
205       {name: 'c', args: {}, pid: 1, ts: 5, tts: 5, cat: 'baz', tid: 1, ph: 'B'},
206       {name: 'c', args: {}, pid: 1, ts: 7, tts: 6, cat: 'baz', tid: 1, ph: 'E'},
207       {name: 'a', args: {}, pid: 1, ts: 8, tts: 8, cat: 'foo', tid: 1, ph: 'E'}
208     ];
209     var m = new tracing.TraceModel(events, false);
210     var t = m.processes[1].threads[1];
211
212     var sA = findSliceNamed(t.sliceGroup, 'a');
213     var sB = findSliceNamed(t.sliceGroup, 'b');
214     var sC = findSliceNamed(t.sliceGroup, 'c');
215
216     assertEquals('a', sA.title);
217     assertEquals('foo', sA.category);
218     assertEquals(0.001, sA.start);
219     assertEquals(0.007, sA.duration);
220     assertEquals(0.004, sA.selfTime);
221     assertEquals(0.005, sA.cpuSelfTime);
222
223     assertEquals('b', sB.title);
224     assertEquals('bar', sB.category);
225     assertEquals(0.002, sB.start);
226     assertEquals(0.001, sB.duration);
227
228     assertEquals('c', sC.title);
229     assertEquals('baz', sC.category);
230     assertEquals(0.005, sC.start);
231     assertEquals(0.002, sC.duration);
232
233     assertTrue(sA.subSlices.length == 2);
234     assertTrue(sA.subSlices[0] == sB);
235     assertTrue(sA.subSlices[1] == sC);
236     assertTrue(sB.parentSlice == sA);
237     assertTrue(sC.parentSlice == sA);
238   });
239
240   test('nestedParsingWithDoubleNesting', function() {
241     var events = [
242       {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
243       {name: 'b', args: {}, pid: 1, ts: 2, cat: 'bar', tid: 1, ph: 'B'},
244       {name: 'c', args: {}, pid: 1, ts: 3, cat: 'baz', tid: 1, ph: 'B'},
245       {name: 'c', args: {}, pid: 1, ts: 5, cat: 'baz', tid: 1, ph: 'E'},
246       {name: 'b', args: {}, pid: 1, ts: 7, cat: 'bar', tid: 1, ph: 'E'},
247       {name: 'a', args: {}, pid: 1, ts: 8, cat: 'foo', tid: 1, ph: 'E'}
248     ];
249     var m = new tracing.TraceModel(events, false);
250     var t = m.processes[1].threads[1];
251
252     var sA = findSliceNamed(t.sliceGroup, 'a');
253     var sB = findSliceNamed(t.sliceGroup, 'b');
254     var sC = findSliceNamed(t.sliceGroup, 'c');
255
256     assertEquals('a', sA.title);
257     assertEquals('foo', sA.category);
258     assertEquals(0.001, sA.start);
259     assertEquals(0.007, sA.duration);
260     assertEquals(0.002, sA.selfTime);
261
262     assertEquals('b', sB.title);
263     assertEquals('bar', sB.category);
264     assertEquals(0.002, sB.start);
265     assertEquals(0.005, sB.duration);
266     assertEquals(0.002, sA.selfTime);
267
268     assertEquals('c', sC.title);
269     assertEquals('baz', sC.category);
270     assertEquals(0.003, sC.start);
271     assertEquals(0.002, sC.duration);
272
273     assertTrue(sA.subSlices.length == 1);
274     assertTrue(sA.subSlices[0] == sB);
275     assertTrue(sB.parentSlice == sA);
276
277     assertTrue(sB.subSlices.length == 1);
278     assertTrue(sB.subSlices[0] == sC);
279     assertTrue(sC.parentSlice == sB);
280   });
281
282
283   test('autoclosing', function() {
284     var events = [
285       // Slice that doesn't finish.
286       {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
287
288       // Slice that does finish to give an 'end time' to make autoclosing work.
289       {name: 'b', args: {}, pid: 1, ts: 1, cat: 'bar', tid: 2, ph: 'B'},
290       {name: 'b', args: {}, pid: 1, ts: 2, cat: 'bar', tid: 2, ph: 'E'}
291     ];
292     var m = new tracing.TraceModel(events);
293     var p = m.processes[1];
294     var t = p.threads[1];
295     var slice = t.sliceGroup.slices[0];
296     assertEquals('a', slice.title);
297     assertEquals('foo', slice.category);
298     assertTrue(slice.didNotFinish);
299     assertEquals(0, slice.start);
300     assertEquals((2 - 1) / 1000, slice.duration);
301   });
302
303   test('autoclosingLoneBegin', function() {
304     var events = [
305       // Slice that doesn't finish.
306       {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'}
307     ];
308     var m = new tracing.TraceModel(events);
309     var p = m.processes[1];
310     var t = p.threads[1];
311     var slice = t.sliceGroup.slices[0];
312     assertEquals('a', slice.title);
313     assertEquals('foo', slice.category);
314     assertTrue(slice.didNotFinish);
315     assertEquals(0, slice.start);
316     assertEquals(0, slice.duration);
317   });
318
319   test('autoclosingWithSubTasks', function() {
320     var events = [
321       {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
322       {name: 'b1', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 1, ph: 'B'},
323       {name: 'b1', args: {}, pid: 1, ts: 3, cat: 'foo', tid: 1, ph: 'E'},
324       {name: 'b2', args: {}, pid: 1, ts: 3, cat: 'foo', tid: 1, ph: 'B'}
325     ];
326     var m = new tracing.TraceModel(events, false);
327     var t = m.processes[1].threads[1];
328
329     var sA = findSliceNamed(t.sliceGroup, 'a');
330     var sB1 = findSliceNamed(t.sliceGroup, 'b1');
331     var sB2 = findSliceNamed(t.sliceGroup, 'b2');
332
333     assertEquals(0.003, sA.end);
334     assertEquals(0.003, sB1.end);
335     assertEquals(0.003, sB2.end);
336   });
337
338   test('autoclosingWithEventsOutsideBounds', function() {
339     var events = [
340       // Slice that begins before min and ends after max of the other threads.
341       {name: 'a', args: {}, pid: 1, ts: 0, cat: 'foo', tid: 1, ph: 'B'},
342       {name: 'b', args: {}, pid: 1, ts: 3, cat: 'foo', tid: 1, ph: 'B'},
343
344       // Slice that does finish to give an 'end time' to establish a basis
345       {name: 'c', args: {}, pid: 1, ts: 1, cat: 'bar', tid: 2, ph: 'B'},
346       {name: 'c', args: {}, pid: 1, ts: 2, cat: 'bar', tid: 2, ph: 'E'}
347     ];
348     var m = new tracing.TraceModel(events, false);
349     var p = m.processes[1];
350     var t = p.threads[1];
351     assertEquals(2, t.sliceGroup.length);
352
353     var slice = findSliceNamed(t.sliceGroup, 'a');
354     assertEquals('a', slice.title);
355     assertEquals('foo', slice.category);
356     assertEquals(0, slice.start);
357     assertEquals(0.003, slice.duration);
358
359     var t2 = p.threads[2];
360     var slice2 = findSliceNamed(t2.sliceGroup, 'c');
361     assertEquals('c', slice2.title);
362     assertEquals('bar', slice2.category);
363     assertEquals(0.001, slice2.start);
364     assertEquals(0.001, slice2.duration);
365
366     assertEquals(0.000, m.bounds.min);
367     assertEquals(0.003, m.bounds.max);
368   });
369
370   test('nestedAutoclosing', function() {
371     var events = [
372       // Tasks that don't finish.
373       {name: 'a1', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
374       {name: 'a2', args: {}, pid: 1, ts: 1.5, cat: 'foo', tid: 1, ph: 'B'},
375
376       // Slice that does finish to give an 'end time' to make autoclosing work.
377       {name: 'b', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 2, ph: 'B'},
378       {name: 'b', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 2, ph: 'E'}
379     ];
380     var m = new tracing.TraceModel(events, false);
381     var t1 = m.processes[1].threads[1];
382     var t2 = m.processes[1].threads[2];
383
384     var sA1 = findSliceNamed(t1.sliceGroup, 'a1');
385     var sA2 = findSliceNamed(t1.sliceGroup, 'a2');
386     var sB = findSliceNamed(t2.sliceGroup, 'b');
387
388     assertEquals(0.002, sA1.end);
389     assertEquals(0.002, sA2.end);
390   });
391
392   test('taskColoring', function() {
393     // The test below depends on hashing of 'a' != 'b'. Fail early if that
394     // assumption is incorrect.
395     assertNotEquals(tvcm.ui.getStringHash('a'), tvcm.ui.getStringHash('b'));
396
397     var events = [
398       {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
399       {name: 'a', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 1, ph: 'E'},
400       {name: 'b', args: {}, pid: 1, ts: 3, cat: 'bar', tid: 1, ph: 'B'},
401       {name: 'b', args: {}, pid: 1, ts: 4, cat: 'bar', tid: 1, ph: 'E'},
402       {name: 'a', args: {}, pid: 1, ts: 5, cat: 'baz', tid: 1, ph: 'B'},
403       {name: 'a', args: {}, pid: 1, ts: 6, cat: 'baz', tid: 1, ph: 'E'}
404     ];
405     var m = new tracing.TraceModel(events);
406     var p = m.processes[1];
407     var t = p.threads[1];
408     var a1 = t.sliceGroup.slices[0];
409     assertEquals('a', a1.title);
410     assertEquals('foo', a1.category);
411     var b = t.sliceGroup.slices[1];
412     assertEquals('b', b.title);
413     assertEquals('bar', b.category);
414     assertNotEquals(a1.colorId, b.colorId);
415     var a2 = t.sliceGroup.slices[2];
416     assertEquals('a', a2.title);
417     assertEquals('baz', a2.category);
418     assertEquals(a1.colorId, a2.colorId);
419   });
420
421   test('multipleThreadParsing', function() {
422     var events = [
423       {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
424       {name: 'a', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 1, ph: 'E'},
425       {name: 'b', args: {}, pid: 1, ts: 3, cat: 'bar', tid: 2, ph: 'B'},
426       {name: 'b', args: {}, pid: 1, ts: 4, cat: 'bar', tid: 2, ph: 'E'}
427     ];
428     var m = new tracing.TraceModel(events);
429     assertEquals(1, m.numProcesses);
430     var p = m.processes[1];
431     assertNotUndefined(p);
432
433     assertEquals(2, p.numThreads);
434
435     // Check thread 1.
436     var t = p.threads[1];
437     assertNotUndefined(t);
438     assertEquals(1, t.sliceGroup.length);
439     assertEquals(1, t.tid);
440
441     var slice = t.sliceGroup.slices[0];
442     assertEquals('a', slice.title);
443     assertEquals('foo', slice.category);
444     assertEquals(0, slice.start);
445     assertEquals((2 - 1) / 1000, slice.duration);
446     assertEquals(0, slice.subSlices.length);
447
448     // Check thread 2.
449     var t = p.threads[2];
450     assertNotUndefined(t);
451     assertEquals(1, t.sliceGroup.length);
452     assertEquals(2, t.tid);
453
454     slice = t.sliceGroup.slices[0];
455     assertEquals('b', slice.title);
456     assertEquals('bar', slice.category);
457     assertEquals((3 - 1) / 1000, slice.start);
458     assertEquals((4 - 3) / 1000, slice.duration);
459     assertEquals(0, slice.subSlices.length);
460   });
461
462   test('multiplePidParsing', function() {
463     var events = [
464       {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
465       {name: 'a', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 1, ph: 'E'},
466       {name: 'b', args: {}, pid: 2, ts: 3, cat: 'bar', tid: 2, ph: 'B'},
467       {name: 'b', args: {}, pid: 2, ts: 4, cat: 'bar', tid: 2, ph: 'E'}
468     ];
469     var m = new tracing.TraceModel(events);
470     assertEquals(2, m.numProcesses);
471     var p = m.processes[1];
472     assertNotUndefined(p);
473
474     assertEquals(1, p.numThreads);
475
476     // Check process 1 thread 1.
477     var t = p.threads[1];
478     assertNotUndefined(t);
479     assertEquals(1, t.sliceGroup.length);
480     assertEquals(1, t.tid);
481
482     var slice = t.sliceGroup.slices[0];
483     assertEquals('a', slice.title);
484     assertEquals('foo', slice.category);
485     assertEquals(0, slice.start);
486     assertEquals((2 - 1) / 1000, slice.duration);
487     assertEquals(0, slice.subSlices.length);
488
489     // Check process 2 thread 2.
490     var p = m.processes[2];
491     assertNotUndefined(p);
492     assertEquals(1, p.numThreads);
493     var t = p.threads[2];
494     assertNotUndefined(t);
495     assertEquals(1, t.sliceGroup.length);
496     assertEquals(2, t.tid);
497
498     slice = t.sliceGroup.slices[0];
499     assertEquals('b', slice.title);
500     assertEquals('bar', slice.category);
501     assertEquals((3 - 1) / 1000, slice.start);
502     assertEquals((4 - 3) / 1000, slice.duration);
503     assertEquals(0, slice.subSlices.length);
504
505     // Check getAllThreads.
506     assertArrayEquals([m.processes[1].threads[1], m.processes[2].threads[2]],
507                       m.getAllThreads());
508   });
509
510   // Process names.
511   test('processNames', function() {
512     var events = [
513       {name: 'process_name', args: {name: 'SomeProcessName'},
514         pid: 1, ts: 0, tid: 1, ph: 'M'},
515       {name: 'process_name', args: {name: 'SomeProcessName'},
516         pid: 2, ts: 0, tid: 1, ph: 'M'}
517     ];
518     var m = new tracing.TraceModel();
519     m.importTraces([events], false, false);
520     assertEquals('SomeProcessName', m.processes[1].name);
521   });
522
523   // Process labels.
524   test('processLabels', function() {
525     var events = [
526       {name: 'process_labels', args: {labels: 'foo,bar,bar,foo,baz'},
527         pid: 1, ts: 0, tid: 1, ph: 'M'},
528       {name: 'process_labels', args: {labels: 'baz'},
529         pid: 2, ts: 0, tid: 1, ph: 'M'}
530     ];
531     var m = new tracing.TraceModel();
532     m.importTraces([events], false, false);
533     assertArrayEquals(['foo', 'bar', 'baz'], m.processes[1].labels);
534     assertArrayEquals(['baz'], m.processes[2].labels);
535   });
536
537   // Process sort index.
538   test('processSortIndex', function() {
539     var events = [
540       {name: 'process_name', args: {name: 'First'},
541         pid: 2, ts: 0, tid: 1, ph: 'M'},
542       {name: 'process_name', args: {name: 'Second'},
543         pid: 2, ts: 0, tid: 1, ph: 'M'},
544       {name: 'process_sort_index', args: {sort_index: 1},
545         pid: 1, ts: 0, tid: 1, ph: 'M'}
546     ];
547     var m = new tracing.TraceModel();
548     m.importTraces([events], false, false);
549
550     // By name, p1 is before p2. But, its sort index overrides that.
551     assertTrue(m.processes[1].compareTo(m.processes[2]) > 0);
552   });
553
554   // Thread names.
555   test('threadNames', function() {
556     var events = [
557       {name: 'thread_name', args: {name: 'Thread 1'},
558         pid: 1, ts: 0, tid: 1, ph: 'M'},
559       {name: 'thread_name', args: {name: 'Thread 2'},
560         pid: 2, ts: 0, tid: 2, ph: 'M'}
561     ];
562     var m = new tracing.TraceModel(events);
563     m.importTraces([events], false, false);
564     assertEquals('Thread 1', m.processes[1].threads[1].name);
565     assertEquals('Thread 2', m.processes[2].threads[2].name);
566   });
567
568   // Thread sort index.
569   test('threadSortIndex', function() {
570     var events = [
571       {name: 'thread_name', args: {name: 'Thread 1'},
572         pid: 1, ts: 0, tid: 1, ph: 'M'},
573       {name: 'thread_name', args: {name: 'Thread 2'},
574         pid: 1, ts: 0, tid: 2, ph: 'M'},
575       {name: 'thread_sort_index', args: {sort_index: 1},
576         pid: 1, ts: 0, tid: 1, ph: 'M'}
577     ];
578     var m = new tracing.TraceModel();
579     m.importTraces([events], false, false);
580
581     // By name, t1 is before t2. But, its sort index overrides that.
582     var t1 = m.processes[1].threads[1];
583     var t2 = m.processes[1].threads[2];
584     assertTrue(t1.compareTo(t2) > 0);
585   });
586
587   // CPU counts.
588   test('cpuCounts', function() {
589     var events = [
590       {name: 'num_cpus', args: {number: 4},
591         pid: 7, ts: 0, tid: 0, ph: 'M'},
592       {name: 'num_cpus', args: {number: 4},
593         pid: 14, ts: 0, tid: 0, ph: 'M'}
594     ];
595     var m = new tracing.TraceModel();
596     m.importTraces([events], false, false);
597     assertEquals(4, m.kernel.softwareMeasuredCpuCount);
598     assertEquals(4, m.kernel.bestGuessAtCpuCount);
599   });
600
601   test('cpuCountsWithSandboxBeingConfused', function() {
602     var events = [
603       {name: 'num_cpus', args: {number: 4},
604         pid: 7, ts: 0, tid: 0, ph: 'M'},
605       {name: 'num_cpus', args: {number: 1},
606         pid: 14, ts: 0, tid: 0, ph: 'M'}
607     ];
608     var m = new tracing.TraceModel();
609     m.importTraces([events], false, false);
610     assertEquals(4, m.kernel.softwareMeasuredCpuCount);
611     assertEquals(4, m.kernel.bestGuessAtCpuCount);
612   });
613
614   test('parsingWhenEndComesFirst', function() {
615     var events = [
616       {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'E'},
617       {name: 'a', args: {}, pid: 1, ts: 4, cat: 'foo', tid: 1, ph: 'B'},
618       {name: 'a', args: {}, pid: 1, ts: 5, cat: 'foo', tid: 1, ph: 'E'}
619     ];
620     var m = new tracing.TraceModel(events, false);
621     var p = m.processes[1];
622     var t = p.threads[1];
623     assertEquals(1, t.sliceGroup.length);
624     assertEquals('a', t.sliceGroup.slices[0].title);
625     assertEquals('foo', t.sliceGroup.slices[0].category);
626     assertEquals(0.004, t.sliceGroup.slices[0].start);
627     assertEquals(0.001, t.sliceGroup.slices[0].duration);
628     assertTrue(m.hasImportWarnings);
629     assertEquals(1, m.importWarnings.length);
630   });
631
632   test('immediateParsing', function() {
633     var events = [
634       // Need to include immediates inside a task so the timeline
635       // recentering/zeroing doesn't clobber their timestamp.
636       {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
637       {name: 'immediate', args: {}, pid: 1, ts: 2, cat: 'bar', tid: 1, ph: 'I'},
638       {name: 'slower', args: {}, pid: 1, ts: 4, cat: 'baz', tid: 1, ph: 'i'},
639       {name: 'a', args: {}, pid: 1, ts: 4, cat: 'foo', tid: 1, ph: 'E'}
640     ];
641     var m = new tracing.TraceModel(events, false);
642     var p = m.processes[1];
643     var t = p.threads[1];
644
645     assertEquals(3, t.sliceGroup.length);
646     assertEquals(0.001, t.sliceGroup.slices[0].start);
647     assertEquals(0.003, t.sliceGroup.slices[0].duration);
648     assertEquals(0.002, t.sliceGroup.slices[1].start);
649     assertEquals(0, t.sliceGroup.slices[1].duration);
650     assertEquals(0.004, t.sliceGroup.slices[2].start);
651
652     var slice = findSliceNamed(t.sliceGroup, 'a');
653     assertEquals('a', slice.title);
654     assertEquals('foo', slice.category);
655     assertEquals(0.003, slice.duration);
656
657     var immed = findSliceNamed(t.sliceGroup, 'immediate');
658     assertEquals('immediate', immed.title);
659     assertEquals('bar', immed.category);
660     assertEquals(0.002, immed.start);
661     assertEquals(0, immed.duration);
662
663     var slower = findSliceNamed(t.sliceGroup, 'slower');
664     assertEquals('slower', slower.title);
665     assertEquals('baz', slower.category);
666     assertEquals(0.004, slower.start);
667     assertEquals(0, slower.duration);
668   });
669
670   test('simpleCounter', function() {
671     var events = [
672       {name: 'ctr', args: {'value': 0}, pid: 1, ts: 0, cat: 'foo', tid: 1,
673         ph: 'C'},
674       {name: 'ctr', args: {'value': 10}, pid: 1, ts: 10, cat: 'foo', tid: 1,
675         ph: 'C'},
676       {name: 'ctr', args: {'value': 0}, pid: 1, ts: 20, cat: 'foo', tid: 1,
677         ph: 'C'}
678
679     ];
680     var m = new tracing.TraceModel(events);
681     var p = m.processes[1];
682     var ctr = m.processes[1].counters['foo.ctr'];
683
684     assertEquals('ctr', ctr.name);
685     assertEquals('foo', ctr.category);
686     assertEquals(3, ctr.numSamples);
687     assertEquals(1, ctr.numSeries);
688
689     assertEquals('value', ctr.series[0].name);
690     assertEquals(tvcm.ui.getStringColorId('ctr.value'), ctr.series[0].color);
691
692     assertArrayEquals([0, 0.01, 0.02], ctr.timestamps);
693
694     var samples = [];
695     ctr.series[0].samples.forEach(function(sample) {
696       samples.push(sample.value);
697     });
698     assertArrayEquals([0, 10, 0], samples);
699
700     assertArrayEquals([0, 10, 0], ctr.totals);
701     assertEquals(10, ctr.maxTotal);
702   });
703
704   test('instanceCounter', function() {
705     var events = [
706       {name: 'ctr', args: {'value': 0}, pid: 1, ts: 0, cat: 'foo', tid: 1,
707         ph: 'C', id: 0},
708       {name: 'ctr', args: {'value': 10}, pid: 1, ts: 10, cat: 'foo', tid: 1,
709         ph: 'C', id: 0},
710       {name: 'ctr', args: {'value': 10}, pid: 1, ts: 10, cat: 'foo', tid: 1,
711         ph: 'C', id: 1},
712       {name: 'ctr', args: {'value': 20}, pid: 1, ts: 15, cat: 'foo', tid: 1,
713         ph: 'C', id: 1},
714       {name: 'ctr', args: {'value': 30}, pid: 1, ts: 18, cat: 'foo', tid: 1,
715         ph: 'C', id: 1},
716       {name: 'ctr', args: {'value': 40}, pid: 1, ts: 20, cat: 'bar', tid: 1,
717         ph: 'C', id: 2}
718     ];
719     var m = new tracing.TraceModel(events);
720     var p = m.processes[1];
721     var ctr = m.processes[1].counters['foo.ctr[0]'];
722     assertEquals('ctr[0]', ctr.name);
723     assertEquals('foo', ctr.category);
724     assertEquals(2, ctr.numSamples);
725     assertEquals(1, ctr.numSeries);
726
727     assertArrayEquals([0, 0.01], ctr.timestamps);
728     var samples = [];
729     ctr.series[0].samples.forEach(function(sample) {
730       samples.push(sample.value);
731     });
732     assertArrayEquals([0, 10], samples);
733
734     ctr = m.processes[1].counters['foo.ctr[1]'];
735     assertEquals('ctr[1]', ctr.name);
736     assertEquals('foo', ctr.category);
737     assertEquals(3, ctr.numSamples);
738     assertEquals(1, ctr.numSeries);
739     assertArrayEquals([0.01, 0.015, 0.018], ctr.timestamps);
740
741     samples = [];
742     ctr.series[0].samples.forEach(function(sample) {
743       samples.push(sample.value);
744     });
745     assertArrayEquals([10, 20, 30], samples);
746
747     ctr = m.processes[1].counters['bar.ctr[2]'];
748     assertEquals('ctr[2]', ctr.name);
749     assertEquals('bar', ctr.category);
750     assertEquals(1, ctr.numSamples);
751     assertEquals(1, ctr.numSeries);
752     assertArrayEquals([0.02], ctr.timestamps);
753     var samples = [];
754     ctr.series[0].samples.forEach(function(sample) {
755       samples.push(sample.value);
756     });
757     assertArrayEquals([40], samples);
758   });
759
760   test('multiCounterUpdateBounds', function() {
761     var ctr = new tracing.trace_model.Counter(undefined, 'testBasicCounter',
762         '', 'testBasicCounter');
763     var value1Series = new tracing.trace_model.CounterSeries(
764         'value1', 'testBasicCounter.value1');
765     var value2Series = new tracing.trace_model.CounterSeries(
766         'value2', 'testBasicCounter.value2');
767     ctr.addSeries(value1Series);
768     ctr.addSeries(value2Series);
769
770     value1Series.addCounterSample(0, 0);
771     value1Series.addCounterSample(1, 1);
772     value1Series.addCounterSample(2, 1);
773     value1Series.addCounterSample(3, 2);
774     value1Series.addCounterSample(4, 3);
775     value1Series.addCounterSample(5, 1);
776     value1Series.addCounterSample(6, 3);
777     value1Series.addCounterSample(7, 3.1);
778
779     value2Series.addCounterSample(0, 0);
780     value2Series.addCounterSample(1, 0);
781     value2Series.addCounterSample(2, 1);
782     value2Series.addCounterSample(3, 1.1);
783     value2Series.addCounterSample(4, 0);
784     value2Series.addCounterSample(5, 7);
785     value2Series.addCounterSample(6, 0);
786     value2Series.addCounterSample(7, 0.5);
787
788     ctr.updateBounds();
789
790     assertEquals(0, ctr.bounds.min);
791     assertEquals(7, ctr.bounds.max);
792     assertEquals(8, ctr.maxTotal);
793     assertArrayEquals([0, 0,
794                        1, 1,
795                        1, 2,
796                        2, 3.1,
797                        3, 3,
798                        1, 8,
799                        3, 3,
800                        3.1, 3.6], ctr.totals);
801   });
802
803   test('multiCounter', function() {
804     var events = [
805       {name: 'ctr', args: {'value1': 0, 'value2': 7}, pid: 1, ts: 0, cat: 'foo', tid: 1, ph: 'C'}, // @suppress longLineCheck
806       {name: 'ctr', args: {'value1': 10, 'value2': 4}, pid: 1, ts: 10, cat: 'foo', tid: 1, ph: 'C'}, // @suppress longLineCheck
807       {name: 'ctr', args: {'value1': 0, 'value2': 1 }, pid: 1, ts: 20, cat: 'foo', tid: 1, ph: 'C'} // @suppress longLineCheck
808     ];
809     var m = new tracing.TraceModel(events);
810     var p = m.processes[1];
811     var ctr = m.processes[1].counters['foo.ctr'];
812     assertEquals('ctr', ctr.name);
813
814     assertEquals('ctr', ctr.name);
815     assertEquals('foo', ctr.category);
816     assertEquals(3, ctr.numSamples);
817     assertEquals(2, ctr.numSeries);
818
819     assertEquals('value1', ctr.series[0].name);
820     assertEquals('value2', ctr.series[1].name);
821     assertEquals(tvcm.ui.getStringColorId('ctr.value1'), ctr.series[0].color);
822     assertEquals(tvcm.ui.getStringColorId('ctr.value2'), ctr.series[1].color);
823
824     assertArrayEquals([0, 0.01, 0.02], ctr.timestamps);
825     var samples = [];
826     ctr.series[0].samples.forEach(function(sample) {
827       samples.push(sample.value);
828     });
829     assertArrayEquals([0, 10, 0], samples);
830
831     var samples1 = [];
832     ctr.series[1].samples.forEach(function(sample) {
833       samples1.push(sample.value);
834     });
835     assertArrayEquals([7, 4, 1], samples1);
836     assertArrayEquals([0, 7,
837                        10, 14,
838                        0, 1], ctr.totals);
839     assertEquals(14, ctr.maxTotal);
840   });
841
842   test('importObjectInsteadOfArray', function() {
843     var events = { traceEvents: [
844       {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
845       {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
846     ] };
847
848     var m = new tracing.TraceModel(events);
849     assertEquals(1, m.numProcesses);
850   });
851
852   test('importString', function() {
853     var events = [
854       {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
855       {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
856     ];
857
858     var m = new tracing.TraceModel(JSON.stringify(events));
859     assertEquals(1, m.numProcesses);
860   });
861
862   test('importStringWithTrailingNewLine', function() {
863     var events = [
864       {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
865       {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
866     ];
867
868     var m = new tracing.TraceModel(JSON.stringify(events) + '\n');
869     assertEquals(1, m.numProcesses);
870   });
871
872   test('importStringWithMissingCloseSquareBracket', function() {
873     var events = [
874       {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
875       {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
876     ];
877
878     var tmp = JSON.stringify(events);
879     assertEquals(']', tmp[tmp.length - 1]);
880
881     // Drop off the trailing ]
882     var dropped = tmp.substring(0, tmp.length - 1);
883     var m = new tracing.TraceModel(dropped);
884     assertEquals(1, m.numProcesses);
885   });
886
887   test('importStringWithEndingCommaButMissingCloseSquareBracket', function() {
888     var lines = [
889       '[',
890       '{"name": "a", "args": {}, "pid": 52, "ts": 524, "cat": "foo", "tid": 53, "ph": "B"},', // @suppress longLineCheck
891       '{"name": "a", "args": {}, "pid": 52, "ts": 560, "cat": "foo", "tid": 53, "ph": "E"},' // @suppress longLineCheck
892     ];
893     var text = lines.join('\n');
894
895     var m = new tracing.TraceModel(text);
896     assertEquals(1, m.numProcesses);
897     assertEquals(1, m.processes[52].threads[53].sliceGroup.length);
898   });
899
900   test('importStringWithMissingCloseSquareBracketAndNewline', function() {
901     var events = [
902       {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
903       {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
904     ];
905
906     var tmp = JSON.stringify(events);
907     assertEquals(']', tmp[tmp.length - 1]);
908
909     // Drop off the trailing ] and add a newline
910     var dropped = tmp.substring(0, tmp.length - 1);
911     var m = new tracing.TraceModel(dropped + '\n');
912     assertEquals(1, m.numProcesses);
913   });
914
915   test('ImportStringEndingCommaButMissingCloseSquareBracketCRLF', function() {
916     var lines = [
917       '[',
918       '{"name": "a", "args": {}, "pid": 52, "ts": 524, "cat": "foo", "tid": 53, "ph": "B"},', // @suppress longLineCheck
919       '{"name": "a", "args": {}, "pid": 52, "ts": 560, "cat": "foo", "tid": 53, "ph": "E"},' // @suppress longLineCheck
920     ];
921     var text = lines.join('\r\n');
922
923     var m = new tracing.TraceModel(text);
924     assertEquals(1, m.numProcesses);
925     assertEquals(1, m.processes[52].threads[53].sliceGroup.length);
926   });
927
928   test('importOldFormat', function() {
929     var lines = [
930       '[',
931       '{"cat":"a","pid":9,"tid":8,"ts":194,"ph":"E","name":"I","args":{}},',
932       '{"cat":"b","pid":9,"tid":8,"ts":194,"ph":"B","name":"I","args":{}}',
933       ']'
934     ];
935     var text = lines.join('\n');
936     var m = new tracing.TraceModel(text);
937     assertEquals(1, m.numProcesses);
938     assertEquals(1, m.processes[9].threads[8].sliceGroup.length);
939   });
940
941   test('startFinishOneSliceOneThread', function() {
942     var events = [
943       // Time is intentionally out of order.
944       {name: 'a', args: {}, pid: 52, ts: 560, cat: 'cat', tid: 53,
945         ph: 'F', id: 72},
946       {name: 'a', pid: 52, ts: 524, cat: 'cat', tid: 53,
947         ph: 'S', id: 72, args: {'foo': 'bar'}}
948     ];
949
950     var m = new tracing.TraceModel(events);
951     var t = m.processes[52].threads[53];
952     assertNotUndefined(t);
953     assertEquals(1, t.asyncSliceGroup.slices.length);
954     assertEquals('a', t.asyncSliceGroup.slices[0].title);
955     assertEquals('cat', t.asyncSliceGroup.slices[0].category);
956     assertEquals(72, t.asyncSliceGroup.slices[0].id);
957     assertEquals('bar', t.asyncSliceGroup.slices[0].args.foo);
958     assertEquals(0, t.asyncSliceGroup.slices[0].start);
959     assertAlmostEquals((60 - 24) / 1000, t.asyncSliceGroup.slices[0].duration);
960     assertEquals(t, t.asyncSliceGroup.slices[0].startThread);
961     assertEquals(t, t.asyncSliceGroup.slices[0].endThread);
962   });
963
964   test('endArgsAddedToSlice', function() {
965     var events = [
966       {name: 'a', args: {x: 1}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
967       {name: 'a', args: {y: 2}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
968     ];
969
970     var m = new tracing.TraceModel(events);
971     assertEquals(1, m.numProcesses);
972     var p = m.processes[52];
973     assertNotUndefined(p);
974
975     assertEquals(1, p.numThreads);
976     var t = p.threads[53];
977     assertNotUndefined(t);
978     assertEquals(1, t.sliceGroup.length);
979     assertEquals(53, t.tid);
980     var slice = t.sliceGroup.slices[0];
981     assertEquals('a', slice.title);
982     assertEquals('foo', slice.category);
983     assertEquals(0, slice.start);
984     assertEquals(0, slice.subSlices.length);
985     assertEquals(1, slice.args['x']);
986     assertEquals(2, slice.args['y']);
987   });
988
989   test('endArgOverrwritesOriginalArgValueIfDuplicated', function() {
990     var events = [
991       {name: 'b', args: {z: 3}, pid: 52, ts: 629, cat: 'foo', tid: 53, ph: 'B'},
992       {name: 'b', args: {z: 4}, pid: 52, ts: 631, cat: 'foo', tid: 53, ph: 'E'}
993     ];
994
995     var m = new tracing.TraceModel(events);
996     assertEquals(1, m.numProcesses);
997     var p = m.processes[52];
998     assertNotUndefined(p);
999
1000     assertEquals(1, p.numThreads);
1001     var t = p.threads[53];
1002     assertNotUndefined(t);
1003     var slice = t.sliceGroup.slices[0];
1004     assertEquals('b', slice.title);
1005     assertEquals('foo', slice.category);
1006     assertEquals(0, slice.start);
1007     assertEquals(0, slice.subSlices.length);
1008     assertEquals(4, slice.args['z']);
1009   });
1010
1011   test('asyncEndArgsAddedToSlice', function() {
1012     var events = [
1013       // Time is intentionally out of order.
1014       {name: 'c', args: {y: 2}, pid: 52, ts: 560, cat: 'foo', tid: 53,
1015         ph: 'F', id: 72},
1016       {name: 'c', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53,
1017         ph: 'S', id: 72}
1018     ];
1019
1020     var m = new tracing.TraceModel(events);
1021     var t = m.processes[52].threads[53];
1022     assertNotUndefined(t);
1023     assertEquals(1, t.asyncSliceGroup.slices.length);
1024     var parentSlice = t.asyncSliceGroup.slices[0];
1025     assertEquals('c', parentSlice.title);
1026     assertEquals('foo', parentSlice.category);
1027
1028     assertNotUndefined(parentSlice.subSlices);
1029     assertEquals(1, parentSlice.subSlices.length);
1030     var subSlice = parentSlice.subSlices[0];
1031     assertEquals(1, subSlice.args['x']);
1032     assertEquals(2, subSlice.args['y']);
1033   });
1034
1035   test('asyncEndArgOverrwritesOriginalArgValueIfDuplicated', function() {
1036     var events = [
1037       // Time is intentionally out of order.
1038       {name: 'd', args: {z: 4}, pid: 52, ts: 560, cat: 'foo', tid: 53,
1039         ph: 'F', id: 72},
1040       {name: 'd', args: {z: 3}, pid: 52, ts: 524, cat: 'foo', tid: 53,
1041         ph: 'S', id: 72}
1042     ];
1043
1044     var m = new tracing.TraceModel(events);
1045     var t = m.processes[52].threads[53];
1046     assertNotUndefined(t);
1047     assertEquals(1, t.asyncSliceGroup.slices.length);
1048     var parentSlice = t.asyncSliceGroup.slices[0];
1049     assertEquals('d', parentSlice.title);
1050     assertEquals('foo', parentSlice.category);
1051
1052     assertNotUndefined(parentSlice.subSlices);
1053     assertEquals(1, parentSlice.subSlices.length);
1054     var subSlice = parentSlice.subSlices[0];
1055     assertEquals(4, subSlice.args['z']);
1056   });
1057
1058   test('asyncStepsInOneThread', function() {
1059     var events = [
1060       // Time is intentionally out of order.
1061       {name: 'a', args: {z: 3}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'F', id: 72}, // @suppress longLineCheck
1062       {name: 'a', args: {step: 's1', y: 2}, pid: 52, ts: 548, cat: 'foo', tid: 53, ph: 'T', id: 72}, // @suppress longLineCheck
1063       {name: 'a', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'S', id: 72} // @suppress longLineCheck
1064     ];
1065
1066     var m = new tracing.TraceModel(events);
1067     var t = m.processes[52].threads[53];
1068     assertNotUndefined(t);
1069     assertEquals(1, t.asyncSliceGroup.slices.length);
1070     var parentSlice = t.asyncSliceGroup.slices[0];
1071     assertEquals('a', parentSlice.title);
1072     assertEquals('foo', parentSlice.category);
1073     assertEquals(0, parentSlice.start);
1074
1075     assertNotUndefined(parentSlice.subSlices);
1076     assertEquals(2, parentSlice.subSlices.length);
1077     var subSlice = parentSlice.subSlices[0];
1078     assertEquals('a', subSlice.title);
1079     assertEquals('foo', subSlice.category);
1080     assertEquals(0, subSlice.start);
1081     assertAlmostEquals((548 - 524) / 1000, subSlice.duration);
1082     assertEquals(1, subSlice.args['x']);
1083
1084     var subSlice = parentSlice.subSlices[1];
1085     assertEquals('a:s1', subSlice.title);
1086     assertEquals('foo', subSlice.category);
1087     assertAlmostEquals((548 - 524) / 1000, subSlice.start);
1088     assertAlmostEquals((560 - 548) / 1000, subSlice.duration);
1089     assertEquals(1, subSlice.args['x']);
1090     assertEquals(2, subSlice.args['y']);
1091     assertEquals(3, subSlice.args['z']);
1092   });
1093
1094   test('asyncStepsMissingStart', function() {
1095     var events = [
1096       // Time is intentionally out of order.
1097       {name: 'a', args: {z: 3}, pid: 52, ts: 560, cat: 'foo', tid: 53,
1098         ph: 'F', id: 72},
1099       {name: 'a', args: {step: 's1', y: 2}, pid: 52, ts: 548, cat: 'foo',
1100         tid: 53, ph: 'T', id: 72}
1101     ];
1102
1103     var m = new tracing.TraceModel(events);
1104     var t = m.processes[52].threads[53];
1105     assertUndefined(t);
1106   });
1107
1108   test('asyncStepsMissingFinish', function() {
1109     var events = [
1110       // Time is intentionally out of order.
1111       {name: 'a', args: {step: 's1', y: 2}, pid: 52, ts: 548, cat: 'foo',
1112         tid: 53, ph: 'T', id: 72},
1113       {name: 'a', args: {z: 3}, pid: 52, ts: 560, cat: 'foo', tid: 53,
1114         ph: 'S', id: 72}
1115     ];
1116
1117     var m = new tracing.TraceModel(events);
1118     var t = m.processes[52].threads[53];
1119     assertUndefined(t);
1120   });
1121
1122   test('asyncStepEndEvent', function() {
1123     var events = [
1124       // Time is intentionally out of order.
1125       {name: 'a', args: {z: 3}, pid: 52, ts: 560, cat: 'foo', tid: 53,
1126         ph: 'F', id: 72},
1127       {name: 'a', args: {step: 's1', y: 2}, pid: 52, ts: 548, cat: 'foo',
1128         tid: 53, ph: 'p', id: 72},
1129       {name: 'a', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53,
1130         ph: 'S', id: 72}
1131     ];
1132
1133     var m = new tracing.TraceModel(events);
1134     var t = m.processes[52].threads[53];
1135     assertNotUndefined(t);
1136     assertEquals(1, t.asyncSliceGroup.slices.length);
1137     var parentSlice = t.asyncSliceGroup.slices[0];
1138     assertEquals('a', parentSlice.title);
1139     assertEquals('foo', parentSlice.category);
1140     assertEquals(0, parentSlice.start);
1141
1142     assertNotUndefined(parentSlice.subSlices);
1143     assertEquals(2, parentSlice.subSlices.length);
1144     var subSlice = parentSlice.subSlices[0];
1145     assertEquals('a:s1', subSlice.title);
1146     assertEquals('foo', subSlice.category);
1147     assertEquals(0, subSlice.start);
1148     assertAlmostEquals((548 - 524) / 1000, subSlice.duration);
1149     assertEquals(1, subSlice.args['x']);
1150     assertEquals(2, subSlice.args['y']);
1151
1152     var subSlice = parentSlice.subSlices[1];
1153     assertEquals('a', subSlice.title);
1154     assertEquals('foo', subSlice.category);
1155     assertAlmostEquals((548 - 524) / 1000, subSlice.start);
1156     assertAlmostEquals((560 - 548) / 1000, subSlice.duration);
1157     assertEquals(1, subSlice.args['x']);
1158     assertEquals(3, subSlice.args['z']);
1159   });
1160
1161   test('asyncStepMismatch', function() {
1162     var events = [
1163       // Time is intentionally out of order.
1164       {name: 'a', args: {z: 3}, pid: 52, ts: 560, cat: 'foo', tid: 53,
1165         ph: 'F', id: 72},
1166       {name: 'a', args: {step: 's2'}, pid: 52, ts: 548, cat: 'foo', tid: 53,
1167         ph: 'T', id: 72},
1168       {name: 'a', args: {step: 's1'}, pid: 52, ts: 548, cat: 'foo', tid: 53,
1169         ph: 'p', id: 72},
1170       {name: 'a', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53,
1171         ph: 'S', id: 72}
1172     ];
1173
1174     var m = new tracing.TraceModel(events);
1175     var t = m.processes[52].threads[53];
1176     assertUndefined(t);
1177     assertTrue(m.hasImportWarnings);
1178   });
1179
1180   test('importSamples', function() {
1181     var events = [
1182       {name: 'a', args: {}, pid: 52, ts: 548, cat: 'test', tid: 53, ph: 'P'},
1183       {name: 'b', args: {}, pid: 52, ts: 548, cat: 'test', tid: 53, ph: 'P'},
1184       {name: 'c', args: {}, pid: 52, ts: 558, cat: 'test', tid: 53, ph: 'P'}
1185     ];
1186     var m = new tracing.TraceModel(events);
1187     var p = m.processes[52];
1188     assertNotUndefined(p);
1189     var t = p.threads[53];
1190     assertNotUndefined(t);
1191     assertEquals(3, t.samples_.length);
1192     assertEquals(0.0, t.samples_[0].start);
1193     assertEquals(0.0, t.samples_[1].start);
1194     assertApproxEquals(0.01, t.samples_[2].start);
1195     assertEquals('a', t.samples_[0].leafStackFrame.title);
1196     assertEquals('b', t.samples_[1].leafStackFrame.title);
1197     assertEquals('c', t.samples_[2].leafStackFrame.title);
1198     assertFalse(m.hasImportWarnings);
1199   });
1200
1201   test('importSamplesMissingArgs', function() {
1202     var events = [
1203       {name: 'a', pid: 52, ts: 548, cat: 'test', tid: 53, ph: 'P'},
1204       {name: 'b', pid: 52, ts: 548, cat: 'test', tid: 53, ph: 'P'},
1205       {name: 'c', pid: 52, ts: 549, cat: 'test', tid: 53, ph: 'P'}
1206     ];
1207     var m = new tracing.TraceModel(events);
1208     var p = m.processes[52];
1209     assertNotUndefined(p);
1210     var t = p.threads[53];
1211     assertNotUndefined(t);
1212     assertNotUndefined(t);
1213     assertEquals(3, t.samples_.length);
1214     assertFalse(m.hasImportWarnings);
1215   });
1216
1217   test('importSimpleObject', function() {
1218     var events = [
1219       {ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1220       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: {snapshot: 15}}, // @suppress longLineCheck
1221       {ts: 20000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: {snapshot: 20}}, // @suppress longLineCheck
1222       {ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'a', args: {}} // @suppress longLineCheck
1223     ];
1224     var m = new tracing.TraceModel();
1225     m.importTraces([events], false);
1226     assertEquals(10, m.bounds.min);
1227     assertEquals(50, m.bounds.max);
1228     assertFalse(m.hasImportWarnings);
1229
1230     var p = m.processes[1];
1231     assertNotUndefined(p);
1232
1233     var i10 = p.objects.getObjectInstanceAt('0x1000', 10);
1234     assertEquals('c', i10.category);
1235     assertEquals(10, i10.creationTs);
1236     assertEquals(50, i10.deletionTs);
1237     assertEquals(2, i10.snapshots.length);
1238
1239     var s15 = i10.snapshots[0];
1240     assertEquals(15, s15.ts);
1241     assertEquals(15, s15.args);
1242
1243     var s20 = i10.snapshots[1];
1244     assertEquals(20, s20.ts);
1245     assertEquals(20, s20.args);
1246   });
1247
1248   test('importImplicitObjects', function() {
1249     var events = [
1250       {ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1251       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a',
1252         args: { snapshot: [
1253           { id: 'subObject/0x1',
1254             foo: 1
1255           }
1256         ]}},
1257       {ts: 20000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a',
1258         args: { snapshot: [
1259           { id: 'subObject/0x1',
1260             foo: 2
1261           },
1262           { id: 'subObject/0x2',
1263             foo: 1
1264           }
1265         ]}}
1266     ];
1267
1268     var m = new tracing.TraceModel();
1269     m.importTraces([events], false);
1270     var p1 = m.processes[1];
1271
1272     var iA = p1.objects.getObjectInstanceAt('0x1000', 10);
1273     var subObjectInstances = p1.objects.getAllInstancesByTypeName()[
1274         'subObject'];
1275
1276     assertEquals(2, subObjectInstances.length);
1277     var subObject1 = p1.objects.getObjectInstanceAt('0x1', 15);
1278     assertEquals('subObject', subObject1.name);
1279     assertEquals(15, subObject1.creationTs);
1280
1281     assertEquals(2, subObject1.snapshots.length);
1282     assertEquals(15, subObject1.snapshots[0].ts);
1283     assertEquals(1, subObject1.snapshots[0].args.foo);
1284     assertEquals(20, subObject1.snapshots[1].ts);
1285     assertEquals(2, subObject1.snapshots[1].args.foo);
1286
1287     var subObject2 = p1.objects.getObjectInstanceAt('0x2', 20);
1288     assertEquals('subObject', subObject2.name);
1289     assertEquals(20, subObject2.creationTs);
1290     assertEquals(1, subObject2.snapshots.length);
1291     assertEquals(20, subObject2.snapshots[0].ts);
1292   });
1293
1294   test('importImplicitObjectWithCategoryOverride', function() {
1295     var events = [
1296       {ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'cat', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1297       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'otherCat', id: '0x1000', name: 'a', // @suppress longLineCheck
1298         args: { snapshot: [
1299           { id: 'subObject/0x1',
1300             cat: 'cat',
1301             foo: 1
1302           }
1303         ]}}
1304     ];
1305
1306     var m = new tracing.TraceModel();
1307     m.importTraces([events], false);
1308     var p1 = m.processes[1];
1309
1310     var iA = p1.objects.getObjectInstanceAt('0x1000', 10);
1311     var subObjectInstances = p1.objects.getAllInstancesByTypeName()[
1312         'subObject'];
1313
1314     assertEquals(1, subObjectInstances.length);
1315   });
1316
1317   test('importImplicitObjectWithBaseTypeOverride', function() {
1318     var events = [
1319       {ts: 10000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'PictureLayerImpl', args: { // @suppress longLineCheck
1320         snapshot: {
1321           base_type: 'LayerImpl'
1322         }
1323       }},
1324       {ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'LayerImpl', args: {}} // @suppress longLineCheck
1325     ];
1326
1327     var m = new tracing.TraceModel();
1328     m.importTraces([events], false);
1329     var p1 = m.processes[1];
1330     assertEquals(0, m.importWarnings.length);
1331
1332     var iA = p1.objects.getObjectInstanceAt('0x1000', 10);
1333     assertEquals(1, iA.snapshots.length);
1334   });
1335
1336   test('importIDRefs', function() {
1337     var events = [
1338       // An object with two snapshots.
1339       {ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1340       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: {snapshot: 15}}, // @suppress longLineCheck
1341       {ts: 20000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: {snapshot: 20}}, // @suppress longLineCheck
1342       {ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1343
1344       // A slice that references the object.
1345       {ts: 17000, pid: 1, tid: 1, ph: 'B', cat: 'c', name: 'taskSlice', args: {my_object: {id_ref: '0x1000'}}}, // @suppress longLineCheck
1346       {ts: 17500, pid: 1, tid: 1, ph: 'E', cat: 'c', name: 'taskSlice', args: {}} // @suppress longLineCheck
1347     ];
1348
1349     var m = new tracing.TraceModel();
1350     m.importTraces([events], false);
1351     var p1 = m.processes[1];
1352
1353     var iA = p1.objects.getObjectInstanceAt('0x1000', 10);
1354     var s15 = iA.getSnapshotAt(15);
1355
1356     var taskSlice = p1.threads[1].sliceGroup.slices[0];
1357     assertEquals(s15, taskSlice.args.my_object);
1358   });
1359
1360   test('importIDRefsThatPointAtEachOther', function() {
1361     var events = [
1362       // An object.
1363       {ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1364       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: { // @suppress longLineCheck
1365         snapshot: { x: {
1366           id: 'foo/0x1001',
1367           value: 'bar'
1368         }}}},
1369       {ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1370
1371       // A slice that references the object.
1372       {ts: 17000, pid: 1, tid: 1, ph: 'B', cat: 'c', name: 'taskSlice', args: {my_object: {id_ref: '0x1001'}}}, // @suppress longLineCheck
1373       {ts: 17500, pid: 1, tid: 1, ph: 'E', cat: 'c', name: 'taskSlice', args: {}} // @suppress longLineCheck
1374     ];
1375
1376     var m = new tracing.TraceModel();
1377     m.importTraces([events], false);
1378     var p1 = m.processes[1];
1379
1380     var iA = p1.objects.getObjectInstanceAt('0x1000', 15);
1381     var iFoo = p1.objects.getObjectInstanceAt('0x1001', 15);
1382     assertNotUndefined(iA);
1383     assertNotUndefined(iFoo);
1384
1385     var a15 = iA.getSnapshotAt(15);
1386     var foo15 = iFoo.getSnapshotAt(15);
1387
1388     var taskSlice = p1.threads[1].sliceGroup.slices[0];
1389     assertEquals(foo15, taskSlice.args.my_object);
1390   });
1391
1392   test('importArrayWithIDs', function() {
1393     var events = [
1394       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: { // @suppress longLineCheck
1395         snapshot: { x: [
1396           {id: 'foo/0x1001', value: 'bar1'},
1397           {id: 'foo/0x1002', value: 'bar2'},
1398           {id: 'foo/0x1003', value: 'bar3'}
1399         ]}}}
1400     ];
1401
1402     var m = new tracing.TraceModel();
1403     m.importTraces([events], false);
1404     var p1 = m.processes[1];
1405
1406     var sA = p1.objects.getSnapshotAt('0x1000', 15);
1407     assertTrue(sA.args.x instanceof Array);
1408     assertEquals(3, sA.args.x.length);
1409     assertTrue(sA.args.x[0] instanceof tracing.trace_model.ObjectSnapshot);
1410     assertTrue(sA.args.x[1] instanceof tracing.trace_model.ObjectSnapshot);
1411     assertTrue(sA.args.x[2] instanceof tracing.trace_model.ObjectSnapshot);
1412   });
1413
1414   test('importDoesNotMutateEventList', function() {
1415     var events = [
1416       // An object.
1417       {ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1418       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: { // @suppress longLineCheck
1419         snapshot: {foo: 15}}},
1420       {ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1421
1422       // A slice that references the object.
1423       {ts: 17000, pid: 1, tid: 1, ph: 'B', cat: 'c', name: 'taskSlice', args: {
1424         my_object: {id_ref: '0x1000'}}
1425       },
1426       {ts: 17500, pid: 1, tid: 1, ph: 'E', cat: 'c', name: 'taskSlice', args: {}} // @suppress longLineCheck
1427     ];
1428
1429     // The A type family exists to mutate the args list provided to
1430     // snapshots.
1431     function ASnapshot() {
1432       tracing.trace_model.ObjectSnapshot.apply(this, arguments);
1433       this.args.foo = 7;
1434     }
1435     ASnapshot.prototype = {
1436       __proto__: tracing.trace_model.ObjectSnapshot.prototype
1437     };
1438
1439     // Import event while the A types are registered, causing the
1440     // arguments of the snapshots to be mutated.
1441     var m = new tracing.TraceModel();
1442     try {
1443       tracing.trace_model.ObjectSnapshot.register('a', ASnapshot);
1444       m.importTraces([events], false);
1445     } finally {
1446       tracing.trace_model.ObjectSnapshot.unregister('a');
1447     }
1448     assertFalse(m.hasImportWarnings);
1449
1450     // Verify that the events array wasn't modified.
1451     assertObjectEquals(
1452         events[1].args,
1453         {snapshot: {foo: 15}});
1454     assertObjectEquals(
1455         events[3].args,
1456         {my_object: {id_ref: '0x1000'}});
1457   });
1458
1459   test('importFlowEvent', function() {
1460     var events = [
1461       { name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 548, ph: 's', args: {} },  // @suppress longLineCheck
1462       { name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 560, ph: 't', args: {} },  // @suppress longLineCheck
1463       { name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 580, ph: 'f', args: {} }   // @suppress longLineCheck
1464     ];
1465
1466     var m = new tracing.TraceModel(events);
1467     var t = m.processes[52].threads[53];
1468
1469     assertNotUndefined(t);
1470     assertEquals(3, t.sliceGroup.slices.length);
1471     assertEquals(2, m.flowEvents.length);
1472
1473     var start = m.flowEvents[0][0];
1474     var step = m.flowEvents[0][1];
1475     var finish = m.flowEvents[1][1];
1476
1477     assertEquals('a', start.title);
1478     assertEquals('foo', start.category);
1479     assertEquals(72, start.id);
1480     assertEquals(0, start.start);
1481     assertEquals(0, start.duration);
1482
1483     assertEquals(start.title, step.title);
1484     assertEquals(start.category, step.category);
1485     assertEquals(start.id, step.id);
1486     assertAlmostEquals(12 / 1000, step.start);
1487     assertEquals(0, step.duration);
1488
1489     assertEquals(start.title, finish.title);
1490     assertEquals(start.category, finish.category);
1491     assertEquals(start.id, finish.id);
1492     assertAlmostEquals((20 + 12) / 1000, finish.start);
1493     assertEquals(0, finish.duration);
1494
1495     assertEquals(2, m.flowIntervalTree.size);
1496   });
1497
1498   test('importOutOfOrderFlowEvent', function() {
1499     var events = [
1500       { name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 548, ph: 's', args: {} },  // @suppress longLineCheck
1501       { name: 'b', cat: 'foo', id: 73, pid: 52, tid: 53, ts: 148, ph: 's', args: {} },  // @suppress longLineCheck
1502       { name: 'b', cat: 'foo', id: 73, pid: 52, tid: 53, ts: 570, ph: 'f', args: {} },   // @suppress longLineCheck
1503       { name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 560, ph: 't', args: {} },  // @suppress longLineCheck
1504       { name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 580, ph: 'f', args: {} }   // @suppress longLineCheck
1505     ];
1506
1507     var expected = [0.4, 0.0, 0.412];
1508     var m = new tracing.TraceModel(events);
1509     assertEquals(3, m.flowIntervalTree.size);
1510
1511     var order = m.flowEvents.map(function(x) { return x[0].start });
1512     for (var i = 0; i < expected.length; ++i)
1513       assertAlmostEquals(expected[i], order[i]);
1514   });
1515
1516   test('importCompleteEvent', function() {
1517     var events = [
1518       { name: 'a', args: {}, pid: 52, ts: 629, dur: 1, cat: 'baz', tid: 53, ph: 'X' },  // @suppress longLineCheck
1519       { name: 'b', args: {}, pid: 52, ts: 730, dur: 20, cat: 'foo', tid: 53, ph: 'X' },  // @suppress longLineCheck
1520       { name: 'c', args: {}, pid: 52, ts: 740, cat: 'baz', tid: 53, ph: 'X' }
1521     ];
1522
1523     var m = new tracing.TraceModel(events);
1524     assertEquals(1, m.numProcesses);
1525     var p = m.processes[52];
1526     assertNotUndefined(p);
1527
1528     assertEquals(1, p.numThreads);
1529     var t = p.threads[53];
1530     assertNotUndefined(t);
1531     assertEquals(3, t.sliceGroup.slices.length);
1532     assertEquals(53, t.tid);
1533
1534     var slice = t.sliceGroup.slices[0];
1535     assertEquals('a', slice.title);
1536     assertEquals('baz', slice.category);
1537     assertAlmostEquals(0, slice.start);
1538     assertAlmostEquals(1 / 1000, slice.duration);
1539     assertEquals(0, slice.subSlices.length);
1540
1541     slice = t.sliceGroup.slices[1];
1542     assertEquals('b', slice.title);
1543     assertEquals('foo', slice.category);
1544     assertAlmostEquals((730 - 629) / 1000, slice.start);
1545     assertAlmostEquals(20 / 1000, slice.duration);
1546     assertEquals(1, slice.subSlices.length);
1547
1548     slice = t.sliceGroup.slices[2];
1549     assertEquals('c', slice.title);
1550     assertTrue(slice.didNotFinish);
1551     assertAlmostEquals(10 / 1000, slice.duration);
1552   });
1553
1554   test('importCompleteEventWithCpuDuration', function() {
1555     var events = [
1556       { name: 'a', args: {}, pid: 52, ts: 629, dur: 1, cat: 'baz', tid: 53, ph: 'X', tts: 12, tdur: 1 },  // @suppress longLineCheck
1557       { name: 'b', args: {}, pid: 52, ts: 730, dur: 20, cat: 'foo', tid: 53, ph: 'X', tts: 110, tdur: 16 },  // @suppress longLineCheck
1558       { name: 'c', args: {}, pid: 52, ts: 740, cat: 'baz', tid: 53, ph: 'X', tts: 115 }  // @suppress longLineCheck
1559     ];
1560
1561     var m = new tracing.TraceModel(events);
1562     assertEquals(1, m.numProcesses);
1563     var p = m.processes[52];
1564     assertNotUndefined(p);
1565
1566     assertEquals(1, p.numThreads);
1567     var t = p.threads[53];
1568     assertNotUndefined(t);
1569     assertEquals(3, t.sliceGroup.slices.length);
1570     assertEquals(53, t.tid);
1571
1572     var slice = t.sliceGroup.slices[0];
1573     assertEquals('a', slice.title);
1574     assertEquals('baz', slice.category);
1575     assertAlmostEquals(0, slice.start);
1576     assertAlmostEquals(1 / 1000, slice.duration);
1577     assertAlmostEquals(12 / 1000, slice.cpuStart);
1578     assertAlmostEquals(1 / 1000, slice.cpuDuration);
1579     assertEquals(0, slice.subSlices.length);
1580
1581     slice = t.sliceGroup.slices[1];
1582     assertEquals('b', slice.title);
1583     assertEquals('foo', slice.category);
1584     assertAlmostEquals((730 - 629) / 1000, slice.start);
1585     assertAlmostEquals(20 / 1000, slice.duration);
1586     assertAlmostEquals(110 / 1000, slice.cpuStart);
1587     assertAlmostEquals(16 / 1000, slice.cpuDuration);
1588     assertEquals(1, slice.subSlices.length);
1589
1590     slice = t.sliceGroup.slices[2];
1591     assertEquals('c', slice.title);
1592     assertTrue(slice.didNotFinish);
1593     assertAlmostEquals(10 / 1000, slice.duration);
1594   });
1595
1596   test('importNestedCompleteEventWithTightBounds', function() {
1597     var events = [
1598       { name: 'a', args: {}, pid: 52, ts: 244654227065, dur: 36075, cat: 'baz', tid: 53, ph: 'X' },  // @suppress longLineCheck
1599       { name: 'b', args: {}, pid: 52, ts: 244654227095, dur: 36045, cat: 'foo', tid: 53, ph: 'X' }  // @suppress longLineCheck
1600     ];
1601
1602     var m = new tracing.TraceModel(events, false);
1603     var t = m.processes[52].threads[53];
1604
1605     var sA = findSliceNamed(t.sliceGroup, 'a');
1606     var sB = findSliceNamed(t.sliceGroup, 'b');
1607
1608     assertEquals('a', sA.title);
1609     assertEquals('baz', sA.category);
1610     assertEquals(244654227.065, sA.start);
1611     assertEquals(36.075, sA.duration);
1612     assertAlmostEquals(0.03, sA.selfTime);
1613
1614     assertEquals('b', sB.title);
1615     assertEquals('foo', sB.category);
1616     assertEquals(244654227.095, sB.start);
1617     assertEquals(36.045, sB.duration);
1618
1619     assertTrue(sA.subSlices.length == 1);
1620     assertTrue(sA.subSlices[0] == sB);
1621     assertTrue(sB.parentSlice == sA);
1622   });
1623
1624   test('importAsyncEventWithSameTimestamp', function() {
1625     var events = [];
1626     // Events are added with ts 0, 1, 1, 2, 2, 3, 3 ...500, 500, 1000
1627     // and use 'seq' to track the order of when the event is recorded.
1628     events.push({name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 0, ph: 'S', args: {'seq': 0}});  // @suppress longLineCheck
1629
1630     for (var i = 1; i <= 1000; i++)
1631       events.push({name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: Math.round(i / 2) , ph: 'T', args: {'seq': i}});  // @suppress longLineCheck
1632
1633     events.push({name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 1000, ph: 'F', args: {'seq': 1001}});  // @suppress longLineCheck
1634
1635     var m = new tracing.TraceModel(events);
1636     var t = m.processes[52].threads[53];
1637
1638     assertEquals(1, t.asyncSliceGroup.slices.length);
1639     var parentSlice = t.asyncSliceGroup.slices[0];
1640     assertEquals('a', parentSlice.title);
1641     assertEquals('foo', parentSlice.category);
1642
1643     assertNotUndefined(parentSlice.subSlices);
1644     var subSlices = parentSlice.subSlices;
1645     assertEquals(1001, subSlices.length);
1646     // Slices should be sorted according to 'ts'. And if 'ts' is the same,
1647     // slices should keep the order that they were recorded.
1648     for (var i = 0; i < 1000; i++)
1649       assertEquals(subSlices[i].args['seq'], i);
1650   });
1651
1652   test('sampleDataSimple', function() {
1653     var events = {
1654       'traceEvents': [],
1655       'stackFrames': {
1656         '1': {
1657           'category': 'mod',
1658           'name': 'main'
1659         },
1660         '2': {
1661           'category': 'mod',
1662           'name': 'a',
1663           'parent': 1
1664         },
1665         '3': {
1666           'category': 'mod',
1667           'name': 'a_sub',
1668           'parent': 2
1669         },
1670         '4': {
1671           'category': 'mod',
1672           'name': 'b',
1673           'parent': 1
1674         }
1675       },
1676       'samples': [
1677         {
1678           'cpu': 0, 'tid': 1, 'ts': 1000.0,
1679           'name': 'cycles:HG', 'sf': 3, 'weight': 1
1680         },
1681         {
1682           'cpu': 0, 'tid': 1, 'ts': 2000.0,
1683           'name': 'cycles:HG', 'sf': 2, 'weight': 1
1684         },
1685         {
1686           'cpu': 1, 'tid': 1, 'ts': 3000.0,
1687           'name': 'cycles:HG', 'sf': 3, 'weight': 1
1688         }
1689       ]
1690     };
1691     var m = new tracing.TraceModel(events, false);
1692     assertNotUndefined(m.kernel.cpus[0]);
1693     assertEquals(1, m.getAllThreads().length);
1694
1695     assertEquals(4, tvcm.dictionaryKeys(m.stackFrames).length);
1696     assertEquals(3, m.samples.length);
1697
1698     var t1 = m.processes[1].threads[1];
1699     assertEquals(3, t1.samples.length);
1700
1701     var c0 = m.kernel.cpus[0];
1702     var c1 = m.kernel.cpus[1];
1703     assertEquals(2, c0.samples.length);
1704     assertEquals(1, c1.samples.length);
1705
1706     assertEquals(c0, m.samples[0].cpu);
1707     assertEquals(t1, m.samples[0].thread);
1708     assertEquals('cycles:HG', m.samples[0].title);
1709     assertEquals(1, m.samples[0].start);
1710     assertArrayEquals(
1711         ['main', 'a', 'a_sub'],
1712         m.samples[0].stackTrace.map(function(x) { return x.title; }));
1713     assertEquals(1, m.samples[0].weight);
1714   });
1715
1716   // TODO(nduca): one slice, two threads
1717   // TODO(nduca): one slice, two pids
1718
1719 });
1720 </script>
1721