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 base::string16& key, | |
34 const base::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 base::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 base::string16 observed_key_; | |
97 base::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 base::string16 kKey; | |
118 const base::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 base::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_.get()); | |
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.get())); | |
162 | |
163 ResetAll(cached_area.get()); | |
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_.get()); | |
181 | |
182 // GetLength, we expect to see one call to load in the proxy. | |
183 EXPECT_FALSE(IsPrimed(cached_area.get())); | |
184 EXPECT_EQ(0u, cached_area->GetLength(kConnectionId)); | |
185 EXPECT_TRUE(IsPrimed(cached_area.get())); | |
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.get())); | |
190 mock_proxy_->CompleteAllPendingCallbacks(); | |
191 EXPECT_FALSE(IsIgnoringAllMutations(cached_area.get())); | |
192 | |
193 // GetKey, expect the one call to load. | |
194 ResetAll(cached_area.get()); | |
195 EXPECT_FALSE(IsPrimed(cached_area.get())); | |
196 EXPECT_TRUE(cached_area->GetKey(kConnectionId, 2).is_null()); | |
197 EXPECT_TRUE(IsPrimed(cached_area.get())); | |
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.get()); | |
204 EXPECT_FALSE(IsPrimed(cached_area.get())); | |
205 EXPECT_TRUE(cached_area->GetItem(kConnectionId, kKey).is_null()); | |
206 EXPECT_TRUE(IsPrimed(cached_area.get())); | |
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_.get()); | |
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.get())); | |
220 EXPECT_TRUE(cached_area->SetItem(kConnectionId, kKey, kValue, kPageUrl)); | |
221 EXPECT_TRUE(IsPrimed(cached_area.get())); | |
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.get()); | |
233 EXPECT_FALSE(IsPrimed(cached_area.get())); | |
234 cached_area->Clear(kConnectionId, kPageUrl); | |
235 EXPECT_TRUE(IsPrimed(cached_area.get())); | |
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.get()); | |
243 EXPECT_FALSE(IsPrimed(cached_area.get())); | |
244 cached_area->RemoveItem(kConnectionId, kKey, kPageUrl); | |
245 EXPECT_TRUE(IsPrimed(cached_area.get())); | |
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.get()); | |
254 mock_proxy_->load_area_return_values_[kKey] = NullableString16(kValue, false); | |
255 EXPECT_FALSE(IsPrimed(cached_area.get())); | |
256 cached_area->RemoveItem(kConnectionId, kKey, kPageUrl); | |
257 EXPECT_TRUE(IsPrimed(cached_area.get())); | |
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_.get()); | |
270 EXPECT_TRUE(cached_area->GetItem(kConnectionId, kKey).is_null()); | |
271 EXPECT_TRUE(IsPrimed(cached_area.get())); | |
272 EXPECT_TRUE(IsIgnoringAllMutations(cached_area.get())); | |
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.get())); | |
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_.get()); | |
293 cached_area->Clear(kConnectionId, kPageUrl); | |
294 EXPECT_TRUE(IsIgnoringAllMutations(cached_area.get())); | |
295 mock_proxy_->CompleteOnePendingCallback(true); | |
296 EXPECT_FALSE(IsIgnoringAllMutations(cached_area.get())); | |
297 | |
298 // Verify that calling Clear twice works as expected, the first | |
299 // completion callback should have been cancelled. | |
300 ResetCacheOnly(cached_area.get()); | |
301 cached_area->Clear(kConnectionId, kPageUrl); | |
302 EXPECT_TRUE(IsIgnoringAllMutations(cached_area.get())); | |
303 cached_area->Clear(kConnectionId, kPageUrl); | |
304 EXPECT_TRUE(IsIgnoringAllMutations(cached_area.get())); | |
305 mock_proxy_->CompleteOnePendingCallback(true); | |
306 EXPECT_TRUE(IsIgnoringAllMutations(cached_area.get())); | |
307 mock_proxy_->CompleteOnePendingCallback(true); | |
308 EXPECT_FALSE(IsIgnoringAllMutations(cached_area.get())); | |
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_.get()); | |
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.get())); | |
320 EXPECT_TRUE(IsIgnoringKeyMutations(cached_area.get(), 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.get(), kKey)); | |
326 | |
327 // RemoveItem | |
328 cached_area->RemoveItem(kConnectionId, kKey, kPageUrl); | |
329 EXPECT_TRUE(IsIgnoringKeyMutations(cached_area.get(), kKey)); | |
330 mock_proxy_->CompleteOnePendingCallback(true); // remove completion | |
331 EXPECT_FALSE(IsIgnoringKeyMutations(cached_area.get(), 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.get(), kKey)); | |
337 mock_proxy_->CompleteOnePendingCallback(true); // set completion | |
338 EXPECT_TRUE(IsIgnoringKeyMutations(cached_area.get(), kKey)); | |
339 mock_proxy_->CompleteOnePendingCallback(true); // remove completion | |
340 EXPECT_FALSE(IsIgnoringKeyMutations(cached_area.get(), 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.get(), kKey)); | |
345 mock_proxy_->CompleteOnePendingCallback(false); | |
346 EXPECT_FALSE(IsPrimed(cached_area.get())); | |
347 } | |
348 | |
349 } // namespace dom_storage | |
OLD | NEW |