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