| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/fileapi/diversion_file_manager.h" |
| |
| #include "base/files/file_util.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "net/base/io_buffer.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace ash { |
| |
| using FinishDivertingResult = DiversionFileManager::FinishDivertingResult; |
| using StartDivertingResult = DiversionFileManager::StartDivertingResult; |
| using StoppedReason = DiversionFileManager::StoppedReason; |
| |
| namespace { |
| |
| DiversionFileManager::Callback IncrementCounterCallback( |
| StoppedReason expected_stopped_reason, |
| const storage::FileSystemURL& expected_url, |
| int* counter, |
| int delta) { |
| return base::BindOnce( |
| [](StoppedReason expected_stopped_reason, |
| const storage::FileSystemURL& expected_url, int* counter, int delta, |
| StoppedReason stopped_reason, const storage::FileSystemURL& url, |
| base::ScopedFD scoped_fd, int64_t file_size, base::File::Error error) { |
| EXPECT_EQ(expected_stopped_reason, stopped_reason); |
| EXPECT_EQ(expected_url, url); |
| *counter += delta; |
| }, |
| expected_stopped_reason, expected_url, counter, delta); |
| } |
| |
| } // namespace |
| |
| class DiversionFileManagerTest : public testing::Test { |
| public: |
| DiversionFileManagerTest() |
| : task_environment_(base::test::TaskEnvironment::MainThreadType::IO, |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME) {} |
| |
| protected: |
| void SynchronousWrite(storage::FileStreamWriter& writer, std::string s) { |
| scoped_refptr<net::StringIOBuffer> buffer = |
| base::MakeRefCounted<net::StringIOBuffer>(s); |
| writer.Write( |
| buffer.get(), buffer->size(), |
| base::BindOnce([](base::RepeatingClosure quit_closure, |
| int byte_count_or_error_code) { quit_closure.Run(); }, |
| task_environment_.QuitClosure())); |
| task_environment_.RunUntilQuit(); |
| } |
| |
| content::BrowserTaskEnvironment task_environment_; |
| }; |
| |
| TEST_F(DiversionFileManagerTest, ImplicitExplicitFinish) { |
| ASSERT_TRUE( |
| ::content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
| |
| scoped_refptr<DiversionFileManager> dfm = |
| base::MakeRefCounted<DiversionFileManager>(); |
| storage::FileSystemURL bar_url = storage::FileSystemURL::CreateForTest( |
| GURL("filesystem:chrome-extension://abc/external/p/q/bar")); |
| storage::FileSystemURL foo_url = storage::FileSystemURL::CreateForTest( |
| GURL("filesystem:chrome-extension://abc/external/p/q/foo")); |
| EXPECT_FALSE(dfm->IsDiverting(bar_url)); |
| EXPECT_FALSE(dfm->IsDiverting(foo_url)); |
| |
| int bar_counter = 0; |
| int foo_counter = 0; |
| |
| ASSERT_EQ( |
| StartDivertingResult::kOK, |
| dfm->StartDiverting(bar_url, base::Seconds(28), |
| IncrementCounterCallback(StoppedReason::kImplicitIdle, |
| bar_url, &bar_counter, 1))); |
| ASSERT_EQ( |
| StartDivertingResult::kOK, |
| dfm->StartDiverting(foo_url, base::Seconds(32), |
| IncrementCounterCallback(StoppedReason::kImplicitIdle, |
| foo_url, &foo_counter, 10))); |
| EXPECT_TRUE(dfm->IsDiverting(bar_url)); |
| EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| |
| task_environment_.FastForwardBy(base::Seconds(30)); |
| EXPECT_FALSE(dfm->IsDiverting(bar_url)); |
| EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| |
| ASSERT_EQ(FinishDivertingResult::kWasNotDiverting, |
| dfm->FinishDiverting(bar_url, IncrementCounterCallback( |
| StoppedReason::kExplicitFinish, |
| bar_url, &bar_counter, 100))); |
| ASSERT_EQ(FinishDivertingResult::kOK, |
| dfm->FinishDiverting(foo_url, IncrementCounterCallback( |
| StoppedReason::kExplicitFinish, |
| foo_url, &foo_counter, 1000))); |
| EXPECT_FALSE(dfm->IsDiverting(bar_url)); |
| EXPECT_FALSE(dfm->IsDiverting(foo_url)); |
| |
| EXPECT_EQ(bar_counter, 1); |
| EXPECT_EQ(foo_counter, 1000); |
| } |
| |
| TEST_F(DiversionFileManagerTest, ReaderKeepsDiversionAlive) { |
| ASSERT_TRUE( |
| ::content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
| |
| scoped_refptr<DiversionFileManager> dfm = |
| base::MakeRefCounted<DiversionFileManager>(); |
| storage::FileSystemURL foo_url = storage::FileSystemURL::CreateForTest( |
| GURL("filesystem:chrome-extension://abc/external/p/q/foo")); |
| |
| int foo_counter = 0; |
| |
| ASSERT_EQ( |
| StartDivertingResult::kOK, |
| dfm->StartDiverting(foo_url, base::Seconds(15), |
| IncrementCounterCallback(StoppedReason::kImplicitIdle, |
| foo_url, &foo_counter, 1))); |
| |
| task_environment_.FastForwardBy(base::Seconds(10)); |
| EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| |
| std::unique_ptr<storage::FileStreamReader> reader = |
| dfm->CreateDivertedFileStreamReader(foo_url, 0); |
| |
| task_environment_.FastForwardBy(base::Seconds(10)); |
| EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| EXPECT_EQ(foo_counter, 0); |
| |
| task_environment_.FastForwardBy(base::Seconds(10)); |
| EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| EXPECT_EQ(foo_counter, 0); |
| |
| reader.reset(); |
| |
| task_environment_.FastForwardBy(base::Seconds(10)); |
| EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| EXPECT_EQ(foo_counter, 0); |
| |
| task_environment_.FastForwardBy(base::Seconds(10)); |
| EXPECT_FALSE(dfm->IsDiverting(foo_url)); |
| EXPECT_EQ(foo_counter, 1); |
| } |
| |
| TEST_F(DiversionFileManagerTest, Writes) { |
| ASSERT_TRUE( |
| ::content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
| |
| scoped_refptr<DiversionFileManager> dfm = |
| base::MakeRefCounted<DiversionFileManager>(); |
| storage::FileSystemURL foo_url = storage::FileSystemURL::CreateForTest( |
| GURL("filesystem:chrome-extension://abc/external/p/q/foo")); |
| |
| base::FilePath temp_dir; |
| ASSERT_TRUE(base::GetTempDir(&temp_dir)); |
| dfm->OverrideTmpfileDirForTesting(temp_dir); |
| |
| static constexpr auto on_implicit_idle = |
| [](StoppedReason stopped_reason, const storage::FileSystemURL& url, |
| base::ScopedFD scoped_fd, int64_t file_size, base::File::Error error) { |
| // We shouldn't get here. We should get to on_explicit_finish instead. |
| NOTREACHED_NORETURN(); |
| }; |
| ASSERT_EQ(StartDivertingResult::kOK, |
| dfm->StartDiverting(foo_url, base::Seconds(15), |
| base::BindOnce(on_implicit_idle))); |
| |
| dfm->TruncateDivertedFile(foo_url, 0, |
| base::BindOnce([](base::File::Error result) { |
| EXPECT_EQ(base::File::FILE_OK, result); |
| })); |
| |
| std::unique_ptr<storage::FileStreamWriter> writer = |
| dfm->CreateDivertedFileStreamWriter(foo_url, 0); |
| |
| SynchronousWrite(*writer, "hi "); |
| dfm->GetDivertedFileInfo( |
| foo_url, {storage::FileSystemOperation::GetMetadataField::kSize}, |
| base::BindOnce( |
| [](base::File::Error result, const base::File::Info& file_info) { |
| EXPECT_EQ(base::File::FILE_OK, result); |
| EXPECT_EQ(3, file_info.size); |
| })); |
| |
| task_environment_.FastForwardBy(base::Seconds(20)); |
| EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| |
| SynchronousWrite(*writer, "there."); |
| dfm->GetDivertedFileInfo( |
| foo_url, {storage::FileSystemOperation::GetMetadataField::kSize}, |
| base::BindOnce( |
| [](base::File::Error result, const base::File::Info& file_info) { |
| EXPECT_EQ(base::File::FILE_OK, result); |
| EXPECT_EQ(9, file_info.size); |
| })); |
| |
| task_environment_.FastForwardBy(base::Seconds(20)); |
| EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| |
| bool on_explicit_finish_called = false; |
| static constexpr auto on_explicit_finish = |
| [](bool* called, StoppedReason stopped_reason, |
| const storage::FileSystemURL& url, base::ScopedFD scoped_fd, |
| int64_t file_size, base::File::Error error) { |
| ASSERT_TRUE(scoped_fd.is_valid()); |
| EXPECT_EQ(file_size, 9u); |
| EXPECT_EQ(base::File::FILE_OK, error); |
| char buf[9] = {0}; |
| EXPECT_TRUE(base::ReadFromFD(scoped_fd.get(), buf)); |
| EXPECT_EQ(buf, base::span_from_cstring("hi there.")); |
| *called = true; |
| }; |
| ASSERT_EQ(FinishDivertingResult::kOK, |
| dfm->FinishDiverting(foo_url, |
| base::BindOnce(on_explicit_finish, |
| &on_explicit_finish_called))); |
| |
| task_environment_.FastForwardBy(base::Seconds(20)); |
| EXPECT_FALSE(dfm->IsDiverting(foo_url)); |
| EXPECT_FALSE(on_explicit_finish_called); |
| |
| writer.reset(); |
| EXPECT_FALSE(dfm->IsDiverting(foo_url)); |
| EXPECT_TRUE(on_explicit_finish_called); |
| } |
| |
| } // namespace ash |