Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(23)

Unified Diff: tools/cc-frame-viewer/src/base/unittest.js

Issue 12225131: [cc] Initial checkin of cc-frame-viewer (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/cc-frame-viewer/src/base/unittest.css ('k') | tools/cc-frame-viewer/src/color_mappings.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/cc-frame-viewer/src/base/unittest.js
diff --git a/tools/cc-frame-viewer/src/base/unittest.js b/tools/cc-frame-viewer/src/base/unittest.js
new file mode 100644
index 0000000000000000000000000000000000000000..7d134b65acac4f15344d20144503034f80b639d9
--- /dev/null
+++ b/tools/cc-frame-viewer/src/base/unittest.js
@@ -0,0 +1,557 @@
+// Copyright (c) 2012 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.
+
+'use strict';
+
+/**
+ * @fileoverview A test harness loosely based on Python unittest, but that
+ * installs global assert methods during the test for backward compatibility
+ * with Closure tests.
+ */
+base.requireStylesheet('base.unittest');
+base.exportTo('base.unittest', function() {
+
+ var NOCATCH_MODE = false;
+
+ // Uncomment the line below to make unit test failures throw exceptions.
+ NOCATCH_MODE = true;
+
+ function createTestCaseDiv(testName, opt_href, opt_alwaysShowErrorLink) {
+ var el = document.createElement('test-case');
+
+ var titleBlockEl = document.createElement('title');
+ titleBlockEl.style.display = 'inline';
+ el.appendChild(titleBlockEl);
+
+ var titleEl = document.createElement('span');
+ titleEl.style.marginRight = '20px';
+ titleBlockEl.appendChild(titleEl);
+
+ var errorLink = document.createElement('a');
+ errorLink.textContent = 'Run individually...';
+ if (opt_href)
+ errorLink.href = opt_href;
+ else
+ errorLink.href = '#' + testName;
+ errorLink.style.display = 'none';
+ titleBlockEl.appendChild(errorLink);
+
+ el.__defineSetter__('status', function(status) {
+ titleEl.textContent = testName + ': ' + status;
+ updateClassListGivenStatus(titleEl, status);
+ if (status == 'FAILED' || opt_alwaysShowErrorLink)
+ errorLink.style.display = '';
+ else
+ errorLink.style.display = 'none';
+ });
+
+ el.addError = function(test, e) {
+ var errorEl = createErrorDiv(test, e);
+ el.appendChild(errorEl);
+ return errorEl;
+ };
+
+ el.addHTMLOutput = function(opt_title, opt_element) {
+ var outputEl = createOutputDiv(opt_title, opt_element);
+ el.appendChild(outputEl);
+ return outputEl.contents;
+ };
+
+ el.status = 'READY';
+ return el;
+ }
+
+ function createErrorDiv(test, e) {
+ var el = document.createElement('test-case-error');
+ el.className = 'unittest-error';
+
+ var stackEl = document.createElement('test-case-stack');
+ if (typeof e == 'string') {
+ stackEl.textContent = e;
+ } else if (e.stack) {
+ var i = document.location.pathname.lastIndexOf('/');
+ var path = document.location.origin +
+ document.location.pathname.substring(0, i);
+ var pathEscaped = path.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
+ var cleanStack = e.stack.replace(new RegExp(pathEscaped, 'g'), '.');
+ stackEl.textContent = cleanStack;
+ } else {
+ stackEl.textContent = e;
+ }
+ el.appendChild(stackEl);
+ return el;
+ }
+
+ function createOutputDiv(opt_title, opt_element) {
+ var el = document.createElement('test-case-output');
+ if (opt_title) {
+ var titleEl = document.createElement('div');
+ titleEl.textContent = opt_title;
+ el.appendChild(titleEl);
+ }
+ var contentEl = opt_element || document.createElement('div');
+ el.appendChild(contentEl);
+
+ el.__defineGetter__('contents', function() {
+ return contentEl;
+ });
+ return el;
+ }
+
+ function statusToClassName(status) {
+ if (status == 'PASSED')
+ return 'unittest-green';
+ else if (status == 'RUNNING' || status == 'READY')
+ return 'unittest-yellow';
+ else
+ return 'unittest-red';
+ }
+
+ function updateClassListGivenStatus(el, status) {
+ var newClass = statusToClassName(status);
+ if (newClass != 'unittest-green')
+ el.classList.remove('unittest-green');
+ if (newClass != 'unittest-yellow')
+ el.classList.remove('unittest-yellow');
+ if (newClass != 'unittest-red')
+ el.classList.remove('unittest-red');
+
+ el.classList.add(newClass);
+ }
+
+ function HTMLTestRunner(opt_title, opt_curHash) {
+ // This constructs a HTMLDivElement and then adds our own runner methods to
+ // it. This is usually done via ui.js' define system, but we dont want our
+ // test runner to be dependent on the UI lib. :)
+ var outputEl = document.createElement('unittest-test-runner');
+ outputEl.__proto__ = HTMLTestRunner.prototype;
+ this.decorate.call(outputEl, opt_title, opt_curHash);
+ return outputEl;
+ }
+
+ HTMLTestRunner.prototype = {
+ __proto__: HTMLDivElement.prototype,
+
+ decorate: function(opt_title, opt_curHash) {
+ this.running = false;
+
+ this.currentTest_ = undefined;
+ this.results = undefined;
+ if (opt_curHash) {
+ var trimmedHash = opt_curHash.substring(1);
+ this.filterFunc_ = function(testName) {
+ return testName.indexOf(trimmedHash) == 0;
+ };
+ } else
+ this.filterFunc_ = function(testName) { return true; };
+
+ this.statusEl_ = document.createElement('title');
+ this.appendChild(this.statusEl_);
+
+ this.resultsEl_ = document.createElement('div');
+ this.appendChild(this.resultsEl_);
+
+ this.title_ = opt_title || document.title;
+
+ this.updateStatus();
+ },
+
+ computeResultStats: function() {
+ var numTestsRun = 0;
+ var numTestsPassed = 0;
+ var numTestsWithErrors = 0;
+ if (this.results) {
+ for (var i = 0; i < this.results.length; i++) {
+ numTestsRun++;
+ if (this.results[i].errors.length)
+ numTestsWithErrors++;
+ else
+ numTestsPassed++;
+ }
+ }
+ return {
+ numTestsRun: numTestsRun,
+ numTestsPassed: numTestsPassed,
+ numTestsWithErrors: numTestsWithErrors
+ };
+ },
+
+ updateStatus: function() {
+ var stats = this.computeResultStats();
+ var status;
+ if (!this.results) {
+ status = 'READY';
+ } else if (this.running) {
+ status = 'RUNNING';
+ } else {
+ if (stats.numTestsRun && stats.numTestsWithErrors == 0)
+ status = 'PASSED';
+ else
+ status = 'FAILED';
+ }
+
+ updateClassListGivenStatus(this.statusEl_, status);
+ this.statusEl_.textContent = this.title_ + ' [' + status + ']';
+ },
+
+ get done() {
+ return this.results && this.running == false;
+ },
+
+ run: function(tests) {
+ this.results = [];
+ this.running = true;
+ this.updateStatus();
+ for (var i = 0; i < tests.length; i++) {
+ if (!this.filterFunc_(tests[i].testName))
+ continue;
+ tests[i].run(this);
+ this.updateStatus();
+ }
+ this.running = false;
+ this.updateStatus();
+ },
+
+ willRunTest: function(test) {
+ this.currentTest_ = test;
+ this.currentResults_ = {testName: test.testName,
+ errors: []};
+ this.results.push(this.currentResults_);
+
+ this.currentTestCaseEl_ = createTestCaseDiv(test.testName);
+ this.currentTestCaseEl_.status = 'RUNNING';
+ this.resultsEl_.appendChild(this.currentTestCaseEl_);
+ },
+
+ /**
+ * Adds some html content to the currently running test
+ * @param {String} opt_title The title for the output.
+ * @param {HTMLElement} opt_element The element to add. If not added, then.
+ * @return {HTMLElement} The element added, or if !opt_element, the element
+ * created.
+ */
+ addHTMLOutput: function(opt_title, opt_element) {
+ return this.currentTestCaseEl_.addHTMLOutput(opt_title, opt_element);
+ },
+
+ addError: function(e) {
+ this.currentResults_.errors.push(e);
+ return this.currentTestCaseEl_.addError(this.currentTest_, e);
+ },
+
+ didRunTest: function(test) {
+ if (!this.currentResults_.errors.length)
+ this.currentTestCaseEl_.status = 'PASSED';
+ else
+ this.currentTestCaseEl_.status = 'FAILED';
+
+ this.currentResults_ = undefined;
+ this.currentTest_ = undefined;
+ }
+ };
+
+ function TestError(opt_message) {
+ var that = new Error(opt_message);
+ Error.captureStackTrace(that, TestError);
+ that.__proto__ = TestError.prototype;
+ return that;
+ }
+
+ TestError.prototype = {
+ __proto__: Error.prototype
+ };
+
+ /*
+ * @constructor TestCase
+ */
+ function TestCase(testMethod, opt_testMethodName) {
+ if (!testMethod)
+ throw new Error('testMethod must be provided');
+ if (testMethod.name == '' && !opt_testMethodName)
+ throw new Error('testMethod must have a name, ' +
+ 'or opt_testMethodName must be provided.');
+
+ this.testMethod_ = testMethod;
+ this.testMethodName_ = opt_testMethodName || testMethod.name;
+ this.results_ = undefined;
+ };
+
+ function forAllAssertAndEnsureMethodsIn_(prototype, fn) {
+ for (var fieldName in prototype) {
+ if (fieldName.indexOf('assert') != 0 &&
+ fieldName.indexOf('ensure') != 0)
+ continue;
+ var fieldValue = prototype[fieldName];
+ if (typeof fieldValue != 'function')
+ continue;
+ fn(fieldName, fieldValue);
+ }
+ }
+
+ TestCase.prototype = {
+ __proto__: Object.prototype,
+
+ get testName() {
+ return this.testMethodName_;
+ },
+
+ bindGlobals_: function() {
+ forAllAssertAndEnsureMethodsIn_(TestCase.prototype,
+ function(fieldName, fieldValue) {
+ global[fieldName] = fieldValue.bind(this);
+ });
+ },
+
+ unbindGlobals_: function() {
+ forAllAssertAndEnsureMethodsIn_(TestCase.prototype,
+ function(fieldName, fieldValue) {
+ delete global[fieldName];
+ });
+ },
+
+ /**
+ * Adds some html content to the currently running test
+ * @param {String} opt_title The title for the output.
+ * @param {HTMLElement} opt_element The element to add. If not added, then.
+ * @return {HTMLElement} The element added, or if !opt_element, the element
+ * created.
+ */
+ addHTMLOutput: function(opt_title, opt_element) {
+ return this.results_.addHTMLOutput(opt_title, opt_element);
+ },
+
+ assertTrue: function(a, opt_message) {
+ if (a)
+ return;
+ var message = opt_message || 'Expected true, got ' + a;
+ throw new TestError(message);
+ },
+
+ assertFalse: function(a, opt_message) {
+ if (!a)
+ return;
+ var message = opt_message || 'Expected false, got ' + a;
+ throw new TestError(message);
+ },
+
+ assertUndefined: function(a, opt_message) {
+ if (a === undefined)
+ return;
+ var message = opt_message || 'Expected undefined, got ' + a;
+ throw new TestError(message);
+ },
+
+ assertNotUndefined: function(a, opt_message) {
+ if (a !== undefined)
+ return;
+ var message = opt_message || 'Expected not undefined, got ' + a;
+ throw new TestError(message);
+ },
+
+ assertNull: function(a, opt_message) {
+ if (a === null)
+ return;
+ var message = opt_message || 'Expected null, got ' + a;
+ throw new TestError(message);
+ },
+
+ assertNotNull: function(a, opt_message) {
+ if (a !== null)
+ return;
+ var message = opt_message || 'Expected non-null, got ' + a;
+ throw new TestError(message);
+ },
+
+ assertEquals: function(a, b, opt_message) {
+ if (a == b)
+ return;
+ var message = opt_message || 'Expected ' + a + ', got ' + b;
+ throw new TestError(message);
+ },
+
+ assertNotEquals: function(a, b, opt_message) {
+ if (a != b)
+ return;
+ var message = opt_message || 'Expected something not equal to ' + b;
+ throw new TestError(message);
+ },
+
+ assertArrayEquals: function(a, b, opt_message) {
+ if (a.length == b.length) {
+ var ok = true;
+ for (var i = 0; i < a.length; i++) {
+ ok &= a[i] === b[i];
+ }
+ if (ok)
+ return;
+ }
+
+ var message = opt_message || 'Expected array ' + a + ', got array ' + b;
+ throw new TestError(message);
+ },
+
+ assertArrayShallowEquals: function(a, b, opt_message) {
+ if (a.length == b.length) {
+ var ok = true;
+ for (var i = 0; i < a.length; i++) {
+ ok &= a[i] === b[i];
+ }
+ if (ok)
+ return;
+ }
+
+ var message = opt_message || 'Expected array ' + b + ', got array ' + a;
+ throw new TestError(message);
+ },
+
+ assertAlmostEquals: function(a, b, opt_message) {
+ if (Math.abs(a - b) < 0.00001)
+ return;
+ var message = opt_message || 'Expected almost ' + a + ', got ' + b;
+ throw new TestError(message);
+ },
+
+ assertThrows: function(fn, opt_message) {
+ try {
+ fn();
+ } catch (e) {
+ return;
+ }
+ var message = opt_message || 'Expected throw from ' + fn;
+ throw new TestError(message);
+ },
+
+ setUp: function() {
+ },
+
+ run: function(results) {
+ this.bindGlobals_();
+ try {
+ this.results_ = results;
+ results.willRunTest(this);
+
+ if (NOCATCH_MODE) {
+ this.setUp();
+ this.testMethod_();
+ this.tearDown();
+ } else {
+ // Set up.
+ try {
+ this.setUp();
+ } catch (e) {
+ results.addError(e);
+ return;
+ }
+
+ // Run.
+ try {
+ this.testMethod_();
+ } catch (e) {
+ results.addError(e);
+ }
+
+ // Tear down.
+ try {
+ this.tearDown();
+ } catch (e) {
+ if (typeof e == 'string')
+ e = new TestError(e);
+ results.addError(e);
+ }
+ }
+ } finally {
+ this.unbindGlobals_();
+ results.didRunTest(this);
+ this.results_ = undefined;
+ }
+ },
+
+ tearDown: function() {
+ }
+
+ };
+
+ /**
+ * Returns an array of TestCase objects correpsonding to the tests
+ * found in the given object. This considers any functions beginning with test
+ * as a potential test.
+ *
+ * @param {object} opt_objectToEnumerate The object to enumerate, or global if
+ * not specified.
+ * @param {RegExp} opt_filter Return only tests that match this regexp.
+ */
+ function discoverTests(opt_objectToEnumerate, opt_filter) {
+ var objectToEnumerate = opt_objectToEnumerate || global;
+
+ var tests = [];
+ for (var testMethodName in objectToEnumerate) {
+ if (testMethodName.search(/^test.+/) != 0)
+ continue;
+
+ if (opt_filter && testMethodName.search(opt_filter) == -1)
+ continue;
+
+ var testMethod = objectToEnumerate[testMethodName];
+ if (typeof testMethod != 'function')
+ continue;
+ var testCase = new TestCase(testMethod, testMethodName);
+ tests.push(testCase);
+ }
+ tests.sort(function(a, b) {
+ return a.testName < b.testName;
+ });
+ return tests;
+ }
+
+ /**
+ * Runs all unit tests.
+ */
+ function runAllTests(opt_objectToEnumerate) {
+ var runner;
+ function init() {
+ if (runner)
+ runner.parentElement.removeChild(runner);
+ runner = new HTMLTestRunner(document.title, document.location.hash);
+ // Stash the runner on global so that the global test runner
+ // can get to it.
+ global.G_testRunner = runner;
+ }
+
+ function append() {
+ document.body.appendChild(runner);
+ }
+
+ function run() {
+ var objectToEnumerate = opt_objectToEnumerate || global;
+ var tests = discoverTests(objectToEnumerate);
+ runner.run(tests);
+ }
+
+ global.addEventListener('hashchange', function() {
+ init();
+ append();
+ run();
+ });
+
+ init();
+ if (document.body)
+ append();
+ else
+ document.addEventListener('DOMContentLoaded', append);
+ global.addEventListener('load', run);
+ }
+
+ if (/_test.html$/.test(document.location.pathname))
+ runAllTests();
+
+ return {
+ HTMLTestRunner: HTMLTestRunner,
+ TestError: TestError,
+ TestCase: TestCase,
+ discoverTests: discoverTests,
+ runAllTests: runAllTests,
+ createErrorDiv_: createErrorDiv,
+ createTestCaseDiv_: createTestCaseDiv
+ };
+});
« no previous file with comments | « tools/cc-frame-viewer/src/base/unittest.css ('k') | tools/cc-frame-viewer/src/color_mappings.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698