Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit e1d3240

Browse files
JiaLiPassionmhevery
authored andcommitted
patch fs methods as macrotask, add test cases of fs watcher (#572)
* patch fs methods as macrotask, add test cases of fs watcher * add generic patch macrotask method
1 parent c297752 commit e1d3240

File tree

3 files changed

+124
-17
lines changed

3 files changed

+124
-17
lines changed

Diff for: lib/common/utils.ts

+34
Original file line numberDiff line numberDiff line change
@@ -481,3 +481,37 @@ export function patchMethod(
481481
}
482482
return delegate;
483483
}
484+
485+
export interface MacroTaskMeta extends TaskData {
486+
name: string;
487+
target: any;
488+
callbackIndex: number;
489+
args: any[];
490+
}
491+
492+
// TODO: support cancel task later if necessary
493+
export function patchMacroTask(
494+
obj: any, funcName: string, metaCreator: (self: any, args: any[]) => MacroTaskMeta) {
495+
let setNative = null;
496+
497+
function scheduleTask(task: Task) {
498+
const data = <MacroTaskMeta>task.data;
499+
data.args[data.callbackIndex] = function() {
500+
task.invoke.apply(this, arguments);
501+
};
502+
setNative.apply(data.target, data.args);
503+
return task;
504+
}
505+
506+
setNative = patchMethod(obj, funcName, (delegate: Function) => function(self: any, args: any[]) {
507+
const meta = metaCreator(self, args);
508+
if (meta.callbackIndex >= 0 && typeof args[meta.callbackIndex] === 'function') {
509+
const task = Zone.current.scheduleMacroTask(
510+
meta.name, args[meta.callbackIndex], meta, scheduleTask, null);
511+
return task;
512+
} else {
513+
// cause an error by calling it directly.
514+
return delegate.apply(self, args);
515+
}
516+
});
517+
}

Diff for: lib/node/fs.ts

+15-10
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {bindArguments} from '../common/utils';
9+
import {patchMacroTask} from '../common/utils';
1010

1111
let fs;
1212
try {
1313
fs = require('fs');
1414
} catch (err) {
1515
}
1616

17-
// TODO(alxhub): Patch `watch` and `unwatchFile`.
18-
const TO_PATCH = [
17+
// watch, watchFile, unwatchFile has been patched
18+
// because EventEmitter has been patched
19+
const TO_PATCH_MACROTASK_METHODS = [
1920
'access', 'appendFile', 'chmod', 'chown', 'close', 'exists', 'fchmod',
2021
'fchown', 'fdatasync', 'fstat', 'fsync', 'ftruncate', 'futimes', 'lchmod',
2122
'lchown', 'link', 'lstat', 'mkdir', 'mkdtemp', 'open', 'read',
@@ -24,11 +25,15 @@ const TO_PATCH = [
2425
];
2526

2627
if (fs) {
27-
TO_PATCH.filter(name => !!fs[name] && typeof fs[name] === 'function').forEach(name => {
28-
fs[name] = ((delegate: Function) => {
29-
return function() {
30-
return delegate.apply(this, bindArguments(<any>arguments, 'fs.' + name));
31-
};
32-
})(fs[name]);
33-
});
28+
TO_PATCH_MACROTASK_METHODS.filter(name => !!fs[name] && typeof fs[name] === 'function')
29+
.forEach(name => {
30+
patchMacroTask(fs, name, (self: any, args: any[]) => {
31+
return {
32+
name: 'fs.' + name,
33+
args: args,
34+
callbackIndex: args.length > 0 ? args.length - 1 : -1,
35+
target: self
36+
};
37+
});
38+
});
3439
}

Diff for: test/node/fs.spec.ts

+75-7
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,83 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {exists} from 'fs';
9+
import {exists, unlink, unwatchFile, watch, watchFile, writeFile} from 'fs';
1010

1111
describe('nodejs file system', () => {
12-
it('has patched exists()', (done) => {
13-
const zoneA = Zone.current.fork({name: 'A'});
14-
zoneA.run(() => {
15-
exists('testfile', (_) => {
16-
expect(Zone.current.name).toBe(zoneA.name);
17-
done();
12+
describe('async method patch test', () => {
13+
it('has patched exists()', (done) => {
14+
const zoneA = Zone.current.fork({name: 'A'});
15+
zoneA.run(() => {
16+
exists('testfile', (_) => {
17+
expect(Zone.current.name).toBe(zoneA.name);
18+
done();
19+
});
20+
});
21+
});
22+
23+
it('has patched exists as macroTask', (done) => {
24+
const zoneASpec = {
25+
name: 'A',
26+
onScheduleTask: (delegate, currentZone, targetZone, task): Task => {
27+
return delegate.scheduleTask(targetZone, task);
28+
}
29+
};
30+
const zoneA = Zone.current.fork(zoneASpec);
31+
spyOn(zoneASpec, 'onScheduleTask').and.callThrough();
32+
zoneA.run(() => {
33+
exists('testfile', (_) => {
34+
expect(zoneASpec.onScheduleTask).toHaveBeenCalled();
35+
done();
36+
});
37+
});
38+
});
39+
});
40+
41+
describe('watcher related methods test', () => {
42+
const zoneASpec = {
43+
name: 'A',
44+
onScheduleTask: (delegate, currentZone, targetZone, task): Task => {
45+
return delegate.scheduleTask(targetZone, task);
46+
}
47+
};
48+
49+
it('fs.watch has been patched as eventTask', (done) => {
50+
spyOn(zoneASpec, 'onScheduleTask').and.callThrough();
51+
const zoneA = Zone.current.fork(zoneASpec);
52+
zoneA.run(() => {
53+
writeFile('testfile', 'test content', () => {
54+
const watcher = watch('testfile', (eventType, filename) => {
55+
expect(filename).toEqual('testfile');
56+
expect(eventType).toEqual('change');
57+
expect(zoneASpec.onScheduleTask).toHaveBeenCalled();
58+
expect(Zone.current.name).toBe('A');
59+
watcher.close();
60+
unlink('testfile', () => {
61+
done();
62+
});
63+
});
64+
writeFile('testfile', 'test new content');
65+
});
66+
});
67+
});
68+
69+
it('fs.watchFile has been patched as eventTask', (done) => {
70+
spyOn(zoneASpec, 'onScheduleTask').and.callThrough();
71+
const zoneA = Zone.current.fork(zoneASpec);
72+
zoneA.run(() => {
73+
writeFile('testfile', 'test content', () => {
74+
watchFile('testfile', {persistent: false, interval: 1000}, (curr, prev) => {
75+
expect(curr.size).toBe(16);
76+
expect(prev.size).toBe(12);
77+
expect(zoneASpec.onScheduleTask).toHaveBeenCalled();
78+
expect(Zone.current.name).toBe('A');
79+
unwatchFile('testfile');
80+
unlink('testfile', () => {
81+
done();
82+
});
83+
});
84+
writeFile('testfile', 'test new content');
85+
});
1886
});
1987
});
2088
});

0 commit comments

Comments
 (0)