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.
5 #include "chrome/browser/chromeos/fileapi/external_file_url_request_job.h"
8 #include "base/files/file_util.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/run_loop.h"
12 #include "base/threading/thread.h"
13 #include "chrome/browser/chromeos/drive/drive_file_stream_reader.h"
14 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
15 #include "chrome/browser/chromeos/drive/drive_test_util.h"
16 #include "chrome/browser/chromeos/drive/fake_file_system.h"
17 #include "chrome/browser/chromeos/drive/file_system_util.h"
18 #include "chrome/browser/prefs/browser_prefs.h"
19 #include "chrome/browser/prefs/pref_service_syncable.h"
20 #include "chrome/browser/profiles/profile_manager.h"
21 #include "chrome/test/base/testing_browser_process.h"
22 #include "chrome/test/base/testing_profile.h"
23 #include "chrome/test/base/testing_profile_manager.h"
24 #include "components/drive/service/fake_drive_service.h"
25 #include "components/drive/service/test_util.h"
26 #include "components/pref_registry/pref_registry_syncable.h"
27 #include "components/pref_registry/testing_pref_service_syncable.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/common/url_constants.h"
30 #include "content/public/test/test_browser_thread_bundle.h"
31 #include "content/public/test/test_file_system_options.h"
32 #include "google_apis/drive/test_util.h"
33 #include "net/base/request_priority.h"
34 #include "net/base/test_completion_callback.h"
35 #include "net/http/http_byte_range.h"
36 #include "net/url_request/redirect_info.h"
37 #include "net/url_request/url_request.h"
38 #include "net/url_request/url_request_context.h"
39 #include "net/url_request/url_request_test_util.h"
40 #include "storage/browser/fileapi/external_mount_points.h"
41 #include "storage/browser/fileapi/file_system_context.h"
42 #include "testing/gtest/include/gtest/gtest.h"
48 // A simple URLRequestJobFactory implementation to create
49 // ExternalFileURLRequestJob.
50 class TestURLRequestJobFactory
: public net::URLRequestJobFactory
{
52 explicit TestURLRequestJobFactory(void* profile_id
)
53 : profile_id_(profile_id
) {}
55 ~TestURLRequestJobFactory() override
{}
57 // net::URLRequestJobFactory override:
58 net::URLRequestJob
* MaybeCreateJobWithProtocolHandler(
59 const std::string
& scheme
,
60 net::URLRequest
* request
,
61 net::NetworkDelegate
* network_delegate
) const override
{
62 return new ExternalFileURLRequestJob(
63 profile_id_
, request
, network_delegate
);
66 net::URLRequestJob
* MaybeInterceptRedirect(
67 net::URLRequest
* request
,
68 net::NetworkDelegate
* network_delegate
,
69 const GURL
& location
) const override
{
73 net::URLRequestJob
* MaybeInterceptResponse(
74 net::URLRequest
* request
,
75 net::NetworkDelegate
* network_delegate
) const override
{
79 bool IsHandledProtocol(const std::string
& scheme
) const override
{
80 return scheme
== content::kExternalFileScheme
;
83 bool IsHandledURL(const GURL
& url
) const override
{
84 return url
.is_valid() && IsHandledProtocol(url
.scheme());
87 bool IsSafeRedirectTarget(const GURL
& location
) const override
{
92 void* const profile_id_
;
93 DISALLOW_COPY_AND_ASSIGN(TestURLRequestJobFactory
);
96 class TestDelegate
: public net::TestDelegate
{
100 const GURL
& redirect_url() const { return redirect_url_
; }
102 // net::TestDelegate override.
103 void OnReceivedRedirect(net::URLRequest
* request
,
104 const net::RedirectInfo
& redirect_info
,
105 bool* defer_redirect
) override
{
106 redirect_url_
= redirect_info
.new_url
;
107 net::TestDelegate::OnReceivedRedirect(
108 request
, redirect_info
, defer_redirect
);
114 DISALLOW_COPY_AND_ASSIGN(TestDelegate
);
119 class ExternalFileURLRequestJobTest
: public testing::Test
{
121 ExternalFileURLRequestJobTest()
122 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
),
123 integration_service_factory_callback_(base::Bind(
124 &ExternalFileURLRequestJobTest::CreateDriveIntegrationService
,
125 base::Unretained(this))),
126 fake_file_system_(NULL
) {}
128 ~ExternalFileURLRequestJobTest() override
{}
130 void SetUp() override
{
131 // Create a testing profile.
132 profile_manager_
.reset(
133 new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
134 ASSERT_TRUE(profile_manager_
->SetUp());
135 Profile
* const profile
=
136 profile_manager_
->CreateTestingProfile("test-user");
138 // Create the drive integration service for the profile.
139 integration_service_factory_scope_
.reset(
140 new drive::DriveIntegrationServiceFactory::ScopedFactoryForTest(
141 &integration_service_factory_callback_
));
142 drive::DriveIntegrationServiceFactory::GetForProfile(profile
);
144 // Create the URL request job factory.
145 test_network_delegate_
.reset(new net::TestNetworkDelegate
);
146 test_url_request_job_factory_
.reset(new TestURLRequestJobFactory(profile
));
147 url_request_context_
.reset(new net::URLRequestContext());
148 url_request_context_
->set_job_factory(test_url_request_job_factory_
.get());
149 url_request_context_
->set_network_delegate(test_network_delegate_
.get());
150 test_delegate_
.reset(new TestDelegate
);
153 void TearDown() override
{ profile_manager_
.reset(); }
155 bool ReadDriveFileSync(const base::FilePath
& file_path
,
156 std::string
* out_content
) {
157 scoped_ptr
<base::Thread
> worker_thread(
158 new base::Thread("ReadDriveFileSync"));
159 if (!worker_thread
->Start())
162 scoped_ptr
<drive::DriveFileStreamReader
> reader(
163 new drive::DriveFileStreamReader(
164 base::Bind(&ExternalFileURLRequestJobTest::GetFileSystem
,
165 base::Unretained(this)),
166 worker_thread
->task_runner().get()));
167 int error
= net::ERR_FAILED
;
168 scoped_ptr
<drive::ResourceEntry
> entry
;
170 base::RunLoop run_loop
;
171 reader
->Initialize(file_path
,
172 net::HttpByteRange(),
173 google_apis::test_util::CreateQuitCallback(
175 google_apis::test_util::CreateCopyResultCallback(
179 if (error
!= net::OK
|| !entry
)
182 // Read data from the reader.
184 if (drive::test_util::ReadAllData(reader
.get(), &content
) != net::OK
)
187 if (static_cast<size_t>(entry
->file_info().size()) != content
.size())
190 *out_content
= content
;
194 scoped_ptr
<net::URLRequestContext
> url_request_context_
;
195 scoped_ptr
<TestDelegate
> test_delegate_
;
198 // Create the drive integration service for the |profile|
199 drive::DriveIntegrationService
* CreateDriveIntegrationService(
201 drive::FakeDriveService
* const drive_service
= new drive::FakeDriveService
;
202 if (!drive::test_util::SetUpTestEntries(drive_service
))
205 const std::string
& drive_mount_name
=
206 drive::util::GetDriveMountPointPath(profile
).BaseName().AsUTF8Unsafe();
207 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
209 storage::kFileSystemTypeDrive
,
210 storage::FileSystemMountOption(),
211 drive::util::GetDriveMountPointPath(profile
));
212 DCHECK(!fake_file_system_
);
213 fake_file_system_
= new drive::test_util::FakeFileSystem(drive_service
);
214 if (!drive_cache_dir_
.CreateUniqueTempDir())
216 return new drive::DriveIntegrationService(profile
,
220 drive_cache_dir_
.path(),
224 drive::FileSystemInterface
* GetFileSystem() { return fake_file_system_
; }
226 content::TestBrowserThreadBundle thread_bundle_
;
227 drive::DriveIntegrationServiceFactory::FactoryCallback
228 integration_service_factory_callback_
;
229 scoped_ptr
<drive::DriveIntegrationServiceFactory::ScopedFactoryForTest
>
230 integration_service_factory_scope_
;
231 scoped_ptr
<drive::DriveIntegrationService
> integration_service_
;
232 drive::test_util::FakeFileSystem
* fake_file_system_
;
234 scoped_ptr
<net::TestNetworkDelegate
> test_network_delegate_
;
235 scoped_ptr
<TestURLRequestJobFactory
> test_url_request_job_factory_
;
237 scoped_ptr
<TestingProfileManager
> profile_manager_
;
238 base::ScopedTempDir drive_cache_dir_
;
239 scoped_refptr
<storage::FileSystemContext
> file_system_context_
;
242 TEST_F(ExternalFileURLRequestJobTest
, NonGetMethod
) {
243 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
244 GURL("externalfile:drive-test-user-hash/root/File 1.txt"),
245 net::DEFAULT_PRIORITY
, test_delegate_
.get()));
246 request
->set_method("POST"); // Set non "GET" method.
249 base::RunLoop().Run();
251 EXPECT_EQ(net::URLRequestStatus::FAILED
, request
->status().status());
252 EXPECT_EQ(net::ERR_METHOD_NOT_SUPPORTED
, request
->status().error());
255 TEST_F(ExternalFileURLRequestJobTest
, RegularFile
) {
256 const GURL
kTestUrl("externalfile:drive-test-user-hash/root/File 1.txt");
257 const base::FilePath
kTestFilePath("drive/root/File 1.txt");
259 // For the first time, the file should be fetched from the server.
261 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
262 kTestUrl
, net::DEFAULT_PRIORITY
, test_delegate_
.get()));
265 base::RunLoop().Run();
267 EXPECT_EQ(net::URLRequestStatus::SUCCESS
, request
->status().status());
268 // It looks weird, but the mime type for the "File 1.txt" is "audio/mpeg"
270 std::string mime_type
;
271 request
->GetMimeType(&mime_type
);
272 EXPECT_EQ("audio/mpeg", mime_type
);
274 // Reading file must be done after |request| runs, otherwise
275 // it'll create a local cache file, and we cannot test correctly.
276 std::string expected_data
;
277 ASSERT_TRUE(ReadDriveFileSync(kTestFilePath
, &expected_data
));
278 EXPECT_EQ(expected_data
, test_delegate_
->data_received());
281 // For the second time, the locally cached file should be used.
282 // The caching emulation is done by FakeFileSystem.
284 test_delegate_
.reset(new TestDelegate
);
285 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
286 GURL("externalfile:drive-test-user-hash/root/File 1.txt"),
287 net::DEFAULT_PRIORITY
, test_delegate_
.get()));
290 base::RunLoop().Run();
292 EXPECT_EQ(net::URLRequestStatus::SUCCESS
, request
->status().status());
293 std::string mime_type
;
294 request
->GetMimeType(&mime_type
);
295 EXPECT_EQ("audio/mpeg", mime_type
);
297 std::string expected_data
;
298 ASSERT_TRUE(ReadDriveFileSync(kTestFilePath
, &expected_data
));
299 EXPECT_EQ(expected_data
, test_delegate_
->data_received());
303 TEST_F(ExternalFileURLRequestJobTest
, HostedDocument
) {
305 test_delegate_
->set_quit_on_redirect(true);
306 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
308 "externalfile:drive-test-user-hash/root/Document 1 "
309 "excludeDir-test.gdoc"),
310 net::DEFAULT_PRIORITY
, test_delegate_
.get()));
313 base::RunLoop().Run();
315 EXPECT_EQ(net::URLRequestStatus::SUCCESS
, request
->status().status());
316 // Make sure that a hosted document triggers redirection.
317 EXPECT_TRUE(request
->is_redirecting());
318 EXPECT_TRUE(test_delegate_
->redirect_url().is_valid());
321 TEST_F(ExternalFileURLRequestJobTest
, RootDirectory
) {
322 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
323 GURL("externalfile:drive-test-user-hash/root"), net::DEFAULT_PRIORITY
,
324 test_delegate_
.get()));
327 base::RunLoop().Run();
329 EXPECT_EQ(net::URLRequestStatus::FAILED
, request
->status().status());
330 EXPECT_EQ(net::ERR_FAILED
, request
->status().error());
333 TEST_F(ExternalFileURLRequestJobTest
, Directory
) {
334 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
335 GURL("externalfile:drive-test-user-hash/root/Directory 1"),
336 net::DEFAULT_PRIORITY
, test_delegate_
.get()));
339 base::RunLoop().Run();
341 EXPECT_EQ(net::URLRequestStatus::FAILED
, request
->status().status());
342 EXPECT_EQ(net::ERR_FAILED
, request
->status().error());
345 TEST_F(ExternalFileURLRequestJobTest
, NonExistingFile
) {
346 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
347 GURL("externalfile:drive-test-user-hash/root/non-existing-file.txt"),
348 net::DEFAULT_PRIORITY
, test_delegate_
.get()));
351 base::RunLoop().Run();
353 EXPECT_EQ(net::URLRequestStatus::FAILED
, request
->status().status());
354 EXPECT_EQ(net::ERR_FILE_NOT_FOUND
, request
->status().error());
357 TEST_F(ExternalFileURLRequestJobTest
, WrongFormat
) {
358 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
359 GURL("externalfile:"), net::DEFAULT_PRIORITY
, test_delegate_
.get()));
362 base::RunLoop().Run();
364 EXPECT_EQ(net::URLRequestStatus::FAILED
, request
->status().status());
365 EXPECT_EQ(net::ERR_INVALID_URL
, request
->status().error());
368 TEST_F(ExternalFileURLRequestJobTest
, Cancel
) {
369 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
370 GURL("externalfile:drive-test-user-hash/root/File 1.txt"),
371 net::DEFAULT_PRIORITY
, test_delegate_
.get()));
373 // Start the request, and cancel it immediately after it.
377 base::RunLoop().Run();
379 EXPECT_EQ(net::URLRequestStatus::CANCELED
, request
->status().status());
382 TEST_F(ExternalFileURLRequestJobTest
, RangeHeader
) {
383 const GURL
kTestUrl("externalfile:drive-test-user-hash/root/File 1.txt");
384 const base::FilePath
kTestFilePath("drive/root/File 1.txt");
386 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
387 kTestUrl
, net::DEFAULT_PRIORITY
, test_delegate_
.get()));
390 request
->SetExtraRequestHeaderByName(
391 "Range", "bytes=3-5", false /* overwrite */);
394 base::RunLoop().Run();
396 EXPECT_EQ(net::URLRequestStatus::SUCCESS
, request
->status().status());
398 // Reading file must be done after |request| runs, otherwise
399 // it'll create a local cache file, and we cannot test correctly.
400 std::string expected_data
;
401 ASSERT_TRUE(ReadDriveFileSync(kTestFilePath
, &expected_data
));
402 EXPECT_EQ(expected_data
.substr(3, 3), test_delegate_
->data_received());
405 TEST_F(ExternalFileURLRequestJobTest
, WrongRangeHeader
) {
406 const GURL
kTestUrl("externalfile:drive-test-user-hash/root/File 1.txt");
408 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
409 kTestUrl
, net::DEFAULT_PRIORITY
, test_delegate_
.get()));
412 request
->SetExtraRequestHeaderByName(
413 "Range", "Wrong Range Header Value", false /* overwrite */);
416 base::RunLoop().Run();
418 EXPECT_EQ(net::URLRequestStatus::FAILED
, request
->status().status());
419 EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE
, request
->status().error());
422 } // namespace chromeos