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.
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"
13 namespace dom_storage
{
16 // A mock implementation of the DomStorageProxy interface.
17 class MockProxy
: public DomStorageProxy
{
23 // DomStorageProxy interface for use by DomStorageCachedArea.
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_
;
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
;
40 observed_value_
= value
;
41 observed_page_url_
= page_url
;
44 virtual void RemoveItem(int connection_id
, const base::string16
& key
,
46 const CompletionCallback
& callback
) OVERRIDE
{
47 pending_callbacks_
.push_back(callback
);
48 observed_remove_item_
= true;
49 observed_connection_id_
= connection_id
;
51 observed_page_url_
= page_url
;
54 virtual void ClearArea(int connection_id
,
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
;
63 // Methods and members for use by test fixtures.
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();
76 void CompleteAllPendingCallbacks() {
77 while (!pending_callbacks_
.empty())
78 CompleteOnePendingCallback(true);
81 void CompleteOnePendingCallback(bool success
) {
82 ASSERT_TRUE(!pending_callbacks_
.empty());
83 pending_callbacks_
.front().Run(success
);
84 pending_callbacks_
.pop_front();
87 typedef std::list
<CompletionCallback
> CallbackList
;
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_
;
101 virtual ~MockProxy() {}
105 class DomStorageCachedAreaTest
: public testing::Test
{
107 DomStorageCachedAreaTest()
109 kOrigin("http://dom_storage/"),
110 kKey(ASCIIToUTF16("key")),
111 kValue(ASCIIToUTF16("value")),
112 kPageUrl("http://dom_storage/page") {
115 const int64 kNamespaceId
;
117 const base::string16 kKey
;
118 const base::string16 kValue
;
121 virtual void SetUp() {
122 mock_proxy_
= new MockProxy();
125 bool IsPrimed(DomStorageCachedArea
* cached_area
) {
126 return cached_area
->map_
.get();
129 bool IsIgnoringAllMutations(DomStorageCachedArea
* cached_area
) {
130 return cached_area
->ignore_all_mutations_
;
133 bool IsIgnoringKeyMutations(DomStorageCachedArea
* cached_area
,
134 const base::string16
& key
) {
135 return cached_area
->should_ignore_key_mutation(key
);
138 void ResetAll(DomStorageCachedArea
* cached_area
) {
139 cached_area
->Reset();
140 mock_proxy_
->ResetObservations();
141 mock_proxy_
->pending_callbacks_
.clear();
144 void ResetCacheOnly(DomStorageCachedArea
* cached_area
) {
145 cached_area
->Reset();
149 scoped_refptr
<MockProxy
> mock_proxy_
;
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
));
163 ResetAll(cached_area
);
164 EXPECT_EQ(kNamespaceId
, cached_area
->namespace_id());
165 EXPECT_EQ(kOrigin
, cached_area
->origin());
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
));
177 TEST_F(DomStorageCachedAreaTest
, Getters
) {
178 const int kConnectionId
= 7;
179 scoped_refptr
<DomStorageCachedArea
> cached_area
=
180 new DomStorageCachedArea(kNamespaceId
, kOrigin
, mock_proxy_
);
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
));
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());
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());
212 TEST_F(DomStorageCachedAreaTest
, Setters
) {
213 const int kConnectionId
= 7;
214 scoped_refptr
<DomStorageCachedArea
> cached_area
=
215 new DomStorageCachedArea(kNamespaceId
, kOrigin
, mock_proxy_
);
217 // SetItem, we expect a call to load followed by a call to set item
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());
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());
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());
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());
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
));
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());
279 // Call the load completion callback.
280 mock_proxy_
->CompleteOnePendingCallback(true);
281 EXPECT_FALSE(IsIgnoringAllMutations(cached_area
));
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());
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
));
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
));
311 TEST_F(DomStorageCachedAreaTest
, KeyMutationsAreIgnoredUntilCompletion
) {
312 const int kConnectionId
= 8;
313 scoped_refptr
<DomStorageCachedArea
> cached_area
=
314 new DomStorageCachedArea(kNamespaceId
, kOrigin
, mock_proxy_
);
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
));
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
));
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
));
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
));
349 } // namespace dom_storage