[Pkg-javascript-commits] [node-tmp] 01/05: Imported Upstream version 0.0.25

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Tue Mar 10 18:50:55 UTC 2015


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

sebastic pushed a commit to branch master
in repository node-tmp.

commit d1460f9ff5d8f71d157d1100a8d7505d04aead6a
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Tue Mar 10 19:31:45 2015 +0100

    Imported Upstream version 0.0.25
---
 .travis.yml            |   1 +
 LICENSE                |  21 +++
 README.md              | 130 +++++++++++++++++--
 lib/tmp.js             | 343 ++++++++++++++++++++++++++++++++++++-------------
 package.json           |   2 +-
 test/base.js           |  74 ++++++++++-
 test/dir-sync-test.js  | 214 ++++++++++++++++++++++++++++++
 test/dir-test.js       |  21 ++-
 test/file-sync-test.js | 190 +++++++++++++++++++++++++++
 test/file-test.js      |  20 ++-
 test/graceful-sync.js  |  20 +++
 test/graceful.js       |   2 +-
 test/keep-sync.js      |  12 ++
 test/spawn-sync.js     |  32 +++++
 test/unsafe-sync.js    |  31 +++++
 15 files changed, 996 insertions(+), 117 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 0175d82..57ed561 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,3 +3,4 @@ node_js:
   - "0.6"
   - "0.8"
   - "0.10"
+  - "0.12"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..72418bd
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 KARASZI István
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 3a1a509..2e9da33 100644
--- a/README.md
+++ b/README.md
@@ -11,8 +11,12 @@ aggressively checks for the existence of the newly created temporary file and
 creates the new file with `O_EXCL` instead of simple `O_CREAT | O_RDRW`, so it
 is safer.
 
-The API is slightly different as well, Tmp does not yet provide synchronous
-calls and all the parameters are optional.
+Tmp offers both an asynchronous and a synchronous API. For all API calls, all
+the parameters are optional.
+
+Tmp uses crypto for determining random file names, or, when using templates,
+a six letter random identifier. And just in case that you do not have that much
+entropy left on your system, Tmp will fall back to pseudo random numbers.
 
 You can set whether you want to remove the temporary file on process exit or
 not, and the destination directory can also be set.
@@ -25,22 +29,48 @@ npm install tmp
 
 ## Usage
 
-### File creation
+### Asynchronous file creation
 
-Simple temporary file creation, the file will be unlinked on process exit.
+Simple temporary file creation, the file will be closed and unlinked on process exit.
 
 ```javascript
 var tmp = require('tmp');
 
-tmp.file(function _tempFileCreated(err, path, fd) {
+tmp.file(function _tempFileCreated(err, path, fd, cleanupCallback) {
   if (err) throw err;
 
   console.log("File: ", path);
   console.log("Filedescriptor: ", fd);
+  
+  // If we don't need the file anymore we could manually call the cleanupCallback
+  // But that is not necessary if we didn't pass the keep option because the library
+  // will clean after itself.
+  cleanupCallback();
 });
 ```
 
-### Directory creation
+### Synchronous file creation
+
+A synchronous version of the above.
+
+```javascript
+var tmp = require('tmp');
+
+var tmpobj = tmp.fileSync();
+console.log("File: ", tmpobj.name);
+console.log("Filedescriptor: ", tmpobj.fd);
+  
+// If we don't need the file anymore we could manually call the removeCallback
+// But that is not necessary if we didn't pass the keep option because the library
+// will clean after itself.
+tmpobj.removeCallback();
+```
+
+Note that this might throw an exception if either the maximum limit of retries
+for creating a temporary name fails, or, in case that you do not have the permission
+to write to the directory where the temporary file should be created in.
+
+### Asynchronous directory creation
 
 Simple temporary directory creation, it will be removed on process exit.
 
@@ -49,17 +79,37 @@ If the directory still contains items on process exit, then it won't be removed.
 ```javascript
 var tmp = require('tmp');
 
-tmp.dir(function _tempDirCreated(err, path) {
+tmp.dir(function _tempDirCreated(err, path, cleanupCallback) {
   if (err) throw err;
 
   console.log("Dir: ", path);
+  
+  // Manual cleanup
+  cleanupCallback();
 });
 ```
 
 If you want to cleanup the directory even when there are entries in it, then
 you can pass the `unsafeCleanup` option when creating it.
 
-### Filename generation
+### Synchronous directory creation
+
+A synchronous version of the above.
+
+```javascript
+var tmp = require('tmp');
+
+var tmpobj = tmp.dirSync();
+console.log("Dir: ", tmpobj.name);
+// Manual cleanup
+tmpobj.removeCallback();
+```
+
+Note that this might throw an exception if either the maximum limit of retries
+for creating a temporary name fails, or, in case that you do not have the permission
+to write to the directory where the temporary directory should be created in.
+
+### Asynchronous filename generation
 
 It is possible with this library to generate a unique filename in the specified
 directory.
@@ -74,9 +124,20 @@ tmp.tmpName(function _tempNameGenerated(err, path) {
 });
 ```
 
+### Synchronous filename generation
+
+A synchrounous version of the above.
+
+```javascript
+var tmp = require('tmp');
+
+var name = tmp.tmpNameSync();
+console.log("Created temporary filename: ", name);
+```
+
 ## Advanced usage
 
-### File creation
+### Asynchronous file creation
 
 Creates a file with mode `0644`, prefix will be `prefix-` and postfix will be `.txt`.
 
@@ -91,7 +152,19 @@ tmp.file({ mode: 0644, prefix: 'prefix-', postfix: '.txt' }, function _tempFileC
 });
 ```
 
-### Directory creation
+### Synchronous file creation
+
+A synchronous version of the above.
+
+```javascript
+var tmp = require('tmp');
+
+var tmpobj = tmp.fileSync({ mode: 0644, prefix: 'prefix-', postfix: '.txt' });
+console.log("File: ", tmpobj.name);
+console.log("Filedescriptor: ", tmpobj.fd);
+```
+
+### Asynchronous directory creation
 
 Creates a directory with mode `0755`, prefix will be `myTmpDir_`.
 
