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 waiting_for_result_ = true;
521 base::RunLoop().Run();
522 waiting_for_result_ = false;
523 }
524 have_result_ = false; // auto-reset for next callback
525 return result_;
526 }
527
callback()528 CompletionOnceCallback callback() {
529 return base::BindOnce(&TestWriteReadCompletionCallback::OnComplete,
530 base::Unretained(this));
531 }
532
ValidateWrittenData()533 void ValidateWrittenData() {
534 TestCompletionCallback callback;
535 int rv = 0;
536 for (;;) {
537 scoped_refptr<IOBufferWithSize> buf =
538 base::MakeRefCounted<IOBufferWithSize>(4);
539 rv = stream_->Read(buf.get(), buf->size(), callback.callback());
540 if (rv == ERR_IO_PENDING) {
541 rv = callback.WaitForResult();
542 }
543 EXPECT_LE(0, rv);
544 if (rv <= 0)
545 break;
546 *total_bytes_read_ += rv;
547 data_read_->append(buf->data(), rv);
548 }
549 }
550
551 private:
OnComplete(int result)552 void OnComplete(int result) {
553 DCHECK_LT(0, result);
554 *total_bytes_written_ += result;
555
556 int rv;
557
558 if (*total_bytes_written_ != kTestDataSize) {
559 // Recurse to finish writing all data.
560 int total_bytes_written = 0, total_bytes_read = 0;
561 std::string data_read;
562 TestWriteReadCompletionCallback callback(
563 stream_, &total_bytes_written, &total_bytes_read, &data_read);
564 rv = stream_->Write(
565 drainable_.get(), drainable_->BytesRemaining(), callback.callback());
566 DCHECK_EQ(ERR_IO_PENDING, rv);
567 rv = callback.WaitForResult();
568 drainable_->DidConsume(total_bytes_written);
569 *total_bytes_written_ += total_bytes_written;
570 *total_bytes_read_ += total_bytes_read;
571 *data_read_ += data_read;
572 } else { // We're done writing all data. Start reading the data.
573 TestInt64CompletionCallback callback64;
574 EXPECT_THAT(stream_->Seek(0, callback64.callback()),
575 IsError(ERR_IO_PENDING));
576 {
577 EXPECT_LE(0, callback64.WaitForResult());
578 }
579 }
580
581 result_ = *total_bytes_written_;
582 have_result_ = true;
583 if (waiting_for_result_)
584 base::RunLoop::QuitCurrentWhenIdleDeprecated();
585 }
586
587 int result_ = 0;
588 bool have_result_ = false;
589 bool waiting_for_result_ = false;
590 raw_ptr<FileStream> stream_;
591 raw_ptr<int> total_bytes_written_;
592 raw_ptr<int> total_bytes_read_;
593 raw_ptr<std::string> data_read_;
594 scoped_refptr<DrainableIOBuffer> drainable_;
595 };
596
TEST_F(FileStreamTest,WriteRead)597 TEST_F(FileStreamTest, WriteRead) {
598 int64_t file_size;
599 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
600
601 auto stream = std::make_unique<FileStream>(
602 base::SingleThreadTaskRunner::GetCurrentDefault());
603 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
604 base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
605 TestCompletionCallback open_callback;
606 int rv = stream->Open(temp_file_path(), flags, open_callback.callback());
607 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
608 EXPECT_THAT(open_callback.WaitForResult(), IsOk());
609
610 TestInt64CompletionCallback callback64;
611 EXPECT_THAT(stream->Seek(file_size, callback64.callback()),
612 IsError(ERR_IO_PENDING));
613 EXPECT_EQ(file_size, callback64.WaitForResult());
614
615 int total_bytes_written = 0;
616 int total_bytes_read = 0;
617 std::string data_read;
618 {
619 // `callback` can't outlive `stream`.
620 TestWriteReadCompletionCallback callback(stream.get(), &total_bytes_written,
621 &total_bytes_read, &data_read);
622
623 scoped_refptr<IOBufferWithSize> buf = CreateTestDataBuffer();
624 rv = stream->Write(buf.get(), buf->size(), callback.callback());
625 if (rv == ERR_IO_PENDING) {
626 rv = callback.WaitForResult();
627 }
628 EXPECT_LT(0, rv);
629 EXPECT_EQ(kTestDataSize, total_bytes_written);
630
631 callback.ValidateWrittenData();
632 }
633 stream.reset();
634
635 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
636 EXPECT_EQ(kTestDataSize * 2, file_size);
637
638 EXPECT_EQ(kTestDataSize * 2, total_bytes_read);
639 const std::string kExpectedFileData =
640 std::string(kTestData) + std::string(kTestData);
641 EXPECT_EQ(kExpectedFileData, data_read);
642 }
643
644 class TestWriteCloseCompletionCallback {
645 public:
TestWriteCloseCompletionCallback(FileStream * stream,int * total_bytes_written)646 TestWriteCloseCompletionCallback(FileStream* stream, int* total_bytes_written)
647 : stream_(stream),
648 total_bytes_written_(total_bytes_written),
649 drainable_(
650 base::MakeRefCounted<DrainableIOBuffer>(CreateTestDataBuffer(),
651 kTestDataSize)) {}
652 TestWriteCloseCompletionCallback(const TestWriteCloseCompletionCallback&) =
653 delete;
654 TestWriteCloseCompletionCallback& operator=(
655 const TestWriteCloseCompletionCallback&) = delete;
656
WaitForResult()657 int WaitForResult() {
658 DCHECK(!waiting_for_result_);
659 while (!have_result_) {
660 waiting_for_result_ = true;
661 base::RunLoop().Run();
662 waiting_for_result_ = false;
663 }
664 have_result_ = false; // auto-reset for next callback
665 return result_;
666 }
667
callback()668 CompletionOnceCallback callback() {
669 return base::BindOnce(&TestWriteCloseCompletionCallback::OnComplete,
670 base::Unretained(this));
671 }
672
673 private:
OnComplete(int result)674 void OnComplete(int result) {
675 DCHECK_LT(0, result);
676 *total_bytes_written_ += result;
677
678 int rv;
679
680 if (*total_bytes_written_ != kTestDataSize) {
681 // Recurse to finish writing all data.
682 int total_bytes_written = 0;
683 TestWriteCloseCompletionCallback callback(stream_, &total_bytes_written);
684 rv = stream_->Write(
685 drainable_.get(), drainable_->BytesRemaining(), callback.callback());
686 DCHECK_EQ(ERR_IO_PENDING, rv);
687 rv = callback.WaitForResult();
688 drainable_->DidConsume(total_bytes_written);
689 *total_bytes_written_ += total_bytes_written;
690 }
691
692 result_ = *total_bytes_written_;
693 have_result_ = true;
694 if (waiting_for_result_)
695 base::RunLoop::QuitCurrentWhenIdleDeprecated();
696 }
697
698 int result_ = 0;
699 bool have_result_ = false;
700 bool waiting_for_result_ = false;
701 raw_ptr<FileStream> stream_;
702 raw_ptr<int> total_bytes_written_;
703 scoped_refptr<DrainableIOBuffer> drainable_;
704 };
705
TEST_F(FileStreamTest,WriteClose)706 TEST_F(FileStreamTest, WriteClose) {
707 int64_t file_size;
708 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
709
710 auto stream = std::make_unique<FileStream>(
711 base::SingleThreadTaskRunner::GetCurrentDefault());
712 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
713 base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
714 TestCompletionCallback open_callback;
715 int rv = stream->Open(temp_file_path(), flags, open_callback.callback());
716 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
717 EXPECT_THAT(open_callback.WaitForResult(), IsOk());
718
719 TestInt64CompletionCallback callback64;
720 EXPECT_THAT(stream->Seek(file_size, callback64.callback()),
721 IsError(ERR_IO_PENDING));
722 EXPECT_EQ(file_size, callback64.WaitForResult());
723
724 int total_bytes_written = 0;
725 {
726 // `callback` can't outlive `stream`.
727 TestWriteCloseCompletionCallback callback(stream.get(),
728 &total_bytes_written);
729 scoped_refptr<IOBufferWithSize> buf = CreateTestDataBuffer();
730 rv = stream->Write(buf.get(), buf->size(), callback.callback());
731 if (rv == ERR_IO_PENDING) {
732 total_bytes_written = callback.WaitForResult();
733 }
734 EXPECT_LT(0, total_bytes_written);
735 EXPECT_EQ(kTestDataSize, total_bytes_written);
736 }
737 stream.reset();
738
739 EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
740 EXPECT_EQ(kTestDataSize * 2, file_size);
741 }
742
TEST_F(FileStreamTest,OpenAndDelete)743 TEST_F(FileStreamTest, OpenAndDelete) {
744 base::Thread worker_thread("StreamTest");
745 ASSERT_TRUE(worker_thread.Start());
746
747 base::ScopedDisallowBlocking disallow_blocking;
748 auto stream = std::make_unique<FileStream>(worker_thread.task_runner());
749 int flags = base::File::FLAG_OPEN | base::File::FLAG_WRITE |
750 base::File::FLAG_ASYNC;
751 TestCompletionCallback open_callback;
752 int rv = stream->Open(temp_file_path(), flags, open_callback.callback());
753 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
754
755 // Delete the stream without waiting for the open operation to be
756 // complete. Should be safe.
757 stream.reset();
758
759 // Force an operation through the worker.
760 auto stream2 = std::make_unique<FileStream>(worker_thread.task_runner());
761 TestCompletionCallback open_callback2;
762 rv = stream2->Open(temp_file_path(), flags, open_callback2.callback());
763 EXPECT_THAT(open_callback2.GetResult(rv), IsOk());
764 stream2.reset();
765
766 // open_callback won't be called.
767 base::RunLoop().RunUntilIdle();
768 EXPECT_FALSE(open_callback.have_result());
769 }
770
771 // Verify that Write() errors are mapped correctly.
TEST_F(FileStreamTest,WriteError)772 TEST_F(FileStreamTest, WriteError) {
773 // Try opening file as read-only and then writing to it using FileStream.
774 uint32_t flags =
775 base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_ASYNC;
776
777 base::File file(temp_file_path(), flags);
778 ASSERT_TRUE(file.IsValid());
779
780 auto stream = std::make_unique<FileStream>(
781 std::move(file), base::SingleThreadTaskRunner::GetCurrentDefault());
782
783 scoped_refptr<IOBuffer> buf = base::MakeRefCounted<IOBufferWithSize>(1);
784 buf->data()[0] = 0;
785
786 TestCompletionCallback callback;
787 int rv = stream->Write(buf.get(), 1, callback.callback());
788 if (rv == ERR_IO_PENDING)
789 rv = callback.WaitForResult();
790 EXPECT_LT(rv, 0);
791
792 stream.reset();
793 base::RunLoop().RunUntilIdle();
794 }
795
796 // Verify that Read() errors are mapped correctly.
TEST_F(FileStreamTest,ReadError)797 TEST_F(FileStreamTest, ReadError) {
798 // Try opening file for write and then reading from it using FileStream.
799 uint32_t flags =
800 base::File::FLAG_OPEN | base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
801
802 base::File file(temp_file_path(), flags);
803 ASSERT_TRUE(file.IsValid());
804
805 auto stream = std::make_unique<FileStream>(
806 std::move(file), base::SingleThreadTaskRunner::GetCurrentDefault());
807
808 scoped_refptr<IOBuffer> buf = base::MakeRefCounted<IOBufferWithSize>(1);
809 TestCompletionCallback callback;
810 int rv = stream->Read(buf.get(), 1, callback.callback());
811 if (rv == ERR_IO_PENDING)
812 rv = callback.WaitForResult();
813 EXPECT_LT(rv, 0);
814
815 stream.reset();
816 base::RunLoop().RunUntilIdle();
817 }
818
819 #if BUILDFLAG(IS_WIN)
820 // Verifies that a FileStream will close itself if it receives a File whose
821 // async flag doesn't match the async state of the underlying handle.
TEST_F(FileStreamTest,AsyncFlagMismatch)822 TEST_F(FileStreamTest, AsyncFlagMismatch) {
823 // Open the test file without async, then make a File with the same sync
824 // handle but with the async flag set to true.
825 uint32_t flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
826 base::File file(temp_file_path(), flags);
827 base::File lying_file(file.TakePlatformFile(), true);
828 ASSERT_TRUE(lying_file.IsValid());
829
830 FileStream stream(std::move(lying_file),
831 base::SingleThreadTaskRunner::GetCurrentDefault());
832 ASSERT_FALSE(stream.IsOpen());
833 TestCompletionCallback callback;
834 scoped_refptr<IOBufferWithSize> buf =
835 base::MakeRefCounted<IOBufferWithSize>(4);
836 int rv = stream.Read(buf.get(), buf->size(), callback.callback());
837 EXPECT_THAT(callback.GetResult(rv), IsError(ERR_UNEXPECTED));
838 }
839 #endif
840
841 #if BUILDFLAG(IS_ANDROID)
842 // TODO(https://crbug.com/894599): flaky on both android and cronet bots.
TEST_F(FileStreamTest,DISABLED_ContentUriRead)843 TEST_F(FileStreamTest, DISABLED_ContentUriRead) {
844 base::FilePath test_dir;
845 base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &test_dir);
846 test_dir = test_dir.AppendASCII("net");
847 test_dir = test_dir.AppendASCII("data");
848 test_dir = test_dir.AppendASCII("file_stream_unittest");
849 ASSERT_TRUE(base::PathExists(test_dir));
850 base::FilePath image_file = test_dir.Append(FILE_PATH_LITERAL("red.png"));
851
852 // Insert the image into MediaStore. MediaStore will do some conversions, and
853 // return the content URI.
854 base::FilePath path = base::InsertImageIntoMediaStore(image_file);
855 EXPECT_TRUE(path.IsContentUri());
856 EXPECT_TRUE(base::PathExists(path));
857 int64_t file_size;
858 EXPECT_TRUE(base::GetFileSize(path, &file_size));
859 EXPECT_LT(0, file_size);
860
861 FileStream stream(base::SingleThreadTaskRunner::GetCurrentDefault());
862 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
863 base::File::FLAG_ASYNC;
864 TestCompletionCallback callback;
865 int rv = stream.Open(path, flags, callback.callback());
866 EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
867 EXPECT_THAT(callback.WaitForResult(), IsOk());
868
869 int total_bytes_read = 0;
870
871 std::string data_read;
872 for (;;) {
873 scoped_refptr<IOBufferWithSize> buf =
874 base::MakeRefCounted<IOBufferWithSize>(4);
875 rv = stream.Read(buf.get(), buf->size(), callback.callback());
876 if (rv == ERR_IO_PENDING)
877 rv = callback.WaitForResult();
878 EXPECT_LE(0, rv);
879 if (rv <= 0)
880 break;
881 total_bytes_read += rv;
882 data_read.append(buf->data(), rv);
883 }
884 EXPECT_EQ(file_size, total_bytes_read);
885 }
886 #endif
887
888 } // namespace
889
890 } // namespace net
891