1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23const common = require('../common'); 24const tmpdir = require('../common/tmpdir'); 25 26const child_process = require('child_process'); 27const assert = require('assert'); 28const fs = require('fs'); 29const fixtures = require('../common/fixtures'); 30 31const fn = fixtures.path('elipses.txt'); 32const rangeFile = fixtures.path('x.txt'); 33 34function test1(options) { 35 let paused = false; 36 let bytesRead = 0; 37 38 const file = fs.createReadStream(fn, options); 39 const fileSize = fs.statSync(fn).size; 40 41 assert.strictEqual(file.bytesRead, 0); 42 43 file.on('open', common.mustCall(function(fd) { 44 file.length = 0; 45 assert.strictEqual(typeof fd, 'number'); 46 assert.strictEqual(file.bytesRead, 0); 47 assert.ok(file.readable); 48 49 // GH-535 50 file.pause(); 51 file.resume(); 52 file.pause(); 53 file.resume(); 54 })); 55 56 file.on('data', function(data) { 57 assert.ok(data instanceof Buffer); 58 assert.ok(data.byteOffset % 8 === 0); 59 assert.ok(!paused); 60 file.length += data.length; 61 62 bytesRead += data.length; 63 assert.strictEqual(file.bytesRead, bytesRead); 64 65 paused = true; 66 file.pause(); 67 68 setTimeout(function() { 69 paused = false; 70 file.resume(); 71 }, 10); 72 }); 73 74 75 file.on('end', common.mustCall(function(chunk) { 76 assert.strictEqual(bytesRead, fileSize); 77 assert.strictEqual(file.bytesRead, fileSize); 78 })); 79 80 81 file.on('close', common.mustCall(function() { 82 assert.strictEqual(bytesRead, fileSize); 83 assert.strictEqual(file.bytesRead, fileSize); 84 })); 85 86 process.on('exit', function() { 87 assert.strictEqual(file.length, 30000); 88 }); 89} 90 91test1({}); 92test1({ 93 fs: { 94 open: common.mustCall(fs.open), 95 read: common.mustCallAtLeast(fs.read, 1), 96 close: common.mustCall(fs.close), 97 } 98}); 99 100{ 101 const file = fs.createReadStream(fn, { encoding: 'utf8' }); 102 file.length = 0; 103 file.on('data', function(data) { 104 assert.strictEqual(typeof data, 'string'); 105 file.length += data.length; 106 107 for (let i = 0; i < data.length; i++) { 108 // http://www.fileformat.info/info/unicode/char/2026/index.htm 109 assert.strictEqual(data[i], '\u2026'); 110 } 111 }); 112 113 file.on('close', common.mustCall()); 114 115 process.on('exit', function() { 116 assert.strictEqual(file.length, 10000); 117 }); 118} 119 120{ 121 const file = 122 fs.createReadStream(rangeFile, { bufferSize: 1, start: 1, end: 2 }); 123 let contentRead = ''; 124 file.on('data', function(data) { 125 contentRead += data.toString('utf-8'); 126 }); 127 file.on('end', common.mustCall(function(data) { 128 assert.strictEqual(contentRead, 'yz'); 129 })); 130} 131 132{ 133 const file = fs.createReadStream(rangeFile, { bufferSize: 1, start: 1 }); 134 file.data = ''; 135 file.on('data', function(data) { 136 file.data += data.toString('utf-8'); 137 }); 138 file.on('end', common.mustCall(function() { 139 assert.strictEqual(file.data, 'yz\n'); 140 })); 141} 142 143{ 144 // Ref: https://github.com/nodejs/node-v0.x-archive/issues/2320 145 const file = fs.createReadStream(rangeFile, { bufferSize: 1.23, start: 1 }); 146 file.data = ''; 147 file.on('data', function(data) { 148 file.data += data.toString('utf-8'); 149 }); 150 file.on('end', common.mustCall(function() { 151 assert.strictEqual(file.data, 'yz\n'); 152 })); 153} 154 155assert.throws( 156 () => { 157 fs.createReadStream(rangeFile, { start: 10, end: 2 }); 158 }, 159 { 160 code: 'ERR_OUT_OF_RANGE', 161 message: 'The value of "start" is out of range. It must be <= "end"' + 162 ' (here: 2). Received 10', 163 name: 'RangeError' 164 }); 165 166{ 167 const stream = fs.createReadStream(rangeFile, { start: 0, end: 0 }); 168 stream.data = ''; 169 170 stream.on('data', function(chunk) { 171 stream.data += chunk; 172 }); 173 174 stream.on('end', common.mustCall(function() { 175 assert.strictEqual(stream.data, 'x'); 176 })); 177} 178 179{ 180 // Verify that end works when start is not specified. 181 const stream = new fs.createReadStream(rangeFile, { end: 1 }); 182 stream.data = ''; 183 184 stream.on('data', function(chunk) { 185 stream.data += chunk; 186 }); 187 188 stream.on('end', common.mustCall(function() { 189 assert.strictEqual(stream.data, 'xy'); 190 })); 191} 192 193if (!common.isWindows) { 194 // Verify that end works when start is not specified, and we do not try to 195 // use positioned reads. This makes sure that this keeps working for 196 // non-seekable file descriptors. 197 tmpdir.refresh(); 198 const filename = `${tmpdir.path}/foo.pipe`; 199 const mkfifoResult = child_process.spawnSync('mkfifo', [filename]); 200 if (!mkfifoResult.error) { 201 child_process.exec(`echo "xyz foobar" > '${filename}'`); 202 const stream = new fs.createReadStream(filename, { end: 1 }); 203 stream.data = ''; 204 205 stream.on('data', function(chunk) { 206 stream.data += chunk; 207 }); 208 209 stream.on('end', common.mustCall(function() { 210 assert.strictEqual(stream.data, 'xy'); 211 fs.unlinkSync(filename); 212 })); 213 } else { 214 common.printSkipMessage('mkfifo not available'); 215 } 216} 217 218{ 219 // Pause and then resume immediately. 220 const pauseRes = fs.createReadStream(rangeFile); 221 pauseRes.pause(); 222 pauseRes.resume(); 223} 224 225{ 226 let file = fs.createReadStream(rangeFile, { autoClose: false }); 227 let data = ''; 228 file.on('data', function(chunk) { data += chunk; }); 229 file.on('end', common.mustCall(function() { 230 assert.strictEqual(data, 'xyz\n'); 231 process.nextTick(function() { 232 assert(!file.closed); 233 assert(!file.destroyed); 234 fileNext(); 235 }); 236 })); 237 238 function fileNext() { 239 // This will tell us if the fd is usable again or not. 240 file = fs.createReadStream(null, { fd: file.fd, start: 0 }); 241 file.data = ''; 242 file.on('data', function(data) { 243 file.data += data; 244 }); 245 file.on('end', common.mustCall(function(err) { 246 assert.strictEqual(file.data, 'xyz\n'); 247 })); 248 process.on('exit', function() { 249 assert(file.closed); 250 assert(file.destroyed); 251 }); 252 } 253} 254 255{ 256 // Just to make sure autoClose won't close the stream because of error. 257 const file = fs.createReadStream(null, { fd: 13337, autoClose: false }); 258 file.on('data', common.mustNotCall()); 259 file.on('error', common.mustCall()); 260 process.on('exit', function() { 261 assert(!file.closed); 262 assert(!file.destroyed); 263 assert(file.fd); 264 }); 265} 266 267{ 268 // Make sure stream is destroyed when file does not exist. 269 const file = fs.createReadStream('/path/to/file/that/does/not/exist'); 270 file.on('data', common.mustNotCall()); 271 file.on('error', common.mustCall()); 272 273 process.on('exit', function() { 274 assert(!file.closed); 275 assert(file.destroyed); 276 }); 277} 278