@@ -105,7 +178,18 @@ tmp.dir({ mode: 0750, prefix: 'myTmpDir_' }, function _tempDirCreated(err, path)
 });
 ```
 
-### mkstemps like
+### Synchronous directory creation
+
+Again, a synchronous version of the above.
+
+```javascript
+var tmp = require('tmp');
+
+var tmpobj = tmp.dirSync({ mode: 0750, prefix: 'myTmpDir_' });
+console.log("Dir: ", tmpobj.name);
+```
+
+### mkstemps like, asynchronously
 
 Creates a new temporary directory with mode `0700` and filename like `/tmp/tmp-nk2J1u`.
 
@@ -119,7 +203,18 @@ tmp.dir({ template: '/tmp/tmp-XXXXXX' }, function _tempDirCreated(err, path) {
 });
 ```
 
-### Filename generation
+### mkstemps like, synchronously
+
+This will behave similarly to the asynchronous version.
+
+```javascript
+var tmp = require('tmp');
+
+var tmpobj = tmp.dirSync({ template: '/tmp/tmp-XXXXXX' });
+console.log("Dir: ", tmpobj.name);
+```
+
+### Asynchronous filename generation
 
 The `tmpName()` function accepts the `prefix`, `postfix`, `dir`, etc. parameters also:
 
