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