[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#924649: marked as done (unblock: node-formidable/1.2.1-2)



Your message dated Sat, 20 Apr 2019 18:47:00 +0000
with message-id <cd86c898-6512-6459-d97d-b1af7a51cc8e@thykier.net>
and subject line Re: Bug#924627: unblock: node-formidable/1.2.1-1
has caused the Debian Bug report #924627,
regarding unblock: node-formidable/1.2.1-2
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
924627: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=924627
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Please unblock package node-formidable

Hello,

node-formidable hasn't been updated for years. Testing version isn't
compatible with our nodejs but this was not seen since there was no
real tests (see https://bugs.debian.org/924589).

I upgraded it and added full tests (build and autopkgtest).

node-formidable reverse dependencies:
 - node-superagent:
   +- node-multiparty (no reverse dependencies)

node-formidable bug does not affect these packages since the failing
functions are not used in it, that's why I decrease bug severity to
important. I tested the 2 reverse dependencies with both old and new
node-formidable with success (sbuild and autopkgtest).

debdiff is big since I embedded some node modules (only for tests, they
are not installed). Upstream changes are not so big. I updated also
debian/* files and added examples (and tests of course).

I think it's not risky to update node-formidable. However I leave you
appreciate if it is opportune.

Cheers,
Xavier

unblock node-formidable/1.2.1-1

- -- System Information:
Debian Release: buster/sid
  APT prefers testing
  APT policy: (900, 'testing'), (500, 'unstable')
Architecture: amd64 (x86_64)

Kernel: Linux 4.19.0-2-amd64 (SMP w/8 CPU cores)
Kernel taint flags: TAINT_OOT_MODULE, TAINT_UNSIGNED_MODULE
Locale: LANG=fr_FR.UTF-8, LC_CTYPE=fr_FR.UTF-8 (charmap=UTF-8), LANGUAGE= (charmap=UTF-8)
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

-----BEGIN PGP SIGNATURE-----

iQJEBAEBCgAuFiEEAN/li4tVV3nRAF7J9tdMp8mZ7ukFAlyLnWUQHHlhZGRAZGVi
aWFuLm9yZwAKCRD210ynyZnu6Y20D/435gzCXhoRtdrUEYR5AVcHF4YTJ9eE8f6C
fpn1W0Eab3pnnsbdePd+zKQn9GYv/QHUlWrxKU9ENyH4ScJVO2ROTISdUKURLpb6
T80YmeKh9TCczRBYOmKP65LPwemVLeK5UE3PZZG5yCXY7f2fQ9A6UuOoaKUTAm1O
80KDevmOUexRpBnQr9QGiUAoL5eYf5DXcTvNZBBd/IVhkW2FbyOp+E9ssBhA0ucb
w6Ep1AFlg5daEW+P6m+pL5X1e8KIQoS9xlOZQufromvUXh+bZta5awUGTTkL2N6F
5++XL5TzgS9MMGWIRS0Y+wYU8DC8HBTBGKqEddWRxqPiUvj76z4UCb4ieXpAnytQ
WpIDgAhXgfX0+5eR0w8mGGl2WY8mRMgBas4exchRabNx4q/YYW23zQszQS1J6LrM
tBmX4JvvVn6Iq+RuMlAbpYp+pMHcayx+i70r1aDTdJO5019UgDsy7jRplMDtj1/h
LYKMX0FYtG00cyvWxURTqAHFGS3SV0UHaehRqrPNRp4OKvOrt8BbfpG/bjOWE48g
Hi7ksYk8EnciGjfdfMcvw6MRBmamGmEgJAuvZSOg3dLNg0PhPyuBbNIT5NmY7k5g
IBI2+FAtJjVJsQ3ddQQGsKAVwK0QYPkiAVw0uTQCqeu78Uc3RvbQXxYiBzCt76m4
Q5VSVyua7Q==
=tDlz
-----END PGP SIGNATURE-----
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..45032a1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/test/tmp/*
+*.upload
+*.un~
+/node_modules/*
diff --git a/.npmignore b/.npmignore
index 4fbabb3..ed16858 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,4 +1,7 @@
-/test/tmp/
+/test
+/tool
+/example
+/benchmark
 *.upload
 *.un~
 *.http
diff --git a/.travis.yml b/.travis.yml
index cb931cb..694a62f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,5 @@
 language: node_js
 node_js:
-  - 0.8
-  - 0.9
-  - "0.10"
+  - 4
+  - 6
+  - 7
diff --git a/Readme.md b/Readme.md
index 1aecf08..ee1abfe 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,13 +1,15 @@
 # Formidable
 
-[![Build Status](https://secure.travis-ci.org/felixge/node-formidable.png?branch=master)](http://travis-ci.org/felixge/node-formidable)
+[![Build Status](https://travis-ci.org/felixge/node-formidable.svg?branch=master)](https://travis-ci.org/felixge/node-formidable)
 
 ## Purpose
 
-A node.js module for parsing form data, especially file uploads.
+A Node.js module for parsing form data, especially file uploads.
 
 ## Current status
 
+**Maintainers Wanted:** Please see https://github.com/felixge/node-formidable/issues/412
+
 This module was developed for [Transloadit](http://transloadit.com/), a service focused on uploading
 and encoding images and videos. It has been battle-tested against hundreds of GB of file uploads from
 a large variety of clients and is considered production-ready.
@@ -22,17 +24,12 @@ a large variety of clients and is considered production-ready.
 
 ## Installation
 
-Via [npm](http://github.com/isaacs/npm):
-```
-npm install formidable@latest
-```
-Manually:
-```
-git clone git://github.com/felixge/node-formidable.git formidable
-vim my.js
-# var formidable = require('./formidable');
+```sh
+npm i -S formidable
 ```
 
+This is a low-level package, and if you're using a high-level framework it may already be included. However, [Express v4](http://expressjs.com) does not include any multipart handling, nor does [body-parser](https://github.com/expressjs/body-parser).
+
 Note: Formidable requires [gently](http://github.com/felixge/node-gently) to run the unit tests, but you won't need it for just using the library.
 
 ## Example
@@ -82,11 +79,10 @@ form.encoding = 'utf-8';
 Sets encoding for incoming form fields.
 
 ```javascript
-form.uploadDir = process.env.TMP || process.env.TMPDIR || process.env.TEMP || '/tmp' || process.cwd();
+form.uploadDir = "/my/dir";
 ```
-The directory for placing file uploads in. You can move them later on using
-`fs.rename()`. The default directory is picked at module load time depending on
-the first existing directory from those listed above.
+Sets the directory for placing file uploads in. You can move them later on using
+`fs.rename()`. The default is `os.tmpdir()`.
 
 ```javascript
 form.keepExtensions = false;
@@ -99,23 +95,35 @@ form.type
 Either 'multipart' or 'urlencoded' depending on the incoming request.
 
 ```javascript
-form.maxFieldsSize = 2 * 1024 * 1024;
+form.maxFieldsSize = 20 * 1024 * 1024;
+```
+Limits the amount of memory all fields together (except files) can allocate in bytes.
+If this value is exceeded, an `'error'` event is emitted. The default
+size is 20MB.
+
+```javascript
+form.maxFileSize = 200 * 1024 * 1024;
 ```
-Limits the amount of memory a field (not file) can allocate in bytes.
+Limits the size of uploaded file.
 If this value is exceeded, an `'error'` event is emitted. The default
-size is 2MB.
+size is 200MB.
 
 ```javascript
-form.maxFields = 0;
+form.maxFields = 1000;
 ```
 Limits the number of fields that the querystring parser will decode. Defaults
-to 0 (unlimited).
+to 1000 (0 for unlimited).
 
 ```javascript
 form.hash = false;
 ```
 If you want checksums calculated for incoming files, set this to either `'sha1'` or `'md5'`.
 
+```javascript
+form.multiples = false;
+```
+If this option is enabled, when you call `form.parse`, the `files` argument will contain arrays of files for inputs which submit multiple files using the HTML5 `multiple` attribute.
+
 ```javascript
 form.bytesReceived
 ```
@@ -129,7 +137,7 @@ The expected number of bytes in this form.
 ```javascript
 form.parse(request, [cb]);
 ```
-Parses an incoming node.js `request` containing form data. If `cb` is provided, all fields an files are collected and passed to the callback:
+Parses an incoming node.js `request` containing form data. If `cb` is provided, all fields and files are collected and passed to the callback:
 
 
 ```javascript
@@ -198,15 +206,20 @@ If hash calculation was set, you can read the hex digest out of this var.
 
 
 #### 'progress'
+
+Emitted after each incoming chunk of data that has been parsed. Can be used to roll your own progress bar.
+
 ```javascript
 form.on('progress', function(bytesReceived, bytesExpected) {
 });
 ```
-Emitted after each incoming chunk of data that has been parsed. Can be used to roll your own progress bar.
 
 
 
 #### 'field'
+
+Emitted whenever a field / value pair has been received.
+
 ```javascript
 form.on('field', function(name, value) {
 });
@@ -214,7 +227,10 @@ form.on('field', function(name, value) {
 
 #### 'fileBegin'
 
-Emitted whenever a field / value pair has been received.
+Emitted whenever a new file is detected in the upload stream. Use this event if
+you want to stream the file to somewhere else while buffering the upload on
+the file system.
+
 ```javascript
 form.on('fileBegin', function(name, file) {
 });
@@ -222,11 +238,8 @@ form.on('fileBegin', function(name, file) {
 
 #### 'file'
 
-Emitted whenever a new file is detected in the upload stream. Use this even if
-you want to stream the file to somewhere else while buffering the upload on
-the file system.
-
 Emitted whenever a field / file pair has been received. `file` is an instance of `File`.
+
 ```javascript
 form.on('file', function(name, file) {
 });
@@ -235,6 +248,7 @@ form.on('file', function(name, file) {
 #### 'error'
 
 Emitted when there is an error processing the incoming form. A request that experiences an error is automatically paused, you will have to manually call `request.resume()` if you want the request to continue firing `'data'` events.
+
 ```javascript
 form.on('error', function(err) {
 });
@@ -243,7 +257,7 @@ form.on('error', function(err) {
 #### 'aborted'
 
 
-Emitted when the request was aborted by the user. Right now this can be due to a 'timeout' or 'close' event on the socket. In the future there will be a separate 'timeout' event (needs a change in the node core).
+Emitted when the request was aborted by the user. Right now this can be due to a 'timeout' or 'close' event on the socket. After this event is emitted, an `error` event will follow. In the future there will be a separate 'timeout' event (needs a change in the node core).
 ```javascript
 form.on('aborted', function() {
 });
@@ -260,11 +274,30 @@ Emitted when the entire request has been received, and all contained files have
 
 ## Changelog
 
+### v1.1.1 (2017-01-15)
+
+ * Fix DeprecationWarning about os.tmpDir() (Christian)
+ * Update `buffer.write` order of arguments for Node 7 (Kornel Lesiński)
+ * JSON Parser emits error events to the IncomingForm (alessio.montagnani)
+ * Improved Content-Disposition parsing (Sebastien)
+ * Access WriteStream of fs during runtime instead of include time (Jonas Amundsen)
+ * Use built-in toString to convert buffer to hex (Charmander)
+ * Add hash to json if present (Nick Stamas)
+ * Add license to package.json (Simen Bekkhus)
+
+### v1.0.14 (2013-05-03)
+
+* Add failing hash tests. (Ben Trask)
+* Enable hash calculation again (Eugene Girshov)
+* Test for immediate data events (Tim Smart)
+* Re-arrange IncomingForm#parse (Tim Smart)
+
 ### v1.0.13
 
 * Only update hash if update method exists (Sven Lito)
 * According to travis v0.10 needs to go quoted (Sven Lito)
 * Bumping build node versions (Sven Lito)
+* Additional fix for empty requests (Eugene Girshov)
 * Change the default to 1000, to match the new Node behaviour. (OrangeDog)
 * Add ability to control maxKeys in the querystring parser. (OrangeDog)
 * Adjust test case to work with node 0.9.x (Eugene Girshov)
@@ -284,120 +317,12 @@ Emitted when the entire request has been received, and all contained files have
 * Remove support for Node.js 0.4 & 0.6 (Andrew Kelley)
 * Documentation improvements (Sven Lito, Andre Azevedo)
 * Add support for application/octet-stream (Ion Lupascu, Chris Scribner)
-* Use os.tmpDir() to get tmp directory (Andrew Kelley)
+* Use os.tmpdir() to get tmp directory (Andrew Kelley)
 * Improve package.json (Andrew Kelley, Sven Lito)
 * Fix benchmark script (Andrew Kelley)
 * Fix scope issue in incoming_forms (Sven Lito)
 * Fix file handle leak on error (OrangeDog)
 
-### v1.0.11
-
-* Calculate checksums for incoming files (sreuter)
-* Add definition parameters to "IncomingForm" as an argument (Math-)
-
-### v1.0.10
-
-* Make parts to be proper Streams (Matt Robenolt)
-
-### v1.0.9
-
-* Emit progress when content length header parsed (Tim Koschützki)
-* Fix Readme syntax due to GitHub changes (goob)
-* Replace references to old 'sys' module in Readme with 'util' (Peter Sugihara)
-
-### v1.0.8
-
-* Strip potentially unsafe characters when using `keepExtensions: true`.
-* Switch to utest / urun for testing
-* Add travis build
-
-### v1.0.7
-
-* Remove file from package that was causing problems when installing on windows. (#102)
-* Fix typos in Readme (Jason Davies).
-
-### v1.0.6
-
-* Do not default to the default to the field name for file uploads where
-  filename="".
-
-### v1.0.5
-
-* Support filename="" in multipart parts
-* Explain unexpected end() errors in parser better
-
-**Note:** Starting with this version, formidable emits 'file' events for empty
-file input fields. Previously those were incorrectly emitted as regular file
-input fields with value = "".
-
-### v1.0.4
-
-* Detect a good default tmp directory regardless of platform. (#88)
-
-### v1.0.3
-
-* Fix problems with utf8 characters (#84) / semicolons in filenames (#58)
-* Small performance improvements
-* New test suite and fixture system
-
-### v1.0.2
-
-* Exclude node\_modules folder from git
-* Implement new `'aborted'` event
-* Fix files in example folder to work with recent node versions
-* Make gently a devDependency
-
-[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.1...v1.0.2)
-
-### v1.0.1
-
-* Fix package.json to refer to proper main directory. (#68, Dean Landolt)
-
-[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.0...v1.0.1)
-
-### v1.0.0
-
-* Add support for multipart boundaries that are quoted strings. (Jeff Craig)
-
-This marks the beginning of development on version 2.0 which will include
-several architectural improvements.
-
-[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.11...v1.0.0)
-
-### v0.9.11
-
-* Emit `'progress'` event when receiving data, regardless of parsing it. (Tim Koschützki)
-* Use [W3C FileAPI Draft](http://dev.w3.org/2006/webapi/FileAPI/) properties for File class
-
-**Important:** The old property names of the File class will be removed in a
-future release.
-
-[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.10...v0.9.11)
-
-### Older releases
-
-These releases were done before starting to maintain the above Changelog:
-
-* [v0.9.10](https://github.com/felixge/node-formidable/compare/v0.9.9...v0.9.10)
-* [v0.9.9](https://github.com/felixge/node-formidable/compare/v0.9.8...v0.9.9)
-* [v0.9.8](https://github.com/felixge/node-formidable/compare/v0.9.7...v0.9.8)
-* [v0.9.7](https://github.com/felixge/node-formidable/compare/v0.9.6...v0.9.7)
-* [v0.9.6](https://github.com/felixge/node-formidable/compare/v0.9.5...v0.9.6)
-* [v0.9.5](https://github.com/felixge/node-formidable/compare/v0.9.4...v0.9.5)
-* [v0.9.4](https://github.com/felixge/node-formidable/compare/v0.9.3...v0.9.4)
-* [v0.9.3](https://github.com/felixge/node-formidable/compare/v0.9.2...v0.9.3)
-* [v0.9.2](https://github.com/felixge/node-formidable/compare/v0.9.1...v0.9.2)
-* [v0.9.1](https://github.com/felixge/node-formidable/compare/v0.9.0...v0.9.1)
-* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
-* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
-* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
-* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
-* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
-* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
-* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
-* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
-* [v0.1.0](https://github.com/felixge/node-formidable/commits/v0.1.0)
-
 ## License
 
 Formidable is licensed under the MIT license.
diff --git a/benchmark/bench-multipart-parser.js b/benchmark/bench-multipart-parser.js
index 49abc43..1978e58 100644
--- a/benchmark/bench-multipart-parser.js
+++ b/benchmark/bench-multipart-parser.js
@@ -44,7 +44,7 @@ parser.onEnd = function() {
 
 var start = +new Date(),
     nparsed = parser.write(buffer),
-    duration = +new Date - start,
+    duration = +new Date() - start,
     mbPerSec = (mb / (duration / 1000)).toFixed(2);
 
 console.log(mbPerSec+' mb/sec');
@@ -59,8 +59,8 @@ function createMultipartBuffer(boundary, size) {
     , tail = '\r\n--'+boundary+'--\r\n'
     , buffer = new Buffer(size);
 
-  buffer.write(head, 'ascii', 0);
-  buffer.write(tail, 'ascii', buffer.length - tail.length);
+  buffer.write(head, 0, 'ascii');
+  buffer.write(tail, buffer.length - tail.length, 'ascii');
   return buffer;
 }
 
diff --git a/debian/changelog b/debian/changelog
index d929834..d805bea 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,22 @@
+node-formidable (1.2.1-1) unstable; urgency=medium
+
+  * Team upload
+  * Bump debhelper compatibility level to 11
+  * Declare compliance with policy 4.3.0
+  * Change section to javascript
+  * Change priority to optional
+  * Update debian/watch
+  * New upstream version 1.2.1 (Closes: #924589)
+  * Add upstream/metadata
+  * Enable tests using pkg-js-tools
+  * Fix VCS fields
+  * Update debian/copyright
+  * Update debian/copyright
+  * Fix install
+  * Install examples
+
+ -- Xavier Guimard <yadd@debian.org>  Thu, 14 Mar 2019 21:17:08 +0100
+
 node-formidable (1.0.13-1) unstable; urgency=low
 
   * Initial release (Closes: #703836)
diff --git a/debian/clean b/debian/clean
new file mode 100644
index 0000000..b979df5
--- /dev/null
+++ b/debian/clean
@@ -0,0 +1 @@
+test/tmp/*
diff --git a/debian/compat b/debian/compat
index 45a4fb7..b4de394 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-8
+11
diff --git a/debian/control b/debian/control
index 115429b..1bab968 100644
--- a/debian/control
+++ b/debian/control
@@ -1,21 +1,23 @@
 Source: node-formidable
-Section: web
-Priority: extra
 Maintainer: Debian Javascript Maintainers <pkg-javascript-devel@lists.alioth.debian.org>
 Uploaders: Jérémy Lal <kapouer@melix.org>
-Build-Depends:
- debhelper (>= 8.0.0)
- , dh-buildinfo
-Standards-Version: 3.9.3
+Section: javascript
+Testsuite: autopkgtest-pkg-nodejs
+Priority: optional
+Build-Depends: debhelper (>= 11~),
+               nodejs,
+               node-hashish,
+               node-request,
+               pkg-js-tools
+Standards-Version: 4.3.0
+Vcs-Browser: https://salsa.debian.org/js-team/node-formidable
+Vcs-Git: https://salsa.debian.org/js-team/node-formidable.git
 Homepage: https://github.com/felixge/node-formidable
-Vcs-Git: git://git.debian.org/collab-maint/node-formidable.git
-Vcs-Browser: http://git.debian.org/?p=collab-maint/node-formidable.git
 
 Package: node-formidable
 Architecture: all
-Depends:
- ${misc:Depends}
- , nodejs
+Depends: ${misc:Depends},
+         nodejs
 Description: Multipart form data parser module for Node.js
  node-formidable is a well-tested parser for multipart/form-data sent by
  http clients. It emphasizes support for file uploads.
diff --git a/debian/copyright b/debian/copyright
index a68a70b..2dd5262 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,10 +1,35 @@
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 Upstream-Name: formidable
 
 Files: *
-Copyright: 2011 Felix Geisendörfer <felix@debuggable.com>
+Copyright: 2011-2013, Felix Geisendörfer <felix@debuggable.com>
 License: Expat
 
+Files: debian/*
+Copyright: 2013, Jérémy Lal <kapouer@melix.org>
+ 2019, Xavier Guimard <yadd@debian.org>
+License: Expat
+
+Files: debian/tests/node_modules/chainsaw/*
+Copyright: 2010, James Halliday <mail@substack.net>
+License: Expat
+
+Files: debian/tests/node_modules/findit/*
+Copyright: 2013-2014, James Halliday <mail@substack.net>
+License: Expat
+Comment: The upstream distribution does not contain an explicit statement of
+ copyright ownership. Pursuant to the Berne Convention for the Protection of
+ Literary and Artistic Works, it is assumed that all content is copyright by
+ its respective authors unless otherwise stated.
+
+Files: debian/tests/node_modules/seq/*
+Copyright: 2011, James Halliday <mail@substack.net>
+License: Expat
+Comment: The upstream distribution does not contain an explicit statement of
+ copyright ownership. Pursuant to the Berne Convention for the Protection of
+ Literary and Artistic Works, it is assumed that all content is copyright by
+ its respective authors unless otherwise stated.
+
 License: Expat
  Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the
diff --git a/debian/examples b/debian/examples
new file mode 100644
index 0000000..0bbe99e
--- /dev/null
+++ b/debian/examples
@@ -0,0 +1 @@
+example/*
diff --git a/debian/install b/debian/install
index bce9365..4af254f 100644
--- a/debian/install
+++ b/debian/install
@@ -1 +1,2 @@
-lib/*	usr/lib/nodejs/formidable/
+lib	usr/lib/nodejs/formidable/
+package.json	usr/lib/nodejs/formidable/
diff --git a/debian/rules b/debian/rules
index 218df65..20809a4 100755
--- a/debian/rules
+++ b/debian/rules
@@ -5,4 +5,4 @@
 #export DH_VERBOSE=1
 
 %:
-	dh $@
+	dh $@ --with nodejs
diff --git a/debian/tests/node_modules/chainsaw/README.markdown b/debian/tests/node_modules/chainsaw/README.markdown
new file mode 100644
index 0000000..9bb1259
--- /dev/null
+++ b/debian/tests/node_modules/chainsaw/README.markdown
@@ -0,0 +1,140 @@
+Chainsaw
+========
+
+Build chainable fluent interfaces the easy way in node.js.
+
+With this meta-module you can write modules with chainable interfaces.
+Chainsaw takes care of all of the boring details and makes nested flow control
+super simple too.
+
+Just call `Chainsaw` with a constructor function like in the examples below.
+In your methods, just do `saw.next()` to move along to the next event and
+`saw.nest()` to create a nested chain.
+
+Examples
+========
+
+add_do.js
+---------
+
+This silly example adds values with a chainsaw.
+
+    var Chainsaw = require('chainsaw');
+    
+    function AddDo (sum) {
+        return Chainsaw(function (saw) {
+            this.add = function (n) {
+                sum += n;
+                saw.next();
+            };
+             
+            this.do = function (cb) {
+                saw.nest(cb, sum);
+            };
+        });
+    }
+    
+    AddDo(0)
+        .add(5)
+        .add(10)
+        .do(function (sum) {
+            if (sum > 12) this.add(-10);
+        })
+        .do(function (sum) {
+            console.log('Sum: ' + sum);
+        })
+    ;
+
+Output:
+    Sum: 5
+
+prompt.js
+---------
+
+This example provides a wrapper on top of stdin with the help of
+[node-lazy](https://github.com/pkrumins/node-lazy) for line-processing.
+
+    var Chainsaw = require('chainsaw');
+    var Lazy = require('lazy');
+    
+    module.exports = Prompt;
+    function Prompt (stream) {
+        var waiting = [];
+        var lines = [];
+        var lazy = Lazy(stream).lines.map(String)
+            .forEach(function (line) {
+                if (waiting.length) {
+                    var w = waiting.shift();
+                    w(line);
+                }
+                else lines.push(line);
+            })
+        ;
+        
+        var vars = {};
+        return Chainsaw(function (saw) {
+            this.getline = function (f) {
+                var g = function (line) {
+                    saw.nest(f, line, vars);
+                };
+                
+                if (lines.length) g(lines.shift());
+                else waiting.push(g);
+            };
+            
+            this.do = function (cb) {
+                saw.nest(cb, vars);
+            };
+        });
+    }
+
+And now for the new Prompt() module in action:
+
+    var util = require('util');
+    var stdin = process.openStdin();
+     
+    Prompt(stdin)
+        .do(function () {
+            util.print('x = ');
+        })
+        .getline(function (line, vars) {
+            vars.x = parseInt(line, 10);
+        })
+        .do(function () {
+            util.print('y = ');
+        })
+        .getline(function (line, vars) {
+            vars.y = parseInt(line, 10);
+        })
+        .do(function (vars) {
+            if (vars.x + vars.y < 10) {
+                util.print('z = ');
+                this.getline(function (line) {
+                    vars.z = parseInt(line, 10);
+                })
+            }
+            else {
+                vars.z = 0;
+            }
+        })
+        .do(function (vars) {
+            console.log('x + y + z = ' + (vars.x + vars.y + vars.z));
+            process.exit();
+        })
+    ;
+
+Installation
+============
+
+With [npm](http://github.com/isaacs/npm), just do:
+    npm install chainsaw
+
+or clone this project on github:
+
+    git clone http://github.com/substack/node-chainsaw.git
+
+To run the tests with [expresso](http://github.com/visionmedia/expresso),
+just do:
+
+    expresso
+
diff --git a/debian/tests/node_modules/chainsaw/examples/add_do.js b/debian/tests/node_modules/chainsaw/examples/add_do.js
new file mode 100644
index 0000000..378705d
--- /dev/null
+++ b/debian/tests/node_modules/chainsaw/examples/add_do.js
@@ -0,0 +1,25 @@
+var Chainsaw = require('chainsaw');
+
+function AddDo (sum) {
+    return Chainsaw(function (saw) {
+        this.add = function (n) {
+            sum += n;
+            saw.next();
+        };
+        
+        this.do = function (cb) {
+            saw.nest(cb, sum);
+        };
+    });
+}
+
+AddDo(0)
+    .add(5)
+    .add(10)
+    .do(function (sum) {
+        if (sum > 12) this.add(-10);
+    })
+    .do(function (sum) {
+        console.log('Sum: ' + sum);
+    })
+;
diff --git a/debian/tests/node_modules/chainsaw/examples/prompt.js b/debian/tests/node_modules/chainsaw/examples/prompt.js
new file mode 100644
index 0000000..0a06d71
--- /dev/null
+++ b/debian/tests/node_modules/chainsaw/examples/prompt.js
@@ -0,0 +1,67 @@
+var Chainsaw = require('chainsaw');
+var Lazy = require('lazy');
+
+module.exports = Prompt;
+function Prompt (stream) {
+    var waiting = [];
+    var lines = [];
+    var lazy = Lazy(stream).lines.map(String)
+        .forEach(function (line) {
+            if (waiting.length) {
+                var w = waiting.shift();
+                w(line);
+            }
+            else lines.push(line);
+        })
+    ;
+    
+    var vars = {};
+    return Chainsaw(function (saw) {
+        this.getline = function (f) {
+            var g = function (line) {
+                saw.nest(f, line, vars);
+            };
+            
+            if (lines.length) g(lines.shift());
+            else waiting.push(g);
+        };
+        
+        this.do = function (cb) {
+            saw.nest(cb, vars);
+        };
+    });
+}
+
+var util = require('util');
+if (__filename === process.argv[1]) {
+    var stdin = process.openStdin();
+    Prompt(stdin)
+        .do(function () {
+            util.print('x = ');
+        })
+        .getline(function (line, vars) {
+            vars.x = parseInt(line, 10);
+        })
+        .do(function () {
+            util.print('y = ');
+        })
+        .getline(function (line, vars) {
+            vars.y = parseInt(line, 10);
+        })
+        .do(function (vars) {
+            if (vars.x + vars.y < 10) {
+                util.print('z = ');
+                this.getline(function (line) {
+                    vars.z = parseInt(line, 10);
+                })
+            }
+            else {
+                vars.z = 0;
+            }
+        })
+        .do(function (vars) {
+            console.log('x + y + z = ' + (vars.x + vars.y + vars.z));
+            process.exit();
+        })
+    ;
+}
diff --git a/debian/tests/node_modules/chainsaw/index.js b/debian/tests/node_modules/chainsaw/index.js
new file mode 100755
index 0000000..597cc48
--- /dev/null
+++ b/debian/tests/node_modules/chainsaw/index.js
@@ -0,0 +1,108 @@
+var Traverse = require('traverse');
+var EventEmitter = require('events').EventEmitter;
+
+module.exports = Chainsaw;
+function Chainsaw (builder) {
+    var saw = Chainsaw.saw(builder, {});
+    var r = builder.call(saw.handlers, saw);
+    if (r !== undefined) saw.handlers = r;
+    return saw.chain();
+};
+
+Chainsaw.saw = function (builder, handlers) {
+    var saw = new EventEmitter;
+    saw.handlers = handlers;
+    saw.actions = [];
+    saw.step = 0;
+    
+    saw.chain = function () {
+        var ch = Traverse(saw.handlers).map(function (node) {
+            if (this.isRoot) return node;
+            var ps = this.path;
+            
+            if (typeof node === 'function') {
+                this.update(function () {
+                    saw.actions.push({
+                        path : ps,
+                        args : [].slice.call(arguments)
+                    });
+                    return ch;
+                });
+            }
+        });
+        
+        process.nextTick(function () {
+            saw.emit('begin');
+            saw.next();
+        });
+        
+        return ch;
+    };
+    
+    saw.next = function () {
+        var action = saw.actions[saw.step];
+        saw.step ++;
+        
+        if (!action) {
+            saw.emit('end');
+        }
+        else if (!action.trap) {
+            var node = saw.handlers;
+            action.path.forEach(function (key) { node = node[key] });
+            node.apply(saw.handlers, action.args);
+        }
+    };
+    
+    saw.nest = function (cb) {
+        var args = [].slice.call(arguments, 1);
+        var autonext = true;
+        
+        if (typeof cb === 'boolean') {
+            var autonext = cb;
+            cb = args.shift();
+        }
+        
+        var s = Chainsaw.saw(builder, {});
+        var r = builder.call(s.handlers, s);
+        
+        if (r !== undefined) s.handlers = r;
+        cb.apply(s.chain(), args);
+        if (autonext !== false) s.on('end', saw.next);
+    };
+    
+    saw.trap = function (name, cb) {
+        var ps = Array.isArray(name) ? name : [name];
+        saw.actions.push({
+            path : ps,
+            step : saw.step,
+            cb : cb,
+            trap : true
+        });
+    };
+    
+    saw.down = function (name) {
+        var ps = (Array.isArray(name) ? name : [name]).join('/');
+        var i = saw.actions.slice(saw.step).map(function (x) {
+            if (x.trap && x.step <= saw.step) return false;
+            return x.path.join('/') == ps;
+        }).indexOf(true);
+        
+        if (i >= 0) saw.step += i;
+        else saw.step = saw.actions.length;
+        
+        var act = saw.actions[saw.step - 1];
+        if (act && act.trap) {
+            // It's a trap!
+            saw.step = act.step;
+            act.cb();
+        }
+        else saw.next();
+    };
+    
+    saw.jump = function (step) {
+        saw.step = step;
+        saw.next();
+    };
+    
+    return saw;
+}; 
diff --git a/debian/tests/node_modules/chainsaw/package.json b/debian/tests/node_modules/chainsaw/package.json
new file mode 100644
index 0000000..66b1b08
--- /dev/null
+++ b/debian/tests/node_modules/chainsaw/package.json
@@ -0,0 +1,61 @@
+{
+  "_from": "chainsaw@>=0.0.7 <0.1",
+  "_id": "chainsaw@0.0.9",
+  "_inBundle": false,
+  "_integrity": "sha1-EaBRAtHEx4W20EFdM21aOhYSkT4=",
+  "_location": "/chainsaw",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "chainsaw@>=0.0.7 <0.1",
+    "name": "chainsaw",
+    "escapedName": "chainsaw",
+    "rawSpec": ">=0.0.7 <0.1",
+    "saveSpec": null,
+    "fetchSpec": ">=0.0.7 <0.1"
+  },
+  "_requiredBy": [
+    "/seq"
+  ],
+  "_resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.0.9.tgz";,
+  "_shasum": "11a05102d1c4c785b6d0415d336d5a3a1612913e",
+  "_spec": "chainsaw@>=0.0.7 <0.1",
+  "_where": "/home/xavier/dev/debian/packages/node-formidable/node_modules/seq",
+  "author": {
+    "name": "James Halliday",
+    "email": "mail@substack.net",
+    "url": "http://substack.net";
+  },
+  "bugs": {
+    "url": "https://github.com/substack/node-chainsaw/issues";
+  },
+  "bundleDependencies": false,
+  "dependencies": {
+    "traverse": ">=0.3.0 <0.4"
+  },
+  "deprecated": false,
+  "description": "Build chainable fluent interfaces the easy way... with a freakin' chainsaw!",
+  "engine": {
+    "node": ">=0.4.0"
+  },
+  "engines": {
+    "node": "*"
+  },
+  "homepage": "https://github.com/substack/node-chainsaw#readme";,
+  "keywords": [
+    "chain",
+    "fluent",
+    "interface",
+    "monad",
+    "monadic"
+  ],
+  "license": "MIT/X11",
+  "main": "./index.js",
+  "name": "chainsaw",
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/substack/node-chainsaw.git"
+  },
+  "version": "0.0.9"
+}
diff --git a/debian/tests/node_modules/findit/README.markdown b/debian/tests/node_modules/findit/README.markdown
new file mode 100644
index 0000000..d622819
--- /dev/null
+++ b/debian/tests/node_modules/findit/README.markdown
@@ -0,0 +1,101 @@
+findit
+======
+
+Recursively walk directory trees. Think `/usr/bin/find`.
+
+example time!
+=============
+
+callback style
+--------------
+
+````javascript
+require('findit').find(__dirname, function (file) {
+    console.log(file);
+})
+````
+
+emitter style
+-------------
+
+````javascript
+var finder = require('findit').find(__dirname);
+
+finder.on('directory', function (dir, stat) {
+    console.log(dir + '/');
+});
+
+finder.on('file', function (file, stat) {
+    console.log(file);
+});
+
+finder.on('link', function (link, stat) {
+    console.log(link);
+});
+````
+
+synchronous
+-----------
+
+````javascript
+var files = require('findit').sync(__dirname);
+    console.dir(files);
+````
+
+methods
+=======
+
+find(basedir, options, cb)
+-----------------
+
+Do an asynchronous recursive walk starting at `basedir`.
+
+Optionally supply an options object. Setting the property 'follow_symlinks'
+will follow symlinks.
+
+Optionally supply a callback that will get the same arguments as the path event
+documented below in "events".
+
+If `basedir` is actually a non-directory regular file, findit emits a single
+"file" event for it then emits "end".
+
+Findit uses `fs.lstat()` so symlinks are not traversed automatically. To have it
+follow symlinks, supply the options argument with 'follow_symlinks' set to true.
+Findit won't traverse an inode that it has seen before so directories can have
+symlink cycles and findit won't blow up.
+
+Returns an EventEmitter. See "events".
+
+sync(basedir, options, cb)
+-----------------
+
+Return an array of files and directories from a synchronous recursive walk
+starting at `basedir`.
+
+Optionally supply an options object. Setting the property 'follow_symlinks'
+will follow symlinks.
+
+An optional callback `cb` will get called with `cb(file, stat)` if specified.
+
+events
+======
+
+file: [ file, stat ]
+--------------------
+
+Emitted for just files which are not directories.
+
+directory : [ directory, stat ]
+-------------------------------
+
+Emitted for directories.
+
+path : [ file, stat ]
+---------------------
+
+Emitted for both files and directories.
+
+end
+---
+
+Emitted when the recursive walk is done.
diff --git a/debian/tests/node_modules/findit/examples/callback.js b/debian/tests/node_modules/findit/examples/callback.js
new file mode 100644
index 0000000..829630e
--- /dev/null
+++ b/debian/tests/node_modules/findit/examples/callback.js
@@ -0,0 +1,3 @@
+require('findit').find(__dirname, function (file) {
+    console.log(file);
+})
diff --git a/debian/tests/node_modules/findit/examples/emitter.js b/debian/tests/node_modules/findit/examples/emitter.js
new file mode 100644
index 0000000..f97adf3
--- /dev/null
+++ b/debian/tests/node_modules/findit/examples/emitter.js
@@ -0,0 +1,9 @@
+var finder = require('findit').find(__dirname);
+
+finder.on('directory', function (dir) {
+    console.log(dir + '/');
+});
+
+finder.on('file', function (file) {
+    console.log(file);
+});
diff --git a/debian/tests/node_modules/findit/examples/sync.js b/debian/tests/node_modules/findit/examples/sync.js
new file mode 100644
index 0000000..b09e0b2
--- /dev/null
+++ b/debian/tests/node_modules/findit/examples/sync.js
@@ -0,0 +1,2 @@
+var files = require('findit').findSync(__dirname);
+console.dir(files);
diff --git a/debian/tests/node_modules/findit/index.js b/debian/tests/node_modules/findit/index.js
new file mode 100644
index 0000000..c1d9eb9
--- /dev/null
+++ b/debian/tests/node_modules/findit/index.js
@@ -0,0 +1,142 @@
+var fs = require('fs');
+var path = require('path');
+var EventEmitter = require('events').EventEmitter;
+var Seq = require('seq');
+
+function createInodeChecker() {
+    var inodes = {};
+    return function inodeSeen(inode) {
+        if (inodes[inode]) {
+            return true;
+        } else {
+            inodes[inode] = true;
+            return false;
+        }
+    }
+}
+
+exports = module.exports = find;
+exports.find = find;
+function find (base, options, cb) {
+    cb = arguments[arguments.length - 1];
+    if (typeof(cb) !== 'function') {
+        cb = undefined;
+    }
+    var em = new EventEmitter;
+    var inodeSeen = createInodeChecker();
+    
+    function finder (dir, f) {
+        Seq()
+            .seq(fs.readdir, dir, Seq)
+            .flatten()
+            .seqEach(function (file) {
+                var p = dir + '/' + file;
+                fs.lstat(p, this.into(p));
+            })
+            .seq(function () {
+                this(null, Object.keys(this.vars));
+            })
+            .flatten()
+            .seqEach(function (file) {
+                var stat = this.vars[file];
+                if (cb) cb(file, stat);
+                
+                if (inodeSeen(stat.ino)) {
+                    // already seen this inode, probably a recursive symlink
+                    this(null);
+                }
+                else {
+                    em.emit('path', file, stat);
+                    
+                    if (stat.isSymbolicLink()) {
+                        em.emit('link', file, stat);
+                        if (options && options.follow_symlinks) {
+                          path.exists(file, function(exists) {
+                            if (exists) {
+                              fs.readlink(file, function(err, resolvedPath) {
+                                if (err) {
+                                  em.emit('error', err);
+                                } else {
+                                  finder(path.resolve(path.dir(file), resolvedPath));
+                                }
+                              });
+                            }
+                          });
+                        } else {
+                          this(null);
+                        }
+                    }
+                    else if (stat.isDirectory()) {
+                        em.emit('directory', file, stat);
+                        finder(file, this);
+                    }
+                    else {
+                        em.emit('file', file, stat);
+                        this(null);
+                    }
+                }
+            })
+            .seq(f.bind({}, null))
+            .catch(em.emit.bind(em, 'error'))
+        ;
+    }
+    
+    fs.lstat(base, function (err, s) {
+        if (err) {
+            em.emit('error', err);
+        }
+        if (s.isDirectory()) {
+            finder(base, em.emit.bind(em, 'end'));
+        }
+        else if (s.isSymbolicLink()) {
+            if (cb) cb(base, s);
+            em.emit('link', base, s);
+            em.emit('end');
+        }
+        else {
+            if (cb) cb(base, s);
+            em.emit('file', base, s);
+            em.emit('end');
+        }
+    });
+    
+    return em;
+};
+
+exports.findSync = function findSync(dir, options, callback) {
+    cb = arguments[arguments.length - 1];
+    if (typeof(cb) !== 'function') {
+        cb = undefined;
+    }
+    var inodeSeen = createInodeChecker();
+    var files = [];
+    var fileQueue = [];
+    var processFile = function processFile(file) {
+        var stat = fs.lstatSync(file);
+        if (inodeSeen(stat.ino)) {
+            return;
+        }
+        files.push(file);
+        cb && cb(file, stat)
+        if (stat.isDirectory()) {
+            fs.readdirSync(file).forEach(function(f) { fileQueue.push(path.join(file, f)); });
+        } else if (stat.isSymbolicLink()) {
+            if (options && options.follow_symlinks && path.existsSync(file)) {
+                fileQueue.push(fs.realpathSync(file));
+            }
+        }
+    };
+    /* we don't include the starting directory unless it is a file */
+    var stat = fs.lstatSync(dir);
+    if (stat.isDirectory()) {
+        fs.readdirSync(dir).forEach(function(f) { fileQueue.push(path.join(dir, f)); });
+    } else {
+        fileQueue.push(dir);
+    }
+    while (fileQueue.length > 0) {
+        processFile(fileQueue.shift());
+    }
+    return files;
+};
+
+exports.find.sync = exports.findSync;
diff --git a/debian/tests/node_modules/findit/package.json b/debian/tests/node_modules/findit/package.json
new file mode 100644
index 0000000..0b4cac9
--- /dev/null
+++ b/debian/tests/node_modules/findit/package.json
@@ -0,0 +1,68 @@
+{
+  "_from": "findit@^0.1.2",
+  "_id": "findit@0.1.2",
+  "_inBundle": false,
+  "_integrity": "sha1-rH/mAM1qMqNWcoNrdM9vHd4uEfg=",
+  "_location": "/findit",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "findit@^0.1.2",
+    "name": "findit",
+    "escapedName": "findit",
+    "rawSpec": "^0.1.2",
+    "saveSpec": null,
+    "fetchSpec": "^0.1.2"
+  },
+  "_requiredBy": [
+    "#DEV:/"
+  ],
+  "_resolved": "https://registry.npmjs.org/findit/-/findit-0.1.2.tgz";,
+  "_shasum": "ac7fe600cd6a32a35672836b74cf6f1dde2e11f8",
+  "_spec": "findit@^0.1.2",
+  "_where": "/home/xavier/dev/debian/packages/node-formidable",
+  "author": {
+    "name": "James Halliday",
+    "email": "mail@substack.net",
+    "url": "http://substack.net";
+  },
+  "bugs": {
+    "url": "https://github.com/substack/node-findit/issues";
+  },
+  "bundleDependencies": false,
+  "dependencies": {
+    "seq": ">=0.1.7"
+  },
+  "deprecated": false,
+  "description": "Walk a directory tree.",
+  "devDependencies": {
+    "expresso": "0.7.x",
+    "hashish": ">=0.0.2 <0.1"
+  },
+  "engine": [
+    "node >=0.2.0"
+  ],
+  "engines": {
+    "node": "*"
+  },
+  "homepage": "https://github.com/substack/node-findit#readme";,
+  "keywords": [
+    "find",
+    "walk",
+    "directory",
+    "recursive",
+    "tree"
+  ],
+  "license": "MIT/X11",
+  "main": "./index.js",
+  "name": "findit",
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/substack/node-findit.git"
+  },
+  "scripts": {
+    "test": "expresso"
+  },
+  "version": "0.1.2"
+}
diff --git a/debian/tests/node_modules/gently/Makefile b/debian/tests/node_modules/gently/Makefile
new file mode 100644
index 0000000..01f7140
--- /dev/null
+++ b/debian/tests/node_modules/gently/Makefile
@@ -0,0 +1,4 @@
+test:
+	@find test/simple/test-*.js | xargs -n 1 -t node
+
+.PHONY: test
\ No newline at end of file
diff --git a/debian/tests/node_modules/gently/Readme.md b/debian/tests/node_modules/gently/Readme.md
new file mode 100644
index 0000000..f8f0c66
--- /dev/null
+++ b/debian/tests/node_modules/gently/Readme.md
@@ -0,0 +1,167 @@
+# Gently
+
+## Purpose
+
+A node.js module that helps with stubbing and behavior verification. It allows you to test the most remote and nested corners of your code while keeping being fully unobtrusive.
+
+## Features
+
+* Overwrite and stub individual object functions
+* Verify that all expected calls have been made in the expected order
+* Restore stubbed functions to their original behavior
+* Detect object / class names from obj.constructor.name and obj.toString()
+* Hijack any required module function or class constructor
+
+## Installation
+
+Via [npm](http://github.com/isaacs/npm):
+
+    npm install gently@latest
+
+## Example
+
+Make sure your dog is working properly:
+
+    function Dog() {}
+
+    Dog.prototype.seeCat = function() {
+      this.bark('whuf, whuf');
+      this.run();
+    }
+
+    Dog.prototype.bark = function(bark) {
+      require('sys').puts(bark);
+    }
+
+    var gently = new (require('gently'))
+      , assert = require('assert')
+      , dog = new Dog();
+
+    gently.expect(dog, 'bark', function(bark) {
+      assert.equal(bark, 'whuf, whuf');
+    });
+    gently.expect(dog, 'run');
+
+    dog.seeCat();
+
+You can also easily test event emitters with this, for example a simple sequence of 2 events emitted by `fs.WriteStream`:
+
+    var gently = new (require('gently'))
+      , stream = new (require('fs').WriteStream)('my_file.txt');
+
+    gently.expect(stream, 'emit', function(event) {
+      assert.equal(event, 'open');
+    });
+
+    gently.expect(stream, 'emit', function(event) {
+      assert.equal(event, 'drain');
+    });
+
+For a full read world example, check out this test case: [test-incoming-form.js](http://github.com/felixge/node-formidable/blob/master/test/simple/test-incoming-form.js) (in [node-formdiable](http://github.com/felixge/node-formidable)).
+
+## API
+
+### Gently
+
+#### new Gently()
+
+Creates a new gently instance. It listens to the process `'exit'` event to make sure all expectations have been verified.
+
+#### gently.expect(obj, method, [[count], stubFn])
+
+Creates an expectation for an objects method to be called. You can optionally specify the call `count` you are expecting, as well as `stubFn` function that will run instead of the original function.
+
+Returns a reference to the function that is getting overwritten.
+
+#### gently.expect([count], stubFn)
+
+Returns a function that is supposed to be executed `count` times, delegating any calls to the provided `stubFn` function. Naming your stubFn closure will help to properly diagnose errors that are being thrown:
+
+    childProcess.exec('ls', gently.expect(function lsCallback(code) {
+      assert.equal(0, code);
+    }));
+
+#### gently.restore(obj, method)
+
+Restores an object method that has been previously overwritten using `gently.expect()`.
+
+#### gently.hijack(realRequire)
+
+Returns a new require functions that catches a reference to all required modules into `gently.hijacked`.
+
+To use this function, include a line like this in your `'my-module.js'`.
+
+    if (global.GENTLY) require = GENTLY.hijack(require);
+
+    var sys = require('sys');
+    exports.hello = function() {
+      sys.log('world');
+    };
+
+Now you can write a test for the module above:
+
+    var gently = global.GENTLY = new (require('gently'))
+      , myModule = require('./my-module');
+
+    gently.expect(gently.hijacked.sys, 'log', function(str) {
+      assert.equal(str, 'world');
+    });
+
+    myModule.hello();
+
+#### gently.stub(location, [exportsName])
+
+Returns a stub class that will be used instead of the real class from the module at `location` with the given `exportsName`.
+
+This allows to test an OOP version of the previous example, where `'my-module.js'`.
+
+    if (global.GENTLY) require = GENTLY.hijack(require);
+
+    var World = require('./world');
+
+    exports.hello = function() {
+      var world = new World();
+      world.hello();
+    }
+
+And `world.js` looks like this:
+
+    var sys = require('sys');
+
+    function World() {
+
+    }
+    module.exports = World;
+
+    World.prototype.hello = function() {
+      sys.log('world');
+    };
+
+Testing `'my-module.js'` can now easily be accomplished:
+
+    var gently = global.GENTLY = new (require('gently'))
+      , WorldStub = gently.stub('./world')
+      , myModule = require('./my-module')
+      , WORLD;
+
+    gently.expect(WorldStub, 'new', function() {
+      WORLD = this;
+    });
+
+    gently.expect(WORLD, 'hello');
+
+    myModule.hello();
+
+#### gently.hijacked
+
+An object that holds the references to all hijacked modules.
+
+#### gently.verify([msg])
+
+Verifies that all expectations of this gently instance have been satisfied. If not called manually, this method is called when the process `'exit'` event is fired.
+
+If `msg` is given, it will appear in any error that might be thrown.
+
+## License
+
+Gently is licensed under the MIT license.
\ No newline at end of file
diff --git a/debian/tests/node_modules/gently/example/dog.js b/debian/tests/node_modules/gently/example/dog.js
new file mode 100644
index 0000000..022fae0
--- /dev/null
+++ b/debian/tests/node_modules/gently/example/dog.js
@@ -0,0 +1,22 @@
+require('../test/common');
+function Dog() {}
+
+Dog.prototype.seeCat = function() {
+  this.bark('whuf, whuf');
+  this.run();
+}
+
+Dog.prototype.bark = function(bark) {
+  require('sys').puts(bark);
+}
+
+var gently = new (require('gently'))
+  , assert = require('assert')
+  , dog = new Dog();
+
+gently.expect(dog, 'bark', function(bark) {
+  assert.equal(bark, 'whuf, whuf');
+});
+gently.expect(dog, 'run');
+
+dog.seeCat();
\ No newline at end of file
diff --git a/debian/tests/node_modules/gently/example/event_emitter.js b/debian/tests/node_modules/gently/example/event_emitter.js
new file mode 100644
index 0000000..7def134
--- /dev/null
+++ b/debian/tests/node_modules/gently/example/event_emitter.js
@@ -0,0 +1,11 @@
+require('../test/common');
+var gently = new (require('gently'))
+  , stream = new (require('fs').WriteStream)('my_file.txt');
+
+gently.expect(stream, 'emit', function(event) {
+  assert.equal(event, 'open');
+});
+
+gently.expect(stream, 'emit', function(event) {
+  assert.equal(event, 'drain');
+});
\ No newline at end of file
diff --git a/debian/tests/node_modules/gently/index.js b/debian/tests/node_modules/gently/index.js
new file mode 100644
index 0000000..69122bd
--- /dev/null
+++ b/debian/tests/node_modules/gently/index.js
@@ -0,0 +1 @@
+module.exports = require('./lib/gently');
\ No newline at end of file
diff --git a/debian/tests/node_modules/gently/lib/gently/.gently.js.swp b/debian/tests/node_modules/gently/lib/gently/.gently.js.swp
new file mode 100644
index 0000000..f2ce523
Binary files /dev/null and b/debian/tests/node_modules/gently/lib/gently/.gently.js.swp differ
diff --git a/debian/tests/node_modules/gently/lib/gently/gently.js b/debian/tests/node_modules/gently/lib/gently/gently.js
new file mode 100644
index 0000000..7ff7e51
--- /dev/null
+++ b/debian/tests/node_modules/gently/lib/gently/gently.js
@@ -0,0 +1,151 @@
+var path = require('path');
+
+function Gently() {
+  this.expectations = [];
+  this.hijacked = {};
+
+  var self = this;
+  process.addListener('exit', function() {
+    self.verify('process exit');
+  });
+};
+module.exports = Gently;
+
+Gently.prototype.stub = function(location, exportsName) {
+  function Stub() {
+    Stub['new'].apply(this, arguments);
+  };
+
+  Stub['new'] = function () {};
+
+  var stubName = 'require('+JSON.stringify(location)+')';
+  if (exportsName) {
+    stubName += '.'+exportsName;
+  }
+
+  Stub.prototype.toString = Stub.toString = function() {
+    return stubName;
+  };
+
+  var exports = this.hijacked[location] || {};
+  if (exportsName) {
+    exports[exportsName] = Stub;
+  } else {
+    exports = Stub;
+  }
+
+  this.hijacked[location] = exports;
+  return Stub;
+};
+
+Gently.prototype.hijack = function(realRequire) {
+  var self = this;
+  return function(location) {
+    return self.hijacked[location] = (self.hijacked[location])
+      ? self.hijacked[location]
+      : realRequire(location);
+  };
+};
+
+Gently.prototype.expect = function(obj, method, count, stubFn) {
+  if (typeof obj != 'function' && typeof obj != 'object') {
+    throw new Error
+      ( 'Bad 1st argument for gently.expect(), '
+      + 'object or function expected, got: '+(typeof obj)
+      );
+  } else if (typeof obj == 'function' && (typeof method != 'string')) {
+    // expect(stubFn) interface
+    stubFn = obj;
+    obj = null;
+    method = null;
+    count = 1;
+  } else if (typeof method == 'function') {
+    // expect(count, stubFn) interface
+    count = obj;
+    stubFn = method;
+    obj = null;
+    method = null;
+  } else if (typeof count == 'function') {
+    // expect(obj, method, stubFn) interface
+    stubFn = count;
+    count = 1;
+  } else if (count === undefined) {
+    // expect(obj, method) interface
+    count = 1;
+  }
+
+  var name = this._name(obj, method, stubFn);
+  while (count-- > 0) {
+    this.expectations.push({obj: obj, method: method, stubFn: stubFn, name: name});
+  }
+
+  var self = this;
+  function delegate() {
+    return self._stubFn(this, obj, method, name, Array.prototype.slice.call(arguments));
+  }
+
+  if (!obj) {
+    return delegate;
+  }
+
+  var original = (obj[method])
+    ? obj[method]._original || obj[method]
+    : undefined;
+
+  obj[method] = delegate;
+  return obj[method]._original = original;
+};
+
+Gently.prototype.restore = function(obj, method) {
+  if (!obj[method] || !obj[method]._original) {
+    throw new Error(this._name(obj, method)+' is not gently stubbed');
+  }
+  obj[method] = obj[method]._original;
+};
+
+Gently.prototype.verify = function(msg) {
+  if (!this.expectations.length) {
+    return;
+  }
+
+  var expectation = this.expectations[0];
+  throw new Error
+    ( 'Expected call to '+expectation.name+' did not happen'
+    + ( (msg)
+        ? ' ('+msg+')'
+        : ''
+      )
+    );
+};
+
+Gently.prototype._stubFn = function(self, obj, method, name, args) {
+  var expectation = this.expectations.shift();
+  if (!expectation) {
+    throw new Error('Unexpected call to '+name+', no call was expected');
+  }
+
+  if (expectation.obj !== obj || expectation.method !== method) {
+    this.expectations.unshift(expectation);
+    throw new Error('Unexpected call to '+name+', expected call to '+ expectation.name);
+  }
+
+  if (expectation.stubFn) {
+    return expectation.stubFn.apply(self, args);
+  }
+};
+
+Gently.prototype._name = function(obj, method, stubFn) {
+  if (obj) {
+    var objectName = obj.toString();
+    if (objectName == '[object Object]' && obj.constructor.name) {
+      objectName = '['+obj.constructor.name+']';
+    }
+    return (objectName)+'.'+method+'()';
+  }
+
+  if (stubFn.name) {
+    return stubFn.name+'()';
+  }
+
+  return '>> '+stubFn.toString()+' <<';
+};
diff --git a/debian/tests/node_modules/gently/lib/gently/index.js b/debian/tests/node_modules/gently/lib/gently/index.js
new file mode 100644
index 0000000..64c1977
--- /dev/null
+++ b/debian/tests/node_modules/gently/lib/gently/index.js
@@ -0,0 +1 @@
+module.exports = require('./gently');
\ No newline at end of file
diff --git a/debian/tests/node_modules/gently/package.json b/debian/tests/node_modules/gently/package.json
new file mode 100644
index 0000000..22feffe
--- /dev/null
+++ b/debian/tests/node_modules/gently/package.json
@@ -0,0 +1,37 @@
+{
+  "_from": "gently@^0.8.0",
+  "_id": "gently@0.8.0",
+  "_inBundle": false,
+  "_integrity": "sha1-vDhduZ7BmU3WpENo4KvrSCsZKyw=",
+  "_location": "/gently",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "gently@^0.8.0",
+    "name": "gently",
+    "escapedName": "gently",
+    "rawSpec": "^0.8.0",
+    "saveSpec": null,
+    "fetchSpec": "^0.8.0"
+  },
+  "_requiredBy": [
+    "#DEV:/"
+  ],
+  "_resolved": "https://registry.npmjs.org/gently/-/gently-0.8.0.tgz";,
+  "_shasum": "bc385db99ec1994dd6a44368e0abeb482b192b2c",
+  "_spec": "gently@^0.8.0",
+  "_where": "/home/xavier/dev/debian/packages/node-formidable",
+  "bundleDependencies": false,
+  "deprecated": false,
+  "description": "## Purpose",
+  "directories": {
+    "lib": "./lib/gently"
+  },
+  "engines": {
+    "node": "*"
+  },
+  "main": "./lib/gently/index",
+  "name": "gently",
+  "version": "0.8.0"
+}
diff --git a/debian/tests/node_modules/seq/README.markdown b/debian/tests/node_modules/seq/README.markdown
new file mode 100644
index 0000000..3689261
--- /dev/null
+++ b/debian/tests/node_modules/seq/README.markdown
@@ -0,0 +1,442 @@
+Seq
+===
+
+Seq is an asynchronous flow control library with a chainable interface for
+sequential and parallel actions. Even the error handling is chainable.
+
+Each action in the chain operates on a stack of values.
+There is also a variables hash for storing values by name.
+
+[TOC]
+
+
+
+Examples
+========
+
+stat_all.js
+-----------
+
+````javascript
+var fs = require('fs');
+var Hash = require('hashish');
+var Seq = require('seq');
+
+Seq()
+    .seq(function () {
+        fs.readdir(__dirname, this);
+    })
+    .flatten()
+    .parEach(function (file) {
+        fs.stat(__dirname + '/' + file, this.into(file));
+    })
+    .seq(function () {
+        var sizes = Hash.map(this.vars, function (s) { return s.size })
+        console.dir(sizes);
+    })
+;
+````
+
+Output:
+
+    { 'stat_all.js': 404, 'parseq.js': 464 }
+
+parseq.js
+---------
+
+````javascript
+var fs = require('fs');
+var exec = require('child_process').exec;
+
+var Seq = require('seq');
+Seq()
+    .seq(function () {
+        exec('whoami', this)
+    })
+    .par(function (who) {
+        exec('groups ' + who, this);
+    })
+    .par(function (who) {
+        fs.readFile(__filename, 'ascii', this);
+    })
+    .seq(function (groups, src) {
+        console.log('Groups: ' + groups.trim());
+        console.log('This file has ' + src.length + ' bytes');
+    })
+;
+````
+
+Output:
+
+    Groups: substack : substack dialout cdrom floppy audio src video plugdev games netdev fuse www
+    This file has 464 bytes
+
+
+
+
+API
+===
+
+Each method executes callbacks with a context (its `this`) described in the next
+section. Every method returns `this`.
+
+Whenever `this()` is called with a non-falsy first argument, the error value
+propagates down to the first `catch` it sees, skipping over all actions in
+between. There is an implicit `catch` at the end of all chains that prints the
+error stack if available and otherwise just prints the error.
+
+
+
+Seq(xs=[])
+----------
+
+The constructor function creates a new `Seq` chain with the methods described
+below. The optional array argument becomes the new context stack.
+
+Array argument is new in 0.3. `Seq()` now behaves like `Seq.ap()`.
+
+
+.seq(cb)
+--------
+.seq(key, cb, *args)
+--------------------
+
+This eponymous function executes actions sequentially.
+Once all running parallel actions are finished executing,
+the supplied callback is `apply()`'d with the context stack.
+
+To execute the next action in the chain, call `this()`. The first
+argument must be the error value. The rest of the values will become the stack
+for the next action in the chain and are also available at `this.args`.
+
+If `key` is specified, the second argument sent to `this` goes to
+`this.vars[key]` in addition to the stack and `this.args`.
+`this.vars` persists across all requests unless it is overwritten.
+
+All arguments after `cb` will be bound to `cb`, which is useful because
+`.bind()` makes you set `this`. If you pass in `Seq` in the arguments list,
+it'll get transformed into `this` so that you can do:
+
+````javascript
+Seq()
+    .seq(fs.readdir, __dirname, Seq)
+    .seq(function (files) { console.dir(files) })
+;
+````
+
+which prints an array of files in `__dirname`.
+
+
+.par(cb)
+--------
+.par(key, cb, *args)
+--------------------
+
+Use `par` to execute actions in parallel.
+Chain multiple parallel actions together and collect all the responses on the
+stack with a sequential operation like `seq`.
+
+Each `par` sets one element in the stack with the second argument to `this()` in
+the order in which it appears, so multiple `par`s can be chained together.
+
+Like with `seq`, the first argument to `this()` should be the error value and
+the second will get pushed to the stack. Further arguments are available in
+`this.args`.
+
+If `key` is specified, the result from the second argument send to `this()` goes
+to `this.vars[key]`.
+`this.vars` persists across all requests unless it is overwritten.
+
+All arguments after `cb` will be bound to `cb`, which is useful because
+`.bind()` makes you set `this`. Like `.seq()`, you can pass along `Seq` in these
+bound arguments and it will get tranformed into `this`.
+
+
+.catch(cb)
+----------
+
+Catch errors. Whenever a function calls `this` with a non-falsy first argument,
+the message propagates down the chain to the first `catch` it sees.
+The callback `cb` fires with the error object as its first argument and the key
+that the action that caused the error was populating, which may be undefined.
+
+`catch` is a sequential action and further actions may appear after a `catch` in
+a chain. If the execution reaches a `catch` in a chain and no error has occured,
+the `catch` is skipped over.
+
+For convenience, there is a default error handler at the end of all chains.
+This default error handler looks like this:
+
+````javascript
+.catch(function (err) {
+    console.error(err.stack ? err.stack : err)
+})
+````
+
+
+.forEach(cb)
+------------
+
+Execute each action in the stack under the context of the chain object.
+`forEach` does not wait for any of the actions to finish and does not itself
+alter the stack, but the callback may alter the stack itself by modifying
+`this.stack`.
+
+The callback is executed `cb(x,i)` where `x` is the element and `i` is the
+index. 
+
+`forEach` is a sequential operation like `seq` and won't run until all pending
+parallel requests yield results.
+
+
+.seqEach(cb)
+------------
+
+Like `forEach`, call `cb` for each element on the stack, but unlike `forEach`,
+`seqEach` waits for the callback to yield with `this` before moving on to the
+next element in the stack.
+
+The callback is executed `cb(x,i)` where `x` is the element and `i` is the
+index. 
+
+If `this()` is supplied non-falsy error, the error propagates downward but any
+other arguments are ignored. `seqEach` does not modify the stack itself.
+
+
+.parEach(cb)
+------------
+.parEach(limit, cb)
+-------------------
+
+Like `forEach`, calls cb for each element in the stack and doesn't wait for the
+callback to yield a result with `this()` before moving on to the next iteration.
+Unlike `forEach`, `parEach` waits for all actions to call `this()` before moving
+along to the next action in the chain.
+
+The callback is executed `cb(x,i)` where `x` is the element and `i` is the
+index. 
+
+`parEach` does not modify the stack itself and errors supplied to `this()`
+propagate.
+
+Optionally, if limit is supplied to `parEach`, at most `limit` callbacks will be
+active at a time.
+
+
+.seqMap(cb)
+-----------
+
+Like `seqEach`, but collect the values supplied to `this` and set the stack to
+these values.
+
+
+.parMap(cb)
+-----------
+.parMap(limit, cb)
+------------------
+
+Like `parEach`, but collect the values supplied to `this` and set the stack to
+these values.
+
+
+.seqFilter(cb)
+-----------
+
+Executes the callback `cb(x, idx)` against each element on the stack, waiting for the
+callback to yield with `this` before moving on to the next element. If the callback 
+returns an error or a falsey value, the element will not be included in the resulting
+stack.
+
+Any errors from the callback are consumed and **do not** propagate.
+
+Calls to `this.into(i)` will place the value, if accepted by the callback, at the index in
+the results as if it were ordered at i-th index on the stack before filtering (with ties
+broken by the values). This implies `this.into` will never override another stack value
+even if their indices collide. Finally, the value will only actually appear at `i` if the
+callback accepts or moves enough values before `i`.
+
+
+.parFilter(cb)
+-----------
+.parFilter(limit, cb)
+------------------
+
+Executes the callback `cb(x, idx)` against each element on the stack, but **does not**
+wait for it to yield before moving on to the next element. If the callback returns an
+error or a falsey value, the element will not be included in the resulting stack.
+
+Any errors from the callback are consumed and **do not** propagate.
+
+Calls to `this.into(i)` will place the value, if accepted by the callback, at the index in
+the results as if it were ordered at i-th index on the stack before filtering (with ties
+broken by the values). This implies `this.into` will never override another stack value
+even if their indices collide. Finally, the value will only actually appear at `i` if the
+callback accepts or moves enough values before `i`.
+
+Optionally, if limit is supplied to `parEach`, at most `limit` callbacks will be
+active at a time.
+
+
+.do(cb)
+-------
+Create a new nested context. `cb`'s first argument is the previous context, and `this`
+is the nested `Seq` object.
+
+
+.flatten(fully=true)
+--------------------
+
+Recursively flatten all the arrays in the stack. Set `fully=false` to flatten
+only one level.
+
+
+.unflatten()
+------------
+
+Turn the contents of the stack into a single array item. You can think of it
+as the inverse of `flatten(false)`.
+
+
+.extend([x,y...])
+-----------------
+
+Like `push`, but takes an array. This is like python's `[].extend()`.
+
+
+.set(xs)
+--------
+
+Set the stack to a new array. This assigns the reference, it does not copy.
+
+
+.empty()
+--------
+
+Set the stack to [].
+
+
+.push(x,y...), .pop(), .shift(), .unshift(x), .splice(...), reverse()
+---------------------------------------------------------------------
+.map(...), .filter(...), .reduce(...)
+-------------------------------------
+
+Executes an array operation on the stack.
+
+The methods `map`, `filter`, and `reduce` are also proxies to their Array counterparts:
+they have identical signatures to the Array methods, operate synchronously on the context
+stack, and do not pass a Context object (unlike `seqMap` and `parMap`).
+
+The result of the transformation is assigned to the context stack; in the case of `reduce`,
+if you do not return an array, the value will be wrapped in one.
+
+````javascript
+Seq([1, 2, 3])
+    .reduce(function(sum, x){ return sum + x; }, 0)
+    .seq(function(sum){
+        console.log('sum: %s', sum);
+        // sum: 6
+        console.log('stack is Array?', Array.isArray(this.stack));
+        // stack is Array: true
+        console.log('stack:', this.stack);
+        // stack: [6]
+    })
+;
+````
+
+
+
+
+Explicit Parameters
+-------------------
+
+For environments like coffee-script or nested logic where threading `this` is
+bothersome, you can use:
+
+* seq_
+* par_
+* forEach_
+* seqEach_
+* parEach_
+* seqMap_
+* parMap_
+
+which work exactly like their un-underscored counterparts except for the first
+parameter to the supplied callback is set to the context, `this`.
+
+
+
+Context Object
+==============
+
+Each callback gets executed with its `this` set to a function in order to yield
+results, error values, and control. The function also has these useful fields:
+
+this.stack
+----------
+
+The execution stack.
+
+this.stack_
+-----------
+
+The previous stack value, mostly used internally for hackish purposes.
+
+this.vars
+---------
+
+A hash of key/values populated with `par(key, ...)`, `seq(key, ...)` and
+`this.into(key)`.
+
+this.into(key)
+--------------
+
+Instead of sending values to the stack, sets a key and returns `this`.
+Use `this.into(key)` interchangeably with `this` for yielding keyed results.
+`into` overrides the optional key set by `par(key, ...)` and `seq(key, ...)`.
+
+this.ok
+-------
+
+Set the `err` to null. Equivalent to `this.bind(this, null)`.
+
+this.args
+---------
+
+`this.args` is like `this.stack`, but it contains all the arguments to `this()`
+past the error value, not just the first. `this.args` is an array with the same
+indices as `this.stack` but also stores keyed values for the last sequential
+operation. Each element in `this.array` is set to `[].slice.call(arguments, 1)`
+from inside `this()`.
+
+this.error
+----------
+
+This is used for error propagation. You probably shouldn't mess with it.
+
+
+
+Installation
+============
+
+With [npm](http://github.com/isaacs/npm), just do:
+
+    npm install seq
+
+or clone this project on github:
+
+    git clone http://github.com/substack/node-seq.git
+
+To run the tests with [expresso](http://github.com/visionmedia/expresso),
+just do:
+
+    expresso
+
+
+
+Dependencies
+------------
+
+This module uses [chainsaw](http://github.com/substack/node-chainsaw)
+When you `npm install seq` this dependency will automatically be installed.
+
+
diff --git a/debian/tests/node_modules/seq/examples/join.js b/debian/tests/node_modules/seq/examples/join.js
new file mode 100644
index 0000000..cc05c4f
--- /dev/null
+++ b/debian/tests/node_modules/seq/examples/join.js
@@ -0,0 +1,18 @@
+var Seq = require('seq');
+Seq()
+    .par(function () {
+        var that = this;
+        setTimeout(function () { that(null, 'a') }, 300);
+    })
+    .par(function () {
+        var that = this;
+        setTimeout(function () { that(null, 'b') }, 200);
+    })
+    .par(function () {
+        var that = this;
+        setTimeout(function () { that(null, 'c') }, 100);
+    })
+    .seq(function (a, b, c) {
+        console.dir([ a, b, c ])
+    })
+;
diff --git a/debian/tests/node_modules/seq/examples/parseq.coffee b/debian/tests/node_modules/seq/examples/parseq.coffee
new file mode 100644
index 0000000..d4ca0ab
--- /dev/null
+++ b/debian/tests/node_modules/seq/examples/parseq.coffee
@@ -0,0 +1,12 @@
+fs = require 'fs'
+exec = require('child_process').exec
+Seq = require 'seq'
+
+Seq()
+    .seq_((next) -> exec 'whoami', next)
+    .par_((next, who) -> exec('groups ' + who, next))
+    .par_((next, who) -> fs.readFile(__filename, 'utf8', next))
+    .seq_((next, groups, src) ->
+        console.log('Groups: ' + groups.trim())
+        console.log('This file has ' + src.length + ' bytes')
+    )
diff --git a/debian/tests/node_modules/seq/examples/parseq.js b/debian/tests/node_modules/seq/examples/parseq.js
new file mode 100644
index 0000000..2cdcf21
--- /dev/null
+++ b/debian/tests/node_modules/seq/examples/parseq.js
@@ -0,0 +1,19 @@
+var fs = require('fs');
+var exec = require('child_process').exec;
+
+var Seq = require('seq');
+Seq()
+    .seq(function () {
+        exec('whoami', this)
+    })
+    .par(function (who) {
+        exec('groups ' + who, this);
+    })
+    .par(function (who) {
+        fs.readFile(__filename, 'utf8', this);
+    })
+    .seq(function (groups, src) {
+        console.log('Groups: ' + groups.trim());
+        console.log('This file has ' + src.length + ' bytes');
+    })
+;
diff --git a/debian/tests/node_modules/seq/examples/stat_all.coffee b/debian/tests/node_modules/seq/examples/stat_all.coffee
new file mode 100644
index 0000000..83ec0b2
--- /dev/null
+++ b/debian/tests/node_modules/seq/examples/stat_all.coffee
@@ -0,0 +1,16 @@
+fs = require 'fs'
+Hash = require 'hashish'
+Seq = require 'seq'
+
+Seq()
+    .seq_((next) ->
+        fs.readdir(__dirname, next)
+    )
+    .flatten()
+    .parEach_((next, file) ->
+        fs.stat(__dirname + '/' + file, next.into(file))
+    )
+    .seq_((next) ->
+        sizes = Hash.map(next.vars, (s) -> s.size)
+        console.dir sizes
+    )
diff --git a/debian/tests/node_modules/seq/examples/stat_all.js b/debian/tests/node_modules/seq/examples/stat_all.js
new file mode 100644
index 0000000..b9962eb
--- /dev/null
+++ b/debian/tests/node_modules/seq/examples/stat_all.js
@@ -0,0 +1,17 @@
+var fs = require('fs');
+var Hash = require('hashish');
+var Seq = require('seq');
+
+Seq()
+    .seq(function () {
+        fs.readdir(__dirname, this);
+    })
+    .flatten()
+    .parEach(function (file) {
+        fs.stat(__dirname + '/' + file, this.into(file));
+    })
+    .seq(function () {
+        var sizes = Hash.map(this.vars, function (s) { return s.size })
+        console.dir(sizes);
+    })
+;
diff --git a/debian/tests/node_modules/seq/index.js b/debian/tests/node_modules/seq/index.js
new file mode 100755
index 0000000..4e0888b
--- /dev/null
+++ b/debian/tests/node_modules/seq/index.js
@@ -0,0 +1,520 @@
+var EventEmitter = require('events').EventEmitter;
+var Hash = require('hashish');
+var Chainsaw = require('chainsaw');
+
+module.exports = Seq;
+function Seq (xs) {
+    if (xs && !Array.isArray(xs) || arguments.length > 1) {
+        throw new Error('Optional argument to Seq() is exactly one Array');
+    }
+    
+    var ch = Chainsaw(function (saw) {
+        builder.call(this, saw, xs || []);
+    });
+    
+    process.nextTick(function () {
+        ch['catch'](function (err) {
+            console.error(err.stack ? err.stack : err)
+        });
+    });
+    return ch;
+}
+
+Seq.ap = Seq; // for compatability with versions <0.3
+
+function builder (saw, xs) {
+    var context = {
+        vars : {},
+        args : {},
+        stack : xs,
+        error : null
+    };
+    context.stack_ = context.stack;
+    
+    function action (step, key, f, g) {
+        var cb = function (err) {
+            var args = [].slice.call(arguments, 1);
+            if (err) {
+                context.error = { message : err, key : key };
+                saw.jump(lastPar);
+                saw.down('catch');
+                g();
+            }
+            else {
+                if (typeof key == 'number') {
+                    context.stack_[key] = args[0];
+                    context.args[key] = args;
+                }
+                else {
+                    context.stack_.push.apply(context.stack_, args);
+                    if (key !== undefined) {
+                        context.vars[key] = args[0];
+                        context.args[key] = args;
+                    }
+                }
+                if (g) g(args, key);
+            }
+        };
+        Hash(context).forEach(function (v,k) { cb[k] = v });
+        
+        cb.into = function (k) {
+            key = k;
+            return cb;
+        };
+        
+        cb.next = function (err, xs) {
+            context.stack_.push.apply(context.stack_, xs);
+            cb.apply(cb, [err].concat(context.stack));
+        };
+        
+        cb.pass = function (err) {
+            cb.apply(cb, [err].concat(context.stack));
+        };
+        
+        cb.ok = cb.bind(cb, null);
+        
+        f.apply(cb, context.stack);
+    }
+    
+    var running = 0;
+    var errors = 0;
+    
+    this.seq = function (key, cb) {
+        var bound = [].slice.call(arguments, 2);
+        
+        if (typeof key === 'function') {
+            if (arguments.length > 1) bound.unshift(cb);
+            cb = key;
+            key = undefined;
+        }
+        
+        if (context.error) saw.next()
+        else if (running === 0) {
+            action(saw.step, key,
+                function () {
+                    context.stack_ = [];
+                    var args = [].slice.call(arguments);
+                    args.unshift.apply(args, bound.map(function (arg) {
+                        return arg === Seq ? this : arg
+                    }, this));
+                    
+                    cb.apply(this, args);
+                }, function () {
+                    context.stack = context.stack_;
+                    saw.next()
+                }
+            );
+        }
+    };
+    
+    var lastPar = null;
+    this.par = function (key, cb) {
+        lastPar = saw.step;
+        
+        if (running == 0) {
+            // empty the active stack for the first par() in a chain
+            context.stack_ = [];
+        }
+        
+        var bound = [].slice.call(arguments, 2);
+        if (typeof key === 'function') {
+            if (arguments.length > 1) bound.unshift(cb);
+            cb = key;
+            key = context.stack_.length;
+            context.stack_.push(null);
+        }
+        var cb_ = function () {
+            var args = [].slice.call(arguments);
+            args.unshift.apply(args, bound.map(function (arg) {
+                return arg === Seq ? this : arg
+            }, this));
+            
+            cb.apply(this, args);
+        };
+        
+        running ++;
+        
+        var step = saw.step;
+        process.nextTick(function () {
+            action(step, key, cb_, function (args) {
+                if (!args) errors ++;
+                
+                running --;
+                if (running == 0) {
+                    context.stack = context.stack_.slice();
+                    saw.step = lastPar;
+                    if (errors > 0) saw.down('catch');
+                    errors = 0;
+                    saw.next();
+                }
+            });
+        });
+        saw.next();
+    };
+    
+    [ 'seq', 'par' ].forEach(function (name) {
+        this[name + '_'] = function (key) {
+            var args = [].slice.call(arguments);
+            
+            var cb = typeof key === 'function'
+                ? args[0] : args[1];
+            
+            var fn = function () {
+                var argv = [].slice.call(arguments);
+                argv.unshift(this);
+                cb.apply(this, argv);
+            };
+            
+            if (typeof key === 'function') {
+                args[0] = fn;
+            }
+            else {
+                args[1] = fn;
+            }
+            
+            this[name].apply(this, args);
+        };
+    }, this);
+    
+    this['catch'] = function (cb) {
+        if (context.error) {
+            cb.call(context, context.error.message, context.error.key);
+            context.error = null;
+        }
+        saw.next();
+    };
+    
+    this.forEach = function (cb) {
+        this.seq(function () {
+            context.stack_ = context.stack.slice();
+            var end = context.stack.length;
+            
+            if (end === 0) this(null)
+            else context.stack.forEach(function (x, i) {
+                action(saw.step, i, function () {
+                    cb.call(this, x, i);
+                    if (i == end - 1) saw.next();
+                });
+            });
+        });
+    };
+    
+    this.seqEach = function (cb) {
+        this.seq(function () {
+            context.stack_ = context.stack.slice();
+            var xs = context.stack.slice();
+            if (xs.length === 0) this(null);
+            else (function next (i) {
+                action(
+                    saw.step, i,
+                    function () { cb.call(this, xs[i], i) },
+                    function (args) {
+                        if (!args || i === xs.length - 1) saw.next();
+                        else next(i + 1);
+                    }
+                );
+            }).bind(this)(0);
+        });
+    };
+    
+    this.parEach = function (limit, cb) {
+        var xs = context.stack.slice();
+        if (cb === undefined) { cb = limit; limit = xs.length }
+        context.stack_ = [];
+        
+        var active = 0;
+        var finished = 0;
+        var queue = [];
+        
+        if (xs.length === 0) saw.next()
+        else xs.forEach(function call (x, i) {
+            if (active >= limit) {
+                queue.push(call.bind(this, x, i));
+            }
+            else {
+                active ++;
+                action(saw.step, i,
+                    function () {
+                        cb.call(this, x, i);
+                    },
+                    function () {
+                        active --;
+                        finished ++;
+                        if (queue.length > 0) queue.shift()();
+                        else if (finished === xs.length) {
+                            saw.next();
+                        }
+                    }
+                );
+            }
+        });
+    };
+    
+    this.parMap = function (limit, cb) {
+        var res = [];
+        var len = context.stack.length;
+        if (cb === undefined) { cb = limit; limit = len }
+        var res = [];
+        
+        Seq()
+            .extend(context.stack)
+            .parEach(limit, function (x, i) {
+                var self = this;
+                
+                var next = function () {
+                    res[i] = arguments[1];
+                    self.apply(self, arguments);
+                };
+                
+                next.stack = self.stack;
+                next.stack_ = self.stack_;
+                next.vars = self.vars;
+                next.args = self.args;
+                next.error = self.error;
+                
+                next.into = function (key) {
+                    return function () {
+                        res[key] = arguments[1];
+                        self.apply(self, arguments);
+                    };
+                };
+                
+                next.ok = function () {
+                    var args = [].slice.call(arguments);
+                    args.unshift(null);
+                    return next.apply(next, args);
+                };
+                
+                cb.apply(next, arguments);
+            })
+            .seq(function () {
+                context.stack = res;
+                saw.next();
+            })
+        ;
+    };
+    
+    this.seqMap = function (cb) {
+        var res = [];
+        var lastIdx = context.stack.length - 1;
+        
+        this.seqEach(function (x, i) {
+            var self = this;
+            
+            var next = function () {
+                res[i] = arguments[1];
+                if (i === lastIdx)
+                    context.stack = res;
+                self.apply(self, arguments);
+            };
+            
+            next.stack = self.stack;
+            next.stack_ = self.stack_;
+            next.vars = self.vars;
+            next.args = self.args;
+            next.error = self.error;
+            
+            next.into = function (key) {
+                return function () {
+                    res[key] = arguments[1];
+                    if (i === lastIdx)
+                        context.stack = res;
+                    self.apply(self, arguments);
+                };
+            };
+            
+            next.ok = function () {
+                var args = [].slice.call(arguments);
+                args.unshift(null);
+                return next.apply(next, args);
+            };
+            
+            cb.apply(next, arguments);
+        });
+    };
+    
+    /**
+     * Consumes any errors that occur in `cb`. Calls to `this.into(i)` will place
+     * that value, if accepted by the filter, at the index in the results as
+     * if it were the i-th index before filtering. (This means it will never 
+     * override another value, and will only actually appear at i if the filter
+     * accepts all values before i.)
+     */
+    this.parFilter = function (limit, cb) {
+        var res = [];
+        var len = context.stack.length;
+        if (cb === undefined) { cb = limit; limit = len }
+        var res = [];
+        
+        Seq()
+            .extend(context.stack)
+            .parEach(limit, function (x, i) {
+                var self = this;
+                
+                var next = function (err, ok) {
+                    if (!err && ok)
+                        res.push([i, x]);
+                    arguments[0] = null; // discard errors
+                    self.apply(self, arguments);
+                };
+                
+                next.stack = self.stack;
+                next.stack_ = self.stack_;
+                next.vars = self.vars;
+                next.args = self.args;
+                next.error = self.error;
+                
+                next.into = function (key) {
+                    return function (err, ok) {
+                        if (!err && ok)
+                            res.push([key, x]);
+                        arguments[0] = null; // discard errors
+                        self.apply(self, arguments);
+                    };
+                };
+                
+                next.ok = function () {
+                    var args = [].slice.call(arguments);
+                    args.unshift(null);
+                    return next.apply(next, args);
+                };
+                
+                cb.apply(next, arguments);
+            })
+            .seq(function () {
+                context.stack = res.sort().map(function(pair){ return pair[1]; });
+                saw.next();
+            })
+        ;
+    };
+    
+    /**
+     * Consumes any errors that occur in `cb`. Calls to `this.into(i)` will place
+     * that value, if accepted by the filter, at the index in the results as
+     * if it were the i-th index before filtering. (This means it will never 
+     * override another value, and will only actually appear at i if the filter
+     * accepts all values before i.)
+     */
+    this.seqFilter = function (cb) {
+        var res = [];
+        var lastIdx = context.stack.length - 1;
+        
+        this.seqEach(function (x, i) {
+            var self = this;
+            
+            var next = function (err, ok) {
+                if (!err && ok)
+                    res.push([i, x]);
+                if (i === lastIdx)
+                    context.stack = res.sort().map(function(pair){ return pair[1]; });
+                arguments[0] = null; // discard errors
+                self.apply(self, arguments);
+            };
+            
+            next.stack = self.stack;
+            next.stack_ = self.stack_;
+            next.vars = self.vars;
+            next.args = self.args;
+            next.error = self.error;
+            
+            next.into = function (key) {
+                return function (err, ok) {
+                    if (!err && ok)
+                        res.push([key, x]);
+                    if (i === lastIdx)
+                        context.stack = res.sort().map(function(pair){ return pair[1]; });
+                    arguments[0] = null; // discard errors
+                    self.apply(self, arguments);
+                };
+            };
+            
+            next.ok = function () {
+                var args = [].slice.call(arguments);
+                args.unshift(null);
+                return next.apply(next, args);
+            };
+            
+            cb.apply(next, arguments);
+        });
+    };
+    
+    [ 'forEach', 'seqEach', 'parEach', 'seqMap', 'parMap', 'seqFilter', 'parFilter' ]
+        .forEach(function (name) {
+            this[name + '_'] = function (cb) {
+                this[name].call(this, function () {
+                    var args = [].slice.call(arguments);
+                    args.unshift(this);
+                    cb.apply(this, args);
+                });
+            };
+        }, this)
+    ;
+    
+    ['push','pop','shift','unshift','splice','reverse']
+        .forEach(function (name) {
+            this[name] = function () {
+                context.stack[name].apply(
+                    context.stack,
+                    [].slice.call(arguments)
+                );
+                saw.next();
+                return this;
+            };
+        }, this)
+    ;
+    
+    [ 'map', 'filter', 'reduce' ]
+        .forEach(function (name) {
+            this[name] = function () {
+                var res = context.stack[name].apply(
+                    context.stack,
+                    [].slice.call(arguments)
+                );
+                // stack must be an array, or bad things happen
+                context.stack = (Array.isArray(res) ? res : [res]);
+                saw.next();
+                return this;
+            };
+        }, this)
+    ;
+    
+    this.extend = function (xs) {
+        if (!Array.isArray(xs)) {
+            throw new Error('argument to .extend() is not an Array');
+        }
+        context.stack.push.apply(context.stack, xs);
+        saw.next();
+    };
+    
+    this.flatten = function (pancake) {
+        var xs = [];
+        // should we fully flatten this array? (default: true)
+        if (pancake === undefined) { pancake = true; }
+        context.stack.forEach(function f (x) {
+            if (Array.isArray(x) && pancake) x.forEach(f);
+            else if (Array.isArray(x)) xs = xs.concat(x);
+            else xs.push(x);
+        });
+        context.stack = xs;
+        saw.next();
+    };
+    
+    this.unflatten = function () {
+        context.stack = [context.stack];
+        saw.next();
+    };
+    
+    this.empty = function () {
+        context.stack = [];
+        saw.next();
+    };
+    
+    this.set = function (stack) {
+        context.stack = stack;
+        saw.next();
+    };
+    
+    this['do'] = function (cb) {
+        saw.nest(cb, context);
+    };
+}
diff --git a/debian/tests/node_modules/seq/package.json b/debian/tests/node_modules/seq/package.json
new file mode 100644
index 0000000..39784d1
--- /dev/null
+++ b/debian/tests/node_modules/seq/package.json
@@ -0,0 +1,74 @@
+{
+  "_from": "seq@>=0.1.7",
+  "_id": "seq@0.3.5",
+  "_inBundle": false,
+  "_integrity": "sha1-rgKvOkJHk9jMvyEtaRdODFTf/jg=",
+  "_location": "/seq",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "seq@>=0.1.7",
+    "name": "seq",
+    "escapedName": "seq",
+    "rawSpec": ">=0.1.7",
+    "saveSpec": null,
+    "fetchSpec": ">=0.1.7"
+  },
+  "_requiredBy": [
+    "/findit"
+  ],
+  "_resolved": "https://registry.npmjs.org/seq/-/seq-0.3.5.tgz";,
+  "_shasum": "ae02af3a424793d8ccbf212d69174e0c54dffe38",
+  "_spec": "seq@>=0.1.7",
+  "_where": "/home/xavier/dev/debian/packages/node-formidable/node_modules/findit",
+  "author": {
+    "name": "James Halliday",
+    "email": "mail@substack.net",
+    "url": "http://substack.net";
+  },
+  "bugs": {
+    "url": "https://github.com/substack/node-seq/issues";
+  },
+  "bundleDependencies": false,
+  "dependencies": {
+    "chainsaw": ">=0.0.7 <0.1",
+    "hashish": ">=0.0.2 <0.1"
+  },
+  "deprecated": false,
+  "description": "Chainable asynchronous flow control with sequential and parallel primitives and pipeline-style error handling",
+  "devDependencies": {
+    "expresso": ">=0.7.x"
+  },
+  "engine": {
+    "node": ">=0.4.0"
+  },
+  "engines": {
+    "node": "*"
+  },
+  "homepage": "https://github.com/substack/node-seq#readme";,
+  "keywords": [
+    "flow-control",
+    "flow",
+    "control",
+    "async",
+    "asynchronous",
+    "chain",
+    "pipeline",
+    "sequence",
+    "sequential",
+    "parallel",
+    "error"
+  ],
+  "license": "MIT/X11",
+  "main": "./index.js",
+  "name": "seq",
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/substack/node-seq.git"
+  },
+  "script": {
+    "test": "expresso"
+  },
+  "version": "0.3.5"
+}
diff --git a/debian/tests/node_modules/urun/.travis.yml b/debian/tests/node_modules/urun/.travis.yml
new file mode 100644
index 0000000..f1d0f13
--- /dev/null
+++ b/debian/tests/node_modules/urun/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+  - 0.4
+  - 0.6
diff --git a/debian/tests/node_modules/urun/Makefile b/debian/tests/node_modules/urun/Makefile
new file mode 100644
index 0000000..6a355bb
--- /dev/null
+++ b/debian/tests/node_modules/urun/Makefile
@@ -0,0 +1,6 @@
+SHELL := /bin/bash
+
+test:
+	@find test -name test-*.js -type f | xargs -tn1 node
+
+.PHONY: test
diff --git a/debian/tests/node_modules/urun/Readme.md b/debian/tests/node_modules/urun/Readme.md
new file mode 100644
index 0000000..0d9ea23
--- /dev/null
+++ b/debian/tests/node_modules/urun/Readme.md
@@ -0,0 +1,45 @@
+# urun
+
+[![Build Status](https://secure.travis-ci.org/felixge/node-urun.png)](http://travis-ci.org/felixge/node-urun)
+
+The minimal test runner.
+
+## Why yet another test runner?
+
+I wanted something simple, that just runs test files, shows progress, and
+behaves like a good UNIX citizen. Now it exists.
+
+## Install
+
+```
+npm install urun
+```
+
+## Usage
+
+In order to execute all test-*.js files inside a given directory, simply do:
+
+```js
+require('urun')(__dirname);
+```
+
+You now get a nice progress indication, as well as detailed output for each
+test that fails.
+
+The only other feature is specifying a regex for the files to run (default is
+`/test-.+\.js$/`), for example:
+
+```js
+require('urun')(__dirname, { include: /.+Test\.js$/ });
+```
+
+## Reporter
+
+```js
+require('urun')(__dirname, { reporter: 'BashReporter' }); // default
+require('urun')(__dirname, { reporter: 'BashTapReporter' }); // tap compliant output
+```
+
+## License
+
+This module is licensed under the MIT license.
diff --git a/debian/tests/node_modules/urun/index.js b/debian/tests/node_modules/urun/index.js
new file mode 100644
index 0000000..05ad807
--- /dev/null
+++ b/debian/tests/node_modules/urun/index.js
@@ -0,0 +1,23 @@
+var FileFilter   = require('./lib/FileFilter');
+var FileFinder   = require('./lib/FileFinder');
+var Runner       = require('./lib/Runner');
+
+module.exports = function(dir, options) {
+  var options  = options || {};
+  var include  = options.include  || /test-.+\.js$/;
+  var Reporter = require('./lib/reporter/'
+    + (process.env.REPORTER || options.reporter || 'BashReporter'));
+
+  var finder   = new FileFinder(dir);
+  var filter   = new FileFilter({include: include});
+
+  finder.execute(function(err, files) {
+    if (err) throw err;
+
+    files = filter.filter(files);
+
+    var runner   = new Runner({files: files});
+    var reporter = new Reporter({runner: runner});
+    runner.execute();
+  });
+};
diff --git a/debian/tests/node_modules/urun/lib/FileExecutor.js b/debian/tests/node_modules/urun/lib/FileExecutor.js
new file mode 100644
index 0000000..0d33451
--- /dev/null
+++ b/debian/tests/node_modules/urun/lib/FileExecutor.js
@@ -0,0 +1,46 @@
+var spawn = require('child_process').spawn;
+
+module.exports = FileExecutor;
+function FileExecutor(options) {
+  this._file   = options.file;
+  this._bin    = process.execPath;
+  this._cwd    = options.cwd || process.cwd();
+  this._output = '';
+}
+
+FileExecutor.prototype.execute = function(cb) {
+  var self  = this;
+  var child = spawn(this._bin, [this._file]);
+
+  this._capture(child.stdout);
+  this._capture(child.stderr);
+
+  child.on('exit', function(code, signal) {
+    cb(self._errorFor(code, signal), self._output);
+  });
+};
+
+FileExecutor.prototype._capture = function(stream) {
+  var self = this;
+
+  stream.setEncoding('utf8');
+  stream.on('data', function(chunk) {
+    self._output += chunk;
+  });
+};
+
+FileExecutor.prototype._errorFor = function(code, signal) {
+    if (!code && !signal) return null;
+
+    var prefix = 'node ' + this.relativePath() + ' died with ';
+
+    if (code) return new Error(prefix + 'code: ' + code);
+    if (signal) return new Error(prefix + 'signal: ' + signal);
+};
+
+FileExecutor.prototype.relativePath = function() {
+  var path = this._file;
+  return (path.indexOf(this._cwd) === 0)
+    ? path = path.substr(this._cwd.length + 1)
+    : path;
+};
diff --git a/debian/tests/node_modules/urun/lib/FileFilter.js b/debian/tests/node_modules/urun/lib/FileFilter.js
new file mode 100644
index 0000000..0ab530a
--- /dev/null
+++ b/debian/tests/node_modules/urun/lib/FileFilter.js
@@ -0,0 +1,16 @@
+module.exports = FileFilter;
+function FileFilter(options) {
+  options = options || {};
+
+  this._include = options.include;
+}
+
+FileFilter.prototype.filter = function(files) {
+  var self = this;
+
+  return files.filter(function(file) {
+    if (!self._include) return true;
+
+    return self._include.test(file);
+  });
+};
diff --git a/debian/tests/node_modules/urun/lib/FileFinder.js b/debian/tests/node_modules/urun/lib/FileFinder.js
new file mode 100644
index 0000000..a1f6c60
--- /dev/null
+++ b/debian/tests/node_modules/urun/lib/FileFinder.js
@@ -0,0 +1,66 @@
+var path = require('path');
+var fs   = require('fs');
+var noOp = function() {};
+
+module.exports = FileFinder;
+function FileFinder(dir) {
+  this._dir = dir;
+}
+
+FileFinder.prototype.execute = function(cb) {
+  var files = [];
+  this._readdir(this._dir, files, function(err) {
+    cb(err, files);
+  });
+};
+
+FileFinder.prototype._readdir = function(dir, results, cb) {
+  var self = this;
+  fs.readdir(dir, function(err, files) {
+    if (err) {
+      cb(err);
+      cb = noOp;
+      return;
+    }
+
+    self._inspect(dir, files, results, cb);
+  });
+};
+
+FileFinder.prototype._inspect = function(dir, files, results, cb) {
+  var remaining = files.length;
+  var self      = this;
+
+  if (!remaining) return cb(null);
+
+  files.forEach(function(file) {
+    var filePath = path.join(dir, file);
+
+    fs.stat(filePath, function(err, stat) {
+      if (err) {
+        cb(err);
+        cb = noOp;
+        return;
+      }
+
+      remaining--;
+      if (stat.isDirectory()) {
+        remaining++;
+        self._readdir(filePath, results, function(err) {
+          if (err) {
+            cb(err);
+            cb = noOp;
+            return;
+          }
+
+          remaining--;
+          if (!remaining) cb(null);
+        });
+      } else {
+        results.push(filePath);
+      }
+
+      if (!remaining) cb(null);
+    });
+  });
+};
diff --git a/debian/tests/node_modules/urun/lib/Runner.js b/debian/tests/node_modules/urun/lib/Runner.js
new file mode 100644
index 0000000..8a30375
--- /dev/null
+++ b/debian/tests/node_modules/urun/lib/Runner.js
@@ -0,0 +1,33 @@
+var FileExecutor = require('./FileExecutor');
+var EventEmitter = require('events').EventEmitter;
+var util         = require('util');
+
+module.exports = Runner;
+util.inherits(Runner, EventEmitter);
+function Runner(options) {
+  this._files = [].concat(options.files);
+  this._cwd   = options.cwd || process.cwd();
+}
+
+Runner.prototype.execute = function() {
+  this.emit('start', [].concat(this._files));
+  this._nextFile();
+};
+
+Runner.prototype._nextFile = function() {
+  var file = this._files.shift();
+  if (!file) {
+    this.emit('end');
+    return;
+  }
+
+  var self         = this;
+  var executor     = new FileExecutor({file: file, cwd: this._cwd});
+  var relativePath = executor.relativePath();
+
+  this.emit('fileStart', relativePath);
+  executor.execute(function(err, output) {
+    self.emit('fileEnd', relativePath, err, output);
+    self._nextFile();
+  });
+};
diff --git a/debian/tests/node_modules/urun/lib/reporter/BashReporter.js b/debian/tests/node_modules/urun/lib/reporter/BashReporter.js
new file mode 100644
index 0000000..22ca0f1
--- /dev/null
+++ b/debian/tests/node_modules/urun/lib/reporter/BashReporter.js
@@ -0,0 +1,101 @@
+var str = require('../str');
+
+module.exports = BashReporter;
+function BashReporter(options) {
+  this._runner      = options.runner;
+  this._currentFile = null;
+  this._lastLine    = null;
+  this._start       = null;
+  this._fail        = 0;
+  this._pass        = 0;
+  this._total       = 0;
+  this._interval    = null;
+
+  this._runner.on('start', this._handleStart.bind(this));
+  this._runner.on('fileStart', this._handleFileStart.bind(this));
+  this._runner.on('fileEnd', this._handleFileEnd.bind(this));
+  this._runner.on('end', this._handleEnd.bind(this));
+}
+
+BashReporter.prototype._handleStart = function(files) {
+  this._start    = Date.now();
+  this._total    = files.length;
+  this._interval = setInterval(this.updateProgress.bind(this), 1000);
+};
+
+BashReporter.prototype._handleFileStart = function(file) {
+  this._currentFile = file;
+  this.updateProgress();
+};
+
+BashReporter.prototype._handleFileEnd = function(file, err, output) {
+  if (err) {
+    this._fail++;
+
+    if (output.substr(0, 1) !== '\n') output = '\n' + output;
+    output = output.replace(/^/mg, '  ');
+
+    process.stdout.write('\n' + output + '\n');
+  } else {
+    this._pass++;
+  }
+
+  this.updateProgress();
+};
+
+BashReporter.prototype._handleEnd = function() {
+  process.stdout.write('\n');
+  clearInterval(this._interval);
+
+  var exitCode = (this._fail > 0)
+    ? 1
+    : 0;
+
+  process.exit(exitCode);
+};
+
+BashReporter.prototype.updateProgress = function() {
+  var template = '\r[%s %s %s/%s %s node %s]';
+
+  var line = str.sprintf(
+    template,
+    this.elapsedTime(),
+    this._fail,
+    this._pass,
+    this._total,
+    this.progress(),
+    this._currentFile
+  );
+
+  this.clearLine();
+  process.stdout.write(line);
+  this._lastLine = line;
+};
+
+BashReporter.prototype.clearLine = function() {
+  if (!this._lastLine) return;
+
+  var spaces = str.repeat(' ', this._lastLine.length);
+  process.stdout.write('\r' + spaces + '\r');
+};
+
+BashReporter.prototype.elapsedTime = function(showMs) {
+  var duration = new Date - this._start;
+
+  var seconds = Math.floor((duration) / 1000);
+  var minutes = Math.floor(seconds / 60);
+  var hours   = Math.floor(minutes / 60);
+
+  seconds -= (minutes * 60) - (hours * 60 * 60);
+  minutes -= hours * 60;
+
+  if (minutes < 10) minutes = '0' + minutes;
+  if (seconds < 10) seconds = '0' + seconds;
+
+  return hours + ':' + minutes + ':' + seconds;
+};
+
+BashReporter.prototype.progress = function() {
+  var index = this._fail + this._pass;
+  return ((index) * 100 / this._total).toFixed(1) + '%';
+};
diff --git a/debian/tests/node_modules/urun/lib/reporter/BashTapReporter.js b/debian/tests/node_modules/urun/lib/reporter/BashTapReporter.js
new file mode 100644
index 0000000..2694cda
--- /dev/null
+++ b/debian/tests/node_modules/urun/lib/reporter/BashTapReporter.js
@@ -0,0 +1,38 @@
+module.exports = BashTapReporter;
+
+function BashTapReporter(options) {
+  this._runner      = options.runner;
+  this._fail        = false;
+  this._count       = 1;
+
+  this._runner.on('start', this._handleStart.bind(this));
+  this._runner.on('fileEnd', this._handleFileEnd.bind(this));
+  this._runner.on('end', this._handleEnd.bind(this));
+}
+
+BashTapReporter.prototype._handleStart = function(files) {
+  console.log('%d..%d', 1, files.length);
+};
+
+BashTapReporter.prototype._handleFileEnd = function(file, err, output) {
+  if (err) {
+    console.log('not ok %d %s', this._count, this._tapTitle(file));
+    console.log(output.replace(/^/gm, '  '));
+    this._fail = true;
+  } else {
+    console.log('ok %d %s', this._count, this._tapTitle(file));
+  }
+  this._count++;
+};
+
+BashTapReporter.prototype._handleEnd = function() {
+  var exitCode = (this._fail)
+    ? 1
+    : 0;
+
+  process.exit(exitCode);
+};
+
+BashTapReporter.prototype._tapTitle = function(title) {
+  return title.replace(/#/g, '');
+}
diff --git a/debian/tests/node_modules/urun/lib/str.js b/debian/tests/node_modules/urun/lib/str.js
new file mode 100644
index 0000000..c6baf73
--- /dev/null
+++ b/debian/tests/node_modules/urun/lib/str.js
@@ -0,0 +1,30 @@
+var str  = module.exports;
+var util = require('util');
+
+// custom / simplified flavor of sprintf
+str.sprintf = function() {
+  var args = Array.prototype.slice.call(arguments);
+  var str = args.shift();
+
+  return str.replace(/%[so]/g, function(m, i, s) {
+    var arg = args.shift();
+    if (m == '%o') {
+      return util.inspect(arg);
+    }
+
+    if (!arg && arg !== 0) {
+      return '';
+    }
+
+    return arg.toString();
+  });
+};
+
+str.repeat = function(string, times) {
+  var repeated = '';
+  for (var i = 0; i < times; i++) {
+    repeated += string;
+  }
+
+  return repeated;
+};
diff --git a/debian/tests/node_modules/urun/node_modules/utest/Makefile b/debian/tests/node_modules/urun/node_modules/utest/Makefile
new file mode 100644
index 0000000..6a355bb
--- /dev/null
+++ b/debian/tests/node_modules/urun/node_modules/utest/Makefile
@@ -0,0 +1,6 @@
+SHELL := /bin/bash
+
+test:
+	@find test -name test-*.js -type f | xargs -tn1 node
+
+.PHONY: test
diff --git a/debian/tests/node_modules/urun/node_modules/utest/Readme.md b/debian/tests/node_modules/urun/node_modules/utest/Readme.md
new file mode 100644
index 0000000..674a92c
--- /dev/null
+++ b/debian/tests/node_modules/urun/node_modules/utest/Readme.md
@@ -0,0 +1,76 @@
+# utest
+
+The minimal unit testing library.
+
+## Why yet another test library?
+
+I wanted something simple, that just does unit tests (no async) and nothing
+else. And each test is supposed to be a standalone UNIX program. Now it exists.
+
+## Install
+
+```
+npm install utest
+```
+
+## Usage
+
+Running a test with utest is very simple:
+
+```js
+var test   = require('utest');
+var assert = require('assert');
+
+test('Number#toFixed', {
+  'returns a string': function() {
+    assert.equal(typeof (5).toFixed(), 'string');
+  },
+
+  'takes number of decimal places': function() {
+    assert.equal((5).toFixed(1), '5.0');
+  },
+
+  'does not round': function() {
+    assert.equal((5.55).toFixed(1), '5.5');
+  },
+});
+```
+
+The only additional feature is running a before/after method:
+
+```js
+var test   = require('utest');
+var assert = require('assert');
+
+test('Date', {
+  before: function() {
+    this.date = new Date;
+  },
+
+  after: function() {
+    this.date = null;
+  },
+
+  'lets you manipulate the year': function() {
+    this.date.setYear(2012);
+    assert.equal(this.date.getFullYear(), 2012);
+  },
+
+  'can be coerced into a number': function() {
+    assert.equal(typeof +this.date, 'number');
+  },
+});
+```
+
+## Future Features
+
+I want to keep this library as minimal as possible, but I do consider the
+addition of the following features:
+
+* TAP output (if TAP=1 in the environment, switch to TapReporter class)
+* Leak detection (automatically add a final test that fails if there are global
+  leaks).
+
+## License
+
+This module is licensed under the MIT license.
diff --git a/debian/tests/node_modules/urun/node_modules/utest/index.js b/debian/tests/node_modules/urun/node_modules/utest/index.js
new file mode 100644
index 0000000..ec222e1
--- /dev/null
+++ b/debian/tests/node_modules/urun/node_modules/utest/index.js
@@ -0,0 +1,16 @@
+module.exports     = utest;
+utest.Collection   = require('./lib/Collection');
+utest.TestCase     = require('./lib/TestCase');
+utest.BashReporter = require('./lib/reporter/BashReporter');
+
+var collection;
+var reporter;
+function utest(name, tests) {
+  if (!collection) {
+    collection = new utest.Collection();
+    reporter   = new utest.BashReporter({collection: collection});
+  }
+
+  var testCase = new utest.TestCase({name: name, tests: tests});
+  collection.add(testCase);
+};
diff --git a/debian/tests/node_modules/urun/node_modules/utest/lib/Collection.js b/debian/tests/node_modules/urun/node_modules/utest/lib/Collection.js
new file mode 100644
index 0000000..db9be5c
--- /dev/null
+++ b/debian/tests/node_modules/urun/node_modules/utest/lib/Collection.js
@@ -0,0 +1,33 @@
+var util         = require('util');
+var EventEmitter = require('events').EventEmitter;
+
+module.exports = Collection;
+util.inherits(Collection, EventEmitter);
+function Collection(options) {
+  this._pass = 0;
+  this._fail = 0;
+}
+
+Collection.prototype.add = function(testCase) {
+  this._start = this._start || Date.now();
+
+  var self = this;
+  testCase
+    .on('pass', function(name) {
+      self._pass++;
+      self.emit('pass', testCase, name);
+    })
+    .on('fail', function(name, err) {
+      self._fail++;
+      self.emit('fail', testCase, name, err);
+    })
+    .run();
+};
+
+Collection.prototype.stats = function() {
+  return {
+    pass     : this._pass,
+    fail     : this._fail,
+    duration : Date.now() - this._start,
+  };
+};
diff --git a/debian/tests/node_modules/urun/node_modules/utest/lib/TestCase.js b/debian/tests/node_modules/urun/node_modules/utest/lib/TestCase.js
new file mode 100644
index 0000000..9e2a017
--- /dev/null
+++ b/debian/tests/node_modules/urun/node_modules/utest/lib/TestCase.js
@@ -0,0 +1,36 @@
+var util         = require('util');
+var EventEmitter = require('events').EventEmitter;
+
+module.exports = TestCase;
+util.inherits(TestCase, EventEmitter);
+function TestCase(properties) {
+  this.name   = properties.name;
+  this._tests = properties.tests;
+}
+
+TestCase.prototype.run = function() {
+  var noop   = function() {};
+  var before = this._tests.before || noop;
+  var after  = this._tests.after || noop;
+
+  for (var test in this._tests) {
+    if (test === 'before' || test === 'after') continue;
+
+    var fn      = this._tests[test];
+    var context = {};
+
+    try {
+      before.call(context);
+      fn.call(context);
+      after.call(context);
+    } catch (_err) {
+      var err = _err;
+    }
+
+    if (!err) {
+      this.emit('pass', test);
+    } else {
+      this.emit('fail', test, err);
+    }
+  }
+};
diff --git a/debian/tests/node_modules/urun/node_modules/utest/lib/reporter/BashReporter.js b/debian/tests/node_modules/urun/node_modules/utest/lib/reporter/BashReporter.js
new file mode 100644
index 0000000..69308fc
--- /dev/null
+++ b/debian/tests/node_modules/urun/node_modules/utest/lib/reporter/BashReporter.js
@@ -0,0 +1,26 @@
+module.exports = BashReporter;
+function BashReporter(options) {
+  this._process    = options.process || process;
+  this._collection = options.collection;
+
+  this._collection.on('fail', this._handleFail.bind(this));
+  this._process.on('exit', this._handleExit.bind(this));
+}
+
+BashReporter.prototype._handleFail = function(testCase, test, error) {
+  this._process.stdout.write('Failed: ' + testCase.name + ' ' + test + '\n\n');
+  var stack = error.stack.replace(/^/gm, '  ');
+  this._process.stdout.write(stack + '\n\n');
+};
+
+BashReporter.prototype._handleExit = function() {
+  var stats = this._collection.stats();
+  this._process.stdout.write(
+    stats.fail + ' fail | ' +
+    stats.pass + ' pass | ' +
+    stats.duration + ' ms ' +
+    '\n'
+  );
+
+  if (stats.fail) this._process.reallyExit(1);
+};
diff --git a/debian/tests/node_modules/urun/node_modules/utest/package.json b/debian/tests/node_modules/urun/node_modules/utest/package.json
new file mode 100644
index 0000000..47b5572
--- /dev/null
+++ b/debian/tests/node_modules/urun/node_modules/utest/package.json
@@ -0,0 +1,48 @@
+{
+  "_from": "utest@0.0.2",
+  "_id": "utest@0.0.2",
+  "_inBundle": false,
+  "_integrity": "sha1-OGLil1MJ6l3glAREpsbuAXlyahY=",
+  "_location": "/urun/utest",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "version",
+    "registry": true,
+    "raw": "utest@0.0.2",
+    "name": "utest",
+    "escapedName": "utest",
+    "rawSpec": "0.0.2",
+    "saveSpec": null,
+    "fetchSpec": "0.0.2"
+  },
+  "_requiredBy": [
+    "/urun"
+  ],
+  "_resolved": "https://registry.npmjs.org/utest/-/utest-0.0.2.tgz";,
+  "_shasum": "3862e2975309ea5de0940444a6c6ee0179726a16",
+  "_spec": "utest@0.0.2",
+  "_where": "/home/xavier/dev/debian/packages/node-formidable/node_modules/urun",
+  "author": {
+    "name": "Felix Geisendörfer",
+    "email": "felix@debuggable.com",
+    "url": "http://debuggable.com/";
+  },
+  "bundleDependencies": false,
+  "dependencies": {},
+  "deprecated": false,
+  "description": "The minimal unit testing library.",
+  "devDependencies": {},
+  "engines": {
+    "node": "*"
+  },
+  "homepage": "https://github.com/felixge/node-utest";,
+  "main": "./index",
+  "name": "utest",
+  "repository": {
+    "url": ""
+  },
+  "scripts": {
+    "test": "make test"
+  },
+  "version": "0.0.2"
+}
diff --git a/debian/tests/node_modules/urun/package.json b/debian/tests/node_modules/urun/package.json
new file mode 100644
index 0000000..c4b7633
--- /dev/null
+++ b/debian/tests/node_modules/urun/package.json
@@ -0,0 +1,54 @@
+{
+  "_from": "urun@^0.0.6",
+  "_id": "urun@0.0.6",
+  "_inBundle": false,
+  "_integrity": "sha1-4PqFaYA2fYa/FG4YC8OJAyCrHS8=",
+  "_location": "/urun",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "urun@^0.0.6",
+    "name": "urun",
+    "escapedName": "urun",
+    "rawSpec": "^0.0.6",
+    "saveSpec": null,
+    "fetchSpec": "^0.0.6"
+  },
+  "_requiredBy": [
+    "#DEV:/"
+  ],
+  "_resolved": "https://registry.npmjs.org/urun/-/urun-0.0.6.tgz";,
+  "_shasum": "e0fa856980367d86bf146e180bc3890320ab1d2f",
+  "_spec": "urun@^0.0.6",
+  "_where": "/home/xavier/dev/debian/packages/node-formidable",
+  "author": {
+    "name": "Felix Geisendörfer",
+    "email": "felix@debuggable.com",
+    "url": "http://debuggable.com/";
+  },
+  "bundleDependencies": false,
+  "dependencies": {
+    "utest": "0.0.2"
+  },
+  "deprecated": false,
+  "description": "The minimal test runner.",
+  "devDependencies": {
+    "sinon": "1.2.0",
+    "utest": "0.0.1"
+  },
+  "engines": {
+    "node": "*"
+  },
+  "homepage": "https://github.com/felixge/node-urun";,
+  "main": "./index",
+  "name": "urun",
+  "optionalDependencies": {},
+  "repository": {
+    "url": ""
+  },
+  "scripts": {
+    "test": "make test"
+  },
+  "version": "0.0.6"
+}
diff --git a/debian/tests/node_modules/utest/.travis.yml b/debian/tests/node_modules/utest/.travis.yml
new file mode 100644
index 0000000..f1d0f13
--- /dev/null
+++ b/debian/tests/node_modules/utest/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+  - 0.4
+  - 0.6
diff --git a/debian/tests/node_modules/utest/Makefile b/debian/tests/node_modules/utest/Makefile
new file mode 100644
index 0000000..6a355bb
--- /dev/null
+++ b/debian/tests/node_modules/utest/Makefile
@@ -0,0 +1,6 @@
+SHELL := /bin/bash
+
+test:
+	@find test -name test-*.js -type f | xargs -tn1 node
+
+.PHONY: test
diff --git a/debian/tests/node_modules/utest/Readme.md b/debian/tests/node_modules/utest/Readme.md
new file mode 100644
index 0000000..0d789fe
--- /dev/null
+++ b/debian/tests/node_modules/utest/Readme.md
@@ -0,0 +1,106 @@
+# utest
+
+[![Build Status](https://secure.travis-ci.org/felixge/node-utest.png)](http://travis-ci.org/felixge/node-utest)
+
+The minimal unit testing library.
+
+## Why yet another test library?
+
+I wanted something simple, that just does unit tests (no async) and where each
+test is a standalone UNIX program. Now it exists.
+
+## How do I run async tests?
+
+Currently there is only one sane way: Do not use a framework. Instead use one
+file per test.
+
+If that becomes an issue, you should write more unit tests. (It is not a unit
+test if it does I/O).
+
+## Install
+
+```
+npm install utest
+```
+
+## Usage
+
+Running a test with utest is very simple:
+
+```js
+var test   = require('utest');
+var assert = require('assert');
+
+test('Number#toFixed', {
+  'returns a string': function() {
+    assert.equal(typeof (5).toFixed(), 'string');
+  },
+
+  'takes number of decimal places': function() {
+    assert.equal((5).toFixed(1), '5.0');
+  },
+
+  'does not round': function() {
+    assert.equal((5.55).toFixed(1), '5.5');
+  },
+});
+```
+
+It is also possible to define a before/after method:
+
+```js
+var test   = require('utest');
+var assert = require('assert');
+
+test('Date', {
+  before: function() {
+    this.date = new Date;
+  },
+
+  after: function() {
+    this.date = null;
+  },
+
+  'lets you manipulate the year': function() {
+    this.date.setYear(2012);
+    assert.equal(this.date.getFullYear(), 2012);
+  },
+
+  'can be coerced into a number': function() {
+    assert.equal(typeof +this.date, 'number');
+  },
+});
+```
+
+Last but not least, you can run individual tests by prefixing them with an
+exclamation mark. This is useful when putting debug statements into the subject
+under test:
+
+```js
+var test   = require('utest');
+var assert = require('assert');
+
+test('MyTest', {
+  '!will be executed': function() {
+    // ...
+  },
+
+  'will not be exectuted': function() {
+    // ...
+  },
+});
+```
+
+## Future Features
+
+I want to keep this library as minimal as possible, but I do consider the
+addition of the following features:
+
+* Nested test cases
+* TAP output (if TAP=1 in the environment, switch to TapReporter class)
+* Leak detection (automatically add a final test that fails if there are global
+  leaks).
+
+## License
+
+This module is licensed under the MIT license.
diff --git a/debian/tests/node_modules/utest/browser.js b/debian/tests/node_modules/utest/browser.js
new file mode 100644
index 0000000..32fd588
--- /dev/null
+++ b/debian/tests/node_modules/utest/browser.js
@@ -0,0 +1,20 @@
+if (!Function.prototype.bind) {
+  Function.prototype.bind = function (scope) {
+    var fn = this;
+    return function () {
+      return fn.apply(scope, arguments);
+    };
+  };
+}
+
+// Not shimed by Browserify:
+process.exit = function () {};
+
+var test = require('./index');
+test.Reporter = test.ConsoleReporter;
+
+module.exports = function (name, tests) {
+  process.nextTick(function () {
+    test(name, tests);
+  });
+};
diff --git a/debian/tests/node_modules/utest/index.js b/debian/tests/node_modules/utest/index.js
new file mode 100644
index 0000000..ac75255
--- /dev/null
+++ b/debian/tests/node_modules/utest/index.js
@@ -0,0 +1,23 @@
+module.exports        = utest;
+utest.Collection      = require('./lib/Collection');
+utest.TestCase        = require('./lib/TestCase');
+utest.BashReporter    = require('./lib/reporter/BashReporter');
+utest.ConsoleReporter = require('./lib/reporter/ConsoleReporter');
+utest.Reporter        = utest.BashReporter;
+
+var collection;
+var reporter;
+function utest(name, tests) {
+  if (!collection) {
+    collection = new utest.Collection();
+    reporter   = new utest.Reporter({collection: collection});
+
+    process.nextTick(collection.run.bind(collection));
+    collection.on('complete', function(stats) {
+      process.exit(stats.fail ? 1 : 0);
+    });
+  }
+
+  var testCase = new utest.TestCase({name: name, tests: tests});
+  collection.add(testCase);
+};
diff --git a/debian/tests/node_modules/utest/lib/Collection.js b/debian/tests/node_modules/utest/lib/Collection.js
new file mode 100644
index 0000000..385bc89
--- /dev/null
+++ b/debian/tests/node_modules/utest/lib/Collection.js
@@ -0,0 +1,54 @@
+var util         = require('util');
+var EventEmitter = require('events').EventEmitter;
+
+module.exports = Collection;
+util.inherits(Collection, EventEmitter);
+function Collection(options) {
+  this._pass                 = 0;
+  this._fail                 = 0;
+  this._skip                 = 0;
+  this._testCases            = [];
+  this._onlyRunSelectedTests = false;
+}
+
+Collection.prototype.add = function(testCase) {
+  this._testCases.push(testCase);
+  if (testCase.hasSelectedTests()) this._onlyRunSelectedTests = true;
+};
+
+Collection.prototype.run = function() {
+  this._start = this._start || Date.now();
+  for (var i = 0; i < this._testCases.length; i++) {
+    var testCase = this._testCases[i];
+    this._runTestCase(testCase);
+  }
+  this.emit('complete', this.stats());
+};
+
+Collection.prototype._runTestCase = function(testCase) {
+  this.emit('run', testCase);
+  var self = this;
+  testCase
+    .on('pass', function(name) {
+      self._pass++;
+      self.emit('pass', testCase, name);
+    })
+    .on('fail', function(name, err) {
+      self._fail++;
+      self.emit('fail', testCase, name, err);
+    })
+    .on('skip', function(name, err) {
+      self._skip++;
+      self.emit('skip', testCase, name);
+    })
+    .run(this._onlyRunSelectedTests);
+};
+
+Collection.prototype.stats = function() {
+  return {
+    pass     : this._pass,
+    fail     : this._fail,
+    skip     : this._skip,
+    duration : Date.now() - this._start,
+  };
+};
diff --git a/debian/tests/node_modules/utest/lib/TestCase.js b/debian/tests/node_modules/utest/lib/TestCase.js
new file mode 100644
index 0000000..1cbe1aa
--- /dev/null
+++ b/debian/tests/node_modules/utest/lib/TestCase.js
@@ -0,0 +1,61 @@
+var util         = require('util');
+var EventEmitter = require('events').EventEmitter;
+var doNothing    = function() {};
+
+module.exports = TestCase;
+util.inherits(TestCase, EventEmitter);
+function TestCase(properties) {
+  this.name   = properties.name;
+  this._tests = properties.tests;
+}
+
+TestCase.prototype.run = function(onlyRunSelectedTests) {
+  var before = this._tests.before || doNothing;
+  var after  = this._tests.after || doNothing;
+
+  for (var test in this._tests) {
+    if (test === 'before' || test === 'after') continue;
+
+    if (onlyRunSelectedTests && !this._isSelected(test)) {
+      this.emit('skip', test);
+      continue;
+    }
+
+    var fn      = this._tests[test];
+    var context = {};
+    var err     = null;
+
+    try {
+      before.call(context);
+      fn.call(context);
+    } catch (_err) {
+      err = _err;
+    } finally {
+      try {
+        after.call(context);
+      } catch (_err) {
+        if (!err) {
+          err = _err;
+        }
+      }
+    }
+
+    if (!err) {
+      this.emit('pass', test);
+    } else {
+      this.emit('fail', test, err);
+    }
+  }
+};
+
+TestCase.prototype.hasSelectedTests = function() {
+  for (var test in this._tests) {
+    if (this._isSelected(test)) return true;
+  }
+
+  return false;
+};
+
+TestCase.prototype._isSelected = function(test) {
+  return test.substr(0, 1) === '!';
+};
diff --git a/debian/tests/node_modules/utest/lib/reporter/BashReporter.js b/debian/tests/node_modules/utest/lib/reporter/BashReporter.js
new file mode 100644
index 0000000..b1a1237
--- /dev/null
+++ b/debian/tests/node_modules/utest/lib/reporter/BashReporter.js
@@ -0,0 +1,34 @@
+var Util = require('util');
+
+module.exports = BashReporter;
+function BashReporter(options) {
+  this._process    = options.process || process;
+  this._collection = options.collection;
+
+  this._collection.on('fail', this._handleFail.bind(this));
+  this._collection.on('complete', this._handleComplete.bind(this));
+}
+
+BashReporter.prototype._handleFail = function(testCase, test, error) {
+  this._process.stdout.write('Failed: ' + testCase.name + ' ' + test + '\n\n');
+  var stack = (error.stack)
+    ? error.stack
+    : 'Exception without stack: ' + Util.inspect(error);
+
+  stack = stack.replace(/^/gm, '  ');
+  this._process.stdout.write(stack + '\n\n');
+};
+
+BashReporter.prototype._handleComplete = function(stats) {
+  var output =
+    stats.fail + ' fail | ' +
+    stats.pass + ' pass | ' +
+    stats.duration + ' ms ' +
+    '\n';
+
+  if (stats.skip) {
+    output = stats.skip + ' SKIPPED | ' + output;
+  }
+
+  this._process.stdout.write(output);
+};
diff --git a/debian/tests/node_modules/utest/lib/reporter/ConsoleReporter.js b/debian/tests/node_modules/utest/lib/reporter/ConsoleReporter.js
new file mode 100644
index 0000000..a67e54c
--- /dev/null
+++ b/debian/tests/node_modules/utest/lib/reporter/ConsoleReporter.js
@@ -0,0 +1,34 @@
+var Util = require('util');
+
+module.exports = ConsoleReporter;
+function ConsoleReporter(options) {
+  this._collection = options.collection;
+
+  this._collection.on('run', this._handleRun.bind(this));
+  this._collection.on('fail', this._handleFail.bind(this));
+  this._collection.on('complete', this._handleComplete.bind(this));
+}
+
+ConsoleReporter.prototype._handleRun = function(testCase) {
+  console.log('Running: ' + testCase.name);
+};
+
+ConsoleReporter.prototype._handleFail = function(testCase, test, error) {
+  var msg   = 'Failed: ' + testCase.name + ' ' + test;
+  var stack = (error.stack)
+    ? error.stack
+    : 'Exception without stack: ' + Util.inspect(error);
+
+  stack = stack.replace(/^/gm, '  ');
+  console.error(msg + '\n\n' + stack + '\n');
+};
+
+ConsoleReporter.prototype._handleComplete = function(stats) {
+  if (stats.skip) {
+    console.warn(stats.skip + ' SKIPPED');
+  }
+  console.info(stats.fail + ' fail | ' +
+    stats.pass  + ' pass | ' +
+    stats.duration + ' ms');
+
+};
diff --git a/debian/tests/node_modules/utest/package.json b/debian/tests/node_modules/utest/package.json
new file mode 100644
index 0000000..e7fd6ab
--- /dev/null
+++ b/debian/tests/node_modules/utest/package.json
@@ -0,0 +1,50 @@
+{
+  "_from": "utest@^0.0.8",
+  "_id": "utest@0.0.8",
+  "_inBundle": false,
+  "_integrity": "sha1-/AlFH+aXuQCNDEMv4NtDnWzzeRQ=",
+  "_location": "/utest",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "utest@^0.0.8",
+    "name": "utest",
+    "escapedName": "utest",
+    "rawSpec": "^0.0.8",
+    "saveSpec": null,
+    "fetchSpec": "^0.0.8"
+  },
+  "_requiredBy": [
+    "#DEV:/"
+  ],
+  "_resolved": "https://registry.npmjs.org/utest/-/utest-0.0.8.tgz";,
+  "_shasum": "fc09451fe697b9008d0c432fe0db439d6cf37914",
+  "_spec": "utest@^0.0.8",
+  "_where": "/home/xavier/dev/debian/packages/node-formidable",
+  "author": {
+    "name": "Felix Geisendörfer",
+    "email": "felix@debuggable.com",
+    "url": "http://debuggable.com/";
+  },
+  "browser": "./browser.js",
+  "bundleDependencies": false,
+  "dependencies": {},
+  "deprecated": false,
+  "description": "The minimal unit testing library.",
+  "devDependencies": {},
+  "engines": {
+    "node": "*"
+  },
+  "homepage": "https://github.com/felixge/node-utest";,
+  "main": "./index",
+  "name": "utest",
+  "optionalDependencies": {},
+  "repository": {
+    "url": ""
+  },
+  "scripts": {
+    "test": "make test"
+  },
+  "version": "0.0.8"
+}
diff --git a/debian/tests/node_modules/utest/syntax.json b/debian/tests/node_modules/utest/syntax.json
new file mode 100644
index 0000000..b98ee1b
--- /dev/null
+++ b/debian/tests/node_modules/utest/syntax.json
@@ -0,0 +1,5 @@
+{
+  "addSemicolons": true,
+  "trimWhitespace": true,
+  "quotes": "'"
+}
diff --git a/debian/tests/pkg-js/files b/debian/tests/pkg-js/files
new file mode 100644
index 0000000..af03f72
--- /dev/null
+++ b/debian/tests/pkg-js/files
@@ -0,0 +1,2 @@
+debian/tests/node_modules
+test
diff --git a/debian/tests/pkg-js/test b/debian/tests/pkg-js/test
new file mode 100644
index 0000000..b104878
--- /dev/null
+++ b/debian/tests/pkg-js/test
@@ -0,0 +1 @@
+NODE_PATH=debian/tests/node_modules node test/run.js
diff --git a/debian/upstream/metadata b/debian/upstream/metadata
new file mode 100644
index 0000000..28cff30
--- /dev/null
+++ b/debian/upstream/metadata
@@ -0,0 +1,7 @@
+---
+Archive: GitHub
+Bug-Database: https://github.com/felixge/node-formidable/issues
+Contact: https://github.com/felixge/node-formidable/issues
+Name: node-formidable
+Repository: https://github.com/felixge/node-formidable.git
+Repository-Browse: https://github.com/felixge/node-formidable
diff --git a/debian/watch b/debian/watch
index c125df3..fe59010 100644
--- a/debian/watch
+++ b/debian/watch
@@ -1,3 +1,5 @@
-version=3
-https://alioth.debian.org/~dapal/npmjs.php?id=formidable \
-	http://registry.npmjs.org/formidable/-/formidable-(\d+.*)\.tgz
+version=4
+opts=\
+dversionmangle=auto,\
+filenamemangle=s/.*\/v?([\d\.-]+)\.tar\.gz/node-formidable-$1.tar.gz/ \
+ https://github.com/felixge/node-formidable/releases .*/archive/v?([\d\.]+).tar.gz
diff --git a/example/json.js b/example/json.js
index eb8a724..0ef5a42 100644
--- a/example/json.js
+++ b/example/json.js
@@ -8,8 +8,8 @@ var common = require('../test/common'),
 
 server = http.createServer(function(req, res) {
   if (req.method !== 'POST') {
-    res.writeHead(200, {'content-type': 'text/plain'})
-    res.end('Please POST a JSON payload to http://localhost:'+port+'/')
+    res.writeHead(200, {'content-type': 'text/plain'});
+    res.end('Please POST a JSON payload to http://localhost:'+port+'/');
     return;
   }
 
@@ -50,18 +50,18 @@ var request = http.request({
   console.log('Status:', response.statusCode);
   response.pipe(process.stdout);
   response.on('end', function() {
-    console.log('\n')
+    console.log('\n');
     process.exit();
   });
   // response.on('data', function(chunk) {
   //   data += chunk.toString('utf8');
   // });
   // response.on('end', function() {
-  //   console.log('Response Data:')
+  //   console.log('Response Data:');
   //   console.log(data);
   //   process.exit();
   // });
-})
+});
 
 request.write('{"numbers":[1,2,3,4,5],"nested":{"key":"value"}}');
 request.end();
diff --git a/example/post.js b/example/post.js
index f6c15a6..8458fbd 100644
--- a/example/post.js
+++ b/example/post.js
@@ -1,7 +1,8 @@
-require('../test/common');
+var common = require('../test/common');
 var http = require('http'),
     util = require('util'),
-    formidable = require('formidable'),
+    formidable = common.formidable,
+    port = common.port,
     server;
 
 server = http.createServer(function(req, res) {
@@ -38,6 +39,6 @@ server = http.createServer(function(req, res) {
     res.end('404');
   }
 });
-server.listen(TEST_PORT);
+server.listen(port);
 
-console.log('listening on http://localhost:'+TEST_PORT+'/');
+console.log('listening on http://localhost:'+port+'/');
diff --git a/example/upload.js b/example/upload.js
index 050cdd9..0b498d6 100644
--- a/example/upload.js
+++ b/example/upload.js
@@ -1,7 +1,9 @@
-require('../test/common');
+var common = require('../test/common');
 var http = require('http'),
     util = require('util'),
-    formidable = require('formidable'),
+    os = require('os'),
+    formidable = common.formidable,
+    port = common.port,
     server;
 
 server = http.createServer(function(req, res) {
@@ -19,7 +21,7 @@ server = http.createServer(function(req, res) {
         files = [],
         fields = [];
 
-    form.uploadDir = TEST_TMP;
+    form.uploadDir = os.tmpdir();
 
     form
       .on('field', function(field, value) {
@@ -43,6 +45,6 @@ server = http.createServer(function(req, res) {
     res.end('404');
   }
 });
-server.listen(TEST_PORT);
+server.listen(port);
 
-console.log('listening on http://localhost:'+TEST_PORT+'/');
+console.log('listening on http://localhost:'+port+'/');
diff --git a/lib/file.js b/lib/file.js
index 1e7184d..50d34c0 100644
--- a/lib/file.js
+++ b/lib/file.js
@@ -1,7 +1,7 @@
 if (global.GENTLY) require = GENTLY.hijack(require);
 
 var util = require('util'),
-    WriteStream = require('fs').WriteStream,
+    fs = require('fs'),
     EventEmitter = require('events').EventEmitter,
     crypto = require('crypto');
 
@@ -23,17 +23,19 @@ function File(properties) {
 
   if(typeof this.hash === 'string') {
     this.hash = crypto.createHash(properties.hash);
+  } else {
+    this.hash = null;
   }
 }
 module.exports = File;
 util.inherits(File, EventEmitter);
 
 File.prototype.open = function() {
-  this._writeStream = new WriteStream(this.path);
+  this._writeStream = new fs.WriteStream(this.path);
 };
 
 File.prototype.toJSON = function() {
-  return {
+  var json = {
     size: this.size,
     path: this.path,
     name: this.name,
@@ -43,16 +45,23 @@ File.prototype.toJSON = function() {
     filename: this.filename,
     mime: this.mime
   };
+  if (this.hash && this.hash != "") {
+    json.hash = this.hash;
+  }
+  return json;
 };
 
 File.prototype.write = function(buffer, cb) {
   var self = this;
+  if (self.hash) {
+    self.hash.update(buffer);
+  }
+
+  if (this._writeStream.closed) {
+    return cb();
+  }
+
   this._writeStream.write(buffer, function() {
-    if (self.hash) {
-      if (self.hash.hasOwnProperty('update')) {
-        self.hash.update(buffer);
-      }
-    }
     self.lastModifiedDate = new Date();
     self.size += buffer.length;
     self.emit('progress', self.size);
@@ -62,10 +71,10 @@ File.prototype.write = function(buffer, cb) {
 
 File.prototype.end = function(cb) {
   var self = this;
+  if (self.hash) {
+    self.hash = self.hash.digest('hex');
+  }
   this._writeStream.end(function() {
-    if(self.hash) {
-      self.hash = self.hash.digest('hex');
-    }
     self.emit('end');
     cb();
   });
diff --git a/lib/incoming_form.js b/lib/incoming_form.js
index 291236d..dbd920b 100644
--- a/lib/incoming_form.js
+++ b/lib/incoming_form.js
@@ -1,5 +1,6 @@
 if (global.GENTLY) require = GENTLY.hijack(require);
 
+var crypto = require('crypto');
 var fs = require('fs');
 var util = require('util'),
     path = require('path'),
@@ -23,13 +24,15 @@ function IncomingForm(opts) {
   this.ended = false;
 
   this.maxFields = opts.maxFields || 1000;
-  this.maxFieldsSize = opts.maxFieldsSize || 2 * 1024 * 1024;
+  this.maxFieldsSize = opts.maxFieldsSize || 20 * 1024 * 1024;
+  this.maxFileSize = opts.maxFileSize || 200 * 1024 * 1024;
   this.keepExtensions = opts.keepExtensions || false;
-  this.uploadDir = opts.uploadDir || os.tmpDir();
+  this.uploadDir = opts.uploadDir || (os.tmpdir && os.tmpdir()) || os.tmpDir();
   this.encoding = opts.encoding || 'utf-8';
   this.headers = null;
   this.type = null;
-  this.hash = false;
+  this.hash = opts.hash || false;
+  this.multiples = opts.multiples || false;
 
   this.bytesReceived = null;
   this.bytesExpected = null;
@@ -37,10 +40,11 @@ function IncomingForm(opts) {
   this._parser = null;
   this._flushing = 0;
   this._fieldsSize = 0;
+  this._fileSize = 0;
   this.openedFiles = [];
 
   return this;
-};
+}
 util.inherits(IncomingForm, EventEmitter);
 exports.IncomingForm = IncomingForm;
 
@@ -74,6 +78,40 @@ IncomingForm.prototype.parse = function(req, cb) {
     return true;
   };
 
+  // Setup callback first, so we don't miss anything from data events emitted
+  // immediately.
+  if (cb) {
+    var fields = {}, files = {};
+    this
+      .on('field', function(name, value) {
+        fields[name] = value;
+      })
+      .on('file', function(name, file) {
+        if (this.multiples) {
+          if (files[name]) {
+            if (!Array.isArray(files[name])) {
+              files[name] = [files[name]];
+            }
+            files[name].push(file);
+          } else {
+            files[name] = file;
+          }
+        } else {
+          files[name] = file;
+        }
+      })
+      .on('error', function(err) {
+        cb(err, fields, files);
+      })
+      .on('end', function() {
+        cb(null, fields, files);
+      });
+  }
+
+  // Parse headers and setup the parser, ready to start listening for data.
+  this.writeHeaders(req.headers);
+
+  // Start listening for data.
   var self = this;
   req
     .on('error', function(err) {
@@ -97,25 +135,6 @@ IncomingForm.prototype.parse = function(req, cb) {
       }
     });
 
-  if (cb) {
-    var fields = {}, files = {};
-    this
-      .on('field', function(name, value) {
-        fields[name] = value;
-      })
-      .on('file', function(name, file) {
-        files[name] = file;
-      })
-      .on('error', function(err) {
-        cb(err, fields, files);
-      })
-      .on('end', function() {
-        cb(null, fields, files);
-      });
-  }
-
-  this.writeHeaders(req.headers);
-
   return this;
 };
 
@@ -126,8 +145,11 @@ IncomingForm.prototype.writeHeaders = function(headers) {
 };
 
 IncomingForm.prototype.write = function(buffer) {
+  if (this.error) {
+    return;
+  }
   if (!this._parser) {
-    this._error(new Error('unintialized parser'));
+    this._error(new Error('uninitialized parser'));
     return;
   }
 
@@ -160,6 +182,7 @@ IncomingForm.prototype.onPart = function(part) {
 IncomingForm.prototype.handlePart = function(part) {
   var self = this;
 
+  // This MUST check exactly for undefined. You can not change it to !part.filename.
   if (part.filename === undefined) {
     var value = ''
       , decoder = new StringDecoder(this.encoding);
@@ -194,6 +217,14 @@ IncomingForm.prototype.handlePart = function(part) {
   this.openedFiles.push(file);
 
   part.on('data', function(buffer) {
+    self._fileSize += buffer.length;
+    if (self._fileSize > self.maxFileSize) {
+      self._error(new Error('maxFileSize exceeded, received '+self._fileSize+' bytes of file data'));
+      return;
+    }
+    if (buffer.length == 0) {
+      return;
+    }
     self.pause();
     file.write(buffer, function() {
       self.resume();
@@ -241,8 +272,8 @@ IncomingForm.prototype._parseContentType = function() {
   }
 
   if (this.headers['content-type'].match(/multipart/i)) {
-    var m;
-    if (m = this.headers['content-type'].match(/boundary=(?:"([^"]+)"|([^;]+))/i)) {
+    var m = this.headers['content-type'].match(/boundary=(?:"([^"]+)"|([^;]+))/i);
+    if (m) {
       this._initMultipart(m[1] || m[2]);
     } else {
       this._error(new Error('bad content-type header, no multipart boundary'));
@@ -264,21 +295,25 @@ IncomingForm.prototype._error = function(err) {
   }
 
   this.error = err;
-  this.pause();
   this.emit('error', err);
 
   if (Array.isArray(this.openedFiles)) {
     this.openedFiles.forEach(function(file) {
       file._writeStream.destroy();
-      setTimeout(fs.unlink, 0, file.path);
+      setTimeout(fs.unlink, 0, file.path, function(error) { });
     });
   }
 };
 
 IncomingForm.prototype._parseContentLength = function() {
+  this.bytesReceived = 0;
   if (this.headers['content-length']) {
-    this.bytesReceived = 0;
     this.bytesExpected = parseInt(this.headers['content-length'], 10);
+  } else if (this.headers['transfer-encoding'] === undefined) {
+    this.bytesExpected = 0;
+  }
+
+  if (this.bytesExpected !== null) {
     this.emit('progress', this.bytesReceived, this.bytesExpected);
   }
 };
@@ -325,10 +360,11 @@ IncomingForm.prototype._initMultipart = function(boundary) {
     headerField = headerField.toLowerCase();
     part.headers[headerField] = headerValue;
 
-    var m;
+    // matches either a quoted-string or a token (RFC 2616 section 19.5.1)
+    var m = headerValue.match(/\bname=("([^"]*)"|([^\(\)<>@,;:\\"\/\[\]\?=\{\}\s\t/]+))/i);
     if (headerField == 'content-disposition') {
-      if (m = headerValue.match(/\bname="([^"]+)"/i)) {
-        part.name = m[1];
+      if (m) {
+        part.name = m[2] || m[3] || '';
       }
 
       part.filename = self._fileName(headerValue);
@@ -366,13 +402,13 @@ IncomingForm.prototype._initMultipart = function(boundary) {
         can be divided by 4, it will result in a number of buytes that
         can be divided vy 3.
         */
-        var offset = parseInt(part.transferBuffer.length / 4) * 4;
-        part.emit('data', new Buffer(part.transferBuffer.substring(0, offset), 'base64'))
+        var offset = parseInt(part.transferBuffer.length / 4, 10) * 4;
+        part.emit('data', new Buffer(part.transferBuffer.substring(0, offset), 'base64'));
         part.transferBuffer = part.transferBuffer.substring(offset);
       };
 
       parser.onPartEnd = function() {
-        part.emit('data', new Buffer(part.transferBuffer, 'base64'))
+        part.emit('data', new Buffer(part.transferBuffer, 'base64'));
         part.emit('end');
       };
       break;
@@ -394,10 +430,12 @@ IncomingForm.prototype._initMultipart = function(boundary) {
 };
 
 IncomingForm.prototype._fileName = function(headerValue) {
-  var m = headerValue.match(/\bfilename="(.*?)"($|; )/i);
+  // matches either a quoted-string or a token (RFC 2616 section 19.5.1)
+  var m = headerValue.match(/\bfilename=("(.*?)"|([^\(\)<>@,;:\\"\/\[\]\?=\{\}\s\t/]+))($|;\s)/i);
   if (!m) return;
 
-  var filename = m[1].substr(m[1].lastIndexOf('\\') + 1);
+  var match = m[2] || m[3] || '';
+  var filename = match.substr(match.lastIndexOf('\\') + 1);
   filename = filename.replace(/%22/g, '"');
   filename = filename.replace(/&#([\d]{4});/g, function(m, code) {
     return String.fromCharCode(code);
@@ -434,10 +472,9 @@ IncomingForm.prototype._initOctetStream = function() {
     type: mime
   });
 
-  file.open();
-
   this.emit('fileBegin', filename, file);
-
+  file.open();
+  this.openedFiles.push(file);
   this._flushing++;
 
   var self = this;
@@ -466,8 +503,10 @@ IncomingForm.prototype._initOctetStream = function() {
     self.ended = true;
 
     var done = function(){
-      self.emit('file', 'file', file);
-      self._maybeEnd();
+      file.end(function() {
+        self.emit('file', 'file', file);
+        self._maybeEnd();
+      });
     };
 
     if(outstandingWrites === 0){
@@ -481,16 +520,12 @@ IncomingForm.prototype._initOctetStream = function() {
 IncomingForm.prototype._initJSONencoded = function() {
   this.type = 'json';
 
-  var parser = new JSONParser()
+  var parser = new JSONParser(this)
     , self = this;
 
-  if (this.bytesExpected) {
-    parser.initWithLength(this.bytesExpected);
-  }
-
   parser.onField = function(key, val) {
     self.emit('field', key, val);
-  }
+  };
 
   parser.onEnd = function() {
     self.ended = true;
@@ -501,14 +536,12 @@ IncomingForm.prototype._initJSONencoded = function() {
 };
 
 IncomingForm.prototype._uploadPath = function(filename) {
-  var name = '';
-  for (var i = 0; i < 32; i++) {
-    name += Math.floor(Math.random() * 16).toString(16);
-  }
+  var buf = crypto.randomBytes(16);
+  var name = 'upload_' + buf.toString('hex');
 
   if (this.keepExtensions) {
     var ext = path.extname(filename);
-    ext     = ext.replace(/(\.[a-z0-9]+).*/, '$1');
+    ext     = ext.replace(/(\.[a-z0-9]+).*/i, '$1');
 
     name += ext;
   }
@@ -523,4 +556,3 @@ IncomingForm.prototype._maybeEnd = function() {
 
   this.emit('end');
 };
-
diff --git a/lib/json_parser.js b/lib/json_parser.js
index 6ce966b..28a23ba 100644
--- a/lib/json_parser.js
+++ b/lib/json_parser.js
@@ -1,35 +1,30 @@
 if (global.GENTLY) require = GENTLY.hijack(require);
 
-var Buffer = require('buffer').Buffer
+var Buffer = require('buffer').Buffer;
 
-function JSONParser() {
-  this.data = new Buffer('');
+function JSONParser(parent) {
+  this.parent = parent;
+  this.chunks = [];
   this.bytesWritten = 0;
-};
-exports.JSONParser = JSONParser;
-
-JSONParser.prototype.initWithLength = function(length) {
-  this.data = new Buffer(length);
 }
+exports.JSONParser = JSONParser;
 
 JSONParser.prototype.write = function(buffer) {
-  if (this.data.length >= this.bytesWritten + buffer.length) {
-    buffer.copy(this.data, this.bytesWritten);
-  } else {
-    this.data = Buffer.concat([this.data, buffer]);
-  }
   this.bytesWritten += buffer.length;
+  this.chunks.push(buffer);
   return buffer.length;
-}
+};
 
 JSONParser.prototype.end = function() {
   try {
-    var fields = JSON.parse(this.data.toString('utf8'))
+    var fields = JSON.parse(Buffer.concat(this.chunks));
     for (var field in fields) {
       this.onField(field, fields[field]);
     }
-  } catch (e) {}
+  } catch (e) {
+    this.parent.emit('error', e);
+  }
   this.data = null;
 
   this.onEnd();
-}
\ No newline at end of file
+};
diff --git a/lib/multipart_parser.js b/lib/multipart_parser.js
index 98a6856..36de2b0 100644
--- a/lib/multipart_parser.js
+++ b/lib/multipart_parser.js
@@ -46,7 +46,7 @@ function MultipartParser() {
 
   this.index = null;
   this.flags = 0;
-};
+}
 exports.MultipartParser = MultipartParser;
 
 MultipartParser.stateToString = function(stateNumber) {
@@ -58,8 +58,8 @@ MultipartParser.stateToString = function(stateNumber) {
 
 MultipartParser.prototype.initWithBoundary = function(str) {
   this.boundary = new Buffer(str.length+4);
-  this.boundary.write('\r\n--', 'ascii', 0);
-  this.boundary.write(str, 'ascii', 4);
+  this.boundary.write('\r\n--', 0);
+  this.boundary.write(str, 4);
   this.lookbehind = new Buffer(this.boundary.length+8);
   this.state = S.START;
 
@@ -127,18 +127,25 @@ MultipartParser.prototype.write = function(buffer) {
         state = S.START_BOUNDARY;
       case S.START_BOUNDARY:
         if (index == boundary.length - 2) {
-          if (c != CR) {
+          if (c == HYPHEN) {
+            flags |= F.LAST_BOUNDARY;
+          } else if (c != CR) {
             return i;
           }
           index++;
           break;
         } else if (index - 1 == boundary.length - 2) {
-          if (c != LF) {
+          if (flags & F.LAST_BOUNDARY && c == HYPHEN){
+            callback('end');
+            state = S.END;
+            flags = 0;
+          } else if (!(flags & F.LAST_BOUNDARY) && c == LF) {
+            index = 0;
+            callback('partBegin');
+            state = S.HEADER_FIELD_START;
+          } else {
             return i;
           }
-          index = 0;
-          callback('partBegin');
-          state = S.HEADER_FIELD_START;
           break;
         }
 
@@ -214,7 +221,7 @@ MultipartParser.prototype.write = function(buffer) {
       case S.PART_DATA:
         prevIndex = index;
 
-        if (index == 0) {
+        if (index === 0) {
           // boyer-moore derrived algorithm to safely skip non-boundary data
           i += boundaryEnd;
           while (i < bufferLength && !(buffer[i] in boundaryChars)) {
@@ -226,7 +233,7 @@ MultipartParser.prototype.write = function(buffer) {
 
         if (index < boundary.length) {
           if (boundary[index] == c) {
-            if (index == 0) {
+            if (index === 0) {
               dataCallback('partData', true);
             }
             index++;
@@ -260,6 +267,7 @@ MultipartParser.prototype.write = function(buffer) {
               callback('partEnd');
               callback('end');
               state = S.END;
+              flags = 0;
             } else {
               index = 0;
             }
@@ -310,7 +318,7 @@ MultipartParser.prototype.end = function() {
       self[callbackSymbol]();
     }
   };
-  if ((this.state == S.HEADER_FIELD_START && this.index == 0) ||
+  if ((this.state == S.HEADER_FIELD_START && this.index === 0) ||
       (this.state == S.PART_DATA && this.index == this.boundary.length)) {
     callback(this, 'partEnd');
     callback(this, 'end');
diff --git a/lib/querystring_parser.js b/lib/querystring_parser.js
index 320ce5a..fcaffe0 100644
--- a/lib/querystring_parser.js
+++ b/lib/querystring_parser.js
@@ -7,7 +7,7 @@ var querystring = require('querystring');
 function QuerystringParser(maxKeys) {
   this.maxKeys = maxKeys;
   this.buffer = '';
-};
+}
 exports.QuerystringParser = QuerystringParser;
 
 QuerystringParser.prototype.write = function(buffer) {
diff --git a/package.json b/package.json
index 4b42a1d..bf07c37 100644
--- a/package.json
+++ b/package.json
@@ -2,14 +2,15 @@
   "name": "formidable",
   "description": "A node.js module for parsing form data, especially file uploads.",
   "homepage": "https://github.com/felixge/node-formidable";,
-  "version": "1.0.13",
+  "license": "MIT",
+  "version": "1.2.1",
   "devDependencies": {
-    "gently": "0.8.0",
-    "findit": "0.1.1",
-    "hashish": "0.0.4",
-    "urun": "~0.0.6",
-    "utest": "0.0.3",
-    "request": "~2.11.4"
+    "gently": "^0.8.0",
+    "findit": "^0.1.2",
+    "hashish": "^0.0.4",
+    "urun": "^0.0.6",
+    "utest": "^0.0.8",
+    "request": "^2.11.4"
   },
   "directories": {
     "lib": "./lib"
@@ -19,9 +20,6 @@
     "test": "node test/run.js",
     "clean": "rm test/tmp/*"
   },
-  "engines": {
-    "node": "<0.9.0"
-  },
   "repository": {
     "type": "git",
     "url": "git://github.com/felixge/node-formidable.git"
diff --git a/test/fixture/http/encoding/beta-sticker-1.png.http b/test/fixture/http/encoding/beta-sticker-1.png.http
new file mode 100644
index 0000000..833b83c
--- /dev/null
+++ b/test/fixture/http/encoding/beta-sticker-1.png.http
@@ -0,0 +1,12 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+Content-Length: 2483
+
+--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+Content-Disposition: form-data; name="sticker"; filename="beta-sticker-1.png"
+Content-Type: image/png
+Content-Transfer-Encoding: base64
+
+iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABh5JREFUeNrMmHtIHEcYwGfv5SNwaovxEanEiJKqlYCCTRo1f0SvDeof1legEcE/YttQaNOiaQjYFFtpKaJILZU8SCRUWqlJGpoWepGLTXqUEnzFxCrnK9DEelbvvPOe/WacuY7r7HmGFjrwsbNzt7u//V7zfYvQ/2xI/9K1/NyvMP9PgCTuGmmL6/0ckD9UOGmbIExUsqMkAPHJjv5QwKRtgKioqDlh5+w/7IFeCuLlxCeA2zQ0IcCwh2qoaLH09fUdTElJ2e/1elU+n0/y+9fvPz4+fvfYsWN3YOoBcXPiocLghD4mBYHhQTCErqWlZU9FRcXJqKiowyqVSk/uSEH4o8fjWVlYWDB2d3e3d3R0WGB5jYqLg/NyGgsKxMNgkDB4451NTU3vxcXF1SlBKB0tFsuVxsbGjlu3bj2GJQeIk8K5RVBqBTMxrYRfuHAh9/jx4+ejo6MPS9I6f6hHPOC6rOLi4vyVlZXf7t27Z5c5/iZfkgMxxyUwFy9ezC0tLe3V6XRJ/MOCAYjWwsLCni0oKCh98uSJaWhoyMZFn0/uT2qBqYi/1NbWxjc0NJwPFUYExc/B53R5eXk5ZrN5YH5+3slFn5+D2uBDzG90IJETExOtzGdC9RelNf78wYMH3xQWFn4Ep0sgyyCr1NmJP6kEIa5tbW3dEx8fXxeKRoJpT76OR3p6enllZWUKTCOwNalFAglWDkTCvLq6+uR2YYKZSw4GQVKNfZQCafjkqhKYTBsTE3NY/uYi2Q4MP5KTkw9QGB3VEMv6G/YioqFLly5lazQavfytxobnUW+PWTGisIyNPEL3QYLB4PPIyMi4EydO7JUBbTIZ0RDYOFPkE8t/OdHczCK6Y/qdzP8BfUTW8Tj/uQndvT1F5vOzVvTLz1PwX4cQbt++fekURsNpSNLIw16v1z/HLsRRgecsSnovm8nxs5bvUe+NN1Bz47fkfBaAXj2aA2BWEsM/3hhFX1/5Fe3NTEAfvn8NXTO+tSH68IiNjU2Qw/AmCzg2XCQp+YyhJAu9c+pl9GJ+KmhiEt38bhjpoyJQRtYudA60k3dwD6o4mouKjmSiolcy0ArRqnXz3rT+knwFEShhNKLNlmmFP7Kf8XxuehHpj0QQmLdPGch/ioYyCSAe57pMaHnJgcprctDdwUkRjKi8CUTWhipvbm7uvlJo3zFNoHJDOznPeGEXqn+9EBUf+AQZXvqU+BEG/KCpHz2flYh+ALO9++ZX5L/Mj3gfevjw4ZRoP+PzD/b4HadPn844c+aMkb0F1DqIz9byzBvquXytvr6+7vr16+Ow9CfN2njjdfFAWpo9o2FnNmm12kQMw24gcvSnhbHb7Y+huHsNlhapLNHSxK3idlq287qhhrkKlSByOBzIZrPhGyCn04ncbjfRGAMV5ZlQxvDw8E+yYi1Q3qpleYjUQlNTU5aysrJqgNBhIAwGVSDCkFj48BVFULA1eCl7XV3dx1CKYK3YqKnY7u9Ti2royclJ76FDh1YhxefgsoFpCIOtra0RuGBQwYbRaLzc1dVlpjA2ZiqmKbWsDAmEYU9Pz8Tg4OCNoqKixNTU1BQostDq6iqBcrlcRBiYfEff1KBR+OnpabPBYOikWlnhtOOWm0zUffpnZ2ednZ2dJtCYMTs7+xkA2x0eHk6gsMYwFPYr/EC1Wo2LMEWzWa1WC1QRZ8FUVgpj42ohD3umWqHjRFxf5RkZGVkCNQ9CcTWQn5+flpSUtBOiMKAt7Fek/FSAmpmZMVdVVZ0dGxv7g4PhteMVlbBIofv0sh4Lbmhtb2+/Cbv1eFpaWmJCQsJODMO0hGGgUghAAay9v7//i5KSki9lmmG+4+Jg/MHaIH6f0dCkqaNFFc5VkViam5v319TUNEDdvRubEGsNYHGqsAwMDFxta2u7DdpdpA+3c+LgWiHfVkCiFnpDw0iLqwgqO6BVKoPo00K6WIDsOzE6OrpE395FzeLgxMn5jVe0dYTa26s5jfFg4VR0nAuwNtrFda1rgmToD6VzVWq3eTPyYAxOwwH5gvT2PiWY7X4fUgJTywp1fivyyL6E+Lb6XvQ0X9AkBeeXZED+p/k+9LcAAwAXm3hBLzoZPAAAAABJRU5ErkJggg==
+--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/--
diff --git a/test/fixture/http/encoding/binaryfile.tar.gz.http b/test/fixture/http/encoding/binaryfile.tar.gz.http
new file mode 100644
index 0000000..4f4fadb
--- /dev/null
+++ b/test/fixture/http/encoding/binaryfile.tar.gz.http
@@ -0,0 +1,12 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+Content-Length: 676
+
+--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+Content-Disposition: form-data; name="file"; filename="binaryfile.tar.gz"
+Content-Type: application/x-gzip
+Content-Transfer-Encoding: base64
+
+H4sIAGiNIU8AA+3R0W6CMBQGYK59iobLZantRDG73osUOGqnFNJWM2N897UghG1ZdmWWLf93U/jP4bRAq8q92hJ/dY1J7kQEqyyLq8yXYrp2ltkqkTKXYiEykYc++ZTLVcLEvQ40dXReWcYSV1pdnL/v+6n+R11mjKVG1ZQ+s3TT2FpXqjhQ+hjzE1mnGxNLkgu+7tOKWjIVmVKTC6XL9ZaeXj4VQhwKWzL+cI4zwgQuuhkh3mhTad/Hkssh3im3027X54JnQ360R/M19OT8kC7SEN7Ooi2VvrEfznHQRWzl83gxttZKmzGehzPRW/+W8X+3fvL8sFet9sS6m3EIma02071MU3Uf9KHrmV1/+y8DAAAAAAAAAAAAAAAAAAAAAMB/9A6txIuJACgAAA==
+--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/--
diff --git a/test/fixture/http/encoding/blank.gif.http b/test/fixture/http/encoding/blank.gif.http
new file mode 100644
index 0000000..7426f5b
--- /dev/null
+++ b/test/fixture/http/encoding/blank.gif.http
@@ -0,0 +1,12 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+Content-Length: 323
+
+--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+Content-Disposition: form-data; name="file"; filename="blank.gif"
+Content-Type: image/gif
+Content-Transfer-Encoding: base64
+
+R0lGODlhAQABAJH/AP///wAAAMDAwAAAACH5BAEAAAIALAAAAAABAAEAAAICVAEAOw==
+--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/--
diff --git a/test/fixture/http/encoding/menu_seperator.png.http b/test/fixture/http/encoding/menu_seperator.png.http
new file mode 100644
index 0000000..d08fd37
--- /dev/null
+++ b/test/fixture/http/encoding/menu_seperator.png.http
@@ -0,0 +1,12 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+Content-Length: 1509
+
+--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+Content-Disposition: form-data; name="image"; filename="menu_separator.png"
+Content-Type: image/png
+Content-Transfer-Encoding: base64
+
+iVBORw0KGgoAAAANSUhEUgAAAAIAAAAYCAIAAABfmbuOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDcxODNBNzJERDcyMTFFMUFBOEVFNDQzOTA0MDJDMjQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDcxODNBNzNERDcyMTFFMUFBOEVFNDQzOTA0MDJDMjQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowNzE4M0E3MERENzIxMUUxQUE4RUU0NDM5MDQwMkMyNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowNzE4M0E3MURENzIxMUUxQUE4RUU0NDM5MDQwMkMyNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pmvhbb8AAAAXSURBVHjaYnHk9PON8WJiAIPBSwEEGAAPrgG+VozFWgAAAABJRU5ErkJggg==
+--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/--
diff --git a/test/fixture/http/encoding/plain.txt.http b/test/fixture/http/encoding/plain.txt.http
new file mode 100644
index 0000000..5e85ad6
--- /dev/null
+++ b/test/fixture/http/encoding/plain.txt.http
@@ -0,0 +1,13 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
+Content-Length: 221
+
+------TLV0SrKD4z1TRxRhAPUvZ
+Content-Disposition: form-data; name="file"; filename="plain.txt"
+Content-Type: text/plain
+Content-Transfer-Encoding: 7bit
+
+I am a plain text file
+
+------TLV0SrKD4z1TRxRhAPUvZ--
diff --git a/test/fixture/http/misc/empty-multipart.http b/test/fixture/http/misc/empty-multipart.http
new file mode 100644
index 0000000..81c6125
--- /dev/null
+++ b/test/fixture/http/misc/empty-multipart.http
@@ -0,0 +1,5 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
+Content-Length: 0
+
diff --git a/test/fixture/http/misc/empty-multipart2.http b/test/fixture/http/misc/empty-multipart2.http
new file mode 100644
index 0000000..eda6528
--- /dev/null
+++ b/test/fixture/http/misc/empty-multipart2.http
@@ -0,0 +1,6 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
+Content-Length: 31
+
+------TLV0SrKD4z1TRxRhAPUvZ--
diff --git a/test/fixture/http/misc/empty-urlencoded.http b/test/fixture/http/misc/empty-urlencoded.http
new file mode 100644
index 0000000..bb197d3
--- /dev/null
+++ b/test/fixture/http/misc/empty-urlencoded.http
@@ -0,0 +1,5 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Length: 0
+Content-Type: application/x-www-form-urlencoded
+
diff --git a/test/fixture/http/misc/empty.http b/test/fixture/http/misc/empty.http
new file mode 100644
index 0000000..4c4cee5
--- /dev/null
+++ b/test/fixture/http/misc/empty.http
@@ -0,0 +1,4 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Length: 0
+
diff --git a/test/fixture/http/misc/minimal.http b/test/fixture/http/misc/minimal.http
new file mode 100644
index 0000000..594cdd3
--- /dev/null
+++ b/test/fixture/http/misc/minimal.http
@@ -0,0 +1,3 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+
diff --git a/test/fixture/http/no-filename/filename-name.http b/test/fixture/http/no-filename/filename-name.http
new file mode 100644
index 0000000..43672a3
--- /dev/null
+++ b/test/fixture/http/no-filename/filename-name.http
@@ -0,0 +1,13 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytyE4wkKlZ5CQJVTG
+Content-Length: 1000
+
+------WebKitFormBoundarytyE4wkKlZ5CQJVTG
+Content-Disposition: form-data; filename="plain.txt"; name="upload"
+Content-Type: text/plain
+
+I am a plain text file
+
+------WebKitFormBoundarytyE4wkKlZ5CQJVTG--
+
diff --git a/test/fixture/http/no-filename/generic.http b/test/fixture/http/no-filename/generic.http
new file mode 100644
index 0000000..e0dee27
--- /dev/null
+++ b/test/fixture/http/no-filename/generic.http
@@ -0,0 +1,13 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytyE4wkKlZ5CQJVTG
+Content-Length: 1000
+
+------WebKitFormBoundarytyE4wkKlZ5CQJVTG
+Content-Disposition: form-data; name="upload"; filename=""
+Content-Type: text/plain
+
+I am a plain text file
+
+------WebKitFormBoundarytyE4wkKlZ5CQJVTG--
+
diff --git a/test/fixture/http/preamble/crlf.http b/test/fixture/http/preamble/crlf.http
new file mode 100644
index 0000000..1d5f709
--- /dev/null
+++ b/test/fixture/http/preamble/crlf.http
@@ -0,0 +1,13 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
+Content-Length: 184
+
+
+------TLV0SrKD4z1TRxRhAPUvZ
+Content-Disposition: form-data; name="upload"; filename="plain.txt"
+Content-Type: text/plain
+
+I am a plain text file
+
+------TLV0SrKD4z1TRxRhAPUvZ--
diff --git a/test/fixture/http/preamble/preamble.http b/test/fixture/http/preamble/preamble.http
new file mode 100644
index 0000000..d14d433
--- /dev/null
+++ b/test/fixture/http/preamble/preamble.http
@@ -0,0 +1,13 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
+Content-Length: 226
+
+This is a preamble which should be ignored
+------TLV0SrKD4z1TRxRhAPUvZ
+Content-Disposition: form-data; name="upload"; filename="plain.txt"
+Content-Type: text/plain
+
+I am a plain text file
+
+------TLV0SrKD4z1TRxRhAPUvZ--
diff --git a/test/fixture/http/special-chars-in-filename/osx-chrome-13.http b/test/fixture/http/special-chars-in-filename/osx-chrome-13.http
new file mode 100644
index 0000000..4ef3917
--- /dev/null
+++ b/test/fixture/http/special-chars-in-filename/osx-chrome-13.http
@@ -0,0 +1,26 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Connection: keep-alive
+Referer: http://localhost:8080/
+Content-Length: 383
+Cache-Control: max-age=0
+Origin: http://localhost:8080
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.220 Safari/535.1
+Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytyE4wkKlZ5CQJVTG
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+Accept-Encoding: gzip,deflate,sdch
+Accept-Language: en-US,en;q=0.8
+Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
+Cookie: jqCookieJar_tablesorter=%7B%22showListTable%22%3A%5B%5B5%2C1%5D%2C%5B1%2C0%5D%5D%7D
+
+------WebKitFormBoundarytyE4wkKlZ5CQJVTG
+Content-Disposition: form-data; name="title"
+
+Weird filename
+------WebKitFormBoundarytyE4wkKlZ5CQJVTG
+Content-Disposition: form-data; name="upload"; filename=": \ ? % * | %22 < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
+Content-Type: text/plain
+
+I am a text file with a funky name!
+
+------WebKitFormBoundarytyE4wkKlZ5CQJVTG--
diff --git a/test/fixture/http/special-chars-in-filename/osx-firefox-3.6.http b/test/fixture/http/special-chars-in-filename/osx-firefox-3.6.http
new file mode 100644
index 0000000..bf49f85
--- /dev/null
+++ b/test/fixture/http/special-chars-in-filename/osx-firefox-3.6.http
@@ -0,0 +1,24 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.22) Gecko/20110902 Firefox/3.6.22
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+Accept-Language: en-us,en;q=0.5
+Accept-Encoding: gzip,deflate
+Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
+Keep-Alive: 115
+Connection: keep-alive
+Referer: http://localhost:8080/
+Content-Type: multipart/form-data; boundary=---------------------------9849436581144108930470211272
+Content-Length: 438
+
+-----------------------------9849436581144108930470211272
+Content-Disposition: form-data; name="title"
+
+Weird filename
+-----------------------------9849436581144108930470211272
+Content-Disposition: form-data; name="upload"; filename=": \ ? % * | " < > . &#9731; ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
+Content-Type: text/plain
+
+I am a text file with a funky name!
+
+-----------------------------9849436581144108930470211272--
diff --git a/test/fixture/http/special-chars-in-filename/osx-safari-5.http b/test/fixture/http/special-chars-in-filename/osx-safari-5.http
new file mode 100644
index 0000000..ff158a4
--- /dev/null
+++ b/test/fixture/http/special-chars-in-filename/osx-safari-5.http
@@ -0,0 +1,23 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Origin: http://localhost:8080
+Content-Length: 383
+User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1
+Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryQJZ1gvhvdgfisJPJ
+Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
+Referer: http://localhost:8080/
+Accept-Language: en-us
+Accept-Encoding: gzip, deflate
+Connection: keep-alive
+
+------WebKitFormBoundaryQJZ1gvhvdgfisJPJ
+Content-Disposition: form-data; name="title"
+
+Weird filename
+------WebKitFormBoundaryQJZ1gvhvdgfisJPJ
+Content-Disposition: form-data; name="upload"; filename=": \ ? % * | %22 < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
+Content-Type: text/plain
+
+I am a text file with a funky name!
+
+------WebKitFormBoundaryQJZ1gvhvdgfisJPJ--
diff --git a/test/fixture/http/special-chars-in-filename/xp-chrome-12.http b/test/fixture/http/special-chars-in-filename/xp-chrome-12.http
new file mode 100644
index 0000000..f0fc533
--- /dev/null
+++ b/test/fixture/http/special-chars-in-filename/xp-chrome-12.http
@@ -0,0 +1,24 @@
+POST /upload HTTP/1.1
+Host: 192.168.56.1:8080
+Connection: keep-alive
+Referer: http://192.168.56.1:8080/
+Content-Length: 344
+Cache-Control: max-age=0
+Origin: http://192.168.56.1:8080
+User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.122 Safari/534.30
+Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryEvqBNplR3ByrwQPa
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+Accept-Encoding: gzip,deflate,sdch
+Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
+Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
+
+------WebKitFormBoundaryEvqBNplR3ByrwQPa
+Content-Disposition: form-data; name="title"
+
+Weird filename
+------WebKitFormBoundaryEvqBNplR3ByrwQPa
+Content-Disposition: form-data; name="upload"; filename=" ? % * | %22 < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
+Content-Type: text/plain
+
+
+------WebKitFormBoundaryEvqBNplR3ByrwQPa--
diff --git a/test/fixture/http/special-chars-in-filename/xp-ie-7.http b/test/fixture/http/special-chars-in-filename/xp-ie-7.http
new file mode 100644
index 0000000..2e2c61c
--- /dev/null
+++ b/test/fixture/http/special-chars-in-filename/xp-ie-7.http
@@ -0,0 +1,22 @@
+POST /upload HTTP/1.1
+Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, */*
+Referer: http://192.168.56.1:8080/
+Accept-Language: de
+User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
+Content-Type: multipart/form-data; boundary=---------------------------7db1fe232017c
+Accept-Encoding: gzip, deflate
+Host: 192.168.56.1:8080
+Content-Length: 368
+Connection: Keep-Alive
+Cache-Control: no-cache
+
+-----------------------------7db1fe232017c
+Content-Disposition: form-data; name="title"
+
+Weird filename
+-----------------------------7db1fe232017c
+Content-Disposition: form-data; name="upload"; filename=" ? % * | " < > . &#9731; ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
+Content-Type: application/octet-stream
+
+
+-----------------------------7db1fe232017c--
diff --git a/test/fixture/http/special-chars-in-filename/xp-ie-8.http b/test/fixture/http/special-chars-in-filename/xp-ie-8.http
new file mode 100644
index 0000000..e2b94fa
--- /dev/null
+++ b/test/fixture/http/special-chars-in-filename/xp-ie-8.http
@@ -0,0 +1,22 @@
+POST /upload HTTP/1.1
+Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, */*
+Referer: http://192.168.56.1:8080/
+Accept-Language: de
+User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)
+Content-Type: multipart/form-data; boundary=---------------------------7db3a8372017c
+Accept-Encoding: gzip, deflate
+Host: 192.168.56.1:8080
+Content-Length: 368
+Connection: Keep-Alive
+Cache-Control: no-cache
+
+-----------------------------7db3a8372017c
+Content-Disposition: form-data; name="title"
+
+Weird filename
+-----------------------------7db3a8372017c
+Content-Disposition: form-data; name="upload"; filename=" ? % * | " < > . &#9731; ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
+Content-Type: application/octet-stream
+
+
+-----------------------------7db3a8372017c--
diff --git a/test/fixture/http/special-chars-in-filename/xp-safari-5.http b/test/fixture/http/special-chars-in-filename/xp-safari-5.http
new file mode 100644
index 0000000..6379ac0
--- /dev/null
+++ b/test/fixture/http/special-chars-in-filename/xp-safari-5.http
@@ -0,0 +1,22 @@
+POST /upload HTTP/1.1
+Host: 192.168.56.1:8080
+Referer: http://192.168.56.1:8080/
+Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
+Accept-Language: en-US
+Origin: http://192.168.56.1:8080
+User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4
+Accept-Encoding: gzip, deflate
+Content-Type: multipart/form-data; boundary=----WebKitFormBoundarykmaWSUbu697WN9TM
+Content-Length: 344
+Connection: keep-alive
+
+------WebKitFormBoundarykmaWSUbu697WN9TM
+Content-Disposition: form-data; name="title"
+
+Weird filename
+------WebKitFormBoundarykmaWSUbu697WN9TM
+Content-Disposition: form-data; name="upload"; filename=" ? % * | %22 < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
+Content-Type: text/plain
+
+
+------WebKitFormBoundarykmaWSUbu697WN9TM--
diff --git a/test/fixture/http/workarounds/missing-hyphens1.http b/test/fixture/http/workarounds/missing-hyphens1.http
new file mode 100644
index 0000000..2826890
--- /dev/null
+++ b/test/fixture/http/workarounds/missing-hyphens1.http
@@ -0,0 +1,12 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
+Content-Length: 178
+
+------TLV0SrKD4z1TRxRhAPUvZ
+Content-Disposition: form-data; name="upload"; filename="plain.txt"
+Content-Type: text/plain
+
+I am a plain text file
+
+------TLV0SrKD4z1TRxRhAPUvZ
diff --git a/test/fixture/http/workarounds/missing-hyphens2.http b/test/fixture/http/workarounds/missing-hyphens2.http
new file mode 100644
index 0000000..8e18194
--- /dev/null
+++ b/test/fixture/http/workarounds/missing-hyphens2.http
@@ -0,0 +1,12 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
+Content-Length: 180
+
+------TLV0SrKD4z1TRxRhAPUvZ
+Content-Disposition: form-data; name="upload"; filename="plain.txt"
+Content-Type: text/plain
+
+I am a plain text file
+
+------TLV0SrKD4z1TRxRhAPUvZ
diff --git a/test/fixture/js/encoding.js b/test/fixture/js/encoding.js
index 83dfb15..fc22026 100644
--- a/test/fixture/js/encoding.js
+++ b/test/fixture/js/encoding.js
@@ -1,19 +1,24 @@
 module.exports['menu_seperator.png.http'] = [
-  {type: 'file', name: 'image', filename: 'menu_separator.png', fixture: 'menu_separator.png'}
+  {type: 'file', name: 'image', filename: 'menu_separator.png', fixture: 'menu_separator.png',
+  sha1: 'c845ca3ea794be298f2a1b79769b71939eaf4e54'}
 ];
 
 module.exports['beta-sticker-1.png.http'] = [
-  {type: 'file', name: 'sticker', filename: 'beta-sticker-1.png', fixture: 'beta-sticker-1.png'}
+  {type: 'file', name: 'sticker', filename: 'beta-sticker-1.png', fixture: 'beta-sticker-1.png',
+  sha1: '6abbcffd12b4ada5a6a084fe9e4584f846331bc4'}
 ];
 
 module.exports['blank.gif.http'] = [
-  {type: 'file', name: 'file', filename: 'blank.gif', fixture: 'blank.gif'}
+  {type: 'file', name: 'file', filename: 'blank.gif', fixture: 'blank.gif',
+  sha1: 'a1fdee122b95748d81cee426d717c05b5174fe96'}
 ];
 
 module.exports['binaryfile.tar.gz.http'] = [
-  {type: 'file', name: 'file', filename: 'binaryfile.tar.gz', fixture: 'binaryfile.tar.gz'}
+  {type: 'file', name: 'file', filename: 'binaryfile.tar.gz', fixture: 'binaryfile.tar.gz',
+  sha1: 'cfabe13b348e5e69287d677860880c52a69d2155'}
 ];
 
 module.exports['plain.txt.http'] = [
-  {type: 'file', name: 'file', filename: 'plain.txt', fixture: 'plain.txt'}
+  {type: 'file', name: 'file', filename: 'plain.txt', fixture: 'plain.txt',
+  sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'}
 ];
diff --git a/test/fixture/js/misc.js b/test/fixture/js/misc.js
index 3e27472..1ae9fb9 100644
--- a/test/fixture/js/misc.js
+++ b/test/fixture/js/misc.js
@@ -2,4 +2,6 @@ module.exports = {
   'empty.http': [],
   'empty-urlencoded.http': [],
   'empty-multipart.http': [],
+  'empty-multipart2.http': [],
+  'minimal.http': [],
 };
diff --git a/test/fixture/js/no-filename.js b/test/fixture/js/no-filename.js
index c36e792..f03b4f0 100644
--- a/test/fixture/js/no-filename.js
+++ b/test/fixture/js/no-filename.js
@@ -1,7 +1,9 @@
 module.exports['generic.http'] = [
-  {type: 'file', name: 'upload', filename: '', fixture: 'plain.txt'},
+  {type: 'file', name: 'upload', filename: '', fixture: 'plain.txt',
+  sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'},
 ];
 
 module.exports['filename-name.http'] = [
-  {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt'},
+  {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt',
+  sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'},
 ];
diff --git a/test/fixture/js/preamble.js b/test/fixture/js/preamble.js
index da569b2..d2e4cfd 100644
--- a/test/fixture/js/preamble.js
+++ b/test/fixture/js/preamble.js
@@ -1,7 +1,9 @@
 module.exports['crlf.http'] = [
-  {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt'},
+  {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt',
+  sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'},
 ];
 
 module.exports['preamble.http'] = [
-  {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt'},
+  {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt',
+  sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'},
 ];
diff --git a/test/fixture/js/special-chars-in-filename.js b/test/fixture/js/special-chars-in-filename.js
index eb76fdc..f5eccda 100644
--- a/test/fixture/js/special-chars-in-filename.js
+++ b/test/fixture/js/special-chars-in-filename.js
@@ -5,7 +5,7 @@ function expect(filename) {
     {type: 'field', name: 'title', value: 'Weird filename'},
     {type: 'file', name: 'upload', filename: filename, fixture: properFilename},
   ];
-};
+}
 
 var webkit = " ? % * | \" < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt";
 var ffOrIe = " ? % * | \" < > . ☃ ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt";
diff --git a/test/fixture/js/workarounds.js b/test/fixture/js/workarounds.js
index d336505..e59c5b2 100644
--- a/test/fixture/js/workarounds.js
+++ b/test/fixture/js/workarounds.js
@@ -1,6 +1,8 @@
 module.exports['missing-hyphens1.http'] = [
-  {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt'},
+  {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt',
+  sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'},
 ];
 module.exports['missing-hyphens2.http'] = [
-  {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt'},
+  {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt',
+  sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'},
 ];
diff --git a/test/fixture/multi_video.upload b/test/fixture/multi_video.upload
new file mode 100644
index 0000000..9c82ba3
Binary files /dev/null and b/test/fixture/multi_video.upload differ
diff --git a/test/fixture/multipart.js b/test/fixture/multipart.js
index a476169..aa354e7 100644
--- a/test/fixture/multipart.js
+++ b/test/fixture/multipart.js
@@ -1,4 +1,4 @@
-exports['rfc1867'] =
+exports.rfc1867 =
   { boundary: 'AaB03x',
     raw:
       '--AaB03x\r\n'+
@@ -54,7 +54,7 @@ exports['noTrailing\r\n'] =
     ]
   };
 
-exports['emptyHeader'] =
+exports.emptyHeader =
   { boundary: 'AaB03x',
     raw:
       '--AaB03x\r\n'+
diff --git a/test/integration/test-fixtures.js b/test/integration/test-fixtures.js
index eb32fd8..6c03194 100644
--- a/test/integration/test-fixtures.js
+++ b/test/integration/test-fixtures.js
@@ -36,7 +36,7 @@ function testNext(fixtures) {
   if (!fixture) return server.close();
 
   var name    = fixture.name;
-  var fixture = fixture.fixture;
+  fixture = fixture.fixture;
 
   uploadFixture(name, function(err, parts) {
     if (err) throw err;
@@ -47,19 +47,21 @@ function testNext(fixtures) {
       assert.equal(parsedPart.name, expectedPart.name);
 
       if (parsedPart.type === 'file') {
-        var filename = parsedPart.value.name;
-        assert.equal(filename, expectedPart.filename);
+        var file = parsedPart.value;
+        assert.equal(file.name, expectedPart.filename);
+        if(expectedPart.sha1) assert.equal(file.hash, expectedPart.sha1);
       }
     });
 
     testNext(fixtures);
   });
-};
+}
 
 function uploadFixture(name, cb) {
   server.once('request', function(req, res) {
     var form = new formidable.IncomingForm();
     form.uploadDir = common.dir.tmp;
+    form.hash = "sha1";
     form.parse(req);
 
     function callback() {
diff --git a/test/legacy/integration/test-multipart-parser.js b/test/legacy/integration/test-multipart-parser.js
index 75232aa..45047fe 100644
--- a/test/legacy/integration/test-multipart-parser.js
+++ b/test/legacy/integration/test-multipart-parser.js
@@ -33,7 +33,7 @@ Object.keys(fixtures).forEach(function(name) {
 
   parser.onHeaderValue = function(b, start, end) {
     headerValue += b.toString('ascii', start, end);
-  }
+  };
 
   parser.onHeaderEnd = function() {
     part.headers[headerField] = headerValue;
@@ -44,13 +44,13 @@ Object.keys(fixtures).forEach(function(name) {
   parser.onPartData = function(b, start, end) {
     var str = b.toString('ascii', start, end);
     part.data += b.slice(start, end);
-  }
+  };
 
   parser.onEnd = function() {
     endCalled = true;
-  }
+  };
 
-  buffer.write(fixture.raw, 'binary', 0);
+  buffer.write(fixture.raw, 0, undefined, 'binary');
 
   while (offset < buffer.length) {
     if (offset + CHUNK_LENGTH < buffer.length) {
diff --git a/test/legacy/simple/test-incoming-form.js b/test/legacy/simple/test-incoming-form.js
index d79a8e2..ddb1325 100644
--- a/test/legacy/simple/test-incoming-form.js
+++ b/test/legacy/simple/test-incoming-form.js
@@ -38,7 +38,7 @@ test(function constructor() {
   assert.strictEqual(form.encoding, 'utf-8');
   assert.strictEqual(form.bytesReceived, null);
   assert.strictEqual(form.bytesExpected, null);
-  assert.strictEqual(form.maxFieldsSize, 2 * 1024 * 1024);
+  assert.strictEqual(form.maxFieldsSize, 20 * 1024 * 1024);
   assert.strictEqual(form._parser, null);
   assert.strictEqual(form._flushing, 0);
   assert.strictEqual(form._fieldsSize, 0);
@@ -62,17 +62,17 @@ test(function parse() {
   var REQ = {headers: {}}
     , emit = {};
 
-  var events = ['error', 'aborted', 'data', 'end'];
-  gently.expect(REQ, 'on', events.length, function(event, fn) {
-    assert.equal(event, events.shift());
-    emit[event] = fn;
-    return this;
-  });
-
   gently.expect(form, 'writeHeaders', function(headers) {
     assert.strictEqual(headers, REQ.headers);
   });
 
+  var EVENTS = ['error', 'aborted', 'data', 'end'];
+  gently.expect(REQ, 'on', EVENTS.length, function(event, fn) {
+    assert.equal(event, EVENTS.shift());
+    emit[event] = fn;
+    return this;
+  });
+
   form.parse(REQ);
 
   (function testPause() {
@@ -196,10 +196,6 @@ test(function parse() {
         REQ = {headers: {}},
         parseCalled = 0;
 
-    gently.expect(REQ, 'on', 4, function() {
-      return this;
-    });
-
     gently.expect(form, 'on', 4, function(event, fn) {
       if (event == 'field') {
         fn('field1', 'foo');
@@ -219,15 +215,17 @@ test(function parse() {
       return this;
     });
 
+    gently.expect(form, 'writeHeaders');
+
+    gently.expect(REQ, 'on', 4, function() {
+      return this;
+    });
+
     var parseCbOk = function (err, fields, files) {
       assert.deepEqual(fields, {field1: 'bar', field2: 'nice'});
       assert.deepEqual(files, {file1: '2', file2: '3'});
     };
-    gently.expect(form, 'writeHeaders');
     form.parse(REQ, parseCbOk);
-    gently.expect(REQ, 'on', 4, function() {
-      return this;
-    });
 
     var ERR = new Error('test');
     gently.expect(form, 'on', 3, function(event, fn) {
@@ -239,6 +237,9 @@ test(function parse() {
         fn(ERR);
         gently.expect(form, 'on');
         gently.expect(form, 'writeHeaders');
+        gently.expect(REQ, 'on', 4, function() {
+          return this;
+        });
       }
       return this;
     });
@@ -248,6 +249,25 @@ test(function parse() {
       assert.deepEqual(fields, {foo: 'bar'});
     });
   })();
+
+  (function testWriteOrder() {
+    gently.expect(EventEmitterStub, 'call');
+    var form    = new IncomingForm();
+    var REQ     = new events.EventEmitter();
+    var BUF     = {};
+    var DATACB  = null;
+
+    REQ.on('newListener', function(event, fn) {
+      if ('data' === event) fn(BUF);
+    });
+
+    gently.expect(form, 'writeHeaders');
+    gently.expect(form, 'write', function(buf) {
+      assert.strictEqual(buf, BUF);
+    });
+
+    form.parse(REQ);
+  })();
 });
 
 test(function pause() {
@@ -311,7 +331,7 @@ test(function write() {
     delete form._parser;
 
     gently.expect(form, '_error', function(err) {
-      assert.ok(err.message.match(/unintialized parser/i));
+      assert.ok(err.message.match(/uninitialized parser/i));
     });
     form.write(BUFFER);
   })();
@@ -378,9 +398,12 @@ test(function parseContentLength() {
   var HEADERS = {};
 
   form.headers = {};
+  gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) {
+    assert.equal(event, 'progress');
+    assert.equal(bytesReceived, 0);
+    assert.equal(bytesExpected, 0);
+  });
   form._parseContentLength();
-  assert.strictEqual(form.bytesReceived, null);
-  assert.strictEqual(form.bytesExpected, null);
 
   form.headers['content-length'] = '8';
   gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) {
@@ -545,7 +568,6 @@ test(function _initUrlencoded() {
 test(function _error() {
   var ERR = new Error('bla');
 
-  gently.expect(form, 'pause');
   gently.expect(form, 'emit', function(event, err) {
     assert.equal(event, 'error');
     assert.strictEqual(err, ERR);
diff --git a/test/legacy/simple/test-multipart-parser.js b/test/legacy/simple/test-multipart-parser.js
index bf2cd5e..c732d32 100644
--- a/test/legacy/simple/test-multipart-parser.js
+++ b/test/legacy/simple/test-multipart-parser.js
@@ -34,7 +34,7 @@ test(function parserError() {
       buffer = new Buffer(5);
 
   parser.initWithBoundary(boundary);
-  buffer.write('--ad', 'ascii', 0);
+  buffer.write('--ad', 0);
   assert.equal(parser.write(buffer), 5);
 });
 
diff --git a/test/legacy/system/test-multi-video-upload.js b/test/legacy/system/test-multi-video-upload.js
index a52db6a..b35ffd6 100644
--- a/test/legacy/system/test-multi-video-upload.js
+++ b/test/legacy/system/test-multi-video-upload.js
@@ -41,12 +41,12 @@ server.on('request', function(req, res) {
       assert.ok(uploads['shortest_video.flv']);
       assert.ok(uploads['shortest_video.flv'].ended);
       assert.ok(uploads['shortest_video.flv'].progress.length > 3);
-      assert.equal(uploads['shortest_video.flv'].file.hash, 'da39a3ee5e6b4b0d3255bfef95601890afd80709');
+      assert.equal(uploads['shortest_video.flv'].file.hash, 'd6a17616c7143d1b1438ceeef6836d1a09186b3a');
       assert.equal(uploads['shortest_video.flv'].progress.slice(-1), uploads['shortest_video.flv'].file.size);
       assert.ok(uploads['shortest_video.mp4']);
       assert.ok(uploads['shortest_video.mp4'].ended);
       assert.ok(uploads['shortest_video.mp4'].progress.length > 3);
-      assert.equal(uploads['shortest_video.mp4'].file.hash, 'da39a3ee5e6b4b0d3255bfef95601890afd80709');
+      assert.equal(uploads['shortest_video.mp4'].file.hash, '937dfd4db263f4887ceae19341dcc8d63bcd557f');
 
       server.close();
       res.writeHead(200);
diff --git a/test/run.js b/test/run.js
index 02d6d5c..887fab3 100755
--- a/test/run.js
+++ b/test/run.js
@@ -1 +1 @@
-require('urun')(__dirname)
+require('urun')(__dirname);
diff --git a/test/standalone/test-issue-46.js b/test/standalone/test-issue-46.js
index 1939328..519bf35 100644
--- a/test/standalone/test-issue-46.js
+++ b/test/standalone/test-issue-46.js
@@ -38,7 +38,7 @@ var server = http.createServer(function(req, res) {
 
   var parts  = [
     {'Content-Disposition': 'form-data; name="foo"', 'body': 'bar'}
-  ]
+  ];
 
   var req = request({method: 'POST', url: url, multipart: parts}, function(e, res, body) {
     var obj = JSON.parse(body);
diff --git a/test/standalone/test-keep-alive-error.js b/test/standalone/test-keep-alive-error.js
new file mode 100644
index 0000000..e0513cc
--- /dev/null
+++ b/test/standalone/test-keep-alive-error.js
@@ -0,0 +1,53 @@
+var assert = require('assert');
+var http = require('http');
+var net = require('net');
+var formidable = require('../../lib/index');
+
+var ok = 0;
+var errors = 0;
+
+var server = http.createServer(function (req, res) {
+  var form = new formidable.IncomingForm();
+  form.on('error', function (e) {
+    errors += 1;
+    res.writeHead(500);
+    res.end();
+  });
+  form.on('end', function () {
+    ok += 1;
+    res.writeHead(200);
+    res.end();
+  });
+  form.parse(req);
+}).listen(0, 'localhost', function () {
+  var client = net.createConnection(server.address().port);
+
+  // first send malformed post upload
+  client.write('POST /upload-test HTTP/1.1\r\n' +
+        'Host: localhost\r\n' +
+        'Connection: keep-alive\r\n' +
+        'Content-Type: multipart/form-data; boundary=----aaa\r\n' +
+        'Content-Length: 10011\r\n\r\n' +
+        '------aaa\n\r'); // expected \r\n
+
+  setTimeout(function () {
+    var buf = new Buffer(10000);
+    buf.fill('a');
+    client.write(buf);
+
+    // correct post upload
+    client.write('POST /upload-test HTTP/1.1\r\n' +
+        'Host: localhost\r\n' +
+        'Connection: keep-alive\r\n' +
+        'Content-Type: multipart/form-data; boundary=----aaa\r\n' +
+        'Content-Length: 13\r\n\r\n' +
+        '------aaa--\r\n');
+
+    setTimeout(function () {
+      assert(ok == 1);
+      assert(errors == 1);
+      client.end();
+      server.close();
+    }, 100);
+  }, 100);
+});
diff --git a/test/tmp/.empty b/test/tmp/.empty
new file mode 100644
index 0000000..e69de29
diff --git a/test/unit/test-file.js b/test/unit/test-file.js
index fc8f36e..1a5f044 100644
--- a/test/unit/test-file.js
+++ b/test/unit/test-file.js
@@ -4,7 +4,7 @@ var assert       = common.assert;
 var File = common.require('file');
 
 var file;
-var now = new Date;
+var now = new Date();
 test('IncomingForm', {
   before: function() {
     file = new File({
@@ -15,7 +15,7 @@ test('IncomingForm', {
       lastModifiedDate: now,
       filename: 'cat.png',
       mime: 'image/png'
-    })
+    });
   },
 
   '#toJSON()': function() {
@@ -30,4 +30,4 @@ test('IncomingForm', {
     assert.equal(now, obj.mtime);
     assert.equal(len, 8);
   }
-});
\ No newline at end of file
+});
diff --git a/test/unit/test-incoming-form.js b/test/unit/test-incoming-form.js
index fe2ac1c..e1b516d 100644
--- a/test/unit/test-incoming-form.js
+++ b/test/unit/test-incoming-form.js
@@ -47,14 +47,17 @@ test('IncomingForm', {
     var ext = path.extname(form._uploadPath('fine.jpg?foo=bar'));
     assert.equal(ext, '.jpg');
 
-    var ext = path.extname(form._uploadPath('fine?foo=bar'));
+    ext = path.extname(form._uploadPath('fine?foo=bar'));
     assert.equal(ext, '');
 
-    var ext = path.extname(form._uploadPath('super.cr2+dsad'));
+    ext = path.extname(form._uploadPath('super.cr2+dsad'));
     assert.equal(ext, '.cr2');
 
-    var ext = path.extname(form._uploadPath('super.bar'));
+    ext = path.extname(form._uploadPath('super.bar'));
     assert.equal(ext, '.bar');
+
+    ext = path.extname(form._uploadPath('file.aAa'));
+    assert.equal(ext, '.aAa');
   },
 });
 

--- End Message ---
--- Begin Message ---
Xavier:
> Package: release.debian.org
> Severity: normal
> User: release.debian.org@packages.debian.org
> Usertags: unblock
> 
> Please unblock package node-formidable
> 
> Hello,
> 
> node-formidable hasn't been updated for years. Testing version isn't
> compatible with our nodejs but this was not seen since there was no
> real tests (see https://bugs.debian.org/924589).
> 
> I upgraded it and added full tests (build and autopkgtest).
> 
> node-formidable reverse dependencies:
>  - node-superagent:
>    +- node-multiparty (no reverse dependencies)
> 
> node-formidable bug does not affect these packages since the failing
> functions are not used in it, that's why I decrease bug severity to
> important. I tested the 2 reverse dependencies with both old and new
> node-formidable with success (sbuild and autopkgtest).
> 
> debdiff is big since I embedded some node modules (only for tests, they
> are not installed). Upstream changes are not so big. I updated also
> debian/* files and added examples (and tests of course).
> 
> I think it's not risky to update node-formidable. However I leave you
> appreciate if it is opportune.
> 
> Cheers,
> Xavier
> 
> unblock node-formidable/1.2.1-1
> 
> [...]
> 

I have added an unblock hint for this.  Honestly, the changes were more
involved (or simply just harder for me to understand) than I hoped.  I
have granted an exception on the basis that the alternative would be
removing it (and we can still do that if this change does not work out).

That said, I would strongly prefer that we have strictly less involved
changes for the future.  I appreciate that additional tests being a
means to that end and they are the primary reason for my "leap of faith"
in this approval.

Thanks,
~Niels

--- End Message ---

Reply to: