OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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 "storage/browser/blob/blob_memory_controller.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/files/file_util.h" |
| 9 #include "base/message_loop/message_loop.h" |
| 10 #include "base/run_loop.h" |
| 11 #include "base/test/test_simple_task_runner.h" |
| 12 #include "base/threading/thread_task_runner_handle.h" |
| 13 #include "storage/browser/blob/blob_data_builder.h" |
| 14 #include "storage/browser/blob/blob_data_item.h" |
| 15 #include "storage/browser/blob/shareable_blob_data_item.h" |
| 16 #include "storage/common/data_element.h" |
| 17 #include "testing/gtest/include/gtest/gtest.h" |
| 18 |
| 19 namespace storage { |
| 20 |
| 21 using Strategy = BlobMemoryController::Strategy; |
| 22 using FileCreationInfo = BlobMemoryController::FileCreationInfo; |
| 23 using base::TestSimpleTaskRunner; |
| 24 using ItemState = ShareableBlobDataItem::State; |
| 25 |
| 26 const std::string kBlobStorageDirectory = "blob_storage"; |
| 27 const size_t kTestBlobStorageIPCThresholdBytes = 20; |
| 28 const size_t kTestBlobStorageMaxSharedMemoryBytes = 50; |
| 29 const size_t kTestBlobStorageMaxBlobMemorySize = 400; |
| 30 const size_t kTestBlobStorageInFlightMemory = 10; |
| 31 const uint64_t kTestBlobStorageMaxDiskSpace = 1000; |
| 32 const uint64_t kTestBlobStorageMinFileSizeBytes = 10; |
| 33 const uint64_t kTestBlobStorageMaxFileSizeBytes = 100; |
| 34 |
| 35 class BlobMemoryControllerTest : public testing::Test { |
| 36 protected: |
| 37 BlobMemoryControllerTest() {} |
| 38 |
| 39 void SetUp() override { |
| 40 ASSERT_EQ(true, base::CreateNewTempDirectory("blob_storage", &temp_dir_)); |
| 41 }; |
| 42 |
| 43 void TearDown() override { |
| 44 files_created_.clear(); |
| 45 // Make sure we clean up the files. |
| 46 base::RunLoop().RunUntilIdle(); |
| 47 file_runner_->RunPendingTasks(); |
| 48 base::RunLoop().RunUntilIdle(); |
| 49 ASSERT_EQ(true, base::DeleteFile(temp_dir_, true)); |
| 50 } |
| 51 |
| 52 std::vector<scoped_refptr<ShareableBlobDataItem>> CreateSharedDataItems( |
| 53 const BlobDataBuilder& builder, |
| 54 const std::vector<ItemState>& states, |
| 55 std::vector<ShareableBlobDataItem*>* pointers_out) { |
| 56 std::vector<scoped_refptr<ShareableBlobDataItem>> result; |
| 57 EXPECT_EQ(builder.items_.size(), states.size()); |
| 58 for (size_t i = 0; i < builder.items_.size(); ++i) { |
| 59 result.push_back(make_scoped_refptr(new ShareableBlobDataItem( |
| 60 builder.uuid(), builder.items_[i], states[i]))); |
| 61 pointers_out->push_back(result.back().get()); |
| 62 } |
| 63 return result; |
| 64 } |
| 65 |
| 66 void SetTestMemoryLimits(BlobMemoryController* controller) { |
| 67 BlobStorageLimits limits; |
| 68 limits.max_ipc_memory_size = kTestBlobStorageIPCThresholdBytes; |
| 69 limits.max_shared_memory_size = kTestBlobStorageMaxSharedMemoryBytes; |
| 70 limits.max_blob_in_memory_space = kTestBlobStorageMaxBlobMemorySize; |
| 71 limits.in_flight_space = kTestBlobStorageInFlightMemory; |
| 72 limits.max_blob_disk_space = kTestBlobStorageMaxDiskSpace; |
| 73 limits.min_page_file_size = kTestBlobStorageMinFileSizeBytes; |
| 74 limits.max_file_size = kTestBlobStorageMaxFileSizeBytes; |
| 75 controller->SetLimitsForTesting(limits); |
| 76 } |
| 77 |
| 78 void SaveFileCreationInfo(bool success, std::vector<FileCreationInfo> info) { |
| 79 file_quota_result_ = success; |
| 80 if (success) |
| 81 files_created_.swap(info); |
| 82 } |
| 83 |
| 84 void SaveMemoryRequest(bool success) { memory_quota_result_ = success; } |
| 85 |
| 86 BlobMemoryController::FileQuotaRequestCallback GetFileCreationCallback() { |
| 87 return base::Bind(&BlobMemoryControllerTest::SaveFileCreationInfo, |
| 88 base::Unretained(this)); |
| 89 } |
| 90 |
| 91 BlobMemoryController::MemoryQuotaRequestCallback GetMemoryRequestCallback() { |
| 92 return base::Bind(&BlobMemoryControllerTest::SaveMemoryRequest, |
| 93 base::Unretained(this)); |
| 94 } |
| 95 |
| 96 bool file_quota_result_ = false; |
| 97 base::FilePath temp_dir_; |
| 98 std::vector<FileCreationInfo> files_created_; |
| 99 bool memory_quota_result_ = false; |
| 100 |
| 101 scoped_refptr<TestSimpleTaskRunner> file_runner_ = new TestSimpleTaskRunner(); |
| 102 |
| 103 base::MessageLoop fake_io_message_loop_; |
| 104 }; |
| 105 |
| 106 TEST_F(BlobMemoryControllerTest, Strategy) { |
| 107 BlobMemoryController controller; |
| 108 SetTestMemoryLimits(&controller); |
| 109 |
| 110 // No transportation needed. |
| 111 EXPECT_EQ(Strategy::NONE_NEEDED, controller.DetermineStrategy(0, 0)); |
| 112 |
| 113 // IPC. |
| 114 EXPECT_EQ(Strategy::IPC, |
| 115 controller.DetermineStrategy(0, kTestBlobStorageIPCThresholdBytes)); |
| 116 |
| 117 // Shared Memory. |
| 118 EXPECT_EQ(Strategy::SHARED_MEMORY, |
| 119 controller.DetermineStrategy(kTestBlobStorageIPCThresholdBytes, |
| 120 kTestBlobStorageMaxSharedMemoryBytes)); |
| 121 |
| 122 // Not too large, as disk isn't enabled. |
| 123 EXPECT_EQ( |
| 124 Strategy::SHARED_MEMORY, |
| 125 controller.DetermineStrategy(0, kTestBlobStorageMaxBlobMemorySize + |
| 126 kTestBlobStorageInFlightMemory)); |
| 127 |
| 128 // Too large. |
| 129 EXPECT_EQ( |
| 130 Strategy::TOO_LARGE, |
| 131 controller.DetermineStrategy(0, kTestBlobStorageMaxBlobMemorySize + |
| 132 kTestBlobStorageInFlightMemory + 1)); |
| 133 |
| 134 // Enable disk, and check file strategies. |
| 135 controller.EnableFilePaging(temp_dir_, file_runner_); |
| 136 EXPECT_EQ(Strategy::FILE, controller.DetermineStrategy( |
| 137 0, kTestBlobStorageMaxBlobMemorySize + 1)); |
| 138 |
| 139 // Too large for disk. |
| 140 controller.EnableFilePaging(temp_dir_, file_runner_); |
| 141 EXPECT_EQ(Strategy::TOO_LARGE, |
| 142 controller.DetermineStrategy(0, kTestBlobStorageMaxDiskSpace + 1)); |
| 143 } |
| 144 |
| 145 TEST_F(BlobMemoryControllerTest, GrantMemory) { |
| 146 const std::string kId = "id"; |
| 147 BlobMemoryController controller; |
| 148 SetTestMemoryLimits(&controller); |
| 149 |
| 150 BlobDataBuilder builder(kId); |
| 151 builder.AppendFutureData(10); |
| 152 builder.AppendFutureData(20); |
| 153 builder.AppendFutureData(30); |
| 154 |
| 155 std::vector<ShareableBlobDataItem*> pointers; |
| 156 std::vector<scoped_refptr<ShareableBlobDataItem>> items = |
| 157 CreateSharedDataItems(builder, |
| 158 {ItemState::QUOTA_NEEDED, ItemState::QUOTA_NEEDED, |
| 159 ItemState::QUOTA_NEEDED}, |
| 160 &pointers); |
| 161 |
| 162 controller.ReserveMemoryQuota(pointers, GetMemoryRequestCallback()); |
| 163 EXPECT_EQ(true, memory_quota_result_); |
| 164 EXPECT_EQ(ItemState::QUOTA_GRANTED, items[0]->state()); |
| 165 EXPECT_EQ(ItemState::QUOTA_GRANTED, items[1]->state()); |
| 166 EXPECT_EQ(ItemState::QUOTA_GRANTED, items[2]->state()); |
| 167 } |
| 168 |
| 169 TEST_F(BlobMemoryControllerTest, GrantMemoryWithPagingFileWaiting) { |
| 170 const std::string kId = "id"; |
| 171 BlobMemoryController controller; |
| 172 SetTestMemoryLimits(&controller); |
| 173 |
| 174 char kData[kTestBlobStorageMaxBlobMemorySize]; |
| 175 std::memset(kData, kTestBlobStorageMaxBlobMemorySize, 'e'); |
| 176 |
| 177 // Add memory item that is the memory quota. |
| 178 BlobDataBuilder builder(kId); |
| 179 builder.AppendFutureData(kTestBlobStorageMaxBlobMemorySize); |
| 180 |
| 181 std::vector<ShareableBlobDataItem*> pointers; |
| 182 std::vector<scoped_refptr<ShareableBlobDataItem>> items = |
| 183 CreateSharedDataItems(builder, {ItemState::QUOTA_NEEDED}, &pointers); |
| 184 |
| 185 controller.ReserveMemoryQuota(pointers, GetMemoryRequestCallback()); |
| 186 EXPECT_EQ(true, memory_quota_result_); |
| 187 memory_quota_result_ = false; |
| 188 EXPECT_EQ(ItemState::QUOTA_GRANTED, items[0]->state()); |
| 189 |
| 190 // Enable disk. |
| 191 controller.EnableFilePaging(temp_dir_, file_runner_); |
| 192 |
| 193 // Create an item that is just a little too big. |
| 194 BlobDataBuilder builder2(kId); |
| 195 builder2.AppendFutureData(kTestBlobStorageInFlightMemory + 1); |
| 196 |
| 197 // Reserve memory, which should request successfuly but we can't fit it yet |
| 198 // (no callback). |
| 199 pointers.clear(); |
| 200 std::vector<scoped_refptr<ShareableBlobDataItem>> items2 = |
| 201 CreateSharedDataItems(builder2, {ItemState::QUOTA_NEEDED}, &pointers); |
| 202 controller.ReserveMemoryQuota(pointers, GetMemoryRequestCallback()); |
| 203 |
| 204 EXPECT_EQ(false, memory_quota_result_); |
| 205 EXPECT_EQ(ItemState::QUOTA_REQUESTED, items2[0]->state()); |
| 206 EXPECT_FALSE(file_runner_->HasPendingTask()); |
| 207 |
| 208 // Add our original item as populated so it's paged to disk. |
| 209 items[0]->item()->data_element_ptr()->SetToBytes( |
| 210 kData, kTestBlobStorageMaxBlobMemorySize); |
| 211 items[0]->set_state(ItemState::POPULATED_WITH_QUOTA); |
| 212 controller.UpdateBlobItemInRecents(items[0].get()); |
| 213 |
| 214 EXPECT_TRUE(file_runner_->HasPendingTask()); |
| 215 file_runner_->RunPendingTasks(); |
| 216 base::RunLoop().RunUntilIdle(); |
| 217 EXPECT_EQ(ItemState::QUOTA_GRANTED, items2[0]->state()); |
| 218 EXPECT_EQ(DataElement::TYPE_FILE, items[0]->item()->type()); |
| 219 } |
| 220 |
| 221 // TODO(dmurph): |
| 222 // * Write test for memory request when quota is available. |
| 223 // * Write test for memory request that fails because disk isn't enabled. |
| 224 // * Write test for memory request when quota isn't available and we're waiting |
| 225 // on disk paging. |
| 226 // * Same as above but then we cancel it. |
| 227 // * Write test for memory request that's pending, and then disk is disabled. |
| 228 // * Write test for file request where we have quota. |
| 229 // * Write test for file request where we don't have quota (and just fail). |
| 230 // * Write test for file request and then we disable disk. |
| 231 |
| 232 } // namespace storage |
OLD | NEW |