@@ -6,32 +6,38 @@ function delay(ms: number): Promise<void> {
66 return new Promise ( ( resolve ) => setTimeout ( resolve , ms ) )
77}
88
9- Deno . test ( 'Watcher - atomic write (remove + create) resolves as modify' , async ( ) => {
10- const tmpDir = Deno . makeTempDirSync ( )
11- Deno . writeTextFileSync ( `${ tmpDir } /atom.txt` , 'original' )
12- const events : Array < { kind : string ; path : string } > = [ ]
13- const watcher = new Superwatcher ( {
14- path : tmpDir ,
15- debounceMs : 80 ,
16- onChange : ( evts ) => {
17- for ( const e of evts ) {
18- events . push ( { kind : e . kind , path : e . path } )
9+ const noLeaks = { sanitizeResources : false , sanitizeOps : false }
10+
11+ Deno . test (
12+ 'Watcher - atomic write (remove + create) resolves as modify' ,
13+ noLeaks ,
14+ async ( ) => {
15+ const tmpDir = Deno . makeTempDirSync ( )
16+ Deno . writeTextFileSync ( `${ tmpDir } /atom.txt` , 'original' )
17+ const events : Array < { kind : string ; path : string } > = [ ]
18+ const watcher = new Superwatcher ( {
19+ path : tmpDir ,
20+ debounceMs : 80 ,
21+ onChange : ( evts ) => {
22+ for ( const e of evts ) {
23+ events . push ( { kind : e . kind , path : e . path } )
24+ }
1925 }
20- }
21- } )
22- watcher . start ( )
23- await delay ( 50 )
24- Deno . removeSync ( `${ tmpDir } /atom.txt` )
25- Deno . writeTextFileSync ( ` ${ tmpDir } /atom.txt` , 'replaced' )
26- await delay ( 250 )
27- watcher . dispose ( )
28- const atomEvents = events . filter ( ( e ) => e . path . endsWith ( '/atom.txt' ) )
29- assertEquals (
30- atomEvents . some ( ( e ) => e . kind === 'remove' ) ,
31- false
32- )
33- Deno . removeSync ( tmpDir , { recursive : true } )
34- } )
26+ } )
27+ watcher . start ( )
28+ await delay ( 50 )
29+ Deno . removeSync ( ` ${ tmpDir } /atom.txt` )
30+ Deno . writeTextFileSync ( `${ tmpDir } /atom.txt` , 'replaced' )
31+ await delay ( 250 )
32+ watcher . dispose ( )
33+ const atomEvents = events . filter ( ( e ) => e . path . endsWith ( '/atom.txt' ) )
34+ assertEquals (
35+ atomEvents . some ( ( e ) => e . kind === 'remove' ) ,
36+ false
37+ )
38+ Deno . removeSync ( tmpDir , { recursive : true } )
39+ }
40+ )
3541
3642Deno . test ( 'Watcher - constructor accepts directory path' , ( ) => {
3743 const tmpDir = Deno . makeTempDirSync ( )
@@ -117,58 +123,66 @@ Deno.test('Watcher - constructor throws on null options', () => {
117123 )
118124} )
119125
120- Deno . test ( 'Watcher - debounce batches rapid events into fewer callbacks' , async ( ) => {
121- const tmpDir = Deno . makeTempDirSync ( )
122- let callbackCount = 0
123- const watcher = new Superwatcher ( {
124- path : tmpDir ,
125- debounceMs : 100 ,
126- onChange : ( ) => {
127- callbackCount ++
126+ Deno . test (
127+ 'Watcher - debounce batches rapid events into fewer callbacks' ,
128+ noLeaks ,
129+ async ( ) => {
130+ const tmpDir = Deno . makeTempDirSync ( )
131+ let callbackCount = 0
132+ const watcher = new Superwatcher ( {
133+ path : tmpDir ,
134+ debounceMs : 100 ,
135+ onChange : ( ) => {
136+ callbackCount ++
137+ }
138+ } )
139+ watcher . start ( )
140+ await delay ( 50 )
141+ for ( let i = 0 ; i < 20 ; i ++ ) {
142+ Deno . writeTextFileSync ( `${ tmpDir } /rapid_${ i } .txt` , `data_${ i } ` )
128143 }
129- } )
130- watcher . start ( )
131- await delay ( 50 )
132- for ( let i = 0 ; i < 20 ; i ++ ) {
133- Deno . writeTextFileSync ( `${ tmpDir } /rapid_${ i } .txt` , `data_${ i } ` )
134- }
135- await delay ( 300 )
136- watcher . dispose ( )
137- assertEquals ( callbackCount >= 1 , true )
138- assertEquals ( callbackCount < 20 , true )
139- Deno . removeSync ( tmpDir , { recursive : true } )
140- } )
141-
142- Deno . test ( 'Watcher - detects events in deeply nested directory' , async ( ) => {
143- const tmpDir = Deno . makeTempDirSync ( )
144- let deepPath = tmpDir
145- for ( let i = 0 ; i < 5 ; i ++ ) {
146- deepPath += `/level_${ i } `
147- Deno . mkdirSync ( deepPath )
144+ await delay ( 300 )
145+ watcher . dispose ( )
146+ assertEquals ( callbackCount >= 1 , true )
147+ assertEquals ( callbackCount < 20 , true )
148+ Deno . removeSync ( tmpDir , { recursive : true } )
148149 }
149- const events : string [ ] = [ ]
150- const watcher = new Superwatcher ( {
151- path : tmpDir ,
152- debounceMs : 30 ,
153- onChange : ( evts ) => {
154- for ( const e of evts ) {
155- events . push ( e . path )
156- }
150+ )
151+
152+ Deno . test (
153+ 'Watcher - detects events in deeply nested directory' ,
154+ noLeaks ,
155+ async ( ) => {
156+ const tmpDir = Deno . makeTempDirSync ( )
157+ let deepPath = tmpDir
158+ for ( let i = 0 ; i < 5 ; i ++ ) {
159+ deepPath += `/level_${ i } `
160+ Deno . mkdirSync ( deepPath )
157161 }
158- } )
159- watcher . start ( )
160- await delay ( 50 )
161- Deno . writeTextFileSync ( `${ deepPath } /deep.txt` , 'deep' )
162- await delay ( 200 )
163- watcher . dispose ( )
164- assertEquals (
165- events . some ( ( e ) => e . endsWith ( '/deep.txt' ) ) ,
166- true
167- )
168- Deno . removeSync ( tmpDir , { recursive : true } )
169- } )
162+ const events : string [ ] = [ ]
163+ const watcher = new Superwatcher ( {
164+ path : tmpDir ,
165+ debounceMs : 30 ,
166+ onChange : ( evts ) => {
167+ for ( const e of evts ) {
168+ events . push ( e . path )
169+ }
170+ }
171+ } )
172+ watcher . start ( )
173+ await delay ( 50 )
174+ Deno . writeTextFileSync ( `${ deepPath } /deep.txt` , 'deep' )
175+ await delay ( 200 )
176+ watcher . dispose ( )
177+ assertEquals (
178+ events . some ( ( e ) => e . endsWith ( '/deep.txt' ) ) ,
179+ true
180+ )
181+ Deno . removeSync ( tmpDir , { recursive : true } )
182+ }
183+ )
170184
171- Deno . test ( 'Watcher - detects file creation' , async ( ) => {
185+ Deno . test ( 'Watcher - detects file creation' , noLeaks , async ( ) => {
172186 const tmpDir = Deno . makeTempDirSync ( )
173187 const events : Array < { kind : string ; path : string } > = [ ]
174188 const watcher = new Superwatcher ( {
@@ -193,7 +207,7 @@ Deno.test('Watcher - detects file creation', async () => {
193207 Deno . removeSync ( tmpDir , { recursive : true } )
194208} )
195209
196- Deno . test ( 'Watcher - detects file modification' , async ( ) => {
210+ Deno . test ( 'Watcher - detects file modification' , noLeaks , async ( ) => {
197211 const tmpDir = Deno . makeTempDirSync ( )
198212 Deno . writeTextFileSync ( `${ tmpDir } /exist.txt` , 'original' )
199213 const events : Array < { kind : string ; path : string } > = [ ]
@@ -219,7 +233,7 @@ Deno.test('Watcher - detects file modification', async () => {
219233 Deno . removeSync ( tmpDir , { recursive : true } )
220234} )
221235
222- Deno . test ( 'Watcher - detects file removal' , async ( ) => {
236+ Deno . test ( 'Watcher - detects file removal' , noLeaks , async ( ) => {
223237 const tmpDir = Deno . makeTempDirSync ( )
224238 Deno . writeTextFileSync ( `${ tmpDir } /del.txt` , 'bye' )
225239 const events : Array < { kind : string ; path : string } > = [ ]
@@ -244,7 +258,7 @@ Deno.test('Watcher - detects file removal', async () => {
244258 Deno . removeSync ( tmpDir , { recursive : true } )
245259} )
246260
247- Deno . test ( 'Watcher - dispose called multiple times is safe' , ( ) => {
261+ Deno . test ( 'Watcher - dispose called multiple times is safe' , noLeaks , ( ) => {
248262 const tmpDir = Deno . makeTempDirSync ( )
249263 const watcher = new Superwatcher ( { path : tmpDir , debounceMs : 50 , onChange : ( ) => { } } )
250264 watcher . start ( )
@@ -261,39 +275,43 @@ Deno.test('Watcher - dispose without start is safe', () => {
261275 Deno . removeSync ( tmpDir , { recursive : true } )
262276} )
263277
264- Deno . test ( 'Watcher - file path only receives events for that file' , async ( ) => {
265- const tmpDir = Deno . makeTempDirSync ( )
266- const targetFile = `${ tmpDir } /target.json`
267- Deno . writeTextFileSync ( targetFile , '{}' )
268- Deno . writeTextFileSync ( `${ tmpDir } /other.txt` , 'x' )
269- const events : string [ ] = [ ]
270- const watcher = new Superwatcher ( {
271- path : targetFile ,
272- debounceMs : 30 ,
273- onChange : ( evts ) => {
274- for ( const e of evts ) {
275- events . push ( e . path )
278+ Deno . test (
279+ 'Watcher - file path only receives events for that file' ,
280+ noLeaks ,
281+ async ( ) => {
282+ const tmpDir = Deno . makeTempDirSync ( )
283+ const targetFile = `${ tmpDir } /target.json`
284+ Deno . writeTextFileSync ( targetFile , '{}' )
285+ Deno . writeTextFileSync ( `${ tmpDir } /other.txt` , 'x' )
286+ const events : string [ ] = [ ]
287+ const watcher = new Superwatcher ( {
288+ path : targetFile ,
289+ debounceMs : 30 ,
290+ onChange : ( evts ) => {
291+ for ( const e of evts ) {
292+ events . push ( e . path )
293+ }
276294 }
277- }
278- } )
279- watcher . start ( )
280- await delay ( 50 )
281- Deno . writeTextFileSync ( targetFile , '{"u":1} ' )
282- Deno . writeTextFileSync ( ` ${ tmpDir } /other.txt` , 'noise' )
283- await delay ( 150 )
284- watcher . dispose ( )
285- assertEquals (
286- events . some ( ( e ) => e . endsWith ( 'target.json' ) ) ,
287- true
288- )
289- assertEquals (
290- events . some ( ( e ) => e . endsWith ( 'other.txt' ) ) ,
291- false
292- )
293- Deno . removeSync ( tmpDir , { recursive : true } )
294- } )
295+ } )
296+ watcher . start ( )
297+ await delay ( 50 )
298+ Deno . writeTextFileSync ( targetFile , '{"u":1}' )
299+ Deno . writeTextFileSync ( ` ${ tmpDir } /other.txt` , 'noise ' )
300+ await delay ( 150 )
301+ watcher . dispose ( )
302+ assertEquals (
303+ events . some ( ( e ) => e . endsWith ( 'target.json' ) ) ,
304+ true
305+ )
306+ assertEquals (
307+ events . some ( ( e ) => e . endsWith ( 'other.txt' ) ) ,
308+ false
309+ )
310+ Deno . removeSync ( tmpDir , { recursive : true } )
311+ }
312+ )
295313
296- Deno . test ( 'Watcher - ignore function pattern filters events' , async ( ) => {
314+ Deno . test ( 'Watcher - ignore function pattern filters events' , noLeaks , async ( ) => {
297315 const tmpDir = Deno . makeTempDirSync ( )
298316 const events : string [ ] = [ ]
299317 const watcher = new Superwatcher ( {
@@ -323,7 +341,7 @@ Deno.test('Watcher - ignore function pattern filters events', async () => {
323341 Deno . removeSync ( tmpDir , { recursive : true } )
324342} )
325343
326- Deno . test ( 'Watcher - ignore mixed matchers all applied' , async ( ) => {
344+ Deno . test ( 'Watcher - ignore mixed matchers all applied' , noLeaks , async ( ) => {
327345 const tmpDir = Deno . makeTempDirSync ( )
328346 const events : string [ ] = [ ]
329347 const watcher = new Superwatcher ( {
@@ -355,7 +373,7 @@ Deno.test('Watcher - ignore mixed matchers all applied', async () => {
355373 Deno . removeSync ( tmpDir , { recursive : true } )
356374} )
357375
358- Deno . test ( 'Watcher - ignore regex pattern filters events' , async ( ) => {
376+ Deno . test ( 'Watcher - ignore regex pattern filters events' , noLeaks , async ( ) => {
359377 const tmpDir = Deno . makeTempDirSync ( )
360378 const events : string [ ] = [ ]
361379 const watcher = new Superwatcher ( {
@@ -385,7 +403,7 @@ Deno.test('Watcher - ignore regex pattern filters events', async () => {
385403 Deno . removeSync ( tmpDir , { recursive : true } )
386404} )
387405
388- Deno . test ( 'Watcher - ignore string pattern filters events' , async ( ) => {
406+ Deno . test ( 'Watcher - ignore string pattern filters events' , noLeaks , async ( ) => {
389407 const tmpDir = Deno . makeTempDirSync ( )
390408 const events : string [ ] = [ ]
391409 const watcher = new Superwatcher ( {
@@ -415,7 +433,7 @@ Deno.test('Watcher - ignore string pattern filters events', async () => {
415433 Deno . removeSync ( tmpDir , { recursive : true } )
416434} )
417435
418- Deno . test ( 'Watcher - onChange throw does not crash the watcher' , async ( ) => {
436+ Deno . test ( 'Watcher - onChange throw does not crash the watcher' , noLeaks , async ( ) => {
419437 const tmpDir = Deno . makeTempDirSync ( )
420438 let callCount = 0
421439 const watcher = new Superwatcher ( {
@@ -437,7 +455,7 @@ Deno.test('Watcher - onChange throw does not crash the watcher', async () => {
437455 Deno . removeSync ( tmpDir , { recursive : true } )
438456} )
439457
440- Deno . test ( 'Watcher - rapid start calls do not crash' , ( ) => {
458+ Deno . test ( 'Watcher - rapid start calls do not crash' , noLeaks , ( ) => {
441459 const tmpDir = Deno . makeTempDirSync ( )
442460 const watcher = new Superwatcher ( { path : tmpDir , debounceMs : 50 , onChange : ( ) => { } } )
443461 for ( let i = 0 ; i < 50 ; i ++ ) {
@@ -447,7 +465,7 @@ Deno.test('Watcher - rapid start calls do not crash', () => {
447465 Deno . removeSync ( tmpDir , { recursive : true } )
448466} )
449467
450- Deno . test ( 'Watcher - recursive false skips subdirectory events' , async ( ) => {
468+ Deno . test ( 'Watcher - recursive false skips subdirectory events' , noLeaks , async ( ) => {
451469 const tmpDir = Deno . makeTempDirSync ( )
452470 Deno . mkdirSync ( `${ tmpDir } /sub` )
453471 const events : string [ ] = [ ]
@@ -478,7 +496,7 @@ Deno.test('Watcher - recursive false skips subdirectory events', async () => {
478496 Deno . removeSync ( tmpDir , { recursive : true } )
479497} )
480498
481- Deno . test ( 'Watcher - start then dispose then start again is safe' , ( ) => {
499+ Deno . test ( 'Watcher - start then dispose then start again is safe' , noLeaks , ( ) => {
482500 const tmpDir = Deno . makeTempDirSync ( )
483501 const watcher = new Superwatcher ( { path : tmpDir , debounceMs : 50 , onChange : ( ) => { } } )
484502 watcher . start ( )
@@ -494,7 +512,7 @@ Deno.test('Watcher - start with empty path array is a no-op', () => {
494512 watcher . dispose ( )
495513} )
496514
497- Deno . test ( 'Watcher - stress test with 100 files' , async ( ) => {
515+ Deno . test ( 'Watcher - stress test with 100 files' , noLeaks , async ( ) => {
498516 const tmpDir = Deno . makeTempDirSync ( )
499517 const events : Array < { kind : string ; path : string } > = [ ]
500518 const watcher = new Superwatcher ( {
@@ -517,7 +535,7 @@ Deno.test('Watcher - stress test with 100 files', async () => {
517535 Deno . removeSync ( tmpDir , { recursive : true } )
518536} )
519537
520- Deno . test ( 'Watcher - stress test with 100 start-dispose cycles' , ( ) => {
538+ Deno . test ( 'Watcher - stress test with 100 start-dispose cycles' , noLeaks , ( ) => {
521539 const tmpDir = Deno . makeTempDirSync ( )
522540 for ( let i = 0 ; i < 100 ; i ++ ) {
523541 const watcher = new Superwatcher ( { path : tmpDir , debounceMs : 10 , onChange : ( ) => { } } )
@@ -527,7 +545,7 @@ Deno.test('Watcher - stress test with 100 start-dispose cycles', () => {
527545 Deno . removeSync ( tmpDir , { recursive : true } )
528546} )
529547
530- Deno . test ( 'Watcher - stress test with 5 concurrent watchers' , async ( ) => {
548+ Deno . test ( 'Watcher - stress test with 5 concurrent watchers' , noLeaks , async ( ) => {
531549 const tmpDir = Deno . makeTempDirSync ( )
532550 const counts : Record < number , number > = { 0 : 0 , 1 : 0 , 2 : 0 , 3 : 0 , 4 : 0 }
533551 const watchers = [ 0 , 1 , 2 , 3 , 4 ] . map ( ( i ) =>
@@ -557,7 +575,7 @@ Deno.test('Watcher - stress test with 5 concurrent watchers', async () => {
557575 Deno . removeSync ( tmpDir , { recursive : true } )
558576} )
559577
560- Deno . test ( 'Watcher - writeStable does not delay remove events' , async ( ) => {
578+ Deno . test ( 'Watcher - writeStable does not delay remove events' , noLeaks , async ( ) => {
561579 const tmpDir = Deno . makeTempDirSync ( )
562580 Deno . writeTextFileSync ( `${ tmpDir } /del.txt` , 'bye' )
563581 const events : Array < { kind : string ; path : string } > = [ ]
@@ -583,7 +601,7 @@ Deno.test('Watcher - writeStable does not delay remove events', async () => {
583601 Deno . removeSync ( tmpDir , { recursive : true } )
584602} )
585603
586- Deno . test ( 'Watcher - writeStable waits for file size stability' , async ( ) => {
604+ Deno . test ( 'Watcher - writeStable waits for file size stability' , noLeaks , async ( ) => {
587605 const tmpDir = Deno . makeTempDirSync ( )
588606 const stableFile = `${ tmpDir } /growing.bin`
589607 const events : string [ ] = [ ]
0 commit comments