text: // Copyright 2009 The Closure Library Authors. All Rights Reserved.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //      http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS-IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  /**
   * @fileoverview A table for showing the results of performance testing.
   *
   * {@see goog.testing.benchmark} for an easy way to use this functionality.
   */
  
  goog.setTestOnly('goog.testing.PerformanceTable');
  goog.provide('goog.testing.PerformanceTable');
  
  goog.require('goog.asserts');
  goog.require('goog.dom');
  goog.require('goog.dom.TagName');
  goog.require('goog.testing.PerformanceTimer');
  
  
  
  /**
   * A UI widget that runs performance tests and displays the results.
   * @param {Element} root The element where the table should be attached.
   * @param {goog.testing.PerformanceTimer=} opt_timer A timer to use for
   *     executing functions and profiling them.
   * @param {number=} opt_precision Number of digits of precision to include in
   *     results.  Defaults to 0.
   * @param {number=} opt_numSamples The number of samples to take. Defaults to 5.
   * @constructor
   * @final
   */
  goog.testing.PerformanceTable = function(
      root, opt_timer, opt_precision, opt_numSamples) {
    /**
     * Where the table should be attached.
     * @private {Element}
     */
    this.root_ = root;
  
    /**
     * Number of digits of precision to include in results.
     * Defaults to 0.
     * @private {number}
     */
    this.precision_ = opt_precision || 0;
  
    var timer = opt_timer;
    if (!timer) {
      timer = new goog.testing.PerformanceTimer();
      timer.setNumSamples(opt_numSamples || 5);
      timer.setDiscardOutliers(true);
    }
  
    /**
     * A timer for running the tests.
     * @private {goog.testing.PerformanceTimer}
     */
    this.timer_ = timer;
  
    this.initRoot_();
  };
  
  
  /**
   * @return {goog.testing.PerformanceTimer} The timer being used.
   */
  goog.testing.PerformanceTable.prototype.getTimer = function() {
    return this.timer_;
  };
  
  
  /**
   * Render the initial table.
   * @private
   */
  goog.testing.PerformanceTable.prototype.initRoot_ = function() {
    this.root_.innerHTML = '<table class="test-results" cellspacing="1">' +
        '  <thead>' +
        '    <tr>' +
        '      <th rowspan="2">Test Description</th>' +
        '      <th rowspan="2">Runs</th>' +
        '      <th colspan="4">Results (ms)</th>' +
        '    </tr>' +
        '    <tr>' +
        '      <th>Average</th>' +
        '      <th>Median</th>' +
        '      <th>Std Dev</th>' +
        '      <th>Minimum</th>' +
        '      <th>Maximum</th>' +
        '    </tr>' +
        '  </thead>' +
        '  <tbody>' +
        '  </tbody>' +
        '</table>';
  };
  
  
  /**
   * @return {!Element} The body of the table.
   * @private
   */
  goog.testing.PerformanceTable.prototype.getTableBody_ = function() {
    return goog.dom.getElementsByTagName(
        goog.dom.TagName.TBODY, goog.asserts.assert(this.root_))[0];
  };
  
  
  /**
   * Round to the specified precision.
   * @param {number} num The number to round.
   * @return {string} The rounded number, as a string.
   * @private
   */
  goog.testing.PerformanceTable.prototype.round_ = function(num) {
    var factor = Math.pow(10, this.precision_);
    return String(Math.round(num * factor) / factor);
  };
  
  
  /**
   * Run the given function with the performance timer, and show the results.
   * @param {Function} fn The function to run.
   * @param {string=} opt_desc A description to associate with this run.
   */
  goog.testing.PerformanceTable.prototype.run = function(fn, opt_desc) {
    this.runTask(
        new goog.testing.PerformanceTimer.Task(/** @type {function()} */ (fn)),
        opt_desc);
  };
  
  
  /**
   * Run the given task with the performance timer, and show the results.
   * @param {goog.testing.PerformanceTimer.Task} task The performance timer task
   *     to run.
   * @param {string=} opt_desc A description to associate with this run.
   */
  goog.testing.PerformanceTable.prototype.runTask = function(task, opt_desc) {
    var results = this.timer_.runTask(task);
    this.recordResults(results, opt_desc);
  };
  
  
  /**
   * Record a performance timer results object to the performance table. See
   * `goog.testing.PerformanceTimer` for details of the format of this
   * object.
   * @param {Object} results The performance timer results object.
   * @param {string=} opt_desc A description to associate with these results.
   */
  goog.testing.PerformanceTable.prototype.recordResults = function(
      results, opt_desc) {
    var average = results['average'];
    var standardDeviation = results['standardDeviation'];
    var isSuspicious = average < 0 || standardDeviation > average * .5;
    var resultsRow = goog.dom.createDom(
        goog.dom.TagName.TR, null,
        goog.dom.createDom(
            goog.dom.TagName.TD, 'test-description',
            opt_desc || 'No description'),
        goog.dom.createDom(
            goog.dom.TagName.TD, 'test-count', String(results['count'])),
        goog.dom.createDom(
            goog.dom.TagName.TD, 'test-average', this.round_(average)),
        goog.dom.createDom(
            goog.dom.TagName.TD, 'test-median', String(results['median'])),
        goog.dom.createDom(
            goog.dom.TagName.TD, 'test-standard-deviation',
            this.round_(standardDeviation)),
        goog.dom.createDom(
            goog.dom.TagName.TD, 'test-minimum', String(results['minimum'])),
        goog.dom.createDom(
            goog.dom.TagName.TD, 'test-maximum', String(results['maximum'])));
    if (isSuspicious) {
      resultsRow.className = 'test-suspicious';
    }
    this.getTableBody_().appendChild(resultsRow);
  };
  
  
  /**
   * Report an error in the table.
   * @param {*} reason The reason for the error.
   */
  goog.testing.PerformanceTable.prototype.reportError = function(reason) {
    this.getTableBody_().appendChild(
        goog.dom.createDom(
            goog.dom.TagName.TR, null,
            goog.dom.createDom(
                goog.dom.TagName.TD, {'class': 'test-error', 'colSpan': 5},
                String(reason))));
  };
  