[Pkg-javascript-commits] [node-glob] 01/08: New upstream version 7.1.1

Jérémy Lal kapouer at moszumanska.debian.org
Fri Nov 11 11:27:50 UTC 2016


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

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

commit 9d26c8d5f4256fa6384da5aa128b60d1d7ce6918
Author: Jérémy Lal <kapouer at melix.org>
Date:   Fri Nov 11 12:19:49 2016 +0100

    New upstream version 7.1.1
---
 .gitignore                        |    6 +-
 .travis.yml                       |    8 +-
 CONTRIBUTING.md                   |   19 +
 README.md                         |  264 ++++++---
 appveyor.yml                      |   19 +
 benchclean.js                     |    6 +
 benchmark.sh                      |   57 ++
 changelog.md                      |   67 +++
 common.js                         |  240 ++++++++
 glob.js                           | 1158 +++++++++++++++++++------------------
 make-benchmark-fixture.sh         |   14 +
 oh-my-glob.gif                    |  Bin 0 -> 510360 bytes
 package.json                      |   31 +-
 prof.sh                           |   19 +
 sync.js                           |  486 ++++++++++++++++
 test/00-setup.js                  |   74 +--
 test/abort.js                     |   19 +
 test/absolute.js                  |   49 ++
 test/bash-comparison.js           |   40 +-
 test/bash-results.json            |  430 ++++----------
 test/broken-symlink.js            |   82 +++
 test/cwd-test.js                  |   56 +-
 test/empty-set.js                 |    1 +
 test/enotsup.js                   |   65 +++
 test/eperm-stat.js                |  107 ++++
 test/error-callback.js            |   21 +-
 test/follow.js                    |   32 +
 test/global-leakage.js            |   20 +
 test/globstar-match.js            |    3 +-
 test/has-magic.js                 |   34 ++
 test/ignore.js                    |  111 ++++
 test/mark.js                      |  102 +++-
 test/match-base.js                |   49 ++
 test/multiple-weird-error.js      |   15 +
 test/negation-test.js             |   16 -
 test/new-glob-optional-options.js |    6 +-
 test/nocase-nomagic.js            |   44 +-
 test/nodir.js                     |   67 +++
 test/nonull.js                    |   31 +
 test/pause-resume.js              |   32 +-
 test/readme-issue.js              |    1 +
 test/realpath.js                  |   93 +++
 test/root-nomount.js              |   33 +-
 test/root.js                      |   59 +-
 test/slash-cwd.js                 |   20 +
 test/stat.js                      |   10 +-
 test/sync-cb-throw.js             |   46 ++
 test/zz-cleanup.js                |    3 +-
 48 files changed, 3051 insertions(+), 1114 deletions(-)

diff --git a/.gitignore b/.gitignore
index c34fdd5..bcafea6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,7 @@
 .*.swp
-test/a/
 node_modules/*
+v8.log
+profile.txt
+nyc_output/
+.nyc_output/
+coverage/
diff --git a/.travis.yml b/.travis.yml
index fca8ef0..ee45280 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,8 @@
+sudo: false
 language: node_js
 node_js:
-  - 0.10
-  - 0.11
+  - '0.10'
+  - '0.12'
+  - '4'
+  - '5'
+  - '6'
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..7cc701e
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,19 @@
+## Contributing
+
+Any change to behavior (including bugfixes) must come with a test.
+
+Patches that fail tests or reduce performance will be rejected.
+
+```
+# to run tests
+npm test
+
+# to re-generate test fixtures
+npm run test-regen
+
+# to benchmark against bash/zsh
+npm run bench
+
+# to profile javascript
+npm run prof
+```
diff --git a/README.md b/README.md
index cc69164..baa1d1b 100644
--- a/README.md
+++ b/README.md
@@ -2,21 +2,20 @@
 
 Match files using the patterns the shell uses, like stars and stuff.
 
+[![Build Status](https://travis-ci.org/isaacs/node-glob.svg?branch=master)](https://travis-ci.org/isaacs/node-glob/) [![Build Status](https://ci.appveyor.com/api/projects/status/kd7f3yftf7unxlsx?svg=true)](https://ci.appveyor.com/project/isaacs/node-glob) [![Coverage Status](https://coveralls.io/repos/isaacs/node-glob/badge.svg?branch=master&service=github)](https://coveralls.io/github/isaacs/node-glob?branch=master)
+
 This is a glob implementation in JavaScript.  It uses the `minimatch`
 library to do its matching.
 
-## Attention: node-glob users!
-
-The API has changed dramatically between 2.x and 3.x. This library is
-now 100% JavaScript, and the integer flags have been replaced with an
-options object.
+![](oh-my-glob.gif)
 
-Also, there's an event emitter class, proper tests, and all the other
-things you've come to expect from node modules.
+## Usage
 
-And best of all, no compilation!
+Install with npm
 
-## Usage
+```
+npm i glob
+```
 
 ```javascript
 var glob = require("glob")
@@ -30,46 +29,108 @@ glob("**/*.js", options, function (er, files) {
 })
 ```
 
-## Features
+## Glob Primer
+
+"Globs" are the patterns you type when you do stuff like `ls *.js` on
+the command line, or put `build/*` in a `.gitignore` file.
+
+Before parsing the path part patterns, braced sections are expanded
+into a set.  Braced sections start with `{` and end with `}`, with any
+number of comma-delimited sections within.  Braced sections may contain
+slash characters, so `a{/b/c,bcd}` would expand into `a/b/c` and `abcd`.
+
+The following characters have special magic meaning when used in a
+path portion:
+
+* `*` Matches 0 or more characters in a single path portion
+* `?` Matches 1 character
+* `[...]` Matches a range of characters, similar to a RegExp range.
+  If the first character of the range is `!` or `^` then it matches
+  any character not in the range.
+* `!(pattern|pattern|pattern)` Matches anything that does not match
+  any of the patterns provided.
+* `?(pattern|pattern|pattern)` Matches zero or one occurrence of the
+  patterns provided.
+* `+(pattern|pattern|pattern)` Matches one or more occurrences of the
+  patterns provided.
+* `*(a|b|c)` Matches zero or more occurrences of the patterns provided
+* `@(pattern|pat*|pat?erN)` Matches exactly one of the patterns
+  provided
+* `**` If a "globstar" is alone in a path portion, then it matches
+  zero or more directories and subdirectories searching for matches.
+  It does not crawl symlinked directories.
 
-Please see the [minimatch
-documentation](https://github.com/isaacs/minimatch) for more details.
+### Dots
 
-Supports these glob features:
+If a file or directory path portion has a `.` as the first character,
+then it will not match any glob pattern unless that pattern's
+corresponding path part also has a `.` as its first character.
 
-* Brace Expansion
-* Extended glob matching
-* "Globstar" `**` matching
+For example, the pattern `a/.*/c` would match the file at `a/.b/c`.
+However the pattern `a/*/c` would not, because `*` does not start with
+a dot character.
 
-See:
+You can make glob treat dots as normal characters by setting
+`dot:true` in the options.
+
+### Basename Matching
+
+If you set `matchBase:true` in the options, and the pattern has no
+slashes in it, then it will seek for any file anywhere in the tree
+with a matching basename.  For example, `*.js` would match
+`test/simple/basic.js`.
+
+### Empty Sets
+
+If no matching files are found, then an empty array is returned.  This
+differs from the shell, where the pattern itself is returned.  For
+example:
+
+    $ echo a*s*d*f
+    a*s*d*f
+
+To get the bash-style behavior, set the `nonull:true` in the options.
+
+### See Also:
 
 * `man sh`
-* `man bash`
+* `man bash` (Search for "Pattern Matching")
 * `man 3 fnmatch`
 * `man 5 gitignore`
 * [minimatch documentation](https://github.com/isaacs/minimatch)
 
+## glob.hasMagic(pattern, [options])
+
+Returns `true` if there are any special characters in the pattern, and
+`false` otherwise.
+
+Note that the options affect the results.  If `noext:true` is set in
+the options object, then `+(a|b)` will not be considered a magic
+pattern.  If the pattern has a brace expansion, like `a/{b/c,x/y}`
+then that is considered magical, unless `nobrace:true` is set in the
+options.
+
 ## glob(pattern, [options], cb)
 
-* `pattern` {String} Pattern to be matched
-* `options` {Object}
-* `cb` {Function}
-  * `err` {Error | null}
-  * `matches` {Array<String>} filenames found matching the pattern
+* `pattern` `{String}` Pattern to be matched
+* `options` `{Object}`
+* `cb` `{Function}`
+  * `err` `{Error | null}`
+  * `matches` `{Array<String>}` filenames found matching the pattern
 
 Perform an asynchronous glob search.
 
 ## glob.sync(pattern, [options])
 
-* `pattern` {String} Pattern to be matched
-* `options` {Object}
-* return: {Array<String>} filenames found matching the pattern
+* `pattern` `{String}` Pattern to be matched
+* `options` `{Object}`
+* return: `{Array<String>}` filenames found matching the pattern
 
 Perform a synchronous glob search.
 
 ## Class: glob.Glob
 
-Create a Glob object by instanting the `glob.Glob` class.
+Create a Glob object by instantiating the `glob.Glob` class.
 
 ```javascript
 var Glob = require("glob").Glob
@@ -81,11 +142,11 @@ immediately.
 
 ### new glob.Glob(pattern, [options], [cb])
 
-* `pattern` {String} pattern to search for
-* `options` {Object}
-* `cb` {Function} Called when an error occurs, or matches are found
-  * `err` {Error | null}
-  * `matches` {Array<String>} filenames found matching the pattern
+* `pattern` `{String}` pattern to search for
+* `options` `{Object}`
+* `cb` `{Function}` Called when an error occurs, or matches are found
+  * `err` `{Error | null}`
+  * `matches` `{Array<String>}` filenames found matching the pattern
 
 Note that if the `sync` flag is set in the options, then matches will
 be immediately available on the `g.found` member.
@@ -94,21 +155,24 @@ be immediately available on the `g.found` member.
 
 * `minimatch` The minimatch object that the glob uses.
 * `options` The options object passed in.
-* `error` The error encountered.  When an error is encountered, the
-  glob object is in an undefined state, and should be discarded.
 * `aborted` Boolean which is set to true when calling `abort()`.  There
   is no way at this time to continue a glob search after aborting, but
   you can re-use the statCache to avoid having to duplicate syscalls.
-* `statCache` Collection of all the stat results the glob search
-  performed.
 * `cache` Convenience object.  Each field has the following possible
   values:
   * `false` - Path does not exist
   * `true` - Path exists
-  * `1` - Path exists, and is not a directory
-  * `2` - Path exists, and is a directory
+  * `'FILE'` - Path exists, and is not a directory
+  * `'DIR'` - Path exists, and is a directory
   * `[file, entries, ...]` - Path exists, is a directory, and the
     array value is the results of `fs.readdir`
+* `statCache` Cache of `fs.stat` results, to prevent statting the same
+  path multiple times.
+* `symlinks` A record of which paths are symbolic links, which is
+  relevant in resolving `**` patterns.
+* `realpathCache` An optional object which is passed to `fs.realpath`
+  to minimize unnecessary syscalls.  It is stored on the instantiated
+  Glob object, and may be re-used.
 
 ### Events
 
@@ -116,14 +180,17 @@ be immediately available on the `g.found` member.
   matches found.  If the `nonull` option is set, and no match was found,
   then the `matches` list contains the original pattern.  The matches
   are sorted, unless the `nosort` flag is set.
-* `match` Every time a match is found, this is emitted with the matched.
+* `match` Every time a match is found, this is emitted with the specific
+  thing that matched. It is not deduplicated or resolved to a realpath.
 * `error` Emitted when an unexpected error is encountered, or whenever
   any fs error occurs if `options.strict` is set.
 * `abort` When `abort()` is called, this event is raised.
 
 ### Methods
 
-* `abort` Stop the search.
+* `pause` Temporarily stop the search
+* `resume` Resume the search
+* `abort` Stop the search forever
 
 ### Options
 
@@ -133,7 +200,14 @@ or have glob-specific ramifications.
 
 All options are false by default, unless otherwise noted.
 
-All options are added to the glob object, as well.
+All options are added to the Glob object, as well.
+
+If you are running many `glob` operations, you can pass a Glob object
+as the `options` argument to a subsequent operation to shortcut some
+`stat` and `readdir` calls.  At the very least, you may pass in shared
+`symlinks`, `statCache`, `realpathCache`, and `cache` options, so that
+parallel glob operations will be sped up by sharing information about
+the filesystem.
 
 * `cwd` The current working directory in which to search.  Defaults
   to `process.cwd()`.
@@ -151,36 +225,57 @@ All options are added to the glob object, as well.
 * `nosort` Don't sort the results.
 * `stat` Set to true to stat *all* results.  This reduces performance
   somewhat, and is completely unnecessary, unless `readdir` is presumed
-  to be an untrustworthy indicator of file existence.  It will cause
-  ELOOP to be triggered one level sooner in the case of cyclical
-  symbolic links.
-* `silent` When an unusual error is encountered
-  when attempting to read a directory, a warning will be printed to
-  stderr.  Set the `silent` option to true to suppress these warnings.
-* `strict` When an unusual error is encountered
-  when attempting to read a directory, the process will just continue on
-  in search of other matches.  Set the `strict` option to raise an error
-  in these cases.
+  to be an untrustworthy indicator of file existence.
+* `silent` When an unusual error is encountered when attempting to
+  read a directory, a warning will be printed to stderr.  Set the
+  `silent` option to true to suppress these warnings.
+* `strict` When an unusual error is encountered when attempting to
+  read a directory, the process will just continue on in search of
+  other matches.  Set the `strict` option to raise an error in these
+  cases.
 * `cache` See `cache` property above.  Pass in a previously generated
   cache object to save some fs calls.
 * `statCache` A cache of results of filesystem information, to prevent
-  unnecessary stat calls.  While it should not normally be necessary to
-  set this, you may pass the statCache from one glob() call to the
+  unnecessary stat calls.  While it should not normally be necessary
+  to set this, you may pass the statCache from one glob() call to the
   options object of another, if you know that the filesystem will not
   change between calls.  (See "Race Conditions" below.)
-* `sync` Perform a synchronous glob search.
+* `symlinks` A cache of known symbolic links.  You may pass in a
+  previously generated `symlinks` object to save `lstat` calls when
+  resolving `**` matches.
+* `sync` DEPRECATED: use `glob.sync(pattern, opts)` instead.
 * `nounique` In some cases, brace-expanded patterns can result in the
   same file showing up multiple times in the result set.  By default,
-  this implementation prevents duplicates in the result set.
-  Set this flag to disable that behavior.
+  this implementation prevents duplicates in the result set.  Set this
+  flag to disable that behavior.
 * `nonull` Set to never return an empty set, instead returning a set
   containing the pattern itself.  This is the default in glob(3).
-* `nocase` Perform a case-insensitive match.  Note that case-insensitive
-  filesystems will sometimes result in glob returning results that are
-  case-insensitively matched anyway, since readdir and stat will not
-  raise an error.
 * `debug` Set to enable debug logging in minimatch and glob.
-* `globDebug` Set to enable debug logging in glob, but not minimatch.
+* `nobrace` Do not expand `{a,b}` and `{1..3}` brace sets.
+* `noglobstar` Do not match `**` against multiple filenames.  (Ie,
+  treat it as a normal `*` instead.)
+* `noext` Do not match `+(a|b)` "extglob" patterns.
+* `nocase` Perform a case-insensitive match.  Note: on
+  case-insensitive filesystems, non-magic patterns will match by
+  default, since `stat` and `readdir` will not raise errors.
+* `matchBase` Perform a basename-only match if the pattern does not
+  contain any slash characters.  That is, `*.js` would be treated as
+  equivalent to `**/*.js`, matching all js files in all directories.
+* `nodir` Do not match directories, only files.  (Note: to match
+  *only* directories, simply put a `/` at the end of the pattern.)
+* `ignore` Add a pattern or an array of glob patterns to exclude matches.
+  Note: `ignore` patterns are *always* in `dot:true` mode, regardless
+  of any other settings.
+* `follow` Follow symlinked directories when expanding `**` patterns.
+  Note that this can result in a lot of duplicate references in the
+  presence of cyclic links.
+* `realpath` Set to true to call `fs.realpath` on all of the results.
+  In the case of a symlink that cannot be resolved, the full absolute
+  path to the matched entry is returned (though it will usually be a
+  broken symlink)
+* `absolute` Set to true to always receive absolute paths for matched
+  files.  Unlike `realpath`, this also affects the values returned in
+  the `match` event.
 
 ## Comparisons to other fnmatch/glob implementations
 
@@ -188,23 +283,16 @@ While strict compliance with the existing standards is a worthwhile
 goal, some discrepancies exist between node-glob and other
 implementations, and are intentional.
 
-If the pattern starts with a `!` character, then it is negated.  Set the
-`nonegate` flag to suppress this behavior, and treat leading `!`
-characters normally.  This is perhaps relevant if you wish to start the
-pattern with a negative extglob pattern like `!(a|B)`.  Multiple `!`
-characters at the start of a pattern will negate the pattern multiple
-times.
-
-If a pattern starts with `#`, then it is treated as a comment, and
-will not match anything.  Use `\#` to match a literal `#` at the
-start of a line, or set the `nocomment` flag to suppress this behavior.
-
 The double-star character `**` is supported by default, unless the
 `noglobstar` flag is set.  This is supported in the manner of bsdglob
-and bash 4.1, where `**` only has special significance if it is the only
+and bash 4.3, where `**` only has special significance if it is the only
 thing in a path part.  That is, `a/**/b` will match `a/x/y/b`, but
 `a/**b` will not.
 
+Note that symlinked directories are not crawled as part of a `**`,
+though their contents may match against subsequent portions of the
+pattern.  This prevents infinite loops and duplicates and the like.
+
 If an escaped pattern has no matches, and the `nonull` flag is set,
 then glob returns the pattern as-provided, rather than
 interpreting the character escapes.  For example,
@@ -218,6 +306,16 @@ other interpretation of the glob pattern.  Thus, a pattern like
 **first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
 checked for validity.  Since those two are valid, matching proceeds.
 
+### Comments and Negation
+
+Previously, this module let you mark a pattern as a "comment" if it
+started with a `#` character, or a "negated" pattern if it started
+with a `!` character.
+
+These options were deprecated in version 5, and removed in version 6.
+
+To specify things that should not match, use the `ignore` option.
+
 ## Windows
 
 **Please only use forward-slashes in glob expressions.**
@@ -248,3 +346,23 @@ calls.
 Users are thus advised not to use a glob result as a guarantee of
 filesystem state in the face of rapid changes.  For the vast majority
 of operations, this is never a problem.
