Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / drive / file_cache_unittest.cc
1 // Copyright (c) 2012 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/file_cache.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/callback_helpers.h"
11 #include "base/file_util.h"
12 #include "base/files/file_enumerator.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/md5.h"
15 #include "base/path_service.h"
16 #include "base/run_loop.h"
17 #include "base/threading/sequenced_worker_pool.h"
18 #include "chrome/browser/chromeos/drive/drive.pb.h"
19 #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h"
20 #include "chrome/browser/chromeos/drive/file_system_util.h"
21 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h"
22 #include "chrome/browser/chromeos/drive/test_util.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/test/test_browser_thread_bundle.h"
25 #include "google_apis/drive/test_util.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27
28 namespace drive {
29 namespace internal {
30 namespace {
31
32 const char kCacheFileDirectory[] = "files";
33
34 // Bitmask of cache states in FileCacheEntry.
35 enum TestFileCacheState {
36   TEST_CACHE_STATE_NONE       = 0,
37   TEST_CACHE_STATE_PINNED     = 1 << 0,
38   TEST_CACHE_STATE_PRESENT    = 1 << 1,
39   TEST_CACHE_STATE_DIRTY      = 1 << 2,
40 };
41
42 }  // namespace
43
44 // Tests FileCache methods from UI thread. It internally uses a real blocking
45 // pool and tests the interaction among threads.
46 // TODO(hashimoto): remove this class. crbug.com/231221.
47 class FileCacheTestOnUIThread : public testing::Test {
48  protected:
49   FileCacheTestOnUIThread() : expected_error_(FILE_ERROR_OK),
50                               expected_cache_state_(0) {
51   }
52
53   virtual void SetUp() OVERRIDE {
54     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
55     const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta");
56     const base::FilePath cache_dir =
57         temp_dir_.path().AppendASCII(kCacheFileDirectory);
58
59     ASSERT_TRUE(base::CreateDirectory(metadata_dir));
60     ASSERT_TRUE(base::CreateDirectory(cache_dir));
61
62     ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
63                                                &dummy_file_path_));
64     fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
65
66     scoped_refptr<base::SequencedWorkerPool> pool =
67         content::BrowserThread::GetBlockingPool();
68     blocking_task_runner_ =
69         pool->GetSequencedTaskRunner(pool->GetSequenceToken());
70
71     metadata_storage_.reset(new ResourceMetadataStorage(
72         metadata_dir,
73         blocking_task_runner_.get()));
74
75     bool success = false;
76     base::PostTaskAndReplyWithResult(
77         blocking_task_runner_.get(),
78         FROM_HERE,
79         base::Bind(&ResourceMetadataStorage::Initialize,
80                    base::Unretained(metadata_storage_.get())),
81         google_apis::test_util::CreateCopyResultCallback(&success));
82     test_util::RunBlockingPoolTask();
83     ASSERT_TRUE(success);
84
85     cache_.reset(new FileCache(
86         metadata_storage_.get(),
87         cache_dir,
88         blocking_task_runner_.get(),
89         fake_free_disk_space_getter_.get()));
90
91     success = false;
92     base::PostTaskAndReplyWithResult(
93         blocking_task_runner_.get(),
94         FROM_HERE,
95         base::Bind(&FileCache::Initialize, base::Unretained(cache_.get())),
96         google_apis::test_util::CreateCopyResultCallback(&success));
97     test_util::RunBlockingPoolTask();
98     ASSERT_TRUE(success);
99   }
100
101   void TestStoreToCache(const std::string& id,
102                         const std::string& md5,
103                         const base::FilePath& source_path,
104                         FileError expected_error,
105                         int expected_cache_state) {
106     expected_error_ = expected_error;
107     expected_cache_state_ = expected_cache_state;
108
109     FileError error = FILE_ERROR_OK;
110     base::PostTaskAndReplyWithResult(
111         blocking_task_runner_,
112         FROM_HERE,
113         base::Bind(&internal::FileCache::Store,
114                    base::Unretained(cache_.get()),
115                    id, md5, source_path,
116                    FileCache::FILE_OPERATION_COPY),
117         google_apis::test_util::CreateCopyResultCallback(&error));
118     test_util::RunBlockingPoolTask();
119
120     if (error == FILE_ERROR_OK) {
121       FileCacheEntry cache_entry;
122       EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &cache_entry));
123       EXPECT_EQ(md5, cache_entry.md5());
124     }
125
126     VerifyCacheFileState(error, id);
127   }
128
129   void TestRemoveFromCache(const std::string& id, FileError expected_error) {
130     expected_error_ = expected_error;
131
132     FileError error = FILE_ERROR_OK;
133     base::PostTaskAndReplyWithResult(
134         blocking_task_runner_,
135         FROM_HERE,
136         base::Bind(&internal::FileCache::Remove,
137                    base::Unretained(cache_.get()),
138                    id),
139         google_apis::test_util::CreateCopyResultCallback(&error));
140     test_util::RunBlockingPoolTask();
141     VerifyRemoveFromCache(error, id);
142   }
143
144   void VerifyRemoveFromCache(FileError error, const std::string& id) {
145     EXPECT_EQ(expected_error_, error);
146
147     FileCacheEntry cache_entry;
148     if (!GetCacheEntryFromOriginThread(id, &cache_entry)) {
149       EXPECT_EQ(FILE_ERROR_OK, error);
150
151       const base::FilePath path = cache_->GetCacheFilePath(id);
152       EXPECT_FALSE(base::PathExists(path));
153     }
154   }
155
156   void TestPin(const std::string& id,
157                FileError expected_error,
158                int expected_cache_state) {
159     expected_error_ = expected_error;
160     expected_cache_state_ = expected_cache_state;
161
162     FileError error = FILE_ERROR_OK;
163     base::PostTaskAndReplyWithResult(
164         blocking_task_runner_,
165         FROM_HERE,
166         base::Bind(&internal::FileCache::Pin,
167                    base::Unretained(cache_.get()),
168                    id),
169         google_apis::test_util::CreateCopyResultCallback(&error));
170     test_util::RunBlockingPoolTask();
171     VerifyCacheFileState(error, id);
172   }
173
174   void TestUnpin(const std::string& id,
175                  FileError expected_error,
176                  int expected_cache_state) {
177     expected_error_ = expected_error;
178     expected_cache_state_ = expected_cache_state;
179
180     FileError error = FILE_ERROR_OK;
181     base::PostTaskAndReplyWithResult(
182         blocking_task_runner_,
183         FROM_HERE,
184         base::Bind(&internal::FileCache::Unpin,
185                    base::Unretained(cache_.get()),
186                    id),
187         google_apis::test_util::CreateCopyResultCallback(&error));
188     test_util::RunBlockingPoolTask();
189     VerifyCacheFileState(error, id);
190   }
191
192   void TestMarkAsMounted(const std::string& id,
193                          FileError expected_error,
194                          int expected_cache_state) {
195     expected_error_ = expected_error;
196     expected_cache_state_ = expected_cache_state;
197
198     FileCacheEntry entry;
199     EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &entry));
200
201     FileError error = FILE_ERROR_OK;
202     base::FilePath cache_file_path;
203
204     base::PostTaskAndReplyWithResult(
205         blocking_task_runner_.get(),
206         FROM_HERE,
207         base::Bind(&FileCache::MarkAsMounted,
208                    base::Unretained(cache_.get()),
209                    id,
210                    &cache_file_path),
211         google_apis::test_util::CreateCopyResultCallback(&error));
212     test_util::RunBlockingPoolTask();
213
214     EXPECT_TRUE(base::PathExists(cache_file_path));
215     EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(id));
216   }
217
218   void TestMarkAsUnmounted(const std::string& id,
219                            const base::FilePath& file_path,
220                            FileError expected_error,
221                            int expected_cache_state) {
222     expected_error_ = expected_error;
223     expected_cache_state_ = expected_cache_state;
224
225     FileError error = FILE_ERROR_OK;
226     base::PostTaskAndReplyWithResult(
227         blocking_task_runner_.get(),
228         FROM_HERE,
229         base::Bind(&FileCache::MarkAsUnmounted,
230                    base::Unretained(cache_.get()),
231                    file_path),
232         google_apis::test_util::CreateCopyResultCallback(&error));
233     test_util::RunBlockingPoolTask();
234
235     base::FilePath cache_file_path;
236     base::PostTaskAndReplyWithResult(
237         blocking_task_runner_,
238         FROM_HERE,
239         base::Bind(&FileCache::GetFile,
240                    base::Unretained(cache_.get()),
241                    id, &cache_file_path),
242         google_apis::test_util::CreateCopyResultCallback(&error));
243     test_util::RunBlockingPoolTask();
244     EXPECT_EQ(FILE_ERROR_OK, error);
245
246     EXPECT_TRUE(base::PathExists(cache_file_path));
247     EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(id));
248   }
249
250   void VerifyCacheFileState(FileError error, const std::string& id) {
251     EXPECT_EQ(expected_error_, error);
252
253     // Verify cache map.
254     FileCacheEntry cache_entry;
255     const bool cache_entry_found =
256         GetCacheEntryFromOriginThread(id, &cache_entry);
257     if ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) ||
258         (expected_cache_state_ & TEST_CACHE_STATE_PINNED)) {
259       ASSERT_TRUE(cache_entry_found);
260       EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_PINNED) != 0,
261                 cache_entry.is_pinned());
262       EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) != 0,
263                 cache_entry.is_present());
264       EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_DIRTY) != 0,
265                 cache_entry.is_dirty());
266     } else {
267       EXPECT_FALSE(cache_entry_found);
268     }
269
270     // Verify actual cache file.
271     base::FilePath dest_path = cache_->GetCacheFilePath(id);
272     EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) != 0,
273               base::PathExists(dest_path));
274   }
275
276   // Helper function to call GetCacheEntry from origin thread.
277   bool GetCacheEntryFromOriginThread(const std::string& id,
278                                      FileCacheEntry* cache_entry) {
279     bool result = false;
280     base::PostTaskAndReplyWithResult(
281         blocking_task_runner_,
282         FROM_HERE,
283         base::Bind(&internal::FileCache::GetCacheEntry,
284                    base::Unretained(cache_.get()),
285                    id,
286                    cache_entry),
287         google_apis::test_util::CreateCopyResultCallback(&result));
288     test_util::RunBlockingPoolTask();
289     return result;
290   }
291
292   // Returns true if the cache entry exists for the given ID.
293   bool CacheEntryExists(const std::string& id) {
294     FileCacheEntry cache_entry;
295     return GetCacheEntryFromOriginThread(id, &cache_entry);
296   }
297
298   // Returns the number of the cache files with name <id>, and Confirm
299   // that they have the <md5>. This should return 1 or 0.
300   size_t CountCacheFiles(const std::string& id, const std::string& md5) {
301     base::FilePath path = cache_->GetCacheFilePath(id);
302     base::FileEnumerator enumerator(path.DirName(),
303                                     false,  // recursive
304                                     base::FileEnumerator::FILES,
305                                     path.BaseName().value());
306     size_t num_files_found = 0;
307     for (base::FilePath current = enumerator.Next(); !current.empty();
308          current = enumerator.Next()) {
309       ++num_files_found;
310       EXPECT_EQ(util::EscapeCacheFileName(id),
311                 current.BaseName().AsUTF8Unsafe());
312     }
313     return num_files_found;
314   }
315
316   content::TestBrowserThreadBundle thread_bundle_;
317   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
318   base::ScopedTempDir temp_dir_;
319   base::FilePath dummy_file_path_;
320
321   scoped_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests>
322       metadata_storage_;
323   scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
324   scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
325
326   FileError expected_error_;
327   int expected_cache_state_;
328   std::string expected_file_extension_;
329 };
330
331 TEST_F(FileCacheTestOnUIThread, StoreToCacheSimple) {
332   std::string id("pdf:1a2b");
333   std::string md5("abcdef0123456789");
334
335   // Store an existing file.
336   TestStoreToCache(id, md5, dummy_file_path_,
337                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
338
339   // Store a non-existent file to the same |id| and |md5|.
340   TestStoreToCache(id, md5,
341                    base::FilePath::FromUTF8Unsafe("non_existent_file"),
342                    FILE_ERROR_FAILED,
343                    TEST_CACHE_STATE_PRESENT);
344
345   // Store a different existing file to the same |id| but different
346   // |md5|.
347   md5 = "new_md5";
348   TestStoreToCache(id, md5, dummy_file_path_,
349                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
350
351   // Verify that there's only one file with name <id>, i.e. previously
352   // cached file with the different md5 should be deleted.
353   EXPECT_EQ(1U, CountCacheFiles(id, md5));
354
355   // Passing empty MD5 marks the entry as dirty.
356   TestStoreToCache(id, std::string(), dummy_file_path_, FILE_ERROR_OK,
357                    TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
358 }
359
360 TEST_F(FileCacheTestOnUIThread, RemoveFromCacheSimple) {
361   std::string id("pdf:1a2b");
362   std::string md5("abcdef0123456789");
363   // First store a file to cache.
364   TestStoreToCache(id, md5, dummy_file_path_,
365                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
366
367   // Then try to remove existing file from cache.
368   TestRemoveFromCache(id, FILE_ERROR_OK);
369
370   // Repeat using non-alphanumeric characters for ID, including '.'
371   // which is an extension separator.
372   id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?";
373   TestStoreToCache(id, md5, dummy_file_path_,
374                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
375
376   TestRemoveFromCache(id, FILE_ERROR_OK);
377 }
378
379 TEST_F(FileCacheTestOnUIThread, PinAndUnpin) {
380   std::string id("pdf:1a2b");
381   std::string md5("abcdef0123456789");
382
383   // First store a file to cache.
384   TestStoreToCache(id, md5, dummy_file_path_,
385                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
386
387   // Pin the existing file in cache.
388   TestPin(id, FILE_ERROR_OK,
389           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
390
391   // Unpin the existing file in cache.
392   TestUnpin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
393
394   // Pin back the same existing file in cache.
395   TestPin(id, FILE_ERROR_OK,
396           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
397
398   // Pin a non-existent file in cache.
399   id = "document:1a2b";
400
401   TestPin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
402
403   // Unpin the previously pinned non-existent file in cache.
404   TestUnpin(id, FILE_ERROR_OK, TEST_CACHE_STATE_NONE);
405
406   // Unpin a file that doesn't exist in cache and is not pinned, i.e. cache
407   // has zero knowledge of the file.
408   id = "not-in-cache:1a2b";
409
410   TestUnpin(id, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
411 }
412
413 TEST_F(FileCacheTestOnUIThread, StoreToCachePinned) {
414   std::string id("pdf:1a2b");
415   std::string md5("abcdef0123456789");
416
417   // Pin a non-existent file.
418   TestPin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
419
420   // Store an existing file to a previously pinned file.
421   TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_OK,
422                    TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
423
424   // Store a non-existent file to a previously pinned and stored file.
425   TestStoreToCache(id, md5,
426                    base::FilePath::FromUTF8Unsafe("non_existent_file"),
427                    FILE_ERROR_FAILED,
428                    TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
429 }
430
431 TEST_F(FileCacheTestOnUIThread, RemoveFromCachePinned) {
432   std::string id("pdf:1a2b");
433   std::string md5("abcdef0123456789");
434
435   // Store a file to cache, and pin it.
436   TestStoreToCache(id, md5, dummy_file_path_,
437                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
438   TestPin(id, FILE_ERROR_OK,
439           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
440
441   // Remove |id| from cache.
442   TestRemoveFromCache(id, FILE_ERROR_OK);
443
444   // Use non-alphanumeric characters for ID, including '.'
445   // which is an extension separator.
446   id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?";
447
448   TestStoreToCache(id, md5, dummy_file_path_,
449                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
450   TestPin(id, FILE_ERROR_OK,
451           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
452
453   TestRemoveFromCache(id, FILE_ERROR_OK);
454 }
455
456 TEST_F(FileCacheTestOnUIThread, MountUnmount) {
457   std::string id("pdf:1a2b");
458   std::string md5("abcdef0123456789");
459
460   // First store a file to cache.
461   TestStoreToCache(id, md5, dummy_file_path_,
462                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
463
464   // Mark the file mounted.
465   TestMarkAsMounted(id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
466   EXPECT_TRUE(CacheEntryExists(id));
467
468   // Try to remove the file.
469   TestRemoveFromCache(id, FILE_ERROR_IN_USE);
470
471   // Clear mounted state of the file.
472   base::FilePath file_path;
473   FileError error = FILE_ERROR_FAILED;
474   base::PostTaskAndReplyWithResult(
475       blocking_task_runner_,
476       FROM_HERE,
477       base::Bind(&FileCache::GetFile,
478                  base::Unretained(cache_.get()),
479                  id, &file_path),
480       google_apis::test_util::CreateCopyResultCallback(&error));
481   test_util::RunBlockingPoolTask();
482   EXPECT_EQ(FILE_ERROR_OK, error);
483
484   TestMarkAsUnmounted(id, file_path, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
485   EXPECT_TRUE(CacheEntryExists(id));
486
487   // Try to remove the file.
488   TestRemoveFromCache(id, FILE_ERROR_OK);
489 }
490
491 TEST_F(FileCacheTestOnUIThread, StoreToCacheNoSpace) {
492   fake_free_disk_space_getter_->set_default_value(0);
493
494   std::string id("pdf:1a2b");
495   std::string md5("abcdef0123456789");
496
497   // Try to store an existing file.
498   TestStoreToCache(id, md5, dummy_file_path_,
499                    FILE_ERROR_NO_LOCAL_SPACE,
500                    TEST_CACHE_STATE_NONE);
501
502   // Verify that there's no files added.
503   EXPECT_EQ(0U, CountCacheFiles(id, md5));
504 }
505
506 TEST_F(FileCacheTestOnUIThread, UpdatePinnedCache) {
507   std::string id("pdf:1a2b");
508   std::string md5("abcdef0123456789");
509   std::string md5_modified("aaaaaa0000000000");
510
511   // Store an existing file.
512   TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_OK,
513                    TEST_CACHE_STATE_PRESENT);
514
515   // Pin the file.
516   TestPin(id, FILE_ERROR_OK,
517           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
518
519   // Store the file with a modified content and md5. It should stay pinned.
520   TestStoreToCache(id, md5_modified, dummy_file_path_, FILE_ERROR_OK,
521                    TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
522 }
523
524 // Tests FileCache methods working with the blocking task runner.
525 class FileCacheTest : public testing::Test {
526  protected:
527   virtual void SetUp() OVERRIDE {
528     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
529     const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta");
530     cache_files_dir_ = temp_dir_.path().AppendASCII(kCacheFileDirectory);
531
532     ASSERT_TRUE(base::CreateDirectory(metadata_dir));
533     ASSERT_TRUE(base::CreateDirectory(cache_files_dir_));
534
535     fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
536
537     metadata_storage_.reset(new ResourceMetadataStorage(
538         metadata_dir,
539         base::MessageLoopProxy::current().get()));
540     ASSERT_TRUE(metadata_storage_->Initialize());
541
542     cache_.reset(new FileCache(
543         metadata_storage_.get(),
544         cache_files_dir_,
545         base::MessageLoopProxy::current().get(),
546         fake_free_disk_space_getter_.get()));
547     ASSERT_TRUE(cache_->Initialize());
548   }
549
550   static bool RenameCacheFilesToNewFormat(FileCache* cache) {
551     return cache->RenameCacheFilesToNewFormat();
552   }
553
554   content::TestBrowserThreadBundle thread_bundle_;
555   base::ScopedTempDir temp_dir_;
556   base::FilePath cache_files_dir_;
557
558   scoped_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests>
559       metadata_storage_;
560   scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
561   scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
562 };
563
564 TEST_F(FileCacheTest, RecoverFilesFromCacheDirectory) {
565   base::FilePath dir_source_root;
566   EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &dir_source_root));
567   const base::FilePath src_path =
568       dir_source_root.AppendASCII("chrome/test/data/chromeos/drive/image.png");
569
570   // Store files. This file should not be moved.
571   EXPECT_EQ(FILE_ERROR_OK, cache_->Store("id_foo", "md5", src_path,
572                                          FileCache::FILE_OPERATION_COPY));
573
574   // Set up files in the cache directory. These files should be moved.
575   const base::FilePath file_directory =
576       temp_dir_.path().AppendASCII(kCacheFileDirectory);
577   ASSERT_TRUE(base::CopyFile(src_path, file_directory.AppendASCII("id_bar")));
578   ASSERT_TRUE(base::CopyFile(src_path, file_directory.AppendASCII("id_baz")));
579
580   // Insert a dirty entry with "id_baz" to |recovered_cache_info|.
581   // This should not prevent the file from being recovered.
582   ResourceMetadataStorage::RecoveredCacheInfoMap recovered_cache_info;
583   recovered_cache_info["id_baz"].is_dirty = true;
584   recovered_cache_info["id_baz"].title = "baz.png";
585
586   // Recover files.
587   const base::FilePath dest_directory = temp_dir_.path().AppendASCII("dest");
588   EXPECT_TRUE(cache_->RecoverFilesFromCacheDirectory(dest_directory,
589                                                      recovered_cache_info));
590
591   // Only two files should be recovered.
592   EXPECT_TRUE(base::PathExists(dest_directory));
593   // base::FileEnumerator does not guarantee the order.
594   if (base::PathExists(dest_directory.AppendASCII("baz00000001.png"))) {
595     EXPECT_TRUE(base::ContentsEqual(
596         src_path,
597         dest_directory.AppendASCII("baz00000001.png")));
598     EXPECT_TRUE(base::ContentsEqual(
599         src_path,
600         dest_directory.AppendASCII("image00000002.png")));
601   } else {
602     EXPECT_TRUE(base::ContentsEqual(
603         src_path,
604         dest_directory.AppendASCII("image00000001.png")));
605     EXPECT_TRUE(base::ContentsEqual(
606         src_path,
607         dest_directory.AppendASCII("baz00000002.png")));
608   }
609   EXPECT_FALSE(base::PathExists(
610       dest_directory.AppendASCII("image00000003.png")));
611 }
612
613 TEST_F(FileCacheTest, Iterator) {
614   base::FilePath src_file;
615   ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
616
617   // Prepare entries.
618   std::map<std::string, std::string> md5s;
619   md5s["id1"] = "md5-1";
620   md5s["id2"] = "md5-2";
621   md5s["id3"] = "md5-3";
622   md5s["id4"] = "md5-4";
623   for (std::map<std::string, std::string>::iterator it = md5s.begin();
624        it != md5s.end(); ++it) {
625     EXPECT_EQ(FILE_ERROR_OK, cache_->Store(
626         it->first, it->second, src_file, FileCache::FILE_OPERATION_COPY));
627   }
628
629   // Iterate.
630   std::map<std::string, std::string> result;
631   scoped_ptr<FileCache::Iterator> it = cache_->GetIterator();
632   for (; !it->IsAtEnd(); it->Advance())
633     result[it->GetID()] = it->GetValue().md5();
634   EXPECT_EQ(md5s, result);
635   EXPECT_FALSE(it->HasError());
636 }
637
638 TEST_F(FileCacheTest, FreeDiskSpaceIfNeededFor) {
639   base::FilePath src_file;
640   ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
641
642   // Store a file as a 'temporary' file and remember the path.
643   const std::string id_tmp = "id_tmp", md5_tmp = "md5_tmp";
644   ASSERT_EQ(FILE_ERROR_OK,
645             cache_->Store(id_tmp, md5_tmp, src_file,
646                           FileCache::FILE_OPERATION_COPY));
647   base::FilePath tmp_path;
648   ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_tmp, &tmp_path));
649
650   // Store a file as a pinned file and remember the path.
651   const std::string id_pinned = "id_pinned", md5_pinned = "md5_pinned";
652   ASSERT_EQ(FILE_ERROR_OK,
653             cache_->Store(id_pinned, md5_pinned, src_file,
654                           FileCache::FILE_OPERATION_COPY));
655   ASSERT_EQ(FILE_ERROR_OK, cache_->Pin(id_pinned));
656   base::FilePath pinned_path;
657   ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_pinned, &pinned_path));
658
659   // Call FreeDiskSpaceIfNeededFor().
660   fake_free_disk_space_getter_->set_default_value(test_util::kLotsOfSpace);
661   fake_free_disk_space_getter_->PushFakeValue(0);
662   const int64 kNeededBytes = 1;
663   EXPECT_TRUE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes));
664
665   // Only 'temporary' file gets removed.
666   FileCacheEntry entry;
667   EXPECT_FALSE(cache_->GetCacheEntry(id_tmp, &entry));
668   EXPECT_FALSE(base::PathExists(tmp_path));
669
670   EXPECT_TRUE(cache_->GetCacheEntry(id_pinned, &entry));
671   EXPECT_TRUE(base::PathExists(pinned_path));
672
673   // Returns false when disk space cannot be freed.
674   fake_free_disk_space_getter_->set_default_value(0);
675   EXPECT_FALSE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes));
676 }
677
678 TEST_F(FileCacheTest, GetFile) {
679   const base::FilePath src_file_path = temp_dir_.path().Append("test.dat");
680   const std::string src_contents = "test";
681   EXPECT_TRUE(google_apis::test_util::WriteStringToFile(src_file_path,
682                                                         src_contents));
683   std::string id("id1");
684   std::string md5(base::MD5String(src_contents));
685
686   const base::FilePath cache_file_directory =
687       temp_dir_.path().AppendASCII(kCacheFileDirectory);
688
689   // Try to get an existing file from cache.
690   EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, md5, src_file_path,
691                                          FileCache::FILE_OPERATION_COPY));
692   base::FilePath cache_file_path;
693   EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path));
694   EXPECT_EQ(
695       cache_file_directory.AppendASCII(util::EscapeCacheFileName(id)).value(),
696       cache_file_path.value());
697
698   std::string contents;
699   EXPECT_TRUE(base::ReadFileToString(cache_file_path, &contents));
700   EXPECT_EQ(src_contents, contents);
701
702   // Get file from cache with different id.
703   id = "id2";
704   EXPECT_EQ(FILE_ERROR_NOT_FOUND, cache_->GetFile(id, &cache_file_path));
705
706   // Pin a non-existent file.
707   EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(id));
708
709   // Get the non-existent pinned file from cache.
710   EXPECT_EQ(FILE_ERROR_NOT_FOUND, cache_->GetFile(id, &cache_file_path));
711
712   // Get a previously pinned and stored file from cache.
713   EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, md5, src_file_path,
714                                          FileCache::FILE_OPERATION_COPY));
715
716   EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path));
717   EXPECT_EQ(
718       cache_file_directory.AppendASCII(util::EscapeCacheFileName(id)).value(),
719       cache_file_path.value());
720
721   contents.clear();
722   EXPECT_TRUE(base::ReadFileToString(cache_file_path, &contents));
723   EXPECT_EQ(src_contents, contents);
724 }
725
726 TEST_F(FileCacheTest, OpenForWrite) {
727   // Prepare a file.
728   base::FilePath src_file;
729   ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
730
731   const std::string id = "id";
732   ASSERT_EQ(FILE_ERROR_OK, cache_->Store(id, "md5", src_file,
733                                          FileCache::FILE_OPERATION_COPY));
734
735   // Entry is not dirty nor opened.
736   EXPECT_FALSE(cache_->IsOpenedForWrite(id));
737   FileCacheEntry entry;
738   EXPECT_TRUE(cache_->GetCacheEntry(id, &entry));
739   EXPECT_FALSE(entry.is_dirty());
740
741   // Open (1).
742   scoped_ptr<base::ScopedClosureRunner> file_closer1;
743   EXPECT_EQ(FILE_ERROR_OK, cache_->OpenForWrite(id, &file_closer1));
744   EXPECT_TRUE(cache_->IsOpenedForWrite(id));
745
746   // Entry is dirty.
747   EXPECT_TRUE(cache_->GetCacheEntry(id, &entry));
748   EXPECT_TRUE(entry.is_dirty());
749
750   // Open (2).
751   scoped_ptr<base::ScopedClosureRunner> file_closer2;
752   EXPECT_EQ(FILE_ERROR_OK, cache_->OpenForWrite(id, &file_closer2));
753   EXPECT_TRUE(cache_->IsOpenedForWrite(id));
754
755   // Close (1).
756   file_closer1.reset();
757   EXPECT_TRUE(cache_->IsOpenedForWrite(id));
758
759   // Close (2).
760   file_closer2.reset();
761   EXPECT_FALSE(cache_->IsOpenedForWrite(id));
762
763   // Try to open non-existent file.
764   EXPECT_EQ(FILE_ERROR_NOT_FOUND,
765             cache_->OpenForWrite("nonexistent_id", &file_closer1));
766 }
767
768 TEST_F(FileCacheTest, UpdateMd5) {
769   // Store test data.
770   const base::FilePath src_file_path = temp_dir_.path().Append("test.dat");
771   const std::string contents_before = "before";
772   EXPECT_TRUE(google_apis::test_util::WriteStringToFile(src_file_path,
773                                                         contents_before));
774   std::string id("id1");
775   EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, base::MD5String(contents_before),
776                                          src_file_path,
777                                          FileCache::FILE_OPERATION_COPY));
778
779   // Modify the cache file.
780   scoped_ptr<base::ScopedClosureRunner> file_closer;
781   EXPECT_EQ(FILE_ERROR_OK, cache_->OpenForWrite(id, &file_closer));
782   base::FilePath cache_file_path;
783   EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path));
784   const std::string contents_after = "after";
785   EXPECT_TRUE(google_apis::test_util::WriteStringToFile(cache_file_path,
786                                                         contents_after));
787
788   // Cannot update MD5 of an opend file.
789   EXPECT_EQ(FILE_ERROR_IN_USE, cache_->UpdateMd5(id));
790
791   // Close file.
792   file_closer.reset();
793
794   // MD5 was cleared by OpenForWrite().
795   FileCacheEntry entry;
796   EXPECT_TRUE(cache_->GetCacheEntry(id, &entry));
797   EXPECT_TRUE(entry.md5().empty());
798
799   // Update MD5.
800   EXPECT_EQ(FILE_ERROR_OK, cache_->UpdateMd5(id));
801   EXPECT_TRUE(cache_->GetCacheEntry(id, &entry));
802   EXPECT_EQ(base::MD5String(contents_after), entry.md5());
803 }
804
805 TEST_F(FileCacheTest, ClearDirty) {
806   // Prepare a file.
807   base::FilePath src_file;
808   ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
809
810   const std::string id = "id";
811   ASSERT_EQ(FILE_ERROR_OK, cache_->Store(id, "md5", src_file,
812                                          FileCache::FILE_OPERATION_COPY));
813
814   // Open the file.
815   scoped_ptr<base::ScopedClosureRunner> file_closer;
816   EXPECT_EQ(FILE_ERROR_OK, cache_->OpenForWrite(id, &file_closer));
817
818   // Entry is dirty.
819   FileCacheEntry entry;
820   EXPECT_TRUE(cache_->GetCacheEntry(id, &entry));
821   EXPECT_TRUE(entry.is_dirty());
822
823   // Cannot clear the dirty bit of an opened entry.
824   EXPECT_EQ(FILE_ERROR_IN_USE, cache_->ClearDirty(id));
825
826   // Close the file and clear the dirty bit.
827   file_closer.reset();
828   EXPECT_EQ(FILE_ERROR_OK, cache_->ClearDirty(id));
829
830   // Entry is not dirty.
831   EXPECT_TRUE(cache_->GetCacheEntry(id, &entry));
832   EXPECT_FALSE(entry.is_dirty());
833 }
834
835 TEST_F(FileCacheTest, RenameCacheFilesToNewFormat) {
836   const base::FilePath file_directory =
837       temp_dir_.path().AppendASCII(kCacheFileDirectory);
838
839   // File with an old style "<prefix>:<ID>.<MD5>" name.
840   ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
841       file_directory.AppendASCII("file:id_koo.md5"), "koo"));
842
843   // File with multiple extensions should be removed.
844   ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
845       file_directory.AppendASCII("id_kyu.md5.mounted"), "kyu (mounted)"));
846   ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
847       file_directory.AppendASCII("id_kyu.md5"), "kyu"));
848
849   // Rename and verify the result.
850   EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get()));
851   std::string contents;
852   EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"),
853                                      &contents));
854   EXPECT_EQ("koo", contents);
855   contents.clear();
856   EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"),
857                                      &contents));
858   EXPECT_EQ("kyu", contents);
859
860   // Rename again.
861   EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get()));
862
863   // Files with new style names are not affected.
864   contents.clear();
865   EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"),
866                                      &contents));
867   EXPECT_EQ("koo", contents);
868   contents.clear();
869   EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"),
870                                      &contents));
871   EXPECT_EQ("kyu", contents);
872 }
873
874 TEST_F(FileCacheTest, ClearAll) {
875   const std::string id("pdf:1a2b");
876   const std::string md5("abcdef0123456789");
877
878   // Store an existing file.
879   base::FilePath src_file;
880   ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
881   ASSERT_EQ(FILE_ERROR_OK,
882             cache_->Store(id, md5, src_file, FileCache::FILE_OPERATION_COPY));
883
884   // Verify that the cache entry is created.
885   FileCacheEntry cache_entry;
886   ASSERT_TRUE(cache_->GetCacheEntry(id, &cache_entry));
887
888   // Clear cache.
889   EXPECT_TRUE(cache_->ClearAll());
890
891   // Verify that the cache is removed.
892   EXPECT_FALSE(cache_->GetCacheEntry(id, &cache_entry));
893   EXPECT_TRUE(base::IsDirectoryEmpty(cache_files_dir_));
894 }
895
896 }  // namespace internal
897 }  // namespace drive