<commit_before><!DOCTYPE html>
<!--
Copyright (c) 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/base/base.html">
<link rel="import" href="/tracing/base/unittest.html">
<link rel="import" href="/tracing/base/unittest/suite_loader.html">
<link rel="import" href="/tracing/base/unittest/test_runner.html">
<link rel="import" href="/tracing/base/unittest/html_test_results.html">
<link rel="import" href="/tracing/ui/base/utils.html">

<style>
  x-base-interactive-test-runner {
    display: -webkit-flex;
    -webkit-flex-direction: column;
  }

  x-base-interactive-test-runner > * {
    -webkit-flex: 0 0 auto;
  }
  x-base-interactive-test-runner > #title {
    font-size: 16pt;
  }

  x-base-interactive-test-runner {
    font-family: sans-serif;
  }

  x-base-interactive-test-runner > h1 {
    margin: 5px 0px 10px 0px;
  }

  x-base-interactive-test-runner > #stats {
  }

  x-base-interactive-test-runner > #controls {
    display: block;
    margin-bottom: 5px;
  }

  x-base-interactive-test-runner > #controls > ul {
    list-style-type: none;
    padding: 0;
    margin: 0;
  }

  x-base-interactive-test-runner > #controls > ul > li {
    float: left;
    margin-right: 10px;
    padding-top: 5px;
    padding-bottom: 5px;
  }

  x-base-interactive-test-runner > #shortform-results {
    color: green;
    height; 40px;
    word-wrap: break-word;
  }

  x-base-interactive-test-runner > #shortform-results > .fail {
    color: darkred;
    font-weight: bold;
  }

  x-base-interactive-test-runner > #results-container {
    -webkit-flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    padding: 0 4px 0 4px;
  }

  .unittest-pending {
    color: orange;
  }
  .unittest-running {
    color: orange;
    font-weight: bold;
  }

  .unittest-passed {
    color: darkgreen;
  }

  .unittest-failed {
    color: darkred;
    font-weight: bold;
  }

  .unittest-exception {
    color: red;
    font-weight: bold;
  }

  .unittest-failure {
    border: 1px solid grey;
    border-radius: 5px;
    padding: 5px;
  }
</style>

<template id="x-base-interactive-test-runner-template">
  <h1 id="title">Tests</h1>
  <div id="stats"></div>
  <div id="controls">
    <ul id="links">
    </ul>
    <div style="clear: both;"></div>

    <div>
      <span>
        <input type="radio" name="test-type-to-run" value="unit" />
        Run unit tests
      </span>
      <span>
        <input type="radio" name="test-type-to-run" value="perf" />
        Run perf tests
      </span>
      <span>
        <input type="radio" name="test-type-to-run" value="all" />
        Run all tests
      </span>
    </div>
    <span>
      <input type="checkbox" id="short-format" /> Short format
    </span>
  </div>
  <div id="shortform-results">
  </div>
  <div id="results-container">
  </div>
</template>

<script>
'use strict';

