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