• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const common = require('../common');
4const { Writable } = require('stream');
5const assert = require('assert');
6
7{
8  const write = new Writable({
9    write(chunk, enc, cb) { cb(); }
10  });
11
12  write.on('finish', common.mustNotCall());
13  write.on('close', common.mustCall());
14
15  write.destroy();
16  assert.strictEqual(write.destroyed, true);
17}
18
19{
20  const write = new Writable({
21    write(chunk, enc, cb) {
22      this.destroy(new Error('asd'));
23      cb();
24    }
25  });
26
27  write.on('error', common.mustCall());
28  write.on('finish', common.mustNotCall());
29  write.end('asd');
30  assert.strictEqual(write.destroyed, true);
31}
32
33{
34  const write = new Writable({
35    write(chunk, enc, cb) { cb(); }
36  });
37
38  const expected = new Error('kaboom');
39
40  write.on('finish', common.mustNotCall());
41  write.on('close', common.mustCall());
42  write.on('error', common.mustCall((err) => {
43    assert.strictEqual(err, expected);
44  }));
45
46  write.destroy(expected);
47  assert.strictEqual(write.destroyed, true);
48}
49
50{
51  const write = new Writable({
52    write(chunk, enc, cb) { cb(); }
53  });
54
55  write._destroy = function(err, cb) {
56    assert.strictEqual(err, expected);
57    cb(err);
58  };
59
60  const expected = new Error('kaboom');
61
62  write.on('finish', common.mustNotCall('no finish event'));
63  write.on('close', common.mustCall());
64  write.on('error', common.mustCall((err) => {
65    assert.strictEqual(err, expected);
66  }));
67
68  write.destroy(expected);
69  assert.strictEqual(write.destroyed, true);
70}
71
72{
73  const write = new Writable({
74    write(chunk, enc, cb) { cb(); },
75    destroy: common.mustCall(function(err, cb) {
76      assert.strictEqual(err, expected);
77      cb();
78    })
79  });
80
81  const expected = new Error('kaboom');
82
83  write.on('finish', common.mustNotCall('no finish event'));
84  write.on('close', common.mustCall());
85
86  // Error is swallowed by the custom _destroy
87  write.on('error', common.mustNotCall('no error event'));
88
89  write.destroy(expected);
90  assert.strictEqual(write.destroyed, true);
91}
92
93{
94  const write = new Writable({
95    write(chunk, enc, cb) { cb(); }
96  });
97
98  write._destroy = common.mustCall(function(err, cb) {
99    assert.strictEqual(err, null);
100    cb();
101  });
102
103  write.destroy();
104  assert.strictEqual(write.destroyed, true);
105}
106
107{
108  const write = new Writable({
109    write(chunk, enc, cb) { cb(); }
110  });
111
112  write._destroy = common.mustCall(function(err, cb) {
113    assert.strictEqual(err, null);
114    process.nextTick(() => {
115      this.end();
116      cb();
117    });
118  });
119
120  const fail = common.mustNotCall('no finish event');
121
122  write.on('finish', fail);
123  write.on('close', common.mustCall());
124
125  write.destroy();
126
127  write.removeListener('finish', fail);
128  write.on('finish', common.mustCall());
129  assert.strictEqual(write.destroyed, true);
130}
131
132{
133  const write = new Writable({
134    write(chunk, enc, cb) { cb(); }
135  });
136
137  const expected = new Error('kaboom');
138
139  write._destroy = common.mustCall(function(err, cb) {
140    assert.strictEqual(err, null);
141    cb(expected);
142  });
143
144  write.on('close', common.mustCall());
145  write.on('finish', common.mustNotCall('no finish event'));
146  write.on('error', common.mustCall((err) => {
147    assert.strictEqual(err, expected);
148  }));
149
150  write.destroy();
151  assert.strictEqual(write.destroyed, true);
152}
153
154{
155  // double error case
156  const write = new Writable({
157    write(chunk, enc, cb) { cb(); }
158  });
159
160  let ticked = false;
161  write.on('close', common.mustCall(() => {
162    assert.strictEqual(ticked, true);
163  }));
164  write.on('error', common.mustCall((err) => {
165    assert.strictEqual(ticked, true);
166    assert.strictEqual(err.message, 'kaboom 1');
167    assert.strictEqual(write._writableState.errorEmitted, true);
168  }));
169
170  const expected = new Error('kaboom 1');
171  write.destroy(expected);
172  write.destroy(new Error('kaboom 2'));
173  assert.strictEqual(write._writableState.errored, expected);
174  assert.strictEqual(write._writableState.errorEmitted, false);
175  assert.strictEqual(write.destroyed, true);
176  ticked = true;
177}
178
179{
180  const writable = new Writable({
181    destroy: common.mustCall(function(err, cb) {
182      process.nextTick(cb, new Error('kaboom 1'));
183    }),
184    write(chunk, enc, cb) {
185      cb();
186    }
187  });
188
189  let ticked = false;
190  writable.on('close', common.mustCall(() => {
191    writable.on('error', common.mustNotCall());
192    writable.destroy(new Error('hello'));
193    assert.strictEqual(ticked, true);
194    assert.strictEqual(writable._writableState.errorEmitted, true);
195  }));
196  writable.on('error', common.mustCall((err) => {
197    assert.strictEqual(ticked, true);
198    assert.strictEqual(err.message, 'kaboom 1');
199    assert.strictEqual(writable._writableState.errorEmitted, true);
200  }));
201
202  writable.destroy();
203  assert.strictEqual(writable.destroyed, true);
204  assert.strictEqual(writable._writableState.errored, null);
205  assert.strictEqual(writable._writableState.errorEmitted, false);
206
207  // Test case where `writable.destroy()` is called again with an error before
208  // the `_destroy()` callback is called.
209  writable.destroy(new Error('kaboom 2'));
210  assert.strictEqual(writable._writableState.errorEmitted, false);
211  assert.strictEqual(writable._writableState.errored, null);
212
213  ticked = true;
214}
215
216{
217  const write = new Writable({
218    write(chunk, enc, cb) { cb(); }
219  });
220
221  write.destroyed = true;
222  assert.strictEqual(write.destroyed, true);
223
224  // The internal destroy() mechanism should not be triggered
225  write.on('close', common.mustNotCall());
226  write.destroy();
227}
228
229{
230  function MyWritable() {
231    assert.strictEqual(this.destroyed, false);
232    this.destroyed = false;
233    Writable.call(this);
234  }
235
236  Object.setPrototypeOf(MyWritable.prototype, Writable.prototype);
237  Object.setPrototypeOf(MyWritable, Writable);
238
239  new MyWritable();
240}
241
242{
243  // Destroy and destroy callback
244  const write = new Writable({
245    write(chunk, enc, cb) { cb(); }
246  });
247
248  write.destroy();
249
250  const expected = new Error('kaboom');
251
252  write.destroy(expected, common.mustCall((err) => {
253    assert.strictEqual(err, undefined);
254  }));
255}
256
257{
258  // Checks that `._undestroy()` restores the state so that `final` will be
259  // called again.
260  const write = new Writable({
261    write: common.mustNotCall(),
262    final: common.mustCall((cb) => cb(), 2),
263    autoDestroy: true
264  });
265
266  write.end();
267  write.once('close', common.mustCall(() => {
268    write._undestroy();
269    write.end();
270  }));
271}
272
273{
274  const write = new Writable();
275
276  write.destroy();
277  write.on('error', common.mustNotCall());
278  write.write('asd', common.expectsError({
279    name: 'Error',
280    code: 'ERR_STREAM_DESTROYED',
281    message: 'Cannot call write after a stream was destroyed'
282  }));
283}
284
285{
286  const write = new Writable({
287    write(chunk, enc, cb) { cb(); }
288  });
289
290  write.on('error', common.mustNotCall());
291
292  write.cork();
293  write.write('asd', common.mustCall());
294  write.uncork();
295
296  write.cork();
297  write.write('asd', common.expectsError({
298    name: 'Error',
299    code: 'ERR_STREAM_DESTROYED',
300    message: 'Cannot call write after a stream was destroyed'
301  }));
302  write.destroy();
303  write.write('asd', common.expectsError({
304    name: 'Error',
305    code: 'ERR_STREAM_DESTROYED',
306    message: 'Cannot call write after a stream was destroyed'
307  }));
308  write.uncork();
309}
310
311{
312  // Call end(cb) after error & destroy
313
314  const write = new Writable({
315    write(chunk, enc, cb) { cb(new Error('asd')); }
316  });
317  write.on('error', common.mustCall(() => {
318    write.destroy();
319    let ticked = false;
320    write.end(common.mustCall((err) => {
321      assert.strictEqual(ticked, true);
322      assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED');
323    }));
324    ticked = true;
325  }));
326  write.write('asd');
327}
328
329{
330  // Call end(cb) after finish & destroy
331
332  const write = new Writable({
333    write(chunk, enc, cb) { cb(); }
334  });
335  write.on('finish', common.mustCall(() => {
336    write.destroy();
337    let ticked = false;
338    write.end(common.mustCall((err) => {
339      assert.strictEqual(ticked, true);
340      assert.strictEqual(err.code, 'ERR_STREAM_ALREADY_FINISHED');
341    }));
342    ticked = true;
343  }));
344  write.end();
345}
346
347{
348  // Call end(cb) after error & destroy and don't trigger
349  // unhandled exception.
350
351  const write = new Writable({
352    write(chunk, enc, cb) { process.nextTick(cb); }
353  });
354  write.once('error', common.mustCall((err) => {
355    assert.strictEqual(err.message, 'asd');
356  }));
357  write.end('asd', common.mustCall((err) => {
358    assert.strictEqual(err.message, 'asd');
359  }));
360  write.destroy(new Error('asd'));
361}
362
363{
364  // Call buffered write callback with error
365
366  const write = new Writable({
367    write(chunk, enc, cb) {
368      process.nextTick(cb, new Error('asd'));
369    },
370    autoDestroy: false
371  });
372  write.cork();
373  write.write('asd', common.mustCall((err) => {
374    assert.strictEqual(err.message, 'asd');
375  }));
376  write.write('asd', common.mustCall((err) => {
377    assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED');
378  }));
379  write.on('error', common.mustCall((err) => {
380    assert.strictEqual(err.message, 'asd');
381  }));
382  write.uncork();
383}
384
385{
386  // Ensure callback order.
387
388  let state = 0;
389  const write = new Writable({
390    write(chunk, enc, cb) {
391      // `setImmediate()` is used on purpose to ensure the callback is called
392      // after `process.nextTick()` callbacks.
393      setImmediate(cb);
394    }
395  });
396  write.write('asd', common.mustCall(() => {
397    assert.strictEqual(state++, 0);
398  }));
399  write.write('asd', common.mustCall((err) => {
400    assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED');
401    assert.strictEqual(state++, 1);
402  }));
403  write.destroy();
404}
405
406{
407  const write = new Writable({
408    autoDestroy: false,
409    write(chunk, enc, cb) {
410      cb();
411      cb();
412    }
413  });
414
415  write.on('error', common.mustCall(() => {
416    assert(write._writableState.errored);
417  }));
418  write.write('asd');
419}
420
421{
422  // Destroy twice
423  const write = new Writable({
424    write(chunk, enc, cb) { cb(); }
425  });
426
427  write.end(common.mustCall());
428  write.destroy();
429  write.destroy();
430}
431