tr.exportTo('tr.b.unittest', function() {
  var THIS_DOC = document.currentScript.ownerDocument;
  var ALL_TEST_TYPES = 'all';

  /**
   * @constructor
   */
  var InteractiveTestRunner = tr.ui.b.define('x-base-interactive-test-runner');

  InteractiveTestRunner.prototype = {
    __proto__: HTMLUnknownElement.prototype,

    decorate: function() {
      this.allTests_ = undefined;

      this.suppressStateChange_ = false;

      this.testFilterString_ = '';
      this.testTypeToRun_ = tr.b.unittest.TestTypes.UNITTEST;
      this.shortFormat_ = false;
      this.testSuiteName_ = '';

      this.rerunPending_ = false;
      this.runner_ = undefined;
      this.results_ = undefined;
      this.headless_ = false;

      this.onResultsStatsChanged_ = this.onResultsStatsChanged_.bind(this);
      this.onTestFailed_ = this.onTestFailed_.bind(this);
      this.onTestPassed_ = this.onTestPassed_.bind(this);

      this.appendChild(tr.ui.b.instantiateTemplate(
          '#x-base-interactive-test-runner-template', THIS_DOC));

      this.querySelector(
          'input[name=test-type-to-run][value=unit]').checked = true;
      var testTypeToRunEls = tr.b.asArray(this.querySelectorAll(
          'input[name=test-type-to-run]'));

      testTypeToRunEls.forEach(
          function(inputEl) {
            inputEl.addEventListener(
                'click', this.onTestTypeToRunClick_.bind(this));
          }, this);

      var shortFormatEl = this.querySelector('#short-format');
      shortFormatEl.checked = this.shortFormat_;
      shortFormatEl.addEventListener(
          'click', this.onShortFormatClick_.bind(this));
      this.updateShortFormResultsDisplay_();

      // Oh, DOM, how I love you. Title is such a convenient property name and I
      // refuse to change my worldview because of tooltips.
      this.__defineSetter__(
          'title',
          function(title) {
            this.querySelector('#title').textContent = title;
          });
    },

    get allTests() {
      return this.allTests_;
    },

    set allTests(allTests) {
      this.allTests_ = allTests;
      this.scheduleRerun_();
    },

    get testLinks() {
      return this.testLinks_;
    },
    set testLinks(testLinks) {
      this.testLinks_ = testLinks;
      var linksEl = this.querySelector('#links');
      linksEl.textContent = '';
      this.testLinks_.forEach(function(l) {
        var link = document.createElement('a');
        link.href = l.linkPath;
        link.textContent = l.title;

        var li = document.createElement('li');
        li.appendChild(link);

        linksEl.appendChild(li);
      }, this);
    },

    get testFilterString() {
      return this.testFilterString_;
    },

    set testFilterString(testFilterString) {
      this.testFilterString_ = testFilterString;
      this.scheduleRerun_();
      if (!this.suppressStateChange_)
        tr.b.dispatchSimpleEvent(this, 'statechange');
    },

    get shortFormat() {
      return this.shortFormat_;
    },

    set shortFormat(shortFormat) {
      this.shortFormat_ = shortFormat;
      this.querySelector('#short-format').checked = shortFormat;
      if (this.results_)
        this.results_.shortFormat = shortFormat;
      if (!this.suppressStateChange_)
        tr.b.dispatchSimpleEvent(this, 'statechange');
    },

    onShortFormatClick_: function(e) {
      this.shortFormat_ = this.querySelector('#short-format').checked;
      this.updateShortFormResultsDisplay_();
      this.updateResultsGivenShortFormat_();
      if (!this.suppressStateChange_)
        tr.b.dispatchSimpleEvent(this, 'statechange');
    },

    updateShortFormResultsDisplay_: function() {
      var display = this.shortFormat_ ? '' : 'none';
      this.querySelector('#shortform-results').style.display = display;
    },

    updateResultsGivenShortFormat_: function() {
      if (!this.results_)
        return;

      if (this.testFilterString_.length || this.testSuiteName_.length)
        this.results_.showHTMLOutput = true;
      else
        this.results_.showHTMLOutput = false;
      this.results_.showPendingAndPassedTests = this.shortFormat_;
    },

    get testTypeToRun() {
      return this.testTypeToRun_;
    },

    set testTypeToRun(testTypeToRun) {
      this.testTypeToRun_ = testTypeToRun;
      var sel;
      switch (testTypeToRun) {
        case tr.b.unittest.TestTypes.UNITTEST:
          sel = 'input[name=test-type-to-run][value=unit]';
          break;
        case tr.b.unittest.TestTypes.PERFTEST:
          sel = 'input[name=test-type-to-run][value=perf]';
          break;
        case ALL_TEST_TYPES:
          sel = 'input[name=test-type-to-run][value=all]';
          break;
        default:
          throw new Error('Invalid test type to run: ' + testTypeToRun);
      }
      this.querySelector(sel).checked = true;
      this.scheduleRerun_();
      if (!this.suppressStateChange_)
        tr.b.dispatchSimpleEvent(this, 'statechange');
    },

    onTestTypeToRunClick_: function(e) {
      switch (e.target.value) {
        case 'unit':
          this.testTypeToRun_ = tr.b.unittest.TestTypes.UNITTEST;
          break;
        case 'perf':
          this.testTypeToRun_ = tr.b.unittest.TestTypes.PERFTEST;
          break;
        case 'all':
          this.testTypeToRun_ = ALL_TEST_TYPES;
          break;
        default:
          throw new Error('Inalid test type: ' + e.target.value);
      }

      this.scheduleRerun_();
      if (!this.suppressStateChange_)
        tr.b.dispatchSimpleEvent(this, 'statechange');
    },

    onTestPassed_: function() {
      this.querySelector('#shortform-results').
          appendChild(document.createTextNode('.'));
    },

    onTestFailed_: function() {
      var span = document.createElement('span');
      span.classList.add('fail');
      span.appendChild(document.createTextNode('F'));
      this.querySelector('#shortform-results').appendChild(span);
    },

    onResultsStatsChanged_: function() {
      var statsEl = this.querySelector('#stats');
      var stats = this.results_.getStats();
      var numTestsOverall = this.runner_.testCases.length;
      var numTestsThatRan = stats.numTestsThatPassed + stats.numTestsThatFailed;
      statsEl.innerHTML =
          '<span>' + numTestsThatRan + '/' + numTestsOverall +
          '</span> tests run, ' +
          '<span class="unittest-failed">' + stats.numTestsThatFailed +
          '</span> failures, ' +
          ' in ' + stats.totalRunTime.toFixed(2) + 'ms.';
    },

    scheduleRerun_: function() {
      if (this.rerunPending_)
        return;
      if (this.runner_) {
        this.rerunPending_ = true;
        this.runner_.beginToStopRunning();
        var doRerun = function() {
          this.rerunPending_ = false;
          this.scheduleRerun_();
        }.bind(this);
        this.runner_.runCompletedPromise.then(
            doRerun, doRerun);
        return;
      }
      this.beginRunning_();
    },

    beginRunning_: function() {
      var resultsContainer = this.querySelector('#results-container');
      if (this.results_) {
        this.results_.removeEventListener('testpassed',
                                          this.onTestPassed_);
        this.results_.removeEventListener('testfailed',
                                          this.onTestFailed_);
        this.results_.removeEventListener('statschange',
                                          this.onResultsStatsChanged_);
        delete this.results_.getHRefForTestCase;
        resultsContainer.removeChild(this.results_);
      }

      this.results_ = new tr.b.unittest.HTMLTestResults();
      this.results_.headless = this.headless_;
      this.results_.getHRefForTestCase = this.getHRefForTestCase.bind(this);
      this.updateResultsGivenShortFormat_();

      this.results_.shortFormat = this.shortFormat_;
      this.results_.addEventListener('testpassed',
                                     this.onTestPassed_);
      this.results_.addEventListener('testfailed',
                                     this.onTestFailed_);
      this.results_.addEventListener('statschange',
                                     this.onResultsStatsChanged_);
      resultsContainer.appendChild(this.results_);

      var tests = this.allTests_.filter(function(test) {
        var i = test.fullyQualifiedName.indexOf(this.testFilterString_);
        if (i == -1)
          return false;
        if (this.testTypeToRun_ !== ALL_TEST_TYPES &&
            test.testType !== this.testTypeToRun_)
          return false;
        return true;
      }, this);

      this.runner_ = new tr.b.unittest.TestRunner(this.results_, tests);
      this.runner_.beginRunning();

      this.runner_.runCompletedPromise.then(
          this.runCompleted_.bind(this),
          this.runCompleted_.bind(this));
    },

    setState: function(state, opt_suppressStateChange) {
      this.suppressStateChange_ = true;
      if (state.testFilterString !== undefined)
        this.testFilterString = state.testFilterString;
      else
        this.testFilterString = '';

      if (state.shortFormat === undefined)
        this.shortFormat = false;
      else
        this.shortFormat = state.shortFormat;

      if (state.testTypeToRun === undefined)
        this.testTypeToRun = tr.b.unittest.TestTypes.UNITTEST;
      else
        this.testTypeToRun = state.testTypeToRun;

      this.testSuiteName_ = state.testSuiteName || '';
      this.headless_ = state.headless || false;

      if (!opt_suppressStateChange)
        this.suppressStateChange_ = false;

      this.onShortFormatClick_();
      this.scheduleRerun_();
      this.suppressStateChange_ = false;
    },

    getDefaultState: function() {
      return {
        testFilterString: '',
        testSuiteName: '',
        shortFormat: false,
        testTypeToRun: tr.b.unittest.TestTypes.UNITTEST
      };
    },

    getState: function() {
      return {
        testFilterString: this.testFilterString_,
        testSuiteName: this.testSuiteName_,
        shortFormat: this.shortFormat_,
        testTypeToRun: this.testTypeToRun_
      };
    },

    getHRefForTestCase: function(testCases) {
      return undefined;
    },

    runCompleted_: function() {
      this.runner_ = undefined;
    }
  };

  function loadAndRunTests(runnerConfig) {

    // The test runner no-ops pushState so keep it around.
    var realWindowHistoryPushState = window.history.pushState.bind(
        window.history);

    function stateToSearchString(defaultState, state) {
      var parts = [];
      for (var k in state) {
        if (state[k] === defaultState[k])
          continue;
        var v = state[k];
        var kv;
        if (v === true) {
          kv = k;
        } else if (v === false) {
          kv = k + '=false';
        } else if (v === '') {
          continue;
        } else {
          kv = k + '=' + v;
        }
        parts.push(kv);
      }
      return parts.join('&');
    }

    function stateFromSearchString(string) {
      var state = {};
      string.split('&').forEach(function(part) {
        if (part == '')
          return;
        var kv = part.split('=');
        var k, v;
        if (kv.length == 1) {
          k = kv[0];
          v = true;
        } else {
          k = kv[0];
          if (kv[1] == 'false')
            v = false;
          else
            v = kv[1];
        }
        state[k] = v;
      });
      return state;
    }

    function getSuiteRelpathsToLoad(state) {
      if (state.testSuiteName) {
        return new Promise(function(resolve) {
          var parts = state.testSuiteName.split('.');
          var testSuiteRelPath = '/' + parts.join('/') + '.html';

          var suiteRelpathsToLoad = [testSuiteRelPath];
          resolve(suiteRelpathsToLoad);
        });
      }
      return runnerConfig.getAllSuiteRelPathsAsync();
    }


    function loadAndRunTestsImpl() {
      var state = stateFromSearchString(
          window.location.search.substring(1));
      updateTitle(state);


      showLoadingOverlay();

      var loader;
      var p = getSuiteRelpathsToLoad(state);
      p = p.then(
        function(suiteRelpathsToLoad) {
          loader = new tr.b.unittest.SuiteLoader(suiteRelpathsToLoad);
          return loader.allSuitesLoadedPromise;
        },
        function(e) {
          hideLoadingOverlay();
          throw e;
        });
      p = p.then(
        function() {
          hideLoadingOverlay();
          Polymer.whenReady(function() {
            runTests(loader, state);
          });
        },
        function(err) {
          hideLoadingOverlay();
          tr.showPanic('Module loading failure', err);
          throw err;
        });
      return p;
    }

    function showLoadingOverlay() {
      var overlay = document.createElement('div');
      overlay.id = 'tests-loading-overlay';
      overlay.style.backgroundColor = 'white';
      overlay.style.boxSizing = 'border-box';
      overlay.style.color = 'black';
      overlay.style.display = '-webkit-flex';
      overlay.style.height = '100%';
      overlay.style.left = 0;
      overlay.style.padding = '8px';
      overlay.style.position = 'fixed';
      overlay.style.top = 0;
      overlay.style.webkitFlexDirection = 'column';
      overlay.style.width = '100%';

      var element = document.createElement('div');
      element.style.webkitFlex = '1 1 auto';
      element.style.overflow = 'auto';
      overlay.appendChild(element);

      element.textContent = 'Loading tests...';
      document.body.appendChild(overlay);
    }
    function hideLoadingOverlay() {
      var overlay = document.body.querySelector('#tests-loading-overlay');
      document.body.removeChild(overlay);
    }

    function updateTitle(state) {
      var testFilterString = state.testFilterString || '';
      var testSuiteName = state.testSuiteName || '';

      var title;
      if (testSuiteName && testFilterString.length) {
        title = testFilterString + ' in ' + testSuiteName;
      } else if (testSuiteName) {
        title = testSuiteName;
      } else if (testFilterString) {
        title = testFilterString + ' in all tests';
      } else {
        title = runnerConfig.title;
      }

      if (state.shortFormat)
        title += '(s)';
      document.title = title;
      var runner = document.querySelector('x-base-interactive-test-runner');
      if (runner)
        runner.title = title;
    }

    function runTests(loader, state) {
      var runner = new tr.b.unittest.InteractiveTestRunner();
      runner.style.width = '100%';
      runner.style.height = '100%';
      runner.testLinks = runnerConfig.testLinks;
      runner.allTests = loader.getAllTests();
      document.body.appendChild(runner);

      runner.setState(state);
      updateTitle(state);

      runner.addEventListener('statechange', function() {
        var state = runner.getState();
        var stateString = stateToSearchString(runner.getDefaultState(),
                                              state);
        if (window.location.search.substring(1) == stateString)
          return;

        updateTitle(state);
        var stateURL;
        if (stateString.length > 0)
          stateURL = window.location.pathname + '?' + stateString;
        else
          stateURL = window.location.pathname;
        realWindowHistoryPushState(state, document.title, stateURL);
      });

      window.addEventListener('popstate', function(state) {
        runner.setState(state, true);
      });

      runner.getHRefForTestCase = function(testCase) {
        var state = runner.getState();
        if (state.testFilterString === '' &&
            state.testSuiteName === '') {
          state.testSuiteName = testCase.suite.name;
          state.testFilterString = '';
          state.shortFormat = false;
        } else {
          state.testSuiteName = testCase.suite.name;
          state.testFilterString = testCase.name;
          state.shortFormat = false;
        }
        var stateString = stateToSearchString(runner.getDefaultState(),
                                              state);
        if (stateString.length > 0)
          return window.location.pathname + '?' + stateString;
        else
          return window.location.pathname;
      }
    }

    loadAndRunTestsImpl();
  }

  return {
    InteractiveTestRunner: InteractiveTestRunner,
    loadAndRunTests: loadAndRunTests
  };
});
</script>
<commit_msg>Add label elements on interactive test runner<commit_after><!DOCTYPE html>
<!--
Copyright (c) 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/base/base.html">
<link rel="import" href="/tracing/base/unittest.html">
<link rel="import" href="/tracing/base/unittest/suite_loader.html">
<link rel="import" href="/tracing/base/unittest/test_runner.html">
<link rel="import" href="/tracing/base/unittest/html_test_results.html">
<link rel="import" href="/tracing/ui/base/utils.html">

