• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23const common = require('../common');
24if (common.isIBMi)
25  common.skip('IBMi does not support fs.watch()');
26
27const assert = require('assert');
28const fs = require('fs');
29const path = require('path');
30
31const tmpdir = require('../common/tmpdir');
32
33if (!common.isMainThread)
34  common.skip('process.chdir is not available in Workers');
35
36const expectFilePath = common.isWindows ||
37                       common.isLinux ||
38                       common.isOSX ||
39                       common.isAIX;
40
41const testDir = tmpdir.path;
42
43tmpdir.refresh();
44
45// Because macOS (and possibly other operating systems) can return a watcher
46// before it is actually watching, we need to repeat the operation to avoid
47// a race condition.
48function repeat(fn) {
49  setImmediate(fn);
50  const interval = setInterval(fn, 5000);
51  return interval;
52}
53
54{
55  const filepath = path.join(testDir, 'watch.txt');
56
57  fs.writeFileSync(filepath, 'hello');
58
59  const watcher = fs.watch(filepath);
60  watcher.on('change', common.mustCall(function(event, filename) {
61    assert.strictEqual(event, 'change');
62
63    if (expectFilePath) {
64      assert.strictEqual(filename, 'watch.txt');
65    }
66    clearInterval(interval);
67    watcher.close();
68  }));
69
70  const interval = repeat(() => { fs.writeFileSync(filepath, 'world'); });
71}
72
73{
74  const filepathAbs = path.join(testDir, 'hasOwnProperty');
75
76  process.chdir(testDir);
77
78  fs.writeFileSync(filepathAbs, 'howdy');
79
80  const watcher =
81    fs.watch('hasOwnProperty', common.mustCall(function(event, filename) {
82      assert.strictEqual(event, 'change');
83
84      if (expectFilePath) {
85        assert.strictEqual(filename, 'hasOwnProperty');
86      }
87      clearInterval(interval);
88      watcher.close();
89    }));
90
91  const interval = repeat(() => { fs.writeFileSync(filepathAbs, 'pardner'); });
92}
93
94{
95  const testsubdir = fs.mkdtempSync(testDir + path.sep);
96  const filepath = path.join(testsubdir, 'newfile.txt');
97
98  const watcher =
99    fs.watch(testsubdir, common.mustCall(function(event, filename) {
100      const renameEv = common.isSunOS || common.isAIX ? 'change' : 'rename';
101      assert.strictEqual(event, renameEv);
102      if (expectFilePath) {
103        assert.strictEqual(filename, 'newfile.txt');
104      } else {
105        assert.strictEqual(filename, null);
106      }
107      clearInterval(interval);
108      watcher.close();
109    }));
110
111  const interval = repeat(() => {
112    fs.rmSync(filepath, { force: true });
113    const fd = fs.openSync(filepath, 'w');
114    fs.closeSync(fd);
115  });
116}
117
118// https://github.com/joyent/node/issues/2293 - non-persistent watcher should
119// not block the event loop
120{
121  fs.watch(__filename, { persistent: false }, common.mustNotCall());
122}
123
124// Whitebox test to ensure that wrapped FSEvent is safe
125// https://github.com/joyent/node/issues/6690
126{
127  let oldhandle;
128  assert.throws(
129    () => {
130      const w = fs.watch(__filename, common.mustNotCall());
131      oldhandle = w._handle;
132      w._handle = { close: w._handle.close };
133      w.close();
134    },
135    {
136      name: 'Error',
137      code: 'ERR_INTERNAL_ASSERTION',
138      message: /^handle must be a FSEvent/,
139    }
140  );
141  oldhandle.close(); // clean up
142}
143
144{
145  let oldhandle;
146  assert.throws(
147    () => {
148      const w = fs.watch(__filename, common.mustNotCall());
149      oldhandle = w._handle;
150      const protoSymbols =
151        Object.getOwnPropertySymbols(Object.getPrototypeOf(w));
152      const kFSWatchStart =
153        protoSymbols.find((val) => val.toString() === 'Symbol(kFSWatchStart)');
154      w._handle = {};
155      w[kFSWatchStart]();
156    },
157    {
158      name: 'Error',
159      code: 'ERR_INTERNAL_ASSERTION',
160      message: /^handle must be a FSEvent/,
161    }
162  );
163  oldhandle.close(); // clean up
164}
165