Skip to content

Commit 308dfd6

Browse files
committed
test: fs.watch file, multiple watcher integ tests
1 parent d8ed3f2 commit 308dfd6

3 files changed

Lines changed: 156 additions & 31 deletions

File tree

dist/phoenix-fs.js

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ function getWindowsDrives(callback) {
4646
});
4747
}
4848

49+
function isSubpath(parent, subPathToCheck) {
50+
const relative = path.relative(parent, subPathToCheck);
51+
const isSubdir = !!relative && !relative.startsWith('..') && !path.isAbsolute(relative);
52+
return isSubdir;
53+
}
54+
4955
/**
5056
*
5157
* @param metadata {Object} Max size can be 4GB
@@ -302,7 +308,7 @@ function _unlink(ws, metadata) {
302308
// eventEmitterID to watcher
303309
const watchersMap = {};
304310
function _watch(ws, metadata) {
305-
const fullPath = metadata.data.path,
311+
const fullPathToWatch = metadata.data.path,
306312
// array of anymatch compatible path definition. Eg. ["**/{node_modules,bower_components}/**"]. full path is checked
307313
ignoredPaths = metadata.data.ignoredPaths,
308314
// contents of a gitIgnore file as text. The given path is used as the base path for gitIgnore
@@ -315,11 +321,23 @@ function _watch(ws, metadata) {
315321

316322
// Filter function to integrate with chokidar
317323
function isIgnored(pathToFilter) {
324+
console.log(`Watching: ${fullPathToWatch} Filtering: ${pathToFilter}`);
325+
if(fullPathToWatch === pathToFilter) {
326+
// if we are watching a file directly given file name, we don't run it though gitignore.
327+
// also we cant get relative path of gitignore with respect to a file as root.
328+
return false;
329+
}
330+
if(!isSubpath(fullPathToWatch, pathToFilter)) {
331+
// Do not watch if the path given is not a subpath of our watched path.
332+
// if we are watching a file directly given file name, Then we get an isignored call for the parent
333+
// dir from chokidar.
334+
return true;
335+
}
336+
const relativePath = path.relative(fullPathToWatch, pathToFilter);
318337
if(anymatch(ignoredPaths, pathToFilter)){
319338
debugMode && console.log("ignored watch path: ", pathToFilter, "rel: ",relativePath);
320339
return true;
321340
}
322-
const relativePath = path.relative(fullPath, pathToFilter);
323341
if(relativePath && gitignore.ignores(relativePath)){
324342
debugMode && console.log("ignored watch gitIgnore path: ", pathToFilter, "rel: ",relativePath);
325343
return true;
@@ -329,9 +347,10 @@ function _watch(ws, metadata) {
329347
}
330348

331349
let readySent = false;
332-
const watcher = chokidar.watch(fullPath, {
350+
const watcher = chokidar.watch(fullPathToWatch, {
333351
persistent,
334352
ignoreInitial,
353+
ignorePermissionErrors: true,
335354
ignored: path => isIgnored(path)
336355
});
337356
const eventEmitterID = generateRandomId();
@@ -350,7 +369,7 @@ function _watch(ws, metadata) {
350369
return;
351370
}
352371
readySent = true;
353-
_reportError(ws, metadata, err, `Error while watching path ${fullPath}`);
372+
_reportError(ws, metadata, err, `Error while watching path ${fullPathToWatch}`);
354373
});
355374
let watchEvents = ['add', 'change', 'unlink', 'addDir', 'unlinkDir'];
356375
for(let watchEvent of watchEvents){
@@ -359,7 +378,7 @@ function _watch(ws, metadata) {
359378
});
360379
}
361380
} catch (err) {
362-
_reportError(ws, metadata, err, `Failed to watch path ${fullPath}`);
381+
_reportError(ws, metadata, err, `Failed to watch path ${fullPathToWatch}`);
363382
}
364383
}
365384

src-tauri/node-src/phoenix-fs.js

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ function getWindowsDrives(callback) {
4646
});
4747
}
4848

49+
function isSubpath(parent, subPathToCheck) {
50+
const relative = path.relative(parent, subPathToCheck);
51+
const isSubdir = !!relative && !relative.startsWith('..') && !path.isAbsolute(relative);
52+
return isSubdir;
53+
}
54+
4955
/**
5056
*
5157
* @param metadata {Object} Max size can be 4GB
@@ -302,7 +308,7 @@ function _unlink(ws, metadata) {
302308
// eventEmitterID to watcher
303309
const watchersMap = {};
304310
function _watch(ws, metadata) {
305-
const fullPath = metadata.data.path,
311+
const fullPathToWatch = metadata.data.path,
306312
// array of anymatch compatible path definition. Eg. ["**/{node_modules,bower_components}/**"]. full path is checked
307313
ignoredPaths = metadata.data.ignoredPaths,
308314
// contents of a gitIgnore file as text. The given path is used as the base path for gitIgnore
@@ -315,11 +321,23 @@ function _watch(ws, metadata) {
315321

316322
// Filter function to integrate with chokidar
317323
function isIgnored(pathToFilter) {
324+
console.log(`Watching: ${fullPathToWatch} Filtering: ${pathToFilter}`);
325+
if(fullPathToWatch === pathToFilter) {
326+
// if we are watching a file directly given file name, we don't run it though gitignore.
327+
// also we cant get relative path of gitignore with respect to a file as root.
328+
return false;
329+
}
330+
if(!isSubpath(fullPathToWatch, pathToFilter)) {
331+
// Do not watch if the path given is not a subpath of our watched path.
332+
// if we are watching a file directly given file name, Then we get an isignored call for the parent
333+
// dir from chokidar.
334+
return true;
335+
}
336+
const relativePath = path.relative(fullPathToWatch, pathToFilter);
318337
if(anymatch(ignoredPaths, pathToFilter)){
319338
debugMode && console.log("ignored watch path: ", pathToFilter, "rel: ",relativePath);
320339
return true;
321340
}
322-
const relativePath = path.relative(fullPath, pathToFilter);
323341
if(relativePath && gitignore.ignores(relativePath)){
324342
debugMode && console.log("ignored watch gitIgnore path: ", pathToFilter, "rel: ",relativePath);
325343
return true;
@@ -329,9 +347,10 @@ function _watch(ws, metadata) {
329347
}
330348

331349
let readySent = false;
332-
const watcher = chokidar.watch(fullPath, {
350+
const watcher = chokidar.watch(fullPathToWatch, {
333351
persistent,
334352
ignoreInitial,
353+
ignorePermissionErrors: true,
335354
ignored: path => isIgnored(path)
336355
});
337356
const eventEmitterID = generateRandomId();
@@ -350,7 +369,7 @@ function _watch(ws, metadata) {
350369
return;
351370
}
352371
readySent = true;
353-
_reportError(ws, metadata, err, `Error while watching path ${fullPath}`);
372+
_reportError(ws, metadata, err, `Error while watching path ${fullPathToWatch}`);
354373
});
355374
let watchEvents = ['add', 'change', 'unlink', 'addDir', 'unlinkDir'];
356375
for(let watchEvent of watchEvents){
@@ -359,7 +378,7 @@ function _watch(ws, metadata) {
359378
});
360379
}
361380
} catch (err) {
362-
_reportError(ws, metadata, err, `Failed to watch path ${fullPath}`);
381+
_reportError(ws, metadata, err, `Failed to watch path ${fullPathToWatch}`);
363382
}
364383
}
365384

test/test-watcher.browser.js

Lines changed: 108 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
/* global expect , Filer, fs, waitForTrue, TEST_TYPE_FS_ACCESS, TEST_TYPE_FILER, TEST_TYPE_TAURI, TEST_TYPE_TAURI_WS*/
22

