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/sync_file_system/drive_backend/metadata_database.h"
8 #include "base/files/scoped_temp_dir.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "third_party/leveldatabase/src/include/leveldb/db.h"
15 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
17 namespace sync_file_system
{
18 namespace drive_backend
{
20 typedef MetadataDatabase::FileSet FileSet
;
21 typedef MetadataDatabase::FileByFileID FileByFileID
;
22 typedef MetadataDatabase::FilesByParent FilesByParent
;
23 typedef MetadataDatabase::FileByParentAndTitle FileByParentAndTitle
;
24 typedef MetadataDatabase::FileByAppID FileByAppID
;
28 const int64 kInitialChangeID
= 1234;
29 const char kSyncRootFolderID
[] = "sync_root_folder_id";
31 bool AreEquivalentMessages(const google::protobuf::MessageLite
& left
,
32 const google::protobuf::MessageLite
& right
);
33 bool AreEquivalent(const google::protobuf::MessageLite
* left
,
34 const google::protobuf::MessageLite
* right
) {
35 return AreEquivalentMessages(*left
, *right
);
38 template <typename Container
>
39 bool AreEquivalentMaps(const Container
& left
, const Container
& right
);
40 template <typename Key
, typename Value
, typename Compare
>
41 bool AreEquivalent(const std::map
<Key
, Value
, Compare
>& left
,
42 const std::map
<Key
, Value
, Compare
>& right
) {
43 return AreEquivalentMaps(left
, right
);
46 template <typename Container
>
47 bool AreEquivalentSets(const Container
& left
, const Container
& right
);
48 template <typename Value
, typename Compare
>
49 bool AreEquivalent(const std::set
<Value
, Compare
>& left
,
50 const std::set
<Value
, Compare
>& right
) {
51 return AreEquivalentSets(left
, right
);
54 bool AreEquivalentMessages(const google::protobuf::MessageLite
& left
,
55 const google::protobuf::MessageLite
& right
) {
56 std::string serialized_left
;
57 std::string serialized_right
;
58 left
.SerializeToString(&serialized_left
);
59 right
.SerializeToString(&serialized_right
);
60 return serialized_left
== serialized_right
;
63 template <typename Container
>
64 bool AreEquivalentMaps(const Container
& left
, const Container
& right
) {
65 if (left
.size() != right
.size())
68 typedef typename
Container::const_iterator const_iterator
;
69 const_iterator left_itr
= left
.begin();
70 const_iterator right_itr
= right
.begin();
72 while (left_itr
!= left
.end()) {
73 if (left_itr
->first
!= right_itr
->first
)
75 if (!AreEquivalent(left_itr
->second
, right_itr
->second
))
84 template <typename Container
>
85 bool AreEquivalentSets(const Container
& left
, const Container
& right
) {
86 if (left
.size() != right
.size())
89 typedef typename
Container::const_iterator const_iterator
;
90 const_iterator left_itr
= left
.begin();
91 const_iterator right_itr
= right
.begin();
92 while (left_itr
!= left
.end()) {
93 if (!AreEquivalent(*left_itr
, *right_itr
))
101 void SyncStatusResultCallback(SyncStatusCode
* status_out
,
102 SyncStatusCode status
) {
103 EXPECT_EQ(SYNC_STATUS_UNKNOWN
, *status_out
);
104 *status_out
= status
;
107 void DatabaseCreateResultCallback(SyncStatusCode
* status_out
,
108 scoped_ptr
<MetadataDatabase
>* database_out
,
109 SyncStatusCode status
,
110 scoped_ptr
<MetadataDatabase
> database
) {
111 EXPECT_EQ(SYNC_STATUS_UNKNOWN
, *status_out
);
112 *status_out
= status
;
113 *database_out
= database
.Pass();
118 class MetadataDatabaseTest
: public testing::Test
{
120 MetadataDatabaseTest() : next_file_id_number_(1) {}
122 virtual ~MetadataDatabaseTest() {}
124 virtual void SetUp() OVERRIDE
{
125 ASSERT_TRUE(database_dir_
.CreateUniqueTempDir());
128 virtual void TearDown() OVERRIDE
{ DropDatabase(); }
131 std::string
GenerateFileID() {
132 return "file_id_" + base::Int64ToString(next_file_id_number_
++);
135 SyncStatusCode
InitializeDatabase() {
136 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
137 MetadataDatabase::Create(base::MessageLoopProxy::current(),
138 database_dir_
.path(),
139 base::Bind(&DatabaseCreateResultCallback
,
140 &status
, &metadata_database_
));
141 message_loop_
.RunUntilIdle();
145 void DropDatabase() {
146 metadata_database_
.reset();
147 message_loop_
.RunUntilIdle();
150 MetadataDatabase
* metadata_database() { return metadata_database_
.get(); }
153 if (!metadata_database_
)
155 return metadata_database_
->db_
.get();
158 scoped_ptr
<leveldb::DB
> OpenLevelDB() {
159 leveldb::DB
* db
= NULL
;
160 leveldb::Options options
;
161 options
.create_if_missing
= true;
162 options
.max_open_files
= 0; // Use minimum.
163 leveldb::Status status
=
164 leveldb::DB::Open(options
, database_dir_
.path().AsUTF8Unsafe(), &db
);
165 EXPECT_TRUE(status
.ok());
166 return make_scoped_ptr(db
);
169 void SetUpServiceMetadata(leveldb::DB
* db
) {
170 ServiceMetadata service_metadata
;
171 service_metadata
.set_largest_change_id(kInitialChangeID
);
172 service_metadata
.set_sync_root_folder_id(kSyncRootFolderID
);
174 ASSERT_TRUE(service_metadata
.SerializeToString(&value
));
175 db
->Put(leveldb::WriteOptions(), "SERVICE", value
);
178 DriveFileMetadata
CreateSyncRoot() {
179 DriveFileMetadata metadata
;
180 metadata
.set_file_id(kSyncRootFolderID
);
181 metadata
.set_parent_folder_id(std::string());
182 metadata
.mutable_synced_details()->set_title("Chrome Syncable FileSystem");
183 metadata
.mutable_synced_details()->set_kind(KIND_FOLDER
);
184 metadata
.set_active(true);
185 metadata
.set_dirty(false);
189 DriveFileMetadata
CreateUnknownFile(const std::string
& app_id
,
190 const std::string
& parent_folder_id
) {
191 DriveFileMetadata metadata
;
192 metadata
.set_file_id(GenerateFileID());
193 metadata
.set_parent_folder_id(parent_folder_id
);
194 metadata
.set_app_id(app_id
);
195 metadata
.set_is_app_root(
196 !app_id
.empty() && parent_folder_id
== kSyncRootFolderID
);
200 DriveFileMetadata
CreateFile(const std::string
& app_id
,
201 const std::string
& parent_folder_id
,
202 const std::string
& title
) {
203 DriveFileMetadata
file(CreateUnknownFile(app_id
, parent_folder_id
));
204 file
.mutable_synced_details()->add_parent_folder_id(parent_folder_id
);
205 file
.mutable_synced_details()->set_title(title
);
206 file
.mutable_synced_details()->set_kind(KIND_FILE
);
207 file
.set_active(true);
208 file
.set_dirty(false);
212 DriveFileMetadata
CreateFolder(const std::string
& app_id
,
213 const std::string
& parent_folder_id
,
214 const std::string
& title
) {
215 DriveFileMetadata
folder(CreateUnknownFile(app_id
, parent_folder_id
));
216 folder
.mutable_synced_details()->add_parent_folder_id(parent_folder_id
);
217 folder
.mutable_synced_details()->set_title(title
);
218 folder
.mutable_synced_details()->set_kind(KIND_FOLDER
);
219 folder
.set_active(true);
220 folder
.set_dirty(false);
224 leveldb::Status
PutFileToDB(leveldb::DB
* db
, const DriveFileMetadata
& file
) {
225 std::string key
= "FILE: " + file
.file_id();
227 file
.SerializeToString(&value
);
228 return db
->Put(leveldb::WriteOptions(), key
, value
);
231 void VerifyReloadConsistency() {
232 scoped_ptr
<MetadataDatabase
> metadata_database_2
;
233 ASSERT_EQ(SYNC_STATUS_OK
,
234 MetadataDatabase::CreateForTesting(
235 metadata_database_
->db_
.Pass(),
236 &metadata_database_2
));
237 metadata_database_
->db_
= metadata_database_2
->db_
.Pass();
239 EXPECT_TRUE(AreEquivalent(metadata_database_
->file_by_file_id_
,
240 metadata_database_2
->file_by_file_id_
));
241 EXPECT_TRUE(AreEquivalent(metadata_database_
->files_by_parent_
,
242 metadata_database_2
->files_by_parent_
));
243 EXPECT_TRUE(AreEquivalent(metadata_database_
->app_root_by_app_id_
,
244 metadata_database_2
->app_root_by_app_id_
));
245 EXPECT_TRUE(AreEquivalent(
246 metadata_database_
->active_file_by_parent_and_title_
,
247 metadata_database_2
->active_file_by_parent_and_title_
));
248 EXPECT_TRUE(AreEquivalent(metadata_database_
->dirty_files_
,
249 metadata_database_2
->dirty_files_
));
252 void VerifyFile(const DriveFileMetadata
& file
) {
253 DriveFileMetadata file_in_metadata_db
;
254 ASSERT_TRUE(metadata_database()->FindFileByFileID(
255 file
.file_id(), &file_in_metadata_db
));
256 EXPECT_TRUE(AreEquivalentMessages(file
, file_in_metadata_db
));
259 SyncStatusCode
RegisterApp(const std::string
& app_id
,
260 const std::string
& folder_id
) {
261 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
262 metadata_database_
->RegisterApp(
264 base::Bind(&SyncStatusResultCallback
, &status
));
265 message_loop_
.RunUntilIdle();
269 SyncStatusCode
DisableApp(const std::string
& app_id
) {
270 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
271 metadata_database_
->DisableApp(
272 app_id
, base::Bind(&SyncStatusResultCallback
, &status
));
273 message_loop_
.RunUntilIdle();
277 SyncStatusCode
EnableApp(const std::string
& app_id
) {
278 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
279 metadata_database_
->EnableApp(
280 app_id
, base::Bind(&SyncStatusResultCallback
, &status
));
281 message_loop_
.RunUntilIdle();
285 SyncStatusCode
UnregisterApp(const std::string
& app_id
) {
286 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
287 metadata_database_
->UnregisterApp(
288 app_id
, base::Bind(&SyncStatusResultCallback
, &status
));
289 message_loop_
.RunUntilIdle();
294 base::ScopedTempDir database_dir_
;
295 base::MessageLoop message_loop_
;
297 scoped_ptr
<MetadataDatabase
> metadata_database_
;
299 int64 next_file_id_number_
;
301 DISALLOW_COPY_AND_ASSIGN(MetadataDatabaseTest
);
304 TEST_F(MetadataDatabaseTest
, InitializationTest_Empty
) {
305 EXPECT_EQ(SYNC_STATUS_OK
, InitializeDatabase());
307 EXPECT_EQ(SYNC_STATUS_OK
, InitializeDatabase());
310 TEST_F(MetadataDatabaseTest
, InitializationTest_SimpleTree
) {
311 std::string app_id
= "app_id";
312 DriveFileMetadata
sync_root(CreateSyncRoot());
313 DriveFileMetadata
app_root(CreateFolder(app_id
, kSyncRootFolderID
, app_id
));
314 DriveFileMetadata
file(CreateFile(app_id
, app_root
.file_id(), "file"));
315 DriveFileMetadata
folder(CreateFolder(app_id
, app_root
.file_id(), "folder"));
316 DriveFileMetadata
file_in_folder(
317 CreateFile(app_id
, folder
.file_id(), "file_in_folder"));
318 DriveFileMetadata
orphaned(CreateUnknownFile(std::string(), "root"));
321 scoped_ptr
<leveldb::DB
> db
= OpenLevelDB();
323 db
->Put(leveldb::WriteOptions(), "VERSION", base::Int64ToString(3));
324 SetUpServiceMetadata(db
.get());
326 EXPECT_TRUE(PutFileToDB(db
.get(), sync_root
).ok());
327 EXPECT_TRUE(PutFileToDB(db
.get(), app_root
).ok());
328 EXPECT_TRUE(PutFileToDB(db
.get(), file
).ok());
329 EXPECT_TRUE(PutFileToDB(db
.get(), folder
).ok());
330 EXPECT_TRUE(PutFileToDB(db
.get(), file_in_folder
).ok());
331 EXPECT_TRUE(PutFileToDB(db
.get(), orphaned
).ok());
334 EXPECT_EQ(SYNC_STATUS_OK
, InitializeDatabase());
336 VerifyFile(sync_root
);
337 VerifyFile(app_root
);
340 VerifyFile(file_in_folder
);
341 EXPECT_FALSE(metadata_database()->FindFileByFileID(orphaned
.file_id(), NULL
));
344 TEST_F(MetadataDatabaseTest
, AppManagementTest
) {
345 DriveFileMetadata
sync_root(CreateSyncRoot());
346 DriveFileMetadata
app_root(
347 CreateFolder("app_id", kSyncRootFolderID
, "app_id"));
348 DriveFileMetadata
folder(
349 CreateFolder(std::string(), kSyncRootFolderID
, "folder"));
350 folder
.set_active(false);
353 scoped_ptr
<leveldb::DB
> db
= OpenLevelDB();
355 db
->Put(leveldb::WriteOptions(), "VERSION", base::Int64ToString(3));
356 SetUpServiceMetadata(db
.get());
358 EXPECT_TRUE(PutFileToDB(db
.get(), sync_root
).ok());
359 EXPECT_TRUE(PutFileToDB(db
.get(), app_root
).ok());
360 EXPECT_TRUE(PutFileToDB(db
.get(), folder
).ok());
363 EXPECT_EQ(SYNC_STATUS_OK
, InitializeDatabase());
364 VerifyFile(sync_root
);
365 VerifyFile(app_root
);
368 folder
.set_app_id("foo");
369 EXPECT_EQ(SYNC_STATUS_OK
, RegisterApp(folder
.app_id(), folder
.file_id()));
371 folder
.set_is_app_root(true);
372 folder
.set_active(true);
373 folder
.set_dirty(true);
374 folder
.set_needs_folder_listing(true);
376 VerifyReloadConsistency();
378 EXPECT_EQ(SYNC_STATUS_OK
, DisableApp(folder
.app_id()));
379 folder
.set_active(false);
381 VerifyReloadConsistency();
383 EXPECT_EQ(SYNC_STATUS_OK
, EnableApp(folder
.app_id()));
384 folder
.set_active(true);
386 VerifyReloadConsistency();
388 EXPECT_EQ(SYNC_STATUS_OK
, UnregisterApp(folder
.app_id()));
389 folder
.set_app_id(std::string());
390 folder
.set_is_app_root(false);
391 folder
.set_active(false);
393 VerifyReloadConsistency();
396 } // namespace drive_backend
397 } // namespace sync_file_system