Home | History | Annotate | Download | only in apps
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <algorithm>
      6 
      7 #include "apps/saved_files_service.h"
      8 #include "base/files/file_path.h"
      9 #include "base/strings/string_number_conversions.h"
     10 #include "base/test/values_test_util.h"
     11 #include "base/values.h"
     12 #include "chrome/browser/extensions/extension_prefs.h"
     13 #include "chrome/browser/extensions/extension_service.h"
     14 #include "chrome/browser/extensions/extension_system.h"
     15 #include "chrome/browser/extensions/test_extension_environment.h"
     16 #include "chrome/test/base/testing_profile.h"
     17 #include "extensions/common/extension.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 
     20 #if !defined(OS_ANDROID)
     21 
     22 #define TRACE_CALL(expression) \
     23   do {                         \
     24     SCOPED_TRACE(#expression); \
     25     expression;                \
     26   } while (0)
     27 
     28 using apps::SavedFileEntry;
     29 using apps::SavedFilesService;
     30 
     31 namespace {
     32 
     33 std::string GenerateId(int i) {
     34   return base::IntToString(i) + ":filename.ext";
     35 }
     36 
     37 }  // namespace
     38 
     39 class SavedFilesServiceUnitTest : public testing::Test {
     40  protected:
     41   virtual void SetUp() OVERRIDE {
     42     testing::Test::SetUp();
     43     extension_ = env_.MakeExtension(*base::test::ParseJson(
     44         "{"
     45         "  \"app\": {"
     46         "    \"background\": {"
     47         "      \"scripts\": [\"background.js\"]"
     48         "    }"
     49         "  },"
     50         "  \"permissions\": ["
     51         "    {\"fileSystem\": [\"retainEntries\"]}"
     52         "  ]"
     53         "}"));
     54     service_ = SavedFilesService::Get(env_.profile());
     55     path_ = base::FilePath(FILE_PATH_LITERAL("filename.ext"));
     56   }
     57 
     58   virtual void TearDown() OVERRIDE {
     59     SavedFilesService::ClearMaxSequenceNumberForTest();
     60     SavedFilesService::ClearLruSizeForTest();
     61     testing::Test::TearDown();
     62   }
     63 
     64   // Check that a registered file entry has the correct value.
     65   void CheckEntrySequenceNumber(int id, int sequence_number) {
     66     std::string id_string = GenerateId(id);
     67     SCOPED_TRACE(id_string);
     68     EXPECT_TRUE(service_->IsRegistered(extension_->id(), id_string));
     69     const SavedFileEntry* entry =
     70         service_->GetFileEntry(extension_->id(), id_string);
     71     ASSERT_TRUE(entry);
     72     EXPECT_EQ(id_string, entry->id);
     73     EXPECT_EQ(path_, entry->path);
     74     EXPECT_TRUE(entry->is_directory);
     75     EXPECT_EQ(sequence_number, entry->sequence_number);
     76   }
     77 
     78   // Check that a range of registered file entries have the correct values.
     79   void CheckRangeEnqueuedInOrder(int start, int end) {
     80     SavedFileEntry entry;
     81     for (int i = start; i < end; i++) {
     82       CheckEntrySequenceNumber(i, i + 1);
     83     }
     84   }
     85 
     86   extensions::TestExtensionEnvironment env_;
     87   const extensions::Extension* extension_;
     88   SavedFilesService* service_;
     89   base::FilePath path_;
     90 };
     91 
     92 TEST_F(SavedFilesServiceUnitTest, RetainTwoFilesTest) {
     93   service_->RegisterFileEntry(extension_->id(), GenerateId(1), path_, true);
     94   service_->RegisterFileEntry(extension_->id(), GenerateId(2), path_, true);
     95   service_->RegisterFileEntry(extension_->id(), GenerateId(3), path_, true);
     96 
     97   // Test that no entry has a sequence number.
     98   TRACE_CALL(CheckEntrySequenceNumber(1, 0));
     99   TRACE_CALL(CheckEntrySequenceNumber(2, 0));
    100   TRACE_CALL(CheckEntrySequenceNumber(3, 0));
    101 
    102   // Test that only entry #1 has a sequence number.
    103   service_->EnqueueFileEntry(extension_->id(), GenerateId(1));
    104   TRACE_CALL(CheckEntrySequenceNumber(1, 1));
    105   TRACE_CALL(CheckEntrySequenceNumber(2, 0));
    106 
    107   // Test that entry #1 has not changed sequence number because it is the most
    108   // recently enqueued entry.
    109   service_->EnqueueFileEntry(extension_->id(), GenerateId(1));
    110   TRACE_CALL(CheckEntrySequenceNumber(1, 1));
    111   TRACE_CALL(CheckEntrySequenceNumber(2, 0));
    112 
    113   // Test that entry #1 is unchanged and entry #2 has been assigned the next
    114   // sequence number.
    115   service_->EnqueueFileEntry(extension_->id(), GenerateId(2));
    116   TRACE_CALL(CheckEntrySequenceNumber(1, 1));
    117   TRACE_CALL(CheckEntrySequenceNumber(2, 2));
    118 
    119   // Test that both entries #1 and #2 are unchanged because #2 is the most
    120   // recently enqueued entry.
    121   service_->EnqueueFileEntry(extension_->id(), GenerateId(2));
    122   TRACE_CALL(CheckEntrySequenceNumber(1, 1));
    123   TRACE_CALL(CheckEntrySequenceNumber(2, 2));
    124 
    125   // Test that entry #1 has been assigned the next sequence number.
    126   service_->EnqueueFileEntry(extension_->id(), GenerateId(1));
    127   TRACE_CALL(CheckEntrySequenceNumber(1, 3));
    128   TRACE_CALL(CheckEntrySequenceNumber(2, 2));
    129   TRACE_CALL(CheckEntrySequenceNumber(3, 0));
    130 
    131   EXPECT_FALSE(service_->IsRegistered(extension_->id(), "another id"));
    132   SavedFileEntry entry;
    133   EXPECT_FALSE(service_->GetFileEntry(extension_->id(), "another id"));
    134 
    135   // ClearQueueIfNoRetainPermission should be a no-op because the app has the
    136   // fileSystem.retainEntries permission.
    137   service_->ClearQueueIfNoRetainPermission(extension_);
    138   TRACE_CALL(CheckEntrySequenceNumber(1, 3));
    139   TRACE_CALL(CheckEntrySequenceNumber(2, 2));
    140   TRACE_CALL(CheckEntrySequenceNumber(3, 0));
    141 
    142   // Test that after a clear, retained file entries are unchanged, but file
    143   // entries that have been registered but not retained are no longer
    144   // registered.
    145   service_->Clear(extension_->id());
    146   TRACE_CALL(CheckEntrySequenceNumber(1, 3));
    147   TRACE_CALL(CheckEntrySequenceNumber(2, 2));
    148   EXPECT_FALSE(service_->IsRegistered(extension_->id(), GenerateId(3)));
    149 }
    150 
    151 TEST_F(SavedFilesServiceUnitTest, NoRetainEntriesPermissionTest) {
    152   extension_ = env_.MakeExtension(*base::test::ParseJson(
    153       "{\"app\": {\"background\": {\"scripts\": [\"background.js\"]}},"
    154       "\"permissions\": [\"fileSystem\"]}"));
    155   service_->RegisterFileEntry(extension_->id(), GenerateId(1), path_, true);
    156   TRACE_CALL(CheckEntrySequenceNumber(1, 0));
    157   SavedFileEntry entry;
    158   service_->EnqueueFileEntry(extension_->id(), GenerateId(1));
    159   TRACE_CALL(CheckEntrySequenceNumber(1, 1));
    160   EXPECT_FALSE(service_->IsRegistered(extension_->id(), "another id"));
    161   EXPECT_FALSE(service_->GetFileEntry(extension_->id(), "another id"));
    162 
    163   // ClearQueueIfNoRetainPermission should clear the queue, since the app does
    164   // not have the "retainEntries" permission.
    165   service_->ClearQueueIfNoRetainPermission(extension_);
    166   std::vector<SavedFileEntry> entries =
    167       service_->GetAllFileEntries(extension_->id());
    168   EXPECT_TRUE(entries.empty());
    169 }
    170 
    171 TEST_F(SavedFilesServiceUnitTest, EvictionTest) {
    172   SavedFilesService::SetLruSizeForTest(10);
    173   for (int i = 0; i < 10; i++) {
    174     service_->RegisterFileEntry(extension_->id(), GenerateId(i), path_, true);
    175     service_->EnqueueFileEntry(extension_->id(), GenerateId(i));
    176   }
    177   service_->RegisterFileEntry(extension_->id(), GenerateId(10), path_, true);
    178 
    179   // Expect that entries 0 to 9 are in the queue, but 10 is not.
    180   TRACE_CALL(CheckRangeEnqueuedInOrder(0, 10));
    181   TRACE_CALL(CheckEntrySequenceNumber(10, 0));
    182   service_->EnqueueFileEntry(extension_->id(), GenerateId(10));
    183 
    184   // Expect that entries 1 to 10 are in the queue, but entry 0 is not.
    185   TRACE_CALL(CheckEntrySequenceNumber(0, 0));
    186   TRACE_CALL(CheckRangeEnqueuedInOrder(1, 11));
    187 
    188   // Check that retained entries are unchanged after a clear.
    189   service_->Clear(extension_->id());
    190   SavedFileEntry entry;
    191   EXPECT_FALSE(service_->GetFileEntry(extension_->id(), GenerateId(0)));
    192   TRACE_CALL(CheckRangeEnqueuedInOrder(1, 11));
    193 
    194   // Expect that entry 2 is now at the back of the queue, and no further entries
    195   // have been evicted.
    196   service_->EnqueueFileEntry(extension_->id(), GenerateId(2));
    197   TRACE_CALL(CheckEntrySequenceNumber(2, 12));
    198   TRACE_CALL(CheckRangeEnqueuedInOrder(1, 1));
    199   TRACE_CALL(CheckRangeEnqueuedInOrder(3, 11));
    200 
    201   // Check that retained entries are unchanged after a clear.
    202   service_->Clear(extension_->id());
    203   TRACE_CALL(CheckEntrySequenceNumber(2, 12));
    204   TRACE_CALL(CheckRangeEnqueuedInOrder(1, 1));
    205   TRACE_CALL(CheckRangeEnqueuedInOrder(3, 11));
    206 }
    207 
    208 TEST_F(SavedFilesServiceUnitTest, SequenceNumberCompactionTest) {
    209   SavedFilesService::SetMaxSequenceNumberForTest(8);
    210   SavedFilesService::SetLruSizeForTest(8);
    211   for (int i = 0; i < 4; i++) {
    212     service_->RegisterFileEntry(extension_->id(), GenerateId(i), path_, true);
    213     service_->EnqueueFileEntry(extension_->id(), GenerateId(i));
    214   }
    215   service_->EnqueueFileEntry(extension_->id(), GenerateId(2));
    216   service_->EnqueueFileEntry(extension_->id(), GenerateId(3));
    217   service_->EnqueueFileEntry(extension_->id(), GenerateId(2));
    218 
    219   // The sequence numbers should be sparse, as they have not gone over the
    220   // limit.
    221   TRACE_CALL(CheckEntrySequenceNumber(0, 1));
    222   TRACE_CALL(CheckEntrySequenceNumber(1, 2));
    223   TRACE_CALL(CheckEntrySequenceNumber(2, 7));
    224   TRACE_CALL(CheckEntrySequenceNumber(3, 6));
    225   service_->Clear(extension_->id());
    226   TRACE_CALL(CheckEntrySequenceNumber(0, 1));
    227   TRACE_CALL(CheckEntrySequenceNumber(1, 2));
    228   TRACE_CALL(CheckEntrySequenceNumber(2, 7));
    229   TRACE_CALL(CheckEntrySequenceNumber(3, 6));
    230 
    231   // This should push the sequence number to the limit of 8, and trigger a
    232   // sequence number compaction. Expect that the sequence numbers are
    233   // contiguous from 1 to 4.
    234   service_->EnqueueFileEntry(extension_->id(), GenerateId(3));
    235   TRACE_CALL(CheckRangeEnqueuedInOrder(0, 4));
    236   service_->Clear(extension_->id());
    237   TRACE_CALL(CheckRangeEnqueuedInOrder(0, 4));
    238 }
    239 #endif
    240