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/fake_file_system.h"
16 #include "chrome/browser/chromeos/drive/file_system_util.h"
17 #include "chrome/browser/chromeos/drive/test_util.h"
18 #include "chrome/browser/drive/fake_drive_service.h"
19 #include "chrome/browser/drive/test_util.h"
20 #include "chrome/browser/prefs/browser_prefs.h"
21 #include "chrome/browser/prefs/pref_service_syncable.h"
22 #include "chrome/browser/profiles/profile_manager.h"
23 #include "chrome/common/url_constants.h"
24 #include "chrome/test/base/testing_browser_process.h"
25 #include "chrome/test/base/testing_profile.h"
26 #include "chrome/test/base/testing_profile_manager.h"
27 #include "components/pref_registry/pref_registry_syncable.h"
28 #include "components/pref_registry/testing_pref_service_syncable.h"
29 #include "content/public/browser/browser_thread.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 virtual ~TestURLRequestJobFactory() {}
57 // net::URLRequestJobFactory override:
58 virtual 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 virtual bool IsHandledProtocol(const std::string
& scheme
) const OVERRIDE
{
67 return scheme
== chrome::kExternalFileScheme
;
70 virtual bool IsHandledURL(const GURL
& url
) const OVERRIDE
{
71 return url
.is_valid() && IsHandledProtocol(url
.scheme());
74 virtual bool IsSafeRedirectTarget(const GURL
& location
) const OVERRIDE
{
79 void* const profile_id_
;
80 DISALLOW_COPY_AND_ASSIGN(TestURLRequestJobFactory
);
83 class TestDelegate
: public net::TestDelegate
{
87 const GURL
& redirect_url() const { return redirect_url_
; }
89 // net::TestDelegate override.
90 virtual void OnReceivedRedirect(net::URLRequest
* request
,
91 const net::RedirectInfo
& redirect_info
,
92 bool* defer_redirect
) OVERRIDE
{
93 redirect_url_
= redirect_info
.new_url
;
94 net::TestDelegate::OnReceivedRedirect(
95 request
, redirect_info
, defer_redirect
);
101 DISALLOW_COPY_AND_ASSIGN(TestDelegate
);
106 class ExternalFileURLRequestJobTest
: public testing::Test
{
108 ExternalFileURLRequestJobTest()
109 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
),
110 integration_service_factory_callback_(base::Bind(
111 &ExternalFileURLRequestJobTest::CreateDriveIntegrationService
,
112 base::Unretained(this))),
113 fake_file_system_(NULL
) {}
115 virtual ~ExternalFileURLRequestJobTest() {}
117 virtual void SetUp() OVERRIDE
{
118 // Create a testing profile.
119 profile_manager_
.reset(
120 new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
121 ASSERT_TRUE(profile_manager_
->SetUp());
122 Profile
* const profile
=
123 profile_manager_
->CreateTestingProfile("test-user");
125 // Create the drive integration service for the profile.
126 integration_service_factory_scope_
.reset(
127 new drive::DriveIntegrationServiceFactory::ScopedFactoryForTest(
128 &integration_service_factory_callback_
));
129 drive::DriveIntegrationServiceFactory::GetForProfile(profile
);
131 // Create the URL request job factory.
132 test_network_delegate_
.reset(new net::TestNetworkDelegate
);
133 test_url_request_job_factory_
.reset(new TestURLRequestJobFactory(profile
));
134 url_request_context_
.reset(new net::URLRequestContext());
135 url_request_context_
->set_job_factory(test_url_request_job_factory_
.get());
136 url_request_context_
->set_network_delegate(test_network_delegate_
.get());
137 test_delegate_
.reset(new TestDelegate
);
140 virtual void TearDown() { profile_manager_
.reset(); }
142 bool ReadDriveFileSync(const base::FilePath
& file_path
,
143 std::string
* out_content
) {
144 scoped_ptr
<base::Thread
> worker_thread(
145 new base::Thread("ReadDriveFileSync"));
146 if (!worker_thread
->Start())
149 scoped_ptr
<drive::DriveFileStreamReader
> reader(
150 new drive::DriveFileStreamReader(
151 base::Bind(&ExternalFileURLRequestJobTest::GetFileSystem
,
152 base::Unretained(this)),
153 worker_thread
->message_loop_proxy().get()));
154 int error
= net::ERR_FAILED
;
155 scoped_ptr
<drive::ResourceEntry
> entry
;
157 base::RunLoop run_loop
;
158 reader
->Initialize(file_path
,
159 net::HttpByteRange(),
160 google_apis::test_util::CreateQuitCallback(
162 google_apis::test_util::CreateCopyResultCallback(
166 if (error
!= net::OK
|| !entry
)
169 // Read data from the reader.
171 if (drive::test_util::ReadAllData(reader
.get(), &content
) != net::OK
)
174 if (static_cast<size_t>(entry
->file_info().size()) != content
.size())
177 *out_content
= content
;
181 scoped_ptr
<net::URLRequestContext
> url_request_context_
;
182 scoped_ptr
<TestDelegate
> test_delegate_
;
185 // Create the drive integration service for the |profile|
186 drive::DriveIntegrationService
* CreateDriveIntegrationService(
188 drive::FakeDriveService
* const drive_service
= new drive::FakeDriveService
;
189 if (!drive::test_util::SetUpTestEntries(drive_service
))
192 const std::string
& drive_mount_name
=
193 drive::util::GetDriveMountPointPath(profile
).BaseName().AsUTF8Unsafe();
194 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
196 storage::kFileSystemTypeDrive
,
197 storage::FileSystemMountOption(),
198 drive::util::GetDriveMountPointPath(profile
));
199 DCHECK(!fake_file_system_
);
200 fake_file_system_
= new drive::test_util::FakeFileSystem(drive_service
);
201 if (!drive_cache_dir_
.CreateUniqueTempDir())
203 return new drive::DriveIntegrationService(profile
,
207 drive_cache_dir_
.path(),
211 drive::FileSystemInterface
* GetFileSystem() { return fake_file_system_
; }
213 content::TestBrowserThreadBundle thread_bundle_
;
214 drive::DriveIntegrationServiceFactory::FactoryCallback
215 integration_service_factory_callback_
;
216 scoped_ptr
<drive::DriveIntegrationServiceFactory::ScopedFactoryForTest
>
217 integration_service_factory_scope_
;
218 scoped_ptr
<drive::DriveIntegrationService
> integration_service_
;
219 drive::test_util::FakeFileSystem
* fake_file_system_
;
221 scoped_ptr
<net::TestNetworkDelegate
> test_network_delegate_
;
222 scoped_ptr
<TestURLRequestJobFactory
> test_url_request_job_factory_
;
224 scoped_ptr
<TestingProfileManager
> profile_manager_
;
225 base::ScopedTempDir drive_cache_dir_
;
226 scoped_refptr
<storage::FileSystemContext
> file_system_context_
;
229 TEST_F(ExternalFileURLRequestJobTest
, NonGetMethod
) {
230 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
231 GURL("externalfile:drive/root/File 1.txt"),
232 net::DEFAULT_PRIORITY
,
233 test_delegate_
.get(),
235 request
->set_method("POST"); // Set non "GET" method.
238 base::RunLoop().Run();
240 EXPECT_EQ(net::URLRequestStatus::FAILED
, request
->status().status());
241 EXPECT_EQ(net::ERR_METHOD_NOT_SUPPORTED
, request
->status().error());
244 TEST_F(ExternalFileURLRequestJobTest
, RegularFile
) {
245 const GURL
kTestUrl("externalfile:drive/root/File 1.txt");
246 const base::FilePath
kTestFilePath("drive/root/File 1.txt");
248 // For the first time, the file should be fetched from the server.
250 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
251 kTestUrl
, net::DEFAULT_PRIORITY
, test_delegate_
.get(), NULL
));
254 base::RunLoop().Run();
256 EXPECT_EQ(net::URLRequestStatus::SUCCESS
, request
->status().status());
257 // It looks weird, but the mime type for the "File 1.txt" is "audio/mpeg"
259 std::string mime_type
;
260 request
->GetMimeType(&mime_type
);
261 EXPECT_EQ("audio/mpeg", mime_type
);
263 // Reading file must be done after |request| runs, otherwise
264 // it'll create a local cache file, and we cannot test correctly.
265 std::string expected_data
;
266 ASSERT_TRUE(ReadDriveFileSync(kTestFilePath
, &expected_data
));
267 EXPECT_EQ(expected_data
, test_delegate_
->data_received());
270 // For the second time, the locally cached file should be used.
271 // The caching emulation is done by FakeFileSystem.
273 test_delegate_
.reset(new TestDelegate
);
274 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
275 GURL("externalfile:drive/root/File 1.txt"),
276 net::DEFAULT_PRIORITY
,
277 test_delegate_
.get(),
281 base::RunLoop().Run();
283 EXPECT_EQ(net::URLRequestStatus::SUCCESS
, request
->status().status());
284 std::string mime_type
;
285 request
->GetMimeType(&mime_type
);
286 EXPECT_EQ("audio/mpeg", mime_type
);
288 std::string expected_data
;
289 ASSERT_TRUE(ReadDriveFileSync(kTestFilePath
, &expected_data
));
290 EXPECT_EQ(expected_data
, test_delegate_
->data_received());
294 TEST_F(ExternalFileURLRequestJobTest
, HostedDocument
) {
296 test_delegate_
->set_quit_on_redirect(true);
297 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
298 GURL("externalfile:drive/root/Document 1 excludeDir-test.gdoc"),
299 net::DEFAULT_PRIORITY
,
300 test_delegate_
.get(),
304 base::RunLoop().Run();
306 EXPECT_EQ(net::URLRequestStatus::SUCCESS
, request
->status().status());
307 // Make sure that a hosted document triggers redirection.
308 EXPECT_TRUE(request
->is_redirecting());
309 EXPECT_TRUE(test_delegate_
->redirect_url().is_valid());
312 TEST_F(ExternalFileURLRequestJobTest
, RootDirectory
) {
313 scoped_ptr
<net::URLRequest
> request(
314 url_request_context_
->CreateRequest(GURL("externalfile:drive/root"),
315 net::DEFAULT_PRIORITY
,
316 test_delegate_
.get(),
320 base::RunLoop().Run();
322 EXPECT_EQ(net::URLRequestStatus::FAILED
, request
->status().status());
323 EXPECT_EQ(net::ERR_FAILED
, request
->status().error());
326 TEST_F(ExternalFileURLRequestJobTest
, Directory
) {
327 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
328 GURL("externalfile:drive/root/Directory 1"),
329 net::DEFAULT_PRIORITY
,
330 test_delegate_
.get(),
334 base::RunLoop().Run();
336 EXPECT_EQ(net::URLRequestStatus::FAILED
, request
->status().status());
337 EXPECT_EQ(net::ERR_FAILED
, request
->status().error());
340 TEST_F(ExternalFileURLRequestJobTest
, NonExistingFile
) {
341 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
342 GURL("externalfile:drive/root/non-existing-file.txt"),
343 net::DEFAULT_PRIORITY
,
344 test_delegate_
.get(),
348 base::RunLoop().Run();
350 EXPECT_EQ(net::URLRequestStatus::FAILED
, request
->status().status());
351 EXPECT_EQ(net::ERR_FILE_NOT_FOUND
, request
->status().error());
354 TEST_F(ExternalFileURLRequestJobTest
, WrongFormat
) {
355 scoped_ptr
<net::URLRequest
> request(
356 url_request_context_
->CreateRequest(GURL("externalfile:"),
357 net::DEFAULT_PRIORITY
,
358 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/root/File 1.txt"),
371 net::DEFAULT_PRIORITY
,
372 test_delegate_
.get(),
375 // Start the request, and cancel it immediately after it.
379 base::RunLoop().Run();
381 EXPECT_EQ(net::URLRequestStatus::CANCELED
, request
->status().status());
384 TEST_F(ExternalFileURLRequestJobTest
, RangeHeader
) {
385 const GURL
kTestUrl("externalfile:drive/root/File 1.txt");
386 const base::FilePath
kTestFilePath("drive/root/File 1.txt");
388 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
389 kTestUrl
, net::DEFAULT_PRIORITY
, test_delegate_
.get(), NULL
));
392 request
->SetExtraRequestHeaderByName(
393 "Range", "bytes=3-5", false /* overwrite */);
396 base::RunLoop().Run();
398 EXPECT_EQ(net::URLRequestStatus::SUCCESS
, request
->status().status());
400 // Reading file must be done after |request| runs, otherwise
401 // it'll create a local cache file, and we cannot test correctly.
402 std::string expected_data
;
403 ASSERT_TRUE(ReadDriveFileSync(kTestFilePath
, &expected_data
));
404 EXPECT_EQ(expected_data
.substr(3, 3), test_delegate_
->data_received());
407 TEST_F(ExternalFileURLRequestJobTest
, WrongRangeHeader
) {
408 const GURL
kTestUrl("externalfile:drive/root/File 1.txt");
410 scoped_ptr
<net::URLRequest
> request(url_request_context_
->CreateRequest(
411 kTestUrl
, net::DEFAULT_PRIORITY
, test_delegate_
.get(), NULL
));
414 request
->SetExtraRequestHeaderByName(
415 "Range", "Wrong Range Header Value", false /* overwrite */);
418 base::RunLoop().Run();
420 EXPECT_EQ(net::URLRequestStatus::FAILED
, request
->status().status());
421 EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE
, request
->status().error());
424 } // namespace chromeos