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