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