mirror of https://github.com/lukechilds/node.git
isaacs
12 years ago
10 changed files with 597 additions and 16 deletions
@ -0,0 +1,27 @@ |
|||
Copyright (c) Isaac Z. Schlueter ("Author") |
|||
All rights reserved. |
|||
|
|||
The BSD License |
|||
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions |
|||
are met: |
|||
|
|||
1. Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
|
|||
2. Redistributions in binary form must reproduce the above copyright |
|||
notice, this list of conditions and the following disclaimer in the |
|||
documentation and/or other materials provided with the distribution. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
|||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS |
|||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
|||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
|||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
|||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
|||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,41 @@ |
|||
var test = require('tap').test |
|||
var fs = require('../graceful-fs.js') |
|||
|
|||
test('open an existing file works', function (t) { |
|||
var start = fs._curOpen |
|||
var fd = fs.openSync(__filename, 'r') |
|||
t.equal(fs._curOpen, start + 1) |
|||
fs.closeSync(fd) |
|||
t.equal(fs._curOpen, start) |
|||
fs.open(__filename, 'r', function (er, fd) { |
|||
if (er) throw er |
|||
t.equal(fs._curOpen, start + 1) |
|||
fs.close(fd, function (er) { |
|||
if (er) throw er |
|||
t.equal(fs._curOpen, start) |
|||
t.end() |
|||
}) |
|||
}) |
|||
}) |
|||
|
|||
test('open a non-existing file throws', function (t) { |
|||
var start = fs._curOpen |
|||
var er |
|||
try { |
|||
var fd = fs.openSync('this file does not exist', 'r') |
|||
} catch (x) { |
|||
er = x |
|||
} |
|||
t.ok(er, 'should throw') |
|||
t.notOk(fd, 'should not get an fd') |
|||
t.equal(er.code, 'ENOENT') |
|||
t.equal(fs._curOpen, start) |
|||
|
|||
fs.open('neither does this file', 'r', function (er, fd) { |
|||
t.ok(er, 'should throw') |
|||
t.notOk(fd, 'should not get an fd') |
|||
t.equal(er.code, 'ENOENT') |
|||
t.equal(fs._curOpen, start) |
|||
t.end() |
|||
}) |
|||
}) |
@ -0,0 +1,27 @@ |
|||
Copyright (c) Isaac Z. Schlueter ("Author") |
|||
All rights reserved. |
|||
|
|||
The BSD License |
|||
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions |
|||
are met: |
|||
|
|||
1. Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
|
|||
2. Redistributions in binary form must reproduce the above copyright |
|||
notice, this list of conditions and the following disclaimer in the |
|||
documentation and/or other materials provided with the distribution. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
|||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS |
|||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
|||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
|||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
|||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
|||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,53 @@ |
|||
# sigmund |
|||
|
|||
Quick and dirty signatures for Objects. |
|||
|
|||
This is like a much faster `deepEquals` comparison, which returns a |
|||
string key suitable for caches and the like. |
|||
|
|||
## Usage |
|||
|
|||
```javascript |
|||
function doSomething (someObj) { |
|||
var key = sigmund(someObj, maxDepth) // max depth defaults to 10 |
|||
var cached = cache.get(key) |
|||
if (cached) return cached) |
|||
|
|||
var result = expensiveCalculation(someObj) |
|||
cache.set(key, result) |
|||
return result |
|||
} |
|||
``` |
|||
|
|||
The resulting key will be as unique and reproducible as calling |
|||
`JSON.stringify` or `util.inspect` on the object, but is much faster. |
|||
In order to achieve this speed, some differences are glossed over. |
|||
For example, the object `{0:'foo'}` will be treated identically to the |
|||
array `['foo']`. |
|||
|
|||
Also, just as there is no way to summon the soul from the scribblings |
|||
of a cocain-addled psychoanalyst, there is no way to revive the object |
|||
from the signature string that sigmund gives you. In fact, it's |
|||
barely even readable. |
|||
|
|||
As with `sys.inspect` and `JSON.stringify`, larger objects will |
|||
produce larger signature strings. |
|||
|
|||
Because sigmund is a bit less strict than the more thorough |
|||
alternatives, the strings will be shorter, and also there is a |
|||
slightly higher chance for collisions. For example, these objects |
|||
have the same signature: |
|||
|
|||
var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]} |
|||
var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']} |
|||
|
|||
Like a good Freudian, sigmund is most effective when you already have |
|||
some understanding of what you're looking for. It can help you help |
|||
yourself, but you must be willing to do some work as well. |
|||
|
|||
Cycles are handled, and cyclical objects are silently omitted (though |
|||
the key is included in the signature output.) |
|||
|
|||
The second argument is the maximum depth, which defaults to 10, |
|||
because that is the maximum object traversal depth covered by most |
|||
insurance carriers. |
@ -0,0 +1,283 @@ |
|||
// different ways to id objects
|
|||
// use a req/res pair, since it's crazy deep and cyclical
|
|||
|
|||
// sparseFE10 and sigmund are usually pretty close, which is to be expected,
|
|||
// since they are essentially the same algorithm, except that sigmund handles
|
|||
// regular expression objects properly.
|
|||
|
|||
|
|||
var http = require('http') |
|||
var util = require('util') |
|||
var sigmund = require('./sigmund.js') |
|||
var sreq, sres, creq, cres, test |
|||
|
|||
http.createServer(function (q, s) { |
|||
sreq = q |
|||
sres = s |
|||
sres.end('ok') |
|||
this.close(function () { setTimeout(function () { |
|||
start() |
|||
}, 200) }) |
|||
}).listen(1337, function () { |
|||
creq = http.get({ port: 1337 }) |
|||
creq.on('response', function (s) { cres = s }) |
|||
}) |
|||
|
|||
function start () { |
|||
test = [sreq, sres, creq, cres] |
|||
// test = sreq
|
|||
// sreq.sres = sres
|
|||
// sreq.creq = creq
|
|||
// sreq.cres = cres
|
|||
|
|||
for (var i in exports.compare) { |
|||
console.log(i) |
|||
var hash = exports.compare[i]() |
|||
console.log(hash) |
|||
console.log(hash.length) |
|||
console.log('') |
|||
} |
|||
|
|||
require('bench').runMain() |
|||
} |
|||
|
|||
function customWs (obj, md, d) { |
|||
d = d || 0 |
|||
var to = typeof obj |
|||
if (to === 'undefined' || to === 'function' || to === null) return '' |
|||
if (d > md || !obj || to !== 'object') return ('' + obj).replace(/[\n ]+/g, '') |
|||
|
|||
if (Array.isArray(obj)) { |
|||
return obj.map(function (i, _, __) { |
|||
return customWs(i, md, d + 1) |
|||
}).reduce(function (a, b) { return a + b }, '') |
|||
} |
|||
|
|||
var keys = Object.keys(obj) |
|||
return keys.map(function (k, _, __) { |
|||
return k + ':' + customWs(obj[k], md, d + 1) |
|||
}).reduce(function (a, b) { return a + b }, '') |
|||
} |
|||
|
|||
function custom (obj, md, d) { |
|||
d = d || 0 |
|||
var to = typeof obj |
|||
if (to === 'undefined' || to === 'function' || to === null) return '' |
|||
if (d > md || !obj || to !== 'object') return '' + obj |
|||
|
|||
if (Array.isArray(obj)) { |
|||
return obj.map(function (i, _, __) { |
|||
return custom(i, md, d + 1) |
|||
}).reduce(function (a, b) { return a + b }, '') |
|||
} |
|||
|
|||
var keys = Object.keys(obj) |
|||
return keys.map(function (k, _, __) { |
|||
return k + ':' + custom(obj[k], md, d + 1) |
|||
}).reduce(function (a, b) { return a + b }, '') |
|||
} |
|||
|
|||
function sparseFE2 (obj, maxDepth) { |
|||
var seen = [] |
|||
var soFar = '' |
|||
function ch (v, depth) { |
|||
if (depth > maxDepth) return |
|||
if (typeof v === 'function' || typeof v === 'undefined') return |
|||
if (typeof v !== 'object' || !v) { |
|||
soFar += v |
|||
return |
|||
} |
|||
if (seen.indexOf(v) !== -1 || depth === maxDepth) return |
|||
seen.push(v) |
|||
soFar += '{' |
|||
Object.keys(v).forEach(function (k, _, __) { |
|||
// pseudo-private values. skip those.
|
|||
if (k.charAt(0) === '_') return |
|||
var to = typeof v[k] |
|||
if (to === 'function' || to === 'undefined') return |
|||
soFar += k + ':' |
|||
ch(v[k], depth + 1) |
|||
}) |
|||
soFar += '}' |
|||
} |
|||
ch(obj, 0) |
|||
return soFar |
|||
} |
|||
|
|||
function sparseFE (obj, maxDepth) { |
|||
var seen = [] |
|||
var soFar = '' |
|||
function ch (v, depth) { |
|||
if (depth > maxDepth) return |
|||
if (typeof v === 'function' || typeof v === 'undefined') return |
|||
if (typeof v !== 'object' || !v) { |
|||
soFar += v |
|||
return |
|||
} |
|||
if (seen.indexOf(v) !== -1 || depth === maxDepth) return |
|||
seen.push(v) |
|||
soFar += '{' |
|||
Object.keys(v).forEach(function (k, _, __) { |
|||
// pseudo-private values. skip those.
|
|||
if (k.charAt(0) === '_') return |
|||
var to = typeof v[k] |
|||
if (to === 'function' || to === 'undefined') return |
|||
soFar += k |
|||
ch(v[k], depth + 1) |
|||
}) |
|||
} |
|||
ch(obj, 0) |
|||
return soFar |
|||
} |
|||
|
|||
function sparse (obj, maxDepth) { |
|||
var seen = [] |
|||
var soFar = '' |
|||
function ch (v, depth) { |
|||
if (depth > maxDepth) return |
|||
if (typeof v === 'function' || typeof v === 'undefined') return |
|||
if (typeof v !== 'object' || !v) { |
|||
soFar += v |
|||
return |
|||
} |
|||
if (seen.indexOf(v) !== -1 || depth === maxDepth) return |
|||
seen.push(v) |
|||
soFar += '{' |
|||
for (var k in v) { |
|||
// pseudo-private values. skip those.
|
|||
if (k.charAt(0) === '_') continue |
|||
var to = typeof v[k] |
|||
if (to === 'function' || to === 'undefined') continue |
|||
soFar += k |
|||
ch(v[k], depth + 1) |
|||
} |
|||
} |
|||
ch(obj, 0) |
|||
return soFar |
|||
} |
|||
|
|||
function noCommas (obj, maxDepth) { |
|||
var seen = [] |
|||
var soFar = '' |
|||
function ch (v, depth) { |
|||
if (depth > maxDepth) return |
|||
if (typeof v === 'function' || typeof v === 'undefined') return |
|||
if (typeof v !== 'object' || !v) { |
|||
soFar += v |
|||
return |
|||
} |
|||
if (seen.indexOf(v) !== -1 || depth === maxDepth) return |
|||
seen.push(v) |
|||
soFar += '{' |
|||
for (var k in v) { |
|||
// pseudo-private values. skip those.
|
|||
if (k.charAt(0) === '_') continue |
|||
var to = typeof v[k] |
|||
if (to === 'function' || to === 'undefined') continue |
|||
soFar += k + ':' |
|||
ch(v[k], depth + 1) |
|||
} |
|||
soFar += '}' |
|||
} |
|||
ch(obj, 0) |
|||
return soFar |
|||
} |
|||
|
|||
|
|||
function flatten (obj, maxDepth) { |
|||
var seen = [] |
|||
var soFar = '' |
|||
function ch (v, depth) { |
|||
if (depth > maxDepth) return |
|||
if (typeof v === 'function' || typeof v === 'undefined') return |
|||
if (typeof v !== 'object' || !v) { |
|||
soFar += v |
|||
return |
|||
} |
|||
if (seen.indexOf(v) !== -1 || depth === maxDepth) return |
|||
seen.push(v) |
|||
soFar += '{' |
|||
for (var k in v) { |
|||
// pseudo-private values. skip those.
|
|||
if (k.charAt(0) === '_') continue |
|||
var to = typeof v[k] |
|||
if (to === 'function' || to === 'undefined') continue |
|||
soFar += k + ':' |
|||
ch(v[k], depth + 1) |
|||
soFar += ',' |
|||
} |
|||
soFar += '}' |
|||
} |
|||
ch(obj, 0) |
|||
return soFar |
|||
} |
|||
|
|||
exports.compare = |
|||
{ |
|||
// 'custom 2': function () {
|
|||
// return custom(test, 2, 0)
|
|||
// },
|
|||
// 'customWs 2': function () {
|
|||
// return customWs(test, 2, 0)
|
|||
// },
|
|||
'JSON.stringify (guarded)': function () { |
|||
var seen = [] |
|||
return JSON.stringify(test, function (k, v) { |
|||
if (typeof v !== 'object' || !v) return v |
|||
if (seen.indexOf(v) !== -1) return undefined |
|||
seen.push(v) |
|||
return v |
|||
}) |
|||
}, |
|||
|
|||
'flatten 10': function () { |
|||
return flatten(test, 10) |
|||
}, |
|||
|
|||
// 'flattenFE 10': function () {
|
|||
// return flattenFE(test, 10)
|
|||
// },
|
|||
|
|||
'noCommas 10': function () { |
|||
return noCommas(test, 10) |
|||
}, |
|||
|
|||
'sparse 10': function () { |
|||
return sparse(test, 10) |
|||
}, |
|||
|
|||
'sparseFE 10': function () { |
|||
return sparseFE(test, 10) |
|||
}, |
|||
|
|||
'sparseFE2 10': function () { |
|||
return sparseFE2(test, 10) |
|||
}, |
|||
|
|||
sigmund: function() { |
|||
return sigmund(test, 10) |
|||
}, |
|||
|
|||
|
|||
// 'util.inspect 1': function () {
|
|||
// return util.inspect(test, false, 1, false)
|
|||
// },
|
|||
// 'util.inspect undefined': function () {
|
|||
// util.inspect(test)
|
|||
// },
|
|||
// 'util.inspect 2': function () {
|
|||
// util.inspect(test, false, 2, false)
|
|||
// },
|
|||
// 'util.inspect 3': function () {
|
|||
// util.inspect(test, false, 3, false)
|
|||
// },
|
|||
// 'util.inspect 4': function () {
|
|||
// util.inspect(test, false, 4, false)
|
|||
// },
|
|||
// 'util.inspect Infinity': function () {
|
|||
// util.inspect(test, false, Infinity, false)
|
|||
// }
|
|||
} |
|||
|
|||
/** results |
|||
**/ |
@ -0,0 +1,38 @@ |
|||
{ |
|||
"name": "sigmund", |
|||
"version": "1.0.0", |
|||
"description": "Quick and dirty signatures for Objects.", |
|||
"main": "sigmund.js", |
|||
"directories": { |
|||
"test": "test" |
|||
}, |
|||
"dependencies": {}, |
|||
"devDependencies": { |
|||
"tap": "~0.3.0" |
|||
}, |
|||
"scripts": { |
|||
"test": "tap test/*.js", |
|||
"bench": "node bench.js" |
|||
}, |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "git://github.com/isaacs/sigmund" |
|||
}, |
|||
"keywords": [ |
|||
"object", |
|||
"signature", |
|||
"key", |
|||
"data", |
|||
"psychoanalysis" |
|||
], |
|||
"author": { |
|||
"name": "Isaac Z. Schlueter", |
|||
"email": "i@izs.me", |
|||
"url": "http://blog.izs.me/" |
|||
}, |
|||
"license": "BSD", |
|||
"readme": "# sigmund\n\nQuick and dirty signatures for Objects.\n\nThis is like a much faster `deepEquals` comparison, which returns a\nstring key suitable for caches and the like.\n\n## Usage\n\n```javascript\nfunction doSomething (someObj) {\n var key = sigmund(someObj, maxDepth) // max depth defaults to 10\n var cached = cache.get(key)\n if (cached) return cached)\n\n var result = expensiveCalculation(someObj)\n cache.set(key, result)\n return result\n}\n```\n\nThe resulting key will be as unique and reproducible as calling\n`JSON.stringify` or `util.inspect` on the object, but is much faster.\nIn order to achieve this speed, some differences are glossed over.\nFor example, the object `{0:'foo'}` will be treated identically to the\narray `['foo']`.\n\nAlso, just as there is no way to summon the soul from the scribblings\nof a cocain-addled psychoanalyst, there is no way to revive the object\nfrom the signature string that sigmund gives you. In fact, it's\nbarely even readable.\n\nAs with `sys.inspect` and `JSON.stringify`, larger objects will\nproduce larger signature strings.\n\nBecause sigmund is a bit less strict than the more thorough\nalternatives, the strings will be shorter, and also there is a\nslightly higher chance for collisions. For example, these objects\nhave the same signature:\n\n var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]}\n var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']}\n\nLike a good Freudian, sigmund is most effective when you already have\nsome understanding of what you're looking for. It can help you help\nyourself, but you must be willing to do some work as well.\n\nCycles are handled, and cyclical objects are silently omitted (though\nthe key is included in the signature output.)\n\nThe second argument is the maximum depth, which defaults to 10,\nbecause that is the maximum object traversal depth covered by most\ninsurance carriers.\n", |
|||
"readmeFilename": "README.md", |
|||
"_id": "sigmund@1.0.0", |
|||
"_from": "sigmund@~1.0.0" |
|||
} |
@ -0,0 +1,39 @@ |
|||
module.exports = sigmund |
|||
function sigmund (subject, maxSessions) { |
|||
maxSessions = maxSessions || 10; |
|||
var notes = []; |
|||
var analysis = ''; |
|||
var RE = RegExp; |
|||
|
|||
function psychoAnalyze (subject, session) { |
|||
if (session > maxSessions) return; |
|||
|
|||
if (typeof subject === 'function' || |
|||
typeof subject === 'undefined') { |
|||
return; |
|||
} |
|||
|
|||
if (typeof subject !== 'object' || !subject || |
|||
(subject instanceof RE)) { |
|||
analysis += subject; |
|||
return; |
|||
} |
|||
|
|||
if (notes.indexOf(subject) !== -1 || session === maxSessions) return; |
|||
|
|||
notes.push(subject); |
|||
analysis += '{'; |
|||
Object.keys(subject).forEach(function (issue, _, __) { |
|||
// pseudo-private values. skip those.
|
|||
if (issue.charAt(0) === '_') return; |
|||
var to = typeof subject[issue]; |
|||
if (to === 'function' || to === 'undefined') return; |
|||
analysis += issue; |
|||
psychoAnalyze(subject[issue], session + 1); |
|||
}); |
|||
} |
|||
psychoAnalyze(subject, 0); |
|||
return analysis; |
|||
} |
|||
|
|||
// vim: set softtabstop=4 shiftwidth=4:
|
@ -0,0 +1,24 @@ |
|||
var test = require('tap').test |
|||
var sigmund = require('../sigmund.js') |
|||
|
|||
|
|||
// occasionally there are duplicates
|
|||
// that's an acceptable edge-case. JSON.stringify and util.inspect
|
|||
// have some collision potential as well, though less, and collision
|
|||
// detection is expensive.
|
|||
var hash = '{abc/def/g{0h1i2{jkl' |
|||
var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]} |
|||
var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']} |
|||
|
|||
var obj3 = JSON.parse(JSON.stringify(obj1)) |
|||
obj3.c = /def/ |
|||
obj3.g[2].cycle = obj3 |
|||
var cycleHash = '{abc/def/g{0h1i2{jklcycle' |
|||
|
|||
test('basic', function (t) { |
|||
t.equal(sigmund(obj1), hash) |
|||
t.equal(sigmund(obj2), hash) |
|||
t.equal(sigmund(obj3), cycleHash) |
|||
t.end() |
|||
}) |
|||
|
@ -0,0 +1,63 @@ |
|||
var test = require("tap").test |
|||
var glob = require('../') |
|||
process.chdir(__dirname) |
|||
|
|||
test("mark, no / on pattern", function (t) { |
|||
glob("a/*", {mark: true}, function (er, results) { |
|||
if (er) |
|||
throw er |
|||
t.same(results, [ 'a/abcdef/', |
|||
'a/abcfed/', |
|||
'a/b/', |
|||
'a/bc/', |
|||
'a/c/', |
|||
'a/cb/', |
|||
'a/symlink/' ]) |
|||
t.end() |
|||
}) |
|||
}) |
|||
|
|||
test("mark=false, no / on pattern", function (t) { |
|||
glob("a/*", function (er, results) { |
|||
if (er) |
|||
throw er |
|||
t.same(results, [ 'a/abcdef', |
|||
'a/abcfed', |
|||
'a/b', |
|||
'a/bc', |
|||
'a/c', |
|||
'a/cb', |
|||
'a/symlink' ]) |
|||
t.end() |
|||
}) |
|||
}) |
|||
|
|||
test("mark=true, / on pattern", function (t) { |
|||
glob("a/*/", {mark: true}, function (er, results) { |
|||
if (er) |
|||
throw er |
|||
t.same(results, [ 'a/abcdef/', |
|||
'a/abcfed/', |
|||
'a/b/', |
|||
'a/bc/', |
|||
'a/c/', |
|||
'a/cb/', |
|||
'a/symlink/' ]) |
|||
t.end() |
|||
}) |
|||
}) |
|||
|
|||
test("mark=false, / on pattern", function (t) { |
|||
glob("a/*/", function (er, results) { |
|||
if (er) |
|||
throw er |
|||
t.same(results, [ 'a/abcdef/', |
|||
'a/abcfed/', |
|||
'a/b/', |
|||
'a/bc/', |
|||
'a/c/', |
|||
'a/cb/', |
|||
'a/symlink/' ]) |
|||
t.end() |
|||
}) |
|||
}) |
Loading…
Reference in new issue