Skip to content

Commit ecd7122

Browse files
committed
test: Use test openers instead of monkey-patching null openers
1 parent f3c8b4a commit ecd7122

File tree

4 files changed

+31
-52
lines changed

4 files changed

+31
-52
lines changed

src/files/filetree.test.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,11 @@ import { assert, assertEquals } from '@std/assert'
22
import { FileIgnoreRules } from './ignore.ts'
33
import { BIDSFile, type FileOpener, type FileTree } from '../types/filetree.ts'
44
import { filesToTree } from './filetree.ts'
5-
import { asyncStreamFromString } from '../tests/utils.ts'
6-
7-
class NullFileOpener implements FileOpener {
8-
size = 0
9-
stream = () => asyncStreamFromString('')
10-
text = () => Promise.resolve('')
11-
readBytes = async (size: number, offset?: number) => new Uint8Array()
12-
}
5+
import { StringOpener } from './openers.test.ts'
136

147
export function pathToFile(path: string, ignored: boolean = false): BIDSFile {
158
const name = path.split('/').pop() as string
16-
return new BIDSFile(path, new NullFileOpener(), ignored)
9+
return new BIDSFile(path, new StringOpener(''), ignored)
1710
}
1811

1912
export function pathsToTree(paths: string[], ignore?: string[]): FileTree {

src/files/tsv.test.ts

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,14 @@ import {
88
import { pathToFile } from './filetree.test.ts'
99
import { BIDSFileDeno } from './deno.ts'
1010
import { loadTSV, loadTSVGZ } from './tsv.ts'
11-
import { asyncStreamFromString } from '../tests/utils.ts'
1211
import { ColumnsMap } from '../types/columns.ts'
1312
import { testAsyncFileAccess } from './access.test.ts'
14-
15-
async function compressedStreamFromString(
16-
str: string,
17-
): Promise<ReadableStream<Uint8Array<ArrayBuffer>>> {
18-
return asyncStreamFromString(str).then((stream) =>
19-
stream.pipeThrough(new CompressionStream('gzip')) as ReadableStream<Uint8Array<ArrayBuffer>>
20-
)
21-
}
13+
import { CompressedStringOpener, StringOpener } from './openers.test.ts'
2214

2315
Deno.test('TSV loading', async (t) => {
2416
await t.step('Empty file produces empty map', async () => {
2517
const file = pathToFile('/empty.tsv')
26-
file.opener.stream = () => asyncStreamFromString('')
18+
file.opener = new StringOpener('')
2719

2820
const map = await loadTSV(file)
2921
// map.size looks for a column called map, so work around it
@@ -32,7 +24,7 @@ Deno.test('TSV loading', async (t) => {
3224

3325
await t.step('Single row file produces header-only map', async () => {
3426
const file = pathToFile('/single_row.tsv')
35-
file.opener.stream = () => asyncStreamFromString('a\tb\tc\n')
27+
file.opener = new StringOpener('a\tb\tc\n')
3628

3729
const map = await loadTSV(file)
3830
assertEquals(map.a, [])
@@ -42,23 +34,23 @@ Deno.test('TSV loading', async (t) => {
4234

4335
await t.step('Single column file produces single column map', async () => {
4436
const file = pathToFile('/single_column.tsv')
45-
file.opener.stream = () => asyncStreamFromString('a\n1\n2\n3\n')
37+
file.opener = new StringOpener('a\n1\n2\n3\n')
4638

4739
const map = await loadTSV(file)
4840
assertEquals(map.a, ['1', '2', '3'])
4941
})
5042

5143
await t.step('Missing final newline is ignored', async () => {
5244
const file = pathToFile('/missing_newline.tsv')
53-
file.opener.stream = () => asyncStreamFromString('a\n1\n2\n3')
45+
file.opener = new StringOpener('a\n1\n2\n3')
5446

5547
const map = await loadTSV(file)
5648
assertEquals(map.a, ['1', '2', '3'])
5749
})
5850

5951
await t.step('Empty row throws issue', async () => {
6052
const file = pathToFile('/empty_row.tsv')
61-
file.opener.stream = () => asyncStreamFromString('a\tb\tc\n1\t2\t3\n\n4\t5\t6\n')
53+
file.opener = new StringOpener('a\tb\tc\n1\t2\t3\n\n4\t5\t6\n')
6254

6355
try {
6456
await loadTSV(file)
@@ -69,7 +61,7 @@ Deno.test('TSV loading', async (t) => {
6961

7062
await t.step('Mismatched row length throws issue', async () => {
7163
const file = pathToFile('/mismatched_row.tsv')
72-
file.opener.stream = () => asyncStreamFromString('a\tb\tc\n1\t2\t3\n4\t5\n')
64+
file.opener = new StringOpener('a\tb\tc\n1\t2\t3\n4\t5\n')
7365

7466
try {
7567
await loadTSV(file)
@@ -82,7 +74,7 @@ Deno.test('TSV loading', async (t) => {
8274
const file = pathToFile('/long.tsv')
8375
// Use 1500 to avoid overlap with default initial capacity
8476
const text = 'a\tb\tc\n' + '1\t2\t3\n'.repeat(1500)
85-
file.opener.stream = () => asyncStreamFromString(text)
77+
file.opener = new StringOpener(text)
8678

8779
let map = await loadTSV(file, 0)
8880
assertEquals(map.a, [])
@@ -91,29 +83,26 @@ Deno.test('TSV loading', async (t) => {
9183

9284
// Do not assume that caching respects maxRows in this test
9385
loadTSV.cache.clear()
94-
file.opener.stream = () => asyncStreamFromString(text)
9586
map = await loadTSV(file, 1)
9687
assertEquals(map.a, ['1'])
9788
assertEquals(map.b, ['2'])
9889
assertEquals(map.c, ['3'])
9990

10091
loadTSV.cache.clear()
101-
file.opener.stream = () => asyncStreamFromString(text)
10292
map = await loadTSV(file, 2)
10393
assertEquals(map.a, ['1', '1'])
10494
assertEquals(map.b, ['2', '2'])
10595
assertEquals(map.c, ['3', '3'])
10696

10797
loadTSV.cache.clear()
108-
file.opener.stream = () => asyncStreamFromString(text)
10998
map = await loadTSV(file, -1)
11099
assertEquals(map.a, Array(1500).fill('1'))
111100
assertEquals(map.b, Array(1500).fill('2'))
112101
assertEquals(map.c, Array(1500).fill('3'))
113102

114103
loadTSV.cache.clear()
115104
// Check that maxRows does not truncate shorter files
116-
file.opener.stream = () => asyncStreamFromString('a\tb\tc\n1\t2\t3\n4\t5\t6\n7\t8\t9\n')
105+
file.opener = new StringOpener('a\tb\tc\n1\t2\t3\n4\t5\t6\n7\t8\t9\n')
117106
map = await loadTSV(file, 4)
118107
assertEquals(map.a, ['1', '4', '7'])
119108
assertEquals(map.b, ['2', '5', '8'])
@@ -125,15 +114,14 @@ Deno.test('TSV loading', async (t) => {
125114
const file = pathToFile('/long.tsv')
126115
// Use 1500 to avoid overlap with default initial capacity
127116
const text = 'a\tb\tc\n' + '1\t2\t3\n'.repeat(1500)
128-
file.opener.stream = () => asyncStreamFromString(text)
117+
file.opener = new StringOpener(text)
129118

130119
let map = await loadTSV(file, 2)
131120
assertEquals(map.a, ['1', '1'])
132121
assertEquals(map.b, ['2', '2'])
133122
assertEquals(map.c, ['3', '3'])
134123

135124
// Replace stream to ensure cache does not depend on deep object equality
136-
file.opener.stream = () => asyncStreamFromString(text)
137125
let repeatMap = await loadTSV(file, 2)
138126
assertStrictEquals(map, repeatMap)
139127

@@ -151,21 +139,19 @@ Deno.test('TSV loading', async (t) => {
151139
const file = pathToFile('/long.tsv')
152140
// Use 1500 to avoid overlap with default initial capacity
153141
const text = 'a\tb\tc\n' + '1\t2\t3\n'.repeat(1500)
154-
file.opener.stream = () => asyncStreamFromString(text)
142+
file.opener = new StringOpener(text)
155143

156144
let map = await loadTSV(file, 2)
157145
assertEquals(map.a, ['1', '1'])
158146
assertEquals(map.b, ['2', '2'])
159147
assertEquals(map.c, ['3', '3'])
160148

161-
file.opener.stream = () => asyncStreamFromString(text)
162149
let repeatMap = await loadTSV(file, 3)
163150
assertNotStrictEquals(map, repeatMap)
164151
assertEquals(repeatMap.a, ['1', '1', '1'])
165152
assertEquals(repeatMap.b, ['2', '2', '2'])
166153
assertEquals(repeatMap.c, ['3', '3', '3'])
167154

168-
file.opener.stream = () => asyncStreamFromString(text)
169155
repeatMap = await loadTSV(file, 2)
170156
assertStrictEquals(map, repeatMap)
171157
assertEquals(repeatMap.a, ['1', '1'])
@@ -175,7 +161,7 @@ Deno.test('TSV loading', async (t) => {
175161

176162
await t.step('Raises issue on duplicate header', async () => {
177163
const file = pathToFile('/duplicate_header.tsv')
178-
file.opener.stream = () => asyncStreamFromString('a\ta\n1\t2\n')
164+
file.opener = new StringOpener('a\ta\n1\t2\n')
179165

180166
try {
181167
await loadTSV(file)
@@ -203,7 +189,7 @@ Deno.test('TSV loading', async (t) => {
203189
Deno.test('TSVGZ loading', async (t) => {
204190
await t.step('No header and empty file produces empty map', async () => {
205191
const file = pathToFile('/empty.tsv.gz')
206-
file.opener.stream = () => compressedStreamFromString('')
192+
file.opener = new CompressedStringOpener('')
207193

208194
const map = await loadTSVGZ(file, [])
209195
// map.size looks for a column called map, so work around it
@@ -212,7 +198,7 @@ Deno.test('TSVGZ loading', async (t) => {
212198

213199
await t.step('Empty file produces header-only map', async () => {
214200
const file = pathToFile('/empty.tsv.gz')
215-
file.opener.stream = () => compressedStreamFromString('')
201+
file.opener = new CompressedStringOpener('')
216202

217203
const map = await loadTSVGZ(file, ['a', 'b', 'c'])
218204
assertEquals(map.a, [])
@@ -222,15 +208,15 @@ Deno.test('TSVGZ loading', async (t) => {
222208

223209
await t.step('Single column file produces single column maps', async () => {
224210
const file = pathToFile('/single_column.tsv')
225-
file.opener.stream = () => compressedStreamFromString('1\n2\n3\n')
211+
file.opener = new CompressedStringOpener('1\n2\n3\n')
226212

227213
const map = await loadTSVGZ(file, ['a'])
228214
assertEquals(map.a, ['1', '2', '3'])
229215
})
230216

231217
await t.step('Mismatched header length throws issue', async () => {
232218
const file = pathToFile('/single_column.tsv.gz')
233-
file.opener.stream = () => compressedStreamFromString('1\n2\n3\n')
219+
file.opener = new CompressedStringOpener('1\n2\n3\n')
234220

235221
try {
236222
await loadTSVGZ(file, ['a', 'b'])
@@ -241,15 +227,15 @@ Deno.test('TSVGZ loading', async (t) => {
241227

242228
await t.step('Missing final newline is ignored', async () => {
243229
const file = pathToFile('/missing_newline.tsv.gz')
244-
file.opener.stream = () => compressedStreamFromString('1\n2\n3')
230+
file.opener = new CompressedStringOpener('1\n2\n3')
245231

246232
const map = await loadTSVGZ(file, ['a'])
247233
assertEquals(map.a, ['1', '2', '3'])
248234
})
249235

250236
await t.step('Empty row throws issue', async () => {
251237
const file = pathToFile('/empty_row.tsv.gz')
252-
file.opener.stream = () => compressedStreamFromString('1\t2\t3\n\n4\t5\t6\n')
238+
file.opener = new CompressedStringOpener('1\t2\t3\n\n4\t5\t6\n')
253239

254240
try {
255241
await loadTSVGZ(file, ['a', 'b', 'c'])
@@ -260,7 +246,7 @@ Deno.test('TSVGZ loading', async (t) => {
260246

261247
await t.step('Mislabeled TSV throws issue', async () => {
262248
const file = pathToFile('/mismatched_row.tsv.gz')
263-
file.opener.stream = () => asyncStreamFromString('a\tb\tc\n1\t2\t3\n4\t5\n')
249+
file.opener = new StringOpener('a\tb\tc\n1\t2\t3\n4\t5\n')
264250

265251
try {
266252
await loadTSVGZ(file, ['a', 'b', 'c'])
@@ -274,33 +260,30 @@ Deno.test('TSVGZ loading', async (t) => {
274260
// Use 1500 to avoid overlap with default initial capacity
275261
const headers = ['a', 'b', 'c']
276262
const text = '1\t2\t3\n'.repeat(1500)
277-
file.opener.stream = () => compressedStreamFromString(text)
263+
file.opener = new CompressedStringOpener(text)
278264

279265
let map = await loadTSVGZ(file, headers, 0)
280266
assertEquals(map.a, [])
281267
assertEquals(map.b, [])
282268
assertEquals(map.c, [])
283269

284-
file.opener.stream = () => compressedStreamFromString(text)
285270
map = await loadTSVGZ(file, headers, 1)
286271
assertEquals(map.a, ['1'])
287272
assertEquals(map.b, ['2'])
288273
assertEquals(map.c, ['3'])
289274

290-
file.opener.stream = () => compressedStreamFromString(text)
291275
map = await loadTSVGZ(file, headers, 2)
292276
assertEquals(map.a, ['1', '1'])
293277
assertEquals(map.b, ['2', '2'])
294278
assertEquals(map.c, ['3', '3'])
295279

296-
file.opener.stream = () => compressedStreamFromString(text)
297280
map = await loadTSVGZ(file, headers, -1)
298281
assertEquals(map.a, Array(1500).fill('1'))
299282
assertEquals(map.b, Array(1500).fill('2'))
300283
assertEquals(map.c, Array(1500).fill('3'))
301284

302285
// Check that maxRows does not truncate shorter files
303-
file.opener.stream = () => compressedStreamFromString('1\t2\t3\n4\t5\t6\n7\t8\t9\n')
286+
file.opener = new CompressedStringOpener('1\t2\t3\n4\t5\t6\n7\t8\t9\n')
304287
map = await loadTSVGZ(file, headers, 4)
305288
assertEquals(map.a, ['1', '4', '7'])
306289
assertEquals(map.b, ['2', '5', '8'])

src/tests/regression.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { assert } from '@std/assert'
22
import { pathsToTree } from '../files/filetree.test.ts'
33
import { validate } from '../validators/bids.ts'
44
import type { BIDSFile } from '../types/filetree.ts'
5-
import { asyncStreamFromString } from './utils.ts'
5+
import { StringOpener } from '../files/openers.test.ts'
66

77
Deno.test('Regression tests', async (t) => {
88
await t.step('Verify ignored files in scans.tsv do not trigger error', async () => {
@@ -18,7 +18,7 @@ Deno.test('Regression tests', async (t) => {
1818
// Without ignore, NOT_INCLUDED is triggered for CT, but the scans file is happy
1919
let ds = pathsToTree(paths)
2020
let scans_tsv = ds.get('sub-01/sub-01_scans.tsv') as BIDSFile
21-
scans_tsv.opener.stream = () => asyncStreamFromString(scans_content)
21+
scans_tsv.opener = new StringOpener(scans_content)
2222
let result = await validate(ds, {
2323
datasetPath: '/dataset',
2424
debug: 'ERROR',
@@ -32,7 +32,7 @@ Deno.test('Regression tests', async (t) => {
3232
// With ignore, NOT_INCLUDED is not triggered for CT, and the scans file is still happy
3333
ds = pathsToTree(paths, ignore)
3434
scans_tsv = ds.get('sub-01/sub-01_scans.tsv') as BIDSFile
35-
scans_tsv.opener.stream = () => asyncStreamFromString(scans_content)
35+
scans_tsv.opener = new StringOpener(scans_content)
3636
result = await validate(ds, {
3737
datasetPath: '/dataset',
3838
debug: 'ERROR',

src/validators/validateFiles.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ import type { GenericSchema, Schema } from '../types/schema.ts'
77
import type { DatasetIssues } from '../issues/datasetIssues.ts'
88
import type { BIDSFile } from '../types/filetree.ts'
99
import { pathsToTree } from '../files/filetree.test.ts'
10+
import { StringOpener } from '../files/openers.test.ts'
1011

1112
const schema = await loadSchema()
1213

13-
function makeContext(path: string): BIDSContext {
14+
function makeContext(path: string, contents: string = ''): BIDSContext {
1415
const tree = pathsToTree([path])
1516
const dataset = new BIDSContextDataset({ schema, tree })
16-
return new BIDSContext(tree.get(path) as BIDSFile, dataset)
17+
const file = tree.get(path) as BIDSFile
18+
file.opener = new StringOpener(contents)
19+
return new BIDSContext(file, dataset)
1720
}
1821

1922
Deno.test('test valid paths', async (t) => {

0 commit comments

Comments
 (0)