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