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