text: 'use strict';
  
  const fs = require('fs');
  const path = require('path');
  const assert = require('assert');
  const rimraf = require('rimraf');
  
  const sharp = require('../../');
  const fixtures = require('../fixtures');
  
  const outputJpg = fixtures.path('output.jpg');
  
  describe('Input/output', function () {
    beforeEach(function () {
      sharp.cache(false);
    });
    afterEach(function () {
      sharp.cache(true);
    });
  
    it('Read from File and write to Stream', function (done) {
      const writable = fs.createWriteStream(outputJpg);
      writable.on('close', function () {
        sharp(outputJpg).toBuffer(function (err, data, info) {
          if (err) throw err;
          assert.strictEqual(true, data.length > 0);
          assert.strictEqual(data.length, info.size);
          assert.strictEqual('jpeg', info.format);
          assert.strictEqual(320, info.width);
          assert.strictEqual(240, info.height);
          rimraf(outputJpg, done);
        });
      });
      sharp(fixtures.inputJpg).resize(320, 240).pipe(writable);
    });
  
    it('Read from Buffer and write to Stream', function (done) {
      const inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
      const writable = fs.createWriteStream(outputJpg);
      writable.on('close', function () {
        sharp(outputJpg).toBuffer(function (err, data, info) {
          if (err) throw err;
          assert.strictEqual(true, data.length > 0);
          assert.strictEqual(data.length, info.size);
          assert.strictEqual('jpeg', info.format);
          assert.strictEqual(320, info.width);
          assert.strictEqual(240, info.height);
          rimraf(outputJpg, done);
        });
      });
      sharp(inputJpgBuffer).resize(320, 240).pipe(writable);
    });
  
    it('Read from Stream and write to File', function (done) {
      const readable = fs.createReadStream(fixtures.inputJpg);
      const pipeline = sharp().resize(320, 240).toFile(outputJpg, function (err, info) {
        if (err) throw err;
        assert.strictEqual(true, info.size > 0);
        assert.strictEqual('jpeg', info.format);
        assert.strictEqual(320, info.width);
        assert.strictEqual(240, info.height);
        rimraf(outputJpg, done);
      });
      readable.pipe(pipeline);
    });
  
    it('Read from Stream and write to Buffer', function (done) {
      const readable = fs.createReadStream(fixtures.inputJpg);
      const pipeline = sharp().resize(320, 240).toBuffer(function (err, data, info) {
        if (err) throw err;
        assert.strictEqual(true, data.length > 0);
        assert.strictEqual(data.length, info.size);
        assert.strictEqual('jpeg', info.format);
        assert.strictEqual(320, info.width);
        assert.strictEqual(240, info.height);
        done();
      });
      readable.pipe(pipeline);
    });
  
    it('Read from Stream and write to Buffer via Promise resolved with Buffer', function () {
      const pipeline = sharp().resize(1, 1);
      fs.createReadStream(fixtures.inputJpg).pipe(pipeline);
      return pipeline
        .toBuffer({ resolveWithObject: false })
        .then(function (data) {
          assert.strictEqual(true, data instanceof Buffer);
          assert.strictEqual(true, data.length > 0);
        });
    });
  
    it('Read from Stream and write to Buffer via Promise resolved with Object', function () {
      const pipeline = sharp().resize(1, 1);
      fs.createReadStream(fixtures.inputJpg).pipe(pipeline);
      return pipeline
        .toBuffer({ resolveWithObject: true })
        .then(function (object) {
          assert.strictEqual('object', typeof object);
          assert.strictEqual('object', typeof object.info);
          assert.strictEqual('jpeg', object.info.format);
          assert.strictEqual(1, object.info.width);
          assert.strictEqual(1, object.info.height);
          assert.strictEqual(3, object.info.channels);
          assert.strictEqual(true, object.data instanceof Buffer);
          assert.strictEqual(true, object.data.length > 0);
        });
    });
  
    it('Read from File and write to Buffer via Promise resolved with Buffer', function () {
      return sharp(fixtures.inputJpg)
        .resize(1, 1)
        .toBuffer({ resolveWithObject: false })
        .then(function (data) {
          assert.strictEqual(true, data instanceof Buffer);
          assert.strictEqual(true, data.length > 0);
        });
    });
  
    it('Read from File and write to Buffer via Promise resolved with Object', function () {
      return sharp(fixtures.inputJpg)
        .resize(1, 1)
        .toBuffer({ resolveWithObject: true })
        .then(function (object) {
          assert.strictEqual('object', typeof object);
          assert.strictEqual('object', typeof object.info);
          assert.strictEqual('jpeg', object.info.format);
          assert.strictEqual(1, object.info.width);
          assert.strictEqual(1, object.info.height);
          assert.strictEqual(3, object.info.channels);
          assert.strictEqual(true, object.data instanceof Buffer);
          assert.strictEqual(true, object.data.length > 0);
        });
    });
  
    it('Read from Stream and write to Stream', function (done) {
      const readable = fs.createReadStream(fixtures.inputJpg);
      const writable = fs.createWriteStream(outputJpg);
      writable.on('close', function () {
        sharp(outputJpg).toBuffer(function (err, data, info) {
          if (err) throw err;
          assert.strictEqual(true, data.length > 0);
          assert.strictEqual(data.length, info.size);
          assert.strictEqual('jpeg', info.format);
          assert.strictEqual(320, info.width);
          assert.strictEqual(240, info.height);
          rimraf(outputJpg, done);
        });
      });
      const pipeline = sharp().resize(320, 240);
      readable.pipe(pipeline).pipe(writable);
    });
  
    it('Read from Uint8Array and write to Buffer', async () => {
      const uint8array = Uint8Array.from([255, 255, 255, 0, 0, 0]);
      const { data, info } = await sharp(uint8array, {
        raw: {
          width: 2,
          height: 1,
          channels: 3
        }
      }).toBuffer({ resolveWithObject: true });
  
      assert.deepStrictEqual(uint8array, new Uint8Array(data));
      assert.strictEqual(info.width, 2);
      assert.strictEqual(info.height, 1);
    });
  
    it('Read from Uint8ClampedArray and output to Buffer', async () => {
      // since a Uint8ClampedArray is the same as Uint8Array but clamps the values
      // between 0-255 it seemed good to add this also
      const uint8array = Uint8ClampedArray.from([255, 255, 255, 0, 0, 0]);
      const { data, info } = await sharp(uint8array, {
        raw: {
          width: 2,
          height: 1,
          channels: 3
        }
      }).toBuffer({ resolveWithObject: true });
  
      assert.deepStrictEqual(uint8array, new Uint8ClampedArray(data));
      assert.strictEqual(info.width, 2);
      assert.strictEqual(info.height, 1);
    });
  
    it('Stream should emit info event', function (done) {
      const readable = fs.createReadStream(fixtures.inputJpg);
      const writable = fs.createWriteStream(outputJpg);
      const pipeline = sharp().resize(320, 240);
      let infoEventEmitted = false;
      pipeline.on('info', function (info) {
        assert.strictEqual('jpeg', info.format);
        assert.strictEqual(320, info.width);
        assert.strictEqual(240, info.height);
        assert.strictEqual(3, info.channels);
        infoEventEmitted = true;
      });
      writable.on('close', function () {
        assert.strictEqual(true, infoEventEmitted);
        rimraf(outputJpg, done);
      });
      readable.pipe(pipeline).pipe(writable);
    });
  
    it('Handle Stream to Stream error ', function (done) {
      const pipeline = sharp().resize(320, 240);
      let anErrorWasEmitted = false;
      pipeline.on('error', function (err) {
        anErrorWasEmitted = !!err;
      }).on('end', function () {
        assert(anErrorWasEmitted);
        rimraf(outputJpg, done);
      });
      const readableButNotAnImage = fs.createReadStream(__filename);
      const writable = fs.createWriteStream(outputJpg);
      readableButNotAnImage.pipe(pipeline).pipe(writable);
    });
  
    it('Handle File to Stream error', function (done) {
      const readableButNotAnImage = sharp(__filename).resize(320, 240);
      let anErrorWasEmitted = false;
      readableButNotAnImage.on('error', function (err) {
        anErrorWasEmitted = !!err;
      }).on('end', function () {
        assert(anErrorWasEmitted);
        rimraf(outputJpg, done);
      });
      const writable = fs.createWriteStream(outputJpg);
      readableButNotAnImage.pipe(writable);
    });
  
    it('Readable side of Stream can start flowing after Writable side has finished', function (done) {
      const readable = fs.createReadStream(fixtures.inputJpg);
      const writable = fs.createWriteStream(outputJpg);
      writable.on('close', function () {
        sharp(outputJpg).toBuffer(function (err, data, info) {
          if (err) throw err;
          assert.strictEqual(true, data.length > 0);
          assert.strictEqual(data.length, info.size);
          assert.strictEqual('jpeg', info.format);
          assert.strictEqual(320, info.width);
          assert.strictEqual(240, info.height);
          rimraf(outputJpg, done);
        });
      });
      const pipeline = sharp().resize(320, 240);
      readable.pipe(pipeline);
      pipeline.on('finish', function () {
        pipeline.pipe(writable);
      });
    });
  
    it('Invalid sequential read option throws', () => {
      assert.throws(() => {
        sharp({ sequentialRead: 'fail' });
      }, /Expected boolean for sequentialRead but received fail of type string/);
    });
  
    it('Sequential read, force JPEG', () =>
      sharp(fixtures.inputJpg, { sequentialRead: true })
        .resize(320, 240)
        .toFormat(sharp.format.jpeg)
        .toBuffer({ resolveWithObject: true })
        .then(({ data, info }) => {
          assert.strictEqual(data.length > 0, true);
          assert.strictEqual(data.length, info.size);
          assert.strictEqual(info.format, 'jpeg');
          assert.strictEqual(info.width, 320);
          assert.strictEqual(info.height, 240);
        })
    );
  
    it('Not sequential read, force JPEG', () =>
      sharp(fixtures.inputJpg, { sequentialRead: false })
        .resize(320, 240)
        .toFormat('jpeg')
        .toBuffer({ resolveWithObject: true })
        .then(({ data, info }) => {
          assert.strictEqual(data.length > 0, true);
          assert.strictEqual(data.length, info.size);
          assert.strictEqual(info.format, 'jpeg');
          assert.strictEqual(info.width, 320);
          assert.strictEqual(info.height, 240);
        })
    );
  
    it('Support output to jpg format', function (done) {
      sharp(fixtures.inputPng)
        .resize(320, 240)
        .toFormat('jpg')
        .toBuffer(function (err, data, info) {
          if (err) throw err;
          assert.strictEqual(true, data.length > 0);
          assert.strictEqual(data.length, info.size);
          assert.strictEqual('jpeg', info.format);
          assert.strictEqual(320, info.width);
          assert.strictEqual(240, info.height);
          done();
        });
    });
  
    it('Support output to tif format', function (done) {
      sharp(fixtures.inputTiff)
        .resize(320, 240)
        .toFormat('tif')
        .toBuffer(function (err, data, info) {
          if (err) throw err;
          assert.strictEqual(true, data.length > 0);
          assert.strictEqual(data.length, info.size);
          assert.strictEqual('tiff', 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 instanceof Error);
        assert.strictEqual('Cannot use same file for input and output', err.message);
        done();
      });
    });
  
    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 instanceof Error);
        assert.strictEqual('Cannot use same file for input and output', err.message);
        done();
      });
    });
  
    it('Fail when output File is input File (relative output, absolute input)', function (done) {
      const relativePath = path.relative(process.cwd(), fixtures.inputJpg);
      sharp(fixtures.inputJpg).toFile(relativePath, function (err) {
        assert(err instanceof Error);
        assert.strictEqual('Cannot use same file for input and output', err.message);
        done();
      });
    });
  
    it('Fail when output File is input File via Promise (relative output, absolute input)', function (done) {
      const relativePath = path.relative(process.cwd(), fixtures.inputJpg);
      sharp(fixtures.inputJpg).toFile(relativePath).then(function (data) {
        assert(false);
        done();
      }).catch(function (err) {
        assert(err instanceof Error);
        assert.strictEqual('Cannot use same file for input and output', err.message);
        done();
      });
    });
  
    it('Fail when output File is input File (relative input, absolute output)', function (done) {
      const relativePath = path.relative(process.cwd(), fixtures.inputJpg);
      sharp(relativePath).toFile(fixtures.inputJpg, function (err) {
        assert(err instanceof Error);
        assert.strictEqual('Cannot use same file for input and output', err.message);
        done();
      });
    });
  
    it('Fail when output File is input File via Promise (relative input, absolute output)', function (done) {
      const relativePath = path.relative(process.cwd(), fixtures.inputJpg);
      sharp(relativePath).toFile(fixtures.inputJpg).then(function (data) {
        assert(false);
        done();
      }).catch(function (err) {
        assert(err instanceof Error);
        assert.strictEqual('Cannot use same file for input and output', err.message);
        done();
      });
    });
  
    it('Fail when output File is empty', function (done) {
      sharp(fixtures.inputJpg).toFile('', function (err) {
        assert(err instanceof Error);
        assert.strictEqual('Missing output file path', err.message);
        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 instanceof Error);
        assert.strictEqual('Missing output file path', err.message);
        done();
      });
    });
  
    it('Fail when input is invalid Buffer', function (done) {
      sharp(Buffer.from([0x1, 0x2, 0x3, 0x4])).toBuffer().then(function () {
        assert(false);
        done();
      }).catch(function (err) {
        assert(err instanceof Error);
        assert.strictEqual('Input buffer contains unsupported image format', err.message);
        done();
      });
    });
  
    it('Fail when input file path is missing', function (done) {
      sharp('does-not-exist').toBuffer().then(function () {
        assert(false);
        done();
      }).catch(function (err) {
        assert(err instanceof Error);
        assert.strictEqual('Input file is missing', err.message);
        done();
      });
    });
  
    describe('Fail for unsupported input', function () {
      it('Undefined', function () {
        assert.throws(function () {
          sharp(undefined);
        });
      });
      it('Null', function () {
        assert.throws(function () {
          sharp(null);
        });
      });
      it('Numeric', function () {
        assert.throws(function () {
          sharp(1);
        });
      });
      it('Boolean', function () {
        assert.throws(function () {
          sharp(true);
        });
      });
      it('Error Object', function () {
        assert.throws(function () {
          sharp(new Error());
        });
      });
    });
  
    it('Promises/A+', function () {
      return sharp(fixtures.inputJpg)
        .resize(320, 240)
        .toBuffer();
    });
  
    it('Invalid output format', function (done) {
      let isValid = false;
      try {
        sharp().toFormat('zoinks');
        isValid = true;
      } catch (e) {}
      assert(!isValid);
      done();
    });
  
    it('File input with corrupt header fails gracefully', function (done) {
      sharp(fixtures.inputJpgWithCorruptHeader)
        .toBuffer(function (err) {
          assert.strictEqual(true, !!err);
          done();
        });
    });
  
    it('Buffer input with corrupt header fails gracefully', function (done) {
      sharp(fs.readFileSync(fixtures.inputJpgWithCorruptHeader))
        .toBuffer(function (err) {
          assert.strictEqual(true, !!err);
          done();
        });
    });
  
    it('Stream input with corrupt header fails gracefully', function (done) {
      const transformer = sharp();
      transformer
        .toBuffer()
        .then(function () {
          done(new Error('Unexpectedly resolved Promise'));
        })
        .catch(function (err) {
          assert.strictEqual(true, !!err);
          done();
        });
      fs
        .createReadStream(fixtures.inputJpgWithCorruptHeader)
        .pipe(transformer);
    });
  
    describe('Output filename with unknown extension', function () {
      const outputZoinks = fixtures.path('output.zoinks');
  
      it('Match JPEG input', function (done) {
        sharp(fixtures.inputJpg)
          .resize(320, 80)
          .toFile(outputZoinks, function (err, info) {
            if (err) throw err;
            assert.strictEqual(true, info.size > 0);
            assert.strictEqual('jpeg', info.format);
            assert.strictEqual(320, info.width);
            assert.strictEqual(80, info.height);
            rimraf(outputZoinks, done);
          });
      });
  
      it('Match PNG input', function (done) {
        sharp(fixtures.inputPng)
          .resize(320, 80)
          .toFile(outputZoinks, function (err, info) {
            if (err) throw err;
            assert.strictEqual(true, info.size > 0);
            assert.strictEqual('png', info.format);
            assert.strictEqual(320, info.width);
            assert.strictEqual(80, info.height);
            rimraf(outputZoinks, done);
          });
      });
  
      it('Match WebP input', function (done) {
        sharp(fixtures.inputWebP)
          .resize(320, 80)
          .toFile(outputZoinks, function (err, info) {
            if (err) throw err;
            assert.strictEqual(true, info.size > 0);
            assert.strictEqual('webp', info.format);
            assert.strictEqual(320, info.width);
            assert.strictEqual(80, info.height);
            rimraf(outputZoinks, done);
          });
      });
  
      it('Match TIFF input', function (done) {
        sharp(fixtures.inputTiff)
          .resize(320, 80)
          .toFile(outputZoinks, function (err, info) {
            if (err) throw err;
            assert.strictEqual(true, info.size > 0);
            assert.strictEqual('tiff', info.format);
            assert.strictEqual(320, info.width);
            assert.strictEqual(80, info.height);
            rimraf(outputZoinks, done);
          });
      });
  
      it('Autoconvert GIF input to PNG output', function (done) {
        sharp(fixtures.inputGif)
          .resize(320, 80)
          .toFile(outputZoinks, function (err, info) {
            if (err) throw err;
            assert.strictEqual(true, info.size > 0);
            assert.strictEqual(sharp.format.magick.input.buffer ? 'gif' : 'png', info.format);
            assert.strictEqual(320, info.width);
            assert.strictEqual(80, info.height);
            rimraf(outputZoinks, done);
          });
      });
  
      it('Force JPEG format for PNG input', function (done) {
        sharp(fixtures.inputPng)
          .resize(320, 80)
          .jpeg()
          .toFile(outputZoinks, function (err, info) {
            if (err) throw err;
            assert.strictEqual(true, info.size > 0);
            assert.strictEqual('jpeg', info.format);
            assert.strictEqual(320, info.width);
            assert.strictEqual(80, info.height);
            rimraf(outputZoinks, done);
          });
      });
    });
  
    it('Input and output formats match when not forcing', function (done) {
      sharp(fixtures.inputJpg)
        .resize(320, 240)
        .png({ compressionLevel: 1, force: false })
        .toBuffer(function (err, data, info) {
          if (err) throw err;
          assert.strictEqual('jpeg', info.format);
          assert.strictEqual(320, info.width);
          assert.strictEqual(240, info.height);
          done();
        });
    });
  
    it('Can force output format with output chaining', function () {
      return sharp(fixtures.inputJpg)
        .resize(320, 240)
        .png({ force: true })
        .jpeg({ force: false })
        .toBuffer({ resolveWithObject: true })
        .then(function (out) {
          assert.strictEqual('png', out.info.format);
        });
    });
  
    it('toFormat=JPEG takes precedence over WebP extension', function (done) {
      const outputWebP = fixtures.path('output.webp');
      sharp(fixtures.inputPng)
        .jpeg()
        .toFile(outputWebP, function (err, info) {
          if (err) throw err;
          assert.strictEqual('jpeg', info.format);
          rimraf(outputWebP, done);
        });
    });
  
    it('toFormat=WebP takes precedence over JPEG extension', function (done) {
      sharp(fixtures.inputPng)
        .webp()
        .toFile(outputJpg, function (err, info) {
          if (err) throw err;
          assert.strictEqual('webp', info.format);
          done();
        });
    });
  
    it('Load Vips V file', function (done) {
      sharp(fixtures.inputV)
        .jpeg()
        .toBuffer(function (err, data, info) {
          if (err) throw err;
          assert.strictEqual(true, data.length > 0);
          assert.strictEqual('jpeg', info.format);
          assert.strictEqual(70, info.width);
          assert.strictEqual(60, info.height);
          fixtures.assertSimilar(fixtures.expected('vfile.jpg'), data, done);
        });
    });
  
    it('Save Vips V file', function (done) {
      const outputV = fixtures.path('output.v');
      sharp(fixtures.inputJpg)
        .extract({ left: 910, top: 1105, width: 70, height: 60 })
        .toFile(outputV, function (err, info) {
          if (err) throw err;
          assert.strictEqual(true, info.size > 0);
          assert.strictEqual('v', info.format);
          assert.strictEqual(70, info.width);
          assert.strictEqual(60, info.height);
          rimraf(outputV, done);
        });
    });
  
    describe('Limit pixel count of input image', () => {
      it('Invalid fails - negative', () => {
        assert.throws(() => {
          sharp({ limitInputPixels: -1 });
        });
      });
  
      it('Invalid fails - float', () => {
        assert.throws(() => {
          sharp({ limitInputPixels: 12.3 });
        });
      });
  
      it('Invalid fails - string', () => {
        assert.throws(() => {
          sharp({ limitInputPixels: 'fail' });
        });
      });
  
      it('Same size as input works', () =>
        sharp(fixtures.inputJpg)
          .metadata()
          .then(({ width, height }) =>
            sharp(fixtures.inputJpg, { limitInputPixels: width * height }).toBuffer()
          )
      );
  
      it('Disabling limit works', () =>
        sharp(fixtures.inputJpgLarge, { limitInputPixels: false })
          .resize(2)
          .toBuffer()
      );
  
      it('Enabling default limit works and fails with a large image', () =>
        sharp(fixtures.inputJpgLarge, { limitInputPixels: true })
          .toBuffer()
          .then(() => {
            assert.fail('Expected to fail');
          })
          .catch(err => {
            assert.strictEqual(err.message, 'Input image exceeds pixel limit');
          })
      );
  
      it('Smaller than input fails', () =>
        sharp(fixtures.inputJpg)
          .metadata()
          .then(({ width, height }) =>
            sharp(fixtures.inputJpg, { limitInputPixels: width * height - 1 })
              .toBuffer()
              .then(() => {
                assert.fail('Expected to fail');
              })
              .catch(err => {
                assert.strictEqual(err.message, 'Input image exceeds pixel limit');
              })
          )
      );
    });
  
    describe('Input options', function () {
      it('Option-less', function () {
        sharp();
      });
      it('Ignore unknown attribute', function () {
        sharp({ unknown: true });
      });
      it('undefined with options fails', function () {
        assert.throws(function () {
          sharp(undefined, {});
        }, /Unsupported input 'undefined' of type undefined when also providing options of type object/);
      });
      it('null with options fails', function () {
        assert.throws(function () {
          sharp(null, {});
        }, /Unsupported input 'null' of type object when also providing options of type object/);
      });
      it('Non-Object options fails', function () {
        assert.throws(function () {
          sharp('test', 'zoinks');
        }, /Invalid input options zoinks/);
      });
      it('Invalid density: string', function () {
        assert.throws(function () {
          sharp({ density: 'zoinks' });
        }, /Expected number between 1 and 100000 for density but received zoinks of type string/);
      });
      it('Setting animated property updates pages property', function () {
        assert.strictEqual(sharp({ animated: false }).options.input.pages, 1);
        assert.strictEqual(sharp({ animated: true }).options.input.pages, -1);
      });
      it('Invalid animated property throws', function () {
        assert.throws(function () {
          sharp({ animated: -1 });
        }, /Expected boolean for animated but received -1 of type number/);
      });
      it('Invalid page property throws', function () {
        assert.throws(function () {
          sharp({ page: -1 });
        }, /Expected integer between 0 and 100000 for page but received -1 of type number/);
      });
      it('Invalid pages property throws', function () {
        assert.throws(function () {
          sharp({ pages: '1' });
        }, /Expected integer between -1 and 100000 for pages but received 1 of type string/);
      });
      it('Valid level property', function () {
        sharp({ level: 1 });
      });
      it('Invalid level property (string) throws', function () {
        assert.throws(function () {
          sharp({ level: '1' });
        }, /Expected integer between 0 and 256 for level but received 1 of type string/);
      });
      it('Invalid level property (negative) throws', function () {
        assert.throws(function () {
          sharp({ level: -1 });
        }, /Expected integer between 0 and 256 for level but received -1 of type number/);
      });
      it('Valid subifd property', function () {
        sharp({ subifd: 1 });
      });
      it('Invalid subifd property (string) throws', function () {
        assert.throws(function () {
          sharp({ subifd: '1' });
        }, /Expected integer between -1 and 100000 for subifd but received 1 of type string/);
      });
      it('Invalid subifd property (float) throws', function () {
        assert.throws(function () {
          sharp({ subifd: 1.2 });
        }, /Expected integer between -1 and 100000 for subifd but received 1.2 of type number/);
      });
    });
  
    describe('create new image', function () {
      it('RGB', function (done) {
        const create = {
          width: 10,
          height: 20,
          channels: 3,
          background: { r: 0, g: 255, b: 0 }
        };
        sharp({ create: create })
          .jpeg()
          .toBuffer(function (err, data, info) {
            if (err) throw err;
            assert.strictEqual(create.width, info.width);
            assert.strictEqual(create.height, info.height);
            assert.strictEqual(create.channels, info.channels);
            assert.strictEqual('jpeg', info.format);
            fixtures.assertSimilar(fixtures.expected('create-rgb.jpg'), data, done);
          });
      });
      it('RGBA', function (done) {
        const create = {
          width: 20,
          height: 10,
          channels: 4,
          background: { r: 255, g: 0, b: 0, alpha: 128 }
        };
        sharp({ create: create })
          .png()
          .toBuffer(function (err, data, info) {
            if (err) throw err;
            assert.strictEqual(create.width, info.width);
            assert.strictEqual(create.height, info.height);
            assert.strictEqual(create.channels, info.channels);
            assert.strictEqual('png', info.format);
            fixtures.assertSimilar(fixtures.expected('create-rgba.png'), data, done);
          });
      });
      it('Invalid channels', function () {
        const create = {
          width: 10,
          height: 20,
          channels: 2,
          background: { r: 0, g: 0, b: 0 }
        };
        assert.throws(function () {
          sharp({ create: create });
        });
      });
      it('Missing background', function () {
        const create = {
          width: 10,
          height: 20,
          channels: 3
        };
        assert.throws(function () {
          sharp({ create: create });
        });
      });
    });
  
    it('Queue length change events', function (done) {
      let eventCounter = 0;
      const queueListener = function (queueLength) {
        assert.strictEqual(true, queueLength === 0 || queueLength === 1);
        eventCounter++;
      };
      sharp.queue.on('change', queueListener);
      sharp(fixtures.inputJpg)
        .resize(320, 240)
        .toBuffer(function (err) {
          process.nextTick(function () {
            sharp.queue.removeListener('change', queueListener);
            if (err) throw err;
            assert.strictEqual(2, eventCounter);
            done();
          });
        });
    });
  
    it('Info event data', function (done) {
      const readable = fs.createReadStream(fixtures.inputJPGBig);
      const inPipeline = sharp()
        .resize(840, 472)
        .raw()
        .on('info', function (info) {
          assert.strictEqual(840, info.width);
          assert.strictEqual(472, info.height);
          assert.strictEqual(3, info.channels);
        });
      const badPipeline = sharp({ raw: { width: 840, height: 500, channels: 3 } })
        .toFormat('jpeg')
        .toBuffer(function (err, data, info) {
          assert.strictEqual(err.message.indexOf('memory area too small') > 0, true);
          const readable = fs.createReadStream(fixtures.inputJPGBig);
          const inPipeline = sharp()
            .resize(840, 472)
            .raw();
          const goodPipeline = sharp({ raw: { width: 840, height: 472, channels: 3 } })
            .toFormat('jpeg')
            .toBuffer(function (err, data, info) {
              if (err) throw err;
              done();
            });
          readable.pipe(inPipeline).pipe(goodPipeline);
        });
      readable.pipe(inPipeline).pipe(badPipeline);
    });
  });
  