Update To 11.40.268.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 tv.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(tv.ui.getStringHash('a'), tv.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(tv.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(tv.ui.getStringColorId('ctr.value1'), ctr.series[0].color);
822     assertEquals(tv.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('nestableAsyncBasic', function() {
1181     var events = [
1182       {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53,
1183         ph: 'b', id: 72},
1184       {name: 'b', args: {x: 1}, pid: 52, ts: 525, cat: 'foo', tid: 53,
1185         ph: 'b', id: 72},
1186       {name: 'b', args: {y: 2}, pid: 52, ts: 560, cat: 'foo', tid: 53,
1187         ph: 'e', id: 72},
1188       {name: 'a', args: {}, pid: 52, ts: 565, cat: 'foo', tid: 53,
1189         ph: 'e', id: 72}
1190     ];
1191
1192     var m = new tracing.TraceModel(events);
1193     var t = m.processes[52].threads[53];
1194     assertNotUndefined(t);
1195     assertEquals(1, t.asyncSliceGroup.slices.length);
1196     var parentSlice = t.asyncSliceGroup.slices[0];
1197     assertEquals('a', parentSlice.title);
1198     assertEquals('foo', parentSlice.category);
1199
1200     assertNotUndefined(parentSlice.subSlices);
1201     assertEquals(1, parentSlice.subSlices.length);
1202     var subSlice = parentSlice.subSlices[0];
1203     // Arguments should include both BEGIN and END event.
1204     assertEquals(1, subSlice.args['x']);
1205     assertEquals(2, subSlice.args['y']);
1206     assertUndefined(subSlice.subSlices);
1207   });
1208
1209   test('nestableAsyncCombinedParams', function() {
1210     var events = [
1211       {name: 'a', args: {x: 1, params: {p1: 'hello', p2: 123}},
1212         pid: 52, ts: 525, cat: 'foo', tid: 53, ph: 'b', id: 72},
1213       {name: 'a', args: {y: 2, params: {p3: 'hi'}}, pid: 52, ts: 560,
1214         cat: 'foo', tid: 53, ph: 'e', id: 72},
1215       {name: 'b', args: {params: {p4: 'foo'}},
1216         pid: 52, ts: 525, cat: 'foo', tid: 53, ph: 'b', id: 73},
1217       {name: 'b', args: {params: ''}, pid: 52, ts: 560,
1218         cat: 'foo', tid: 53, ph: 'e', id: 73},
1219       {name: 'c', args: {params: {p5: 'bar'}},
1220         pid: 52, ts: 525, cat: 'foo', tid: 53, ph: 'b', id: 74},
1221       {name: 'c', args: {}, pid: 52, ts: 560,
1222         cat: 'foo', tid: 53, ph: 'e', id: 74}
1223     ];
1224
1225     var m = new tracing.TraceModel(events);
1226     var t = m.processes[52].threads[53];
1227     assertNotUndefined(t);
1228     assertEquals(3, t.asyncSliceGroup.slices.length);
1229
1230     var sliceA = t.asyncSliceGroup.slices[0];
1231     // Arguments should include both BEGIN and END event.
1232     assertEquals(1, sliceA.args['x']);
1233     assertEquals(2, sliceA.args['y']);
1234     var paramsA = sliceA.args['params'];
1235     assertNotUndefined(paramsA);
1236     assertEquals('hello', paramsA.p1);
1237     assertEquals(123, paramsA.p2);
1238     assertEquals('hi', paramsA.p3);
1239
1240     var sliceB = t.asyncSliceGroup.slices[1];
1241     // Arguments should include both BEGIN and END event.
1242     var paramsB = sliceB.args['params'];
1243     assertNotUndefined(paramsB);
1244     assertEquals('foo', paramsB.p4);
1245
1246     var sliceC = t.asyncSliceGroup.slices[2];
1247     // Arguments should include both BEGIN and END event.
1248     var paramsC = sliceC.args['params'];
1249     assertNotUndefined(paramsC);
1250     assertEquals('bar', paramsC.p5);
1251   });
1252
1253   test('nestableAsyncManyLevels', function() {
1254     // There are 5 nested levels.
1255     var events = [
1256       {name: 'l1', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53,
1257         ph: 'b', id: 72},
1258       {name: 'l2', args: {}, pid: 52, ts: 525, cat: 'foo', tid: 53,
1259         ph: 'b', id: 72},
1260       {name: 'l3', args: {}, pid: 52, ts: 526, cat: 'foo', tid: 53,
1261         ph: 'b', id: 72},
1262       {name: 'l4', args: {}, pid: 52, ts: 527, cat: 'foo', tid: 53,
1263         ph: 'b', id: 72},
1264       {name: 'l5', args: {}, pid: 52, ts: 528, cat: 'foo', tid: 53,
1265         ph: 'b', id: 72},
1266       {name: 'l5', args: {}, pid: 52, ts: 529, cat: 'foo', tid: 53,
1267         ph: 'e', id: 72},
1268       {name: 'l4', args: {}, pid: 52, ts: 530, cat: 'foo', tid: 53,
1269         ph: 'e', id: 72},
1270       {name: 'l3', args: {}, pid: 52, ts: 531, cat: 'foo', tid: 53,
1271         ph: 'e', id: 72},
1272       {name: 'l2', args: {}, pid: 52, ts: 532, cat: 'foo', tid: 53,
1273         ph: 'e', id: 72},
1274       {name: 'l1', args: {}, pid: 52, ts: 533, cat: 'foo', tid: 53,
1275         ph: 'e', id: 72}
1276     ];
1277
1278     var m = new tracing.TraceModel(events);
1279     var t = m.processes[52].threads[53];
1280     assertNotUndefined(t);
1281     // Perfectly matched events should not produce a warning.
1282     assertFalse(m.hasImportWarnings);
1283     assertEquals(1, t.asyncSliceGroup.slices.length);
1284
1285     var l1Slice = t.asyncSliceGroup.slices[0];
1286     assertEquals('l1', l1Slice.title);
1287     assertAlmostEquals(0, l1Slice.start);
1288     assertAlmostEquals(9 / 1000, l1Slice.duration);
1289
1290     assertNotUndefined(l1Slice.subSlices);
1291     assertEquals(1, l1Slice.subSlices.length);
1292     var l2Slice = l1Slice.subSlices[0];
1293     assertEquals('l2', l2Slice.title);
1294     assertAlmostEquals(1 / 1000, l2Slice.start);
1295     assertAlmostEquals(7 / 1000, l2Slice.duration);
1296
1297     assertNotUndefined(l2Slice.subSlices);
1298     assertEquals(1, l2Slice.subSlices.length);
1299     var l3Slice = l2Slice.subSlices[0];
1300     assertEquals('l3', l3Slice.title);
1301     assertAlmostEquals(2 / 1000, l3Slice.start);
1302     assertAlmostEquals(5 / 1000, l3Slice.duration);
1303
1304     assertNotUndefined(l3Slice.subSlices);
1305     assertEquals(1, l3Slice.subSlices.length);
1306     var l4Slice = l3Slice.subSlices[0];
1307     assertEquals('l4', l4Slice.title);
1308     assertAlmostEquals(3 / 1000, l4Slice.start);
1309     assertAlmostEquals(3 / 1000, l4Slice.duration);
1310
1311     assertNotUndefined(l4Slice.subSlices);
1312     assertEquals(1, l4Slice.subSlices.length);
1313     var l5Slice = l4Slice.subSlices[0];
1314     assertEquals('l5', l5Slice.title);
1315     assertAlmostEquals(4 / 1000, l5Slice.start);
1316     assertAlmostEquals(1 / 1000, l5Slice.duration);
1317   });
1318
1319   test('nestableAsyncInstantEvent', function() {
1320     var events = [
1321       {name: 'c', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53,
1322         ph: 'n', id: 71},
1323       {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53,
1324         ph: 'b', id: 72},
1325       {name: 'd', args: {}, pid: 52, ts: 525, cat: 'foo', tid: 53,
1326         ph: 'n', id: 72},
1327       {name: 'a', args: {}, pid: 52, ts: 565, cat: 'foo', tid: 53,
1328         ph: 'e', id: 72}
1329     ];
1330
1331     var m = new tracing.TraceModel(events);
1332     var t = m.processes[52].threads[53];
1333     assertNotUndefined(t);
1334     assertEquals(2, t.asyncSliceGroup.slices.length);
1335     var instantSlice = t.asyncSliceGroup.slices[0];
1336     assertEquals('c', instantSlice.title);
1337     assertAlmostEquals(0, instantSlice.start);
1338     assertAlmostEquals(0, instantSlice.duration);
1339     assertUndefined(instantSlice.subSlices);
1340
1341     var nestedSlice = t.asyncSliceGroup.slices[1];
1342     assertEquals('a', nestedSlice.title);
1343     assertAlmostEquals(0, nestedSlice.start);
1344     assertAlmostEquals((565-524) / 1000, nestedSlice.duration);
1345     assertNotUndefined(nestedSlice.subSlices);
1346     assertEquals(1, nestedSlice.subSlices.length);
1347     var nestedInstantSlice = nestedSlice.subSlices[0];
1348     assertUndefined(nestedInstantSlice.subSlices);
1349     assertEquals('d', nestedInstantSlice.title);
1350   });
1351
1352   test('nestableAsyncUnmatchedOuterBeginEvent', function() {
1353     var events = [
1354       {name: 'a', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53,
1355         ph: 'b', id: 72},
1356       {name: 'b', args: {}, pid: 52, ts: 525, cat: 'foo', tid: 53,
1357         ph: 'b', id: 72},
1358       {name: 'b', args: {y: 2}, pid: 52, ts: 560, cat: 'foo', tid: 53,
1359         ph: 'e', id: 72}
1360     ];
1361
1362     var m = new tracing.TraceModel(events);
1363     var t = m.processes[52].threads[53];
1364     assertNotUndefined(t);
1365     // Unmatched BEGIN should produce a warning.
1366     assertTrue(m.hasImportWarnings);
1367     assertEquals(1, t.asyncSliceGroup.slices.length);
1368     var parentSlice = t.asyncSliceGroup.slices[0];
1369     assertEquals('a', parentSlice.title);
1370     assertEquals('foo', parentSlice.category);
1371     assertAlmostEquals(0, parentSlice.start);
1372     // Unmatched BEGIN event ends at the last event of that ID.
1373     assertAlmostEquals(36 / 1000, parentSlice.duration);
1374     // Arguments should include only include its arguments.
1375     assertUndefined(parentSlice.args['y']);
1376     assertEquals(1, parentSlice.args['x']);
1377     assertNotUndefined(parentSlice.error);
1378
1379     assertNotUndefined(parentSlice.subSlices);
1380     assertEquals(1, parentSlice.subSlices.length);
1381     var subSlice = parentSlice.subSlices[0];
1382     assertAlmostEquals(1 / 1000, subSlice.start);
1383     assertAlmostEquals(35 / 1000, subSlice.duration);
1384     assertUndefined(subSlice.subSlices);
1385     // Arguments should include those of the END event.
1386     assertEquals(2, subSlice.args['y']);
1387     assertUndefined(subSlice.subSlices);
1388   });
1389
1390   test('nestableAsyncUnmatchedInnerBeginEvent', function() {
1391     var events = [
1392       {name: 'a', args: {z: 3}, pid: 52, ts: 524, cat: 'foo', tid: 53,
1393         ph: 'b', id: 72},
1394       {name: 'c', args: {}, pid: 52, ts: 525, cat: 'foo', tid: 53,
1395         ph: 'n', id: 72},
1396       {name: 'b', args: {x: 1}, pid: 52, ts: 525, cat: 'foo', tid: 53,
1397         ph: 'b', id: 72},
1398       {name: 'a', args: {y: 2}, pid: 52, ts: 565, cat: 'foo', tid: 53,
1399         ph: 'e', id: 72}
1400     ];
1401
1402     var m = new tracing.TraceModel(events);
1403     var t = m.processes[52].threads[53];
1404     assertNotUndefined(t);
1405     // Unmatched BEGIN should produce a warning.
1406     assertTrue(m.hasImportWarnings);
1407     assertEquals(1, t.asyncSliceGroup.slices.length);
1408     var parentSlice = t.asyncSliceGroup.slices[0];
1409     assertEquals('a', parentSlice.title);
1410     assertEquals('foo', parentSlice.category);
1411     assertAlmostEquals(0, parentSlice.start);
1412     assertAlmostEquals(41 / 1000, parentSlice.duration);
1413     // Arguments should include both BEGIN and END event.
1414     assertEquals(2, parentSlice.args['y']);
1415     assertEquals(3, parentSlice.args['z']);
1416     assertUndefined(parentSlice.args['x']);
1417
1418     assertNotUndefined(parentSlice.subSlices);
1419     assertEquals(2, parentSlice.subSlices.length);
1420     var subSliceInstant = parentSlice.subSlices[0];
1421     var subSliceUnmatched = parentSlice.subSlices[1];
1422     assertEquals('c', subSliceInstant.title);
1423     assertEquals('b', subSliceUnmatched.title);
1424     // Unmatched BEGIN ends at the last event of that ID.
1425     assertAlmostEquals(1 / 1000, subSliceUnmatched.start);
1426     assertAlmostEquals(40 / 1000, subSliceUnmatched.duration);
1427     assertUndefined(subSliceUnmatched.subSlices);
1428     assertEquals(1, subSliceUnmatched.args['x']);
1429     assertUndefined(subSliceUnmatched['y']);
1430     assertNotUndefined(subSliceUnmatched.error);
1431     assertAlmostEquals(1 / 1000, subSliceInstant.start);
1432     assertAlmostEquals(0, subSliceInstant.duration);
1433     assertUndefined(subSliceInstant.subSlices);
1434   });
1435
1436   test('nestableAsyncUnmatchedOuterEndEvent', function() {
1437     // Events are intentionally out-of-order.
1438     var events = [
1439       {name: 'b', args: {x: 1}, pid: 52, ts: 525, cat: 'foo', tid: 53,
1440         ph: 'b', id: 72},
1441       {name: 'b', args: {y: 2}, pid: 52, ts: 560, cat: 'foo', tid: 53,
1442         ph: 'e', id: 72},
1443       {name: 'a', args: {z: 3}, pid: 52, ts: 524, cat: 'foo', tid: 53,
1444         ph: 'e', id: 72}
1445     ];
1446
1447     var m = new tracing.TraceModel(events);
1448     var t = m.processes[52].threads[53];
1449     assertNotUndefined(t);
1450     // Unmatched END should produce a warning.
1451     assertTrue(m.hasImportWarnings);
1452     assertEquals(2, t.asyncSliceGroup.slices.length);
1453     var unmatchedSlice = t.asyncSliceGroup.slices[0];
1454     var slice = t.asyncSliceGroup.slices[1];
1455     assertEquals('a', unmatchedSlice.title);
1456     assertAlmostEquals(0, unmatchedSlice.start);
1457     // Unmatched END event begins at the first event of that ID. In this
1458     // case, the first event happens to be the same unmatched event.
1459     assertAlmostEquals(0 / 1000, unmatchedSlice.duration);
1460     assertUndefined(unmatchedSlice.args['x']);
1461     assertUndefined(unmatchedSlice.args['y']);
1462     assertEquals(3, unmatchedSlice.args['z']);
1463     assertNotUndefined(unmatchedSlice.error);
1464     assertUndefined(unmatchedSlice.subSlices);
1465
1466     assertEquals('b', slice.title);
1467     assertAlmostEquals(1 / 1000, slice.start);
1468     assertAlmostEquals(35 / 1000, slice.duration);
1469     // Arguments should include both BEGIN and END event.
1470     assertEquals(1, slice.args['x']);
1471     assertEquals(2, slice.args['y']);
1472     assertUndefined(slice.subSlices);
1473   });
1474
1475   test('nestableAsyncUnmatchedInnerEndEvent', function() {
1476     var events = [
1477       {name: 'a', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53,
1478         ph: 'b', id: 72},
1479       {name: 'c', args: {}, pid: 52, ts: 525, cat: 'foo', tid: 53,
1480         ph: 'n', id: 72},
1481       {name: 'b', args: {z: 3}, pid: 52, ts: 525, cat: 'foo', tid: 53,
1482         ph: 'e', id: 72},
1483       {name: 'a', args: {y: 2}, pid: 52, ts: 565, cat: 'foo', tid: 53,
1484         ph: 'e', id: 72}
1485     ];
1486
1487     var m = new tracing.TraceModel(events);
1488     var t = m.processes[52].threads[53];
1489     assertNotUndefined(t);
1490     // Unmatched END should produce a warning.
1491     assertTrue(m.hasImportWarnings);
1492     assertEquals(1, t.asyncSliceGroup.slices.length);
1493     var parentSlice = t.asyncSliceGroup.slices[0];
1494     assertEquals('a', parentSlice.title);
1495     assertAlmostEquals(0, parentSlice.start);
1496     assertAlmostEquals(41 / 1000, parentSlice.duration);
1497     // Arguments should include both BEGIN and END event.
1498     assertEquals(1, parentSlice.args['x']);
1499     assertEquals(2, parentSlice.args['y']);
1500
1501     assertNotUndefined(parentSlice.subSlices);
1502     assertEquals(2, parentSlice.subSlices.length);
1503     var subSliceInstant = parentSlice.subSlices[0];
1504     var subSliceUnmatched = parentSlice.subSlices[1];
1505     assertEquals('c', subSliceInstant.title);
1506     assertEquals('b', subSliceUnmatched.title);
1507     // Unmatched END begins at the first event of that ID.
1508     assertAlmostEquals(0 / 1000, subSliceUnmatched.start);
1509     assertAlmostEquals(1 / 1000, subSliceUnmatched.duration);
1510     // Arguments should include both BEGIN and END event.
1511     assertUndefined(subSliceUnmatched.args['x']);
1512     assertUndefined(subSliceUnmatched.args['y']);
1513     assertEquals(3, subSliceUnmatched.args['z']);
1514     assertNotUndefined(subSliceUnmatched.error);
1515
1516     assertUndefined(subSliceUnmatched.subSlices);
1517     assertAlmostEquals(1 / 1000, subSliceInstant.start);
1518     assertAlmostEquals(0, subSliceInstant.duration);
1519     assertUndefined(subSliceInstant.subSlices);
1520   });
1521
1522   test('importSamples', function() {
1523     var events = [
1524       {name: 'a', args: {}, pid: 52, ts: 548, cat: 'test', tid: 53, ph: 'P'},
1525       {name: 'b', args: {}, pid: 52, ts: 548, cat: 'test', tid: 53, ph: 'P'},
1526       {name: 'c', args: {}, pid: 52, ts: 558, cat: 'test', tid: 53, ph: 'P'}
1527     ];
1528     var m = new tracing.TraceModel(events);
1529     var p = m.processes[52];
1530     assertNotUndefined(p);
1531     var t = p.threads[53];
1532     assertNotUndefined(t);
1533     assertEquals(3, t.samples_.length);
1534     assertEquals(0.0, t.samples_[0].start);
1535     assertEquals(0.0, t.samples_[1].start);
1536     assertApproxEquals(0.01, t.samples_[2].start);
1537     assertEquals('a', t.samples_[0].leafStackFrame.title);
1538     assertEquals('b', t.samples_[1].leafStackFrame.title);
1539     assertEquals('c', t.samples_[2].leafStackFrame.title);
1540     assertFalse(m.hasImportWarnings);
1541   });
1542
1543   test('importSamplesMissingArgs', function() {
1544     var events = [
1545       {name: 'a', pid: 52, ts: 548, cat: 'test', tid: 53, ph: 'P'},
1546       {name: 'b', pid: 52, ts: 548, cat: 'test', tid: 53, ph: 'P'},
1547       {name: 'c', pid: 52, ts: 549, cat: 'test', tid: 53, ph: 'P'}
1548     ];
1549     var m = new tracing.TraceModel(events);
1550     var p = m.processes[52];
1551     assertNotUndefined(p);
1552     var t = p.threads[53];
1553     assertNotUndefined(t);
1554     assertNotUndefined(t);
1555     assertEquals(3, t.samples_.length);
1556     assertFalse(m.hasImportWarnings);
1557   });
1558
1559   test('importSimpleObject', function() {
1560     var events = [
1561       {ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1562       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: {snapshot: 15}}, // @suppress longLineCheck
1563       {ts: 20000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: {snapshot: 20}}, // @suppress longLineCheck
1564       {ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'a', args: {}} // @suppress longLineCheck
1565     ];
1566     var m = new tracing.TraceModel();
1567     m.importTraces([events], false);
1568     assertEquals(10, m.bounds.min);
1569     assertEquals(50, m.bounds.max);
1570     assertFalse(m.hasImportWarnings);
1571
1572     var p = m.processes[1];
1573     assertNotUndefined(p);
1574
1575     var i10 = p.objects.getObjectInstanceAt('0x1000', 10);
1576     assertEquals('c', i10.category);
1577     assertEquals(10, i10.creationTs);
1578     assertEquals(50, i10.deletionTs);
1579     assertEquals(2, i10.snapshots.length);
1580
1581     var s15 = i10.snapshots[0];
1582     assertEquals(15, s15.ts);
1583     assertEquals(15, s15.args);
1584
1585     var s20 = i10.snapshots[1];
1586     assertEquals(20, s20.ts);
1587     assertEquals(20, s20.args);
1588   });
1589
1590   test('importImplicitObjects', function() {
1591     var events = [
1592       {ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1593       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a',
1594         args: { snapshot: [
1595           { id: 'subObject/0x1',
1596             foo: 1
1597           }
1598         ]}},
1599       {ts: 20000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a',
1600         args: { snapshot: [
1601           { id: 'subObject/0x1',
1602             foo: 2
1603           },
1604           { id: 'subObject/0x2',
1605             foo: 1
1606           }
1607         ]}}
1608     ];
1609
1610     var m = new tracing.TraceModel();
1611     m.importTraces([events], false);
1612     var p1 = m.processes[1];
1613
1614     var iA = p1.objects.getObjectInstanceAt('0x1000', 10);
1615     var subObjectInstances = p1.objects.getAllInstancesByTypeName()[
1616         'subObject'];
1617
1618     assertEquals(2, subObjectInstances.length);
1619     var subObject1 = p1.objects.getObjectInstanceAt('0x1', 15);
1620     assertEquals('subObject', subObject1.name);
1621     assertEquals(15, subObject1.creationTs);
1622
1623     assertEquals(2, subObject1.snapshots.length);
1624     assertEquals(15, subObject1.snapshots[0].ts);
1625     assertEquals(1, subObject1.snapshots[0].args.foo);
1626     assertEquals(20, subObject1.snapshots[1].ts);
1627     assertEquals(2, subObject1.snapshots[1].args.foo);
1628
1629     var subObject2 = p1.objects.getObjectInstanceAt('0x2', 20);
1630     assertEquals('subObject', subObject2.name);
1631     assertEquals(20, subObject2.creationTs);
1632     assertEquals(1, subObject2.snapshots.length);
1633     assertEquals(20, subObject2.snapshots[0].ts);
1634   });
1635
1636   test('importImplicitObjectWithCategoryOverride', function() {
1637     var events = [
1638       {ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'cat', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1639       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'otherCat', id: '0x1000', name: 'a', // @suppress longLineCheck
1640         args: { snapshot: [
1641           { id: 'subObject/0x1',
1642             cat: 'cat',
1643             foo: 1
1644           }
1645         ]}}
1646     ];
1647
1648     var m = new tracing.TraceModel();
1649     m.importTraces([events], false);
1650     var p1 = m.processes[1];
1651
1652     var iA = p1.objects.getObjectInstanceAt('0x1000', 10);
1653     var subObjectInstances = p1.objects.getAllInstancesByTypeName()[
1654         'subObject'];
1655
1656     assertEquals(1, subObjectInstances.length);
1657   });
1658
1659   test('importImplicitObjectWithBaseTypeOverride', function() {
1660     var events = [
1661       {ts: 10000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'PictureLayerImpl', args: { // @suppress longLineCheck
1662         snapshot: {
1663           base_type: 'LayerImpl'
1664         }
1665       }},
1666       {ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'LayerImpl', args: {}} // @suppress longLineCheck
1667     ];
1668
1669     var m = new tracing.TraceModel();
1670     m.importTraces([events], false);
1671     var p1 = m.processes[1];
1672     assertEquals(0, m.importWarnings.length);
1673
1674     var iA = p1.objects.getObjectInstanceAt('0x1000', 10);
1675     assertEquals(1, iA.snapshots.length);
1676   });
1677
1678   test('importIDRefs', function() {
1679     var events = [
1680       // An object with two snapshots.
1681       {ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1682       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: {snapshot: 15}}, // @suppress longLineCheck
1683       {ts: 20000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: {snapshot: 20}}, // @suppress longLineCheck
1684       {ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1685
1686       // A slice that references the object.
1687       {ts: 17000, pid: 1, tid: 1, ph: 'B', cat: 'c', name: 'taskSlice', args: {my_object: {id_ref: '0x1000'}}}, // @suppress longLineCheck
1688       {ts: 17500, pid: 1, tid: 1, ph: 'E', cat: 'c', name: 'taskSlice', args: {}} // @suppress longLineCheck
1689     ];
1690
1691     var m = new tracing.TraceModel();
1692     m.importTraces([events], false);
1693     var p1 = m.processes[1];
1694
1695     var iA = p1.objects.getObjectInstanceAt('0x1000', 10);
1696     var s15 = iA.getSnapshotAt(15);
1697
1698     var taskSlice = p1.threads[1].sliceGroup.slices[0];
1699     assertEquals(s15, taskSlice.args.my_object);
1700   });
1701
1702   test('importIDRefsThatPointAtEachOther', function() {
1703     var events = [
1704       // An object.
1705       {ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1706       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: { // @suppress longLineCheck
1707         snapshot: { x: {
1708           id: 'foo/0x1001',
1709           value: 'bar'
1710         }}}},
1711       {ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1712
1713       // A slice that references the object.
1714       {ts: 17000, pid: 1, tid: 1, ph: 'B', cat: 'c', name: 'taskSlice', args: {my_object: {id_ref: '0x1001'}}}, // @suppress longLineCheck
1715       {ts: 17500, pid: 1, tid: 1, ph: 'E', cat: 'c', name: 'taskSlice', args: {}} // @suppress longLineCheck
1716     ];
1717
1718     var m = new tracing.TraceModel();
1719     m.importTraces([events], false);
1720     var p1 = m.processes[1];
1721
1722     var iA = p1.objects.getObjectInstanceAt('0x1000', 15);
1723     var iFoo = p1.objects.getObjectInstanceAt('0x1001', 15);
1724     assertNotUndefined(iA);
1725     assertNotUndefined(iFoo);
1726
1727     var a15 = iA.getSnapshotAt(15);
1728     var foo15 = iFoo.getSnapshotAt(15);
1729
1730     var taskSlice = p1.threads[1].sliceGroup.slices[0];
1731     assertEquals(foo15, taskSlice.args.my_object);
1732   });
1733
1734   test('importArrayWithIDs', function() {
1735     var events = [
1736       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: { // @suppress longLineCheck
1737         snapshot: { x: [
1738           {id: 'foo/0x1001', value: 'bar1'},
1739           {id: 'foo/0x1002', value: 'bar2'},
1740           {id: 'foo/0x1003', value: 'bar3'}
1741         ]}}}
1742     ];
1743
1744     var m = new tracing.TraceModel();
1745     m.importTraces([events], false);
1746     var p1 = m.processes[1];
1747
1748     var sA = p1.objects.getSnapshotAt('0x1000', 15);
1749     assertTrue(sA.args.x instanceof Array);
1750     assertEquals(3, sA.args.x.length);
1751     assertTrue(sA.args.x[0] instanceof tracing.trace_model.ObjectSnapshot);
1752     assertTrue(sA.args.x[1] instanceof tracing.trace_model.ObjectSnapshot);
1753     assertTrue(sA.args.x[2] instanceof tracing.trace_model.ObjectSnapshot);
1754   });
1755
1756   test('importDoesNotMutateEventList', function() {
1757     var events = [
1758       // An object.
1759       {ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1760       {ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: { // @suppress longLineCheck
1761         snapshot: {foo: 15}}},
1762       {ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
1763
1764       // A slice that references the object.
1765       {ts: 17000, pid: 1, tid: 1, ph: 'B', cat: 'c', name: 'taskSlice', args: {
1766         my_object: {id_ref: '0x1000'}}
1767       },
1768       {ts: 17500, pid: 1, tid: 1, ph: 'E', cat: 'c', name: 'taskSlice', args: {}} // @suppress longLineCheck
1769     ];
1770
1771     // The A type family exists to mutate the args list provided to
1772     // snapshots.
1773     function ASnapshot() {
1774       tracing.trace_model.ObjectSnapshot.apply(this, arguments);
1775       this.args.foo = 7;
1776     }
1777     ASnapshot.prototype = {
1778       __proto__: tracing.trace_model.ObjectSnapshot.prototype
1779     };
1780
1781     // Import event while the A types are registered, causing the
1782     // arguments of the snapshots to be mutated.
1783     var m = new tracing.TraceModel();
1784     try {
1785       tracing.trace_model.ObjectSnapshot.register('a', ASnapshot);
1786       m.importTraces([events], false);
1787     } finally {
1788       tracing.trace_model.ObjectSnapshot.unregister('a');
1789     }
1790     assertFalse(m.hasImportWarnings);
1791
1792     // Verify that the events array wasn't modified.
1793     assertObjectEquals(
1794         events[1].args,
1795         {snapshot: {foo: 15}});
1796     assertObjectEquals(
1797         events[3].args,
1798         {my_object: {id_ref: '0x1000'}});
1799   });
1800
1801   test('importFlowEvent', function() {
1802     var events = [
1803       { name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 548, ph: 's', args: {} },  // @suppress longLineCheck
1804       { name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 560, ph: 't', args: {} },  // @suppress longLineCheck
1805       { name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 580, ph: 'f', args: {} }   // @suppress longLineCheck
1806     ];
1807
1808     var m = new tracing.TraceModel(events);
1809     var t = m.processes[52].threads[53];
1810
1811     assertNotUndefined(t);
1812     assertEquals(3, t.sliceGroup.slices.length);
1813     assertEquals(2, m.flowEvents.length);
1814
1815     var start = m.flowEvents[0][0];
1816     var step = m.flowEvents[0][1];
1817     var finish = m.flowEvents[1][1];
1818
1819     assertEquals('a', start.title);
1820     assertEquals('foo', start.category);
1821     assertEquals(72, start.id);
1822     assertEquals(0, start.start);
1823     assertEquals(0, start.duration);
1824
1825     assertEquals(start.title, step.title);
1826     assertEquals(start.category, step.category);
1827     assertEquals(start.id, step.id);
1828     assertAlmostEquals(12 / 1000, step.start);
1829     assertEquals(0, step.duration);
1830
1831     assertEquals(start.title, finish.title);
1832     assertEquals(start.category, finish.category);
1833     assertEquals(start.id, finish.id);
1834     assertAlmostEquals((20 + 12) / 1000, finish.start);
1835     assertEquals(0, finish.duration);
1836
1837     assertEquals(2, m.flowIntervalTree.size);
1838   });
1839
1840   test('importOutOfOrderFlowEvent', function() {
1841     var events = [
1842       { name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 548, ph: 's', args: {} },  // @suppress longLineCheck
1843       { name: 'b', cat: 'foo', id: 73, pid: 52, tid: 53, ts: 148, ph: 's', args: {} },  // @suppress longLineCheck
1844       { name: 'b', cat: 'foo', id: 73, pid: 52, tid: 53, ts: 570, ph: 'f', args: {} },   // @suppress longLineCheck
1845       { name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 560, ph: 't', args: {} },  // @suppress longLineCheck
1846       { name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 580, ph: 'f', args: {} }   // @suppress longLineCheck
1847     ];
1848
1849     var expected = [0.4, 0.0, 0.412];
1850     var m = new tracing.TraceModel(events);
1851     assertEquals(3, m.flowIntervalTree.size);
1852
1853     var order = m.flowEvents.map(function(x) { return x[0].start });
1854     for (var i = 0; i < expected.length; ++i)
1855       assertAlmostEquals(expected[i], order[i]);
1856   });
1857
1858   test('importCompleteEvent', function() {
1859     var events = [
1860       { name: 'a', args: {}, pid: 52, ts: 629, dur: 1, cat: 'baz', tid: 53, ph: 'X' },  // @suppress longLineCheck
1861       { name: 'b', args: {}, pid: 52, ts: 730, dur: 20, cat: 'foo', tid: 53, ph: 'X' },  // @suppress longLineCheck
1862       { name: 'c', args: {}, pid: 52, ts: 740, cat: 'baz', tid: 53, ph: 'X' }
1863     ];
1864
1865     var m = new tracing.TraceModel(events);
1866     assertEquals(1, m.numProcesses);
1867     var p = m.processes[52];
1868     assertNotUndefined(p);
1869
1870     assertEquals(1, p.numThreads);
1871     var t = p.threads[53];
1872     assertNotUndefined(t);
1873     assertEquals(3, t.sliceGroup.slices.length);
1874     assertEquals(53, t.tid);
1875
1876     var slice = t.sliceGroup.slices[0];
1877     assertEquals('a', slice.title);
1878     assertEquals('baz', slice.category);
1879     assertAlmostEquals(0, slice.start);
1880     assertAlmostEquals(1 / 1000, slice.duration);
1881     assertEquals(0, slice.subSlices.length);
1882
1883     slice = t.sliceGroup.slices[1];
1884     assertEquals('b', slice.title);
1885     assertEquals('foo', slice.category);
1886     assertAlmostEquals((730 - 629) / 1000, slice.start);
1887     assertAlmostEquals(20 / 1000, slice.duration);
1888     assertEquals(1, slice.subSlices.length);
1889
1890     slice = t.sliceGroup.slices[2];
1891     assertEquals('c', slice.title);
1892     assertTrue(slice.didNotFinish);
1893     assertAlmostEquals(10 / 1000, slice.duration);
1894   });
1895
1896   test('importCompleteEventWithCpuDuration', function() {
1897     var events = [
1898       { name: 'a', args: {}, pid: 52, ts: 629, dur: 1, cat: 'baz', tid: 53, ph: 'X', tts: 12, tdur: 1 },  // @suppress longLineCheck
1899       { name: 'b', args: {}, pid: 52, ts: 730, dur: 20, cat: 'foo', tid: 53, ph: 'X', tts: 110, tdur: 16 },  // @suppress longLineCheck
1900       { name: 'c', args: {}, pid: 52, ts: 740, cat: 'baz', tid: 53, ph: 'X', tts: 115 }  // @suppress longLineCheck
1901     ];
1902
1903     var m = new tracing.TraceModel(events);
1904     assertEquals(1, m.numProcesses);
1905     var p = m.processes[52];
1906     assertNotUndefined(p);
1907
1908     assertEquals(1, p.numThreads);
1909     var t = p.threads[53];
1910     assertNotUndefined(t);
1911     assertEquals(3, t.sliceGroup.slices.length);
1912     assertEquals(53, t.tid);
1913
1914     var slice = t.sliceGroup.slices[0];
1915     assertEquals('a', slice.title);
1916     assertEquals('baz', slice.category);
1917     assertAlmostEquals(0, slice.start);
1918     assertAlmostEquals(1 / 1000, slice.duration);
1919     assertAlmostEquals(12 / 1000, slice.cpuStart);
1920     assertAlmostEquals(1 / 1000, slice.cpuDuration);
1921     assertEquals(0, slice.subSlices.length);
1922
1923     slice = t.sliceGroup.slices[1];
1924     assertEquals('b', slice.title);
1925     assertEquals('foo', slice.category);
1926     assertAlmostEquals((730 - 629) / 1000, slice.start);
1927     assertAlmostEquals(20 / 1000, slice.duration);
1928     assertAlmostEquals(110 / 1000, slice.cpuStart);
1929     assertAlmostEquals(16 / 1000, slice.cpuDuration);
1930     assertEquals(1, slice.subSlices.length);
1931
1932     slice = t.sliceGroup.slices[2];
1933     assertEquals('c', slice.title);
1934     assertTrue(slice.didNotFinish);
1935     assertAlmostEquals(10 / 1000, slice.duration);
1936   });
1937
1938   test('importNestedCompleteEventWithTightBounds', function() {
1939     var events = [
1940       { name: 'a', args: {}, pid: 52, ts: 244654227065, dur: 36075, cat: 'baz', tid: 53, ph: 'X' },  // @suppress longLineCheck
1941       { name: 'b', args: {}, pid: 52, ts: 244654227095, dur: 36045, cat: 'foo', tid: 53, ph: 'X' }  // @suppress longLineCheck
1942     ];
1943
1944     var m = new tracing.TraceModel(events, false);
1945     var t = m.processes[52].threads[53];
1946
1947     var sA = findSliceNamed(t.sliceGroup, 'a');
1948     var sB = findSliceNamed(t.sliceGroup, 'b');
1949
1950     assertEquals('a', sA.title);
1951     assertEquals('baz', sA.category);
1952     assertEquals(244654227.065, sA.start);
1953     assertEquals(36.075, sA.duration);
1954     assertAlmostEquals(0.03, sA.selfTime);
1955
1956     assertEquals('b', sB.title);
1957     assertEquals('foo', sB.category);
1958     assertEquals(244654227.095, sB.start);
1959     assertEquals(36.045, sB.duration);
1960
1961     assertTrue(sA.subSlices.length == 1);
1962     assertTrue(sA.subSlices[0] == sB);
1963     assertTrue(sB.parentSlice == sA);
1964   });
1965
1966   test('importAsyncEventWithSameTimestamp', function() {
1967     var events = [];
1968     // Events are added with ts 0, 1, 1, 2, 2, 3, 3 ...500, 500, 1000
1969     // and use 'seq' to track the order of when the event is recorded.
1970     events.push({name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 0, ph: 'S', args: {'seq': 0}});  // @suppress longLineCheck
1971
1972     for (var i = 1; i <= 1000; i++)
1973       events.push({name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: Math.round(i / 2) , ph: 'T', args: {'seq': i}});  // @suppress longLineCheck
1974
1975     events.push({name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 1000, ph: 'F', args: {'seq': 1001}});  // @suppress longLineCheck
1976
1977     var m = new tracing.TraceModel(events);
1978     var t = m.processes[52].threads[53];
1979
1980     assertEquals(1, t.asyncSliceGroup.slices.length);
1981     var parentSlice = t.asyncSliceGroup.slices[0];
1982     assertEquals('a', parentSlice.title);
1983     assertEquals('foo', parentSlice.category);
1984
1985     assertNotUndefined(parentSlice.subSlices);
1986     var subSlices = parentSlice.subSlices;
1987     assertEquals(1001, subSlices.length);
1988     // Slices should be sorted according to 'ts'. And if 'ts' is the same,
1989     // slices should keep the order that they were recorded.
1990     for (var i = 0; i < 1000; i++)
1991       assertEquals(subSlices[i].args['seq'], i);
1992   });
1993
1994   test('sampleDataSimple', function() {
1995     var events = {
1996       'traceEvents': [],
1997       'stackFrames': {
1998         '1': {
1999           'category': 'mod',
2000           'name': 'main'
2001         },
2002         '2': {
2003           'category': 'mod',
2004           'name': 'a',
2005           'parent': 1
2006         },
2007         '3': {
2008           'category': 'mod',
2009           'name': 'a_sub',
2010           'parent': 2
2011         },
2012         '4': {
2013           'category': 'mod',
2014           'name': 'b',
2015           'parent': 1
2016         }
2017       },
2018       'samples': [
2019         {
2020           'cpu': 0, 'tid': 1, 'ts': 1000.0,
2021           'name': 'cycles:HG', 'sf': 3, 'weight': 1
2022         },
2023         {
2024           'cpu': 0, 'tid': 1, 'ts': 2000.0,
2025           'name': 'cycles:HG', 'sf': 2, 'weight': 1
2026         },
2027         {
2028           'cpu': 1, 'tid': 1, 'ts': 3000.0,
2029           'name': 'cycles:HG', 'sf': 3, 'weight': 1
2030         }
2031       ]
2032     };
2033     var m = new tracing.TraceModel(events, false);
2034     assertNotUndefined(m.kernel.cpus[0]);
2035     assertEquals(1, m.getAllThreads().length);
2036
2037     assertEquals(4, tv.dictionaryKeys(m.stackFrames).length);
2038     assertEquals(3, m.samples.length);
2039
2040     var t1 = m.processes[1].threads[1];
2041     assertEquals(3, t1.samples.length);
2042
2043     var c0 = m.kernel.cpus[0];
2044     var c1 = m.kernel.cpus[1];
2045     assertEquals(2, c0.samples.length);
2046     assertEquals(1, c1.samples.length);
2047
2048     assertEquals(c0, m.samples[0].cpu);
2049     assertEquals(t1, m.samples[0].thread);
2050     assertEquals('cycles:HG', m.samples[0].title);
2051     assertEquals(1, m.samples[0].start);
2052     assertArrayEquals(
2053         ['main', 'a', 'a_sub'],
2054         m.samples[0].stackTrace.map(function(x) { return x.title; }));
2055     assertEquals(1, m.samples[0].weight);
2056   });
2057
2058   // TODO(nduca): one slice, two threads
2059   // TODO(nduca): one slice, two pids
2060
2061 });
2062 </script>
2063