Name: js-handler/node_modules/nodeunit/node_modules/tap/lib/tap-harness.js
| 1: | // a thing that runs tests. |
| 2: | // Every "test" is also a harness. If they do not have a harness, |
| 3: | // then they are attached to the defaut "global harness", |
| 4: | // which writes its results to stdout. |
| 5: | |
| 6: | |
| 7: | // TODO: |
| 8: | // - Bailout should stop running any tests. |
| 9: | // - "skip" in the test config obj should skip it. |
| 10: | |
| 11: | module.exports = Harness |
| 12: | var EE = require("events").EventEmitter |
| 13: | require("inherits")(Harness, EE) |
| 14: | |
| 15: | var Results = require("./tap-results") |
| 16: | , TapProducer = require("./tap-producer") |
| 17: | , assert = require("./tap-assert") |
| 18: | |
| 19: | function Harness (Test) { |
| 20: | if (!(this instanceof Harness)) return new Harness(Test) |
| 21: | |
| 22: | //console.error("Test in "+this.constructor.name, Test) |
| 23: | |
| 24: | this._Test = Test |
| 25: | this._plan = null |
| 26: | this._children = [] |
| 27: | this._started = false |
| 28: | |
| 29: | this._testCount = 0 |
| 30: | this._planSum = 0 |
| 31: | |
| 32: | this.results = new Results() |
| 33: | // emit result events on the harness. |
| 34: | //this.results.on("result", function (res) { |
| 35: | // console.error("proxying result ev from res to harness") |
| 36: | // this.emit("result", res) |
| 37: | //}.bind(this)) |
| 38: | var me = this |
| 39: | this.results.on("result", this.emit.bind(this, "result")) |
| 40: | |
| 41: | var p = this.process.bind(this) |
| 42: | this.process = function () { |
| 43: | this._started = true |
| 44: | process.nextTick(p) |
| 45: | } |
| 46: | |
| 47: | this.output = new TapProducer() |
| 48: | EE.call(this) |
| 49: | } |
| 50: | |
| 51: | // this function actually only gets called bound to |
| 52: | // the Harness object, and on process.nextTick. Even if |
| 53: | // passed as an event handler, everything *else* will |
| 54: | // happen before it gets called. |
| 55: | Harness.prototype.process = function () { |
| 56: | //console.error("harness process") |
| 57: | // "end" can emit multiple times, so only actually move on |
| 58: | // to the next test if the current one is actually over. |
| 59: | // TODO: multiple in-process tests, if all are marked "async" |
| 60: | if (this._current) { |
| 61: | if (!this._current._ended) return |
| 62: | // handle the current one before moving onto the next. |
| 63: | this.childEnd(this._current) |
| 64: | } |
| 65: | var skip = true |
| 66: | while (skip) { |
| 67: | //console.error("checking for skips") |
| 68: | var current = this._current = this._children.shift() |
| 69: | if (current) { |
| 70: | skip = current.conf.skip |
| 71: | if (skip) { |
| 72: | //console.error("add a failure for the skipping") |
| 73: | this.results.add(assert.fail(current.conf.name |
| 74: | ,{skip:true, diag:false})) |
| 75: | } |
| 76: | } else skip = false |
| 77: | } |
| 78: | |
| 79: | // keep processing through skipped tests, instead of running them. |
| 80: | if (current && this._bailedOut) { |
| 81: | return this.process() |
| 82: | } |
| 83: | |
| 84: | //console.error("got current?", !!current) |
| 85: | if (current) { |
| 86: | current.on("end", this.process) |
| 87: | current.emit("ready") |
| 88: | //console.error("emitted ready") |
| 89: | //console.error("_plan", this._plan, this.constructor.name) |
| 90: | } else { |
| 91: | //console.error("Harness process: no more left. ending") |
| 92: | if (this._endNice) { |
| 93: | this._endNice() |
| 94: | } else { |
| 95: | this.end() |
| 96: | } |
| 97: | } |
| 98: | } |
| 99: | |
| 100: | Harness.prototype.end = function () { |
| 101: | if (this._children.length) { |
| 102: | return this.process() |
| 103: | } |
| 104: | //console.error("harness end", this.constructor.name) |
| 105: | if (this._bailedOut) return |
| 106: | |
| 107: | // can't call .end() more than once. |
| 108: | if (this._ended) { |
| 109: | //console.error("adding failure for end calling") |
| 110: | this.results.add(assert.fail("end called more than once")) |
| 111: | } |
| 112: | |
| 113: | // see if the plan is completed properly, if there was one. |
| 114: | if (this._plan !== null) { |
| 115: | var total = this._testCount |
| 116: | if (total !== this._plan) { |
| 117: | this.results.add(assert.equal(total, this._plan, "test count != plan")) |
| 118: | } |
| 119: | this._plan = total |
| 120: | } |
| 121: | |
| 122: | //console.error("setting ended true", this.constructor.name) |
| 123: | this._ended = true |
| 124: | this.emit("end") |
| 125: | } |
| 126: | |
| 127: | Harness.prototype.plan = function (p) { |
| 128: | //console.error("setting plan", new Error().stack) |
| 129: | if (this._plan !== null) { |
| 130: | //console.error("about to add failure for calling plan") |
| 131: | return this.results.add(assert.fail("plan set multiple times")) |
| 132: | } |
| 133: | this._plan = p |
| 134: | if (p === 0 || this.results.testsTotal) { |
| 135: | this.end() |
| 136: | } |
| 137: | } |
| 138: | |
| 139: | Harness.prototype.childEnd = function (child) { |
| 140: | //console.error("childEnd") |
| 141: | this._testCount ++ |
| 142: | this._planSum += child._plan |
| 143: | //console.error("adding set of child.results") |
| 144: | |
| 145: | this.results.add(child.conf.name || "(unnamed test)") |
| 146: | this.results.addSet(child.results) |
| 147: | this.emit("childEnd", child) |
| 148: | // was this planned? |
| 149: | if (this._plan === this._testCount) { |
| 150: | //console.error("plan", [this._plan, this._testCount]) |
| 151: | return this.end() |
| 152: | } |
| 153: | } |
| 154: | |
| 155: | function copyObj(o) { |
| 156: | var copied = {} |
| 157: | Object.keys(o).forEach(function (k) { copied[k] = o[k] }) |
| 158: | return copied |
| 159: | } |
| 160: | |
| 161: | Harness.prototype.test = function test (name, conf, cb) { |
| 162: | if (this._bailedOut) return |
| 163: | |
| 164: | if (typeof conf === "function") cb = conf, conf = null |
| 165: | if (typeof name === "object") conf = name, name = null |
| 166: | if (typeof name === "function") cb = name, name = null |
| 167: | |
| 168: | conf = (conf ? copyObj(conf) : {}) |
| 169: | name = name || "" |
| 170: | |
| 171: | //console.error("making test", [name, conf, cb]) |
| 172: | |
| 173: | // timeout: value in milliseconds. Defaults to 30s |
| 174: | // Set to Infinity to have no timeout. |
| 175: | if (isNaN(conf.timeout)) conf.timeout = 30000 |
| 176: | var t = new this._Test(this, name, conf) |
| 177: | var self = this |
| 178: | if (cb) { |
| 179: | //console.error("attaching cb to ready event") |
| 180: | t.on("ready", function () { |
| 181: | if (!isNaN(conf.timeout) && isFinite(conf.timeout)) { |
| 182: | var timer = setTimeout(this.timeout.bind(this), conf.timeout) |
| 183: | var clear = function () { |
| 184: | clearTimeout(timer) |
| 185: | } |
| 186: | t.on("end", clear) |
| 187: | t.on("bailout", function (message) { |
| 188: | self.bailout(message) |
| 189: | clear() |
| 190: | }) |
| 191: | } |
| 192: | }) |
| 193: | t.on("ready", cb.bind(t, t)) |
| 194: | // proxy the child results to this object. |
| 195: | //t.on("result", function (res) { |
| 196: | // console.error("in harness, proxying result up") |
| 197: | // t.results.add(res) |
| 198: | //}) |
| 199: | } |
| 200: | return t |
| 201: | } |
| 202: | |
| 203: | Harness.prototype.bailout = function (message) { |
| 204: | // console.error("Harness bailout", this.constructor.name) |
| 205: | message = message || "" |
| 206: | //console.error("adding bailout message result") |
| 207: | this.results.add({bailout: message}) |
| 208: | // console.error(">>> results after bailout" , this.results) |
| 209: | this._bailedOut = true |
| 210: | this.emit("bailout", message) |
| 211: | this.output.end({bailout: message}) |
| 212: | } |
| 213: | |
| 214: | Harness.prototype.add = function (child) { |
| 215: | //console.error("adding child") |
| 216: | this._children.push(child) |
| 217: | if (!this._started) this.process() |
| 218: | } |
| 219: | |
| 220: | // the tearDown function is *always* guaranteed to happen. |
| 221: | // Even if there's a bailout. |
| 222: | Harness.prototype.tearDown = function (fn) { |
| 223: | this.on("end", fn) |
| 224: | } |
