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 void SetItemState(ShareableBlobDataItem* item, ItemState state) { |
| 97 item->state_ = state; |
| 98 } |
| 99 |
| 100 bool file_quota_result_ = false; |
| 101 base::FilePath temp_dir_; |
| 102 std::vector<FileCreationInfo> files_created_; |
| 103 bool memory_quota_result_ = false; |
| 104 |
| 105 scoped_refptr<TestSimpleTaskRunner> file_runner_ = new TestSimpleTaskRunner(); |
| 106 |
| 107 base::MessageLoop fake_io_message_loop_; |
| 108 }; |
| 109 |
| 110 TEST_F(BlobMemoryControllerTest, Strategy) { |
| 111 BlobMemoryController controller; |
| 112 SetTestMemoryLimits(&controller); |
| 113 |
| 114 // No transportation needed. |
| 115 EXPECT_EQ(Strategy::NONE_NEEDED, controller.DetermineStrategy(0, 0)); |
| 116 |
| 117 // IPC. |
| 118 EXPECT_EQ(Strategy::IPC, |
| 119 controller.DetermineStrategy(0, kTestBlobStorageIPCThresholdBytes)); |
| 120 |
| 121 // Shared Memory. |
| 122 EXPECT_EQ(Strategy::SHARED_MEMORY, |
| 123 controller.DetermineStrategy(kTestBlobStorageIPCThresholdBytes, |
| 124 kTestBlobStorageMaxSharedMemoryBytes)); |
| 125 |
| 126 // Not too large, as disk isn't enabled. |
| 127 EXPECT_EQ( |
| 128 Strategy::SHARED_MEMORY, |
| 129 controller.DetermineStrategy(0, kTestBlobStorageMaxBlobMemorySize + |
| 130 kTestBlobStorageInFlightMemory)); |
| 131 |
| 132 // Too large. |
| 133 EXPECT_EQ( |
| 134 Strategy::TOO_LARGE, |
| 135 controller.DetermineStrategy(0, kTestBlobStorageMaxBlobMemorySize + |
| 136 kTestBlobStorageInFlightMemory + 1)); |
| 137 |
| 138 // Enable disk, and check file strategies. |
| 139 controller.EnableDisk(temp_dir_, file_runner_); |
| 140 EXPECT_EQ(Strategy::FILE, controller.DetermineStrategy( |
| 141 0, kTestBlobStorageMaxBlobMemorySize + 1)); |
| 142 |
| 143 // Too large for disk. |
| 144 controller.EnableDisk(temp_dir_, file_runner_); |
| 145 EXPECT_EQ(Strategy::TOO_LARGE, |
| 146 controller.DetermineStrategy(0, kTestBlobStorageMaxDiskSpace + 1)); |
| 147 } |
| 148 |
| 149 TEST_F(BlobMemoryControllerTest, GrantMemory) { |
| 150 const std::string kId = "id"; |
| 151 BlobMemoryController controller; |
| 152 SetTestMemoryLimits(&controller); |
| 153 |
| 154 BlobDataBuilder builder(kId); |
| 155 builder.AppendFutureData(10); |
| 156 builder.AppendFutureData(20); |
| 157 builder.AppendFutureData(30); |
| 158 |
| 159 std::vector<ShareableBlobDataItem*> pointers; |
| 160 std::vector<scoped_refptr<ShareableBlobDataItem>> items = |
| 161 CreateSharedDataItems(builder, |
| 162 {ItemState::QUOTA_NEEDED, ItemState::QUOTA_NEEDED, |
| 163 ItemState::QUOTA_NEEDED}, |
| 164 &pointers); |
| 165 |
| 166 controller.ReserveMemoryQuota(pointers, GetMemoryRequestCallback()); |
| 167 EXPECT_EQ(true, memory_quota_result_); |
| 168 EXPECT_EQ(ItemState::QUOTA_GRANTED, items[0]->state()); |
| 169 EXPECT_EQ(ItemState::QUOTA_GRANTED, items[1]->state()); |
| 170 EXPECT_EQ(ItemState::QUOTA_GRANTED, items[2]->state()); |
| 171 } |
| 172 |
| 173 TEST_F(BlobMemoryControllerTest, GrantMemoryWithPagingFileWaiting) { |
| 174 const std::string kId = "id"; |
| 175 BlobMemoryController controller; |
| 176 SetTestMemoryLimits(&controller); |
| 177 |
| 178 char kData[kTestBlobStorageMaxBlobMemorySize]; |
| 179 std::memset(kData, kTestBlobStorageMaxBlobMemorySize, 'e'); |
| 180 |
| 181 // Add memory item that is the memory quota. |
| 182 BlobDataBuilder builder(kId); |
| 183 builder.AppendFutureData(kTestBlobStorageMaxBlobMemorySize); |
| 184 |
| 185 std::vector<ShareableBlobDataItem*> pointers; |
| 186 std::vector<scoped_refptr<ShareableBlobDataItem>> items = |
| 187 CreateSharedDataItems(builder, {ItemState::QUOTA_NEEDED}, &pointers); |
| 188 |
| 189 controller.ReserveMemoryQuota(pointers, GetMemoryRequestCallback()); |
| 190 EXPECT_EQ(true, memory_quota_result_); |
| 191 memory_quota_result_ = false; |
| 192 EXPECT_EQ(ItemState::QUOTA_GRANTED, items[0]->state()); |
| 193 |
| 194 // Enable disk. |
| 195 controller.EnableDisk(temp_dir_, file_runner_); |
| 196 |
| 197 // Create an item that is just a little too big. |
| 198 BlobDataBuilder builder2(kId); |
| 199 builder2.AppendFutureData(kTestBlobStorageInFlightMemory + 1); |
| 200 |
| 201 // Reserve memory, which should request successfuly but we can't fit it yet |
| 202 // (no callback). |
| 203 pointers.clear(); |
| 204 std::vector<scoped_refptr<ShareableBlobDataItem>> items2 = |
| 205 CreateSharedDataItems(builder2, {ItemState::QUOTA_NEEDED}, &pointers); |
| 206 controller.ReserveMemoryQuota(pointers, GetMemoryRequestCallback()); |
| 207 |
| 208 EXPECT_EQ(false, memory_quota_result_); |
| 209 EXPECT_EQ(ItemState::QUOTA_REQUESTED, items2[0]->state()); |
| 210 EXPECT_FALSE(file_runner_->HasPendingTask()); |
| 211 |
| 212 // Add our original item as populated so it's paged to disk. |
| 213 items[0]->item()->data_element_ptr()->SetToBytes( |
| 214 kData, kTestBlobStorageMaxBlobMemorySize); |
| 215 SetItemState(items[0].get(), ItemState::POPULATED_WITH_QUOTA); |
| 216 controller.UpdateBlobItemInRecents(items[0].get()); |
| 217 |
| 218 EXPECT_TRUE(file_runner_->HasPendingTask()); |
| 219 file_runner_->RunPendingTasks(); |
| 220 base::RunLoop().RunUntilIdle(); |
| 221 EXPECT_EQ(ItemState::QUOTA_GRANTED, items2[0]->state()); |
| 222 EXPECT_EQ(DataElement::TYPE_FILE, items[0]->item()->type()); |
| 223 } |
| 224 |
| 225 // TODO(dmurph): |
| 226 // * Write test for memory request when quota is available. |
| 227 // * Write test for memory request that fails because disk isn't enabled. |
| 228 // * Write test for memory request when quota isn't available and we're waiting |
| 229 // on disk paging. |
| 230 // * Same as above but then we cancel it. |
| 231 // * Write test for memory request that's pending, and then disk is disabled. |
| 232 // * Write test for file request where we have quota. |
| 233 // * Write test for file request where we don't have quota (and just fail). |
| 234 // * Write test for file request and then we disable disk. |
| 235 |
| 236 } // namespace storage |
OLD | NEW |