Home | History | Annotate | Download | only in lib
      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 
     30 var describe;
     31 var it;
     32 var specify;
     33 var before;
     34 var after;
     35 var beforeEach;
     36 var afterEach;
     37 var RunAllTests;
     38 
     39 var assert = require('assert');
     40 
     41 (function() {
     42 var TIMEOUT = 1000;
     43 
     44 var context = {
     45   beingDescribed: undefined,
     46   currentSuiteIndex: 0,
     47   suites: []
     48 };
     49 
     50 function 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 
     70 RunAllTests = 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 
     81 function 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 
     90 TestCase.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 
    109 TestCase.prototype.MarkAsDone = function() {
    110   this.hasDone = true;
    111   clearTimeout(this.timer);
    112 }
    113 
    114 TestCase.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 
    162 function 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 
    185 TestSuite.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 
    199 TestSuite.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 
    209 TestSuite.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 
    221 describe = 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 
    235 specify = it = function(description, fn) {
    236   context.beingDescribed.cases.push({description: description, fn: fn});
    237 }
    238 
    239 before = function(fn) {
    240   context.beingDescribed.before = fn;
    241 }
    242 
    243 after = function(fn) {
    244   context.beingDescribed.after = fn;
    245 }
    246 
    247 beforeEach = function(fn) {
    248   context.beingDescribed.beforeEach = fn;
    249 }
    250 
    251 afterEach = function(fn) {
    252   context.beingDescribed.afterEach = fn;
    253 }
    254 
    255 }());
    256