1 // Copyright 2015 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.
7 #include "base/callback_forward.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/threading/sequenced_worker_pool.h"
10 #include "chrome/browser/extensions/app_data_migrator.h"
11 #include "chrome/browser/extensions/extension_special_storage_policy.h"
12 #include "chrome/test/base/testing_profile.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/indexed_db_context.h"
15 #include "content/public/browser/storage_partition.h"
16 #include "content/public/test/mock_blob_url_request_context.h"
17 #include "content/public/test/test_browser_thread_bundle.h"
18 #include "extensions/browser/extension_registry.h"
19 #include "extensions/common/extension.h"
20 #include "extensions/common/extension_builder.h"
21 #include "extensions/common/manifest.h"
22 #include "storage/browser/fileapi/file_system_context.h"
23 #include "storage/browser/fileapi/file_system_operation_runner.h"
24 #include "storage/browser/fileapi/file_system_url.h"
25 #include "testing/gtest/include/gtest/gtest.h"
28 scoped_ptr
<TestingProfile
> GetTestingProfile() {
29 TestingProfile::Builder profile_builder
;
30 return profile_builder
.Build();
34 namespace extensions
{
36 class AppDataMigratorTest
: public testing::Test
{
39 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
) {}
41 void SetUp() override
{
42 profile_
= GetTestingProfile();
43 registry_
= ExtensionRegistry::Get(profile_
.get());
44 migrator_
= scoped_ptr
<AppDataMigrator
>(
45 new AppDataMigrator(profile_
.get(), registry_
));
48 content::BrowserContext::GetDefaultStoragePartition(profile_
.get());
50 idb_context_
= default_partition_
->GetIndexedDBContext();
51 idb_context_
->SetTaskRunnerForTesting(
52 base::MessageLoop::current()->message_loop_proxy().get());
54 default_fs_context_
= default_partition_
->GetFileSystemContext();
56 url_request_context_
= scoped_ptr
<content::MockBlobURLRequestContext
>(
57 new content::MockBlobURLRequestContext(default_fs_context_
));
60 void TearDown() override
{}
63 content::TestBrowserThreadBundle thread_bundle_
;
64 scoped_ptr
<TestingProfile
> profile_
;
65 scoped_ptr
<AppDataMigrator
> migrator_
;
66 content::StoragePartition
* default_partition_
;
67 ExtensionRegistry
* registry_
;
68 storage::FileSystemContext
* default_fs_context_
;
69 content::IndexedDBContext
* idb_context_
;
70 scoped_ptr
<content::MockBlobURLRequestContext
> url_request_context_
;
73 scoped_refptr
<const Extension
> GetTestExtension(bool platform_app
) {
74 scoped_refptr
<const Extension
> app
;
76 app
= ExtensionBuilder()
79 .Set("name", "test app")
81 .Set("app", DictionaryBuilder().Set(
83 DictionaryBuilder().Set(
84 "scripts", ListBuilder().Append(
87 ListBuilder().Append("unlimitedStorage")))
90 app
= ExtensionBuilder()
91 .SetManifest(DictionaryBuilder()
92 .Set("name", "test app")
94 .Set("app", DictionaryBuilder().Set(
96 DictionaryBuilder().Set(
97 "local_path", "index.html")))
99 ListBuilder().Append("unlimitedStorage")))
105 void MigrationCallback() {
108 void DidWrite(base::File::Error status
, int64 bytes
, bool complete
) {
109 base::MessageLoop::current()->Quit();
112 void DidCreate(base::File::Error status
) {
115 void DidOpenFileSystem(const GURL
& root
,
116 const std::string
& name
,
117 base::File::Error result
) {
120 void OpenFileSystems(storage::FileSystemContext
* fs_context
,
121 GURL extension_url
) {
122 fs_context
->OpenFileSystem(extension_url
, storage::kFileSystemTypeTemporary
,
123 storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
,
124 base::Bind(&DidOpenFileSystem
));
126 fs_context
->OpenFileSystem(extension_url
, storage::kFileSystemTypePersistent
,
127 storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
,
128 base::Bind(&DidOpenFileSystem
));
129 base::MessageLoop::current()->RunUntilIdle();
132 void GenerateTestFiles(content::MockBlobURLRequestContext
* url_request_context
,
133 const Extension
* ext
,
134 storage::FileSystemContext
* fs_context
,
136 profile
->GetExtensionSpecialStoragePolicy()->GrantRightsForExtension(ext
,
139 base::FilePath
path(FILE_PATH_LITERAL("test.txt"));
141 extensions::Extension::GetBaseURLFromExtensionId(ext
->id());
143 OpenFileSystems(fs_context
, extension_url
);
145 storage::FileSystemURL fs_temp_url
= fs_context
->CreateCrackedFileSystemURL(
146 extension_url
, storage::kFileSystemTypeTemporary
, path
);
148 storage::FileSystemURL fs_persistent_url
=
149 fs_context
->CreateCrackedFileSystemURL(
150 extension_url
, storage::kFileSystemTypePersistent
, path
);
152 content::ScopedTextBlob
blob1(*url_request_context
, "blob-id:success1",
155 fs_context
->operation_runner()->CreateFile(fs_temp_url
, false,
156 base::Bind(&DidCreate
));
158 fs_context
->operation_runner()->CreateFile(fs_persistent_url
, false,
159 base::Bind(&DidCreate
));
160 base::MessageLoop::current()->RunUntilIdle();
162 fs_context
->operation_runner()->Write(url_request_context
, fs_temp_url
,
163 blob1
.GetBlobDataHandle(), 0,
164 base::Bind(&DidWrite
));
165 base::MessageLoop::current()->Run();
166 fs_context
->operation_runner()->Write(url_request_context
, fs_persistent_url
,
167 blob1
.GetBlobDataHandle(), 0,
168 base::Bind(&DidWrite
));
169 base::MessageLoop::current()->Run();
172 void VerifyFileContents(base::File file
,
173 const base::Closure
& on_close_callback
) {
174 ASSERT_EQ(14, file
.GetLength());
175 scoped_ptr
<char[]> buffer(new char[15]);
177 file
.Read(0, buffer
.get(), 14);
178 buffer
.get()[14] = 0;
180 std::string expected
= "Hello, world!\n";
181 std::string actual
= buffer
.get();
182 EXPECT_EQ(expected
, actual
);
185 if (!on_close_callback
.is_null())
186 on_close_callback
.Run();
187 base::MessageLoop::current()->Quit();
190 void VerifyTestFilesMigrated(content::StoragePartition
* new_partition
,
191 const Extension
* new_ext
) {
193 extensions::Extension::GetBaseURLFromExtensionId(new_ext
->id());
194 storage::FileSystemContext
* new_fs_context
=
195 new_partition
->GetFileSystemContext();
197 OpenFileSystems(new_fs_context
, extension_url
);
199 base::FilePath
path(FILE_PATH_LITERAL("test.txt"));
201 storage::FileSystemURL fs_temp_url
=
202 new_fs_context
->CreateCrackedFileSystemURL(
203 extension_url
, storage::kFileSystemTypeTemporary
, path
);
204 storage::FileSystemURL fs_persistent_url
=
205 new_fs_context
->CreateCrackedFileSystemURL(
206 extension_url
, storage::kFileSystemTypePersistent
, path
);
208 new_fs_context
->operation_runner()->OpenFile(
209 fs_temp_url
, base::File::FLAG_READ
| base::File::FLAG_OPEN
,
210 base::Bind(&VerifyFileContents
));
211 base::MessageLoop::current()->Run();
212 new_fs_context
->operation_runner()->OpenFile(
213 fs_persistent_url
, base::File::FLAG_READ
| base::File::FLAG_OPEN
,
214 base::Bind(&VerifyFileContents
));
215 base::MessageLoop::current()->Run();
218 TEST_F(AppDataMigratorTest
, ShouldMigrate
) {
219 scoped_refptr
<const Extension
> old_ext
= GetTestExtension(false);
220 scoped_refptr
<const Extension
> new_ext
= GetTestExtension(true);
222 EXPECT_TRUE(AppDataMigrator::NeedsMigration(old_ext
.get(), new_ext
.get()));
225 TEST_F(AppDataMigratorTest
, ShouldNotMigratePlatformApp
) {
226 scoped_refptr
<const Extension
> old_ext
= GetTestExtension(true);
227 scoped_refptr
<const Extension
> new_ext
= GetTestExtension(true);
229 EXPECT_FALSE(AppDataMigrator::NeedsMigration(old_ext
.get(), new_ext
.get()));
232 TEST_F(AppDataMigratorTest
, ShouldNotMigrateLegacyApp
) {
233 scoped_refptr
<const Extension
> old_ext
= GetTestExtension(false);
234 scoped_refptr
<const Extension
> new_ext
= GetTestExtension(false);
236 EXPECT_FALSE(AppDataMigrator::NeedsMigration(old_ext
.get(), new_ext
.get()));
239 TEST_F(AppDataMigratorTest
, NoOpMigration
) {
240 scoped_refptr
<const Extension
> old_ext
= GetTestExtension(false);
241 scoped_refptr
<const Extension
> new_ext
= GetTestExtension(true);
243 // Nothing to migrate. Basically this should just not cause an error
244 migrator_
->DoMigrationAndReply(old_ext
.get(), new_ext
.get(),
245 base::Bind(&MigrationCallback
));
248 TEST_F(AppDataMigratorTest
, FileSystemMigration
) {
249 scoped_refptr
<const Extension
> old_ext
= GetTestExtension(false);
250 scoped_refptr
<const Extension
> new_ext
= GetTestExtension(true);
252 GenerateTestFiles(url_request_context_
.get(), old_ext
.get(),
253 default_fs_context_
, profile_
.get());
255 migrator_
->DoMigrationAndReply(old_ext
.get(), new_ext
.get(),
256 base::Bind(&MigrationCallback
));
258 base::MessageLoop::current()->RunUntilIdle();
260 registry_
->AddEnabled(new_ext
);
262 extensions::Extension::GetBaseURLFromExtensionId(new_ext
->id());
264 content::StoragePartition
* new_partition
=
265 content::BrowserContext::GetStoragePartitionForSite(profile_
.get(),
268 ASSERT_NE(new_partition
->GetPath(), default_partition_
->GetPath());
270 VerifyTestFilesMigrated(new_partition
, new_ext
.get());
273 } // namespace extensions