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