From c670358d7e885b9bb187e80b0fc511b5a4132a4a Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 10 Dec 2018 15:48:50 +0100 Subject: [PATCH] benchmark,test: add brotli Co-authored-by: Hackzzila Backport-PR-URL: https://github.com/nodejs/node/pull/27681 PR-URL: https://github.com/nodejs/node/pull/24938 Reviewed-By: Ruben Bridgewater Reviewed-By: Matteo Collina Reviewed-By: Jan Krems Reviewed-By: James M Snell Reviewed-By: Myles Borins Reviewed-By: Ali Ijaz Sheikh Reviewed-By: Daniel Bevenius --- benchmark/zlib/creation.js | 3 +- benchmark/zlib/pipe.js | 11 ++- test/fixtures/person.jpg.br | Bin 0 -> 45173 bytes test/parallel/test-zlib-brotli-flush.js | 27 +++++++ test/parallel/test-zlib-brotli-from-brotli.js | 32 ++++++++ test/parallel/test-zlib-brotli-from-string.js | 34 ++++++++ .../test-zlib-brotli-kmaxlength-rangeerror.js | 28 +++++++ test/parallel/test-zlib-brotli.js | 73 ++++++++++++++++++ test/parallel/test-zlib-bytes-read.js | 3 +- .../parallel/test-zlib-convenience-methods.js | 2 + test/parallel/test-zlib-empty-buffer.js | 4 +- test/parallel/test-zlib-invalid-input.js | 3 +- test/parallel/test-zlib-random-byte-pipes.js | 26 ++++--- test/parallel/test-zlib-write-after-flush.js | 37 +++++---- test/parallel/test-zlib-zero-byte.js | 30 +++---- test/parallel/test-zlib.js | 3 +- 16 files changed, 267 insertions(+), 49 deletions(-) create mode 100644 test/fixtures/person.jpg.br create mode 100644 test/parallel/test-zlib-brotli-flush.js create mode 100644 test/parallel/test-zlib-brotli-from-brotli.js create mode 100644 test/parallel/test-zlib-brotli-from-string.js create mode 100644 test/parallel/test-zlib-brotli-kmaxlength-rangeerror.js create mode 100644 test/parallel/test-zlib-brotli.js diff --git a/benchmark/zlib/creation.js b/benchmark/zlib/creation.js index 4984bf1a86b755..f23759fa0ebf38 100644 --- a/benchmark/zlib/creation.js +++ b/benchmark/zlib/creation.js @@ -4,7 +4,8 @@ const zlib = require('zlib'); const bench = common.createBenchmark(main, { type: [ - 'Deflate', 'DeflateRaw', 'Inflate', 'InflateRaw', 'Gzip', 'Gunzip', 'Unzip' + 'Deflate', 'DeflateRaw', 'Inflate', 'InflateRaw', 'Gzip', 'Gunzip', 'Unzip', + 'BrotliCompress', 'BrotliDecompress' ], options: ['true', 'false'], n: [5e5] diff --git a/benchmark/zlib/pipe.js b/benchmark/zlib/pipe.js index 9b05749bbb824d..6a1c427bc8380b 100644 --- a/benchmark/zlib/pipe.js +++ b/benchmark/zlib/pipe.js @@ -6,15 +6,18 @@ const zlib = require('zlib'); const bench = common.createBenchmark(main, { inputLen: [1024], duration: [5], - type: ['string', 'buffer'] + type: ['string', 'buffer'], + algorithm: ['gzip', 'brotli'] }); -function main({ inputLen, duration, type }) { +function main({ inputLen, duration, type, algorithm }) { const buffer = Buffer.alloc(inputLen, fs.readFileSync(__filename)); const chunk = type === 'buffer' ? buffer : buffer.toString('utf8'); - const input = zlib.createGzip(); - const output = zlib.createGunzip(); + const input = algorithm === 'gzip' ? + zlib.createGzip() : zlib.createBrotliCompress(); + const output = algorithm === 'gzip' ? + zlib.createGunzip() : zlib.createBrotliDecompress(); let readFromOutput = 0; input.pipe(output); diff --git a/test/fixtures/person.jpg.br b/test/fixtures/person.jpg.br new file mode 100644 index 0000000000000000000000000000000000000000..7d35b1238fb7ec7324c3837dd41e7958d746480e GIT binary patch literal 45173 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 Fe0+PQAB_M2 literal 0 HcmV?d00001 diff --git a/test/parallel/test-zlib-brotli-flush.js b/test/parallel/test-zlib-brotli-flush.js new file mode 100644 index 00000000000000..6883fb903a6478 --- /dev/null +++ b/test/parallel/test-zlib-brotli-flush.js @@ -0,0 +1,27 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fixtures = require('../common/fixtures'); + +const file = fixtures.readSync('person.jpg'); +const chunkSize = 16; +const deflater = new zlib.BrotliCompress(); + +const chunk = file.slice(0, chunkSize); +const expectedFull = Buffer.from('iweA/9j/4AAQSkZJRgABAQEASA==', 'base64'); +let actualFull; + +deflater.write(chunk, function() { + deflater.flush(function() { + const bufs = []; + let buf; + while (buf = deflater.read()) + bufs.push(buf); + actualFull = Buffer.concat(bufs); + }); +}); + +process.once('exit', function() { + assert.deepStrictEqual(actualFull, expectedFull); +}); diff --git a/test/parallel/test-zlib-brotli-from-brotli.js b/test/parallel/test-zlib-brotli-from-brotli.js new file mode 100644 index 00000000000000..a0afc425d25a6d --- /dev/null +++ b/test/parallel/test-zlib-brotli-from-brotli.js @@ -0,0 +1,32 @@ +'use strict'; +// Test unzipping a file that was created with a non-node brotli lib, +// piped in as fast as possible. + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const path = require('path'); +const fixtures = require('../common/fixtures'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const decompress = new zlib.BrotliDecompress(); + +const fs = require('fs'); + +const fixture = fixtures.path('person.jpg.br'); +const unzippedFixture = fixtures.path('person.jpg'); +const outputFile = path.resolve(tmpdir.path, 'person.jpg'); +const expect = fs.readFileSync(unzippedFixture); +const inp = fs.createReadStream(fixture); +const out = fs.createWriteStream(outputFile); + +inp.pipe(decompress).pipe(out); +out.on('close', common.mustCall(() => { + const actual = fs.readFileSync(outputFile); + assert.strictEqual(actual.length, expect.length); + for (let i = 0, l = actual.length; i < l; i++) { + assert.strictEqual(actual[i], expect[i], `byte[${i}]`); + } +})); diff --git a/test/parallel/test-zlib-brotli-from-string.js b/test/parallel/test-zlib-brotli-from-string.js new file mode 100644 index 00000000000000..ab3673374f1d2f --- /dev/null +++ b/test/parallel/test-zlib-brotli-from-string.js @@ -0,0 +1,34 @@ +'use strict'; +// Test compressing and uncompressing a string with brotli + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' + + 't. Morbi faucibus, purus at gravida dictum, libero arcu ' + + 'convallis lacus, in commodo libero metus eu nisi. Nullam' + + ' commodo, neque nec porta placerat, nisi est fermentum a' + + 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' + + 'n non diam orci. Proin quis elit turpis. Suspendisse non' + + ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' + + 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' + + 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; +const expectedBase64Compress = 'G/gBQBwHdky2aHV5KK9Snf05//1pPdmNw/7232fnIm1IB' + + 'K1AA8RsN8OB8Nb7Lpgk3UWWUlzQXZyHQeBBbXMTQXC1j7' + + 'wg3LJs9LqOGHRH2bj/a2iCTLLx8hBOyTqgoVuD1e+Qqdn' + + 'f1rkUNyrWq6LtOhWgxP3QUwdhKGdZm3rJWaDDBV7+pDk1' + + 'MIkrmjp4ma2xVi5MsgJScA3tP1I7mXeby6MELozrwoBQD' + + 'mVTnEAicZNj4lkGqntJe2qSnGyeMmcFgraK94vCg/4iLu' + + 'Tw5RhKhnVY++dZ6niUBmRqIutsjf5TzwF5iAg8a9UkjF5' + + '2eZ0tB2vo6v8SqVfNMkBmmhxr0NT9LkYF69aEjlYzj7IE' + + 'KmEUQf1HBogRYhFIt4ymRNEgHAIzOyNEsQM='; + +zlib.brotliCompress(inputString, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString('base64'), expectedBase64Compress); +})); + +const buffer = Buffer.from(expectedBase64Compress, 'base64'); +zlib.brotliDecompress(buffer, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString(), inputString); +})); diff --git a/test/parallel/test-zlib-brotli-kmaxlength-rangeerror.js b/test/parallel/test-zlib-brotli-kmaxlength-rangeerror.js new file mode 100644 index 00000000000000..c1765eec2983ee --- /dev/null +++ b/test/parallel/test-zlib-brotli-kmaxlength-rangeerror.js @@ -0,0 +1,28 @@ +'use strict'; +require('../common'); + +// This test ensures that zlib throws a RangeError if the final buffer needs to +// be larger than kMaxLength and concatenation fails. +// https://github.com/nodejs/node/pull/1811 + +const assert = require('assert'); + +// Change kMaxLength for zlib to trigger the error without having to allocate +// large Buffers. +const buffer = require('buffer'); +const oldkMaxLength = buffer.kMaxLength; +buffer.kMaxLength = 128; +const zlib = require('zlib'); +buffer.kMaxLength = oldkMaxLength; + +const encoded = Buffer.from('G38A+CXCIrFAIAM=', 'base64'); + +// Async +zlib.brotliDecompress(encoded, function(err) { + assert.ok(err instanceof RangeError); +}); + +// Sync +assert.throws(function() { + zlib.brotliDecompressSync(encoded); +}, RangeError); diff --git a/test/parallel/test-zlib-brotli.js b/test/parallel/test-zlib-brotli.js new file mode 100644 index 00000000000000..2dc60aff4058ab --- /dev/null +++ b/test/parallel/test-zlib-brotli.js @@ -0,0 +1,73 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const zlib = require('zlib'); + +// Test some brotli-specific properties of the brotli streams that can not +// be easily covered through expanding zlib-only tests. + +const sampleBuffer = fixtures.readSync('/pss-vectors.json'); + +{ + // Test setting the quality parameter at stream creation: + const sizes = []; + for (let quality = zlib.constants.BROTLI_MIN_QUALITY; + quality <= zlib.constants.BROTLI_MAX_QUALITY; + quality++) { + const encoded = zlib.brotliCompressSync(sampleBuffer, { + params: { + [zlib.constants.BROTLI_PARAM_QUALITY]: quality + } + }); + sizes.push(encoded.length); + } + + // Increasing quality should roughly correspond to decreasing compressed size: + for (let i = 0; i < sizes.length - 1; i++) { + assert(sizes[i + 1] <= sizes[i] * 1.05, sizes); // 5 % margin of error. + } + assert(sizes[0] > sizes[sizes.length - 1], sizes); +} + +{ + // Test that setting out-of-bounds option values or keys fails. + common.expectsError(() => { + zlib.createBrotliCompress({ + params: { + 10000: 0 + } + }); + }, { + code: 'ERR_BROTLI_INVALID_PARAM', + type: RangeError, + message: '10000 is not a valid Brotli parameter' + }); + + // Test that accidentally using duplicate keys fails. + common.expectsError(() => { + zlib.createBrotliCompress({ + params: { + '0': 0, + '00': 0 + } + }); + }, { + code: 'ERR_BROTLI_INVALID_PARAM', + type: RangeError, + message: '00 is not a valid Brotli parameter' + }); + + common.expectsError(() => { + zlib.createBrotliCompress({ + params: { + // This is a boolean flag + [zlib.constants.BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING]: 42 + } + }); + }, { + code: 'ERR_ZLIB_INITIALIZATION_FAILED', + type: Error, + message: 'Initialization failed' + }); +} diff --git a/test/parallel/test-zlib-bytes-read.js b/test/parallel/test-zlib-bytes-read.js index 493478e78d33d0..0b40f0bccc72c4 100644 --- a/test/parallel/test-zlib-bytes-read.js +++ b/test/parallel/test-zlib-bytes-read.js @@ -25,7 +25,8 @@ for (const method of [ ['createGzip', 'createGunzip', false], ['createGzip', 'createUnzip', false], ['createDeflate', 'createInflate', true], - ['createDeflateRaw', 'createInflateRaw', true] + ['createDeflateRaw', 'createInflateRaw', true], + ['createBrotliCompress', 'createBrotliDecompress', true] ]) { let compWriter; let compData = Buffer.alloc(0); diff --git a/test/parallel/test-zlib-convenience-methods.js b/test/parallel/test-zlib-convenience-methods.js index 1cc393914a947e..52ca8b136f10aa 100644 --- a/test/parallel/test-zlib-convenience-methods.js +++ b/test/parallel/test-zlib-convenience-methods.js @@ -52,6 +52,8 @@ for (const [type, expect] of [ ['gzip', 'unzip', 'Gzip', 'Unzip'], ['deflate', 'inflate', 'Deflate', 'Inflate'], ['deflateRaw', 'inflateRaw', 'DeflateRaw', 'InflateRaw'], + ['brotliCompress', 'brotliDecompress', + 'BrotliCompress', 'BrotliDecompress'], ]) { zlib[method[0]](expect, opts, common.mustCall((err, result) => { zlib[method[1]](result, opts, common.mustCall((err, result) => { diff --git a/test/parallel/test-zlib-empty-buffer.js b/test/parallel/test-zlib-empty-buffer.js index e351db54571732..3b52896d29965e 100644 --- a/test/parallel/test-zlib-empty-buffer.js +++ b/test/parallel/test-zlib-empty-buffer.js @@ -10,9 +10,11 @@ const emptyBuffer = Buffer.alloc(0); [ zlib.deflateRawSync, zlib.inflateRawSync, 'raw sync' ], [ zlib.deflateSync, zlib.inflateSync, 'deflate sync' ], [ zlib.gzipSync, zlib.gunzipSync, 'gzip sync' ], + [ zlib.brotliCompressSync, zlib.brotliDecompressSync, 'br sync' ], [ promisify(zlib.deflateRaw), promisify(zlib.inflateRaw), 'raw' ], [ promisify(zlib.deflate), promisify(zlib.inflate), 'deflate' ], - [ promisify(zlib.gzip), promisify(zlib.gunzip), 'gzip' ] + [ promisify(zlib.gzip), promisify(zlib.gunzip), 'gzip' ], + [ promisify(zlib.brotliCompress), promisify(zlib.brotliDecompress), 'br' ] ]) { const compressed = await compress(emptyBuffer); const decompressed = await decompress(compressed); diff --git a/test/parallel/test-zlib-invalid-input.js b/test/parallel/test-zlib-invalid-input.js index 10c0f3622790d0..dbfb5235b8a908 100644 --- a/test/parallel/test-zlib-invalid-input.js +++ b/test/parallel/test-zlib-invalid-input.js @@ -38,7 +38,8 @@ const unzips = [ zlib.Unzip(), zlib.Gunzip(), zlib.Inflate(), - zlib.InflateRaw() + zlib.InflateRaw(), + zlib.BrotliDecompress() ]; nonStringInputs.forEach(common.mustCall((input) => { diff --git a/test/parallel/test-zlib-random-byte-pipes.js b/test/parallel/test-zlib-random-byte-pipes.js index 9c679ab0a0a2fa..c7026163e77586 100644 --- a/test/parallel/test-zlib-random-byte-pipes.js +++ b/test/parallel/test-zlib-random-byte-pipes.js @@ -141,14 +141,18 @@ class HashStream extends Stream { } } - -const inp = new RandomReadStream({ total: 1024, block: 256, jitter: 16 }); -const out = new HashStream(); -const gzip = zlib.createGzip(); -const gunz = zlib.createGunzip(); - -inp.pipe(gzip).pipe(gunz).pipe(out); - -out.on('data', common.mustCall((c) => { - assert.strictEqual(c, inp._hash, `Hash '${c}' equals '${inp._hash}'.`); -})); +for (const [ createCompress, createDecompress ] of [ + [ zlib.createGzip, zlib.createGunzip ], + [ zlib.createBrotliCompress, zlib.createBrotliDecompress ], +]) { + const inp = new RandomReadStream({ total: 1024, block: 256, jitter: 16 }); + const out = new HashStream(); + const gzip = createCompress(); + const gunz = createDecompress(); + + inp.pipe(gzip).pipe(gunz).pipe(out); + + out.on('data', common.mustCall((c) => { + assert.strictEqual(c, inp._hash, `Hash '${c}' equals '${inp._hash}'.`); + })); +} diff --git a/test/parallel/test-zlib-write-after-flush.js b/test/parallel/test-zlib-write-after-flush.js index 6d8d787343426f..2fcae2a2139768 100644 --- a/test/parallel/test-zlib-write-after-flush.js +++ b/test/parallel/test-zlib-write-after-flush.js @@ -24,22 +24,27 @@ const common = require('../common'); const assert = require('assert'); const zlib = require('zlib'); -const gzip = zlib.createGzip(); -const gunz = zlib.createUnzip(); +for (const [ createCompress, createDecompress ] of [ + [ zlib.createGzip, zlib.createGunzip ], + [ zlib.createBrotliCompress, zlib.createBrotliDecompress ], +]) { + const gzip = createCompress(); + const gunz = createDecompress(); -gzip.pipe(gunz); + gzip.pipe(gunz); -let output = ''; -const input = 'A line of data\n'; -gunz.setEncoding('utf8'); -gunz.on('data', (c) => output += c); -gunz.on('end', common.mustCall(() => { - assert.strictEqual(output, input); - assert.strictEqual(gzip._nextFlush, -1); -})); + let output = ''; + const input = 'A line of data\n'; + gunz.setEncoding('utf8'); + gunz.on('data', (c) => output += c); + gunz.on('end', common.mustCall(() => { + assert.strictEqual(output, input); + assert.strictEqual(gzip._nextFlush, -1); + })); -// make sure that flush/write doesn't trigger an assert failure -gzip.flush(); -gzip.write(input); -gzip.end(); -gunz.read(0); + // Make sure that flush/write doesn't trigger an assert failure + gzip.flush(); + gzip.write(input); + gzip.end(); + gunz.read(0); +} diff --git a/test/parallel/test-zlib-zero-byte.js b/test/parallel/test-zlib-zero-byte.js index 57eefcf2676c60..fc57960f1e56cb 100644 --- a/test/parallel/test-zlib-zero-byte.js +++ b/test/parallel/test-zlib-zero-byte.js @@ -22,18 +22,22 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); - const zlib = require('zlib'); -const gz = zlib.Gzip(); -const emptyBuffer = Buffer.alloc(0); -let received = 0; -gz.on('data', function(c) { - received += c.length; -}); -gz.on('end', common.mustCall(function() { - assert.strictEqual(received, 20); -})); -gz.on('finish', common.mustCall()); -gz.write(emptyBuffer); -gz.end(); +for (const Compressor of [ zlib.Gzip, zlib.BrotliCompress ]) { + const gz = Compressor(); + const emptyBuffer = Buffer.alloc(0); + let received = 0; + gz.on('data', function(c) { + received += c.length; + }); + + gz.on('end', common.mustCall(function() { + const expected = Compressor === zlib.Gzip ? 20 : 1; + assert.strictEqual(received, expected, + `${received}, ${expected}, ${Compressor.name}`); + })); + gz.on('finish', common.mustCall()); + gz.write(emptyBuffer); + gz.end(); +} diff --git a/test/parallel/test-zlib.js b/test/parallel/test-zlib.js index 1b6855a0b92062..69921104d65cc0 100644 --- a/test/parallel/test-zlib.js +++ b/test/parallel/test-zlib.js @@ -32,7 +32,8 @@ let zlibPairs = [ [zlib.Gzip, zlib.Gunzip], [zlib.Deflate, zlib.Unzip], [zlib.Gzip, zlib.Unzip], - [zlib.DeflateRaw, zlib.InflateRaw] + [zlib.DeflateRaw, zlib.InflateRaw], + [zlib.BrotliCompress, zlib.BrotliDecompress], ]; // how fast to trickle through the slowstream