Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 "chrome/browser/sync_file_system/drive_backend/metadata_database.h" | |
| 6 | |
| 7 #include "base/bind.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" | |
| 16 | |
| 17 namespace sync_file_system { | |
| 18 namespace drive_backend { | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 const int64 kInitialChangeID = 1234; | |
| 23 const char kSyncRootFolderID[] = "sync_root_folder_id"; | |
| 24 | |
| 25 template <typename Value> | |
| 26 bool AreEquivalentProtobuf(const Value& left, const Value& right) { | |
|
kinuko
2013/07/04 15:59:11
AreEquivalentProtobuf's' (for all AreEquivalentFoo
tzik
2013/07/05 07:42:28
Done.
| |
| 27 std::string serialized_left; | |
| 28 std::string serialized_right; | |
| 29 left.SerializeToString(&serialized_left); | |
| 30 right.SerializeToString(&serialized_right); | |
| 31 return serialized_left == serialized_right; | |
| 32 } | |
| 33 | |
| 34 void SyncStatusResultCallback(SyncStatusCode* status_out, | |
| 35 SyncStatusCode status) { | |
| 36 EXPECT_EQ(SYNC_STATUS_UNKNOWN, *status_out); | |
| 37 *status_out = status; | |
| 38 } | |
| 39 | |
| 40 // Returns true if given two std::map<Key, protobuf*> is equivalent under '=='. | |
| 41 template <typename Container> | |
| 42 bool AreEquivalentPtrMap(const Container& left, const Container& right) { | |
| 43 if (left.size() != right.size()) | |
| 44 return false; | |
| 45 | |
| 46 typedef typename Container::const_iterator const_iterator; | |
| 47 const_iterator left_itr = left.begin(); | |
| 48 const_iterator right_itr = right.begin(); | |
| 49 | |
| 50 while (left_itr != left.end()) { | |
| 51 if (left_itr->first != right_itr->first) | |
| 52 return false; | |
| 53 if (!AreEquivalentProtobuf(*left_itr->second, *right_itr->second)) | |
| 54 return false; | |
| 55 | |
| 56 ++left_itr; | |
| 57 ++right_itr; | |
| 58 } | |
| 59 return true; | |
| 60 } | |
| 61 | |
| 62 // Returns true if given two std::set<protobuf*> is equivalent under '=='. | |
| 63 template <typename Container> | |
| 64 bool AreEquivalentPtrSet(const Container& left, const Container& right) { | |
| 65 if (left.size() != right.size()) | |
| 66 return false; | |
| 67 | |
| 68 typedef typename Container::const_iterator const_iterator; | |
| 69 const_iterator left_itr = left.begin(); | |
| 70 const_iterator right_itr = right.begin(); | |
| 71 while (left_itr != left.end()) { | |
| 72 if (!AreEquivalentProtobuf(**left_itr, **right_itr)) | |
| 73 return false; | |
| 74 ++left_itr; | |
| 75 ++right_itr; | |
| 76 } | |
| 77 return true; | |
| 78 } | |
| 79 | |
| 80 // Returns true if given two std::map<Key, std::set<Value*> > is equivalent | |
| 81 // under '=='. | |
| 82 template <typename Container> | |
| 83 bool AreEquivalentPtrSetMap(const Container& left, const Container& right) { | |
| 84 if (left.size() != right.size()) | |
| 85 return false; | |
| 86 | |
| 87 typedef typename Container::const_iterator const_iterator; | |
| 88 const_iterator left_itr = left.begin(); | |
| 89 const_iterator right_itr = right.begin(); | |
| 90 | |
| 91 while (left_itr != left.end()) { | |
| 92 if (left_itr->first != right_itr->first) | |
| 93 return false; | |
| 94 if (!AreEquivalentPtrSet(left_itr->second, right_itr->second)) | |
| 95 return false; | |
| 96 ++left_itr; | |
| 97 ++right_itr; | |
| 98 } | |
| 99 return true; | |
| 100 } | |
| 101 | |
| 102 } // namespace | |
| 103 | |
| 104 class SyncFS_MetadataDatabaseTest : public testing::Test { | |
|
kinuko
2013/07/04 15:59:11
I'm slightly not sure about this naming.
Does thi
tzik
2013/07/05 07:42:28
It doesn't conflict, but I feel a bit uneasy to pu
kinuko
2013/07/08 04:18:56
We're already adding generic test names, if we rea
| |
| 105 public: | |
| 106 SyncFS_MetadataDatabaseTest() | |
| 107 : next_file_id_number_(1) { | |
| 108 } | |
| 109 | |
| 110 virtual ~SyncFS_MetadataDatabaseTest() { | |
| 111 } | |
| 112 | |
| 113 virtual void SetUp() OVERRIDE { | |
| 114 ASSERT_TRUE(database_dir_.CreateUniqueTempDir()); | |
| 115 } | |
| 116 | |
| 117 virtual void TearDown() OVERRIDE { | |
| 118 DropDatabase(); | |
| 119 } | |
| 120 | |
| 121 protected: | |
| 122 std::string GenerateFileID() { | |
| 123 return "file_id_" + base::Int64ToString(next_file_id_number_++); | |
| 124 } | |
| 125 | |
| 126 SyncStatusCode InitializeDatabase() { | |
| 127 SyncStatusCode status = SYNC_STATUS_UNKNOWN; | |
| 128 metadata_database_.reset(new MetadataDatabase( | |
| 129 base::MessageLoopProxy::current())); | |
| 130 metadata_database_->Initialize( | |
| 131 database_dir_.path(), | |
| 132 base::Bind(&SyncStatusResultCallback, &status)); | |
| 133 message_loop_.RunUntilIdle(); | |
| 134 return status; | |
| 135 } | |
| 136 | |
| 137 void DropDatabase() { | |
| 138 metadata_database_.reset(); | |
| 139 message_loop_.RunUntilIdle(); | |
| 140 } | |
| 141 | |
| 142 MetadataDatabase* metadata_database() { | |
| 143 return metadata_database_.get(); | |
| 144 } | |
| 145 | |
| 146 leveldb::DB* db() { | |
| 147 if (!metadata_database_) | |
| 148 return NULL; | |
| 149 return metadata_database_->db_.get(); | |
| 150 } | |
| 151 | |
| 152 scoped_ptr<leveldb::DB> OpenDB() { | |
| 153 bool created = false; | |
| 154 SyncStatusCode status = SYNC_STATUS_UNKNOWN; | |
| 155 scoped_ptr<leveldb::DB> db = MetadataDatabase::OpenDatabase( | |
| 156 database_dir_.path(), &status, &created); | |
| 157 EXPECT_EQ(status, SYNC_STATUS_OK); | |
| 158 return db.Pass(); | |
| 159 } | |
| 160 | |
| 161 void SetUpServiceMetadata(leveldb::DB* db) { | |
| 162 ServiceMetadata service_metadata; | |
| 163 service_metadata.set_largest_change_id(kInitialChangeID); | |
| 164 service_metadata.set_sync_root_folder_id(kSyncRootFolderID); | |
| 165 std::string value; | |
| 166 ASSERT_TRUE(service_metadata.SerializeToString(&value)); | |
| 167 db->Put(leveldb::WriteOptions(), "SERVICE", value); | |
| 168 } | |
| 169 | |
| 170 DriveFileMetadata CreateSyncRoot() { | |
| 171 DriveFileMetadata metadata; | |
| 172 metadata.set_file_id(kSyncRootFolderID); | |
| 173 metadata.set_parent_folder_id(std::string()); | |
| 174 metadata.mutable_synced_details()->set_title("Chrome Syncable FileSystem"); | |
| 175 metadata.mutable_synced_details()->set_kind(KIND_FOLDER); | |
| 176 metadata.set_active(true); | |
| 177 return metadata; | |
| 178 } | |
| 179 | |
| 180 DriveFileMetadata CreateUnknownFile(const std::string& app_id, | |
| 181 const std::string& parent_folder_id) { | |
| 182 DriveFileMetadata metadata; | |
| 183 metadata.set_file_id(GenerateFileID()); | |
| 184 metadata.set_parent_folder_id(parent_folder_id); | |
| 185 metadata.set_app_id(app_id); | |
| 186 metadata.set_is_app_root(parent_folder_id == kSyncRootFolderID); | |
| 187 return metadata; | |
| 188 } | |
| 189 | |
| 190 DriveFileMetadata CreateFile(const std::string& app_id, | |
| 191 const std::string& parent_folder_id, | |
| 192 const std::string& title) { | |
| 193 DriveFileMetadata file(CreateUnknownFile(app_id, parent_folder_id)); | |
| 194 file.mutable_synced_details()->add_parent_folder_id(parent_folder_id); | |
| 195 file.mutable_synced_details()->set_title(title); | |
| 196 file.mutable_synced_details()->set_kind(KIND_FILE); | |
| 197 file.set_active(true); | |
| 198 file.set_dirty(false); | |
| 199 return file; | |
| 200 } | |
| 201 | |
| 202 DriveFileMetadata CreateFolder(const std::string& app_id, | |
| 203 const std::string& parent_folder_id, | |
| 204 const std::string& title) { | |
| 205 DriveFileMetadata folder(CreateUnknownFile(app_id, parent_folder_id)); | |
| 206 folder.mutable_synced_details()->add_parent_folder_id(parent_folder_id); | |
| 207 folder.mutable_synced_details()->set_title(title); | |
| 208 folder.mutable_synced_details()->set_kind(KIND_FOLDER); | |
| 209 folder.set_active(true); | |
| 210 folder.set_dirty(false); | |
| 211 return folder; | |
| 212 } | |
| 213 | |
| 214 DriveFileMetadata CreateUnsupportedFile(const std::string& app_id, | |
| 215 const std::string& parent_folder_id, | |
| 216 const std::string& title) { | |
| 217 DriveFileMetadata file(CreateUnknownFile(app_id, parent_folder_id)); | |
| 218 file.mutable_synced_details()->add_parent_folder_id(parent_folder_id); | |
| 219 file.mutable_synced_details()->set_title(title); | |
| 220 file.mutable_synced_details()->set_kind(KIND_UNSUPPORTED); | |
| 221 file.set_active(false); | |
| 222 file.set_dirty(false); | |
| 223 return file; | |
| 224 } | |
| 225 | |
| 226 leveldb::Status PutFileToDB(leveldb::DB* db, const DriveFileMetadata& file) { | |
| 227 std::string key = "FILE: " + file.file_id(); | |
| 228 std::string value; | |
| 229 file.SerializeToString(&value); | |
| 230 return db->Put(leveldb::WriteOptions(), key, value); | |
| 231 } | |
| 232 | |
| 233 void VerifyReloadConsistency() { | |
|
kinuko
2013/07/04 15:59:11
Is it same if we save all internal maps of metadat
| |
| 234 MetadataDatabase::InitializeInfo info; | |
| 235 | |
| 236 ASSERT_EQ(SYNC_STATUS_OK, | |
| 237 MetadataDatabase::ReadDatabaseContents( | |
| 238 metadata_database_->db_.get(), &info)); | |
| 239 | |
| 240 leveldb::WriteBatch batch; | |
| 241 ASSERT_EQ(SYNC_STATUS_OK, | |
| 242 MetadataDatabase::ConstructDataStructure(&info, &batch)); | |
| 243 | |
| 244 EXPECT_TRUE(AreEquivalentPtrMap(info.file_by_file_id, | |
| 245 metadata_database_->file_by_file_id_)); | |
| 246 EXPECT_TRUE(AreEquivalentPtrSetMap(info.files_by_parent, | |
| 247 metadata_database_->files_by_parent_)); | |
| 248 EXPECT_TRUE(AreEquivalentPtrMap(info.app_root_by_app_id, | |
| 249 metadata_database_->app_root_by_app_id_)); | |
| 250 EXPECT_TRUE(AreEquivalentPtrMap( | |
| 251 info.active_file_by_parent_and_title, | |
| 252 metadata_database_->active_file_by_parent_and_title_)); | |
| 253 } | |
| 254 | |
| 255 void VerifyMetadataExists(const DriveFileMetadata& file) { | |
| 256 DriveFileMetadata file_in_metadata_db; | |
| 257 ASSERT_TRUE(metadata_database()->FindFileByFileID( | |
| 258 file.file_id(), &file_in_metadata_db)); | |
| 259 EXPECT_TRUE(AreEquivalentProtobuf(file, file_in_metadata_db)); | |
| 260 } | |
| 261 | |
| 262 private: | |
| 263 base::ScopedTempDir database_dir_; | |
| 264 base::MessageLoop message_loop_; | |
| 265 | |
| 266 scoped_ptr<MetadataDatabase> metadata_database_; | |
| 267 | |
| 268 int64 next_file_id_number_; | |
| 269 | |
| 270 DISALLOW_COPY_AND_ASSIGN(SyncFS_MetadataDatabaseTest); | |
| 271 }; | |
| 272 | |
| 273 TEST_F(SyncFS_MetadataDatabaseTest, InitializationTest_Empty) { | |
| 274 EXPECT_EQ(SYNC_STATUS_OK, InitializeDatabase()); | |
| 275 VerifyReloadConsistency(); | |
| 276 DropDatabase(); | |
| 277 EXPECT_EQ(SYNC_STATUS_OK, InitializeDatabase()); | |
| 278 } | |
| 279 | |
| 280 TEST_F(SyncFS_MetadataDatabaseTest, InitializationTest_SimpleTree) { | |
| 281 std::string app_id = "app_id"; | |
| 282 DriveFileMetadata sync_root(CreateSyncRoot()); | |
| 283 DriveFileMetadata app_root(CreateFolder(app_id, kSyncRootFolderID, app_id)); | |
| 284 DriveFileMetadata file(CreateFile(app_id, app_root.file_id(), "file")); | |
| 285 DriveFileMetadata folder(CreateFolder(app_id, app_root.file_id(), "folder")); | |
| 286 DriveFileMetadata file_in_folder( | |
| 287 CreateFile(app_id, folder.file_id(), "file_in_folder")); | |
| 288 DriveFileMetadata orphaned(CreateUnknownFile(std::string(), "root")); | |
| 289 | |
| 290 { | |
| 291 scoped_ptr<leveldb::DB> db = OpenDB(); | |
| 292 ASSERT_TRUE(db); | |
| 293 db->Put(leveldb::WriteOptions(), "VERSION", base::Int64ToString(3)); | |
| 294 SetUpServiceMetadata(db.get()); | |
| 295 | |
| 296 EXPECT_TRUE(PutFileToDB(db.get(), sync_root).ok()); | |
| 297 EXPECT_TRUE(PutFileToDB(db.get(), app_root).ok()); | |
| 298 EXPECT_TRUE(PutFileToDB(db.get(), file).ok()); | |
| 299 EXPECT_TRUE(PutFileToDB(db.get(), folder).ok()); | |
| 300 EXPECT_TRUE(PutFileToDB(db.get(), file_in_folder).ok()); | |
| 301 EXPECT_TRUE(PutFileToDB(db.get(), orphaned).ok()); | |
| 302 } | |
| 303 | |
| 304 EXPECT_EQ(SYNC_STATUS_OK, InitializeDatabase()); | |
| 305 VerifyReloadConsistency(); | |
| 306 | |
| 307 VerifyMetadataExists(sync_root); | |
| 308 VerifyMetadataExists(app_root); | |
| 309 VerifyMetadataExists(file); | |
| 310 VerifyMetadataExists(folder); | |
| 311 VerifyMetadataExists(file_in_folder); | |
| 312 EXPECT_FALSE(metadata_database()->FindFileByFileID(orphaned.file_id(), NULL)); | |
| 313 } | |
| 314 | |
| 315 } // namespace drive_backend | |
| 316 } // namespace sync_file_system | |
| OLD | NEW |