Skip to content

Commit d692795

Browse files
committed
attempt to fix flakey tests on win
1 parent 61162a5 commit d692795

1 file changed

Lines changed: 62 additions & 38 deletions

File tree

test/reader-watch.test.js

Lines changed: 62 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,14 @@ import { createReaderWatch } from '../src/reader-watch.js'
1010

1111
/** @import {Reader} from 'styled-map-package' */
1212

13+
const isWin = process.platform === 'win32'
1314
const __dirname = path.dirname(fileURLToPath(import.meta.url))
1415
const FIXTURE1_PATH = path.join(__dirname, 'fixtures', 'demotiles-z2.smp')
1516
const FIXTURE2_PATH = path.join(__dirname, 'fixtures', 'osm-bright-z6.smp')
1617

1718
/** @param {import('node:test').TestContext} t */
18-
function tempDir(t) {
19-
const dir = fs.mkdtempSync(path.join(tmpdir(), 'comapeo-map-server-'))
20-
t.after(async () => {
21-
console.log('tempDir cleanup')
22-
// Wait on Windows to ensure all file handles are released
23-
if (process.platform === 'win32') {
24-
await setTimeout(1000)
25-
}
26-
console.log('attempting to delete')
27-
fs.rmSync(dir, { recursive: true, force: true })
28-
})
29-
return dir
19+
function tempDir() {
20+
return fs.mkdtempSync(path.join(tmpdir(), 'comapeo-map-server-'))
3021
}
3122

3223
test('createReaderWatch - valid smp file', async (t) => {
@@ -41,12 +32,15 @@ test('createReaderWatch - valid smp file', async (t) => {
4132
})
4233

4334
test('createReaderWatch - file does not exist initially', async (t) => {
44-
const dir = tempDir(t)
35+
const dir = tempDir()
4536
const nonExistentPath = path.join(dir, 'nonexistent.smp')
4637
assert(!fs.existsSync(nonExistentPath), 'File should not exist yet')
4738

4839
const getReader = createReaderWatch(nonExistentPath)
49-
t.after(getReader.close)
40+
t.after(async () => {
41+
await getReader.close()
42+
await rmDir(dir)
43+
})
5044
const reader1 = getReader()
5145
// Attempting to read from non-existent file should throw
5246
await assert.rejects(
@@ -55,33 +49,34 @@ test('createReaderWatch - file does not exist initially', async (t) => {
5549
'Reading from non-existent file should throw error with ENOENT code and filepath in message',
5650
)
5751
fs.copyFileSync(FIXTURE1_PATH, nonExistentPath)
58-
await setTimeout(200) // Wait for watcher to detect change
52+
await syncDir(dir) // Ensure copy is flushed and directory is synced
5953
const reader2 = getReader()
6054
// Now the reader should point to the new file
6155
const style = await reader2.getStyle()
6256
assert(style, 'Style should be readable after file is created')
6357
})
6458

6559
test('createReaderWatch - file is replaced with different file', async (t) => {
66-
const dir = tempDir(t)
60+
const dir = tempDir()
6761
const targetPath = path.join(dir, 'replaced.smp')
6862

6963
// Start with the fixture file
7064
fs.copyFileSync(FIXTURE1_PATH, targetPath)
71-
// Ensure file is fully written (otherwise the copy triggers the watcher)
72-
await setTimeout(200)
65+
await syncDir(dir)
7366

7467
const getReader = createReaderWatch(targetPath)
75-
t.after(getReader.close)
68+
t.after(async () => {
69+
await getReader.close()
70+
await rmDir(dir)
71+
})
7672

7773
// Read from initial file
7874
const reader1 = getReader()
7975
const style1 = await reader1.getStyle()
8076
assert.equal(style1.name, 'MapLibre', 'Expected style name from fixture 1')
8177

8278
fs.copyFileSync(FIXTURE2_PATH, targetPath)
83-
// Wait for file system watcher to detect the change
84-
await setTimeout(200)
79+
await syncDir(dir)
8580

8681
// The reader should be recreated
8782
const reader2 = getReader()
@@ -90,13 +85,16 @@ test('createReaderWatch - file is replaced with different file', async (t) => {
9085
})
9186

9287
test('createReaderWatch - file is deleted', async (t) => {
93-
const dir = tempDir(t)
88+
const dir = tempDir()
9489
const targetPath = path.join(dir, 'to-delete.smp')
9590
fs.copyFileSync(FIXTURE1_PATH, targetPath)
9691
await setTimeout(200) // Ensure file is fully written
9792

9893
const getReader = createReaderWatch(targetPath)
99-
t.after(getReader.close)
94+
t.after(async () => {
95+
await getReader.close()
96+
await rmDir(dir)
97+
})
10098

10199
const reader1 = getReader()
102100
const style1 = await reader1.getStyle()
@@ -108,7 +106,7 @@ test('createReaderWatch - file is deleted', async (t) => {
108106

109107
// Delete the file
110108
fs.unlinkSync(targetPath)
111-
await setTimeout(200) // Wait for watcher to detect change
109+
await syncDir(dir)
112110

113111
const reader2 = getReader()
114112
try {
@@ -126,15 +124,18 @@ test('createReaderWatch - file is deleted', async (t) => {
126124
})
127125

128126
test('createReaderWatch - invalid file (non-smp file)', async (t) => {
129-
const dir = tempDir(t)
127+
const dir = tempDir()
130128
const invalidPath = path.join(dir, 'invalid.smp')
131129

132130
// Create an invalid file (just some text)
133131
fs.writeFileSync(invalidPath, 'This is not a valid SMP file')
134-
await setTimeout(200) // Ensure file is fully written
132+
await syncDir(dir)
135133

136134
const getReader = createReaderWatch(invalidPath)
137-
t.after(getReader.close)
135+
t.after(async () => {
136+
await getReader.close()
137+
await rmDir(dir)
138+
})
138139

139140
const reader = getReader()
140141
assert(reader, 'Reader instance should be returned even for invalid file')
@@ -146,7 +147,7 @@ test('createReaderWatch - invalid file (non-smp file)', async (t) => {
146147
)
147148

148149
fs.copyFileSync(FIXTURE1_PATH, invalidPath)
149-
await setTimeout(200) // Wait for watcher to detect change
150+
await syncDir(dir)
150151

151152
const reader2 = getReader()
152153
const style = await reader2.getStyle()
@@ -158,12 +159,15 @@ test('createReaderWatch - invalid file (non-smp file)', async (t) => {
158159
})
159160

160161
test('createReaderWatch - close() cleans up watcher', async (t) => {
161-
const dir = tempDir(t)
162+
const dir = tempDir()
162163
const targetPath = path.join(dir, 'to-close.smp')
163164
fs.copyFileSync(FIXTURE1_PATH, targetPath)
164-
await setTimeout(200) // Ensure file is fully written
165+
await syncDir(dir)
165166

166167
const getReader = createReaderWatch(targetPath)
168+
t.after(async () => {
169+
await rmDir(dir)
170+
})
167171

168172
const reader1 = getReader()
169173
const style = await reader1.getStyle()
@@ -174,7 +178,7 @@ test('createReaderWatch - close() cleans up watcher', async (t) => {
174178

175179
// Modify the file after closing
176180
fs.copyFileSync(FIXTURE2_PATH, targetPath)
177-
await setTimeout(200) // Wait to see if any watcher events occur
181+
await syncDir(dir)
178182

179183
const reader2 = getReader()
180184
// The reader should still be the old instance
@@ -194,14 +198,17 @@ test('createReaderWatch - close() cleans up watcher', async (t) => {
194198

195199
test('createReaderWatch - AbortSignal integration', async (t) => {
196200
const controller = new AbortController()
197-
const dir = tempDir(t)
201+
const dir = tempDir()
198202
const targetPath = path.join(dir, 'with-signal.smp')
199203
fs.copyFileSync(FIXTURE1_PATH, targetPath)
200-
await setTimeout(200) // Ensure file is fully written
204+
await syncDir(dir)
201205
const getReader = createReaderWatch(targetPath, {
202206
signal: controller.signal,
203207
})
204-
t.after(getReader.close)
208+
t.after(async () => {
209+
await getReader.close()
210+
await rmDir(dir)
211+
})
205212

206213
const reader = getReader()
207214
const style = await reader.getStyle()
@@ -211,7 +218,7 @@ test('createReaderWatch - AbortSignal integration', async (t) => {
211218
controller.abort()
212219

213220
fs.copyFileSync(FIXTURE2_PATH, targetPath)
214-
await setTimeout(200)
221+
await syncDir(dir)
215222

216223
// reader should still be same instance because watcher is closed
217224
const reader2 = getReader()
@@ -229,18 +236,22 @@ test('createReaderWatch - AbortSignal integration', async (t) => {
229236
})
230237

231238
test('createReaderWatch - watcher only recreates reader for target file changes', async (t) => {
232-
const dir = tempDir(t)
239+
const dir = tempDir()
233240

234241
const targetPath = path.join(dir, 'target.smp')
235242
const otherPath = path.join(dir, 'other-file.txt')
236243

237244
// Copy fixture to target
238245
fs.copyFileSync(FIXTURE1_PATH, targetPath)
239-
await setTimeout(200) // Ensure file is fully written
246+
await syncDir(dir)
240247

241248
const getReader = createReaderWatch(targetPath)
242249

243250
const reader1 = getReader()
251+
t.after(async () => {
252+
await getReader.close()
253+
await rmDir(dir)
254+
})
244255
const style1 = await reader1.getStyle()
245256
assert(style1, 'Initial style should be readable')
246257

@@ -249,7 +260,7 @@ test('createReaderWatch - watcher only recreates reader for target file changes'
249260
// the reader should NOT be recreated. However, on some platforms filename
250261
// may be null, causing recreation. We test the happy path here.
251262
fs.writeFileSync(otherPath, 'Some other content')
252-
await setTimeout(200) // Ensure file is fully written
263+
await syncDir(dir)
253264

254265
// reader should be same instance (not changed because of file watcher)
255266
const reader2 = getReader()
@@ -282,3 +293,16 @@ async function concat(stream) {
282293
}
283294
return Buffer.concat(chunks)
284295
}
296+
297+
/** @param {string} dirPath */
298+
async function syncDir(dirPath) {
299+
const dh = await fs.promises.open(dirPath, 'r')
300+
await dh.sync()
301+
await dh.close()
302+
await setTimeout(isWin ? 50 : 10)
303+
}
304+
305+
/** @param {string} dirPath */
306+
async function rmDir(dirPath) {
307+
await fs.promises.rm(dirPath, { recursive: true, force: true, maxRetries: 5 })
308+
}

0 commit comments

Comments
 (0)