33
function _setupTests(testType) {
4-
let testPath, watcher;
4+
let testPath, watcher, watcher2;
55

66
function consoleLogToShell(message) {
77
return window.__TAURI__.invoke("console_log", {message});
88
}
99

10+
async function _waitForSomeTime(timeMS) {
11+
return new Promise(resolve=>{
12+
setTimeout(resolve, timeMS);
13+
});
14+
}
15+
1016
async function _validate_exists(path) {
1117
let resolveP;
1218
const promise = new Promise((resolve) => {resolveP = resolve;});
@@ -82,6 +88,44 @@ function _setupTests(testType) {
8288
await promise;
8389
}
8490

91+
const ADD_DIR = fs.WATCH_EVENTS.ADD_DIR,
92+
UNLINK_DIR = fs.WATCH_EVENTS.UNLINK_DIR,
93+
ADD_FILE = fs.WATCH_EVENTS.ADD_FILE,
94+
UNLINK_FILE = fs.WATCH_EVENTS.UNLINK_FILE,
95+
CHANGE = fs.WATCH_EVENTS.CHANGE;
96+
97+
async function _createNewWatcher(watchPath, pathChangeArray) {
98+
const newWatcher = await fs.watchAsync(watchPath);
99+
const watchEvents = [ADD_DIR, UNLINK_DIR, ADD_FILE, UNLINK_FILE, CHANGE];
100+
for(let watchEvent of watchEvents) {
101+
newWatcher.on(watchEvent, function ({path}) {
102+
console.log(`Watcher: ${newWatcher.eventEmitterID}: path ${watchEvent}`, path);
103+
pathChangeArray.push({path, watchEvent});
104+
});
105+
}
106+
return newWatcher;
107+
}
108+
109+
async function initWatcher(watchPath, pathChangeArray) {
110+
if(watcher) {
111+
await fs.unwatchAsync(watcher);
112+
watcher = null;
113+
}
114+
watcher = await _createNewWatcher(watchPath, pathChangeArray);
115+
console.log("watcher init done: ", watcher.eventEmitterID);
116+
return watcher;
117+
}
118+
119+
async function initWatcher2(watchPath, pathChangeArray) {
120+
if(watcher2) {
121+
await fs.unwatchAsync(watcher2);
122+
watcher2 = null;
123+
}
124+
watcher2 = await _createNewWatcher(watchPath, pathChangeArray);
125+
console.log("watcher 2 init done: ", watcher2.eventEmitterID);
126+
return watcher2;
127+
}
128+
85129
before(async function () {
86130
switch (testType) {
87131
case TEST_TYPE_FS_ACCESS: testPath = window.mountTestPath;break;
@@ -118,6 +162,10 @@ function _setupTests(testType) {
118162
await fs.unwatchAsync(watcher);
119163
watcher = null;
120164
}
165+
if(watcher2){
166+
await fs.unwatchAsync(watcher2);
167+
watcher2 = null;
168+
}
121169
await _clean();
122170
});
123171

@@ -190,6 +238,24 @@ function _setupTests(testType) {
190238
expect(addedPaths[0]).to.eql(pathCreated);
191239
});
192240

241+
it(`Should phoenix ${testType} watch a file`, async function () {
242+
const watchPath = `${testPath}/a.txt`,
243+
fileInSameDirNotWatched = `${testPath}/b.txt`;
244+
await _writeTestFile(watchPath);
245+
246+
const pathChangeArray = [];
247+
248+
await initWatcher(watchPath, pathChangeArray);
249+
250+
await _writeTestFile(watchPath);
251+
await _writeTestFile(fileInSameDirNotWatched);
252+
253+
await waitForTrue(()=>{return pathChangeArray.length === 1;},10000);
254+
await _waitForSomeTime(100); // maybe some more events might come in so wait for some time to be sure?
255+
expect(pathChangeArray).to.deep.include({ path: watchPath, watchEvent: CHANGE});
256+
expect(pathChangeArray.length).to.eql(1);
257+
});
258+
193259
it(`Should phoenix ${testType} watch for file rename`, async function () {
194260
const watchPath = `${testPath}/watch`;
195261
await _creatDirAndValidate(watchPath);
@@ -218,25 +284,46 @@ function _setupTests(testType) {
218284
expect(addedPaths[0]).to.eql(pathRenamed);
219285
});
220286

221-
const ADD_DIR = fs.WATCH_EVENTS.ADD_DIR,
222-
UNLINK_DIR = fs.WATCH_EVENTS.UNLINK_DIR,
223-
ADD_FILE = fs.WATCH_EVENTS.ADD_FILE,
224-
UNLINK_FILE = fs.WATCH_EVENTS.UNLINK_FILE,
225-
CHANGE = fs.WATCH_EVENTS.CHANGE;
226-
async function initWatchers(watchPath, pathChangeArray) {
227-
if(watcher) {
228-
await fs.unwatchAsync(watcher);
229-
watcher = null;
230-
}
231-
watcher = await fs.watchAsync(watchPath);
232-
const watchEvents = [ADD_DIR, UNLINK_DIR, ADD_FILE, UNLINK_FILE, CHANGE];
233-
for(let watchEvent of watchEvents) {
234-
watcher.on(watchEvent, function ({path}) {
235-
console.log(`path ${watchEvent}`, path);
236-
pathChangeArray.push({path, watchEvent});
237-
});
238-
}
239-
}
287+
it(`Should phoenix ${testType} watch support multiple watchers concurrently on same dir`, async function () {
288+
const watchPath = `${testPath}/watch`;
289+
await _creatDirAndValidate(watchPath);
290+
291+
const pathChangeArray = [], watcher2PathChangeArray = [];
292+
let pathCreated = `${watchPath}/x`;
293+
const nestedFile = `${watchPath}/x/a.txt`;
294+
295+
await initWatcher(watchPath, pathChangeArray);
296+
await initWatcher2(watchPath, watcher2PathChangeArray);
297+
298+
await _creatDirAndValidate(pathCreated);
299+
await _writeTestFile(nestedFile);
300+
301+
await waitForTrue(()=>{return pathChangeArray.length === 2;},10000);
302+
await waitForTrue(()=>{return watcher2PathChangeArray.length === 2;},10000);
303+
expect(pathChangeArray).to.deep.include({ path: pathCreated, watchEvent: ADD_DIR});
304+
expect(pathChangeArray).to.deep.include({ path: nestedFile, watchEvent: ADD_FILE});
305+
expect(watcher2PathChangeArray).to.deep.include({ path: pathCreated, watchEvent: ADD_DIR});
306+
expect(watcher2PathChangeArray).to.deep.include({ path: nestedFile, watchEvent: ADD_FILE});
307+
});
308+
309+
it(`Should phoenix ${testType} watch support multiple watchers concurrently on same file`, async function () {
310+
const watchPath = `${testPath}/a.txt`;
311+
await _writeTestFile(watchPath);
312+
313+
const pathChangeArray = [], watcher2PathChangeArray = [];
314+
315+
await initWatcher(watchPath, pathChangeArray);
316+
await initWatcher2(watchPath, watcher2PathChangeArray);
317+
318+
await _writeTestFile(watchPath);
319+
await _writeTestFile(watchPath);
320+
321+
await waitForTrue(()=>{return pathChangeArray.length === 1;},10000);
322+
await waitForTrue(()=>{return watcher2PathChangeArray.length === 1;},10000);
323+
await _waitForSomeTime(100); // maybe some more events might come in so wait for some time to be sure?
324+
expect(pathChangeArray).to.deep.include({ path: watchPath, watchEvent: CHANGE});
325+
expect(watcher2PathChangeArray).to.deep.include({ path: watchPath, watchEvent: CHANGE});
326+
});
240327

241328
it(`Should phoenix ${testType} watch for folder rename with nested contents`, async function () {
242329
const watchPath = `${testPath}/watch`;
@@ -248,7 +335,7 @@ function _setupTests(testType) {
248335
await _creatDirAndValidate(pathCreated);
249336
await _writeTestFile(nestedFile);
250337

251-
await initWatchers(watchPath, pathChangeArray);
338+
await initWatcher(watchPath, pathChangeArray);
252339

253340
const pathRenamed = `${watchPath}/y`;
254341
await _rename(pathCreated, pathRenamed);

0 commit comments

Comments
 (0)