Nigel Tao | 9a4532c | 2024-01-31 05:46:15 | [diff] [blame] | 1 | // Copyright 2024 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 "chrome/browser/ash/fileapi/diversion_file_manager.h" |
| 6 | |
| 7 | #include "base/files/file_util.h" |
| 8 | #include "content/public/browser/browser_thread.h" |
| 9 | #include "content/public/test/browser_task_environment.h" |
| 10 | #include "net/base/io_buffer.h" |
| 11 | #include "testing/gtest/include/gtest/gtest.h" |
| 12 | |
| 13 | namespace ash { |
| 14 | |
| 15 | using FinishDivertingResult = DiversionFileManager::FinishDivertingResult; |
| 16 | using StartDivertingResult = DiversionFileManager::StartDivertingResult; |
| 17 | using StoppedReason = DiversionFileManager::StoppedReason; |
| 18 | |
| 19 | namespace { |
| 20 | |
| 21 | DiversionFileManager::Callback IncrementCounterCallback( |
| 22 | StoppedReason expected_stopped_reason, |
| 23 | const storage::FileSystemURL& expected_url, |
| 24 | int* counter, |
| 25 | int delta) { |
| 26 | return base::BindOnce( |
| 27 | [](StoppedReason expected_stopped_reason, |
| 28 | const storage::FileSystemURL& expected_url, int* counter, int delta, |
| 29 | StoppedReason stopped_reason, const storage::FileSystemURL& url, |
| 30 | base::ScopedFD scoped_fd, int64_t file_size, base::File::Error error) { |
| 31 | EXPECT_EQ(expected_stopped_reason, stopped_reason); |
| 32 | EXPECT_EQ(expected_url, url); |
| 33 | *counter += delta; |
| 34 | }, |
| 35 | expected_stopped_reason, expected_url, counter, delta); |
| 36 | } |
| 37 | |
| 38 | } // namespace |
| 39 | |
| 40 | class DiversionFileManagerTest : public testing::Test { |
| 41 | public: |
| 42 | DiversionFileManagerTest() |
| 43 | : task_environment_(base::test::TaskEnvironment::MainThreadType::IO, |
| 44 | base::test::TaskEnvironment::TimeSource::MOCK_TIME) {} |
| 45 | |
| 46 | protected: |
| 47 | void SynchronousWrite(storage::FileStreamWriter& writer, std::string s) { |
| 48 | scoped_refptr<net::StringIOBuffer> buffer = |
| 49 | base::MakeRefCounted<net::StringIOBuffer>(s); |
| 50 | writer.Write( |
| 51 | buffer.get(), buffer->size(), |
| 52 | base::BindOnce([](base::RepeatingClosure quit_closure, |
| 53 | int byte_count_or_error_code) { quit_closure.Run(); }, |
| 54 | task_environment_.QuitClosure())); |
| 55 | task_environment_.RunUntilQuit(); |
| 56 | } |
| 57 | |
| 58 | content::BrowserTaskEnvironment task_environment_; |
| 59 | }; |
| 60 | |
| 61 | TEST_F(DiversionFileManagerTest, ImplicitExplicitFinish) { |
| 62 | ASSERT_TRUE( |
| 63 | ::content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
| 64 | |
| 65 | scoped_refptr<DiversionFileManager> dfm = |
| 66 | base::MakeRefCounted<DiversionFileManager>(); |
| 67 | storage::FileSystemURL bar_url = storage::FileSystemURL::CreateForTest( |
| 68 | GURL("filesystem:chrome-extension://abc/external/p/q/bar")); |
| 69 | storage::FileSystemURL foo_url = storage::FileSystemURL::CreateForTest( |
| 70 | GURL("filesystem:chrome-extension://abc/external/p/q/foo")); |
| 71 | EXPECT_FALSE(dfm->IsDiverting(bar_url)); |
| 72 | EXPECT_FALSE(dfm->IsDiverting(foo_url)); |
| 73 | |
| 74 | int bar_counter = 0; |
| 75 | int foo_counter = 0; |
| 76 | |
| 77 | ASSERT_EQ( |
| 78 | StartDivertingResult::kOK, |
| 79 | dfm->StartDiverting(bar_url, base::Seconds(28), |
| 80 | IncrementCounterCallback(StoppedReason::kImplicitIdle, |
| 81 | bar_url, &bar_counter, 1))); |
| 82 | ASSERT_EQ( |
| 83 | StartDivertingResult::kOK, |
| 84 | dfm->StartDiverting(foo_url, base::Seconds(32), |
| 85 | IncrementCounterCallback(StoppedReason::kImplicitIdle, |
| 86 | foo_url, &foo_counter, 10))); |
| 87 | EXPECT_TRUE(dfm->IsDiverting(bar_url)); |
| 88 | EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| 89 | |
| 90 | task_environment_.FastForwardBy(base::Seconds(30)); |
| 91 | EXPECT_FALSE(dfm->IsDiverting(bar_url)); |
| 92 | EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| 93 | |
| 94 | ASSERT_EQ(FinishDivertingResult::kWasNotDiverting, |
| 95 | dfm->FinishDiverting(bar_url, IncrementCounterCallback( |
| 96 | StoppedReason::kExplicitFinish, |
| 97 | bar_url, &bar_counter, 100))); |
| 98 | ASSERT_EQ(FinishDivertingResult::kOK, |
| 99 | dfm->FinishDiverting(foo_url, IncrementCounterCallback( |
| 100 | StoppedReason::kExplicitFinish, |
| 101 | foo_url, &foo_counter, 1000))); |
| 102 | EXPECT_FALSE(dfm->IsDiverting(bar_url)); |
| 103 | EXPECT_FALSE(dfm->IsDiverting(foo_url)); |
| 104 | |
| 105 | EXPECT_EQ(bar_counter, 1); |
| 106 | EXPECT_EQ(foo_counter, 1000); |
| 107 | } |
| 108 | |
| 109 | TEST_F(DiversionFileManagerTest, ReaderKeepsDiversionAlive) { |
| 110 | ASSERT_TRUE( |
| 111 | ::content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
| 112 | |
| 113 | scoped_refptr<DiversionFileManager> dfm = |
| 114 | base::MakeRefCounted<DiversionFileManager>(); |
| 115 | storage::FileSystemURL foo_url = storage::FileSystemURL::CreateForTest( |
| 116 | GURL("filesystem:chrome-extension://abc/external/p/q/foo")); |
| 117 | |
| 118 | int foo_counter = 0; |
| 119 | |
| 120 | ASSERT_EQ( |
| 121 | StartDivertingResult::kOK, |
| 122 | dfm->StartDiverting(foo_url, base::Seconds(15), |
| 123 | IncrementCounterCallback(StoppedReason::kImplicitIdle, |
| 124 | foo_url, &foo_counter, 1))); |
| 125 | |
| 126 | task_environment_.FastForwardBy(base::Seconds(10)); |
| 127 | EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| 128 | |
| 129 | std::unique_ptr<storage::FileStreamReader> reader = |
| 130 | dfm->CreateDivertedFileStreamReader(foo_url, 0); |
| 131 | |
| 132 | task_environment_.FastForwardBy(base::Seconds(10)); |
| 133 | EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| 134 | EXPECT_EQ(foo_counter, 0); |
| 135 | |
| 136 | task_environment_.FastForwardBy(base::Seconds(10)); |
| 137 | EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| 138 | EXPECT_EQ(foo_counter, 0); |
| 139 | |
| 140 | reader.reset(); |
| 141 | |
| 142 | task_environment_.FastForwardBy(base::Seconds(10)); |
| 143 | EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| 144 | EXPECT_EQ(foo_counter, 0); |
| 145 | |
| 146 | task_environment_.FastForwardBy(base::Seconds(10)); |
| 147 | EXPECT_FALSE(dfm->IsDiverting(foo_url)); |
| 148 | EXPECT_EQ(foo_counter, 1); |
| 149 | } |
| 150 | |
| 151 | TEST_F(DiversionFileManagerTest, Writes) { |
| 152 | ASSERT_TRUE( |
| 153 | ::content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
| 154 | |
| 155 | scoped_refptr<DiversionFileManager> dfm = |
| 156 | base::MakeRefCounted<DiversionFileManager>(); |
| 157 | storage::FileSystemURL foo_url = storage::FileSystemURL::CreateForTest( |
| 158 | GURL("filesystem:chrome-extension://abc/external/p/q/foo")); |
| 159 | |
| 160 | base::FilePath temp_dir; |
| 161 | ASSERT_TRUE(base::GetTempDir(&temp_dir)); |
| 162 | dfm->OverrideTmpfileDirForTesting(temp_dir); |
| 163 | |
| 164 | static constexpr auto on_implicit_idle = |
| 165 | [](StoppedReason stopped_reason, const storage::FileSystemURL& url, |
| 166 | base::ScopedFD scoped_fd, int64_t file_size, base::File::Error error) { |
| 167 | // We shouldn't get here. We should get to on_explicit_finish instead. |
Peter Boström | 01ab59a | 2024-08-15 02:39:49 | [diff] [blame] | 168 | NOTREACHED(); |
Nigel Tao | 9a4532c | 2024-01-31 05:46:15 | [diff] [blame] | 169 | }; |
| 170 | ASSERT_EQ(StartDivertingResult::kOK, |
| 171 | dfm->StartDiverting(foo_url, base::Seconds(15), |
| 172 | base::BindOnce(on_implicit_idle))); |
| 173 | |
| 174 | dfm->TruncateDivertedFile(foo_url, 0, |
| 175 | base::BindOnce([](base::File::Error result) { |
| 176 | EXPECT_EQ(base::File::FILE_OK, result); |
| 177 | })); |
| 178 | |
| 179 | std::unique_ptr<storage::FileStreamWriter> writer = |
| 180 | dfm->CreateDivertedFileStreamWriter(foo_url, 0); |
| 181 | |
| 182 | SynchronousWrite(*writer, "hi "); |
| 183 | dfm->GetDivertedFileInfo( |
| 184 | foo_url, {storage::FileSystemOperation::GetMetadataField::kSize}, |
| 185 | base::BindOnce( |
| 186 | [](base::File::Error result, const base::File::Info& file_info) { |
| 187 | EXPECT_EQ(base::File::FILE_OK, result); |
| 188 | EXPECT_EQ(3, file_info.size); |
| 189 | })); |
| 190 | |
| 191 | task_environment_.FastForwardBy(base::Seconds(20)); |
| 192 | EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| 193 | |
| 194 | SynchronousWrite(*writer, "there."); |
| 195 | dfm->GetDivertedFileInfo( |
| 196 | foo_url, {storage::FileSystemOperation::GetMetadataField::kSize}, |
| 197 | base::BindOnce( |
| 198 | [](base::File::Error result, const base::File::Info& file_info) { |
| 199 | EXPECT_EQ(base::File::FILE_OK, result); |
| 200 | EXPECT_EQ(9, file_info.size); |
| 201 | })); |
| 202 | |
| 203 | task_environment_.FastForwardBy(base::Seconds(20)); |
| 204 | EXPECT_TRUE(dfm->IsDiverting(foo_url)); |
| 205 | |
| 206 | bool on_explicit_finish_called = false; |
| 207 | static constexpr auto on_explicit_finish = |
| 208 | [](bool* called, StoppedReason stopped_reason, |
| 209 | const storage::FileSystemURL& url, base::ScopedFD scoped_fd, |
| 210 | int64_t file_size, base::File::Error error) { |
| 211 | ASSERT_TRUE(scoped_fd.is_valid()); |
danakj | 580718646 | 2024-06-06 20:10:37 | [diff] [blame] | 212 | EXPECT_EQ(file_size, 9u); |
Nigel Tao | 9a4532c | 2024-01-31 05:46:15 | [diff] [blame] | 213 | EXPECT_EQ(base::File::FILE_OK, error); |
| 214 | char buf[9] = {0}; |
danakj | 580718646 | 2024-06-06 20:10:37 | [diff] [blame] | 215 | EXPECT_TRUE(base::ReadFromFD(scoped_fd.get(), buf)); |
| 216 | EXPECT_EQ(buf, base::span_from_cstring("hi there.")); |
Nigel Tao | 9a4532c | 2024-01-31 05:46:15 | [diff] [blame] | 217 | *called = true; |
| 218 | }; |
| 219 | ASSERT_EQ(FinishDivertingResult::kOK, |
| 220 | dfm->FinishDiverting(foo_url, |
| 221 | base::BindOnce(on_explicit_finish, |
| 222 | &on_explicit_finish_called))); |
| 223 | |
| 224 | task_environment_.FastForwardBy(base::Seconds(20)); |
| 225 | EXPECT_FALSE(dfm->IsDiverting(foo_url)); |
| 226 | EXPECT_FALSE(on_explicit_finish_called); |
| 227 | |
| 228 | writer.reset(); |
| 229 | EXPECT_FALSE(dfm->IsDiverting(foo_url)); |
| 230 | EXPECT_TRUE(on_explicit_finish_called); |
| 231 | } |
| 232 | |
| 233 | } // namespace ash |