OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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 <list> |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/utf_string_conversions.h" |
| 9 #include "testing/gtest/include/gtest/gtest.h" |
| 10 #include "webkit/dom_storage/dom_storage_cached_area.h" |
| 11 #include "webkit/dom_storage/dom_storage_proxy.h" |
| 12 |
| 13 namespace dom_storage { |
| 14 |
| 15 namespace { |
| 16 // A mock implementation of the DomStorageProxy interface. |
| 17 class MockProxy : public DomStorageProxy { |
| 18 public: |
| 19 MockProxy() { |
| 20 ResetObservations(); |
| 21 } |
| 22 |
| 23 // DomStorageProxy interface for use by DomStorageCachedArea. |
| 24 |
| 25 virtual void LoadArea(int connection_id, ValuesMap* values, |
| 26 const CompletionCallback& callback) OVERRIDE { |
| 27 pending_callbacks_.push_back(callback); |
| 28 observed_load_area_ = true; |
| 29 observed_connection_id_ = connection_id; |
| 30 *values = load_area_return_values_; |
| 31 } |
| 32 |
| 33 virtual void SetItem(int connection_id, const string16& key, |
| 34 const string16& value, const GURL& page_url, |
| 35 const CompletionCallback& callback) OVERRIDE { |
| 36 pending_callbacks_.push_back(callback); |
| 37 observed_set_item_ = true; |
| 38 observed_connection_id_ = connection_id; |
| 39 observed_key_ = key; |
| 40 observed_value_ = value; |
| 41 observed_page_url_ = page_url; |
| 42 } |
| 43 |
| 44 virtual void RemoveItem(int connection_id, const string16& key, |
| 45 const GURL& page_url, |
| 46 const CompletionCallback& callback) OVERRIDE { |
| 47 pending_callbacks_.push_back(callback); |
| 48 observed_remove_item_ = true; |
| 49 observed_connection_id_ = connection_id; |
| 50 observed_key_ = key; |
| 51 observed_page_url_ = page_url; |
| 52 } |
| 53 |
| 54 virtual void ClearArea(int connection_id, |
| 55 const GURL& page_url, |
| 56 const CompletionCallback& callback) OVERRIDE { |
| 57 pending_callbacks_.push_back(callback); |
| 58 observed_clear_area_ = true; |
| 59 observed_connection_id_ = connection_id; |
| 60 observed_page_url_ = page_url; |
| 61 } |
| 62 |
| 63 // Methods and members for use by test fixtures. |
| 64 |
| 65 void ResetObservations() { |
| 66 observed_load_area_ = false; |
| 67 observed_set_item_ = false; |
| 68 observed_remove_item_ = false; |
| 69 observed_clear_area_ = false; |
| 70 observed_connection_id_ = 0; |
| 71 observed_key_.clear(); |
| 72 observed_value_.clear(); |
| 73 observed_page_url_ = GURL(); |
| 74 } |
| 75 |
| 76 void CompleteAllPendingCallbacks() { |
| 77 while (!pending_callbacks_.empty()) |
| 78 CompleteOnePendingCallback(true); |
| 79 } |
| 80 |
| 81 void CompleteOnePendingCallback(bool success) { |
| 82 ASSERT_TRUE(!pending_callbacks_.empty()); |
| 83 pending_callbacks_.front().Run(success); |
| 84 pending_callbacks_.pop_front(); |
| 85 } |
| 86 |
| 87 typedef std::list<CompletionCallback> CallbackList; |
| 88 |
| 89 ValuesMap load_area_return_values_; |
| 90 CallbackList pending_callbacks_; |
| 91 bool observed_load_area_; |
| 92 bool observed_set_item_; |
| 93 bool observed_remove_item_; |
| 94 bool observed_clear_area_; |
| 95 int observed_connection_id_; |
| 96 string16 observed_key_; |
| 97 string16 observed_value_; |
| 98 GURL observed_page_url_; |
| 99 |
| 100 private: |
| 101 virtual ~MockProxy() {} |
| 102 }; |
| 103 } // namespace |
| 104 |
| 105 class DomStorageCachedAreaTest : public testing::Test { |
| 106 public: |
| 107 DomStorageCachedAreaTest() |
| 108 : kNamespaceId(10), |
| 109 kOrigin("http://dom_storage/"), |
| 110 kKey(ASCIIToUTF16("key")), |
| 111 kValue(ASCIIToUTF16("value")), |
| 112 kPageUrl("http://dom_storage/page") { |
| 113 } |
| 114 |
| 115 const int64 kNamespaceId; |
| 116 const GURL kOrigin; |
| 117 const string16 kKey; |
| 118 const string16 kValue; |
| 119 const GURL kPageUrl; |
| 120 |
| 121 virtual void SetUp() { |
| 122 mock_proxy_ = new MockProxy(); |
| 123 } |
| 124 |
| 125 bool IsPrimed(DomStorageCachedArea* cached_area) { |
| 126 return cached_area->map_.get(); |
| 127 } |
| 128 |
| 129 bool IsIgnoringAllMutations(DomStorageCachedArea* cached_area) { |
| 130 return cached_area->ignore_all_mutations_; |
| 131 } |
| 132 |
| 133 bool IsIgnoringKeyMutations(DomStorageCachedArea* cached_area, |
| 134 const string16& key) { |
| 135 return cached_area->should_ignore_key_mutation(key); |
| 136 } |
| 137 |
| 138 void ResetAll(DomStorageCachedArea* cached_area) { |
| 139 cached_area->Reset(); |
| 140 mock_proxy_->ResetObservations(); |
| 141 mock_proxy_->pending_callbacks_.clear(); |
| 142 } |
| 143 |
| 144 void ResetCacheOnly(DomStorageCachedArea* cached_area) { |
| 145 cached_area->Reset(); |
| 146 } |
| 147 |
| 148 protected: |
| 149 scoped_refptr<MockProxy> mock_proxy_; |
| 150 }; |
| 151 |
| 152 TEST_F(DomStorageCachedAreaTest, Basics) { |
| 153 EXPECT_TRUE(mock_proxy_->HasOneRef()); |
| 154 scoped_refptr<DomStorageCachedArea> cached_area = |
| 155 new DomStorageCachedArea(kNamespaceId, kOrigin, mock_proxy_); |
| 156 EXPECT_EQ(kNamespaceId, cached_area->namespace_id()); |
| 157 EXPECT_EQ(kOrigin, cached_area->origin()); |
| 158 EXPECT_FALSE(mock_proxy_->HasOneRef()); |
| 159 cached_area->ApplyMutation(NullableString16(kKey, false), |
| 160 NullableString16(kValue, false)); |
| 161 EXPECT_FALSE(IsPrimed(cached_area)); |
| 162 |
| 163 ResetAll(cached_area); |
| 164 EXPECT_EQ(kNamespaceId, cached_area->namespace_id()); |
| 165 EXPECT_EQ(kOrigin, cached_area->origin()); |
| 166 |
| 167 const int kConnectionId = 1; |
| 168 EXPECT_EQ(0u, cached_area->GetLength(kConnectionId)); |
| 169 EXPECT_TRUE(cached_area->SetItem(kConnectionId, kKey, kValue, kPageUrl)); |
| 170 EXPECT_EQ(1u, cached_area->GetLength(kConnectionId)); |
| 171 EXPECT_EQ(kKey, cached_area->GetKey(kConnectionId, 0).string()); |
| 172 EXPECT_EQ(kValue, cached_area->GetItem(kConnectionId, kKey).string()); |
| 173 cached_area->RemoveItem(kConnectionId, kKey, kPageUrl); |
| 174 EXPECT_EQ(0u, cached_area->GetLength(kConnectionId)); |
| 175 } |
| 176 |
| 177 TEST_F(DomStorageCachedAreaTest, Getters) { |
| 178 const int kConnectionId = 7; |
| 179 scoped_refptr<DomStorageCachedArea> cached_area = |
| 180 new DomStorageCachedArea(kNamespaceId, kOrigin, mock_proxy_); |
| 181 |
| 182 // GetLength, we expect to see one call to load in the proxy. |
| 183 EXPECT_FALSE(IsPrimed(cached_area)); |
| 184 EXPECT_EQ(0u, cached_area->GetLength(kConnectionId)); |
| 185 EXPECT_TRUE(IsPrimed(cached_area)); |
| 186 EXPECT_TRUE(mock_proxy_->observed_load_area_); |
| 187 EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_); |
| 188 EXPECT_EQ(1u, mock_proxy_->pending_callbacks_.size()); |
| 189 EXPECT_TRUE(IsIgnoringAllMutations(cached_area)); |
| 190 mock_proxy_->CompleteAllPendingCallbacks(); |
| 191 EXPECT_FALSE(IsIgnoringAllMutations(cached_area)); |
| 192 |
| 193 // GetKey, expect the one call to load. |
| 194 ResetAll(cached_area); |
| 195 EXPECT_FALSE(IsPrimed(cached_area)); |
| 196 EXPECT_TRUE(cached_area->GetKey(kConnectionId, 2).is_null()); |
| 197 EXPECT_TRUE(IsPrimed(cached_area)); |
| 198 EXPECT_TRUE(mock_proxy_->observed_load_area_); |
| 199 EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_); |
| 200 EXPECT_EQ(1u, mock_proxy_->pending_callbacks_.size()); |
| 201 |
| 202 // GetItem, ditto. |
| 203 ResetAll(cached_area); |
| 204 EXPECT_FALSE(IsPrimed(cached_area)); |
| 205 EXPECT_TRUE(cached_area->GetItem(kConnectionId, kKey).is_null()); |
| 206 EXPECT_TRUE(IsPrimed(cached_area)); |
| 207 EXPECT_TRUE(mock_proxy_->observed_load_area_); |
| 208 EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_); |
| 209 EXPECT_EQ(1u, mock_proxy_->pending_callbacks_.size()); |
| 210 } |
| 211 |
| 212 TEST_F(DomStorageCachedAreaTest, Setters) { |
| 213 const int kConnectionId = 7; |
| 214 scoped_refptr<DomStorageCachedArea> cached_area = |
| 215 new DomStorageCachedArea(kNamespaceId, kOrigin, mock_proxy_); |
| 216 |
| 217 // SetItem, we expect a call to load followed by a call to set item |
| 218 // in the proxy. |
| 219 EXPECT_FALSE(IsPrimed(cached_area)); |
| 220 EXPECT_TRUE(cached_area->SetItem(kConnectionId, kKey, kValue, kPageUrl)); |
| 221 EXPECT_TRUE(IsPrimed(cached_area)); |
| 222 EXPECT_TRUE(mock_proxy_->observed_load_area_); |
| 223 EXPECT_TRUE(mock_proxy_->observed_set_item_); |
| 224 EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_); |
| 225 EXPECT_EQ(kPageUrl, mock_proxy_->observed_page_url_); |
| 226 EXPECT_EQ(kKey, mock_proxy_->observed_key_); |
| 227 EXPECT_EQ(kValue, mock_proxy_->observed_value_); |
| 228 EXPECT_EQ(2u, mock_proxy_->pending_callbacks_.size()); |
| 229 |
| 230 // Clear, we expect a just the one call to clear in the proxy since |
| 231 // there's no need to load the data prior to deleting it. |
| 232 ResetAll(cached_area); |
| 233 EXPECT_FALSE(IsPrimed(cached_area)); |
| 234 cached_area->Clear(kConnectionId, kPageUrl); |
| 235 EXPECT_TRUE(IsPrimed(cached_area)); |
| 236 EXPECT_TRUE(mock_proxy_->observed_clear_area_); |
| 237 EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_); |
| 238 EXPECT_EQ(kPageUrl, mock_proxy_->observed_page_url_); |
| 239 EXPECT_EQ(1u, mock_proxy_->pending_callbacks_.size()); |
| 240 |
| 241 // RemoveItem with nothing to remove, expect just one call to load. |
| 242 ResetAll(cached_area); |
| 243 EXPECT_FALSE(IsPrimed(cached_area)); |
| 244 cached_area->RemoveItem(kConnectionId, kKey, kPageUrl); |
| 245 EXPECT_TRUE(IsPrimed(cached_area)); |
| 246 EXPECT_TRUE(mock_proxy_->observed_load_area_); |
| 247 EXPECT_FALSE(mock_proxy_->observed_remove_item_); |
| 248 EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_); |
| 249 EXPECT_EQ(1u, mock_proxy_->pending_callbacks_.size()); |
| 250 |
| 251 // RemoveItem with something to remove, expect a call to load followed |
| 252 // by a call to remove. |
| 253 ResetAll(cached_area); |
| 254 mock_proxy_->load_area_return_values_[kKey] = NullableString16(kValue, false); |
| 255 EXPECT_FALSE(IsPrimed(cached_area)); |
| 256 cached_area->RemoveItem(kConnectionId, kKey, kPageUrl); |
| 257 EXPECT_TRUE(IsPrimed(cached_area)); |
| 258 EXPECT_TRUE(mock_proxy_->observed_load_area_); |
| 259 EXPECT_TRUE(mock_proxy_->observed_remove_item_); |
| 260 EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_); |
| 261 EXPECT_EQ(kPageUrl, mock_proxy_->observed_page_url_); |
| 262 EXPECT_EQ(kKey, mock_proxy_->observed_key_); |
| 263 EXPECT_EQ(2u, mock_proxy_->pending_callbacks_.size()); |
| 264 } |
| 265 |
| 266 TEST_F(DomStorageCachedAreaTest, MutationsAreIgnoredUntilLoadCompletion) { |
| 267 const int kConnectionId = 7; |
| 268 scoped_refptr<DomStorageCachedArea> cached_area = |
| 269 new DomStorageCachedArea(kNamespaceId, kOrigin, mock_proxy_); |
| 270 EXPECT_TRUE(cached_area->GetItem(kConnectionId, kKey).is_null()); |
| 271 EXPECT_TRUE(IsPrimed(cached_area)); |
| 272 EXPECT_TRUE(IsIgnoringAllMutations(cached_area)); |
| 273 |
| 274 // Before load completion, the mutation should be ignored. |
| 275 cached_area->ApplyMutation(NullableString16(kKey, false), |
| 276 NullableString16(kValue, false)); |
| 277 EXPECT_TRUE(cached_area->GetItem(kConnectionId, kKey).is_null()); |
| 278 |
| 279 // Call the load completion callback. |
| 280 mock_proxy_->CompleteOnePendingCallback(true); |
| 281 EXPECT_FALSE(IsIgnoringAllMutations(cached_area)); |
| 282 |
| 283 // Verify that mutations are now applied. |
| 284 cached_area->ApplyMutation(NullableString16(kKey, false), |
| 285 NullableString16(kValue, false)); |
| 286 EXPECT_EQ(kValue, cached_area->GetItem(kConnectionId, kKey).string()); |
| 287 } |
| 288 |
| 289 TEST_F(DomStorageCachedAreaTest, MutationsAreIgnoredUntilClearCompletion) { |
| 290 const int kConnectionId = 4; |
| 291 scoped_refptr<DomStorageCachedArea> cached_area = |
| 292 new DomStorageCachedArea(kNamespaceId, kOrigin, mock_proxy_); |
| 293 cached_area->Clear(kConnectionId, kPageUrl); |
| 294 EXPECT_TRUE(IsIgnoringAllMutations(cached_area)); |
| 295 mock_proxy_->CompleteOnePendingCallback(true); |
| 296 EXPECT_FALSE(IsIgnoringAllMutations(cached_area)); |
| 297 |
| 298 // Verify that calling Clear twice works as expected, the first |
| 299 // completion callback should have been cancelled. |
| 300 ResetCacheOnly(cached_area); |
| 301 cached_area->Clear(kConnectionId, kPageUrl); |
| 302 EXPECT_TRUE(IsIgnoringAllMutations(cached_area)); |
| 303 cached_area->Clear(kConnectionId, kPageUrl); |
| 304 EXPECT_TRUE(IsIgnoringAllMutations(cached_area)); |
| 305 mock_proxy_->CompleteOnePendingCallback(true); |
| 306 EXPECT_TRUE(IsIgnoringAllMutations(cached_area)); |
| 307 mock_proxy_->CompleteOnePendingCallback(true); |
| 308 EXPECT_FALSE(IsIgnoringAllMutations(cached_area)); |
| 309 } |
| 310 |
| 311 TEST_F(DomStorageCachedAreaTest, KeyMutationsAreIgnoredUntilCompletion) { |
| 312 const int kConnectionId = 8; |
| 313 scoped_refptr<DomStorageCachedArea> cached_area = |
| 314 new DomStorageCachedArea(kNamespaceId, kOrigin, mock_proxy_); |
| 315 |
| 316 // SetItem |
| 317 EXPECT_TRUE(cached_area->SetItem(kConnectionId, kKey, kValue, kPageUrl)); |
| 318 mock_proxy_->CompleteOnePendingCallback(true); // load completion |
| 319 EXPECT_FALSE(IsIgnoringAllMutations(cached_area)); |
| 320 EXPECT_TRUE(IsIgnoringKeyMutations(cached_area, kKey)); |
| 321 cached_area->ApplyMutation(NullableString16(kKey, false), |
| 322 NullableString16(true)); |
| 323 EXPECT_EQ(kValue, cached_area->GetItem(kConnectionId, kKey).string()); |
| 324 mock_proxy_->CompleteOnePendingCallback(true); // set completion |
| 325 EXPECT_FALSE(IsIgnoringKeyMutations(cached_area, kKey)); |
| 326 |
| 327 // RemoveItem |
| 328 cached_area->RemoveItem(kConnectionId, kKey, kPageUrl); |
| 329 EXPECT_TRUE(IsIgnoringKeyMutations(cached_area, kKey)); |
| 330 mock_proxy_->CompleteOnePendingCallback(true); // remove completion |
| 331 EXPECT_FALSE(IsIgnoringKeyMutations(cached_area, kKey)); |
| 332 |
| 333 // Multiple mutations to the same key. |
| 334 EXPECT_TRUE(cached_area->SetItem(kConnectionId, kKey, kValue, kPageUrl)); |
| 335 cached_area->RemoveItem(kConnectionId, kKey, kPageUrl); |
| 336 EXPECT_TRUE(IsIgnoringKeyMutations(cached_area, kKey)); |
| 337 mock_proxy_->CompleteOnePendingCallback(true); // set completion |
| 338 EXPECT_TRUE(IsIgnoringKeyMutations(cached_area, kKey)); |
| 339 mock_proxy_->CompleteOnePendingCallback(true); // remove completion |
| 340 EXPECT_FALSE(IsIgnoringKeyMutations(cached_area, kKey)); |
| 341 |
| 342 // A failed set item operation should Reset the cache. |
| 343 EXPECT_TRUE(cached_area->SetItem(kConnectionId, kKey, kValue, kPageUrl)); |
| 344 EXPECT_TRUE(IsIgnoringKeyMutations(cached_area, kKey)); |
| 345 mock_proxy_->CompleteOnePendingCallback(false); |
| 346 EXPECT_FALSE(IsPrimed(cached_area)); |
| 347 } |
| 348 |
| 349 } // namespace dom_storage |
OLD | NEW |