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'; 23 24const common = require('../common'); 25const { Readable, Writable } = require('stream'); 26const assert = require('assert'); 27 28function toArray(callback) { 29 const stream = new Writable({ objectMode: true }); 30 const list = []; 31 stream.write = function(chunk) { 32 list.push(chunk); 33 }; 34 35 stream.end = common.mustCall(function() { 36 callback(list); 37 }); 38 39 return stream; 40} 41 42function fromArray(list) { 43 const r = new Readable({ objectMode: true }); 44 r._read = common.mustNotCall(); 45 list.forEach(function(chunk) { 46 r.push(chunk); 47 }); 48 r.push(null); 49 50 return r; 51} 52 53{ 54 // Verify that objects can be read from the stream 55 const r = fromArray([{ one: '1' }, { two: '2' }]); 56 57 const v1 = r.read(); 58 const v2 = r.read(); 59 const v3 = r.read(); 60 61 assert.deepStrictEqual(v1, { one: '1' }); 62 assert.deepStrictEqual(v2, { two: '2' }); 63 assert.strictEqual(v3, null); 64} 65 66{ 67 // Verify that objects can be piped into the stream 68 const r = fromArray([{ one: '1' }, { two: '2' }]); 69 70 r.pipe(toArray(common.mustCall(function(list) { 71 assert.deepStrictEqual(list, [ 72 { one: '1' }, 73 { two: '2' }, 74 ]); 75 }))); 76} 77 78{ 79 // Verify that read(n) is ignored 80 const r = fromArray([{ one: '1' }, { two: '2' }]); 81 const value = r.read(2); 82 83 assert.deepStrictEqual(value, { one: '1' }); 84} 85 86{ 87 // Verify that objects can be synchronously read 88 const r = new Readable({ objectMode: true }); 89 const list = [{ one: '1' }, { two: '2' }]; 90 r._read = function(n) { 91 const item = list.shift(); 92 r.push(item || null); 93 }; 94 95 r.pipe(toArray(common.mustCall(function(list) { 96 assert.deepStrictEqual(list, [ 97 { one: '1' }, 98 { two: '2' }, 99 ]); 100 }))); 101} 102 103{ 104 // Verify that objects can be asynchronously read 105 const r = new Readable({ objectMode: true }); 106 const list = [{ one: '1' }, { two: '2' }]; 107 r._read = function(n) { 108 const item = list.shift(); 109 process.nextTick(function() { 110 r.push(item || null); 111 }); 112 }; 113 114 r.pipe(toArray(common.mustCall(function(list) { 115 assert.deepStrictEqual(list, [ 116 { one: '1' }, 117 { two: '2' }, 118 ]); 119 }))); 120} 121 122{ 123 // Verify that strings can be read as objects 124 const r = new Readable({ 125 objectMode: true 126 }); 127 r._read = common.mustNotCall(); 128 const list = ['one', 'two', 'three']; 129 list.forEach(function(str) { 130 r.push(str); 131 }); 132 r.push(null); 133 134 r.pipe(toArray(common.mustCall(function(array) { 135 assert.deepStrictEqual(array, list); 136 }))); 137} 138 139{ 140 // Verify read(0) behavior for object streams 141 const r = new Readable({ 142 objectMode: true 143 }); 144 r._read = common.mustNotCall(); 145 146 r.push('foobar'); 147 r.push(null); 148 149 r.pipe(toArray(common.mustCall(function(array) { 150 assert.deepStrictEqual(array, ['foobar']); 151 }))); 152} 153 154{ 155 // Verify the behavior of pushing falsey values 156 const r = new Readable({ 157 objectMode: true 158 }); 159 r._read = common.mustNotCall(); 160 161 r.push(false); 162 r.push(0); 163 r.push(''); 164 r.push(null); 165 166 r.pipe(toArray(common.mustCall(function(array) { 167 assert.deepStrictEqual(array, [false, 0, '']); 168 }))); 169} 170 171{ 172 // Verify high watermark _read() behavior 173 const r = new Readable({ 174 highWaterMark: 6, 175 objectMode: true 176 }); 177 let calls = 0; 178 const list = ['1', '2', '3', '4', '5', '6', '7', '8']; 179 180 r._read = function(n) { 181 calls++; 182 }; 183 184 list.forEach(function(c) { 185 r.push(c); 186 }); 187 188 const v = r.read(); 189 190 assert.strictEqual(calls, 0); 191 assert.strictEqual(v, '1'); 192 193 const v2 = r.read(); 194 assert.strictEqual(v2, '2'); 195 196 const v3 = r.read(); 197 assert.strictEqual(v3, '3'); 198 199 assert.strictEqual(calls, 1); 200} 201 202{ 203 // Verify high watermark push behavior 204 const r = new Readable({ 205 highWaterMark: 6, 206 objectMode: true 207 }); 208 r._read = common.mustNotCall(); 209 for (let i = 0; i < 6; i++) { 210 const bool = r.push(i); 211 assert.strictEqual(bool, i !== 5); 212 } 213} 214 215{ 216 // Verify that objects can be written to stream 217 const w = new Writable({ objectMode: true }); 218 219 w._write = function(chunk, encoding, cb) { 220 assert.deepStrictEqual(chunk, { foo: 'bar' }); 221 cb(); 222 }; 223 224 w.on('finish', common.mustCall()); 225 w.write({ foo: 'bar' }); 226 w.end(); 227} 228 229{ 230 // Verify that multiple objects can be written to stream 231 const w = new Writable({ objectMode: true }); 232 const list = []; 233 234 w._write = function(chunk, encoding, cb) { 235 list.push(chunk); 236 cb(); 237 }; 238 239 w.on('finish', common.mustCall(function() { 240 assert.deepStrictEqual(list, [0, 1, 2, 3, 4]); 241 })); 242 243 w.write(0); 244 w.write(1); 245 w.write(2); 246 w.write(3); 247 w.write(4); 248 w.end(); 249} 250 251{ 252 // Verify that strings can be written as objects 253 const w = new Writable({ 254 objectMode: true 255 }); 256 const list = []; 257 258 w._write = function(chunk, encoding, cb) { 259 list.push(chunk); 260 process.nextTick(cb); 261 }; 262 263 w.on('finish', common.mustCall(function() { 264 assert.deepStrictEqual(list, ['0', '1', '2', '3', '4']); 265 })); 266 267 w.write('0'); 268 w.write('1'); 269 w.write('2'); 270 w.write('3'); 271 w.write('4'); 272 w.end(); 273} 274 275{ 276 // Verify that stream buffers finish until callback is called 277 const w = new Writable({ 278 objectMode: true 279 }); 280 let called = false; 281 282 w._write = function(chunk, encoding, cb) { 283 assert.strictEqual(chunk, 'foo'); 284 285 process.nextTick(function() { 286 called = true; 287 cb(); 288 }); 289 }; 290 291 w.on('finish', common.mustCall(function() { 292 assert.strictEqual(called, true); 293 })); 294 295 w.write('foo'); 296 w.end(); 297} 298