-
-
Notifications
You must be signed in to change notification settings - Fork 36.5k
Split oversized test file test/parallel/test-fs-promises-file-handle-writer.js (1117 lines ->170 lines ) #65384
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1241ee1
93c27b2
6daa4df
a356998
9a5ab14
4eccc99
50fb9c0
10c0ac7
31e4598
2aaaf75
d583701
41ad57c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| const { | ||
|
Check failure on line 1 in test/parallel/test-fs-promises-file-handle-pipeto.js
|
||
| pipeTo | ||
| } = require('stream/iter'); | ||
|
Check failure on line 3 in test/parallel/test-fs-promises-file-handle-pipeto.js
|
||
| const { open } = fs.promises; | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
| const assert = require('assert'); | ||
|
|
||
| const tmpdir = require('../common/tmpdir'); | ||
|
|
||
| tmpdir.refresh(); | ||
|
|
||
| const tmpDir = tmpdir.path; | ||
|
|
||
|
|
||
| // ============================================================================= | ||
| // pipeTo() with transforms - uppercase through writer | ||
| // ============================================================================= | ||
|
|
||
| async function testPipeToWithTransform() { | ||
| const srcPath = path.join(tmpDir, 'writer-transform-src.txt'); | ||
| const dstPath = path.join(tmpDir, 'writer-transform-dst.txt'); | ||
| const data = 'hello world from transforms test\n'.repeat(200); | ||
| fs.writeFileSync(srcPath, data); | ||
|
|
||
| function uppercase(chunks) { | ||
| if (chunks === null) return null; | ||
| const out = new Array(chunks.length); | ||
| for (let i = 0; i < chunks.length; i++) { | ||
| const src = chunks[i]; | ||
| const buf = Buffer.allocUnsafe(src.length); | ||
| for (let j = 0; j < src.length; j++) { | ||
| const b = src[j]; | ||
| buf[j] = (b >= 0x61 && b <= 0x7a) ? b - 0x20 : b; | ||
| } | ||
| out[i] = buf; | ||
| } | ||
| return out; | ||
| } | ||
|
|
||
| const rfh = await open(srcPath, 'r'); | ||
| const wfh = await open(dstPath, 'w'); | ||
| const w = wfh.writer(); | ||
|
|
||
| await pipeTo(rfh.pull(), uppercase, w); | ||
|
|
||
| await rfh.close(); | ||
| await wfh.close(); | ||
|
|
||
| assert.strictEqual(fs.readFileSync(dstPath, 'utf8'), data.toUpperCase()); | ||
| } | ||
|
|
||
|
|
||
| // ============================================================================= | ||
| // pipeTo() integration - pipe source through writer | ||
| // ============================================================================= | ||
|
|
||
| async function testPipeToIntegration() { | ||
| const srcPath = path.join(tmpDir, 'writer-pipeto-src.txt'); | ||
| const dstPath = path.join(tmpDir, 'writer-pipeto-dst.txt'); | ||
| const data = 'The quick brown fox jumps over the lazy dog.\n'.repeat(500); | ||
| fs.writeFileSync(srcPath, data); | ||
|
|
||
| const rfh = await open(srcPath, 'r'); | ||
| const wfh = await open(dstPath, 'w'); | ||
| const w = wfh.writer(); | ||
|
|
||
| const totalBytes = await pipeTo(rfh.pull(), w); | ||
|
|
||
| await rfh.close(); | ||
| await wfh.close(); | ||
|
|
||
| assert.strictEqual(totalBytes, Buffer.byteLength(data)); | ||
| assert.strictEqual(fs.readFileSync(dstPath, 'utf8'), data); | ||
| } | ||
|
|
||
|
|
||
| Promise.all([ | ||
| testPipeToIntegration(), | ||
| testPipeToWithTransform(), | ||
| ]).then(common.mustCall()); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| 'use strict'; | ||
|
|
||
| const common = require('../common'); | ||
| const assert = require('assert'); | ||
| const fs = require('fs'); | ||
| const { open } = fs.promises; | ||
| const path = require('path'); | ||
| const tmpdir = require('../common/tmpdir'); | ||
|
|
||
| tmpdir.refresh(); | ||
|
|
||
| const tmpDir = tmpdir.path; | ||
|
|
||
| // ============================================================================= | ||
| // Pre-aborted signal rejects write/writev/end | ||
| // ============================================================================= | ||
|
|
||
| async function testWriteWithAbortedSignalRejects() { | ||
| const filePath = path.join(tmpDir, 'writer-signal-write.txt'); | ||
| const fh = await open(filePath, 'w'); | ||
| const w = fh.writer(); | ||
|
Check failure on line 21 in test/parallel/test-fs-promises-file-handle-writer-abort.js
|
||
|
|
||
| await assert.rejects( | ||
| w.write(Buffer.from('data'), { signal: AbortSignal.abort() }), | ||
| { name: 'AbortError' }, | ||
| ); | ||
|
|
||
| // Writer should still be usable after a signal rejection | ||
| await w.write(Buffer.from('ok')); | ||
| await w.end(); | ||
| await fh.close(); | ||
|
|
||
| assert.strictEqual(fs.readFileSync(filePath, 'utf8'), 'ok'); | ||
| } | ||
|
|
||
| async function testWritevWithAbortedSignalRejects() { | ||
| const filePath = path.join(tmpDir, 'writer-signal-writev.txt'); | ||
| const fh = await open(filePath, 'w'); | ||
| const w = fh.writer(); | ||
|
|
||
| await assert.rejects( | ||
| w.writev([Buffer.from('a'), Buffer.from('b')], { signal: AbortSignal.abort() }), | ||
| { name: 'AbortError' }, | ||
| ); | ||
|
|
||
| await w.writev([Buffer.from('ok')]); | ||
| await w.end(); | ||
| await fh.close(); | ||
|
|
||
| assert.strictEqual(fs.readFileSync(filePath, 'utf8'), 'ok'); | ||
| } | ||
|
|
||
| async function testEndWithAbortedSignalRejects() { | ||
| const filePath = path.join(tmpDir, 'writer-signal-end.txt'); | ||
| const fh = await open(filePath, 'w'); | ||
| const w = fh.writer(); | ||
|
|
||
| await w.write(Buffer.from('data')); | ||
|
|
||
| await assert.rejects( | ||
| w.end({ signal: AbortSignal.abort() }), | ||
| { name: 'AbortError' }, | ||
| ); | ||
|
|
||
| // end() was rejected so writer is still open - end it cleanly | ||
| const totalBytes = await w.end(); | ||
| await fh.close(); | ||
|
|
||
| assert.strictEqual(totalBytes, 4); | ||
| assert.strictEqual(fs.readFileSync(filePath, 'utf8'), 'data'); | ||
| } | ||
|
|
||
| Promise.all([ | ||
| testWriteWithAbortedSignalRejects(), | ||
| testWritevWithAbortedSignalRejects(), | ||
| testEndWithAbortedSignalRejects(), | ||
| ]).then(common.mustCall()); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
|
|
||
| 'use strict'; | ||
|
|
||
| const common = require('../common'); | ||
| const assert = require('assert'); | ||
| const fs = require('fs'); | ||
| const { open } = fs.promises; | ||
| const path = require('path'); | ||
| const tmpdir = require('../common/tmpdir'); | ||
|
|
||
| tmpdir.refresh(); | ||
|
|
||
| const tmpDir = tmpdir.path; | ||
|
|
||
| // ============================================================================= | ||
| // Symbol.asyncDispose - await using | ||
| // ============================================================================= | ||
|
|
||
| async function testAsyncDispose() { | ||
| const filePath = path.join(tmpDir, 'writer-async-dispose.txt'); | ||
| { | ||
| await using fh = await open(filePath, 'w'); | ||
| await using w = fh.writer({ autoClose: true }); | ||
| await w.write(Buffer.from('async dispose')); | ||
| } | ||
| // Both writer and file handle should be cleaned up | ||
| assert.strictEqual(fs.readFileSync(filePath, 'utf8'), 'async dispose'); | ||
|
|
||
| // Verify the handle is actually closed by trying to open a new one | ||
| // (if the old one were still open with a write lock on some OSes, | ||
| // this could fail - but it should succeed). | ||
| const fh2 = await open(filePath, 'r'); | ||
| await fh2.close(); | ||
| } | ||
|
|
||
| // ============================================================================= | ||
| // Symbol.asyncDispose - cleanup on error (await using unwinds) | ||
| // ============================================================================= | ||
|
|
||
| async function testAsyncDisposeOnError() { | ||
| const filePath = path.join(tmpDir, 'writer-dispose-error.txt'); | ||
| const fh = await open(filePath, 'w'); | ||
|
|
||
| try { | ||
| await using w = fh.writer(); | ||
| await w.write(Buffer.from('before error')); | ||
| throw new Error('intentional'); | ||
| } catch (e) { | ||
| assert.strictEqual(e.message, 'intentional'); | ||
|
Check failure on line 49 in test/parallel/test-fs-promises-file-handle-writer-dispose.js
|
||
| } | ||
|
|
||
| // If asyncDispose ran, the handle should be unlocked and reusable | ||
| const w2 = fh.writer(); | ||
| await w2.write(Buffer.from('after error')); | ||
| await w2.end(); | ||
| await fh.close(); | ||
|
|
||
| const content = fs.readFileSync(filePath, 'utf8'); | ||
| assert.ok(content.includes('after error'), | ||
| `Expected 'after error' in ${JSON.stringify(content)}`); | ||
| } | ||
|
|
||
| // ============================================================================= | ||
| // Symbol.dispose calls fail() | ||
| // ============================================================================= | ||
|
|
||
| async function testSyncDispose() { | ||
| const filePath = path.join(tmpDir, 'writer-sync-dispose.txt'); | ||
| const fh = await open(filePath, 'w'); | ||
|
|
||
| { | ||
| using w = fh.writer(); | ||
| await w.write(Buffer.from('before dispose')); | ||
| } | ||
| // Symbol.dispose calls fail(), which unlocks the handle. | ||
| // The handle should be reusable. | ||
| const w2 = fh.writer(); | ||
| await w2.write(Buffer.from('after dispose')); | ||
| await w2.end(); | ||
| await fh.close(); | ||
|
|
||
| const content = fs.readFileSync(filePath, 'utf8'); | ||
| assert.ok(content.includes('after dispose'), | ||
| `Expected 'after dispose' in ${JSON.stringify(content)}`); | ||
| } | ||
|
|
||
| // ============================================================================= | ||
| // Symbol.dispose on error unwind | ||
| // ============================================================================= | ||
|
|
||
| async function testSyncDisposeOnError() { | ||
| const filePath = path.join(tmpDir, 'writer-sync-dispose-error.txt'); | ||
| const fh = await open(filePath, 'w'); | ||
|
|
||
| try { | ||
| using w = fh.writer(); | ||
| await w.write(Buffer.from('data')); | ||
| throw new Error('intentional'); | ||
| } catch (e) { | ||
| assert.strictEqual(e.message, 'intentional'); | ||
| } | ||
|
|
||
| // Handle should be unlocked and reusable after sync dispose | ||
| const w2 = fh.writer(); | ||
| await w2.write(Buffer.from('recovered')); | ||
| await w2.end(); | ||
| await fh.close(); | ||
|
|
||
| const content = fs.readFileSync(filePath, 'utf8'); | ||
| assert.ok(content.includes('recovered'), | ||
| `Expected 'recovered' in ${JSON.stringify(content)}`); | ||
| } | ||
|
|
||
| // ============================================================================= | ||
| // asyncDispose waits for pending end() when closing | ||
| // ============================================================================= | ||
|
|
||
| async function testAsyncDisposeWhileClosing() { | ||
| const filePath = path.join(tmpDir, 'writer-dispose-closing.txt'); | ||
| const fh = await open(filePath, 'w'); | ||
| const w = fh.writer({ autoClose: true }); | ||
|
|
||
| await w.write(Buffer.from('closing test')); | ||
|
|
||
| // Start end() but don't await - writer is now "closing" | ||
| const endPromise = w.end(); | ||
|
|
||
| // asyncDispose should wait for the pending end, not call fail() | ||
| await w[Symbol.asyncDispose](); | ||
| await endPromise; | ||
|
|
||
| assert.strictEqual(fs.readFileSync(filePath, 'utf8'), 'closing test'); | ||
| } | ||
|
|
||
|
|
||
| // ============================================================================= | ||
| // asyncDispose calls fail() on open writer (not graceful cleanup) | ||
| // ============================================================================= | ||
|
|
||
| async function testAsyncDisposeCallsFail() { | ||
| const filePath = path.join(tmpDir, 'writer-dispose-fails.txt'); | ||
| const fh = await open(filePath, 'w'); | ||
| const w = fh.writer(); | ||
|
|
||
| await w.write(Buffer.from('some data')); | ||
|
|
||
| // Dispose without end() - should call fail(), not graceful cleanup | ||
| await w[Symbol.asyncDispose](); | ||
|
|
||
| // Writer should be in errored state - write should reject | ||
| await assert.rejects( | ||
| w.write(Buffer.from('more')), | ||
| (err) => err instanceof Error, | ||
| ); | ||
|
|
||
| // Handle should be unlocked and reusable | ||
| const w2 = fh.writer(); | ||
| await w2.end(); | ||
| await fh.close(); | ||
| } | ||
|
|
||
| Promise.all([ | ||
| testAsyncDispose(), | ||
| testAsyncDisposeOnError(), | ||
| testSyncDispose(), | ||
| testSyncDisposeOnError(), | ||
| testAsyncDisposeWhileClosing(), | ||
| testAsyncDisposeCallsFail(), | ||
| ]).then(common.mustCall()); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These need the
'use strict';at the top and therequire('../common')There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should be caught by running
make lintThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!!!! I'll do that!!