1 // Copyright 2013 The Chromium Authors. All rights reserved.
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 <utility>
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/macros.h"
11 #include "base/memory/scoped_refptr.h"
12 #include "base/run_loop.h"
13 #include "base/task/post_task.h"
14 #include "base/task/task_traits.h"
15 #include "base/task/thread_pool.h"
16 #include "base/task/thread_pool/thread_pool_instance.h"
17 #include "base/test/task_environment.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "base/threading/thread_task_runner_handle.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/test/browser_task_environment.h"
22 #include "storage/browser/file_system/external_mount_points.h"
23 #include "storage/browser/file_system/file_system_backend.h"
24 #include "storage/browser/file_system/file_system_context.h"
25 #include "storage/browser/file_system/file_system_operation_runner.h"
26 #include "storage/browser/test/mock_special_storage_policy.h"
27 #include "storage/browser/test/test_file_system_context.h"
28 #include "storage/browser/test/test_file_system_options.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "url/gurl.h"
31 #include "url/origin.h"
32
33 using storage::FileSystemContext;
34 using storage::FileSystemOperationRunner;
35 using storage::FileSystemType;
36 using storage::FileSystemURL;
37
38 namespace content {
39
40 namespace {
41
GetStatus(bool * done,base::File::Error * status_out,base::File::Error status)42 void GetStatus(bool* done,
43 base::File::Error* status_out,
44 base::File::Error status) {
45 ASSERT_FALSE(*done);
46 *done = true;
47 *status_out = status;
48 }
49
GetCancelStatus(bool * operation_done,bool * cancel_done,base::File::Error * status_out,base::File::Error status)50 void GetCancelStatus(bool* operation_done,
51 bool* cancel_done,
52 base::File::Error* status_out,
53 base::File::Error status) {
54 // Cancel callback must be always called after the operation's callback.
55 ASSERT_TRUE(*operation_done);
56 ASSERT_FALSE(*cancel_done);
57 *cancel_done = true;
58 *status_out = status;
59 }
60
DidOpenFile(base::File file,base::OnceClosure on_close_callback)61 void DidOpenFile(base::File file, base::OnceClosure on_close_callback) {}
62
63 } // namespace
64
65 class FileSystemOperationRunnerTest : public testing::Test {
66 protected:
FileSystemOperationRunnerTest()67 FileSystemOperationRunnerTest() {}
~FileSystemOperationRunnerTest()68 ~FileSystemOperationRunnerTest() override {}
69
SetUp()70 void SetUp() override {
71 ASSERT_TRUE(base_.CreateUniqueTempDir());
72 base::FilePath base_dir = base_.GetPath();
73 file_system_context_ =
74 storage::CreateFileSystemContextForTesting(nullptr, base_dir);
75 }
76
TearDown()77 void TearDown() override {
78 file_system_context_ = nullptr;
79 base::RunLoop().RunUntilIdle();
80 }
81
URL(const std::string & path)82 FileSystemURL URL(const std::string& path) {
83 return file_system_context_->CreateCrackedFileSystemURL(
84 url::Origin::Create(GURL("http://example.com")),
85 storage::kFileSystemTypeTemporary,
86 base::FilePath::FromUTF8Unsafe(path));
87 }
88
operation_runner()89 FileSystemOperationRunner* operation_runner() {
90 return file_system_context_->operation_runner();
91 }
92
93 private:
94 base::ScopedTempDir base_;
95 base::test::SingleThreadTaskEnvironment task_environment_;
96 scoped_refptr<FileSystemContext> file_system_context_;
97
98 DISALLOW_COPY_AND_ASSIGN(FileSystemOperationRunnerTest);
99 };
100
TEST_F(FileSystemOperationRunnerTest,NotFoundError)101 TEST_F(FileSystemOperationRunnerTest, NotFoundError) {
102 bool done = false;
103 base::File::Error status = base::File::FILE_ERROR_FAILED;
104
105 // Regular NOT_FOUND error, which is called asynchronously.
106 operation_runner()->Truncate(URL("foo"), 0,
107 base::BindOnce(&GetStatus, &done, &status));
108 ASSERT_FALSE(done);
109 base::RunLoop().RunUntilIdle();
110 ASSERT_TRUE(done);
111 ASSERT_EQ(base::File::FILE_ERROR_NOT_FOUND, status);
112 }
113
TEST_F(FileSystemOperationRunnerTest,InvalidURLError)114 TEST_F(FileSystemOperationRunnerTest, InvalidURLError) {
115 bool done = false;
116 base::File::Error status = base::File::FILE_ERROR_FAILED;
117
118 // Invalid URL error, which calls DidFinish synchronously.
119 operation_runner()->Truncate(FileSystemURL(), 0,
120 base::BindOnce(&GetStatus, &done, &status));
121 // The error call back shouldn't be fired synchronously.
122 ASSERT_FALSE(done);
123
124 base::RunLoop().RunUntilIdle();
125 ASSERT_TRUE(done);
126 ASSERT_EQ(base::File::FILE_ERROR_INVALID_URL, status);
127 }
128
TEST_F(FileSystemOperationRunnerTest,NotFoundErrorAndCancel)129 TEST_F(FileSystemOperationRunnerTest, NotFoundErrorAndCancel) {
130 bool done = false;
131 bool cancel_done = false;
132 base::File::Error status = base::File::FILE_ERROR_FAILED;
133 base::File::Error cancel_status = base::File::FILE_ERROR_FAILED;
134
135 // Call Truncate with non-existent URL, and try to cancel it immediately
136 // after that (before its callback is fired).
137 FileSystemOperationRunner::OperationID id = operation_runner()->Truncate(
138 URL("foo"), 0, base::BindOnce(&GetStatus, &done, &status));
139 operation_runner()->Cancel(id, base::BindOnce(&GetCancelStatus, &done,
140 &cancel_done, &cancel_status));
141
142 ASSERT_FALSE(done);
143 ASSERT_FALSE(cancel_done);
144 base::RunLoop().RunUntilIdle();
145
146 ASSERT_TRUE(done);
147 ASSERT_TRUE(cancel_done);
148 ASSERT_EQ(base::File::FILE_ERROR_NOT_FOUND, status);
149 ASSERT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, cancel_status);
150 }
151
TEST_F(FileSystemOperationRunnerTest,InvalidURLErrorAndCancel)152 TEST_F(FileSystemOperationRunnerTest, InvalidURLErrorAndCancel) {
153 bool done = false;
154 bool cancel_done = false;
155 base::File::Error status = base::File::FILE_ERROR_FAILED;
156 base::File::Error cancel_status = base::File::FILE_ERROR_FAILED;
157
158 // Call Truncate with invalid URL, and try to cancel it immediately
159 // after that (before its callback is fired).
160 FileSystemOperationRunner::OperationID id = operation_runner()->Truncate(
161 FileSystemURL(), 0, base::BindOnce(&GetStatus, &done, &status));
162 operation_runner()->Cancel(id, base::BindOnce(&GetCancelStatus, &done,
163 &cancel_done, &cancel_status));
164
165 ASSERT_FALSE(done);
166 ASSERT_FALSE(cancel_done);
167 base::RunLoop().RunUntilIdle();
168
169 ASSERT_TRUE(done);
170 ASSERT_TRUE(cancel_done);
171 ASSERT_EQ(base::File::FILE_ERROR_INVALID_URL, status);
172 ASSERT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, cancel_status);
173 }
174
TEST_F(FileSystemOperationRunnerTest,CancelWithInvalidId)175 TEST_F(FileSystemOperationRunnerTest, CancelWithInvalidId) {
176 const FileSystemOperationRunner::OperationID kInvalidId = -1;
177 bool done = true; // The operation is not running.
178 bool cancel_done = false;
179 base::File::Error cancel_status = base::File::FILE_ERROR_FAILED;
180 operation_runner()->Cancel(
181 kInvalidId,
182 base::BindOnce(&GetCancelStatus, &done, &cancel_done, &cancel_status));
183
184 ASSERT_TRUE(cancel_done);
185 ASSERT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, cancel_status);
186 }
187
188 class MultiThreadFileSystemOperationRunnerTest : public testing::Test {
189 public:
MultiThreadFileSystemOperationRunnerTest()190 MultiThreadFileSystemOperationRunnerTest()
191 : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP) {}
192
SetUp()193 void SetUp() override {
194 ASSERT_TRUE(base_.CreateUniqueTempDir());
195
196 base::FilePath base_dir = base_.GetPath();
197 file_system_context_ = base::MakeRefCounted<FileSystemContext>(
198 base::ThreadTaskRunnerHandle::Get().get(),
199 base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}).get(),
200 storage::ExternalMountPoints::CreateRefCounted().get(),
201 base::MakeRefCounted<storage::MockSpecialStoragePolicy>().get(),
202 nullptr, std::vector<std::unique_ptr<storage::FileSystemBackend>>(),
203 std::vector<storage::URLRequestAutoMountHandler>(), base_dir,
204 storage::CreateAllowFileAccessOptions());
205
206 // Disallow IO on the main loop.
207 base::ThreadRestrictions::SetIOAllowed(false);
208 }
209
TearDown()210 void TearDown() override {
211 base::ThreadRestrictions::SetIOAllowed(true);
212 file_system_context_ = nullptr;
213 }
214
URL(const std::string & path)215 FileSystemURL URL(const std::string& path) {
216 return file_system_context_->CreateCrackedFileSystemURL(
217 url::Origin::Create(GURL("http://example.com")),
218 storage::kFileSystemTypeTemporary,
219 base::FilePath::FromUTF8Unsafe(path));
220 }
221
operation_runner()222 FileSystemOperationRunner* operation_runner() {
223 return file_system_context_->operation_runner();
224 }
225
226 private:
227 base::ScopedTempDir base_;
228 content::BrowserTaskEnvironment task_environment_;
229 scoped_refptr<FileSystemContext> file_system_context_;
230
231 DISALLOW_COPY_AND_ASSIGN(MultiThreadFileSystemOperationRunnerTest);
232 };
233
TEST_F(MultiThreadFileSystemOperationRunnerTest,OpenAndShutdown)234 TEST_F(MultiThreadFileSystemOperationRunnerTest, OpenAndShutdown) {
235 // Call OpenFile and immediately shutdown the runner.
236 operation_runner()->OpenFile(
237 URL("foo"), base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE,
238 base::BindOnce(&DidOpenFile));
239 operation_runner()->Shutdown();
240
241 // Wait until the task posted on the blocking thread is done.
242 base::ThreadPoolInstance::Get()->FlushForTesting();
243 // This should finish without thread assertion failure on debug build.
244 }
245
246 } // namespace content
247