text: 'use strict';
  
  const common = require('../common');
  const { Duplex } = require('stream');
  const assert = require('assert');
  
  {
    const duplex = new Duplex({
      write(chunk, enc, cb) { cb(); },
      read() {}
    });
  
    duplex.resume();
  
    duplex.on('end', common.mustNotCall());
    duplex.on('finish', common.mustNotCall());
    duplex.on('close', common.mustCall());
  
    duplex.destroy();
    assert.strictEqual(duplex.destroyed, true);
  }
  
  {
    const duplex = new Duplex({
      write(chunk, enc, cb) { cb(); },
      read() {}
    });
    duplex.resume();
  
    const expected = new Error('kaboom');
  
    duplex.on('end', common.mustNotCall());
    duplex.on('finish', common.mustNotCall());
    duplex.on('error', common.mustCall((err) => {
      assert.strictEqual(err, expected);
    }));
  
    duplex.destroy(expected);
    assert.strictEqual(duplex.destroyed, true);
  }
  
  {
    const duplex = new Duplex({
      write(chunk, enc, cb) { cb(); },
      read() {}
    });
  
    duplex._destroy = common.mustCall(function(err, cb) {
      assert.strictEqual(err, expected);
      cb(err);
    });
  
    const expected = new Error('kaboom');
  
    duplex.on('finish', common.mustNotCall('no finish event'));
    duplex.on('error', common.mustCall((err) => {
      assert.strictEqual(err, expected);
    }));
  
    duplex.destroy(expected);
    assert.strictEqual(duplex.destroyed, true);
  }
  
  {
    const expected = new Error('kaboom');
    const duplex = new Duplex({
      write(chunk, enc, cb) { cb(); },
      read() {},
      destroy: common.mustCall(function(err, cb) {
        assert.strictEqual(err, expected);
        cb();
      })
    });
    duplex.resume();
  
    duplex.on('end', common.mustNotCall('no end event'));
    duplex.on('finish', common.mustNotCall('no finish event'));
  
    // Error is swallowed by the custom _destroy
    duplex.on('error', common.mustNotCall('no error event'));
    duplex.on('close', common.mustCall());
  
    duplex.destroy(expected);
    assert.strictEqual(duplex.destroyed, true);
  }
  
  {
    const duplex = new Duplex({
      write(chunk, enc, cb) { cb(); },
      read() {}
    });
  
    duplex._destroy = common.mustCall(function(err, cb) {
      assert.strictEqual(err, null);
      cb();
    });
  
    duplex.destroy();
    assert.strictEqual(duplex.destroyed, true);
  }
  
  {
    const duplex = new Duplex({
      write(chunk, enc, cb) { cb(); },
      read() {}
    });
    duplex.resume();
  
    duplex._destroy = common.mustCall(function(err, cb) {
      assert.strictEqual(err, null);
      process.nextTick(() => {
        this.push(null);
        this.end();
        cb();
      });
    });
  
    const fail = common.mustNotCall('no finish or end event');
  
    duplex.on('finish', fail);
    duplex.on('end', fail);
  
    duplex.destroy();
  
    duplex.removeListener('end', fail);
    duplex.removeListener('finish', fail);
    duplex.on('end', common.mustCall());
    duplex.on('finish', common.mustCall());
    assert.strictEqual(duplex.destroyed, true);
  }
  
  {
    const duplex = new Duplex({
      write(chunk, enc, cb) { cb(); },
      read() {}
    });
  
    const expected = new Error('kaboom');
  
    duplex._destroy = common.mustCall(function(err, cb) {
      assert.strictEqual(err, null);
      cb(expected);
    });
  
    duplex.on('finish', common.mustNotCall('no finish event'));
    duplex.on('end', common.mustNotCall('no end event'));
    duplex.on('error', common.mustCall((err) => {
      assert.strictEqual(err, expected);
    }));
  
    duplex.destroy();
    assert.strictEqual(duplex.destroyed, true);
  }
  
  {
    const duplex = new Duplex({
      write(chunk, enc, cb) { cb(); },
      read() {},
      allowHalfOpen: true
    });
    duplex.resume();
  
    duplex.on('finish', common.mustNotCall());
    duplex.on('end', common.mustNotCall());
  
    duplex.destroy();
    assert.strictEqual(duplex.destroyed, true);
  }
  
  {
    const duplex = new Duplex({
      write(chunk, enc, cb) { cb(); },
      read() {},
    });
  
    duplex.destroyed = true;
    assert.strictEqual(duplex.destroyed, true);
  
    // The internal destroy() mechanism should not be triggered
    duplex.on('finish', common.mustNotCall());
    duplex.on('end', common.mustNotCall());
    duplex.destroy();
  }
  
  {
    function MyDuplex() {
      assert.strictEqual(this.destroyed, false);
      this.destroyed = false;
      Duplex.call(this);
    }
  
    Object.setPrototypeOf(MyDuplex.prototype, Duplex.prototype);
    Object.setPrototypeOf(MyDuplex, Duplex);
  
    new MyDuplex();
  }
  
  {
    const duplex = new Duplex({
      writable: false,
      autoDestroy: true,
      write(chunk, enc, cb) { cb(); },
      read() {},
    });
    duplex.push(null);
    duplex.resume();
    duplex.on('close', common.mustCall());
  }
  
  {
    const duplex = new Duplex({
      readable: false,
      autoDestroy: true,
      write(chunk, enc, cb) { cb(); },
      read() {},
    });
    duplex.end();
    duplex.on('close', common.mustCall());
  }
  
  {
    const duplex = new Duplex({
      allowHalfOpen: false,
      autoDestroy: true,
      write(chunk, enc, cb) { cb(); },
      read() {},
    });
    duplex.push(null);
    duplex.resume();
    const orgEnd = duplex.end;
    duplex.end = common.mustNotCall();
    duplex.on('end', () => {
      // Ensure end() is called in next tick to allow
      // any pending writes to be invoked first.
      process.nextTick(() => {
        duplex.end = common.mustCall(orgEnd);
      });
    });
    duplex.on('close', common.mustCall());
  }
  