1'use strict'; 2const common = require('../common'); 3const assert = require('assert'); 4const path = require('path'); 5const fs = require('fs'); 6const os = require('os'); 7const spawn = require('child_process').spawn; 8const tmpdir = require('../common/tmpdir'); 9 10let cat, grep, wc; 11 12const KB = 1024; 13const MB = KB * KB; 14 15 16// Make sure process chaining allows desired data flow: 17// check cat <file> | grep 'x' | wc -c === 1MB 18// This helps to make sure no data is lost between pipes. 19 20{ 21 tmpdir.refresh(); 22 const file = path.resolve(tmpdir.path, 'data.txt'); 23 const buf = Buffer.alloc(MB).fill('x'); 24 25 // Most OS commands that deal with data, attach special 26 // meanings to new line - for example, line buffering. 27 // So cut the buffer into lines at some points, forcing 28 // data flow to be split in the stream. 29 for (let i = 1; i < KB; i++) 30 buf.write(os.EOL, 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 wc.stdout.on('data', common.mustCall((data) => { 65 // Grep always adds one extra byte at the end. 66 assert.strictEqual(data.toString().trim(), (MB + 1).toString()); 67 })); 68} 69