1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/base/file_stream.h"
6
7 #include <string>
8 #include <utility>
9
10 #include "base/files/file.h"
11 #include "base/files/file_util.h"
12 #include "base/functional/bind.h"
13 #include "base/functional/callback.h"
14 #include "base/memory/raw_ptr.h"
15 #include "base/path_service.h"
16 #include "base/run_loop.h"
17 #include "base/strings/string_util.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "base/task/current_thread.h"
20 #include "base/task/single_thread_task_runner.h"
21 #include "base/test/test_timeouts.h"
22 #include "base/threading/thread.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "build/build_config.h"
25 #include "net/base/io_buffer.h"
26 #include "net/base/net_errors.h"
27 #include "net/base/test_completion_callback.h"
28 #include "net/log/test_net_log.h"
29 #include "net/test/gtest_util.h"
30 #include "net/test/test_with_task_environment.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33 #include "testing/platform_test.h"
34
35 using net::test::IsError;
36 using net::test::IsOk;
37
38 #if BUILDFLAG(IS_ANDROID)
39 #include "base/test/test_file_util.h"
40 #endif
41
42 namespace net {
43
44 namespace {
45
46 constexpr char kTestData[] = "0123456789";
47 constexpr int kTestDataSize = std::size(kTestData) - 1;
48
49 // Creates an IOBufferWithSize that contains the kTestDataSize.
CreateTestDataBuffer()50 scoped_refptr<IOBufferWithSize> CreateTestDataBuffer() {
51 scoped_refptr<IOBufferWithSize> buf =
52 base::MakeRefCounted<IOBufferWithSize>(kTestDataSize);
53 memcpy(buf->data(), kTestData, kTestDataSize);
54 return buf;
55 }
56
57 } // namespace
58
59 class FileStreamTest : public PlatformTest, public WithTaskEnvironment {
60 public:
SetUp()61 void SetUp() override {
62 PlatformTest::SetUp();
63
64 base::CreateTemporaryFile(&temp_file_path_);
65 base::WriteFile(temp_file_path_, kTestData);
66 }
TearDown()67 void TearDown() override {
68 // FileStreamContexts must be asynchronously closed on the file task runner
69 // before they can be deleted. Pump the RunLoop to avoid leaks.
70 base::RunLoop().RunUntilIdle();
71 EXPECT_TRUE(base::DeleteFile(temp_file_path_));
72
73 PlatformTest::TearDown();
74 }
75
temp_file_path() const76 const base::FilePath temp_file_path() const { return temp_file_path_; }
77
78 private:
79 base::FilePath temp_file_path_;
80 };
81
82 namespace {
83
TEST_F(FileStreamTest,OpenExplicitClose)84 TEST_F(FileStreamTest, OpenExplicitClose) {
85 TestCompletionCallback callback;
86 FileStream stream(base::SingleThreadTaskRunner::GetCurrentDefault());
87 int flags = base::File::FLAG_OPEN |
88 base::File::FLAG_READ |
89 base::File::FLAG_ASYNC;
90 int rv = stream.Open(temp_file_path(), flags, callback.callback());
91 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
92 EXPECT_THAT(callback.WaitForResult(), IsOk());
93 EXPECT_TRUE(stream.IsOpen());
94 EXPECT_THAT(stream.Close(callback.callback()), IsError(ERR_IO_PENDING));
95 EXPECT_THAT(callback.WaitForResult(), IsOk());
96 EXPECT_FALSE(stream.IsOpen());
97 }
98
TEST_F(FileStreamTest,OpenExplicitCloseOrphaned)99 TEST_F(FileStreamTest, OpenExplicitCloseOrphaned) {
100 TestCompletionCallback callback;
101 auto stream = std::make_unique<FileStream>(
102 base::SingleThreadTaskRunner::GetCurrentDefault());
103 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
104 base::File::FLAG_ASYNC;
105 int rv = stream->Open(temp_file_path(), flags, callback.callback());
106 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
107 EXPECT_THAT(callback.WaitForResult(), IsOk());
108 EXPECT_TRUE(stream->IsOpen());
109 EXPECT_THAT(stream->Close(callback.callback()), IsError(ERR_IO_PENDING));
110 stream.reset();
111 // File isn't actually closed yet.
112 base::RunLoop runloop;
113 runloop.RunUntilIdle();
114 // The file should now be closed, though the callback has not been called.
115 }
116
117 // Test the use of FileStream with a file handle provided at construction.
TEST_F(FileStreamTest,UseFileHandle)118 TEST_F(FileStreamTest, UseFileHandle) {
119 int rv = 0;
120 TestCompletionCallback callback;
121 TestInt64CompletionCallback callback64;
122 // 1. Test reading with a file handle.
123 ASSERT_TRUE(base::WriteFile(temp_file_path(), kTestData));
124 int flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |
125 base::File::FLAG_ASYNC;
126 base::File file1(temp_file_path(), flags);
127
128 // Seek to the beginning of the file and read.
129 auto read_stream = std::make_unique<FileStream>(
130 std::move(file1), base::SingleThreadTaskRunner::GetCurrentDefault());
131 ASSERT_THAT(read_stream->Seek(0, callback64.callback()),
132 IsError(ERR_IO_PENDING));
133 ASSERT_EQ(0, callback64.WaitForResult());
134 // Read into buffer and compare.
135 scoped_refptr<IOBufferWithSize> read_buffer =
136 base::MakeRefCounted<IOBufferWithSize>(kTestDataSize);
137 rv = read_stream->Read(read_buffer.get(), kTestDataSize, callback.callback());
138 ASSERT_EQ(kTestDataSize, callback.GetResult(rv));
139 ASSERT_EQ(0, memcmp(kTestData, read_buffer->data(), kTestDataSize));
140 read_stream.reset();
141
142 // 2. Test writing with a file handle.
143 base::DeleteFile(temp_file_path());
144 flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE |
145 base::File::FLAG_ASYNC;
146 base::File file2(temp_file_path(), flags);
147
148 auto write_stream = std::make_unique<FileStream>(
149 std::move(file2), base::SingleThreadTaskRunner::GetCurrentDefault());
150 ASSERT_THAT(write_stream->Seek(0, callback64.callback()),
151 IsError(ERR_IO_PENDING));
152 ASSERT_EQ(0, callback64.WaitForResult());
153 scoped_refptr<IOBufferWithSize> write_buffer = CreateTestDataBuffer();
154 rv = write_stream->Write(write_buffer.get(), kTestDataSize,
155 callback.callback());
156 ASSERT_EQ(kTestDataSize, callback.GetResult(rv));
157 write_stream.reset();
158
159 // Read into buffer and compare to make sure the handle worked fine.
160 ASSERT_EQ(kTestDataSize,
161 base::ReadFile(temp_file_path(), read_buffer->data(),
162 kTestDataSize));
163 ASSERT_EQ(0, memcmp(kTestData, read_buffer->data(), kTestDataSize));
164 }
165
TEST_F(FileStreamTest,UseClosedStream)166 TEST_F(FileStreamTest, UseClosedStream) {
167 int rv = 0;
168 TestCompletionCallback callback;
169 TestInt64CompletionCallback callback64;
170
171 FileStream stream(base::SingleThreadTaskRunner::GetCurrentDefault());
172
173 EXPECT_FALSE(stream.IsOpen());
174
175 // Try seeking...
176 rv = stream.Seek(5, callback64.callback());
177 EXPECT_THAT(callback64.GetResult(rv), IsError(ERR_UNEXPECTED));
178
179 // Try reading...
180 scoped_refptr<IOBufferWithSize> buf =
181 base::MakeRefCounted<IOBufferWithSize>(10);
182 rv = stream.Read(buf.get(), buf->size(), callback.callback());
183 EXPECT_THAT(callback.GetResult(rv), IsError(ERR_UNEXPECTED));
184 }
185
TEST_F(FileStreamTest,Read)186 TEST_F(FileStreamTest, Read) {
187 int64_t file_size;
188 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
189
190 FileStream stream(base::SingleThreadTaskRunner::GetCurrentDefault());
191 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
192 base::File::FLAG_ASYNC;
193 TestCompletionCallback callback;
194 int rv = stream.Open(temp_file_path(), flags, callback.callback());
195 EXPECT_THAT(callback.GetResult(rv), IsOk());
196
197 int total_bytes_read = 0;
198
199 std::string data_read;
200 for (;;) {
201 scoped_refptr<IOBufferWithSize> buf =
202 base::MakeRefCounted<IOBufferWithSize>(4);
203 rv = stream.Read(buf.get(), buf->size(), callback.callback());
204 rv = callback.GetResult(rv);
205 EXPECT_LE(0, rv);
206 if (rv <= 0)
207 break;
208 total_bytes_read += rv;
209 data_read.append(buf->data(), rv);
210 }
211 EXPECT_EQ(file_size, total_bytes_read);
212 EXPECT_EQ(kTestData, data_read);
213 }
214
TEST_F(FileStreamTest,Read_EarlyDelete)215 TEST_F(FileStreamTest, Read_EarlyDelete) {
216 int64_t file_size;
217 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
218
219 auto stream = std::make_unique<FileStream>(
220 base::SingleThreadTaskRunner::GetCurrentDefault());
221 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
222 base::File::FLAG_ASYNC;
223 TestCompletionCallback callback;
224 int rv = stream->Open(temp_file_path(), flags, callback.callback());
225 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
226 EXPECT_THAT(callback.WaitForResult(), IsOk());
227
228 scoped_refptr<IOBufferWithSize> buf =
229 base::MakeRefCounted<IOBufferWithSize>(4);
230 rv = stream->Read(buf.get(), buf->size(), callback.callback());
231 stream.reset(); // Delete instead of closing it.
232 if (rv < 0) {
233 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
234 // The callback should not be called if the request is cancelled.
235 base::RunLoop().RunUntilIdle();
236 EXPECT_FALSE(callback.have_result());
237 } else {
238 EXPECT_EQ(std::string(kTestData, rv), std::string(buf->data(), rv));
239 }
240 }
241
TEST_F(FileStreamTest,Read_FromOffset)242 TEST_F(FileStreamTest, Read_FromOffset) {
243 int64_t file_size;
244 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
245
246 FileStream stream(base::SingleThreadTaskRunner::GetCurrentDefault());
247 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
248 base::File::FLAG_ASYNC;
249 TestCompletionCallback callback;
250 int rv = stream.Open(temp_file_path(), flags, callback.callback());
251 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
252 EXPECT_THAT(callback.WaitForResult(), IsOk());
253
254 TestInt64CompletionCallback callback64;
255 const int64_t kOffset = 3;
256 rv = stream.Seek(kOffset, callback64.callback());
257 ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
258 int64_t new_offset = callback64.WaitForResult();
259 EXPECT_EQ(kOffset, new_offset);
260
261 int total_bytes_read = 0;
262
263 std::string data_read;
264 for (;;) {
265 scoped_refptr<IOBufferWithSize> buf =
266 base::MakeRefCounted<IOBufferWithSize>(4);
267 rv = stream.Read(buf.get(), buf->size(), callback.callback());
268 if (rv == ERR_IO_PENDING)
269 rv = callback.WaitForResult();
270 EXPECT_LE(0, rv);
271 if (rv <= 0)
272 break;
273 total_bytes_read += rv;
274 data_read.append(buf->data(), rv);
275 }
276 EXPECT_EQ(file_size - kOffset, total_bytes_read);
277 EXPECT_EQ(kTestData + kOffset, data_read);
278 }
279
TEST_F(FileStreamTest,Write)280 TEST_F(FileStreamTest, Write) {
281 FileStream stream(base::SingleThreadTaskRunner::GetCurrentDefault());
282 int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
283 base::File::FLAG_ASYNC;
284 TestCompletionCallback callback;
285 int rv = stream.Open(temp_file_path(), flags, callback.callback());
286 EXPECT_THAT(callback.GetResult(rv), IsOk());
287
288 int64_t file_size;
289 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
290 EXPECT_EQ(0, file_size);
291
292 scoped_refptr<IOBuffer> buf = CreateTestDataBuffer();
293 rv = stream.Write(buf.get(), kTestDataSize, callback.callback());
294 rv = callback.GetResult(rv);
295 EXPECT_EQ(kTestDataSize, rv);
296
297 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
298 EXPECT_EQ(kTestDataSize, file_size);
299
300 std::string data_read;
301 EXPECT_TRUE(base::ReadFileToString(temp_file_path(), &data_read));
302 EXPECT_EQ(kTestData, data_read);
303 }
304
TEST_F(FileStreamTest,Write_EarlyDelete)305 TEST_F(FileStreamTest, Write_EarlyDelete) {
306 auto stream = std::make_unique<FileStream>(
307 base::SingleThreadTaskRunner::GetCurrentDefault());
308 int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
309 base::File::FLAG_ASYNC;
310 TestCompletionCallback callback;
311 int rv = stream->Open(temp_file_path(), flags, callback.callback());
312 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
313 EXPECT_THAT(callback.WaitForResult(), IsOk());
314
315 int64_t file_size;
316 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
317 EXPECT_EQ(0, file_size);
318
319 scoped_refptr<IOBufferWithSize> buf = CreateTestDataBuffer();
320 rv = stream->Write(buf.get(), buf->size(), callback.callback());
321 stream.reset();
322 if (rv < 0) {
323 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
324 // The callback should not be called if the request is cancelled.
325 base::RunLoop().RunUntilIdle();
326 EXPECT_FALSE(callback.have_result());
327 } else {
328 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
329 EXPECT_EQ(file_size, rv);
330 }
331 }
332
TEST_F(FileStreamTest,Write_FromOffset)333 TEST_F(FileStreamTest, Write_FromOffset) {
334 int64_t file_size;
335 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
336
337 FileStream stream(base::SingleThreadTaskRunner::GetCurrentDefault());
338 int flags = base::File::FLAG_OPEN | base::File::FLAG_WRITE |
339 base::File::FLAG_ASYNC;
340 TestCompletionCallback callback;
341 int rv = stream.Open(temp_file_path(), flags, callback.callback());
342 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
343 EXPECT_THAT(callback.WaitForResult(), IsOk());
344
345 TestInt64CompletionCallback callback64;
346 const int64_t kOffset = kTestDataSize;
347 rv = stream.Seek(kOffset, callback64.callback());
348 ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
349 int64_t new_offset = callback64.WaitForResult();
350 EXPECT_EQ(kTestDataSize, new_offset);
351
352 int total_bytes_written = 0;
353
354 scoped_refptr<IOBufferWithSize> buffer = CreateTestDataBuffer();
355 int buffer_size = buffer->size();
356 scoped_refptr<DrainableIOBuffer> drainable =
357 base::MakeRefCounted<DrainableIOBuffer>(std::move(buffer), buffer_size);
358 while (total_bytes_written != kTestDataSize) {
359 rv = stream.Write(drainable.get(), drainable->BytesRemaining(),
360 callback.callback());
361 if (rv == ERR_IO_PENDING)
362 rv = callback.WaitForResult();
363 EXPECT_LT(0, rv);
364 if (rv <= 0)
365 break;
366 drainable->DidConsume(rv);
367 total_bytes_written += rv;
368 }
369 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
370 EXPECT_EQ(file_size, kTestDataSize * 2);
371 }
372
TEST_F(FileStreamTest,BasicReadWrite)373 TEST_F(FileStreamTest, BasicReadWrite) {
374 int64_t file_size;
375 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
376
377 auto stream = std::make_unique<FileStream>(
378 base::SingleThreadTaskRunner::GetCurrentDefault());
379 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
380 base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
381 TestCompletionCallback callback;
382 int rv = stream->Open(temp_file_path(), flags, callback.callback());
383 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
384 EXPECT_THAT(callback.WaitForResult(), IsOk());
385
386 int64_t total_bytes_read = 0;
387
388 std::string data_read;
389 for (;;) {
390 scoped_refptr<IOBufferWithSize> buf =
391 base::MakeRefCounted<IOBufferWithSize>(4);
392 rv = stream->Read(buf.get(), buf->size(), callback.callback());
393 if (rv == ERR_IO_PENDING)
394 rv = callback.WaitForResult();
395 EXPECT_LE(0, rv);
396 if (rv <= 0)
397 break;
398 total_bytes_read += rv;
399 data_read.append(buf->data(), rv);
400 }
401 EXPECT_EQ(file_size, total_bytes_read);
402 EXPECT_TRUE(data_read == kTestData);
403
404 int total_bytes_written = 0;
405
406 scoped_refptr<IOBufferWithSize> buffer = CreateTestDataBuffer();
407 int buffer_size = buffer->size();
408 scoped_refptr<DrainableIOBuffer> drainable =
409 base::MakeRefCounted<DrainableIOBuffer>(std::move(buffer), buffer_size);
410 while (total_bytes_written != kTestDataSize) {
411 rv = stream->Write(drainable.get(), drainable->BytesRemaining(),
412 callback.callback());
413 if (rv == ERR_IO_PENDING)
414 rv = callback.WaitForResult();
415 EXPECT_LT(0, rv);
416 if (rv <= 0)
417 break;
418 drainable->DidConsume(rv);
419 total_bytes_written += rv;
420 }
421
422 stream.reset();
423
424 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
425 EXPECT_EQ(kTestDataSize * 2, file_size);
426 }
427
TEST_F(FileStreamTest,BasicWriteRead)428 TEST_F(FileStreamTest, BasicWriteRead) {
429 int64_t file_size;
430 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
431
432 auto stream = std::make_unique<FileStream>(
433 base::SingleThreadTaskRunner::GetCurrentDefault());
434 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
435 base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
436 TestCompletionCallback callback;
437 int rv = stream->Open(temp_file_path(), flags, callback.callback());
438 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
439 EXPECT_THAT(callback.WaitForResult(), IsOk());
440
441 TestInt64CompletionCallback callback64;
442 rv = stream->Seek(file_size, callback64.callback());
443 ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
444 int64_t offset = callback64.WaitForResult();
445 EXPECT_EQ(offset, file_size);
446
447 int total_bytes_written = 0;
448
449 scoped_refptr<IOBufferWithSize> buffer = CreateTestDataBuffer();
450 int buffer_size = buffer->size();
451 scoped_refptr<DrainableIOBuffer> drainable =
452 base::MakeRefCounted<DrainableIOBuffer>(std::move(buffer), buffer_size);
453 while (total_bytes_written != kTestDataSize) {
454 rv = stream->Write(drainable.get(), drainable->BytesRemaining(),
455 callback.callback());
456 if (rv == ERR_IO_PENDING)
457 rv = callback.WaitForResult();
458 EXPECT_LT(0, rv);
459 if (rv <= 0)
460 break;
461 drainable->DidConsume(rv);
462 total_bytes_written += rv;
463 }
464
465 EXPECT_EQ(kTestDataSize, total_bytes_written);
466
467 rv = stream->Seek(0, callback64.callback());
468 ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
469 offset = callback64.WaitForResult();
470 EXPECT_EQ(0, offset);
471
472 int total_bytes_read = 0;
473
474 std::string data_read;
475 for (;;) {
476 scoped_refptr<IOBufferWithSize> buf =
477 base::MakeRefCounted<IOBufferWithSize>(4);
478 rv = stream->Read(buf.get(), buf->size(), callback.callback());
479 if (rv == ERR_IO_PENDING)
480 rv = callback.WaitForResult();
481 EXPECT_LE(0, rv);
482 if (rv <= 0)
483 break;
484 total_bytes_read += rv;
485 data_read.append(buf->data(), rv);
486 }
487 stream.reset();
488
489 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
490 EXPECT_EQ(kTestDataSize * 2, file_size);
491
492 EXPECT_EQ(kTestDataSize * 2, total_bytes_read);
493 const std::string kExpectedFileData =
494 std::string(kTestData) + std::string(kTestData);
495 EXPECT_EQ(kExpectedFileData, data_read);
496 }
497
498 class TestWriteReadCompletionCallback {
499 public:
TestWriteReadCompletionCallback(FileStream * stream,int * total_bytes_written,int * total_bytes_read,std::string * data_read)500 TestWriteReadCompletionCallback(FileStream* stream,
501 int* total_bytes_written,
502 int* total_bytes_read,
503 std::string* data_read)
504 : stream_(stream),
505 total_bytes_written_(total_bytes_written),
506 total_bytes_read_(total_bytes_read),
507 data_read_(data_read),
508 drainable_(
509 base::MakeRefCounted<DrainableIOBuffer>(CreateTestDataBuffer(),
510 kTestDataSize)) {}
511
512 TestWriteReadCompletionCallback(const TestWriteReadCompletionCallback&) =
513 delete;
514 TestWriteReadCompletionCallback& operator=(
515 const TestWriteReadCompletionCallback&) = delete;
516
WaitForResult()517 int WaitForResult() {
518 DCHECK(!waiting_for_result_);
519 while (!have_result_) {
520 base::RunLoop loop;
521 quit_closure_ = loop.QuitWhenIdleClosure();
522 waiting_for_result_ = true;
523 loop.Run();
524 waiting_for_result_ = false;
525 }
526 have_result_ = false; // auto-reset for next callback
527 return result_;
528 }
529
callback()530 CompletionOnceCallback callback() {
531 return base::BindOnce(&TestWriteReadCompletionCallback::OnComplete,
532 base::Unretained(this));
533 }
534
ValidateWrittenData()535 void ValidateWrittenData() {
536 TestCompletionCallback callback;
537 int rv = 0;
538 for (;;) {
539 scoped_refptr<IOBufferWithSize> buf =
540 base::MakeRefCounted<IOBufferWithSize>(4);
541 rv = stream_->Read(buf.get(), buf->size(), callback.callback());
542 if (rv == ERR_IO_PENDING) {
543 rv = callback.WaitForResult();
544 }
545 EXPECT_LE(0, rv);
546 if (rv <= 0)
547 break;
548 *total_bytes_read_ += rv;
549 data_read_->append(buf->data(), rv);
550 }
551 }
552
553 private:
OnComplete(int result)554 void OnComplete(int result) {
555 DCHECK_LT(0, result);
556 *total_bytes_written_ += result;
557
558 int rv;
559
560 if (*total_bytes_written_ != kTestDataSize) {
561 // Recurse to finish writing all data.
562 int total_bytes_written = 0, total_bytes_read = 0;
563 std::string data_read;
564 TestWriteReadCompletionCallback callback(
565 stream_, &total_bytes_written, &total_bytes_read, &data_read);
566 rv = stream_->Write(
567 drainable_.get(), drainable_->BytesRemaining(), callback.callback());
568 DCHECK_EQ(ERR_IO_PENDING, rv);
569 rv = callback.WaitForResult();
570 drainable_->DidConsume(total_bytes_written);
571 *total_bytes_written_ += total_bytes_written;
572 *total_bytes_read_ += total_bytes_read;
573 *data_read_ += data_read;
574 } else { // We're done writing all data. Start reading the data.
575 TestInt64CompletionCallback callback64;
576 EXPECT_THAT(stream_->Seek(0, callback64.callback()),
577 IsError(ERR_IO_PENDING));
578 {
579 EXPECT_LE(0, callback64.WaitForResult());
580 }
581 }
582
583 result_ = *total_bytes_written_;
584 have_result_ = true;
585 if (waiting_for_result_)
586 std::move(quit_closure_).Run();
587 }
588
589 int result_ = 0;
590 bool have_result_ = false;
591 bool waiting_for_result_ = false;
592 raw_ptr<FileStream> stream_;
593 raw_ptr<int> total_bytes_written_;
594 raw_ptr<int> total_bytes_read_;
595 raw_ptr<std::string> data_read_;
596 scoped_refptr<DrainableIOBuffer> drainable_;
597 base::OnceClosure quit_closure_;
598 };
599
TEST_F(FileStreamTest,WriteRead)600 TEST_F(FileStreamTest, WriteRead) {
601 int64_t file_size;
602 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
603
604 auto stream = std::make_unique<FileStream>(
605 base::SingleThreadTaskRunner::GetCurrentDefault());
606 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
607 base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
608 TestCompletionCallback open_callback;
609 int rv = stream->Open(temp_file_path(), flags, open_callback.callback());
610 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
611 EXPECT_THAT(open_callback.WaitForResult(), IsOk());
612
613 TestInt64CompletionCallback callback64;
614 EXPECT_THAT(stream->Seek(file_size, callback64.callback()),
615 IsError(ERR_IO_PENDING));
616 EXPECT_EQ(file_size, callback64.WaitForResult());
617
618 int total_bytes_written = 0;
619 int total_bytes_read = 0;
620 std::string data_read;
621 {
622 // `callback` can't outlive `stream`.
623 TestWriteReadCompletionCallback callback(stream.get(), &total_bytes_written,
624 &total_bytes_read, &data_read);
625
626 scoped_refptr<IOBufferWithSize> buf = CreateTestDataBuffer();
627 rv = stream->Write(buf.get(), buf->size(), callback.callback());
628 if (rv == ERR_IO_PENDING) {
629 rv = callback.WaitForResult();
630 }
631 EXPECT_LT(0, rv);
632 EXPECT_EQ(kTestDataSize, total_bytes_written);
633
634 callback.ValidateWrittenData();
635 }
636 stream.reset();
637
638 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
639 EXPECT_EQ(kTestDataSize * 2, file_size);
640
641 EXPECT_EQ(kTestDataSize * 2, total_bytes_read);
642 const std::string kExpectedFileData =
643 std::string(kTestData) + std::string(kTestData);
644 EXPECT_EQ(kExpectedFileData, data_read);
645 }
646
647 class TestWriteCloseCompletionCallback {
648 public:
TestWriteCloseCompletionCallback(FileStream * stream,int * total_bytes_written)649 TestWriteCloseCompletionCallback(FileStream* stream, int* total_bytes_written)
650 : stream_(stream),
651 total_bytes_written_(total_bytes_written),
652 drainable_(
653 base::MakeRefCounted<DrainableIOBuffer>(CreateTestDataBuffer(),
654 kTestDataSize)) {}
655 TestWriteCloseCompletionCallback(const TestWriteCloseCompletionCallback&) =
656 delete;
657 TestWriteCloseCompletionCallback& operator=(
658 const TestWriteCloseCompletionCallback&) = delete;
659
WaitForResult()660 int WaitForResult() {
661 DCHECK(!waiting_for_result_);
662 while (!have_result_) {
663 base::RunLoop loop;
664 quit_closure_ = loop.QuitWhenIdleClosure();
665 waiting_for_result_ = true;
666 loop.Run();
667 waiting_for_result_ = false;
668 }
669 have_result_ = false; // auto-reset for next callback
670 return result_;
671 }
672
callback()673 CompletionOnceCallback callback() {
674 return base::BindOnce(&TestWriteCloseCompletionCallback::OnComplete,
675 base::Unretained(this));
676 }
677
678 private:
OnComplete(int result)679 void OnComplete(int result) {
680 DCHECK_LT(0, result);
681 *total_bytes_written_ += result;
682
683 int rv;
684
685 if (*total_bytes_written_ != kTestDataSize) {
686 // Recurse to finish writing all data.
687 int total_bytes_written = 0;
688 TestWriteCloseCompletionCallback callback(stream_, &total_bytes_written);
689 rv = stream_->Write(
690 drainable_.get(), drainable_->BytesRemaining(), callback.callback());
691 DCHECK_EQ(ERR_IO_PENDING, rv);
692 rv = callback.WaitForResult();
693 drainable_->DidConsume(total_bytes_written);
694 *total_bytes_written_ += total_bytes_written;
695 }
696
697 result_ = *total_bytes_written_;
698 have_result_ = true;
699 if (waiting_for_result_)
700 std::move(quit_closure_).Run();
701 }
702
703 int result_ = 0;
704 bool have_result_ = false;
705 bool waiting_for_result_ = false;
706 raw_ptr<FileStream> stream_;
707 raw_ptr<int> total_bytes_written_;
708 scoped_refptr<DrainableIOBuffer> drainable_;
709 base::OnceClosure quit_closure_;
710 };
711
TEST_F(FileStreamTest,WriteClose)712 TEST_F(FileStreamTest, WriteClose) {
713 int64_t file_size;
714 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
715
716 auto stream = std::make_unique<FileStream>(
717 base::SingleThreadTaskRunner::GetCurrentDefault());
718 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
719 base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
720 TestCompletionCallback open_callback;
721 int rv = stream->Open(temp_file_path(), flags, open_callback.callback());
722 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
723 EXPECT_THAT(open_callback.WaitForResult(), IsOk());
724
725 TestInt64CompletionCallback callback64;
726 EXPECT_THAT(stream->Seek(file_size, callback64.callback()),
727 IsError(ERR_IO_PENDING));
728 EXPECT_EQ(file_size, callback64.WaitForResult());
729
730 int total_bytes_written = 0;
731 {
732 // `callback` can't outlive `stream`.
733 TestWriteCloseCompletionCallback callback(stream.get(),
734 &total_bytes_written);
735 scoped_refptr<IOBufferWithSize> buf = CreateTestDataBuffer();
736 rv = stream->Write(buf.get(), buf->size(), callback.callback());
737 if (rv == ERR_IO_PENDING) {
738 total_bytes_written = callback.WaitForResult();
739 }
740 EXPECT_LT(0, total_bytes_written);
741 EXPECT_EQ(kTestDataSize, total_bytes_written);
742 }
743 stream.reset();
744
745 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
746 EXPECT_EQ(kTestDataSize * 2, file_size);
747 }
748
TEST_F(FileStreamTest,OpenAndDelete)749 TEST_F(FileStreamTest, OpenAndDelete) {
750 base::Thread worker_thread("StreamTest");
751 ASSERT_TRUE(worker_thread.Start());
752
753 base::ScopedDisallowBlocking disallow_blocking;
754 auto stream = std::make_unique<FileStream>(worker_thread.task_runner());
755 int flags = base::File::FLAG_OPEN | base::File::FLAG_WRITE |
756 base::File::FLAG_ASYNC;
757 TestCompletionCallback open_callback;
758 int rv = stream->Open(temp_file_path(), flags, open_callback.callback());
759 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
760
761 // Delete the stream without waiting for the open operation to be
762 // complete. Should be safe.
763 stream.reset();
764
765 // Force an operation through the worker.
766 auto stream2 = std::make_unique<FileStream>(worker_thread.task_runner());
767 TestCompletionCallback open_callback2;
768 rv = stream2->Open(temp_file_path(), flags, open_callback2.callback());
769 EXPECT_THAT(open_callback2.GetResult(rv), IsOk());
770 stream2.reset();
771
772 // open_callback won't be called.
773 base::RunLoop().RunUntilIdle();
774 EXPECT_FALSE(open_callback.have_result());
775 }
776
777 // Verify that Write() errors are mapped correctly.
TEST_F(FileStreamTest,WriteError)778 TEST_F(FileStreamTest, WriteError) {
779 // Try opening file as read-only and then writing to it using FileStream.
780 uint32_t flags =
781 base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_ASYNC;
782
783 base::File file(temp_file_path(), flags);
784 ASSERT_TRUE(file.IsValid());
785
786 auto stream = std::make_unique<FileStream>(
787 std::move(file), base::SingleThreadTaskRunner::GetCurrentDefault());
788
789 scoped_refptr<IOBuffer> buf = base::MakeRefCounted<IOBufferWithSize>(1);
790 buf->data()[0] = 0;
791
792 TestCompletionCallback callback;
793 int rv = stream->Write(buf.get(), 1, callback.callback());
794 if (rv == ERR_IO_PENDING)
795 rv = callback.WaitForResult();
796 EXPECT_LT(rv, 0);
797
798 stream.reset();
799 base::RunLoop().RunUntilIdle();
800 }
801
802 // Verify that Read() errors are mapped correctly.
TEST_F(FileStreamTest,ReadError)803 TEST_F(FileStreamTest, ReadError) {
804 // Try opening file for write and then reading from it using FileStream.
805 uint32_t flags =
806 base::File::FLAG_OPEN | base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
807
808 base::File file(temp_file_path(), flags);
809 ASSERT_TRUE(file.IsValid());
810
811 auto stream = std::make_unique<FileStream>(
812 std::move(file), base::SingleThreadTaskRunner::GetCurrentDefault());
813
814 scoped_refptr<IOBuffer> buf = base::MakeRefCounted<IOBufferWithSize>(1);
815 TestCompletionCallback callback;
816 int rv = stream->Read(buf.get(), 1, callback.callback());
817 if (rv == ERR_IO_PENDING)
818 rv = callback.WaitForResult();
819 EXPECT_LT(rv, 0);
820
821 stream.reset();
822 base::RunLoop().RunUntilIdle();
823 }
824
825 #if BUILDFLAG(IS_WIN)
826 // Verifies that a FileStream will close itself if it receives a File whose
827 // async flag doesn't match the async state of the underlying handle.
TEST_F(FileStreamTest,AsyncFlagMismatch)828 TEST_F(FileStreamTest, AsyncFlagMismatch) {
829 // Open the test file without async, then make a File with the same sync
830 // handle but with the async flag set to true.
831 uint32_t flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
832 base::File file(temp_file_path(), flags);
833 base::File lying_file(file.TakePlatformFile(), true);
834 ASSERT_TRUE(lying_file.IsValid());
835
836 FileStream stream(std::move(lying_file),
837 base::SingleThreadTaskRunner::GetCurrentDefault());
838 ASSERT_FALSE(stream.IsOpen());
839 TestCompletionCallback callback;
840 scoped_refptr<IOBufferWithSize> buf =
841 base::MakeRefCounted<IOBufferWithSize>(4);
842 int rv = stream.Read(buf.get(), buf->size(), callback.callback());
843 EXPECT_THAT(callback.GetResult(rv), IsError(ERR_UNEXPECTED));
844 }
845 #endif
846
847 #if BUILDFLAG(IS_ANDROID)
848 // TODO(https://crbug.com/894599): flaky on both android and cronet bots.
TEST_F(FileStreamTest,DISABLED_ContentUriRead)849 TEST_F(FileStreamTest, DISABLED_ContentUriRead) {
850 base::FilePath test_dir;
851 base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &test_dir);
852 test_dir = test_dir.AppendASCII("net");
853 test_dir = test_dir.AppendASCII("data");
854 test_dir = test_dir.AppendASCII("file_stream_unittest");
855 ASSERT_TRUE(base::PathExists(test_dir));
856 base::FilePath image_file = test_dir.Append(FILE_PATH_LITERAL("red.png"));
857
858 // Insert the image into MediaStore. MediaStore will do some conversions, and
859 // return the content URI.
860 base::FilePath path = base::InsertImageIntoMediaStore(image_file);
861 EXPECT_TRUE(path.IsContentUri());
862 EXPECT_TRUE(base::PathExists(path));
863 int64_t file_size;
864 EXPECT_TRUE(base::GetFileSize(path, &file_size));
865 EXPECT_LT(0, file_size);
866
867 FileStream stream(base::SingleThreadTaskRunner::GetCurrentDefault());
868 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
869 base::File::FLAG_ASYNC;
870 TestCompletionCallback callback;
871 int rv = stream.Open(path, flags, callback.callback());
872 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
873 EXPECT_THAT(callback.WaitForResult(), IsOk());
874
875 int total_bytes_read = 0;
876
877 std::string data_read;
878 for (;;) {
879 scoped_refptr<IOBufferWithSize> buf =
880 base::MakeRefCounted<IOBufferWithSize>(4);
881 rv = stream.Read(buf.get(), buf->size(), callback.callback());
882 if (rv == ERR_IO_PENDING)
883 rv = callback.WaitForResult();
884 EXPECT_LE(0, rv);
885 if (rv <= 0)
886 break;
887 total_bytes_read += rv;
888 data_read.append(buf->data(), rv);
889 }
890 EXPECT_EQ(file_size, total_bytes_read);
891 }
892 #endif
893
894 } // namespace
895
896 } // namespace net
897