• 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 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