Skip to content

Commit

Permalink
test: fs.watch file, multiple watcher integ tests
Browse files Browse the repository at this point in the history
  • Loading branch information
abose committed Sep 17, 2023
1 parent e0e4240 commit 94361a3
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 31 deletions.
29 changes: 24 additions & 5 deletions dist/phoenix-fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ function getWindowsDrives(callback) {
});
}

function isSubpath(parent, subPathToCheck) {
const relative = path.relative(parent, subPathToCheck);
const isSubdir = !!relative && !relative.startsWith('..') && !path.isAbsolute(relative);
return isSubdir;
}

/**
*
* @param metadata {Object} Max size can be 4GB
Expand Down Expand Up @@ -302,7 +308,7 @@ function _unlink(ws, metadata) {
// eventEmitterID to watcher
const watchersMap = {};
function _watch(ws, metadata) {
const fullPath = metadata.data.path,
const fullPathToWatch = metadata.data.path,
// array of anymatch compatible path definition. Eg. ["**/{node_modules,bower_components}/**"]. full path is checked
ignoredPaths = metadata.data.ignoredPaths,
// contents of a gitIgnore file as text. The given path is used as the base path for gitIgnore
Expand All @@ -315,11 +321,23 @@ function _watch(ws, metadata) {

// Filter function to integrate with chokidar
function isIgnored(pathToFilter) {
console.log(`Watching: ${fullPathToWatch} Filtering: ${pathToFilter}`);
if(fullPathToWatch === pathToFilter) {
// if we are watching a file directly given file name, we don't run it though gitignore.
// also we cant get relative path of gitignore with respect to a file as root.
return false;
}
if(!isSubpath(fullPathToWatch, pathToFilter)) {
// Do not watch if the path given is not a subpath of our watched path.
// if we are watching a file directly given file name, Then we get an isignored call for the parent
// dir from chokidar.
return true;
}
const relativePath = path.relative(fullPathToWatch, pathToFilter);
if(anymatch(ignoredPaths, pathToFilter)){
debugMode && console.log("ignored watch path: ", pathToFilter, "rel: ",relativePath);
return true;
}
const relativePath = path.relative(fullPath, pathToFilter);
if(relativePath && gitignore.ignores(relativePath)){
debugMode && console.log("ignored watch gitIgnore path: ", pathToFilter, "rel: ",relativePath);
return true;
Expand All @@ -329,9 +347,10 @@ function _watch(ws, metadata) {
}

let readySent = false;
const watcher = chokidar.watch(fullPath, {
const watcher = chokidar.watch(fullPathToWatch, {
persistent,
ignoreInitial,
ignorePermissionErrors: true,
ignored: path => isIgnored(path)
});
const eventEmitterID = generateRandomId();
Expand All @@ -350,7 +369,7 @@ function _watch(ws, metadata) {
return;
}
readySent = true;
_reportError(ws, metadata, err, `Error while watching path ${fullPath}`);
_reportError(ws, metadata, err, `Error while watching path ${fullPathToWatch}`);
});
let watchEvents = ['add', 'change', 'unlink', 'addDir', 'unlinkDir'];
for(let watchEvent of watchEvents){
Expand All @@ -359,7 +378,7 @@ function _watch(ws, metadata) {
});
}
} catch (err) {
_reportError(ws, metadata, err, `Failed to watch path ${fullPath}`);
_reportError(ws, metadata, err, `Failed to watch path ${fullPathToWatch}`);
}
}

Expand Down
29 changes: 24 additions & 5 deletions src-tauri/node-src/phoenix-fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ function getWindowsDrives(callback) {
});
}

function isSubpath(parent, subPathToCheck) {
const relative = path.relative(parent, subPathToCheck);
const isSubdir = !!relative && !relative.startsWith('..') && !path.isAbsolute(relative);
return isSubdir;
}

/**
*
* @param metadata {Object} Max size can be 4GB
Expand Down Expand Up @@ -302,7 +308,7 @@ function _unlink(ws, metadata) {
// eventEmitterID to watcher
const watchersMap = {};
function _watch(ws, metadata) {
const fullPath = metadata.data.path,
const fullPathToWatch = metadata.data.path,
// array of anymatch compatible path definition. Eg. ["**/{node_modules,bower_components}/**"]. full path is checked
ignoredPaths = metadata.data.ignoredPaths,
// contents of a gitIgnore file as text. The given path is used as the base path for gitIgnore
Expand All @@ -315,11 +321,23 @@ function _watch(ws, metadata) {

// Filter function to integrate with chokidar
function isIgnored(pathToFilter) {
console.log(`Watching: ${fullPathToWatch} Filtering: ${pathToFilter}`);
if(fullPathToWatch === pathToFilter) {
// if we are watching a file directly given file name, we don't run it though gitignore.
// also we cant get relative path of gitignore with respect to a file as root.
return false;
}
if(!isSubpath(fullPathToWatch, pathToFilter)) {
// Do not watch if the path given is not a subpath of our watched path.
// if we are watching a file directly given file name, Then we get an isignored call for the parent
// dir from chokidar.
return true;
}
const relativePath = path.relative(fullPathToWatch, pathToFilter);
if(anymatch(ignoredPaths, pathToFilter)){
debugMode && console.log("ignored watch path: ", pathToFilter, "rel: ",relativePath);
return true;
}
const relativePath = path.relative(fullPath, pathToFilter);
if(relativePath && gitignore.ignores(relativePath)){
debugMode && console.log("ignored watch gitIgnore path: ", pathToFilter, "rel: ",relativePath);
return true;
Expand All @@ -329,9 +347,10 @@ function _watch(ws, metadata) {
}

let readySent = false;
const watcher = chokidar.watch(fullPath, {
const watcher = chokidar.watch(fullPathToWatch, {
persistent,
ignoreInitial,
ignorePermissionErrors: true,
ignored: path => isIgnored(path)
});
const eventEmitterID = generateRandomId();
Expand All @@ -350,7 +369,7 @@ function _watch(ws, metadata) {
return;
}
readySent = true;
_reportError(ws, metadata, err, `Error while watching path ${fullPath}`);
_reportError(ws, metadata, err, `Error while watching path ${fullPathToWatch}`);
});
let watchEvents = ['add', 'change', 'unlink', 'addDir', 'unlinkDir'];
for(let watchEvent of watchEvents){
Expand All @@ -359,7 +378,7 @@ function _watch(ws, metadata) {
});
}
} catch (err) {
_reportError(ws, metadata, err, `Failed to watch path ${fullPath}`);
_reportError(ws, metadata, err, `Failed to watch path ${fullPathToWatch}`);
}
}

Expand Down
129 changes: 108 additions & 21 deletions test/test-watcher.browser.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
/* global expect , Filer, fs, waitForTrue, TEST_TYPE_FS_ACCESS, TEST_TYPE_FILER, TEST_TYPE_TAURI, TEST_TYPE_TAURI_WS*/

