Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: improve zlib tests #14455

Closed
wants to merge 10 commits into from
8 changes: 5 additions & 3 deletions test/parallel/test-zlib-from-gzip-with-trailing-garbage.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ assert.throws(
);

zlib.gunzip(data, common.mustCall((err, result) => {
assert(err instanceof Error);
assert.strictEqual(err.code, 'Z_DATA_ERROR');
assert.strictEqual(err.message, 'unknown compression method');
common.expectsError({
code: 'Z_DATA_ERROR',
type: Error,
message: 'unknown compression method'
})(err);
assert.strictEqual(result, undefined);
}));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loses the check that result is undefined. I'd prefer leaving the old code. I don't think common.expectsError() adds significant value, but the check for result is in fact significant.


Expand Down
4 changes: 2 additions & 2 deletions test/parallel/test-zlib-from-gzip.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ const inp = fs.createReadStream(fixture);
const out = fs.createWriteStream(outputFile);

inp.pipe(gunzip).pipe(out);
out.on('close', function() {
out.on('close', common.mustCall(() => {
const actual = fs.readFileSync(outputFile);
assert.strictEqual(actual.length, expect.length, 'length should match');
for (let i = 0, l = actual.length; i < l; i++) {
assert.strictEqual(actual[i], expect[i], `byte[${i}]`);
}
});
}));
22 changes: 11 additions & 11 deletions test/parallel/test-zlib-from-string.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
'use strict';
// test compressing and uncompressing a string with zlib

require('../common');
const common = require('../common');
const assert = require('assert');
const zlib = require('zlib');

Expand Down Expand Up @@ -54,32 +54,32 @@ const expectedBase64Gzip = 'H4sIAAAAAAAAA11RS05DMQy8yhzg6d2BPSAkJPZu4laWkjiN4' +
'mHo33kJO8xfkckmLjE5XMKBQ4gxIsfvCZ44doUThF2mcZq8q2' +
'sHnHNzRtagj5AQAA';

zlib.deflate(inputString, function(err, buffer) {
zlib.deflate(inputString, common.mustCall((err, buffer) => {
assert.strictEqual(buffer.toString('base64'), expectedBase64Deflate,
'deflate encoded string should match');
});
}));

zlib.gzip(inputString, function(err, buffer) {
zlib.gzip(inputString, common.mustCall((err, buffer) => {
// Can't actually guarantee that we'll get exactly the same
// deflated bytes when we compress a string, since the header
// depends on stuff other than the input string itself.
// However, decrypting it should definitely yield the same
// result that we're expecting, and this should match what we get
// from inflating the known valid deflate data.
zlib.gunzip(buffer, function(err, gunzipped) {
zlib.gunzip(buffer, common.mustCall((err, gunzipped) => {
assert.strictEqual(gunzipped.toString(), inputString,
'Should get original string after gzip/gunzip');
});
});
}));
}));

let buffer = Buffer.from(expectedBase64Deflate, 'base64');
zlib.unzip(buffer, function(err, buffer) {
zlib.unzip(buffer, common.mustCall((err, buffer) => {
assert.strictEqual(buffer.toString(), inputString,
'decoded inflated string should match');
});
}));

buffer = Buffer.from(expectedBase64Gzip, 'base64');
zlib.unzip(buffer, function(err, buffer) {
zlib.unzip(buffer, common.mustCall((err, buffer) => {
assert.strictEqual(buffer.toString(), inputString,
'decoded gunzipped string should match');
});
}));
48 changes: 21 additions & 27 deletions test/parallel/test-zlib-invalid-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,45 +22,39 @@
'use strict';
// test uncompressing invalid input

require('../common');
const common = require('../common');
const assert = require('assert');
const zlib = require('zlib');

const nonStringInputs = [1, true, { a: 1 }, ['a']];
const nonStringInputs = [
1,
true,
{ a: 1 },
['a']
];

console.error('Doing the non-strings');
nonStringInputs.forEach(function(input) {
// zlib.Unzip classes need to get valid data, or else they'll throw.
const unzips = [
zlib.Unzip(),
zlib.Gunzip(),
zlib.Inflate(),
zlib.InflateRaw()
];

nonStringInputs.forEach(common.mustCall((input) => {
// zlib.gunzip should not throw an error when called with bad input.
assert.doesNotThrow(function() {
zlib.gunzip(input, function(err, buffer) {
// zlib.gunzip should pass the error to the callback.
assert.ok(err);
});
});
});

console.error('Doing the unzips');
// zlib.Unzip classes need to get valid data, or else they'll throw.
const unzips = [ zlib.Unzip(),
zlib.Gunzip(),
zlib.Inflate(),
zlib.InflateRaw() ];
const hadError = [];
unzips.forEach(function(uz, i) {
console.error(`Error for ${uz.constructor.name}`);
uz.on('error', function(er) {
console.error('Error event', er);
hadError[i] = true;
});
}, nonStringInputs.length));

uz.on('end', function(er) {
throw new Error(`end event should not be emitted ${uz.constructor.name}`);
});
unzips.forEach(common.mustCall((uz, i) => {
uz.on('error', common.mustCall());
uz.on('end', common.mustNotCall);

// this will trigger error event
uz.write('this is not valid compressed data.');
});

process.on('exit', function() {
assert.deepStrictEqual(hadError, [true, true, true, true], 'expect 4 errors');
});
}, unzips.length));
193 changes: 86 additions & 107 deletions test/parallel/test-zlib-random-byte-pipes.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,124 +27,120 @@ if (!common.hasCrypto)
const assert = require('assert');
const crypto = require('crypto');
const stream = require('stream');
const util = require('util');
const zlib = require('zlib');

