| 
 | 1 | +import * as common from '../common/index.mjs';  | 
 | 2 | +import * as fixtures from '../common/fixtures.mjs';  | 
 | 3 | +import fs from 'fs';  | 
 | 4 | +import assert from 'assert';  | 
 | 5 | + | 
 | 6 | +// This test ensures that "position" argument is correctly validated  | 
 | 7 | + | 
 | 8 | +const filepath = fixtures.path('x.txt');  | 
 | 9 | + | 
 | 10 | +const buffer = Buffer.from('xyz\n');  | 
 | 11 | +const offset = 0;  | 
 | 12 | +const length = buffer.byteLength;  | 
 | 13 | + | 
 | 14 | +// allowedErrors is an array of acceptable internal errors  | 
 | 15 | +// For example, on some platforms read syscall might return -EFBIG  | 
 | 16 | +async function testValid(position, allowedErrors = []) {  | 
 | 17 | +  let fdSync;  | 
 | 18 | +  try {  | 
 | 19 | +    fdSync = fs.openSync(filepath, 'r');  | 
 | 20 | +    fs.readSync(fdSync, buffer, offset, length, position);  | 
 | 21 | +    fs.readSync(fdSync, buffer, { offset, length, position });  | 
 | 22 | +  } catch (err) {  | 
 | 23 | +    if (!allowedErrors.includes(err.code)) {  | 
 | 24 | +      assert.fail(err);  | 
 | 25 | +    }  | 
 | 26 | +  } finally {  | 
 | 27 | +    if (fdSync) fs.closeSync(fdSync);  | 
 | 28 | +  }  | 
 | 29 | + | 
 | 30 | +  fs.open(filepath, 'r', common.mustSucceed((fd) => {  | 
 | 31 | +    try {  | 
 | 32 | +      if (allowedErrors.length) {  | 
 | 33 | +        fs.read(fd, buffer, offset, length, position, common.mustCall((err, ...results) => {  | 
 | 34 | +          if (err && !allowedErrors.includes(err.code)) {  | 
 | 35 | +            assert.fail(err);  | 
 | 36 | +          }  | 
 | 37 | +        }));  | 
 | 38 | +        fs.read(fd, { buffer, offset, length, position }, common.mustCall((err, ...results) => {  | 
 | 39 | +          if (err && !allowedErrors.includes(err.code)) {  | 
 | 40 | +            assert.fail(err);  | 
 | 41 | +          }  | 
 | 42 | +        }));  | 
 | 43 | +      } else {  | 
 | 44 | +        fs.read(fd, buffer, offset, length, position, common.mustSucceed());  | 
 | 45 | +        fs.read(fd, { buffer, offset, length, position }, common.mustSucceed());  | 
 | 46 | +      }  | 
 | 47 | +    } finally {  | 
 | 48 | +      fs.close(fd, common.mustSucceed());  | 
 | 49 | +    }  | 
 | 50 | +  }));  | 
 | 51 | +}  | 
 | 52 | + | 
 | 53 | +async function testInvalid(code, position, internalCatch = false) {  | 
 | 54 | +  let fdSync;  | 
 | 55 | +  try {  | 
 | 56 | +    fdSync = fs.openSync(filepath, 'r');  | 
 | 57 | +    assert.throws(  | 
 | 58 | +      () => fs.readSync(fdSync, buffer, offset, length, position),  | 
 | 59 | +      { code }  | 
 | 60 | +    );  | 
 | 61 | +    assert.throws(  | 
 | 62 | +      () => fs.readSync(fdSync, buffer, { offset, length, position }),  | 
 | 63 | +      { code }  | 
 | 64 | +    );  | 
 | 65 | +  } finally {  | 
 | 66 | +    if (fdSync) fs.closeSync(fdSync);  | 
 | 67 | +  }  | 
 | 68 | + | 
 | 69 | +  // Set this flag for catching errors via first argument of callback function  | 
 | 70 | +  if (internalCatch) {  | 
 | 71 | +    fs.open(filepath, 'r', common.mustSucceed((fd) => {  | 
 | 72 | +      try {  | 
 | 73 | +        fs.read(fd, buffer, offset, length, position, (err, ...results) => {  | 
 | 74 | +          assert.strictEqual(err.code, code);  | 
 | 75 | +        });  | 
 | 76 | +        fs.read(fd, { buffer, offset, length, position }, (err, ...results) => {  | 
 | 77 | +          assert.strictEqual(err.code, code);  | 
 | 78 | +        });  | 
 | 79 | +      } finally {  | 
 | 80 | +        fs.close(fd, common.mustSucceed());  | 
 | 81 | +      }  | 
 | 82 | +    }));  | 
 | 83 | +  } else {  | 
 | 84 | +    fs.open(filepath, 'r', common.mustSucceed((fd) => {  | 
 | 85 | +      try {  | 
 | 86 | +        assert.throws(  | 
 | 87 | +          () => fs.read(fd, buffer, offset, length, position, common.mustNotCall()),  | 
 | 88 | +          { code }  | 
 | 89 | +        );  | 
 | 90 | +        assert.throws(  | 
 | 91 | +          () => fs.read(fd, { buffer, offset, length, position }, common.mustNotCall()),  | 
 | 92 | +          { code }  | 
 | 93 | +        );  | 
 | 94 | +      } finally {  | 
 | 95 | +        fs.close(fd, common.mustSucceed());  | 
 | 96 | +      }  | 
 | 97 | +    }));  | 
 | 98 | +  }  | 
 | 99 | +}  | 
 | 100 | + | 
 | 101 | +{  | 
 | 102 | +  await testValid(undefined);  | 
 | 103 | +  await testValid(null);  | 
 | 104 | +  await testValid(-1);  | 
 | 105 | +  await testValid(-1n);  | 
 | 106 | + | 
 | 107 | +  await testValid(0);  | 
 | 108 | +  await testValid(0n);  | 
 | 109 | +  await testValid(1);  | 
 | 110 | +  await testValid(1n);  | 
 | 111 | +  await testValid(9);  | 
 | 112 | +  await testValid(9n);  | 
 | 113 | +  await testValid(Number.MAX_SAFE_INTEGER, [ 'EFBIG' ]);  | 
 | 114 | + | 
 | 115 | +  await testValid(2n ** 63n - 1n - BigInt(length), [ 'EFBIG' ]);  | 
 | 116 | +  await testInvalid('ERR_OUT_OF_RANGE', 2n ** 63n);  | 
 | 117 | + | 
 | 118 | +  // TODO(LiviaMedeiros): test `2n ** 63n - BigInt(length)`  | 
 | 119 | + | 
 | 120 | +  await testInvalid('ERR_OUT_OF_RANGE', NaN);  | 
 | 121 | +  await testInvalid('ERR_OUT_OF_RANGE', -Infinity);  | 
 | 122 | +  await testInvalid('ERR_OUT_OF_RANGE', Infinity);  | 
 | 123 | +  await testInvalid('ERR_OUT_OF_RANGE', -0.999);  | 
 | 124 | +  await testInvalid('ERR_OUT_OF_RANGE', -(2n ** 64n));  | 
 | 125 | +  await testInvalid('ERR_OUT_OF_RANGE', Number.MAX_SAFE_INTEGER + 1);  | 
 | 126 | +  await testInvalid('ERR_OUT_OF_RANGE', Number.MAX_VALUE);  | 
 | 127 | + | 
 | 128 | +  for (const badTypeValue of [  | 
 | 129 | +    false, true, '1', Symbol(1), {}, [], () => {}, Promise.resolve(1),  | 
 | 130 | +  ]) {  | 
 | 131 | +    await testInvalid('ERR_INVALID_ARG_TYPE', badTypeValue);  | 
 | 132 | +  }  | 
 | 133 | +}  | 
0 commit comments