<style>
  x-base-interactive-test-runner {
    display: -webkit-flex;
    -webkit-flex-direction: column;
  }

  x-base-interactive-test-runner > * {
    -webkit-flex: 0 0 auto;
  }
  x-base-interactive-test-runner > #title {
    font-size: 16pt;
  }

  x-base-interactive-test-runner {
    font-family: sans-serif;
  }

  x-base-interactive-test-runner > h1 {
    margin: 5px 0px 10px 0px;
  }

  x-base-interactive-test-runner > #stats {
  }

  x-base-interactive-test-runner > #controls {
    display: block;
    margin-bottom: 5px;
  }

  x-base-interactive-test-runner > #controls > ul {
    list-style-type: none;
    padding: 0;
    margin: 0;
  }

  x-base-interactive-test-runner > #controls > ul > li {
    float: left;
    margin-right: 10px;
    padding-top: 5px;
    padding-bottom: 5px;
  }

  x-base-interactive-test-runner > #shortform-results {
    color: green;
    height; 40px;
    word-wrap: break-word;
  }

  x-base-interactive-test-runner > #shortform-results > .fail {
    color: darkred;
    font-weight: bold;
  }

  x-base-interactive-test-runner > #results-container {
    -webkit-flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    padding: 0 4px 0 4px;
  }

  .unittest-pending {
    color: orange;
  }
  .unittest-running {
    color: orange;
    font-weight: bold;
  }

  .unittest-passed {
    color: darkgreen;
  }

  .unittest-failed {
    color: darkred;
    font-weight: bold;
  }

  .unittest-exception {
    color: red;
    font-weight: bold;
  }

  .unittest-failure {
    border: 1px solid grey;
    border-radius: 5px;
    padding: 5px;
  }
