diff --git a/index.js b/index.js index 949d146f..16ad407d 100755 --- a/index.js +++ b/index.js @@ -72,7 +72,7 @@ util.inherits(Sharp, stream.Duplex); Sharp.prototype._write = function(chunk, encoding, callback) { /*jslint unused: false */ if (this.options.streamIn) { - if (typeof chunk === 'object' || chunk instanceof Buffer) { + if (typeof chunk === 'object' && chunk instanceof Buffer) { if (typeof this.options.bufferIn === 'undefined') { // Create new Buffer this.options.bufferIn = new Buffer(chunk.length); @@ -98,13 +98,10 @@ module.exports.gravity = {'center': 0, 'centre': 0, 'north': 1, 'east': 2, 'sout Sharp.prototype.crop = function(gravity) { this.options.canvas = 'c'; - if (typeof gravity !== 'undefined') { - // Is this a supported gravity? - if (!Number.isNaN(gravity) && gravity >= 0 && gravity <= 4) { - this.options.gravity = gravity; - } else { - throw new Error('Unsupported crop gravity ' + gravity); - } + if (typeof gravity === 'number' && !Number.isNaN(gravity) && gravity >= 0 && gravity <= 4) { + this.options.gravity = gravity; + } else { + throw new Error('Unsupported crop gravity ' + gravity); } return this; }; @@ -264,7 +261,7 @@ Sharp.prototype.resize = function(width, height) { if (!width) { this.options.width = -1; } else { - if (!Number.isNaN(width)) { + if (typeof width === 'number' && !Number.isNaN(width)) { this.options.width = width; } else { throw new Error('Invalid width ' + width); @@ -273,7 +270,7 @@ Sharp.prototype.resize = function(width, height) { if (!height) { this.options.height = -1; } else { - if (!Number.isNaN(height)) { + if (typeof height === 'number' && !Number.isNaN(height)) { this.options.height = height; } else { throw new Error('Invalid height ' + height); @@ -458,10 +455,10 @@ Sharp.prototype.metadata = function(callback) { Get and set cache memory and item limits */ module.exports.cache = function(memory, items) { - if (Number.isNaN(memory)) { + if (typeof memory !== 'number' || Number.isNaN(memory)) { memory = null; } - if (Number.isNaN(items)) { + if (typeof items !== 'number' || Number.isNaN(items)) { items = null; } return sharp.cache(memory, items); @@ -471,7 +468,7 @@ module.exports.cache = function(memory, items) { Get and set size of thread pool */ module.exports.concurrency = function(concurrency) { - if (Number.isNaN(concurrency)) { + if (typeof concurrency !== 'number' || Number.isNaN(concurrency)) { concurrency = null; } return sharp.concurrency(concurrency); diff --git a/test/unit/alpha.js b/test/unit/alpha.js index b33814d9..1b6f9a87 100755 --- a/test/unit/alpha.js +++ b/test/unit/alpha.js @@ -30,11 +30,24 @@ describe('Alpha transparency', function() { .toFile(fixtures.path('output.flatten-hex-orange.jpg'), done); }); + it('Do not flatten', function(done) { + sharp(fixtures.inputPngWithTransparency) + .flatten(false) + .toBuffer(function(err, data) { + if (err) throw err; + sharp(data).metadata(function(err, metadata) { + if (err) throw err; + assert.strictEqual('png', metadata.format); + assert.strictEqual(4, metadata.channels); + done(); + }); + }); + }); + it('Ignored for JPEG', function(done) { sharp(fixtures.inputJpg) .background('#ff0000') .flatten() - .resize(500, 400) .toBuffer(function(err, data) { if (err) throw err; sharp(data).metadata(function(err, metadata) { diff --git a/test/unit/colourspace.js b/test/unit/colourspace.js index 2feb5d72..be3b67ff 100755 --- a/test/unit/colourspace.js +++ b/test/unit/colourspace.js @@ -22,6 +22,13 @@ describe('Colour space conversion', function() { .toFile(fixtures.path('output.greyscale-gamma-2.2.jpg'), done); }); + it('Not to greyscale', function(done) { + sharp(fixtures.inputJpg) + .resize(320, 240) + .greyscale(false) + .toFile(fixtures.path('output.greyscale-not.jpg'), done); + }); + it('From 1-bit TIFF to sRGB WebP [slow]', function(done) { sharp(fixtures.inputTiff) .webp() diff --git a/test/unit/crop.js b/test/unit/crop.js index f02efe1b..c9e8ff11 100755 --- a/test/unit/crop.js +++ b/test/unit/crop.js @@ -79,4 +79,15 @@ describe('Crop gravities', function() { }); }); + it('Invalid', function(done) { + var isValid = true; + try { + sharp(fixtures.inputJpg).crop(5); + } catch (err) { + isValid = false; + } + assert.strictEqual(false, isValid); + done(); + }); + }); diff --git a/test/unit/gamma.js b/test/unit/gamma.js index da57b207..9542a13a 100755 --- a/test/unit/gamma.js +++ b/test/unit/gamma.js @@ -1,5 +1,7 @@ 'use strict'; +var assert = require('assert'); + var sharp = require('../../index'); var fixtures = require('../fixtures'); @@ -10,12 +12,14 @@ describe('Gamma correction', function() { .resize(129, 111) .toFile(fixtures.path('output.gamma-0.0.jpg'), done); }); + it('value of 2.2 (default)', function(done) { sharp(fixtures.inputJpgWithGammaHoliness) .resize(129, 111) .gamma() .toFile(fixtures.path('output.gamma-2.2.jpg'), done); }); + it('value of 3.0', function(done) { sharp(fixtures.inputJpgWithGammaHoliness) .resize(129, 111) @@ -23,4 +27,15 @@ describe('Gamma correction', function() { .toFile(fixtures.path('output.gamma-3.0.jpg'), done); }); + it('invalid value', function(done) { + var isValid = true; + try { + sharp(fixtures.inputJpgWithGammaHoliness).gamma(4); + } catch (err) { + isValid = false; + } + assert.strictEqual(false, isValid); + done(); + }); + }); diff --git a/test/unit/io.js b/test/unit/io.js index 3ede1b15..cc733d4e 100755 --- a/test/unit/io.js +++ b/test/unit/io.js @@ -67,6 +67,18 @@ describe('Input/output', function() { readable.pipe(pipeline); }); + it('Read from Stream and write to Buffer via Promise', function(done) { + var readable = fs.createReadStream(fixtures.inputJpg); + var pipeline = sharp().resize(1, 1); + pipeline.toBuffer().then(function(data) { + assert.strictEqual(true, data.length > 0); + done(); + }).catch(function(err) { + throw err; + }); + readable.pipe(pipeline); + }); + it('Read from Stream and write to Stream', function(done) { var readable = fs.createReadStream(fixtures.inputJpg); var writable = fs.createWriteStream(fixtures.outputJpg); @@ -114,6 +126,34 @@ describe('Input/output', function() { readableButNotAnImage.pipe(writable); }); + it('Sequential read', function(done) { + sharp(fixtures.inputJpg) + .sequentialRead() + .resize(320, 240) + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(true, data.length > 0); + assert.strictEqual('jpeg', info.format); + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); + done(); + }); + }); + + it('Not sequential read', function(done) { + sharp(fixtures.inputJpg) + .sequentialRead(false) + .resize(320, 240) + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(true, data.length > 0); + assert.strictEqual('jpeg', info.format); + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); + done(); + }); + }); + it('Fail when output File is input File', function(done) { sharp(fixtures.inputJpg).toFile(fixtures.inputJpg, function(err) { assert(!!err); @@ -121,6 +161,33 @@ describe('Input/output', function() { }); }); + it('Fail when output File is input File via Promise', function(done) { + sharp(fixtures.inputJpg).toFile(fixtures.inputJpg).then(function(data) { + assert(false); + done(); + }).catch(function(err) { + assert(!!err); + done(); + }); + }); + + it('Fail when output File is empty', function(done) { + sharp(fixtures.inputJpg).toFile('', function(err) { + assert(!!err); + done(); + }); + }); + + it('Fail when output File is empty via Promise', function(done) { + sharp(fixtures.inputJpg).toFile('').then(function(data) { + assert(false); + done(); + }).catch(function(err) { + assert(!!err); + done(); + }); + }); + it('Fail when input is empty Buffer', function(done) { var fail = false; try { @@ -161,6 +228,42 @@ describe('Input/output', function() { }); }); + it('Invalid quality', function(done) { + var isValid = true; + try { + sharp(fixtures.inputJpg).quality(-1); + } catch (err) { + isValid = false; + } + assert.strictEqual(false, isValid); + done(); + }); + + it('Progressive image', function(done) { + sharp(fixtures.inputJpg) + .resize(320, 240) + .png() + .progressive(false) + .toBuffer(function(err, nonProgressive, info) { + if (err) throw err; + assert.strictEqual(true, nonProgressive.length > 0); + assert.strictEqual('png', info.format); + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); + sharp(nonProgressive) + .progressive() + .toBuffer(function(err, progressive, info) { + if (err) throw err; + assert.strictEqual(true, progressive.length > 0); + assert.strictEqual(true, progressive.length > nonProgressive.length); + assert.strictEqual('png', info.format); + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); + done(); + }); + }); + }); + describe('Output filename without extension uses input format', function() { it('JPEG', function(done) { diff --git a/test/unit/metadata.js b/test/unit/metadata.js index 9e70c17e..36ad3bbd 100755 --- a/test/unit/metadata.js +++ b/test/unit/metadata.js @@ -99,7 +99,7 @@ describe('Image metadata', function() { }); }); - it('Promise', function(done) { + it('File in, Promise out', function(done) { sharp(fixtures.inputJpg).metadata().then(function(metadata) { assert.strictEqual('jpeg', metadata.format); assert.strictEqual(2725, metadata.width); @@ -111,6 +111,23 @@ describe('Image metadata', function() { }); }); + it('Stream in, Promise out', function(done) { + var readable = fs.createReadStream(fixtures.inputJpg); + var pipeline = sharp(); + pipeline.metadata().then(function(metadata) { + assert.strictEqual('jpeg', metadata.format); + assert.strictEqual(2725, metadata.width); + assert.strictEqual(2225, metadata.height); + assert.strictEqual('srgb', metadata.space); + assert.strictEqual(3, metadata.channels); + assert.strictEqual(false, metadata.hasAlpha); + done(); + }).catch(function(err) { + throw err; + }); + readable.pipe(pipeline); + }); + it('Stream', function(done) { var readable = fs.createReadStream(fixtures.inputJpg); var pipeline = sharp().metadata(function(err, metadata) { diff --git a/test/unit/resize.js b/test/unit/resize.js index 3e4048ae..9fdfd7f9 100755 --- a/test/unit/resize.js +++ b/test/unit/resize.js @@ -62,6 +62,28 @@ describe('Resize dimensions', function() { }); }); + it('Invalid width', function(done) { + var isValid = true; + try { + sharp(fixtures.inputJpg).resize('spoons', 240); + } catch (err) { + isValid = false; + } + assert.strictEqual(false, isValid); + done(); + }); + + it('Invalid height', function(done) { + var isValid = true; + try { + sharp(fixtures.inputJpg).resize(320, 'spoons'); + } catch (err) { + isValid = false; + } + assert.strictEqual(false, isValid); + done(); + }); + it('TIFF embed known to cause rounding errors', function(done) { sharp(fixtures.inputTiff).resize(240, 320).embed().jpeg().toBuffer(function(err, data, info) { if (err) throw err; @@ -117,26 +139,46 @@ describe('Resize dimensions', function() { }); }); - it('Do not enlarge if input width is already less than output width', function(done) { - sharp(fixtures.inputJpg).resize(2800).withoutEnlargement().toBuffer(function(err, data, info) { - if (err) throw err; - assert.strictEqual(true, data.length > 0); - assert.strictEqual('jpeg', info.format); - assert.strictEqual(2725, info.width); - assert.strictEqual(2225, info.height); - done(); - }); + it('Do not enlarge when input width is already less than output width', function(done) { + sharp(fixtures.inputJpg) + .resize(2800) + .withoutEnlargement() + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(true, data.length > 0); + assert.strictEqual('jpeg', info.format); + assert.strictEqual(2725, info.width); + assert.strictEqual(2225, info.height); + done(); + }); }); - it('Do not enlarge if input height is already less than output height', function(done) { - sharp(fixtures.inputJpg).resize(null, 2300).withoutEnlargement().toBuffer(function(err, data, info) { - if (err) throw err; - assert.strictEqual(true, data.length > 0); - assert.strictEqual('jpeg', info.format); - assert.strictEqual(2725, info.width); - assert.strictEqual(2225, info.height); - done(); - }); + it('Do not enlarge when input height is already less than output height', function(done) { + sharp(fixtures.inputJpg) + .resize(null, 2300) + .withoutEnlargement() + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(true, data.length > 0); + assert.strictEqual('jpeg', info.format); + assert.strictEqual(2725, info.width); + assert.strictEqual(2225, info.height); + done(); + }); + }); + + it('Do enlarge when input width is less than output width', function(done) { + sharp(fixtures.inputJpg) + .resize(2800) + .withoutEnlargement(false) + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(true, data.length > 0); + assert.strictEqual('jpeg', info.format); + assert.strictEqual(2800, info.width); + assert.strictEqual(2286, info.height); + done(); + }); }); }); diff --git a/test/unit/sharpen.js b/test/unit/sharpen.js new file mode 100755 index 00000000..1d09fd04 --- /dev/null +++ b/test/unit/sharpen.js @@ -0,0 +1,34 @@ +'use strict'; + +var assert = require('assert'); + +var sharp = require('../../index'); +var fixtures = require('../fixtures'); + +describe('Sharpen', function() { + + it('sharpen image is larger than non-sharpen', function(done) { + sharp(fixtures.inputJpg) + .resize(320, 240) + .sharpen(false) + .toBuffer(function(err, notSharpened, info) { + if (err) throw err; + assert.strictEqual(true, notSharpened.length > 0); + assert.strictEqual('jpeg', info.format); + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); + sharp(notSharpened) + .sharpen() + .toBuffer(function(err, sharpened, info) { + if (err) throw err; + assert.strictEqual(true, sharpened.length > 0); + assert.strictEqual(true, sharpened.length > notSharpened.length); + assert.strictEqual('jpeg', info.format); + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); + done(); + }); + }); + }); + +}); diff --git a/test/unit/util.js b/test/unit/util.js index 6f917135..e1e7291d 100755 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -9,10 +9,20 @@ describe('Utilities', function() { describe('Cache', function() { it('Can be disabled', function() { - sharp.cache(0); + var cache = sharp.cache(0, 0); + assert.strictEqual(0, cache.memory); + assert.strictEqual(0, cache.items); }); it('Can be set to a maximum of 50MB and 500 items', function() { + var cache = sharp.cache(50, 500); + assert.strictEqual(50, cache.memory); + assert.strictEqual(500, cache.items); + }); + it('Ignores invalid values', function() { sharp.cache(50, 500); + var cache = sharp.cache('spoons'); + assert.strictEqual(50, cache.memory); + assert.strictEqual(500, cache.items); }); }); @@ -25,6 +35,11 @@ describe('Utilities', function() { sharp.concurrency(0); assert.strictEqual(defaultConcurrency, sharp.concurrency()); }); + it('Ignores invalid values', function() { + sharp.concurrency(0); + sharp.concurrency('spoons'); + assert.strictEqual(defaultConcurrency, sharp.concurrency()); + }); }); describe('Counters', function() {