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 i18n-values="dir:textdirection;">
      9 <title>TimelineAnalysis tests</title>
     10 <script src="base.js"></script>
     11 <style>
     12   .timeline-view {
     13     border: 1px solid black;
     14     margin: 10px;
     15   }
     16   .timeline-find-dialog {
     17     border: 1px solid black;
     18     margin: 10px;
     19   }
     20 </style>
     21 </head>
     22 <body>
     23   <script>
     24     base.require('unittest');
     25     base.require('tracing_controller');
     26     base.require('test_utils');
     27     base.require('timeline_model');
     28     base.require('trace_event_importer');
     29     base.require('timeline_analysis');
     30     base.require('timeline_filter');
     31     base.require('tracks.timeline_counter_track');
     32     base.require('tracks.timeline_slice_track');
     33     base.require('tracks.timeline_thread_track');
     34     base.require('timeline'); /* TODO(nduca): reduce dependency */
     35   </script>
     36   <script>
     37     'use strict';
     38 
     39     var TimelineCounter = tracing.TimelineCounter;
     40     var TimelineThread = tracing.TimelineThread;
     41     var TimelineAnalysisView = tracing.TimelineAnalysisView;
     42     var TimelineModel = tracing.TimelineModel;
     43     var TimelineThreadTrack = tracks.TimelineThreadTrack;
     44     var TimelineSelection = tracing.TimelineSelection;
     45     var TimelineTitleFilter = tracing.TimelineTitleFilter;
     46     var TimelineCounterTrack = tracks.TimelineCounterTrack;
     47     var TimelineSliceTrack = tracks.TimelineSliceTrack;
     48     var newSliceNamed = test_utils.newSliceNamed;
     49     var newSliceCategory = test_utils.newSliceCategory;
     50 
     51     function createSelectionWithSingleSlice(withCategory) {
     52       var model = new TimelineModel();
     53       var t53 = model.getOrCreateProcess(52).getOrCreateThread(53);
     54       if (withCategory)
     55         t53.pushSlice(newSliceCategory('foo', 'b', 0, 0.002));
     56       else
     57         t53.pushSlice(newSliceNamed('b', 0, 0.002));
     58 
     59       var t53track = new TimelineThreadTrack();
     60       t53track.thread = t53;
     61 
     62       var selection = new TimelineSelection();
     63       t53track.addAllObjectsMatchingFilterToSelection(
     64         new TimelineTitleFilter('b'), selection);
     65       assertEquals(1, selection.length);
     66 
     67       return selection;
     68     }
     69 
     70     function createSelectionWithTwoSlices() {
     71       var events = [
     72         {name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
     73         {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'},
     74         {name: 'aa', args: {}, pid: 52, ts: 640, cat: 'foo', tid: 53, ph: 'B'},
     75         {name: 'aa', args: {}, pid: 52, ts: 700, cat: 'foo', tid: 53, ph: 'E'}
     76       ];
     77       var model = new TimelineModel(events);
     78 
     79       var t53track = new TimelineThreadTrack();
     80       t53track.thread = model.processes[52].threads[53];
     81 
     82       var selection = new TimelineSelection();
     83       t53track.addAllObjectsMatchingFilterToSelection(
     84         new TimelineTitleFilter('a'), selection);
     85       assertEquals(2, selection.length);
     86 
     87       return selection;
     88     }
     89 
     90     function createSelectionWithTwoSlicesSameTitle() {
     91       var events = [
     92         {name: 'c', args: {}, pid: 52, ts: 620, cat: 'foo', tid: 53, ph: 'B'},
     93         {name: 'c', args: {}, pid: 52, ts: 660, cat: 'foo', tid: 53, ph: 'E'},
     94         {name: 'c', args: {}, pid: 52, ts: 740, cat: 'foo', tid: 53, ph: 'B'},
     95         {name: 'c', args: {}, pid: 52, ts: 800, cat: 'foo', tid: 53, ph: 'E'}
     96       ];
     97       var model = new TimelineModel(events);
     98 
     99       var t53track = new TimelineThreadTrack();
    100       t53track.thread = model.processes[52].threads[53];
    101 
    102       var selection = new TimelineSelection();
    103       t53track.addAllObjectsMatchingFilterToSelection(
    104         new TimelineTitleFilter('c'), selection);
    105       assertEquals(2, selection.length);
    106 
    107       return selection;
    108     }
    109 
    110     function createSelectionWithCounters(numSamples) {
    111       if (numSamples > 2 || numSamples < 1)
    112         throw new Error('This function only supports 1 or 2 samples');
    113       var events = [
    114         {name: 'ctr', args: {'value': 0}, pid: 1, ts: 0, cat: 'foo', tid: 1,
    115          ph: 'C', id: 0},
    116         {name: 'ctr', args: {'value': 10}, pid: 1, ts: 10, cat: 'foo', tid: 1,
    117          ph: 'C', id: 0}
    118       ];
    119       var model = new TimelineModel(events);
    120       var p = model.processes[1];
    121       var ctr = model.processes[1].counters['foo.ctr[0]'];
    122       assertEquals('ctr[0]', ctr.name);
    123       assertEquals(2, ctr.numSamples);
    124       assertEquals(1, ctr.numSeries);
    125       assertArrayEquals([0, 0.01], ctr.timestamps);
    126       assertArrayEquals([0, 10], ctr.samples);
    127 
    128       var selection = new TimelineSelection();
    129       var t1track = new TimelineThreadTrack();
    130       selection.addCounterSample(t1track, ctr, 1);
    131 
    132       if (numSamples == 1)
    133         return selection;
    134 
    135       selection.addCounterSample(t1track, ctr, 0);
    136       return selection;
    137     }
    138 
    139     function createSelectionWithTwoSeriesSingleCounter() {
    140       var events = [
    141         {name: 'ctr', args: {'bytesallocated': 0, 'bytesfree': 25}, pid: 1,
    142          ts: 0, cat: 'foo', tid: 1, ph: 'C', id: 0},
    143         {name: 'ctr', args: {'bytesallocated': 10, 'bytesfree': 15}, pid: 1,
    144          ts: 10, cat: 'foo', tid: 1, ph: 'C', id: 0},
    145         {name: 'ctr', args: {'bytesallocated': 20, 'bytesfree': 5}, pid: 1,
    146          ts: 20, cat: 'foo', tid: 1, ph: 'C', id: 0}
    147       ];
    148       var model = new TimelineModel(events);
    149       var p = model.processes[1];
    150       var ctr = model.processes[1].counters['foo.ctr[0]'];
    151       assertEquals('ctr[0]', ctr.name);
    152       assertEquals(3, ctr.numSamples);
    153       assertEquals(2, ctr.numSeries);
    154 
    155       var selection = new TimelineSelection();
    156       var t1track = new TimelineThreadTrack();
    157       selection.addCounterSample(t1track, ctr, 1);
    158 
    159       return selection;
    160     }
    161 
    162     function createSelectionWithTwoSeriesTwoCounters() {
    163       var ctr1 = new TimelineCounter(null, 0, '', 'ctr1');
    164       ctr1.seriesNames.push('bytesallocated', 'bytesfree');
    165       ctr1.seriesColors.push(0, 1);
    166       ctr1.timestamps.push(0, 10, 20);
    167       ctr1.samples.push(0, 25, 10, 15, 20, 5);
    168 
    169       var ctr2 = new TimelineCounter(null, 0, '', 'ctr2');
    170       ctr2.seriesNames.push('bytesallocated', 'bytesfree');
    171       ctr2.seriesColors.push(0, 1);
    172       ctr2.timestamps.push(0, 10, 20);
    173       ctr2.samples.push(0, 25, 10, 15, 20, 5);
    174 
    175       var selection = new TimelineSelection();
    176       var t1track = new TimelineThreadTrack();
    177       selection.addCounterSample(t1track, ctr1, 1);
    178       selection.addCounterSample(t1track, ctr2, 2);
    179 
    180       return selection;
    181     }
    182 
    183     function createSelectionWithTwoCountersDiffSeriesDiffHits() {
    184       var ctr1 = new TimelineCounter(null, 0, '', 'a');
    185       ctr1.seriesNames.push('bytesallocated');
    186       ctr1.seriesColors.push(0);
    187       ctr1.timestamps.push(0, 10, 20);
    188       ctr1.samples.push(0, 25, 10);
    189       assertEquals('a', ctr1.name);
    190       assertEquals(3, ctr1.numSamples);
    191       assertEquals(1, ctr1.numSeries);
    192 
    193       var ctr2 = new TimelineCounter(null, 0, '', 'b');
    194       ctr2.seriesNames.push('bytesallocated', 'bytesfree');
    195       ctr2.seriesColors.push(0, 1);
    196       ctr2.timestamps.push(0, 10, 20, 30);
    197       ctr2.samples.push(0, 25, 10, 15, 20, 5, 25, 0);
    198       assertEquals('b', ctr2.name);
    199       assertEquals(4, ctr2.numSamples);
    200       assertEquals(2, ctr2.numSeries);
    201 
    202       var selection = new TimelineSelection();
    203       var t1track = new TimelineThreadTrack();
    204       selection.addCounterSample(t1track, ctr1, 1);
    205       selection.addCounterSample(t1track, ctr2, 2);
    206 
    207       return selection;
    208     }
    209 
    210     function createSelectionWithSingleSliceSingleCounter() {
    211       var model = new TimelineModel();
    212       var thread = model.getOrCreateProcess(1).getOrCreateThread(1);
    213       thread.pushSlice(newSliceNamed('b', 1, 5));
    214 
    215       var ctr1 = model.getOrCreateProcess(1).getOrCreateCounter('cat', 'ctr1');
    216       ctr1.seriesNames.push('bytesallocated', 'bytesfree');
    217       ctr1.seriesColors.push(0, 1);
    218       ctr1.timestamps.push(0, 10, 20);
    219       ctr1.samples.push(0, 25, 10, 15, 20, 5);
    220       assertEquals('ctr1', ctr1.name);
    221       assertEquals(3, ctr1.numSamples);
    222       assertEquals(2, ctr1.numSeries);
    223 
    224       var ctr1track = new TimelineCounterTrack();
    225       ctr1track.counter = ctr1;
    226 
    227       var threadTrack = new TimelineSliceTrack();
    228       threadTrack.slices = thread.slices;
    229 
    230       var selection = new TimelineSelection();
    231       selection.addCounterSample(ctr1track, ctr1, 1);
    232       threadTrack.addAllObjectsMatchingFilterToSelection(
    233         new TimelineTitleFilter('b'), selection);
    234       assertEquals(2, selection.length);
    235       return selection;
    236     }
    237 
    238     function createSelectionWithNormallyDistributedSamples(numSlices) {
    239       // Distance between start times is normally distributed, with mu = 16ms
    240       // and sigma = 3ms.
    241       var startTimes = [
    242         0, 18.4362262859, 32.5378088645, 44.8978868054,
    243         63.4772725504, 77.438888345, 92.0102867913, 99.6208686689,
    244         119.150576393, 137.54545468, 153.991587743, 175.456095568,
    245         193.395772651, 205.691644582, 218.740054982, 239.308480724,
    246         250.880949151, 268.528689601, 281.950478133, 296.791635722,
    247         315.862427391, 333.954888221, 342.392899581, 362.364373939,
    248         377.593380892, 392.296896748, 415.779941407, 435.517713864,
    249         454.581222491, 470.329018858, 488.37029095, 502.283017166,
    250         521.15141113, 534.36224697, 554.425018316, 574.89913248,
    251         589.60294439, 604.780562233, 615.481610668, 630.055628965,
    252         645.908449096, 661.776084055, 673.276049017, 689.776401428,
    253         704.440135004, 716.33262401, 732.380086528, 743.970715322,
    254         756.506690025, 772.391485532, 794.636984401, 803.801415494,
    255         819.006502926, 837.610127549, 854.551103283, 875.170613672,
    256         891.508235124, 905.263299017, 929.309555683, 943.417968804,
    257         957.289319239, 972.302910569, 986.669355637, 1002.71558868,
    258         1013.83359637, 1030.16840733, 1040.39503139, 1057.61583325,
    259         1075.64709686, 1086.67671319, 1100.4617455, 1118.4871842,
    260         1129.98143488, 1144.52318588, 1160.36966285, 1179.50049042,
    261         1195.03088169, 1215.98199401, 1226.66591838, 1245.83650314,
    262         1268.18058265, 1285.11047342, 1301.71570575, 1316.40723345,
    263         1329.94342488, 1343.7569577, 1358.28267513, 1371.17560308,
    264         1386.42247119, 1401.51767749, 1417.52489051, 1440.98712348,
    265         1457.80113781, 1475.66079406, 1494.64137536, 1509.52941903,
    266         1524.54762552, 1545.42960714, 1565.19444597, 1580.56308936,
    267         1596.72211651];
    268 
    269       var events = [];
    270 
    271       var model = new TimelineModel();
    272       var thread = model.getOrCreateProcess(52).getOrCreateThread(53);
    273       var duration = 1; // 1ms
    274 
    275       for (var i = 0; i < startTimes.length; ++i) {
    276         for (var j = 0; j < numSlices; ++j) {
    277           var name = 'slice' + String(numSlices - 1 - j);
    278           thread.slices.push(newSliceNamed(name, startTimes[i], duration));
    279         }
    280       }
    281 
    282       var t53track = new TimelineThreadTrack();
    283       t53track.thread = model.processes[52].threads[53];
    284 
    285       var selection = new TimelineSelection();
    286       t53track.addAllObjectsMatchingFilterToSelection(
    287         new TimelineTitleFilter('slice'), selection);
    288       assertEquals(101 * numSlices, selection.length);
    289 
    290       return selection;
    291     }
    292 
    293     function testAnalysisViewWithSingleSlice() {
    294       var selection = createSelectionWithSingleSlice();
    295 
    296       var analysisEl = new TimelineAnalysisView();
    297       analysisEl.selection = selection;
    298       this.addHTMLOutput(undefined, analysisEl);
    299     }
    300 
    301     function testAnalysisViewWithSingleSliceCategory() {
    302       var selection = createSelectionWithSingleSlice(true);
    303 
    304       var analysisEl = new TimelineAnalysisView();
    305       analysisEl.selection = selection;
    306       this.addHTMLOutput(undefined, analysisEl);
    307     }
    308 
    309     function testAnalysisViewWithMultipleSlices() {
    310       var selection = createSelectionWithTwoSlices();
    311 
    312       var analysisEl = new TimelineAnalysisView();
    313       analysisEl.selection = selection;
    314       this.addHTMLOutput(undefined, analysisEl);
    315     }
    316 
    317     function testAnalysisViewWithMultipleSlicesSameTitle() {
    318       var selection = createSelectionWithTwoSlicesSameTitle();
    319 
    320       var analysisEl = new TimelineAnalysisView();
    321       analysisEl.selection = selection;
    322       this.addHTMLOutput(undefined, analysisEl);
    323     }
    324 
    325     function testAnalysisViewWithSingleCounterWithTwoSeries() {
    326       var selection = createSelectionWithTwoSeriesSingleCounter();
    327 
    328       var analysisEl = new TimelineAnalysisView();
    329       analysisEl.selection = selection;
    330       this.addHTMLOutput(undefined, analysisEl);
    331     }
    332 
    333     function testAnalysisViewWithTwoCountersWithTwoSeries() {
    334       var selection = createSelectionWithTwoSeriesTwoCounters();
    335 
    336       var analysisEl = new TimelineAnalysisView();
    337       analysisEl.selection = selection;
    338       this.addHTMLOutput(undefined, analysisEl);
    339     }
    340 
    341     function testAnalysisViewWithSingleSliceSingleCounter() {
    342       var selection = createSelectionWithSingleSliceSingleCounter();
    343 
    344       var analysisEl = new TimelineAnalysisView();
    345       analysisEl.selection = selection;
    346       this.addHTMLOutput(undefined, analysisEl);
    347     }
    348 
    349     function testSelectionWithNormallyDistributedSamples() {
    350       var numSlices = 1;
    351       var selection = createSelectionWithNormallyDistributedSamples(numSlices);
    352 
    353       var analysisEl = new TimelineAnalysisView();
    354       analysisEl.selection = selection;
    355       this.addHTMLOutput(undefined, analysisEl);
    356     }
    357 
    358     function StubAnalysisResults() {
    359       this.tables = [];
    360     }
    361     StubAnalysisResults.prototype = {
    362       __proto__: Object.protoype,
    363 
    364       appendTable: function(parent, className) {
    365         var table = {
    366           className: className,
    367           rows: []
    368         };
    369         table.className = className;
    370         this.tables.push(table);
    371         return table;
    372       },
    373 
    374       appendTableHeader: function(table, label) {
    375         if (table.tableHeader)
    376           throw new Error('Only one summary header allowed.');
    377         table.tableHeader = label;
    378       },
    379 
    380       appendSummaryRow: function(table, label, opt_text) {
    381         table.rows.push({label: label,
    382                          text: opt_text});
    383       },
    384 
    385       appendSpacingRow: function(table) {
    386         table.rows.push({spacing: true});
    387       },
    388 
    389       appendSummaryRowTime: function(table, label, time) {
    390         table.rows.push({label: label,
    391                          time: time});
    392       },
    393 
    394       appendDataRow: function(table, label, duration, occurences, details) {
    395         table.rows.push({label: label,
    396                          duration: duration,
    397                          occurences: occurences,
    398                          details: details});
    399       }
    400     };
    401 
    402     function testAnalyzeSelectionWithSingleSlice() {
    403       var selection = createSelectionWithSingleSlice();
    404 
    405       var results = new StubAnalysisResults();
    406       tracing.analyzeSelection_(results, selection);
    407       assertEquals(1, results.tables.length);
    408       var table = results.tables[0];
    409       assertEquals('Selected slice:', table.tableHeader);
    410       assertEquals(3, table.rows.length);
    411 
    412       assertEquals('b', table.rows[0].text);
    413       assertEquals(0, table.rows[1].time);
    414       assertAlmostEquals(0.002, table.rows[2].time);
    415     }
    416 
    417     function testAnalyzeSelectionWithFalsyArgs() {
    418       var model = new TimelineModel();
    419       var t53 = model.getOrCreateProcess(52).getOrCreateThread(53);
    420       var slice = newSliceNamed('b', 0, 0.002);
    421       slice.args.bar = 0;
    422       slice.args.foo = false;
    423       t53.pushSlice(slice);
    424       var t53track = new TimelineThreadTrack();
    425       t53track.thread = t53;
    426       var selection = new TimelineSelection();
    427       t53track.addAllObjectsMatchingFilterToSelection(
    428         new TimelineTitleFilter('b'), selection);
    429       assertEquals(1, selection.length);
    430 
    431       var analysisEl = new TimelineAnalysisView();
    432       analysisEl.selection = selection;
    433       this.addHTMLOutput(undefined, analysisEl);
    434       var rows = analysisEl.querySelectorAll('tr');
    435       assertEquals(rows.length, 7);
    436       assertEquals(' bar', rows[5].children[0].textContent);
    437       assertEquals('0', rows[5].children[1].textContent);
    438       assertEquals(' foo', rows[6].children[0].textContent);
    439       assertEquals('false', rows[6].children[1].textContent);
    440     }
    441 
    442     function testAnalyzeSelectionWithSingleSliceCategory() {
    443       var selection = createSelectionWithSingleSlice(true);
    444 
    445       var results = new StubAnalysisResults();
    446       tracing.analyzeSelection_(results, selection);
    447       assertEquals(1, results.tables.length);
    448       var table = results.tables[0];
    449       assertEquals('Selected slice:', table.tableHeader);
    450       assertEquals(4, table.rows.length);
    451 
    452       assertEquals('b', table.rows[0].text);
    453       assertEquals('foo', table.rows[1].text);
    454       assertEquals(0, table.rows[2].time);
    455       assertAlmostEquals(0.002, table.rows[3].time);
    456     }
    457 
    458     function testAnalyzeSelectionWithTwoSlices() {
    459       var selection = createSelectionWithTwoSlices();
    460 
    461       var results = new StubAnalysisResults();
    462       tracing.analyzeSelection_(results, selection);
    463       assertEquals(1, results.tables.length);
    464       var table = results.tables[0];
    465       assertEquals('Slices:', table.tableHeader);
    466       assertEquals(6, table.rows.length);
    467 
    468       assertEquals('a', table.rows[0].label);
    469       assertEquals(1, table.rows[0].occurences);
    470       assertAlmostEquals(0.04, table.rows[0].duration);
    471       assertEquals('aa', table.rows[1].label);
    472       assertEquals(1, table.rows[1].occurences);
    473       assertAlmostEquals(0.06, table.rows[1].duration);
    474       assertEquals('*Totals', table.rows[2].label);
    475       assertEquals(2, table.rows[2].occurences);
    476       assertAlmostEquals(0.1, table.rows[2].duration);
    477 
    478       assertEquals('Selection start', table.rows[4].label);
    479       assertAlmostEquals(0, table.rows[4].time);
    480 
    481       assertEquals('Selection extent', table.rows[5].label);
    482       assertAlmostEquals(0.18, table.rows[5].time);
    483     }
    484 
    485     function testAnalyzeSelectionWithTwoSlicesSameTitle() {
    486       var selection = createSelectionWithTwoSlicesSameTitle();
    487 
    488       var results = new StubAnalysisResults();
    489       tracing.analyzeSelection_(results, selection);
    490       assertEquals(1, results.tables.length);
    491       var table = results.tables[0];
    492 
    493       assertEquals('Slices:', table.tableHeader);
    494       assertEquals(5, table.rows.length);
    495 
    496       assertEquals('c', table.rows[0].label);
    497       assertEquals('2', table.rows[0].occurences);
    498       assertAlmostEquals(0.04, table.rows[0].details.min);
    499       assertAlmostEquals(0.05, table.rows[0].details.avg);
    500       assertAlmostEquals(0.06, table.rows[0].details.max);
    501       assertAlmostEquals(0.1, table.rows[0].duration);
    502       assertEquals('*Totals', table.rows[1].label);
    503       assertAlmostEquals(0.1, table.rows[1].duration);
    504       assertEquals('Selection start', table.rows[3].label);
    505       assertAlmostEquals(0, table.rows[3].time);
    506       assertEquals('Selection extent', table.rows[4].label);
    507       assertAlmostEquals(0.18, table.rows[4].time);
    508     }
    509 
    510     function testAnalyzeSelectionWithSingleCounter() {
    511       var selection = createSelectionWithCounters(1);
    512 
    513       var results = new StubAnalysisResults();
    514       tracing.analyzeSelection_(results, selection);
    515       assertEquals(1, results.tables.length);
    516       var table = results.tables[0];
    517       assertEquals('Selected counter:', table.tableHeader);
    518       assertEquals(3, table.rows.length);
    519 
    520       assertEquals('Title', table.rows[0].label);
    521       assertEquals('Timestamp', table.rows[1].label);
    522       assertEquals('value', table.rows[2].label);
    523       assertEquals(10, table.rows[2].text);
    524     }
    525 
    526     function testAnalyzeSelectionWithBasicTwoSeriesTwoCounters() {
    527       var selection = createSelectionWithTwoSeriesTwoCounters();
    528 
    529       var results = new StubAnalysisResults();
    530       tracing.analyzeSelection_(results, selection);
    531       assertEquals(1, results.tables.length);
    532       var table = results.tables[0];
    533       assertEquals('Counters:', table.tableHeader);
    534       assertEquals(4, table.rows.length);
    535 
    536       assertEquals('ctr1: bytesallocated', table.rows[0].label);
    537       assertEquals('ctr1: bytesfree', table.rows[1].label);
    538       assertEquals('ctr2: bytesallocated', table.rows[2].label);
    539       assertEquals('ctr2: bytesfree', table.rows[3].label);
    540     }
    541 
    542     function testAnalyzeSelectionWithComplexSeriesTwoCounters() {
    543       var selection = createSelectionWithTwoCountersDiffSeriesDiffHits();
    544 
    545       var results = new StubAnalysisResults();
    546       tracing.analyzeSelection_(results, selection);
    547       assertEquals(1, results.tables.length);
    548       var table = results.tables[0];
    549       assertEquals('Counters:', table.tableHeader);
    550       assertEquals(3, table.rows.length);
    551 
    552       assertEquals('a: bytesallocated', table.rows[0].label);
    553       assertEquals('b: bytesallocated', table.rows[1].label);
    554       assertEquals('b: bytesfree', table.rows[2].label);
    555     }
    556 
    557     function testAnalyzeSelectionWithCounterAndSlices() {
    558       var selection = createSelectionWithSingleSliceSingleCounter();
    559 
    560       var results = new StubAnalysisResults();
    561       tracing.analyzeSelection_(results, selection);
    562       assertEquals(2, results.tables.length);
    563       var sliceTable = results.tables[0];
    564       var counterTable = results.tables[1];
    565 
    566       assertEquals('Selected slice:', sliceTable.tableHeader);
    567       assertEquals(3, sliceTable.rows.length);
    568 
    569       assertEquals('Selected counter:', counterTable.tableHeader);
    570       assertEquals(4, counterTable.rows.length);
    571     }
    572 
    573     function testAnalyzeSelectionWithNormallyDistributedSamples() {
    574       var numSlices = 2;
    575       var selection = createSelectionWithNormallyDistributedSamples(numSlices);
    576 
    577       var results = new StubAnalysisResults();
    578       tracing.analyzeSelection_(results, selection);
    579       assertEquals(1, results.tables.length);
    580 
    581       assertEquals('slice0', results.tables[0].rows[0].label);
    582       assertEquals(
    583           63, Math.round(results.tables[0].rows[0].details.frequency));
    584       assertEquals(
    585           16, Math.round(results.tables[0].rows[0].details.frequency_stddev));
    586 
    587       assertEquals('slice1', results.tables[0].rows[1].label);
    588       assertEquals(
    589           63, Math.round(results.tables[0].rows[1].details.frequency));
    590       assertEquals(
    591           16, Math.round(results.tables[0].rows[1].details.frequency_stddev));
    592     }
    593   </script>
    594 </body>
    595 </html>
    596