const Stream = stream.Stream;

// emit random bytes, and keep a shasum
function RandomReadStream(opt) {
Stream.call(this);
class RandomReadStream extends Stream {
constructor(opt) {
super();

this.readable = true;
this._paused = false;
this._processing = false;

this._hasher = crypto.createHash('sha1');
opt = opt || {};

// base block size.
opt.block = opt.block || 256 * 1024;
this.readable = true;
this._paused = false;
this._processing = false;

// total number of bytes to emit
opt.total = opt.total || 256 * 1024 * 1024;
this._remaining = opt.total;
this._hasher = crypto.createHash('sha1');
opt = opt || {};

// how variable to make the block sizes
opt.jitter = opt.jitter || 1024;
// base block size.
opt.block = opt.block || 256 * 1024;

this._opt = opt;
// total number of bytes to emit
opt.total = opt.total || 256 * 1024 * 1024;
this._remaining = opt.total;

this._process = this._process.bind(this);
// how variable to make the block sizes
opt.jitter = opt.jitter || 1024;

process.nextTick(this._process);
}
this._opt = opt;

util.inherits(RandomReadStream, Stream);
this._process = this._process.bind(this);

RandomReadStream.prototype.pause = function() {
this._paused = true;
this.emit('pause');
};
process.nextTick(this._process);
}

RandomReadStream.prototype.resume = function() {
// console.error("rrs resume");
this._paused = false;
this.emit('resume');
this._process();
};
pause() {
this._paused = true;
this.emit('pause');
}

RandomReadStream.prototype._process = function() {
if (this._processing) return;
if (this._paused) return;
resume() {
// console.error("rrs resume");
this._paused = false;
this.emit('resume');
this._process();
}

this._processing = true;
_process() {
if (this._processing) return;
if (this._paused) return;

if (!this._remaining) {
this._hash = this._hasher.digest('hex').toLowerCase().trim();
this._processing = false;
this._processing = true;

this.emit('end');
return;
}
if (!this._remaining) {
this._hash = this._hasher.digest('hex').toLowerCase().trim();
this._processing = false;

// figure out how many bytes to output
// if finished, then just emit end.
let block = this._opt.block;
const jitter = this._opt.jitter;
if (jitter) {
block += Math.ceil(Math.random() * jitter - (jitter / 2));
}
block = Math.min(block, this._remaining);
const buf = Buffer.allocUnsafe(block);
for (let i = 0; i < block; i++) {
buf[i] = Math.random() * 256;
}
this.emit('end');
return;
}

this._hasher.update(buf);
// figure out how many bytes to output
// if finished, then just emit end.
let block = this._opt.block;
const jitter = this._opt.jitter;
if (jitter) {
block += Math.ceil(Math.random() * jitter - (jitter / 2));
}
block = Math.min(block, this._remaining);
const buf = Buffer.allocUnsafe(block);
for (let i = 0; i < block; i++) {
buf[i] = Math.random() * 256;
}

this._remaining -= block;
this._hasher.update(buf);

console.error('block=%d\nremain=%d\n', block, this._remaining);
this._processing = false;
this._remaining -= block;

this.emit('data', buf);
process.nextTick(this._process);
};
this._processing = false;

this.emit('data', buf);
process.nextTick(this._process);
}
}

// a filter that just verifies a shasum
function HashStream() {
Stream.call(this);
class HashStream extends Stream {
constructor() {
super();
this.readable = this.writable = true;
this._hasher = crypto.createHash('sha1');
}

this.readable = this.writable = true;
this._hasher = crypto.createHash('sha1');
}
write(c) {
// Simulate the way that an fs.ReadStream returns false
// on *every* write like a jerk, only to resume a
// moment later.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated, but we shouldn’t keep this in the source code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heh... indeed. completely missed that!

this._hasher.update(c);
process.nextTick(this.resume.bind(this));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to replace these with arrow functions if you like :)

return false;
}

resume() {
this.emit('resume');
process.nextTick(this.emit.bind(this, 'drain'));
}

util.inherits(HashStream, Stream);

HashStream.prototype.write = function(c) {
// Simulate the way that an fs.ReadStream returns false
// on *every* write like a jerk, only to resume a
// moment later.
this._hasher.update(c);
process.nextTick(this.resume.bind(this));
return false;
};

HashStream.prototype.resume = function() {
this.emit('resume');
process.nextTick(this.emit.bind(this, 'drain'));
};

HashStream.prototype.end = function(c) {
if (c) {
this.write(c);
end(c) {
if (c) {
this.write(c);
}
this._hash = this._hasher.digest('hex').toLowerCase().trim();
this.emit('data', this._hash);
this.emit('end');
}
this._hash = this._hasher.digest('hex').toLowerCase().trim();
this.emit('data', this._hash);
this.emit('end');
};
}


const inp = new RandomReadStream({ total: 1024, block: 256, jitter: 16 });
Expand All @@ -154,23 +150,6 @@ const gunz = zlib.createGunzip();

inp.pipe(gzip).pipe(gunz).pipe(out);

inp.on('data', function(c) {
console.error('inp data', c.length);
});

gzip.on('data', function(c) {
console.error('gzip data', c.length);
});

gunz.on('data', function(c) {
console.error('gunz data', c.length);
});

out.on('data', function(c) {
console.error('out data', c.length);
});

out.on('data', common.mustCall(function(c) {
console.error('hash=%s', c);
out.on('data', common.mustCall((c) => {
assert.strictEqual(c, inp._hash, 'hashes should match');
}));
Loading