From 4d871a39f5da0c818534de47c44f0badc3926aa5 Mon Sep 17 00:00:00 2001 From: Qvazar Date: Wed, 26 Aug 2015 16:45:13 +0200 Subject: [PATCH] test running, but web worker isn't being called? --- gulpfile.js | 15 +- karma.conf.js | 69 ++++++++ package.json | 11 +- spec/{empty.js => empty-spec.js} | 0 spec/untar-spec.js | 18 +++ spec/untar.js | 9 -- test-main.js | 24 +++ untar-worker.js | 270 +++++++++++++++---------------- untar.js | 111 +++++++++++-- 9 files changed, 361 insertions(+), 166 deletions(-) create mode 100644 karma.conf.js rename spec/{empty.js => empty-spec.js} (100%) create mode 100644 spec/untar-spec.js delete mode 100644 spec/untar.js create mode 100644 test-main.js diff --git a/gulpfile.js b/gulpfile.js index 59b4779..6ac27dc 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -6,7 +6,7 @@ var gulp = require("gulp"), addSrc = require("gulp-add-src"), concat = require("gulp-concat"), jshint = require("gulp-jshint"), - jasmine = require("gulp-jasmine"); + KarmaServer = require('karma').Server; gulp.task("default", function() { return gulp.src("untar-worker.js") @@ -16,7 +16,7 @@ gulp.task("default", function() { .pipe(jshint.reporter("fail")) .pipe(uglify()) .pipe(insert.transform(function(contents, file) { - var str = ["\nworkerScriptUri = URL.createObjectURL(new Blob([\""]; + var str = ["\nvar workerScriptUri = URL.createObjectURL(createBlob([\""]; str.push(contents.replace(/"/g, '\\"')); str.push("\"]));"); @@ -37,10 +37,9 @@ gulp.task("default", function() { .pipe(gulp.dest("build/dist")); }); -gulp.task("test", ["default"], function() { - return gulp.src("spec/*.js") - .pipe(jasmine({ - includeStackTrace: true, - verbose: true - })); +gulp.task("test", ["default"], function(done) { + new KarmaServer({ + configFile: __dirname + '/karma.conf.js', + singleRun: true + }, done).start(); }); \ No newline at end of file diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..e2e2c52 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,69 @@ +// Karma configuration +// Generated on Wed Aug 26 2015 11:44:53 GMT+0200 (Vesteuropa, sommertid) + +module.exports = function(config) { + config.set({ + + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: '', + + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['jasmine', 'requirejs'], + + + // list of files / patterns to load in the browser + files: [ + 'https://www.promisejs.org/polyfills/promise-6.1.0.js', + 'test-main.js', + {pattern: 'build/dev/**/*.js', included: false}, + {pattern: 'spec/**/*.*', included: false} + ], + + + // list of files to exclude + exclude: [ + ], + + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + }, + + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['progress'], + + + // web server port + port: 9876, + + + // enable / disable colors in the output (reporters and logs) + colors: true, + + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: false, + + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['PhantomJS'], + + browserNoActivityTimeout: 60000, + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: false + }) +} diff --git a/package.json b/package.json index 629aa7c..b392a05 100644 --- a/package.json +++ b/package.json @@ -26,14 +26,21 @@ }, "homepage": "https://github.com/Qvazar/js-untar", "devDependencies": { + "es6-promise": "^3.0.2", "gulp": "^3.9.0", "gulp-add-src": "^0.2.0", "gulp-concat": "^2.6.0", "gulp-insert": "^0.5.0", - "gulp-jasmine": "^2.0.1", "gulp-jshint": "^1.11.2", "gulp-sourcemaps": "^1.5.2", "gulp-uglify": "^1.3.0", - "gulp-umd": "^0.2.0" + "gulp-umd": "^0.2.0", + "jasmine-core": "^2.3.4", + "karma": "^0.13.9", + "karma-jasmine": "^0.3.6", + "karma-phantomjs-launcher": "^0.2.1", + "karma-requirejs": "^0.2.2", + "phantomjs": "^1.9.18", + "requirejs": "^2.1.20" } } diff --git a/spec/empty.js b/spec/empty-spec.js similarity index 100% rename from spec/empty.js rename to spec/empty-spec.js diff --git a/spec/untar-spec.js b/spec/untar-spec.js new file mode 100644 index 0000000..ed9803d --- /dev/null +++ b/spec/untar-spec.js @@ -0,0 +1,18 @@ +define(["build/dev/untar"], function(untar) { + describe("untar", function() { + it("should unpack 3 files and a directory with 3 files", function(done) { + untar("/base/spec/data/test.tar", { + onExtract: function(file) { done(); } + }).then( + function(files) { + expect(files.length).toBe(6); + done(); + }, + function(err) { + done.fail(JSON.stringify(err)); + } + ); + }, 20000); + }); +}); + diff --git a/spec/untar.js b/spec/untar.js deleted file mode 100644 index 1ac8bf8..0000000 --- a/spec/untar.js +++ /dev/null @@ -1,9 +0,0 @@ -var untar = require("../build/dev/untar.js"); - -describe("untar", function() { - it("should unpack 3 files and a directory with 3 files", function() { - untar("data/test.tar").then(function(files) { - expect(files.length).toBe(6); - }); - }); -}) \ No newline at end of file diff --git a/test-main.js b/test-main.js new file mode 100644 index 0000000..c2173ad --- /dev/null +++ b/test-main.js @@ -0,0 +1,24 @@ +var allTestFiles = []; +var TEST_REGEXP = /(spec|test)\.js$/i; + +// Get a list of all the test files to include +Object.keys(window.__karma__.files).forEach(function(file) { + if (TEST_REGEXP.test(file)) { + // Normalize paths to RequireJS module names. + // If you require sub-dependencies of test files to be loaded as-is (requiring file extension) + // then do not normalize the paths + var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, ''); + allTestFiles.push(normalizedTestModule); + } +}); + +require.config({ + // Karma serves files under /base, which is the basePath from your config file + baseUrl: '/base', + + // dynamically load all test files + deps: allTestFiles, + + // we have to kickoff jasmine, as it is asynchronous + callback: window.__karma__.start +}); diff --git a/untar-worker.js b/untar-worker.js index 195229c..8b4b432 100644 --- a/untar-worker.js +++ b/untar-worker.js @@ -1,169 +1,163 @@ -(function() { - "use strict"; +/* globals postMessage: false, DataView: false, self: false */ +/* jshint -W097 */ +"use strict"; - onmessage = function(source) { - if (typeof source === "string") { - loadArrayBuffer(source).then( - untarBuffer, - function(err) { postMessage({ type: "error", data: err }); } - ); +self.onmessage = function(msg) { + postLog("info", "Received message."); + try { + if (msg.data.type === "extract") { + untarBuffer(msg.data.buffer); } else { - untarBuffer(source); + throw new Error("Unknown message type."); } - }; - - function loadArrayBuffer(uri) { - return new Promise(function(resolve, reject) { - try { - var request = new XMLHttpRequest(); - - request.addEventListener("progress", function(e) { - postMessage({ type: "loading", data: e }); - }); - - request.addEventListener("load", function(e) { - resolve(request.response); - }); - - request.addEventListener("error", reject); - request.addEventListener("abort", reject); - - request.open("GET", uri); - request.responseType = "arraybuffer"; - request.send(); - } catch (err) { - reject(err); - } - }); + } catch (err) { + postError(err); } +}; - function untarBuffer(arrayBuffer) { - try { - var tarFileStream = new TarFileStream(arrayBuffer); - while (tarFileStream.hasNext()) { - var file = tarFileStream.next(); +function postError(err) { + postMessage({ type: "error", data: err }); +} +function postLog(level, msg) { + postMessage({ type: "log", data: { level: level, msg: msg }}); +} + +function untarBuffer(arrayBuffer) { + try { + postLog("info", "buffer size: " + arrayBuffer.byteLength); + var tarFileStream = new TarFileStream(arrayBuffer); + while (tarFileStream.hasNext()) { + var file = tarFileStream.next(); + + if (file.buffer) { postMessage({ type: "extract", data: file }, [file.buffer]); } - - postMessage({ type: "complete" }); - } catch (err) { - postMessage({ type: "error", data: err }); } + + postMessage({ type: "complete" }); + } catch (err) { + postError(err); } +} - function TarFile() { +function TarFile() { - } +} - TarFile.prototype = { +TarFile.prototype = { - }; +}; - function Stream(arrayBuffer) { - this._bufferView = new DataView(arrayBuffer); - this._position = 0; - } +function Stream(arrayBuffer) { + this._bufferView = new DataView(arrayBuffer); + this._position = 0; +} - Stream.prototype = { - readString: function(charCount) { - var charSize = 1; - var byteCount = charCount * charSize; +Stream.prototype = { + readString: function(charCount) { + var charSize = 1; + var byteCount = charCount * charSize; - var charCodes = []; + var charCodes = []; - for (var i = 0; i < charCount; ++i) { - var charCode = this._bufferView.getUint8(this.position() + (i * charSize)); - if (charCode !== 0) { - charCodes.push(charCode); - } else { - break; - } - } - - this.seek(byteCount); - - return String.fromCharCode.apply(null, charCodes); - }, - - readBuffer: function(byteCount) { - return this._bufferView.buffer.slice(this._position, byteCount); - }, - - seek: function(byteCount) { - this._position += byteCount; - }, - - peekUint32: function() { - return this._bufferView.getUint32(this.position()); - }, - - position: function(newpos) { - if (newpos === undefined) { - return this._position; + for (var i = 0; i < charCount; ++i) { + var charCode = this._bufferView.getUint8(this.position() + (i * charSize)); + if (charCode !== 0) { + charCodes.push(charCode); } else { - this._position = newpos; + break; } } - }; - function TarFileStream(arrayBuffer) { - this._stream = new Stream(arrayBuffer); + this.seek(byteCount); + + return String.fromCharCode.apply(null, charCodes); + }, + + readBuffer: function(byteCount) { + return this._bufferView.buffer.slice(this._position, byteCount); + }, + + seek: function(byteCount) { + this._position += byteCount; + }, + + peekUint32: function() { + return this._bufferView.getUint32(this.position()); + }, + + position: function(newpos) { + if (newpos === undefined) { + return this._position; + } else { + this._position = newpos; + } + }, + + size: function() { + return this._bufferView.byteLength; } +}; - TarFileStream.prototype = { - hasNext: function() { - return this._stream.peekUint32() !== 0; - }, +function TarFileStream(arrayBuffer) { + this._stream = new Stream(arrayBuffer); +} - next: function() { - var stream = this._stream; - var file = new TarFile(); +TarFileStream.prototype = { + hasNext: function() { + return this._stream.position() < this._stream.size() && this._stream.peekUint32() !== 0; + }, - var headerBeginPos = stream.position; - var dataBeginPos = headerBeginPos + 512; + next: function() { + var stream = this._stream; + var file = new TarFile(); - // Read header - file.name = stream.readString(100); - file.mode = stream.readString(8); - file.uid = stream.readString(8); - file.gid = stream.readString(8); - file.size = parseInt(stream.readString(12), 8); - file.modificationTime = parseInt(stream.readString(12), 8); - file.checksum = stream.readString(8); - file.type = stream.readString(1); - file.linkname = stream.readString(1); - file.ustarFormat = stream.readString(6); + var headerBeginPos = stream.position; + var dataBeginPos = headerBeginPos + 512; - if (file.ustarFormat === "ustar") { - file.version = stream.readString(2); - file.uname = stream.readString(32); - file.gname = stream.readString(32); - file.devmajor = stream.readString(8); - file.devminor = stream.readString(8); - file.namePrefix = stream.readString(155); + // Read header + file.name = stream.readString(100); + file.mode = stream.readString(8); + file.uid = stream.readString(8); + file.gid = stream.readString(8); + file.size = parseInt(stream.readString(12), 8); + file.modificationTime = parseInt(stream.readString(12), 8); + file.checksum = stream.readString(8); + file.type = stream.readString(1); + file.linkname = stream.readString(1); + file.ustarFormat = stream.readString(6); - if (file.namePrefix.length > 0) { - file.name = file.namePrefix + file.name; - } + if (file.ustarFormat === "ustar") { + file.version = stream.readString(2); + file.uname = stream.readString(32); + file.gname = stream.readString(32); + file.devmajor = stream.readString(8); + file.devminor = stream.readString(8); + file.namePrefix = stream.readString(155); + + if (file.namePrefix.length > 0) { + file.name = file.namePrefix + file.name; } - - stream.position(dataBeginPos); - - // Normal file is either "\0" or 0. - if (file.type === 0 || file.type === "\0") { - file.buffer = stream.readBuffer(file.size); - } else if (file.type == 5) { - // Directory - should we do anything with this? Nope! - } else { - // We only care about real files, not symlinks. - } - - // File data is padded to reach a 512 byte boundary; skip the padded bytes. - var bytesToSkipCount = 512 - file.size % 512; - stream.seek(bytesToSkipCount); - - return file; } - }; -}()); + + stream.position(dataBeginPos); + + // Normal file is either "\0" or 0. + if (file.type === 0 || file.type === "\0") { + file.buffer = stream.readBuffer(file.size); + } else if (file.type == 5) { + // Directory - should we do anything with this? Nope! + } else { + // We only care about real files, not symlinks. + } + + // File data is padded to reach a 512 byte boundary; skip the padded bytes. + var bytesToSkipCount = 512 - file.size % 512; + stream.seek(bytesToSkipCount); + + return file; + } +}; + +postMessage({ type: "ready" }); \ No newline at end of file diff --git a/untar.js b/untar.js index 023f598..cf89724 100644 --- a/untar.js +++ b/untar.js @@ -1,5 +1,73 @@ +/* globals window: false, Blob: false, Promise: false, console: false, XMLHttpRequest: false, Worker: false */ +/* jshint -W097 */ +"use strict"; + var workerScriptUri; // Included at compile time +var URL = window.URL || window.webkitURL; + +var createBlob = (function() { + if (typeof window.Blob === "function") { + return function(dataArray) { return new Blob(dataArray); }; + } else { + var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder; + + return function(dataArray) { + var builder = new BlobBuilder(); + + for (var i = 0; i < dataArray.length; ++i) { + builder.append(dataArray[i]); + } + + return builder.getBlob(); + }; + } +}()); + +function createBlob(dataArray) { + if (typeof window.Blob === "function") { + return new Blob(dataArray); + } else { + var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder; + var builder = new BlobBuilder(); + + for (var i = 0; i < dataArray.length; ++i) { + builder.append(dataArray[i]); + } + + return builder.getBlob(); + } +} + +function loadArrayBuffer(uri) { + console.info("loadArrayBuffer called"); + + return new Promise(function(resolve, reject) { + var request = new XMLHttpRequest(); + + /* + request.addEventListener("progress", function(e) { + postMessage({ type: "loading", data: e }); + }); + */ + + request.addEventListener("load", function(e) { + if (request.status >= 200 && request.status < 400) { + resolve(request.response); + } else { + reject(new Error(request.status + " " + request.statusText)); + } + }); + + request.addEventListener("error", function(err) { reject(err); }); + request.addEventListener("abort", function(err) { reject(err); }); + + request.open("GET", uri, true); + request.responseType = "arraybuffer"; + request.send(); + }); +} + /** source = ArrayBuffer or a url string. If an ArrayBuffer, it will be transfered to the web worker and will thus not be available in the window after. options = { @@ -10,12 +78,13 @@ options = { } */ function untar(source, options) { - "use strict"; + console.info("untar called"); + if (typeof Promise !== "function") { throw new Error("Promise implementation not available in this environment."); } - if (typeof Worker !== "function") { + if (!window.Worker) { throw new Error("Worker implementation not available in this environment."); } @@ -29,11 +98,40 @@ function untar(source, options) { var onError = options.onError || noop; var worker = new Worker(workerScriptUri); + + function initWorker() { + // Is source a string? Then assume it's a URL and download it. + if (typeof source === "string") { + loadArrayBuffer(source).then( + function(buffer) { + console.info("Loaded tar file, extracting."); + worker.postMessage({ type: "extract", buffer: buffer }, [buffer]); + }, + function(err) { + onError(err); + reject(err); + } + ); + } else { + console.info("Extracting tar file."); + worker.postMessage({ type: "extract", buffer: source }, [source]); + } + } + var files = []; var msgData; worker.onmessage = function(message) { + message = message.data; + switch (message.type) { + case "ready": + console.info("Worker is ready."); + initWorker(); + break; + case "log": + console[message.data.level]("Worker: " + message.data.msg); + break; case "loading": onLoading(message.data); break; @@ -52,26 +150,22 @@ function untar(source, options) { reject(msgData); break; default: - msgData = new Error("Unknown message from worker."); + msgData = new Error("Unknown message from worker: " + message.type); onError(msgData); reject(msgData); break; } }; - - // Don't transfer if source is a string. Only ArrayBuffer can be transfered. - worker.postMessage(source, (typeof source === "string" ? undefined : [source])); }); } function TarFile(orig) { - "use strict"; this._blobUrl = null; for (var p in orig) { switch (p) { case "buffer": - this.blob = new Blob([orig.buffer]); + this.blob = createBlob([orig.buffer]); break; default: this[p] = orig[p]; @@ -82,7 +176,6 @@ function TarFile(orig) { TarFile.prototype = { getObjectUrl: function() { - "use strict"; if (!this._blobUrl) { this._blobUrl = URL.createObjectURL(this.blob); }