First working end-to-end unzip test passing

This commit is contained in:
David Humphrey (:humph) david.humphrey@senecacollege.ca 2014-03-25 16:05:30 -04:00
parent 5a13dbfa4d
commit 3411ba2dd3
5 changed files with 147 additions and 174 deletions

View File

@ -44,15 +44,8 @@ define(function(require) {
var Deflater = require("zip.js/deflate"); var Deflater = require("zip.js/deflate");
var TEXT_PLAIN = "text/plain"; var TEXT_PLAIN = "text/plain";
var MESSAGE_EVENT = "message"; var MESSAGE_EVENT = "message";
var appendABViewSupported;
try {
appendABViewSupported = new Blob([ new DataView(new ArrayBuffer(0)) ]).size === 0;
} catch (e) {
}
function Crc32() { function Crc32() {
var crc = -1, that = this; var crc = -1, that = this;
that.append = function(data) { that.append = function(data) {
@ -78,17 +71,6 @@ define(function(require) {
return table; return table;
})(); })();
function blobSlice(blob, index, length) {
if (blob.slice)
return blob.slice(index, index + length);
else if (blob.webkitSlice)
return blob.webkitSlice(index, index + length);
else if (blob.mozSlice)
return blob.mozSlice(index, index + length);
else if (blob.msSlice)
return blob.msSlice(index, index + length);
}
function getDataHelper(byteLength, bytes) { function getDataHelper(byteLength, bytes) {
var dataBuffer, dataArray; var dataBuffer, dataArray;
dataBuffer = new ArrayBuffer(byteLength); dataBuffer = new ArrayBuffer(byteLength);
@ -103,171 +85,73 @@ define(function(require) {
} }
// Readers // Readers
function Reader() {
}
function TextReader(text) { // Filer fs-based File Reader, given a path and a fs instance
var that = this, blobReader; function FileReader(path, fs) {
function init(callback, onerror) {
var blob = new Blob([ text ], {
type : TEXT_PLAIN
});
blobReader = new BlobReader(blob);
blobReader.init(function() {
that.size = blobReader.size;
callback();
}, onerror);
}
function readUint8Array(index, length, callback, onerror) {
blobReader.readUint8Array(index, length, callback, onerror);
}
that.size = 0;
that.init = init;
that.readUint8Array = readUint8Array;
}
TextReader.prototype = new Reader();
TextReader.prototype.constructor = TextReader;
function Uint8Reader(u8) {
var that = this; var that = this;
function init(callback, onerror) { function init(callback, onerror) {
that.size = u8.length; fs.stat(path, function(err, stats) {
if(err) return onerror(err);
that.size = stats.size;
callback(); callback();
}
function readUint8Array(index, length, callback, onerror) {
callback(new Uint8Array(u8.subarray(index, index + length)));
}
that.size = 0;
that.init = init;
that.readUint8Array = readUint8Array;
}
Uint8Reader.prototype = new Reader();
Uint8Reader.prototype.constructor = Uint8Reader;
function BlobReader(blob) {
var that = this;
function init(callback) {
this.size = blob.size;
callback();
}
function readUint8Array(index, length, callback, onerror) {
var reader = new FileReader();
reader.onload = function(e) {
callback(new Uint8Array(e.target.result));
};
reader.onerror = onerror;
reader.readAsArrayBuffer(blobSlice(blob, index, length));
}
that.size = 0;
that.init = init;
that.readUint8Array = readUint8Array;
}
BlobReader.prototype = new Reader();
BlobReader.prototype.constructor = BlobReader;
// Writers
function Writer() {
}
Writer.prototype.getData = function(callback) {
callback(this.data);
};
function TextWriter(encoding) {
var that = this, blob;
function init(callback) {
blob = new Blob([], {
type : TEXT_PLAIN
}); });
callback();
}
function writeUint8Array(array, callback) {
blob = new Blob([ blob, appendABViewSupported ? array : array.buffer ], {
type : TEXT_PLAIN
});
callback();
} }
function getData(callback, onerror) { function getData(callback, onerror) {
var reader = new FileReader(); if(that.data) {
reader.onload = function(e) { callback();
callback(e.target.result); return;
};
reader.onerror = onerror;
reader.readAsText(blob, encoding);
} }
fs.readFile(path, function(err, data) {
if(err) return onerror(err);
that.data = data;
callback();
});
}
function readUint8Array(index, length, callback, onerror) {
getData(function() {
callback(that.data.subarray(index, index + length));
}, onerror);
}
that.size = 0;
that.init = init; that.init = init;
that.writeUint8Array = writeUint8Array; that.readUint8Array = readUint8Array;
that.getData = getData;
} }
TextWriter.prototype = new Writer(); FileReader.prototype.checkCrc32 = false;
TextWriter.prototype.constructor = TextWriter;
function Uint8Writer() { // Filer fs-based File Writer
var that = this, u8; function FileWriter(path, fs, size) {
var that = this;
function init(callback) { function init(callback) {
u8 = new Uint8Array(); // TODO: Overwrite the file. Should we also have a way to fail if exists?
fs.unlink(path, function() {
callback(); callback();
} });
function writeUint8Array(array, callback) {
var both = new Uint8Array(u8.length + array.length);
both.set(u8);
both.set(array, u8.length);
u8 = both;
} }
function getData(callback) { function getData(callback) {
callback(u8); callback(that.data);
} }
function writeUint8Array(array, callback, onerror) {
fs.appendFile(path, array, function(err) {
if(err) return onerror(err);
that.data = array;
callback();
});
}
that.size = size;
that.init = init; that.init = init;
that.writeUint8Array = writeUint8Array; that.writeUint8Array = writeUint8Array;
that.getData = getData; that.getData = getData;
} }
Uint8Writer.prototype = new Writer();
Uint8Writer.prototype.constructor = Uint8Writer;
function BlobWriter(contentType) {
var blob, that = this;
function init(callback) {
blob = new Blob([], {
type : contentType
});
callback();
}
function writeUint8Array(array, callback) {
blob = new Blob([ blob, appendABViewSupported ? array : array.buffer ], {
type : contentType
});
callback();
}
function getData(callback) {
callback(blob);
}
that.init = init;
that.writeUint8Array = writeUint8Array;
that.getData = getData;
}
BlobWriter.prototype = new Writer();
BlobWriter.prototype.constructor = BlobWriter;
// inflate/deflate core functions // inflate/deflate core functions
function launchProcess(process, reader, writer, offset, size, onappend, onprogress, onend, onreaderror, onwriteerror) { function launchProcess(process, reader, writer, offset, size, onappend, onprogress, onend, onreaderror, onwriteerror) {
@ -309,7 +193,7 @@ define(function(require) {
} }
function inflate(reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) { function inflate(reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) {
var worker, crc32 = new Crc32(); var crc32 = new Crc32();
function oninflateappend(sending, array) { function oninflateappend(sending, array) {
if (computeCrc32 && !sending) if (computeCrc32 && !sending)
@ -321,11 +205,11 @@ define(function(require) {
} }
launchProcess(new Inflater(), reader, writer, offset, size, oninflateappend, onprogress, oninflateend, onreaderror, onwriteerror); launchProcess(new Inflater(), reader, writer, offset, size, oninflateappend, onprogress, oninflateend, onreaderror, onwriteerror);
return worker; return;
} }
function deflate(reader, writer, level, onend, onprogress, onreaderror, onwriteerror) { function deflate(reader, writer, level, onend, onprogress, onreaderror, onwriteerror) {
var worker, crc32 = new Crc32(); var crc32 = new Crc32();
function ondeflateappend(sending, array) { function ondeflateappend(sending, array) {
if (sending) if (sending)
@ -337,7 +221,7 @@ define(function(require) {
} }
launchProcess(new Deflater(), reader, writer, 0, reader.size, ondeflateappend, onprogress, ondeflateend, onreaderror, onwriteerror); launchProcess(new Deflater(), reader, writer, 0, reader.size, ondeflateappend, onprogress, ondeflateend, onreaderror, onwriteerror);
return worker; return;
} }
function copy(reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) { function copy(reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) {
@ -472,7 +356,10 @@ define(function(require) {
readCommonHeader(that, data, 4, false, onerror); readCommonHeader(that, data, 4, false, onerror);
dataOffset = that.offset + 30 + that.filenameLength + that.extraFieldLength; dataOffset = that.offset + 30 + that.filenameLength + that.extraFieldLength;
writer.init(function() { writer.init(function() {
if (that.compressionMethod === 0)
copy(reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror); copy(reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror);
else
inflate(reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror);
}, onwriteerror); }, onwriteerror);
}, onreaderror); }, onreaderror);
}; };
@ -676,14 +563,8 @@ define(function(require) {
} }
return { return {
Reader : Reader, FileReader : FileReader,
Writer : Writer, FileWriter : FileWriter,
BlobReader : BlobReader,
Uint8Reader : Uint8Reader,
TextReader : TextReader,
BlobWriter : BlobWriter,
Uint8Writer : Uint8Writer,
TextWriter : TextWriter,
createReader : function(reader, callback, onerror) { createReader : function(reader, callback, onerror) {
reader.init(function() { reader.init(function() {
callback(createZipReader(reader, onerror)); callback(createZipReader(reader, onerror));

View File

@ -476,6 +476,44 @@ define(function(require) {
request.send(); request.send();
}; };
Shell.prototype.unzip = function(path, options, callback) {
var fs = this.fs;
if(typeof options === 'function') {
callback = options;
options = {};
}
options = options || {};
callback = callback || function(){};
if(!path) {
callback(new Errors.EINVAL('missing path argument'));
return;
}
path = Path.resolve(this.cwd, path);
var destination = Path.resolve(options.destination || this.cwd);
var Zip = require('zip.js/zip');
Zip.createReader(new Zip.FileReader(path, fs), function(reader) {
reader.getEntries(function(entries) {
function inflate(entry, callback) {
var path = Path.join(destination, entry.filename);
if(entry.directory) {
this.mkdirp(path, callback);
} else {
entry.getData(new Zip.FileWriter(path, fs, entry.uncompressedSize), function() { callback(); });
}
}
async.eachSeries(entries, inflate, function(error) {
if(error) return callback(error);
reader.close(callback);
});
});
}, callback);
};
return Shell; return Shell;
}); });

View File

@ -0,0 +1,53 @@
define(["Filer", "util"], function(Filer, util) {
describe('FileSystemShell.unzip', function() {
beforeEach(util.setup);
afterEach(util.cleanup);
it('should be a function', function() {
var shell = util.shell();
expect(shell.unzip).to.be.a('function');
});
it('should fail when path argument is absent', function(done) {
var fs = util.fs();
var shell = fs.Shell();
shell.unzip(null, function(err) {
expect(err).to.exist;
done();
});
});
it('should download and unzip the contents of a zip file', function(done) {
var fs = util.fs();
var shell = fs.Shell();
var url = "test-file.txt.zip";
var contents = "This is a test file used in some of the tests.\n";
fs.writeFile('/original', contents, function(err) {
if(err) throw err;
shell.wget(url, {filename: url}, function(err, path) {
if(err) throw err;
shell.unzip(path, function(err) {
if(err) throw err;
fs.readFile('/original', function(err, originalData) {
if(err) throw err;
fs.readFile('/test-file.txt', function(err, data) {
if(err) throw err;
expect(util.typedArrayEqual(data, originalData)).to.be.true;
done();
});
});
});
});
});
});
});
});

BIN
tests/test-file.txt.zip Normal file

Binary file not shown.

View File

@ -58,6 +58,7 @@ define([
"spec/shell/env.spec", "spec/shell/env.spec",
"spec/shell/mkdirp.spec", "spec/shell/mkdirp.spec",
"spec/shell/wget.spec", "spec/shell/wget.spec",
"spec/shell/unzip.spec",
// Ported node.js tests (filenames match names in https://github.com/joyent/node/tree/master/test) // Ported node.js tests (filenames match names in https://github.com/joyent/node/tree/master/test)
"spec/node-js/simple/test-fs-mkdir", "spec/node-js/simple/test-fs-mkdir",