1'use strict'; 2const common = require('../common'); 3const assert = require('assert'); 4const path = require('path'); 5const fs = require('fs'); 6const spawn = require('child_process').spawn; 7const tmpdir = require('../common/tmpdir'); 8 9let cat, grep, wc; 10 11const KB = 1024; 12const MB = KB * KB; 13 14 15// Make sure process chaining allows desired data flow: 16// check cat <file> | grep 'x' | wc -c === 1MB 17// This helps to make sure no data is lost between pipes. 18 19{ 20 tmpdir.refresh(); 21 const file = path.resolve(tmpdir.path, 'data.txt'); 22 const buf = Buffer.alloc(MB).fill('x'); 23 24 // Most OS commands that deal with data, attach special meanings to new line - 25 // for example, line buffering. So cut the buffer into lines at some points, 26 // forcing data flow to be split in the stream. Do not use os.EOL for \n as 27 // that is 2 characters on Windows and is sometimes converted to 1 character 28 // which causes the test to fail. 29 for (let i = 1; i < KB; i++) 30 buf.write('\n', i * KB); 31 fs.writeFileSync(file, buf.toString()); 32 33 cat = spawn('cat', [file]); 34 grep = spawn('grep', ['x'], { stdio: [cat.stdout, 'pipe', 'pipe'] }); 35 wc = spawn('wc', ['-c'], { stdio: [grep.stdout, 'pipe', 'pipe'] }); 36 37 // Extra checks: We never try to start reading data ourselves. 38 cat.stdout._handle.readStart = common.mustNotCall(); 39 grep.stdout._handle.readStart = common.mustNotCall(); 40 41 // Keep an array of error codes and assert on them during process exit. This 42 // is because stdio can still be open when a child process exits, and we don't 43 // want to lose information about what caused the error. 44 const errors = []; 45 process.on('exit', () => { 46 assert.deepStrictEqual(errors, []); 47 }); 48 49 [cat, grep, wc].forEach((child, index) => { 50 const errorHandler = (thing, type) => { 51 // Don't want to assert here, as we might miss error code info. 52 console.error(`unexpected ${type} from child #${index}:\n${thing}`); 53 }; 54 55 child.stderr.on('data', (d) => { errorHandler(d, 'data'); }); 56 child.on('error', (err) => { errorHandler(err, 'error'); }); 57 child.on('exit', common.mustCall((code) => { 58 if (code !== 0) { 59 errors.push(`child ${index} exited with code ${code}`); 60 } 61 })); 62 }); 63 64 let wcBuf = ''; 65 wc.stdout.on('data', common.mustCall((data) => { 66 wcBuf += data; 67 })); 68 69 process.on('exit', () => { 70 // Grep always adds one extra byte at the end. 71 assert.strictEqual(wcBuf.trim(), (MB + 1).toString()); 72 }); 73} 74