</style>

<template id="x-base-interactive-test-runner-template">
  <h1 id="title">Tests</h1>
  <div id="stats"></div>
  <div id="controls">
    <ul id="links">
    </ul>
    <div style="clear: both;"></div>

    <div>
      <span>
        <label>
          <input type="radio" name="test-type-to-run" value="unit" />
          Run unit tests
        </label>
      </span>
      <span>
        <label>
          <input type="radio" name="test-type-to-run" value="perf" />
          Run perf tests
          </label>
      </span>
      <span>
        <label>
          <input type="radio" name="test-type-to-run" value="all" />
          Run all tests
        </label>
      </span>
    </div>
    <span>
      <label>
        <input type="checkbox" id="short-format" /> Short format</label>
    </span>
  </div>
  <div id="shortform-results">
  </div>
  <div id="results-container">
  </div>
</template>

<script>
'use strict';

tr.exportTo('tr.b.unittest', function() {
  var THIS_DOC = document.currentScript.ownerDocument;
  var ALL_TEST_TYPES = 'all';

  /**
   * @constructor
   */
  var InteractiveTestRunner = tr.ui.b.define('x-base-interactive-test-runner');

  InteractiveTestRunner.prototype = {
    __proto__: HTMLUnknownElement.prototype,

    decorate: function() {
      this.allTests_ = undefined;

      this.suppressStateChange_ = false;

      this.testFilterString_ = '';
      this.testTypeToRun_ = tr.b.unittest.TestTypes.UNITTEST;
      this.shortFormat_ = false;
      this.testSuiteName_ = '';

      this.rerunPending_ = false;
      this.runner_ = undefined;
      this.results_ = undefined;
      this.headless_ = false;

      this.onResultsStatsChanged_ = this.onResultsStatsChanged_.bind(this);
      this.onTestFailed_ = this.onTestFailed_.bind(this);
      this.onTestPassed_ = this.onTestPassed_.bind(this);

      this.appendChild(tr.ui.b.instantiateTemplate(
          '#x-base-interactive-test-runner-template', THIS_DOC));

      this.querySelector(
          'input[name=test-type-to-run][value=unit]').checked = true;
      var testTypeToRunEls = tr.b.asArray(this.querySelectorAll(
          'input[name=test-type-to-run]'));

      testTypeToRunEls.forEach(
          function(inputEl) {
            inputEl.addEventListener(
                'click', this.onTestTypeToRunClick_.bind(this));
          }, this);

      var shortFormatEl = this.querySelector('#short-format');
      shortFormatEl.checked = this.shortFormat_;
      shortFormatEl.addEventListener(
          'click', this.onShortFormatClick_.bind(this));
      this.updateShortFormResultsDisplay_();

      // Oh, DOM, how I love you. Title is such a convenient property name and I
      // refuse to change my worldview because of tooltips.
      this.__defineSetter__(
          'title',
          function(title) {
            this.querySelector('#title').textContent = title;
          });
    },

    get allTests() {
      return this.allTests_;
    },

    set allTests(allTests) {
      this.allTests_ = allTests;
      this.scheduleRerun_();
    },

    get testLinks() {
      return this.testLinks_;
    },
    set testLinks(testLinks) {
      this.testLinks_ = testLinks;
      var linksEl = this.querySelector('#links');
      linksEl.textContent = '';
      this.testLinks_.forEach(function(l) {
        var link = document.createElement('a');
        link.href = l.linkPath;
        link.textContent = l.title;

        var li = document.createElement('li');
        li.appendChild(link);

        linksEl.appendChild(li);
      }, this);
    },

    get testFilterString() {
      return this.testFilterString_;
    },

    set testFilterString(testFilterString) {
      this.testFilterString_ = testFilterString;
      this.scheduleRerun_();
      if (!this.suppressStateChange_)
        tr.b.dispatchSimpleEvent(this, 'statechange');
    },

    get shortFormat() {
      return this.shortFormat_;
    },

    set shortFormat(shortFormat) {
      this.shortFormat_ = shortFormat;
      this.querySelector('#short-format').checked = shortFormat;
      if (this.results_)
        this.results_.shortFormat = shortFormat;
      if (!this.suppressStateChange_)
        tr.b.dispatchSimpleEvent(this, 'statechange');
    },

    onShortFormatClick_: function(e) {
      this.shortFormat_ = this.querySelector('#short-format').checked;
      this.updateShortFormResultsDisplay_();
      this.updateResultsGivenShortFormat_();
      if (!this.suppressStateChange_)
        tr.b.dispatchSimpleEvent(this, 'statechange');
    },

    updateShortFormResultsDisplay_: function() {
      var display = this.shortFormat_ ? '' : 'none';
      this.querySelector('#shortform-results').style.display = display;
    },

    updateResultsGivenShortFormat_: function() {
      if (!this.results_)
        return;

      if (this.testFilterString_.length || this.testSuiteName_.length)
        this.results_.showHTMLOutput = true;
      else
        this.results_.showHTMLOutput = false;
      this.results_.showPendingAndPassedTests = this.shortFormat_;
    },

    get testTypeToRun() {
      return this.testTypeToRun_;
    },

    set testTypeToRun(testTypeToRun) {
      this.testTypeToRun_ = testTypeToRun;
      var sel;
      switch (testTypeToRun) {
        case tr.b.unittest.TestTypes.UNITTEST:
          sel = 'input[name=test-type-to-run][value=unit]';
          break;
        case tr.b.unittest.TestTypes.PERFTEST:
          sel = 'input[name=test-type-to-run][value=perf]';
          break;
        case ALL_TEST_TYPES:
          sel = 'input[name=test-type-to-run][value=all]';
          break;
        default:
          throw new Error('Invalid test type to run: ' + testTypeToRun);
      }
      this.querySelector(sel).checked = true;
      this.scheduleRerun_();
      if (!this.suppressStateChange_)
        tr.b.dispatchSimpleEvent(this, 'statechange');
    },

    onTestTypeToRunClick_: function(e) {
      switch (e.target.value) {
        case 'unit':
          this.testTypeToRun_ = tr.b.unittest.TestTypes.UNITTEST;
          break;
        case 'perf':
          this.testTypeToRun_ = tr.b.unittest.TestTypes.PERFTEST;
          break;
        case 'all':
          this.testTypeToRun_ = ALL_TEST_TYPES;
          break;
        default:
          throw new Error('Inalid test type: ' + e.target.value);
      }

      this.scheduleRerun_();
      if (!this.suppressStateChange_)
        tr.b.dispatchSimpleEvent(this, 'statechange');
    },

    onTestPassed_: function() {
      this.querySelector('#shortform-results').
          appendChild(document.createTextNode('.'));
    },

    onTestFailed_: function() {
      var span = document.createElement('span');
      span.classList.add('fail');
      span.appendChild(document.createTextNode('F'));
      this.querySelector('#shortform-results').appendChild(span);
    },

    onResultsStatsChanged_: function() {
      var statsEl = this.querySelector('#stats');
      var stats = this.results_.getStats();
      var numTestsOverall = this.runner_.testCases.length;
      var numTestsThatRan = stats.numTestsThatPassed + stats.numTestsThatFailed;
      statsEl.innerHTML =
          '<span>' + numTestsThatRan + '/' + numTestsOverall +
          '</span> tests run, ' +
          '<span class="unittest-failed">' + stats.numTestsThatFailed +
          '</span> failures, ' +
          ' in ' + stats.totalRunTime.toFixed(2) + 'ms.';
    },

    scheduleRerun_: function() {
      if (this.rerunPending_)
        return;
      if (this.runner_) {
        this.rerunPending_ = true;
        this.runner_.beginToStopRunning();
        var doRerun = function() {
          this.rerunPending_ = false;
          this.scheduleRerun_();
        }.bind(this);
        this.runner_.runCompletedPromise.then(
            doRerun, doRerun);
        return;
      }
      this.beginRunning_();
    },

    beginRunning_: function() {
      var resultsContainer = this.querySelector('#results-container');
      if (this.results_) {
        this.results_.removeEventListener('testpassed',
                                          this.onTestPassed_);
        this.results_.removeEventListener('testfailed',
                                          this.onTestFailed_);
        this.results_.removeEventListener('statschange',
                                          this.onResultsStatsChanged_);
        delete this.results_.getHRefForTestCase;
        resultsContainer.removeChild(this.results_);
      }

      this.results_ = new tr.b.unittest.HTMLTestResults();
      this.results_.headless = this.headless_;
      this.results_.getHRefForTestCase = this.getHRefForTestCase.bind(this);
      this.updateResultsGivenShortFormat_();

      this.results_.shortFormat = this.shortFormat_;
      this.results_.addEventListener('testpassed',
                                     this.onTestPassed_);
      this.results_.addEventListener('testfailed',
                                     this.onTestFailed_);
      this.results_.addEventListener('statschange',
                                     this.onResultsStatsChanged_);
      resultsContainer.appendChild(this.results_);

      var tests = this.allTests_.filter(function(test) {
        var i = test.fullyQualifiedName.indexOf(this.testFilterString_);
        if (i == -1)
          return false;
        if (this.testTypeToRun_ !== ALL_TEST_TYPES &&
            test.testType !== this.testTypeToRun_)
          return false;
        return true;
      }, this);

      this.runner_ = new tr.b.unittest.TestRunner(this.results_, tests);
      this.runner_.beginRunning();

      this.runner_.runCompletedPromise.then(
          this.runCompleted_.bind(this),
          this.runCompleted_.bind(this));
    },

    setState: function(state, opt_suppressStateChange) {
      this.suppressStateChange_ = true;
      if (state.testFilterString !== undefined)
        this.testFilterString = state.testFilterString;
      else
        this.testFilterString = '';

      if (state.shortFormat === undefined)
        this.shortFormat = false;
      else
        this.shortFormat = state.shortFormat;

      if (state.testTypeToRun === undefined)
        this.testTypeToRun = tr.b.unittest.TestTypes.UNITTEST;
      else
        this.testTypeToRun = state.testTypeToRun;

      this.testSuiteName_ = state.testSuiteName || '';
      this.headless_ = state.headless || false;

      if (!opt_suppressStateChange)
        this.suppressStateChange_ = false;

      this.onShortFormatClick_();
      this.scheduleRerun_();
      this.suppressStateChange_ = false;
    },

    getDefaultState: function() {
      return {
        testFilterString: '',
        testSuiteName: '',
        shortFormat: false,
        testTypeToRun: tr.b.unittest.TestTypes.UNITTEST
      };
    },

    getState: function() {
      return {
        testFilterString: this.testFilterString_,
        testSuiteName: this.testSuiteName_,
        shortFormat: this.shortFormat_,
        testTypeToRun: this.testTypeToRun_
      };
    },

    getHRefForTestCase: function(testCases) {
      return undefined;
    },

    runCompleted_: function() {
      this.runner_ = undefined;
    }
  };

  function loadAndRunTests(runnerConfig) {

    // The test runner no-ops pushState so keep it around.
    var realWindowHistoryPushState = window.history.pushState.bind(
        window.history);

    function stateToSearchString(defaultState, state) {
      var parts = [];
      for (var k in state) {
        if (state[k] === defaultState[k])
          continue;
        var v = state[k];
        var kv;
        if (v === true) {
          kv = k;
        } else if (v === false) {
          kv = k + '=false';
        } else if (v === '') {
          continue;
        } else {
          kv = k + '=' + v;
        }
        parts.push(kv);
      }
      return parts.join('&');
    }

    function stateFromSearchString(string) {
      var state = {};
      string.split('&').forEach(function(part) {
        if (part == '')
          return;
        var kv = part.split('=');
        var k, v;
        if (kv.length == 1) {
          k = kv[0];
          v = true;
        } else {
          k = kv[0];
          if (kv[1] == 'false')
            v = false;
          else
            v = kv[1];
        }
        state[k] = v;
      });
      return state;
    }

    function getSuiteRelpathsToLoad(state) {
      if (state.testSuiteName) {
        return new Promise(function(resolve) {
          var parts = state.testSuiteName.split('.');
          var testSuiteRelPath = '/' + parts.join('/') + '.html';

          var suiteRelpathsToLoad = [testSuiteRelPath];
          resolve(suiteRelpathsToLoad);
        });
      }
      return runnerConfig.getAllSuiteRelPathsAsync();
    }


    function loadAndRunTestsImpl() {
      var state = stateFromSearchString(
          window.location.search.substring(1));
      updateTitle(state);


      showLoadingOverlay();

      var loader;
      var p = getSuiteRelpathsToLoad(state);
      p = p.then(
        function(suiteRelpathsToLoad) {
          loader = new tr.b.unittest.SuiteLoader(suiteRelpathsToLoad);
          return loader.allSuitesLoadedPromise;
        },
        function(e) {
          hideLoadingOverlay();
          throw e;
        });
      p = p.then(
        function() {
          hideLoadingOverlay();
          Polymer.whenReady(function() {
            runTests(loader, state);
          });
        },
        function(err) {
          hideLoadingOverlay();
          tr.showPanic('Module loading failure', err);
          throw err;
        });
      return p;
    }

    function showLoadingOverlay() {
      var overlay = document.createElement('div');
      overlay.id = 'tests-loading-overlay';
      overlay.style.backgroundColor = 'white';
      overlay.style.boxSizing = 'border-box';
      overlay.style.color = 'black';
      overlay.style.display = '-webkit-flex';
      overlay.style.height = '100%';
      overlay.style.left = 0;
      overlay.style.padding = '8px';
      overlay.style.position = 'fixed';
      overlay.style.top = 0;
      overlay.style.webkitFlexDirection = 'column';
      overlay.style.width = '100%';

      var element = document.createElement('div');
      element.style.webkitFlex = '1 1 auto';
      element.style.overflow = 'auto';
      overlay.appendChild(element);

      element.textContent = 'Loading tests...';
      document.body.appendChild(overlay);
    }
    function hideLoadingOverlay() {
      var overlay = document.body.querySelector('#tests-loading-overlay');
      document.body.removeChild(overlay);
    }

    function updateTitle(state) {
      var testFilterString = state.testFilterString || '';
      var testSuiteName = state.testSuiteName || '';

      var title;
      if (testSuiteName && testFilterString.length) {
        title = testFilterString + ' in ' + testSuiteName;
      } else if (testSuiteName) {
        title = testSuiteName;
      } else if (testFilterString) {
        title = testFilterString + ' in all tests';
      } else {
        title = runnerConfig.title;
      }

      if (state.shortFormat)
        title += '(s)';
      document.title = title;
      var runner = document.querySelector('x-base-interactive-test-runner');
      if (runner)
        runner.title = title;
    }

    function runTests(loader, state) {
      var runner = new tr.b.unittest.InteractiveTestRunner();
      runner.style.width = '100%';
      runner.style.height = '100%';
      runner.testLinks = runnerConfig.testLinks;
      runner.allTests = loader.getAllTests();
      document.body.appendChild(runner);

      runner.setState(state);
      updateTitle(state);

      runner.addEventListener('statechange', function() {
        var state = runner.getState();
        var stateString = stateToSearchString(runner.getDefaultState(),
                                              state);
        if (window.location.search.substring(1) == stateString)
          return;

        updateTitle(state);
        var stateURL;
        if (stateString.length > 0)
          stateURL = window.location.pathname + '?' + stateString;
        else
          stateURL = window.location.pathname;
        realWindowHistoryPushState(state, document.title, stateURL);
      });

      window.addEventListener('popstate', function(state) {
        runner.setState(state, true);
      });

      runner.getHRefForTestCase = function(testCase) {
        var state = runner.getState();
        if (state.testFilterString === '' &&
            state.testSuiteName === '') {
          state.testSuiteName = testCase.suite.name;
          state.testFilterString = '';
          state.shortFormat = false;
        } else {
          state.testSuiteName = testCase.suite.name;
          state.testFilterString = testCase.name;
          state.shortFormat = false;
        }
        var stateString = stateToSearchString(runner.getDefaultState(),
                                              state);
        if (stateString.length > 0)
          return window.location.pathname + '?' + stateString;
        else
          return window.location.pathname;
      }
    }

    loadAndRunTestsImpl();
  }

  return {
    InteractiveTestRunner: InteractiveTestRunner,
    loadAndRunTests: loadAndRunTests
  };
});
</script>

{'fraction_non_alphanumeric': 0.1251187755206862, 'fraction_numerical': 0.003235830401397057, 'mean_word_length': 2.270070540812899, 'pattern_counts': {'":': 0, '<': 103, '<?xml version=': 0, '>': 135, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 26, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4552929', 'n_tokens_mistral': 11701, 'n_tokens_neox': 11384, 'n_words': 2065}