file_cache_unittest.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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/run_loop.h"
15#include "base/strings/string_util.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
27namespace drive {
28namespace internal {
29namespace {
30
31const char kCacheFileDirectory[] = "files";
32
33// Bitmask of cache states in FileCacheEntry.
34enum 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.
46class 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
376TEST_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
401TEST_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
420TEST_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
454TEST_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
472TEST_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
497TEST_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
513TEST_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
534TEST_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
575TEST_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
599TEST_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
626TEST_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
647TEST_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
682TEST_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
697TEST_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.
716class 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
755TEST_F(FileCacheTest, ScanCacheFile) {
756  // Set up files in the cache directory.
757  const base::FilePath file_directory =
758      temp_dir_.path().AppendASCII(kCacheFileDirectory);
759  ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
760      file_directory.AppendASCII("id_foo"), "foo"));
761
762  // Remove the existing DB.
763  const base::FilePath metadata_directory =
764      temp_dir_.path().AppendASCII("meta");
765  ASSERT_TRUE(base::DeleteFile(metadata_directory, true /* recursive */));
766
767  // Create a new cache and initialize it.
768  metadata_storage_.reset(new ResourceMetadataStorage(
769      metadata_directory, base::MessageLoopProxy::current().get()));
770  ASSERT_TRUE(metadata_storage_->Initialize());
771
772  cache_.reset(new FileCache(metadata_storage_.get(),
773                             file_directory,
774                             base::MessageLoopProxy::current().get(),
775                             fake_free_disk_space_getter_.get()));
776  ASSERT_TRUE(cache_->Initialize());
777
778  // Check contents of the cache.
779  FileCacheEntry cache_entry;
780  EXPECT_TRUE(cache_->GetCacheEntry("id_foo", &cache_entry));
781  EXPECT_TRUE(cache_entry.is_present());
782  EXPECT_EQ(base::MD5String("foo"), cache_entry.md5());
783}
784
785TEST_F(FileCacheTest, Iterator) {
786  base::FilePath src_file;
787  ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
788
789  // Prepare entries.
790  std::map<std::string, std::string> md5s;
791  md5s["id1"] = "md5-1";
792  md5s["id2"] = "md5-2";
793  md5s["id3"] = "md5-3";
794  md5s["id4"] = "md5-4";
795  for (std::map<std::string, std::string>::iterator it = md5s.begin();
796       it != md5s.end(); ++it) {
797    EXPECT_EQ(FILE_ERROR_OK, cache_->Store(
798        it->first, it->second, src_file, FileCache::FILE_OPERATION_COPY));
799  }
800
801  // Iterate.
802  std::map<std::string, std::string> result;
803  scoped_ptr<FileCache::Iterator> it = cache_->GetIterator();
804  for (; !it->IsAtEnd(); it->Advance())
805    result[it->GetID()] = it->GetValue().md5();
806  EXPECT_EQ(md5s, result);
807  EXPECT_FALSE(it->HasError());
808}
809
810TEST_F(FileCacheTest, FreeDiskSpaceIfNeededFor) {
811  base::FilePath src_file;
812  ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
813
814  // Store a file as a 'temporary' file and remember the path.
815  const std::string id_tmp = "id_tmp", md5_tmp = "md5_tmp";
816  ASSERT_EQ(FILE_ERROR_OK,
817            cache_->Store(id_tmp, md5_tmp, src_file,
818                          FileCache::FILE_OPERATION_COPY));
819  base::FilePath tmp_path;
820  ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_tmp, &tmp_path));
821
822  // Store a file as a pinned file and remember the path.
823  const std::string id_pinned = "id_pinned", md5_pinned = "md5_pinned";
824  ASSERT_EQ(FILE_ERROR_OK,
825            cache_->Store(id_pinned, md5_pinned, src_file,
826                          FileCache::FILE_OPERATION_COPY));
827  ASSERT_EQ(FILE_ERROR_OK, cache_->Pin(id_pinned));
828  base::FilePath pinned_path;
829  ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_pinned, &pinned_path));
830
831  // Call FreeDiskSpaceIfNeededFor().
832  fake_free_disk_space_getter_->set_default_value(test_util::kLotsOfSpace);
833  fake_free_disk_space_getter_->PushFakeValue(0);
834  const int64 kNeededBytes = 1;
835  EXPECT_TRUE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes));
836
837  // Only 'temporary' file gets removed.
838  FileCacheEntry entry;
839  EXPECT_FALSE(cache_->GetCacheEntry(id_tmp, &entry));
840  EXPECT_FALSE(base::PathExists(tmp_path));
841
842  EXPECT_TRUE(cache_->GetCacheEntry(id_pinned, &entry));
843  EXPECT_TRUE(base::PathExists(pinned_path));
844
845  // Returns false when disk space cannot be freed.
846  fake_free_disk_space_getter_->set_default_value(0);
847  EXPECT_FALSE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes));
848}
849
850TEST_F(FileCacheTest, GetFile) {
851  const base::FilePath src_file_path = temp_dir_.path().Append("test.dat");
852  const std::string src_contents = "test";
853  EXPECT_TRUE(google_apis::test_util::WriteStringToFile(src_file_path,
854                                                        src_contents));
855  std::string id("id1");
856  std::string md5(base::MD5String(src_contents));
857
858  const base::FilePath cache_file_directory =
859      temp_dir_.path().AppendASCII(kCacheFileDirectory);
860
861  // Try to get an existing file from cache.
862  EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, md5, src_file_path,
863                                         FileCache::FILE_OPERATION_COPY));
864  base::FilePath cache_file_path;
865  EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path));
866  EXPECT_EQ(
867      cache_file_directory.AppendASCII(util::EscapeCacheFileName(id)).value(),
868      cache_file_path.value());
869
870  std::string contents;
871  EXPECT_TRUE(base::ReadFileToString(cache_file_path, &contents));
872  EXPECT_EQ(src_contents, contents);
873
874  // Get file from cache with different id.
875  id = "id2";
876  EXPECT_EQ(FILE_ERROR_NOT_FOUND, cache_->GetFile(id, &cache_file_path));
877
878  // Pin a non-existent file.
879  EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(id));
880
881  // Get the non-existent pinned file from cache.
882  EXPECT_EQ(FILE_ERROR_NOT_FOUND, cache_->GetFile(id, &cache_file_path));
883
884  // Get a previously pinned and stored file from cache.
885  EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, md5, src_file_path,
886                                         FileCache::FILE_OPERATION_COPY));
887
888  EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path));
889  EXPECT_EQ(
890      cache_file_directory.AppendASCII(util::EscapeCacheFileName(id)).value(),
891      cache_file_path.value());
892
893  contents.clear();
894  EXPECT_TRUE(base::ReadFileToString(cache_file_path, &contents));
895  EXPECT_EQ(src_contents, contents);
896}
897
898TEST_F(FileCacheTest, CanonicalizeIDs) {
899  ResourceIdCanonicalizer id_canonicalizer = base::Bind(
900      (ResourceIdCanonicalizer::RunType*)(&StringToUpperASCII));
901  const std::string id("abc");
902  const std::string md5("abcdef0123456789");
903
904  const base::FilePath file_directory =
905      temp_dir_.path().AppendASCII(kCacheFileDirectory);
906
907  // Store a file to the cache.
908  base::FilePath file;
909  EXPECT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), &file));
910  EXPECT_EQ(FILE_ERROR_OK,
911            cache_->Store(id, md5, file, FileCache::FILE_OPERATION_COPY));
912
913  // Canonicalize IDs.
914  EXPECT_TRUE(cache_->CanonicalizeIDs(id_canonicalizer));
915
916  const std::string canonicalized_id = id_canonicalizer.Run(id);
917  FileCacheEntry entry;
918  EXPECT_FALSE(cache_->GetCacheEntry(id, &entry));
919  EXPECT_TRUE(cache_->GetCacheEntry(canonicalized_id, &entry));
920}
921
922TEST_F(FileCacheTest, RenameCacheFilesToNewFormat) {
923  const base::FilePath file_directory =
924      temp_dir_.path().AppendASCII(kCacheFileDirectory);
925
926  // File with an old style "<prefix>:<ID>.<MD5>" name.
927  ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
928      file_directory.AppendASCII("file:id_koo.md5"), "koo"));
929
930  // File with multiple extensions should be removed.
931  ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
932      file_directory.AppendASCII("id_kyu.md5.mounted"), "kyu (mounted)"));
933  ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
934      file_directory.AppendASCII("id_kyu.md5"), "kyu"));
935
936  // Rename and verify the result.
937  EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get()));
938  std::string contents;
939  EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"),
940                                     &contents));
941  EXPECT_EQ("koo", contents);
942  contents.clear();
943  EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"),
944                                     &contents));
945  EXPECT_EQ("kyu", contents);
946
947  // Rename again.
948  EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get()));
949
950  // Files with new style names are not affected.
951  contents.clear();
952  EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"),
953                                     &contents));
954  EXPECT_EQ("koo", contents);
955  contents.clear();
956  EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"),
957                                     &contents));
958  EXPECT_EQ("kyu", contents);
959}
960
961TEST_F(FileCacheTest, ClearAll) {
962  const std::string id("pdf:1a2b");
963  const std::string md5("abcdef0123456789");
964
965  // Store an existing file.
966  base::FilePath src_file;
967  ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
968  ASSERT_EQ(FILE_ERROR_OK,
969            cache_->Store(id, md5, src_file, FileCache::FILE_OPERATION_COPY));
970
971  // Verify that the cache entry is created.
972  FileCacheEntry cache_entry;
973  ASSERT_TRUE(cache_->GetCacheEntry(id, &cache_entry));
974
975  // Clear cache.
976  EXPECT_TRUE(cache_->ClearAll());
977
978  // Verify that the cache is removed.
979  EXPECT_FALSE(cache_->GetCacheEntry(id, &cache_entry));
980  EXPECT_TRUE(file_util::IsDirectoryEmpty(cache_files_dir_));
981}
982
983}  // namespace internal
984}  // namespace drive
985