+
+## Contributing
+
+Any change to behavior (including bugfixes) must come with a test.
+
+Patches that fail tests or reduce performance will be rejected.
+
+```
+# to run tests
+npm test
+
+# to re-generate test fixtures
+npm run test-regen
+
+# to benchmark against bash/zsh
+npm run bench
+
+# to profile javascript
+npm run prof
+```
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..b29e6c6
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,19 @@
+environment:
+  matrix:
+    - nodejs_version: '5'
+    - nodejs_version: '4'
+    - nodejs_version: '0.12'
+install:
+  - ps: Install-Product node $env:nodejs_version
+  - set CI=true
+  - npm -g install npm at latest
+  - set PATH=%APPDATA%\npm;%PATH%
+  - npm install
+matrix:
+  fast_finish: true
+build: off
+version: '{build}'
+shallow_clone: true
+clone_depth: 1
+test_script:
+  - npm test
diff --git a/benchclean.js b/benchclean.js
new file mode 100644
index 0000000..de90d8a
--- /dev/null
+++ b/benchclean.js
@@ -0,0 +1,6 @@
+var rimraf = require('rimraf')
+var bf = (process.env.TMPDIR || '/tmp') + '/benchmark-fixture'
+rimraf('{' + [bf, 'v8.log', 'profile.txt'].join(',') + '}', function (er) {
+  if (er)
+    throw er
+})
diff --git a/benchmark.sh b/benchmark.sh
new file mode 100644
index 0000000..df31b8b
--- /dev/null
+++ b/benchmark.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+export CDPATH=
+
+tmp=${TMPDIR:-/tmp}
+bash make-benchmark-fixture.sh
+wd=$PWD
+cd $tmp/benchmark-fixture
+
+set -e
+
+if [[ "`bash --version`" =~ version\ 4 ]]; then
+  echo Bash timing:
+  time bash -c 'shopt -s globstar; echo **/*.txt | wc -w'
+fi
+
+echo
+if type zsh; then
+  echo Zsh timing:
+  time zsh -c 'echo **/*.txt | wc -w'
+fi
+
+echo
+
+echo Node statSync and readdirSync timing:
+time node -e '
+  var fs=require("fs");
+  var count = 0;
+  function walk (path) {
+    if (path.slice(-4) === ".txt") count++;
+    var stat = fs.statSync(path);
+    if (stat.isDirectory()) {
+      fs.readdirSync(path).forEach(function(entry) {
+        walk(path + "/" + entry);
+      })
+    }
+  }
+  walk(".");
+  console.log(count)'
+echo
+
+echo Node glob.sync timing:
+time node -e '
+  var glob=require(process.argv[1]);
+  console.log(glob.sync("**/*.txt").length);' "$wd"
+echo
+
+echo Node glob async timing:
+time node -e '
+  var glob=require(process.argv[1]);
+  glob("**/*.txt", function (er, files) {
+    console.log(files.length)
+  })' "$wd"
+echo
+
+echo Node glob with --prof
+cd $wd
+bash prof.sh
diff --git a/changelog.md b/changelog.md
new file mode 100644
index 0000000..4163677
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,67 @@
+## 7.0
+
+- Raise error if `options.cwd` is specified, and not a directory
+
+## 6.0
+
+- Remove comment and negation pattern support
+- Ignore patterns are always in `dot:true` mode
+
+## 5.0
+
+- Deprecate comment and negation patterns
+- Fix regression in `mark` and `nodir` options from making all cache
+  keys absolute path.
+- Abort if `fs.readdir` returns an error that's unexpected
+- Don't emit `match` events for ignored items
+- Treat ENOTSUP like ENOTDIR in readdir
+
+## 4.5
+
+- Add `options.follow` to always follow directory symlinks in globstar
+- Add `options.realpath` to call `fs.realpath` on all results
+- Always cache based on absolute path
+
+## 4.4
+
+- Add `options.ignore`
+- Fix handling of broken symlinks
+
+## 4.3
+
+- Bump minimatch to 2.x
+- Pass all tests on Windows
+
+## 4.2
+
+- Add `glob.hasMagic` function
+- Add `options.nodir` flag
+
+## 4.1
+
+- Refactor sync and async implementations for performance
+- Throw if callback provided to sync glob function
+- Treat symbolic links in globstar results the same as Bash 4.3
+
+## 4.0
+
+- Use `^` for dependency versions (bumped major because this breaks
+  older npm versions)
+- Ensure callbacks are only ever called once
+- switch to ISC license
+
+## 3.x
+
+- Rewrite in JavaScript
+- Add support for setting root, cwd, and windows support
+- Cache many fs calls
+- Add globstar support
+- emit match events
+
+## 2.x
+
+- Use `glob.h` and `fnmatch.h` from NetBSD
+
+## 1.x
+
+- `glob.h` static binding.
diff --git a/common.js b/common.js
new file mode 100644
index 0000000..66651bb
--- /dev/null
+++ b/common.js
@@ -0,0 +1,240 @@
+exports.alphasort = alphasort
+exports.alphasorti = alphasorti
+exports.setopts = setopts
+exports.ownProp = ownProp
+exports.makeAbs = makeAbs
+exports.finish = finish
+exports.mark = mark
+exports.isIgnored = isIgnored
+exports.childrenIgnored = childrenIgnored
+
+function ownProp (obj, field) {
+  return Object.prototype.hasOwnProperty.call(obj, field)
+}
+
+var path = require("path")
+var minimatch = require("minimatch")
+var isAbsolute = require("path-is-absolute")
+var Minimatch = minimatch.Minimatch
+
+function alphasorti (a, b) {
+  return a.toLowerCase().localeCompare(b.toLowerCase())
+}
+
+function alphasort (a, b) {
+  return a.localeCompare(b)
+}
+
+function setupIgnores (self, options) {
+  self.ignore = options.ignore || []
+
+  if (!Array.isArray(self.ignore))
+    self.ignore = [self.ignore]
+
+  if (self.ignore.length) {
+    self.ignore = self.ignore.map(ignoreMap)
+  }
+}
+
+// ignore patterns are always in dot:true mode.
+function ignoreMap (pattern) {
+  var gmatcher = null
+  if (pattern.slice(-3) === '/**') {
+    var gpattern = pattern.replace(/(\/\*\*)+$/, '')
+    gmatcher = new Minimatch(gpattern, { dot: true })
+  }
+
+  return {
+    matcher: new Minimatch(pattern, { dot: true }),
+    gmatcher: gmatcher
+  }
+}
+
+function setopts (self, pattern, options) {
+  if (!options)
+    options = {}
+
+  // base-matching: just use globstar for that.
+  if (options.matchBase && -1 === pattern.indexOf("/")) {
+    if (options.noglobstar) {
+      throw new Error("base matching requires globstar")
+    }
+    pattern = "**/" + pattern
+  }
+
+  self.silent = !!options.silent
+  self.pattern = pattern
+  self.strict = options.strict !== false
+  self.realpath = !!options.realpath
+  self.realpathCache = options.realpathCache || Object.create(null)
+  self.follow = !!options.follow
+  self.dot = !!options.dot
+  self.mark = !!options.mark
+  self.nodir = !!options.nodir
+  if (self.nodir)
+    self.mark = true
+  self.sync = !!options.sync
+  self.nounique = !!options.nounique
+  self.nonull = !!options.nonull
+  self.nosort = !!options.nosort
+  self.nocase = !!options.nocase
+  self.stat = !!options.stat
+  self.noprocess = !!options.noprocess
+  self.absolute = !!options.absolute
+
+  self.maxLength = options.maxLength || Infinity
+  self.cache = options.cache || Object.create(null)
+  self.statCache = options.statCache || Object.create(null)
+  self.symlinks = options.symlinks || Object.create(null)
+
+  setupIgnores(self, options)
+
+  self.changedCwd = false
+  var cwd = process.cwd()
+  if (!ownProp(options, "cwd"))
+    self.cwd = cwd
+  else {
+    self.cwd = path.resolve(options.cwd)
+    self.changedCwd = self.cwd !== cwd
+  }
+
+  self.root = options.root || path.resolve(self.cwd, "/")
+  self.root = path.resolve(self.root)
+  if (process.platform === "win32")
+    self.root = self.root.replace(/\\/g, "/")
+
+  // TODO: is an absolute `cwd` supposed to be resolved against `root`?
+  // e.g. { cwd: '/test', root: __dirname } === path.join(__dirname, '/test')
+  self.cwdAbs = isAbsolute(self.cwd) ? self.cwd : makeAbs(self, self.cwd)
+  if (process.platform === "win32")
+    self.cwdAbs = self.cwdAbs.replace(/\\/g, "/")
+  self.nomount = !!options.nomount
+
+  // disable comments and negation in Minimatch.
+  // Note that they are not supported in Glob itself anyway.
+  options.nonegate = true
+  options.nocomment = true
+
+  self.minimatch = new Minimatch(pattern, options)
+  self.options = self.minimatch.options
+}
+
+function finish (self) {
+  var nou = self.nounique
+  var all = nou ? [] : Object.create(null)
+
+  for (var i = 0, l = self.matches.length; i < l; i ++) {
+    var matches = self.matches[i]
+    if (!matches || Object.keys(matches).length === 0) {
+      if (self.nonull) {
+        // do like the shell, and spit out the literal glob
+        var literal = self.minimatch.globSet[i]
+        if (nou)
+          all.push(literal)
+        else
+          all[literal] = true
+      }
+    } else {
+      // had matches
+      var m = Object.keys(matches)
+      if (nou)
+        all.push.apply(all, m)
+      else
+        m.forEach(function (m) {
+          all[m] = true
+        })
+    }
+  }
+
+  if (!nou)
+    all = Object.keys(all)
+
+  if (!self.nosort)
+    all = all.sort(self.nocase ? alphasorti : alphasort)
+
+  // at *some* point we statted all of these
+  if (self.mark) {
+    for (var i = 0; i < all.length; i++) {
+      all[i] = self._mark(all[i])
+    }
+    if (self.nodir) {
+      all = all.filter(function (e) {
+        var notDir = !(/\/$/.test(e))
+        var c = self.cache[e] || self.cache[makeAbs(self, e)]
+        if (notDir && c)
+          notDir = c !== 'DIR' && !Array.isArray(c)
+        return notDir
+      })
+    }
+  }
+
+  if (self.ignore.length)
+    all = all.filter(function(m) {
+      return !isIgnored(self, m)
+    })
+
+  self.found = all
+}
+
+function mark (self, p) {
+  var abs = makeAbs(self, p)
+  var c = self.cache[abs]
+  var m = p
+  if (c) {
+    var isDir = c === 'DIR' || Array.isArray(c)
+    var slash = p.slice(-1) === '/'
+
+    if (isDir && !slash)
+      m += '/'
+    else if (!isDir && slash)
+      m = m.slice(0, -1)
+
+    if (m !== p) {
+      var mabs = makeAbs(self, m)
+      self.statCache[mabs] = self.statCache[abs]
+      self.cache[mabs] = self.cache[abs]
+    }
+  }
+
+  return m
+}
+
+// lotta situps...
+function makeAbs (self, f) {
+  var abs = f
+  if (f.charAt(0) === '/') {
+    abs = path.join(self.root, f)
+  } else if (isAbsolute(f) || f === '') {
+    abs = f
+  } else if (self.changedCwd) {
+    abs = path.resolve(self.cwd, f)
+  } else {
+    abs = path.resolve(f)
+  }
+
+  if (process.platform === 'win32')
+    abs = abs.replace(/\\/g, '/')
+
+  return abs
+}
+
+
+// Return true, if pattern ends with globstar '**', for the accompanying parent directory.
+// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents
+function isIgnored (self, path) {
+  if (!self.ignore.length)
+    return false
+
+  return self.ignore.some(function(item) {
+    return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path))
+  })
+}
+
+function childrenIgnored (self, path) {
+  if (!self.ignore.length)
+    return false
+
+  return self.ignore.some(function(item) {
+    return !!(item.gmatcher && item.gmatcher.match(path))
+  })
+}
diff --git a/glob.js b/glob.js
index 6941fc7..bfdd7a1 100644
--- a/glob.js
+++ b/glob.js
@@ -1,24 +1,30 @@
 // Approach:
 //
 // 1. Get the minimatch set
-// 2. For each pattern in the set, PROCESS(pattern)
+// 2. For each pattern in the set, PROCESS(pattern, false)
 // 3. Store matches per-set, then uniq them
 //
-// PROCESS(pattern)
+// PROCESS(pattern, inGlobStar)
 // Get the first [n] items from pattern that are all strings
 // Join these together.  This is PREFIX.
 //   If there is no more remaining, then stat(PREFIX) and
 //   add to matches if it succeeds.  END.
-// readdir(PREFIX) as ENTRIES
-//   If fails, END
+//
+// If inGlobStar and PREFIX is symlink and points to dir
+//   set ENTRIES = []
+// else readdir(PREFIX) as ENTRIES
+//   If fail, END
+//
+// with ENTRIES
 //   If pattern[n] is GLOBSTAR
 //     // handle the case where the globstar match is empty
 //     // by pruning it out, and testing the resulting pattern
-//     PROCESS(pattern[0..n] + pattern[n+1 .. $])
+//     PROCESS(pattern[0..n] + pattern[n+1 .. $], false)
 //     // handle other cases.
 //     for ENTRY in ENTRIES (not dotfiles)
 //       // attach globstar + tail onto the entry
-//       PROCESS(pattern[0..n] + ENTRY + pattern[n .. $])
+//       // Mark that this entry is a globstar match
+//       PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true)
 //
 //   else // not globstar
 //     for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot)
@@ -32,148 +38,102 @@
 //   `true` for files, and [children,...] for directories, or `false` for
 //   things that don't exist.
 
-
-
 module.exports = glob
 