@@ -133,6 +228,16 @@ tmp.tmpName({ template: '/tmp/tmp-XXXXXX' }, function _tempNameGenerated(err, pa
 });
 ```
 
+### Synchronous filename generation
+
+The `tmpNameSync()` function works similarly to `tmpName()`.
+
+```javascript
+var tmp = require('tmp');
+var tmpname = tmp.tmpNameSync({ template: '/tmp/tmp-XXXXXX' });
+console.log("Created temporary filename: ", tmpname);
+```
+
 ## Graceful cleanup
 
 One may want to cleanup the temporary files even when an uncaught exception
@@ -155,6 +260,7 @@ All options are optional :)
   * `dir`: the optional temporary directory, fallbacks to system default (guesses from environment)
   * `tries`: how many times should the function try to get a unique filename before giving up, default `3`
   * `keep`: signals that the temporary file or directory should not be deleted on exit, default is `false`, means delete
+    * Please keep in mind that it is recommended in this case to call the provided `cleanupCallback` function manually.
   * `unsafeCleanup`: recursively removes the created temporary directory, even when it's not empty. default is `false`
 
 [1]: http://nodejs.org/
diff --git a/lib/tmp.js b/lib/tmp.js
index ea84faa..b59ba34 100644
--- a/lib/tmp.js
+++ b/lib/tmp.js
@@ -1,7 +1,7 @@
 /*!
  * Tmp
  *
- * Copyright (c) 2011-2013 KARASZI Istvan <github at spam.raszi.hu>
+ * Copyright (c) 2011-2015 KARASZI Istvan <github at spam.raszi.hu>
  *
  * MIT Licensed
  */
@@ -13,10 +13,13 @@ var
   fs     = require('fs'),
   path   = require('path'),
   os     = require('os'),
+  crypto = require('crypto'),
   exists = fs.exists || path.exists,
+  existsSync = fs.existsSync || path.existsSync,
   tmpDir = os.tmpDir || _getTMPDir,
   _c     = require('constants');
 
+
 /**
  * The working inner variables.
  */
@@ -25,8 +28,16 @@ var
   _TMP = tmpDir(),
 
   // the random characters to choose from
-  randomChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz",
-  randomCharsLength = randomChars.length,
+  RANDOM_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
+
+  TEMPLATE_PATTERN = /XXXXXX/,
+
+  DEFAULT_TRIES = 3,
+
+  CREATE_FLAGS = _c.O_CREAT | _c.O_EXCL | _c.O_RDWR,
+
+  DIR_MODE = 448 /* 0700 */,
+  FILE_MODE = 384 /* 0600 */,
 
   // this will hold the objects need to be removed on exit
   _removeObjects = [],
@@ -35,13 +46,40 @@ var
   _uncaughtException = false;
 
 /**
+ * Random name generator based on crypto.
+ * Adapted from http://blog.tompawlak.org/how-to-generate-random-values-nodejs-javascript
+ *
+ * @param {Number} howMany
+ * @return {String}
+ * @api private
+ */
+function _randomChars(howMany) {
+  var
+    value = [],
+    rnd = null;
+
+  // make sure that we do not fail because we ran out of entropy
+  try {
+    rnd = crypto.randomBytes(howMany);
+  } catch (e) {
+    rnd = crypto.pseudoRandomBytes(howMany);
+  }
+
+  for (var i = 0; i < howMany; i++) {
+    value.push(RANDOM_CHARS[rnd[i] % RANDOM_CHARS.length]);
+  }
+
+  return value.join('');
+}
+
+/**
  * Gets the temp directory.
  *
  * @return {String}
  * @api private
  */
 function _getTMPDir() {
-  var tmpNames = [ 'TMPDIR', 'TMP', 'TEMP' ];
+  var tmpNames = ['TMPDIR', 'TMP', 'TEMP'];
 
   for (var i = 0, length = tmpNames.length; i < length; i++) {
     if (_isUndefined(process.env[tmpNames[i]])) continue;
@@ -74,19 +112,51 @@ function _isUndefined(obj) {
  * @api private
  */
 function _parseArguments(options, callback) {
-  if (!callback || typeof callback != "function") {
-    callback = options;
+  if (typeof options == 'function') {
+    var
+      tmp = options;
+      options = callback || {};
+      callback = tmp;
+  } else if (typeof options == 'undefined') {
     options = {};
   }
 
-  return [ options, callback ];
+  return [options, callback];
 }
 
 /**
- * Gets a temporary file name.
+ * Generates a new temporary name.
  *
  * @param {Object} opts
- * @param {Function} cb
+ * @returns {String}
+ * @api private
+ */
+function _generateTmpName(opts) {
+  if (opts.name) {
+    return path.join(opts.dir || _TMP, opts.name);
+  }
+
+  // mkstemps like template
+  if (opts.template) {
+    return opts.template.replace(TEMPLATE_PATTERN, _randomChars(6));
+  }
+
+  // prefix and postfix
+  var name = [
+    opts.prefix || 'tmp-',
+    process.pid,
+    _randomChars(12),
+    opts.postfix || ''
+  ].join('');
+
+  return path.join(opts.dir || _TMP, name);
+}
+
+/**
+ * Gets a temporary file name.
+ *
+ * @param {Object} options
+ * @param {Function} callback
  * @api private
  */
 function _getTmpName(options, callback) {
@@ -94,49 +164,23 @@ function _getTmpName(options, callback) {
     args = _parseArguments(options, callback),
     opts = args[0],
     cb = args[1],
-    template = opts.template,
-    templateDefined = !_isUndefined(template),
-    tries = opts.tries || 3;
+    tries = opts.tries || DEFAULT_TRIES;
 
   if (isNaN(tries) || tries < 0)
     return cb(new Error('Invalid tries'));
 
-  if (templateDefined && !template.match(/XXXXXX/))
+  if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
     return cb(new Error('Invalid template provided'));
 
-  function _getName() {
-
-    // prefix and postfix
-    if (!templateDefined) {
-      var name = [
-        (_isUndefined(opts.prefix)) ? 'tmp-' : opts.prefix,
-        process.pid,
-        (Math.random() * 0x1000000000).toString(36),
-        opts.postfix
-      ].join('');
-
-      return path.join(opts.dir || _TMP, name);
-    }
-
-    // mkstemps like template
-    var chars = [];
-
-    for (var i = 0; i < 6; i++) {
-      chars.push(randomChars.substr(Math.floor(Math.random() * randomCharsLength), 1));
-    }
-
-    return template.replace(/XXXXXX/, chars.join(''));
-  }
-
   (function _getUniqueName() {
-    var name = _getName();
+    var name = _generateTmpName(opts);
 
     // check whether the path exists then retry if needed
     exists(name, function _pathExists(pathExists) {
       if (pathExists) {
         if (tries-- > 0) return _getUniqueName();
 
-        return cb(new Error('Could not get a unique tmp filename, max tries reached'));
+        return cb(new Error('Could not get a unique tmp filename, max tries reached ' + name));
       }
 
       cb(null, name);
@@ -145,6 +189,35 @@ function _getTmpName(options, callback) {
 }
 
 /**
+ * Synchronous version of _getTmpName.
+ *
+ * @param {Object} options
+ * @returns {String}
+ * @api private
+ */
+function _getTmpNameSync(options) {
+  var
+    args = _parseArguments(options),
+    opts = args[0],
+    tries = opts.tries || DEFAULT_TRIES;
+
+  if (isNaN(tries) || tries < 0)
+    throw new Error('Invalid tries');
+
+  if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
+    throw new Error('Invalid template provided');
+
+  do {
+    var name = _generateTmpName(opts);
+    if (!existsSync(name)) {
+      return name;
+    }
+  } while (tries-- > 0);
+
+  throw new Error('Could not get a unique tmp filename, max tries reached');
+}
+
+/**
  * Creates and opens a temporary file.
  *
  * @param {Object} options
@@ -164,61 +237,71 @@ function _createTmpFile(options, callback) {
     if (err) return cb(err);
 
     // create and open the file
-    fs.open(name, _c.O_CREAT | _c.O_EXCL | _c.O_RDWR, opts.mode || 0600, function _fileCreated(err, fd) {
+    fs.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err, fd) {
       if (err) return cb(err);
 
-      var removeCallback = _prepareRemoveCallback(fs.unlinkSync.bind(fs), name);
-
-      if (!opts.keep) {
-        _removeObjects.unshift(removeCallback);
-      }
-
-      cb(null, name, fd, removeCallback);
+      cb(null, name, fd, _prepareTmpFileRemoveCallback(name, fd, opts));
     });
   });
 }
 
 /**
- * Removes files and folders in a directory recursively.
+ * Synchronous version of _createTmpFile.
  *
- * @param {String} dir
+ * @param {Object} options
+ * @returns {Object} object consists of name, fd and removeCallback
+ * @api private
  */
-function _rmdirRecursiveSync(dir) {
-  var files = fs.readdirSync(dir);
-
-  for (var i = 0, length = files.length; i < length; i++) {
-    var file = path.join(dir, files[i]);
-    // lstat so we don't recurse into symlinked directories.
-    var stat = fs.lstatSync(file);
-
-    if (stat.isDirectory()) {
-      _rmdirRecursiveSync(file);
-    } else {
-      fs.unlinkSync(file);
-    }
-  }
+function _createTmpFileSync(options) {
+  var
+    args = _parseArguments(options),
+    opts = args[0];
+
+    opts.postfix = opts.postfix || '.tmp';
+
+  var name = _getTmpNameSync(opts);
+  var fd = fs.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE);
 
-  fs.rmdirSync(dir);
+  return {
+    name : name,
+    fd : fd,
+    removeCallback : _prepareTmpFileRemoveCallback(name, fd, opts)
+  };
 }
 
 /**
+ * Removes files and folders in a directory recursively.
  *
- * @param {Function} removeFunction
- * @param {String} path
- * @returns {Function}
- * @private
+ * @param {String} root
+ * @api private
  */
-function _prepareRemoveCallback(removeFunction, path) {
-  var called = false;
-  return function() {
-    if (called) {
-      return;
+function _rmdirRecursiveSync(root) {
+  var dirs = [root];
+
+  do {
+    var
+      dir = dirs.pop(),
+      canRemove = true,
+      files = fs.readdirSync(dir);
+
+    for (var i = 0, length = files.length; i < length; i++) {
+      var
+        file = path.join(dir, files[i]),
+        stat = fs.lstatSync(file); // lstat so we don't recurse into symlinked directories
+
+      if (stat.isDirectory()) {
+        canRemove = false;
+        dirs.push(dir);
+        dirs.push(file);
+      } else {
+        fs.unlinkSync(file);
+      }
     }
 
-    removeFunction(path);
-
-    called = true;
-  };
+    if (canRemove) {
+      fs.rmdirSync(dir);
+    }
+  } while (dirs.length !== 0);
 }
 
 /**
@@ -239,26 +322,101 @@ function _createTmpDir(options, callback) {
     if (err) return cb(err);
 
     // create the directory
-    fs.mkdir(name, opts.mode || 0700, function _dirCreated(err) {
+    fs.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err) {
       if (err) return cb(err);
 
-      var removeCallback = _prepareRemoveCallback(
-        opts.unsafeCleanup
-          ? _rmdirRecursiveSync
-          : fs.rmdirSync.bind(fs),
-        name
-      );
-
-      if (!opts.keep) {
-        _removeObjects.unshift(removeCallback);
-      }
-
-      cb(null, name, removeCallback);
+      cb(null, name, _prepareTmpDirRemoveCallback(name, opts));
     });
   });
 }
 
 /**
+ * Synchronous version of _createTmpDir.
+ *
+ * @param {Object} options
+ * @returns {Object} object consists of name and removeCallback
+ * @api private
+ */
+function _createTmpDirSync(options) {
+  var
+    args = _parseArguments(options),
+    opts = args[0];
+
+  var name = _getTmpNameSync(opts);
+  fs.mkdirSync(name, opts.mode || DIR_MODE);
+
+  return {
+    name : name,
+    removeCallback : _prepareTmpDirRemoveCallback(name, opts)
+  };
+}
+
+/**
+ * Prepares the callback for removal of the temporary file.
+ *
+ * @param {String} name
+ * @param {int} fd
+ * @param {Object} opts
+ * @api private
+ * @returns {Function} the callback
+ */
+function _prepareTmpFileRemoveCallback(name, fd, opts) {
+  var removeCallback = _prepareRemoveCallback(function _removeCallback(fdPath) {
+    fs.closeSync(fdPath[0]);
+    fs.unlinkSync(fdPath[1]);
+  }, [fd, name]);
+
+  if (!opts.keep) {
+    _removeObjects.unshift(removeCallback);
+  }
+
+  return removeCallback;
+}
+
+/**
+ * Prepares the callback for removal of the temporary directory.
+ *
+ * @param {String} name
+ * @param {Object} opts
+ * @api private
+ * @returns {Function} the callback
+ */
+function _prepareTmpDirRemoveCallback(name, opts) {
+
+  var removeCallback = _prepareRemoveCallback(
+    opts.unsafeCleanup
+        ? _rmdirRecursiveSync
+        : fs.rmdirSync.bind(fs),
+    name);
+
+  if (!opts.keep) {
+    _removeObjects.unshift(removeCallback);
+  }
+
+  return removeCallback;
+}
+
+/**
+ * Creates a guarded function wrapping the removeFunction call.
+ *
+ * @param {Function} removeFunction
+ * @param {Object} arg
+ * @returns {Function}
+ * @api private
+ */
+function _prepareRemoveCallback(removeFunction, arg) {
+  var called = false;
+
+  return function _cleanupCallback() {
+    if (called) return;
+
+    removeFunction(arg);
+
+    called = true;
+  };
+}
+
+/**
  * The garbage collector.
  *
  * @api private
@@ -286,7 +444,7 @@ var version = process.versions.node.split('.').map(function (value) {
 });
 
 if (version[0] === 0 && (version[1] < 9 || version[1] === 9 && version[2] < 5)) {
-  process.addListener('uncaughtException', function _uncaughtExceptionThrown( err ) {
+  process.addListener('uncaughtException', function _uncaughtExceptionThrown(err) {
     _uncaughtException = true;
     _garbageCollector();
 
@@ -302,6 +460,9 @@ process.addListener('exit', function _exit(code) {
 // exporting all the needed methods
 module.exports.tmpdir = _TMP;
 module.exports.dir = _createTmpDir;
+module.exports.dirSync = _createTmpDirSync;
 module.exports.file = _createTmpFile;
+module.exports.fileSync = _createTmpFileSync;
 module.exports.tmpName = _getTmpName;
+module.exports.tmpNameSync = _getTmpNameSync;
 module.exports.setGracefulCleanup = _setGracefulCleanup;
diff --git a/package.json b/package.json
index eeb3127..9b01e9b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name":        "tmp",
-  "version":     "0.0.24",
+  "version":     "0.0.25",
   "description": "Temporary file and directory creator",
   "author":      "KARASZI István <github at spam.raszi.hu> (http://raszi.hu/)",
 
diff --git a/test/base.js b/test/base.js
index 498d8fb..00094c7 100644
--- a/test/base.js
+++ b/test/base.js
@@ -1,7 +1,12 @@
 var
   assert = require('assert'),
   path   = require('path'),
-  exec   = require('child_process').exec;
+  exec   = require('child_process').exec,
+  tmp    = require('../lib/tmp');
+
+// make sure that we do not test spam the global tmp
+tmp.TMP_DIR = './tmp';
+
 
 function _spawnTestWithError(testFile, params, cb) {
   _spawnTest(true, testFile, params, cb);
@@ -13,7 +18,6 @@ function _spawnTestWithoutError(testFile, params, cb) {
 
 function _spawnTest(passError, testFile, params, cb) {
   var
-    filename,
     node_path = process.argv[0],
     command = [ node_path, path.join(__dirname, testFile) ].concat(params).join(' ');
 
@@ -37,38 +41,100 @@ function _testStat(stat, mode) {
 }
 
 function _testPrefix(prefix) {
-  return function _testPrefixGenerated(err, name, fd) {
+  return function _testPrefixGenerated(err, name) {
     assert.equal(path.basename(name).slice(0, prefix.length), prefix, 'should have the provided prefix');
   };
 }
 
+function _testPrefixSync(prefix) {
+  return function _testPrefixGeneratedSync(result) {
+    if (result instanceof Error) {
+      throw result;
+    }
+    _testPrefix(prefix)(null, result.name, result.fd);
+  };
+}
+
 function _testPostfix(postfix) {
-  return function _testPostfixGenerated(err, name, fd) {
+  return function _testPostfixGenerated(err, name) {
     assert.equal(name.slice(name.length - postfix.length, name.length), postfix, 'should have the provided postfix');
   };
 }
 
+function _testPostfixSync(postfix) {
+  return function _testPostfixGeneratedSync(result) {
+    if (result instanceof Error) {
+      throw result;
+    }
+    _testPostfix(postfix)(null, result.name, result.fd);
+  };
+}
+
 function _testKeep(type, keep, cb) {
   _spawnTestWithError('keep.js', [ type, keep ], cb);
 }
 
+function _testKeepSync(type, keep, cb) {
+  _spawnTestWithError('keep-sync.js', [ type, keep ], cb);
+}
+
 function _testGraceful(type, graceful, cb) {
   _spawnTestWithoutError('graceful.js', [ type, graceful ], cb);
 }
 
+function _testGracefulSync(type, graceful, cb) {
+  _spawnTestWithoutError('graceful-sync.js', [ type, graceful ], cb);
+}
+
 function _assertName(err, name) {
   assert.isString(name);
   assert.isNotZero(name.length);
 }
 
+function _assertNameSync(result) {
+  if (result instanceof Error) {
+    throw result;
+  }
+  var name = typeof(result) == 'string' ? result : result.name;
+  _assertName(null, name);
+}
+
+function _testName(expected){
+  return function _testNameGenerated(err, name) {
+    assert.equal(expected, name, 'should have the provided name');
+  };
+}
+
+function _testNameSync(expected){
+  return function _testNameGeneratedSync(result) {
+    if (result instanceof Error) {
+      throw result;
+    }
+    _testName(expected)(null, result.name, result.fd);
+  };
+}
+
 function _testUnsafeCleanup(unsafe, cb) {
   _spawnTestWithoutError('unsafe.js', [ 'dir', unsafe ], cb);
 }
 
+function _testUnsafeCleanupSync(unsafe, cb) {
+  _spawnTestWithoutError('unsafe-sync.js', [ 'dir', unsafe ], cb);
+}
+
 module.exports.testStat = _testStat;
 module.exports.testPrefix = _testPrefix;
+module.exports.testPrefixSync = _testPrefixSync;
 module.exports.testPostfix = _testPostfix;
+module.exports.testPostfixSync = _testPostfixSync;
 module.exports.testKeep = _testKeep;
+module.exports.testKeepSync = _testKeepSync;
 module.exports.testGraceful = _testGraceful;
+module.exports.testGracefulSync = _testGracefulSync;
 module.exports.assertName = _assertName;
+module.exports.assertNameSync = _assertNameSync;
+module.exports.testName = _testName;
+module.exports.testNameSync = _testNameSync;
 module.exports.testUnsafeCleanup = _testUnsafeCleanup;
+module.exports.testUnsafeCleanupSync = _testUnsafeCleanupSync;
+
diff --git a/test/dir-sync-test.js b/test/dir-sync-test.js
new file mode 100644
index 0000000..68d8d93
--- /dev/null
+++ b/test/dir-sync-test.js
@@ -0,0 +1,214 @@
+var
+  vows   = require('vows'),
+  assert = require('assert'),
+
+  path       = require('path'),
+  fs         = require('fs'),
+  existsSync = fs.existsSync || path.existsSync,
+
+  tmp    = require('../lib/tmp.js'),
+  Test   = require('./base.js');
+
+
+function _testDir(mode) {
+  return function _testDirGenerated(result) {
+    assert.ok(existsSync(result.name), 'should exist');
+
+    var stat = fs.statSync(result.name);
+    assert.ok(stat.isDirectory(), 'should be a directory');
+
+    Test.testStat(stat, mode);
+  };
+}
+
+vows.describe('Synchronous directory creation').addBatch({
+  'when using without parameters': {
+    topic: function () {
+      return tmp.dirSync();
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should be a directory': _testDir(040700),
+    'should have the default prefix': Test.testPrefixSync('tmp-')
+  },
+
+  'when using with prefix': {
+    topic: function () {
+      return tmp.dirSync({ prefix: 'something' });
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should be a directory': _testDir(040700),
+    'should have the provided prefix': Test.testPrefixSync('something')
+  },
+
+  'when using with postfix': {
+    topic: function () {
+      return tmp.dirSync({ postfix: '.txt' });
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should be a directory': _testDir(040700),
+    'should have the provided postfix': Test.testPostfixSync('.txt')
+  },
+
+  'when using template': {
+    topic: function () {
+      return tmp.dirSync({ template: path.join(tmp.tmpdir, 'clike-XXXXXX-postfix') });
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should be a directory': _testDir(040700),
+    'should have the provided prefix': Test.testPrefixSync('clike-'),
+    'should have the provided postfix': Test.testPostfixSync('-postfix')
+  },
+
+  'when using name': {
+    topic: function () {
+      return tmp.dirSync({ name: 'using-name' });
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should have the provided name': Test.testNameSync(path.join(tmp.tmpdir, 'using-name')),
+    'should be a directory': function (result) {
+      _testDir(040700)(result);
+      result.removeCallback();
+      assert.ok(!existsSync(result.name), 'Directory should be removed');
+    }
+  },
+
+  'when using multiple options': {
+    topic: function () {
+      return tmp.dirSync({ prefix: 'foo', postfix: 'bar', mode: 0750 });
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should be a directory': _testDir(040750),
+    'should have the provided prefix': Test.testPrefixSync('foo'),
+    'should have the provided postfix': Test.testPostfixSync('bar')
+  },
+
+  'when using multiple options and mode': {
+    topic: function () {
+      return tmp.dirSync({ prefix: 'complicated', postfix: 'options', mode: 0755 });
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should be a directory': _testDir(040755),
+    'should have the provided prefix': Test.testPrefixSync('complicated'),
+    'should have the provided postfix': Test.testPostfixSync('options')
+  },
+
+  'no tries': {
+    topic: function () {
+      try {
+        return tmp.dirSync({ tries: -1 });
+      }
+      catch (e) {
+        return e;
+      }
+    },
+
+    'should return with an error': function (topic) {
+      assert.instanceOf(topic, Error);
+    }
+  },
+
+  'keep testing': {
+    topic: function () {
+      Test.testKeepSync('dir', '1', this.callback);
+    },
+
+    'should not return with an error': assert.isNull,
+    'should return with a name': Test.assertName,
+    'should be a dir': function (err, name) {
+      _testDir(040700)({ name: name });
+      fs.rmdirSync(name);
+    }
+  },
+
+  'unlink testing': {
+    topic: function () {
+      Test.testKeepSync('dir', '0', this.callback);
+    },
+
+    'should not return with error': assert.isNull,
+    'should return with a name': Test.assertName,
+    'should not exist': function (err, name) {
+      assert.ok(!existsSync(name), 'Directory should be removed');
+    }
+  },
+
+  'non graceful testing': {
+    topic: function () {
+      Test.testGracefulSync('dir', '0', this.callback);
+    },
+
+    'should not return with error': assert.isNull,
+    'should return with a name': Test.assertName,
+    'should be a dir': function (err, name) {
+      _testDir(040700)({ name: name });
+      fs.rmdirSync(name);
+    }
+  },
+
+  'graceful testing': {
+    topic: function () {
+      Test.testGracefulSync('dir', '1', this.callback);
+    },
+
+    'should not return with an error': assert.isNull,
+    'should return with a name': Test.assertName,
+    'should not exist': function (err, name) {
+      assert.ok(!existsSync(name), 'Directory should be removed');
+    }
+  },
+
+  'unsafeCleanup === true': {
+    topic: function () {
+      Test.testUnsafeCleanupSync('1', this.callback);
+    },
+
+    'should not return with an error': assert.isNull,
+    'should return with a name': Test.assertName,
+    'should not exist': function (err, name) {
+      assert.ok(!existsSync(name), 'Directory should be removed');
+    },
+    'should remove symlinked dir': function(err, name) {
+      assert.ok(
+        !existsSync(name + '/symlinkme-target'),
+        'should remove target'
+      );
+    },
+    'should not remove contents of symlink dir': function(err, name) {
+      assert.ok(
+        existsSync(__dirname + '/symlinkme/file.js'),
+        'should not remove symlinked directory\'s content'
+      );
+    }
+  },
+
+  'unsafeCleanup === false': {
+    topic: function () {
+      Test.testUnsafeCleanupSync('0', this.callback);
+    },
+
+    'should not return with an error': assert.isNull,
+    'should return with a name': Test.assertName,
+    'should be a directory': function (err, name) {
+       _testDir(040700)({name:name});
+    }
+  },
+
+  'remove callback': {
+    topic: function () {
+      return tmp.dirSync();
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'removeCallback should remove directory': function (result) {
+      result.removeCallback();
+      assert.ok(!existsSync(result.name), 'Directory should be removed');
+    }
+  }
+}).exportTo(module);
diff --git a/test/dir-test.js b/test/dir-test.js
index 2e4e529..363eecc 100644
--- a/test/dir-test.js
+++ b/test/dir-test.js
@@ -60,11 +60,22 @@ vows.describe('Directory creation').addBatch({
 
     'should not return with error': assert.isNull,
     'should return with a name': Test.assertName,
-    'should be a file': _testDir(040700),
+    'should be a directory': _testDir(040700),
     'should have the provided prefix': Test.testPrefix('clike-'),
     'should have the provided postfix': Test.testPostfix('-postfix')
   },
 
+  'when using name': {
+    topic: function () {
+      tmp.dir({ name: 'using-name' }, this.callback);
+    },
+
+    'should not return with an error': assert.isNull,
+    'should return with a name': Test.assertName,
+    'should be a directory': _testDir(040700),
+    'should have the provided name': Test.testName(path.join(tmp.tmpdir, 'using-name'))
+  },
+
   'when using multiple options': {
     topic: function () {
       tmp.dir({ prefix: 'foo', postfix: 'bar', mode: 0750 }, this.callback);
@@ -118,7 +129,7 @@ vows.describe('Directory creation').addBatch({
     'should not return with error': assert.isNull,
     'should return with a name': Test.assertName,
     'should not exist': function (err, name) {
-      assert.ok(!existsSync(name), "Directory should be removed");
+      assert.ok(!existsSync(name), 'Directory should be removed');
     }
   },
 
@@ -143,7 +154,7 @@ vows.describe('Directory creation').addBatch({
     'should not return with an error': assert.isNull,
     'should return with a name': Test.assertName,
     'should not exist': function (err, name) {
-      assert.ok(!existsSync(name), "Directory should be removed");
+      assert.ok(!existsSync(name), 'Directory should be removed');
     }
   },
 
@@ -155,7 +166,7 @@ vows.describe('Directory creation').addBatch({
     'should not return with an error': assert.isNull,
     'should return with a name': Test.assertName,
     'should not exist': function (err, name) {
-      assert.ok(!existsSync(name), "Directory should be removed");
+      assert.ok(!existsSync(name), 'Directory should be removed');
     },
     'should remove symlinked dir': function(err, name) {
       assert.ok(
@@ -190,7 +201,7 @@ vows.describe('Directory creation').addBatch({
     'should return with a name': Test.assertName,
     'removeCallback should remove directory': function (_err, name, removeCallback) {
       removeCallback();
-      assert.ok(!existsSync(name), "Directory should be removed");
+      assert.ok(!existsSync(name), 'Directory should be removed');
     }
   }
 }).exportTo(module);
diff --git a/test/file-sync-test.js b/test/file-sync-test.js
new file mode 100644
index 0000000..44c1d22
--- /dev/null
+++ b/test/file-sync-test.js
@@ -0,0 +1,190 @@
+var
+  vows   = require('vows'),
+  assert = require('assert'),
+
+  path       = require('path'),
+  fs         = require('fs'),
+  existsSync = fs.existsSync || path.existsSync,
+
+  tmp    = require('../lib/tmp.js'),
+  Test   = require('./base.js');
+
+
+function _testFile(mode, fdTest) {
+  return function _testFileGenerated(result) {
+    assert.ok(existsSync(result.name), 'should exist');
+
+    var stat = fs.statSync(result.name);
+    assert.equal(stat.size, 0, 'should have zero size');
+    assert.ok(stat.isFile(), 'should be a file');
+
+    Test.testStat(stat, mode);
+
+    // check with fstat as well (fd checking)
+    if (fdTest) {
+      var fstat = fs.fstatSync(result.fd);
+      assert.deepEqual(fstat, stat, 'fstat results should be the same');
+
+      var data = new Buffer('something');
+      assert.equal(fs.writeSync(result.fd, data, 0, data.length, 0), data.length, 'should be writable');
+      assert.ok(!fs.closeSync(result.fd), 'should not return with error');
+    }
+  };
+}
+
+vows.describe('Synchronous file creation').addBatch({
+  'when using without parameters': {
+    topic: function () {
+      return tmp.fileSync();
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should be a file': _testFile(0100600, true),
+    'should have the default prefix': Test.testPrefixSync('tmp-'),
+    'should have the default postfix': Test.testPostfixSync('.tmp')
+  },
+
+  'when using with prefix': {
+    topic: function () {
+      return tmp.fileSync({ prefix: 'something' });
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should be a file': _testFile(0100600, true),
+    'should have the provided prefix': Test.testPrefixSync('something')
+  },
+
+  'when using with postfix': {
+    topic: function () {
+      return tmp.fileSync({ postfix: '.txt' });
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should be a file': _testFile(0100600, true),
+    'should have the provided postfix': Test.testPostfixSync('.txt')
+  },
+
+  'when using template': {
+    topic: function () {
+      return tmp.fileSync({ template: path.join(tmp.tmpdir, 'clike-XXXXXX-postfix') });
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should be a file': _testFile(0100600, true),
+    'should have the provided prefix': Test.testPrefixSync('clike-'),
+    'should have the provided postfix': Test.testPostfixSync('-postfix')
+  },
+
+  'when using name': {
+    topic: function () {
+      return tmp.fileSync({ name: 'using-name.tmp' });
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should have the provided name': Test.testNameSync(path.join(tmp.tmpdir, 'using-name.tmp')),
+    'should be a file': function (result) {
+      _testFile(0100600, true);
+      fs.unlinkSync(result.name);
+    }
+  },
+
+  'when using multiple options': {
+    topic: function () {
+      return tmp.fileSync({ prefix: 'foo', postfix: 'bar', mode: 0640 });
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should be a file': _testFile(0100640, true),
+    'should have the provided prefix': Test.testPrefixSync('foo'),
+    'should have the provided postfix': Test.testPostfixSync('bar')
+  },
+
+  'when using multiple options and mode': {
+    topic: function () {
+      return tmp.fileSync({ prefix: 'complicated', postfix: 'options', mode: 0644 });
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'should be a file': _testFile(0100644, true),
+    'should have the provided prefix': Test.testPrefixSync('complicated'),
+    'should have the provided postfix': Test.testPostfixSync('options')
+  },
+
+  'no tries': {
+    topic: function () {
+      try {
+        return tmp.fileSync({ tries: -1 });
+      }
+      catch (e) {
+        return e;
+      }
+    },
+
+    'should return with an error': function (topic) {
+        assert.instanceOf(topic, Error);
+    }
+  },
+
+  'keep testing': {
+    topic: function () {
+      Test.testKeepSync('file', '1', this.callback);
+    },
+
+    'should not return with an error': assert.isNull,
+    'should return with a name': Test.assertName,
+    'should be a file': function (err, name) {
+      _testFile(0100600, false)({name:name});
+      fs.unlinkSync(name);
+    }
+  },
+
+  'unlink testing': {
+    topic: function () {
+      Test.testKeepSync('file', '0', this.callback);
+    },
+
+    'should not return with an error': assert.isNull,
+    'should return with a name': Test.assertName,
+    'should not exist': function (err, name) {
+      assert.ok(!existsSync(name), 'File should be removed');
+    }
+  },
+
+  'non graceful testing': {
+    topic: function () {
+      Test.testGracefulSync('file', '0', this.callback);
+    },
+
+    'should not return with error': assert.isNull,
+    'should return with a name': Test.assertName,
+    'should be a file': function (err, name) {
+      _testFile(0100600, false)({name:name});
+      fs.unlinkSync(name);
+    }
+  },
+
+  'graceful testing': {
+    topic: function () {
+      Test.testGracefulSync('file', '1', this.callback);
+    },
+
+    'should not return with an error': assert.isNull,
+    'should return with a name': Test.assertName,
+    'should not exist': function (err, name) {
+      assert.ok(!existsSync(name), 'File should be removed');
+    }
+  },
+
+  'remove callback': {
+    topic: function () {
+      return tmp.fileSync();
+    },
+
+    'should return with a name': Test.assertNameSync,
+    'removeCallback should remove file': function (result) {
+      result.removeCallback();
+      assert.ok(!existsSync(result.name), 'File should be removed');
+    }
+  }
+
+}).exportTo(module);
diff --git a/test/file-test.js b/test/file-test.js
index d9605b3..b710859 100644
--- a/test/file-test.js
+++ b/test/file-test.js
@@ -79,6 +79,20 @@ vows.describe('File creation').addBatch({
     'should have the provided postfix': Test.testPostfix('-postfix')
   },
 
+  'when using name': {
+    topic: function () {
+      tmp.file({ name: 'using-name.tmp' }, this.callback);
+    },
+
+    'should not return with an error': assert.isNull,
+    'should return with a name': Test.assertName,
+    'should have the provided name': Test.testName(path.join(tmp.tmpdir, 'using-name.tmp')),
+    'should be a file': function (err, name) {
+      _testFile(0100600, true);
+      fs.unlinkSync(name);
+    }
+  },
+
   'when using multiple options': {
     topic: function () {
       tmp.file({ prefix: 'foo', postfix: 'bar', mode: 0640 }, this.callback);
@@ -132,7 +146,7 @@ vows.describe('File creation').addBatch({
     'should not return with an error': assert.isNull,
     'should return with a name': Test.assertName,
     'should not exist': function (err, name) {
-      assert.ok(!existsSync(name), "File should be removed");
+      assert.ok(!existsSync(name), 'File should be removed');
     }
   },
 
@@ -157,7 +171,7 @@ vows.describe('File creation').addBatch({
     'should not return with an error': assert.isNull,
     'should return with a name': Test.assertName,
     'should not exist': function (err, name) {
-      assert.ok(!existsSync(name), "File should be removed");
+      assert.ok(!existsSync(name), 'File should be removed');
     }
   },
 
@@ -170,7 +184,7 @@ vows.describe('File creation').addBatch({
     'should return with a name': Test.assertName,
     'removeCallback should remove file': function (_err, name, _fd, removeCallback) {
       removeCallback();
-      assert.ok(!existsSync(name), "File should be removed");
+      assert.ok(!existsSync(name), 'File should be removed');
     }
   }
 
diff --git a/test/graceful-sync.js b/test/graceful-sync.js
new file mode 100644
index 0000000..37766ff
--- /dev/null
+++ b/test/graceful-sync.js
@@ -0,0 +1,20 @@
+var
+  tmp = require('../lib/tmp'),
+  spawn = require('./spawn-sync');
+
+var graceful = spawn.arg;
+
+if (graceful) {
+  tmp.setGracefulCleanup();
+}
+
+try {
+  var result = spawn.tmpFunction();
+  spawn.out(result.name, function () {
+    throw new Error('Thrown on purpose');
+  });
+}
+catch (e) {
+  spawn.err(e, spawn.exit);
+}
+
diff --git a/test/graceful.js b/test/graceful.js
index c898656..dbe554e 100644
--- a/test/graceful.js
+++ b/test/graceful.js
@@ -10,6 +10,6 @@ if (graceful) {
 
 spawn.tmpFunction(function (err, name) {
   spawn.out(name, function () {
-    throw new Error("Thrown on purpose");
+    throw new Error('Thrown on purpose');
   });
 });
diff --git a/test/keep-sync.js b/test/keep-sync.js
new file mode 100644
index 0000000..6cd8b18
--- /dev/null
+++ b/test/keep-sync.js
@@ -0,0 +1,12 @@
+var spawn = require('./spawn-sync');
+
+var keep = spawn.arg;
+
+try {
+  var result = spawn.tmpFunction({ keep: keep });
+  spawn.out(result.name, spawn.exit);
+}
+catch (e) {
+  spawn.err(err, spawn.exit);
+}
+
diff --git a/test/spawn-sync.js b/test/spawn-sync.js
new file mode 100644
index 0000000..bde2db4
--- /dev/null
+++ b/test/spawn-sync.js
@@ -0,0 +1,32 @@
+var
+  fs = require('fs'),
+  tmp = require('../lib/tmp');
+
+function _writeSync(stream, str, cb) {
+  var flushed = stream.write(str);
+  if (flushed) {
+    return cb(null);
+  }
+
+  stream.once('drain', function _flushed() {
+    cb(null);
+  });
+}
+
+module.exports.out = function (str, cb) {
+  _writeSync(process.stdout, str, cb);
+};
+
+module.exports.err = function (str, cb) {
+  _writeSync(process.stderr, str, cb);
+};
+
+module.exports.exit = function () {
+  process.exit(0);
+};
+
+var type = process.argv[2];
+module.exports.tmpFunction = (type == 'file') ? tmp.fileSync : tmp.dirSync;
+
+var arg = (process.argv[3] && parseInt(process.argv[3], 10) === 1) ? true : false;
+module.exports.arg = arg;
diff --git a/test/unsafe-sync.js b/test/unsafe-sync.js
new file mode 100644
index 0000000..f09129f
--- /dev/null
+++ b/test/unsafe-sync.js
@@ -0,0 +1,31 @@
+var
+  fs    = require('fs'),
+  join  = require('path').join,
+  spawn = require('./spawn-sync');
+
+var unsafe = spawn.arg;
+
+try {
+  var result = spawn.tmpFunction({ unsafeCleanup: unsafe });
+  try {
+    // file that should be removed
+    var fd = fs.openSync(join(result.name, 'should-be-removed.file'), 'w');
+    fs.closeSync(fd);
+
+    // in tree source
+    var symlinkSource = join(__dirname, 'symlinkme');
+    // testing target
+    var symlinkTarget = join(result.name, 'symlinkme-target');
+
+    // symlink that should be removed but the contents should be preserved.
+    fs.symlinkSync(symlinkSource, symlinkTarget, 'dir');
+
+    spawn.out(result.name, spawn.exit);
+  } catch (e) {
+    spawn.err(e.toString(), spawn.exit);
+  }
+}
+catch (e) {
+  spawn.err(err, spawn.exit);
+}
+

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



More information about the Pkg-javascript-commits mailing list