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