• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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