Home | History | Annotate | Download | only in src
      1 <!DOCTYPE html>
      2 <html>
      3 <!--
      4 Copyright (c) 2012 The Chromium Authors. All rights reserved.
      5 Use of this source code is governed by a BSD-style license that can be
      6 found in the LICENSE file.
      7 -->
      8 <head>
      9 <title>TraceEventImporter tests</title>
     10 <script src="base.js"></script>
     11 <script>
     12   base.require('unittest');
     13   base.require('test_utils');
     14   base.require('trace_event_importer');
     15 </script>
     16 </head>
     17 <body>
     18 <script>
     19 'use strict';
     20 
     21 var findSliceNamed = test_utils.findSliceNamed;
     22 
     23 function testCanImportEmpty() {
     24   self.assertFalse(tracing.TraceEventImporter.canImport([]));
     25   self.assertFalse(tracing.TraceEventImporter.canImport(''));
     26 }
     27 
     28 function testBasicSingleThreadNonnestedParsing() {
     29   var events = [
     30     {name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
     31     {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'},
     32     {name: 'b', args: {}, pid: 52, ts: 629, cat: 'bar', tid: 53, ph: 'B'},
     33     {name: 'b', args: {}, pid: 52, ts: 631, cat: 'bar', tid: 53, ph: 'E'}
     34   ];
     35 
     36   var m = new tracing.TimelineModel(events);
     37   assertEquals(1, m.numProcesses);
     38   var p = m.processes[52];
     39   assertNotUndefined(p);
     40 
     41   assertEquals(1, p.numThreads);
     42   var t = p.threads[53];
     43   assertNotUndefined(t);
     44   assertEquals(2, t.slices.length);
     45   assertEquals(53, t.tid);
     46   var slice = t.slices[0];
     47   assertEquals('a', slice.title);
     48   assertEquals('foo', slice.category);
     49   assertEquals(0, slice.start);
     50   assertAlmostEquals((560 - 520) / 1000, slice.duration);
     51   assertEquals(0, slice.subSlices.length);
     52 
     53   slice = t.slices[1];
     54   assertEquals('b', slice.title);
     55   assertEquals('bar', slice.category);
     56   assertAlmostEquals((629 - 520) / 1000, slice.start);
     57   assertAlmostEquals((631 - 629) / 1000, slice.duration);
     58   assertEquals(0, slice.subSlices.length);
     59 }
     60 
     61 function testArgumentDupeCreatesNonFailingImportError() {
     62   var events = [
     63     {name: 'a', args: {'x': 1}, pid: 1, ts: 520, cat: 'foo', tid: 1, ph: 'B'},
     64     {name: 'a', args: {'x': 2}, pid: 1, ts: 560, cat: 'foo', tid: 1, ph: 'E'}
     65   ];
     66 
     67   var m = new tracing.TimelineModel(events);
     68   var t = m.processes[1].threads[1];
     69   var sA = findSliceNamed(t.slices, 'a');
     70 
     71   assertEquals(2, sA.args.x);
     72   assertEquals(m.importErrors.length, 1);
     73 }
     74 
     75 function testCategoryBeginEndMismatchPrefersBegin() {
     76   var events = [
     77     {name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
     78     {name: 'a', args: {}, pid: 52, ts: 560, cat: 'bar', tid: 53, ph: 'E'}
     79   ];
     80 
     81   var m = new tracing.TimelineModel(events);
     82   assertEquals(1, m.numProcesses);
     83   var p = m.processes[52];
     84   assertNotUndefined(p);
     85 
     86   assertEquals(1, p.numThreads);
     87   var t = p.threads[53];
     88   assertNotUndefined(t);
     89   assertEquals(1, t.slices.length);
     90   assertEquals(53, t.tid);
     91   var slice = t.slices[0];
     92   assertEquals('a', slice.title);
     93   assertEquals('foo', slice.category);
     94 }
     95 
     96 function testNestedParsing() {
     97   var events = [
     98     {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
     99     {name: 'b', args: {}, pid: 1, ts: 2, cat: 'bar', tid: 1, ph: 'B'},
    100     {name: 'b', args: {}, pid: 1, ts: 3, cat: 'bar', tid: 1, ph: 'E'},
    101     {name: 'a', args: {}, pid: 1, ts: 4, cat: 'foo', tid: 1, ph: 'E'}
    102   ];
    103   var m = new tracing.TimelineModel(events, false);
    104   var t = m.processes[1].threads[1];
    105 
    106   var sA = findSliceNamed(t.slices, 'a');
    107   var sB = findSliceNamed(t.slices, 'b');
    108 
    109   assertEquals('a', sA.title);
    110   assertEquals('foo', sA.category);
    111   assertEquals(0.001, sA.start);
    112   assertEquals(0.003, sA.duration);
    113 
    114   assertEquals('b', sB.title);
    115   assertEquals('bar', sB.category);
    116   assertEquals(0.002, sB.start);
    117   assertEquals(0.001, sB.duration);
    118 }
    119 
    120 function testAutoclosing() {
    121   var events = [
    122     // Slice that doesn't finish.
    123     {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
    124 
    125     // Slice that does finish to give an 'end time' to make autoclosing work.
    126     {name: 'b', args: {}, pid: 1, ts: 1, cat: 'bar', tid: 2, ph: 'B'},
    127     {name: 'b', args: {}, pid: 1, ts: 2, cat: 'bar', tid: 2, ph: 'E'}
    128   ];
    129   var m = new tracing.TimelineModel(events);
    130   var p = m.processes[1];
    131   var t = p.threads[1];
    132   var slice = t.slices[0];
    133   assertEquals('a', slice.title);
    134   assertEquals('foo', slice.category);
    135   assertTrue(slice.didNotFinish);
    136   assertEquals(0, slice.start);
    137   assertEquals((2 - 1) / 1000, slice.duration);
    138 }
    139 
    140 function testAutoclosingLoneBegin() {
    141   var events = [
    142     // Slice that doesn't finish.
    143     {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'}
    144   ];
    145   var m = new tracing.TimelineModel(events);
    146   var p = m.processes[1];
    147   var t = p.threads[1];
    148   var slice = t.slices[0];
    149   assertEquals('a', slice.title);
    150   assertEquals('foo', slice.category);
    151   assertTrue(slice.didNotFinish);
    152   assertEquals(0, slice.start);
    153   assertEquals(0, slice.duration);
    154 }
    155 
    156 function testAutoclosingWithSubTasks() {
    157   var events = [
    158     {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
    159     {name: 'b1', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 1, ph: 'B'},
    160     {name: 'b1', args: {}, pid: 1, ts: 3, cat: 'foo', tid: 1, ph: 'E'},
    161     {name: 'b2', args: {}, pid: 1, ts: 3, cat: 'foo', tid: 1, ph: 'B'}
    162   ];
    163   var m = new tracing.TimelineModel(events, false);
    164   var t = m.processes[1].threads[1];
    165 
    166   var sA = findSliceNamed(t.slices, 'a');
    167   var sB1 = findSliceNamed(t.slices, 'b1');
    168   var sB2 = findSliceNamed(t.slices, 'b2');
    169 
    170   assertEquals(0.003, sA.end);
    171   assertEquals(0.003, sB1.end);
    172   assertEquals(0.003, sB2.end);
    173 }
    174 
    175 function testAutoclosingWithEventsOutsideRange() {
    176   var events = [
    177     // Slice that begins before min and ends after max of the other threads.
    178     {name: 'a', args: {}, pid: 1, ts: 0, cat: 'foo', tid: 1, ph: 'B'},
    179     {name: 'b', args: {}, pid: 1, ts: 3, cat: 'foo', tid: 1, ph: 'B'},
    180 
    181     // Slice that does finish to give an 'end time' to establish a basis
    182     {name: 'c', args: {}, pid: 1, ts: 1, cat: 'bar', tid: 2, ph: 'B'},
    183     {name: 'c', args: {}, pid: 1, ts: 2, cat: 'bar', tid: 2, ph: 'E'}
    184   ];
    185   var m = new tracing.TimelineModel(events, false);
    186   var p = m.processes[1];
    187   var t = p.threads[1];
    188   assertEquals(2, t.slices.length);
    189 
    190   var slice = findSliceNamed(t.slices, 'a');
    191   assertEquals('a', slice.title);
    192   assertEquals('foo', slice.category);
    193   assertEquals(0, slice.start);
    194   assertEquals(0.003, slice.duration);
    195 
    196   var t2 = p.threads[2];
    197   var slice2 = findSliceNamed(t2.slices, 'c');
    198   assertEquals('c', slice2.title);
    199   assertEquals('bar', slice2.category);
    200   assertEquals(0.001, slice2.start);
    201   assertEquals(0.001, slice2.duration);
    202 
    203   assertEquals(0.000, m.minTimestamp);
    204   assertEquals(0.003, m.maxTimestamp);
    205 }
    206 
    207 function testNestedAutoclosing() {
    208   var events = [
    209     // Tasks that don't finish.
    210     {name: 'a1', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
    211     {name: 'a2', args: {}, pid: 1, ts: 1.5, cat: 'foo', tid: 1, ph: 'B'},
    212 
    213     // Slice that does finish to give an 'end time' to make autoclosing work.
    214     {name: 'b', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 2, ph: 'B'},
    215     {name: 'b', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 2, ph: 'E'}
    216   ];
    217   var m = new tracing.TimelineModel(events, false);
    218   var t1 = m.processes[1].threads[1];
    219   var t2 = m.processes[1].threads[2];
    220 
    221   var sA1 = findSliceNamed(t1.slices, 'a1');
    222   var sA2 = findSliceNamed(t1.slices, 'a2');
    223   var sB = findSliceNamed(t2.slices, 'b');
    224 
    225   assertEquals(0.002, sA1.end);
    226   assertEquals(0.002, sA2.end);
    227 }
    228 
    229 function testTaskColoring() {
    230   // The test below depends on hashing of 'a' != 'b'. Fail early if that
    231   // assumption is incorrect.
    232   assertNotEquals(tracing.getStringHash('a'), tracing.getStringHash('b'));
    233 
    234   var events = [
    235     {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
    236     {name: 'a', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 1, ph: 'E'},
    237     {name: 'b', args: {}, pid: 1, ts: 3, cat: 'bar', tid: 1, ph: 'B'},
    238     {name: 'b', args: {}, pid: 1, ts: 4, cat: 'bar', tid: 1, ph: 'E'},
    239     {name: 'a', args: {}, pid: 1, ts: 5, cat: 'baz', tid: 1, ph: 'B'},
    240     {name: 'a', args: {}, pid: 1, ts: 6, cat: 'baz', tid: 1, ph: 'E'}
    241   ];
    242   var m = new tracing.TimelineModel(events);
    243   var p = m.processes[1];
    244   var t = p.threads[1];
    245   var a1 = t.slices[0];
    246   assertEquals('a', a1.title);
    247   assertEquals('foo', a1.category);
    248   var b = t.slices[1];
    249   assertEquals('b', b.title);
    250   assertEquals('bar', b.category);
    251   assertNotEquals(a1.colorId, b.colorId);
    252   var a2 = t.slices[2];
    253   assertEquals('a', a2.title);
    254   assertEquals('baz', a2.category);
    255   assertEquals(a1.colorId, a2.colorId);
    256 }
    257 
    258 function testMultipleThreadParsing() {
    259   var events = [
    260     {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
    261     {name: 'a', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 1, ph: 'E'},
    262     {name: 'b', args: {}, pid: 1, ts: 3, cat: 'bar', tid: 2, ph: 'B'},
    263     {name: 'b', args: {}, pid: 1, ts: 4, cat: 'bar', tid: 2, ph: 'E'}
    264   ];
    265   var m = new tracing.TimelineModel(events);
    266   assertEquals(1, m.numProcesses);
    267   var p = m.processes[1];
    268   assertNotUndefined(p);
    269 
    270   assertEquals(2, p.numThreads);
    271 
    272   // Check thread 1.
    273   var t = p.threads[1];
    274   assertNotUndefined(t);
    275   assertEquals(1, t.slices.length);
    276   assertEquals(1, t.tid);
    277 
    278   var slice = t.slices[0];
    279   assertEquals('a', slice.title);
    280   assertEquals('foo', slice.category);
    281   assertEquals(0, slice.start);
    282   assertEquals((2 - 1) / 1000, slice.duration);
    283   assertEquals(0, slice.subSlices.length);
    284 
    285   // Check thread 2.
    286   var t = p.threads[2];
    287   assertNotUndefined(t);
    288   assertEquals(1, t.slices.length);
    289   assertEquals(2, t.tid);
    290 
    291   slice = t.slices[0];
    292   assertEquals('b', slice.title);
    293   assertEquals('bar', slice.category);
    294   assertEquals((3 - 1) / 1000, slice.start);
    295   assertEquals((4 - 3) / 1000, slice.duration);
    296   assertEquals(0, slice.subSlices.length);
    297 }
    298 
    299 function testMultiplePidParsing() {
    300   var events = [
    301     {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
    302     {name: 'a', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 1, ph: 'E'},
    303     {name: 'b', args: {}, pid: 2, ts: 3, cat: 'bar', tid: 2, ph: 'B'},
    304     {name: 'b', args: {}, pid: 2, ts: 4, cat: 'bar', tid: 2, ph: 'E'}
    305   ];
    306   var m = new tracing.TimelineModel(events);
    307   assertEquals(2, m.numProcesses);
    308   var p = m.processes[1];
    309   assertNotUndefined(p);
    310 
    311   assertEquals(1, p.numThreads);
    312 
    313   // Check process 1 thread 1.
    314   var t = p.threads[1];
    315   assertNotUndefined(t);
    316   assertEquals(1, t.slices.length);
    317   assertEquals(1, t.tid);
    318 
    319   var slice = t.slices[0];
    320   assertEquals('a', slice.title);
    321   assertEquals('foo', slice.category);
    322   assertEquals(0, slice.start);
    323   assertEquals((2 - 1) / 1000, slice.duration);
    324   assertEquals(0, slice.subSlices.length);
    325 
    326   // Check process 2 thread 2.
    327   var p = m.processes[2];
    328   assertNotUndefined(p);
    329   assertEquals(1, p.numThreads);
    330   var t = p.threads[2];
    331   assertNotUndefined(t);
    332   assertEquals(1, t.slices.length);
    333   assertEquals(2, t.tid);
    334 
    335   slice = t.slices[0];
    336   assertEquals('b', slice.title);
    337   assertEquals('bar', slice.category);
    338   assertEquals((3 - 1) / 1000, slice.start);
    339   assertEquals((4 - 3) / 1000, slice.duration);
    340   assertEquals(0, slice.subSlices.length);
    341 
    342   // Check getAllThreads.
    343   assertArrayEquals([m.processes[1].threads[1], m.processes[2].threads[2]],
    344                     m.getAllThreads());
    345 }
    346 
    347 // Thread names.
    348 function testThreadNames() {
    349   var events = [
    350     {name: 'thread_name', args: {name: 'Thread 1'},
    351       pid: 1, ts: 0, tid: 1, ph: 'M'},
    352     {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
    353     {name: 'a', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 1, ph: 'E'},
    354     {name: 'b', args: {}, pid: 2, ts: 3, cat: 'foo', tid: 2, ph: 'B'},
    355     {name: 'b', args: {}, pid: 2, ts: 4, cat: 'foo', tid: 2, ph: 'E'},
    356     {name: 'thread_name', args: {name: 'Thread 2'},
    357       pid: 2, ts: 0, tid: 2, ph: 'M'}
    358   ];
    359   var m = new tracing.TimelineModel(events);
    360   assertEquals('Thread 1', m.processes[1].threads[1].name);
    361   assertEquals('Thread 2', m.processes[2].threads[2].name);
    362 }
    363 
    364 function testParsingWhenEndComesFirst() {
    365   var events = [
    366     {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'E'},
    367     {name: 'a', args: {}, pid: 1, ts: 4, cat: 'foo', tid: 1, ph: 'B'},
    368     {name: 'a', args: {}, pid: 1, ts: 5, cat: 'foo', tid: 1, ph: 'E'}
    369   ];
    370   var m = new tracing.TimelineModel(events, false);
    371   var p = m.processes[1];
    372   var t = p.threads[1];
    373   assertEquals(1, t.slices.length);
    374   assertEquals('a', t.slices[0].title);
    375   assertEquals('foo', t.slices[0].category);
    376   assertEquals(0.004, t.slices[0].start);
    377   assertEquals(0.001, t.slices[0].duration);
    378   assertEquals(1, m.importErrors.length);
    379 }
    380 
    381 function testImmediateParsing() {
    382   var events = [
    383     // Need to include immediates inside a task so the timeline
    384     // recentering/zeroing doesn't clobber their timestamp.
    385     {name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
    386     {name: 'immediate', args: {}, pid: 1, ts: 2, cat: 'bar', tid: 1, ph: 'I'},
    387     {name: 'a', args: {}, pid: 1, ts: 4, cat: 'foo', tid: 1, ph: 'E'}
    388   ];
    389   var m = new tracing.TimelineModel(events, false);
    390   var p = m.processes[1];
    391   var t = p.threads[1];
    392   assertEquals(2, t.slices.length);
    393   assertEquals(0.002, t.slices[0].start);
    394   assertEquals(0, t.slices[0].duration);
    395   assertEquals(0.001, t.slices[1].start);
    396   assertEquals(0.003, t.slices[1].duration);
    397 
    398   assertEquals(2, t.slices.length);
    399   var slice = findSliceNamed(t.slices, 'a');
    400   assertEquals('a', slice.title);
    401   assertEquals('foo', slice.category);
    402   assertEquals(0.003, slice.duration);
    403 
    404   var immed = findSliceNamed(t.slices, 'immediate');
    405   assertEquals('immediate', immed.title);
    406   assertEquals('bar', immed.category);
    407   assertEquals(0.002, immed.start);
    408   assertEquals(0, immed.duration);
    409   assertEquals(0, immed.subSlices.length);
    410 }
    411 
    412 function testSimpleCounter() {
    413   var events = [
    414     {name: 'ctr', args: {'value': 0}, pid: 1, ts: 0, cat: 'foo', tid: 1,
    415      ph: 'C'},
    416     {name: 'ctr', args: {'value': 10}, pid: 1, ts: 10, cat: 'foo', tid: 1,
    417      ph: 'C'},
    418     {name: 'ctr', args: {'value': 0}, pid: 1, ts: 20, cat: 'foo', tid: 1,
    419      ph: 'C'}
    420 
    421   ];
    422   var m = new tracing.TimelineModel(events);
    423   var p = m.processes[1];
    424   var ctr = m.processes[1].counters['foo.ctr'];
    425 
    426   assertEquals('ctr', ctr.name);
    427   assertEquals('foo', ctr.category);
    428   assertEquals(3, ctr.numSamples);
    429   assertEquals(1, ctr.numSeries);
    430 
    431   assertArrayEquals(['value'], ctr.seriesNames);
    432   assertArrayEquals([tracing.getStringColorId('ctr.value')], ctr.seriesColors);
    433   assertArrayEquals([0, 0.01, 0.02], ctr.timestamps);
    434   assertArrayEquals([0, 10, 0], ctr.samples);
    435   assertArrayEquals([0, 10, 0], ctr.totals);
    436   assertEquals(10, ctr.maxTotal);
    437 }
    438 
    439 function testInstanceCounter() {
    440   var events = [
    441     {name: 'ctr', args: {'value': 0}, pid: 1, ts: 0, cat: 'foo', tid: 1,
    442      ph: 'C', id: 0},
    443     {name: 'ctr', args: {'value': 10}, pid: 1, ts: 10, cat: 'foo', tid: 1,
    444      ph: 'C', id: 0},
    445     {name: 'ctr', args: {'value': 10}, pid: 1, ts: 10, cat: 'foo', tid: 1,
    446      ph: 'C', id: 1},
    447     {name: 'ctr', args: {'value': 20}, pid: 1, ts: 15, cat: 'foo', tid: 1,
    448      ph: 'C', id: 1},
    449     {name: 'ctr', args: {'value': 30}, pid: 1, ts: 18, cat: 'foo', tid: 1,
    450      ph: 'C', id: 1},
    451     {name: 'ctr', args: {'value': 40}, pid: 1, ts: 20, cat: 'bar', tid: 1,
    452      ph: 'C', id: 2}
    453   ];
    454   var m = new tracing.TimelineModel(events);
    455   var p = m.processes[1];
    456   var ctr = m.processes[1].counters['foo.ctr[0]'];
    457   assertEquals('ctr[0]', ctr.name);
    458   assertEquals('foo', ctr.category);
    459   assertEquals(2, ctr.numSamples);
    460   assertEquals(1, ctr.numSeries);
    461   assertArrayEquals([0, 0.01], ctr.timestamps);
    462   assertArrayEquals([0, 10], ctr.samples);
    463 
    464   var ctr = m.processes[1].counters['foo.ctr[1]'];
    465   assertEquals('ctr[1]', ctr.name);
    466   assertEquals('foo', ctr.category);
    467   assertEquals(3, ctr.numSamples);
    468   assertEquals(1, ctr.numSeries);
    469   assertArrayEquals([0.01, 0.015, 0.018], ctr.timestamps);
    470   assertArrayEquals([10, 20, 30], ctr.samples);
    471 
    472   var ctr = m.processes[1].counters['bar.ctr[2]'];
    473   assertEquals('ctr[2]', ctr.name);
    474   assertEquals('bar', ctr.category);
    475   assertEquals(1, ctr.numSamples);
    476   assertEquals(1, ctr.numSeries);
    477   assertArrayEquals([0.02], ctr.timestamps);
    478   assertArrayEquals([40], ctr.samples);
    479 }
    480 
    481 function testMultiCounterUpdateBounds() {
    482   var ctr = new tracing.TimelineCounter(undefined, 'testBasicCounter',
    483       '', 'testBasicCounter');
    484   ctr.seriesNames = ['value1', 'value2'];
    485   ctr.seriesColors = ['testBasicCounter.value1', 'testBasicCounter.value2'];
    486   ctr.timestamps = [0, 1, 2, 3, 4, 5, 6, 7];
    487   ctr.samples = [0, 0,
    488                  1, 0,
    489                  1, 1,
    490                  2, 1.1,
    491                  3, 0,
    492                  1, 7,
    493                  3, 0,
    494                  3.1, 0.5];
    495   ctr.updateBounds();
    496   assertEquals(0, ctr.minTimestamp);
    497   assertEquals(7, ctr.maxTimestamp);
    498   assertEquals(8, ctr.maxTotal);
    499   assertArrayEquals([0, 0,
    500                      1, 1,
    501                      1, 2,
    502                      2, 3.1,
    503                      3, 3,
    504                      1, 8,
    505                      3, 3,
    506                      3.1, 3.6], ctr.totals);
    507 }
    508 
    509 function testMultiCounter() {
    510   var events = [
    511     {name: 'ctr', args: {'value1': 0, 'value2': 7}, pid: 1, ts: 0, cat: 'foo',
    512      tid: 1, ph: 'C'},
    513     {name: 'ctr', args: {'value1': 10, 'value2': 4}, pid: 1, ts: 10, cat: 'foo',
    514      tid: 1, ph: 'C'},
    515     {name: 'ctr', args: {'value1': 0, 'value2': 1 }, pid: 1, ts: 20, cat: 'foo',
    516      tid: 1, ph: 'C'}
    517   ];
    518   var m = new tracing.TimelineModel(events);
    519   var p = m.processes[1];
    520   var ctr = m.processes[1].counters['foo.ctr'];
    521   assertEquals('ctr', ctr.name);
    522 
    523   assertEquals('ctr', ctr.name);
    524   assertEquals('foo', ctr.category);
    525   assertEquals(3, ctr.numSamples);
    526   assertEquals(2, ctr.numSeries);
    527 
    528   assertArrayEquals(['value1', 'value2'], ctr.seriesNames);
    529   assertArrayEquals([tracing.getStringColorId('ctr.value1'),
    530                      tracing.getStringColorId('ctr.value2')],
    531                     ctr.seriesColors);
    532   assertArrayEquals([0, 0.01, 0.02], ctr.timestamps);
    533   assertArrayEquals([0, 7,
    534                      10, 4,
    535                      0, 1], ctr.samples);
    536   assertArrayEquals([0, 7,
    537                      10, 14,
    538                      0, 1], ctr.totals);
    539   assertEquals(14, ctr.maxTotal);
    540 }
    541 
    542 function testImportObjectInsteadOfArray() {
    543   var events = { traceEvents: [
    544     {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
    545     {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
    546   ] };
    547 
    548   var m = new tracing.TimelineModel(events);
    549   assertEquals(1, m.numProcesses);
    550 }
    551 
    552 function testImportString() {
    553   var events = [
    554     {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
    555     {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
    556   ];
    557 
    558   var m = new tracing.TimelineModel(JSON.stringify(events));
    559   assertEquals(1, m.numProcesses);
    560 }
    561 
    562 function testImportStringWithTrailingNewLine() {
    563   var events = [
    564     {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
    565     {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
    566   ];
    567 
    568   var m = new tracing.TimelineModel(JSON.stringify(events) + '\n');
    569   assertEquals(1, m.numProcesses);
    570 }
    571 
    572 function testImportStringWithMissingCloseSquareBracket() {
    573   var events = [
    574     {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
    575     {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
    576   ];
    577 
    578   var tmp = JSON.stringify(events);
    579   assertEquals(']', tmp[tmp.length - 1]);
    580 
    581   // Drop off the trailing ]
    582   var dropped = tmp.substring(0, tmp.length - 1);
    583   var m = new tracing.TimelineModel(dropped);
    584   assertEquals(1, m.numProcesses);
    585 }
    586 
    587 function testImportStringWithEndingCommaButMissingCloseSquareBracket() {
    588   var lines = [
    589     '[',
    590     '{"name": "a", "args": {}, "pid": 52, "ts": 524, "cat": "foo", "tid": 53, "ph": "B"},',
    591     '{"name": "a", "args": {}, "pid": 52, "ts": 560, "cat": "foo", "tid": 53, "ph": "E"},'
    592     ]
    593   var text = lines.join('\n');
    594 
    595   var m = new tracing.TimelineModel(text);
    596   assertEquals(1, m.numProcesses);
    597   assertEquals(1, m.processes[52].threads[53].slices.length);
    598 }
    599 
    600 function testImportStringWithMissingCloseSquareBracketAndNewline() {
    601   var events = [
    602     {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
    603     {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
    604   ];
    605 
    606   var tmp = JSON.stringify(events);
    607   assertEquals(']', tmp[tmp.length - 1]);
    608 
    609   // Drop off the trailing ] and add a newline
    610   var dropped = tmp.substring(0, tmp.length - 1);
    611   var m = new tracing.TimelineModel(dropped + '\n');
    612   assertEquals(1, m.numProcesses);
    613 }
    614 
    615 function testImportStringWithEndingCommaButMissingCloseSquareBracketCRLF() {
    616   var lines = [
    617     '[',
    618     '{"name": "a", "args": {}, "pid": 52, "ts": 524, "cat": "foo", "tid": 53, "ph": "B"},',
    619     '{"name": "a", "args": {}, "pid": 52, "ts": 560, "cat": "foo", "tid": 53, "ph": "E"},'
    620     ]
    621   var text = lines.join('\r\n');
    622 
    623   var m = new tracing.TimelineModel(text);
    624   assertEquals(1, m.numProcesses);
    625   assertEquals(1, m.processes[52].threads[53].slices.length);
    626 }
    627 
    628 function testStartFinishOneSliceOneThread() {
    629   var events = [
    630     // Time is intentionally out of order.
    631     {name: 'a', args: {}, pid: 52, ts: 560, cat: 'cat', tid: 53,
    632        ph: 'F', id: 72},
    633     {name: 'a', pid: 52, ts: 524, cat: 'cat', tid: 53,
    634        ph: 'S', id: 72, args: {'foo': 'bar'}}
    635   ];
    636 
    637   var m = new tracing.TimelineModel(events);
    638   var t = m.processes[52].threads[53];
    639   assertNotUndefined(t);
    640   assertEquals(1, t.asyncSlices.slices.length);
    641   assertEquals('a', t.asyncSlices.slices[0].title);
    642   assertEquals('cat', t.asyncSlices.slices[0].category);
    643   assertEquals(72, t.asyncSlices.slices[0].id);
    644   assertEquals('bar', t.asyncSlices.slices[0].args.foo);
    645   assertEquals(0, t.asyncSlices.slices[0].start);
    646   assertAlmostEquals((60 - 24) / 1000, t.asyncSlices.slices[0].duration);
    647   assertEquals(t, t.asyncSlices.slices[0].startThread);
    648   assertEquals(t, t.asyncSlices.slices[0].endThread);
    649 }
    650 
    651 function testEndArgsAddedToSlice() {
    652   var events = [
    653     {name: 'a', args: {x: 1}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
    654     {name: 'a', args: {y: 2}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
    655   ];
    656 
    657   var m = new tracing.TimelineModel(events);
    658   assertEquals(1, m.numProcesses);
    659   var p = m.processes[52];
    660   assertNotUndefined(p);
    661 
    662   assertEquals(1, p.numThreads);
    663   var t = p.threads[53];
    664   assertNotUndefined(t);
    665   assertEquals(1, t.slices.length);
    666   assertEquals(53, t.tid);
    667   var slice = t.slices[0];
    668   assertEquals('a', slice.title);
    669   assertEquals('foo', slice.category);
    670   assertEquals(0, slice.start);
    671   assertEquals(0, slice.subSlices.length);
    672   assertEquals(1, slice.args['x']);
    673   assertEquals(2, slice.args['y']);
    674 }
    675 
    676 function testEndArgOverrwritesOriginalArgValueIfDuplicated() {
    677   var events = [
    678     {name: 'b', args: {z: 3}, pid: 52, ts: 629, cat: 'foo', tid: 53, ph: 'B'},
    679     {name: 'b', args: {z: 4}, pid: 52, ts: 631, cat: 'foo', tid: 53, ph: 'E'}
    680   ];
    681 
    682   var m = new tracing.TimelineModel(events);
    683   assertEquals(1, m.numProcesses);
    684   var p = m.processes[52];
    685   assertNotUndefined(p);
    686 
    687   assertEquals(1, p.numThreads);
    688   var t = p.threads[53];
    689   assertNotUndefined(t);
    690   var slice = t.slices[0];
    691   assertEquals('b', slice.title);
    692   assertEquals('foo', slice.category);
    693   assertEquals(0, slice.start);
    694   assertEquals(0, slice.subSlices.length);
    695   assertEquals(4, slice.args['z']);
    696 }
    697 
    698 function testAsyncEndArgsAddedToSlice() {
    699   var events = [
    700     // Time is intentionally out of order.
    701     {name: 'c', args: {y: 2}, pid: 52, ts: 560, cat: 'foo', tid: 53,
    702        ph: 'F', id: 72},
    703     {name: 'c', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53,
    704        ph: 'S', id: 72}
    705   ];
    706 
    707   var m = new tracing.TimelineModel(events);
    708   var t = m.processes[52].threads[53];
    709   assertNotUndefined(t);
    710   assertEquals(1, t.asyncSlices.slices.length);
    711   var parentSlice = t.asyncSlices.slices[0];
    712   assertEquals('c', parentSlice.title);
    713   assertEquals('foo', parentSlice.category);
    714 
    715   assertNotUndefined(parentSlice.subSlices);
    716   assertEquals(1, parentSlice.subSlices.length);
    717   var subSlice = parentSlice.subSlices[0];
    718   assertEquals(1, subSlice.args['x']);
    719   assertEquals(2, subSlice.args['y']);
    720 }
    721 
    722 function testAsyncEndArgOverrwritesOriginalArgValueIfDuplicated() {
    723   var events = [
    724     // Time is intentionally out of order.
    725     {name: 'd', args: {z: 4}, pid: 52, ts: 560, cat: 'foo', tid: 53,
    726        ph: 'F', id: 72},
    727     {name: 'd', args: {z: 3}, pid: 52, ts: 524, cat: 'foo', tid: 53,
    728        ph: 'S', id: 72}
    729   ];
    730 
    731   var m = new tracing.TimelineModel(events);
    732   var t = m.processes[52].threads[53];
    733   assertNotUndefined(t);
    734   assertEquals(1, t.asyncSlices.slices.length);
    735   var parentSlice = t.asyncSlices.slices[0];
    736   assertEquals('d', parentSlice.title);
    737   assertEquals('foo', parentSlice.category);
    738 
    739   assertNotUndefined(parentSlice.subSlices);
    740   assertEquals(1, parentSlice.subSlices.length);
    741   var subSlice = parentSlice.subSlices[0];
    742   assertEquals(4, subSlice.args['z']);
    743 }
    744 
    745 function testAsyncStepsInOneThread() {
    746   var events = [
    747     // Time is intentionally out of order.
    748     {name: 'a', args: {z: 3}, pid: 52, ts: 560, cat: 'foo', tid: 53,
    749        ph: 'F', id: 72},
    750     {name: 'a', args: {step: 's1', y: 2}, pid: 52, ts: 548, cat: 'foo', tid: 53,
    751        ph: 'T', id: 72},
    752     {name: 'a', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53,
    753        ph: 'S', id: 72}
    754   ];
    755 
    756   var m = new tracing.TimelineModel(events);
    757   var t = m.processes[52].threads[53];
    758   assertNotUndefined(t);
    759   assertEquals(1, t.asyncSlices.slices.length);
    760   var parentSlice = t.asyncSlices.slices[0];
    761   assertEquals('a', parentSlice.title);
    762   assertEquals('foo', parentSlice.category);
    763   assertEquals(0, parentSlice.start);
    764 
    765   assertNotUndefined(parentSlice.subSlices);
    766   assertEquals(2, parentSlice.subSlices.length);
    767   var subSlice = parentSlice.subSlices[0];
    768   assertEquals('a', subSlice.title);
    769   assertEquals('foo', subSlice.category);
    770   assertEquals(0, subSlice.start);
    771   assertAlmostEquals((548 - 524) / 1000, subSlice.duration);
    772   assertEquals(1, subSlice.args['x']);
    773 
    774   var subSlice = parentSlice.subSlices[1];
    775   assertEquals('a:s1', subSlice.title);
    776   assertEquals('foo', subSlice.category);
    777   assertAlmostEquals((548 - 524) / 1000, subSlice.start);
    778   assertAlmostEquals((560 - 548) / 1000, subSlice.duration);
    779   assertEquals(2, subSlice.args['y']);
    780   assertEquals(3, subSlice.args['z']);
    781 }
    782 
    783 function testAsyncStepsMissingStart() {
    784   var events = [
    785     // Time is intentionally out of order.
    786     {name: 'a', args: {z: 3}, pid: 52, ts: 560, cat: 'foo', tid: 53,
    787        ph: 'F', id: 72},
    788     {name: 'a', args: {step: 's1', y: 2}, pid: 52, ts: 548, cat: 'foo', tid: 53,
    789        ph: 'T', id: 72}
    790   ];
    791 
    792   var m = new tracing.TimelineModel(events);
    793   var t = m.processes[52].threads[53];
    794   assertUndefined(t);
    795 }
    796 
    797 function testAsyncStepsMissingFinish() {
    798   var events = [
    799     // Time is intentionally out of order.
    800     {name: 'a', args: {step: 's1', y: 2}, pid: 52, ts: 548, cat: 'foo', tid: 53,
    801        ph: 'T', id: 72},
    802     {name: 'a', args: {z: 3}, pid: 52, ts: 560, cat: 'foo', tid: 53,
    803        ph: 'S', id: 72}
    804   ];
    805 
    806   var m = new tracing.TimelineModel(events);
    807   var t = m.processes[52].threads[53];
    808   assertUndefined(t);
    809 }
    810 
    811 // TODO(nduca): one slice, two threads
    812 // TODO(nduca): one slice, two pids
    813 </script>
    814 </body>
    815 </html>
    816