[Pkg-javascript-commits] [node-tap] 01/19: Imported Upstream version 4.0.0

Jérémy Lal kapouer at moszumanska.debian.org
Sat Nov 12 01:03:55 UTC 2016


This is an automated email from the git hooks/post-receive script.

kapouer pushed a commit to branch master
in repository node-tap.

commit 18f66a55b46c6d0d3435f4a79ef800f0f13eff9c
Author: Jérémy Lal <kapouer at melix.org>
Date:   Sun Jan 3 17:57:50 2016 +0100

    Imported Upstream version 4.0.0
---
 .gitignore                                         |    3 +-
 .npmignore                                         |    0
 .travis.yml                                        |    6 +
 AUTHORS                                            |    1 +
 CHANGELOG.md                                       |   98 ++
 CONTRIBUTING.md                                    |   14 +
 LICENSE                                            |   32 +-
 README.md                                          |  851 +++++++++++--
 TODO.md                                            |    8 +
 bin/is-exe.js                                      |   23 +
 bin/mochatap.js                                    |   14 +
 bin/run.js                                         |  554 +++++++++
 bin/tap-http.js                                    |   19 -
 bin/tap-reader.js                                  |   33 -
 bin/tap.js                                         |  147 ---
 bin/usage.txt                                      |   93 ++
 coverage-example/lib/bar.js                        |   15 -
 coverage-example/lib/foo.js                        |   15 -
 coverage-example/test/bar.test.js                  |   20 -
 coverage-example/test/baz.test.js                  |   29 -
 coverage-example/test/foo.test.js                  |   20 -
 example/long-slow-many.js                          |   37 +
 example/test/test-example.js                       |  237 ----
 lib/assert.js                                      |  281 +++++
 lib/main.js                                        |   16 -
 lib/mocha.js                                       |   55 +
 lib/root.js                                        |   90 ++
 lib/stack.js                                       |  252 ++++
 lib/synonyms.js                                    |   92 ++
 lib/tap-assert.js                                  |  466 -------
 lib/tap-browser-harness.js                         |   63 -
 lib/tap-consumer.js                                |  246 ----
 lib/tap-cov-html.js                                |   78 --
 lib/tap-global-harness.js                          |   80 --
 lib/tap-harness.js                                 |  226 ----
 lib/tap-producer.js                                |  131 --
 lib/tap-results.js                                 |   72 --
 lib/tap-runner.js                                  |  502 --------
 lib/tap-test.js                                    |  110 --
 lib/test.js                                        | 1314 ++++++++++++++++++++
 package.json                                       |   71 +-
 scripts/generate-test-test.js                      |  119 ++
 test-disabled/bailout.js                           |   36 -
 test-disabled/foo.js                               |    1 -
 test-disabled/t.js                                 |   16 -
 test/asserts.js                                    |  241 ++++
 test/buffer_compare.js                             |   10 +-
 test/common.js                                     |  109 +-
 test/coverage-export.js                            |   80 ++
 test/debug-test.js                                 |   36 +-
 test/deep-strict.js                                |   72 +-
 test/deep.js                                       |   87 +-
 test/exit-code.js                                  |    2 +-
 test/expect-error-event.js                         |   22 +
 test/expose-gc-test.js                             |   53 +-
 test/fixtures/assert.js                            |  332 +++++
 test/fixtures/cat.js                               |    1 +
 test/fixtures/gc-script.js                         |    1 +
 test/fixtures/never-finish.js                      |    5 +
 test/fixtures/success.js                           |    2 +-
 test/fixtures/todo-skip-descriptions.js            |    4 -
 test/fixtures/trivial-success.js                   |    2 +-
 test/fixtures/using-require-hook.faux              |    1 +
 test/fixtures/using-require-hook.js                |   19 +
 test/independent-timeouts.js                       |   30 +-
 test/isolated-conf-test.js                         |   16 -
 test/meta-test.js                                  |   73 --
 test/nested-async.js                               |   60 +-
 test/nested-test.js                                |   37 +-
 test/non-enumerable-match.js                       |   14 +
 test/non-tap-output.js                             |   12 -
 test/only-non-tap-output.js                        |   50 +
 test/output-childtest-description.js               |   50 -
 test/require-hooks.js                              |   27 +
 test/result-trap.js                                |   20 -
 test/runner.js                                     |  510 ++++++++
 test/segv.js                                       |  107 +-
 test/simple-harness-test-with-plan.js              |   16 +-
 test/simple-harness-test.js                        |   14 +-
 test/test-assert-todo-skip.js                      |   13 -
 test/test-directives.js                            |   48 -
 test/test-skip.js                                  |    5 -
 test/test-test.js                                  |  164 ++-
 test/test.js                                       |  135 ++
 test/test/assert-todo-skip-bail.tap                |   22 +
 test/test/assert-todo-skip.js                      |   13 +
 test/test/assert-todo-skip.tap                     |   22 +
 test/test/async-bail.tap                           |   14 +
 test/test/async.js                                 |   13 +
 test/test/async.tap                                |   14 +
 test/test/bail-child-bail.tap                      |   10 +
 test/test/bail-child.js                            |   17 +
 test/test/bail-child.tap                           |   10 +
 test/test/bail-error-object-bail.tap               |    3 +
 test/test/bail-error-object.js                     |    8 +
 test/test/bail-error-object.tap                    |    3 +
 test/test/bail-fail-spawn-bail.tap                 |   21 +
 test/test/bail-fail-spawn.js                       |   13 +
 test/test/bail-fail-spawn.tap                      |   21 +
 test/test/bailout-bail.tap                         |   23 +
 test/test/bailout.js                               |   34 +
 test/test/bailout.tap                              |   26 +
 test/test/buffer-yaml-bail.tap                     |    7 +
 test/test/buffer-yaml.js                           |    5 +
 test/test/buffer-yaml.tap                          |    9 +
 test/test/console-log-bail.tap                     |   42 +
 test/test/console-log.js                           |   62 +
 test/test/console-log.tap                          |   42 +
 test/test/empty-bail.tap                           |    4 +
 test/test/empty.js                                 |    1 +
 test/test/empty.tap                                |    4 +
 test/test/end-end-bail.tap                         |   11 +
 test/test/end-end.js                               |   41 +
 test/test/end-end.tap                              |   48 +
 test/test/end-event-timing-bail.tap                |   20 +
 test/test/end-event-timing.js                      |   25 +
 test/test/end-event-timing.tap                     |   20 +
 test/test/end-exception-bail.tap                   |   13 +
 test/{end-exception/t.js => test/end-exception.js} |    2 +-
 test/test/end-exception.tap                        |   15 +
 test/test/equivalent-bail.tap                      |    9 +
 test/test/equivalent.js                            |   12 +
 test/test/equivalent.tap                           |   37 +
 test/test/exit-on-bailout-bail.tap                 |    9 +
 test/test/exit-on-bailout.js                       |   13 +
 test/test/exit-on-bailout.tap                      |    9 +
 test/test/mochalike-bail.tap                       |   52 +
 test/test/mochalike-ok-bail.tap                    |   46 +
 test/test/mochalike-ok.js                          |   49 +
 test/test/mochalike-ok.tap                         |   46 +
 test/test/mochalike.js                             |   71 ++
 test/test/mochalike.tap                            |  102 ++
 test/test/nesting-bail.tap                         |   19 +
 test/test/nesting.js                               |   44 +
 test/test/nesting.tap                              |   51 +
 test/test/non-tap-output-bail.tap                  |   14 +
 test/test/non-tap-output.js                        |   12 +
 test/test/non-tap-output.tap                       |   14 +
 test/test/not-ok-bail.tap                          |    7 +
 test/test/not-ok-nested-bail.tap                   |   12 +
 test/test/not-ok-nested.js                         |    9 +
 test/test/not-ok-nested.tap                        |   24 +
 test/test/not-ok.js                                |    3 +
 test/test/not-ok.tap                               |    9 +
 test/test/ok-bail.tap                              |   29 +
 test/test/ok-exit-fail-bail.tap                    |    5 +
 test/test/ok-exit-fail.js                          |    3 +
 test/test/ok-exit-fail.tap                         |    5 +
 test/test/ok.js                                    |   50 +
 test/test/ok.tap                                   |   29 +
 test/test/pending-handles-bail.tap                 |   10 +
 test/test/pending-handles.js                       |   10 +
 test/test/pending-handles.tap                      |   19 +
 test/test/plan-failures-bail.tap                   |   12 +
 test/test/plan-failures.js                         |   25 +
 test/test/plan-failures.tap                        |   89 ++
 test/test/plan-too-many-bail.tap                   |   15 +
 test/test/plan-too-many.js                         |   11 +
 test/test/plan-too-many.tap                        |   40 +
 test/test/pragma-bail.tap                          |   17 +
 test/test/pragma.js                                |   14 +
 test/test/pragma.tap                               |   20 +
 test/test/promise-return-bail.tap                  |   14 +
 test/test/promise-return-mocha-bail.tap            |   17 +
 test/test/promise-return-mocha.js                  |   39 +
 test/test/promise-return-mocha.tap                 |   37 +
 test/test/promise-return.js                        |   24 +
 test/test/promise-return.tap                       |   22 +
 test/test/root-teardown-bail.tap                   |   15 +
 test/test/root-teardown.js                         |   33 +
 test/test/root-teardown.tap                        |   15 +
 test/test/skip-all-bail.tap                        |    4 +
 test/test/skip-all.js                              |    1 +
 test/test/skip-all.tap                             |    4 +
 test/test/skip-bail.tap                            |    5 +
 test/test/skip.js                                  |    5 +
 test/test/skip.tap                                 |    5 +
 test/test/sparse-array-bail.tap                    |    7 +
 test/test/sparse-array.js                          |    4 +
 test/test/sparse-array.tap                         |   13 +
 test/test/spawn-bail.tap                           |   20 +
 test/test/spawn-empty-bail.tap                     |    8 +
 test/test/spawn-empty.js                           |    4 +
 test/test/spawn-empty.tap                          |    8 +
 test/test/spawn-stderr-bail.tap                    |   11 +
 test/test/spawn-stderr.js                          |    9 +
 test/test/spawn-stderr.tap                         |   11 +
 test/test/spawn.js                                 |   48 +
 test/test/spawn.tap                                |  106 ++
 test/test/throw-bail.tap                           |   17 +
 test/test/throw.js                                 |   37 +
 test/test/throw.tap                                |   48 +
 test/test/throws-and-plans-bail.tap                |   16 +
 test/test/throws-and-plans.js                      |   59 +
 test/test/throws-and-plans.tap                     |   96 ++
 test/test/throws-bail.tap                          |   11 +
 test/test/throws.js                                |   12 +
 test/test/throws.tap                               |   18 +
 test/test/timeout-bail.tap                         |   13 +
 test/test/timeout.js                               |   25 +
 test/test/timeout.tap                              |   42 +
 test/test/todo-bail.tap                            |   30 +
 test/test/todo.js                                  |   27 +
 test/test/todo.tap                                 |   30 +
 test/test/unfinished-bail.tap                      |   11 +
 test/test/unfinished.js                            |   38 +
 test/test/unfinished.tap                           |   61 +
 test/timeout.js                                    |   38 +-
 test/trivial-success.js                            |    2 +
 test/undefined_indented.js                         |   27 -
 test/valid-command.js                              |   37 -
 211 files changed, 8610 insertions(+), 3374 deletions(-)

diff --git a/.gitignore b/.gitignore
index 300a374..534108e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 node_modules/
 coverage/
-coverage-example/coverage/
+.nyc_output/
+nyc_output/
diff --git a/.npmignore b/.npmignore
deleted file mode 100644
index e69de29..0000000
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..7f22ad5
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,6 @@
+sudo: false
+language: node_js
+node_js:
+  - '0.10'
+  - '0.12'
+  - '4'
diff --git a/AUTHORS b/AUTHORS
index b7f6eb2..dffc361 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -9,3 +9,4 @@ Trent Mick <trentm at gmail.com>
 Corey Richardson <kb1pkl at aim.com>
 Raynos <raynos2 at gmail.com>
 Siddharth Mahendraker <siddharth_mahen at me.com>
+Ryan Graham <r.m.graham at gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..4b1fa57
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,98 @@
+## 4.0
+
+Raise an error if `t.end()` is explicitly called more than once.  This
+is a breaking change, because it can cause previously-passing tests to
+fail, if they did `t.end()` in multiple places.
+
+Support promises returned by mochalike functions.
+
+## 3.1
+
+Support sending coverage output to both codecov.io and coveralls.io.
+
+## 3.0
+
+Upgrade to nyc 5.  This means that `config.nyc.exclude` arrays in
+`package.json` now take globs instead of regular expressions.
+
+## 2.3
+
+Use the name of the function supplied to `t.test(fn)` as the test name
+if a string name is not provided.
+
+Better support for sparse arrays.
+
+## 2.2
+
+Add support for Codecov.io as well as Coveralls.io.
+
+Catch failures that come after an otherwise successful test set.
+
+Fix timing of `t.on('end')` so that event fires *before* the next
+child test is started, instead of immediately after it.
+
+`t.throws()` can now be supplied a regexp for the expected Error
+message.
+
+## 2.1
+
+Exit in failure on root test bailout.
+
+Support promises returned by `t.test(fn)` function.
+
+## 2.0
+
+Update matching behavior using [tmatch](http://npm.im/tmatch).  This
+is a breaking change to `t.match`, `t.similar`, `t.has`, etc., but
+brings them more in line with what people epirically seem to expect
+these functions to do.
+
+Deal with pending handles left open when a child process gets a
+`SIGTERM` on timeout.
+
+Remove domains in favor of more reliable and less invasive state and
+error-catching bookkeeping.
+
+## 1.4
+
+Add `t.contains()` alias for `t.match()`.
+
+Use `deeper` for deep object similarity testing.
+
+Treat unfinished tests as failures.
+
+Add support for pragmas in TAP output.
+
+## 1.3
+
+Bind all Test methods to object.
+
+Add `t.tearDown()`, `t.autoend()`, so that the root export is Just
+Another Test Object, which just happens to be piping to stdout.
+
+Support getting an error object in bailout()
+
+## 1.2
+
+Better support for exit status codes.
+
+## 1.1
+
+Add coverage using nyc.
+
+If a `COVERALLS_REPO_TOKEN` is provided, then run tests with coverage,
+and pipe to coveralls.
+
+## 1.0
+
+Complete rewrite from 0.x.
+
+Child tests implemented as nested TAP output, similar to Perl's `Test::More`.
+
+## 0.x
+
+The 0.x versions used a "flattened" approach to child tests, which
+requires some bookkeeping.
+
+It worked, mostly, but its primary success was inspiring
+[tape](http://npm.im/tape) and tap v1 and beyond.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..0c278d9
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,14 @@
+- Check the `TODO.md` file to see stuff that is likely to be accepted.
+- Every patch should have a new test that fails without the patch and
+  passes with the patch.
+- All tests should pass on Node 0.8 and above.  If some tests have to
+  be skipped for very old Node versions that's fine, but the
+  functionality should still work as intended.
+- Run `node scripts/generate-test-test.js test/test/*.js` to
+  re-generate the output tests whenever output is changed.  However,
+  when you do this, make sure to check the change to ensure that it's
+  what you intended, and that it didn't cause any other inadvertent
+  changes.
+- Prefer adding cases to an existing test rather than writing a new
+  one from scratch.  For example, add a new test in `test/test/*.js`
+  rather than create a new test that validates test output.
diff --git a/LICENSE b/LICENSE
index 05a4010..19129e3 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,23 +1,15 @@
-Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
-All rights reserved.
+The ISC License
 
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use,
-copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following
-conditions:
+Copyright (c) Isaac Z. Schlueter and Contributors
 
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
 
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/README.md b/README.md
index bed3955..8a6c61c 100644
--- a/README.md
+++ b/README.md
@@ -1,86 +1,777 @@
-This is a mix-and-match set of utilities that you can use to write test
-harnesses and frameworks that communicate with one another using the
-Test Anything Protocol.
-
-If you don't yet know what TAP is, [you better ask
-somebody](http://testanything.org/).
-
-Default Usage:
-
-1. Make a directory.  Maybe call it 'test'.  That'd be nice and obvious.
-2. Put a bunch of test scripts in there.  If they're node programs, then
-   they should be ".js".  Anything else is assumed to be some kind of shell
-   script, which should have a shebang line.
-3. `npm install tap`
-4. Update package.json scripts.test to include `tap ./test` [example
-   gist](https://gist.github.com/4469613)
-5. `npm test`
-
-The output will be TAP-compliant.
-
-For extra special bonus points, you can do something like this:
-
-    var test = require("tap").test
-    test("make sure the thingie is a thing", function (t) {
-      t.equal(thingie, "thing", "thingie should be thing")
-      t.deepEqual(array, ["foo", "bar"], "array has foo and bar elements")
-      t.strictDeepEqual(object, {foo: 42, bar: "thingie"}, "object has foo (Number) and bar (String) property")
-      t.type(thingie, "string", "type of thingie is string")
-      t.ok(true, "this is always true")
-      t.notOk(false, "this is never true")
-      t.test("a child test", function (t) {
-        t.equal(this, superEasy, "right!?")
-        t.similar(7, 2, "ever notice 7 is kinda like 2?", {todo: true})
-        t.test("so skippable", {skip: true}, function (t) {
-          t.plan(1) // only one test in this block
-          t.ok(true, "but when the flag changes, it'll pass")
-          // no need to end, since we had a plan.
-        })
-        t.end()
-      })
-      t.ok(99, "can also skip individual assertions", {skip: true})
-      // end lets it know it's over.
-      t.end()
-    })
-    test("another one", function (t) {
-      t.plan(1)
-      t.ok(true, "It's ok to plan, and also end.  Watch.")
-      t.end() // but it must match the plan!
+# node-tap
+
+A <abbr title="Test Anything Protocol">TAP</abbr> test framework for
+Node.js.
+
+It includes a command line test runner for consuming TAP-generating
+test scripts, and a JavaScript framework for writing such scripts.
+
+Built-in support for code coverage (including instrumenting child
+processes).  Coverage is printed to the command line in a terse table
+by default, but tap can also open up your web browser to a pretty
+report if you add `--coverage-report=lcov` to the command.
+
+Works with all exception-throwing assertion libraries (chai, should,
+node's built-in `require('assert')`, or just throwing yourself) but
+also has a [huge library of built-in assertions](#asserts) that you
+can use if you want to have each one reported as successes.
+
+Outputs in a wide variety of formats using the
+[tap-mocha-reporter](http://npm.im/tap-mocha-reporter) module.  (That
+is, you can get spec output by doing `-Rspec`.  The default output is
+called 'classic', based on tap 0.x's output, but with color and timing
+info.)
+
+[![Build Status](https://travis-ci.org/isaacs/node-tap.svg?branch=master)](https://travis-ci.org/isaacs/node-tap) [![Coverage Status](https://coveralls.io/repos/isaacs/node-tap/badge.svg?branch=master)](https://coveralls.io/r/isaacs/node-tap?branch=master)
+
+## USAGE
+
+Write your tests in JavaScript
+
+```javascript
+var tap = require('tap')
+
+// you can test stuff just using the top level object.
+// no suites or subtests required.
+
+tap.equal(1, 1, 'check if numbers still work')
+tap.notEqual(1, 2, '1 should not equal 2')
+
+// also you can group things into sub-tests.
+// Sub-tests will be run in sequential order always,
+// so they're great for async things.
+
+tap.test('first stuff', function (t) {
+  t.ok(true, 'true is ok')
+  t.similar({a: [1,2,3]}, {a: [1,2,3]})
+  // call t.end() when you're done
+  t.end()
+})
+
+// If you have a bunch of setup stuff that MUST work or else
+// the rest of the tests are not worth running, then you can
+// pass `{ bail: true }` to make it bail out on failure.
+
+tap.test('must succeed or all is lost', { bail: true }, function (t) {
+  db = new DataBorscht()
+  t.ok(db, 'borscht setup must succeed')
+  t.end()
+})
+
+// You can also bail out based on specific conditions, or with a
+// different error message of your choosing.
+tap.test('must mostly succeed or all is lost', function (t) {
+  db = new DataBorscht()
+
+  t.ok(db, 'borscht setup')
+  if (!db) {
+    t.bailout('the borscht is lost.  I cannot continue.')
+    return
+  }
+
+  t.ok(db.connection, 'db must have connection')
+  t.ok(db.username, 'db must have username')
+  t.equal(db.color, 'red', 'borscht should be red')
+  if (!t.passing())
+    t.bailout('something weird with the data borscht.')
+
+  t.end()
+})
+
+// you can specify a 'plan' if you know how many
+// tests there will be in advance. Handy when
+// order is irrelevant and things happen in parallel.
+
+// Note that the function name is used if no name is provided!
+tap.test(function planned (t) {
+  t.plan(2)
+  setTimeout(function () {
+    t.ok(true, 'a timeout')
+  })
+  setTimeout(function () {
+    t.ok(true, 'b timeout')
+  })
+})
+
+// you can do `var test = require('tap').test` if you like
+// it's pre-bound to the root tap object.
+
+var test = require('tap').test
+
+// subtests can have subtests
+test(function parent (t) {
+  t.test(function child (tt) {
+    tt.throws(function () {
+      throw new Error('fooblz')
+    }, {
+      message: 'fooblz'
+    }, 'throw a fooblz')
+
+    // throws also uses function name if no name provided
+    tt.throws(function throw_whatever () { throw 1 })
+
+    tt.end()
+  })
+
+  t.end()
+})
+
+// thrown errors just fail the current test, so you can
+// also use your own assert library if you like.
+// Of course, this means it won't be able to print out the
+// number of passing asserts, since passes will be silent.
+
+test('my favorite assert lib', function (t) {
+  var assert = require('assert')
+  assert.ok(true, 'true is ok')
+  assert.equal(1, 1, 'math works')
+
+  // Since it can't read the plan, using a custom assert lib
+  // means that you MUST use t.end()
+  t.end()
+})
+
+// You can mark tests as 'todo' either using a conf object,
+// or simply by omitting the callback.
+test('solve halting problem')
+test('prove p=np', { todo: true }, function (t) {
+  // i guess stuff goes here
+  t.fail('traveling salesmen must pack their own bags')
+  t.end()
+})
+
+// Prefer mocha/rspec/lab style global objects?
+// Got you covered.  This is a little experimental,
+// patches definitely welcome.
+tap.mochaGlobals()
+describe('suite ride bro', function () {
+  it('should have a wheel', function () {
+    assert.ok(thingie.wheel, 'wheel')
+  })
+  it('can happen async', function (done) {
+    setTimeout(function () {
+      assert.ok('ok')
+      done()
     })
+  })
+})
+
+// Read on for a complete list of asserts, methods, etc.
+```
+
+You can run tests using the `tap` executable.  Put this in your
+package.json file:
+
+```json
+{
+  "scripts": {
+    "test": "tap test/*.js"
+  }
+}
+```
+
+and then you can run `npm test` to run your test scripts.
+
+Command line behavior and flags:
+
+```
+$ tap -h
+Usage:
+  tap [options] <files>
+
+Executes all the files and interprets their output as TAP
+formatted test result data.
+
+To parse TAP data from stdin, specify "-" as a filename.
+
+Short options are parsed gnu-style, so for example '-bCRspec' would be
+equivalent to '--bail --no-color --reporter=spec'
+
+Options:
+
+  -c --color                  Use colors (Default for TTY)
+
+  -C --no-color               Do not use colors (Default for non-TTY)
+
+  -b --bail                   Bail out on first failure
+
+  -B --no-bail                Do not bail out on first failure (Default)
+
+  -R<type> --reporter=<type>  Use the specified reporter.  Defaults to
+                              'classic' when colors are in use, or 'tap'
+                              when colors are disabled.
+
+                              Available reporters:
+                              classic doc dot dump html htmlcov json
+                              jsoncov jsonstream landing list markdown
+                              min nyan progress silent spec tap xunit
+
+  -s<file> --save=<file>      If <file> exists, then it should be a line-
+                              delimited list of test files to run.  If
+                              <file> is not present, then all command-line
+                              positional arguments are run.
+
+                              After the set of test files are run, any
+                              failed test files are written back to the
+                              save file.
+
+                              This way, repeated runs with -s<file> will
+                              re-run failures until all the failures are
+                              passing, and then once again run all tests.
+
+                              It's a good idea to .gitignore the file
+                              used for this purpose, as it will churn a
+                              lot.
+
+  --coverage --cov            Capture coverage information using 'nyc'
+
+                              If a COVERALLS_REPO_TOKEN environment
+                              variable is set, then coverage is
+                              captured by default and sent to the
+                              coveralls.io service. If a CODECOV_TOKEN
+                              environment variable is set, then coverage is
+                              captured by default and sent to the
+                              codecov.io service.
+
+  --no-coverage --no-cov      Do not capture coverage information.
+                              Note that if nyc is already loaded, then
+                              the coverage info will still be captured.
+
+  --coverage-report=<type>    Output coverage information using the
+                              specified istanbul/nyc reporter type.
+
+                              Default is 'text' when running on the
+                              command line, or 'text-lcov' when piping
+                              to coveralls or codecov.
+
+                              If 'lcov' is used, then the report will
+                              be opened in a web browser after running.
+
+                              This can be run on its own at any time
+                              after a test run that included coverage.
+
+  -t<n> --timeout=<n>         Time out test files after <n> seconds.
+                              Defaults to 30, or the value of the
+                              TAP_TIMEOUT environment variable.
+
+  -h --help                   print this thing you're looking at
+
+  -v --version                show the version of this program
+
+  -gc --expose-gc             Expose the gc() function to Node tests
+
+  --debug                     Run JavaScript tests with node --debug
+
+  --debug-brk                 Run JavaScript tests with node --debug-brk
+
+  --harmony                   Enable all Harmony flags in JavaScript tests
+
+  --strict                    Run JS tests in 'use strict' mode
+
+  --                          Stop parsing flags, and treat any additional
+                              command line arguments as filenames.
+```
+
+## Coverage
+
+This module uses [nyc](http://npm.im/nyc) to track code coverage, even
+across subprocess boundaries.  It is included by default, and there's
+nothing you need to do but enable it.  Adding coverage *will* make
+your tests run slightly slower, but that's to be expected.
+
+To generate coverage information, run your tests with the `--cov`
+argument.
+
+To specify a report format, you can use `--coverage-report=<type>`.
+The default type is `text`, which produces a pretty text-only table on
+the terminal.  If you specify `--coverage-report=lcov`, then tap will
+attempt to open a web browser to view the report after the test run.
+
+If you use this a lot, you may want to add `coverage` and
+`.nyc_output` to your `.gitignore` and/or `.npmignore` files.
+
+### Travis-CI and Coveralls.io/CodeCov.io Integration
+
+You can very easily take advantage of continuous test coverage reports
+by using [Travis-CI](https://travis-ci.org) and
+[Coveralls](https://coveralls.io).
+
+1. Enable Travis-CI by signing up, enabling tests on your repo, and
+   adding a `.travis.yml` file to your repo.  You can use [this
+   module's .travis.yml file as an
+   example](https://github.com/isaacs/node-tap/blob/master/.travis.yml)
+2. Enable Coveralls.io or CodeCov.io by signing up, and adding the
+   repo.  Note the repo API token.
+3. Back at Travis-CI, add a private environment variable.  The name of
+   the environment variable is `COVERALLS_REPO_TOKEN` for Coveralls,
+   or `CODECOV_TOKEN` for CodeCov.io, and the value is the token you
+   got from Coveralls or CodeCov.
+4. When that token is set in the environment variable, `tap` will
+   automatically generate coverage information and send it to the
+   appropriate place.
+
+## API
+
+### tap = require('tap')
+
+The root `tap` object is an instance of the Test class with a few
+slight modifications.
+
+1. The `tearDown()`, `plan()`, and `test()` methods are pre-bound onto
+   the root object, so that you don't have to call them as methods.
+2. By default, it pipes to stdout, so running a test directly just
+   dumps the TAP data for inspection.  (You can of course
+   `tap.unpipe(process.stdout)` if you want to direct it elsewhere.)
+3. Various other things are hung onto it for convenience, since it is
+   the main package export.
+4. The test ends automatically when `process.on('exit')` fires, so
+   there is no need to call `tap.end()` explicitly.
+5. Adding a `tearDown` function triggers `autoend` behavior.
+   Otherwise, the `end` would potentially never arrive, if for example
+   `tearDown` is used to close a server or cancel some long-running
+   process, because `process.on('exit')` would never fire of its own
+   accord.
+
+### tap.synonyms
+
+A list of all of the canonical assert methods and their synonyms.
+
+### tap.mochaGlobals()
+
+Method that injects `describe()` and `it()` into the global
+environment for mocha-like BDD style test definition.
+
+This feature is incomplete, experimental, and may change drastically
+in the future.  Feedback is welcome.
+
+### tap.Test
+
+The `Test` class is the main thing you'll be touching when you use
+this module.
+
+The most common way to instantiate a `Test` object by calling the
+`test` method on the root or any other `Test` object.  The callback
+passed to `test(name, fn)` will receive a child `Test` object as its
+argument.
+
+A `Test` object is a Readable Stream.  Child tests automatically send
+their data to their parent, and the root `require('tap')` object pipes
+to stdout by default.  However, you can instantiate a `Test` object
+and then pipe it wherever you like.  The only limit is your imagination.
+
+#### t.test([name], [options], [function])
+
+Create a subtest.
+
+If the function is omitted, then it will be marked as a "todo" or
+"pending" test.
+
+If the function has a name, and no name is provided, then the function
+name will be used as the test name.  If no test name is provided, then
+the name will be `(unnamed test)`.
+
+The function gets a Test object as its only argument.  From there, you
+can call the `t.end()` method on that object to end the test, or use
+the `t.plan()` method to specify how many child tests or asserts the
+test will have.
+
+If the function returns a `Promise` object (that is, an object with a
+`then` method), then when the promise is rejected or fulfilled, the
+test will be either ended or failed.
+
+If the function is not provided, then this will be treated as a `todo`
+test.
+
+The options object is the same as would be passed to any assert, with
+two additional fields that are only relevant for child tests:
+
+* `timeout`: The number of ms to allow the test to run.
+* `bail`: Set to `true` to bail out on the first test failure.
+* `autoend`: Automatically `end()` the test on the next turn of the
+  event loop after its internal queue is drained.
+
+#### t.tearDown(function)
+
+Run the supplied function when `t.end()` is called, or when the `plan`
+is met.
+
+Note that when called on the root `tap` export, this also triggers
+`autoend` behavior.
+
+#### t.autoend()
+
+Automatically end the test as soon as there is nothing pending in its
+queue.
+
+The automatic end is deferred with a `setTimeout`, and any new action
+will cancel and re-schedule the timer.  Nonetheless, calling this
+method means that any slow asynchronous behavior may be lost, if it
+comes after the `end()` is auto-triggered.
+
+This behavior is triggered on the root `tap` object when
+`tap.tearDown()` is called.
+
+#### t.plan(number)
+
+Specify that a given number of tests are going to be run.
+
+This may only be called *before* running any asserts or child tests.
+
+#### t.end()
+
+Call when tests are done running.  This is not necessary if `t.plan()`
+was used, or if the test function returns a Promise.
+
+If you call `t.end()` explicitly more than once, an error will be
+raised.
+
+#### t.bailout([reason])
 
-Node-tap is actually a collection of several modules, any of which may be
-mixed and matched however you please.
-
-If you don't like this test framework, and think you can do much much
-better, *I strongly encourage you to do so!*  If you use this library,
-however, at least to output TAP-compliant results when `process.env.TAP`
-is set, then the data coming out of your framework will be much more
-consumable by machines.
-
-You can also use this to build programs that *consume* the TAP data, so
-this is very useful for CI systems and such.
-
-* tap-assert: A collection of assert functions that return TAP result
-  objects.
-* tap-consumer: A stream interface for consuming TAP data.
-* tap-producer: A class that produces a TAP stream by taking in result
-  objects.
-* tap-results: A class for keeping track of TAP result objects as they
-  pass by, counting up skips, passes, fails, and so on.
-* tap-runner: A program that runs through a directory running all the
-  tests in it.  (Tests which may or may not be TAP-outputting tests.  But
-  it's better if they are.)
-* tap-test: A class for actually running tests.
-* tap-harness: A class that runs tests.  (Tests are also Harnesses,
-  which is how sub-tests run.)
-* tap-global-harness: A default harness that provides the top-level
-  support for running TAP tests.
-
-## Experimental Code Coverage with runforcover & bunker:
+Pull the proverbial ejector seat.
+
+Use this when things are severely broken, and cannot be reasonably
+handled.  Immediately terminates the entire test run.
+
+#### t.passing()
+
+Return true if everything so far is ok.
+
+Note that all assert methods also return `true` if they pass.
+
+#### t.comment(message)
+
+Print the supplied message as a TAP comment.
+
+Note that you can always use `console.error()` for debugging (or
+`console.log()` as long as the message doesn't look like TAP formatted
+data).
+
+#### t.fail(message, extra)
+
+Emit a failing test point.  This method, and `pass()`, are the basic
+building blocks of all fancier assertions.
+
+#### t.pass(message)
+
+Emit a passing test point.  This method, and `fail()`, are the basic
+building blocks of all fancier assertions.
+
+#### t.pragma(set)
+
+Sets a `pragma` switch for a set of boolean keys in the argument.
+
+The only pragma currently supported by the TAP parser is `strict`,
+which tells the parser to treat non-TAP output as a failure.
+
+Example:
 
 ```
-TAP_COV=1 tap ./test [--cover=./lib,foo.js] [--coverage-dir=./coverage]
+var t = require('tap')
+console.log('this non-TAP output is ok')
+t.pragma({ strict: true })
+console.log('but this will cause a failure')
 ```
 
-This feature is experimental, and will most likely change somewhat
-before being finalized.  Feedback welcome.
+### Asserts
+
+The `Test` object has a collection of assertion methods, many of which
+are given several synonyms for compatibility with other test runners
+and the vagaries of human expectations and spelling.  When a synonym
+is multi-word in `camelCase` the corresponding lower case and
+`snake_case` versions are also created as synonyms.
+
+All assertion methods take optional `message` and `extra` arguments as
+the last two params.  The `message` is the name of the test.  The
+`extra` argument can contain any arbitrary data about the test, but
+the following fields are "special".
+
+* `todo` Set to boolean `true` or a String to mark this as pending
+* `skip` Set to boolean `true` or a String to mark this as skipped
+* `at` Generated by the framework.  The location where the assertion
+  was called.  Do not set this field.
+* `stack` Generated by the framework.  The stack trace to the point
+  where the assertion was called.  Do not set this field.
+
+#### t.ok(obj, message, extra)
+
+Verifies that the object is truthy.
+
+Synonyms: `t.true`, `t.assert`
+
+#### t.notOk(obj, message, extra)
+
+Verifies that the object is not truthy.
+
+Synonyms: `t.false`, `t.assertNot`
+
+#### t.error(obj, message, extra)
+
+If the object is an error, then the assertion fails.
+
+Note: if an error is encountered unexpectedly, it's often better to
+simply throw it.  The Test object will handle this as a failure.
+
+Synonyms: `t.ifErr`, `t.ifError`
+
+#### t.throws(fn, [expectedError], message, extra)
+
+Expect the function to throw an error.  If an expected error is
+provided, then also verify that the thrown error matches the expected
+error.
+
+If the function has a name, and the message is not provided, then the
+function name will be used as the message.
+
+If the function is not provided, then this will be treated as a `todo`
+test.
+
+Caveat: if you pass a `extra` object to t.throws, then you MUST also
+pass in an expected error, or else it will read the diag object as the
+expected error, and get upset when your thrown error doesn't match
+`{skip:true}` or whatever.
+
+For example, this will not work as expected:
+
+```javascript
+t.throws(function() {throw new Error('x')}, { skip: true })
+```
+
+But this is fine:
+
+```javascript
+// note the empty 'expected error' object.
+// since it has no fields, it'll only verify that the thrown thing is
+// an object, not the value of any properties
+t.throws(function() {throw new Error('x')}, {}, { skip: true })
+```
+
+The expected error is tested against the throw error using `t.match`,
+so regular expressions and the like are fine.  If the expected error
+is an `Error` object, then the `stack` field is ignored, since that
+will generally never match.
+
+Synonyms: `t.throw`
+
+#### t.doesNotThrow(fn, message, extra)
+
+Verify that the provided function does not throw.
+
+If the function has a name, and the message is not provided, then the
+function name will be used as the message.
+
+If the function is not provided, then this will be treated as a `todo`
+test.
+
+Note: if an error is encountered unexpectedly, it's often better to
+simply throw it.  The Test object will handle this as a failure.
+
+Synonyms: `t.notThrow`
+
+#### t.equal(found, wanted, message, extra)
+
+Verify that the object found is exactly the same (that is, `===`) to
+the object that is wanted.
+
+Synonyms: `t.equals`, `t.isEqual`, `t.is`, `t.strictEqual`,
+`t.strictEquals`, `t.strictIs`, `t.isStrict`, `t.isStrictly`
+
+#### t.notEqual(found, notWanted, message, extra)
+
+Inverse of `t.equal()`.
+
+Verify that the object found is not exactly the same (that is, `!==`) as
+the object that is wanted.
+
+Synonyms: `t.inequal`, `t.notEqual`, `t.notEquals`,
+`t.notStrictEqual`, `t.notStrictEquals`, `t.isNotEqual`, `t.isNot`,
+`t.doesNotEqual`, `t.isInequal`
+
+#### t.same(found, wanted, message, extra)
+
+Verify that the found object is deeply equivalent to the wanted
+object.  Use non-strict equality for scalars (ie, `==`).
+
+Synonyms: `t.equivalent`, `t.looseEqual`, `t.looseEquals`,
+`t.deepEqual`, `t.deepEquals`, `t.isLoose`, `t.looseIs`
+
+#### t.notSame(found, notWanted, message, extra)
+
+Inverse of `t.same()`.
+
+Verify that the found object is not deeply equivalent to the
+unwanted object.  Uses non-strict inequality (ie, `!=`) for scalars.
+
+Synonyms: `t.inequivalent`, `t.looseInequal`, `t.notDeep`,
+`t.deepInequal`, `t.notLoose`, `t.looseNot`
+
+#### t.strictSame(found, wanted, message, extra)
+
+Strict version of `t.same()`.
+
+Verify that the found object is deeply equivalent to the wanted
+object.  Use strict equality for scalars (ie, `===`).
+
+Synonyms: `t.strictEquivalent`, `t.strictDeepEqual`, `t.sameStrict`,
+`t.deepIs`, `t.isDeeply`, `t.isDeep`, `t.strictDeepEquals`
+
+#### t.strictNotSame(found, notWanted, message, extra)
+
+Inverse of `t.strictSame()`.
+
+Verify that the found object is not deeply equivalent to the unwanted
+object.  Use strict equality for scalars (ie, `===`).
+
+Synonyms: `t.strictInequivalent`, `t.strictDeepInequal`,
+`t.notSameStrict`, `t.deepNot`, `t.notDeeply`, `t.strictDeepInequals`,
+`t.notStrictSame`
+
+#### t.match(found, pattern, message, extra)
+
+Verify that the found object matches the pattern provided.
+
+If pattern is a regular expression, and found is a string, then verify
+that the string matches the pattern.
+
+If the pattern is a string, and found is a string, then verify that
+the pattern occurs within the string somewhere.
+
+If pattern is an object, then verify that all of the (enumerable)
+fields in the pattern match the corresponding fields in the object
+using this same algorithm.  For example, the pattern `{x:/a[sdf]{3}/}`
+would successfully match `{x:'asdf',y:'z'}`.
+
+This is useful when you want to verify that an object has a certain
+set of required fields, but additional fields are ok.
+
+Synonyms: `t.has`, `t.hasFields`, `t.matches`, `t.similar`, `t.like`,
+`t.isLike`, `t.includes`, `t.include`, `t.contains`
+
+#### t.notMatch(found, pattern, message, extra)
+
+Interse of `match()`
+
+Verify that the found object does not match the pattern provided.
+
+Synonyms: `t.dissimilar`, `t.unsimilar`, `t.notSimilar`, `t.unlike`,
+`t.isUnlike`, `t.notLike`, `t.isNotLike`, `t.doesNotHave`,
+`t.isNotSimilar`, `t.isDissimilar`
+
+#### t.type(object, type, message, extra)
+
+Verify that the object is of the type provided.
+
+Type can be a string that matches the `typeof` value of the object, or
+the string name of any constructor in the object's prototype chain, or
+a constructor function in the object's prototype chain.
+
+For example, all the following will pass:
+
+```javascript
+t.type(new Date(), 'object')
+t.type(new Date(), 'Date')
+t.type(new Date(), Date)
+```
+
+Synonyms: `t.isa`, `t.isA`
+
+### Advanced Usage
+
+These methods are primarily for internal use, but can be handy in some
+unusual situations.  If you find yourself using them frequently, you
+*may* be Doing It Wrong.  However, if you find them useful, you should
+feel perfectly comfortable using them.
+
+Please [let us know](https://github.com/isaacs/node-tap/issues) if you
+frequently encounter situations requiring advanced usage, because this
+may indicate a shortcoming in the "non-advanced" API.
+
+#### t.stdin()
+
+Parse standard input as if it was a child test named `/dev/stdin`.
+
+This is primarily for use in the test runner, so that you can do
+`some-tap-emitting-program | tap other-file.js - -Rnyan`.
+
+#### t.spawn(command, arguments, [options], [name], [extra])
+
+Sometimes, instead of running a child test directly inline, you might
+want to run a TAP producting test as a child process, and treat its
+standard output as the TAP stream.
+
+That's what this method does.
+
+It is primarily used by the executable runner, to run all of the
+filename arguments provided on the command line.
+
+The `options` object is passed to `child_process.spawn`, and can
+contain stuff like stdio directives and environment vars.
+
+The `extra` arg is the same that would be passed to any assertion or
+child test, with the addition of the following fields:
+
+* `bail`: Set to `true` to bail out on the first failure.  This is
+  done by checking the output and then forcibly killing the process,
+  but also sets the `TAP_BAIL` environment variable, which node-tap
+  uses to set this field internally as well.
+* `timeout`: The number of ms to allow the child process to continue.
+  If it goes beyond this time, the child process will be forcibly
+  killed.
+
+#### t.addAssert(name, length, fn)
+
+This is used for creating assertion methods on the `Test` class.
+
+It's a little bit advanced, but it's also super handy sometimes.  All
+of the assert methods below are created using this function, and it
+can be used to create application-specific assertions in your tests.
+
+The name is the method name that will be created.  The length is the
+number of arguments the assertion operates on.  (The `message` and
+`extra` arguments will alwasy be appended to the end.)
+
+For example, you could have a file at `test/setup.js` that does the
+following:
+
+```javascript
+var tap = require('tap')
+
+// convenience
+if (module === require.main) {
+  tap.pass('ok')
+  return
+}
+
+// Add an assertion that a string is in Title Case
+// It takes one argument (the string to be tested)
+tap.Test.prototype.addAssert('titleCase', 1, function (str, message, extra) {
+  message = message || 'should be in Title Case'
+  // the string in Title Case
+  // A fancier implementation would avoid capitalizing little words
+  // to get `Silence of the Lambs` instead of `Silence Of The Lambs`
+  // But whatever, it's just an example.
+  var tc = str.toLowerCase().replace(/\b./, function (match) {
+    return match.toUpperCase()
+  })
+
+  // should always return another assert call, or
+  // this.pass(message) or this.fail(message, extra)
+  return this.equal(str, tc, message, extra)
+})
+```
+
+Then in your individual tests, you'd do this:
+
+```javascript
+require('./setup.js') // adds the assert
+var tap = require('tap')
+tap.titleCase('This Passes')
+tap.titleCase('however, tHis tOTaLLy faILS')
+```
+
+#### t.endAll()
+
+Call the `end()` method on all child tests, and then on this one.
+
+#### t.current()
+
+Return the currently active test.
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..a0ec681
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,8 @@
+- fix the markdown reporter
+- make it faster (seems like mostly this is just node spawn() overhead)
+- read a config file at ~/.taprc for setting default colors,
+  reporters, etc
+- make colors (and diff colors) configurable
+- tests for reporter output
+- split lib/stack.js out into a separate module
+- Add more of the mocha globals when you call tap.mochaGlobals()
diff --git a/bin/is-exe.js b/bin/is-exe.js
new file mode 100644
index 0000000..4f2d5b3
--- /dev/null
+++ b/bin/is-exe.js
@@ -0,0 +1,23 @@
+if (process.platform === 'win32') {
+  // On windows, there is no good way to check that a file is executable
+  module.exports = function isExe () {
+    return true
+  }
+} else {
+  module.exports = function isExe (stat) {
+    var mod = stat.mode
+    var uid = stat.uid
+    var gid = stat.gid
+    var u = parseInt('100', 8)
+    var g = parseInt('010', 8)
+    var o = parseInt('001', 8)
+    var ug = u | g
+
+    var ret = (mod & o) ||
+      (mod & g) && process.getgid && gid === process.getgid() ||
+      (mod & u) && process.getuid && uid === process.getuid() ||
+      (mod & ug) && process.getuid && process.getuid() === 0
+
+    return ret
+  }
+}
diff --git a/bin/mochatap.js b/bin/mochatap.js
new file mode 100755
index 0000000..09a921e
--- /dev/null
+++ b/bin/mochatap.js
@@ -0,0 +1,14 @@
+#!/usr/bin/env node
+var tap = require('../lib/root.js')
+var args = process.argv.slice(2)
+
+if (args.length === 1) {
+  var path = require('path')
+  var file = path.resolve(args[0])
+  tap.mochaGlobals()
+  require(file)
+} else {
+  for (var i = 0; i < args.length; i++) {
+    tap.spawn(process.execPath, [__filename, args[i]])
+  }
+}
diff --git a/bin/run.js b/bin/run.js
new file mode 100755
index 0000000..7cc3c1b
--- /dev/null
+++ b/bin/run.js
@@ -0,0 +1,554 @@
+#!/usr/bin/env node
+var node = process.execPath
+var fs = require('fs')
+var spawn = require('child_process').spawn
+var fg = require('foreground-child')
+var signalExit = require('signal-exit')
+var opener = require('opener')
+var supportsColor = require('supports-color')
+var nycBin = require.resolve('nyc/bin/nyc.js')
+var glob = require('glob')
+var isExe = require('./is-exe.js')
+
+var coverageServiceTest = process.env.COVERAGE_SERVICE_TEST === 'true'
+
+// console.error(process.argv.join(' '))
+// console.error('CST=%j', process.env.COVERAGE_SERVICE_TEST)
+// console.error('CRT=%j', process.env.COVERALLS_REPO_TOKEN)
+// console.error('CCT=%j', process.env.CODECOV_TOKEN)
+if (coverageServiceTest) {
+  console.log('COVERAGE_SERVICE_TEST')
+}
+
+// Add new coverage services here.
+// it'll check for the environ named and pipe appropriately.
+var coverageServices = [
+  {
+    env: 'COVERALLS_REPO_TOKEN',
+    bin: require.resolve('coveralls/bin/coveralls.js'),
+    name: 'Coveralls'
+  },
+  {
+    env: 'CODECOV_TOKEN',
+    bin: require.resolve('codecov.io/bin/codecov.io.js'),
+    name: 'Codecov'
+  }
+]
+
+main()
+
+function main () {
+  var args = process.argv.slice(2)
+
+  if (!args.length && process.stdin.isTTY) {
+    console.error(usage())
+    process.exit(1)
+  }
+
+  var options = parseArgs(args)
+  if (!options) {
+    return
+  }
+
+  process.stdout.on('error', function (er) {
+    if (er.code === 'EPIPE') {
+      process.exit()
+    } else {
+      throw er
+    }
+  })
+
+  // By definition, the next block cannot be covered, because
+  // they are only relevant when coverage is turned off.
+
+  /* istanbul ignore if */
+  if (options.coverage && !global.__coverage__) {
+    respawnWithCoverage(options)
+    return
+  }
+
+  /* istanbul ignore if */
+  if (options.coverageReport &&
+    (!global.__coverage__ || coverageServiceTest) &&
+    options.files.length === 0) {
+    runCoverageReport(options)
+    return
+  }
+
+  setupTapEnv(options)
+
+  options.files = globFiles(options.files)
+
+  if (options.files.length === 0) {
+    console.error('Reading TAP data from stdin (use "-" argument to suppress)')
+    options.files.push('-')
+  }
+
+  if (options.files.length === 1 && options.files[0] === '-') {
+    stdinOnly(options)
+    return
+  }
+
+  runTests(options)
+}
+
+function parseArgs (args) {
+  var options = {}
+
+  options.nodeArgs = []
+  options.timeout = process.env.TAP_TIMEOUT || 30
+  // coverage tools run slow.
+  if (global.__coverage__) {
+    options.timeout = 240
+  }
+
+  options.color = supportsColor
+  if (process.env.TAP_COLORS !== undefined) {
+    options.color = !!(+process.env.TAP_COLORS)
+  }
+  options.reporter = null
+  options.files = []
+  options.bail = false
+  options.saveFile = null
+
+  var singleFlags = {
+    b: 'bail',
+    B: 'no-bail',
+    c: 'color',
+    C: 'no-color',
+    h: 'help',
+    '?': 'help',
+    v: 'version'
+  }
+  var singleOpts = {
+    R: 'reporter',
+    t: 'timeout',
+    s: 'save'
+  }
+
+  // If we're running under Travis-CI with a Coveralls.io token,
+  // then it's a safe bet that we ought to output coverage.
+  options.pipeToService = false
+  for (var i = 0; i < coverageServices.length && !options.pipeToService; i++) {
+    if (process.env[coverageServices[i].env]) {
+      options.pipeToService = true
+    }
+  }
+  options.coverage = options.pipeToService
+
+  options.coverageReport = null
+
+  for (i = 0; i < args.length; i++) {
+    var arg = args[i]
+    if (arg.charAt(0) !== '-' || arg === '-') {
+      options.files.push(arg)
+      continue
+    }
+
+    // short-flags
+    if (arg.charAt(1) !== '-' && arg !== '-gc') {
+      var expand = []
+      for (var f = 1; f < arg.length; f++) {
+        var fc = arg.charAt(f)
+        var sf = singleFlags[fc]
+        var so = singleOpts[fc]
+        if (sf) {
+          expand.push('--' + sf)
+        } else if (so) {
+          var soval = arg.slice(f + 1)
+          if (soval.charAt(0) !== '=') {
+            soval = '=' + soval
+          }
+          expand.push('--' + so + soval)
+          f = arg.length
+        } else if (arg !== '-' + fc) {
+          expand.push('-' + fc)
+        }
+      }
+      if (expand.length) {
+        args.splice.apply(args, [i, 1].concat(expand))
+        i--
+        continue
+      }
+    }
+
+    var key = arg
+    var val = null
+    if (key.match(/^--/) && arg.indexOf('=') !== -1) {
+      var kv = arg.split('=')
+      key = kv.shift()
+      val = kv.join('=')
+    }
+
+    switch (key) {
+      case '--help':
+        console.log(usage())
+        return null
+
+      case '--version':
+        console.log(require('../package.json').version)
+        return null
+
+      case '--__coverage__':
+        // NYC will not wrap a module in node_modules.
+        // So, we need to tell the child proc when it's been added.
+        global.__coverage__ = global.__coverage__ || {}
+        continue
+
+      case '--coverage-report':
+        options.coverageReport = val || args[++i]
+        if (!options.coverageReport) {
+          if (options.pipeToService || coverageServiceTest) {
+            options.coverageReport = 'text-lcov'
+          } else {
+            options.coverageReport = 'text'
+          }
+        }
+        continue
+
+      case '--no-cov': case '--no-coverage':
+        options.coverage = false
+        continue
+
+      case '--cov': case '--coverage':
+        options.coverage = true
+        continue
+
+      case '--save':
+        val = val || args[++i]
+        options.saveFile = val
+        continue
+
+      case '--reporter':
+        val = val || args[++i]
+        options.reporter = val
+        continue
+
+      case '--gc': case '-gc': case '--expose-gc':
+        options.nodeArgs.push('--expose-gc')
+        continue
+
+      case '--strict':
+        options.nodeArgs.push('--use_strict')
+        continue
+
+      case '--debug':
+        options.nodeArgs.push('--debug')
+        continue
+
+      case '--debug-brk':
+        options.nodeArgs.push('--debug-brk')
+        continue
+
+      case '--harmony':
+        options.nodeArgs.push('--harmony')
+        continue
+
+      case '--color':
+        options.color = true
+        continue
+
+      case '--no-color':
+        options.color = false
+        continue
+
+      case '--timeout':
+        val = val || args[++i]
+        options.timeout = +val
+        continue
+
+      case '--bail':
+        options.bail = true
+        continue
+
+      case '--no-bail':
+        options.bail = false
+        continue
+
+      case '--':
+        options.files = options.files.concat(args.slice(i + 1))
+        i = args.length
+        continue
+
+      default:
+        throw new Error('Unknown argument: ' + arg)
+    }
+  }
+
+  if (process.env.TAP === '1') {
+    options.reporter = 'tap'
+  }
+
+  // default to tap for non-tty envs
+  if (!options.reporter) {
+    options.reporter = options.color ? 'classic' : 'tap'
+  }
+
+  return options
+}
+
+/* istanbul ignore function */
+function respawnWithCoverage (options) {
+  // console.error('respawn with coverage')
+  // Re-spawn with coverage
+  var args = [nycBin].concat(
+    '--silent',
+    process.execArgv,
+    process.argv.slice(1),
+    '--__coverage__'
+  )
+  var child = fg(node, args)
+  child.removeAllListeners('close')
+  child.on('close', function (code, signal) {
+    if (signal) {
+      return process.kill(process.pid, signal)
+    }
+    if (code) {
+      return process.exit(code)
+    }
+    // console.error('run coverage report')
+    args = [__filename, '--no-coverage', '--coverage-report']
+    if (options.coverageReport) {
+      args.push(options.coverageReport)
+    }
+    fg(node, args)
+  })
+}
+
+function pipeToCoverageServices (options, child) {
+  // console.error('pipe to services')
+  var piped = false
+  for (var i = 0; i < coverageServices.length; i++) {
+    // console.error('pipe to service?', coverageServices[i].env)
+    if (process.env[coverageServices[i].env]) {
+      pipeToCoverageService(coverageServices[i], options, child)
+      piped = true
+    }
+  }
+
+  /* istanbul ignore if */
+  if (!piped) {
+    throw new Error('unknown service, internal error')
+  }
+}
+
+function pipeToCoverageService (service, options, child) {
+  // console.error('pipe to service yes', service.env)
+  var bin = service.bin
+
+  if (coverageServiceTest) {
+    // console.error('use fakey test bin')
+    // test scaffolding.
+    // don't actually send stuff to the service
+    bin = require.resolve('../test/fixtures/cat.js')
+    console.log('%s:%s', service.name, process.env[service.env])
+  }
+
+  var ca = spawn(node, [bin], {
+    stdio: [ 'pipe', 1, 2 ]
+  })
+
+  child.stdout.pipe(ca.stdin)
+
+  ca.on('close', function (code, signal) {
+    if (signal) {
+      process.kill(process.pid, signal)
+    } else if (code) {
+      process.exit(code)
+    } else {
+      console.log('Successfully piped to ' + service.name)
+    }
+  })
+
+  signalExit(function (code, signal) {
+    child.kill('SIGHUP')
+    ca.kill('SIGHUP')
+  })
+}
+
+/* istanbul ignore function */
+function runCoverageReport (options) {
+  var args = [nycBin, 'report', '--reporter', options.coverageReport]
+  // console.error('run coverage report', args)
+
+  // automatically hook into coveralls and/or codecov
+  if (options.coverageReport === 'text-lcov' && options.pipeToService) {
+    // console.error('pipeToService')
+    var child = spawn(node, args, { stdio: [ 0, 'pipe', 2 ] })
+
+    pipeToCoverageServices(options, child)
+  } else {
+    // otherwise just run the reporter
+    child = fg(node, args)
+    if (options.coverageReport === 'lcov') {
+      child.on('exit', function () {
+        opener('coverage/lcov-report/index.html')
+      })
+    }
+  }
+}
+
+function usage () {
+  return fs.readFileSync(__dirname + '/usage.txt', 'utf8')
+    .split('@@REPORTERS@@')
+    .join(getReporters())
+}
+
+function getReporters () {
+  var types = require('tap-mocha-reporter').types
+  types = types.reduce(function (str, t) {
+    var ll = str.split('\n').pop().length + t.length
+    if (ll < 40) {
+      return str + ' ' + t
+    } else {
+      return str + '\n' + t
+    }
+  }, '').trim()
+  var ind = '                              '
+  return ind + types.split('\n').join('\n' + ind)
+}
+
+function setupTapEnv (options) {
+  process.env.TAP_TIMEOUT = options.timeout
+  if (options.color) {
+    process.env.TAP_COLORS = 1
+  } else {
+    process.env.TAP_COLORS = 0
+  }
+
+  if (options.bail) {
+    process.env.TAP_BAIL = '1'
+  }
+}
+
+function globFiles (files) {
+  return files.reduce(function (acc, f) {
+    if (f === '-') {
+      acc.push(f)
+      return acc
+    }
+
+    // glob claims patterns MUST not include any '\'s
+    if (!/\\/.test(f)) {
+      f = glob.sync(f) || f
+    }
+    return acc.concat(f)
+  }, [])
+}
+
+function stdinOnly (options) {
+  var TMR = require('tap-mocha-reporter')
+  // if we didn't specify any files, then just passthrough
+  // to the reporter, so we don't get '/dev/stdin' in the suite list.
+  // We have to pause() before piping to switch streams2 into old-mode
+  process.stdin.pause()
+  var reporter = new TMR(options.reporter)
+  process.stdin.pipe(reporter)
+  process.stdin.resume()
+}
+
+function readSaveFile (options) {
+  if (options.saveFile) {
+    try {
+      return fs.readFileSync(options.saveFile, 'utf8').trim().split('\n')
+    } catch (er) {}
+  }
+  return options.files
+}
+
+function saveFails (options, tap) {
+  if (!options.saveFile) {
+    return
+  }
+  var fails = []
+  tap.on('result', function (res) {
+    // we will continue to re-run todo tests, even though they're
+    // not technically "failures".
+    if (!res.ok && !res.extra.skip) {
+      fails.push(res.extra.file)
+    }
+  })
+
+  tap.on('end', function () {
+    if (!fails.length) {
+      try {
+        fs.unlinkSync(options.saveFile)
+      } catch (er) {}
+    } else {
+      try {
+        fs.writeFileSync(options.saveFile, fails.join('\n') + '\n')
+      } catch (er) {}
+    }
+  })
+}
+
+function runAllFiles (options, saved, tap) {
+  var doStdin = false
+  var opt = {
+    env: Object.keys(process.env).reduce(function (env, k) {
+      if (!env[k]) {
+        env[k] = process.env[k]
+      }
+      return env
+    }, {
+      TAP: 1
+    })
+  }
+
+  for (var i = 0; i < options.files.length; i++) {
+    var file = options.files[i]
+    if (saved.indexOf(file) === -1) {
+      continue
+    }
+
+    // Pick up stdin after all the other files are handled.
+    if (file === '-') {
+      doStdin = true
+      continue
+    }
+
+    var st = fs.statSync(options.files[i])
+    var extra = {}
+    if (options.timeout) {
+      extra.timeout = options.timeout * 1000
+    }
+
+    extra.file = file
+
+    if (file.match(/\.js$/)) {
+      tap.spawn(node, options.nodeArgs.concat(file), opt, file, extra)
+    } else if (st.isDirectory()) {
+      var dir = fs.readdirSync(file).map(function (f) {
+        return file + '/' + f
+      })
+      options.files.push.apply(options.files, dir)
+    } else if (isExe(st)) {
+      tap.spawn(options.files[i], [], opt, file, extra)
+    }
+  }
+
+  if (doStdin) {
+    tap.stdin()
+  }
+}
+
+function runTests (options) {
+  var saved = readSaveFile(options)
+
+  // At this point, we know we need to use the tap root,
+  // because there are 1 or more files to spawn.
+  var tap = require('../lib/root.js')
+
+  // if not -Rtap, then output what the user wants.
+  if (options.reporter !== 'tap') {
+    var TMR = require('tap-mocha-reporter')
+    tap.unpipe(process.stdout)
+    tap.pipe(new TMR(options.reporter))
+  }
+
+  saveFails(options, tap)
+
+  runAllFiles(options, saved, tap)
+
+  tap.end()
+}
diff --git a/bin/tap-http.js b/bin/tap-http.js
deleted file mode 100644
index 296910f..0000000
--- a/bin/tap-http.js
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env node
-
-// just an example, really
-// Run with `node tap-http.js path/to/tests/`
-
-var argv = process.argv.slice(2)
-  , path = require("path")
-  , Runner = require("../lib/tap-runner")
-
-  , http = require("http")
-  , server = http.createServer(function (req, res) {
-    // it'd be nice to return a non-200 if the tests fail, but we don't
-    // know the status until it's done, so that would mean not being able
-    // to pipe the output
-    res.writeHead(200, {'content-type': 'text/plain'})
-    new Runner(argv, null).pipe(res)
-  })
-
-server.listen(1337)
diff --git a/bin/tap-reader.js b/bin/tap-reader.js
deleted file mode 100755
index b196cc2..0000000
--- a/bin/tap-reader.js
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env node
-
-// read a tap stream from stdin.
-
-var TapConsumer = require("../lib/tap-consumer")
-  , TapProducer = require("../lib/tap-producer")
-
-var tc = new TapConsumer
-  , tp = new TapProducer
-
-//process.stdin.pipe(tc)
-process.stdin.on("data", function (c) {
-  c = c + ""
-  // console.error(JSON.stringify(c).substr(0, 100))
-  tc.write(c)
-})
-process.stdin.on("end", function () { tc.end() })
-process.stdin.resume()
-//tc.pipe(tp)
-tc.on("data", function (c) {
-  tp.write(c)
-})
-tc.on("end", function () { tp.end() })
-
-tp.on("data", function (c) {
-  console.error(["output write", c])
-  process.stdout.write(c)
-})
-
-tp.on("end", function (er, total, ok) {
-  if (er) throw er
-  process.exit(total - ok)
-})
diff --git a/bin/tap.js b/bin/tap.js
deleted file mode 100755
index c666040..0000000
--- a/bin/tap.js
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/usr/bin/env node
-
-var argv = process.argv.slice(2)
-  , path = require("path")
-  , Runner = require("../lib/tap-runner")
-
-  , nopt = require("nopt")
-
-  , knownOpts =
-    { cover: [path, false]
-    , "cover-dir": path
-    , stderr: Boolean
-    , stdout: Boolean
-    , diag: Boolean
-    , version: Boolean
-    , tap: Boolean
-    , timeout: Number
-    , gc: Boolean
-    , debug: Boolean
-    , "debug-brk": Boolean
-    , strict: Boolean
-    , harmony: Boolean
-    }
-
-  , shorthands =
-    // debugging 1: show stderr
-    { d: ["--stderr"]
-    // debugging 2: show stderr and tap
-    , dd: ["--stderr", "--tap"]
-    // debugging 3: show stderr, tap, AND always show diagnostics.
-    , ddd: ["--stderr", "--tap", "--diag"]
-    , "expose-gc": ["--gc"]
-    , g: ["--gc"]
-    , e: ["--stderr"]
-    , t: ["--timeout"]
-    , o: ["--tap"]
-    , c: ["--cover"]
-    , v: ["--version"]
-    , "?": ["--help"]
-    , h: ["--help"]
-    }
-
-  , defaults =
-    { cover: "./lib"
-    , "cover-dir": "./coverage"
-    , stderr: process.env.TAP_STDERR !== '0'
-    , tap: process.env.TAP
-    , diag: process.env.TAP_DIAG
-    , timeout: +process.env.TAP_TIMEOUT || 30
-    , gc: false
-    , debug: false
-    , "debug-brk": false
-    , strict: false
-    , harmony: false
-    , version: false
-    , help: false }
-
-  , options = nopt(knownOpts, shorthands)
-
-if (options.version) {
-  console.log(require("../package.json").version)
-  process.exit(0)
-}
-
-if (options.help) {
-  console.log(function(){/*
-
-Usage:
-    tap <options> <files>
-
-    Run the files as tap tests, parse the output, and report the results
-
-Options:
-
-    --stderr     Print standard error output of tests to standard error.
-    --tap        Print raw tap output.
-    --diag       Print diagnostic output for passed tests, as well as failed.
-                 (Implies --tap)
-    --gc         Expose the garbage collector to tests.
-    --timeout    Maximum time to wait for a subtest, in seconds. Default: 30
-    --debug      Pass the '--debug' flag to node for debugging
-    --debug-brk  Pass the '--debug-brk' flag to node for debugging
-    --strict     Enforce strict mode when running tests.
-    --harmony    Enable harmony features for tests.
-    --version    Print the version of node tap.
-    --help       Print this help.
-
-Please report bugs!  https://github.com/isaacs/node-tap/issues
-
-*/}.toString().split(/\n/).slice(1, -1).join("\n"))
-  process.exit(0)
-}
-
-
-Object.keys(defaults).forEach(function (k) {
-  if (!options.hasOwnProperty(k)) options[k] = defaults[k]
-})
-
-// other tests that might rely on these
-if (options.diag) process.env.TAP_DIAG = true
-if (options.tap) process.env.TAP = true
-if (options.timeout) process.env.TAP_TIMEOUT = options.timeout
-
-var r = new Runner(options)
-  , TapProducer = require("../lib/tap-producer")
-
-if (options.tap || options.diag) {
-  r.pipe(process.stdout)
-} else {
-  r.on("file", function (file, results, details) {
-    var s = (details.ok ? "" : "not ") + "ok "+results.name
-      , n = details.pass + "/" + details.testsTotal
-      , dots = new Array(Math.max(1, 60 - s.length - n.length)).join(".")
-    console.log("%s %s %s", s, dots, n)
-    if (details.ok) {
-      if (details.skip) {
-        console.log("  skipped: %s", details.skip)
-      }
-      if (details.todo) {
-        console.log("  todo: %s", details.todo)
-      }
-    } else {
-      // console.error(details)
-      console.log("    Command: %s", results.command)
-      console.log("    " + TapProducer.encode(details.list)
-                  .split(/\n/).join("\n    "))
-    }
-  })
-  r.on("end", function () {
-    //console.log(r)
-    var s = "total"
-      , n = r.results.pass + "/" + r.results.testsTotal
-      , dots = new Array(60 - s.length - n.length).join(".")
-      , ok = r.results.ok ? "ok" : "not ok"
-    console.log("%s %s %s\n\n%s", s, dots, n, ok)
-    if (r.doCoverage) {
-      console.error( "\nCoverage: %s\n"
-                   , path.resolve(r.coverageOutDir, "index.html") )
-    }
-  })
-}
-
-
-
-r.on("end", function () {
-  process.exit(r.results.ok ? 0 : 1)
-})
diff --git a/bin/usage.txt b/bin/usage.txt
new file mode 100644
index 0000000..8ca733b
--- /dev/null
+++ b/bin/usage.txt
@@ -0,0 +1,93 @@
+Usage:
+  tap [options] <files>
+
+Executes all the files and interprets their output as TAP
+formatted test result data.
+
+To parse TAP data from stdin, specify "-" as a filename.
+
+Short options are parsed gnu-style, so for example '-bCRspec' would be
+equivalent to '--bail --no-color --reporter=spec'
+
+Options:
+
+  -c --color                  Use colors (Default for TTY)
+
+  -C --no-color               Do not use colors (Default for non-TTY)
+
+  -b --bail                   Bail out on first failure
+
+  -B --no-bail                Do not bail out on first failure (Default)
+
+  -R<type> --reporter=<type>  Use the specified reporter.  Defaults to
+                              'classic' when colors are in use, or 'tap'
+                              when colors are disabled.
+
+                              Available reporters:
+@@REPORTERS@@
+
+  -s<file> --save=<file>      If <file> exists, then it should be a line-
+                              delimited list of test files to run.  If
+                              <file> is not present, then all command-line
+                              positional arguments are run.
+
+                              After the set of test files are run, any
+                              failed test files are written back to the
+                              save file.
+
+                              This way, repeated runs with -s<file> will
+                              re-run failures until all the failures are
+                              passing, and then once again run all tests.
+
+                              It's a good idea to .gitignore the file
+                              used for this purpose, as it will churn a
+                              lot.
+
+  --coverage --cov            Capture coverage information using 'nyc'
+
+                              If a COVERALLS_REPO_TOKEN environment
+                              variable is set, then coverage is
+                              captured by default and sent to the
+                              coveralls.io service. If a CODECOV_TOKEN
+                              environment variable is set, then coverage is
+                              captured by default and sent to the
+                              codecov.io service.
+
+  --no-coverage --no-cov      Do not capture coverage information.
+                              Note that if nyc is already loaded, then
+                              the coverage info will still be captured.
+
+  --coverage-report=<type>    Output coverage information using the
+                              specified istanbul/nyc reporter type.
+
+                              Default is 'text' when running on the
+                              command line, or 'text-lcov' when piping
+                              to coveralls or codecov.
+
+                              If 'lcov' is used, then the report will
+                              be opened in a web browser after running.
+
+                              This can be run on its own at any time
+                              after a test run that included coverage.
+
+  -t<n> --timeout=<n>         Time out test files after <n> seconds.
+                              Defaults to 30, or the value of the
+                              TAP_TIMEOUT environment variable.
+
+  -h --help                   print this thing you're looking at
+
+  -v --version                show the version of this program
+
+  -gc --expose-gc             Expose the gc() function to Node tests
+
+  --debug                     Run JavaScript tests with node --debug
+
+  --debug-brk                 Run JavaScript tests with node --debug-brk
+
+  --harmony                   Enable all Harmony flags in JavaScript tests
+
+  --strict                    Run JS tests in 'use strict' mode
+
+  --                          Stop parsing flags, and treat any additional
+                              command line arguments as filenames.
+
diff --git a/coverage-example/lib/bar.js b/coverage-example/lib/bar.js
deleted file mode 100644
index e7cb7ad..0000000
--- a/coverage-example/lib/bar.js
+++ /dev/null
@@ -1,15 +0,0 @@
-var Bar = module.exports = function(str) {
-  this.bar = str;
-  this.str = str;
-};
-
-Bar.prototype.foo = function() {
-  var self = this;
-  return self.bar;
-};
-
-Bar.prototype.baz = function() {
-  var self = this;
-  return self.str;
-};
-
diff --git a/coverage-example/lib/foo.js b/coverage-example/lib/foo.js
deleted file mode 100644
index cb9ee8f..0000000
--- a/coverage-example/lib/foo.js
+++ /dev/null
@@ -1,15 +0,0 @@
-var Foo = module.exports = function(str) {
-  this.foo = str;
-  this.str = str;
-};
-
-Foo.prototype.bar = function() {
-  var self = this;
-  return self.foo;
-};
-
-Foo.prototype.baz = function() {
-  var self = this;
-  return self.str;
-};
-
diff --git a/coverage-example/test/bar.test.js b/coverage-example/test/bar.test.js
deleted file mode 100644
index 91e4bc2..0000000
--- a/coverage-example/test/bar.test.js
+++ /dev/null
@@ -1,20 +0,0 @@
-var test = require('tap').test,
-    Bar = require('../lib/bar'),
-    bar;
-
-test('setup', function(t) {
-  bar = new Bar('baz');
-  t.ok(bar);
-  t.end();
-});
-
-test('bar', function(t) {
-  t.equal('baz', bar.foo());
-  t.end();
-});
-
-test('teardown', function(t) {
-  t.ok(true);
-  t.end();
-});
-
diff --git a/coverage-example/test/baz.test.js b/coverage-example/test/baz.test.js
deleted file mode 100644
index fae22d8..0000000
--- a/coverage-example/test/baz.test.js
+++ /dev/null
@@ -1,29 +0,0 @@
-var test = require('tap').test,
-    Foo = require('../lib/foo'),
-    Bar = require('../lib/bar'),
-    foo, bar;
-
-test('setup', function(t) {
-  foo = new Foo('baz');
-  t.ok(foo);
-  bar = new Bar('baz');
-  t.ok(bar);
-  t.end();
-});
-
-test('baz from Foo', function(t) {
-  t.equal('baz', foo.baz());
-  t.end();
-});
-
-test('baz from Bar', function(t) {
-  t.equal('baz', bar.baz());
-  t.end();
-});
-
-
-test('teardown', function(t) {
-  t.ok(true);
-  t.end();
-});
-
diff --git a/coverage-example/test/foo.test.js b/coverage-example/test/foo.test.js
deleted file mode 100644
index 2aed8fd..0000000
--- a/coverage-example/test/foo.test.js
+++ /dev/null
@@ -1,20 +0,0 @@
-var test = require('tap').test,
-    Foo = require('../lib/foo'),
-    foo;
-
-test('setup', function(t) {
-  foo = new Foo('baz');
-  t.ok(foo);
-  t.end();
-});
-
-test('bar', function(t) {
-  t.equal('baz', foo.bar());
-  t.end();
-});
-
-test('teardown', function(t) {
-  t.ok(true);
-  t.end();
-});
-
diff --git a/example/long-slow-many.js b/example/long-slow-many.js
new file mode 100644
index 0000000..7475d9d
--- /dev/null
+++ b/example/long-slow-many.js
@@ -0,0 +1,37 @@
+var t = require('../lib/root.js')
+t.plan(2)
+
+t.test('gun show', function (t) {
+  t.plan(100)
+
+  var n = 0
+  var i = setInterval(function () {
+    if (n % 123 === 5) {
+      t.fail('FIRE!')
+      t.fail('THE BUILDING IS ON FIRE')
+    } else {
+      t.pass('this is ok')
+      t.pass('i am ok with how things are proceeding')
+    }
+    if (++n === 50) {
+      return clearInterval(i)
+    }
+  }, 100)
+})
+
+t.test('wondermark', function (t) {
+  t.plan(500)
+  var n = 0
+  var j = setInterval(function () {
+    if (n % 123 === 35) {
+      t.fail('I AM EATING BREAKFAST')
+      t.fail('FUCKING SEALIONS')
+    } else {
+      t.pass('excuse me')
+      t.pass('pardon me')
+    }
+    if (++n === 250) {
+      return clearInterval(j)
+    }
+  }, 10)
+})
diff --git a/example/test/test-example.js b/example/test/test-example.js
deleted file mode 100644
index cd2549b..0000000
--- a/example/test/test-example.js
+++ /dev/null
@@ -1,237 +0,0 @@
-var tap = require("tap")
-  , test = tap.test
-  , plan = tap.plan
-  , math
-
-test("load sut", function (t) {
-  math = require("../lib/math")
-  t.ok(math, "object loaded")
-  t.end()
-})
-
-test("validate constants", function (t) {
-  t.equal(math.LN10, 2.302585092994046, "ln 10")
-  t.equal(math.PI, 3.141592653589793, "pi")
-  t.equal(math.E, 2.718281828459045, "e")
-  t.equal(math.LOG10E, 0.4342944819032518, "log 10 e")
-  t.equal(math.SQRT2, 1.4142135623730951, "sqrt 2")
-  t.equal(math.SQRT1_2, 0.7071067811865476, "sqrt 1/2")
-  t.equal(math.LN2, 0.6931471805599453, "ln2")
-  t.end()
-})
-
-test("using this", function (t) {
-  // this also works.
-  this.equal(t, this, "call in scope of test obj")
-  this.end()
-})
-
-// test setTimeout, just a trivial example.
-test("setTimeout", function (t) {
-  var start = Date.now()
-  setTimeout(function () {
-    t.ok(Date.now() >= start + 50, "timeout fired after delay")
-    t.end()
-  }, 50)
-})
-
-// another way to do the same, using a plan.
-// this is more robust, but annoying when you have a long list
-// of tests for something.  For async stuff, it's generally better,
-// since there's a higher risk of the control flowing off to lala land.
-test("setTimeout planned", function (t) {
-  t.plan(1)
-  var start = Date.now()
-  setTimeout(function () {
-    t.ok(Date.now() >= start + 50, "timeout fired after delay")
-  }, 50)
-})
-
-// plans also are good for cases where things may fire in a non-deterministic
-// order, since it won't be as obvious when everything is done.
-test("setTimeout parallel", function (t) {
-  t.plan(2)
-  var start = Date.now()
-  setTimeout(function A () {
-    t.ok(Date.now() >= start + 50, "timeout A fired after delay")
-  }, 50)
-  setTimeout(function B () {
-    t.ok(Date.now() >= start + 50, "timeout B fired after delay")
-  }, 50)
-})
-
-// something slightly less hello worldy
-test("async test", function (t) {
-  t.plan(4)
-  var fs = require("fs")
-  t.ok(fs, "fs library should load")
-  var rs = fs.createReadStream(__filename)
-  t.ok(rs, "read stream should start fine.")
-  rs.on("open", function (fd) {
-    t.type(fd, "number", "file descriptor should be a number")
-    t.equal(fd, rs.fd, "fd should match stream fd")
-  })
-})
-
-// you can bail out of the entire everything if something is just
-// Not Right (db not installed, etc.)
-test("tarp", function (parent) {
-  if (7 === 5) {
-    parent.bailout("math is broken")
-  }
-  // bailout bubbles up a bit like "error" events
-  // if unhandled, then the parent will bail, as well.
-  parent.test("child bailouts", function (child) {
-    child.on("bailout", function (s) {
-      parent.fail("children shouldn't bail.")
-    })
-    child.bailout("try to bail out, but instead just fail a test")
-  })
-
-  parent.test("child bailout 2", function (child) {
-    child.bailout("this one will bail out")
-  })
-})
-
-// tests marked "todo" can fail without counting against the overall score
-// never ever ever write tests to "verify" incorrect behavior!
-test("unfinished test", function (t) {
-  t.equal(math.cos(math.PI), -1, "cos(PI)")
-  t.equal(math.sin(math.PI),  0, "sin(PI)")
-  t.equal(math.face, "your face", "math.face should be your face # TODO")
-  t.end()
-})
-
-// tests can have children.
-test("http server", function (t) {
-  // one test plus 4 children.
-  t.plan(5)
-
-  var http = require("http")
-    , PORT = 12346
-
-  t.ok(http, "http module should load")
-  var server
-
-  t.test("set up server", function (t) {
-    t.plan(2)
-    server = http.createServer(function (req, res) {
-      t.comment("Request: "+req.url)
-      res.writeHead(200, {})
-      res.end(req.method + " " + req.url)
-    })
-    t.ok(server, "createServer should create a server")
-    server.listen(PORT, t.cb("listen should fire callback"))
-  })
-
-  // set the "parallel" flag on this one.
-  // That signals the harness to proceed immediately to the next test,
-  // and run them in parallel.
-  // Default behavior is to wait for each test to complete before proceeding
-  // to the next one.
-  // The first not-parallel test encountered will cause it to wait for that
-  // test, as well as all the parallel tests before it.
-  // A, B', C', D', E (where ' means "parallel")
-  // Runs A, and then B, C, and D in parallel, and then E.
-  t.test("testing POST", {parallel: true}, function (t) {
-    t.plan(1)
-    http.request("POST", { method: "POST"
-                         , host: "localhost"
-                         , path: "/foo"
-                         , port: PORT }).on("response", function (res) {
-      t.bufferStream(res, function (s) { t.equal(s, "POST /foo") })
-    }).end()
-  })
-
-  t.test("testing GET", {parallel: true}, function (t) {
-    t.plan(1)
-    http.request("POST", { method: "GET"
-                         , host: "localhost"
-                         , path: "/foo"
-                         , port: PORT }).on("response", function (res) {
-      t.bufferStream(res, function (s) { t.equal(s, "GET /foo") })
-    }).end()
-  })
-
-  // wrap in a test so that if this throws, it'll log as a failed test.
-  t.test("teardown", function (t) {
-    server.close()
-    t.end()
-  })
-})
-
-// yo dawg!
-test("meta-tests", function (t) {
-  t.plan(5)
-
-  // t.fails() wraps a child test and succeeds if it fails.
-  t.fails(t.test("this should fail", function (t) {
-    t.ok(false, "assert false")
-    t.end()
-  }))
-
-  // t.timesOut() wraps a child test and succeeds if it times out.
-  // if t.end() is called, or if a plan is completed, then it fails.
-  // set the timeout really low so that it will not take forever.
-  t.timesOut(t.test("this should timeout", { timeout: 1 }, function (t) {
-    t.ok(true, "assert true")
-    // t.end() never called.
-  }))
-
-  // t.incomplete() wraps a child test and succeeds if it ends before
-  // the plan is finished.
-  t.incomplete(t.test("this should be incomplete", function (t) {
-    t.plan(100)
-    t.ok(true, "assert true")
-    // calling end prematurely.
-    t.end()
-  }))
-
-  // t.bailsOut() wraps a child test and succeeds if it calls bailout()
-  t.bailsOut(t.test("this should bailout", function (t) {
-    t.bailout("oh noes, bailing out!")
-  }))
-
-  // low-level analysis of subtests
-  t.test("verifying test success/failure expectations", function (t) {
-    t.once("end", function () {
-      var res = t.results
-        , is = t.equal
-      // hijack!
-      t.clear()
-      is(res.ok,         false, "ok")
-
-      is(res.bailedOut,  false, "bailed out")
-
-      is(res.skip,       2, "skips")
-      is(res.skipPass,   1, "skip that passed")
-      is(res.skipFail,   1, "skip that failed")
-
-      is(res.todo,       2, "todos")
-      is(res.todoPass,   1, "todo that passed")
-      is(res.todoFail,   1, "todo that failed")
-
-      is(res.failTotal,  3, "failures total")
-      is(res.fail,       1, "relevant failure")
-
-      is(res.passTotal,  3, "passes total")
-      is(res.pass,       1, "relevant pass")
-
-      is(res.testsTotal, 6, "total tests")
-      is(res.tests,      2, "should be 2 relevant tests")
-
-      t.end()
-    })
-
-    // run the metatest.
-    // *this* is the actual SUT in this case.
-    t.ok(false, "failing todo #todo")
-    // can also set #todo or #skip explicitly
-    t.ok(true, "succeeding todo", {todo: true})
-    t.ok(false, "failing skip #skip", {skip: true})
-    t.ok(true, "suceeding skip #skip")
-    t.ok(false, "failing test")
-    t.ok(true, "succeeding test")
-    t.end()
-  })
-})
diff --git a/lib/assert.js b/lib/assert.js
new file mode 100644
index 0000000..2b20b8b
--- /dev/null
+++ b/lib/assert.js
@@ -0,0 +1,281 @@
+var synonyms = require('./synonyms.js')
+var deeper = require('deeper') // strict
+var shallower = require('only-shallow') // in touch with its feelings
+var tmatch = require('tmatch') // ok with partial estimates
+
+// Load Buffer the old way for browserify's sake
+var Buffer = require('buffer').Buffer // eslint-disable-line
+
+// this is actually the "working half" of the Test class.
+// each method figures out if it's a pass or fail, and decorates
+// the extra bit, and then calls either pass() or fail() or some
+// other assert method.
+//
+// typically, a plugin would do this on a specific instance, eg on
+// the root test harness instance.  but we do this here to add some
+// useful prototype methods.
+
+exports.decorate = decorate
+
+function decorate (t) {
+  t.addAssert('ok', 1, function (obj, message, extra) {
+    message = message || 'expect truthy value'
+    if (obj) {
+      return this.pass(message, extra)
+    }
+
+    return this.fail(message, extra)
+  })
+
+  t.addAssert('notOk', 1, function (obj, message, extra) {
+    message = message || 'expect falsey value'
+    return this.ok(!obj, message, extra)
+  })
+
+  t.addAssert('error', 1, function (er, message, extra) {
+    if (!er) {
+      return this.pass(message || 'should not error', extra)
+    }
+
+    if (!(er instanceof Error)) {
+      extra.found = er
+      return this.fail(message || 'non-Error error encountered', extra)
+    }
+
+    message = message || er.message
+    extra.found = er
+    return this.fail(message, extra)
+  })
+
+  t.addAssert('equal', 2, function (f, w, m, e) {
+    m = m || 'should be equal'
+    if (f === w) {
+      return this.pass(m, e)
+    }
+
+    e.found = f
+    e.wanted = w
+    e.compare = '==='
+
+    if (typeof f === 'object' &&
+        typeof w === 'object' &&
+        f &&
+        w &&
+        shallower(f, w)) {
+      e.note = 'Objects never === one another'
+    }
+
+    return this.fail(m, e)
+  })
+
+  t.addAssert('not', 2, function (f, w, m, e) {
+    m = m || 'should not be equal'
+    if (f !== w) {
+      return this.pass(m, e)
+    }
+
+    e.found = f
+    e.doNotWant = w
+    e.compare = '!=='
+
+    return this.fail(m, e)
+  })
+
+  t.addAssert('same', 2, function (f, w, m, e) {
+    m = m || 'should be equivalent'
+    e.found = f
+    e.wanted = w
+    return this.ok(shallower(f, w), m, e)
+  })
+
+  t.addAssert('notSame', 2, function (f, w, m, e) {
+    m = m || 'should not be equivalent'
+    e.found = f
+    e.doNotWant = w
+    return this.notOk(shallower(f, w), m, e)
+  })
+
+  t.addAssert('strictSame', 2, function (f, w, m, e) {
+    m = m || 'should be equivalent strictly'
+    e.found = f
+    e.wanted = w
+    return this.ok(deeper(f, w), m, e)
+  })
+
+  t.addAssert('strictNotSame', 2, function (f, w, m, e) {
+    m = m || 'should be equivalent strictly'
+    e.found = f
+    e.doNotWant = w
+    return this.notOk(deeper(f, w), m, e)
+  })
+
+  t.addAssert('match', 2, function (f, w, m, e) {
+    m = m || 'should match pattern provided'
+    e.found = f
+    e.pattern = w
+    return this.ok(tmatch(f, w), m, e)
+  })
+
+  t.addAssert('notMatch', 2, function (f, w, m, e) {
+    m = m || 'should not match pattern provided'
+    e.found = f
+    e.pattern = w
+    return this.ok(!tmatch(f, w), m, e)
+  })
+
+  t.addAssert('type', 2, function (obj, klass, m, e) {
+    var name = klass
+    if (typeof name === 'function') {
+      name = name.name || '(anonymous constructor)'
+    }
+    m = m || 'type is ' + name
+
+    // simplest case, it literally is the same thing
+    if (obj === klass) {
+      return this.pass(m, e)
+    }
+
+    var type = typeof obj
+    if (!obj && type === 'object') {
+      type = 'null'
+    }
+
+    if (type === 'object' && klass !== 'object') {
+      if (typeof klass === 'function') {
+        e.found = Object.getPrototypeOf(obj).constructor.name
+        e.wanted = name
+        return this.ok(obj instanceof klass, m, e)
+      }
+
+      // check prototype chain for name
+      // at this point, we already know klass is not a function
+      // if the klass specified is an obj in the proto chain, pass
+      // if the name specified is the name of a ctor in the chain, pass
+      var p = obj
+      do {
+        var ctor = p.constructor && p.constructor.name
+        if (p === klass || ctor === name) {
+          return this.pass(m, e)
+        }
+        p = Object.getPrototypeOf(p)
+      } while (p)
+    }
+
+    return this.equal(type, name, m, e)
+  })
+
+  t.addAssert('throws', 4, function (fn_, wanted_, m_, e_, m, e__) {
+    var fn, wanted, e
+    for (var i = 0; i < arguments.length - 1; i++) {
+      var arg = arguments[i]
+      if (typeof arg === 'function') {
+        fn = arg
+      } else if (typeof arg === 'string' && arg) {
+        m = arg
+      } else if (typeof arg === 'object') {
+        if (!wanted) {
+          wanted = arg
+        } else {
+          e = arg
+        }
+      }
+    }
+
+    for (i in e__) {
+      e[i] = e__[i]
+    }
+
+    if (!m) {
+      m = fn && fn.name || 'expected to throw'
+    }
+
+    if (wanted) {
+      if (wanted instanceof Error) {
+        var w = {
+          message: wanted.message
+        }
+        if (wanted.name) {
+          w.name = wanted.name
+        }
+
+        for (i in wanted) {
+          w[i] = wanted[i]
+        }
+        wanted = w
+
+        m += ': ' + (wanted.name || 'Error') + ' ' + wanted.message
+        e = e || {}
+        if (e !== wanted) {
+          e.wanted = wanted
+        }
+      } else if (typeof wanted === 'string') {
+        wanted = {
+          message: wanted
+        }
+      }
+    }
+
+    if (typeof fn !== 'function') {
+      e = e || {}
+      e.todo = true
+      return this.pass(m, e)
+    }
+
+    try {
+      fn()
+      return this.fail(m, e)
+    } catch (er) {
+      // 'name' is a getter.
+      if (er.name) {
+        er.name = er.name + ''
+      }
+
+      if (wanted) {
+        if (Object.prototype.toString.call(wanted) === '[object RegExp]') {
+          return this.match(er.message, wanted, m, e)
+        }
+        return this.has(er, wanted, m, e)
+      } else {
+        return this.pass(m, e)
+      }
+    }
+  })
+
+  t.addAssert('doesNotThrow', 1, function (fn, m, e) {
+    if (typeof fn === 'string') {
+      var x = fn
+      fn = m
+      m = x
+    }
+
+    if (!m) {
+      m = fn && fn.name || 'expected to not throw'
+    }
+
+    if (typeof fn !== 'function') {
+      e.todo = true
+      return this.pass(m, e)
+    }
+
+    try {
+      fn()
+      return this.pass(m, e)
+    } catch (er) {
+      return this.fail(m, this._extraFromError(er, e))
+    }
+  })
+
+  // synonyms are helpful.
+  Object.keys(synonyms).forEach(function (c) {
+    if (t[c]) {
+      synonyms[c].forEach(function (s) {
+        Object.defineProperty(t, s, {
+          value: t[c],
+          enumerable: false,
+          configurable: true,
+          writable: true
+        })
+      })
+    }
+  })
+}
diff --git a/lib/main.js b/lib/main.js
deleted file mode 100644
index a9a520a..0000000
--- a/lib/main.js
+++ /dev/null
@@ -1,16 +0,0 @@
-
-var GlobalHarness = require("./tap-global-harness")
-
-// this lets you do stuff like:
-// var test = require("tap").test
-// test(...)
-// to run stuff in the global harness.
-exports = module.exports = new GlobalHarness()
-
-exports.createProducer = exports.Producer = require("./tap-producer")
-exports.createConsumer = exports.Consumer = require("./tap-consumer")
-exports.yamlish = require("yamlish")
-exports.createTest = exports.Test = require("./tap-test")
-exports.createHarness = exports.Harness = require("./tap-harness")
-exports.createRunner = exports.Runner = require("./tap-runner")
-exports.assert = require("./tap-assert")
diff --git a/lib/mocha.js b/lib/mocha.js
new file mode 100644
index 0000000..c0637ba
--- /dev/null
+++ b/lib/mocha.js
@@ -0,0 +1,55 @@
+// API surface inspired by Mocha,
+// Copyright (c) TJ Holowaychuk <tj at vision-media.ca>
+//
+// Using these functions still outputs TAP, of course.
+var stack = require('./stack.js')
+
+exports.it = it
+exports.describe = describe
+exports.global = function () {
+  for (var g in exports) {
+    global[g] = exports[g]
+  }
+}
+
+var t = require('./root.js')
+
+function it (name, fn) {
+  var c = t.current()
+  var at = stack.at(it)
+
+  if (fn && fn.length) {
+    c.test(name, { at: at }, function (tt) {
+      return fn(function (err) {
+        if (err) tt.threw(err)
+        else tt.end()
+      })
+    })
+  } else {
+    c.doesNotThrow(fn, name || 'unnamed test', { at: at })
+  }
+}
+
+function describe (name, fn) {
+  var c = t.current()
+  var at = stack.at(describe)
+  if (!fn) {
+    c.test(name, { at: at })
+  } else if (fn.length) {
+    c.test(name, { at: at }, function (tt) {
+      return fn(function (err) {
+        if (err) tt.threw(err)
+        else tt.end()
+      })
+    })
+  } else {
+    c.test(name, { at: at }, function (tt) {
+      var ret = fn()
+      if (ret && ret.then) {
+        return ret
+      } else {
+        tt.end()
+      }
+    })
+  }
+}
diff --git a/lib/root.js b/lib/root.js
new file mode 100644
index 0000000..9622c87
--- /dev/null
+++ b/lib/root.js
@@ -0,0 +1,90 @@
+var Test = require('./test.js')
+
+var tap = new Test()
+module.exports = tap
+
+if (tap._timer && tap._timer.unref) {
+  tap._timer.unref()
+}
+
+tap._name = 'TAP'
+
+// No sense continuing after bailout!
+tap.on('bailout', function () {
+  process.exit(1)
+})
+
+process.on('exit', function (code) {
+  tap.endAll()
+
+  if (!tap._ok && code === 0) {
+    process.exit(1)
+  }
+})
+
+// need to autoend if a teardown is added.
+// otherwise we may never see process.on('exit')!
+tap.tearDown = function (fn) {
+  var ret = Test.prototype.tearDown.apply(tap, arguments)
+  tap.autoend()
+  return ret
+}
+
+tap.pipe(process.stdout)
+
+tap.mocha = require('./mocha.js')
+tap.mochaGlobals = tap.mocha.global
+tap.Test = Test
+tap.synonyms = require('./synonyms.js')
+
+process.on('uncaughtException', function onUncaught (er) {
+  var child = tap
+  while (child._currentChild && child._currentChild instanceof Test) {
+    child = child._currentChild
+  }
+  child.threw(er)
+})
+
+// SIGTERM means being forcibly killed, almost always by timeout
+var onExit = require('signal-exit')
+onExit(function (code, signal) {
+  if (signal !== 'SIGTERM') {
+    return
+  }
+
+  var handles = process._getActiveHandles().filter(function (h) {
+    return h !== process.stdout &&
+    h !== process.stdin &&
+    h !== process.stderr
+  })
+  var requests = process._getActiveRequests()
+  var msg = 'received SIGTERM with pending event queue activity'
+  tap.fail(msg, {
+    requests: requests.map(function (r) {
+      var ret = { type: r.constructor.name }
+      if (r.context) {
+        ret.context = r.context
+      }
+      return ret
+    }),
+    handles: handles.map(function (h) {
+      var ret = { type: h.constructor.name }
+      if (h.msecs) {
+        ret.msecs = h.msecs
+      }
+      if (h._events) {
+        ret.events = Object.keys(h._events)
+      }
+      if (h._sockname) {
+        ret.sockname = h._sockname
+      }
+      if (h._connectionKey) {
+        ret.connectionKey = h._connectionKey
+      }
+      return ret
+    }),
+    at: null
+  })
+
+  tap.end()
+})
diff --git a/lib/stack.js b/lib/stack.js
new file mode 100644
index 0000000..2767267
--- /dev/null
+++ b/lib/stack.js
@@ -0,0 +1,252 @@
+exports.capture = capture
+exports.captureString = captureString
+exports.at = at
+exports.parseLine = parseLine
+exports.clean = clean
+
+var cwd = process.cwd()
+// var methods =[
+//   'getThis',
+//   'getFunction',
+//   'getTypeName',
+//   'getFunctionName',
+//   'getMethodName',
+//   'getFileName',
+//   'getLineNumber',
+//   'getColumnNumber',
+//   'getEvalOrigin',
+//   'isToplevel',
+//   'isEval',
+//   'isNative',
+//   'isConstructor'
+// ]
+
+function clean (stack) {
+  if (!Array.isArray(stack)) {
+    stack = stack.split('\n')
+  }
+
+  var internals = [
+    /\(domain.js:[0-9]+:[0-9]+\)$/,
+    /\(events.js:[0-9]+:[0-9]+\)$/,
+    /\(node.js:[0-9]+:[0-9]+\)$/,
+    /\(timers.js:[0-9]+:[0-9]+\)$/,
+    /\(module.js:[0-9]+:[0-9]+\)$/,
+    /GeneratorFunctionPrototype.next \(native\)/,
+    /node_modules[\\\/]tap[\\\/](.*?)\.js:[0-9]:[0-9]\)?$/
+  ]
+
+  if (!(/^\s*at /.test(stack[0])) &&
+    (/^\s*at /.test(stack[1]))) {
+    stack = stack.slice(1)
+  }
+
+  stack = stack.map(function (st) {
+    var isInternal = internals.some(function (internal) {
+      return internal.test(st)
+    })
+
+    if (isInternal) {
+      return null
+    }
+
+    return st.trim()
+      .replace(/^\s*at /, '')
+      .replace(cwd + '/', '')
+      .replace(cwd + '\\', '')
+  }).filter(function (st) {
+    return st
+  }).join('\n').trim()
+
+  if (stack) {
+    return stack + '\n'
+  } else {
+    return null
+  }
+}
+
+function captureString (limit, fn) {
+  if (typeof limit === 'function') {
+    fn = limit
+    limit = Infinity
+  }
+  if (!fn) {
+    fn = captureString
+  }
+
+  var limitBefore = Error.stackTraceLimit
+  if (limit) {
+    Error.stackTraceLimit = limit
+  }
+
+  var obj = {}
+
+  Error.captureStackTrace(obj, fn)
+  var stack = obj.stack
+  Error.stackTraceLimit = limitBefore
+
+  return clean(stack)
+}
+
+function capture (limit, fn) {
+  if (typeof limit === 'function') {
+    fn = limit
+    limit = Infinity
+  }
+  if (!fn) {
+    fn = capture
+  }
+  var prepBefore = Error.prepareStackTrace
+  var limitBefore = Error.stackTraceLimit
+
+  Error.prepareStackTrace = function (obj, site) {
+    return site
+  }
+
+  if (limit) {
+    Error.stackTraceLimit = limit
+  }
+
+  var obj = {}
+  Error.captureStackTrace(obj, fn)
+  var stack = obj.stack
+  Error.prepareStackTrace = prepBefore
+  Error.stackTraceLimit = limitBefore
+
+  return stack
+}
+
+function at (fn) {
+  if (!fn) {
+    fn = at
+  }
+
+  var site = capture(1, fn)[0]
+
+  if (!site) {
+    return {}
+  }
+
+  var res = {
+    file: site.getFileName(),
+    line: site.getLineNumber(),
+    column: site.getColumnNumber()
+  }
+
+  if (res.file.indexOf(cwd + '/') === 0 ||
+    res.file.indexOf(cwd + '\\') === 0) {
+    res.file = res.file.substr(cwd.length + 1)
+  }
+
+  if (site.isConstructor()) {
+    res.constructor = true
+  }
+
+  if (site.isEval()) {
+    res.evalOrigin = site.getEvalOrigin()
+  }
+
+  if (site.isNative()) {
+    res.native = true
+  }
+
+  try {
+    var typename = site.getTypeName()
+  } catch (er) {}
+
+  if (typename &&
+    typename !== 'Object' &&
+    typename !== '[object Object]') {
+    res.type = typename
+  }
+
+  var fname = site.getFunctionName()
+  if (fname) {
+    res.function = fname
+  }
+
+  var meth = site.getMethodName()
+  if (meth && fname !== meth) {
+    res.method = meth
+  }
+
+  return res
+}
+
+var re = new RegExp(
+  '^' +
+  // Sometimes we strip out the '    at' because it's noisy
+  '(?:\\s*at )?' +
+  // $1 = ctor if 'new'
+  '(?:(new) )?' +
+  // Object.method [as foo] (, maybe
+  // $2 = function name
+  // $3 = method name
+  '(?:([^\\(\\[]*)(?: \\[as ([^\\]]+)\\])? \\()?' +
+  // (eval at <anonymous> (file.js:1:1),
+  // $4 = eval origin
+  // $5:$6:$7 are eval file/line/col, but not normally reported
+  '(?:eval at ([^ ]+) \\(([^\\)]+):([0-9]+):([0-9]+)\\), )?' +
+  // file:line:col
+  // $8:$9:$10
+  // $11 = 'native' if native
+  '(?:([^\\)]+):([0-9]+):([0-9]+)|(native))' +
+  // maybe close the paren, then end
+  '\\)?$'
+)
+
+function parseLine (line) {
+  var match = line && line.match(re)
+  if (!match) {
+    return null
+  }
+
+  var ctor = match[1] === 'new'
+  var fname = match[2]
+  var meth = match[3]
+  var evalOrigin = match[4]
+  var evalFile = match[5]
+  var evalLine = +match[6]
+  var evalCol = +match[7]
+  var file = match[8]
+  var lnum = match[9]
+  var col = match[10]
+  var native = match[11] === 'native'
+
+  var res = {
+    file: file,
+    line: +lnum,
+    column: +col
+  }
+
+  if (res.file &&
+    (res.file.indexOf(cwd + '/') === 0 ||
+    res.file.indexOf(cwd + '\\') === 0)) {
+    res.file = res.file.substr(cwd.length + 1)
+  }
+
+  if (ctor) {
+    res.constructor = true
+  }
+
+  if (evalOrigin) {
+    res.evalOrigin = evalOrigin
+    res.evalLine = evalLine
+    res.evalColumn = evalCol
+    res.evalFile = evalFile
+  }
+
+  if (native) {
+    res.native = true
+  }
+
+  if (fname) {
+    res.function = fname
+  }
+
+  if (meth && fname !== meth) {
+    res.method = meth
+  }
+
+  return res
+}
diff --git a/lib/synonyms.js b/lib/synonyms.js
new file mode 100644
index 0000000..5a8e369
--- /dev/null
+++ b/lib/synonyms.js
@@ -0,0 +1,92 @@
+// A list of all the synonyms of assert methods.
+// In addition to these, multi-word camelCase are also synonymized to
+// all lowercase and snake_case
+module.exports = multiword({
+  ok: ['true', 'assert'],
+  notOk: ['false', 'assertNot'],
+
+  error: ['ifError', 'ifErr'],
+  throws: ['throw'],
+  doesNotThrow: ['notThrow'],
+
+  // exactly the same.  ===
+  equal: [
+    'equals', 'isEqual', 'is', 'strictEqual', 'strictEquals', 'strictIs',
+    'isStrict', 'isStrictly'
+  ],
+
+  // not equal.  !==
+  not: [
+    'inequal', 'notEqual', 'notEquals', 'notStrictEqual', 'notStrictEquals',
+    'isNotEqual', 'isNot', 'doesNotEqual', 'isInequal'
+  ],
+
+  // deep equivalence.  == for scalars
+  same: [
+    'equivalent', 'looseEqual', 'looseEquals', 'deepEqual',
+    'deepEquals', 'isLoose', 'looseIs', 'isEquivalent'
+  ],
+
+  // deep inequivalence. != for scalars
+  notSame: [
+    'inequivalent', 'looseInequal', 'notDeep', 'deepInequal',
+    'notLoose', 'looseNot', 'notEquivalent', 'isNotDeepEqual',
+    'isNotDeeply', 'notDeepEqual', 'isInequivalent',
+    'isNotEquivalent'
+  ],
+
+  // deep equivalence, === for scalars
+  strictSame: [
+    'strictEquivalent', 'strictDeepEqual', 'sameStrict', 'deepIs',
+    'isDeeply', 'isDeep', 'strictDeepEquals'
+  ],
+
+  // deep inequivalence, !== for scalars
+  strictNotSame: [
+    'strictInequivalent', 'strictDeepInequal', 'notSameStrict', 'deepNot',
+    'notDeeply', 'strictDeepInequals', 'notStrictSame'
+  ],
+
+  // found has the fields in wanted, string matches regexp
+  match: [
+    'has', 'hasFields', 'matches', 'similar', 'like', 'isLike',
+    'includes', 'include', 'isSimilar', 'contains'
+  ],
+
+  notMatch: [
+    'dissimilar', 'unsimilar', 'notSimilar', 'unlike', 'isUnlike',
+    'notLike', 'isNotLike', 'doesNotHave', 'isNotSimilar', 'isDissimilar'
+  ],
+
+  type: [
+    'isa', 'isA'
+  ]
+})
+
+function multiword (obj) {
+  for (var i in obj) {
+    var list = obj[i]
+    var res = [ multiword_(i) ].concat(list.map(multiword_))
+    res = res.reduce(function (set, i) {
+      set.push.apply(set, i)
+      return set
+    }, [])
+    obj[i] = res
+  }
+  return obj
+}
+
+function multiword_ (str) {
+  var res = [ str ]
+  if (str.match(/[A-Z]/)) {
+    res.push(str.toLowerCase())
+    res.push(str.replace(/[A-Z]/g, function ($0) {
+      return '_' + $0.toLowerCase()
+    }))
+  }
+  return res
+}
+
+if (require.main === module) {
+  console.log(module.exports)
+}
diff --git a/lib/tap-assert.js b/lib/tap-assert.js
deleted file mode 100644
index 9838961..0000000
--- a/lib/tap-assert.js
+++ /dev/null
@@ -1,466 +0,0 @@
-// an assert module that returns tappable data for each assertion.
-var difflet = require('difflet')
-  , deepEqual = require('deep-equal')
-  , bufferEqual = require('buffer-equal')
-  , Buffer = require('buffer').Buffer
-
-module.exports = assert
-
-var syns = {}
-  , id = 1
-
-function assert (ok, message, extra) {
-  if (extra && extra.skip) return assert.skip(message, extra)
-
-  //console.error("assert %j", [ok, message, extra])
-  //if (extra && extra.skip) return assert.skip(message, extra)
-  //console.error("assert", [ok, message, extra])
-  ok = !!ok
-  var res = { id : id ++, ok: ok }
-
-  var caller = getCaller(extra && extra.error)
-  if (extra && extra.error) {
-    res.type = extra.error.name
-    res.message = extra.error.message
-    res.code = extra.error.code
-             || extra.error.type
-    res.errno = extra.error.errno
-    delete extra.error
-  }
-  if (caller.file) {
-    res.file = caller.file
-    res.line = +caller.line
-    res.column = +caller.column
-  }
-  res.stack = caller.stack
-
-  res.name = message || "(unnamed assert)"
-
-  if (extra) Object.keys(extra).forEach(function (k) {
-    if (!res.hasOwnProperty(k)) res[k] = extra[k]
-  })
-
-  // strings and objects are hard to diff by eye
-  if (!ok &&
-      res.hasOwnProperty("found") &&
-      res.hasOwnProperty("wanted") &&
-      res.found !== res.wanted) {
-    if (typeof res.wanted !== typeof res.found ||
-        typeof res.wanted === "object" && (!res.found || !res.wanted)) {
-      res.type = { found: typeof found
-                 , wanted: typeof wanted }
-    } else if (typeof res.wanted === "string") {
-      res.diff = diffString(res.found, res.wanted)
-    } else if (typeof res.wanted === "object") {
-      res.diff = diffObject(res.found, res.wanted)
-    }
-  }
-
-  //console.error("assert return", res)
-
-  return res
-}
-assert.ok = assert
-syns.ok = [ "true", "assert" ]
-
-
-function notOk (ok, message, extra) {
-  return assert(!ok, message, extra)
-}
-assert.notOk = notOk
-syns.notOk = [ "false", "notok" ]
-
-function error (er, message, extra) {
-  if (!er) {
-    // just like notOk(er)
-    return assert(!er, message, extra)
-  }
-  message = message || er.message
-  extra = extra || {}
-  extra.error = er
-  return assert.fail(message, extra)
-}
-assert.error = error
-syns.error = [ "ifError", "ifErr", "iferror" ]
-
-
-function pass (message, extra) {
-  return assert(true, message, extra)
-}
-assert.pass = pass
-
-function fail (message, extra) {
-  //console.error("assert.fail", [message, extra])
-  //if (extra && extra.skip) return assert.skip(message, extra)
-  return assert(false, message, extra)
-}
-assert.fail = fail
-
-function skip (message, extra) {
-  //console.error("assert.skip", message, extra)
-  if (!extra) extra = {}
-  return { id: id ++, skip: true, name: message || "",
-           explanation: extra.explanation || "" }
-}
-assert.skip = skip
-
-function throws (fn, wanted, message, extra) {
-  if (typeof wanted === "string") {
-    extra = message
-    message = wanted
-    wanted = null
-  }
-
-  if (extra && extra.skip) return assert.skip(message, extra)
-
-  var found = null
-  try {
-    fn()
-  } catch (e) {
-    found = { name: e.name, message: e.message }
-  }
-
-  extra = extra || {}
-
-  extra.found = found
-  if (wanted) {
-    wanted = { name: wanted.name, message: wanted.message }
-    extra.wanted = wanted
-  }
-
-  if (!message) {
-    message = "Expected to throw"
-    if (wanted) message += ": "+wanted.name + " " + wanted.message
-  }
-
-  return (wanted) ? assert.similar(found, wanted, message, extra)
-                  : assert.ok(found, message, extra)
-}
-assert.throws = throws
-
-
-function doesNotThrow (fn, message, extra) {
-  if (extra && extra.skip) return assert.skip(message, extra)
-  var found = null
-  try {
-    fn()
-  } catch (e) {
-    found = {name: e.name, message: e.message}
-  }
-  message = message || "Should not throw"
-
-  return assert.equal(found, null, message, extra)
-}
-assert.doesNotThrow = doesNotThrow
-
-
-function equal (a, b, message, extra) {
-  if (extra && extra.skip) return assert.skip(message, extra)
-  extra = extra || {}
-  message = message || "should be equal"
-  extra.found = a
-  extra.wanted = b
-  return assert(a === b, message, extra)
-}
-assert.equal = equal
-syns.equal = ["equals"
-             ,"isEqual"
-             ,"is"
-             ,"strictEqual"
-             ,"strictEquals"]
-
-
-function equivalent (a, b, message, extra) {
-  if (extra && extra.skip) return assert.skip(message, extra)
-  var extra = extra || {}
-  message = message || "should be equivalent"
-  extra.found = a
-  extra.wanted = b
-
-  if (Buffer.isBuffer(a) && Buffer.isBuffer(b)) {
-    return assert(bufferEqual(a, b), message, extra)
-  } else {
-    return assert(deepEqual(a, b), message, extra)
-  }
-}
-assert.equivalent = equivalent
-syns.equivalent = ["isEquivalent"
-                  ,"looseEqual"
-                  ,"looseEquals"
-                  ,"isDeeply"
-                  ,"same"
-                  ,"deepEqual"
-                  ,"deepEquals"]
-
-function strictDeepEqual (a, b, message, extra) {
-  if (extra && extra.skip) return assert.skip(message, extra)
-  var extra = extra || {}
-  message = message || "should be strictly equal"
-  extra.found = a
-  extra.wanted = b
-
-  if (Buffer.isBuffer(a) && Buffer.isBuffer(b)) {
-    return assert(bufferEqual(a, b), message, extra)
-  } else {
-    return assert(deepEqual(a, b, {strict: true}), message, extra)
-  }
-}
-assert.strictDeepEqual = strictDeepEqual
-syns.strictDeepEqual = ["isStrictEquivalent"
-                  ,"isStrictly"
-                  ,"exactSame"
-                  ,"strictDeepEqual"
-                  ,"strictDeepEquals"]
-
-
-function inequal (a, b, message, extra) {
-  if (extra && extra.skip) return assert.skip(message, extra)
-  extra = extra || {}
-  message = message || "should not be equal"
-  extra.found = a
-  extra.doNotWant = b
-  return assert(a !== b, message, extra)
-}
-assert.inequal = inequal
-syns.inequal = ["notEqual"
-               ,"notEquals"
-               ,"notStrictEqual"
-               ,"notStrictEquals"
-               ,"isNotEqual"
-               ,"isNot"
-               ,"not"
-               ,"doesNotEqual"
-               ,"isInequal"]
-
-
-function inequivalent (a, b, message, extra) {
-  if (extra && extra.skip) return assert.skip(message, extra)
-  extra = extra || {}
-  message = message || "should not be equivalent"
-  extra.found = a
-  extra.doNotWant = b
-  
-  if (Buffer.isBuffer(a) && Buffer.isBuffer(b)) {
-    return assert(!bufferEqual(a, b), message, extra)
-  } else {
-    return assert(!deepEqual(a, b), message, extra)
-  }
-}
-assert.inequivalent = inequivalent
-syns.inequivalent = ["notEquivalent"
-                    ,"notDeepEqual"
-                    ,"notDeeply"
-                    ,"notSame"
-                    ,"isNotDeepEqual"
-                    ,"isNotDeeply"
-                    ,"isNotEquivalent"
-                    ,"isInequivalent"]
-
-function similar (a, b, message, extra, flip) {
-  if (extra && extra.skip) return assert.skip(message, extra)
-  // test that a has all the fields in b
-  message = message || "should be similar"
-
-  if (typeof a === "string" &&
-      (Object.prototype.toString.call(b) === "[object RegExp]")) {
-    extra = extra || {}
-    extra.pattern = b
-    extra.string = a
-    var ok = a.match(b)
-    extra.match = ok
-    if (flip) ok = !ok
-    return assert.ok(ok, message, extra)
-  }
-
-  var isObj = assert(a && typeof a === "object", message, extra)
-  if (!isObj.ok) {
-    // not an object
-    if (a == b) isObj.ok = true
-    if (flip) isObj.ok = !isObj.ok
-    return isObj
-  }
-
-  var eq = flip ? inequivalent : equivalent
-  return eq(selectFields(a, b), b, message, extra)
-}
-assert.similar = similar
-syns.similar = ["isSimilar"
-               ,"has"
-               ,"hasFields"
-               ,"like"
-               ,"isLike"]
-
-function dissimilar (a, b, message, extra) {
-  if (extra && extra.skip) return assert.skip(message, extra)
-  message = message || "should be dissimilar"
-  return similar(a, b, message, extra, true)
-}
-assert.dissimilar = dissimilar
-syns.dissimilar = ["unsimilar"
-                  ,"notSimilar"
-                  ,"unlike"
-                  ,"isUnlike"
-                  ,"notLike"
-                  ,"isNotLike"
-                  ,"doesNotHave"
-                  ,"isNotSimilar"
-                  ,"isDissimilar"]
-
-function type (thing, t, message, extra) {
-  if (extra && extra.skip) return assert.skip(message, extra)
-  var name = t
-  if (typeof name === "function") name = name.name || "(anonymous ctor)"
-  //console.error("name=%s", name)
-  message = message || "type is "+name
-  var type = typeof thing
-  //console.error("type=%s", type)
-  if (!thing && type === "object") type = "null"
-  if (type === "object" && t !== "object") {
-    if (typeof t === "function") {
-      //console.error("it is a function!")
-      extra = extra || {}
-      extra.found = Object.getPrototypeOf(thing).constructor.name
-      extra.wanted = name
-      //console.error(thing instanceof t, name)
-      return assert.ok(thing instanceof t, message, extra)
-    }
-
-    //console.error("check prototype chain")
-    // check against classnames or objects in prototype chain, as well.
-    // type(new Error("asdf"), "Error")
-    // type(Object.create(foo), foo)
-    var p = thing
-    while (p = Object.getPrototypeOf(p)) {
-      if (p === t || p.constructor && p.constructor.name === t) {
-        type = name
-        break
-      }
-    }
-  }
-  //console.error(type, name, type === name)
-  return assert.equal(type, name, message, extra)
-}
-assert.type = type
-syns.type = ["isa"]
-
-// synonyms are helpful.
-Object.keys(syns).forEach(function (c) {
-  syns[c].forEach(function (s) {
-    Object.defineProperty(assert, s, { value: assert[c], enumerable: false })
-  })
-})
-
-// helpers below
-
-function selectFields (a, b) {
-  // get the values in A of the fields in B
-  var ret = Array.isArray(b) ? [] : {}
-  Object.keys(b).forEach(function (k) {
-    if (!a.hasOwnProperty(k)) return
-    var v = b[k]
-      , av = a[k]
-    if (v && av && typeof v === "object" && typeof av === "object"
-       && !(v instanceof Date)
-       && !(v instanceof RegExp)
-       && !(v instanceof String)
-       && !(v instanceof Boolean)
-       && !(v instanceof Number)
-       && !(Array.isArray(v))) {
-      ret[k] = selectFields(av, v)
-    } else ret[k] = av
-  })
-  return ret
-}
-
-function sortObject (obj) {
-  if (typeof obj !== 'object' || Array.isArray(obj) || obj === null) {
-    return obj
-  }
-   
-  return Object.keys(obj).sort().reduce(function (acc, key) {
-    acc[key] = sortObject(obj[key])
-    return acc
-  }, {})
-}
-
-function stringify (a) {
-  return JSON.stringify(sortObject(a), (function () {
-    var seen = []
-      , keys = []
-    return function (key, val) {
-      var s = seen.indexOf(val)
-      if (s !== -1) {
-        return "[Circular: "+keys[s]+"]"
-      }
-      if (val && typeof val === "object" || typeof val === "function") {
-        seen.push(val)
-        keys.push(val["!"] || val.name || key || "<root>")
-        if (typeof val === "function") {
-          return val.toString().split(/\n/)[0]
-        } else if (typeof val.toUTCString === "function") {
-          return val.toUTCString()
-        }
-      }
-      return val
-  }})())
-}
-
-function diffString (f, w) {
-  if (w === f) return null
-  var p = 0
-    , l = w.length
-  while (p < l && w.charAt(p) === f.charAt(p)) p ++
-  w = stringify(w).substr(1).replace(/"$/, "")
-  f = stringify(f).substr(1).replace(/"$/, "")
-  return diff(f, w, p)
-}
-
-function diffObject (f, w) {
-  return difflet({ indent : 2, comment : true }).compare(w, f)
-}
-
-function diff (f, w, p) {
-  if (w === f) return null
-  var i = p || 0 // it's going to be at least p. JSON can only be bigger.
-    , l = w.length
-  while (i < l && w.charAt(i) === f.charAt(i)) i ++
-  var pos = Math.max(0, i - 20)
-  w = w.substr(pos, 40)
-  f = f.substr(pos, 40)
-  var pointer = i - pos
-  return "FOUND:  "+f+"\n"
-       + "WANTED: "+w+"\n"
-       + (new Array(pointer + 9).join(" "))
-       + "^ (at position = "+p+")"
-}
-
-function getCaller (er) {
-  // get the first file/line that isn't this file.
-  if (!er) er = new Error
-  var stack = er.stack || ""
-  stack = stack.split(/\n/)
-  for (var i = 1, l = stack.length; i < l; i ++) {
-    var s = stack[i].match(/\(([^):]+):([0-9]+):([0-9]+)\)$/)
-    if (!s) continue
-    var file = s[1]
-      , line = +s[2]
-      , col = +s[3]
-    if (file.indexOf(__dirname) === 0) continue
-    if (file.match(/tap-test\/test.js$/)) continue
-    else break
-  }
-  var res = {}
-  if (file && file !== __filename && !file.match(/tap-test\/test.js$/)) {
-    res.file = file
-    res.line = line
-    res.column = col
-  }
-
-  res.stack = stack.slice(1).map(function (s) {
-    return s.replace(/^\s*at\s*/, "")
-  })
-
-  return res
-}
-
-
diff --git a/lib/tap-browser-harness.js b/lib/tap-browser-harness.js
deleted file mode 100644
index 9eaa0e4..0000000
--- a/lib/tap-browser-harness.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// this is just a harness that pipes to stdout.
-// It's the default one.
-module.exports = BrowserHarness
-
-var BrowserHarness = global.TAP_Browser_Harness
-  , inherits = require("inherits")
-  , Results = require("./tap-results")
-  , Harness = require("./tap-harness")
-  , Test = require("./tap-test")
-
-inherits(BrowserHarness, Harness)
-function BrowserHarness (outPipe) {
-  //console.error("calling BrowserHarness")
-  if (browserHarness) return browserHarness
-  if (!(this instanceof BrowserHarness)) {
-    return browserHarness = new BrowserHarness
-  }
-  browserHarness = global.TAP_Browser_Harness = this
-  Harness.call(this, Test)
-
-  if (outPipe) this.output.pipe(outPipe)
-
-  this.test = this.test.bind(this)
-
-  this.plan = this.plan.bind(this)
-
-  var output = this.output
-  this.on("childEnd", function (child) {
-    //console.error("childEnd in global harness")
-    //console.error(child.results)
-    // write out the stuff for this child.
-    //console.error("child.conf", child.conf)
-
-    // maybe write some other stuff about the number of tests in this
-    // thing, etc.  I dunno.
-    //console.error("child results", child.results)
-    this.results.list.forEach(function (res) {
-      //delete res.error
-      //console.error("child resuilt", res)
-      output.write(res)
-    })
-    //console.error("wrote child results")
-    this.results.list.length = 0
-  })
-
-  var streamEnded = false
-  this.on("end", function () {
-    //console.error("global ending the stream")
-    if (!streamEnded) {
-      this.results.list.forEach(function (res) {
-        output.write(res)
-      })
-      this.results.list.length = 0
-      output.end()
-      streamEnded = true
-    }
-  })
-
-  // TODO: handle global errors
-  // process.on("unhandledException", function (e) {
-  //   this.bailout("unhandled exception: " + e.message)
-  // })
-}
diff --git a/lib/tap-consumer.js b/lib/tap-consumer.js
deleted file mode 100644
index 0b991a5..0000000
--- a/lib/tap-consumer.js
+++ /dev/null
@@ -1,246 +0,0 @@
-module.exports = TapConsumer
-
-// pipe a stream into this that's emitting tap-formatted data,
-// and it'll emit "data" events with test objects or comment strings
-// and an "end" event with the final results.
-
-var yamlish = require("yamlish")
-  , Results = require("./tap-results")
-  , inherits = require("inherits")
-
-TapConsumer.decode = TapConsumer.parse = function (str) {
-  var tc = new TapConsumer
-    , list = []
-  tc.on("data", function (res) {
-    list.push(res)
-  })
-  tc.end(str)
-  tc.results.list = list
-  return tc.results
-}
-
-var Stream = require("stream").Stream
-inherits(TapConsumer, Stream)
-function TapConsumer () {
-  if (!(this instanceof TapConsumer)) {
-    return new TapConsumer
-  }
-
-  Stream.call(this)
-  this.results = new Results
-  this.readable = this.writable = true
-
-  this.on("data", function (res) {
-    if (typeof res === "object") this.results.add(res)
-  })
-
-  this._plan = null
-  this._buffer = ""
-  this._indent = []
-  this._current = null
-  this._actualCount = 0
-  this._passed = []
-  this._failed = []
-  //console.error("TapConsumer ctor done")
-}
-
-TapConsumer.prototype.bailedOut = false
-
-TapConsumer.prototype.write = function (chunk) {
-  if (!this.writable) this.emit("error", new Error("not writable"))
-  if (this.bailedOut) return true
-
-  this._buffer = this._buffer + chunk
-  // split it up into lines.
-  var lines = this._buffer.split(/\r?\n/)
-  // ignore the last line, since it might be incomplete.
-  this._buffer = lines.pop()
-
-  for (var i = 0, l = lines.length; i < l; i ++) {
-    //console.error([i, lines[i]])
-    // see if it's indented.
-    var line = lines[i]
-      , spaces = (this._indent.length && !line.trim())
-               || line.match(/^\s/)
-    // at this level, only interested in fully undented stuff.
-    if (spaces) {
-      var c = i
-      while (c < l && (!lines[c].trim() || lines[c].match(/^\s/))) {
-        this._indent.push(lines[c++])
-      }
-      //console.error(c-i, "indented", this._indent, this._current)
-      i = c - 1
-      continue
-    }
-    // some kind of line.  summary, ok, notok, comment, or garbage.
-    // this also finishes parsing any of the indented lines from before
-    this._parseLine(line)
-  }
-  return true
-}
-
-TapConsumer.prototype.end = function () {
-  // finish up any hanging indented sections or final buffer
-  if (this._buffer.match(/^\s/)) this._indent.push(this.buffer)
-  else this._parseLine(this._buffer)
-
-  if (!this.bailedOut &&
-      this._plan !== null &&
-      this.results.testsTotal !== this._plan) {
-    while (this._actualCount < this._plan) {
-      this.emit("data", {ok: false, name:"MISSING TEST",
-                         id:this._actualCount ++ })
-    }
-  }
-
-  this._parseLine("")
-  this._buffer = ""
-  this.writable = false
-  this.emit("end", null, this._actualCount, this._passed)
-}
-
-TapConsumer.prototype._parseLine = function (line) {
-  if (this.bailedOut) return
-  //console.error("_parseLine", [line])
-  // if there are any indented lines, and there is a
-  // current object already, then they belong to it.
-  // if there is not a current object, then they're garbage.
-  if (this._current && this._indent.length) {
-    this._parseIndented()
-  }
-  this._indent.length = 0
-  if (this._current) {
-    if (this._current.ok) this._passed.push(this._current.id)
-    else this._failed.push(this._current.id)
-    this.emit("data", this._current)
-  }
-  this._current = null
-  line = line.trim()
-  if (!line) return
-  // try to see what kind of line this is.
-
-  var bo
-  if (bo = line.match(/^bail out!\s*(.*)$/i)) {
-    this.bailedOut = true
-    // this.emit("error", new Error(line))
-    this.emit("bailout", bo[1])
-    return
-  }
-
-  if (line.match(/^#/)) { // just a comment
-    line = line.replace(/^#+/, "").trim()
-    // console.error("outputting comment", [line])
-    if (line) this.emit("data", line)
-    return
-  }
-
-  var plan = line.match(/^([0-9]+)\.\.([0-9]+)(?:\s+#(.*))?$/)
-  if (plan) {
-    var start = +(plan[1])
-      , end = +(plan[2])
-      , comment = plan[3]
-
-    // TODO: maybe do something else with this?
-    // it might be something like: "1..0 #Skip because of reasons"
-    this._plan = end
-    this.emit("plan", end, comment)
-    // plan must come before or after all tests.
-    if (this._actualCount !== 0) {
-      this._sawPlan = true
-    }
-    return
-  }
-
-  if (line.match(/^(not )?ok(?:\s+([0-9]+))?/)) {
-    this._parseResultLine(line)
-    return
-  }
-
-  // garbage.  emit as a comment.
-  //console.error("emitting", [line.trim()])
-  if (line.trim()) this.emit("data", line.trim())
-}
-
-TapConsumer.prototype._parseDirective = function (line) {
-  line = line.trim()
-  if (line.match(/^TODO\b/i)) {
-    return { todo:true, explanation: line.replace(/^TODO\s*/i, "") }
-  } else if (line.match(/^SKIP\b/i)) {
-    return { skip:true, explanation: line.replace(/^SKIP\s*/i, "") }
-  }
-}
-
-TapConsumer.prototype._parseResultLine = function (line) {
-  this._actualCount ++
-  if (this._sawPlan) {
-    this.emit("data", {ok: false, name:"plan in the middle of tests"
-                      ,id:this._actualCount ++})
-  }
-  var parsed = line.match(/^(not )?ok(?: ([0-9]+))?(?:(?: - )?(.*))?$/)
-    , ok = !parsed[1]
-    , id = +(parsed[2] || this._actualCount)
-    , rest = parsed[3] || ""
-    , name
-    , res = { id:id, ok:ok }
-
-  // split on un-escaped # characters
-
-  //console.log("# "+JSON.stringify([name, rest]))
-  rest = rest.replace(/([^\\])((?:\\\\)*)#/g, "$1\n$2").split("\n")
-  name = rest.shift()
-  rest = rest.filter(function (r) { return r.trim() }).join("#")
-  //console.log("# "+JSON.stringify([name, rest]))
-
-  // now, let's see if there's a directive in there.
-  var dir = this._parseDirective(rest.trim())
-  if (!dir) name += rest ? "#" + rest : ""
-  else {
-    res.ok = true
-    if (dir.skip) res.skip = true
-    else if (dir.todo) res.todo = true
-    if (dir.explanation) res.explanation = dir.explanation
-  }
-  res.name = name
-
-  //console.error(line, [ok, id, name])
-  this._current = res
-}
-
-TapConsumer.prototype._parseIndented = function () {
-  // pull yamlish block out
-  var ind = this._indent
-    , ys
-    , ye
-    , yind
-    , diag
-  //console.error(ind, this._indent)
-  for (var i = 0, l = ind.length; i < l; i ++) {
-    var line = ind[i]
-    if (line === undefined) continue
-    var lt = line.trim()
-
-    if (!ys) {
-      ys = line.match(/^(\s*)---(.*)$/)
-      if (ys) {
-        yind = ys[1]
-        diag = [ys[2]]
-        //console.error([line,ys, diag])
-        continue
-      } else if (lt) this.emit("data", lt)
-    } else if (ys && !ye) {
-      if (line === yind + "...") ye = true
-      else {
-        diag.push(line.substr(yind.length))
-      }
-    } else if (ys && ye && lt) this.emit("data", lt)
-  }
-  if (diag) {
-    //console.error('about to parse', diag)
-    diag = yamlish.decode(diag.join("\n"))
-    //console.error('parsed', diag)
-    Object.keys(diag).forEach(function (k) {
-      //console.error(this._current, k)
-      if (!this._current.hasOwnProperty(k)) this._current[k] = diag[k]
-    }, this)
-  }
-}
diff --git a/lib/tap-cov-html.js b/lib/tap-cov-html.js
deleted file mode 100644
index 3c1c192..0000000
--- a/lib/tap-cov-html.js
+++ /dev/null
@@ -1,78 +0,0 @@
-var fs = require('fs'),
-    path = require('path'),
-    asyncMap = require("slide").asyncMap,
-    util = require('util');
-
-var CovHtml = module.exports = function(cov_stats, cov_dir, cb) {
-  var index = [];
-
-  asyncMap(
-    Object.keys(cov_stats),
-    function(f, cb) {
-      var st = cov_stats[f],
-          missing_lines = st.missing.map(function(l) {
-            return l.number;
-          }),
-          out = '<!doctype html>\n<html lang="en">\n<head>\n  ' +
-                '<meta charset="utf-8">\n  <title>' +
-
-      f + ' (' + st.loc + ')</title>\n' +
-      '<style type="text/css">\n' + 
-      'li {\n' +
-      '  font-family: monospace;\n' +
-      '  white-space: pre;\n' +
-      '}\n' +
-      '</style>\n' +
-      '</head>\n<body>\n' +
-      '<h1>' + f + ' (' + st.loc + ')' + '</h1>' +
-      '<h2>Run: ' + (st.missing.length ? st.loc - st.missing.length : st.loc) + ', Missing: ' +
-      st.missing.length + ', Percentage: ' + st.percentage + '</h2>' +
-      '<h2>Source:</h2>\n' +
-      '<ol>\n' + 
-      st.lines.map(function(line) {
-        var number = line.number,
-            color = (missing_lines.indexOf(number) !== -1) ? '#fcc' : '#cfc';
-        return '<li id="L' + line.number + '" style="background-color: ' + color +
-               ';">' + line.source.replace(/</g, "<") + '</li>';
-      }).join('\n') + 
-      '</ol>\n' +
-      '<h2>Data</h2>\n'+
-      '<pre>' + util.inspect(st, true, Infinity, false).replace(/</g, "<") + '</pre></body>\n</html>';
-
-      fs.writeFile(
-        cov_dir + '/' + 
-        f.replace(process.cwd() + '/', '').replace(/\//g, '+') + '.html',
-        out,
-        'utf8',
-        function(err) {
-          if (err) {
-            throw err;
-          }
-          index.push(f);
-          cb();
-        });
-    },
-    function(err) {
-      if (err) {
-        throw err;
-      }
-      var out = '<!doctype html>\n<html lang="en">\n<head>\n  ' +
-          '<meta charset="utf-8">\n  <title>Coverage Index</title>\n</head>\n' +
-          '<body>\n<h1>Code Coverage Information</h1>\n<ul>' +
-          index.map(function(fname) {
-            return '<li><a href="' +
-            fname.replace(process.cwd() + '/', '').replace(/\//g, '+') + '.html' +
-            '">' + fname + '</a></li>';
-          }).join('\n') + '</ul>\n</body>\n</html>';
-
-      fs.writeFile(cov_dir + '/index.html', out, 'utf8', function(err) {
-        if (err) {
-          throw err;
-        }
-        cb();
-      });   
-    }
-  );
-};
-
-
diff --git a/lib/tap-global-harness.js b/lib/tap-global-harness.js
deleted file mode 100644
index 3b04131..0000000
--- a/lib/tap-global-harness.js
+++ /dev/null
@@ -1,80 +0,0 @@
-// this is just a harness that pipes to stdout.
-// It's the default one.
-module.exports = GlobalHarness
-
-var globalHarness = global.TAP_Global_Harness
-  , inherits = require("inherits")
-  , Results = require("./tap-results")
-  , Harness = require("./tap-harness")
-  , Test = require("./tap-test")
-
-inherits(GlobalHarness, Harness)
-function GlobalHarness () {
-  //console.error("calling GlobalHarness")
-  if (globalHarness) return globalHarness
-  if (!(this instanceof GlobalHarness)) {
-    return globalHarness = new GlobalHarness
-  }
-
-  globalHarness = global.TAP_Global_Harness = this
-  Harness.call(this, Test)
-
-  this.output.pipe(process.stdout)
-  //this.output.on("data", function () {
-  //  process.nextTick(process.stdout.flush.bind(process.stdout))
-  //})
-
-  this.test = this.test.bind(this)
-
-  this.plan = this.plan.bind(this)
-
-  var output = this.output
-  this.on("childEnd", function (child) {
-    //console.error("childEnd in global harness")
-    //console.error(child.results)
-    // write out the stuff for this child.
-    //console.error("child.conf", child.conf)
-
-    // maybe write some other stuff about the number of tests in this
-    // thing, etc.  I dunno.
-    //console.error("child results", child.results)
-    this.results.list.forEach(function (res) {
-      //delete res.error
-      //console.error("child resuilt", res)
-      output.write(res)
-    })
-    //console.error("wrote child results")
-    this.results.list.length = 0
-  })
-
-  var streamEnded = false
-  this.on("end", function () {
-    //console.error("global ending the stream")
-    if (!streamEnded) {
-      this.results.list.forEach(function (res) {
-        output.write(res)
-      })
-      this.results.list.length = 0
-      output.end()
-      streamEnded = true
-
-      // If we had fails, then make sure that the exit code
-      // reflects that failure.
-      var exitCode
-      if (!this.results.ok)
-        exitCode = process.exitCode = 1
-      if (exitCode !== 0) {
-        process.on('exit', function (code) {
-          if (code === 0)
-            process.exit(exitCode)
-        })
-      }
-    }
-  })
-
-  //this.on("end", this.output.end.bind(this.output))
-
-  process.on("unhandledException", function (e) {
-    this.bailout("unhandled exception: " + e.message)
-  })
-}
diff --git a/lib/tap-harness.js b/lib/tap-harness.js
deleted file mode 100644
index 1de0ca6..0000000
--- a/lib/tap-harness.js
+++ /dev/null
@@ -1,226 +0,0 @@
-// a thing that runs tests.
-// Every "test" is also a harness.  If they do not have a harness,
-// then they are attached to the defaut "global harness",
-// which writes its results to stdout.
-
-
-// TODO:
-// - Bailout should stop running any tests.
-// - "skip" in the test config obj should skip it.
-
-module.exports = Harness
-var EE = require("events").EventEmitter
-require("inherits")(Harness, EE)
-
-var Results = require("./tap-results")
-  , TapProducer = require("./tap-producer")
-  , assert = require("./tap-assert")
-
-function Harness (Test) {
-  if (!(this instanceof Harness)) return new Harness(Test)
-
-  //console.error("Test in "+this.constructor.name, Test)
-
-  this._Test = Test
-  this._plan = null
-  this._children = []
-  this._processing = false
-
-  this._testCount = 0
-  this._planSum = 0
-
-  this.results = new Results()
-  // emit result events on the harness.
-  //this.results.on("result", function (res) {
-  //  console.error("proxying result ev from res to harness")
-  //  this.emit("result", res)
-  //}.bind(this))
-  var me = this
-  this.results.on("result", this.emit.bind(this, "result"))
-
-  var p = this.process.bind(this)
-  this.process = function () {
-    this._processing = true
-    process.nextTick(p)
-  }
-
-  this.output = new TapProducer()
-  EE.call(this)
-}
-
-// this function actually only gets called bound to
-// the Harness object, and on process.nextTick.  Even if
-// passed as an event handler, everything *else* will
-// happen before it gets called.
-Harness.prototype.process = function () {
-  //console.error("harness process")
-  // "end" can emit multiple times, so only actually move on
-  // to the next test if the current one is actually over.
-  // TODO: multiple in-process tests, if all are marked "async"
-  if (this._current) {
-    if (!this._current._ended) return
-    // handle the current one before moving onto the next.
-    this.childEnd(this._current)
-  }
-  var skip = true
-  while (skip) {
-    //console.error("checking for skips")
-    var current = this._current = this._children.shift()
-    if (current) {
-      skip = current.conf.skip
-      if (skip) {
-        //console.error("add a failure for the skipping")
-        this.results.add(assert.fail(current.conf.name
-                                    ,{skip:true, diag:false}))
-      }
-    } else skip = false
-  }
-
-  // keep processing through skipped tests, instead of running them.
-  if (current && this._bailedOut) {
-    return this.process()
-  }
-
-  //console.error("got current?", !!current)
-  if (current) {
-    current.on("end", this.process)
-    current.emit("ready")
-    //console.error("emitted ready")
-    //console.error("_plan", this._plan, this.constructor.name)
-  } else if (!this._plan || (this._plan && this._plan === this._testCount)) {
-    //console.error("Harness process: no more left.  ending")
-    if (this._endNice) {
-      this._endNice()
-    } else {
-      this.end()
-    }
-  } else {
-    this._processing = false;
-  }
-}
-
-Harness.prototype.end = function () {
-  if (this._children.length) {
-    return this.process()
-  }
-  //console.error("harness end", this.constructor.name)
-  if (this._bailedOut) return
-
-  // can't call .end() more than once.
-  if (this._ended) {
-    //console.error("adding failure for end calling")
-    this.results.add(assert.fail("end called more than once"))
-  }
-
-  // see if the plan is completed properly, if there was one.
-  if (this._plan !== null) {
-    var total = this._testCount
-    if (total !== this._plan) {
-      this.results.add(assert.equal(total, this._plan, "test count != plan"))
-    }
-    this._plan = total
-  }
-
-  //console.error("setting ended true", this.constructor.name)
-  this._ended = true
-  this.emit("end")
-}
-
-Harness.prototype.plan = function (p) {
-  //console.error("setting plan", new Error().stack)
-  if (this._plan !== null) {
-    //console.error("about to add failure for calling plan")
-    return this.results.add(assert.fail("plan set multiple times"))
-  }
-  this._plan = p
-  if (p === 0 || this.results.testsTotal) {
-    this.end()
-  }
-}
-
-Harness.prototype.childEnd = function (child) {
-  //console.error("childEnd")
-  this._testCount ++
-  this._planSum += child._plan
-  //console.error("adding set of child.results")
-
-  this.results.add(child.conf.name || "(unnamed test)")
-  this.results.addSet(child.results)
-  this.emit("childEnd", child)
-  // was this planned?
-  if (this._plan === this._testCount) {
-    //console.error("plan", [this._plan, this._testCount])
-    return this.end()
-  }
-}
-
-function copyObj(o) {
-  var copied = {}
-  Object.keys(o).forEach(function (k) { copied[k] = o[k] })
-  return copied
-}
-
-Harness.prototype.test = function test (name, conf, cb) {
-  if (this._bailedOut) return
-
-  if (typeof conf === "function") cb = conf, conf = null
-  if (typeof name === "object") conf = name, name = null
-  if (typeof name === "function") cb = name, name = null
-
-  conf = (conf ? copyObj(conf) : {})
-  name = name || ""
-
-  //console.error("making test", [name, conf, cb])
-
-  // timeout: value in milliseconds. Defaults to 30s
-  // Set to Infinity to have no timeout.
-  if (isNaN(conf.timeout)) conf.timeout = 30000
-  var t = new this._Test(this, name, conf)
-  var self = this
-  if (cb) {
-    //console.error("attaching cb to ready event")
-    t.on("ready", function () {
-      if (!isNaN(conf.timeout) && isFinite(conf.timeout)) {
-        var timer = setTimeout(this.timeout.bind(this), conf.timeout)
-        var clear = function () {
-          clearTimeout(timer)
-        }
-        t.on("end", clear)
-        t.on("bailout", function (message) {
-          self.bailout(message)
-          clear()
-        })
-      }
-    })
-    t.on("ready", cb.bind(t, t))
-    // proxy the child results to this object.
-    //t.on("result", function (res) {
-    //  console.error("in harness, proxying result up")
-    //  t.results.add(res)
-    //})
-  }
-  return t
-}
-
-Harness.prototype.bailout = function (message) {
-  // console.error("Harness bailout", this.constructor.name)
-  message = message || ""
-  //console.error("adding bailout message result")
-  this.results.add({bailout: message})
-  // console.error(">>> results after bailout" , this.results)
-  this._bailedOut = true
-  this.emit("bailout", message)
-  this.output.end({bailout: message})
-}
-
-Harness.prototype.add = function (child) {
-  //console.error("adding child")
-  this._children.push(child)
-  if (!this._processing) this.process()
-}
-
-// the tearDown function is *always* guaranteed to happen.
-// Even if there's a bailout.
-Harness.prototype.tearDown = function (fn) {
-  this.on("end", fn)
-}
diff --git a/lib/tap-producer.js b/lib/tap-producer.js
deleted file mode 100644
index b70d0ce..0000000
--- a/lib/tap-producer.js
+++ /dev/null
@@ -1,131 +0,0 @@
-module.exports = TapProducer
-
-var Results = require("./tap-results")
-  , inherits = require("inherits")
-  , yamlish = require("yamlish")
-
-TapProducer.encode = function (result, diag) {
-  var tp = new TapProducer(diag)
-    , out = ""
-  tp.on("data", function (c) { out += c })
-  if (Array.isArray(result)) {
-    result.forEach(tp.write, tp)
-  } else tp.write(result)
-  tp.end()
-  return out
-}
-
-var Stream = require("stream").Stream
-inherits(TapProducer, Stream)
-function TapProducer (diag) {
-  Stream.call(this)
-  this.diag = diag
-  this.count = 0
-  this.readable = this.writable = true
-  this.results = new Results
-}
-
-TapProducer.prototype.trailer = true
-
-TapProducer.prototype.write = function (res) {
-  // console.error("TapProducer.write", res)
-  if (typeof res === "function") throw new Error("wtf?")
-  if (!this.writable) this.emit("error", new Error("not writable"))
-
-  if (!this._didHead) {
-    this.emit("data", "TAP version 13\n")
-    this._didHead = true
-  }
-
-  var diag = res.diag
-  if (diag === undefined) diag = this.diag
-
-  this.emit("data", encodeResult(res, this.count + 1, diag))
-
-  if (typeof res === "string") return true
-
-  if (res.bailout) {
-    var bo = "bail out!"
-    if (typeof res.bailout === "string") bo += " " + res.bailout
-    this.emit("data", bo)
-    return
-  }
-  this.results.add(res, false)
-
-  this.count ++
-}
-
-TapProducer.prototype.end = function (res) {
-  if (res) this.write(res)
-  // console.error("TapProducer end", res, this.results)
-  this.emit("data", "\n1.."+this.results.testsTotal+"\n")
-  if (this.trailer && typeof this.trailer !== "string") {
-    // summary trailer.
-    var trailer = "tests "+this.results.testsTotal + "\n"
-    if (this.results.pass) {
-      trailer += "pass  " + this.results.pass + "\n"
-    }
-    if (this.results.fail) {
-      trailer += "fail  " + this.results.fail + "\n"
-    }
-    if (this.results.skip) {
-      trailer += "skip  "+this.results.skip + "\n"
-    }
-    if (this.results.todo) {
-      trailer += "todo  "+this.results.todo + "\n"
-    }
-    if (this.results.bailedOut) {
-      trailer += "bailed out" + "\n"
-    }
-
-    if (this.results.testsTotal === this.results.pass) {
-      trailer += "\nok\n"
-    }
-    this.trailer = trailer
-  }
-  if (this.trailer) this.write(this.trailer)
-  this.writable = false
-  this.emit("end", null, this.count, this.ok)
-}
-
-function encodeResult (res, count, diag) {
-  // console.error(res, count, diag)
-  if (typeof res === "string") {
-    res = res.split(/\r?\n/).map(function (l) {
-      if (!l.trim()) return l.trim()
-      return "# " + l
-    }).join("\n")
-    if (res.substr(-1) !== "\n") res += "\n"
-    return res
-  }
-
-  if (res.bailout) return ""
-
-
-  if (!!process.env.TAP_NODIAG) diag = false
-  else if (!!process.env.TAP_DIAG) diag = true
-  else if (diag === undefined) diag = !res.ok
-
-  var output = ""
-  res.name = res.name && ("" + res.name).trim()
-  output += ( !res.ok ? "not " : "") + "ok " + count
-            + ( !res.name ? ""
-              : " " + res.name.replace(/[\r\n]/g, " ") )
-            + ( res.skip ? " # SKIP " + ( res.explanation || "" )
-              : res.todo ? " # TODO " + ( res.explanation || "" )
-              : "" )
-            + "\n"
-
-  if (!diag) return output
-  var d = {}
-    , dc = 0
-  Object.keys(res).filter(function (k) {
-    return k !== "ok" && k !== "name" && k !== "id"
-  }).forEach(function (k) {
-    dc ++
-    d[k] = res[k]
-  })
-  //console.error(d, "about to encode")
-  if (dc > 0) output += "  ---"+yamlish.encode(d)+"\n  ...\n"
-  return output
-}
diff --git a/lib/tap-results.js b/lib/tap-results.js
deleted file mode 100644
index 3243cd7..0000000
--- a/lib/tap-results.js
+++ /dev/null
@@ -1,72 +0,0 @@
-// A class for counting up results in a test harness.
-
-module.exports = Results
-
-var inherits = require("inherits")
-  , EventEmitter = require("events").EventEmitter
-
-inherits(Results, EventEmitter)
-
-function Results (r) {
-  //console.error("result constructor", r)
-  this.ok = true
-  this.addSet(r)
-}
-
-Results.prototype.addSet = function (r) {
-  //console.error("add set of results", r)
-  r = r || {ok: true}
-  ; [ "todo"
-    , "todoPass"
-    , "todoFail"
-    , "skip"
-    , "skipPass"
-    , "skipFail"
-    , "pass"
-    , "passTotal"
-    , "fail"
-    , "failTotal"
-    , "tests"
-    , "testsTotal" ].forEach(function (k) {
-      this[k] = (this[k] || 0) + (r[k] || 0)
-      //console.error([k, this[k]])
-    }, this)
-
-  this.ok = this.ok && r.ok && true
-  this.bailedOut = this.bailedOut || r.bailedOut || false
-  this.list = (this.list || []).concat(r.list || [])
-  this.emit("set", this.list)
-  //console.error("after addSet", this)
-}
-
-Results.prototype.add = function (r, addToList) {
-  if (typeof r === 'object') {
-    var pf = r.ok ? "pass" : "fail"
-      , PF = r.ok ? "Pass" : "Fail"
-
-    this.testsTotal ++
-    this[pf + "Total"] ++
-
-    if (r.skip) {
-      this["skip" + PF] ++
-      this.skip ++
-    } else if (r.todo) {
-      this["todo" + PF] ++
-      this.todo ++
-    } else {
-      this.tests ++
-      this[pf] ++
-    }
-
-    if (r.bailout || typeof r.bailout === "string") {
-      // console.error("Bailing out in result")
-      this.bailedOut = true
-    }
-    this.ok = !!(this.ok && (r.ok || r.skip || r.todo))
-  }
-
-  if (addToList === false) return
-  this.list = this.list || []
-  this.list.push(r)
-  this.emit("result", r)
-}
diff --git a/lib/tap-runner.js b/lib/tap-runner.js
deleted file mode 100644
index d3689f0..0000000
--- a/lib/tap-runner.js
+++ /dev/null
@@ -1,502 +0,0 @@
-var fs = require("fs")
-  , child_process = require("child_process")
-  , path = require("path")
-  , chain = require("slide").chain
-  , asyncMap = require("slide").asyncMap
-  , TapProducer = require("./tap-producer.js")
-  , TapConsumer = require("./tap-consumer.js")
-  , assert = require("./tap-assert.js")
-  , inherits = require("inherits")
-  , util = require("util")
-  , CovHtml = require("./tap-cov-html.js")
-  , glob = require("glob")
-
-  // XXX Clean up the coverage options
-  , doCoverage = process.env.TAP_COV
-               || process.env.npm_package_config_coverage
-               || process.env.npm_config_coverage
-
-module.exports = Runner
-
-inherits(Runner, TapProducer)
-
-function Runner (options, cb) {
-  this.options = options
-
-  var diag = this.options.diag
-  var dir = this.options.argv.remain
-  TapProducer.call(this, diag)
-
-  this.doCoverage = doCoverage
-  // An array of full paths to files to obtain coverage
-  this.coverageFiles = []
-  // The source of these files
-  this.coverageFilesSource = {}
-  // Where to write coverage information
-  this.coverageOutDir = this.options["coverage-dir"]
-  // Temporary test files bunkerified we'll remove later
-  this.f2delete = []
-  // Raw coverage stats, as read from JSON files
-  this.rawCovStats = []
-  // Processed coverage information, per file to cover:
-  this.covStats = {}
-
-  if (dir) {
-    var filesToCover = this.options.cover
-
-    if (doCoverage) {
-      var mkdirp = require("mkdirp")
-      this.coverageOutDir = path.resolve(this.coverageOutDir)
-      this.getFilesToCover(filesToCover)
-      var self = this
-      return mkdirp(this.coverageOutDir, '0755', function (er) {
-        if (er) return self.emit("error", er)
-        self.run(dir, cb)
-      })
-    }
-
-    this.run(dir, cb)
-  }
-}
-
-
-Runner.prototype.run = function() {
-  var self = this
-    , args = Array.prototype.slice.call(arguments)
-    , cb = args.pop() || finish
-
-  function finish (er) {
-    if (er) {
-      self.emit("error", er)
-    }
-
-    if (!doCoverage) return self.end()
-
-    // Cleanup temporary test files with coverage:
-    self.f2delete.forEach(function(f) {
-      fs.unlinkSync(f)
-    })
-    self.getFilesToCoverSource(function(err, data) {
-      if (err) {
-        self.emit("error", err)
-      }
-      self.getPerFileCovInfo(function(err, data) {
-        if (err) {
-          self.emit("error", err)
-        }
-        self.mergeCovStats(function(err, data) {
-          if (err) {
-            self.emit("error", err)
-          }
-          CovHtml(self.covStats, self.coverageOutDir, function() {
-            self.end()
-          })
-        })
-      })
-    })
-  }
-
-  if (Array.isArray(args[0])) {
-    args = args[0]
-  }
-  self.runFiles(args, "", cb)
-}
-
-Runner.prototype.runDir = function (dir, cb) {
-  var self = this
-  fs.readdir(dir, function (er, files) {
-    if (er) {
-      self.write(assert.fail("failed to readdir " + dir, { error: er }))
-      self.end()
-      return
-    }
-    files = files.sort(function(a, b) {
-      return a > b ? 1 : -1
-    })
-    files = files.filter(function(f) {
-      return !f.match(/^\./)
-    })
-    files = files.map(function(file) {
-      return path.resolve(dir, file)
-    })
-
-    self.runFiles(files, path.resolve(dir), cb)
-  })
-}
-
-
-// glob the filenames so that test/*.js works on windows
-Runner.prototype.runFiles = function (files, dir, cb) {
-  var self = this
-  var globRes = []
-  chain(files.map(function (f) {
-    return function (cb) {
-      glob(f, function (er, files) {
-        if (er)
-          return cb(er)
-        globRes.push.apply(globRes, files)
-        cb()
-      })
-    }
-  }), function (er) {
-    if (er)
-      return cb(er)
-    runFiles(self, globRes, dir, cb)
-  })
-}
-
-// set some default options for node debugging tests
-function setOptionsForDebug(self) {
-  // Note: we automatically increase the default timeout here. Yes
-  // the user can specify --timeout to increase, but by default,
-  // 30 seconds is not a long time to debug your test.
-  self.options.timeout = 1000000;
-
-  // Note: we automatically turn on stderr so user can see the 'debugger listening on port' message.
-  // Without this it looks like tap has hung..
-  self.options.stderr = true;
-}
-
-function runFiles(self, files, dir, cb) {
-  chain(files.map(function(f) {
-    return function (cb) {
-      if (self._bailedOut) return
-      var relDir = dir || path.dirname(f)
-        , fileName = relDir === "." ? f : f.substr(relDir.length + 1)
-
-      self.write(fileName)
-      fs.lstat(f, function(er, st) {
-        if (er) {
-          self.write(assert.fail("failed to stat " + f, {error: er}))
-          return cb()
-        }
-
-        var cmd = f, args = [], env = {}
-
-        if (path.extname(f) === ".js") {
-          cmd = process.execPath
-          if (self.options.gc) {
-            args.push("--expose-gc")
-          }
-          if (self.options.debug) {
-            args.push("--debug")
-            setOptionsForDebug(self)
-          }
-          if (self.options["debug-brk"]) {
-            args.push("--debug-brk")
-            setOptionsForDebug(self)
-          }
-          if (self.options.strict) {
-            args.push("--use-strict")
-          }
-          if (self.options.harmony) {
-            args.push("--harmony")
-          }
-          args.push(fileName)
-        } else if (path.extname(f) === ".coffee") {
-          cmd = "coffee"
-          args.push(fileName)
-        } else {
-          // Check if file is executable
-          if ((st.mode & parseInt('0100', 8)) && process.getuid) {
-            if (process.getuid() != st.uid) {
-              return cb()
-            }
-          } else if ((st.mode & parseInt('0010', 8)) && process.getgid) {
-            if (process.getgid() != st.gid) {
-              return cb()
-            }
-          } else if ((st.mode & parseInt('0001', 8)) == 0) {
-            return cb()
-          }
-          cmd = path.resolve(cmd)
-        }
-
-        if (st.isDirectory()) {
-          return self.runDir(f, cb)
-        }
-
-        if (doCoverage && path.extname(f) === ".js") {
-          var foriginal = fs.readFileSync(f, "utf8")
-            , fcontents = self.coverHeader() + foriginal + self.coverFooter()
-            , tmpBaseName = path.basename(f, path.extname(f))
-                          + ".with-coverage." + process.pid + path.extname(f)
-            , tmpFname = path.resolve(path.dirname(f), tmpBaseName)
-
-          fs.writeFileSync(tmpFname, fcontents, "utf8")
-          args.splice(-1, 1, tmpFname)
-        }
-
-        for (var i in process.env) {
-          env[i] = process.env[i]
-        }
-        env.TAP = 1
-
-        var cp = child_process.spawn(cmd, args, { env: env, cwd: relDir })
-          , out = ""
-          , err = ""
-          , tc = new TapConsumer()
-          , childTests = [f]
-
-        var timeout = setTimeout(function () {
-          if (!cp._ended) {
-            cp._timedOut = true
-            cp.kill()
-          }
-        }, self.options.timeout * 1000)
-
-        tc.on("data", function(c) {
-          self.emit("result", c)
-          self.write(c)
-        })
-
-        tc.on("bailout", function (message) {
-          clearTimeout(timeout)
-          console.log("# " + f.substr(process.cwd().length + 1))
-          process.stderr.write(err)
-          process.stdout.write(out + "\n")
-          self._bailedOut = true
-          cp._ended = true
-          cp.kill()
-        })
-
-        cp.stdout.pipe(tc)
-        cp.stdout.on("data", function (c) { out += c })
-        cp.stderr.on("data", function (c) {
-          if (self.options.stderr) process.stderr.write(c)
-          err += c
-        })
-
-        cp.on("close", function (code, signal) {
-          if (cp._ended) return
-          cp._ended = true
-          var ok = !cp._timedOut && code === 0
-          clearTimeout(timeout)
-          //childTests.forEach(function (c) { self.write(c) })
-          var res = { name: path.dirname(f).replace(process.cwd() + "/", "")
-                          + "/" + fileName
-                    , ok: ok
-                    , exit: code }
-
-          if (cp._timedOut)
-            res.timedOut = cp._timedOut
-          if (signal)
-            res.signal = signal
-
-          if (err) {
-            res.stderr = err
-            if (tc.results.ok &&
-                tc.results.tests === 0 &&
-                !self.options.stderr) {
-              // perhaps a compilation error or something else failed.
-              // no need if stderr is set, since it will have been
-              // output already anyway.
-              console.error(err)
-            }
-          }
-
-          // tc.results.ok = tc.results.ok && ok
-          tc.results.add(res)
-          res.command = '"'+[cmd].concat(args).join(" ")+'"'
-          self.emit("result", res)
-          self.emit("file", f, res, tc.results)
-          self.write(res)
-          self.write("\n")
-          if (doCoverage) {
-            self.f2delete.push(tmpFname)
-          }
-          cb()
-        })
-      })
-    }
-  }), cb)
-
-  return self
-}
-
-
-// Get an array of full paths to files we are interested into obtain
-// code coverage.
-Runner.prototype.getFilesToCover = function(filesToCover) {
-  var self = this
-  filesToCover = filesToCover.split(",").map(function(f) {
-    return path.resolve(f)
-  }).filter(function(f) {
-    var existsSync = fs.existsSync || path.existsSync;
-    return existsSync(f)
-  })
-
-  function recursive(f) {
-    if (path.extname(f) === "") {
-      // Is a directory:
-      fs.readdirSync(f).forEach(function(p) {
-        recursive(f + "/" + p)
-      })
-    } else {
-      self.coverageFiles.push(f)
-    }
-  }
-  filesToCover.forEach(function(f) {
-    recursive(f)
-  })
-}
-
-// Prepend to every test file to run. Note tap.test at the very top due it
-// "plays" with include paths.
-Runner.prototype.coverHeader = function() {
-  // semi here since we're injecting it before the first line,
-  // and don't want to mess up line numbers in the test files.
-  return "var ___TAP_COVERAGE = require("
-       + JSON.stringify(require.resolve("runforcover"))
-       + ").cover(/.*/g);"
-}
-
-// Append at the end of every test file to run. Actually, the stuff which gets
-// the coverage information.
-// Maybe it would be better to move into a separate file template so editing
-// could be easier.
-Runner.prototype.coverFooter = function() {
-  var self = this
-  // This needs to be a string with proper interpolations:
-  return [ ""
-  , "var ___TAP = require(" + JSON.stringify(require.resolve("./main.js")) + ")"
-  , "if (typeof ___TAP._plan === 'number') ___TAP._plan ++"
-  , "___TAP.test(" + JSON.stringify("___coverage") + ", function(t) {"
-  , "  var covFiles = " + JSON.stringify(self.coverageFiles)
-  , "    , covDir = " + JSON.stringify(self.coverageOutDir)
-  , "    , path = require('path')"
-  , "    , fs = require('fs')"
-  , "    , testFnBase = path.basename(__filename, '.js') + '.json'"
-  , "    , testFn = path.resolve(covDir, testFnBase)"
-  , ""
-  , "  function asyncForEach(arr, fn, callback) {"
-  , "    if (!arr.length) {"
-  , "      return callback()"
-  , "    }"
-  , "    var completed = 0"
-  , "    arr.forEach(function(i) {"
-  , "      fn(i, function (err) {"
-  , "        if (err) {"
-  , "          callback(err)"
-  , "          callback = function () {}"
-  , "        } else {"
-  , "          completed += 1"
-  , "          if (completed === arr.length) {"
-  , "            callback()"
-  , "          }"
-  , "        }"
-  , "      })"
-  , "    })"
-  , "  }"
-  , ""
-  , "  ___TAP_COVERAGE(function(coverageData) {"
-  , "    var outObj = {}"
-  , "    asyncForEach(covFiles, function(f, cb) {"
-  , "      if (coverageData[f]) {"
-  , "        var stats = coverageData[f].stats()"
-  , "          , stObj = stats"
-  , "        stObj.lines = stats.lines.map(function (l) {"
-  , "          return { number: l.lineno, source: l.source() }"
-  , "        })"
-  , "        outObj[f] = stObj"
-  , "      }"
-  , "      cb()"
-  , "    }, function(err) {"
-  , "      ___TAP_COVERAGE.release()"
-  , "      fs.writeFileSync(testFn, JSON.stringify(outObj))"
-  , "      t.end()"
-  , "    })"
-  , "  })"
-  , "})" ].join("\n")
-}
-
-
-Runner.prototype.getFilesToCoverSource = function(cb) {
-  var self = this
-  asyncMap(self.coverageFiles, function(f, cb) {
-    fs.readFile(f, "utf8", function(err, data) {
-      var lc = 0
-      if (err) {
-        cb(err)
-      }
-      self.coverageFilesSource[f] = data.split("\n").map(function(l) {
-        lc += 1
-        return { number: lc, source: l }
-      })
-      cb()
-    })
-  }, cb)
-}
-
-Runner.prototype.getPerFileCovInfo = function(cb) {
-  var self = this
-    , covPath = path.resolve(self.coverageOutDir)
-
-  fs.readdir(covPath, function(err, files) {
-    if (err) {
-      self.emit("error", err)
-    }
-    var covFiles = files.filter(function(f) {
-      return path.extname(f) === ".json"
-    })
-    asyncMap(covFiles, function(f, cb) {
-      fs.readFile(path.resolve(covPath, f), "utf8", function(err, data) {
-        if (err) {
-          cb(err)
-        }
-        self.rawCovStats.push(JSON.parse(data))
-        cb()
-      })
-    }, function(f, cb) {
-      fs.unlink(path.resolve(covPath, f), cb)
-    }, cb)
-  })
-}
-
-Runner.prototype.mergeCovStats = function(cb) {
-  var self = this
-  self.rawCovStats.forEach(function(st) {
-    Object.keys(st).forEach(function(i) {
-      // If this is the first time we reach this file, just add the info:
-      if (!self.covStats[i]) {
-        self.covStats[i] = {
-          missing: st[i].lines
-        }
-      } else {
-        // If we already added info for this file before, we need to remove
-        // from self.covStats any line not duplicated again (since it has
-        // run on such case)
-        self.covStats[i].missing = self.covStats[i].missing.filter(
-          function(l) {
-            return (st[i].lines.indexOf(l))
-          })
-      }
-    })
-  })
-
-  // This is due to a bug into
-  // chrisdickinson/node-bunker/blob/feature/add-coverage-interface
-  // which is using array indexes for line numbers instead of the right number
-  Object.keys(self.covStats).forEach(function(f) {
-    self.covStats[f].missing = self.covStats[f].missing.map(function(line) {
-      return { number: line.number, source: line.source }
-    })
-  })
-
-  Object.keys(self.coverageFilesSource).forEach(function(f) {
-    if (!self.covStats[f]) {
-      self.covStats[f] = { missing: self.coverageFilesSource[f]
-                          , percentage: 0
-      }
-    }
-    self.covStats[f].lines = self.coverageFilesSource[f]
-    self.covStats[f].loc = self.coverageFilesSource[f].length
-
-    if (!self.covStats[f].percentage) {
-      self.covStats[f].percentage =
-        1 - (self.covStats[f].missing.length / self.covStats[f].loc)
-    }
-
-  })
-  cb()
-}
diff --git a/lib/tap-test.js b/lib/tap-test.js
deleted file mode 100644
index ec73321..0000000
--- a/lib/tap-test.js
+++ /dev/null
@@ -1,110 +0,0 @@
-// This is a very simple test framework that leverages the tap framework
-// to run tests and output tap-parseable results.
-
-module.exports = Test
-
-var assert = require("./tap-assert")
-  , inherits = require("inherits")
-  , Results = require("./tap-results")
-  , Harness = require("./tap-harness")
-
-// tests are also test harnesses
-inherits(Test, Harness)
-
-function Test (harness, name, conf) {
-  //console.error("test ctor")
-  if (!(this instanceof Test)) return new Test(harness, name, conf)
-
-  Harness.call(this, Test)
-
-  conf.name = name || conf.name || "(anonymous)"
-  this.conf = conf
-
-  this.harness = harness
-  this.harness.add(this)
-}
-
-// it's taking too long!
-Test.prototype.timeout = function () {
-  // detect false alarms
-  if (this._ended) return
-  this.fail("Timeout!")
-  this.end()
-}
-
-Test.prototype.clear = function () {
-  this._started = false
-  this._ended = false
-  this._plan = null
-  this._bailedOut = false
-  this._testCount = 0
-  this.results = new Results()
-}
-
-// this gets called if a test throws ever
-Test.prototype.threw = function (ex) {
-  //console.error("threw!", ex.stack)
-  this.fail(ex.name + ": " + ex.message, { error: ex, thrown: true })
-  // may emit further failing tests if the plan is not completed
-  //console.error("end, because it threw")
-  if (!this._ended) this.end()
-}
-
-Test.prototype.comment = function (m) {
-  if (typeof m !== "string") {
-    return this.fail("Test.comment argument must be a string")
-  }
-  this.result("\n" + m.trim())
-}
-
-Test.prototype.result = function (res) {
-  this.results.add(res)
-  this._testCount ++
-  this.emit("result", res)
-  if (this._plan === this._testCount) {
-    process.nextTick(this._endNice.bind(this))
-  }
-}
-
-Test.prototype._endNice = function () {
-  if (!this._ended) this.end()
-}
-
-// parasitic
-// Who says you can't do multiple inheritance in js?
-Object.getOwnPropertyNames(assert).forEach(function (k) {
-  if (k === "prototype" || k === "name") return
-  var d = Object.getOwnPropertyDescriptor(assert, k)
-    , v = d.value
-  if (!v) return
-  d.value = assertParasite(v)
-  Object.defineProperty(Test.prototype, k, d)
-})
-
-function assertParasite (fn) { return function _testAssert () {
-  //console.error("_testAssert", fn.name, arguments)
-  if (this._bailedOut) return
-  var res = fn.apply(assert, arguments)
-  this.result(res)
-  return res
-}}
-
-// a few tweaks on the EE emit function, because
-// we want to catch all thrown errors and bubble up "bailout"
-Test.prototype.emit = (function (em) { return function (t) {
-  // bailouts bubble until handled
-  if (t === "bailout" &&
-      this.listeners(t).length === 0 &&
-      this.harness) {
-    return this.harness.bailout(arguments[1])
-  }
-
-  if (t === "error") return em.apply(this, arguments)
-  try {
-    em.apply(this, arguments)
-  } catch (ex) {
-    // any exceptions in a test are a failure
-    //console.error("caught!", ex.stack)
-    this.threw(ex)
-  }
-}})(Harness.prototype.emit)
diff --git a/lib/test.js b/lib/test.js
new file mode 100644
index 0000000..76951cf
--- /dev/null
+++ b/lib/test.js
@@ -0,0 +1,1314 @@
+module.exports = Test
+
+var Readable = require('stream').Readable
+if (!Readable || process.version.match(/^v0\.10/)) {
+  Readable = require('readable-stream').Readable
+}
+
+var util = require('util')
+util.inherits(Test, Readable)
+
+// A sigil object for implicit end() calls that should not
+// trigger an error if the user then calls t.end()
+var IMPLICIT = {}
+
+var yaml = require('js-yaml')
+var stack = require('./stack.js')
+var tapAsserts = require('./assert.js')
+var assert = require('assert')
+var spawn = require('child_process').spawn
+var Parser = require('tap-parser')
+var path = require('path')
+var Module = require('module').Module
+var fs = require('fs')
+var binpath = path.resolve(__dirname, '../bin')
+
+function hasOwn (obj, key) {
+  return Object.prototype.hasOwnProperty.call(obj, key)
+}
+
+function Test (options) {
+  options = options || {}
+
+  if (!(this instanceof Test)) {
+    return new Test(options)
+  }
+
+  this._autoend = !!options.autoend
+  this._name = ''
+  this._ok = true
+  this._pass = 0
+  this._fail = 0
+  this._skip = 0
+  this._todo = 0
+  this._count = 0
+  this._bailedOut = false
+  this._endEmitted = false
+  this._explicitEnded = false
+  this._multiEndThrew = false
+
+  if (Object.prototype.hasOwnProperty.call(options, 'bail')) {
+    this._bail = !!options.bail
+  } else {
+    this._bail = process.env.TAP_BAIL === '1'
+  }
+
+  this._passes = []
+  this._fails = []
+  this._skips = []
+  this._todos = []
+
+  this._plan = -1
+  this._queue = []
+  this._currentChild = null
+  this._ending = false
+  this._ended = false
+  this._planFinished = false
+
+  this._parent = null
+  this._printedVersion = false
+
+  this._startTime = process.hrtime()
+  this._calledAt = options.at || stack.at(this.test)
+  if (!this._calledAt || !this._calledAt.file) {
+    this._calledAt = stack.at(Test)
+  }
+
+  this._timer = null
+  this._timeout = 0
+  if (options.timeout !== Infinity &&
+    !isNaN(options.timeout) &&
+    options.timeout > 0) {
+    this.setTimeout(options.timeout)
+  }
+
+  Readable.apply(this, options)
+
+  // Bind all methods.
+  var bound = {}
+  for (var m in this) {
+    if (typeof this[m] === 'function') {
+      this[m] = this[m].bind(this)
+      bound[m] = true
+    }
+  }
+  Object.getOwnPropertyNames(Test.prototype).forEach(function (name) {
+    if (typeof this[name] === 'function' && !bound[name]) {
+      Object.defineProperty(this, name, {
+        value: this[name].bind(this),
+        enumerable: false,
+        configurable: true,
+        writable: true
+      })
+    }
+  }, this)
+}
+
+Test.prototype.tearDown = Test.prototype.teardown = function (fn) {
+  this.on('end', fn)
+}
+
+Test.prototype.setTimeout = function (n) {
+  if (n === Infinity) {
+    if (this._timer) {
+      clearTimeout(this._timer)
+    }
+    this._timeout = 0
+    return
+  }
+
+  if (isNaN(n) || n <= 0) {
+    throw new TypeError('setTimeout: number > 0 required')
+  }
+
+  this._timeout = n
+  if (this._timer) {
+    clearTimeout(this._timer)
+  }
+
+  var self = this
+  this._timer = setTimeout(function () {
+    self._onTimeout()
+  }, n)
+}
+
+Test.prototype._onTimeout = function () {
+  var s = this
+  while (s._currentChild && (s._currentChild instanceof Test)) {
+    s._queue = []
+    s.end(IMPLICIT)
+    s = s._currentChild
+  }
+
+  // anything that was pending will have to wait.
+  s.fail('timeout!', {
+    expired: this._name,
+    timeout: this._timeout,
+    at: this._calledAt
+  })
+  s.end(IMPLICIT)
+
+  this.endAll()
+}
+
+// Called when endAll() is fired and there's stuff in the queue
+Test.prototype._queueFail = function () {
+  var queue = this._queue
+  this._queue = []
+
+  queue.forEach(function (q) {
+    var what = q[0]
+    var msg = what + ' left in queue'
+    var extra = { at: this._calledAt }
+
+    switch (what) {
+      case 'test':
+        extra = q[2]
+        msg = 'child test left in queue: ' + (q[1] || '(unnamed)')
+        break
+
+      case 'printResult':
+        if (q[2] === 'test unfinished: ' + (this._name || '(unnamed)')) {
+          return
+        }
+        msg = (q[1] ? 'ok' : 'not ok') + ' - ' + q[2].trim()
+        msg = 'test point left in queue: ' + msg
+        extra = q[3]
+        extra.at = extra.at || null
+        break
+
+      case 'spawn':
+        extra = q[5]
+        extra.command = q[1]
+        extra.args = q[2]
+        extra.options = q[3]
+        msg = 'spawn left in queue: ' + (q[4] || '(unnamed)')
+        break
+
+      case 'end':
+        return
+    }
+
+    if (this._parent) {
+      extra.test = this._name
+    }
+
+    this.fail(msg, extra)
+  }, this)
+}
+
+Test.prototype.endAll = function () {
+  var child = this._currentChild
+
+  if (this._queue && this._queue.length) {
+    this._queueFail()
+  }
+
+  if (child) {
+    if (!child._ended && child.fail) {
+      var msg = 'test unfinished: ' + (child._name || '(unnamed)')
+      var extra = { at: child._calledAt }
+      if (child._plan !== -1) {
+        extra.plan = child._plan
+        extra.count = child._count
+      }
+      child.fail(msg, extra)
+    }
+
+    if (child.end) {
+      child.end(IMPLICIT)
+    }
+
+    if (child.endAll) {
+      child.endAll()
+    }
+
+    if (child.kill) {
+      child.kill('SIGTERM')
+    }
+
+    child._bailedOut = true
+  }
+
+  this.end(IMPLICIT)
+}
+
+Test.prototype._extraFromError = function (er, extra) {
+  extra = extra || {}
+  if (!er || typeof er !== 'object') {
+    extra.error = er
+    return extra
+  }
+
+  var message = er.message
+  var addName = true
+
+  if (!message && er.stack) {
+    message = er.stack.split('\n')[0]
+    addName = false
+  }
+
+  er.message = ''
+  var st = er.stack
+  if (st) {
+    st = st.split('\n')
+    // parse out the 'at' bit from the first line.
+    extra.at = stack.parseLine(st[1])
+    extra.stack = stack.clean(st)
+  }
+  er.message = message
+
+  if (addName && er.name) {
+    message = er.name + ': ' + message
+  }
+
+  Object.keys(er).forEach(function (k) {
+    if (k === 'message') {
+      return
+    }
+    extra[k] = er[k]
+  })
+
+  extra.message = message
+
+  return extra
+}
+
+Test.prototype.threw = function threw (er, extra, proxy) {
+  this._ok = false
+  this._threw = this._threw || er
+
+  if (this._name && !proxy) {
+    er.test = this._name
+  }
+
+  if (!extra) {
+    extra = this._extraFromError(er)
+  }
+
+  // If we've already ended, then try to pass this up the chain
+  // Presumably, eventually the root harness will catch it and
+  // deal with it, since that only ends on process exit.
+  if (this._ended) {
+    if (this._parent) {
+      return this._parent.threw(er, extra, true)
+    } else {
+      throw er
+    }
+  }
+
+  this.fail(extra.message || er.message, extra)
+  if (!proxy) {
+    this.end(IMPLICIT)
+  }
+}
+
+Test.prototype.pragma = function (set) {
+  if (this._bailedOut) {
+    return
+  }
+
+  if (this._currentChild) {
+    this._queue.push(['pragma', set])
+    return
+  }
+
+  for (var i in set) {
+    this.push('pragma ' + (set[i] ? '+' : '-') + i + '\n')
+  }
+}
+
+Test.prototype.plan = function (n, comment) {
+  if (this._bailedOut) {
+    return
+  }
+
+  if (this._currentChild) {
+    this._queue.push(['plan', n, comment])
+    return
+  }
+
+  if (this._plan !== -1) {
+    throw new Error('Cannot set plan more than once')
+  }
+
+  if (typeof n !== 'number' || n < 0) {
+    throw new Error('plan must be a number')
+  }
+
+  // Cannot get any tests after a trailing plan, or a plan of 0
+  var ending = false
+  if (this._count !== 0 || n === 0) {
+    ending = true
+  }
+
+  this._plan = n
+  comment = comment ? ' # ' + comment.trim() : ''
+  this.push('1..' + n + comment + '\n')
+
+  if (ending) {
+    this.end(IMPLICIT)
+  }
+}
+
+Test.prototype.test = function test (name, extra, cb) {
+  if (this._bailedOut) {
+    return
+  }
+
+  if (this._autoendTimer) {
+    clearTimeout(this._autoendTimer)
+  }
+
+  if (typeof name === 'function') {
+    cb = name
+    name = ''
+    extra = {}
+  } else if (typeof extra === 'function') {
+    cb = extra
+    extra = {}
+  }
+
+  if (!cb) {
+    extra = extra || {}
+    extra.todo = true
+  } else if (typeof cb !== 'function') {
+    throw new Error('test() requires a callback')
+  }
+
+  if (!name && cb && cb.name) {
+    name = cb.name
+  }
+
+  if (extra.skip || extra.todo) {
+    return this.pass(name, extra)
+  }
+
+  // will want this captured now in case child fails.
+  if (!hasOwn(extra, 'at')) {
+    extra.at = stack.at(test)
+  }
+
+  if (this._currentChild) {
+    this._queue.push(['test', name, extra, cb])
+    return
+  }
+
+  var child = new Test(extra)
+  name = name || '(unnamed test)'
+
+  child._name = name
+  child._parent = this
+  if (this._bail) {
+    child._bail = this._bail
+  }
+
+  this._currentChild = child
+  var self = this
+  childStream(self, child)
+  var results
+  child.on('complete', function (res) {
+    results = pruneFailures(res)
+  })
+  child.on('end', function () {
+    if (child._threw && child._ok) {
+      child._ok = false
+      extra.error = child._threw
+      if (extra.error.stack) {
+        extra.error.stack = stack.clean(extra.error.stack)
+      }
+    }
+    extra.results = results
+    self._currentChild = null
+    if (results) {
+      name += ' # time=' + results.time + 'ms'
+    }
+    self.ok(child._ok, name, extra)
+    if (!self._ended) {
+      self.push('\n')
+    }
+  })
+  child.on('bailout', function (message) {
+    rootBail(self, message)
+  })
+
+  // still need try/catch for synchronous errors
+  self._level = child
+  child.comment('Subtest: ' + name)
+  try {
+    var cbRet = cb(child)
+    if (cbRet && typeof cbRet.then === 'function') {
+      // promise
+      cbRet.then(function () {
+        child.end(IMPLICIT)
+      }, function (reason) {
+        child.fail(reason.message, child._extraFromError(reason))
+        child.end(IMPLICIT)
+      })
+    }
+  } catch (er) {
+    child.threw(er)
+  }
+  self._level = self
+}
+
+Test.prototype.current = function () {
+  var t = this
+  while (t._level && t !== t._level) {
+    t = t._level
+  }
+  return t
+}
+
+// stdin is a bit different than a typical child stream
+// It's not treated as a "child test", because we typically
+// don't want to indent it or treat as a suite in reporters.
+// This is most often used by the runner when - is passed
+// as an arg, to run a reporter on a previous run.
+// We DO however need to parse it to set the exit failure.
+Test.prototype.stdin = function (name, extra) {
+  if (typeof name === 'object') {
+    extra = name
+    name = null
+  }
+
+  if (!name) {
+    name = '/dev/stdin'
+  }
+
+  if (!extra) {
+    extra = {}
+  }
+
+  if (!extra.at) {
+    extra.at = stack.at(stdin)
+  }
+
+  if (this._currentChild) {
+    this._queue.push(['stdin', name, extra])
+    return
+  }
+
+  if (extra.skip) {
+    return this.pass(name, extra)
+  }
+
+  var stdin = process.stdin
+  this._currentChild = stdin
+  var start = process.hrtime()
+  var parser = new Parser()
+  var self = this
+
+  childStream(self, stdin)
+
+  stdin.on('data', function (c) {
+    parser.write(c)
+  })
+
+  stdin.emit('data', '# Subtest: ' + name + '\n')
+
+  stdin.on('end', function () {
+    parser.end()
+  })
+
+  parser.on('complete', function (res) {
+    self._currentChild = null
+    extra.results = pruneFailures(res)
+    var dur = process.hrtime(start)
+    var time = Math.round(dur[0] * 1e6 + dur[1] / 1e3) / 1e3
+    name += ' # time=' + time + 'ms'
+    self.ok(res.ok, name, extra)
+    if (!self._ended) {
+      self.push('\n')
+    }
+    self._processQueue()
+  })
+
+  parser.on('bailout', function (message) {
+    rootBail(self, message)
+  })
+
+  process.stdin.resume()
+}
+
+function pruneFailures (res) {
+  if (res.failures) {
+    res.failures = res.failures.filter(function (f) {
+      return f.tapError
+    })
+    if (!res.failures.length) {
+      delete res.failures
+    }
+  }
+  return res
+}
+
+function rootBail (self, message) {
+  var p = self
+  while (p._parent) {
+    p._bailedOut = true
+    p = p._parent
+  }
+  p.bailout(message)
+}
+
+function childStream (self, child) {
+  var bailedOut = false
+  var linebuf = ''
+  child.on('data', function (c) {
+    if (bailedOut) {
+      return
+    }
+    linebuf += c
+    var lines = linebuf.split('\n')
+    linebuf = lines.pop()
+    lines.forEach(function (line) {
+      if (bailedOut) {
+        return
+      }
+      if (line.match(/^\s*Bail out!/)) {
+        bailedOut = true
+      }
+      if (line.match(/^\s*TAP version \d+$/)) {
+        return
+      }
+      if (line.trim()) {
+        line = '    ' + line
+      }
+      self.push(line + '\n')
+    })
+  })
+  child.on('end', function () {
+    if (bailedOut) {
+      return
+    }
+    if (linebuf) {
+      self.push('    ' + linebuf + '\n')
+    }
+  })
+}
+
+Test.prototype.spawn = function spawnTest (cmd, args, options, name, extra) {
+  if (typeof args === 'string') {
+    args = [ args ]
+  }
+
+  args = args || []
+
+  if (typeof options === 'string') {
+    name = options
+    options = {}
+  }
+
+  options = options || {}
+  if (!name) {
+    if (cmd === process.execPath) {
+      name = args.map(function (a) {
+        if (a.indexOf(process.cwd()) === 0) {
+          return './' + a.substr(process.cwd().length + 1)
+        } else {
+          return a
+        }
+      }).join(' ')
+    } else {
+      name = cmd + ' ' + args.join(' ')
+    }
+  }
+
+  extra = extra || {}
+
+  assert.equal(typeof cmd, 'string')
+  assert(Array.isArray(args))
+  assert.equal(options && typeof options, 'object')
+  assert.equal(typeof name, 'string')
+  assert.equal(extra && typeof extra, 'object')
+
+  // stdout must be a pipe
+  if (options.stdio) {
+    if (typeof options.stdio === 'string') {
+      options.stdio = [ options.stdio, 'pipe', options.stdio ]
+    } else {
+      options.stdio[1] = 'pipe'
+    }
+  }
+
+  // will want this captured now in case child fails, before enqueue
+  if (!hasOwn(extra, 'at')) {
+    extra.at = stack.at(spawnTest)
+  }
+
+  if (this._autoendTimer) {
+    clearTimeout(this._autoendTimer)
+  }
+
+  if (this._currentChild) {
+    this._queue.push(['spawn', cmd, args, options, name, extra])
+    return
+  }
+
+  if (extra.skip || extra.todo) {
+    return this.pass(name, extra)
+  }
+
+  if (this._bail || options.bail) {
+    if (!options.env) {
+      options.env = Object.keys(process.env).reduce(function (env, k) {
+        env[k] = process.env[k]
+        return env
+      }, {})
+    }
+    options.env.TAP_BAIL = '1'
+  }
+
+  var start = process.hrtime()
+  var child = spawn(cmd, args, options)
+  var parser = new Parser()
+  var self = this
+  this._currentChild = child
+
+  childStream(self, child.stdout)
+
+  child.stdout.on('data', function (c) {
+    parser.write(c)
+  })
+
+  if (this._bail) {
+    bailOnFail(this, child.stdout, parser)
+  }
+
+  child.stdout.emit('data', '# Subtest: ' + name + '\n')
+  // The only thing that's actually *required* to be a valid TAP output
+  // is a plan and/or at least one ok/not-ok line.  If we don't get any
+  // of those, then emit a bogus 1..0 so we read it as a skip.
+  var sawTests = false
+  parser.once('assert', function () {
+    sawTests = true
+  })
+
+  parser.once('plan', function () {
+    sawTests = true
+  })
+
+  child.stdout.on('end', function () {
+    parser.write('\n')
+    if (!sawTests) {
+      child.stdout.emit('data', '1..0\n')
+    }
+    parser.end()
+  })
+
+  var results
+  parser.on('complete', function (res) {
+    results = pruneFailures(res)
+  })
+
+  if (extra.timeout) {
+    var timer = setTimeout(function () {
+      extra.failure = 'timeout'
+      child.kill('SIGTERM')
+      // give it 1 more second to finish up
+      timer = setTimeout(function () {
+        child.stdout.emit('data', '\n\nnot ok - timeout\n\n')
+        child.kill('SIGKILL')
+      }, 1000)
+    }, extra.timeout)
+  }
+
+  child.on('close', function onclose (code, signal) {
+    clearTimeout(timer)
+    self._currentChild = null
+    extra.results = results
+    var dur = process.hrtime(start)
+    var time = Math.round(dur[0] * 1e6 + dur[1] / 1e3) / 1e3
+    if (code) {
+      extra.exitCode = code
+    }
+    if (signal) {
+      extra.signal = signal
+    }
+    extra.command = cmd
+    extra.arguments = args
+
+    if (signal || code) {
+      results.ok = false
+    }
+
+    if (results.count === 0 && !signal && !code) {
+      extra.skip = 'No tests found'
+      if (results.plan && results.plan.skipReason) {
+        extra.skip = results.plan.skipReason
+      }
+    } else {
+      name += ' # time=' + time + 'ms'
+    }
+
+    self.ok(results.ok && !code && !signal, name, extra)
+    if (!self._ended) {
+      self.push('\n')
+    }
+    self._processQueue()
+  })
+
+  parser.on('bailout', function (message) {
+    child.kill('SIGTERM')
+    rootBail(self, message)
+  })
+
+  if (child.stderr) {
+    child.stderr.on('data', function (c) {
+      process.stderr.write(c)
+    })
+  }
+}
+
+function bailOnFail (self, stream, parser) {
+  parser.on('child', function (c) {
+    bailOnFail(self, stream, c)
+  })
+
+  parser.on('assert', function (res) {
+    if (!res.todo && !res.skip && !res.ok) {
+      var ind = new Array(parser.level * 4 + 1).join(' ')
+      parser.buffer = ''
+      stream.emit('data', ind + 'Bail out! # ' + res.name + '\n')
+    }
+  })
+}
+
+Test.prototype.done = Test.prototype.end = function end (implicit) {
+  if (this._bailedOut) {
+    return
+  }
+
+  if (this._currentChild) {
+    this._queue.push(['end'])
+    return
+  }
+
+  if (implicit !== IMPLICIT && !this._multiEndThrew) {
+    if (this._explicitEnded) {
+      this._multiEndThrew = true
+      this.threw(new Error('test end() method called more than once'))
+      return
+    }
+    this._explicitEnded = true
+  }
+
+  if (this._ended || this._ending) {
+    return
+  }
+
+  clearTimeout(this._timer)
+  // Emiting the missing tests can trigger a call to end()
+  // guard against that with the 'ending' flag
+  this._ending = true
+  var missing = this._plan - this._count
+  while (missing > 0) {
+    this.fail('missing test', { at: false })
+    missing--
+  }
+
+  if (this._plan === -1) {
+    this.plan(this._count)
+  }
+
+  var final = {
+    plan: { start: 1, end: this._plan },
+    count: this._count,
+    pass: this._pass,
+    ok: this._ok
+  }
+
+  if (this._fail) {
+    final.fail = this._fail
+  }
+
+  if (this._bailedOut) {
+    final.bailout = true
+  }
+
+  if (this._todo) {
+    final.todo = this._todo
+  }
+
+  if (this._skip) {
+    final.skip = this._skip
+  }
+
+  this._ended = true
+
+  // This is nice, but too noisy.
+  // TODO: prove-style commenting at the end.
+  // this.comment(final)
+  if (!this._ok) {
+    // comment a bit at the end so we know what happened.
+    if (this._plan !== this._count) {
+      this.comment('actual test count(%d) != plan(%d)',
+        this._count, this._plan)
+    }
+
+    if (this._fail > 0) {
+      this.comment('failed %d of %d tests', this._fail, this._count)
+    }
+    if (this._todo > 0) {
+      this.comment('todo: %d', this._todo)
+    }
+    if (this._skip > 0) {
+      this.comment('skip: %d', this._skip)
+    }
+  }
+
+  var dur = process.hrtime(this._startTime)
+  final.time = Math.round(dur[0] * 1e6 + dur[1] / 1e3) / 1e3
+  if (!this._parent) {
+    this.comment('time=%sms', final.time)
+  }
+
+  this.emit('complete', final)
+  if (!this._endEmitted) {
+    this._endEmitted = true
+    this.emit('end')
+    if (this._parent) {
+      this._parent._processQueue()
+    }
+  }
+}
+
+Test.prototype._processQueue = function () {
+  if (this._bailedOut) {
+    return
+  }
+
+  if (this._processingQueue) {
+    return
+  }
+
+  this._processingQueue = true
+  while (this._queue.length && !this._currentChild) {
+    var q = this._queue.shift()
+    var m = q.shift()
+    this[m].apply(this, q)
+  }
+  this._processingQueue = false
+}
+
+Test.prototype._shouldAutoend = function () {
+  var should = (
+  this._autoend &&
+    !this._currentChild &&
+    !this._queue.length &&
+    !this._ending &&
+    this._plan === -1
+  )
+  return should
+}
+
+Test.prototype._maybeAutoend = function () {
+  if (this._autoendTimer) {
+    clearTimeout(this._autoendTimer)
+  }
+
+  if (this._shouldAutoend()) {
+    var self = this
+    self._autoendTimer = setTimeout(function () {
+      if (self._shouldAutoend()) {
+        self._autoendTimer = setTimeout(function () {
+          if (self._shouldAutoend()) {
+            self.end(IMPLICIT)
+            self._bailedOut = true
+          }
+        })
+      }
+    })
+  }
+}
+
+Test.prototype.autoend = function () {
+  this._autoend = true
+  this._maybeAutoend()
+}
+
+Test.prototype._read = function (n, cb) {}
+
+Test.prototype.printResult = function printResult (ok, message, extra) {
+  if (this._bailedOut) {
+    return
+  }
+
+  if (typeof ok !== 'boolean') {
+    throw new TypeError('boolean `ok` required')
+  }
+
+  if (message === undefined) {
+    throw new TypeError('string `message` required')
+  }
+
+  message += ''
+
+  if (extra && typeof extra !== 'object') {
+    throw new TypeError('optional `extra` arg must be object')
+  }
+
+  extra = extra || {}
+
+  var n = this._count + 1
+  if (this._plan !== -1 && n > this._plan) {
+    // Don't over-report failures.
+    // it's already had problems, just ignore this.
+    if (!this._ok) {
+      return
+    }
+
+    var er = new Error('test count exceeds plan')
+    Error.captureStackTrace(er, this._currentAssert || printResult)
+    var c = this
+    var name = this._name
+    for (c = c._parent; c; c = c.parent) {
+      name += ' < ' + (c._name || '(unnamed test)')
+    }
+    er.test = name
+    er.count = n
+    er.plan = this._plan
+    throw er
+  }
+
+  if (this._ended) {
+    throw new Error('test after end() was called')
+  }
+
+  var fn = this._currentAssert
+  this._currentAssert = null
+  if (!ok && !extra.skip && !hasOwn(extra, 'at')) {
+    assert.equal(typeof fn, 'function')
+    extra.at = stack.at(fn)
+    if (!extra.todo) {
+      extra.stack = stack.captureString(80, fn)
+    }
+  }
+
+  if (this._autoendTimer) {
+    clearTimeout(this._autoendTimer)
+  }
+
+  if (this._currentChild) {
+    // Might get abandoned in queue
+    if (!hasOwn(extra, 'at')) {
+      extra.at = stack.at(fn)
+    }
+    this._queue.push(['printResult', ok, message, extra])
+    return
+  }
+
+  var res = { ok: ok, message: message, extra: extra }
+
+  if (extra.todo) {
+    this._todo++
+    this._todos.push(res)
+  }
+
+  if (extra.skip) {
+    this._skip++
+    this._skips.push(res)
+  }
+
+  if (ok) {
+    this._pass++
+    this._passes.push(res)
+  } else {
+    this._fail++
+    this._fails.push(res)
+  }
+  this._count++
+
+  this.emit('result', res)
+  if (message) {
+    message = ' - ' + message
+  }
+
+  message = message.replace(/[\n\r]/g, ' ').replace(/\t/g, '  ')
+
+  this.push(util.format('%s %d%s', ok ? 'ok' : 'not ok', n, message))
+
+  var ending = false
+  if (n === this._plan) {
+    ending = true
+  }
+
+  if (extra.skip) {
+    this.push(' # SKIP')
+    if (typeof extra.skip === 'string') {
+      this.push(' ' + extra.skip)
+    }
+  } else if (extra.todo) {
+    this._todo++
+    this.push(' # TODO')
+    if (typeof extra.todo === 'string') {
+      this.push(' ' + extra.todo)
+    }
+  }
+
+  if (!ok && !extra.todo && !extra.skip) {
+    this._ok = false
+  }
+
+  this.push('\n')
+
+  // If we're skipping, no need for diags.
+  if (!ok && !extra.skip) {
+    this.writeDiags(extra)
+  }
+
+  if (this._bail && !ok && !extra.skip && !extra.todo) {
+    this.bailout(message.replace(/^ - /, ''))
+  }
+
+  if (ending) {
+    this.end(IMPLICIT)
+  }
+
+  this._maybeAutoend()
+}
+
+function cleanYamlObj (object, isRoot, seen) {
+  if (object === undefined) {
+    return null
+  }
+
+  if (typeof object === 'function') {
+    return object.toString()
+  }
+
+  if (Buffer.isBuffer(object)) {
+    return 'Buffer\n' + object.toString('hex').split('').reduce(function (set, c) {
+      if (set.length && set[set.length - 1].length === 1) {
+        set[set.length - 1] += c
+        if (set.length && set.length % 20 === 0) {
+          set[set.length - 1] += '\n'
+        } else {
+          set[set.length - 1] += ' '
+        }
+      } else {
+        set.push(c)
+      }
+      return set
+    }, []).join('').trim()
+  }
+
+  if (object && typeof object === 'object') {
+    if (object instanceof RegExp) {
+      return object.toString()
+    }
+
+    var isArray = Array.isArray(object)
+
+    // Fill in any holes.  This means we lose expandos,
+    // but we were gonna lose those anyway.
+    if (isArray) {
+      object = Array.apply(null, object)
+    }
+
+    var set = isArray ? [] : {}
+    var keys = Object.getOwnPropertyNames(object)
+    var stack = null
+    var newObj = keys.reduce(function (set, k) {
+      if (isRoot && (k === 'todo' || k === 'skip')) {
+        return set
+      }
+
+      // magic property!
+      if (isArray && k === 'length') {
+        return set
+      }
+
+      // Don't dump massive EventEmitter and Domain
+      // objects onto the output, that's never friendly.
+      if (object &&
+        typeof object === 'object' &&
+        object instanceof Error &&
+        /^domain/.test(k)) {
+        return set
+      }
+
+      if (isRoot && k === 'at' && !object[k]) {
+        return set
+      }
+
+      if (isRoot && k === 'stack') {
+        stack = object[k]
+        return set
+      }
+
+      if (seen.indexOf(object[k]) !== -1) {
+        set[k] = '[Circular]'
+      } else {
+        set[k] = cleanYamlObj(object[k], false, seen.concat([object]))
+      }
+      return set
+    }, set)
+    if (stack) {
+      newObj.stack = stack
+    }
+    return newObj
+  }
+
+  return object
+}
+
+Test.prototype.writeDiags = function (extra) {
+  var file = extra.at && extra.at.file && path.resolve(extra.at.file)
+  if (file && (file.indexOf(__dirname) === 0 || file.indexOf(binpath) === 0)) {
+    delete extra.at
+  }
+
+  if (extra.at && extra.at.file && extra.at.line && !extra.source) {
+    var content
+    file = path.resolve(extra.at.file)
+    try {
+      content = Module.wrap(fs.readFileSync(file))
+    } catch (er) {}
+    if (content) {
+      content = (content.split('\n')[extra.at.line - 1] || '').trim()
+      if (content) {
+        extra.source = content + '\n'
+      }
+    }
+  }
+
+  // some objects are not suitable for yaml.
+  var obj = cleanYamlObj(extra, true, [])
+
+  if (obj && typeof obj === 'object' && Object.keys(obj).length) {
+    var y = yaml.safeDump(obj).split('\n').map(function (l) {
+      return l.trim() ? '  ' + l : l.trim()
+    }).join('\n')
+    y = '  ---\n' + y + '  ...\n'
+    this.push(y)
+  }
+}
+
+Test.prototype.passing = function () {
+  return this._ok
+}
+
+Test.prototype.pass = function pass (message, extra) {
+  if (!this._currentAssert) {
+    this._currentAssert = pass
+  }
+  this.printResult(true, message || '(unnamed test)', extra)
+  return true
+}
+
+Test.prototype.fail = function fail (message, extra) {
+  if (!this._currentAssert) {
+    this._currentAssert = fail
+  }
+
+  if (message && typeof message === 'object') {
+    extra = message
+    message = ''
+  } else {
+    if (!message) {
+      message = ''
+    }
+    if (!extra) {
+      extra = {}
+    }
+  }
+
+  this.printResult(false, message, extra)
+
+  var ret = true
+  if (!extra.todo && !extra.skip) {
+    this._ok = ret = false
+  }
+
+  return ret
+}
+
+Test.prototype.addAssert = function (name, length, fn) {
+  if (!name) {
+    throw new TypeError('name is required for addAssert')
+  }
+
+  if (!(typeof length === 'number' && length >= 0)) {
+    throw new TypeError('number of args required')
+  }
+
+  if (typeof fn !== 'function') {
+    throw new TypeError('function required for addAssert')
+  }
+
+  if (Test.prototype[name] || this[name]) {
+    throw new TypeError('attempt to re-define `' + name + '` assert')
+  }
+
+  this[name] = function ASSERT () {
+    if (!this._currentAssert) {
+      this._currentAssert = ASSERT
+    }
+    var args = new Array(length + 2)
+    for (var i = 0; i < length; i++) {
+      args[i] = arguments[i]
+    }
+    if (typeof arguments[length] === 'object') {
+      args[length] = ''
+      args[length + 1] = arguments[length]
+    } else {
+      args[length] = arguments[length] || ''
+      args[length + 1] = arguments[length + 1] || {}
+    }
+
+    return fn.apply(this, args)
+  }
+}
+
+Test.prototype.comment = function () {
+  if (this._bailedOut) {
+    return
+  }
+
+  if (this._currentChild) {
+    this._queue.push(['comment', message])
+    return
+  }
+
+  var message = util.format.apply(util, arguments)
+  message = '# ' + message.split(/\r?\n/).join('\n# ')
+
+  this.push(message + '\n')
+}
+
+Test.prototype.push = function (c, e) {
+  if (this._bailedOut) {
+    return true
+  }
+
+  // We *always* want data coming out immediately.  Test runners have a
+  // very special relationship with zalgo. It's more important to ensure
+  // that any console.log() lines that appear in the midst of tests are
+  // not taken out of context
+  if (this._readableState) {
+    this._readableState.sync = false
+  }
+
+  if (!this._printedVersion && !this._parent) {
+    this._printedVersion = true
+    Readable.prototype.push.call(this, 'TAP version 13\n')
+  }
+
+  return Readable.prototype.push.call(this, c, e)
+}
+
+Test.prototype.bailout = function (message) {
+  message = message ? ' # ' + ('' + message).trim() : ''
+  message = message.replace(/^( #)+ /, ' # ')
+  message = message.replace(/[\r\n]/g, ' ')
+  this.push('Bail out!' + message + '\n')
+  this._bailedOut = true
+  this._ok = false
+  this.emit('bailout', message)
+  this.push(null)
+}
+
+// Add all the asserts
+tapAsserts.decorate(Test.prototype)
diff --git a/package.json b/package.json
index 1f99390..db3ba7c 100644
--- a/package.json
+++ b/package.json
@@ -1,41 +1,68 @@
 {
   "name": "tap",
-  "version": "0.7.1",
+  "version": "4.0.0",
   "author": "Isaac Z. Schlueter <i at izs.me> (http://blog.izs.me)",
   "description": "A Test-Anything-Protocol library",
-  "bin": "bin/tap.js",
-  "main": "lib/main.js",
+  "bin": {
+    "tap": "bin/run.js"
+  },
+  "main": "lib/root.js",
   "engines": {
     "node": ">=0.8"
   },
   "dependencies": {
-    "buffer-equal": "~0.0.0",
-    "deep-equal": "^1.0.0",
-    "difflet": "~0.2.0",
-    "glob": "^4.3.5",
-    "inherits": "*",
+    "codecov.io": "^0.1.6",
+    "coveralls": "^2.11.2",
+    "deeper": "^2.1.0",
+    "foreground-child": "^1.2.0",
+    "glob": "^6.0.1",
+    "js-yaml": "^3.3.1",
     "mkdirp": "^0.5.0",
-    "nopt": "^3.0.1",
-    "runforcover": "~0.0.2",
-    "slide": "*",
-    "yamlish": "*"
+    "nyc": "^5.1.0",
+    "only-shallow": "^1.0.2",
+    "opener": "^1.4.1",
+    "readable-stream": "^2.0.2",
+    "signal-exit": "^2.0.0",
+    "supports-color": "^1.3.1",
+    "tap-mocha-reporter": "0.0 || 1",
+    "tap-parser": "^1.2.2",
+    "tmatch": "^1.0.2"
   },
   "keywords": [
     "assert",
     "test",
     "tap"
   ],
-  "contributors": [
-    "Isaac Z. Schlueter <i at izs.me> (http://blog.izs.me)",
-    "baudehlo <helpme+github at gmail.com>"
-  ],
-  "license": {
-    "type": "MIT",
-    "url": "https://github.com/isaacs/node-tap/raw/master/LICENSE"
-  },
+  "license": "ISC",
   "repository": "https://github.com/isaacs/node-tap.git",
   "scripts": {
-    "test": "node --use_strict bin/tap.js test/*.*"
+    "regen-fixtures": "node scripts/generate-test-test.js test/test/*.js",
+    "test": "node bin/run.js test/*.* --coverage",
+    "posttest": "standard"
   },
-  "devDependencies": {}
+  "devDependencies": {
+    "bluebird": "^2.9.34",
+    "standard": "^5.4.1",
+    "which": "^1.1.1"
+  },
+  "files": [
+    "lib/assert.js",
+    "lib/mocha.js",
+    "lib/root.js",
+    "lib/stack.js",
+    "lib/synonyms.js",
+    "lib/test.js",
+    "bin/mochatap.js",
+    "bin/run.js",
+    "bin/usage.txt",
+    "bin/is-exe.js"
+  ],
+  "config": {
+    "nyc": {
+      "exclude": [
+        "node_modules/**",
+        "test/**"
+      ]
+    }
+  }
 }
diff --git a/scripts/generate-test-test.js b/scripts/generate-test-test.js
new file mode 100644
index 0000000..94c2ece
--- /dev/null
+++ b/scripts/generate-test-test.js
@@ -0,0 +1,119 @@
+#!/usr/bin/env node
+
+var spawn = require('child_process').spawn
+var path = require('path')
+var node = process.execPath
+var fs = require('fs')
+var yaml = require('js-yaml')
+
+var queue = []
+var running = false
+
+for (var i = 2; i < process.argv.length; i++) {
+  generate(process.argv[i], false)
+  generate(process.argv[i], true)
+}
+
+function generate (file, bail) {
+  if (running) {
+    queue.push([file, bail])
+    return
+  }
+  running = true
+
+  file = path.resolve(file)
+  var f = file
+  if (f.indexOf(process.cwd()) === 0) {
+    f = './' + file.substr(process.cwd().length + 1)
+  }
+
+  var outfile = file.replace(/\.js$/, (bail ? '-bail' : '') + '.tap')
+  console.error(outfile)
+  var output = ''
+  var c = spawn(node, [file], {
+    env: {
+      TAP_BAIL: bail ? 1 : 0
+    }
+  })
+
+  c.stdout.on('data', function (d) {
+    output += d
+  })
+  c.on('close', function () {
+    var timep = '# time=[0-9.]+(ms)?'
+    var timere = new RegExp(timep, 'g')
+    output = output.replace(timere, '___/' + timep + '/~~~')
+
+    output = output.split(file).join('___/.*/~~~' + path.basename(file))
+    output = output.split(f).join('___/.*/~~~' + path.basename(f))
+
+    output = output.split(node + ' ___/').join('\0N1\0')
+    output = output.split(path.basename(node) + ' ___/').join('\0N1\0')
+
+    output = output.split(node).join('\0N2\0')
+    output = output.split(path.basename(node)).join('\0N2\0')
+
+    output = output.split('\0N1\0').join('___/.*(node|iojs)')
+    output = output.split('\0N2\0').join('___/.*(node|iojs)/~~~')
+
+    output = yamlishToJson(output)
+
+    fs.writeFileSync(outfile, output)
+
+    running = false
+    if (queue.length) {
+      var q = queue.shift()
+      generate(q[0], q[1])
+    }
+  })
+}
+
+function deStackify (data) {
+  return Object.keys(data).sort().reduce(function (res, k) {
+    if (k === 'stack' && typeof data[k] === 'string') {
+      return res
+    } else if (k === 'time' && typeof data[k] === 'number') {
+      return res
+    } else if (typeof data[k] === 'object' && data[k]) {
+      res[k] = deStackify(data[k])
+    } else {
+      res[k] = data[k]
+    }
+    return res
+  }, Array.isArray(data) ? [] : {})
+}
+
+function yamlishToJson (output) {
+  var lines = output.split(/\n/)
+  var ret = ''
+  var inyaml = false
+  var y = ''
+  var startlen = 0
+  for (var i = 0; i < lines.length; i++) {
+    var line = lines[i]
+    if (inyaml) {
+      if (line.match(/^\s+\.\.\.$/) && line.length === startlen) {
+        var data = yaml.safeLoad(y)
+        data = deStackify(data)
+        data = JSON.stringify(data)
+        ret += new Array(startlen - 2).join(' ') +
+          data + '\n' + line + '\n'
+        inyaml = false
+        y = ''
+      } else {
+        y += line + '\n'
+      }
+    } else if (line.match(/^\s*---$/)) {
+      startlen = line.length
+      inyaml = true
+      y = ''
+      ret += line + '\n'
+    } else {
+      ret += line + '\n'
+    }
+  }
+  if (inyaml) {
+    throw new Error('neverending yaml\n' + y)
+  }
+  return ret
+}
diff --git a/test-disabled/bailout.js b/test-disabled/bailout.js
deleted file mode 100644
index 498035c..0000000
--- a/test-disabled/bailout.js
+++ /dev/null
@@ -1,36 +0,0 @@
-var tap = require("tap")
-  , test = tap.test
-
-test("bailout test", { skip: false }, function (t) {
-
-  // t.once("bailout", function () {
-  //   console.error("bailout event")//, t)
-  //   t.clear()
-  // })
-
-  // t.once("end", function () {
-  //   console.error("end event")
-  // })
-
-  // simulate three tests where the second bails out.
-  t.test("first", function (t) {
-    t.pass("this is ok")
-    t.end()
-  })
-
-  t.test("bailout", function (t) {
-    console.error("bailout test")
-    t.pass("pass")
-    t.bailout("bail out message")
-    t.fail("fail")
-    t.end()
-  })
-
-  t.test("second (should not happen)", function (t) {
-    t.fail("this should not happen")
-    t.end()
-  })
-
-  t.end()
-
-})
diff --git a/test-disabled/foo.js b/test-disabled/foo.js
deleted file mode 100644
index 6360156..0000000
--- a/test-disabled/foo.js
+++ /dev/null
@@ -1 +0,0 @@
-process.stdin
diff --git a/test-disabled/t.js b/test-disabled/t.js
deleted file mode 100644
index 581d24b..0000000
--- a/test-disabled/t.js
+++ /dev/null
@@ -1,16 +0,0 @@
-var test = require('tap').test;
-
-function foo() {
-  throw new Error('one');
-}
-
-test('demonstrate bug in t.throws', function (t) {
-  t.throws(
-    function () {
-      foo();
-    },
-    new Error('two')),
-    // "this should throw",
-    // {}); // not 'one'!
-  t.end();
-});
diff --git a/test/asserts.js b/test/asserts.js
new file mode 100644
index 0000000..bfe689c
--- /dev/null
+++ b/test/asserts.js
@@ -0,0 +1,241 @@
+var test = require('../lib/root.js').test
+var Test = require('../lib/test.js')
+var util = require('util')
+var truthies = [ true, 1, 'ok', Infinity, function () {}, {}, [], /./ ]
+var falsies = [ false, 0, '', NaN, null, undefined ]
+
+test('ok finds truthiness, notOk finds falsiness', function (t) {
+  var tt = new Test()
+  truthies.forEach(function (truthy) {
+    t.ok(truthy, util.inspect(truthy) + ' is truthy')
+    t.notOk(tt.ok(!truthy), util.inspect(truthy) + ' is not falsey')
+  })
+  falsies.forEach(function (falsey) {
+    t.notOk(falsey, util.inspect(falsey) + ' is falsey')
+    t.notOk(tt.ok(falsey), util.inspect(falsey) + ' is not truthy')
+  })
+  t.end()
+})
+
+test('error tests for errorness', function (t) {
+  var er = new Error('ok')
+  var tt = new Test()
+  var c = ''
+  tt.on('end', function () {
+    t.match(c, /non-Error error encountered/, 'warns about non-errors')
+    t.end()
+  })
+  tt.on('data', function (chunk) { c += chunk })
+
+  t.notOk(tt.error(er), 'fails when presented error')
+
+  var circular = {}
+  circular.circular = circular
+  t.notOk(tt.error(circular), 'fails when presented circular object')
+
+  falsies.forEach(function (falsey) {
+    t.error(falsey, 'passes with ' + util.inspect(falsey))
+  })
+  truthies.forEach(function (truthy) {
+    t.notOk(tt.error(truthy), 'fails with ' + util.inspect(truthy))
+  })
+  t.end()
+})
+
+test('throws expects to catch', function (t) {
+  var tt = new Test()
+  t.throws(function () {
+    throw 'x' // eslint-disable-line
+  })
+  t.throws('message first', function () { throw new Error('x') })
+
+  t.throws(function () { throw new Error('x') },
+    new Error('x'), 'test thrown result')
+
+  t.notOk(tt.throws(function () { throw new Error('x') },
+    new Error('y'), 'test thrown result'))
+
+  t.throws('test thrown result',
+    function () { throw new Error('x') },
+    new Error('x'), { foo: 'bar' })
+  t.notOk(tt.throws('test thrown result',
+    function () { throw new Error('x') },
+    new Error('y'), { foo: 'bar' }))
+
+  t.throws('test thrown non-Error object',
+    function () {
+      throw { ok: 'yup' } // eslint-disable-line
+    },
+    { ok: 'yup' })
+  t.notOk(tt.throws('test thrown non-Error object',
+    function () {
+      throw { ok: 'yup' } // eslint-disable-line
+    },
+    { ok: 'nope' }))
+  t.notOk(tt.throws(function () {}))
+  t.end()
+})
+
+test('doesNotThrow expects not to catch', function (t) {
+  var tt = new Test()
+  t.notOk(tt.doesNotThrow(function () {
+    throw 'x' // eslint-disable-line
+  }))
+  t.doesNotThrow(function () {})
+  t.end()
+})
+
+test('equal is strict equality', function (t) {
+  var tt = new Test()
+  t.equal(1, 1, 'number 1')
+  t.equal('a', 'a', 'letter a')
+  t.notOk(tt.equal('1', 1), 'number 1 !== letter 1')
+  t.notOk(tt.equal({x: 1}, {x: 1}), 'similar objects not ===')
+  t.notOk(tt.equal(NaN, NaN), 'NaN cat')
+  var o = {x: 1}
+  t.equal(o, o, 'object identity')
+  t.end()
+})
+
+test('not is strict inequality', function (t) {
+  var tt = new Test()
+  t.notOk(tt.not(1, 1), 'number 1')
+  t.notOk(tt.not('a', 'a'), 'letter a')
+  t.not('1', 1, 'number 1 !== letter 1')
+  t.not({x: 1}, {x: 1}, 'similar objects not ===')
+  t.not(NaN, NaN, 'NaN cat')
+  var o = {x: 1}
+  t.notOk(tt.not(o, o), 'object identity')
+  t.end()
+})
+
+test('same is deep equivalence', function (t) {
+  var tt = new Test()
+  t.same({ x: 1 }, { x: 1 })
+  t.same({ x: '1' }, { x: 1 })
+  t.notOk(tt.same([1, 2], '1,2'))
+  t.notOk(tt.same({ x: 1, y: 2 }, { x: 1 }))
+  t.notOk(tt.same({ x: 1 }, { x: 1, y: 2 }))
+  t.same([1, 2], ['1', '2'])
+  t.same([1, '2'], ['1', 2])
+  t.end()
+})
+
+test('notSame is deep inequivalence', function (t) {
+  var tt = new Test()
+  t.notOk(tt.notSame({ x: 1 }, { x: 1 }))
+  t.notOk(tt.notSame({ x: '1' }, { x: 1 }))
+  t.not_same([1, 2], '1,2')
+  t.not_same({ x: 1, y: 2 }, { x: 1 })
+  t.not_same({ x: 1 }, { x: 1, y: 2 })
+  t.not_ok(tt.not_same([1, 2], ['1', '2']))
+  t.notOk(tt.not_same([1, '2'], ['1', 2]))
+  t.end()
+})
+
+test('strictSame is deep strict equality', function (t) {
+  var tt = new Test()
+  t.strictSame({ x: 1 }, { x: 1 })
+  t.notOk(tt.strictSame({ x: '1' }, { x: 1 }))
+  t.not_ok(tt.strict_same([1, 2], '1,2'))
+  t.not_ok(tt.strict_same({ x: 1, y: 2 }, { x: 1 }))
+  t.not_ok(tt.strict_same({ x: 1 }, { x: 1, y: 2 }))
+  t.not_ok(tt.strict_same([1, 2], ['1', '2']))
+  t.notOk(tt.strict_same([1, '2'], ['1', 2]))
+  t.strictSame([1, 2], [1, 2])
+  t.end()
+})
+
+test('strictNotSame is deep strict inequality', function (t) {
+  var tt = new Test()
+  t.notOk(tt.notStrictSame({ x: 1 }, { x: 1 }))
+  t.notStrictSame({ x: '1' }, { x: 1 })
+  t.not_strict_same([1, 2], '1,2')
+  t.not_strict_same({ x: 1, y: 2 }, { x: 1 })
+  t.not_strict_same({ x: 1 }, { x: 1, y: 2 })
+  t.not_strict_same([1, 2], ['1', '2'])
+  t.not_strict_same([1, '2'], ['1', 2])
+  t.notOk(tt.not_strict_same([1, 2], [1, 2]))
+  t.end()
+})
+
+test('match is pattern matching', function (t) {
+  var tt = new Test()
+  t.match('asdf', /sd./)
+  t.match('asdf', 'sd')
+  t.notOk(tt.match(/sd./, 'asdf'))
+  t.notOk(tt.match('asdf', 'xyz'))
+  t.match({ x: 1, y: 2 }, { x: 1 })
+  t.notOk(tt.match({ x: 1, y: 2 }, { x: 2 }))
+  t.match('asdf', 'asdf')
+  t.match('1', 1)
+  t.match(1, '1')
+
+  // XXX debatable?
+  t.notOk(tt.match(1, [1]))
+  t.notOk(tt.match([1, 2, 3], 1))
+
+  t.end()
+})
+
+test('notMatch is pattern unmatching', function (t) {
+  var tt = new Test()
+  t.not_ok(tt.not_match('asdf', /sd./))
+  t.not_match(/sd./, 'asdf')
+  t.not_match('asdf', 'xyz')
+  t.not_ok(tt.not_match('asdf', 'sd'))
+  t.not_ok(tt.not_match({ x: 1, y: 2 }, { x: 1 }))
+  t.not_match({ x: 1, y: 2 }, { x: 2 })
+  t.not_ok(tt.not_match('asdf', 'asdf'))
+  t.not_ok(tt.not_match('1', 1))
+  t.not_ok(tt.not_match(1, '1'))
+
+  // XXX debatable?
+  t.not_match(1, [1])
+  t.not_match([1, 2, 3], 1)
+
+  t.end()
+})
+
+test('type is type checking', function (t) {
+  function fn () {}
+
+  t.type(fn, 'function')
+  t.type(fn, fn)
+  // t.type(fn, 'Function')
+  // t.type(fn, Function)
+
+  t.type(new Date(), 'object')
+  t.type(new Date(), 'Date')
+  t.type(new Date(), Date)
+
+  t.type(/./, 'object')
+  t.type(/./, 'RegExp')
+  t.type(/./, RegExp)
+
+  t.type('.', 'string')
+
+  function Parent () {}
+  function Child () {}
+  Child.prototype = Object.create(Parent.prototype, {
+    constructor: {
+      value: Child
+    }
+  })
+  var c = new Child()
+  t.type(c, 'object')
+  t.type(c, 'Child')
+  t.type(c, 'Parent')
+  t.type(c, 'Object')
+  t.type(c, Child)
+  t.type(c, Parent)
+  t.type(c, Object)
+
+  t.type(null, 'null')
+  t.type(null, null)
+  t.type(undefined, 'undefined')
+  t.type(undefined, undefined)
+  t.type(NaN, 'number')
+
+  t.end()
+})
diff --git a/test/buffer_compare.js b/test/buffer_compare.js
index b1e1505..6309f9e 100644
--- a/test/buffer_compare.js
+++ b/test/buffer_compare.js
@@ -1,11 +1,11 @@
-var test = require("../").test
+var test = require('../').test
 
-test("same buffers", function (t) {
-  t.same(new Buffer([3,4,243]), new Buffer([3,4,243]))
+test('same buffers', function (t) {
+  t.same(new Buffer([3, 4, 243]), new Buffer([3, 4, 243]))
   t.end()
 })
 
-test("not same buffers", function (t) {
-  t.notSame(new Buffer([3,5,243]), new Buffer([3,4,243]))
+test('not same buffers', function (t) {
+  t.notSame(new Buffer([3, 5, 243]), new Buffer([3, 4, 243]))
   t.end()
 })
diff --git a/test/common.js b/test/common.js
index 7cc43c1..22c924a 100644
--- a/test/common.js
+++ b/test/common.js
@@ -1,32 +1,83 @@
-exports.taps = ["Tests for the foo module"
-               ,{ok:true, name:"test that the foo is fooish"
-                ,file:"foo.js", line:8, name:"fooish test"
-                ,stack:new Error("fooish").stack}
-               ,{ok:false, name:"a test that the bar is barish"
-                ,file:"bar.js", line:25
-                ,expected:"bar\nbar\nbaz", actual:"rab\nrib\nzib"
-                ,hash:{more:"\nstuff\nhere\n",regexp:/asdf/}}
-               ,"Quux module tests"
-               ,"This is a longer comment"
-               ,{ok:true, name:"an easy one."}
-               ,{ok:false, name:"bloooooo"
-                ,expected:"blerggeyyy"
-                ,actual:"blorggeyy"}
-               ,{ok:false, name:"array test"
-                ,expected:[{ok:true},{ok:true},{stack:new Error().stack}]
-                ,actual:[1234567890,123456789,{error:new Error("yikes")}]}
-               ,{ok:true, name:"nulltest"
-                ,expected:undefined, actual:null}
-               ,{ok:true, name:"weird key test"
-                ,expected:"weird key"
-                ,actual:"weird key"
-                ,"this object":{"has a ":"weird key"
-                               ,"and a looooooooonnnnnnnnnggg":"jacket"}}
-               ,{ok:true, name:"regexp test"
-                ,regexp:/asdf/,function:function (a,b) { return a + b }}
-               ]
+exports.taps = [
+  'Tests for the foo module',
+  {
+    ok: true,
+    file: 'foo.js',
+    line: 8,
+    name: 'fooish test',
+    stack: new Error('fooish').stack
+  },
+  {
+    ok: false,
+    name: 'a test that the bar is barish',
+    file: 'bar.js',
+    line: 25,
+    expected: 'bar\nbar\nbaz',
+    actual: 'rab\nrib\nzib',
+    hash: {
+      more: '\nstuff\nhere\n',
+      regexp: /asdf/
+    }
+  },
+  'Quux module tests',
+  'This is a longer comment',
+  {
+    ok: true,
+    name: 'an easy one.'
+  },
+  {
+    ok: false,
+    name: 'bloooooo',
+    expected: 'blerggeyyy',
+    actual: 'blorggeyy'
+  },
+  {
+    ok: false,
+    name: 'array test',
+    expected: [
+      {
+        ok: true
+      },
+      {
+        ok: true
+      },
+      {
+        stack: new Error().stack
+      }
+    ],
+    actual: [
+      1234567890,
+      123456789,
+      {
+        error: new Error('yikes')
+      }
+    ]
+  },
+  {
+    ok: true,
+    name: 'nulltest',
+    expected: undefined,
+    actual: null
+  },
+  {
+    ok: true,
+    name: 'weird key test',
+    expected: 'weird key',
+    actual: 'weird key',
+    'this object': {
+      'has a ': 'weird key',
+      'and a looooooooonnnnnnnnnggg': 'jacket'
+    }
+  },
+  {
+    ok: true,
+    name: 'regexp test',
+    regexp: /asdf/,
+    function: function (a, b) { return a + b }
+  }
+]
 
 if (require.main === module) {
-  console.log("1..1")
-  console.log("ok 1 - just setup, nothing relevant")
+  console.log('1..1')
+  console.log('ok 1 - just setup, nothing relevant')
 }
diff --git a/test/coverage-export.js b/test/coverage-export.js
new file mode 100644
index 0000000..de74dbe
--- /dev/null
+++ b/test/coverage-export.js
@@ -0,0 +1,80 @@
+'use strict'
+
+var t = require('../')
+var spawn = require('child_process').spawn
+var node = process.execPath
+var run = require.resolve('../bin/run.js')
+var ok = require.resolve('./test/ok.js')
+var testRe = /COVERAGE_SERVICE_TEST/
+
+t.test('generate some coverage data', function (tt) {
+  spawn(node, [run, ok, '--coverage'], {
+    stdio: 'ignore'
+  }).on('close', function (code, signal) {
+    tt.equal(code, 0)
+    tt.equal(signal, null)
+    tt.end()
+  })
+})
+
+t.test('no coverage export when no tokens in env', function (tt) {
+  doTest(tt, {}, function (actual) {
+    tt.notMatch(actual, /Codecov:codecov_token\n/)
+    tt.notMatch(actual, /Coveralls:coveralls_token\n/)
+    tt.end()
+  })
+})
+
+t.test('coverage to Coveralls', function (tt) {
+  var env = {
+    COVERALLS_REPO_TOKEN: 'coveralls_token'
+  }
+  doTest(tt, env, function (actual) {
+    tt.notMatch(actual, /Codecov:codecov_token\n/)
+    tt.match(actual, /Coveralls:coveralls_token\n/)
+    tt.end()
+  })
+})
+
+t.test('coverage to Codecov', function (tt) {
+  var env = {
+    CODECOV_TOKEN: 'codecov_token'
+  }
+  doTest(tt, env, function (actual) {
+    tt.match(actual, /Codecov:codecov_token\n/)
+    tt.notMatch(actual, /Coveralls:coveralls_token\n/)
+    tt.end()
+  })
+})
+
+t.test('coverage to both Codecov and Coveralls', function (tt) {
+  var env = {
+    COVERALLS_REPO_TOKEN: 'coveralls_token',
+    CODECOV_TOKEN: 'codecov_token'
+  }
+  doTest(tt, env, function (actual) {
+    tt.match(actual, /Codecov:codecov_token\n/)
+    tt.match(actual, /Coveralls:coveralls_token\n/)
+    tt.end()
+  })
+})
+
+function doTest (tt, env, cb) {
+  var args = [ run, '--no-coverage', '--coverage-report=text-lcov' ]
+  env.COVERAGE_SERVICE_TEST = 'true'
+  var output = ''
+  var child = spawn(node, args, {
+    stdio: [0, 'pipe', 2],
+    env: env
+  })
+  child.stdout.setEncoding('utf-8')
+  child.stdout.on('data', function (data) {
+    output += data
+  })
+  child.on('close', function (code, signal) {
+    tt.equal(code, 0)
+    tt.equal(signal, null)
+    tt.match(output, testRe)
+    cb(output.toString().split('FN:27,parseArgs')[0])
+  })
+}
diff --git a/test/debug-test.js b/test/debug-test.js
index d290315..68dcf6d 100644
--- a/test/debug-test.js
+++ b/test/debug-test.js
@@ -1,17 +1,23 @@
-var tap = require("../")
-  , fs = require("fs")
-  , cp = require("child_process")
-  , util = require("util")
+var t = require('../')
+var cp = require('child_process')
+var main = require.resolve('../bin/run.js')
+var ok = require.resolve('./test/ok.js')
+var node = process.execPath
 
-tap.test("debug test", function (t) {
-  console.error("debug test")
-  t.plan(1)
-  console.error("t.plan="+t._plan)
-
-  cp.exec("../bin/tap.js --debug meta-test.js", function (err, stdo, stde) {
-    console.error(util.inspect(stde))
-    // node 0.10 print "debugger", 0.12 and iojs are printing "Debugger"
-    t.notEqual(stde.indexOf("ebugger listening on port"), -1, "Should output debugger message")
-    t.end();
-  })
+t.plan(1)
+var child = cp.spawn(node, [main, '--debug', ok])
+var stde = ''
+var done = false
+child.stderr.on('data', function (c) {
+  stde += c
+  if (stde.indexOf('ebugger listening on port') !== -1) {
+    t.pass('Should output debugger message')
+    done = true
+    child.kill()
+  }
+})
+child.stderr.on('end', function () {
+  if (!done) {
+    t.fail('did not find debugger message')
+  }
 })
diff --git a/test/deep-strict.js b/test/deep-strict.js
index 7dc9d5b..df57b6c 100644
--- a/test/deep-strict.js
+++ b/test/deep-strict.js
@@ -1,43 +1,59 @@
-var tap = require("../")
-  , test = tap.test
+var tap = require('../')
+var test = tap.test
 
 test("strictDeepEquals shouldn't care about key order", function (t) {
-  t.strictDeepEqual({ a : 1, b : 2 }, { b : 2, a : 1 })
+  t.strictDeepEqual({ a: 1, b: 2 }, { b: 2, a: 1 })
   t.end()
 })
 
 test("strictDeepEquals shouldn't care about key order recursively", function (t) {
   t.strictDeepEqual(
-    { x : { a : 1, b : 2 }, y : { c : 3, d : 4 } },
-    { y : { d : 4, c : 3 }, x : { b : 2, a : 1 } }
+    { x: { a: 1, b: 2 }, y: { c: 3, d: 4 } },
+    { y: { d: 4, c: 3 }, x: { b: 2, a: 1 } }
   )
   t.end()
 })
 
 test("strictDeepEquals shoudn't care about key order (but still might)", function (t) {
   t.strictDeepEqual(
-    [ { foo:
-        { z: 100
-        , y: 200
-        , x: 300 } }
-    , "bar"
-    , 11
-    , { baz:
-        { d : 4
-        , a: 1
-        , b: 2
-        , c: 3 } } ]
-  , [ { foo :
-        { z: 100
-        , y: 200
-        , x: 300 } }
-    , "bar"
-    , 11
-    , { baz:
-        { a: 1
-        , b: 2
-        , c: 3
-        , d: 4 } } ]
+    [
+      {
+        foo: {
+          z: 100,
+          y: 200,
+          x: 300
+        }
+      },
+      'bar',
+      11,
+      {
+        baz: {
+          d: 4,
+          a: 1,
+          b: 2,
+          c: 3
+        }
+      }
+    ],
+    [
+      {
+        foo: {
+          z: 100,
+          y: 200,
+          x: 300
+        }
+      },
+      'bar',
+      11,
+      {
+        baz: {
+          a: 1,
+          b: 2,
+          c: 3,
+          d: 4
+        }
+      }
+    ]
   )
   t.end()
-});
+})
diff --git a/test/deep.js b/test/deep.js
index 6235116..d516d1a 100644
--- a/test/deep.js
+++ b/test/deep.js
@@ -1,43 +1,72 @@
-var tap = require("../")
-  , test = tap.test
+var tap = require('../')
+var test = tap.test
 
 test("deepEquals shouldn't care about key order and types", function (t) {
-  t.deepEqual({ a : 1, b : 2 }, { b : 2, a : "1" })
+  t.deepEqual({ a: 1, b: 2 }, { b: 2, a: '1' })
   t.end()
 })
 
 test("deepEquals shouldn't care about key order recursively and types", function (t) {
   t.deepEqual(
-    { x : { a : 1, b : 2 }, y : { c : 3, d : 4 } },
-    { y : { d : 4, c : 3 }, x : { b : "2", a : "1" } }
+    { x: { a: 1, b: 2 }, y: { c: 3, d: 4 } },
+    { y: { d: 4, c: 3 }, x: { b: '2', a: '1' } }
   )
   t.end()
 })
 
-test("deepEquals shoudn't care about key order (but still might) and types", function (t) {
+test("deepEquals shouldn't care about key order (but still might) and types", function (t) {
   t.deepEqual(
-    [ { foo:
-        { z: 100
-        , y: 200
-        , x: 300 } }
-    , "bar"
-    , 11
-    , { baz:
-        { d : 4
-        , a: 1
-        , b: 2
-        , c: 3 } } ]
-  , [ { foo :
-        { z: 100
-        , y: 200
-        , x: 300 } }
-    , "bar"
-    , 11
-    , { baz:
-        { a: "1"
-        , b: "2"
-        , c: "3"
-        , d: "4" } } ]
+    [
+      {
+        foo: {
+          z: 100,
+          y: 200,
+          x: 300
+        }
+      },
+      'bar',
+      11,
+      {
+        baz: {
+          d: 4,
+          a: 1,
+          b: 2,
+          c: 3
+        }
+      }
+    ],
+    [
+      {
+        foo: {
+          z: 100,
+          y: 200,
+          x: 300
+        }
+      },
+      'bar',
+      11,
+      {
+        baz: {
+          a: '1',
+          b: '2',
+          c: '3',
+          d: '4'
+        }
+      }
+    ]
   )
   t.end()
-});
+})
+
+test("deepEquals shouldn't blow up on circular data structures", function (t) {
+  var x1 = { z: 4 }
+  var y1 = { x: x1 }
+  x1.y = y1
+
+  var x2 = { z: 4 }
+  var y2 = { x: x2 }
+  x2.y = y2
+
+  t.deepEquals(x1, x2)
+  t.end()
+})
diff --git a/test/exit-code.js b/test/exit-code.js
index a2e52de..cda81d9 100644
--- a/test/exit-code.js
+++ b/test/exit-code.js
@@ -1,6 +1,6 @@
 var spawn = require('child_process').spawn
 var node = process.execPath
-var bin = require.resolve('../bin/tap.js')
+var bin = require.resolve('../bin/run.js')
 var test = require('../').test
 var path = require('path')
 var fixtures = path.resolve(__dirname, 'fixtures')
diff --git a/test/expect-error-event.js b/test/expect-error-event.js
new file mode 100644
index 0000000..e97f87a
--- /dev/null
+++ b/test/expect-error-event.js
@@ -0,0 +1,22 @@
+// make sure domains don't obscure things.
+var tap = require('..')
+var EE = require('events').EventEmitter
+
+function testFunction (t) {
+  var ee = new EE()
+
+  t.throws(function () {
+    ee.emit('error', new Error('one'))
+  }, new Error('one'))
+
+  try {
+    ee.emit('error', new Error('two'))
+    t.fail('should throw')
+  } catch (er) {
+    t.match(er, { message: 'two' }, 'threw expected error')
+  }
+  t.end()
+}
+
+tap.test('child', testFunction)
+testFunction(tap)
diff --git a/test/expose-gc-test.js b/test/expose-gc-test.js
index 6a91fdc..b7b864b 100644
--- a/test/expose-gc-test.js
+++ b/test/expose-gc-test.js
@@ -1,53 +1,34 @@
-var tap = require("../")
-  , fs = require("fs")
-  , cp = require("child_process")
-  , path = require("path")
-  , dir = path.resolve(__dirname, "..")
-  , script = path.resolve(dir, "gc-script.js")
-  , opt = { cwd: dir }
-
-fs.writeFileSync(script, "console.error(!!global.gc)", "utf8")
+var tap = require('../')
+var cp = require('child_process')
+var path = require('path')
+var dir = path.resolve(__dirname, '..')
+var opt = { cwd: dir }
 
 tap.test("gc test when the gc isn't there", function (t) {
-  console.error("gc test")
   t.plan(1)
-  console.error("t.plan="+t._plan)
-
-  cp.exec("bin/tap.js ./gc-script.js", opt, function (err, stdo, stde) {
-    console.error("assert gc does not exist")
-    t.equal(stde, "false\n")
+  cp.exec('bin/run.js test/fixtures/gc-script.js', opt, function (err, stdo, stde) {
+    if (err) throw err
+    t.equal(stde, 'false\n')
   })
 })
 
-tap.test("gc test when the gc should be there", function (t) {
-  console.error("gc test")
+tap.test('gc test when the gc should be there', function (t) {
   t.plan(2)
-  console.error("t.plan="+t._plan)
-
-  t.test("test for gc using --gc", function (t) {
-    console.error("gc test using --gc")
+  t.test('test for gc using --gc', function (t) {
     t.plan(1)
-    console.error("t.plan="+t._plan)
 
-    cp.exec("bin/tap.js --gc ./gc-script.js", opt, function (err, stdo, stde) {
-      console.error("assert gc exists")
-      t.equal(stde, "true\n")
+    cp.exec('bin/run.js --gc test/fixtures/gc-script.js', opt, function (err, stdo, stde) {
+      if (err) throw err
+      t.equal(stde, 'true\n')
     })
   })
 
-  t.test("test for gc using --expose-gc", function (t) {
-    console.error("gc test using --expose-gc")
+  t.test('test for gc using --expose-gc', function (t) {
     t.plan(1)
-    console.error("t.plan="+t._plan)
 
-    cp.exec("bin/tap.js --expose-gc ./gc-script.js", opt, function (err, stdo, stde) {
-      console.error("assert gc exists")
-      t.equal(stde, "true\n")
+    cp.exec('bin/run.js --expose-gc test/fixtures/gc-script.js', opt, function (err, stdo, stde) {
+      if (err) throw err
+      t.equal(stde, 'true\n')
     })
   })
 })
-
-tap.test("cleanup", function (t) {
-  fs.unlinkSync(script)
-  t.end()
-});
diff --git a/test/fixtures/assert.js b/test/fixtures/assert.js
new file mode 100644
index 0000000..ed04353
--- /dev/null
+++ b/test/fixtures/assert.js
@@ -0,0 +1,332 @@
+// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
+//
+// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
+//
+// Originally from narwhal.js (http://narwhaljs.org)
+// Copyright (c) 2009 Thomas Robinson <280north.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the 'Software'), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+'use strict'
+
+// UTILITY
+var compare = process.binding('buffer').compare
+var util = require('util')
+var Buffer = require('buffer').Buffer
+var pSlice = Array.prototype.slice
+
+// 1. The assert module provides functions that throw
+// AssertionError's when particular conditions are not met. The
+// assert module must conform to the following interface.
+
+var assert = module.exports = ok
+
+// 2. The AssertionError is defined in assert.
+// new assert.AssertionError({ message: message,
+//                             actual: actual,
+//                             expected: expected })
+
+assert.AssertionError = function AssertionError (options) {
+  this.name = 'AssertionError'
+  this.actual = options.actual
+  this.expected = options.expected
+  this.operator = options.operator
+  if (options.message) {
+    this.message = options.message
+    this.generatedMessage = false
+  } else {
+    this.message = getMessage(this)
+    this.generatedMessage = true
+  }
+  var stackStartFunction = options.stackStartFunction || fail
+  Error.captureStackTrace(this, stackStartFunction)
+}
+
+// assert.AssertionError instanceof Error
+util.inherits(assert.AssertionError, Error)
+
+function truncate (s, n) {
+  if (typeof s === 'string') {
+    return s.length < n ? s : s.slice(0, n)
+  } else {
+    return s
+  }
+}
+
+function getMessage (self) {
+  return truncate(util.inspect(self.actual), 128) + ' ' +
+  self.operator + ' ' +
+  truncate(util.inspect(self.expected), 128)
+}
+
+// At present only the three keys mentioned above are used and
+// understood by the spec. Implementations or sub modules can pass
+// other keys to the AssertionError's constructor - they will be
+// ignored.
+
+// 3. All of the following functions must throw an AssertionError
+// when a corresponding condition is not met, with a message that
+// may be undefined if not provided.  All assertion methods provide
+// both the actual and expected values to the assertion error for
+// display purposes.
+
+function fail (actual, expected, message, operator, stackStartFunction) {
+  throw new assert.AssertionError({
+    message: message,
+    actual: actual,
+    expected: expected,
+    operator: operator,
+    stackStartFunction: stackStartFunction
+  })
+}
+
+// EXTENSION! allows for well behaved errors defined elsewhere.
+assert.fail = fail
+
+// 4. Pure assertion tests whether a value is truthy, as determined
+// by !!guard.
+// assert.ok(guard, message_opt)
+// This statement is equivalent to assert.equal(true, !!guard,
+// message_opt);. To test strictly for the value true, use
+// assert.strictEqual(true, guard, message_opt);.
+
+function ok (value, message) {
+  if (!value) fail(value, true, message, '==', assert.ok)
+}
+assert.ok = ok
+
+// 5. The equality assertion tests shallow, coercive equality with
+// ==.
+// assert.equal(actual, expected, message_opt)
+
+assert.equal = function equal (actual, expected, message) {
+  if (actual != expected) fail(actual, expected, message, '==', assert.equal) // eslint-disable-line
+}
+
+// 6. The non-equality assertion tests for whether two objects are not equal
+// with != assert.notEqual(actual, expected, message_opt)
+
+assert.notEqual = function notEqual (actual, expected, message) {
+  if (actual == expected) { // eslint-disable-line
+    fail(actual, expected, message, '!=', assert.notEqual)
+  }
+}
+
+// 7. The equivalence assertion tests a deep equality relation.
+// assert.deepEqual(actual, expected, message_opt)
+
+assert.deepEqual = function deepEqual (actual, expected, message) {
+  if (!_deepEqual(actual, expected, false)) {
+    fail(actual, expected, message, 'deepEqual', assert.deepEqual)
+  }
+}
+
+assert.deepStrictEqual = function deepStrictEqual (actual, expected, message) {
+  if (!_deepEqual(actual, expected, true)) {
+    fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual)
+  }
+}
+
+function _deepEqual (actual, expected, strict) {
+  // 7.1. All identical values are equivalent, as determined by ===.
+  if (actual === expected) {
+    return true
+  } else if (actual instanceof Buffer && expected instanceof Buffer) {
+    return compare(actual, expected) === 0
+
+  // 7.2. If the expected value is a Date object, the actual value is
+  // equivalent if it is also a Date object that refers to the same time.
+  } else if (util.isDate(actual) && util.isDate(expected)) {
+    return actual.getTime() === expected.getTime()
+
+  // 7.3 If the expected value is a RegExp object, the actual value is
+  // equivalent if it is also a RegExp object with the same source and
+  // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
+  } else if (util.isRegExp(actual) && util.isRegExp(expected)) {
+    return actual.source === expected.source &&
+    actual.global === expected.global &&
+    actual.multiline === expected.multiline &&
+    actual.lastIndex === expected.lastIndex &&
+    actual.ignoreCase === expected.ignoreCase
+
+  // 7.4. Other pairs that do not both pass typeof value == 'object',
+  // equivalence is determined by ==.
+  } else if ((actual === null || typeof actual !== 'object') &&
+    (expected === null || typeof expected !== 'object')) {
+    return strict ? actual === expected : actual == expected // eslint-disable-line
+
+  // 7.5 For all other Object pairs, including Array objects, equivalence is
+  // determined by having the same number of owned properties (as verified
+  // with Object.prototype.hasOwnProperty.call), the same set of keys
+  // (although not necessarily the same order), equivalent values for every
+  // corresponding key, and an identical 'prototype' property. Note: this
+  // accounts for both named and indexed properties on Arrays.
+  } else {
+    return objEquiv(actual, expected, strict)
+  }
+}
+
+function isArguments (object) {
+  return Object.prototype.toString.call(object) === '[object Arguments]'
+}
+
+function objEquiv (a, b, strict) {
+  if (a === null || a === undefined || b === null || b === undefined) {
+    return false
+  }
+  // if one is a primitive, the other must be same
+  if (util.isPrimitive(a) || util.isPrimitive(b)) {
+    return a === b
+  }
+  if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) {
+    return false
+  }
+  var aIsArgs = isArguments(a)
+  var bIsArgs = isArguments(b)
+  if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) {
+    return false
+  }
+  if (aIsArgs) {
+    a = pSlice.call(a)
+    b = pSlice.call(b)
+    return _deepEqual(a, b, strict)
+  }
+  var ka = Object.keys(a)
+  var kb = Object.keys(b)
+  var key, i
+  // having the same number of owned properties (keys incorporates
+  // hasOwnProperty)
+  if (ka.length !== kb.length) {
+    return false
+  }
+  // the same set of keys (although not necessarily the same order),
+  ka.sort()
+  kb.sort()
+  // ~~~cheap key test
+  for (i = ka.length - 1; i >= 0; i--) {
+    if (ka[i] !== kb[i]) {
+      return false
+    }
+  }
+  // equivalent values for every corresponding key, and
+  // ~~~possibly expensive deep test
+  for (i = ka.length - 1; i >= 0; i--) {
+    key = ka[i]
+    if (!_deepEqual(a[key], b[key], strict)) return false
+  }
+  return true
+}
+
+// 8. The non-equivalence assertion tests for any deep inequality.
+// assert.notDeepEqual(actual, expected, message_opt)
+
+assert.notDeepEqual = function notDeepEqual (actual, expected, message) {
+  if (_deepEqual(actual, expected, false)) {
+    fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual)
+  }
+}
+
+assert.notDeepStrictEqual = notDeepStrictEqual
+function notDeepStrictEqual (actual, expected, message) {
+  if (_deepEqual(actual, expected, true)) {
+    fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual)
+  }
+}
+
+// 9. The strict equality assertion tests strict equality, as determined by ===.
+// assert.strictEqual(actual, expected, message_opt)
+
+assert.strictEqual = function strictEqual (actual, expected, message) {
+  if (actual !== expected) {
+    fail(actual, expected, message, '===', assert.strictEqual)
+  }
+}
+
+// 10. The strict non-equality assertion tests for strict inequality, as
+// determined by !==.  assert.notStrictEqual(actual, expected, message_opt)
+
+assert.notStrictEqual = function notStrictEqual (actual, expected, message) {
+  if (actual === expected) {
+    fail(actual, expected, message, '!==', assert.notStrictEqual)
+  }
+}
+
+function expectedException (actual, expected) {
+  if (!actual || !expected) {
+    return false
+  }
+
+  if (Object.prototype.toString.call(expected) === '[object RegExp]') {
+    return expected.test(actual)
+  } else if (actual instanceof expected) {
+    return true
+  } else if (expected.call({}, actual) === true) {
+    return true
+  }
+
+  return false
+}
+
+function _throws (shouldThrow, block, expected, message) {
+  var actual
+
+  if (typeof block !== 'function') {
+    throw new TypeError('block must be a function')
+  }
+
+  if (typeof expected === 'string') {
+    message = expected
+    expected = null
+  }
+
+  try {
+    block()
+  } catch (e) {
+    actual = e
+  }
+
+  message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
+    (message ? ' ' + message : '.')
+
+  if (shouldThrow && !actual) {
+    fail(actual, expected, 'Missing expected exception' + message)
+  }
+
+  if (!shouldThrow && expectedException(actual, expected)) {
+    fail(actual, expected, 'Got unwanted exception' + message)
+  }
+
+  if ((shouldThrow && actual && expected &&
+    !expectedException(actual, expected)) || (!shouldThrow && actual)) {
+    throw actual
+  }
+}
+
+// 11. Expected to throw an error:
+// assert.throws(block, Error_opt, message_opt)
+
+assert.throws = function (block, error, message) {
+  _throws.apply(this, [true].concat(pSlice.call(arguments)))
+}
+
+// EXTENSION! This is annoying to write outside this module.
+assert.doesNotThrow = function (block, message) {
+  _throws.apply(this, [false].concat(pSlice.call(arguments)))
+}
+
+assert.ifError = function (err) { if (err) throw err }
diff --git a/test/fixtures/cat.js b/test/fixtures/cat.js
new file mode 100644
index 0000000..96cde69
--- /dev/null
+++ b/test/fixtures/cat.js
@@ -0,0 +1 @@
+process.stdin.pipe(process.stdout)
diff --git a/test/fixtures/gc-script.js b/test/fixtures/gc-script.js
new file mode 100644
index 0000000..654ec19
--- /dev/null
+++ b/test/fixtures/gc-script.js
@@ -0,0 +1 @@
+console.error(!!global.gc)
diff --git a/test/fixtures/never-finish.js b/test/fixtures/never-finish.js
new file mode 100644
index 0000000..7a1998f
--- /dev/null
+++ b/test/fixtures/never-finish.js
@@ -0,0 +1,5 @@
+var t = require('../..')
+t.pass('this is ok')
+setInterval(function () {
+  t.pass('still going...')
+}, 1000)
diff --git a/test/fixtures/success.js b/test/fixtures/success.js
index c09e43b..91497c5 100644
--- a/test/fixtures/success.js
+++ b/test/fixtures/success.js
@@ -1,3 +1,3 @@
-console.log('TAP Version 13')
+console.log('TAP version 13')
 console.log('ok - 1 and exit 0')
 console.log('1..1')
diff --git a/test/fixtures/todo-skip-descriptions.js b/test/fixtures/todo-skip-descriptions.js
deleted file mode 100644
index c1c6ac5..0000000
--- a/test/fixtures/todo-skip-descriptions.js
+++ /dev/null
@@ -1,4 +0,0 @@
-console.log('ok - always passes # SKIP skip it, skip it good')
-console.log('not ok - false # SKIP always fails')
-console.log('ok - bonus # TODO remove todo directive')
-console.log('not ok - expected # TODO implement a thing')
diff --git a/test/fixtures/trivial-success.js b/test/fixtures/trivial-success.js
index 092bc2b..8b13789 100644
--- a/test/fixtures/trivial-success.js
+++ b/test/fixtures/trivial-success.js
@@ -1 +1 @@
-;
+
diff --git a/test/fixtures/using-require-hook.faux b/test/fixtures/using-require-hook.faux
new file mode 100644
index 0000000..f375d57
--- /dev/null
+++ b/test/fixtures/using-require-hook.faux
@@ -0,0 +1 @@
+The actual content of this file is ignored.
diff --git a/test/fixtures/using-require-hook.js b/test/fixtures/using-require-hook.js
new file mode 100644
index 0000000..bc8c5c0
--- /dev/null
+++ b/test/fixtures/using-require-hook.js
@@ -0,0 +1,19 @@
+// We're simulating that some test failed in `using-require-hook.faux`
+// which was compiled to a js module with more lines than the file on disk.
+var Module = require('module')
+var path = require('path')
+
+var filename = path.resolve(__dirname, 'using-require-hook.faux')
+var m = new Module(filename, module)
+m.filename = filename
+m.paths = Module._nodeModulePaths(path.dirname(filename))
+m._compile('(' + runTapTest.toString() + ')()', filename)
+
+function runTapTest () {
+  var tap = require('../..')
+
+  tap.test(function (t) {
+    t.plan(1)
+    t.equal(1, 2) // failing test so tap tries to find the source code
+  })
+}
diff --git a/test/independent-timeouts.js b/test/independent-timeouts.js
index 5a35e61..6408234 100644
--- a/test/independent-timeouts.js
+++ b/test/independent-timeouts.js
@@ -1,16 +1,30 @@
 // https://github.com/isaacs/node-tap/issues/23
 
-var tap = require("../")
-  , test = tap.test
+var tap = require('../')
+var test = tap.test
+var Test = tap.Test
 
-test("finishes in time", {timeout: 500}, function(t) {
+test('finishes in time', {timeout: 100}, function (t) {
   setTimeout(function () {
-    t.end();
-  }, 300);
+    t.end()
+  }, 60)
 })
-test("finishes in time too", {timeout: 500}, function(t) {
+
+test('finishes in time too', {timeout: 100}, function (t) {
   setTimeout(function () {
-    t.end();
-  }, 300);
+    t.end()
+  }, 60)
 })
 
+test('does not finish in time', function (t) {
+  t.plan(1)
+  var tt = new Test()
+  tt.test('timeout', { timeout: 50 }, function (ttt) {
+    setTimeout(function () {
+      ttt.fail('shouldve timed out')
+      ttt.end()
+      t.notOk(tt._ok)
+    }, 60)
+  })
+  tt.end()
+})
diff --git a/test/isolated-conf-test.js b/test/isolated-conf-test.js
deleted file mode 100644
index d8bfae6..0000000
--- a/test/isolated-conf-test.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// https://github.com/isaacs/node-tap/issues/24
-
-var tap = require("../")
-  , test = tap.test
-
-var config = {foo: "bar"}
-test("one", config, function(t) {
-  t.equal(t.conf.foo, "bar")
-  t.equal(t.conf.name, "one")  // before fix this would be "two"
-  t.end()
-})
-test("two", config, function(t) {
-  t.equal(t.conf.foo, "bar")
-  t.equal(t.conf.name, "two")
-  t.end()
-})
diff --git a/test/meta-test.js b/test/meta-test.js
deleted file mode 100644
index 8f56f26..0000000
--- a/test/meta-test.js
+++ /dev/null
@@ -1,73 +0,0 @@
-var tap = require("../")
-  , test = tap.test
-
-test("meta test", { skip: false }, function (t) {
-
-  function thr0w() { throw new Error('raburt') }
-  function noop () {}
-
-  // this also tests the ok/notOk functions
-  t.once("end", section2)
-  t.ok(true, "true is ok")
-  t.ok(noop, "function is ok")
-  t.ok({}, "object is ok")
-  t.ok(t, "t is ok")
-  t.ok(100, "number is ok")
-  t.ok("asdf", "string is ok")
-  t.notOk(false, "false is notOk")
-  t.notOk(0, "0 is notOk")
-  t.notOk(null, "null is notOk")
-  t.notOk(undefined, "undefined is notOk")
-  t.notOk(NaN, "NaN is notOk")
-  t.notOk("", "empty string is notOk")
-  t.throws(thr0w, "Thrower throws");
-  t.doesNotThrow(noop, "noop does not throw");
-  t.similar({foo:"bar", bar:"foo"}, {foo:"bar"}, "similar objects are ok");
-  t.dissimilar({}, {mandatory:"value"}, "dissimilar objects are ok");
-  t.dissimilar(null, {}, "null is dissimilar from an object, even with no keys");
-
-  // a few failures.
-  t.ifError(new Error("this is an error"))
-  t.ifError({ message: "this is a custom error" })
-  t.ok(false, "false is not ok")
-  t.notOk(true, "true is not not ok")
-  t.similar(null, {}, "Null is not similar to an object, even with no keys");
-  t.throws(noop, "noop does not throw");
-  t.throws(noop, new Error("Whoops!"), "noop does not throw an Error");
-  t.throws(noop, {name:"MyError", message:"Whoops!"}, "noop does not throw a MyError");
-  t.doesNotThrow(thr0w, "thrower does throw");
-
-  // things that are like other things
-  t.like("asdf", "asdf")
-  t.like("asdf", /^a.*f$/)
-  t.like(100, 100)
-  t.like(100, '100')
-  t.like(100, 100.0)
-  t.unlike("asdf", "fdsa")
-  t.unlike("asdf", /^you jelly, bro?/)
-  t.unlike(100, 100.1)
-  t.like(true, 1)
-  t.like(null, undefined)
-  t.like(true, [1])
-  t.like(false, [])
-  t.like('', [])
-  t.end()
-
-  function section2 () {
-    var results = t.results
-    t.clear()
-    t.ok(true, "sanity check")
-    t.notOk(results.ok, "not ok")
-    t.equal(results.tests, 39, "total test count")
-    t.equal(results.passTotal, 30, "tests passed")
-    t.equal(results.fail, 9, "tests failed")
-    t.type(results.ok, "boolean", "ok is boolean")
-    t.type(results.skip, "number", "skip is number")
-    t.type(results, "Results", "results isa Results")
-    t.type(t, "Test", "test isa Test")
-    t.type(t, "Harness", "test isa Harness")
-    t.end()
-  }
-})
-
-
diff --git a/test/nested-async.js b/test/nested-async.js
index ae1cda3..4efe5d2 100644
--- a/test/nested-async.js
+++ b/test/nested-async.js
@@ -1,36 +1,36 @@
-var test = require("../").test
+var test = require('../').test
 
-test('Harness async test support', function(t) {
-  t.plan(3);
+test('Harness async test support', function (t) {
+  t.plan(3)
 
-  t.ok(true, 'sync child A');
+  t.ok(true, 'sync child A')
 
-  t.test('sync child B', function(tt) {
-    tt.plan(2);
+  t.test('sync child B', function (tt) {
+    tt.plan(2)
 
-    setTimeout(function(){
-      tt.test('async grandchild A', function(ttt) {
-        ttt.plan(1);
-        ttt.ok(true);
-      });
-    }, 50);
+    setTimeout(function () {
+      tt.test('async grandchild A', function (ttt) {
+        ttt.plan(1)
+        ttt.ok(true)
+      })
+    }, 50)
 
-    setTimeout(function() {
-      tt.test('async grandchild B', function(ttt) {
-        ttt.plan(1);
-        ttt.ok(true);
-      });
-    }, 100);
-  });
+    setTimeout(function () {
+      tt.test('async grandchild B', function (ttt) {
+        ttt.plan(1)
+        ttt.ok(true)
+      })
+    }, 100)
+  })
 
-  setTimeout(function() {
-    t.test('async child', function(tt) {
-      tt.plan(2);
-      tt.ok(true, 'sync grandchild in async child A');
-      tt.test('sync grandchild in async child B', function(ttt) {
-        ttt.plan(1);
-        ttt.ok(true);
-      });
-    });
-  }, 200);
-});
+  setTimeout(function () {
+    t.test('async child', function (tt) {
+      tt.plan(2)
+      tt.ok(true, 'sync grandchild in async child A')
+      tt.test('sync grandchild in async child B', function (ttt) {
+        ttt.plan(1)
+        ttt.ok(true)
+      })
+    })
+  }, 200)
+})
diff --git a/test/nested-test.js b/test/nested-test.js
index d85ff21..b600431 100644
--- a/test/nested-test.js
+++ b/test/nested-test.js
@@ -1,22 +1,21 @@
-var tap = require("../"),
-    test = tap.test;
+var tap = require('../')
+var test = tap.test
 
-test("parent", function (t) {
+test('parent', function (t) {
   // TODO: Make grandchildren tests count?
-  t.plan(3);
-  t.ok(true, 'p test');
-  t.test("subtest", function (t) {
-    t.ok(true, 'ch test');
-    t.test('nested subtest', function(t) {
-      t.ok(true, 'grch test');
-      t.end();
-    });
-    t.end();
-  });
-  t.test('another subtest', function(t) {
-    t.ok(true, 'ch test 2');
-    t.end();
-  });
-  t.end();
+  t.plan(3)
+  t.ok(true, 'p test')
+  t.test('subtest', function (t) {
+    t.ok(true, 'ch test')
+    t.test('nested subtest', function (t) {
+      t.ok(true, 'grch test')
+      t.end()
+    })
+    t.end()
+  })
+  t.test('another subtest', function (t) {
+    t.ok(true, 'ch test 2')
+    t.end()
+  })
+  t.end()
 })
-
diff --git a/test/non-enumerable-match.js b/test/non-enumerable-match.js
new file mode 100644
index 0000000..d40013d
--- /dev/null
+++ b/test/non-enumerable-match.js
@@ -0,0 +1,14 @@
+var t = require('../')
+
+var e = Object.create(null, {
+  id: {
+    value: 1,
+    enumerable: false,
+    writable: false,
+    configurable: false
+  }
+})
+t.has(e, {id: 1})
+
+var f = Object.create(e)
+t.has(f, {id: 1})
diff --git a/test/non-tap-output.js b/test/non-tap-output.js
deleted file mode 100644
index 929e9aa..0000000
--- a/test/non-tap-output.js
+++ /dev/null
@@ -1,12 +0,0 @@
-console.log("everything is fine\n"
-           +"there are no errors\n"
-           +"this output is not haiku.\n\n"
-           +"is 8 ok?\n"
-           +"ok, 8 can stay.\n"
-           +"ok 100 might be confusing\n"
-           +"  but: nevertheless, here we are\n"
-           +"  this: is indented\n"
-           +"  and: it\n"
-           +"  might: ~\n"
-           +"  be: yaml?\n"
-           +"ok done now, exiting")
diff --git a/test/only-non-tap-output.js b/test/only-non-tap-output.js
new file mode 100644
index 0000000..f86d70a
--- /dev/null
+++ b/test/only-non-tap-output.js
@@ -0,0 +1,50 @@
+if (process.argv[2] === 'child') {
+  console.log('this is the child bit')
+} else if (process.argv[2] !== 'silent') {
+  var t = require('../')
+  var Test = t.Test
+  var tt = new Test()
+
+  var out = ''
+  tt.on('data', function (c) {
+    out += c
+  })
+  tt.on('complete', function (res) {
+    t.has(res, {
+      plan: { start: 1, end: 2 },
+      count: 2,
+      pass: 2,
+      ok: true,
+      skip: 2
+    })
+    t.has(tt._skips, [
+      {
+        ok: true,
+        message: './test/only-non-tap-output.js child',
+        extra: {
+          at: {},
+          results: {},
+          command: process.execPath,
+          arguments: {},
+          skip: 'No tests found'
+        }
+      },
+      {
+        ok: true,
+        message: './test/only-non-tap-output.js silent',
+        extra: {
+          at: {},
+          results: {},
+          command: process.execPath,
+          arguments: {},
+          skip: 'No tests found'
+        }
+      }
+    ])
+    t.ok(tt.passing(), 'should still be passing')
+  })
+
+  tt.spawn(process.execPath, [__filename, 'child'])
+  tt.spawn(process.execPath, [__filename, 'silent'])
+  tt.end()
+}
diff --git a/test/output-childtest-description.js b/test/output-childtest-description.js
deleted file mode 100644
index e025ba8..0000000
--- a/test/output-childtest-description.js
+++ /dev/null
@@ -1,50 +0,0 @@
-var tap  =  require("../")
-  , fs   =  require("fs")
-  , path =  require('path')
-  , cp   =  require("child_process")
-  , nestedTests = 
-      [ "var test = require('..').test"
-      , "test('parent test description', function (t) {"
-      , " t.plan(2)"
-      , " t.ok(true, 'test in parent')"
-      , " t.test('child test description', function (t) {"
-      , "    t.plan(1)"
-      , "    t.ok(true, 'test in child')  "
-      , " })"
-      , "})"
-      ].join("\n")
-  , nestedTestsFile = path.join(__dirname, "nested-tests-fixture.js")
-
-fs.writeFileSync(nestedTestsFile, nestedTests, "utf8")
-console.log(nestedTestsFile);
-
-tap.test("nested tests, parent and child pass", function (t) {
-  /*
-   * Ensure the output includes the following lines in the right order:
-   * '# parent test description'
-   *   'ok 1 test in parent'
-   * '# child test description'
-   *   'ok 2 test in child'
-   */
-
-  t.plan(5)
-
-  cp.exec("node " + nestedTestsFile, function (err, stdo, stde) {
-    var lines = stdo.split("\n")
-      , parentDes =  lines.indexOf("# parent test description")
-      , parentRes =  lines.indexOf("ok 1 test in parent")
-      , childDes  =  lines.indexOf("# child test description")
-      , childRes  =  lines.indexOf("ok 2 test in child")
-
-    t.notEqual(parentDes, -1, "outputs parent description")
-    t.notEqual(childDes, -1, "outputs child description")
-
-    t.ok(parentDes < parentRes ,  "outputs parent description before parent result")
-    t.ok(parentRes < childDes  ,  "outputs parent result before child description")
-    t.ok(childDes  < childRes  ,  "outputs child description before child result")
-
-    fs.unlinkSync(nestedTestsFile);
-    t.end()
-  })
-})
-
diff --git a/test/require-hooks.js b/test/require-hooks.js
new file mode 100644
index 0000000..f8b93c0
--- /dev/null
+++ b/test/require-hooks.js
@@ -0,0 +1,27 @@
+var bin = require.resolve('../bin/run.js')
+var execFile = require('child_process').execFile
+var path = require('path')
+var test = require('../').test
+
+var fixtures = path.resolve(__dirname, 'fixtures')
+var node = process.execPath
+
+test('compile-to-js require hook', function (t) {
+  t.plan(1)
+
+  t.test('failing test with unmatched stack-trace', function (t) {
+    t.plan(6)
+
+    function verifyOutput (err, stdout, stderr) {
+      t.ok(!!err, 'Should have failed to run')
+      t.match(stdout, 'file: test/fixtures/using-require-hook.faux',
+        'error happened in the *.faux file')
+      t.notMatch(stdout, 'source:',
+        'omits the source because the line cannot be resolved')
+    }
+
+    var file = path.resolve(fixtures, 'using-require-hook.js')
+    execFile(node, [bin, file], verifyOutput)
+    execFile(node, [file], verifyOutput)
+  })
+})
diff --git a/test/result-trap.js b/test/result-trap.js
deleted file mode 100644
index 6db0af7..0000000
--- a/test/result-trap.js
+++ /dev/null
@@ -1,20 +0,0 @@
-var tap = require("../")
-
-tap.test("trap result", function (t0) {
-
-  var t1 = new(tap.Harness)(tap.Test).test()
-
-  var emitted = false
-  t1.on("result", function (res) {
-    if (!emitted) {
-      emitted = true
-      t0.equal(res.found, 3)
-      t0.equal(res.wanted, 4)
-
-      t0.end()
-      t1.end()
-    }
-  })
-
-  t1.equal(1 + 2, 4)
-})
diff --git a/test/runner.js b/test/runner.js
new file mode 100644
index 0000000..f518cba
--- /dev/null
+++ b/test/runner.js
@@ -0,0 +1,510 @@
+var t = require('../')
+var spawn = require('child_process').spawn
+var node = process.execPath
+var run = require.resolve('../bin/run.js')
+var ok = require.resolve('./test/ok.js')
+var notok = require.resolve('./test/not-ok.js')
+var colorRe = new RegExp('\u001b\\[[0-9;]+m') // eslint-disable-line
+var bailRe = new RegExp('^Bail out! # this is not ok$', 'm')
+var okre = new RegExp('test[\\\\/]test[/\\\\]ok\\.js \\.+ 10/10( [0-9\.]+m?s)?$', 'm')
+var notokre = new RegExp('test[\\\\/]test[/\\\\]not-ok\\.js \\.+ 0/1( [0-9\.]+m?s)?$', 'm')
+var fs = require('fs')
+var which = require('which')
+
+t.test('usage', function (t) {
+  function usageTest (t, child, c) {
+    var out = ''
+    var std = c === 0 ? 'stdout' : 'stderr'
+    child[std].on('data', function (o) {
+      out += o
+    })
+    child.on('close', function (code) {
+      t.equal(code, c, 'code should be ' + c)
+      t.match(out, /^Usage:\n/, 'should print usage')
+      t.end()
+    })
+  }
+
+  var stdin = 0
+  if (!process.stdin.isTTY) {
+    try {
+      stdin = fs.openSync('/dev/tty', 'r')
+    } catch (er) {
+      stdin = null
+    }
+  }
+  var opt = { skip: stdin === null ? 'could not load tty' : false }
+  t.test('shows usage when stdin is a tty', opt, function (t) {
+    var child = spawn(node, [run], { stdio: [ stdin, 'pipe', 'pipe' ] })
+    usageTest(t, child, 1)
+  })
+
+  t.test('shows usage with -h (even with file)', function (t) {
+    var child = spawn(node, [run, '-h', __filename])
+    usageTest(t, child, 0)
+  })
+
+  t.test('shows usage with --help (even with file)', function (t) {
+    var child = spawn(node, [run, '--help', __filename])
+    usageTest(t, child, 0)
+  })
+
+  t.end()
+})
+
+t.test('colors', function (t) {
+  function colorTest (args, env, hasColor) {
+    return function (t) {
+      var out = ''
+      env = env || {}
+      env.TAP = 0
+      args = [run, ok].concat(args || [])
+      var child = spawn(node, args, { env: env })
+      child.stdout.on('data', function (o) {
+        out += o
+      })
+      child.on('close', function (code) {
+        t.equal(code, 0, 'code should be 0')
+        if (hasColor) {
+          t.match(out, colorRe)
+        } else {
+          t.notMatch(out, colorRe)
+        }
+        t.end()
+      })
+    }
+  }
+
+  t.test('no colors by default for non-TTY',
+    colorTest([], {}, false))
+
+  t.test('force colors with -c or --color', function (t) {
+    ;[ '-c', '--color' ].forEach(function (c) {
+      t.test(c, colorTest([c], {}, true))
+    })
+    t.end()
+  })
+
+  t.test('force no colors with -C or --no-color', function (t) {
+    ;[ '-C', '--no-color' ].forEach(function (c) {
+      t.test(c, colorTest([c], {}, false))
+    })
+    t.end()
+  })
+
+  t.test('env.TAP_COLORS', function (t) {
+    t.test('0', colorTest([], { TAP_COLORS: 0 }, false))
+    t.test('1', colorTest([], { TAP_COLORS: 1 }, true))
+    t.end()
+  })
+
+  t.end()
+})
+
+t.test('bailout args', function (t) {
+  function bailTest (args, env, bail) {
+    return function (t) {
+      var out = ''
+      env = env || {}
+      env.TAP = 0
+      env.TAP_COLORS = 0
+      args = [run, notok, ok, '-C', '-Rclassic'].concat(args || [])
+      var child = spawn(node, args, { env: env })
+      child.stdout.on('data', function (o) {
+        out += o
+      })
+      child.on('close', function (code) {
+        t.equal(code, 1, 'code should be 1')
+        if (bail) {
+          t.match(out, bailRe, 'should show bail out')
+          t.notMatch(out, okre, 'should not run second test')
+        } else {
+          t.notMatch(out, bailRe, 'should not bail out')
+          t.match(out, okre, 'should run second test')
+        }
+        t.end()
+      })
+    }
+  }
+
+  t.test('force bailout with -b or --bail', function (t) {
+    t.test('-b', bailTest(['-b'], {}, true))
+    t.test('-Bb', bailTest(['-Bb'], {}, true))
+    t.test('--bail', bailTest(['--bail'], {}, true))
+    t.test('--no-bail --bail', bailTest(['--no-bail', '--bail'], {}, true))
+    t.test('TAP_BAIL=1', bailTest([], { TAP_BAIL: 1 }, true))
+    t.end()
+  })
+
+  t.test('do not bail out with -B or --no-bail', function (t) {
+    t.test('-B', bailTest(['-B'], {}, false))
+    t.test('-bB', bailTest(['-bB'], {}, false))
+    t.test('--no-bail', bailTest(['--no-bail'], {}, false))
+    t.test('--bail --no-bail', bailTest(['--bail', '--no-bail'], {}, false))
+    t.test('TAP_BAIL=0', bailTest([], { TAP_BAIL: 0 }, false))
+    t.end()
+  })
+
+  t.end()
+})
+
+t.test('path globbing', function (t) {
+  var glob = 'fixtures/*-success.js'
+  var opt = { env: { TAP: 1 }, cwd: __dirname }
+  var child = spawn(node, [run, glob], opt)
+  var out = ''
+  child.stdout.on('data', function (c) {
+    out += c
+  })
+  child.on('close', function (code) {
+    t.equal(code, 0, 'exits successfully')
+    t.match(out, /trivial-success.js/g, 'includes a matched file')
+    t.end()
+  })
+})
+
+t.test('save-file', function (t) {
+  var saveFile = 'runner-save-test-' + process.pid
+  var n = 0
+  function saveFileTest (cb) {
+    var args = [run, '-s' + saveFile, ok, notok, '-CRclassic']
+    // also test the expanded versions for added coverage
+    if (++n === 1) {
+      args = [
+        run, '--save', saveFile,
+        ok, notok, '-C', '--reporter', 'classic'
+      ]
+    }
+
+    var child = spawn(node, args, { env: { TAP: 0 }, stdio: [0, 'pipe', 2] })
+    var out = ''
+    child.stdout.on('data', function (c) {
+      out += c
+    })
+    child.on('close', function (code) {
+      cb(code, out)
+    })
+  }
+
+  t.test('run with "ok.js" in save file', function (t) {
+    fs.writeFileSync(saveFile, ok + '\n')
+    saveFileTest(function (code, out) {
+      t.equal(code, 0, 'should exit successfully')
+      t.match(out, okre, 'should run ok.js test')
+      t.notMatch(out, notokre, 'should not run not-ok.js test')
+      t.throws(function () {
+        fs.statSync(saveFile)
+      }, 'should delete save file')
+      t.end()
+    })
+  })
+
+  t.test('run with empty save file', function (t) {
+    saveFileTest(function (code, out) {
+      t.equal(code, 1, 'should fail test')
+      t.match(out, okre, 'should run ok.js test')
+      t.match(out, notokre, 'should run not-ok.js test')
+      t.equal(fs.readFileSync(saveFile, 'utf8'), notok + '\n',
+        'should save not-ok.js')
+      t.end()
+    })
+  })
+
+  t.test('run with "not-ok.js" in save file', function (t) {
+    saveFileTest(function (code, out) {
+      t.equal(code, 1, 'should fail test')
+      t.notMatch(out, okre, 'should not run ok.js test')
+      t.match(out, notokre, 'should run not-ok.js test')
+      t.equal(fs.readFileSync(saveFile, 'utf8'), notok + '\n',
+        'should save not-ok.js')
+      t.end()
+    })
+  })
+
+  t.test('cleanup', function (t) {
+    fs.unlinkSync(saveFile)
+    t.end()
+  })
+
+  t.end()
+})
+
+t.test('version', function (t) {
+  var version = require('../package.json').version
+  function versionTest (arg) {
+    return function (t) {
+      var child = spawn(node, [run].concat(arg))
+      var out = ''
+      child.stdout.on('data', function (o) {
+        out += o
+      })
+      child.on('close', function (code, signal) {
+        t.equal(code, 0)
+        t.equal(signal, null)
+        t.equal(out, version + '\n')
+        t.end()
+      })
+    }
+  }
+  t.test('-v', versionTest('-v'))
+  t.test('--version', versionTest('--version'))
+  t.test('--version', versionTest(['--version', __filename]))
+  t.end()
+})
+
+;(function () {
+  try {
+    var headBin = which.sync('head')
+  } catch (er) {}
+
+  var skip = false
+  if (!headBin) {
+    skip = 'head program not available'
+  }
+
+  t.test('handle EPIPE gracefully', { skip: skip }, function (t) {
+    var head = spawn(headBin, ['-5'])
+    var child = spawn(node, [run, ok], {
+      stdio: [ 0, head.stdin, 'pipe' ]
+    })
+    var err = ''
+    child.stderr.on('data', function (c) {
+      err += c
+    })
+    child.on('close', function (code, signal) {
+      t.equal(err, '', 'should not spew errors')
+      t.end()
+    })
+  })
+})()
+
+t.test('--no-cov args', function (t) {
+  // this is a weird test, because we want to cover the
+  // --no-cov case, but still get coverage for it, so
+  // we test with --no-cov --coverage so it switches back.
+  var args = [
+    run, ok,
+    '--no-cov', '--no-coverage',
+    '--cov', '--coverage'
+  ]
+  spawn(node, args).on('close', function (code, signal) {
+    t.equal(code, 0)
+    t.equal(signal, null)
+    t.end()
+  })
+})
+
+t.test('unknown arg throws', function (t) {
+  // { arg: unknown }
+  var cases = {
+    '--wtf': '--wtf',
+    '-Bcav': '-a',
+    '-wtf': '-w'
+  }
+  Object.keys(cases).forEach(function (c) {
+    t.test(c, function (t) {
+      badArgTest(t, c, cases[c])
+    })
+  })
+  t.end()
+
+  function badArgTest (t, arg, error) {
+    var expectCode = process.version.match(/^v0\.10/) ? 8 : 1
+    var child = spawn(node, [run, arg])
+    var err = ''
+    child.stderr.on('data', function (c) {
+      err += c
+    })
+    child.on('close', function (code, signal) {
+      t.match(err, new RegExp('Error: Unknown argument: ' + error))
+      t.equal(code, expectCode)
+      t.equal(signal, null)
+      t.end()
+    })
+  }
+})
+
+t.test('read from stdin', function (t) {
+  function stripTime (s) {
+    return s.split(ok).join('test/test/ok.js')
+      .replace(/[0-9\.]+m?s/g, '{{TIME}}')
+      .replace(/\n\r/g, '\n')
+  }
+
+  var expect = ''
+  t.test('generated expected output', function (t) {
+    var args = [run, ok, '--reporter', 'spec']
+    var child = spawn(node, args, {
+      env: {
+        TAP: 0,
+        TAP_BAIL: 0
+      }
+    })
+    child.stdout.on('data', function (c) {
+      expect += c
+    })
+    child.on('close', function (code, signal) {
+      expect = stripTime(expect)
+      t.equal(code, 0)
+      t.equal(signal, null)
+      t.end()
+    })
+  })
+
+  function pipeTest (t, warn, repArgs) {
+    var args = [run, ok]
+    var err = ''
+    var out = ''
+    var expectError = ''
+    if (warn) {
+      expectError = 'Reading TAP data from stdin (use "-" argument to suppress)\n'
+    }
+
+    var repClosed = false
+    var runClosed = true
+    var repChild = spawn(node, repArgs, {
+      env: {
+        TAP: 0,
+        TAP_BAIL: 0
+      }
+    })
+    var runChild = spawn(node, args, {
+      stdio: [ 0, repChild.stdin, 2 ],
+      env: {
+        TAP_BAIL: 0
+      }
+    })
+    repChild.stderr.on('data', function (c) {
+      err += c
+    })
+    repChild.stdout.on('data', function (c) {
+      out += c
+    })
+    runChild.on('exit', function (code, signal) {
+      t.equal(code, 0)
+      t.equal(signal, null)
+      t.notOk(repClosed)
+      repChild.stdin.end()
+      runClosed = true
+    })
+    repChild.on('close', function (code, signal) {
+      repClosed = true
+      t.ok(runClosed)
+      t.equal(code, 0)
+      t.equal(signal, null)
+      t.equal(err, expectError)
+      t.equal(stripTime(out), expect)
+      t.end()
+    })
+  }
+
+  t.test('warns if - is not an arg', function (t) {
+    pipeTest(t, true, [run, '--reporter=spec'])
+  })
+
+  t.test('does not warn if - is present', function (t) {
+    pipeTest(t, false, [run, '--reporter=spec', '-'])
+  })
+
+  t.test('stdin along with files', function (t) {
+    expect = '\n' +
+      'test/test/ok.js\n' +
+      '  nesting\n' +
+      '    first\n' +
+      '      ✓ true is ok\n' +
+      '      ✓ doag is also okay\n' +
+      '    second\n' +
+      '      ✓ but that is ok\n' +
+      '      ✓ this passes\n' +
+      '      ✓ nested ok\n' +
+      '\n' +
+      '  ✓ this passes\n' +
+      '  ✓ this passes too\n' +
+      '  async kid\n' +
+      '    ✓ timeout\n' +
+      '    ✓ timeout\n' +
+      '\n' +
+      '  ✓ pass after async kid\n' +
+      '/dev/stdin\n' +
+      '  test/test/ok.js\n' +
+      '    nesting\n' +
+      '      first\n' +
+      '        ✓ true is ok\n' +
+      '        ✓ doag is also okay\n' +
+      '      second\n' +
+      '        ✓ but that is ok\n' +
+      '        ✓ this passes\n' +
+      '        ✓ nested ok\n' +
+      '    ✓ this passes\n' +
+      '    ✓ this passes too\n' +
+      '    async kid\n' +
+      '      ✓ timeout\n' +
+      '      ✓ timeout\n' +
+      '    ✓ pass after async kid\n' +
+      '\n' +
+      '\n' +
+      '  20 passing ({{TIME}})\n'
+
+    pipeTest(t, false, [run, '--reporter', 'spec', '-', ok])
+  })
+  t.end()
+})
+
+t.test('separate filename args with --', function (t) {
+  var args = [ run, '--', '-xyz', ok ]
+  var child = spawn(node, args)
+  child.on('close', function (code, signal) {
+    t.equal(code, 0)
+    t.equal(signal, null)
+    t.end()
+  })
+})
+
+t.test('-t or --timeout to set timeout', function (t) {
+  var nf = require.resolve('./fixtures/never-finish.js')
+  var args = [run, nf]
+  var dur = '.2'
+  if (global.__coverage__) {
+    dur = '.9'
+  }
+  var timers = [
+    '-t' + dur,
+    ['-t', '0' + dur],
+    '-t=' + dur,
+    '--timeout=' + dur,
+    ['--timeout', dur]
+  ]
+  timers.forEach(function (timer) {
+    t.test([].concat(timer).join(' '), function (t) {
+      var child = spawn(node, args.concat(timer))
+      var out = ''
+      child.stdout.on('data', function (c) {
+        out += c
+      })
+      child.on('close', function (code, signal) {
+        t.equal(code, 1)
+        t.equal(signal, null)
+        t.match(out, /received SIGTERM with pending event queue activity/)
+        t.end()
+      })
+    })
+  })
+  t.end()
+})
+
+t.test('non-zero exit is reported as failure', function (t) {
+  var file = require.resolve('./test/ok-exit-fail.js')
+  var args = [run, file, '-Rclassic']
+  var child = spawn(node, args, { env: { TAP: 0 } })
+  var out = ''
+  child.stdout.on('data', function (c) {
+    out += c
+  })
+  child.on('close', function (code, signal) {
+    t.equal(code, 1)
+    t.equal(signal, null)
+    t.match(out, '  not ok ' + file)
+    t.match(out, /\n+\s+exitCode: 1\n/)
+    t.end()
+  })
+})
diff --git a/test/segv.js b/test/segv.js
index 6d6d779..ab2499e 100644
--- a/test/segv.js
+++ b/test/segv.js
@@ -1,21 +1,53 @@
-var test = require('../').test
-var Runner = require('../lib/tap-runner.js')
-var TC = require('../lib/tap-consumer.js')
-
+var tap = require('../')
+var test = tap.test
+var Test = tap.Test
 var fs = require('fs')
-var path = require('path')
 var spawn = require('child_process').spawn
+
 var segv =
-    'int main (void) {\n' +
-    '   char *s = "hello world";\n' +
-    '   *s = \'H\';\n' +
-    '}\n'
-var compiled = false
+'int main (void) {\n' +
+  '   char *s = "hello world";\n' +
+  "   *s = 'H';\n" +
+  '}\n'
+
+// omit some stuff from the ends of lines that will differ
+// we slice the actual output later to just compare the fronts
+// also sort the lines, because Node v0.8 sets keys on objects
+// in different order.
+var expect =
+('TAP version 13\n' +
+'    # Subtest: ./segv\n' +
+'    1..0\n' +
+'not ok 1 - ./segv  # time=\n' +
+'  ---\n' +
+'  at:\n' +
+'    file: test/segv.js\n' +
+'    line: \n' +
+'    column: \n' +
+'  results:\n' +
+'    ok: false\n' +
+'    count: 0\n' +
+'    pass: 0\n' +
+'    plan:\n' +
+'      start: 1\n' +
+'      end: 0\n' +
+'      skipAll: true\n' +
+'  signal: SIG\n' +
+'  command: ./segv\n' +
+'  arguments: []\n' +
+'  source: |\n' +
+"    tt.spawn('./segv')\n" +
+'  ...\n' +
+'\n' +
+'1..1\n' +
+'# failed 1 of 1 tests\n' +
+'# time=\n').trim().split('\n')
 
 test('setup', function (t) {
   fs.writeFile('segv.c', segv, 'utf8', function (er) {
-    if (er)
+    if (er) {
       throw er
+    }
     var cp = spawn('gcc', ['segv.c', '-o', 'segv'])
     cp.on('exit', function (code, sig) {
       if (code !== 0) {
@@ -29,42 +61,33 @@ test('setup', function (t) {
 })
 
 test('segv', function (t) {
-  var r = new Runner({argv:{remain:['./segv']}})
-  var tc = new TC()
-  var expect =
-      [ 'TAP version 13'
-      , './segv'
-      , { 'id': 1,
-          'ok': false,
-          'name': ' ././segv',
-          'exit': null,
-          'timedOut': true,
-          'signal': process.platform === 'linux' ? 'SIGSEGV' : 'SIGTERM',
-          'command': '"' + path.resolve('./segv') + '"' }
-      , 'tests 1'
-      , 'fail  1' ]
-  r.pipe(tc)
-  tc.on('data', function (d) {
-    var e = expect.shift()
-
-    // specific signal can be either term or bus
-    if (d.signal && e.signal)
-      e.signal = d.signal === "SIGTERM" || d.signal === "SIGBUS" ?
-        d.signal : e.signal
-
-    t.same(d, e)
+  var tt = new Test({ bail: false })
+  tt.spawn('./segv')
+  var res = ''
+  tt.on('data', function (c) {
+    res += c
   })
-  tc.on('end', function () {
-    t.equal(expect.length, 0)
+  tt.on('end', function () {
+    res = res.trim().split('\n')
+    res = res.sort()
+    expect = expect.sort()
+    var ok = true
+    expect.forEach(function (line, i) {
+      if (ok) {
+        ok = t.equal(res[i].substr(0, line.length), line)
+      }
+    })
     t.end()
   })
+  tt.end()
 })
 
 test('cleanup', function (t) {
-  fs.unlink('segv.c', function () {
-    fs.unlink('segv', function () {
-      t.pass('cleaned up')
-      t.end()
-    })
+  t.plan(2)
+  fs.unlink('segv', function (er) {
+    t.ifError(er, 'clean up segv')
+  })
+  fs.unlink('segv.c', function (er) {
+    t.ifError(er, 'clean up segv.c')
   })
 })
diff --git a/test/simple-harness-test-with-plan.js b/test/simple-harness-test-with-plan.js
index 813c4cf..6c81831 100644
--- a/test/simple-harness-test-with-plan.js
+++ b/test/simple-harness-test-with-plan.js
@@ -1,16 +1,16 @@
-var tap = require("../")
-  , test = tap.test
-  , plan = tap.plan
+var tap = require('../')
+var test = tap.test
+var plan = tap.plan
 
 plan(2)
 
-test("trivial success", function (t) {
-  t.ok(true, "it works")
+test('trivial success', function (t) {
+  t.ok(true, 'it works')
   t.end()
 })
 
-test("two tests", function (t) {
-  t.equal(255, 0xFF, "math should work")
-  t.notOk(false, "false should not be ok")
+test('two tests', function (t) {
+  t.equal(255, 0xFF, 'math should work')
+  t.notOk(false, 'false should not be ok')
   t.end()
 })
diff --git a/test/simple-harness-test.js b/test/simple-harness-test.js
index 64451ae..db1bbe4 100644
--- a/test/simple-harness-test.js
+++ b/test/simple-harness-test.js
@@ -1,13 +1,13 @@
-var tap = require("../")
-  , test = tap.test
+var tap = require('../')
+var test = tap.test
 
-test("trivial success", function (t) {
-  t.ok(true, "it works")
+test('trivial success', function (t) {
+  t.ok(true, 'it works')
   t.end()
 })
 
-test("two tests", function (t) {
-  t.equal(255, 0xFF, "math should work")
-  t.notOk(false, "false should not be ok")
+test('two tests', function (t) {
+  t.equal(255, 0xFF, 'math should work')
+  t.notOk(false, 'false should not be ok')
   t.end()
 })
diff --git a/test/test-assert-todo-skip.js b/test/test-assert-todo-skip.js
deleted file mode 100644
index 281551e..0000000
--- a/test/test-assert-todo-skip.js
+++ /dev/null
@@ -1,13 +0,0 @@
-var test = require('../').test
-
-test('not much', function(t) {
-  t.ok(true, 'always passes', {skip: true, explanation: 'skip it good'})
-  t.ok(false, 'false', {skip: true, explanation: 'always fails'})
-  t.ok(true, 'bonus', {todo: true, explanation: 'remove todo directive'})
-  t.ok(false, 'expected', {todo: true, explanation: 'implement a thing'})
-  t.ok(true, 'always passes without explanation', {skip: true})
-  t.ok(false, 'false without explanation', {skip: true})
-  t.ok(true, 'bonus without explanation', {todo: true})
-  t.ok(false, 'expected without explanation', {todo: true})
-  t.end()
-})
diff --git a/test/test-directives.js b/test/test-directives.js
deleted file mode 100644
index 5f771ae..0000000
--- a/test/test-directives.js
+++ /dev/null
@@ -1,48 +0,0 @@
-var execFile = require('child_process').execFile
-var node = process.execPath
-var bin = require.resolve('../bin/tap.js')
-var test = require('../').test
-var path = require('path')
-var fixtures = path.resolve(__dirname, 'fixtures')
-
-test('captures test descriptions', function (t) {
-  var useConsole = path.resolve(fixtures, 'todo-skip-descriptions.js')
-  var useTap = require.resolve('./test-assert-todo-skip.js')
-
-  t.test('raw TAP > TAP consumer > TAP producer', function(t) {
-    var args = [bin, '--tap', useConsole]
-    execFile(node, args, assertTapDirectives.bind(this, t))
-  })
-
-  t.test('raw TAP > TAP consumer > summary', function(t) {
-    var args = [bin, '--no-tap', useConsole]
-    execFile(node, args, assertDirectivesSummary.bind(this, t))
-  })
-
-  t.test('TAP producer via require("tap")', function(t) {
-    var args = [useTap]
-    execFile(node, args, assertTapDirectives.bind(this, t))
-  })
-
-  function assertTapDirectives(t, err, stdout, stderr) {
-    t.ifError(err, 'overall result is PASS')
-    t.assert(/passes # SKIP skip/i.test(stdout), 'captures ok SKIP')
-    t.assert(/false # SKIP always/i.test(stdout), 'captures not ok SKIP')
-    t.assert(!/skipped: \d+/.test(stdout), 'skip summary not in TAP output')
-    t.assert(/bonus # TODO remove/i.test(stdout), 'captures ok TODO')
-    t.assert(/expected # TODO implem/i.test(stdout), 'captures not ok TODO')
-    t.assert(!/todo: \d+/.test(stdout), 'todo summary is not in TAP output')
-    t.assert(!/undefined/.test(stdout), 'no ugly "undefined" in output')
-    t.end()
-  }
-
-  function assertDirectivesSummary(t, err, stdout, stderr) {
-    t.ifError(err, 'overall result is PASS')
-    t.assert(!/# SKIP/i.test(stdout), 'no SKIP in summary')
-    t.assert(/skipped: 2/.test(stdout), 'skip summary is not in TAP output')
-    t.assert(!/# TODO/i.test(stdout), 'no TODO in summary')
-    t.assert(/todo: 2/.test(stdout), 'todo summary is not in TAP output')
-    t.assert(!/undefined/.test(stdout), 'no ugly "undefined" in output')
-    t.end()
-  }
-})
diff --git a/test/test-skip.js b/test/test-skip.js
deleted file mode 100644
index 568da3e..0000000
--- a/test/test-skip.js
+++ /dev/null
@@ -1,5 +0,0 @@
-var tap = require('../');
-
-tap.test('does not count as failure', { skip: true }, function(t) {
-  t.end();
-})
diff --git a/test/test-test.js b/test/test-test.js
index f383941..5aa9cbd 100644
--- a/test/test-test.js
+++ b/test/test-test.js
@@ -1,91 +1,81 @@
-var tap = require("../")
-  , test = tap.test
-  , Test = require("../lib/tap-test")
-  , Harness = require("../lib/tap-harness")
+var tap = require('../')
+var test = tap.test
+var Test = tap.Test
 
-test("testing the test object", function (t) {
-
-  t.isa(t, Test, "test object should be instanceof Test")
-  t.isa(t, Harness, "test object should be instanceof Harness")
-  t.is(t._Test, Test, "test._Test should be the Test class")
+test('testing the test object', function (t) {
+  t.isa(t, Test, 'test object should be instanceof Test')
 
   // now test all the methods.
-  ; [ "isNotDeepEqual"
-    , "equals"
-    , "inequivalent"
-    , "threw"
-    , "strictEqual"
-    , "emit"
-    , "fail"
-    , "strictEquals"
-    , "notLike"
-    , "dissimilar"
-    , "true"
-    , "assert"
-    , "is"
-    , "ok"
-    , "isEqual"
-    , "isDeeply"
-    , "deepEqual"
-    , "deepEquals"
-    , "pass"
-    , "length"
-    , "skip"
-    , "isNotEqual"
-    , "looseEquals"
-    , "false"
-    , "notDeeply"
-    , "ifErr"
-    , "hasFields"
-    , "isNotDeeply"
-    , "like"
-    , "similar"
-    , "notOk"
-    , "isDissimilar"
-    , "isEquivalent"
-    , "doesNotEqual"
-    , "isSimilar"
-    , "notDeepEqual"
-    , "type"
-    , "notok"
-    , "isInequivalent"
-    , "isNot"
-    , "same"
-    , "isInequal"
-    , "_endNice"
-    , "ifError"
-    , "iferror"
-    , "clear"
-    , "has"
-    , "not"
-    , "timeout"
-    , "notSimilar"
-    , "isUnlike"
-    , "notEquals"
-    , "unsimilar"
-    , "result"
-    , "doesNotThrow"
-    , "error"
-    , "constructor"
-    , "notEqual"
-    , "throws"
-    , "isLike"
-    , "isNotSimilar"
-    , "isNotEquivalent"
-    , "inequal"
-    , "notEquivalent"
-    , "isNotLike"
-    , "equivalent"
-    , "looseEqual"
-    , "equal"
-    , "unlike"
-    , "doesNotHave"
-    , "comment"
-    , "isa"
-    ].forEach(function (method) {
-      t.ok(t[method], "should have "+method+" method")
-      t.isa(t[method], "function", method+" method should be a function")
-    })
-    t.end()
+  ;[
+    'isNotDeepEqual',
+    'equals',
+    'inequivalent',
+    'threw',
+    'strictEqual',
+    'emit',
+    'fail',
+    'strictEquals',
+    'notLike',
+    'dissimilar',
+    'true',
+    'assert',
+    'is',
+    'ok',
+    'isEqual',
+    'isDeeply',
+    'deepEqual',
+    'deepEquals',
+    'pass',
+    'isNotEqual',
+    'looseEquals',
+    'false',
+    'notDeeply',
+    'ifErr',
+    'hasFields',
+    'isNotDeeply',
+    'like',
+    'similar',
+    'notOk',
+    'isDissimilar',
+    'isEquivalent',
+    'doesNotEqual',
+    'isSimilar',
+    'notDeepEqual',
+    'type',
+    'notok',
+    'isInequivalent',
+    'isNot',
+    'same',
+    'isInequal',
+    'ifError',
+    'iferror',
+    'has',
+    'not',
+    'notSimilar',
+    'isUnlike',
+    'notEquals',
+    'unsimilar',
+    'doesNotThrow',
+    'error',
+    'constructor',
+    'notEqual',
+    'throws',
+    'isLike',
+    'isNotSimilar',
+    'isNotEquivalent',
+    'inequal',
+    'notEquivalent',
+    'isNotLike',
+    'equivalent',
+    'looseEqual',
+    'equal',
+    'unlike',
+    'doesNotHave',
+    'comment',
+    'isa'
+  ].forEach(function (method) {
+    t.ok(t[method], 'should have ' + method + ' method')
+    t.isa(t[method], 'function', method + ' method should be a function')
+  })
+  t.end()
 })
-
diff --git a/test/test.js b/test/test.js
new file mode 100644
index 0000000..645fb2a
--- /dev/null
+++ b/test/test.js
@@ -0,0 +1,135 @@
+var glob = require('glob')
+var t = require('../lib/root.js')
+var spawn = require('child_process').spawn
+var node = process.execPath
+var fs = require('fs')
+var dir = __dirname + '/test/'
+var path = require('path')
+var yaml = require('js-yaml')
+
+function regEsc (str) {
+  return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')
+}
+
+if (process.argv[2]) {
+  var file = path.resolve(dir, process.argv[2])
+  runTest(file, false)
+  runTest(file, true)
+} else {
+  glob.sync(dir + '*.js').forEach(function (file) {
+    runTest(file, false)
+    runTest(file, true)
+  })
+}
+
+function runTest (file, bail) {
+  var skip = false
+  if (file.match(/\bpending-handles.js$/) && process.env.TRAVIS) {
+    skip = 'pending handles test is too timing dependent for Travis'
+  }
+
+  var resfile = file.replace(/\.js$/, (bail ? '-bail' : '') + '.tap')
+  try {
+    var want = fs.readFileSync(resfile, 'utf8').split('\n')
+  } catch (er) {
+    console.error(er)
+    console.error(file)
+    console.error(resfile)
+    return
+  }
+
+  var f = file.substr(dir.length)
+  t.test(f + (bail ? ' bail' : ''), { skip: skip }, function (t) {
+    var child = spawn(node, [file], {
+      stdio: [ 0, 'pipe', 'pipe' ],
+      env: {
+        TAP_BAIL: bail ? 1 : 0
+      }
+    })
+
+    var found = ''
+
+    child.stdout.setEncoding('utf8')
+    child.stdout.on('data', function (c) {
+      found += c
+    })
+    child.on('close', function (er) {
+      found = found.split('\n')
+      var inyaml = false
+      var startlen = 0
+      var y = ''
+
+      // walk line by line so yamlish (json) can be handled
+      // otherwise making any changes in this lib would hurt
+      for (var f = 0, w = 0;
+           f < found.length && w < want.length;
+           f++, w++) {
+        var wline = want[w]
+        var fline = found[f]
+        var wdata = false
+
+        if (inyaml) {
+          if (fline.match(/^\s*\.\.\.$/) && fline.length === startlen) {
+            var data = yaml.safeLoad(y)
+            inyaml = false
+            y = ''
+            wdata = JSON.parse(wline)
+            patternify(wdata)
+            t.match(data, wdata)
+            f--
+          } else {
+            y += fline + '\n'
+            w--
+          }
+          continue
+        } else {
+          t.match(fline, patternify(wline),
+                  'line ' + f + ' ' +
+                  wline.replace(/# (todo|skip)/gi, '- $1'),
+                  { test: f })
+
+          if (fline.match(/^\s*\-\-\-$/)) {
+            startlen = fline.length
+            inyaml = true
+            y = ''
+          }
+        }
+
+        if (!t.passing()) {
+          return t.end()
+        }
+      }
+      t.end()
+    })
+  })
+}
+
+function patternify (pattern) {
+  if (typeof pattern === 'object' && pattern) {
+    Object.keys(pattern).forEach(function (k) {
+      pattern[k] = patternify(pattern[k])
+    })
+    return pattern
+  }
+
+  if (typeof pattern !== 'string') {
+    return pattern
+  }
+
+  var re = /___\/(.*?)\/~~~/
+  var match = pattern.match(re)
+  if (!match) {
+    return pattern
+  }
+
+  var pl = pattern.split('___/')
+  var p = '^' + regEsc(pl.shift())
+
+  pl.forEach(function (wlpart) {
+    var wlp = wlpart.split('/~~~')
+    p += wlp.shift()
+    p += regEsc(wlp.join('/~~~'))
+  })
+  p += '$'
+  return new RegExp(p)
+}
diff --git a/test/test/assert-todo-skip-bail.tap b/test/test/assert-todo-skip-bail.tap
new file mode 100644
index 0000000..2f2ecce
--- /dev/null
+++ b/test/test/assert-todo-skip-bail.tap
@@ -0,0 +1,22 @@
+TAP version 13
+    # Subtest: not much
+    ok 1 - always passes # SKIP skip it good
+    not ok 2 - false # SKIP always fails
+    ok 3 - bonus # TODO remove todo directive
+    not ok 4 - expected # TODO implement a thing
+      ---
+      {"at":{"column":5,"file":"test/test/assert-todo-skip.js","line":7},"source":"t.ok(false, 'expected', {todo: 'implement a thing'})\n"}
+      ...
+    ok 5 - always passes without explanation # SKIP
+    not ok 6 - false without explanation # SKIP
+    ok 7 - bonus without explanation # TODO
+    not ok 8 - expected without explanation # TODO
+      ---
+      {"at":{"column":5,"file":"test/test/assert-todo-skip.js","line":11},"source":"t.ok(false, 'expected without explanation', {todo: true})\n"}
+      ...
+    1..8
+ok 1 - not much ___/# time=[0-9.]+(ms)?/~~~
+
+1..1
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/assert-todo-skip.js b/test/test/assert-todo-skip.js
new file mode 100644
index 0000000..4ae353b
--- /dev/null
+++ b/test/test/assert-todo-skip.js
@@ -0,0 +1,13 @@
+var test = require('../../lib/root.js').test
+
+test('not much', function (t) {
+  t.ok(true, 'always passes', {skip: 'skip it good'})
+  t.ok(false, 'false', {skip: 'always fails'})
+  t.ok(true, 'bonus', {todo: 'remove todo directive'})
+  t.ok(false, 'expected', {todo: 'implement a thing'})
+  t.ok(true, 'always passes without explanation', {skip: true})
+  t.ok(false, 'false without explanation', {skip: true})
+  t.ok(true, 'bonus without explanation', {todo: true})
+  t.ok(false, 'expected without explanation', {todo: true})
+  t.end()
+})
diff --git a/test/test/assert-todo-skip.tap b/test/test/assert-todo-skip.tap
new file mode 100644
index 0000000..2f2ecce
--- /dev/null
+++ b/test/test/assert-todo-skip.tap
@@ -0,0 +1,22 @@
+TAP version 13
+    # Subtest: not much
+    ok 1 - always passes # SKIP skip it good
+    not ok 2 - false # SKIP always fails
+    ok 3 - bonus # TODO remove todo directive
+    not ok 4 - expected # TODO implement a thing
+      ---
+      {"at":{"column":5,"file":"test/test/assert-todo-skip.js","line":7},"source":"t.ok(false, 'expected', {todo: 'implement a thing'})\n"}
+      ...
+    ok 5 - always passes without explanation # SKIP
+    not ok 6 - false without explanation # SKIP
+    ok 7 - bonus without explanation # TODO
+    not ok 8 - expected without explanation # TODO
+      ---
+      {"at":{"column":5,"file":"test/test/assert-todo-skip.js","line":11},"source":"t.ok(false, 'expected without explanation', {todo: true})\n"}
+      ...
+    1..8
+ok 1 - not much ___/# time=[0-9.]+(ms)?/~~~
+
+1..1
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/async-bail.tap b/test/test/async-bail.tap
new file mode 100644
index 0000000..075711f
--- /dev/null
+++ b/test/test/async-bail.tap
@@ -0,0 +1,14 @@
+TAP version 13
+    # Subtest: first test
+    ok 1 - this is ok
+    1..1
+ok 1 - first test ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: second test (async)
+    ok 1 - this is ok
+    1..1
+ok 2 - second test (async) ___/# time=[0-9.]+(ms)?/~~~
+
+1..2
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/async.js b/test/test/async.js
new file mode 100644
index 0000000..2f741bf
--- /dev/null
+++ b/test/test/async.js
@@ -0,0 +1,13 @@
+var t = require('../..')
+
+t.test('first test', function (t) {
+  t.pass('this is ok')
+  t.end()
+})
+
+setTimeout(function () {
+  t.test('second test (async)', function (t) {
+    t.pass('this is ok')
+    t.end()
+  })
+})
diff --git a/test/test/async.tap b/test/test/async.tap
new file mode 100644
index 0000000..075711f
--- /dev/null
+++ b/test/test/async.tap
@@ -0,0 +1,14 @@
+TAP version 13
+    # Subtest: first test
+    ok 1 - this is ok
+    1..1
+ok 1 - first test ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: second test (async)
+    ok 1 - this is ok
+    1..1
+ok 2 - second test (async) ___/# time=[0-9.]+(ms)?/~~~
+
+1..2
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/bail-child-bail.tap b/test/test/bail-child-bail.tap
new file mode 100644
index 0000000..06b4b0a
--- /dev/null
+++ b/test/test/bail-child-bail.tap
@@ -0,0 +1,10 @@
+TAP version 13
+    # Subtest: bail fail
+        # Subtest: failer
+        not ok 1 - this fails
+          ---
+          {"at":{"column":7,"file":"test/test/bail-child.js","line":7},"source":"t.fail('this fails')\n"}
+          ...
+        Bail out! # this fails
+Bail out! # this fails
+
diff --git a/test/test/bail-child.js b/test/test/bail-child.js
new file mode 100644
index 0000000..24183d5
--- /dev/null
+++ b/test/test/bail-child.js
@@ -0,0 +1,17 @@
+// test that failOnBail is contagious to child processes
+
+var t = require('../..')
+
+t.test('bail fail', { bail: true }, function (t) {
+  t.test('failer', function (t) {
+    t.fail('this fails')
+    t.ok('should not see this')
+    t.end()
+  })
+  t.end()
+})
+
+t.test('this should never happen', function (t) {
+  t.fail('nope')
+  t.end()
+})
diff --git a/test/test/bail-child.tap b/test/test/bail-child.tap
new file mode 100644
index 0000000..06b4b0a
--- /dev/null
+++ b/test/test/bail-child.tap
@@ -0,0 +1,10 @@
+TAP version 13
+    # Subtest: bail fail
+        # Subtest: failer
+        not ok 1 - this fails
+          ---
+          {"at":{"column":7,"file":"test/test/bail-child.js","line":7},"source":"t.fail('this fails')\n"}
+          ...
+        Bail out! # this fails
+Bail out! # this fails
+
diff --git a/test/test/bail-error-object-bail.tap b/test/test/bail-error-object-bail.tap
new file mode 100644
index 0000000..3cd45b4
--- /dev/null
+++ b/test/test/bail-error-object-bail.tap
@@ -0,0 +1,3 @@
+TAP version 13
+Bail out! # TypeError: wat
+
diff --git a/test/test/bail-error-object.js b/test/test/bail-error-object.js
new file mode 100644
index 0000000..352e64f
--- /dev/null
+++ b/test/test/bail-error-object.js
@@ -0,0 +1,8 @@
+var t = require('../..')
+
+var EE = require('events').EventEmitter
+var ee = new EE()
+ee.on('error', t.bailout.bind(t))
+setTimeout(function () {
+  ee.emit('error', new TypeError('wat'))
+})
diff --git a/test/test/bail-error-object.tap b/test/test/bail-error-object.tap
new file mode 100644
index 0000000..3cd45b4
--- /dev/null
+++ b/test/test/bail-error-object.tap
@@ -0,0 +1,3 @@
+TAP version 13
+Bail out! # TypeError: wat
+
diff --git a/test/test/bail-fail-spawn-bail.tap b/test/test/bail-fail-spawn-bail.tap
new file mode 100644
index 0000000..d382356
--- /dev/null
+++ b/test/test/bail-fail-spawn-bail.tap
@@ -0,0 +1,21 @@
+TAP version 13
+    # Subtest: bail fail
+        # Subtest: ./test/test/nesting.js
+            # Subtest: nesting
+            1..2
+                # Subtest: first
+                1..2
+                ok 1 - true is ok
+                ok 2 - doag is also okay
+            ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+                # Subtest: second
+                ok 1 - but that is ok
+                ok 2 - this passes
+                not ok 3 - nested failure
+                  ---
+                  {"at":{"column":10,"file":"test/test/nesting.js","function":"foo","line":14},"compare":"===","found":1,"source":"tt.equal(1, '1', 'nested failure')\n","wanted":"1"}
+                  ...
+                Bail out! # nested failure
+Bail out! # nested failure
+
diff --git a/test/test/bail-fail-spawn.js b/test/test/bail-fail-spawn.js
new file mode 100644
index 0000000..d042488
--- /dev/null
+++ b/test/test/bail-fail-spawn.js
@@ -0,0 +1,13 @@
+// test that failOnBail is contagious to child processes
+
+var t = require('../..')
+
+t.test('bail fail', { bail: true }, function (t) {
+  t.spawn(process.execPath, [require.resolve('./nesting.js')])
+  t.end()
+})
+
+t.test('this should never happen', function (t) {
+  t.fail('nope')
+  t.end()
+})
diff --git a/test/test/bail-fail-spawn.tap b/test/test/bail-fail-spawn.tap
new file mode 100644
index 0000000..d382356
--- /dev/null
+++ b/test/test/bail-fail-spawn.tap
@@ -0,0 +1,21 @@
+TAP version 13
+    # Subtest: bail fail
+        # Subtest: ./test/test/nesting.js
+            # Subtest: nesting
+            1..2
+                # Subtest: first
+                1..2
+                ok 1 - true is ok
+                ok 2 - doag is also okay
+            ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+                # Subtest: second
+                ok 1 - but that is ok
+                ok 2 - this passes
+                not ok 3 - nested failure
+                  ---
+                  {"at":{"column":10,"file":"test/test/nesting.js","function":"foo","line":14},"compare":"===","found":1,"source":"tt.equal(1, '1', 'nested failure')\n","wanted":"1"}
+                  ...
+                Bail out! # nested failure
+Bail out! # nested failure
+
diff --git a/test/test/bailout-bail.tap b/test/test/bailout-bail.tap
new file mode 100644
index 0000000..8a95934
--- /dev/null
+++ b/test/test/bailout-bail.tap
@@ -0,0 +1,23 @@
+TAP version 13
+    # Subtest: nesting
+    1..2
+        # Subtest: first
+        1..2
+        ok 1 - true is ok
+        ok 2 - doag is also okay
+    ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+        # Subtest: second
+        ok 1 - but that is ok
+        ok 2 - this passes
+        1..2
+    ok 2 - second ___/# time=[0-9.]+(ms)?/~~~
+ok 1 - nesting ___/# time=[0-9.]+(ms)?/~~~
+
+ok 2 - this passes
+not ok 3 - this fails
+  ---
+  {"at":{"column":3,"file":"test/test/bailout.js","line":18},"source":"t.fail('this fails')\n"}
+  ...
+Bail out! # this fails
+
diff --git a/test/test/bailout.js b/test/test/bailout.js
new file mode 100644
index 0000000..7ca05ba
--- /dev/null
+++ b/test/test/bailout.js
@@ -0,0 +1,34 @@
+var t = require('../../lib/test.js')()
+
+t.test('nesting', function (t) {
+  t.plan(2)
+  t.test('first', function (tt) {
+    tt.plan(2)
+    tt.ok(true, 'true is ok')
+    tt.ok('doeg', 'doag is also okay')
+  })
+  t.test('second', function (tt) {
+    tt.ok('no plan', 'but that is ok')
+    tt.pass('this passes')
+    tt.end()
+  })
+})
+
+t.pass('this passes')
+t.fail('this fails')
+
+t.test('async kid', function (t) {
+  t.plan(2)
+  setTimeout(function () {
+    t.pass('first timeout', { foo: 'blz' })
+  }, 50)
+  setTimeout(function () {
+    t.bailout('cannot continue')
+  })
+})
+
+t.pass('pass after async kid')
+
+t.end()
+
+t.pipe(process.stdout)
diff --git a/test/test/bailout.tap b/test/test/bailout.tap
new file mode 100644
index 0000000..52fd84e
--- /dev/null
+++ b/test/test/bailout.tap
@@ -0,0 +1,26 @@
+TAP version 13
+    # Subtest: nesting
+    1..2
+        # Subtest: first
+        1..2
+        ok 1 - true is ok
+        ok 2 - doag is also okay
+    ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+        # Subtest: second
+        ok 1 - but that is ok
+        ok 2 - this passes
+        1..2
+    ok 2 - second ___/# time=[0-9.]+(ms)?/~~~
+ok 1 - nesting ___/# time=[0-9.]+(ms)?/~~~
+
+ok 2 - this passes
+not ok 3 - this fails
+  ---
+  {"at":{"column":3,"file":"test/test/bailout.js","line":18},"source":"t.fail('this fails')\n"}
+  ...
+    # Subtest: async kid
+    1..2
+    Bail out! # cannot continue
+Bail out! # cannot continue
+
diff --git a/test/test/buffer-yaml-bail.tap b/test/test/buffer-yaml-bail.tap
new file mode 100644
index 0000000..625c02e
--- /dev/null
+++ b/test/test/buffer-yaml-bail.tap
@@ -0,0 +1,7 @@
+TAP version 13
+not ok 1 - should be equivalent
+  ---
+  {"at":{"column":3,"file":"test/test/buffer-yaml.js","line":5},"found":"Buffer\n6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f\n74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20\n6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b\n6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f\n74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20\n6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b\n6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74  [...]
+  ...
+Bail out! # should be equivalent
+
diff --git a/test/test/buffer-yaml.js b/test/test/buffer-yaml.js
new file mode 100644
index 0000000..2955b6d
--- /dev/null
+++ b/test/test/buffer-yaml.js
@@ -0,0 +1,5 @@
+var t = require('../..')
+
+var str = new Buffer(new Array(100).join('not ok'))
+var xyz = new Buffer(new Array(101).join('not ok'))
+t.same(str, xyz)
diff --git a/test/test/buffer-yaml.tap b/test/test/buffer-yaml.tap
new file mode 100644
index 0000000..31575ef
--- /dev/null
+++ b/test/test/buffer-yaml.tap
@@ -0,0 +1,9 @@
+TAP version 13
+not ok 1 - should be equivalent
+  ---
+  {"at":{"column":3,"file":"test/test/buffer-yaml.js","line":5},"found":"Buffer\n6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f\n74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20\n6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b\n6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f\n74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20\n6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b\n6e 6f 74 20 6f 6b 6e 6f 74 20 6f 6b 6e 6f 74  [...]
+  ...
+1..1
+# failed 1 of 1 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/console-log-bail.tap b/test/test/console-log-bail.tap
new file mode 100644
index 0000000..bc9e33a
--- /dev/null
+++ b/test/test/console-log-bail.tap
@@ -0,0 +1,42 @@
+>>>> before any tests
+TAP version 13
+    # Subtest: nesting
+    1..2
+        # Subtest: first
+        1..3
+        ok 1 - true is ok
+>>>> an object is { a: 'thing', to: [ 'inspect' ] }
+        ok 2 - doag is also okay
+        ok 3 - doag is very okay
+    ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+>>>> after first child
+        # Subtest: second
+        ok 1 - but that is ok
+        ok 2 - this passes
+        ok 3 - nested ok
+        ok 4 - nested ok second
+        1..4
+    ok 2 - second ___/# time=[0-9.]+(ms)?/~~~
+ok 1 - nesting ___/# time=[0-9.]+(ms)?/~~~
+
+>>>> after second child
+>>>> after child test
+ok 2 - this passes
+ok 3 - this passes too
+>>>> after pass() calls
+    # Subtest: async kid
+>>>> in async kid, before plan
+    1..2
+>>>> in async kid, after plan
+>>>> after async kid
+>>>> after all tests, before end()
+>>>> after end() called
+    ok 1 - timeout
+    ok 2 - timeout
+ok 4 - async kid ___/# time=[0-9.]+(ms)?/~~~
+
+ok 5 - pass after async kid
+1..5
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/console-log.js b/test/test/console-log.js
new file mode 100644
index 0000000..b2f6923
--- /dev/null
+++ b/test/test/console-log.js
@@ -0,0 +1,62 @@
+var t = require('../../lib/root.js')
+
+console.log('>>>> before any tests')
+
+t.test('nesting', function (t) {
+  t.plan(2)
+  t.test('first', function (tt) {
+    tt.plan(3)
+    tt.ok(true, 'true is ok')
+    console.log('>>>> an object is', { a: 'thing', to: [ 'inspect' ] })
+    tt.assert('doeg', 'doag is also okay')
+    tt.assert('doeg', 'doag is very okay')
+  })
+  console.log('>>>> after first child')
+  t.test('second', function (tt) {
+    function foo () {
+      tt.ok('no plan', 'but that is ok')
+      tt.pass('this passes')
+      tt.equal(1, 1, 'nested ok')
+      console.trace('in foo')
+      tt.equal(1, 1, 'nested ok second')
+      tt.end()
+    }
+    function bar () {
+      return foo()
+    }
+    function baz () {
+      return bar()
+    }
+    baz()
+  })
+  console.log('>>>> after second child')
+})
+
+console.log('>>>> after child test')
+
+t.pass('this passes')
+t.pass('this passes too')
+
+console.log('>>>> after pass() calls')
+
+t.test('async kid', function (t) {
+  console.log('>>>> in async kid, before plan')
+  t.plan(2)
+  console.log('>>>> in async kid, after plan')
+  setTimeout(function () {
+    t.ok(true, 'timeout', { foo: 'blz' })
+  }, 50)
+  setTimeout(function () {
+    t.pass('timeout')
+  })
+})
+
+console.log('>>>> after async kid')
+
+t.pass('pass after async kid')
+
+console.log('>>>> after all tests, before end()')
+
+t.end()
+
+console.log('>>>> after end() called')
diff --git a/test/test/console-log.tap b/test/test/console-log.tap
new file mode 100644
index 0000000..bc9e33a
--- /dev/null
+++ b/test/test/console-log.tap
@@ -0,0 +1,42 @@
+>>>> before any tests
+TAP version 13
+    # Subtest: nesting
+    1..2
+        # Subtest: first
+        1..3
+        ok 1 - true is ok
+>>>> an object is { a: 'thing', to: [ 'inspect' ] }
+        ok 2 - doag is also okay
+        ok 3 - doag is very okay
+    ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+>>>> after first child
+        # Subtest: second
+        ok 1 - but that is ok
+        ok 2 - this passes
+        ok 3 - nested ok
+        ok 4 - nested ok second
+        1..4
+    ok 2 - second ___/# time=[0-9.]+(ms)?/~~~
+ok 1 - nesting ___/# time=[0-9.]+(ms)?/~~~
+
+>>>> after second child
+>>>> after child test
+ok 2 - this passes
+ok 3 - this passes too
+>>>> after pass() calls
+    # Subtest: async kid
+>>>> in async kid, before plan
+    1..2
+>>>> in async kid, after plan
+>>>> after async kid
+>>>> after all tests, before end()
+>>>> after end() called
+    ok 1 - timeout
+    ok 2 - timeout
+ok 4 - async kid ___/# time=[0-9.]+(ms)?/~~~
+
+ok 5 - pass after async kid
+1..5
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/empty-bail.tap b/test/test/empty-bail.tap
new file mode 100644
index 0000000..e71e248
--- /dev/null
+++ b/test/test/empty-bail.tap
@@ -0,0 +1,4 @@
+TAP version 13
+1..0
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/empty.js b/test/test/empty.js
new file mode 100644
index 0000000..2042587
--- /dev/null
+++ b/test/test/empty.js
@@ -0,0 +1 @@
+require('../..')
diff --git a/test/test/empty.tap b/test/test/empty.tap
new file mode 100644
index 0000000..e71e248
--- /dev/null
+++ b/test/test/empty.tap
@@ -0,0 +1,4 @@
+TAP version 13
+1..0
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/end-end-bail.tap b/test/test/end-end-bail.tap
new file mode 100644
index 0000000..546f663
--- /dev/null
+++ b/test/test/end-end-bail.tap
@@ -0,0 +1,11 @@
+TAP version 13
+    # Subtest: end end
+    1..0
+ok 1 - end end ___/# time=[0-9.]+(ms)?/~~~
+
+not ok 2 - Error: test end() method called more than once
+  ---
+  {"message":"Error: test end() method called more than once","test":"end end"}
+  ...
+Bail out! # Error: test end() method called more than once
+
diff --git a/test/test/end-end.js b/test/test/end-end.js
new file mode 100644
index 0000000..37ddaef
--- /dev/null
+++ b/test/test/end-end.js
@@ -0,0 +1,41 @@
+var t = require('../..')
+
+t.test('end end', function (t) {
+  t.end()
+  t.end()
+})
+
+t.test('end then async end', function (t) {
+  t.end()
+  setTimeout(function () {
+    t.end()
+  })
+})
+
+t.test('double async end', function (t) {
+  setTimeout(function () {
+    t.end()
+  })
+  setTimeout(function () {
+    t.end()
+  }, 100)
+})
+
+t.test('plan end', function (t) {
+  t.plan(1)
+  t.pass('this is fine')
+  t.end()
+})
+
+t.test('plan then async end', function (t) {
+  t.plan(1)
+  t.pass('this is fine')
+  setTimeout(function () { t.end() })
+})
+
+t.test('plan end end', function (t) {
+  t.plan(1)
+  t.pass('this is fine')
+  t.end()
+  t.end()
+})
diff --git a/test/test/end-end.tap b/test/test/end-end.tap
new file mode 100644
index 0000000..178a167
--- /dev/null
+++ b/test/test/end-end.tap
@@ -0,0 +1,48 @@
+TAP version 13
+    # Subtest: end end
+    1..0
+ok 1 - end end ___/# time=[0-9.]+(ms)?/~~~
+
+not ok 2 - Error: test end() method called more than once
+  ---
+  {"message":"Error: test end() method called more than once","test":"end end"}
+  ...
+    # Subtest: end then async end
+    1..0
+ok 3 - end then async end ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: double async end
+    1..0
+ok 4 - double async end ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: plan end
+    1..1
+    ok 1 - this is fine
+ok 5 - plan end ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: plan then async end
+    1..1
+    ok 1 - this is fine
+ok 6 - plan then async end ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: plan end end
+    1..1
+    ok 1 - this is fine
+ok 7 - plan end end ___/# time=[0-9.]+(ms)?/~~~
+
+not ok 8 - Error: test end() method called more than once
+  ---
+  {"message":"Error: test end() method called more than once","test":"plan end end"}
+  ...
+not ok 9 - Error: test end() method called more than once
+  ---
+  {"message":"Error: test end() method called more than once","test":"end then async end"}
+  ...
+not ok 10 - Error: test end() method called more than once
+  ---
+  {"message":"Error: test end() method called more than once","test":"double async end"}
+  ...
+1..10
+# failed 4 of 10 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/end-event-timing-bail.tap b/test/test/end-event-timing-bail.tap
new file mode 100644
index 0000000..b239042
--- /dev/null
+++ b/test/test/end-event-timing-bail.tap
@@ -0,0 +1,20 @@
+TAP version 13
+    # Subtest: first
+> start first
+> before ending first
+    1..0
+ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+> first end event
+    # Subtest: second
+> start second
+> after ending first
+> before ending second
+    1..0
+ok 2 - second ___/# time=[0-9.]+(ms)?/~~~
+
+> second end event
+> after ending second
+1..2
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/end-event-timing.js b/test/test/end-event-timing.js
new file mode 100644
index 0000000..6d88b93
--- /dev/null
+++ b/test/test/end-event-timing.js
@@ -0,0 +1,25 @@
+var t = require('../..')
+
+t.test('first', function (t) {
+  console.log('> start first')
+  process.nextTick(function () {
+    t.on('end', function () {
+      console.log('> first end event')
+    })
+    console.log('> before ending first')
+    t.end()
+    console.log('> after ending first')
+  })
+})
+
+t.test('second', function (t) {
+  console.log('> start second')
+  process.nextTick(function () {
+    t.on('end', function () {
+      console.log('> second end event')
+    })
+    console.log('> before ending second')
+    t.end()
+    console.log('> after ending second')
+  })
+})
diff --git a/test/test/end-event-timing.tap b/test/test/end-event-timing.tap
new file mode 100644
index 0000000..b239042
--- /dev/null
+++ b/test/test/end-event-timing.tap
@@ -0,0 +1,20 @@
+TAP version 13
+    # Subtest: first
+> start first
+> before ending first
+    1..0
+ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+> first end event
+    # Subtest: second
+> start second
+> after ending first
+> before ending second
+    1..0
+ok 2 - second ___/# time=[0-9.]+(ms)?/~~~
+
+> second end event
+> after ending second
+1..2
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/end-exception-bail.tap b/test/test/end-exception-bail.tap
new file mode 100644
index 0000000..16176f1
--- /dev/null
+++ b/test/test/end-exception-bail.tap
@@ -0,0 +1,13 @@
+TAP version 13
+    # Subtest: (unnamed test)
+    1..1
+    ok 1 - should be equal
+ok 1 - (unnamed test) ___/# time=[0-9.]+(ms)?/~~~
+
+end()
+not ok 2 - Error: beep
+  ---
+  {"at":{"column":11,"file":"test/test/end-exception.js","function":"Test.<anonymous>","line":8},"message":"Error: beep","source":"throw new Error('beep')\n","test":"(unnamed test)"}
+  ...
+Bail out! # Error: beep
+
diff --git a/test/end-exception/t.js b/test/test/end-exception.js
similarity index 80%
rename from test/end-exception/t.js
rename to test/test/end-exception.js
index eaa5b46..0369993 100644
--- a/test/end-exception/t.js
+++ b/test/test/end-exception.js
@@ -1,4 +1,4 @@
-var test = require("../../").test
+var test = require('../../').test
 
 test(function (t) {
   t.plan(1)
diff --git a/test/test/end-exception.tap b/test/test/end-exception.tap
new file mode 100644
index 0000000..3f3af72
--- /dev/null
+++ b/test/test/end-exception.tap
@@ -0,0 +1,15 @@
+TAP version 13
+    # Subtest: (unnamed test)
+    1..1
+    ok 1 - should be equal
+ok 1 - (unnamed test) ___/# time=[0-9.]+(ms)?/~~~
+
+end()
+not ok 2 - Error: beep
+  ---
+  {"at":{"column":11,"file":"test/test/end-exception.js","function":"Test.<anonymous>","line":8},"message":"Error: beep","source":"throw new Error('beep')\n","test":"(unnamed test)"}
+  ...
+1..2
+# failed 1 of 2 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/equivalent-bail.tap b/test/test/equivalent-bail.tap
new file mode 100644
index 0000000..1bfe331
--- /dev/null
+++ b/test/test/equivalent-bail.tap
@@ -0,0 +1,9 @@
+TAP version 13
+    # Subtest: child test
+    not ok 1 - should be equal
+      ---
+      {"at":{"column":5,"file":"test/test/equivalent.js","line":3},"compare":"===","found":"foo\nbaz\nbar\n","source":"t.equal('foo\\nbaz\\nbar\\n', 'foo\\nblerb\\nbar\\n')\n","wanted":"foo\nblerb\nbar\n"}
+      ...
+    Bail out! # should be equal
+Bail out! # should be equal
+
diff --git a/test/test/equivalent.js b/test/test/equivalent.js
new file mode 100644
index 0000000..cb85fd7
--- /dev/null
+++ b/test/test/equivalent.js
@@ -0,0 +1,12 @@
+var t = require('../..')
+t.test('child test', function (t) {
+  t.equal('foo\nbaz\nbar\n', 'foo\nblerb\nbar\n')
+  t.equal('foo', 'foople')
+  t.equal(1, '1')
+  t.same({ foo: 'bar', bar: 1, extra: 9 }, { bar: 1, foo: 'baz', missing: true })
+  t.same({ foop: 2, foo: 'bar', bar: 1, extra: 9 },
+    { bar: 1, foo: 'baz', foop: 2, missing: true })
+  t.same({ foo: 'baz', bar: 1, extra: 9, x: [1, 2], prop: 1 }, { prop: 1, bar: 1, foo: 'baz', missing: true, x: [1, 2], z: 1 })
+
+  t.end()
+})
diff --git a/test/test/equivalent.tap b/test/test/equivalent.tap
new file mode 100644
index 0000000..bb6c926
--- /dev/null
+++ b/test/test/equivalent.tap
@@ -0,0 +1,37 @@
+TAP version 13
+    # Subtest: child test
+    not ok 1 - should be equal
+      ---
+      {"at":{"column":5,"file":"test/test/equivalent.js","line":3},"compare":"===","found":"foo\nbaz\nbar\n","source":"t.equal('foo\\nbaz\\nbar\\n', 'foo\\nblerb\\nbar\\n')\n","wanted":"foo\nblerb\nbar\n"}
+      ...
+    not ok 2 - should be equal
+      ---
+      {"at":{"column":5,"file":"test/test/equivalent.js","line":4},"compare":"===","found":"foo","source":"t.equal('foo', 'foople')\n","wanted":"foople"}
+      ...
+    not ok 3 - should be equal
+      ---
+      {"at":{"column":5,"file":"test/test/equivalent.js","line":5},"compare":"===","found":1,"source":"t.equal(1, '1')\n","wanted":"1"}
+      ...
+    not ok 4 - should be equivalent
+      ---
+      {"at":{"column":5,"file":"test/test/equivalent.js","line":6},"found":{"bar":1,"extra":9,"foo":"bar"},"source":"t.same({ foo: 'bar', bar: 1, extra: 9 }, { bar: 1, foo: 'baz', missing: true })\n","wanted":{"bar":1,"foo":"baz","missing":true}}
+      ...
+    not ok 5 - should be equivalent
+      ---
+      {"at":{"column":5,"file":"test/test/equivalent.js","line":7},"found":{"bar":1,"extra":9,"foo":"bar","foop":2},"source":"t.same({ foop: 2, foo: 'bar', bar: 1, extra: 9 },\n","wanted":{"bar":1,"foo":"baz","foop":2,"missing":true}}
+      ...
+    not ok 6 - should be equivalent
+      ---
+      {"at":{"column":5,"file":"test/test/equivalent.js","line":9},"found":{"bar":1,"extra":9,"foo":"baz","prop":1,"x":[1,2]},"source":"t.same({ foo: 'baz', bar: 1, extra: 9, x: [1, 2], prop: 1 }, { prop: 1, bar: 1, foo: 'baz', missing: true, x: [1, 2], z: 1 })\n","wanted":{"bar":1,"foo":"baz","missing":true,"prop":1,"x":[1,2],"z":1}}
+      ...
+    1..6
+    # failed 6 of 6 tests
+not ok 1 - child test ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/equivalent.js","line":2},"results":{"count":6,"fail":6,"ok":false,"pass":0,"plan":{"end":6,"start":1}},"source":"t.test('child test', function (t) {\n"}
+  ...
+
+1..1
+# failed 1 of 1 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/exit-on-bailout-bail.tap b/test/test/exit-on-bailout-bail.tap
new file mode 100644
index 0000000..ec40ad4
--- /dev/null
+++ b/test/test/exit-on-bailout-bail.tap
@@ -0,0 +1,9 @@
+log in root, before child
+TAP version 13
+    # Subtest: child
+log in child, before grandchild
+        # Subtest: grandchild
+log in grandchild, before bailout
+        Bail out! # cannot continue
+Bail out! # cannot continue
+
diff --git a/test/test/exit-on-bailout.js b/test/test/exit-on-bailout.js
new file mode 100644
index 0000000..a8dfbc0
--- /dev/null
+++ b/test/test/exit-on-bailout.js
@@ -0,0 +1,13 @@
+var tap = require('../..')
+
+console.log('log in root, before child')
+tap.test('child', function (t) {
+  console.log('log in child, before grandchild')
+  t.test('grandchild', function (t) {
+    console.log('log in grandchild, before bailout')
+    t.bailout('cannot continue')
+    console.log('log in grandchild, after bailout')
+  })
+  console.log('log in child, after grandchild')
+})
+console.log('log in root, after child')
diff --git a/test/test/exit-on-bailout.tap b/test/test/exit-on-bailout.tap
new file mode 100644
index 0000000..ec40ad4
--- /dev/null
+++ b/test/test/exit-on-bailout.tap
@@ -0,0 +1,9 @@
+log in root, before child
+TAP version 13
+    # Subtest: child
+log in child, before grandchild
+        # Subtest: grandchild
+log in grandchild, before bailout
+        Bail out! # cannot continue
+Bail out! # cannot continue
+
diff --git a/test/test/mochalike-bail.tap b/test/test/mochalike-bail.tap
new file mode 100644
index 0000000..52321c4
--- /dev/null
+++ b/test/test/mochalike-bail.tap
@@ -0,0 +1,52 @@
+TAP version 13
+    # Subtest: a set of tests to be done later
+    ok 1 - should have a thingie # TODO
+    ok 2 - should have a second whoosits also # TODO
+        # Subtest: the subset
+        ok 1 - should be a child thingie # TODO
+        ok 2 - should also be a whoosits # TODO
+            # Subtest: has some of these things
+            1..0
+        ok 3 - has some of these things ___/# time=[0-9.]+(ms)?/~~~
+
+        1..3
+    ok 3 - the subset ___/# time=[0-9.]+(ms)?/~~~
+
+    1..3
+ok 1 - a set of tests to be done later ___/# time=[0-9.]+(ms)?/~~~
+
+ok 2 - describe todo # TODO
+    # Subtest: another set of tests
+    ok 1 - is a second set # TODO
+    ok 2 - looks like english # TODO
+    ok 3 - is marked TODO # TODO
+    1..3
+ok 3 - another set of tests ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: reasonably indented things
+        # Subtest: first subset
+        ok 1 - has no asserts, only fails to throw
+        ok 2 - is todo # TODO
+            # Subtest: is async
+            1..0
+        ok 3 - is async ___/# time=[0-9.]+(ms)?/~~~
+
+        1..3
+    ok 1 - first subset ___/# time=[0-9.]+(ms)?/~~~
+
+        # Subtest: second subset
+        1..0
+    ok 2 - second subset ___/# time=[0-9.]+(ms)?/~~~
+
+    1..2
+ok 4 - reasonably indented things ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: failing indented things
+        # Subtest: first subset
+        not ok 1 - has no asserts, only throws
+          ---
+          {"actual":false,"at":{"column":7,"file":"test/test/mochalike.js","line":55},"expected":true,"generatedMessage":false,"message":"AssertionError: false is not true on line 50","name":"AssertionError","operator":"==","source":"ok(false, 'false is not true on line 50')\n"}
+          ...
+        Bail out! # has no asserts, only throws
+Bail out! # has no asserts, only throws
+
diff --git a/test/test/mochalike-ok-bail.tap b/test/test/mochalike-ok-bail.tap
new file mode 100644
index 0000000..69002eb
--- /dev/null
+++ b/test/test/mochalike-ok-bail.tap
@@ -0,0 +1,46 @@
+TAP version 13
+    # Subtest: a set of tests to be done later
+    ok 1 - should have a thingie # TODO
+    ok 2 - should have a second whoosits also # TODO
+        # Subtest: the subset
+        ok 1 - should be a child thingie # TODO
+        ok 2 - should also be a whoosits # TODO
+            # Subtest: has some of these things
+            1..0
+        ok 3 - has some of these things ___/# time=[0-9.]+(ms)?/~~~
+
+        1..3
+    ok 3 - the subset ___/# time=[0-9.]+(ms)?/~~~
+
+    1..3
+ok 1 - a set of tests to be done later ___/# time=[0-9.]+(ms)?/~~~
+
+ok 2 - describe todo # TODO
+    # Subtest: another set of tests
+    ok 1 - is a second set # TODO
+    ok 2 - looks like english # TODO
+    ok 3 - is marked TODO # TODO
+    1..3
+ok 3 - another set of tests ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: reasonably indented things
+        # Subtest: first subset
+        ok 1 - has no asserts, only fails to throw
+        ok 2 - is todo # TODO
+            # Subtest: is async
+            1..0
+        ok 3 - is async ___/# time=[0-9.]+(ms)?/~~~
+
+        1..3
+    ok 1 - first subset ___/# time=[0-9.]+(ms)?/~~~
+
+        # Subtest: second subset
+        1..0
+    ok 2 - second subset ___/# time=[0-9.]+(ms)?/~~~
+
+    1..2
+ok 4 - reasonably indented things ___/# time=[0-9.]+(ms)?/~~~
+
+1..4
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/mochalike-ok.js b/test/test/mochalike-ok.js
new file mode 100644
index 0000000..28fe185
--- /dev/null
+++ b/test/test/mochalike-ok.js
@@ -0,0 +1,49 @@
+if (typeof describe !== 'function') {
+  var t = require('../../lib/root.js')
+  t.mochaGlobals()
+}
+
+/* global describe, it */
+
+var ok = require('../fixtures/assert.js')
+
+describe('a set of tests to be done later', function () {
+  it('should have a thingie')
+  it('should have a second whoosits also')
+  describe('the subset', function () {
+    it('should be a child thingie')
+    it('should also be a whoosits')
+    it('has some of these things', function (done) {
+      ok(true, 'true is truthy')
+      ok(10, 'ten is also truthy')
+      setTimeout(function () {
+        done()
+      })
+    })
+  })
+})
+
+describe('describe todo')
+
+describe('another set of tests', function (done) {
+  it('is a second set')
+  it('looks like english')
+  it('is marked TODO')
+  done()
+})
+
+describe('reasonably indented things', function () {
+  describe('first subset', function () {
+    it('has no asserts, only fails to throw', function () {
+      // no throwing here
+    })
+    it('is todo')
+    it('is async', function (done) {
+      setTimeout(done)
+    })
+  })
+  describe('second subset', function () {
+    ok(true, 'true is truly truthy')
+    ok({}, 'objectify the truthiness')
+  })
+})
diff --git a/test/test/mochalike-ok.tap b/test/test/mochalike-ok.tap
new file mode 100644
index 0000000..69002eb
--- /dev/null
+++ b/test/test/mochalike-ok.tap
@@ -0,0 +1,46 @@
+TAP version 13
+    # Subtest: a set of tests to be done later
+    ok 1 - should have a thingie # TODO
+    ok 2 - should have a second whoosits also # TODO
+        # Subtest: the subset
+        ok 1 - should be a child thingie # TODO
+        ok 2 - should also be a whoosits # TODO
+            # Subtest: has some of these things
+            1..0
+        ok 3 - has some of these things ___/# time=[0-9.]+(ms)?/~~~
+
+        1..3
+    ok 3 - the subset ___/# time=[0-9.]+(ms)?/~~~
+
+    1..3
+ok 1 - a set of tests to be done later ___/# time=[0-9.]+(ms)?/~~~
+
+ok 2 - describe todo # TODO
+    # Subtest: another set of tests
+    ok 1 - is a second set # TODO
+    ok 2 - looks like english # TODO
+    ok 3 - is marked TODO # TODO
+    1..3
+ok 3 - another set of tests ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: reasonably indented things
+        # Subtest: first subset
+        ok 1 - has no asserts, only fails to throw
+        ok 2 - is todo # TODO
+            # Subtest: is async
+            1..0
+        ok 3 - is async ___/# time=[0-9.]+(ms)?/~~~
+
+        1..3
+    ok 1 - first subset ___/# time=[0-9.]+(ms)?/~~~
+
+        # Subtest: second subset
+        1..0
+    ok 2 - second subset ___/# time=[0-9.]+(ms)?/~~~
+
+    1..2
+ok 4 - reasonably indented things ___/# time=[0-9.]+(ms)?/~~~
+
+1..4
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/mochalike.js b/test/test/mochalike.js
new file mode 100644
index 0000000..31274d6
--- /dev/null
+++ b/test/test/mochalike.js
@@ -0,0 +1,71 @@
+if (typeof describe !== 'function') {
+  var t = require('../../lib/root.js')
+  t.mochaGlobals()
+}
+
+/* global describe, it */
+
+var ok = require('../fixtures/assert.js')
+
+describe('a set of tests to be done later', function () {
+  it('should have a thingie')
+  it('should have a second whoosits also')
+  describe('the subset', function () {
+    it('should be a child thingie')
+    it('should also be a whoosits')
+    it('has some of these things', function (done) {
+      ok(true, 'true is truthy')
+      ok(10, 'ten is also truthy')
+      setTimeout(function () {
+        done()
+      })
+    })
+  })
+})
+
+describe('describe todo')
+
+describe('another set of tests', function (done) {
+  it('is a second set')
+  it('looks like english')
+  it('is marked TODO')
+  done()
+})
+
+describe('reasonably indented things', function () {
+  describe('first subset', function () {
+    it('has no asserts, only fails to throw', function () {
+      // no throwing here
+    })
+    it('is todo')
+    it('is async', function (done) {
+      setTimeout(done)
+    })
+  })
+  describe('second subset', function () {
+    ok(true, 'true is truly truthy')
+    ok({}, 'objectify the truthiness')
+  })
+})
+
+describe('failing indented things', function () {
+  describe('first subset', function () {
+    'just some line breaks'
+    it('has no asserts, only throws', function () {
+      ok(false, 'false is not true on line 50')
+    })
+    it('does not throw')
+  })
+  describe('second subset', function () {
+    ok(true, 'true is truly truthy')
+    ok(!{}, 'objectify the truthiness')
+  })
+})
+
+describe('a test passing an error to done() callback', function () {
+  it('is marked as failed', function (done) {
+    process.nextTick(function () {
+      done(new Error('error arg'))
+    })
+  })
+})
diff --git a/test/test/mochalike.tap b/test/test/mochalike.tap
new file mode 100644
index 0000000..2c15607
--- /dev/null
+++ b/test/test/mochalike.tap
@@ -0,0 +1,102 @@
+TAP version 13
+    # Subtest: a set of tests to be done later
+    ok 1 - should have a thingie # TODO
+    ok 2 - should have a second whoosits also # TODO
+        # Subtest: the subset
+        ok 1 - should be a child thingie # TODO
+        ok 2 - should also be a whoosits # TODO
+            # Subtest: has some of these things
+            1..0
+        ok 3 - has some of these things ___/# time=[0-9.]+(ms)?/~~~
+
+        1..3
+    ok 3 - the subset ___/# time=[0-9.]+(ms)?/~~~
+
+    1..3
+ok 1 - a set of tests to be done later ___/# time=[0-9.]+(ms)?/~~~
+
+ok 2 - describe todo # TODO
+    # Subtest: another set of tests
+    ok 1 - is a second set # TODO
+    ok 2 - looks like english # TODO
+    ok 3 - is marked TODO # TODO
+    1..3
+ok 3 - another set of tests ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: reasonably indented things
+        # Subtest: first subset
+        ok 1 - has no asserts, only fails to throw
+        ok 2 - is todo # TODO
+            # Subtest: is async
+            1..0
+        ok 3 - is async ___/# time=[0-9.]+(ms)?/~~~
+
+        1..3
+    ok 1 - first subset ___/# time=[0-9.]+(ms)?/~~~
+
+        # Subtest: second subset
+        1..0
+    ok 2 - second subset ___/# time=[0-9.]+(ms)?/~~~
+
+    1..2
+ok 4 - reasonably indented things ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: failing indented things
+        # Subtest: first subset
+        not ok 1 - has no asserts, only throws
+          ---
+          {"actual":false,"at":{"column":7,"file":"test/test/mochalike.js","line":55},"expected":true,"generatedMessage":false,"message":"AssertionError: false is not true on line 50","name":"AssertionError","operator":"==","source":"ok(false, 'false is not true on line 50')\n"}
+          ...
+        ok 2 - does not throw # TODO
+        1..2
+        # failed 1 of 2 tests
+        # todo: 2
+    not ok 1 - first subset ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":3,"file":"test/test/mochalike.js","line":52},"results":{"count":2,"fail":1,"ok":false,"pass":1,"plan":{"end":2,"start":1},"todo":2},"source":"describe('first subset', function () {\n"}
+      ...
+
+        # Subtest: second subset
+        not ok 1 - AssertionError: objectify the truthiness
+          ---
+          {"actual":false,"at":{"column":5,"file":"test/test/mochalike.js","line":61},"expected":true,"generatedMessage":false,"message":"AssertionError: objectify the truthiness","name":"AssertionError","operator":"==","source":"ok(!{}, 'objectify the truthiness')\n","test":"second subset"}
+          ...
+        1..1
+        # failed 1 of 1 tests
+    not ok 2 - second subset ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":3,"file":"test/test/mochalike.js","line":59},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"describe('second subset', function () {\n"}
+      ...
+
+    1..2
+    # failed 2 of 2 tests
+not ok 5 - failing indented things ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":1,"file":"test/test/mochalike.js","line":51},"results":{"count":2,"fail":2,"ok":false,"pass":0,"plan":{"end":2,"start":1}},"source":"describe('failing indented things', function () {\n"}
+  ...
+
+    # Subtest: a test passing an error to done() callback
+        # Subtest: is marked as failed
+        not ok 1 - Error: error arg
+          ---
+          {"at":{"column":12,"file":"test/test/mochalike.js","line":68},"message":"Error: error arg","source":"done(new Error('error arg'))\n","test":"is marked as failed"}
+          ...
+        1..1
+        # failed 1 of 1 tests
+    not ok 1 - is marked as failed ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":3,"file":"test/test/mochalike.js","line":66},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"it('is marked as failed', function (done) {\n"}
+      ...
+
+    1..1
+    # failed 1 of 1 tests
+not ok 6 - a test passing an error to done() callback ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":1,"file":"test/test/mochalike.js","line":65},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"describe('a test passing an error to done() callback', function () {\n"}
+  ...
+
+1..6
+# failed 2 of 6 tests
+# todo: 2
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/nesting-bail.tap b/test/test/nesting-bail.tap
new file mode 100644
index 0000000..e9947a8
--- /dev/null
+++ b/test/test/nesting-bail.tap
@@ -0,0 +1,19 @@
+TAP version 13
+    # Subtest: nesting
+    1..2
+        # Subtest: first
+        1..2
+        ok 1 - true is ok
+        ok 2 - doag is also okay
+    ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+        # Subtest: second
+        ok 1 - but that is ok
+        ok 2 - this passes
+        not ok 3 - nested failure
+          ---
+          {"at":{"column":10,"file":"test/test/nesting.js","function":"foo","line":14},"compare":"===","found":1,"source":"tt.equal(1, '1', 'nested failure')\n","wanted":"1"}
+          ...
+        Bail out! # nested failure
+Bail out! # nested failure
+
diff --git a/test/test/nesting.js b/test/test/nesting.js
new file mode 100644
index 0000000..61f8bff
--- /dev/null
+++ b/test/test/nesting.js
@@ -0,0 +1,44 @@
+var t = require('../../lib/test.js')()
+
+t.test('nesting', function (t) {
+  t.plan(2)
+  t.test('first', function (tt) {
+    tt.plan(2)
+    tt.ok(true, 'true is ok')
+    tt.assert('doeg', 'doag is also okay')
+  })
+  t.test('second', function (tt) {
+    function foo () {
+      tt.ok('no plan', 'but that is ok')
+      tt.pass('this passes')
+      tt.equal(1, '1', 'nested failure')
+      tt.end()
+    }
+    function bar () {
+      return foo()
+    }
+    function baz () {
+      return bar()
+    }
+    baz()
+  })
+})
+
+t.pass('this passes')
+t.fail('this fails')
+
+t.test('async kid', function (t) {
+  t.plan(2)
+  setTimeout(function () {
+    t.ok(false, 'first timeout', { foo: 'blz' })
+  }, 50)
+  setTimeout(function () {
+    t.pass('second timeout')
+  })
+})
+
+t.pass('pass after async kid')
+
+t.end()
+
+t.pipe(process.stdout)
diff --git a/test/test/nesting.tap b/test/test/nesting.tap
new file mode 100644
index 0000000..a74dbf8
--- /dev/null
+++ b/test/test/nesting.tap
@@ -0,0 +1,51 @@
+TAP version 13
+    # Subtest: nesting
+    1..2
+        # Subtest: first
+        1..2
+        ok 1 - true is ok
+        ok 2 - doag is also okay
+    ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+        # Subtest: second
+        ok 1 - but that is ok
+        ok 2 - this passes
+        not ok 3 - nested failure
+          ---
+          {"at":{"column":10,"file":"test/test/nesting.js","function":"foo","line":14},"compare":"===","found":1,"source":"tt.equal(1, '1', 'nested failure')\n","wanted":"1"}
+          ...
+        1..3
+        # failed 1 of 3 tests
+    not ok 2 - second ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":5,"file":"test/test/nesting.js","line":10},"results":{"count":3,"fail":1,"ok":false,"pass":2,"plan":{"end":3,"start":1}},"source":"t.test('second', function (tt) {\n"}
+      ...
+    # failed 1 of 2 tests
+not ok 1 - nesting ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/nesting.js","line":3},"results":{"count":2,"fail":1,"ok":false,"pass":1,"plan":{"end":2,"start":1}},"source":"t.test('nesting', function (t) {\n"}
+  ...
+
+ok 2 - this passes
+not ok 3 - this fails
+  ---
+  {"at":{"column":3,"file":"test/test/nesting.js","line":28},"source":"t.fail('this fails')\n"}
+  ...
+    # Subtest: async kid
+    1..2
+    ok 1 - second timeout
+    not ok 2 - first timeout
+      ---
+      {"at":{"column":7,"file":"test/test/nesting.js","line":33,"method":"_onTimeout"},"foo":"blz","source":"t.ok(false, 'first timeout', { foo: 'blz' })\n"}
+      ...
+    # failed 1 of 2 tests
+not ok 4 - async kid ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/nesting.js","line":30},"results":{"count":2,"fail":1,"ok":false,"pass":1,"plan":{"end":2,"start":1}},"source":"t.test('async kid', function (t) {\n"}
+  ...
+
+ok 5 - pass after async kid
+1..5
+# failed 3 of 5 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/non-tap-output-bail.tap b/test/test/non-tap-output-bail.tap
new file mode 100644
index 0000000..5562e7e
--- /dev/null
+++ b/test/test/non-tap-output-bail.tap
@@ -0,0 +1,14 @@
+everything is fine
+there are no errors
+this output is not haiku.
+
+is 8 ok?
+ok, 8 can stay.
+ok 100 might be confusing
+  but: nevertheless, here we are
+  this: is indented
+  and: it
+  might: ~
+  be: yaml?
+ok done now, exiting
+
diff --git a/test/test/non-tap-output.js b/test/test/non-tap-output.js
new file mode 100644
index 0000000..8137b89
--- /dev/null
+++ b/test/test/non-tap-output.js
@@ -0,0 +1,12 @@
+console.log('everything is fine\n' +
+  'there are no errors\n' +
+  'this output is not haiku.\n\n' +
+  'is 8 ok?\n' +
+  'ok, 8 can stay.\n' +
+  'ok 100 might be confusing\n' +
+  '  but: nevertheless, here we are\n' +
+  '  this: is indented\n' +
+  '  and: it\n' +
+  '  might: ~\n' +
+  '  be: yaml?\n' +
+  'ok done now, exiting')
diff --git a/test/test/non-tap-output.tap b/test/test/non-tap-output.tap
new file mode 100644
index 0000000..5562e7e
--- /dev/null
+++ b/test/test/non-tap-output.tap
@@ -0,0 +1,14 @@
+everything is fine
+there are no errors
+this output is not haiku.
+
+is 8 ok?
+ok, 8 can stay.
+ok 100 might be confusing
+  but: nevertheless, here we are
+  this: is indented
+  and: it
+  might: ~
+  be: yaml?
+ok done now, exiting
+
diff --git a/test/test/not-ok-bail.tap b/test/test/not-ok-bail.tap
new file mode 100644
index 0000000..d8fee22
--- /dev/null
+++ b/test/test/not-ok-bail.tap
@@ -0,0 +1,7 @@
+TAP version 13
+not ok 1 - this is not ok
+  ---
+  {"at":{"column":3,"file":"test/test/not-ok.js","line":3},"source":"t.fail('this is not ok')\n"}
+  ...
+Bail out! # this is not ok
+
diff --git a/test/test/not-ok-nested-bail.tap b/test/test/not-ok-nested-bail.tap
new file mode 100644
index 0000000..701ad1c
--- /dev/null
+++ b/test/test/not-ok-nested-bail.tap
@@ -0,0 +1,12 @@
+TAP version 13
+    # Subtest: gp
+    1..1
+        # Subtest: parent
+        1..1
+        not ok 1 - fail
+          ---
+          {"at":{"column":7,"file":"test/test/not-ok-nested.js","line":7},"source":"t.fail('fail')\n"}
+          ...
+        Bail out! # fail
+Bail out! # fail
+
diff --git a/test/test/not-ok-nested.js b/test/test/not-ok-nested.js
new file mode 100644
index 0000000..ab9e3d5
--- /dev/null
+++ b/test/test/not-ok-nested.js
@@ -0,0 +1,9 @@
+var t = require('../..')
+
+t.test('gp', function (t) {
+  t.plan(1)
+  t.test('parent', function (t) {
+    t.plan(1)
+    t.fail('fail')
+  })
+})
diff --git a/test/test/not-ok-nested.tap b/test/test/not-ok-nested.tap
new file mode 100644
index 0000000..fa3a135
--- /dev/null
+++ b/test/test/not-ok-nested.tap
@@ -0,0 +1,24 @@
+TAP version 13
+    # Subtest: gp
+    1..1
+        # Subtest: parent
+        1..1
+        not ok 1 - fail
+          ---
+          {"at":{"column":7,"file":"test/test/not-ok-nested.js","line":7},"source":"t.fail('fail')\n"}
+          ...
+        # failed 1 of 1 tests
+    not ok 1 - parent ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":5,"file":"test/test/not-ok-nested.js","line":5},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test('parent', function (t) {\n"}
+      ...
+    # failed 1 of 1 tests
+not ok 1 - gp ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/not-ok-nested.js","line":3},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test('gp', function (t) {\n"}
+  ...
+
+1..1
+# failed 1 of 1 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/not-ok.js b/test/test/not-ok.js
new file mode 100644
index 0000000..e63856b
--- /dev/null
+++ b/test/test/not-ok.js
@@ -0,0 +1,3 @@
+var t = require('../..')
+
+t.fail('this is not ok')
diff --git a/test/test/not-ok.tap b/test/test/not-ok.tap
new file mode 100644
index 0000000..97d416f
--- /dev/null
+++ b/test/test/not-ok.tap
@@ -0,0 +1,9 @@
+TAP version 13
+not ok 1 - this is not ok
+  ---
+  {"at":{"column":3,"file":"test/test/not-ok.js","line":3},"source":"t.fail('this is not ok')\n"}
+  ...
+1..1
+# failed 1 of 1 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/ok-bail.tap b/test/test/ok-bail.tap
new file mode 100644
index 0000000..e78dfaf
--- /dev/null
+++ b/test/test/ok-bail.tap
@@ -0,0 +1,29 @@
+TAP version 13
+    # Subtest: nesting
+    1..2
+        # Subtest: first
+        1..2
+        ok 1 - true is ok
+        ok 2 - doag is also okay
+    ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+        # Subtest: second
+        ok 1 - but that is ok
+        ok 2 - this passes
+        ok 3 - nested ok
+        1..3
+    ok 2 - second ___/# time=[0-9.]+(ms)?/~~~
+ok 1 - nesting ___/# time=[0-9.]+(ms)?/~~~
+
+ok 2 - this passes
+ok 3 - this passes too
+    # Subtest: async kid
+    1..2
+    ok 1 - timeout
+    ok 2 - timeout
+ok 4 - async kid ___/# time=[0-9.]+(ms)?/~~~
+
+ok 5 - pass after async kid
+1..5
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/ok-exit-fail-bail.tap b/test/test/ok-exit-fail-bail.tap
new file mode 100644
index 0000000..edf0509
--- /dev/null
+++ b/test/test/ok-exit-fail-bail.tap
@@ -0,0 +1,5 @@
+TAP version 13
+ok 1 - this passes but the exit code makes it not ok
+1..1
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/ok-exit-fail.js b/test/test/ok-exit-fail.js
new file mode 100644
index 0000000..c448c8c
--- /dev/null
+++ b/test/test/ok-exit-fail.js
@@ -0,0 +1,3 @@
+var t = require('../..')
+t.pass('this passes but the exit code makes it not ok')
+process.exit(1)
diff --git a/test/test/ok-exit-fail.tap b/test/test/ok-exit-fail.tap
new file mode 100644
index 0000000..edf0509
--- /dev/null
+++ b/test/test/ok-exit-fail.tap
@@ -0,0 +1,5 @@
+TAP version 13
+ok 1 - this passes but the exit code makes it not ok
+1..1
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/ok.js b/test/test/ok.js
new file mode 100644
index 0000000..3c218c3
--- /dev/null
+++ b/test/test/ok.js
@@ -0,0 +1,50 @@
+var t = require('../..')
+var test = t.test
+
+test(function nesting (t) {
+  var plan = t.plan
+  var test = t.test
+  plan(2)
+  test('first', function (tt) {
+    var plan = tt.plan
+    var ok = tt.ok
+    var assert = tt.assert
+    plan(2)
+    ok(true, 'true is ok')
+    assert('doeg', 'doag is also okay')
+  })
+  test('second', function (tt) {
+    var pass = tt.pass
+    var ok = tt.ok
+    var equal = tt.equal
+    var done = tt.done
+    function foo () {
+      ok('no plan', 'but that is ok')
+      pass('this passes')
+      equal(1, 1, 'nested ok')
+      done()
+    }
+    function bar () {
+      return foo()
+    }
+    function baz () {
+      return bar()
+    }
+    baz()
+  })
+})
+
+t.pass('this passes')
+t.pass('this passes too')
+
+t.test('async kid', function (t) {
+  t.plan(2)
+  setTimeout(function () {
+    t.ok(true, 'timeout', { foo: 'blz' })
+  })
+  setTimeout(function () {
+    t.pass('timeout')
+  })
+})
+
+t.pass('pass after async kid')
diff --git a/test/test/ok.tap b/test/test/ok.tap
new file mode 100644
index 0000000..e78dfaf
--- /dev/null
+++ b/test/test/ok.tap
@@ -0,0 +1,29 @@
+TAP version 13
+    # Subtest: nesting
+    1..2
+        # Subtest: first
+        1..2
+        ok 1 - true is ok
+        ok 2 - doag is also okay
+    ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+        # Subtest: second
+        ok 1 - but that is ok
+        ok 2 - this passes
+        ok 3 - nested ok
+        1..3
+    ok 2 - second ___/# time=[0-9.]+(ms)?/~~~
+ok 1 - nesting ___/# time=[0-9.]+(ms)?/~~~
+
+ok 2 - this passes
+ok 3 - this passes too
+    # Subtest: async kid
+    1..2
+    ok 1 - timeout
+    ok 2 - timeout
+ok 4 - async kid ___/# time=[0-9.]+(ms)?/~~~
+
+ok 5 - pass after async kid
+1..5
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/pending-handles-bail.tap b/test/test/pending-handles-bail.tap
new file mode 100644
index 0000000..540652c
--- /dev/null
+++ b/test/test/pending-handles-bail.tap
@@ -0,0 +1,10 @@
+TAP version 13
+    # Subtest: ___/.*/~~~pending-handles.js child
+    ok 1 - this is ok
+    not ok 2 - received SIGTERM with pending event queue activity
+      ---
+      {"handles":[{"msecs":100000,"type":"Timer"}],"requests":[]}
+      ...
+    Bail out! # received SIGTERM with pending event queue activity
+Bail out! # received SIGTERM with pending event queue activity
+
diff --git a/test/test/pending-handles.js b/test/test/pending-handles.js
new file mode 100644
index 0000000..b5f8e6f
--- /dev/null
+++ b/test/test/pending-handles.js
@@ -0,0 +1,10 @@
+var t = require('../..')
+
+if (process.argv[2] === 'child') {
+  t.pass('this is ok')
+  setInterval(function () {}, 100000)
+} else {
+  t.spawn(process.execPath, [__filename, 'child'], {}, '', {
+    timeout: 900
+  })
+}
diff --git a/test/test/pending-handles.tap b/test/test/pending-handles.tap
new file mode 100644
index 0000000..6e3f102
--- /dev/null
+++ b/test/test/pending-handles.tap
@@ -0,0 +1,19 @@
+TAP version 13
+    # Subtest: ___/.*/~~~pending-handles.js child
+    ok 1 - this is ok
+    not ok 2 - received SIGTERM with pending event queue activity
+      ---
+      {"handles":[{"msecs":100000,"type":"Timer"}],"requests":[]}
+      ...
+    1..2
+    # failed 1 of 2 tests
+    ___/# time=[0-9.]+(ms)?/~~~
+not ok 1 - ___/.*/~~~pending-handles.js child ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"arguments":["___/.*/~~~pending-handles.js","child"],"at":{"column":5,"file":"test/test/pending-handles.js","line":7},"command":"___/.*(node|iojs)/~~~","failure":"timeout","results":{"count":2,"fail":1,"ok":false,"pass":1,"plan":{"end":2,"start":1}},"signal":"SIGTERM","source":"t.spawn(process.execPath, [__filename, 'child'], {}, '', {\n","timeout":900}
+  ...
+
+1..1
+# failed 1 of 1 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/plan-failures-bail.tap b/test/test/plan-failures-bail.tap
new file mode 100644
index 0000000..4cef963
--- /dev/null
+++ b/test/test/plan-failures-bail.tap
@@ -0,0 +1,12 @@
+TAP version 13
+    # Subtest: pass then pass plan()
+    1..1
+    ok 1 - ok
+ok 1 - pass then pass plan() ___/# time=[0-9.]+(ms)?/~~~
+
+not ok 2 - Error: test count exceeds plan
+  ---
+  {"at":{"column":12,"file":"test/test/plan-failures.js","function":"test","line":11},"count":2,"message":"Error: test count exceeds plan","plan":1,"source":"t[second]('extra')\n","test":"pass then pass plan()"}
+  ...
+Bail out! # Error: test count exceeds plan
+
diff --git a/test/test/plan-failures.js b/test/test/plan-failures.js
new file mode 100644
index 0000000..0cb4a14
--- /dev/null
+++ b/test/test/plan-failures.js
@@ -0,0 +1,25 @@
+var t = require('../..')
+
+function test (t, first, second, plan) {
+  if (plan) {
+    t.plan(1)
+  }
+  t[first]('ok')
+  if (!plan) {
+    t.end()
+  }
+  t[second]('extra')
+}
+
+var methods = [ 'pass', 'fail' ]
+
+methods.forEach(function (first) {
+  methods.forEach(function (second) {
+    t.test(first + ' then ' + second + ' plan()', function (t) {
+      test(t, first, second, true)
+    })
+    t.test(first + ' then ' + second + ' end()', function (t) {
+      test(t, first, second, false)
+    })
+  })
+})
diff --git a/test/test/plan-failures.tap b/test/test/plan-failures.tap
new file mode 100644
index 0000000..804e419
--- /dev/null
+++ b/test/test/plan-failures.tap
@@ -0,0 +1,89 @@
+TAP version 13
+    # Subtest: pass then pass plan()
+    1..1
+    ok 1 - ok
+ok 1 - pass then pass plan() ___/# time=[0-9.]+(ms)?/~~~
+
+not ok 2 - Error: test count exceeds plan
+  ---
+  {"at":{"column":12,"file":"test/test/plan-failures.js","function":"test","line":11},"count":2,"message":"Error: test count exceeds plan","plan":1,"source":"t[second]('extra')\n","test":"pass then pass plan()"}
+  ...
+    # Subtest: pass then pass end()
+    ok 1 - ok
+    1..1
+ok 3 - pass then pass end() ___/# time=[0-9.]+(ms)?/~~~
+
+not ok 4 - Error: test count exceeds plan
+  ---
+  {"at":{"column":12,"file":"test/test/plan-failures.js","function":"test","line":11},"count":2,"message":"Error: test count exceeds plan","plan":1,"source":"t[second]('extra')\n","test":"pass then pass end()"}
+  ...
+    # Subtest: pass then fail plan()
+    1..1
+    ok 1 - ok
+ok 5 - pass then fail plan() ___/# time=[0-9.]+(ms)?/~~~
+
+not ok 6 - Error: test count exceeds plan
+  ---
+  {"at":{"column":12,"file":"test/test/plan-failures.js","function":"test","line":11},"count":2,"message":"Error: test count exceeds plan","plan":1,"source":"t[second]('extra')\n","test":"pass then fail plan()"}
+  ...
+    # Subtest: pass then fail end()
+    ok 1 - ok
+    1..1
+ok 7 - pass then fail end() ___/# time=[0-9.]+(ms)?/~~~
+
+not ok 8 - Error: test count exceeds plan
+  ---
+  {"at":{"column":12,"file":"test/test/plan-failures.js","function":"test","line":11},"count":2,"message":"Error: test count exceeds plan","plan":1,"source":"t[second]('extra')\n","test":"pass then fail end()"}
+  ...
+    # Subtest: fail then pass plan()
+    1..1
+    not ok 1 - ok
+      ---
+      {"at":{"column":11,"file":"test/test/plan-failures.js","function":"test","line":7},"source":"t[first]('ok')\n"}
+      ...
+    # failed 1 of 1 tests
+not ok 9 - fail then pass plan() ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":7,"file":"test/test/plan-failures.js","line":18},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test(first + ' then ' + second + ' plan()', function (t) {\n"}
+  ...
+
+    # Subtest: fail then pass end()
+    not ok 1 - ok
+      ---
+      {"at":{"column":11,"file":"test/test/plan-failures.js","function":"test","line":7},"source":"t[first]('ok')\n"}
+      ...
+    1..1
+    # failed 1 of 1 tests
+not ok 10 - fail then pass end() ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":7,"file":"test/test/plan-failures.js","line":21},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test(first + ' then ' + second + ' end()', function (t) {\n"}
+  ...
+
+    # Subtest: fail then fail plan()
+    1..1
+    not ok 1 - ok
+      ---
+      {"at":{"column":11,"file":"test/test/plan-failures.js","function":"test","line":7},"source":"t[first]('ok')\n"}
+      ...
+    # failed 1 of 1 tests
+not ok 11 - fail then fail plan() ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":7,"file":"test/test/plan-failures.js","line":18},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test(first + ' then ' + second + ' plan()', function (t) {\n"}
+  ...
+
+    # Subtest: fail then fail end()
+    not ok 1 - ok
+      ---
+      {"at":{"column":11,"file":"test/test/plan-failures.js","function":"test","line":7},"source":"t[first]('ok')\n"}
+      ...
+    1..1
+    # failed 1 of 1 tests
+not ok 12 - fail then fail end() ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":7,"file":"test/test/plan-failures.js","line":21},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test(first + ' then ' + second + ' end()', function (t) {\n"}
+  ...
+
+1..12
+# failed 8 of 12 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/plan-too-many-bail.tap b/test/test/plan-too-many-bail.tap
new file mode 100644
index 0000000..72d89ac
--- /dev/null
+++ b/test/test/plan-too-many-bail.tap
@@ -0,0 +1,15 @@
+TAP version 13
+    # Subtest: children plan too big
+    1..9
+    ok 1 - this is ok
+    ok 2 - i am ok with how this is proceeding
+        # Subtest: grandchild
+        1..8
+        ok 1 - i am planning big things
+        not ok 2 - test unfinished: grandchild
+          ---
+          {"at":{"column":5,"file":"test/test/plan-too-many.js","line":7},"count":1,"plan":8,"source":"t.test('grandchild', function (tt) {\n"}
+          ...
+        Bail out! # test unfinished: grandchild
+Bail out! # test unfinished: grandchild
+
diff --git a/test/test/plan-too-many.js b/test/test/plan-too-many.js
new file mode 100644
index 0000000..abef07e
--- /dev/null
+++ b/test/test/plan-too-many.js
@@ -0,0 +1,11 @@
+var t = require('../../lib/root.js')
+
+t.test('children plan too big', function (t) {
+  t.plan(9)
+  t.pass('this is ok')
+  t.pass('i am ok with how this is proceeding')
+  t.test('grandchild', function (tt) {
+    tt.plan(8)
+    tt.pass('i am planning big things')
+  })
+})
diff --git a/test/test/plan-too-many.tap b/test/test/plan-too-many.tap
new file mode 100644
index 0000000..bc06283
--- /dev/null
+++ b/test/test/plan-too-many.tap
@@ -0,0 +1,40 @@
+TAP version 13
+    # Subtest: children plan too big
+    1..9
+    ok 1 - this is ok
+    ok 2 - i am ok with how this is proceeding
+        # Subtest: grandchild
+        1..8
+        ok 1 - i am planning big things
+        not ok 2 - test unfinished: grandchild
+          ---
+          {"at":{"column":5,"file":"test/test/plan-too-many.js","line":7},"count":1,"plan":8,"source":"t.test('grandchild', function (tt) {\n"}
+          ...
+        not ok 3 - missing test
+        not ok 4 - missing test
+        not ok 5 - missing test
+        not ok 6 - missing test
+        not ok 7 - missing test
+        not ok 8 - missing test
+        # failed 7 of 8 tests
+    not ok 3 - grandchild ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":5,"file":"test/test/plan-too-many.js","line":7},"results":{"count":8,"fail":7,"ok":false,"pass":1,"plan":{"end":8,"start":1}},"source":"t.test('grandchild', function (tt) {\n"}
+      ...
+
+    not ok 4 - missing test
+    not ok 5 - missing test
+    not ok 6 - missing test
+    not ok 7 - missing test
+    not ok 8 - missing test
+    not ok 9 - missing test
+    # failed 7 of 9 tests
+not ok 1 - children plan too big ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/plan-too-many.js","line":3},"results":{"count":9,"fail":7,"ok":false,"pass":2,"plan":{"end":9,"start":1}},"source":"t.test('children plan too big', function (t) {\n"}
+  ...
+
+1..1
+# failed 1 of 1 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/pragma-bail.tap b/test/test/pragma-bail.tap
new file mode 100644
index 0000000..b6b751a
--- /dev/null
+++ b/test/test/pragma-bail.tap
@@ -0,0 +1,17 @@
+TAP version 13
+    # Subtest: ___/.*/~~~pragma.js child
+    this is not tap but it is ok
+    ok 1 - an ok test
+    pragma +strict
+    this is not tap and it is not ok
+    pragma -strict
+    more ok non-tap
+    ok 2 - ending now
+    1..2
+    ___/# time=[0-9.]+(ms)?/~~~
+not ok 1 - ___/.*/~~~pragma.js child ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"arguments":["___/.*/~~~pragma.js","child"],"at":{"column":5,"file":"test/test/pragma.js","line":4},"command":"___/.*(node|iojs)/~~~","results":{"count":2,"failures":[{"data":"this is not tap and it is not ok\n","tapError":"Non-TAP data encountered in strict mode"}],"ok":false,"pass":2,"plan":{"end":2,"start":1}},"source":"t.spawn(process.execPath, [__filename, 'child'])\n"}
+  ...
+Bail out! # ___/.*/~~~pragma.js child ___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/pragma.js b/test/test/pragma.js
new file mode 100644
index 0000000..9b66d4c
--- /dev/null
+++ b/test/test/pragma.js
@@ -0,0 +1,14 @@
+var t = require('../..')
+
+if (process.argv[2] !== 'child') {
+  t.spawn(process.execPath, [__filename, 'child'])
+} else {
+  console.log('this is not tap but it is ok')
+  t.pass('an ok test')
+  t.pragma({ strict: true })
+  console.log('this is not tap and it is not ok')
+  t.pragma({ strict: false })
+  console.log('more ok non-tap')
+  t.pass('ending now')
+  t.end()
+}
diff --git a/test/test/pragma.tap b/test/test/pragma.tap
new file mode 100644
index 0000000..600924f
--- /dev/null
+++ b/test/test/pragma.tap
@@ -0,0 +1,20 @@
+TAP version 13
+    # Subtest: ___/.*/~~~pragma.js child
+    this is not tap but it is ok
+    ok 1 - an ok test
+    pragma +strict
+    this is not tap and it is not ok
+    pragma -strict
+    more ok non-tap
+    ok 2 - ending now
+    1..2
+    ___/# time=[0-9.]+(ms)?/~~~
+not ok 1 - ___/.*/~~~pragma.js child ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"arguments":["___/.*/~~~pragma.js","child"],"at":{"column":5,"file":"test/test/pragma.js","line":4},"command":"___/.*(node|iojs)/~~~","results":{"count":2,"failures":[{"data":"this is not tap and it is not ok\n","tapError":"Non-TAP data encountered in strict mode"}],"ok":false,"pass":2,"plan":{"end":2,"start":1}},"source":"t.spawn(process.execPath, [__filename, 'child'])\n"}
+  ...
+
+1..1
+# failed 1 of 1 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/promise-return-bail.tap b/test/test/promise-return-bail.tap
new file mode 100644
index 0000000..60c26ad
--- /dev/null
+++ b/test/test/promise-return-bail.tap
@@ -0,0 +1,14 @@
+TAP version 13
+    # Subtest: auto-end on resolve
+    ok 1 - true is ok
+    1..1
+ok 1 - auto-end on resolve ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: rejected
+    not ok 1 - expected error
+      ---
+      {"at":{"column":34,"file":"test/test/promise-return.js","line":22},"message":"Error: expected error","source":"setTimeout(reject.bind(null, new Error('expected error'), 150))\n"}
+      ...
+    Bail out! # expected error
+Bail out! # expected error
+
diff --git a/test/test/promise-return-mocha-bail.tap b/test/test/promise-return-mocha-bail.tap
new file mode 100644
index 0000000..cf7640e
--- /dev/null
+++ b/test/test/promise-return-mocha-bail.tap
@@ -0,0 +1,17 @@
+TAP version 13
+    # Subtest: auto-end on resolve
+    1..0
+ok 1 - auto-end on resolve ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: auto-end on resolve without cb
+    1..0
+ok 2 - auto-end on resolve without cb ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: rejected
+    not ok 1 - expected error
+      ---
+      {"at":{"column":34,"file":"test/test/promise-return-mocha.js","line":31},"message":"Error: expected error","source":"setTimeout(reject.bind(null, new Error('expected error'), 150))\n"}
+      ...
+    Bail out! # expected error
+Bail out! # expected error
+
diff --git a/test/test/promise-return-mocha.js b/test/test/promise-return-mocha.js
new file mode 100644
index 0000000..6a99c99
--- /dev/null
+++ b/test/test/promise-return-mocha.js
@@ -0,0 +1,39 @@
+/* global describe */
+var P
+if (typeof Promise === 'undefined') {
+  // Making sure this is testable in node 0.8/0.10
+  P = require('bluebird')
+} else {
+  P = Promise
+}
+
+var t = require('../..')
+t.mochaGlobals()
+
+describe('auto-end on resolve', function (done) {
+  return new P(function (resolve) {
+    setTimeout(function () {
+      resolve()
+    }, 150)
+  })
+})
+
+describe('auto-end on resolve without cb', function () {
+  return new P(function (resolve) {
+    setTimeout(function () {
+      resolve()
+    }, 150)
+  })
+})
+
+describe('rejected', function (done) {
+  return new P(function (resolve, reject) {
+    setTimeout(reject.bind(null, new Error('expected error'), 150))
+  })
+})
+
+describe('rejected without cb', function () {
+  return new P(function (resolve, reject) {
+    setTimeout(reject.bind(null, new Error('expected error'), 150))
+  })
+})
diff --git a/test/test/promise-return-mocha.tap b/test/test/promise-return-mocha.tap
new file mode 100644
index 0000000..d1eab96
--- /dev/null
+++ b/test/test/promise-return-mocha.tap
@@ -0,0 +1,37 @@
+TAP version 13
+    # Subtest: auto-end on resolve
+    1..0
+ok 1 - auto-end on resolve ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: auto-end on resolve without cb
+    1..0
+ok 2 - auto-end on resolve without cb ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: rejected
+    not ok 1 - expected error
+      ---
+      {"at":{"column":34,"file":"test/test/promise-return-mocha.js","line":31},"message":"Error: expected error","source":"setTimeout(reject.bind(null, new Error('expected error'), 150))\n"}
+      ...
+    1..1
+    # failed 1 of 1 tests
+not ok 3 - rejected ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":1,"file":"test/test/promise-return-mocha.js","line":29},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"describe('rejected', function (done) {\n"}
+  ...
+
+    # Subtest: rejected without cb
+    not ok 1 - expected error
+      ---
+      {"at":{"column":34,"file":"test/test/promise-return-mocha.js","line":37},"message":"Error: expected error","source":"setTimeout(reject.bind(null, new Error('expected error'), 150))\n"}
+      ...
+    1..1
+    # failed 1 of 1 tests
+not ok 4 - rejected without cb ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":1,"file":"test/test/promise-return-mocha.js","line":35},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"describe('rejected without cb', function () {\n"}
+  ...
+
+1..4
+# failed 2 of 4 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/promise-return.js b/test/test/promise-return.js
new file mode 100644
index 0000000..2aba487
--- /dev/null
+++ b/test/test/promise-return.js
@@ -0,0 +1,24 @@
+var P
+if (typeof Promise === 'undefined') {
+  // Making sure this is testable in node 0.8/0.10
+  P = require('bluebird')
+} else {
+  P = Promise
+}
+
+var t = require('../..')
+
+t.test('auto-end on resolve', function (t) {
+  return new P(function (resolve) {
+    setTimeout(function () {
+      t.ok(true, 'true is ok')
+      resolve()
+    }, 150)
+  })
+})
+
+t.test('rejected', function (t) {
+  return new P(function (resolve, reject) {
+    setTimeout(reject.bind(null, new Error('expected error'), 150))
+  })
+})
diff --git a/test/test/promise-return.tap b/test/test/promise-return.tap
new file mode 100644
index 0000000..8ef4786
--- /dev/null
+++ b/test/test/promise-return.tap
@@ -0,0 +1,22 @@
+TAP version 13
+    # Subtest: auto-end on resolve
+    ok 1 - true is ok
+    1..1
+ok 1 - auto-end on resolve ___/# time=[0-9.]+(ms)?/~~~
+
+    # Subtest: rejected
+    not ok 1 - expected error
+      ---
+      {"at":{"column":34,"file":"test/test/promise-return.js","line":22},"message":"Error: expected error","source":"setTimeout(reject.bind(null, new Error('expected error'), 150))\n"}
+      ...
+    1..1
+    # failed 1 of 1 tests
+not ok 2 - rejected ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/promise-return.js","line":20},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test('rejected', function (t) {\n"}
+  ...
+
+1..2
+# failed 1 of 2 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/root-teardown-bail.tap b/test/test/root-teardown-bail.tap
new file mode 100644
index 0000000..a45e0ef
--- /dev/null
+++ b/test/test/root-teardown-bail.tap
@@ -0,0 +1,15 @@
+TAP version 13
+    # Subtest: child test
+    ok 1 - this is ok
+    1..1
+ok 1 - child test ___/# time=[0-9.]+(ms)?/~~~
+
+ok 2 - one
+ok 3 - two
+ok 4 - three
+ok 5 - four
+ok 6 - five
+ok 7 - six
+1..7
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/root-teardown.js b/test/test/root-teardown.js
new file mode 100644
index 0000000..adb2a49
--- /dev/null
+++ b/test/test/root-teardown.js
@@ -0,0 +1,33 @@
+var tap = require('../..')
+
+var timer = setTimeout(function () {
+  throw new Error('timer should never ding')
+}, 1000)
+
+tap.tearDown(function () {
+  clearTimeout(timer)
+})
+
+tap.test('child test', function (t) {
+  t.pass('this is ok')
+  setTimeout(function () {
+    t.end()
+  }, 100)
+})
+
+tap.pass('one')
+setTimeout(function () {
+  tap.pass('two')
+  setTimeout(function () {
+    tap.pass('three')
+    setTimeout(function () {
+      tap.pass('four')
+      setTimeout(function () {
+        tap.pass('five')
+        setTimeout(function () {
+          tap.pass('six')
+        })
+      })
+    })
+  })
+})
diff --git a/test/test/root-teardown.tap b/test/test/root-teardown.tap
new file mode 100644
index 0000000..a45e0ef
--- /dev/null
+++ b/test/test/root-teardown.tap
@@ -0,0 +1,15 @@
+TAP version 13
+    # Subtest: child test
+    ok 1 - this is ok
+    1..1
+ok 1 - child test ___/# time=[0-9.]+(ms)?/~~~
+
+ok 2 - one
+ok 3 - two
+ok 4 - three
+ok 5 - four
+ok 6 - five
+ok 7 - six
+1..7
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/skip-all-bail.tap b/test/test/skip-all-bail.tap
new file mode 100644
index 0000000..f8178dc
--- /dev/null
+++ b/test/test/skip-all-bail.tap
@@ -0,0 +1,4 @@
+TAP version 13
+1..0 # Skip all these tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/skip-all.js b/test/test/skip-all.js
new file mode 100644
index 0000000..f666f82
--- /dev/null
+++ b/test/test/skip-all.js
@@ -0,0 +1 @@
+require('../..').plan(0, 'Skip all these tests')
diff --git a/test/test/skip-all.tap b/test/test/skip-all.tap
new file mode 100644
index 0000000..f8178dc
--- /dev/null
+++ b/test/test/skip-all.tap
@@ -0,0 +1,4 @@
+TAP version 13
+1..0 # Skip all these tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/skip-bail.tap b/test/test/skip-bail.tap
new file mode 100644
index 0000000..f951d08
--- /dev/null
+++ b/test/test/skip-bail.tap
@@ -0,0 +1,5 @@
+TAP version 13
+ok 1 - does not count as failure # SKIP
+1..1
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/skip.js b/test/test/skip.js
new file mode 100644
index 0000000..f3c694a
--- /dev/null
+++ b/test/test/skip.js
@@ -0,0 +1,5 @@
+var tap = require('../..')
+
+tap.test('does not count as failure', { skip: true }, function (t) {
+  t.end()
+})
diff --git a/test/test/skip.tap b/test/test/skip.tap
new file mode 100644
index 0000000..f951d08
--- /dev/null
+++ b/test/test/skip.tap
@@ -0,0 +1,5 @@
+TAP version 13
+ok 1 - does not count as failure # SKIP
+1..1
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/sparse-array-bail.tap b/test/test/sparse-array-bail.tap
new file mode 100644
index 0000000..35bd711
--- /dev/null
+++ b/test/test/sparse-array-bail.tap
@@ -0,0 +1,7 @@
+TAP version 13
+not ok 1 - should match pattern provided
+  ---
+  {"at":{"column":3,"file":"test/test/sparse-array.js","line":3},"found":[1,null,3],"pattern":[1,2,3],"source":"t.similar([1,, 3], [1, 2, 3]) // eslint-disable-line\n"}
+  ...
+Bail out! # should match pattern provided
+
diff --git a/test/test/sparse-array.js b/test/test/sparse-array.js
new file mode 100644
index 0000000..f340e19
--- /dev/null
+++ b/test/test/sparse-array.js
@@ -0,0 +1,4 @@
+var t = require('../..')
+
+t.similar([1,, 3], [1, 2, 3]) // eslint-disable-line
+t.similar([1,,,], [1, 2, 3]) // eslint-disable-line
diff --git a/test/test/sparse-array.tap b/test/test/sparse-array.tap
new file mode 100644
index 0000000..885b289
--- /dev/null
+++ b/test/test/sparse-array.tap
@@ -0,0 +1,13 @@
+TAP version 13
+not ok 1 - should match pattern provided
+  ---
+  {"at":{"column":3,"file":"test/test/sparse-array.js","line":3},"found":[1,null,3],"pattern":[1,2,3],"source":"t.similar([1,, 3], [1, 2, 3]) // eslint-disable-line\n"}
+  ...
+not ok 2 - should match pattern provided
+  ---
+  {"at":{"column":3,"file":"test/test/sparse-array.js","line":4},"found":[1,null,null],"pattern":[1,2,3],"source":"t.similar([1,,,], [1, 2, 3]) // eslint-disable-line\n"}
+  ...
+1..2
+# failed 2 of 2 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/spawn-bail.tap b/test/test/spawn-bail.tap
new file mode 100644
index 0000000..dbe5f0a
--- /dev/null
+++ b/test/test/spawn-bail.tap
@@ -0,0 +1,20 @@
+TAP version 13
+    # Subtest: ___/.*/~~~spawn.js child
+        # Subtest: nesting
+        1..2
+            # Subtest: first
+            1..2
+            ok 1 - true is ok
+            ok 2 - doag is also okay
+        ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+            # Subtest: second
+            ok 1 - but that is ok
+            ok 2 - this passes
+            not ok 3 - nested failure
+              ---
+              {"at":{"column":10,"file":"test/test/spawn.js","function":"foo","line":22},"compare":"===","found":1,"source":"tt.equal(1, '1', 'nested failure')\n","wanted":"1"}
+              ...
+            Bail out! # nested failure
+Bail out! # nested failure
+
diff --git a/test/test/spawn-empty-bail.tap b/test/test/spawn-empty-bail.tap
new file mode 100644
index 0000000..2f29bea
--- /dev/null
+++ b/test/test/spawn-empty-bail.tap
@@ -0,0 +1,8 @@
+TAP version 13
+    # Subtest: ___/.*/~~~spawn-empty.js child
+    1..0
+ok 1 - ___/.*/~~~spawn-empty.js child # SKIP No tests found
+
+1..1
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/spawn-empty.js b/test/test/spawn-empty.js
new file mode 100644
index 0000000..26b63a2
--- /dev/null
+++ b/test/test/spawn-empty.js
@@ -0,0 +1,4 @@
+if (process.argv[2] !== 'child') {
+  var t = require('../..')
+  t.spawn(process.execPath, [__filename, 'child'])
+}
diff --git a/test/test/spawn-empty.tap b/test/test/spawn-empty.tap
new file mode 100644
index 0000000..2f29bea
--- /dev/null
+++ b/test/test/spawn-empty.tap
@@ -0,0 +1,8 @@
+TAP version 13
+    # Subtest: ___/.*/~~~spawn-empty.js child
+    1..0
+ok 1 - ___/.*/~~~spawn-empty.js child # SKIP No tests found
+
+1..1
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/spawn-stderr-bail.tap b/test/test/spawn-stderr-bail.tap
new file mode 100644
index 0000000..2bb908e
--- /dev/null
+++ b/test/test/spawn-stderr-bail.tap
@@ -0,0 +1,11 @@
+TAP version 13
+    # Subtest: ___/.*/~~~spawn-stderr.js child
+    stdout
+    ok 1 - this is ok
+    1..1
+    ___/# time=[0-9.]+(ms)?/~~~
+ok 1 - ___/.*/~~~spawn-stderr.js child ___/# time=[0-9.]+(ms)?/~~~
+
+1..1
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/spawn-stderr.js b/test/test/spawn-stderr.js
new file mode 100644
index 0000000..a02ca04
--- /dev/null
+++ b/test/test/spawn-stderr.js
@@ -0,0 +1,9 @@
+var tap = require('../..')
+
+if (!process.argv[2]) {
+  tap.spawn(process.execPath, [ __filename, 'child' ])
+} else {
+  console.error('stderr')
+  console.log('stdout')
+  tap.pass('this is ok')
+}
diff --git a/test/test/spawn-stderr.tap b/test/test/spawn-stderr.tap
new file mode 100644
index 0000000..2bb908e
--- /dev/null
+++ b/test/test/spawn-stderr.tap
@@ -0,0 +1,11 @@
+TAP version 13
+    # Subtest: ___/.*/~~~spawn-stderr.js child
+    stdout
+    ok 1 - this is ok
+    1..1
+    ___/# time=[0-9.]+(ms)?/~~~
+ok 1 - ___/.*/~~~spawn-stderr.js child ___/# time=[0-9.]+(ms)?/~~~
+
+1..1
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/spawn.js b/test/test/spawn.js
new file mode 100644
index 0000000..fe49a1e
--- /dev/null
+++ b/test/test/spawn.js
@@ -0,0 +1,48 @@
+var t = require('../../lib/test.js')()
+t.pipe(process.stdout)
+process.on('exit', function () {
+  t.end()
+})
+
+if (process.argv[2] !== 'child') {
+  t.spawn(process.execPath, [__filename, 'child'])
+}
+
+t.test('nesting', function (t) {
+  t.plan(2)
+  t.test('first', function (tt) {
+    tt.plan(2)
+    tt.ok(true, 'true is ok')
+    tt.assert('doeg', 'doag is also okay')
+  })
+  t.test('second', function (tt) {
+    function foo () {
+      tt.ok('no plan', 'but that is ok')
+      tt.pass('this passes')
+      tt.equal(1, '1', 'nested failure')
+      tt.end()
+    }
+    function bar () {
+      return foo()
+    }
+    function baz () {
+      return bar()
+    }
+    baz()
+  })
+})
+
+t.pass('this passes')
+t.fail('this fails')
+
+t.test('async kid', function (t) {
+  t.plan(2)
+  setTimeout(function () {
+    t.ok(false, 'first timeout', { foo: 'blz' })
+  }, 50)
+  setTimeout(function () {
+    t.pass('second timeout')
+  })
+})
+
+t.pass('pass after async kid')
diff --git a/test/test/spawn.tap b/test/test/spawn.tap
new file mode 100644
index 0000000..8d4b660
--- /dev/null
+++ b/test/test/spawn.tap
@@ -0,0 +1,106 @@
+TAP version 13
+    # Subtest: ___/.*/~~~spawn.js child
+        # Subtest: nesting
+        1..2
+            # Subtest: first
+            1..2
+            ok 1 - true is ok
+            ok 2 - doag is also okay
+        ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+            # Subtest: second
+            ok 1 - but that is ok
+            ok 2 - this passes
+            not ok 3 - nested failure
+              ---
+              {"at":{"column":10,"file":"test/test/spawn.js","function":"foo","line":22},"compare":"===","found":1,"source":"tt.equal(1, '1', 'nested failure')\n","wanted":"1"}
+              ...
+            1..3
+            # failed 1 of 3 tests
+        not ok 2 - second ___/# time=[0-9.]+(ms)?/~~~
+          ---
+          {"at":{"column":5,"file":"test/test/spawn.js","line":18},"results":{"count":3,"fail":1,"ok":false,"pass":2,"plan":{"end":3,"start":1}},"source":"t.test('second', function (tt) {\n"}
+          ...
+        # failed 1 of 2 tests
+    not ok 1 - nesting ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":3,"file":"test/test/spawn.js","line":11},"results":{"count":2,"fail":1,"ok":false,"pass":1,"plan":{"end":2,"start":1}},"source":"t.test('nesting', function (t) {\n"}
+      ...
+
+    ok 2 - this passes
+    not ok 3 - this fails
+      ---
+      {"at":{"column":3,"file":"test/test/spawn.js","line":36},"source":"t.fail('this fails')\n"}
+      ...
+        # Subtest: async kid
+        1..2
+        ok 1 - second timeout
+        not ok 2 - first timeout
+          ---
+          {"at":{"column":7,"file":"test/test/spawn.js","line":41,"method":"_onTimeout"},"foo":"blz","source":"t.ok(false, 'first timeout', { foo: 'blz' })\n"}
+          ...
+        # failed 1 of 2 tests
+    not ok 4 - async kid ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":3,"file":"test/test/spawn.js","line":38},"results":{"count":2,"fail":1,"ok":false,"pass":1,"plan":{"end":2,"start":1}},"source":"t.test('async kid', function (t) {\n"}
+      ...
+
+    ok 5 - pass after async kid
+    1..5
+    # failed 3 of 5 tests
+    ___/# time=[0-9.]+(ms)?/~~~
+not ok 1 - ___/.*/~~~spawn.js child ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"arguments":["___/.*/~~~spawn.js","child"],"at":{"column":5,"file":"test/test/spawn.js","line":8},"command":"___/.*(node|iojs)/~~~","results":{"count":5,"fail":3,"ok":false,"pass":2,"plan":{"end":5,"start":1}},"source":"t.spawn(process.execPath, [__filename, 'child'])\n"}
+  ...
+
+    # Subtest: nesting
+    1..2
+        # Subtest: first
+        1..2
+        ok 1 - true is ok
+        ok 2 - doag is also okay
+    ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+        # Subtest: second
+        ok 1 - but that is ok
+        ok 2 - this passes
+        not ok 3 - nested failure
+          ---
+          {"at":{"column":10,"file":"test/test/spawn.js","function":"foo","line":22},"compare":"===","found":1,"source":"tt.equal(1, '1', 'nested failure')\n","wanted":"1"}
+          ...
+        1..3
+        # failed 1 of 3 tests
+    not ok 2 - second ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":5,"file":"test/test/spawn.js","line":18},"results":{"count":3,"fail":1,"ok":false,"pass":2,"plan":{"end":3,"start":1}},"source":"t.test('second', function (tt) {\n"}
+      ...
+    # failed 1 of 2 tests
+not ok 2 - nesting ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/spawn.js","line":11},"results":{"count":2,"fail":1,"ok":false,"pass":1,"plan":{"end":2,"start":1}},"source":"t.test('nesting', function (t) {\n"}
+  ...
+
+ok 3 - this passes
+not ok 4 - this fails
+  ---
+  {"at":{"column":3,"file":"test/test/spawn.js","line":36},"source":"t.fail('this fails')\n"}
+  ...
+    # Subtest: async kid
+    1..2
+    ok 1 - second timeout
+    not ok 2 - first timeout
+      ---
+      {"at":{"column":7,"file":"test/test/spawn.js","line":41,"method":"_onTimeout"},"foo":"blz","source":"t.ok(false, 'first timeout', { foo: 'blz' })\n"}
+      ...
+    # failed 1 of 2 tests
+not ok 5 - async kid ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/spawn.js","line":38},"results":{"count":2,"fail":1,"ok":false,"pass":1,"plan":{"end":2,"start":1}},"source":"t.test('async kid', function (t) {\n"}
+  ...
+
+ok 6 - pass after async kid
+1..6
+# failed 4 of 6 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/throw-bail.tap b/test/test/throw-bail.tap
new file mode 100644
index 0000000..5d22d0f
--- /dev/null
+++ b/test/test/throw-bail.tap
@@ -0,0 +1,17 @@
+TAP version 13
+    # Subtest: nesting
+    1..3
+        # Subtest: first
+        1..2
+        ok 1 - true is ok
+        ok 2 - doag is also okay
+    ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+        # Subtest: async thrower
+        not ok 1 - Error: THINK FAST! and also lines so many
+          ---
+          {"at":{"column":16,"file":"test/test/throw.js","function":"null._onTimeout","line":12},"jerk":true,"message":"Error: THINK FAST!\nand also lines\nso many","source":"var er = new Error('THINK FAST!\\nand also lines\\nso many')\n","test":"async thrower"}
+          ...
+        Bail out! # Error: THINK FAST! and also lines so many
+Bail out! # Error: THINK FAST! and also lines so many
+
diff --git a/test/test/throw.js b/test/test/throw.js
new file mode 100644
index 0000000..5e6d7de
--- /dev/null
+++ b/test/test/throw.js
@@ -0,0 +1,37 @@
+var t = require('../..')
+
+t.test('nesting', function (t) {
+  t.plan(3)
+  t.test('first', function (tt) {
+    tt.plan(2)
+    tt.ok(true, 'true is ok')
+    tt.ok('doeg', 'doag is also okay')
+  })
+  t.test('async thrower', function (tt) {
+    setTimeout(function () {
+      var er = new Error('THINK FAST!\nand also lines\nso many')
+      er.jerk = true
+      throw er
+    })
+  })
+  t.test('thrower', function (tt) {
+    var er = new Error('here hold this for a second')
+    er.syscall = 'ring ring'
+    throw er
+  })
+})
+
+t.pass('this passes')
+t.fail('this fails')
+
+t.test('async kid', function (t) {
+  t.plan(2)
+  setTimeout(function () {
+    t.pass('first timeout', { foo: 'blz' })
+  }, 50)
+  setTimeout(function () {
+    t.bailout('cannot continue')
+  })
+})
+
+t.pass('pass after async kid')
diff --git a/test/test/throw.tap b/test/test/throw.tap
new file mode 100644
index 0000000..cd0e14c
--- /dev/null
+++ b/test/test/throw.tap
@@ -0,0 +1,48 @@
+TAP version 13
+    # Subtest: nesting
+    1..3
+        # Subtest: first
+        1..2
+        ok 1 - true is ok
+        ok 2 - doag is also okay
+    ok 1 - first ___/# time=[0-9.]+(ms)?/~~~
+
+        # Subtest: async thrower
+        not ok 1 - Error: THINK FAST! and also lines so many
+          ---
+          {"at":{"column":16,"file":"test/test/throw.js","function":"null._onTimeout","line":12},"jerk":true,"message":"Error: THINK FAST!\nand also lines\nso many","source":"var er = new Error('THINK FAST!\\nand also lines\\nso many')\n","test":"async thrower"}
+          ...
+        1..1
+        # failed 1 of 1 tests
+    not ok 2 - async thrower ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":5,"file":"test/test/throw.js","line":10},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test('async thrower', function (tt) {\n"}
+      ...
+
+        # Subtest: thrower
+        not ok 1 - Error: here hold this for a second
+          ---
+          {"at":{"column":14,"file":"test/test/throw.js","line":18},"message":"Error: here hold this for a second","source":"var er = new Error('here hold this for a second')\n","syscall":"ring ring","test":"thrower"}
+          ...
+        1..1
+        # failed 1 of 1 tests
+    not ok 3 - thrower ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":5,"file":"test/test/throw.js","line":17},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test('thrower', function (tt) {\n"}
+      ...
+    # failed 2 of 3 tests
+not ok 1 - nesting ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/throw.js","line":3},"results":{"count":3,"fail":2,"ok":false,"pass":1,"plan":{"end":3,"start":1}},"source":"t.test('nesting', function (t) {\n"}
+  ...
+
+ok 2 - this passes
+not ok 3 - this fails
+  ---
+  {"at":{"column":3,"file":"test/test/throw.js","line":25},"source":"t.fail('this fails')\n"}
+  ...
+    # Subtest: async kid
+    1..2
+    Bail out! # cannot continue
+Bail out! # cannot continue
+
diff --git a/test/test/throws-and-plans-bail.tap b/test/test/throws-and-plans-bail.tap
new file mode 100644
index 0000000..b5f9332
--- /dev/null
+++ b/test/test/throws-and-plans-bail.tap
@@ -0,0 +1,16 @@
+TAP version 13
+ok 1 - expect truthy value
+    # Subtest: plans of 1
+    ok 1 - before sync thrower
+        # Subtest: sync thrower
+        1..1
+        ok 1 - before the bomb
+    ok 2 - sync thrower ___/# time=[0-9.]+(ms)?/~~~
+
+    not ok 3 - Error: pwnSync
+      ---
+      {"at":{"column":11,"file":"test/test/throws-and-plans.js","line":10},"message":"Error: pwnSync","source":"throw new Error('pwnSync')\n","test":"sync thrower"}
+      ...
+    Bail out! # Error: pwnSync
+Bail out! # Error: pwnSync
+
diff --git a/test/test/throws-and-plans.js b/test/test/throws-and-plans.js
new file mode 100644
index 0000000..a2096ae
--- /dev/null
+++ b/test/test/throws-and-plans.js
@@ -0,0 +1,59 @@
+var t = require('../..')
+t.ok('true')
+
+t.test('plans of 1', function (t) {
+  t.pass('before sync thrower')
+
+  t.test('sync thrower', function (tt) {
+    tt.plan(1)
+    tt.pass('before the bomb')
+    throw new Error('pwnSync')
+  })
+
+  t.test('async thrower', function (tt) {
+    tt.plan(3)
+    tt.pass('before set the bomb')
+    setTimeout(function () {
+      tt.pass('before the bomb')
+      throw new Error('pwn')
+    })
+    tt.pass('after set the bomb')
+  })
+  t.pass('after child')
+  t.end()
+})
+
+t.test('no assert only throw', function (t) {
+  var assert = require('../fixtures/assert.js')
+  assert(true, 'true is truthy')
+  assert(false, 'false is truthy right?')
+})
+
+t.test('plans of 8', function (t) {
+  t.pass('before child')
+
+  t.test('sync thrower', function (tt) {
+    tt.plan(8)
+    tt.pass('before the bomb')
+
+    throw new Error('pwnSync')
+    tt.pass('after the bomb') // eslint-disable-line
+    tt.end() // eslint-disable-line
+  })
+
+  // return t.end()
+
+  t.test('async thrower', function (tt) {
+    tt.plan(8)
+    tt.pass('before set the bomb')
+    setTimeout(function () {
+      tt.pass('before the bomb')
+      throw new Error('pwn')
+      tt.pass('after the bomb') // eslint-disable-line
+      tt.end() // eslint-disable-line
+    })
+    tt.pass('after set the bomb')
+  })
+  t.pass('after child')
+  t.end()
+})
diff --git a/test/test/throws-and-plans.tap b/test/test/throws-and-plans.tap
new file mode 100644
index 0000000..a0e3d19
--- /dev/null
+++ b/test/test/throws-and-plans.tap
@@ -0,0 +1,96 @@
+TAP version 13
+ok 1 - expect truthy value
+    # Subtest: plans of 1
+    ok 1 - before sync thrower
+        # Subtest: sync thrower
+        1..1
+        ok 1 - before the bomb
+    ok 2 - sync thrower ___/# time=[0-9.]+(ms)?/~~~
+
+    not ok 3 - Error: pwnSync
+      ---
+      {"at":{"column":11,"file":"test/test/throws-and-plans.js","line":10},"message":"Error: pwnSync","source":"throw new Error('pwnSync')\n","test":"sync thrower"}
+      ...
+        # Subtest: async thrower
+        1..3
+        ok 1 - before set the bomb
+        ok 2 - after set the bomb
+        ok 3 - before the bomb
+    ok 4 - async thrower ___/# time=[0-9.]+(ms)?/~~~
+
+    ok 5 - after child
+    1..5
+    # failed 1 of 5 tests
+not ok 2 - plans of 1 ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/throws-and-plans.js","line":4},"results":{"count":5,"fail":1,"ok":false,"pass":4,"plan":{"end":5,"start":1}},"source":"t.test('plans of 1', function (t) {\n"}
+  ...
+
+    # Subtest: no assert only throw
+    not ok 1 - AssertionError: false is truthy right?
+      ---
+      {"actual":false,"at":{"column":3,"file":"test/test/throws-and-plans.js","line":29},"expected":true,"generatedMessage":false,"message":"AssertionError: false is truthy right?","name":"AssertionError","operator":"==","source":"assert(false, 'false is truthy right?')\n","test":"no assert only throw"}
+      ...
+    1..1
+    # failed 1 of 1 tests
+not ok 3 - no assert only throw ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/throws-and-plans.js","line":26},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test('no assert only throw', function (t) {\n"}
+  ...
+
+    # Subtest: plans of 8
+    ok 1 - before child
+        # Subtest: sync thrower
+        1..8
+        ok 1 - before the bomb
+        not ok 2 - Error: pwnSync
+          ---
+          {"at":{"column":11,"file":"test/test/throws-and-plans.js","line":39},"message":"Error: pwnSync","source":"throw new Error('pwnSync')\n","test":"sync thrower"}
+          ...
+        not ok 3 - missing test
+        not ok 4 - missing test
+        not ok 5 - missing test
+        not ok 6 - missing test
+        not ok 7 - missing test
+        not ok 8 - missing test
+        # failed 7 of 8 tests
+    not ok 2 - sync thrower ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":5,"file":"test/test/throws-and-plans.js","line":35},"results":{"count":8,"fail":7,"ok":false,"pass":1,"plan":{"end":8,"start":1}},"source":"t.test('sync thrower', function (tt) {\n"}
+      ...
+
+        # Subtest: async thrower
+        1..8
+        ok 1 - before set the bomb
+        ok 2 - after set the bomb
+        not ok 3 - Error: pwn
+          ---
+          {"at":{"column":13,"file":"test/test/throws-and-plans.js","function":"null._onTimeout","line":18},"message":"Error: pwn","source":"throw new Error('pwn')\n","test":"async thrower"}
+          ...
+        not ok 4 - missing test
+        not ok 5 - missing test
+        not ok 6 - missing test
+        not ok 7 - missing test
+        not ok 8 - missing test
+        # failed 6 of 8 tests
+    not ok 3 - async thrower ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":5,"file":"test/test/throws-and-plans.js","line":46},"results":{"count":8,"fail":6,"ok":false,"pass":2,"plan":{"end":8,"start":1}},"source":"t.test('async thrower', function (tt) {\n"}
+      ...
+
+    ok 4 - after child
+    1..4
+    # failed 2 of 4 tests
+not ok 4 - plans of 8 ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/throws-and-plans.js","line":32},"results":{"count":4,"fail":2,"ok":false,"pass":2,"plan":{"end":4,"start":1}},"source":"t.test('plans of 8', function (t) {\n"}
+  ...
+
+not ok 5 - Error: pwn
+  ---
+  {"at":{"column":13,"file":"test/test/throws-and-plans.js","function":"null._onTimeout","line":51},"message":"Error: pwn","source":"throw new Error('pwn')\n","test":"TAP"}
+  ...
+1..5
+# failed 4 of 5 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/throws-bail.tap b/test/test/throws-bail.tap
new file mode 100644
index 0000000..1936582
--- /dev/null
+++ b/test/test/throws-bail.tap
@@ -0,0 +1,11 @@
+TAP version 13
+    # Subtest: throws should match a regex
+    1..2
+    ok 1 - passing_thrower
+    not ok 2 - failing_thrower
+      ---
+      {"at":{"column":5,"file":"test/test/throws.js","line":9},"found":"test","pattern":"/fasdfsadf/","source":"t.throws(function failing_thrower () {\n"}
+      ...
+    Bail out! # failing_thrower
+Bail out! # failing_thrower
+
diff --git a/test/test/throws.js b/test/test/throws.js
new file mode 100644
index 0000000..0713401
--- /dev/null
+++ b/test/test/throws.js
@@ -0,0 +1,12 @@
+var t = require('../..')
+
+t.test('throws should match a regex', function (t) {
+  t.plan(2)
+  t.throws(function passing_thrower () {
+    throw new Error('test')
+  }, /test/)
+
+  t.throws(function failing_thrower () {
+    throw new Error('test')
+  }, /fasdfsadf/)
+})
diff --git a/test/test/throws.tap b/test/test/throws.tap
new file mode 100644
index 0000000..f7d72b9
--- /dev/null
+++ b/test/test/throws.tap
@@ -0,0 +1,18 @@
+TAP version 13
+    # Subtest: throws should match a regex
+    1..2
+    ok 1 - passing_thrower
+    not ok 2 - failing_thrower
+      ---
+      {"at":{"column":5,"file":"test/test/throws.js","line":9},"found":"test","pattern":"/fasdfsadf/","source":"t.throws(function failing_thrower () {\n"}
+      ...
+    # failed 1 of 2 tests
+not ok 1 - throws should match a regex ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/throws.js","line":3},"results":{"count":2,"fail":1,"ok":false,"pass":1,"plan":{"end":2,"start":1}},"source":"t.test('throws should match a regex', function (t) {\n"}
+  ...
+
+1..1
+# failed 1 of 1 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/timeout-bail.tap b/test/test/timeout-bail.tap
new file mode 100644
index 0000000..d324b73
--- /dev/null
+++ b/test/test/timeout-bail.tap
@@ -0,0 +1,13 @@
+TAP version 13
+    # Subtest: parent of timeout test
+        # Subtest: timeout test
+            # Subtest: this never completes
+                # Subtest: baby
+                ok 1 - expect truthy value
+                not ok 2 - timeout!
+                  ---
+                  {"at":{"column":5,"file":"test/test/timeout.js","line":4},"expired":"timeout test","source":"t.test('timeout test', { timeout: 50 }, function (t) {\n","timeout":50}
+                  ...
+                Bail out! # timeout!
+Bail out! # timeout!
+
diff --git a/test/test/timeout.js b/test/test/timeout.js
new file mode 100644
index 0000000..26ca842
--- /dev/null
+++ b/test/test/timeout.js
@@ -0,0 +1,25 @@
+var t = require('../../lib/root.js')
+
+t.test('parent of timeout test', function (t) {
+  t.test('timeout test', { timeout: 50 }, function (t) {
+    t.test('this never completes', function (tt) {
+      tt.test('baby', function (tt) {
+        tt.ok('wait a sec...')
+        var timer = setTimeout(function () {
+          tt.pass('ok done')
+          tt.end()
+        }, 1000)
+        if (timer.unref) {
+          timer.unref()
+        }
+      })
+      tt.pass('p')
+      tt.pass('a')
+      tt.pass('s')
+      tt.pass('s')
+      tt.end()
+    })
+    t.end()
+  })
+  t.end()
+})
diff --git a/test/test/timeout.tap b/test/test/timeout.tap
new file mode 100644
index 0000000..3a19fb9
--- /dev/null
+++ b/test/test/timeout.tap
@@ -0,0 +1,42 @@
+TAP version 13
+    # Subtest: parent of timeout test
+        # Subtest: timeout test
+            # Subtest: this never completes
+                # Subtest: baby
+                ok 1 - expect truthy value
+                not ok 2 - timeout!
+                  ---
+                  {"at":{"column":5,"file":"test/test/timeout.js","line":4},"expired":"timeout test","source":"t.test('timeout test', { timeout: 50 }, function (t) {\n","timeout":50}
+                  ...
+                1..2
+                # failed 1 of 2 tests
+            not ok 1 - baby ___/# time=[0-9.]+(ms)?/~~~
+              ---
+              {"at":{"column":10,"file":"test/test/timeout.js","line":6},"results":{"count":2,"fail":1,"ok":false,"pass":1,"plan":{"end":2,"start":1}},"source":"tt.test('baby', function (tt) {\n"}
+              ...
+
+            1..1
+            # failed 1 of 1 tests
+        not ok 1 - this never completes ___/# time=[0-9.]+(ms)?/~~~
+          ---
+          {"at":{"column":7,"file":"test/test/timeout.js","line":5},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test('this never completes', function (tt) {\n"}
+          ...
+
+        1..1
+        # failed 1 of 1 tests
+    not ok 1 - timeout test ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":5,"file":"test/test/timeout.js","line":4},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test('timeout test', { timeout: 50 }, function (t) {\n","timeout":50}
+      ...
+
+    1..1
+    # failed 1 of 1 tests
+not ok 1 - parent of timeout test ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":3,"file":"test/test/timeout.js","line":3},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test('parent of timeout test', function (t) {\n"}
+  ...
+
+1..1
+# failed 1 of 1 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/todo-bail.tap b/test/test/todo-bail.tap
new file mode 100644
index 0000000..d753ebd
--- /dev/null
+++ b/test/test/todo-bail.tap
@@ -0,0 +1,30 @@
+TAP version 13
+    # Subtest: a set of tests to be done later
+    ok 1 - should have a thingie # TODO
+    ok 2 - should have a second whoosits also # TODO
+        # Subtest: the subset
+        ok 1 - should be a child thingie # TODO
+        ok 2 - should also be a whoosits # TODO
+            # Subtest: has some of these things
+            ok 1 - true is truthy
+            ok 2 - ten is also truthy
+            1..2
+        ok 3 - has some of these things ___/# time=[0-9.]+(ms)?/~~~
+
+        1..3
+    ok 3 - the subset ___/# time=[0-9.]+(ms)?/~~~
+
+    1..3
+ok 1 - a set of tests to be done later ___/# time=[0-9.]+(ms)?/~~~
+
+ok 2 - (unnamed test) # TODO
+    # Subtest: another set of tests
+    ok 1 - is a second set # TODO
+    ok 2 - looks like english # TODO
+    ok 3 - is marked TODO # TODO
+    1..3
+ok 3 - another set of tests ___/# time=[0-9.]+(ms)?/~~~
+
+1..3
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/todo.js b/test/test/todo.js
new file mode 100644
index 0000000..64c0686
--- /dev/null
+++ b/test/test/todo.js
@@ -0,0 +1,27 @@
+var t = require('../../lib/root.js')
+
+t.test('a set of tests to be done later', function (t) {
+  t.test('should have a thingie')
+  t.test('should have a second whoosits also')
+  t.test('the subset', function (t) {
+    t.test('should be a child thingie')
+    t.test('should also be a whoosits')
+    t.test('has some of these things', function (t) {
+      t.ok(true, 'true is truthy')
+      t.ok(10, 'ten is also truthy')
+      t.end()
+    })
+    t.end()
+  })
+  t.end()
+})
+
+// literally nothing
+t.test()
+
+t.test('another set of tests', function (t) {
+  t.test('is a second set')
+  t.test('looks like english')
+  t.test('is marked TODO')
+  t.end()
+})
diff --git a/test/test/todo.tap b/test/test/todo.tap
new file mode 100644
index 0000000..d753ebd
--- /dev/null
+++ b/test/test/todo.tap
@@ -0,0 +1,30 @@
+TAP version 13
+    # Subtest: a set of tests to be done later
+    ok 1 - should have a thingie # TODO
+    ok 2 - should have a second whoosits also # TODO
+        # Subtest: the subset
+        ok 1 - should be a child thingie # TODO
+        ok 2 - should also be a whoosits # TODO
+            # Subtest: has some of these things
+            ok 1 - true is truthy
+            ok 2 - ten is also truthy
+            1..2
+        ok 3 - has some of these things ___/# time=[0-9.]+(ms)?/~~~
+
+        1..3
+    ok 3 - the subset ___/# time=[0-9.]+(ms)?/~~~
+
+    1..3
+ok 1 - a set of tests to be done later ___/# time=[0-9.]+(ms)?/~~~
+
+ok 2 - (unnamed test) # TODO
+    # Subtest: another set of tests
+    ok 1 - is a second set # TODO
+    ok 2 - looks like english # TODO
+    ok 3 - is marked TODO # TODO
+    1..3
+ok 3 - another set of tests ___/# time=[0-9.]+(ms)?/~~~
+
+1..3
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/test/unfinished-bail.tap b/test/test/unfinished-bail.tap
new file mode 100644
index 0000000..8fa635d
--- /dev/null
+++ b/test/test/unfinished-bail.tap
@@ -0,0 +1,11 @@
+TAP version 13
+    # Subtest: t1
+        # Subtest: t11
+        1..1
+        not ok 1 - test unfinished: t11
+          ---
+          {"at":{"column":5,"file":"test/test/unfinished.js","line":4},"count":0,"plan":1,"source":"t.test('t11', function (t) {\n"}
+          ...
+        Bail out! # test unfinished: t11
+Bail out! # test unfinished: t11
+
diff --git a/test/test/unfinished.js b/test/test/unfinished.js
new file mode 100644
index 0000000..3a3d8b2
--- /dev/null
+++ b/test/test/unfinished.js
@@ -0,0 +1,38 @@
+var tap = require('../..')
+
+tap.test('t1', function (t) {
+  t.test('t11', function (t) {
+    t.plan(1)
+    process.on('some event that never happens', function () {
+      t.pass('ok')
+    })
+  })
+
+  // Nothing beyond this point will actually be processed
+  // Everything will just get stuffed into the queue, because
+  // the child test above is unfinished.  When the process
+  // ends, because there's no pending IO, it'll emit a bunch of
+  // failures indicating that they were abandoned in the queue.
+
+  t.ok(true, 'this would be ok if it ever happened')
+  t.end()
+})
+
+tap.equal(1, 1, '1 === 1')
+tap.ok('this is ok')
+tap.fail('failsome', { hoo: 'hah' })
+
+tap.spawn('node', [__filename], {}, 'spawny', { rar: 'grr' })
+tap.spawn('node', ['--version'], {}, '', { rar: 'grr' })
+
+tap.test(function (t) {
+  process.nextTick(t.end)
+})
+
+tap.test('', function (t) {
+  process.nextTick(t.end)
+})
+
+tap.test('t2', function (t) {
+  process.nextTick(t.end)
+})
diff --git a/test/test/unfinished.tap b/test/test/unfinished.tap
new file mode 100644
index 0000000..2543ec1
--- /dev/null
+++ b/test/test/unfinished.tap
@@ -0,0 +1,61 @@
+TAP version 13
+    # Subtest: t1
+        # Subtest: t11
+        1..1
+        not ok 1 - test unfinished: t11
+          ---
+          {"at":{"column":5,"file":"test/test/unfinished.js","line":4},"count":0,"plan":1,"source":"t.test('t11', function (t) {\n"}
+          ...
+        # failed 1 of 1 tests
+    not ok 1 - t11 ___/# time=[0-9.]+(ms)?/~~~
+      ---
+      {"at":{"column":5,"file":"test/test/unfinished.js","line":4},"results":{"count":1,"fail":1,"ok":false,"pass":0,"plan":{"end":1,"start":1}},"source":"t.test('t11', function (t) {\n"}
+      ...
+
+    not ok 2 - test point left in queue: ok - this would be ok if it ever happened
+      ---
+      {"at":{"column":5,"file":"test/test/unfinished.js","line":17},"source":"t.ok(true, 'this would be ok if it ever happened')\n","test":"t1"}
+      ...
+    1..2
+    # failed 2 of 2 tests
+not ok 1 - t1 ___/# time=[0-9.]+(ms)?/~~~
+  ---
+  {"at":{"column":5,"file":"test/test/unfinished.js","line":3},"results":{"count":2,"fail":2,"ok":false,"pass":0,"plan":{"end":2,"start":1}},"source":"tap.test('t1', function (t) {\n"}
+  ...
+
+not ok 2 - test point left in queue: ok - 1 === 1
+  ---
+  {"at":{"column":5,"file":"test/test/unfinished.js","line":21},"source":"tap.equal(1, 1, '1 === 1')\n"}
+  ...
+not ok 3 - test point left in queue: ok - expect truthy value
+  ---
+  {"at":{"column":5,"file":"test/test/unfinished.js","line":22},"source":"tap.ok('this is ok')\n"}
+  ...
+not ok 4 - test point left in queue: not ok - failsome
+  ---
+  {"at":{"column":5,"file":"test/test/unfinished.js","line":23},"hoo":"hah","source":"tap.fail('failsome', { hoo: 'hah' })\n"}
+  ...
+not ok 5 - spawn left in queue: spawny
+  ---
+  {"args":["___/.*/~~~unfinished.js"],"at":{"column":5,"file":"test/test/unfinished.js","line":25},"command":"___/.*(node|iojs)/~~~","options":{},"rar":"grr","source":"tap.spawn('___/.*(node|iojs)/~~~', [__filename], {}, 'spawny', { rar: 'grr' })\n"}
+  ...
+not ok 6 - spawn left in queue: ___/.*(node|iojs)/~~~ --version
+  ---
+  {"args":["--version"],"at":{"column":5,"file":"test/test/unfinished.js","line":26},"command":"___/.*(node|iojs)/~~~","options":{},"rar":"grr","source":"tap.spawn('___/.*(node|iojs)/~~~', ['--version'], {}, '', { rar: 'grr' })\n"}
+  ...
+not ok 7 - child test left in queue: (unnamed)
+  ---
+  {"at":{"column":5,"file":"test/test/unfinished.js","line":28},"source":"tap.test(function (t) {\n"}
+  ...
+not ok 8 - child test left in queue: (unnamed)
+  ---
+  {"at":{"column":5,"file":"test/test/unfinished.js","line":32},"source":"tap.test('', function (t) {\n"}
+  ...
+not ok 9 - child test left in queue: t2
+  ---
+  {"at":{"column":5,"file":"test/test/unfinished.js","line":36},"source":"tap.test('t2', function (t) {\n"}
+  ...
+1..9
+# failed 9 of 9 tests
+___/# time=[0-9.]+(ms)?/~~~
+
diff --git a/test/timeout.js b/test/timeout.js
index 4ee409c..988862a 100644
--- a/test/timeout.js
+++ b/test/timeout.js
@@ -1,33 +1,29 @@
-var tap = require("../")
+var tap = require('../')
 
-tap.test("timeout test with plan only", function (t) {
-  console.error("timeout test")
+tap.test('timeout test with plan only', function (t) {
   t.plan(2)
-  console.error("t.plan="+t._plan)
   setTimeout(function () {
-    console.error("a assert")
-    t.ok(true, "a")
-  }, 1000)
+    t.ok(true, 'a')
+  }, 100)
   setTimeout(function () {
-    console.error("b assert")
-    t.ok(true, "b")
-  }, 1000)
+    t.ok(true, 'b')
+  }, 100)
 })
 
-tap.test("timeout test with plan and end", function (t) {
-  console.error("timeout test")
+tap.test('timeout test with plan and end', function (t) {
   t.plan(2)
 
   var tc = 2
-  console.error("t.plan="+t._plan)
   setTimeout(function () {
-    console.error("a assert")
-    t.ok(true, "a")
-    if (-- tc === 0) t.end()
-  }, 1000)
+    t.ok(true, 'a')
+    if (--tc === 0) {
+      t.end()
+    }
+  }, 100)
   setTimeout(function () {
-    console.error("b assert")
-    t.ok(true, "b")
-    if (-- tc === 0) t.end()
-  }, 1000)
+    t.ok(true, 'b')
+    if (--tc === 0) {
+      t.end()
+    }
+  }, 100)
 })
diff --git a/test/trivial-success.js b/test/trivial-success.js
index e69de29..a77d7fd 100644
--- a/test/trivial-success.js
+++ b/test/trivial-success.js
@@ -0,0 +1,2 @@
+console.log('1..1')
+console.log('ok')
diff --git a/test/undefined_indented.js b/test/undefined_indented.js
deleted file mode 100644
index 98abe02..0000000
--- a/test/undefined_indented.js
+++ /dev/null
@@ -1,27 +0,0 @@
-var tap = require("../")
-
-tap.test("consume yaml", function (t) {
-  t.plan(1)
-
-  var s =
-    [ "not ok 1 beep boop"
-    , "  ---"
-    , "    stack:"
-    , "      - rawr"
-    , "      - dinosaurs"
-    , "  ..."
-    ].join("\n")
-  , c = tap.createConsumer()
-
-  c.on("data", function (res) {
-    t.same(res, {
-      id: 1
-      , ok: false
-      , name: " beep boop" // <-- should perhaps .trim() this?
-      , stack: [ "rawr", "dinosaurs" ]
-    })
-    t.end()
-  })
-  c.write(s)
-  c.end()
-})
diff --git a/test/valid-command.js b/test/valid-command.js
deleted file mode 100644
index f38e270..0000000
--- a/test/valid-command.js
+++ /dev/null
@@ -1,37 +0,0 @@
-var test = require('../').test
-var Runner = require('../lib/tap-runner.js')
-var TC = require('../lib/tap-consumer.js')
-
-test('valid command', function (t) {
-  var r = new Runner({argv:{remain:['./end-exception/t.js']}})
-  var tc = new TC()
-  var node = process.execPath
-  var expectObj ={
-    'id': 1,
-    'ok': false,
-    'name': ' ./end-exception/t.js',
-    'timedOut': true,
-    'command': '"' + node + ' t.js"',
-    exit: null
-  }
-  if (process.platform === 'linux' && process.version < 'v0.11.0') {
-    expectObj.exit = 143
-  } else {
-    expectObj.signal = 'SIGTERM'
-  }
-  var expect =
-      [ 'TAP version 13'
-      , 't.js'
-      , expectObj
-      , 'tests 1'
-      , 'fail  1' ]
-  r.pipe(tc)
-  tc.on('data', function (d) {
-    var e = expect.shift()
-    t.same(d, e)
-  })
-  tc.on('end', function () {
-    t.equal(expect.length, 0)
-    t.end()
-  })
-})

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-tap.git



More information about the Pkg-javascript-commits mailing list