1 | const tape = require('tape')
|
2 | const create = require('./helpers/create')
|
3 | const Replicator = require('./helpers/replicator')
|
4 |
|
5 | tape('basic fd read', function (t) {
|
6 | const drive = create()
|
7 | const content = Buffer.alloc(10000).fill('0123456789abcdefghijklmnopqrstuvwxyz')
|
8 |
|
9 | drive.writeFile('hi', content, function (err) {
|
10 | t.error(err, 'no error')
|
11 |
|
12 | drive.open('hi', 'r', function (err, fd) {
|
13 | t.error(err, 'no error')
|
14 | t.same(typeof fd, 'number')
|
15 | t.ok(fd > 5)
|
16 |
|
17 | const underflow = 37
|
18 | const buf = Buffer.alloc(content.length - underflow)
|
19 | let pos = 0
|
20 |
|
21 | drive.read(fd, buf, 0, buf.length, 0, function (err, bytesRead) {
|
22 | t.error(err, 'no error')
|
23 | pos += bytesRead
|
24 | t.same(bytesRead, buf.length, 'filled the buffer')
|
25 | t.same(buf, content.slice(0, buf.length))
|
26 |
|
27 | drive.read(fd, buf, 0, buf.length, pos, function (err, bytesRead) {
|
28 | t.error(err, 'no error')
|
29 | pos += bytesRead
|
30 | t.same(bytesRead, underflow, 'read missing bytes')
|
31 | t.same(buf.slice(0, underflow), content.slice(content.length - underflow))
|
32 | t.same(pos, content.length, 'read full file')
|
33 |
|
34 | drive.read(fd, buf, 0, buf.length, pos, function (err, bytesRead) {
|
35 | t.error(err, 'no error')
|
36 | t.same(bytesRead, 0, 'no more to read')
|
37 |
|
38 | drive.close(fd, function (err) {
|
39 | t.error(err, 'no error')
|
40 | t.end()
|
41 | })
|
42 | })
|
43 | })
|
44 | })
|
45 | })
|
46 | })
|
47 | })
|
48 |
|
49 | tape('basic fd read with implicit position', function (t) {
|
50 | const drive = create()
|
51 | const content = Buffer.alloc(10000).fill('0123456789abcdefghijklmnopqrstuvwxyz')
|
52 |
|
53 | drive.writeFile('hi', content, function (err) {
|
54 | t.error(err, 'no error')
|
55 |
|
56 | drive.open('hi', 'r', function (err, fd) {
|
57 | t.error(err, 'no error')
|
58 | t.same(typeof fd, 'number')
|
59 | t.ok(fd > 5)
|
60 |
|
61 | const underflow = 37
|
62 | const buf = Buffer.alloc(content.length - underflow)
|
63 | let pos = 0
|
64 |
|
65 | drive.read(fd, buf, 0, buf.length, function (err, bytesRead) {
|
66 | t.error(err, 'no error')
|
67 | pos += bytesRead
|
68 | t.same(bytesRead, buf.length, 'filled the buffer')
|
69 | t.same(buf, content.slice(0, buf.length))
|
70 |
|
71 | drive.read(fd, buf, 0, buf.length, function (err, bytesRead) {
|
72 | t.error(err, 'no error')
|
73 | pos += bytesRead
|
74 | t.same(bytesRead, underflow, 'read missing bytes')
|
75 | t.same(buf.slice(0, underflow), content.slice(content.length - underflow))
|
76 | t.same(pos, content.length, 'read full file')
|
77 |
|
78 | drive.read(fd, buf, 0, buf.length, function (err, bytesRead) {
|
79 | t.error(err, 'no error')
|
80 | t.same(bytesRead, 0, 'no more to read')
|
81 |
|
82 | drive.close(fd, function (err) {
|
83 | t.error(err, 'no error')
|
84 | t.end()
|
85 | })
|
86 | })
|
87 | })
|
88 | })
|
89 | })
|
90 | })
|
91 | })
|
92 |
|
93 | tape('fd read with zero length', function (t) {
|
94 | const drive = create()
|
95 | const content = Buffer.alloc(10000).fill('0123456789abcdefghijklmnopqrstuvwxyz')
|
96 |
|
97 | drive.writeFile('hi', content, function (err) {
|
98 | t.error(err, 'no error')
|
99 |
|
100 | drive.open('hi', 'r', function (err, fd) {
|
101 | t.error(err, 'no error')
|
102 |
|
103 | const buf = Buffer.alloc(content.length)
|
104 |
|
105 | drive.read(fd, buf, 0, 0, function (err, bytesRead) {
|
106 | t.error(err, 'no error')
|
107 | t.same(bytesRead, 0)
|
108 | t.end()
|
109 | })
|
110 | })
|
111 | })
|
112 | })
|
113 |
|
114 | tape('fd read with out-of-bounds offset', function (t) {
|
115 | const drive = create()
|
116 | const content = Buffer.alloc(10000).fill('0123456789abcdefghijklmnopqrstuvwxyz')
|
117 |
|
118 | drive.writeFile('hi', content, function (err) {
|
119 | t.error(err, 'no error')
|
120 |
|
121 | drive.open('hi', 'r', function (err, fd) {
|
122 | t.error(err, 'no error')
|
123 |
|
124 | const buf = Buffer.alloc(content.length)
|
125 |
|
126 | drive.read(fd, buf, content.length, 10, function (err, bytesRead) {
|
127 | t.error(err, 'no error')
|
128 | t.same(bytesRead, 0)
|
129 | t.end()
|
130 | })
|
131 | })
|
132 | })
|
133 | })
|
134 |
|
135 | tape('fd read with out-of-bounds length', function (t) {
|
136 | const drive = create()
|
137 | const content = Buffer.alloc(10000).fill('0123456789abcdefghijklmnopqrstuvwxyz')
|
138 |
|
139 | drive.writeFile('hi', content, function (err) {
|
140 | t.error(err, 'no error')
|
141 |
|
142 | drive.open('hi', 'r', function (err, fd) {
|
143 | t.error(err, 'no error')
|
144 |
|
145 | const buf = Buffer.alloc(content.length)
|
146 |
|
147 | drive.read(fd, buf, 0, content.length + 1, function (err, bytesRead) {
|
148 | t.error(err, 'no error')
|
149 | t.same(bytesRead, content.length)
|
150 | t.end()
|
151 | })
|
152 | })
|
153 | })
|
154 | })
|
155 |
|
156 | tape('fd read of empty drive', function (t) {
|
157 | const drive = create()
|
158 | drive.open('hi', 'r', function (err, fd) {
|
159 | t.true(err)
|
160 | t.same(err.errno, 2)
|
161 | t.end()
|
162 | })
|
163 | })
|
164 |
|
165 | tape('fd read of invalid file', function (t) {
|
166 | const drive = create()
|
167 | const content = Buffer.alloc(10000).fill('0123456789abcdefghijklmnopqrstuvwxyz')
|
168 |
|
169 | drive.writeFile('hi', content, function (err) {
|
170 | t.error(err, 'no error')
|
171 | drive.open('hello', 'r', function (err, fd) {
|
172 | t.true(err)
|
173 | t.same(err.errno, 2)
|
174 | t.end()
|
175 | })
|
176 | })
|
177 | })
|
178 |
|
179 | tape('fd basic write, creating file', function (t) {
|
180 | const drive = create()
|
181 | const content = Buffer.alloc(10000).fill('0123456789abcdefghijklmnopqrstuvwxyz')
|
182 | drive.open('hello', 'w+', function (err, fd) {
|
183 | t.error(err, 'no error')
|
184 | drive.write(fd, content, 0, content.length, 0, function (err, bytesWritten) {
|
185 | t.error(err, 'no error')
|
186 | t.same(bytesWritten, content.length)
|
187 | drive.close(fd, err => {
|
188 | t.error(err, 'no error')
|
189 | drive.readFile('hello', function (err, readContent) {
|
190 | t.error(err, 'no error')
|
191 | t.true(readContent.equals(content))
|
192 | t.end()
|
193 | })
|
194 | })
|
195 | })
|
196 | })
|
197 | })
|
198 |
|
199 | tape('fd basic write, appending file', function (t) {
|
200 | const drive = create()
|
201 | const content = Buffer.alloc(10000).fill('0123456789abcdefghijklmnopqrstuvwxyz')
|
202 | let first = content.slice(0, 2000)
|
203 | let second = content.slice(2000)
|
204 |
|
205 | drive.writeFile('hello', first, err => {
|
206 | t.error(err, 'no error')
|
207 | writeSecond()
|
208 | })
|
209 |
|
210 | function writeSecond () {
|
211 | drive.open('hello', 'a', function (err, fd) {
|
212 | t.error(err, 'no error')
|
213 | drive.write(fd, second, 0, second.length, first.length, function (err, bytesWritten) {
|
214 | t.error(err, 'no error')
|
215 | t.same(bytesWritten, second.length)
|
216 | drive.close(fd, err => {
|
217 | t.error(err, 'no error')
|
218 | drive.readFile('hello', function (err, readContent) {
|
219 | t.error(err, 'no error')
|
220 | t.true(readContent.equals(content))
|
221 | t.end()
|
222 | })
|
223 | })
|
224 | })
|
225 | })
|
226 | }
|
227 | })
|
228 |
|
229 | tape('fd basic write, overwrite file', function (t) {
|
230 | const drive = create()
|
231 | const content = Buffer.alloc(10000).fill('0123456789abcdefghijklmnopqrstuvwxyz')
|
232 | let first = content.slice(0, 2000)
|
233 | let second = content.slice(2000)
|
234 |
|
235 | drive.writeFile('hello', first, err => {
|
236 | t.error(err, 'no error')
|
237 | writeSecond()
|
238 | })
|
239 |
|
240 | function writeSecond () {
|
241 | drive.open('hello', 'w', function (err, fd) {
|
242 | t.error(err, 'no error')
|
243 | drive.write(fd, second, 0, second.length, 0, function (err, bytesWritten) {
|
244 | t.error(err, 'no error')
|
245 | t.same(bytesWritten, second.length)
|
246 | drive.close(fd, err => {
|
247 | t.error(err, 'no error')
|
248 | drive.readFile('hello', function (err, readContent) {
|
249 | t.error(err, 'no error')
|
250 | t.true(readContent.equals(second))
|
251 | t.end()
|
252 | })
|
253 | })
|
254 | })
|
255 | })
|
256 | }
|
257 | })
|
258 |
|
259 | tape('fd stateful write', function (t) {
|
260 | const drive = create()
|
261 | const content = Buffer.alloc(10000).fill('0123456789abcdefghijklmnopqrstuvwxyz')
|
262 | let first = content.slice(0, 2000)
|
263 | let second = content.slice(2000)
|
264 |
|
265 | drive.open('hello', 'w', function (err, fd) {
|
266 | t.error(err, 'no error')
|
267 | drive.write(fd, first, 0, first.length, 0, function (err) {
|
268 | t.error(err, 'no error')
|
269 | drive.write(fd, second, 0, second.length, first.length, function (err) {
|
270 | t.error(err, 'no error')
|
271 | drive.close(fd, err => {
|
272 | t.error(err, 'no error')
|
273 | drive.readFile('hello', function (err, readContent) {
|
274 | t.error(err, 'no error')
|
275 | t.true(readContent.equals(content))
|
276 | t.end()
|
277 | })
|
278 | })
|
279 | })
|
280 | })
|
281 | })
|
282 | })
|
283 |
|
284 | tape('huge stateful write + stateless read', function (t) {
|
285 | const NUM_SLICES = 1000
|
286 | const SLICE_SIZE = 4096
|
287 | const READ_SIZE = Math.floor(4096 * 2.75)
|
288 |
|
289 | const drive = create()
|
290 |
|
291 | const content = Buffer.alloc(SLICE_SIZE * NUM_SLICES).fill('0123456789abcdefghijklmnopqrstuvwxyz')
|
292 | let slices = new Array(NUM_SLICES).fill(0).map((_, i) => content.slice(SLICE_SIZE * i, SLICE_SIZE * (i + 1)))
|
293 |
|
294 | drive.open('hello', 'w+', function (err, fd) {
|
295 | t.error(err, 'no error')
|
296 | writeSlices(drive, fd, err => {
|
297 | t.error(err, 'no errors during write')
|
298 | drive.open('hello', 'r', function (err, fd) {
|
299 | t.error(err, 'no error')
|
300 | compareSlices(drive, fd)
|
301 | })
|
302 | })
|
303 | })
|
304 |
|
305 | function compareSlices (drive, fd) {
|
306 | let read = 0
|
307 | readNext()
|
308 |
|
309 | function readNext () {
|
310 | const buf = Buffer.alloc(READ_SIZE)
|
311 | const pos = read * READ_SIZE
|
312 | drive.read(fd, buf, 0, READ_SIZE, pos, (err, bytesRead) => {
|
313 | if (err) return t.fail(err)
|
314 | if (!buf.slice(0, bytesRead).equals(content.slice(pos, pos + READ_SIZE))) {
|
315 | return t.fail(`Slices do not match at position: ${read}`)
|
316 | }
|
317 | if (++read * READ_SIZE >= NUM_SLICES * SLICE_SIZE) {
|
318 | return t.end()
|
319 | }
|
320 | return readNext(drive, fd)
|
321 | })
|
322 | }
|
323 | }
|
324 |
|
325 | function writeSlices (drive, fd, cb) {
|
326 | let written = 0
|
327 | writeNext()
|
328 |
|
329 | function writeNext () {
|
330 | const buf = slices[written]
|
331 | drive.write(fd, buf, 0, SLICE_SIZE, err => {
|
332 | if (err) return cb(err)
|
333 | if (++written === NUM_SLICES) return drive.close(fd, cb)
|
334 | return writeNext()
|
335 | })
|
336 | }
|
337 | }
|
338 | })
|
339 |
|
340 | tape('fd random-access write fails', function (t) {
|
341 | const drive = create()
|
342 | const content = Buffer.alloc(10000).fill('0123456789abcdefghijklmnopqrstuvwxyz')
|
343 | let first = content.slice(0, 2000)
|
344 | let second = content.slice(2000)
|
345 |
|
346 | drive.open('hello', 'w', function (err, fd) {
|
347 | t.error(err, 'no error')
|
348 | drive.write(fd, first, 0, first.length, 0, function (err) {
|
349 | t.error(err, 'no error')
|
350 | drive.write(fd, second, 0, second.length, first.length - 500, function (err) {
|
351 | t.true(err)
|
352 | t.same(err.errno, 9)
|
353 | t.end()
|
354 | })
|
355 | })
|
356 | })
|
357 | })
|
358 |
|
359 | tape('fd parallel reads', function (t) {
|
360 | t.plan(3 * 2 + 1)
|
361 |
|
362 | const drive = create()
|
363 | const ws = drive.createWriteStream('test')
|
364 |
|
365 | ws.write('foo')
|
366 | setImmediate(function () {
|
367 | ws.write('bar')
|
368 | setImmediate(function () {
|
369 | ws.write('baz')
|
370 | setImmediate(function () {
|
371 | ws.write('quux')
|
372 | setImmediate(function () {
|
373 | ws.write('www')
|
374 | ws.end(function () {
|
375 | drive.open('test', 'r', function (err, fd) {
|
376 | t.error(err, 'no error')
|
377 |
|
378 | drive.read(fd, Buffer.alloc(13), 0, 13, 0, function (err, read, buf) {
|
379 | t.error(err, 'no error')
|
380 | t.same(read, 13)
|
381 | t.same(buf, Buffer.from('foobarbazquux'))
|
382 | })
|
383 | drive.read(fd, Buffer.alloc(7), 0, 7, 9, function (err, read, buf) {
|
384 | t.error(err, 'no error')
|
385 | t.same(read, 7)
|
386 | t.same(buf, Buffer.from('quuxwww'))
|
387 | })
|
388 | })
|
389 | })
|
390 | })
|
391 | })
|
392 | })
|
393 | })
|
394 | })
|
395 |
|
396 | tape('fd close cancels pending reads', function (t) {
|
397 | const r = new Replicator(t)
|
398 |
|
399 | var drive = create()
|
400 | var clone = null
|
401 | var stream = null
|
402 | var totalRead = null
|
403 |
|
404 | drive.on('ready', function () {
|
405 | clone = create(drive.key)
|
406 | drive.writeFile('/hello.txt', 'hello', function (err) {
|
407 | const [s1, s2] = r.replicate(drive, clone)
|
408 | stream = s1
|
409 | s1.on('error', () => {})
|
410 | s2.on('error', () => {})
|
411 | return onwrite()
|
412 | })
|
413 | })
|
414 |
|
415 | function onwrite () {
|
416 | clone.open('/hello.txt', 'r', (err, descriptor) => {
|
417 | stream.destroy()
|
418 | stream.on('close', () => {
|
419 |
|
420 | var totalRead = null
|
421 | clone.read(descriptor, Buffer.allocUnsafe(64), 1024, 0, (err, total, buf) => {
|
422 | t.true(err, 'read errored')
|
423 | totalRead = total
|
424 | t.end()
|
425 | })
|
426 | setImmediate(() => {
|
427 |
|
428 | clone.close(descriptor, err => {
|
429 | t.error(err, 'no error')
|
430 | t.false(totalRead)
|
431 | })
|
432 | })
|
433 | })
|
434 | })
|
435 | }
|
436 | })
|
437 |
|
438 | tape('opening a writable fd on a read-only drive errors', function (t) {
|
439 | const r = new Replicator(t)
|
440 |
|
441 | var drive = create()
|
442 | var clone = null
|
443 | var stream = null
|
444 | var totalRead = null
|
445 |
|
446 | drive.on('ready', function () {
|
447 | clone = create(drive.key)
|
448 | drive.writeFile('hello', 'world', () => {
|
449 | const [s1, s2] = r.replicate(drive, clone)
|
450 | stream = s1
|
451 | s1.on('error', () => {})
|
452 | s2.on('error', () => {})
|
453 | clone.open('hello', 'w+', (err, fd) => {
|
454 | t.true(err, 'writable fd open failed correctly')
|
455 | t.end()
|
456 | })
|
457 | })
|
458 | })
|
459 | })
|