function _setupTests(testType) {
let testPath, watcher;
let testPath, watcher, watcher2;

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

async function _waitForSomeTime(timeMS) {
return new Promise(resolve=>{
setTimeout(resolve, timeMS);
});
}

async function _validate_exists(path) {
let resolveP;
const promise = new Promise((resolve) => {resolveP = resolve;});
Expand Down Expand Up @@ -82,6 +88,44 @@ function _setupTests(testType) {
await promise;
}

const ADD_DIR = fs.WATCH_EVENTS.ADD_DIR,
UNLINK_DIR = fs.WATCH_EVENTS.UNLINK_DIR,
ADD_FILE = fs.WATCH_EVENTS.ADD_FILE,
UNLINK_FILE = fs.WATCH_EVENTS.UNLINK_FILE,
CHANGE = fs.WATCH_EVENTS.CHANGE;

async function _createNewWatcher(watchPath, pathChangeArray) {
const newWatcher = await fs.watchAsync(watchPath);
const watchEvents = [ADD_DIR, UNLINK_DIR, ADD_FILE, UNLINK_FILE, CHANGE];
for(let watchEvent of watchEvents) {
newWatcher.on(watchEvent, function ({path}) {
console.log(`Watcher: ${newWatcher.eventEmitterID}: path ${watchEvent}`, path);
pathChangeArray.push({path, watchEvent});
});
}
return newWatcher;
}

async function initWatcher(watchPath, pathChangeArray) {
if(watcher) {
await fs.unwatchAsync(watcher);
watcher = null;
}
watcher = await _createNewWatcher(watchPath, pathChangeArray);
console.log("watcher init done: ", watcher.eventEmitterID);
return watcher;
}

async function initWatcher2(watchPath, pathChangeArray) {
if(watcher2) {
await fs.unwatchAsync(watcher2);
watcher2 = null;
}
watcher2 = await _createNewWatcher(watchPath, pathChangeArray);
console.log("watcher 2 init done: ", watcher2.eventEmitterID);
return watcher2;
}

before(async function () {
switch (testType) {
case TEST_TYPE_FS_ACCESS: testPath = window.mountTestPath;break;
Expand Down Expand Up @@ -118,6 +162,10 @@ function _setupTests(testType) {
await fs.unwatchAsync(watcher);
watcher = null;
}
if(watcher2){
await fs.unwatchAsync(watcher2);
watcher2 = null;
}
await _clean();
});

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

it(`Should phoenix ${testType} watch a file`, async function () {
const watchPath = `${testPath}/a.txt`,
fileInSameDirNotWatched = `${testPath}/b.txt`;
await _writeTestFile(watchPath);

const pathChangeArray = [];

await initWatcher(watchPath, pathChangeArray);

await _writeTestFile(watchPath);
await _writeTestFile(fileInSameDirNotWatched);

await waitForTrue(()=>{return pathChangeArray.length === 1;},10000);
await _waitForSomeTime(100); // maybe some more events might come in so wait for some time to be sure?
expect(pathChangeArray).to.deep.include({ path: watchPath, watchEvent: CHANGE});
expect(pathChangeArray.length).to.eql(1);
});

it(`Should phoenix ${testType} watch for file rename`, async function () {
const watchPath = `${testPath}/watch`;
await _creatDirAndValidate(watchPath);
Expand Down Expand Up @@ -218,25 +284,46 @@ function _setupTests(testType) {
expect(addedPaths[0]).to.eql(pathRenamed);
});

const ADD_DIR = fs.WATCH_EVENTS.ADD_DIR,
UNLINK_DIR = fs.WATCH_EVENTS.UNLINK_DIR,
ADD_FILE = fs.WATCH_EVENTS.ADD_FILE,
UNLINK_FILE = fs.WATCH_EVENTS.UNLINK_FILE,
CHANGE = fs.WATCH_EVENTS.CHANGE;
async function initWatchers(watchPath, pathChangeArray) {
if(watcher) {
await fs.unwatchAsync(watcher);
watcher = null;
}
watcher = await fs.watchAsync(watchPath);
const watchEvents = [ADD_DIR, UNLINK_DIR, ADD_FILE, UNLINK_FILE, CHANGE];
for(let watchEvent of watchEvents) {
watcher.on(watchEvent, function ({path}) {
console.log(`path ${watchEvent}`, path);
pathChangeArray.push({path, watchEvent});
});
}
}
it(`Should phoenix ${testType} watch support multiple watchers concurrently on same dir`, async function () {
const watchPath = `${testPath}/watch`;
await _creatDirAndValidate(watchPath);

const pathChangeArray = [], watcher2PathChangeArray = [];
let pathCreated = `${watchPath}/x`;
const nestedFile = `${watchPath}/x/a.txt`;

await initWatcher(watchPath, pathChangeArray);
await initWatcher2(watchPath, watcher2PathChangeArray);

await _creatDirAndValidate(pathCreated);
await _writeTestFile(nestedFile);

await waitForTrue(()=>{return pathChangeArray.length === 2;},10000);
await waitForTrue(()=>{return watcher2PathChangeArray.length === 2;},10000);
expect(pathChangeArray).to.deep.include({ path: pathCreated, watchEvent: ADD_DIR});
expect(pathChangeArray).to.deep.include({ path: nestedFile, watchEvent: ADD_FILE});
expect(watcher2PathChangeArray).to.deep.include({ path: pathCreated, watchEvent: ADD_DIR});
expect(watcher2PathChangeArray).to.deep.include({ path: nestedFile, watchEvent: ADD_FILE});
});

it(`Should phoenix ${testType} watch support multiple watchers concurrently on same file`, async function () {
const watchPath = `${testPath}/a.txt`;
await _writeTestFile(watchPath);

const pathChangeArray = [], watcher2PathChangeArray = [];

await initWatcher(watchPath, pathChangeArray);
await initWatcher2(watchPath, watcher2PathChangeArray);

await _writeTestFile(watchPath);
await _writeTestFile(watchPath);

await waitForTrue(()=>{return pathChangeArray.length === 1;},10000);
await waitForTrue(()=>{return watcher2PathChangeArray.length === 1;},10000);
await _waitForSomeTime(100); // maybe some more events might come in so wait for some time to be sure?
expect(pathChangeArray).to.deep.include({ path: watchPath, watchEvent: CHANGE});
expect(watcher2PathChangeArray).to.deep.include({ path: watchPath, watchEvent: CHANGE});
});

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

await initWatchers(watchPath, pathChangeArray);
await initWatcher(watchPath, pathChangeArray);

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

0 comments on commit 94361a3

Please sign in to comment.