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