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/chromeos/drive/resource_metadata_storage.h" 6 7 #include <algorithm> 8 9 #include "base/file_util.h" 10 #include "base/files/scoped_temp_dir.h" 11 #include "chrome/browser/chromeos/drive/drive.pb.h" 12 #include "chrome/browser/chromeos/drive/test_util.h" 13 #include "content/public/test/test_browser_thread_bundle.h" 14 #include "testing/gtest/include/gtest/gtest.h" 15 #include "third_party/leveldatabase/src/include/leveldb/db.h" 16 17 namespace drive { 18 namespace internal { 19 20 class ResourceMetadataStorageTest : public testing::Test { 21 protected: 22 virtual void SetUp() OVERRIDE { 23 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 24 25 storage_.reset(new ResourceMetadataStorage( 26 temp_dir_.path(), base::MessageLoopProxy::current().get())); 27 ASSERT_TRUE(storage_->Initialize()); 28 } 29 30 // Overwrites |storage_|'s version. 31 void SetDBVersion(int version) { 32 ResourceMetadataHeader header; 33 ASSERT_TRUE(storage_->GetHeader(&header)); 34 header.set_version(version); 35 EXPECT_TRUE(storage_->PutHeader(header)); 36 } 37 38 bool CheckValidity() { 39 return storage_->CheckValidity(); 40 } 41 42 // Puts a child entry. 43 void PutChild(const std::string& parent_resource_id, 44 const std::string& child_base_name, 45 const std::string& child_resource_id) { 46 storage_->resource_map_->Put( 47 leveldb::WriteOptions(), 48 ResourceMetadataStorage::GetChildEntryKey(parent_resource_id, 49 child_base_name), 50 child_resource_id); 51 } 52 53 // Removes a child entry. 54 void RemoveChild(const std::string& parent_resource_id, 55 const std::string& child_base_name) { 56 storage_->resource_map_->Delete( 57 leveldb::WriteOptions(), 58 ResourceMetadataStorage::GetChildEntryKey(parent_resource_id, 59 child_base_name)); 60 } 61 62 content::TestBrowserThreadBundle thread_bundle_; 63 base::ScopedTempDir temp_dir_; 64 scoped_ptr<ResourceMetadataStorage, 65 test_util::DestroyHelperForTests> storage_; 66 }; 67 68 TEST_F(ResourceMetadataStorageTest, LargestChangestamp) { 69 const int64 kLargestChangestamp = 1234567890; 70 EXPECT_TRUE(storage_->SetLargestChangestamp(kLargestChangestamp)); 71 EXPECT_EQ(kLargestChangestamp, storage_->GetLargestChangestamp()); 72 } 73 74 TEST_F(ResourceMetadataStorageTest, PutEntry) { 75 const std::string key1 = "abcdefg"; 76 const std::string key2 = "abcd"; 77 const std::string key3 = "efgh"; 78 const std::string name2 = "ABCD"; 79 const std::string name3 = "EFGH"; 80 81 ResourceEntry entry1; 82 entry1.set_resource_id(key1); 83 84 // key1 not found. 85 ResourceEntry result; 86 EXPECT_FALSE(storage_->GetEntry(key1, &result)); 87 88 // Put entry1. 89 EXPECT_TRUE(storage_->PutEntry(entry1)); 90 91 // key1 found. 92 ASSERT_TRUE(storage_->GetEntry(key1, &result)); 93 EXPECT_EQ(key1, result.resource_id()); 94 95 // key2 not found. 96 EXPECT_FALSE(storage_->GetEntry(key2, &result)); 97 98 // Put entry2 as a child of entry1. 99 ResourceEntry entry2; 100 entry2.set_parent_resource_id(key1); 101 entry2.set_resource_id(key2); 102 entry2.set_base_name(name2); 103 EXPECT_TRUE(storage_->PutEntry(entry2)); 104 105 // key2 found. 106 EXPECT_TRUE(storage_->GetEntry(key2, &result)); 107 EXPECT_EQ(key2, storage_->GetChild(key1, name2)); 108 109 // Put entry3 as a child of entry2. 110 ResourceEntry entry3; 111 entry3.set_parent_resource_id(key2); 112 entry3.set_resource_id(key3); 113 entry3.set_base_name(name3); 114 EXPECT_TRUE(storage_->PutEntry(entry3)); 115 116 // key3 found. 117 EXPECT_TRUE(storage_->GetEntry(key3, &result)); 118 EXPECT_EQ(key3, storage_->GetChild(key2, name3)); 119 120 // Change entry3's parent to entry1. 121 entry3.set_parent_resource_id(key1); 122 EXPECT_TRUE(storage_->PutEntry(entry3)); 123 124 // entry3 is a child of entry1 now. 125 EXPECT_TRUE(storage_->GetChild(key2, name3).empty()); 126 EXPECT_EQ(key3, storage_->GetChild(key1, name3)); 127 128 // Remove entries. 129 EXPECT_TRUE(storage_->RemoveEntry(key3)); 130 EXPECT_FALSE(storage_->GetEntry(key3, &result)); 131 EXPECT_TRUE(storage_->RemoveEntry(key2)); 132 EXPECT_FALSE(storage_->GetEntry(key2, &result)); 133 EXPECT_TRUE(storage_->RemoveEntry(key1)); 134 EXPECT_FALSE(storage_->GetEntry(key1, &result)); 135 } 136 137 TEST_F(ResourceMetadataStorageTest, Iterator) { 138 // Prepare data. 139 std::vector<ResourceEntry> entries; 140 ResourceEntry entry; 141 142 entry.set_resource_id("entry1"); 143 entries.push_back(entry); 144 entry.set_resource_id("entry2"); 145 entries.push_back(entry); 146 entry.set_resource_id("entry3"); 147 entries.push_back(entry); 148 entry.set_resource_id("entry4"); 149 entries.push_back(entry); 150 151 for (size_t i = 0; i < entries.size(); ++i) 152 EXPECT_TRUE(storage_->PutEntry(entries[i])); 153 154 // Insert some cache entries. 155 std::map<std::string, FileCacheEntry> cache_entries; 156 cache_entries[entries[0].resource_id()].set_md5("aaaaaa"); 157 cache_entries[entries[1].resource_id()].set_md5("bbbbbb"); 158 for (std::map<std::string, FileCacheEntry>::iterator it = 159 cache_entries.begin(); it != cache_entries.end(); ++it) 160 EXPECT_TRUE(storage_->PutCacheEntry(it->first, it->second)); 161 162 // Iterate and check the result. 163 std::map<std::string, ResourceEntry> found_entries; 164 std::map<std::string, FileCacheEntry> found_cache_entries; 165 scoped_ptr<ResourceMetadataStorage::Iterator> it = storage_->GetIterator(); 166 ASSERT_TRUE(it); 167 for (; !it->IsAtEnd(); it->Advance()) { 168 const ResourceEntry& entry = it->Get(); 169 found_entries[entry.resource_id()] = entry; 170 171 FileCacheEntry cache_entry; 172 if (it->GetCacheEntry(&cache_entry)) 173 found_cache_entries[entry.resource_id()] = cache_entry; 174 } 175 EXPECT_FALSE(it->HasError()); 176 177 EXPECT_EQ(entries.size(), found_entries.size()); 178 for (size_t i = 0; i < entries.size(); ++i) 179 EXPECT_EQ(1U, found_entries.count(entries[i].resource_id())); 180 181 EXPECT_EQ(cache_entries.size(), found_cache_entries.size()); 182 for (std::map<std::string, FileCacheEntry>::iterator it = 183 cache_entries.begin(); it != cache_entries.end(); ++it) { 184 ASSERT_EQ(1U, found_cache_entries.count(it->first)); 185 EXPECT_EQ(it->second.md5(), found_cache_entries[it->first].md5()); 186 } 187 } 188 189 TEST_F(ResourceMetadataStorageTest, PutCacheEntry) { 190 FileCacheEntry entry; 191 const std::string key1 = "abcdefg"; 192 const std::string key2 = "abcd"; 193 const std::string md5_1 = "foo"; 194 const std::string md5_2 = "bar"; 195 196 // Put cache entries. 197 entry.set_md5(md5_1); 198 EXPECT_TRUE(storage_->PutCacheEntry(key1, entry)); 199 entry.set_md5(md5_2); 200 EXPECT_TRUE(storage_->PutCacheEntry(key2, entry)); 201 202 // Get cache entires. 203 EXPECT_TRUE(storage_->GetCacheEntry(key1, &entry)); 204 EXPECT_EQ(md5_1, entry.md5()); 205 EXPECT_TRUE(storage_->GetCacheEntry(key2, &entry)); 206 EXPECT_EQ(md5_2, entry.md5()); 207 208 // Remove cache entries. 209 EXPECT_TRUE(storage_->RemoveCacheEntry(key1)); 210 EXPECT_FALSE(storage_->GetCacheEntry(key1, &entry)); 211 212 EXPECT_TRUE(storage_->RemoveCacheEntry(key2)); 213 EXPECT_FALSE(storage_->GetCacheEntry(key2, &entry)); 214 } 215 216 TEST_F(ResourceMetadataStorageTest, CacheEntryIterator) { 217 // Prepare data. 218 std::map<std::string, FileCacheEntry> entries; 219 FileCacheEntry cache_entry; 220 221 cache_entry.set_md5("aA"); 222 entries["entry1"] = cache_entry; 223 cache_entry.set_md5("bB"); 224 entries["entry2"] = cache_entry; 225 cache_entry.set_md5("cC"); 226 entries["entry3"] = cache_entry; 227 cache_entry.set_md5("dD"); 228 entries["entry4"] = cache_entry; 229 230 for (std::map<std::string, FileCacheEntry>::iterator it = entries.begin(); 231 it != entries.end(); ++it) 232 EXPECT_TRUE(storage_->PutCacheEntry(it->first, it->second)); 233 234 // Insert some dummy entries. 235 ResourceEntry entry; 236 entry.set_resource_id("entry1"); 237 EXPECT_TRUE(storage_->PutEntry(entry)); 238 entry.set_resource_id("entry2"); 239 EXPECT_TRUE(storage_->PutEntry(entry)); 240 241 // Iterate and check the result. 242 scoped_ptr<ResourceMetadataStorage::CacheEntryIterator> it = 243 storage_->GetCacheEntryIterator(); 244 ASSERT_TRUE(it); 245 size_t num_entries = 0; 246 for (; !it->IsAtEnd(); it->Advance()) { 247 EXPECT_EQ(1U, entries.count(it->GetID())); 248 EXPECT_EQ(entries[it->GetID()].md5(), it->GetValue().md5()); 249 ++num_entries; 250 } 251 EXPECT_FALSE(it->HasError()); 252 EXPECT_EQ(entries.size(), num_entries); 253 } 254 255 TEST_F(ResourceMetadataStorageTest, GetChildren) { 256 const std::string parents_id[] = { "mercury", "venus", "mars", "jupiter", 257 "saturn" }; 258 std::vector<std::vector<std::pair<std::string, std::string> > > 259 children_name_id(arraysize(parents_id)); 260 // Skip children_name_id[0/1] here because Mercury and Venus have no moon. 261 children_name_id[2].push_back(std::make_pair("phobos", "mars_i")); 262 children_name_id[2].push_back(std::make_pair("deimos", "mars_ii")); 263 children_name_id[3].push_back(std::make_pair("io", "jupiter_i")); 264 children_name_id[3].push_back(std::make_pair("europa", "jupiter_ii")); 265 children_name_id[3].push_back(std::make_pair("ganymede", "jupiter_iii")); 266 children_name_id[3].push_back(std::make_pair("calisto", "jupiter_iv")); 267 children_name_id[4].push_back(std::make_pair("mimas", "saturn_i")); 268 children_name_id[4].push_back(std::make_pair("enceladus", "saturn_ii")); 269 children_name_id[4].push_back(std::make_pair("tethys", "saturn_iii")); 270 children_name_id[4].push_back(std::make_pair("dione", "saturn_iv")); 271 children_name_id[4].push_back(std::make_pair("rhea", "saturn_v")); 272 children_name_id[4].push_back(std::make_pair("titan", "saturn_vi")); 273 children_name_id[4].push_back(std::make_pair("iapetus", "saturn_vii")); 274 275 // Put parents. 276 for (size_t i = 0; i < arraysize(parents_id); ++i) { 277 ResourceEntry entry; 278 entry.set_resource_id(parents_id[i]); 279 EXPECT_TRUE(storage_->PutEntry(entry)); 280 } 281 282 // Put children. 283 for (size_t i = 0; i < children_name_id.size(); ++i) { 284 for (size_t j = 0; j < children_name_id[i].size(); ++j) { 285 ResourceEntry entry; 286 entry.set_parent_resource_id(parents_id[i]); 287 entry.set_base_name(children_name_id[i][j].first); 288 entry.set_resource_id(children_name_id[i][j].second); 289 EXPECT_TRUE(storage_->PutEntry(entry)); 290 } 291 } 292 293 // Put some dummy cache entries. 294 for (size_t i = 0; i < arraysize(parents_id); ++i) { 295 FileCacheEntry cache_entry; 296 EXPECT_TRUE(storage_->PutCacheEntry(parents_id[i], cache_entry)); 297 } 298 299 // Try to get children. 300 for (size_t i = 0; i < children_name_id.size(); ++i) { 301 std::vector<std::string> children; 302 storage_->GetChildren(parents_id[i], &children); 303 EXPECT_EQ(children_name_id[i].size(), children.size()); 304 for (size_t j = 0; j < children_name_id[i].size(); ++j) { 305 EXPECT_EQ(1, std::count(children.begin(), 306 children.end(), 307 children_name_id[i][j].second)); 308 } 309 } 310 } 311 312 TEST_F(ResourceMetadataStorageTest, OpenExistingDB) { 313 const std::string parent_id1 = "abcdefg"; 314 const std::string child_name1 = "WXYZABC"; 315 const std::string child_id1 = "qwerty"; 316 317 ResourceEntry entry1; 318 entry1.set_resource_id(parent_id1); 319 ResourceEntry entry2; 320 entry2.set_resource_id(child_id1); 321 entry2.set_parent_resource_id(parent_id1); 322 entry2.set_base_name(child_name1); 323 324 // Put some data. 325 EXPECT_TRUE(storage_->PutEntry(entry1)); 326 EXPECT_TRUE(storage_->PutEntry(entry2)); 327 328 // Close DB and reopen. 329 storage_.reset(new ResourceMetadataStorage( 330 temp_dir_.path(), base::MessageLoopProxy::current().get())); 331 ASSERT_TRUE(storage_->Initialize()); 332 333 // Can read data. 334 ResourceEntry result; 335 ASSERT_TRUE(storage_->GetEntry(parent_id1, &result)); 336 EXPECT_EQ(parent_id1, result.resource_id()); 337 338 ASSERT_TRUE(storage_->GetEntry(child_id1, &result)); 339 EXPECT_EQ(child_id1, result.resource_id()); 340 EXPECT_EQ(parent_id1, result.parent_resource_id()); 341 EXPECT_EQ(child_name1, result.base_name()); 342 343 EXPECT_EQ(child_id1, storage_->GetChild(parent_id1, child_name1)); 344 } 345 346 TEST_F(ResourceMetadataStorageTest, IncompatibleDB) { 347 const int64 kLargestChangestamp = 1234567890; 348 const std::string key1 = "abcd"; 349 350 ResourceEntry entry; 351 entry.set_resource_id(key1); 352 353 // Put some data. 354 ResourceEntry result; 355 EXPECT_TRUE(storage_->SetLargestChangestamp(kLargestChangestamp)); 356 EXPECT_TRUE(storage_->PutEntry(entry)); 357 EXPECT_TRUE(storage_->GetEntry(key1, &result)); 358 359 // Set incompatible version and reopen DB. 360 SetDBVersion(ResourceMetadataStorage::kDBVersion - 1); 361 storage_.reset(new ResourceMetadataStorage( 362 temp_dir_.path(), base::MessageLoopProxy::current().get())); 363 ASSERT_TRUE(storage_->Initialize()); 364 365 // Data is erased because of the incompatible version. 366 EXPECT_EQ(0, storage_->GetLargestChangestamp()); 367 EXPECT_FALSE(storage_->GetEntry(key1, &result)); 368 } 369 370 TEST_F(ResourceMetadataStorageTest, WrongPath) { 371 // Create a file. 372 base::FilePath path; 373 ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), &path)); 374 375 storage_.reset(new ResourceMetadataStorage( 376 path, base::MessageLoopProxy::current().get())); 377 // Cannot initialize DB beacause the path does not point a directory. 378 ASSERT_FALSE(storage_->Initialize()); 379 } 380 381 TEST_F(ResourceMetadataStorageTest, CheckValidity) { 382 const std::string key1 = "foo"; 383 const std::string name1 = "hoge"; 384 const std::string key2 = "bar"; 385 const std::string name2 = "fuga"; 386 const std::string key3 = "boo"; 387 const std::string name3 = "piyo"; 388 389 // Empty storage is valid. 390 EXPECT_TRUE(CheckValidity()); 391 392 // Put entry with key1. 393 ResourceEntry entry; 394 entry.set_resource_id(key1); 395 entry.set_base_name(name1); 396 EXPECT_TRUE(storage_->PutEntry(entry)); 397 EXPECT_TRUE(CheckValidity()); 398 399 // Put entry with key2 under key1. 400 entry.set_resource_id(key2); 401 entry.set_parent_resource_id(key1); 402 entry.set_base_name(name2); 403 EXPECT_TRUE(storage_->PutEntry(entry)); 404 EXPECT_TRUE(CheckValidity()); 405 406 RemoveChild(key1, name2); 407 EXPECT_FALSE(CheckValidity()); // Missing parent-child relationship. 408 409 // Add back parent-child relationship between key1 and key2. 410 PutChild(key1, name2, key2); 411 EXPECT_TRUE(CheckValidity()); 412 413 // Add parent-child relationship between key2 and key3. 414 PutChild(key2, name3, key3); 415 EXPECT_FALSE(CheckValidity()); // key3 is not stored in the storage. 416 417 // Put entry with key3 under key2. 418 entry.set_resource_id(key3); 419 entry.set_parent_resource_id(key2); 420 entry.set_base_name(name3); 421 EXPECT_TRUE(storage_->PutEntry(entry)); 422 EXPECT_TRUE(CheckValidity()); 423 424 // Parent-child relationship with wrong name. 425 RemoveChild(key2, name3); 426 EXPECT_FALSE(CheckValidity()); 427 PutChild(key2, name2, key3); 428 EXPECT_FALSE(CheckValidity()); 429 430 // Fix up the relationship between key2 and key3. 431 RemoveChild(key2, name2); 432 EXPECT_FALSE(CheckValidity()); 433 PutChild(key2, name3, key3); 434 EXPECT_TRUE(CheckValidity()); 435 436 // Add some cache entries. 437 FileCacheEntry cache_entry; 438 EXPECT_TRUE(storage_->PutCacheEntry(key1, cache_entry)); 439 EXPECT_TRUE(storage_->PutCacheEntry(key2, cache_entry)); 440 441 // Remove key2. 442 RemoveChild(key1, name2); 443 EXPECT_FALSE(CheckValidity()); 444 EXPECT_TRUE(storage_->RemoveEntry(key2)); 445 EXPECT_FALSE(CheckValidity()); 446 447 // Remove key3. 448 RemoveChild(key2, name3); 449 EXPECT_FALSE(CheckValidity()); 450 EXPECT_TRUE(storage_->RemoveEntry(key3)); 451 EXPECT_TRUE(CheckValidity()); 452 453 // Remove key1. 454 EXPECT_TRUE(storage_->RemoveEntry(key1)); 455 EXPECT_TRUE(CheckValidity()); 456 } 457 458 } // namespace internal 459 } // namespace drive 460