// Copyright 2014 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // This file emulates Mocha test framework used in promises-aplus tests. var describe; var it; var specify; var before; var after; var beforeEach; var afterEach; var RunAllTests; var assert = require('assert'); (function() { var TIMEOUT = 1000; function PostMicrotask(fn) { var o = {}; Object.observe(o, function() { fn(); }); // Change something to enqueue a microtask. o.x = 'hello'; } var context = { beingDescribed: undefined, currentSuiteIndex: 0, suites: [] }; function Run() { function current() { while (context.currentSuiteIndex < context.suites.length && context.suites[context.currentSuiteIndex].hasRun) { ++context.currentSuiteIndex; } if (context.suites.length == context.currentSuiteIndex) { return undefined; } return context.suites[context.currentSuiteIndex]; } var suite = current(); if (!suite) { // done print('All tests have run.'); return; } suite.Run(); } RunAllTests = function() { context.currentSuiteIndex = 0; var numRegularTestCases = 0; for (var i = 0; i < context.suites.length; ++i) { numRegularTestCases += context.suites[i].numRegularTestCases(); } print(context.suites.length + ' suites and ' + numRegularTestCases + ' test cases are found'); Run(); }; function TestCase(name, before, fn, after, isRegular) { this.name = name; this.before = before; this.fn = fn; this.after = after; this.isRegular = isRegular; this.hasDone = false; } TestCase.prototype.RunFunction = function(suite, fn, postAction) { if (!fn) { postAction(); return; } try { if (fn.length === 0) { // synchronous fn(); postAction(); } else { // asynchronous fn(postAction); } } catch (e) { suite.ReportError(this, e); } } TestCase.prototype.MarkAsDone = function() { this.hasDone = true; clearTimeout(this.timer); } TestCase.prototype.Run = function(suite, postAction) { print('Running ' + suite.description + '#' + this.name + ' ...'); assert.clear(); this.timer = setTimeout(function() { suite.ReportError(this, Error('timeout')); }.bind(this), TIMEOUT); this.RunFunction(suite, this.before, function(e) { if (this.hasDone) { return; } if (e instanceof Error) { return suite.ReportError(this, e); } if (assert.fails.length > 0) { return suite.ReportError(this, assert.fails[0]); } this.RunFunction(suite, this.fn, function(e) { if (this.hasDone) { return; } if (e instanceof Error) { return suite.ReportError(this, e); } if (assert.fails.length > 0) { return suite.ReportError(this, assert.fails[0]); } this.RunFunction(suite, this.after, function(e) { if (this.hasDone) { return; } if (e instanceof Error) { return suite.ReportError(this, e); } if (assert.fails.length > 0) { return suite.ReportError(this, assert.fails[0]); } this.MarkAsDone(); if (this.isRegular) { print('PASS: ' + suite.description + '#' + this.name); } PostMicrotask(postAction); }.bind(this)); }.bind(this)); }.bind(this)); }; function TestSuite(described) { this.description = described.description; this.cases = []; this.currentIndex = 0; this.hasRun = false; if (described.before) { this.cases.push(new TestCase(this.description + ' :before', undefined, described.before, undefined, false)); } for (var i = 0; i < described.cases.length; ++i) { this.cases.push(new TestCase(described.cases[i].description, described.beforeEach, described.cases[i].fn, described.afterEach, true)); } if (described.after) { this.cases.push(new TestCase(this.description + ' :after', undefined, described.after, undefined, false)); } } TestSuite.prototype.Run = function() { this.hasRun = this.currentIndex === this.cases.length; if (this.hasRun) { PostMicrotask(Run); return; } // TestCase.prototype.Run cannot throw an exception. this.cases[this.currentIndex].Run(this, function() { ++this.currentIndex; PostMicrotask(Run); }.bind(this)); }; TestSuite.prototype.numRegularTestCases = function() { var n = 0; for (var i = 0; i < this.cases.length; ++i) { if (this.cases[i].isRegular) { ++n; } } return n; } TestSuite.prototype.ReportError = function(testCase, e) { if (testCase.hasDone) { return; } testCase.MarkAsDone(); this.hasRun = this.currentIndex === this.cases.length; print('FAIL: ' + this.description + '#' + testCase.name + ': ' + e.name + ' (' + e.message + ')'); ++this.currentIndex; PostMicrotask(Run); }; describe = function(description, fn) { var parent = context.beingDescribed; var incomplete = { cases: [], description: parent ? parent.description + ' ' + description : description, parent: parent, }; context.beingDescribed = incomplete; fn(); context.beingDescribed = parent; context.suites.push(new TestSuite(incomplete)); } specify = it = function(description, fn) { context.beingDescribed.cases.push({description: description, fn: fn}); } before = function(fn) { context.beingDescribed.before = fn; } after = function(fn) { context.beingDescribed.after = fn; } beforeEach = function(fn) { context.beingDescribed.beforeEach = fn; } afterEach = function(fn) { context.beingDescribed.afterEach = fn; } }());