'use strict'; const common = require('../common'); const assert = require('assert'); const zlib = require('zlib'); const path = require('path'); const fs = require('fs'); const util = require('util'); const stream = require('stream'); var zlibPairs = [ [zlib.Deflate, zlib.Inflate], [zlib.Gzip, zlib.Gunzip], [zlib.Deflate, zlib.Unzip], [zlib.Gzip, zlib.Unzip], [zlib.DeflateRaw, zlib.InflateRaw] ]; // how fast to trickle through the slowstream var trickle = [128, 1024, 1024 * 1024]; // tunable options for zlib classes. // several different chunk sizes var chunkSize = [128, 1024, 1024 * 16, 1024 * 1024]; // this is every possible value. var level = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; var windowBits = [8, 9, 10, 11, 12, 13, 14, 15]; var memLevel = [1, 2, 3, 4, 5, 6, 7, 8, 9]; var strategy = [0, 1, 2, 3, 4]; // it's nice in theory to test every combination, but it // takes WAY too long. Maybe a pummel test could do this? if (!process.env.PUMMEL) { trickle = [1024]; chunkSize = [1024 * 16]; level = [6]; memLevel = [8]; windowBits = [15]; strategy = [0]; } var testFiles = ['person.jpg', 'elipses.txt', 'empty.txt']; if (process.env.FAST) { zlibPairs = [[zlib.Gzip, zlib.Unzip]]; testFiles = ['person.jpg']; } const tests = {}; testFiles.forEach(function(file) { tests[file] = fs.readFileSync(path.resolve(common.fixturesDir, file)); }); // stream that saves everything function BufferStream() { this.chunks = []; this.length = 0; this.writable = true; this.readable = true; } util.inherits(BufferStream, stream.Stream); BufferStream.prototype.write = function(c) { this.chunks.push(c); this.length += c.length; return true; }; BufferStream.prototype.end = function(c) { if (c) this.write(c); // flatten var buf = Buffer.allocUnsafe(this.length); var i = 0; this.chunks.forEach(function(c) { c.copy(buf, i); i += c.length; }); this.emit('data', buf); this.emit('end'); return true; }; function SlowStream(trickle) { this.trickle = trickle; this.offset = 0; this.readable = this.writable = true; } util.inherits(SlowStream, stream.Stream); SlowStream.prototype.write = function() { throw new Error('not implemented, just call ss.end(chunk)'); }; SlowStream.prototype.pause = function() { this.paused = true; this.emit('pause'); }; SlowStream.prototype.resume = function() { const emit = () => { if (this.paused) return; if (this.offset >= this.length) { this.ended = true; return this.emit('end'); } var end = Math.min(this.offset + this.trickle, this.length); var c = this.chunk.slice(this.offset, end); this.offset += c.length; this.emit('data', c); process.nextTick(emit); }; if (this.ended) return; this.emit('resume'); if (!this.chunk) return; this.paused = false; emit(); }; SlowStream.prototype.end = function(chunk) { // walk over the chunk in blocks. this.chunk = chunk; this.length = chunk.length; this.resume(); return this.ended; }; // for each of the files, make sure that compressing and // decompressing results in the same data, for every combination // of the options set above. var failures = 0; var total = 0; var done = 0; Object.keys(tests).forEach(function(file) { var test = tests[file]; chunkSize.forEach(function(chunkSize) { trickle.forEach(function(trickle) { windowBits.forEach(function(windowBits) { level.forEach(function(level) { memLevel.forEach(function(memLevel) { strategy.forEach(function(strategy) { zlibPairs.forEach(function(pair) { var Def = pair[0]; var Inf = pair[1]; var opts = { level: level, windowBits: windowBits, memLevel: memLevel, strategy: strategy }; total++; var def = new Def(opts); var inf = new Inf(opts); var ss = new SlowStream(trickle); var buf = new BufferStream(); // verify that the same exact buffer comes out the other end. buf.on('data', function(c) { var msg = file + ' ' + chunkSize + ' ' + JSON.stringify(opts) + ' ' + Def.name + ' -> ' + Inf.name; var ok = true; var testNum = ++done; for (var i = 0; i < Math.max(c.length, test.length); i++) { if (c[i] !== test[i]) { ok = false; failures++; break; } } if (ok) { console.log('ok ' + (testNum) + ' ' + msg); } else { console.log('not ok ' + (testNum) + ' ' + msg); console.log(' ...'); console.log(' testfile: ' + file); console.log(' type: ' + Def.name + ' -> ' + Inf.name); console.log(' position: ' + i); console.log(' options: ' + JSON.stringify(opts)); console.log(' expect: ' + test[i]); console.log(' actual: ' + c[i]); console.log(' chunkSize: ' + chunkSize); console.log(' ---'); } }); // the magic happens here. ss.pipe(def).pipe(inf).pipe(buf); ss.end(test); }); }); }); }); }); }); }); }); process.on('exit', function(code) { console.log('1..' + done); assert.strictEqual(done, total, (total - done) + ' tests left unfinished'); assert.strictEqual(failures, 0, 'some test failures'); });