1'use strict';
2const common = require('../common');
3const assert = require('assert');
4const child_process = require('child_process');
5const fs = require('fs');
6const stream = require('stream');
7
8if (!common.isLinux) {
9  common.skip('The fs watch limit is OS-dependent');
10}
11
12if (common.isPi) {
13  common.skip('Too slow for Raspberry Pi devices');
14}
15
16try {
17  // Ensure inotify limit is low enough for the test to actually exercise the
18  // limit with small enough resources.
19  const limit = Number(
20    fs.readFileSync('/proc/sys/fs/inotify/max_user_watches', 'utf8'));
21  if (limit > 16384)
22    common.skip('inotify limit is quite large');
23} catch (e) {
24  if (e.code === 'ENOENT')
25    common.skip('the inotify /proc subsystem does not exist');
26  // Fail on other errors.
27  throw e;
28}
29
30const processes = [];
31const gatherStderr = new stream.PassThrough();
32gatherStderr.setEncoding('utf8');
33gatherStderr.setMaxListeners(Infinity);
34
35let finished = false;
36function spawnProcesses() {
37  for (let i = 0; i < 10; ++i) {
38    const proc = child_process.spawn(
39      process.execPath,
40      [ '-e',
41        `process.chdir(${JSON.stringify(__dirname)});
42        for (const file of fs.readdirSync('.'))
43          fs.watch(file, () => {});`,
44      ], { stdio: ['inherit', 'inherit', 'pipe'] });
45    proc.stderr.pipe(gatherStderr);
46    processes.push(proc);
47  }
48
49  setTimeout(() => {
50    if (!finished && processes.length < 200)
51      spawnProcesses();
52  }, 100);
53}
54
55spawnProcesses();
56
57let accumulated = '';
58gatherStderr.on('data', common.mustCallAtLeast((chunk) => {
59  accumulated += chunk;
60  if (accumulated.includes('Error:') && !finished) {
61    assert(
62      accumulated.includes('ENOSPC: System limit for number ' +
63                           'of file watchers reached') ||
64      accumulated.includes('EMFILE: '),
65      accumulated);
66    console.log(`done after ${processes.length} processes, cleaning up`);
67    finished = true;
68    processes.forEach((proc) => proc.kill());
69  }
70}, 1));
71