-var fs
-try { fs = require("graceful-fs") } catch (e) { fs = require("fs") }
-
-var minimatch = require("minimatch")
-, Minimatch = minimatch.Minimatch
-, inherits = require("inherits")
-, EE = require("events").EventEmitter
-, path = require("path")
-, isDir = {}
-, assert = require("assert").ok
-, once = require("once")
+var fs = require('fs')
+var rp = require('fs.realpath')
+var minimatch = require('minimatch')
+var Minimatch = minimatch.Minimatch
+var inherits = require('inherits')
+var EE = require('events').EventEmitter
+var path = require('path')
+var assert = require('assert')
+var isAbsolute = require('path-is-absolute')
+var globSync = require('./sync.js')
+var common = require('./common.js')
+var alphasort = common.alphasort
+var alphasorti = common.alphasorti
+var setopts = common.setopts
+var ownProp = common.ownProp
+var inflight = require('inflight')
+var util = require('util')
+var childrenIgnored = common.childrenIgnored
+var isIgnored = common.isIgnored
+
+var once = require('once')
 
 function glob (pattern, options, cb) {
-  if (typeof options === "function") cb = options, options = {}
+  if (typeof options === 'function') cb = options, options = {}
   if (!options) options = {}
 
-  if (typeof options === "number") {
-    deprecated()
-    return
+  if (options.sync) {
+    if (cb)
+      throw new TypeError('callback provided to sync glob')
+    return globSync(pattern, options)
   }
 
-  var g = new Glob(pattern, options, cb)
-  return g.sync ? g.found : g
-}
-
-glob.fnmatch = deprecated
-
-function deprecated () {
-  throw new Error("glob's interface has changed. Please see the docs.")
+  return new Glob(pattern, options, cb)
 }
 
 glob.sync = globSync
-function globSync (pattern, options) {
-  if (typeof options === "number") {
-    deprecated()
-    return
-  }
+var GlobSync = glob.GlobSync = globSync.GlobSync
 
-  options = options || {}
-  options.sync = true
-  return glob(pattern, options)
-}
-
-this._processingEmitQueue = false
+// old api surface
+glob.glob = glob
 
-glob.Glob = Glob
-inherits(Glob, EE)
-function Glob (pattern, options, cb) {
-  if (!(this instanceof Glob)) {
-    return new Glob(pattern, options, cb)
+function extend (origin, add) {
+  if (add === null || typeof add !== 'object') {
+    return origin
   }
 
-  if (typeof options === "function") {
-    cb = options
-    options = null
+  var keys = Object.keys(add)
+  var i = keys.length
+  while (i--) {
+    origin[keys[i]] = add[keys[i]]
   }
+  return origin
+}
 
-  if (typeof cb === "function") {
-    cb = once(cb)
-    this.on("error", cb)
-    this.on("end", function (matches) {
-      cb(null, matches)
-    })
-  }
+glob.hasMagic = function (pattern, options_) {
+  var options = extend({}, options_)
+  options.noprocess = true
 
-  options = options || {}
+  var g = new Glob(pattern, options)
+  var set = g.minimatch.set
 
-  this._endEmitted = false
-  this.EOF = {}
-  this._emitQueue = []
+  if (!pattern)
+    return false
 
-  this.paused = false
-  this._processingEmitQueue = false
-
-  this.maxDepth = options.maxDepth || 1000
-  this.maxLength = options.maxLength || Infinity
-  this.cache = options.cache || {}
-  this.statCache = options.statCache || {}
-
-  this.changedCwd = false
-  var cwd = process.cwd()
-  if (!options.hasOwnProperty("cwd")) this.cwd = cwd
-  else {
-    this.cwd = options.cwd
-    this.changedCwd = path.resolve(options.cwd) !== cwd
-  }
+  if (set.length > 1)
+    return true
 
-  this.root = options.root || path.resolve(this.cwd, "/")
-  this.root = path.resolve(this.root)
-  if (process.platform === "win32")
-    this.root = this.root.replace(/\\/g, "/")
+  for (var j = 0; j < set[0].length; j++) {
+    if (typeof set[0][j] !== 'string')
+      return true
+  }
 
-  this.nomount = !!options.nomount
+  return false
+}
 
-  if (!pattern) {
-    throw new Error("must provide pattern")
+glob.Glob = Glob
+inherits(Glob, EE)
+function Glob (pattern, options, cb) {
+  if (typeof options === 'function') {
+    cb = options
+    options = null
   }
 
-  // base-matching: just use globstar for that.
-  if (options.matchBase && -1 === pattern.indexOf("/")) {
-    if (options.noglobstar) {
-      throw new Error("base matching requires globstar")
-    }
-    pattern = "**/" + pattern
+  if (options && options.sync) {
+    if (cb)
+      throw new TypeError('callback provided to sync glob')
+    return new GlobSync(pattern, options)
   }
 
-  this.strict = options.strict !== false
-  this.dot = !!options.dot
-  this.mark = !!options.mark
-  this.sync = !!options.sync
-  this.nounique = !!options.nounique
-  this.nonull = !!options.nonull
-  this.nosort = !!options.nosort
-  this.nocase = !!options.nocase
-  this.stat = !!options.stat
-
-  this.debug = !!options.debug || !!options.globDebug
-
-  if (/\bglob\b/.test(process.env.NODE_DEBUG || ''))
-    this.debug = true
-
-  if (this.debug)
-    this.log = console.error
-
-  this.silent = !!options.silent
-
-  var mm = this.minimatch = new Minimatch(pattern, options)
-  this.options = mm.options
-  pattern = this.pattern = mm.pattern
-
-  this.error = null
-  this.aborted = false
-
-  // list of all the patterns that ** has resolved do, so
-  // we can avoid visiting multiple times.
-  this._globstars = {}
+  if (!(this instanceof Glob))
+    return new Glob(pattern, options, cb)
 
-  EE.call(this)
+  setopts(this, pattern, options)
+  this._didRealPath = false
 
   // process each pattern in the minimatch set
   var n = this.minimatch.set.length
@@ -184,210 +144,177 @@ function Glob (pattern, options, cb) {
   // Keep them as a list so we can fill in when nonull is set.
   this.matches = new Array(n)
 
-  if (this.minimatch.set.length === 0) {
-    return process.nextTick(this._finish.bind(this))
-  }
-
-  this.minimatch.set.forEach(iterator.bind(this))
-  function iterator (pattern, i, set) {
-    this._process(pattern, 0, i, function (er) {
-      if (er) this.emit("error", er)
-      if (-- n <= 0) this._finish()
+  if (typeof cb === 'function') {
+    cb = once(cb)
+    this.on('error', cb)
+    this.on('end', function (matches) {
+      cb(null, matches)
     })
   }
-}
 
-Glob.prototype.log = function () {}
+  var self = this
+  var n = this.minimatch.set.length
+  this._processing = 0
+  this.matches = new Array(n)
 
-Glob.prototype._finish = function () {
-  assert(this instanceof Glob)
+  this._emitQueue = []
+  this._processQueue = []
+  this.paused = false
+
+  if (this.noprocess)
+    return this
 
-  var nou = this.nounique
-  , all = nou ? [] : {}
-
-  for (var i = 0, l = this.matches.length; i < l; i ++) {
-    var matches = this.matches[i]
-    this.log("matches[%d] =", i, matches)
-    // do like the shell, and spit out the literal glob
-    if (!matches) {
-      if (this.nonull) {
-        var literal = this.minimatch.globSet[i]
-        if (nou) all.push(literal)
-        else all[literal] = true
+  if (n === 0)
+    return done()
+
+  var sync = true
+  for (var i = 0; i < n; i ++) {
+    this._process(this.minimatch.set[i], i, false, done)
+  }
+  sync = false
+
+  function done () {
+    --self._processing
+    if (self._processing <= 0) {
+      if (sync) {
+        process.nextTick(function () {
+          self._finish()
+        })
+      } else {
+        self._finish()
       }
-    } else {
-      // had matches
-      var m = Object.keys(matches)
-      if (nou) all.push.apply(all, m)
-      else m.forEach(function (m) {
-        all[m] = true
-      })
     }
   }
+}
 
-  if (!nou) all = Object.keys(all)
+Glob.prototype._finish = function () {
+  assert(this instanceof Glob)
+  if (this.aborted)
+    return
 
-  if (!this.nosort) {
-    all = all.sort(this.nocase ? alphasorti : alphasort)
-  }
+  if (this.realpath && !this._didRealpath)
+    return this._realpath()
 
-  if (this.mark) {
-    // at *some* point we statted all of these
-    all = all.map(this._mark, this)
-  }
+  common.finish(this)
+  this.emit('end', this.found)
+}
 
-  this.log("emitting end", all)
+Glob.prototype._realpath = function () {
+  if (this._didRealpath)
+    return
 
-  this.EOF = this.found = all
-  this.emitMatch(this.EOF)
-}
+  this._didRealpath = true
 
-function alphasorti (a, b) {
-  a = a.toLowerCase()
-  b = b.toLowerCase()
-  return alphasort(a, b)
+  var n = this.matches.length
+  if (n === 0)
+    return this._finish()
+
+  var self = this
+  for (var i = 0; i < this.matches.length; i++)
+    this._realpathSet(i, next)
+
+  function next () {
+    if (--n === 0)
+      self._finish()
+  }
 }
 
-function alphasort (a, b) {
-  return a > b ? 1 : a < b ? -1 : 0
+Glob.prototype._realpathSet = function (index, cb) {
+  var matchset = this.matches[index]
+  if (!matchset)
+    return cb()
+
+  var found = Object.keys(matchset)
+  var self = this
+  var n = found.length
+
+  if (n === 0)
+    return cb()
+
+  var set = this.matches[index] = Object.create(null)
+  found.forEach(function (p, i) {
+    // If there's a problem with the stat, then it means that
+    // one or more of the links in the realpath couldn't be
+    // resolved.  just return the abs value in that case.
+    p = self._makeAbs(p)
+    rp.realpath(p, self.realpathCache, function (er, real) {
+      if (!er)
+        set[real] = true
+      else if (er.syscall === 'stat')
+        set[p] = true
+      else
+        self.emit('error', er) // srsly wtf right here
+
+      if (--n === 0) {
+        self.matches[index] = set
+        cb()
+      }
+    })
+  })
 }
 
 Glob.prototype._mark = function (p) {
-  var c = this.cache[p]
-  var m = p
-  if (c) {
-    var isDir = c === 2 || Array.isArray(c)
-    var slash = p.slice(-1) === '/'
-
-    if (isDir && !slash)
-      m += '/'
-    else if (!isDir && slash)
-      m = m.slice(0, -1)
-
-    if (m !== p) {
-      this.statCache[m] = this.statCache[p]
-      this.cache[m] = this.cache[p]
-    }
-  }
+  return common.mark(this, p)
+}
 
-  return m
+Glob.prototype._makeAbs = function (f) {
+  return common.makeAbs(this, f)
 }
 
 Glob.prototype.abort = function () {
   this.aborted = true
-  this.emit("abort")
+  this.emit('abort')
 }
 
 Glob.prototype.pause = function () {
-  if (this.paused) return
-  if (this.sync)
-    this.emit("error", new Error("Can't pause/resume sync glob"))
-  this.paused = true
-  this.emit("pause")
+  if (!this.paused) {
+    this.paused = true
+    this.emit('pause')
+  }
 }
 
 Glob.prototype.resume = function () {
-  if (!this.paused) return
-  if (this.sync)
-    this.emit("error", new Error("Can't pause/resume sync glob"))
-  this.paused = false
-  this.emit("resume")
-  this._processEmitQueue()
-  //process.nextTick(this.emit.bind(this, "resume"))
-}
-
-Glob.prototype.emitMatch = function (m) {
-  this.log('emitMatch', m)
-  this._emitQueue.push(m)
-  this._processEmitQueue()
-}
-
-Glob.prototype._processEmitQueue = function (m) {
-  this.log("pEQ paused=%j processing=%j m=%j", this.paused,
-           this._processingEmitQueue, m)
-  var done = false
-  while (!this._processingEmitQueue &&
-         !this.paused) {
-    this._processingEmitQueue = true
-    var m = this._emitQueue.shift()
-    this.log(">processEmitQueue", m === this.EOF ? ":EOF:" : m)
-    if (!m) {
-      this.log(">processEmitQueue, falsey m")
-      this._processingEmitQueue = false
-      break
-    }
-
-    if (m === this.EOF || !(this.mark && !this.stat)) {
-      this.log("peq: unmarked, or eof")
-      next.call(this, 0, false)
-    } else if (this.statCache[m]) {
-      var sc = this.statCache[m]
-      var exists
-      if (sc)
-        exists = sc.isDirectory() ? 2 : 1
-      this.log("peq: stat cached")
-      next.call(this, exists, exists === 2)
-    } else {
-      this.log("peq: _stat, then next")
-      this._stat(m, next)
+  if (this.paused) {
+    this.emit('resume')
+    this.paused = false
+    if (this._emitQueue.length) {
+      var eq = this._emitQueue.slice(0)
+      this._emitQueue.length = 0
+      for (var i = 0; i < eq.length; i ++) {
+        var e = eq[i]
+        this._emitMatch(e[0], e[1])
+      }
     }
-  }
-  done = true
-
-  function next(exists, isDir) {
-    this.log("next", m, exists, isDir)
-    var ev = m === this.EOF ? "end" : "match"
-
-    // "end" can only happen once.
-    assert(!this._endEmitted)
-    if (ev === "end")
-      this._endEmitted = true
-
-    if (exists) {
-      // Doesn't mean it necessarily doesn't exist, it's possible
-      // we just didn't check because we don't care that much, or
-      // this is EOF anyway.
-      if (isDir && !m.match(/\/$/)) {
-        m = m + "/"
-      } else if (!isDir && m.match(/\/$/)) {
-        m = m.replace(/\/+$/, "")
+    if (this._processQueue.length) {
+      var pq = this._processQueue.slice(0)
+      this._processQueue.length = 0
+      for (var i = 0; i < pq.length; i ++) {
+        var p = pq[i]
+        this._processing--
+        this._process(p[0], p[1], p[2], p[3])
       }
     }
-    this.log("emit", ev, m)
-    this.emit(ev, m)
-    this._processingEmitQueue = false
-    if (done && m !== this.EOF && !this.paused)
-      this._processEmitQueue()
   }
 }
 
-Glob.prototype._process = function (pattern, depth, index, cb_) {
+Glob.prototype._process = function (pattern, index, inGlobStar, cb) {
   assert(this instanceof Glob)
+  assert(typeof cb === 'function')
 
-  var cb = function cb (er, res) {
-    assert(this instanceof Glob)
-    if (this.paused) {
-      if (!this._processQueue) {
-        this._processQueue = []
-        this.once("resume", function () {
-          var q = this._processQueue
-          this._processQueue = null
-          q.forEach(function (cb) { cb() })
-        })
-      }
-      this._processQueue.push(cb_.bind(this, er, res))
-    } else {
-      cb_.call(this, er, res)
-    }
-  }.bind(this)
+  if (this.aborted)
+    return
 
-  if (this.aborted) return cb()
+  this._processing++
+  if (this.paused) {
+    this._processQueue.push([pattern, index, inGlobStar, cb])
+    return
+  }
 
-  if (depth > this.maxDepth) return cb()
+  //console.error('PROCESS %d', this._processing, pattern)
 
   // Get the first [n] parts of pattern that are all strings.
   var n = 0
-  while (typeof pattern[n] === "string") {
+  while (typeof pattern[n] === 'string') {
     n ++
   }
   // now n is the index of the first one that is *not* a string.
@@ -397,28 +324,7 @@ Glob.prototype._process = function (pattern, depth, index, cb_) {
   switch (n) {
     // if not, then this is rather simple
     case pattern.length:
-      prefix = pattern.join("/")
-      this._stat(prefix, function (exists, isDir) {
-        // either it's there, or it isn't.
-        // nothing more to do, either way.
-        if (exists) {
-          if (prefix && isAbsolute(prefix) && !this.nomount) {
-            if (prefix.charAt(0) === "/") {
-              prefix = path.join(this.root, prefix)
-            } else {
-              prefix = path.resolve(this.root, prefix)
-            }
-          }
-
-          if (process.platform === "win32")
-            prefix = prefix.replace(/\\/g, "/")
-
-          this.matches[index] = this.matches[index] || {}
-          this.matches[index][prefix] = true
-          this.emitMatch(prefix)
-        }
-        return cb()
-      })
+      this._processSimple(pattern.join('/'), index, cb)
       return
 
     case 0:
@@ -429,320 +335,458 @@ Glob.prototype._process = function (pattern, depth, index, cb_) {
 
     default:
       // pattern has some string bits in the front.
-      // whatever it starts with, whether that's "absolute" like /foo/bar,
-      // or "relative" like "../baz"
-      prefix = pattern.slice(0, n)
-      prefix = prefix.join("/")
+      // whatever it starts with, whether that's 'absolute' like /foo/bar,
+      // or 'relative' like '../baz'
+      prefix = pattern.slice(0, n).join('/')
       break
   }
 
+  var remain = pattern.slice(n)
+
   // get the list of entries.
   var read
-  if (prefix === null) read = "."
-  else if (isAbsolute(prefix) || isAbsolute(pattern.join("/"))) {
-    if (!prefix || !isAbsolute(prefix)) {
-      prefix = "/" + prefix
-    }
+  if (prefix === null)
+    read = '.'
+  else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) {
+    if (!prefix || !isAbsolute(prefix))
+      prefix = '/' + prefix
     read = prefix
-
-    // if (process.platform === "win32")
-    //   read = prefix = prefix.replace(/^[a-zA-Z]:|\\/g, "/")
-
-    this.log('absolute: ', prefix, this.root, pattern, read)
-  } else {
+  } else
     read = prefix
-  }
 
-  this.log('readdir(%j)', read, this.cwd, this.root)
+  var abs = this._makeAbs(read)
 
-  return this._readdir(read, function (er, entries) {
-    if (er) {
-      // not a directory!
-      // this means that, whatever else comes after this, it can never match
-      return cb()
-    }
+  //if ignored, skip _processing
+  if (childrenIgnored(this, read))
+    return cb()
 
-    // globstar is special
-    if (pattern[n] === minimatch.GLOBSTAR) {
-      // test without the globstar, and with every child both below
-      // and replacing the globstar.
-      var s = [ pattern.slice(0, n).concat(pattern.slice(n + 1)) ]
-      entries.forEach(function (e) {
-        if (e.charAt(0) === "." && !this.dot) return
-        // instead of the globstar
-        s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1)))
-        // below the globstar
-        s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n)))
-      }, this)
-
-      s = s.filter(function (pattern) {
-        var key = gsKey(pattern)
-        var seen = !this._globstars[key]
-        this._globstars[key] = true
-        return seen
-      }, this)
-
-      if (!s.length)
-        return cb()
+  var isGlobStar = remain[0] === minimatch.GLOBSTAR
+  if (isGlobStar)
+    this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb)
+  else
+    this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb)
+}
 
-      // now asyncForEach over this
-      var l = s.length
-      , errState = null
-      s.forEach(function (gsPattern) {
-        this._process(gsPattern, depth + 1, index, function (er) {
-          if (errState) return
-          if (er) return cb(errState = er)
-          if (--l <= 0) return cb()
-        })
-      }, this)
+Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) {
+  var self = this
+  this._readdir(abs, inGlobStar, function (er, entries) {
+    return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb)
+  })
+}
 
-      return
+Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {
+
+  // if the abs isn't a dir, then nothing can match!
+  if (!entries)
+    return cb()
+
+  // It will only match dot entries if it starts with a dot, or if
+  // dot is set.  Stuff like @(.foo|.bar) isn't allowed.
+  var pn = remain[0]
+  var negate = !!this.minimatch.negate
+  var rawGlob = pn._glob
+  var dotOk = this.dot || rawGlob.charAt(0) === '.'
+
+  var matchedEntries = []
+  for (var i = 0; i < entries.length; i++) {
+    var e = entries[i]
+    if (e.charAt(0) !== '.' || dotOk) {
+      var m
+      if (negate && !prefix) {
+        m = !e.match(pn)
+      } else {
+        m = e.match(pn)
+      }
+      if (m)
+        matchedEntries.push(e)
     }
+  }
 
-    // not a globstar
-    // It will only match dot entries if it starts with a dot, or if
-    // dot is set.  Stuff like @(.foo|.bar) isn't allowed.
-    var pn = pattern[n]
-    var negate = !!this.minimatch.negate;
-    var rawGlob = pattern[n]._glob
-    , dotOk = this.dot || rawGlob.charAt(0) === "."
-
-    entries = entries.filter(function (e) {
-      if (e.charAt(0) !== "." || dotOk) {
-        if (negate && n === 0) {
-          return !e.match(pattern[n]);
-        } else {
-          return e.match(pattern[n]);
-        }
+  //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries)
+
+  var len = matchedEntries.length
+  // If there are no matched entries, then nothing matches.
+  if (len === 0)
+    return cb()
+
+  // if this is the last remaining pattern bit, then no need for
+  // an additional stat *unless* the user has specified mark or
+  // stat explicitly.  We know they exist, since readdir returned
+  // them.
+
+  if (remain.length === 1 && !this.mark && !this.stat) {
+    if (!this.matches[index])
+      this.matches[index] = Object.create(null)
+
+    for (var i = 0; i < len; i ++) {
+      var e = matchedEntries[i]
+      if (prefix) {
+        if (prefix !== '/')
+          e = prefix + '/' + e
+        else
+          e = prefix + e
       }
 
-      return null;
-    })
+      if (e.charAt(0) === '/' && !this.nomount) {
+        e = path.join(this.root, e)
+      }
+      this._emitMatch(index, e)
+    }
+    // This was the last one, and no stats were needed
+    return cb()
+  }
 
-    // If n === pattern.length - 1, then there's no need for the extra stat
-    // *unless* the user has specified "mark" or "stat" explicitly.
-    // We know that they exist, since the readdir returned them.
-    if (n === pattern.length - 1 &&
-        !this.mark &&
-        !this.stat) {
-      entries.forEach(function (e) {
-        if (prefix) {
-          if (prefix !== "/") e = prefix + "/" + e
-          else e = prefix + e
-        }
-        if (e.charAt(0) === "/" && !this.nomount) {
-          e = path.join(this.root, e)
-        }
-
-        if (process.platform === "win32")
-          e = e.replace(/\\/g, "/")
-
-        this.matches[index] = this.matches[index] || {}
-        this.matches[index][e] = true
-        this.emitMatch(e)
-      }, this)
-      return cb.call(this)
+  // now test all matched entries as stand-ins for that part
+  // of the pattern.
+  remain.shift()
+  for (var i = 0; i < len; i ++) {
+    var e = matchedEntries[i]
+    var newPattern
+    if (prefix) {
+      if (prefix !== '/')
+        e = prefix + '/' + e
+      else
+        e = prefix + e
     }
+    this._process([e].concat(remain), index, inGlobStar, cb)
+  }
+  cb()
+}
 
+Glob.prototype._emitMatch = function (index, e) {
+  if (this.aborted)
+    return
 
-    // now test all the remaining entries as stand-ins for that part
-    // of the pattern.
-    var l = entries.length
-    , errState = null
-    if (l === 0) return cb() // no matches possible
-    entries.forEach(function (e) {
-      var p = pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1))
-      this._process(p, depth + 1, index, function (er) {
-        if (errState) return
-        if (er) return cb(errState = er)
-        if (--l === 0) return cb.call(this)
-      })
-    }, this)
-  })
+  if (isIgnored(this, e))
+    return
 
-}
+  if (this.paused) {
+    this._emitQueue.push([index, e])
+    return
+  }
+
+  var abs = isAbsolute(e) ? e : this._makeAbs(e)
+
+  if (this.mark)
+    e = this._mark(e)
+
+  if (this.absolute)
+    e = abs
+
+  if (this.matches[index][e])
+    return
+
+  if (this.nodir) {
+    var c = this.cache[abs]
+    if (c === 'DIR' || Array.isArray(c))
+      return
+  }
 
-function gsKey (pattern) {
-  return '**' + pattern.map(function (p) {
-    return (p === minimatch.GLOBSTAR) ? '**' : (''+p)
-  }).join('/')
+  this.matches[index][e] = true
+
+  var st = this.statCache[abs]
+  if (st)
+    this.emit('stat', e, st)
+
+  this.emit('match', e)
 }
 
-Glob.prototype._stat = function (f, cb) {
-  assert(this instanceof Glob)
-  var abs = f
-  if (f.charAt(0) === "/") {
-    abs = path.join(this.root, f)
-  } else if (this.changedCwd) {
-    abs = path.resolve(this.cwd, f)
+Glob.prototype._readdirInGlobStar = function (abs, cb) {
+  if (this.aborted)
+    return
+
+  // follow all symlinked directories forever
+  // just proceed as if this is a non-globstar situation
+  if (this.follow)
+    return this._readdir(abs, false, cb)
+
+  var lstatkey = 'lstat\0' + abs
+  var self = this
+  var lstatcb = inflight(lstatkey, lstatcb_)
+
+  if (lstatcb)
+    fs.lstat(abs, lstatcb)
+
+  function lstatcb_ (er, lstat) {
+    if (er && er.code === 'ENOENT')
+      return cb()
+
+    var isSym = lstat && lstat.isSymbolicLink()
+    self.symlinks[abs] = isSym
+
+    // If it's not a symlink or a dir, then it's definitely a regular file.
+    // don't bother doing a readdir in that case.
+    if (!isSym && lstat && !lstat.isDirectory()) {
+      self.cache[abs] = 'FILE'
+      cb()
+    } else
+      self._readdir(abs, false, cb)
   }
+}
+
+Glob.prototype._readdir = function (abs, inGlobStar, cb) {
+  if (this.aborted)
+    return
+
+  cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb)
+  if (!cb)
+    return
 
-  if (f.length > this.maxLength) {
-    var er = new Error("Path name too long")
-    er.code = "ENAMETOOLONG"
-    er.path = f
-    return this._afterStat(f, abs, cb, er)
+  //console.error('RD %j %j', +inGlobStar, abs)
+  if (inGlobStar && !ownProp(this.symlinks, abs))
+    return this._readdirInGlobStar(abs, cb)
+
+  if (ownProp(this.cache, abs)) {
+    var c = this.cache[abs]
+    if (!c || c === 'FILE')
+      return cb()
+
+    if (Array.isArray(c))
+      return cb(null, c)
   }
 
-  this.log('stat', [this.cwd, f, '=', abs])
+  var self = this
+  fs.readdir(abs, readdirCb(this, abs, cb))
+}
 
-  if (!this.stat && this.cache.hasOwnProperty(f)) {
-    var exists = this.cache[f]
-    , isDir = exists && (Array.isArray(exists) || exists === 2)
-    if (this.sync) return cb.call(this, !!exists, isDir)
-    return process.nextTick(cb.bind(this, !!exists, isDir))
+function readdirCb (self, abs, cb) {
+  return function (er, entries) {
+    if (er)
+      self._readdirError(abs, er, cb)
+    else
+      self._readdirEntries(abs, entries, cb)
   }
+}
 
-  var stat = this.statCache[abs]
-  if (this.sync || stat) {
-    var er
-    try {
-      stat = fs.statSync(abs)
-    } catch (e) {
-      er = e
+Glob.prototype._readdirEntries = function (abs, entries, cb) {
+  if (this.aborted)
+    return
+
+  // if we haven't asked to stat everything, then just
+  // assume that everything in there exists, so we can avoid
+  // having to stat it a second time.
+  if (!this.mark && !this.stat) {
+    for (var i = 0; i < entries.length; i ++) {
+      var e = entries[i]
+      if (abs === '/')
+        e = abs + e
+      else
+        e = abs + '/' + e
+      this.cache[e] = true
     }
-    this._afterStat(f, abs, cb, er, stat)
-  } else {
-    fs.stat(abs, this._afterStat.bind(this, f, abs, cb))
   }
+
+  this.cache[abs] = entries
+  return cb(null, entries)
 }
 
-Glob.prototype._afterStat = function (f, abs, cb, er, stat) {
-  var exists
-  assert(this instanceof Glob)
+Glob.prototype._readdirError = function (f, er, cb) {
+  if (this.aborted)
+    return
 
-  if (abs.slice(-1) === "/" && stat && !stat.isDirectory()) {
-    this.log("should be ENOTDIR, fake it")
+  // handle errors, and cache the information
+  switch (er.code) {
+    case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205
+    case 'ENOTDIR': // totally normal. means it *does* exist.
+      var abs = this._makeAbs(f)
+      this.cache[abs] = 'FILE'
+      if (abs === this.cwdAbs) {
+        var error = new Error(er.code + ' invalid cwd ' + this.cwd)
+        error.path = this.cwd
+        error.code = er.code
+        this.emit('error', error)
+        this.abort()
+      }
+      break
+
+    case 'ENOENT': // not terribly unusual
+    case 'ELOOP':
+    case 'ENAMETOOLONG':
+    case 'UNKNOWN':
+      this.cache[this._makeAbs(f)] = false
+      break
 
-    er = new Error("ENOTDIR, not a directory '" + abs + "'")
-    er.path = abs
-    er.code = "ENOTDIR"
-    stat = null
+    default: // some unusual error.  Treat as failure.
+      this.cache[this._makeAbs(f)] = false
+      if (this.strict) {
+        this.emit('error', er)
+        // If the error is handled, then we abort
+        // if not, we threw out of here
+        this.abort()
+      }
+      if (!this.silent)
+        console.error('glob error', er)
+      break
   }
 
-  var emit = !this.statCache[abs]
-  this.statCache[abs] = stat
+  return cb()
+}
 
-  if (er || !stat) {
-    exists = false
-  } else {
-    exists = stat.isDirectory() ? 2 : 1
-    if (emit)
-      this.emit('stat', f, stat)
-  }
-  this.cache[f] = this.cache[f] || exists
-  cb.call(this, !!exists, exists === 2)
+Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) {
+  var self = this
+  this._readdir(abs, inGlobStar, function (er, entries) {
+    self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb)
+  })
 }
 
-Glob.prototype._readdir = function (f, cb) {
-  assert(this instanceof Glob)
-  var abs = f
-  if (f.charAt(0) === "/") {
-    abs = path.join(this.root, f)
-  } else if (isAbsolute(f)) {
-    abs = f
-  } else if (this.changedCwd) {
-    abs = path.resolve(this.cwd, f)
-  }
 
-  if (f.length > this.maxLength) {
-    var er = new Error("Path name too long")
-    er.code = "ENAMETOOLONG"
-    er.path = f
-    return this._afterReaddir(f, abs, cb, er)
-  }
+Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {
+  //console.error('pgs2', prefix, remain[0], entries)
 
-  this.log('readdir', [this.cwd, f, abs])
-  if (this.cache.hasOwnProperty(f)) {
-    var c = this.cache[f]
-    if (Array.isArray(c)) {
-      if (this.sync) return cb.call(this, null, c)
-      return process.nextTick(cb.bind(this, null, c))
-    }
+  // no entries means not a dir, so it can never have matches
+  // foo.txt/** doesn't match foo.txt
+  if (!entries)
+    return cb()
 
-    if (!c || c === 1) {
-      // either ENOENT or ENOTDIR
-      var code = c ? "ENOTDIR" : "ENOENT"
-      , er = new Error((c ? "Not a directory" : "Not found") + ": " + f)
-      er.path = f
-      er.code = code
-      this.log(f, er)
-      if (this.sync) return cb.call(this, er)
-      return process.nextTick(cb.bind(this, er))
-    }
+  // test without the globstar, and with every child both below
+  // and replacing the globstar.
+  var remainWithoutGlobStar = remain.slice(1)
+  var gspref = prefix ? [ prefix ] : []
+  var noGlobStar = gspref.concat(remainWithoutGlobStar)
+
+  // the noGlobStar pattern exits the inGlobStar state
+  this._process(noGlobStar, index, false, cb)
+
+  var isSym = this.symlinks[abs]
+  var len = entries.length
+
+  // If it's a symlink, and we're in a globstar, then stop
+  if (isSym && inGlobStar)
+    return cb()
 
-    // at this point, c === 2, meaning it's a dir, but we haven't
-    // had to read it yet, or c === true, meaning it's *something*
-    // but we don't have any idea what.  Need to read it, either way.
+  for (var i = 0; i < len; i++) {
+    var e = entries[i]
+    if (e.charAt(0) === '.' && !this.dot)
+      continue
+
+    // these two cases enter the inGlobStar state
+    var instead = gspref.concat(entries[i], remainWithoutGlobStar)
+    this._process(instead, index, true, cb)
+
+    var below = gspref.concat(entries[i], remain)
+    this._process(below, index, true, cb)
   }
 
-  if (this.sync) {
-    var er, entries
-    try {
-      entries = fs.readdirSync(abs)
-    } catch (e) {
-      er = e
+  cb()
+}
+
+Glob.prototype._processSimple = function (prefix, index, cb) {
+  // XXX review this.  Shouldn't it be doing the mounting etc
+  // before doing stat?  kinda weird?
+  var self = this
+  this._stat(prefix, function (er, exists) {
+    self._processSimple2(prefix, index, er, exists, cb)
+  })
+}
+Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) {
+
+  //console.error('ps2', prefix, exists)
+
+  if (!this.matches[index])
+    this.matches[index] = Object.create(null)
+
+  // If it doesn't exist, then just mark the lack of results
+  if (!exists)
+    return cb()
+
+  if (prefix && isAbsolute(prefix) && !this.nomount) {
+    var trail = /[\/\\]$/.test(prefix)
+    if (prefix.charAt(0) === '/') {
+      prefix = path.join(this.root, prefix)
+    } else {
+      prefix = path.resolve(this.root, prefix)
+      if (trail)
+        prefix += '/'
     }
-    return this._afterReaddir(f, abs, cb, er, entries)
   }
 
-  fs.readdir(abs, this._afterReaddir.bind(this, f, abs, cb))
+  if (process.platform === 'win32')
+    prefix = prefix.replace(/\\/g, '/')
+
+  // Mark this as a match
+  this._emitMatch(index, prefix)
+  cb()
 }
 
-Glob.prototype._afterReaddir = function (f, abs, cb, er, entries) {
-  assert(this instanceof Glob)
-  if (entries && !er) {
-    this.cache[f] = entries
-    // if we haven't asked to stat everything for suresies, then just
-    // assume that everything in there exists, so we can avoid
-    // having to stat it a second time.  This also gets us one step
-    // further into ELOOP territory.
-    if (!this.mark && !this.stat) {
-      entries.forEach(function (e) {
-        if (f === "/") e = f + e
-        else e = f + "/" + e
-        this.cache[e] = true
-      }, this)
-    }
+// Returns either 'DIR', 'FILE', or false
+Glob.prototype._stat = function (f, cb) {
+  var abs = this._makeAbs(f)
+  var needDir = f.slice(-1) === '/'
+
+  if (f.length > this.maxLength)
+    return cb()
 
-    return cb.call(this, er, entries)
+  if (!this.stat && ownProp(this.cache, abs)) {
+    var c = this.cache[abs]
+
+    if (Array.isArray(c))
+      c = 'DIR'
+
+    // It exists, but maybe not how we need it
+    if (!needDir || c === 'DIR')
+      return cb(null, c)
+
+    if (needDir && c === 'FILE')
+      return cb()
+
+    // otherwise we have to stat, because maybe c=true
+    // if we know it exists, but not what it is.
   }
 
-  // now handle errors, and cache the information
-  if (er) switch (er.code) {
-    case "ENOTDIR": // totally normal. means it *does* exist.
-      this.cache[f] = 1
-      return cb.call(this, er)
-    case "ENOENT": // not terribly unusual
-    case "ELOOP":
-    case "ENAMETOOLONG":
-    case "UNKNOWN":
-      this.cache[f] = false
-      return cb.call(this, er)
-    default: // some unusual error.  Treat as failure.
-      this.cache[f] = false
-      if (this.strict) this.emit("error", er)
-      if (!this.silent) console.error("glob error", er)
-      return cb.call(this, er)
+  var exists
+  var stat = this.statCache[abs]
+  if (stat !== undefined) {
+    if (stat === false)
+      return cb(null, stat)
+    else {
+      var type = stat.isDirectory() ? 'DIR' : 'FILE'
+      if (needDir && type === 'FILE')
+        return cb()
+      else
+        return cb(null, type, stat)
+    }
+  }
+
+  var self = this
+  var statcb = inflight('stat\0' + abs, lstatcb_)
+  if (statcb)
+    fs.lstat(abs, statcb)
+
+  function lstatcb_ (er, lstat) {
+    if (lstat && lstat.isSymbolicLink()) {
+      // If it's a symlink, then treat it as the target, unless
+      // the target does not exist, then treat it as a file.
+      return fs.stat(abs, function (er, stat) {
+        if (er)
+          self._stat2(f, abs, null, lstat, cb)
+        else
+          self._stat2(f, abs, er, stat, cb)
+      })
+    } else {
+      self._stat2(f, abs, er, lstat, cb)
+    }
   }
 }
 
-var isAbsolute = process.platform === "win32" ? absWin : absUnix
+Glob.prototype._stat2 = function (f, abs, er, stat, cb) {
+  if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) {
+    this.statCache[abs] = false
+    return cb()
+  }
+
+  var needDir = f.slice(-1) === '/'
+  this.statCache[abs] = stat
 
-function absWin (p) {
-  if (absUnix(p)) return true
-  // pull off the device/UNC bit from a windows path.
-  // from node's lib/path.js
-  var splitDeviceRe =
-      /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/
-    , result = splitDeviceRe.exec(p)
-    , device = result[1] || ''
-    , isUnc = device && device.charAt(1) !== ':'
-    , isAbsolute = !!result[2] || isUnc // UNC paths are always absolute
+  if (abs.slice(-1) === '/' && stat && !stat.isDirectory())
+    return cb(null, false, stat)
 
-  return isAbsolute
-}
+  var c = true
+  if (stat)
+    c = stat.isDirectory() ? 'DIR' : 'FILE'
+  this.cache[abs] = this.cache[abs] || c
+
+  if (needDir && c === 'FILE')
+    return cb()
 
-function absUnix (p) {
-  return p.charAt(0) === "/" || p === ""
+  return cb(null, c, stat)
 }
diff --git a/make-benchmark-fixture.sh b/make-benchmark-fixture.sh
new file mode 100644
index 0000000..2f394fa
--- /dev/null
+++ b/make-benchmark-fixture.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+tmp=${TMPDIR:-/tmp}
+export CDPATH=
+set -e
+if ! [ -d $tmp/benchmark-fixture ]; then
+  echo Making benchmark fixtures
+  mkdir $tmp/benchmark-fixture
+  cd $tmp/benchmark-fixture
+  dirnames=`echo {0..9}/{0..9}/{0..9}/{0..9}` # 10000 dirs
+  filenames=`echo {0..9}/{0..9}/{0..9}/{0..9}/{0..9}.txt`
+  echo $dirnames | xargs mkdir -p
+  echo $filenames | xargs touch
+fi
diff --git a/oh-my-glob.gif b/oh-my-glob.gif
new file mode 100644
index 0000000..a1568c1
Binary files /dev/null and b/oh-my-glob.gif differ
diff --git a/package.json b/package.json
index 38dc86f..e403b4f 100644
--- a/package.json
+++ b/package.json
@@ -2,31 +2,42 @@
   "author": "Isaac Z. Schlueter <i at izs.me> (http://blog.izs.me/)",
   "name": "glob",
   "description": "a little globber",
-  "version": "4.0.5",
+  "version": "7.1.1",
   "repository": {
     "type": "git",
     "url": "git://github.com/isaacs/node-glob.git"
   },
   "main": "glob.js",
+  "files": [
+    "glob.js",
+    "sync.js",
+    "common.js"
+  ],
   "engines": {
     "node": "*"
   },
-  "optionalDependencies": {
-    "graceful-fs": "^3.0.2"
-  },
   "dependencies": {
+    "fs.realpath": "^1.0.0",
+    "inflight": "^1.0.4",
     "inherits": "2",
-    "minimatch": "^1.0.0",
-    "once": "^1.3.0"
+    "minimatch": "^3.0.2",
+    "once": "^1.3.0",
+    "path-is-absolute": "^1.0.0"
   },
   "devDependencies": {
-    "tap": "~0.4.0",
     "mkdirp": "0",
-    "rimraf": "1"
+    "rimraf": "^2.2.8",
+    "tap": "^7.1.2",
+    "tick": "0.0.6"
   },
   "scripts": {
-    "test": "tap test/*.js",
-    "test-regen": "TEST_REGEN=1 node test/00-setup.js"
+    "prepublish": "npm run benchclean",
+    "profclean": "rm -f v8.log profile.txt",
+    "test": "tap test/*.js --cov",
+    "test-regen": "npm run profclean && TEST_REGEN=1 node test/00-setup.js",
+    "bench": "bash benchmark.sh",
+    "prof": "bash prof.sh && cat profile.txt",
+    "benchclean": "node benchclean.js"
   },
   "license": "ISC"
 }
diff --git a/prof.sh b/prof.sh
new file mode 100644
index 0000000..7fa97fd
--- /dev/null
+++ b/prof.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+export CDPATH=
+set -e
+
+tmp=${TMPDIR:-/tmp}
+bash make-benchmark-fixture.sh
+wd=$PWD
+cd $tmp/benchmark-fixture
+
+node --prof -e '
+  var glob=require(process.argv[1]);
+  glob("**/*.txt", function (er, files) {
+    console.log(files.length)
+  })
+  //console.log(glob.sync("**/*.txt").length);
+  ' "$wd"
+mv *v8.log "$wd/v8.log"
+cd "$wd"
+node-tick-processor > profile.txt
diff --git a/sync.js b/sync.js
new file mode 100644
index 0000000..c952134
--- /dev/null
+++ b/sync.js
@@ -0,0 +1,486 @@
+module.exports = globSync
+globSync.GlobSync = GlobSync
+
+var fs = require('fs')
+var rp = require('fs.realpath')
+var minimatch = require('minimatch')
+var Minimatch = minimatch.Minimatch
+var Glob = require('./glob.js').Glob
+var util = require('util')
+var path = require('path')
+var assert = require('assert')
+var isAbsolute = require('path-is-absolute')
+var common = require('./common.js')
+var alphasort = common.alphasort
+var alphasorti = common.alphasorti
+var setopts = common.setopts
+var ownProp = common.ownProp
+var childrenIgnored = common.childrenIgnored
+var isIgnored = common.isIgnored
+
+function globSync (pattern, options) {
+  if (typeof options === 'function' || arguments.length === 3)
+    throw new TypeError('callback provided to sync glob\n'+
+                        'See: https://github.com/isaacs/node-glob/issues/167')
+
+  return new GlobSync(pattern, options).found
+}
+
+function GlobSync (pattern, options) {
+  if (!pattern)
+    throw new Error('must provide pattern')
+
+  if (typeof options === 'function' || arguments.length === 3)
+    throw new TypeError('callback provided to sync glob\n'+
+                        'See: https://github.com/isaacs/node-glob/issues/167')
+
+  if (!(this instanceof GlobSync))
+    return new GlobSync(pattern, options)
+
+  setopts(this, pattern, options)
+
+  if (this.noprocess)
+    return this
+
+  var n = this.minimatch.set.length
+  this.matches = new Array(n)
+  for (var i = 0; i < n; i ++) {
+    this._process(this.minimatch.set[i], i, false)
+  }
+  this._finish()
+}
+
+GlobSync.prototype._finish = function () {
+  assert(this instanceof GlobSync)
+  if (this.realpath) {
+    var self = this
+    this.matches.forEach(function (matchset, index) {
+      var set = self.matches[index] = Object.create(null)
+      for (var p in matchset) {
+        try {
+          p = self._makeAbs(p)
+          var real = rp.realpathSync(p, self.realpathCache)
+          set[real] = true
+        } catch (er) {
+          if (er.syscall === 'stat')
+            set[self._makeAbs(p)] = true
+          else
+            throw er
+        }
+      }
+    })
+  }
+  common.finish(this)
+}
+
+
+GlobSync.prototype._process = function (pattern, index, inGlobStar) {
+  assert(this instanceof GlobSync)
+
+  // Get the first [n] parts of pattern that are all strings.
+  var n = 0
+  while (typeof pattern[n] === 'string') {
+    n ++
+  }
+  // now n is the index of the first one that is *not* a string.
+
+  // See if there's anything else
+  var prefix
+  switch (n) {
+    // if not, then this is rather simple
+    case pattern.length:
+      this._processSimple(pattern.join('/'), index)
+      return
+
+    case 0:
+      // pattern *starts* with some non-trivial item.
+      // going to readdir(cwd), but not include the prefix in matches.
+      prefix = null
+      break
+
+    default:
+      // pattern has some string bits in the front.
+      // whatever it starts with, whether that's 'absolute' like /foo/bar,
+      // or 'relative' like '../baz'
+      prefix = pattern.slice(0, n).join('/')
+      break
+  }
+
+  var remain = pattern.slice(n)
+
+  // get the list of entries.
+  var read
+  if (prefix === null)
+    read = '.'
+  else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) {
+    if (!prefix || !isAbsolute(prefix))
+      prefix = '/' + prefix
+    read = prefix
+  } else
+    read = prefix
+
+  var abs = this._makeAbs(read)
+
+  //if ignored, skip processing
+  if (childrenIgnored(this, read))
+    return
+
+  var isGlobStar = remain[0] === minimatch.GLOBSTAR
+  if (isGlobStar)
+    this._processGlobStar(prefix, read, abs, remain, index, inGlobStar)
+  else
+    this._processReaddir(prefix, read, abs, remain, index, inGlobStar)
+}
+
+
+GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) {
+  var entries = this._readdir(abs, inGlobStar)
+
+  // if the abs isn't a dir, then nothing can match!
+  if (!entries)
+    return
+
+  // It will only match dot entries if it starts with a dot, or if
+  // dot is set.  Stuff like @(.foo|.bar) isn't allowed.
+  var pn = remain[0]
+  var negate = !!this.minimatch.negate
+  var rawGlob = pn._glob
+  var dotOk = this.dot || rawGlob.charAt(0) === '.'
+
+  var matchedEntries = []
+  for (var i = 0; i < entries.length; i++) {
+    var e = entries[i]
+    if (e.charAt(0) !== '.' || dotOk) {
+      var m
+      if (negate && !prefix) {
+        m = !e.match(pn)
+      } else {
+        m = e.match(pn)
+      }
+      if (m)
+        matchedEntries.push(e)
+    }
+  }
+
+  var len = matchedEntries.length
+  // If there are no matched entries, then nothing matches.
+  if (len === 0)
+    return
+
+  // if this is the last remaining pattern bit, then no need for
+  // an additional stat *unless* the user has specified mark or
+  // stat explicitly.  We know they exist, since readdir returned
+  // them.
+
+  if (remain.length === 1 && !this.mark && !this.stat) {
+    if (!this.matches[index])
+      this.matches[index] = Object.create(null)
+
+    for (var i = 0; i < len; i ++) {
+      var e = matchedEntries[i]
+      if (prefix) {
+        if (prefix.slice(-1) !== '/')
+          e = prefix + '/' + e
+        else
+          e = prefix + e
+      }
+
+      if (e.charAt(0) === '/' && !this.nomount) {
+        e = path.join(this.root, e)
+      }
+      this._emitMatch(index, e)
+    }
+    // This was the last one, and no stats were needed
+    return
+  }
+
+  // now test all matched entries as stand-ins for that part
+  // of the pattern.
+  remain.shift()
+  for (var i = 0; i < len; i ++) {
+    var e = matchedEntries[i]
+    var newPattern
+    if (prefix)
+      newPattern = [prefix, e]
+    else
+      newPattern = [e]
+    this._process(newPattern.concat(remain), index, inGlobStar)
+  }
+}
+
+
+GlobSync.prototype._emitMatch = function (index, e) {
+  if (isIgnored(this, e))
+    return
+
+  var abs = this._makeAbs(e)
+
+  if (this.mark)
+    e = this._mark(e)
+
+  if (this.absolute) {
+    e = abs
+  }
+
+  if (this.matches[index][e])
+    return
+
+  if (this.nodir) {
+    var c = this.cache[abs]
+    if (c === 'DIR' || Array.isArray(c))
+      return
+  }
+
+  this.matches[index][e] = true
+
+  if (this.stat)
+    this._stat(e)
+}
+
+
+GlobSync.prototype._readdirInGlobStar = function (abs) {
+  // follow all symlinked directories forever
+  // just proceed as if this is a non-globstar situation
+  if (this.follow)
+    return this._readdir(abs, false)
+
+  var entries
+  var lstat
+  var stat
+  try {
+    lstat = fs.lstatSync(abs)
+  } catch (er) {
+    if (er.code === 'ENOENT') {
+      // lstat failed, doesn't exist
+      return null
+    }
+  }
+
+  var isSym = lstat && lstat.isSymbolicLink()
+  this.symlinks[abs] = isSym
+
+  // If it's not a symlink or a dir, then it's definitely a regular file.
+  // don't bother doing a readdir in that case.
+  if (!isSym && lstat && !lstat.isDirectory())
+    this.cache[abs] = 'FILE'
+  else
+    entries = this._readdir(abs, false)
+
+  return entries
+}
+
+GlobSync.prototype._readdir = function (abs, inGlobStar) {
+  var entries
+
+  if (inGlobStar && !ownProp(this.symlinks, abs))
+    return this._readdirInGlobStar(abs)
+
+  if (ownProp(this.cache, abs)) {
+    var c = this.cache[abs]
+    if (!c || c === 'FILE')
+      return null
+
+    if (Array.isArray(c))
+      return c
+  }
+
+  try {
+    return this._readdirEntries(abs, fs.readdirSync(abs))
+  } catch (er) {
+    this._readdirError(abs, er)
+    return null
+  }
+}
+
+GlobSync.prototype._readdirEntries = function (abs, entries) {
+  // if we haven't asked to stat everything, then just
+  // assume that everything in there exists, so we can avoid
+  // having to stat it a second time.
+  if (!this.mark && !this.stat) {
+    for (var i = 0; i < entries.length; i ++) {
+      var e = entries[i]
+      if (abs === '/')
+        e = abs + e
+      else
+        e = abs + '/' + e
+      this.cache[e] = true
+    }
+  }
+
+  this.cache[abs] = entries
+
+  // mark and cache dir-ness
+  return entries
+}
+
+GlobSync.prototype._readdirError = function (f, er) {
+  // handle errors, and cache the information
+  switch (er.code) {
+    case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205
+    case 'ENOTDIR': // totally normal. means it *does* exist.
+      var abs = this._makeAbs(f)
+      this.cache[abs] = 'FILE'
+      if (abs === this.cwdAbs) {
+        var error = new Error(er.code + ' invalid cwd ' + this.cwd)
+        error.path = this.cwd
+        error.code = er.code
+        throw error
+      }
+      break
+
+    case 'ENOENT': // not terribly unusual
+    case 'ELOOP':
+    case 'ENAMETOOLONG':
+    case 'UNKNOWN':
+      this.cache[this._makeAbs(f)] = false
+      break
+
+    default: // some unusual error.  Treat as failure.
+      this.cache[this._makeAbs(f)] = false
+      if (this.strict)
+        throw er
+      if (!this.silent)
+        console.error('glob error', er)
+      break
+  }
+}
+
+GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) {
+
+  var entries = this._readdir(abs, inGlobStar)
+
+  // no entries means not a dir, so it can never have matches
+  // foo.txt/** doesn't match foo.txt
+  if (!entries)
+    return
+
+  // test without the globstar, and with every child both below
+  // and replacing the globstar.
+  var remainWithoutGlobStar = remain.slice(1)
+  var gspref = prefix ? [ prefix ] : []
+  var noGlobStar = gspref.concat(remainWithoutGlobStar)
+
+  // the noGlobStar pattern exits the inGlobStar state
+  this._process(noGlobStar, index, false)
+
+  var len = entries.length
+  var isSym = this.symlinks[abs]
+
+  // If it's a symlink, and we're in a globstar, then stop
+  if (isSym && inGlobStar)
+    return
+
+  for (var i = 0; i < len; i++) {
+    var e = entries[i]
+    if (e.charAt(0) === '.' && !this.dot)
+      continue
+
+    // these two cases enter the inGlobStar state
+    var instead = gspref.concat(entries[i], remainWithoutGlobStar)
+    this._process(instead, index, true)
+
+    var below = gspref.concat(entries[i], remain)
+    this._process(below, index, true)
+  }
+}
+
+GlobSync.prototype._processSimple = function (prefix, index) {
+  // XXX review this.  Shouldn't it be doing the mounting etc
+  // before doing stat?  kinda weird?
+  var exists = this._stat(prefix)
+
+  if (!this.matches[index])
+    this.matches[index] = Object.create(null)
+
+  // If it doesn't exist, then just mark the lack of results
+  if (!exists)
+    return
+
+  if (prefix && isAbsolute(prefix) && !this.nomount) {
+    var trail = /[\/\\]$/.test(prefix)
+    if (prefix.charAt(0) === '/') {
+      prefix = path.join(this.root, prefix)
+    } else {
+      prefix = path.resolve(this.root, prefix)
+      if (trail)
+        prefix += '/'
+    }
+  }
+
+  if (process.platform === 'win32')
+    prefix = prefix.replace(/\\/g, '/')
+
+  // Mark this as a match
+  this._emitMatch(index, prefix)
+}
+
+// Returns either 'DIR', 'FILE', or false
+GlobSync.prototype._stat = function (f) {
+  var abs = this._makeAbs(f)
+  var needDir = f.slice(-1) === '/'
+
+  if (f.length > this.maxLength)
+    return false
+
+  if (!this.stat && ownProp(this.cache, abs)) {
+    var c = this.cache[abs]
+
+    if (Array.isArray(c))
+      c = 'DIR'
+
+    // It exists, but maybe not how we need it
+    if (!needDir || c === 'DIR')
+      return c
+
+    if (needDir && c === 'FILE')
+      return false
+
+    // otherwise we have to stat, because maybe c=true
+    // if we know it exists, but not what it is.
+  }
+
+  var exists
+  var stat = this.statCache[abs]
+  if (!stat) {
+    var lstat
+    try {
+      lstat = fs.lstatSync(abs)
+    } catch (er) {
+      if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) {
+        this.statCache[abs] = false
+        return false
+      }
+    }
+
+    if (lstat && lstat.isSymbolicLink()) {
+      try {
+        stat = fs.statSync(abs)
+      } catch (er) {
+        stat = lstat
+      }
+    } else {
+      stat = lstat
+    }
+  }
+
+  this.statCache[abs] = stat
+
+  var c = true
+  if (stat)
+    c = stat.isDirectory() ? 'DIR' : 'FILE'
+
+  this.cache[abs] = this.cache[abs] || c
+
+  if (needDir && c === 'FILE')
+    return false
+
+  return c
+}
+
+GlobSync.prototype._mark = function (p) {
+  return common.mark(this, p)
+}
+
+GlobSync.prototype._makeAbs = function (f) {
+  return common.makeAbs(this, f)
+}
diff --git a/test/00-setup.js b/test/00-setup.js
index 245afaf..df9d523 100644
--- a/test/00-setup.js
+++ b/test/00-setup.js
@@ -1,6 +1,7 @@
 // just a little pre-run script to set up the fixtures.
 // zz-finish cleans it up
 
+require("./global-leakage.js")
 var mkdirp = require("mkdirp")
 var path = require("path")
 var i = 0
@@ -8,6 +9,8 @@ var tap = require("tap")
 var fs = require("fs")
 var rimraf = require("rimraf")
 
+var fixtureDir = path.resolve(__dirname, 'fixtures')
+
 var files =
 [ "a/.abcdef/x/y/z/a"
 , "a/abcdef/g/h"
@@ -16,26 +19,27 @@ var files =
 , "a/bc/e/f"
 , "a/c/d/c/b"
 , "a/cb/e/f"
+, "a/x/.y/b"
+, "a/z/.y/b"
 ]
 
-var symlinkTo = path.resolve(__dirname, "a/symlink/a/b/c")
+var symlinkTo = path.resolve(fixtureDir, "a/symlink/a/b/c")
 var symlinkFrom = "../.."
 
 files = files.map(function (f) {
-  return path.resolve(__dirname, f)
+  return path.resolve(fixtureDir, f)
 })
 
 tap.test("remove fixtures", function (t) {
-  rimraf(path.resolve(__dirname, "a"), function (er) {
-    t.ifError(er, "remove fixtures")
-    t.end()
-  })
+  rimraf.sync(fixtureDir)
+  t.end()
 })
 
 files.forEach(function (f) {
   tap.test(f, function (t) {
+    f = path.resolve(fixtureDir, f)
     var d = path.dirname(f)
-    mkdirp(d, 0755, function (er) {
+    mkdirp(d, '0755', function (er) {
       if (er) {
         t.fail(er)
         return t.bailout()
@@ -51,13 +55,11 @@ files.forEach(function (f) {
 if (process.platform !== "win32") {
   tap.test("symlinky", function (t) {
     var d = path.dirname(symlinkTo)
-    console.error("mkdirp", d)
-    mkdirp(d, 0755, function (er) {
-      t.ifError(er)
-      fs.symlink(symlinkFrom, symlinkTo, "dir", function (er) {
-        t.ifError(er, "make symlink")
-        t.end()
-      })
+    mkdirp(d, '0755', function (er) {
+      if (er)
+        throw er
+      fs.symlinkSync(symlinkFrom, symlinkTo, "dir")
+      t.end()
     })
   })
 }
@@ -74,7 +76,6 @@ if (process.platform !== "win32") {
   })
 })
 
-
 // generate the bash pattern test-fixtures if possible
 if (process.platform === "win32" || !process.env.TEST_REGEN) {
   console.error("Windows, or TEST_REGEN unset.  Using cached fixtures.")
@@ -85,34 +86,37 @@ var spawn = require("child_process").spawn;
 var globs =
   // put more patterns here.
   // anything that would be directly in / should be in /tmp/glob-test
-  ["test/a/*/+(c|g)/./d"
-  ,"test/a/**/[cg]/../[cg]"
-  ,"test/a/{b,c,d,e,f}/**/g"
-  ,"test/a/b/**"
-  ,"test/**/g"
-  ,"test/a/abc{fed,def}/g/h"
-  ,"test/a/abc{fed/g,def}/**/"
-  ,"test/a/abc{fed/g,def}/**///**/"
-  ,"test/**/a/**/"
-  ,"test/+(a|b|c)/a{/,bc*}/**"
-  ,"test/*/*/*/f"
-  ,"test/**/f"
-  ,"test/a/symlink/a/b/c/a/b/c/a/b/c//a/b/c////a/b/c/**/b/c/**"
+  ["a/*/+(c|g)/./d"
+  ,"a/**/[cg]/../[cg]"
+  ,"a/{b,c,d,e,f}/**/g"
+  ,"a/b/**"
+  ,"**/g"
+  ,"a/abc{fed,def}/g/h"
+  ,"a/abc{fed/g,def}/**/"
+  ,"a/abc{fed/g,def}/**///**/"
+  ,"**/a/**/"
+  ,"+(a|b|c)/a{/,bc*}/**"
+  ,"*/*/*/f"
+  ,"**/f"
+  ,"a/symlink/a/b/c/a/b/c/a/b/c//a/b/c////a/b/c/**/b/c/**"
   ,"{./*/*,/tmp/glob-test/*}"
   ,"{/tmp/glob-test/*,*}" // evil owl face!  how you taunt me!
-  ,"test/a/!(symlink)/**"
+  ,"a/!(symlink)/**"
+  ,"a/symlink/a/**/*"
   ]
 var bashOutput = {}
 var fs = require("fs")
 
 globs.forEach(function (pattern) {
   tap.test("generate fixture " + pattern, function (t) {
-    var cmd = "shopt -s globstar && " +
-              "shopt -s extglob && " +
-              "shopt -s nullglob && " +
-              // "shopt >&2; " +
-              "eval \'for i in " + pattern + "; do echo $i; done\'"
-    var cp = spawn("bash", ["-c", cmd], { cwd: path.dirname(__dirname) })
+    var opts = [
+      "-O", "globstar",
+      "-O", "extglob",
+      "-O", "nullglob",
+      "-c",
+      "for i in " + pattern + "; do echo $i; done"
+    ]
+    var cp = spawn("bash", opts, { cwd: fixtureDir })
     var out = []
     cp.stdout.on("data", function (c) {
       out.push(c)
diff --git a/test/abort.js b/test/abort.js
new file mode 100644
index 0000000..1aeb58f
--- /dev/null
+++ b/test/abort.js
@@ -0,0 +1,19 @@
+require("./global-leakage.js")
+var test = require("tap").test
+var glob = require('../')
+var assert = require("assert")
+var fs = require("fs")
+process.chdir(__dirname)
+
+test("abort prevents any action", function (t) {
+  glob("a/**").abort()
+  glob("a/").abort()
+  glob("a/b/*").abort()
+
+  glob.Glob.prototype.emit = fs.readdir = fs.stat = fs.lstat = assert.fail
+
+  setTimeout(function () {
+    t.pass("if it gets here then it worked")
+    t.end()
+  }, 100)
+})
diff --git a/test/absolute.js b/test/absolute.js
new file mode 100644
index 0000000..704caa9
--- /dev/null
+++ b/test/absolute.js
@@ -0,0 +1,49 @@
+require('./global-leakage.js')
+var t = require('tap')
+var glob = require('../')
+var common = require('../common.js')
+var pattern = 'a/b/**';
+var bashResults = require('./bash-results.json')
+var isAbsolute = require('path-is-absolute')
+process.chdir(__dirname + '/fixtures')
+
+t.Test.prototype.addAssert('isAbsolute', 1, function (file, message, extra) {
+  extra.found = file
+  return this.ok(isAbsolute(file), message || 'must be absolute', extra)
+})
+
+var marks = [ true, false ]
+marks.forEach(function (mark) {
+  t.test('mark=' + mark, function (t) {
+    t.plan(2)
+
+    t.test('Emits absolute matches if option set', function (t) {
+      var g = new glob.Glob(pattern, { absolute: true })
+
+      var matchCount = 0
+      g.on('match', function (m) {
+        t.isAbsolute(m)
+        matchCount++
+      })
+
+      g.on('end', function (results) {
+        t.equal(matchCount, bashResults[pattern].length, 'must match all files')
+        t.equal(results.length, bashResults[pattern].length, 'must match all files')
+        results.forEach(function (m) {
+          t.isAbsolute(m)
+        })
+        t.end()
+      })
+    })
+
+    t.test('returns absolute results synchronously', function (t) {
+      var results = glob.sync(pattern, { absolute: true })
+
+      t.equal(results.length, bashResults[pattern].length, 'must match all files')
+      results.forEach(function (m) {
+        t.ok(isAbsolute(m), 'must be absolute', { found: m })
+      })
+      t.end()
+    })
+  })
+})
diff --git a/test/bash-comparison.js b/test/bash-comparison.js
index 239ed1a..296cc51 100644
--- a/test/bash-comparison.js
+++ b/test/bash-comparison.js
@@ -1,15 +1,29 @@
 // basic test
 // show that it does the same thing by default as the shell.
+require("./global-leakage.js")
 var tap = require("tap")
-, child_process = require("child_process")
-, bashResults = require("./bash-results.json")
-, globs = Object.keys(bashResults)
-, glob = require("../")
-, path = require("path")
+var child_process = require("child_process")
+var bashResults = require("./bash-results.json")
+var globs = Object.keys(bashResults)
+var glob = require("../")
+var path = require("path")
+var isAbsolute = require("path-is-absolute")
 
 // run from the root of the project
 // this is usually where you're at anyway, but be sure.
-process.chdir(path.resolve(__dirname, ".."))
+var root = path.dirname(__dirname)
+var fixtures = path.resolve(__dirname, 'fixtures')
+process.chdir(fixtures)
+
+function cacheCheck(g, t) {
+  // verify that path cache keys are all absolute
+  var caches = [ 'cache', 'statCache', 'symlinks' ]
+  caches.forEach(function (c) {
+    Object.keys(g[c]).forEach(function (p) {
+      t.ok(isAbsolute(p), p + ' should be absolute')
+    })
+  })
+}
 
 function alphasort (a, b) {
   a = a.toLowerCase()
@@ -22,19 +36,21 @@ globs.forEach(function (pattern) {
   // anything regarding the symlink thing will fail on windows, so just skip it
   if (process.platform === "win32" &&
       expect.some(function (m) {
-        return /\/symlink\//.test(m)
+        return /\bsymlink\b/.test(m)
       }))
     return
 
   tap.test(pattern, function (t) {
-    glob(pattern, function (er, matches) {
+    var g = glob(pattern, function (er, matches) {
       if (er)
         throw er
 
       // sort and unmark, just to match the shell results
       matches = cleanResults(matches)
-
       t.deepEqual(matches, expect, pattern)
+
+      // verify that path cache keys are all absolute
+      cacheCheck(g, t)
       t.end()
     })
   })
@@ -42,7 +58,7 @@ globs.forEach(function (pattern) {
   tap.test(pattern + " sync", function (t) {
     var matches = cleanResults(glob.sync(pattern))
 
-    t.deepEqual(matches, expect, "should match shell")
+    t.deepEqual(matches, expect, "should match shell (sync)")
     t.end()
   })
 })
@@ -55,9 +71,9 @@ function cleanResults (m) {
   }).sort(alphasort).reduce(function (set, f) {
     if (f !== set[set.length - 1]) set.push(f)
     return set
-  }, []).sort(alphasort).map(function (f) {
+  }, []).map(function (f) {
     // de-windows
     return (process.platform !== 'win32') ? f
            : f.replace(/^[a-zA-Z]:[\/\\]+/, '/').replace(/[\\\/]+/g, '/')
-  })
+  }).sort(alphasort)
 }
diff --git a/test/bash-results.json b/test/bash-results.json
index 8223d06..a833af7 100644
--- a/test/bash-results.json
+++ b/test/bash-results.json
@@ -1,313 +1,96 @@
 {
-  "test/a/*/+(c|g)/./d": [
-    "test/a/b/c/./d"
+  "a/*/+(c|g)/./d": [
+    "a/b/c/./d"
   ],
-  "test/a/**/[cg]/../[cg]": [
-    "test/a/abcdef/g/../g",
-    "test/a/abcfed/g/../g",
-    "test/a/b/c/../c",
-    "test/a/c/../c",
-    "test/a/c/d/c/../c",
-    "test/a/symlink/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c"
+  "a/**/[cg]/../[cg]": [
+    "a/abcdef/g/../g",
+    "a/abcfed/g/../g",
+    "a/b/c/../c",
+    "a/c/../c",
+    "a/c/d/c/../c",
+    "a/symlink/a/b/c/../c"
   ],
-  "test/a/{b,c,d,e,f}/**/g": [],
-  "test/a/b/**": [
-    "test/a/b",
-    "test/a/b/c",
-    "test/a/b/c/d"
+  "a/{b,c,d,e,f}/**/g": [],
+  "a/b/**": [
+    "a/b",
+    "a/b/c",
+    "a/b/c/d"
   ],
-  "test/**/g": [
-    "test/a/abcdef/g",
-    "test/a/abcfed/g"
+  "**/g": [
+    "a/abcdef/g",
+    "a/abcfed/g"
   ],
-  "test/a/abc{fed,def}/g/h": [
-    "test/a/abcdef/g/h",
-    "test/a/abcfed/g/h"
+  "a/abc{fed,def}/g/h": [
+    "a/abcdef/g/h",
+    "a/abcfed/g/h"
   ],
-  "test/a/abc{fed/g,def}/**/": [
-    "test/a/abcdef",
-    "test/a/abcdef/g",
-    "test/a/abcfed/g"
+  "a/abc{fed/g,def}/**/": [
+    "a/abcdef",
+    "a/abcdef/g",
+    "a/abcfed/g"
   ],
-  "test/a/abc{fed/g,def}/**///**/": [
-    "test/a/abcdef",
-    "test/a/abcdef/g",
-    "test/a/abcfed/g"
+  "a/abc{fed/g,def}/**///**/": [
+    "a/abcdef",
+    "a/abcdef/g",
+    "a/abcfed/g"
   ],
-  "test/**/a/**/": [
-    "test/a",
-    "test/a/abcdef",
-    "test/a/abcdef/g",
-    "test/a/abcfed",
-    "test/a/abcfed/g",
-    "test/a/b",
-    "test/a/b/c",
-    "test/a/bc",
-    "test/a/bc/e",
-    "test/a/c",
-    "test/a/c/d",
-    "test/a/c/d/c",
-    "test/a/cb",
-    "test/a/cb/e",
-    "test/a/symlink",
-    "test/a/symlink/a",
-    "test/a/symlink/a/b",
-    "test/a/symlink/a/b/c",
-    "test/a/symlink/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b"
+  "**/a/**/": [
+    "a",
+    "a/abcdef",
+    "a/abcdef/g",
+    "a/abcfed",
+    "a/abcfed/g",
+    "a/b",
+    "a/b/c",
+    "a/bc",
+    "a/bc/e",
+    "a/c",
+    "a/c/d",
+    "a/c/d/c",
+    "a/cb",
+    "a/cb/e",
+    "a/symlink",
+    "a/symlink/a",
+    "a/symlink/a/b",
+    "a/symlink/a/b/c",
+    "a/symlink/a/b/c/a",
+    "a/symlink/a/b/c/a/b",
+    "a/symlink/a/b/c/a/b/c",
+    "a/x",
+    "a/z"
   ],
-  "test/+(a|b|c)/a{/,bc*}/**": [
-    "test/a/abcdef",
-    "test/a/abcdef/g",
-    "test/a/abcdef/g/h",
-    "test/a/abcfed",
-    "test/a/abcfed/g",
-    "test/a/abcfed/g/h"
+  "+(a|b|c)/a{/,bc*}/**": [
+    "a/abcdef",
+    "a/abcdef/g",
+    "a/abcdef/g/h",
+    "a/abcfed",
+    "a/abcfed/g",
+    "a/abcfed/g/h"
   ],
-  "test/*/*/*/f": [
-    "test/a/bc/e/f",
-    "test/a/cb/e/f"
+  "*/*/*/f": [
+    "a/bc/e/f",
+    "a/cb/e/f"
   ],
-  "test/**/f": [
-    "test/a/bc/e/f",
-    "test/a/cb/e/f"
+  "**/f": [
+    "a/bc/e/f",
+    "a/cb/e/f"
   ],
-  "test/a/symlink/a/b/c/a/b/c/a/b/c//a/b/c////a/b/c/**/b/c/**": [
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
-    "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c"
+  "a/symlink/a/b/c/a/b/c/a/b/c//a/b/c////a/b/c/**/b/c/**": [
+    "a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c",
+    "a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a",
+    "a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b",
+    "a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c"
   ],
   "{./*/*,/tmp/glob-test/*}": [
-    "./examples/g.js",
-    "./examples/usr-local.js",
-    "./node_modules/graceful-fs",
-    "./node_modules/inherits",
-    "./node_modules/minimatch",
-    "./node_modules/mkdirp",
-    "./node_modules/once",
-    "./node_modules/rimraf",
-    "./node_modules/tap",
-    "./test/00-setup.js",
-    "./test/a",
-    "./test/bash-comparison.js",
-    "./test/bash-results.json",
-    "./test/cwd-test.js",
-    "./test/empty-set.js",
-    "./test/error-callback.js",
-    "./test/globstar-match.js",
-    "./test/mark.js",
-    "./test/negation-test.js",
-    "./test/new-glob-optional-options.js",
-    "./test/nocase-nomagic.js",
-    "./test/pause-resume.js",
-    "./test/readme-issue.js",
-    "./test/root-nomount.js",
-    "./test/root.js",
-    "./test/stat.js",
-    "./test/zz-cleanup.js",
+    "./a/abcdef",
+    "./a/abcfed",
+    "./a/b",
+    "./a/bc",
+    "./a/c",
+    "./a/cb",
+    "./a/symlink",
+    "./a/x",
+    "./a/z",
     "/tmp/glob-test/asdf",
     "/tmp/glob-test/bar",
     "/tmp/glob-test/baz",
@@ -324,33 +107,34 @@
     "/tmp/glob-test/quux",
     "/tmp/glob-test/qwer",
     "/tmp/glob-test/rewq",
-    "examples",
-    "glob.js",
-    "LICENSE",
-    "node_modules",
-    "package.json",
-    "README.md",
-    "test"
+    "a"
   ],
-  "test/a/!(symlink)/**": [
-    "test/a/abcdef",
-    "test/a/abcdef/g",
-    "test/a/abcdef/g/h",
-    "test/a/abcfed",
-    "test/a/abcfed/g",
-    "test/a/abcfed/g/h",
-    "test/a/b",
-    "test/a/b/c",
-    "test/a/b/c/d",
-    "test/a/bc",
-    "test/a/bc/e",
-    "test/a/bc/e/f",
-    "test/a/c",
-    "test/a/c/d",
-    "test/a/c/d/c",
-    "test/a/c/d/c/b",
-    "test/a/cb",
-    "test/a/cb/e",
-    "test/a/cb/e/f"
+  "a/!(symlink)/**": [
+    "a/abcdef",
+    "a/abcdef/g",
+    "a/abcdef/g/h",
+    "a/abcfed",
+    "a/abcfed/g",
+    "a/abcfed/g/h",
+    "a/b",
+    "a/b/c",
+    "a/b/c/d",
+    "a/bc",
+    "a/bc/e",
+    "a/bc/e/f",
+    "a/c",
+    "a/c/d",
+    "a/c/d/c",
+    "a/c/d/c/b",
+    "a/cb",
+    "a/cb/e",
+    "a/cb/e/f",
+    "a/x",
+    "a/z"
+  ],
+  "a/symlink/a/**/*": [
+    "a/symlink/a/b",
+    "a/symlink/a/b/c",
+    "a/symlink/a/b/c/a"
   ]
 }
diff --git a/test/broken-symlink.js b/test/broken-symlink.js
new file mode 100644
index 0000000..4648d38
--- /dev/null
+++ b/test/broken-symlink.js
@@ -0,0 +1,82 @@
+var fs = require('fs')
+var test = require('tap').test
+var glob = require('../')
+var mkdirp = require('mkdirp')
+
+if (process.platform === 'win32')
+  return require('tap').plan(0, 'skip on windows')
+
+process.chdir(__dirname)
+
+var link = 'fixtures/a/broken-link/link'
+
+var patterns = [
+  'fixtures/a/broken-link/*',
+  'fixtures/a/broken-link/**',
+  'fixtures/a/broken-link/**/link',
+  'fixtures/a/broken-link/**/*',
+  'fixtures/a/broken-link/link',
+  'fixtures/a/broken-link/{link,asdf}',
+  'fixtures/a/broken-link/+(link|asdf)',
+  'fixtures/a/broken-link/!(asdf)'
+]
+
+var opts = [
+  null,
+  { nonull: true },
+  { mark: true },
+  { stat: true },
+  { follow: true }
+]
+
+test('set up broken symlink', function (t) {
+  cleanup()
+  mkdirp.sync('fixtures/a/broken-link')
+  fs.symlinkSync('this-does-not-exist', 'fixtures/a/broken-link/link')
+  t.end()
+})
+
+test('async test', function (t) {
+  var count = patterns.length * opts.length
+  t.plan(patterns.length)
+  patterns.forEach(function (pattern) {
+    t.test(pattern, function (t) {
+      t.plan(opts.length)
+
+      opts.forEach(function (opt) {
+        glob(pattern, opt, cb(opt))
+      })
+
+      function cb (opt) { return function (er, res) {
+        if (er)
+          throw er
+        var msg = pattern + ' ' + JSON.stringify(opt)
+        t.notEqual(res.indexOf(link), -1, msg)
+      }}
+    })
+  })
+})
+
+test('sync test', function (t) {
+  t.plan(patterns.length)
+  patterns.forEach(function (pattern) {
+    t.test(pattern, function (t) {
+      t.plan(opts.length)
+
+      opts.forEach(function (opt) {
+        var res = glob.sync(pattern, opt)
+        t.notEqual(res.indexOf(link), -1, 'opt=' + JSON.stringify(opt))
+      })
+    })
+  })
+})
+
+test('cleanup', function (t) {
+  cleanup()
+  t.end()
+})
+
+function cleanup () {
+  try { fs.unlinkSync('fixtures/a/broken-link/link') } catch (e) {}
+  try { fs.rmdirSync('fixtures/a/broken-link') } catch (e) {}
+}
diff --git a/test/cwd-test.js b/test/cwd-test.js
index 352c27e..a57ddd0 100644
--- a/test/cwd-test.js
+++ b/test/cwd-test.js
@@ -1,55 +1,91 @@
+require("./global-leakage.js")
 var tap = require("tap")
 
 var origCwd = process.cwd()
-process.chdir(__dirname)
+process.chdir(__dirname + '/fixtures')
+var path = require('path')
+var isAbsolute = require('path-is-absolute')
+var glob = require('../')
+
+function cacheCheck(g, t) {
+  // verify that path cache keys are all absolute
+  var caches = [ 'cache', 'statCache', 'symlinks' ]
+  caches.forEach(function (c) {
+    Object.keys(g[c]).forEach(function (p) {
+      t.ok(isAbsolute(p), p + ' should be absolute')
+    })
+  })
+}
 
 tap.test("changing cwd and searching for **/d", function (t) {
-  var glob = require('../')
-  var path = require('path')
   t.test('.', function (t) {
-    glob('**/d', function (er, matches) {
+    var g = glob('**/d', function (er, matches) {
       t.ifError(er)
       t.like(matches, [ 'a/b/c/d', 'a/c/d' ])
+      cacheCheck(g, t)
       t.end()
     })
   })
 
   t.test('a', function (t) {
-    glob('**/d', {cwd:path.resolve('a')}, function (er, matches) {
+    var g = glob('**/d', {cwd:path.resolve('a')}, function (er, matches) {
       t.ifError(er)
       t.like(matches, [ 'b/c/d', 'c/d' ])
+      cacheCheck(g, t)
       t.end()
     })
   })
 
   t.test('a/b', function (t) {
-    glob('**/d', {cwd:path.resolve('a/b')}, function (er, matches) {
+    var g = glob('**/d', {cwd:path.resolve('a/b')}, function (er, matches) {
       t.ifError(er)
       t.like(matches, [ 'c/d' ])
+      cacheCheck(g, t)
       t.end()
     })
   })
 
   t.test('a/b/', function (t) {
-    glob('**/d', {cwd:path.resolve('a/b/')}, function (er, matches) {
+    var g = glob('**/d', {cwd:path.resolve('a/b/')}, function (er, matches) {
       t.ifError(er)
       t.like(matches, [ 'c/d' ])
+      cacheCheck(g, t)
       t.end()
     })
   })
 
   t.test('.', function (t) {
-    glob('**/d', {cwd: process.cwd()}, function (er, matches) {
+    var g = glob('**/d', {cwd: process.cwd()}, function (er, matches) {
       t.ifError(er)
       t.like(matches, [ 'a/b/c/d', 'a/c/d' ])
+      cacheCheck(g, t)
       t.end()
     })
   })
 
-  t.test('cd -', function (t) {
-    process.chdir(origCwd)
+  t.end()
+})
+
+tap.test('non-dir cwd should raise error', function (t) {
+  var notdir = 'a/b/c/d'
+  var notdirRE = /a[\\\/]b[\\\/]c[\\\/]d/
+  var abs = path.resolve(notdir)
+  var expect = new Error('ENOTDIR invalid cwd ' + abs)
+  expect.code = 'ENOTDIR'
+  expect.path = notdirRE
+  expect.stack = undefined
+  var msg = 'raise error when cwd is not a dir'
+
+  t.throws(function () {
+    glob.sync('*', { cwd: notdir })
+  }, expect)
+  glob('*', { cwd: notdir }, function (er, results) {
+    t.match(er, expect)
     t.end()
   })
+})
 
+tap.test('cd -', function (t) {
+  process.chdir(origCwd)
   t.end()
 })
diff --git a/test/empty-set.js b/test/empty-set.js
index 3b627b0..75956fb 100644
--- a/test/empty-set.js
+++ b/test/empty-set.js
@@ -1,3 +1,4 @@
+require("./global-leakage.js")
 var test = require('tap').test
 var glob = require("../glob.js")
 
diff --git a/test/enotsup.js b/test/enotsup.js
new file mode 100644
index 0000000..5de3b62
--- /dev/null
+++ b/test/enotsup.js
@@ -0,0 +1,65 @@
+var fs = require('fs')
+var readdir = fs.readdir
+var readdirSync = fs.readdirSync
+var sawAsyncENOTSUP = false
+var sawSyncENOTSUP = false
+
+var path = require('path')
+var fixtureDir = path.resolve(__dirname, 'fixtures')
+var allowedDirs = [
+  path.resolve(fixtureDir, 'a'),
+  path.resolve(fixtureDir, 'a', 'abcdef'),
+  path.resolve(fixtureDir, 'a', 'abcfed')
+]
+
+fs.readdirSync = function (p) {
+  if (allowedDirs.indexOf(path.resolve(p)) === -1 &&
+      !p.match(/[\\\/]node_modules[\\\/]/)) {
+    sawSyncENOTSUP = true
+    var er = new Error('ENOTSUP: Operation not supported')
+    er.path = path
+    er.code = 'ENOTSUP'
+    throw er
+  }
+  return readdirSync.call(fs, p)
+}
+
+fs.readdir = function (p, cb) {
+  if (allowedDirs.indexOf(path.resolve(p)) === -1 &&
+      !p.match(/[\\\/]node_modules[\\\/]/)) {
+    setTimeout(function () {
+      sawAsyncENOTSUP = true
+      er = new Error('ENOTSUP: Operation not supported')
+      er.path = path
+      er.code = 'ENOTSUP'
+      return cb(er)
+    })
+  } else {
+    readdir.call(fs, p, cb)
+  }
+}
+
+var glob = require('../')
+var test = require('tap').test
+var common = require('../common.js')
+process.chdir(__dirname + '/fixtures')
+
+var pattern = 'a/**/h'
+var expect = [ 'a/abcdef/g/h', 'a/abcfed/g/h' ]
+
+var options = { strict: true, silent: false }
+
+test(pattern + ' ' + JSON.stringify(options), function (t) {
+  var res = glob.sync(pattern, options).sort()
+  t.same(res, expect, 'sync results')
+  t.ok(sawSyncENOTSUP, 'saw sync ENOTSUP')
+
+  var g = glob(pattern, options, function (er, res) {
+    if (er)
+      throw er
+    t.ok(sawAsyncENOTSUP, 'saw async ENOTSUP')
+    res = res.sort()
+    t.same(res, expect, 'async results')
+    t.end()
+  })
+})
diff --git a/test/eperm-stat.js b/test/eperm-stat.js
new file mode 100644
index 0000000..a895a1e
--- /dev/null
+++ b/test/eperm-stat.js
@@ -0,0 +1,107 @@
+require("./global-leakage.js")
+var dir = __dirname + '/fixtures'
+
+var fs = require('fs')
+var expect = [
+  'a/abcdef',
+  'a/abcdef/g',
+  'a/abcdef/g/h',
+  'a/abcfed',
+  'a/abcfed/g',
+  'a/abcfed/g/h'
+]
+
+var lstat = fs.lstat
+var lstatSync = fs.lstatSync
+var badPaths = /\ba[\\\/]?$|\babcdef\b/
+
+fs.lstat = function (path, cb) {
+  // synthetically generate a non-ENOENT error
+  if (badPaths.test(path)) {
+    var er = new Error('synthetic')
+    er.code = 'EPERM'
+    return process.nextTick(cb.bind(null, er))
+  }
+
+  return lstat.call(fs, path, cb)
+}
+
+fs.lstatSync = function (path) {
+  // synthetically generate a non-ENOENT error
+  if (badPaths.test(path)) {
+    var er = new Error('synthetic')
+    er.code = 'EPERM'
+    throw er
+  }
+
+  return lstatSync.call(fs, path)
+}
+
+var glob = require('../')
+var t = require('tap')
+
+t.test('stat errors other than ENOENT are ok', function (t) {
+  t.plan(2)
+  t.test('async', function (t) {
+    glob('a/*abc*/**', { stat: true, cwd: dir }, function (er, matches) {
+      if (er)
+        throw er
+      t.same(matches, expect)
+      t.end()
+    })
+  })
+
+  t.test('sync', function (t) {
+    var matches = glob.sync('a/*abc*/**', { stat: true, cwd: dir })
+    t.same(matches, expect)
+    t.end()
+  })
+})
+
+t.test('globstar with error in root', function (t) {
+  var expect = [
+    'a',
+    'a/abcdef',
+    'a/abcdef/g',
+    'a/abcdef/g/h',
+    'a/abcfed',
+    'a/abcfed/g',
+    'a/abcfed/g/h',
+    'a/b',
+    'a/b/c',
+    'a/b/c/d',
+    'a/bc',
+    'a/bc/e',
+    'a/bc/e/f',
+    'a/c',
+    'a/c/d',
+    'a/c/d/c',
+    'a/c/d/c/b',
+    'a/cb',
+    'a/cb/e',
+    'a/cb/e/f',
+    'a/symlink',
+    'a/symlink/a',
+    'a/symlink/a/b',
+    'a/symlink/a/b/c',
+    'a/x',
+    'a/z'
+  ]
+
+  var pattern = 'a/**'
+  t.plan(2)
+  t.test('async', function (t) {
+    glob(pattern, { cwd: dir }, function (er, matches) {
+      if (er)
+        throw er
+      t.same(matches, expect)
+      t.end()
+    })
+  })
+
+  t.test('sync', function (t) {
+    var matches = glob.sync(pattern, { cwd: dir })
+    t.same(matches, expect)
+    t.end()
+  })
+})
diff --git a/test/error-callback.js b/test/error-callback.js
index 60d3ba1..b60c295 100644
--- a/test/error-callback.js
+++ b/test/error-callback.js
@@ -1,5 +1,12 @@
-var fs
-try { fs = require('graceful-fs') } catch (e) { fs = require('fs') }
+require("./global-leakage.js")
+var logCalled
+var console_error = console.error
+console.error = function () {
+  logCalled = [].slice.call(arguments, 0)
+  console.error = console_error
+}
+
+var fs = require('fs')
 var test = require('tap').test
 var glob = require('../')
 
@@ -19,3 +26,13 @@ test('error callback', function(t) {
     t.end()
   })
 })
+
+test('called console.error for weird error', function (t) {
+  // Need a setTimeout, since the console.error happens directly AFTER
+  // the emit('error') with the error.
+  setTimeout(function () {
+    t.has(logCalled, [ 'glob error', { message: 'mock fs.readdir error' } ],
+          'got expected error printed to console.error')
+    t.end()
+  })
+})
diff --git a/test/follow.js b/test/follow.js
new file mode 100644
index 0000000..5e1d013
--- /dev/null
+++ b/test/follow.js
@@ -0,0 +1,32 @@
+var glob = require('../')
+var test = require('tap').test
+
+process.chdir(__dirname + '/fixtures')
+
+if (process.platform === 'win32') {
+  require('tap').plan(0, 'skip on windows')
+  return
+}
+
+test('follow symlinks', function (t) {
+  var pattern = 'a/symlink/**'
+  var syncNoFollow = glob.sync(pattern).sort()
+  var syncFollow = glob.sync(pattern, { follow: true }).sort()
+  glob(pattern, function (er, res) {
+    if (er)
+      throw er
+    var noFollow = res.sort()
+    glob(pattern, { follow: true }, function (er, res) {
+      if (er)
+        throw er
+      var follow = res.sort()
+
+      t.same(follow, syncFollow, 'sync and async follow should match')
+      t.same(noFollow, syncNoFollow, 'sync and async noFollow should match')
+      var long = 'a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c'
+      t.notEqual(follow.indexOf(long), -1, 'follow should have long entry')
+      t.notEqual(syncFollow.indexOf(long), -1, 'syncFollow should have long entry')
+      t.end()
+    })
+  })
+})
diff --git a/test/global-leakage.js b/test/global-leakage.js
new file mode 100644
index 0000000..8cd3a5c
--- /dev/null
+++ b/test/global-leakage.js
@@ -0,0 +1,20 @@
+if (require.main === module)
+  return require('tap').pass('ok')
+
+var before = Object.keys(global).sort().filter(function (t) {
+  return t !== '__coverage__' && t !== '__core-js_shared__'
+}).join(':')
+var assert = require('assert')
+var glob = require('../')
+
+process.on('exit', function() {
+  delete global.TAP_Global_Harness
+  var after = Object.keys(global).sort().filter(function (t) {
+    return t !== '__coverage__' && t !== '__core-js_shared__'
+  }).join(':')
+  if (after !== before) {
+    console.log('- ' + before)
+    console.log('+ ' + after)
+  }
+  assert.equal(before, after)
+})
diff --git a/test/globstar-match.js b/test/globstar-match.js
index 9b234fa..68f391b 100644
--- a/test/globstar-match.js
+++ b/test/globstar-match.js
@@ -1,3 +1,4 @@
+require("./global-leakage.js")
 var Glob = require("../glob.js").Glob
 var test = require('tap').test
 
@@ -6,11 +7,9 @@ test('globstar should not have dupe matches', function(t) {
   var g = new Glob(pattern, { cwd: __dirname })
   var matches = []
   g.on('match', function(m) {
-    console.error('match %j', m)
     matches.push(m)
   })
   g.on('end', function(set) {
-    console.error('set', set)
     matches = matches.sort()
     set = set.sort()
     t.same(matches, set, 'should have same set of matches')
diff --git a/test/has-magic.js b/test/has-magic.js
new file mode 100644
index 0000000..4565090
--- /dev/null
+++ b/test/has-magic.js
@@ -0,0 +1,34 @@
+require("./global-leakage.js")
+var test = require("tap").test
+var glob = require('../')
+process.chdir(__dirname)
+
+glob.GlobSync.prototype._process = glob.Glob.prototype._process = function () {
+  throw new Error('should not call _process() in these tests')
+}
+
+test("create glob object without processing", function (t) {
+  t.ok(glob('a', {noprocess:true}) instanceof glob.Glob)
+  t.ok(glob.GlobSync('a', {noprocess:true}) instanceof glob.GlobSync)
+  t.end()
+})
+
+test("non-string pattern is evil magic", function (t) {
+  var patterns = [ 0, null, 12, {x:1}, undefined, /x/, NaN ]
+  patterns.forEach(function (p) {
+    t.throws('' + p, function () { glob.hasMagic(p) })
+  })
+  t.end()
+})
+
+test("detect magic in glob patterns", function (t) {
+  t.notOk(glob.hasMagic(""), "no magic in ''")
+  t.notOk(glob.hasMagic("a/b/c/"), "no magic a/b/c/")
+  t.ok(glob.hasMagic("a/b/**/"), "magic in a/b/**/")
+  t.ok(glob.hasMagic("a/b/?/"), "magic in a/b/?/")
+  t.ok(glob.hasMagic("a/b/+(x|y)"), "magic in a/b/+(x|y)")
+  t.notOk(glob.hasMagic("a/b/+(x|y)", {noext:true}), "no magic in a/b/+(x|y) noext")
+  t.ok(glob.hasMagic('{a,b}'), 'magic in {a,b}')
+  t.notOk(glob.hasMagic('{a,b}', {nobrace:true}), 'magic in {a,b} nobrace:true')
+  t.end()
+})
diff --git a/test/ignore.js b/test/ignore.js
new file mode 100644
index 0000000..d391753
--- /dev/null
+++ b/test/ignore.js
@@ -0,0 +1,111 @@
+require('./global-leakage.js')
+// Ignore option test
+// Show that glob ignores results matching pattern on ignore option
+
+var glob = require('../glob.js')
+var test = require('tap').test
+
+// [pattern, ignore, expect, opt (object) or cwd (string)]
+var cases = [
+  [ '*', null, ['abcdef', 'abcfed', 'b', 'bc', 'c', 'cb', 'symlink', 'x', 'z'], 'a'],
+  [ '*', 'b', ['abcdef', 'abcfed', 'bc', 'c', 'cb', 'symlink', 'x', 'z'], 'a'],
+  [ '*', 'b*', ['abcdef', 'abcfed', 'c', 'cb', 'symlink', 'x', 'z'], 'a'],
+  [ 'b/**', 'b/c/d', ['b', 'b/c'], 'a'],
+  [ 'b/**', 'd', ['b', 'b/c', 'b/c/d'], 'a'],
+  [ 'b/**', 'b/c/**', ['b'], 'a'],
+  [ '**/d', 'b/c/d', ['c/d'], 'a'],
+  [ 'a/**/[gh]', ['a/abcfed/g/h'], ['a/abcdef/g', 'a/abcdef/g/h', 'a/abcfed/g']],
+  [ '*', ['c', 'bc', 'symlink', 'abcdef'], ['abcfed', 'b', 'cb', 'x', 'z'], 'a'],
+  [ '**', ['c/**', 'bc/**', 'symlink/**', 'abcdef/**'], ['abcfed', 'abcfed/g', 'abcfed/g/h', 'b', 'b/c', 'b/c/d', 'cb', 'cb/e', 'cb/e/f', 'x', 'z'], 'a'],
+  [ 'a/**', ['a/**'], []],
+  [ 'a/**', ['a/**/**'], []],
+  [ 'a/b/**', ['a/b'], ['a/b/c', 'a/b/c/d']],
+  [ '**', ['b'], ['abcdef', 'abcdef/g', 'abcdef/g/h', 'abcfed', 'abcfed/g', 'abcfed/g/h', 'b/c', 'b/c/d', 'bc', 'bc/e', 'bc/e/f', 'c', 'c/d', 'c/d/c', 'c/d/c/b', 'cb', 'cb/e', 'cb/e/f', 'symlink', 'symlink/a', 'symlink/a/b', 'symlink/a/b/c', 'x', 'z'], 'a'],
+  [ '**', ['b', 'c'], ['abcdef', 'abcdef/g', 'abcdef/g/h', 'abcfed', 'abcfed/g', 'abcfed/g/h', 'b/c', 'b/c/d', 'bc', 'bc/e', 'bc/e/f', 'c/d', 'c/d/c', 'c/d/c/b', 'cb', 'cb/e', 'cb/e/f', 'symlink', 'symlink/a', 'symlink/a/b', 'symlink/a/b/c', 'x', 'z'], 'a'],
+  [ '**', ['b**'], ['abcdef', 'abcdef/g', 'abcdef/g/h', 'abcfed', 'abcfed/g', 'abcfed/g/h', 'b/c', 'b/c/d', 'bc/e', 'bc/e/f', 'c', 'c/d', 'c/d/c', 'c/d/c/b', 'cb', 'cb/e', 'cb/e/f', 'symlink', 'symlink/a', 'symlink/a/b', 'symlink/a/b/c', 'x', 'z'], 'a'],
+  [ '**', ['b/**'], ['abcdef', 'abcdef/g', 'abcdef/g/h', 'abcfed', 'abcfed/g', 'abcfed/g/h', 'bc', 'bc/e', 'bc/e/f', 'c', 'c/d', 'c/d/c', 'c/d/c/b', 'cb', 'cb/e', 'cb/e/f', 'symlink', 'symlink/a', 'symlink/a/b', 'symlink/a/b/c', 'x', 'z'], 'a'],
+  [ '**', ['b**/**'], ['abcdef', 'abcdef/g', 'abcdef/g/h', 'abcfed', 'abcfed/g', 'abcfed/g/h', 'c', 'c/d', 'c/d/c', 'c/d/c/b', 'cb', 'cb/e', 'cb/e/f', 'symlink', 'symlink/a', 'symlink/a/b', 'symlink/a/b/c', 'x', 'z'], 'a'],
+  [ '**', ['ab**ef/**'], ['abcfed', 'abcfed/g', 'abcfed/g/h', 'b', 'b/c', 'b/c/d', 'bc', 'bc/e', 'bc/e/f', 'c', 'c/d', 'c/d/c', 'c/d/c/b', 'cb', 'cb/e', 'cb/e/f', 'symlink', 'symlink/a', 'symlink/a/b', 'symlink/a/b/c', 'x', 'z'], 'a'],
+  [ '**', ['abc{def,fed}/**'], ['b', 'b/c', 'b/c/d', 'bc', 'bc/e', 'bc/e/f', 'c', 'c/d', 'c/d/c', 'c/d/c/b', 'cb', 'cb/e', 'cb/e/f', 'symlink', 'symlink/a', 'symlink/a/b', 'symlink/a/b/c', 'x', 'z'], 'a'],
+  [ '**', ['abc{def,fed}/*'], ['abcdef', 'abcdef/g/h', 'abcfed', 'abcfed/g/h', 'b', 'b/c', 'b/c/d', 'bc', 'bc/e', 'bc/e/f', 'c', 'c/d', 'c/d/c', 'c/d/c/b', 'cb', 'cb/e', 'cb/e/f', 'symlink', 'symlink/a', 'symlink/a/b', 'symlink/a/b/c', 'x', 'z'], 'a'],
+  [ 'c/**', ['c/*'], ['c', 'c/d/c', 'c/d/c/b'], 'a'],
+  [ 'a/c/**', ['a/c/*'], ['a/c', 'a/c/d/c', 'a/c/d/c/b']],
+  [ 'a/c/**', ['a/c/**', 'a/c/*', 'a/c/*/c'], []],
+  [ 'a/**/.y', ['a/x/**'], ['a/z/.y']],
+  [ 'a/**/.y', ['a/x/**'], ['a/z/.y'], { dot: true }],
+  [ 'a/**/b', ['a/x/**'], ['a/b', 'a/c/d/c/b', 'a/symlink/a/b']],
+  [ 'a/**/b', ['a/x/**'], ['a/b', 'a/c/d/c/b', 'a/symlink/a/b', 'a/z/.y/b'], { dot: true }],
+  [ '*/.abcdef', 'a/**', [] ],
+  [ 'a/*/.y/b', 'a/x/**', [ 'a/z/.y/b' ] ]
+]
+
+process.chdir(__dirname + '/fixtures')
+
+cases.forEach(function (c, i) {
+  var pattern = c[0]
+  var ignore = c[1]
+  var expect = c[2].sort()
+  var opt = c[3]
+  var name = i + ' ' + pattern + ' ' + JSON.stringify(ignore)
+  if (typeof opt === 'string')
+    opt = { cwd: opt }
+
+  if (opt)
+    name += ' ' + JSON.stringify(opt)
+  else
+    opt = {}
+
+  var matches = []
+
+  opt.ignore = ignore
+
+  test(name, function (t) {
+    glob(pattern, opt, function (er, res) {
+      if (er)
+        throw er
+
+      if (process.platform === 'win32') {
+        expect = expect.filter(function (f) {
+          return !/\bsymlink\b/.test(f)
+        })
+      }
+
+      t.same(res.sort(), expect, 'async')
+      t.same(matches.sort(), expect, 'match events')
+      res = glob.sync(pattern, opt)
+      t.same(res.sort(), expect, 'sync')
+      t.end()
+    }).on('match', function (p) {
+      matches.push(p)
+    })
+  })
+})
+
+test('race condition', function (t) {
+  process.chdir(__dirname)
+  var pattern = 'fixtures/*'
+  ;[true, false].forEach(function (dot) {
+    ;['fixtures/**', null].forEach(function (ignore) {
+      ;[false, true].forEach(function (nonull) {
+        ;[false, process.cwd(), '.'].forEach(function (cwd) {
+          var opt = {
+            dot: dot,
+            ignore: ignore,
+            nonull: nonull,
+          }
+          if (cwd)
+            opt.cwd = cwd
+          var expect = ignore ? [] : [ 'fixtures/a' ]
+          t.test(JSON.stringify(opt), function (t) {
+            t.plan(2)
+            t.same(glob.sync(pattern, opt), expect)
+            glob(pattern, opt).on('end', function (res) {
+              t.same(res, expect)
+            })
+          })
+        })
+      })
+    })
+  })
+  t.end()
+})
diff --git a/test/mark.js b/test/mark.js
index bf411c0..764c332 100644
--- a/test/mark.js
+++ b/test/mark.js
@@ -1,6 +1,7 @@
+require("./global-leakage.js")
 var test = require("tap").test
 var glob = require('../')
-process.chdir(__dirname)
+process.chdir(__dirname + '/fixtures')
 
 // expose timing issues
 var lag = 5
@@ -11,9 +12,35 @@ glob.Glob.prototype._stat = function(o) { return function(f, cb) {
   }.bind(this), lag += 5)
 }}(glob.Glob.prototype._stat)
 
+test('mark with cwd', function (t) {
+  var pattern = '*/*'
+  var opt = { mark: true, cwd: 'a' }
+  glob(pattern, opt, function (er, res) {
+    if (er)
+      throw er
+
+    var expect = [
+      'abcdef/g/',
+      'abcfed/g/',
+      'b/c/',
+      'bc/e/',
+      'c/d/',
+      'cb/e/',
+    ].sort()
+
+    if (process.platform !== 'win32')
+      expect.push('symlink/a/')
+
+    t.same(res.sort(), expect)
+    t.same(glob.sync(pattern, opt).sort(), expect)
+    t.end()
+  })
+})
 
 test("mark, with **", function (t) {
-  glob("a/*b*/**", {mark: true}, function (er, results) {
+  var pattern = 'a/*b*/**'
+  var opt = { mark: true }
+  glob(pattern, opt, function (er, results) {
     if (er)
       throw er
     var expect =
@@ -34,12 +61,15 @@ test("mark, with **", function (t) {
         'a/cb/e/f' ]
 
     t.same(results, expect)
+    t.same(glob.sync(pattern, opt), expect)
     t.end()
   })
 })
 
 test("mark, no / on pattern", function (t) {
-  glob("a/*", {mark: true}, function (er, results) {
+  var pattern = 'a/*'
+  var opt = { mark: true }
+  glob(pattern, opt, function (er, results) {
     if (er)
       throw er
     var expect = [ 'a/abcdef/',
@@ -47,12 +77,17 @@ test("mark, no / on pattern", function (t) {
                    'a/b/',
                    'a/bc/',
                    'a/c/',
-                   'a/cb/' ]
+                   'a/cb/',
+                   'a/x/',
+                   'a/z/' ]
 
     if (process.platform !== "win32")
       expect.push('a/symlink/')
 
+    expect = expect.sort()
+
     t.same(results, expect)
+    t.same(glob.sync(pattern, opt), expect)
     t.end()
   }).on('match', function(m) {
     t.similar(m, /\/$/)
@@ -60,7 +95,9 @@ test("mark, no / on pattern", function (t) {
 })
 
 test("mark=false, no / on pattern", function (t) {
-  glob("a/*", function (er, results) {
+  var pattern = 'a/*'
+  var opt = null
+  glob(pattern, opt, function (er, results) {
     if (er)
       throw er
     var expect = [ 'a/abcdef',
@@ -68,11 +105,17 @@ test("mark=false, no / on pattern", function (t) {
                    'a/b',
                    'a/bc',
                    'a/c',
-                   'a/cb' ]
+                   'a/cb',
+                   'a/x',
+                   'a/z' ]
 
     if (process.platform !== "win32")
       expect.push('a/symlink')
+
+    expect = expect.sort()
+
     t.same(results, expect)
+    t.same(glob.sync(pattern, opt), expect)
     t.end()
   }).on('match', function(m) {
     t.similar(m, /[^\/]$/)
@@ -80,7 +123,9 @@ test("mark=false, no / on pattern", function (t) {
 })
 
 test("mark=true, / on pattern", function (t) {
-  glob("a/*/", {mark: true}, function (er, results) {
+  var pattern = 'a/*/'
+  var opt = { mark: true }
+  glob(pattern, opt, function (er, results) {
     if (er)
       throw er
     var expect = [ 'a/abcdef/',
@@ -88,10 +133,17 @@ test("mark=true, / on pattern", function (t) {
                     'a/b/',
                     'a/bc/',
                     'a/c/',
-                    'a/cb/' ]
+                    'a/cb/',
+                    'a/x/',
+                    'a/z/' ]
+
     if (process.platform !== "win32")
       expect.push('a/symlink/')
+
+    expect = expect.sort()
+
     t.same(results, expect)
+    t.same(glob.sync(pattern, opt), expect)
     t.end()
   }).on('match', function(m) {
     t.similar(m, /\/$/)
@@ -99,7 +151,9 @@ test("mark=true, / on pattern", function (t) {
 })
 
 test("mark=false, / on pattern", function (t) {
-  glob("a/*/", function (er, results) {
+  var pattern = "a/*/"
+  var opt = null
+  glob(pattern, opt, function (er, results) {
     if (er)
       throw er
     var expect = [ 'a/abcdef/',
@@ -107,12 +161,40 @@ test("mark=false, / on pattern", function (t) {
                    'a/b/',
                    'a/bc/',
                    'a/c/',
-                   'a/cb/' ]
+                   'a/cb/',
+                   'a/x/',
+                   'a/z/' ]
     if (process.platform !== "win32")
       expect.push('a/symlink/')
+
+    expect = expect.sort()
+
     t.same(results, expect)
+    t.same(glob.sync(pattern, opt), expect)
     t.end()
   }).on('match', function(m) {
     t.similar(m, /\/$/)
   })
 })
+
+var cwd = process.cwd().replace(/[\/\\]+$/, '').replace(/\\/g, '/')
+;[true,false].forEach(function (mark) {
+  ;[true,false].forEach(function (slash) {
+    test("cwd mark:" + mark + " slash:" + slash, function (t) {
+      var pattern = cwd + (slash ? '/' : '')
+      glob(pattern, {mark:mark}, function (er, results) {
+        t.equal(results.length, 1)
+        var res = results[0].replace(/\\/g, '/')
+        var syncRes = glob.sync(pattern, {mark:mark})
+        syncRes = syncRes[0].replace(/\\/g, '/')
+        if (slash || mark)
+          t.equal(res, cwd + '/')
+        else
+          t.equal(res.indexOf(cwd), 0)
+        t.equal(syncRes, res, 'sync should match async')
+        t.end()
+      })
+    })
+  })
+})
+
diff --git a/test/match-base.js b/test/match-base.js
new file mode 100644
index 0000000..051b584
--- /dev/null
+++ b/test/match-base.js
@@ -0,0 +1,49 @@
+var t = require('tap')
+var glob = require('../')
+var path = require('path')
+var fixtureDir = path.resolve(__dirname, 'fixtures')
+
+var pattern = 'a*'
+var expect = [
+  'a',
+  'a/abcdef',
+  'a/abcfed',
+]
+
+if (process.platform !== 'win32')
+  expect.push('a/symlink/a', 'a/symlink/a/b/c/a')
+
+t.test('chdir', function (t) {
+  var origCwd = process.cwd()
+  process.chdir(fixtureDir)
+  t.same(glob.sync(pattern, { matchBase: true }), expect)
+  t.same(glob(pattern, { matchBase: true, sync: true }), expect)
+  glob(pattern, { matchBase: true }, function (er, res) {
+    if (er)
+      throw er
+    t.same(res, expect)
+    process.chdir(origCwd)
+    t.end()
+  })
+})
+
+t.test('cwd', function (t) {
+  t.same(glob.sync(pattern, { matchBase: true, cwd: fixtureDir }), expect)
+  t.same(glob(pattern, { matchBase: true, sync: true, cwd: fixtureDir }), expect)
+  glob(pattern, { matchBase: true, cwd: fixtureDir }, function (er, res) {
+    if (er)
+      throw er
+    t.same(res, expect)
+    t.end()
+  })
+})
+
+t.test('noglobstar', function (t) {
+  t.throws(function () {
+    glob(pattern, { matchBase:true, noglobstar: true })
+  })
+  t.throws(function () {
+    glob.sync(pattern, { matchBase:true, noglobstar: true })
+  })
+  t.end()
+})
diff --git a/test/multiple-weird-error.js b/test/multiple-weird-error.js
new file mode 100644
index 0000000..4a9e45e
--- /dev/null
+++ b/test/multiple-weird-error.js
@@ -0,0 +1,15 @@
+var t = require('tap')
+var fs = require('fs')
+fs.readdir = function(path, cb) { cb(new Error('expected')) }
+var glob = require('../')
+
+// also test that silent:true is actually silent!
+console.error = function () { throw 'SILENCE, INSECT!' }
+
+t.plan(2)
+glob('*', { silent: true }, function(err, files) {
+  t.ok(err, 'got first error')
+})
+glob('*', { silent: true }, function(err, files) {
+  t.ok(err, 'got second error')
+})
diff --git a/test/negation-test.js b/test/negation-test.js
deleted file mode 100644
index fc679e2..0000000
--- a/test/negation-test.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// Negation test
-// Show that glob respect's minimatch's negate flag
-
-var glob = require('../glob.js')
-var test = require('tap').test
-
-test('glob respects minimatch negate flag when activated with leading !', function(t) {
-  var expect = ["abcdef/g", "abcfed/g", "c/d", "cb/e", "symlink/a"]
-  var results = glob("!b**/*", {cwd: 'a'}, function (er, results) {
-    if (er)
-      throw er
-
-    t.same(results, expect)
-    t.end()
-  });
-});
diff --git a/test/new-glob-optional-options.js b/test/new-glob-optional-options.js
index 3e7dc5a..9dc29f5 100644
--- a/test/new-glob-optional-options.js
+++ b/test/new-glob-optional-options.js
@@ -1,10 +1,12 @@
+require("./global-leakage.js")
 var Glob = require('../glob.js').Glob;
 var test = require('tap').test;
+var f = __filename.replace(/\\/g, '/')
 
 test('new glob, with cb, and no options', function (t) {
-  new Glob(__filename, function(er, results) {
+  new Glob(f, function(er, results) {
     if (er) throw er;
-    t.same(results, [__filename]);
+    t.same(results, [f]);
     t.end();
   });
 });
diff --git a/test/nocase-nomagic.js b/test/nocase-nomagic.js
index 5a29b08..6bd46c6 100644
--- a/test/nocase-nomagic.js
+++ b/test/nocase-nomagic.js
@@ -1,8 +1,14 @@
-var fs
-try { fs = require('graceful-fs') } catch (e) { fs = require('fs') }
+require("./global-leakage.js")
+var fs = require('fs')
 var test = require('tap').test;
 var glob = require('../');
 
+var cwd = process.cwd()
+var drive = 'c'
+if (/^[a-zA-Z]:[\\\/]/.test(cwd)) {
+  drive = cwd.charAt(0).toLowerCase()
+}
+
 test('mock fs', function(t) {
   var stat = fs.stat
   var statSync = fs.statSync
@@ -11,11 +17,11 @@ test('mock fs', function(t) {
 
   function fakeStat(path) {
     var ret
-    switch (path.toLowerCase()) {
-      case '/tmp': case '/tmp/': case 'c:\\tmp': case 'c:\\tmp\\':
+    switch (path.toLowerCase().replace(/\\/g, '/')) {
+      case '/tmp': case '/tmp/': case drive+':\\tmp': case drive+':\\tmp\\':
         ret = { isDirectory: function() { return true } }
         break
-      case '/tmp/a': case 'c:\\tmp\\a':
+      case '/tmp/a': case drive+':/tmp/a':
         ret = { isDirectory: function() { return false } }
         break
     }
@@ -39,11 +45,11 @@ test('mock fs', function(t) {
 
   function fakeReaddir(path) {
     var ret
-    switch (path.toLowerCase()) {
-      case '/tmp': case '/tmp/': case 'c:\\tmp': case 'c:\\tmp\\':
+    switch (path.toLowerCase().replace(/\\/g, '/')) {
+      case '/tmp': case '/tmp/': case drive+':/tmp': case drive+':/tmp/':
         ret = [ 'a', 'A' ]
         break
-      case '/': case 'c:\\':
+      case '/': case drive+':/':
         ret = ['tmp', 'tMp', 'tMP', 'TMP']
     }
     return ret
@@ -79,18 +85,26 @@ test('nocase, nomagic', function(t) {
                '/tmp/a' ]
   if(process.platform.match(/^win/)) {
     want = want.map(function(p) {
-      return 'C:' + p
+      return drive+':' + p
     })
   }
   glob('/tmp/a', { nocase: true }, function(er, res) {
     if (er)
       throw er
+    if (process.platform.match(/^win/))
+      res = res.map(function (r) {
+        return r.replace(/\\/g, '/').replace(new RegExp('^' + drive + ':', 'i'), drive+':')
+      })
     t.same(res.sort(), want)
     if (--n === 0) t.end()
   })
   glob('/tmp/A', { nocase: true }, function(er, res) {
     if (er)
       throw er
+    if (process.platform.match(/^win/))
+      res = res.map(function (r) {
+        return r.replace(/\\/g, '/').replace(new RegExp('^' + drive + ':', 'i'), drive+':')
+      })
     t.same(res.sort(), want)
     if (--n === 0) t.end()
   })
@@ -108,18 +122,28 @@ test('nocase, with some magic', function(t) {
                '/tmp/a' ]
   if(process.platform.match(/^win/)) {
     want = want.map(function(p) {
-      return 'C:' + p
+      return drive + ':' + p
     })
   }
 
   glob('/tmp/*', { nocase: true }, function(er, res) {
     if (er)
       throw er
+    if (process.platform.match(/^win/)) {
+      res = res.map(function (r) {
+        return r.replace(/\\/g, '/').replace(new RegExp('^' + drive + ':', 'i'), drive+':')
+      })
+    }
     t.same(res.sort(), want)
   })
   glob('/tmp/*', { nocase: true }, function(er, res) {
     if (er)
       throw er
+    if (process.platform.match(/^win/)) {
+      res = res.map(function (r) {
+        return r.replace(/\\/g, '/').replace(new RegExp('^' + drive + ':', 'i'), drive+':')
+      })
+    }
     t.same(res.sort(), want)
   })
 })
diff --git a/test/nodir.js b/test/nodir.js
new file mode 100644
index 0000000..1f09569
--- /dev/null
+++ b/test/nodir.js
@@ -0,0 +1,67 @@
+require("./global-leakage.js")
+var test = require("tap").test
+var glob = require('../')
+var path = require('path')
+var isAbsolute = require('path-is-absolute')
+process.chdir(__dirname + '/fixtures')
+
+function cacheCheck(g, t) {
+  // verify that path cache keys are all absolute
+  var caches = [ 'cache', 'statCache', 'symlinks' ]
+  caches.forEach(function (c) {
+    Object.keys(g[c]).forEach(function (p) {
+      t.ok(isAbsolute(p), p + ' should be absolute')
+    })
+  })
+}
+
+// [pattern, options, expect]
+var root = path.resolve('a')
+var cases = [
+  [ '*/**', { cwd: 'a' }, [
+      'abcdef/g/h',
+      'abcfed/g/h',
+      'b/c/d',
+      'bc/e/f',
+      'c/d/c/b',
+      'cb/e/f'
+    ]
+  ],
+  [ 'a/*b*/**', {}, [
+      'a/abcdef/g/h',
+      'a/abcfed/g/h',
+      'a/b/c/d',
+      'a/bc/e/f',
+      'a/cb/e/f'
+    ]
+  ],
+  [ 'a/*b*/**/', {}, [] ],
+  [ '*/*', { cwd: 'a' }, [] ],
+  [ '/*/*', { root: root }, [] ],
+  [ '/b*/**', { root: root }, [
+      '/b/c/d',
+      '/bc/e/f'
+    ].map(function (m) {
+      return path.join(root, m).replace(/\\/g, '/')
+    })
+  ]
+]
+
+cases.forEach(function (c) {
+  var pattern = c[0]
+  var options = c[1] || {}
+  options.nodir = true
+  var expect = c[2].sort()
+  test(pattern + ' ' + JSON.stringify(options), function (t) {
+    var res = glob.sync(pattern, options).sort()
+    t.same(res, expect, 'sync results')
+    var g = glob(pattern, options, function (er, res) {
+      if (er)
+        throw er
+      res = res.sort()
+      t.same(res, expect, 'async results')
+      cacheCheck(g, t)
+      t.end()
+    })
+  })
+})
diff --git a/test/nonull.js b/test/nonull.js
new file mode 100644
index 0000000..5f744e3
--- /dev/null
+++ b/test/nonull.js
@@ -0,0 +1,31 @@
+require("./global-leakage.js")
+var test = require("tap").test
+var glob = require('../')
+var common = require('../common.js')
+process.chdir(__dirname)
+
+// [pattern, options, expect]
+var cases = [
+  [ 'a/*NOFILE*/**/', {}, [ 'a/*NOFILE*/**/' ] ],
+  [ '*/*', { cwd: 'NODIR' }, [ '*/*' ] ],
+  [ 'NOFILE', {}, [ 'NOFILE' ] ],
+  [ 'NOFILE', { cwd: 'NODIR' }, [ 'NOFILE' ] ]
+]
+
+cases.forEach(function (c) {
+  var pattern = c[0]
+  var options = c[1] || {}
+  options.nonull = true
+  var expect = c[2].sort()
+  test(pattern + ' ' + JSON.stringify(options), function (t) {
+    var res = glob.sync(pattern, options).sort()
+    t.same(res, expect, 'sync results')
+    var g = glob(pattern, options, function (er, res) {
+      if (er)
+        throw er
+      res = res.sort()
+      t.same(res, expect, 'async results')
+      t.end()
+    })
+  })
+})
diff --git a/test/pause-resume.js b/test/pause-resume.js
index e1ffbab..d6fb807 100644
--- a/test/pause-resume.js
+++ b/test/pause-resume.js
@@ -1,17 +1,15 @@
+require("./global-leakage.js")
 // show that no match events happen while paused.
 var tap = require("tap")
-, child_process = require("child_process")
+var child_process = require("child_process")
 // just some gnarly pattern with lots of matches
-, pattern = "test/a/!(symlink)/**"
-, bashResults = require("./bash-results.json")
-, patterns = Object.keys(bashResults)
-, glob = require("../")
-, Glob = glob.Glob
-, path = require("path")
+var pattern = "a/!(symlink)/**"
+var bashResults = require("./bash-results.json")
+var glob = require("../")
+var Glob = glob.Glob
+var path = require("path")
 
-// run from the root of the project
-// this is usually where you're at anyway, but be sure.
-process.chdir(path.resolve(__dirname, ".."))
+process.chdir(__dirname + '/fixtures')
 
 function alphasort (a, b) {
   a = a.toLowerCase()
@@ -37,17 +35,9 @@ function cleanResults (m) {
 var globResults = []
 tap.test("use a Glob object, and pause/resume it", function (t) {
   var g = new Glob(pattern)
-  , paused = false
-  , res = []
-  , expect = bashResults[pattern]
-
-  g.on("pause", function () {
-    console.error("pause")
-  })
-
-  g.on("resume", function () {
-    console.error("resume")
-  })
+  var paused = false
+  var res = []
+  var expect = bashResults[pattern]
 
   g.on("match", function (m) {
     t.notOk(g.paused, "must not be paused")
diff --git a/test/readme-issue.js b/test/readme-issue.js
index 0b4e0be..1c35ecb 100644
--- a/test/readme-issue.js
+++ b/test/readme-issue.js
@@ -1,3 +1,4 @@
+require("./global-leakage.js")
 var test = require("tap").test
 var glob = require("../")
 
diff --git a/test/realpath.js b/test/realpath.js
new file mode 100644
index 0000000..3be5556
--- /dev/null
+++ b/test/realpath.js
@@ -0,0 +1,93 @@
+var glob = require('../')
+var test = require('tap').test
+// pattern to find a bunch of duplicates
+var pattern = 'a/symlink/{*,**/*/*/*,*/*/**,*/*/*/*/*/*}'
+var path = require('path')
+var fixtureDir = path.resolve(__dirname, 'fixtures')
+process.chdir(fixtureDir)
+
+if (process.platform === 'win32')
+  return require('tap').plan(0, 'skip on windows')
+
+// options, results
+// realpath:true set on each option
+var cases = [
+  [ {},
+    [ 'a/symlink', 'a/symlink/a', 'a/symlink/a/b' ] ],
+
+  [ { mark: true },
+    [ 'a/symlink/', 'a/symlink/a/', 'a/symlink/a/b/' ] ],
+
+  [ { stat: true },
+    [ 'a/symlink', 'a/symlink/a', 'a/symlink/a/b' ] ],
+
+  [ { follow: true },
+    [ 'a/symlink', 'a/symlink/a', 'a/symlink/a/b' ] ],
+
+  [ { cwd: 'a' },
+    [ 'symlink', 'symlink/a', 'symlink/a/b' ],
+    pattern.substr(2) ],
+
+  [ { cwd: 'a' },
+    [],
+    'no one here but us chickens' ],
+
+  [ { nonull: true },
+    [ 'no one here but us chickens',
+      'no one here but us sheep' ],
+    'no one here but us {chickens,sheep}' ],
+
+  [ { nounique: true },
+    [ 'a/symlink',
+      'a/symlink',
+      'a/symlink',
+      'a/symlink/a',
+      'a/symlink/a',
+      'a/symlink/a/b',
+      'a/symlink/a/b' ] ],
+
+  [ { nounique: true, mark: true },
+    [ 'a/symlink/',
+      'a/symlink/',
+      'a/symlink/',
+      'a/symlink/a/',
+      'a/symlink/a/',
+      'a/symlink/a/b/',
+      'a/symlink/a/b/' ] ],
+
+  [ { nounique: true, mark: true, follow: true },
+    [ 'a/symlink/',
+      'a/symlink/',
+      'a/symlink/',
+      'a/symlink/a/',
+      'a/symlink/a/',
+      'a/symlink/a/',
+      'a/symlink/a/b/',
+      'a/symlink/a/b/' ] ],
+]
+
+cases.forEach(function (c) {
+  var opt = c[0]
+  var expect = c[1]
+  if (!(opt.nonull && expect[0].match(/^no one here/))) {
+    expect = expect.map(function (d) {
+      d = (opt.cwd ? path.resolve(opt.cwd) : fixtureDir) + '/' + d
+      return d.replace(/\\/g, '/')
+    })
+  }
+  var p = c[2] || pattern
+
+  opt.realpath = true
+
+  test(JSON.stringify(opt), function (t) {
+    opt.realpath = true
+    var sync = glob.sync(p, opt)
+    t.same(sync, expect, 'sync')
+    glob(p, opt, function (er, async) {
+      if (er)
+        throw er
+      t.same(async, expect, 'async')
+      t.end()
+    })
+  })
+})
diff --git a/test/root-nomount.js b/test/root-nomount.js
index 3ac5979..db2f721 100644
--- a/test/root-nomount.js
+++ b/test/root-nomount.js
@@ -1,39 +1,48 @@
+require("./global-leakage.js")
 var tap = require("tap")
+var glob = require('../')
+var path = require('path')
+var isAbsolute = require('path-is-absolute')
 
-var origCwd = process.cwd()
-process.chdir(__dirname)
+function cacheCheck(g, t) {
+  // verify that path cache keys are all absolute
+  var caches = [ 'cache', 'statCache', 'symlinks' ]
+  caches.forEach(function (c) {
+    Object.keys(g[c]).forEach(function (p) {
+      t.ok(isAbsolute(p), p + ' should be absolute')
+    })
+  })
+}
+
+process.chdir(__dirname + '/fixtures')
 
 tap.test("changing root and searching for /b*/**", function (t) {
-  var glob = require('../')
-  var path = require('path')
   t.test('.', function (t) {
-    glob('/b*/**', { globDebug: true, root: '.', nomount: true }, function (er, matches) {
+    var g = glob('/b*/**', { root: '.', nomount: true }, function (er, matches) {
       t.ifError(er)
       t.like(matches, [])
+      cacheCheck(g, t)
       t.end()
     })
   })
 
   t.test('a', function (t) {
-    glob('/b*/**', { globDebug: true, root: path.resolve('a'), nomount: true }, function (er, matches) {
+    var g = glob('/b*/**', { root: path.resolve('a'), nomount: true }, function (er, matches) {
       t.ifError(er)
       t.like(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ])
+      cacheCheck(g, t)
       t.end()
     })
   })
 
   t.test('root=a, cwd=a/b', function (t) {
-    glob('/b*/**', { globDebug: true, root: 'a', cwd: path.resolve('a/b'), nomount: true }, function (er, matches) {
+    var g = glob('/b*/**', { root: 'a', cwd: path.resolve('a/b'), nomount: true }, function (er, matches) {
       t.ifError(er)
       t.like(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ])
+      cacheCheck(g, t)
       t.end()
     })
   })
 
-  t.test('cd -', function (t) {
-    process.chdir(origCwd)
-    t.end()
-  })
-
   t.end()
 })
diff --git a/test/root.js b/test/root.js
index 95c23f9..14ff15a 100644
--- a/test/root.js
+++ b/test/root.js
@@ -1,23 +1,34 @@
+require("./global-leakage.js")
 var t = require("tap")
 
-var origCwd = process.cwd()
-process.chdir(__dirname)
+process.chdir(__dirname + '/fixtures')
 
 var glob = require('../')
 var path = require('path')
+var isAbsolute = require('path-is-absolute')
+
+function cacheCheck(g, t) {
+  // verify that path cache keys are all absolute
+  var caches = [ 'cache', 'statCache', 'symlinks' ]
+  caches.forEach(function (c) {
+    Object.keys(g[c]).forEach(function (p) {
+      t.ok(isAbsolute(p), p + ' should be absolute')
+    })
+  })
+}
 
 t.test('.', function (t) {
-  glob('/b*/**', { globDebug: true, root: '.' }, function (er, matches) {
+  var g = glob('/b*/**', { root: '.' }, function (er, matches) {
     t.ifError(er)
-    t.like(matches, [])
+    t.same(matches, [])
+    cacheCheck(g, t)
     t.end()
   })
 })
 
 
 t.test('a', function (t) {
-  console.error("root=" + path.resolve('a'))
-  glob('/b*/**', { globDebug: true, root: path.resolve('a') }, function (er, matches) {
+  var g = glob('/b*/**', { root: path.resolve('a') }, function (er, matches) {
     t.ifError(er)
     var wanted = [
         '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f'
@@ -25,22 +36,46 @@ t.test('a', function (t) {
         return path.join(path.resolve('a'), m).replace(/\\/g, '/')
       })
 
-    t.like(matches, wanted)
+    t.same(matches, wanted)
+    cacheCheck(g, t)
     t.end()
   })
 })
 
 t.test('root=a, cwd=a/b', function (t) {
-  glob('/b*/**', { globDebug: true, root: 'a', cwd: path.resolve('a/b') }, function (er, matches) {
+  var g = glob('/b*/**', { root: 'a', cwd: path.resolve('a/b') }, function (er, matches) {
+    t.ifError(er)
+    t.same(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ].map(function (m) {
+      return path.join(path.resolve('a'), m).replace(/\\/g, '/')
+    }))
+    cacheCheck(g, t)
+    t.end()
+  })
+})
+
+t.test('combined with absolute option', function(t) {
+  var g = glob('/b*/**', { root: path.resolve('a'), absolute: true }, function (er, matches) {
     t.ifError(er)
-    t.like(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ].map(function (m) {
+    t.same(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ].map(function (m) {
       return path.join(path.resolve('a'), m).replace(/\\/g, '/')
     }))
+    cacheCheck(g, t)
     t.end()
   })
 })
 
-t.test('cd -', function (t) {
-  process.chdir(origCwd)
-  t.end()
+t.test('cwdAbs when root=a, absolute=true', function(t) {
+   var g = glob('/b*/**', { root: path.resolve('a'), absolute: true }, function (er, matches) {
+    t.ifError(er)
+    t.same(g.cwdAbs, process.cwd().replace(/\\/g, '/'))
+    t.end()
+  })
+})
+
+t.test('cwdAbs when root=a, absolute=true, cwd=__dirname', function(t) {
+   var g = glob('/b*/**', { root: path.resolve('a'), absolute: true, cwd: __dirname }, function (er, matches) {
+    t.ifError(er)
+    t.same(g.cwdAbs, __dirname.replace(/\\/g, '/'))
+    t.end()
+  })
 })
diff --git a/test/slash-cwd.js b/test/slash-cwd.js
new file mode 100644
index 0000000..fc1f1b0
--- /dev/null
+++ b/test/slash-cwd.js
@@ -0,0 +1,20 @@
+// regression test to make sure that slash-ended patterns
+// don't match files when using a different cwd.
+var glob = require('../')
+var test = require('tap').test
+var pattern = '../{*.md,test}/'
+var expect = [ '../test/' ]
+var cwd = __dirname
+var opt = { cwd: cwd }
+process.chdir(__dirname + '/..')
+
+test('slashes only match directories', function (t) {
+  var sync = glob.sync(pattern, { cwd: cwd })
+  t.same(sync, expect, 'sync test')
+  glob(pattern, { cwd: cwd }, function (er, async) {
+    if (er)
+      throw er
+    t.same(async, expect, 'async test')
+    t.end()
+  })
+})
diff --git a/test/stat.js b/test/stat.js
index f555b39..3e5862a 100644
--- a/test/stat.js
+++ b/test/stat.js
@@ -1,16 +1,20 @@
+require("./global-leakage.js")
 var glob = require('../')
 var test = require('tap').test
 var path = require('path')
+var Stats = require('fs').Stats
+var dir = __dirname + '/fixtures'
 
 test('stat all the things', function(t) {
-  var g = new glob.Glob('a/*abc*/**', { stat: true, cwd: __dirname })
+  var g = new glob.Glob('a/*abc*/**', { stat: true, cwd: dir })
   var matches = []
   g.on('match', function(m) {
     matches.push(m)
   })
   var stats = []
-  g.on('stat', function(m) {
+  g.on('stat', function(m, st) {
     stats.push(m)
+    t.ok(st instanceof Stats)
   })
   g.on('end', function(eof) {
     stats = stats.sort()
@@ -20,7 +24,7 @@ test('stat all the things', function(t) {
     t.same(eof, matches)
     var cache = Object.keys(this.statCache)
     t.same(cache.map(function (f) {
-      return path.relative(__dirname, f).replace(/\\/g, '/')
+      return path.relative(dir, f).replace(/\\/g, '/')
     }).sort(), matches)
 
     cache.forEach(function(c) {
diff --git a/test/sync-cb-throw.js b/test/sync-cb-throw.js
new file mode 100644
index 0000000..911eedc
--- /dev/null
+++ b/test/sync-cb-throw.js
@@ -0,0 +1,46 @@
+require("./global-leakage.js")
+var test = require('tap').test
+var g = require('../')
+test('sync throws if provided callback', function (t) {
+  t.throws(function () {
+    g('*', {sync:true}, function() {})
+  })
+  t.throws(function () {
+    g.sync('*', function() {})
+  })
+  t.throws(function () {
+    g.sync('*', {}, function() {})
+  })
+
+  t.throws(function () {
+    g.Glob('*', {sync:true}, function() {})
+  })
+
+  t.throws(function () {
+    g.GlobSync('*', {}, function() {})
+  })
+
+  t.throws(function () {
+    g.GlobSync('*', function() {})
+  })
+
+  t.throws(function () {
+    g.GlobSync()
+  })
+
+  t.throws(function () {
+    g.sync()
+  })
+
+  t.throws(function () {
+    g()
+  })
+
+  t.throws(function () {
+    g.Glob()
+  })
+
+  t.end()
+})
+
+
diff --git a/test/zz-cleanup.js b/test/zz-cleanup.js
index e085f0f..f404df3 100644
--- a/test/zz-cleanup.js
+++ b/test/zz-cleanup.js
@@ -1,10 +1,11 @@
+require("./global-leakage.js")
 // remove the fixtures
 var tap = require("tap")
 , rimraf = require("rimraf")
 , path = require("path")
 
 tap.test("cleanup fixtures", function (t) {
-  rimraf(path.resolve(__dirname, "a"), function (er) {
+  rimraf(path.resolve(__dirname, "fixtures"), function (er) {
     t.ifError(er, "removed")
     t.end()
   })

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



More information about the Pkg-javascript-commits mailing list