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