1// Copyright 2014 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28// This file emulates Mocha test framework used in promises-aplus tests. 29 30var describe; 31var it; 32var specify; 33var before; 34var after; 35var beforeEach; 36var afterEach; 37var RunAllTests; 38 39var assert = require('assert'); 40 41(function() { 42var TIMEOUT = 1000; 43 44var context = { 45 beingDescribed: undefined, 46 currentSuiteIndex: 0, 47 suites: [] 48}; 49 50function Run() { 51 function current() { 52 while (context.currentSuiteIndex < context.suites.length && 53 context.suites[context.currentSuiteIndex].hasRun) { 54 ++context.currentSuiteIndex; 55 } 56 if (context.suites.length == context.currentSuiteIndex) { 57 return undefined; 58 } 59 return context.suites[context.currentSuiteIndex]; 60 } 61 var suite = current(); 62 if (!suite) { 63 // done 64 print('All tests have run.'); 65 return; 66 } 67 suite.Run(); 68} 69 70RunAllTests = function() { 71 context.currentSuiteIndex = 0; 72 var numRegularTestCases = 0; 73 for (var i = 0; i < context.suites.length; ++i) { 74 numRegularTestCases += context.suites[i].numRegularTestCases(); 75 } 76 print(context.suites.length + ' suites and ' + numRegularTestCases + 77 ' test cases are found'); 78 Run(); 79}; 80 81function TestCase(name, before, fn, after, isRegular) { 82 this.name = name; 83 this.before = before; 84 this.fn = fn; 85 this.after = after; 86 this.isRegular = isRegular; 87 this.hasDone = false; 88} 89 90TestCase.prototype.RunFunction = function(suite, fn, postAction) { 91 if (!fn) { 92 postAction(); 93 return; 94 } 95 try { 96 if (fn.length === 0) { 97 // synchronous 98 fn(); 99 postAction(); 100 } else { 101 // asynchronous 102 fn(postAction); 103 } 104 } catch (e) { 105 suite.ReportError(this, e); 106 } 107} 108 109TestCase.prototype.MarkAsDone = function() { 110 this.hasDone = true; 111 clearTimeout(this.timer); 112} 113 114TestCase.prototype.Run = function(suite, postAction) { 115 print('Running ' + suite.description + '#' + this.name + ' ...'); 116 assert.clear(); 117 118 this.timer = setTimeout(function() { 119 suite.ReportError(this, Error('timeout')); 120 }.bind(this), TIMEOUT); 121 122 this.RunFunction(suite, this.before, function(e) { 123 if (this.hasDone) { 124 return; 125 } 126 if (e instanceof Error) { 127 return suite.ReportError(this, e); 128 } 129 if (assert.fails.length > 0) { 130 return suite.ReportError(this, assert.fails[0]); 131 } 132 this.RunFunction(suite, this.fn, function(e) { 133 if (this.hasDone) { 134 return; 135 } 136 if (e instanceof Error) { 137 return suite.ReportError(this, e); 138 } 139 if (assert.fails.length > 0) { 140 return suite.ReportError(this, assert.fails[0]); 141 } 142 this.RunFunction(suite, this.after, function(e) { 143 if (this.hasDone) { 144 return; 145 } 146 if (e instanceof Error) { 147 return suite.ReportError(this, e); 148 } 149 if (assert.fails.length > 0) { 150 return suite.ReportError(this, assert.fails[0]); 151 } 152 this.MarkAsDone(); 153 if (this.isRegular) { 154 print('PASS: ' + suite.description + '#' + this.name); 155 } 156 %EnqueueMicrotask(postAction); 157 }.bind(this)); 158 }.bind(this)); 159 }.bind(this)); 160}; 161 162function TestSuite(described) { 163 this.description = described.description; 164 this.cases = []; 165 this.currentIndex = 0; 166 this.hasRun = false; 167 168 if (described.before) { 169 this.cases.push(new TestCase(this.description + ' :before', undefined, 170 described.before, undefined, false)); 171 } 172 for (var i = 0; i < described.cases.length; ++i) { 173 this.cases.push(new TestCase(described.cases[i].description, 174 described.beforeEach, 175 described.cases[i].fn, 176 described.afterEach, 177 true)); 178 } 179 if (described.after) { 180 this.cases.push(new TestCase(this.description + ' :after', 181 undefined, described.after, undefined, false)); 182 } 183} 184 185TestSuite.prototype.Run = function() { 186 this.hasRun = this.currentIndex === this.cases.length; 187 if (this.hasRun) { 188 %EnqueueMicrotask(Run); 189 return; 190 } 191 192 // TestCase.prototype.Run cannot throw an exception. 193 this.cases[this.currentIndex].Run(this, function() { 194 ++this.currentIndex; 195 %EnqueueMicrotask(Run); 196 }.bind(this)); 197}; 198 199TestSuite.prototype.numRegularTestCases = function() { 200 var n = 0; 201 for (var i = 0; i < this.cases.length; ++i) { 202 if (this.cases[i].isRegular) { 203 ++n; 204 } 205 } 206 return n; 207} 208 209TestSuite.prototype.ReportError = function(testCase, e) { 210 if (testCase.hasDone) { 211 return; 212 } 213 testCase.MarkAsDone(); 214 this.hasRun = this.currentIndex === this.cases.length; 215 print('FAIL: ' + this.description + '#' + testCase.name + ': ' + 216 e.name + ' (' + e.message + ')'); 217 ++this.currentIndex; 218 %EnqueueMicrotask(Run); 219}; 220 221describe = function(description, fn) { 222 var parent = context.beingDescribed; 223 var incomplete = { 224 cases: [], 225 description: parent ? parent.description + ' ' + description : description, 226 parent: parent, 227 }; 228 context.beingDescribed = incomplete; 229 fn(); 230 context.beingDescribed = parent; 231 232 context.suites.push(new TestSuite(incomplete)); 233} 234 235specify = it = function(description, fn) { 236 context.beingDescribed.cases.push({description: description, fn: fn}); 237} 238 239before = function(fn) { 240 context.beingDescribed.before = fn; 241} 242 243after = function(fn) { 244 context.beingDescribed.after = fn; 245} 246 247beforeEach = function(fn) { 248 context.beingDescribed.beforeEach = fn; 249} 250 251afterEach = function(fn) { 252 context.beingDescribed.afterEach = fn; 253} 254 255}()); 256