• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23const common = require('../common');
24const assert = require('assert');
25const PassThrough = require('_stream_passthrough');
26const Transform = require('_stream_transform');
27
28{
29  // Verify writable side consumption
30  const tx = new Transform({
31    highWaterMark: 10
32  });
33
34  let transformed = 0;
35  tx._transform = function(chunk, encoding, cb) {
36    transformed += chunk.length;
37    tx.push(chunk);
38    cb();
39  };
40
41  for (let i = 1; i <= 10; i++) {
42    tx.write(Buffer.allocUnsafe(i));
43  }
44  tx.end();
45
46  assert.strictEqual(tx.readableLength, 10);
47  assert.strictEqual(transformed, 10);
48  assert.strictEqual(tx._transformState.writechunk.length, 5);
49  assert.deepStrictEqual(tx.writableBuffer.map(function(c) {
50    return c.chunk.length;
51  }), [6, 7, 8, 9, 10]);
52}
53
54{
55  // Verify passthrough behavior
56  const pt = new PassThrough();
57
58  pt.write(Buffer.from('foog'));
59  pt.write(Buffer.from('bark'));
60  pt.write(Buffer.from('bazy'));
61  pt.write(Buffer.from('kuel'));
62  pt.end();
63
64  assert.strictEqual(pt.read(5).toString(), 'foogb');
65  assert.strictEqual(pt.read(5).toString(), 'arkba');
66  assert.strictEqual(pt.read(5).toString(), 'zykue');
67  assert.strictEqual(pt.read(5).toString(), 'l');
68}
69
70{
71  // Verify object passthrough behavior
72  const pt = new PassThrough({ objectMode: true });
73
74  pt.write(1);
75  pt.write(true);
76  pt.write(false);
77  pt.write(0);
78  pt.write('foo');
79  pt.write('');
80  pt.write({ a: 'b' });
81  pt.end();
82
83  assert.strictEqual(pt.read(), 1);
84  assert.strictEqual(pt.read(), true);
85  assert.strictEqual(pt.read(), false);
86  assert.strictEqual(pt.read(), 0);
87  assert.strictEqual(pt.read(), 'foo');
88  assert.strictEqual(pt.read(), '');
89  assert.deepStrictEqual(pt.read(), { a: 'b' });
90}
91
92{
93  // Verify passthrough constructor behavior
94  const pt = PassThrough();
95
96  assert(pt instanceof PassThrough);
97}
98
99{
100  // Verify transform constructor behavior
101  const pt = Transform();
102
103  assert(pt instanceof Transform);
104}
105
106{
107  // Perform a simple transform
108  const pt = new Transform();
109  pt._transform = function(c, e, cb) {
110    const ret = Buffer.alloc(c.length, 'x');
111    pt.push(ret);
112    cb();
113  };
114
115  pt.write(Buffer.from('foog'));
116  pt.write(Buffer.from('bark'));
117  pt.write(Buffer.from('bazy'));
118  pt.write(Buffer.from('kuel'));
119  pt.end();
120
121  assert.strictEqual(pt.read(5).toString(), 'xxxxx');
122  assert.strictEqual(pt.read(5).toString(), 'xxxxx');
123  assert.strictEqual(pt.read(5).toString(), 'xxxxx');
124  assert.strictEqual(pt.read(5).toString(), 'x');
125}
126
127{
128  // Verify simple object transform
129  const pt = new Transform({ objectMode: true });
130  pt._transform = function(c, e, cb) {
131    pt.push(JSON.stringify(c));
132    cb();
133  };
134
135  pt.write(1);
136  pt.write(true);
137  pt.write(false);
138  pt.write(0);
139  pt.write('foo');
140  pt.write('');
141  pt.write({ a: 'b' });
142  pt.end();
143
144  assert.strictEqual(pt.read(), '1');
145  assert.strictEqual(pt.read(), 'true');
146  assert.strictEqual(pt.read(), 'false');
147  assert.strictEqual(pt.read(), '0');
148  assert.strictEqual(pt.read(), '"foo"');
149  assert.strictEqual(pt.read(), '""');
150  assert.strictEqual(pt.read(), '{"a":"b"}');
151}
152
153{
154  // Verify async passthrough
155  const pt = new Transform();
156  pt._transform = function(chunk, encoding, cb) {
157    setTimeout(function() {
158      pt.push(chunk);
159      cb();
160    }, 10);
161  };
162
163  pt.write(Buffer.from('foog'));
164  pt.write(Buffer.from('bark'));
165  pt.write(Buffer.from('bazy'));
166  pt.write(Buffer.from('kuel'));
167  pt.end();
168
169  pt.on('finish', common.mustCall(function() {
170    assert.strictEqual(pt.read(5).toString(), 'foogb');
171    assert.strictEqual(pt.read(5).toString(), 'arkba');
172    assert.strictEqual(pt.read(5).toString(), 'zykue');
173    assert.strictEqual(pt.read(5).toString(), 'l');
174  }));
175}
176
177{
178  // Verify asymmetric transform (expand)
179  const pt = new Transform();
180
181  // Emit each chunk 2 times.
182  pt._transform = function(chunk, encoding, cb) {
183    setTimeout(function() {
184      pt.push(chunk);
185      setTimeout(function() {
186        pt.push(chunk);
187        cb();
188      }, 10);
189    }, 10);
190  };
191
192  pt.write(Buffer.from('foog'));
193  pt.write(Buffer.from('bark'));
194  pt.write(Buffer.from('bazy'));
195  pt.write(Buffer.from('kuel'));
196  pt.end();
197
198  pt.on('finish', common.mustCall(function() {
199    assert.strictEqual(pt.read(5).toString(), 'foogf');
200    assert.strictEqual(pt.read(5).toString(), 'oogba');
201    assert.strictEqual(pt.read(5).toString(), 'rkbar');
202    assert.strictEqual(pt.read(5).toString(), 'kbazy');
203    assert.strictEqual(pt.read(5).toString(), 'bazyk');
204    assert.strictEqual(pt.read(5).toString(), 'uelku');
205    assert.strictEqual(pt.read(5).toString(), 'el');
206  }));
207}
208
209{
210  // Verify asymmetric transform (compress)
211  const pt = new Transform();
212
213  // Each output is the first char of 3 consecutive chunks,
214  // or whatever's left.
215  pt.state = '';
216
217  pt._transform = function(chunk, encoding, cb) {
218    if (!chunk)
219      chunk = '';
220    const s = chunk.toString();
221    setTimeout(() => {
222      this.state += s.charAt(0);
223      if (this.state.length === 3) {
224        pt.push(Buffer.from(this.state));
225        this.state = '';
226      }
227      cb();
228    }, 10);
229  };
230
231  pt._flush = function(cb) {
232    // Just output whatever we have.
233    pt.push(Buffer.from(this.state));
234    this.state = '';
235    cb();
236  };
237
238  pt.write(Buffer.from('aaaa'));
239  pt.write(Buffer.from('bbbb'));
240  pt.write(Buffer.from('cccc'));
241  pt.write(Buffer.from('dddd'));
242  pt.write(Buffer.from('eeee'));
243  pt.write(Buffer.from('aaaa'));
244  pt.write(Buffer.from('bbbb'));
245  pt.write(Buffer.from('cccc'));
246  pt.write(Buffer.from('dddd'));
247  pt.write(Buffer.from('eeee'));
248  pt.write(Buffer.from('aaaa'));
249  pt.write(Buffer.from('bbbb'));
250  pt.write(Buffer.from('cccc'));
251  pt.write(Buffer.from('dddd'));
252  pt.end();
253
254  // 'abcdeabcdeabcd'
255  pt.on('finish', common.mustCall(function() {
256    assert.strictEqual(pt.read(5).toString(), 'abcde');
257    assert.strictEqual(pt.read(5).toString(), 'abcde');
258    assert.strictEqual(pt.read(5).toString(), 'abcd');
259  }));
260}
261
262// This tests for a stall when data is written to a full stream
263// that has empty transforms.
264{
265  // Verify complex transform behavior
266  let count = 0;
267  let saved = null;
268  const pt = new Transform({ highWaterMark: 3 });
269  pt._transform = function(c, e, cb) {
270    if (count++ === 1)
271      saved = c;
272    else {
273      if (saved) {
274        pt.push(saved);
275        saved = null;
276      }
277      pt.push(c);
278    }
279
280    cb();
281  };
282
283  pt.once('readable', function() {
284    process.nextTick(function() {
285      pt.write(Buffer.from('d'));
286      pt.write(Buffer.from('ef'), common.mustCall(function() {
287        pt.end();
288      }));
289      assert.strictEqual(pt.read().toString(), 'abcdef');
290      assert.strictEqual(pt.read(), null);
291    });
292  });
293
294  pt.write(Buffer.from('abc'));
295}
296
297
298{
299  // Verify passthrough event emission
300  const pt = new PassThrough();
301  let emits = 0;
302  pt.on('readable', function() {
303    emits++;
304  });
305
306  pt.write(Buffer.from('foog'));
307  pt.write(Buffer.from('bark'));
308
309  assert.strictEqual(emits, 0);
310  assert.strictEqual(pt.read(5).toString(), 'foogb');
311  assert.strictEqual(String(pt.read(5)), 'null');
312  assert.strictEqual(emits, 0);
313
314  pt.write(Buffer.from('bazy'));
315  pt.write(Buffer.from('kuel'));
316
317  assert.strictEqual(emits, 0);
318  assert.strictEqual(pt.read(5).toString(), 'arkba');
319  assert.strictEqual(pt.read(5).toString(), 'zykue');
320  assert.strictEqual(pt.read(5), null);
321
322  pt.end();
323
324  assert.strictEqual(emits, 1);
325  assert.strictEqual(pt.read(5).toString(), 'l');
326  assert.strictEqual(pt.read(5), null);
327  assert.strictEqual(emits, 1);
328}
329
330{
331  // Verify passthrough event emission reordering
332  const pt = new PassThrough();
333  let emits = 0;
334  pt.on('readable', function() {
335    emits++;
336  });
337
338  pt.write(Buffer.from('foog'));
339  pt.write(Buffer.from('bark'));
340
341  assert.strictEqual(emits, 0);
342  assert.strictEqual(pt.read(5).toString(), 'foogb');
343  assert.strictEqual(pt.read(5), null);
344
345  pt.once('readable', common.mustCall(function() {
346    assert.strictEqual(pt.read(5).toString(), 'arkba');
347    assert.strictEqual(pt.read(5), null);
348
349    pt.once('readable', common.mustCall(function() {
350      assert.strictEqual(pt.read(5).toString(), 'zykue');
351      assert.strictEqual(pt.read(5), null);
352      pt.once('readable', common.mustCall(function() {
353        assert.strictEqual(pt.read(5).toString(), 'l');
354        assert.strictEqual(pt.read(5), null);
355        assert.strictEqual(emits, 3);
356      }));
357      pt.end();
358    }));
359    pt.write(Buffer.from('kuel'));
360  }));
361
362  pt.write(Buffer.from('bazy'));
363}
364
365{
366  // Verify passthrough facade
367  const pt = new PassThrough();
368  const datas = [];
369  pt.on('data', function(chunk) {
370    datas.push(chunk.toString());
371  });
372
373  pt.on('end', common.mustCall(function() {
374    assert.deepStrictEqual(datas, ['foog', 'bark', 'bazy', 'kuel']);
375  }));
376
377  pt.write(Buffer.from('foog'));
378  setTimeout(function() {
379    pt.write(Buffer.from('bark'));
380    setTimeout(function() {
381      pt.write(Buffer.from('bazy'));
382      setTimeout(function() {
383        pt.write(Buffer.from('kuel'));
384        setTimeout(function() {
385          pt.end();
386        }, 10);
387      }, 10);
388    }, 10);
389  }, 10);
390}
391
392{
393  // Verify object transform (JSON parse)
394  const jp = new Transform({ objectMode: true });
395  jp._transform = function(data, encoding, cb) {
396    try {
397      jp.push(JSON.parse(data));
398      cb();
399    } catch (er) {
400      cb(er);
401    }
402  };
403
404  // Anything except null/undefined is fine.
405  // those are "magic" in the stream API, because they signal EOF.
406  const objects = [
407    { foo: 'bar' },
408    100,
409    'string',
410    { nested: { things: [ { foo: 'bar' }, 100, 'string' ] } },
411  ];
412
413  let ended = false;
414  jp.on('end', function() {
415    ended = true;
416  });
417
418  objects.forEach(function(obj) {
419    jp.write(JSON.stringify(obj));
420    const res = jp.read();
421    assert.deepStrictEqual(res, obj);
422  });
423
424  jp.end();
425  // Read one more time to get the 'end' event
426  jp.read();
427
428  process.nextTick(common.mustCall(function() {
429    assert.strictEqual(ended, true);
430  }));
431}
432
433{
434  // Verify object transform (JSON stringify)
435  const js = new Transform({ objectMode: true });
436  js._transform = function(data, encoding, cb) {
437    try {
438      js.push(JSON.stringify(data));
439      cb();
440    } catch (er) {
441      cb(er);
442    }
443  };
444
445  // Anything except null/undefined is fine.
446  // those are "magic" in the stream API, because they signal EOF.
447  const objects = [
448    { foo: 'bar' },
449    100,
450    'string',
451    { nested: { things: [ { foo: 'bar' }, 100, 'string' ] } },
452  ];
453
454  let ended = false;
455  js.on('end', function() {
456    ended = true;
457  });
458
459  objects.forEach(function(obj) {
460    js.write(obj);
461    const res = js.read();
462    assert.strictEqual(res, JSON.stringify(obj));
463  });
464
465  js.end();
466  // Read one more time to get the 'end' event
467  js.read();
468
469  process.nextTick(common.mustCall(function() {
470    assert.strictEqual(ended, true);
471  }));
472}
473