Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / extensions / browser / app_window / app_window_geometry_cache_unittest.cc
1 // Copyright 2014 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 "extensions/browser/app_window/app_window_geometry_cache.h"
6
7 #include "base/files/file_path.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/prefs/mock_pref_change_callback.h"
10 #include "base/prefs/pref_service_factory.h"
11 #include "base/prefs/testing_pref_store.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "components/pref_registry/pref_registry_syncable.h"
14 #include "content/public/test/test_browser_context.h"
15 #include "content/public/test/test_browser_thread.h"
16 #include "content/public/test/test_utils.h"
17 #include "extensions/browser/extension_pref_value_map.h"
18 #include "extensions/browser/extension_prefs.h"
19 #include "extensions/browser/extensions_test.h"
20 #include "extensions/browser/null_app_sorting.h"
21 #include "extensions/common/extension_builder.h"
22 #include "extensions/common/value_builder.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 using content::BrowserThread;
26
27 namespace extensions {
28
29 namespace {
30 const char kWindowId[] = "windowid";
31 const char kWindowId2[] = "windowid2";
32
33 // Create a very simple extension with id.
34 scoped_refptr<Extension> CreateExtension(const std::string& id) {
35   return ExtensionBuilder()
36       .SetManifest(DictionaryBuilder().Set("name", "test").Set(
37           "version", "0.1"))
38       .SetID(id)
39       .Build();
40 }
41
42 }  // namespace
43
44 // Base class for tests.
45 class AppWindowGeometryCacheTest : public ExtensionsTest {
46  public:
47   AppWindowGeometryCacheTest()
48       : ui_thread_(BrowserThread::UI, &ui_message_loop_) {}
49
50   // testing::Test overrides:
51   void SetUp() override;
52   void TearDown() override;
53
54   void AddGeometryAndLoadExtension(const std::string& extension_id,
55                                    const std::string& window_id,
56                                    const gfx::Rect& bounds,
57                                    const gfx::Rect& screen_bounds,
58                                    ui::WindowShowState state);
59
60   // Spins the UI threads' message loops to make sure any task
61   // posted to sync the geometry to the value store gets a chance to run.
62   void WaitForSync();
63
64   void LoadExtension(const std::string& extension_id);
65   void UnloadExtension(const std::string& extension_id);
66
67   // Creates and adds an extension with associated prefs. Returns the extension
68   // ID.
69   std::string AddExtensionWithPrefs(const std::string& name);
70
71  protected:
72   base::MessageLoopForUI ui_message_loop_;
73   content::TestBrowserThread ui_thread_;
74   scoped_ptr<ExtensionPrefValueMap> extension_pref_value_map_;
75   scoped_ptr<PrefService> pref_service_;
76   scoped_ptr<ExtensionPrefs> extension_prefs_;
77   scoped_ptr<AppWindowGeometryCache> cache_;
78 };
79
80 void AppWindowGeometryCacheTest::SetUp() {
81   ExtensionsTest::SetUp();
82
83   // Set up all the dependencies of ExtensionPrefs.
84   extension_pref_value_map_.reset(new ExtensionPrefValueMap);
85   base::PrefServiceFactory factory;
86   factory.set_user_prefs(new TestingPrefStore);
87   factory.set_extension_prefs(new TestingPrefStore);
88   user_prefs::PrefRegistrySyncable* pref_registry =
89       new user_prefs::PrefRegistrySyncable;
90   // Prefs should be registered before the PrefService is created.
91   ExtensionPrefs::RegisterProfilePrefs(pref_registry);
92   pref_service_ = factory.Create(pref_registry).Pass();
93
94   extension_prefs_.reset(ExtensionPrefs::Create(
95       pref_service_.get(),
96       browser_context()->GetPath().AppendASCII("Extensions"),
97       extension_pref_value_map_.get(),
98       scoped_ptr<AppSorting>(new NullAppSorting),
99       false /* extensions_disabled */,
100       std::vector<ExtensionPrefsObserver*>()));
101
102   cache_.reset(
103       new AppWindowGeometryCache(browser_context(), extension_prefs_.get()));
104   cache_->SetSyncDelayForTests(0);
105 }
106
107 void AppWindowGeometryCacheTest::TearDown() {
108   cache_.reset();
109   extension_prefs_.reset();
110   pref_service_.reset();
111   extension_pref_value_map_.reset();
112
113   ExtensionsTest::TearDown();
114 }
115
116 void AppWindowGeometryCacheTest::AddGeometryAndLoadExtension(
117     const std::string& extension_id,
118     const std::string& window_id,
119     const gfx::Rect& bounds,
120     const gfx::Rect& screen_bounds,
121     ui::WindowShowState state) {
122   scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
123   base::DictionaryValue* value = new base::DictionaryValue;
124   value->SetInteger("x", bounds.x());
125   value->SetInteger("y", bounds.y());
126   value->SetInteger("w", bounds.width());
127   value->SetInteger("h", bounds.height());
128   value->SetInteger("screen_bounds_x", screen_bounds.x());
129   value->SetInteger("screen_bounds_y", screen_bounds.y());
130   value->SetInteger("screen_bounds_w", screen_bounds.width());
131   value->SetInteger("screen_bounds_h", screen_bounds.height());
132   value->SetInteger("state", state);
133   dict->SetWithoutPathExpansion(window_id, value);
134   extension_prefs_->SetGeometryCache(extension_id, dict.Pass());
135   LoadExtension(extension_id);
136 }
137
138 void AppWindowGeometryCacheTest::WaitForSync() {
139   content::RunAllPendingInMessageLoop();
140 }
141
142 void AppWindowGeometryCacheTest::LoadExtension(
143     const std::string& extension_id) {
144   cache_->LoadGeometryFromStorage(extension_id);
145   WaitForSync();
146 }
147
148 void AppWindowGeometryCacheTest::UnloadExtension(
149     const std::string& extension_id) {
150   scoped_refptr<Extension> extension = CreateExtension(extension_id);
151   cache_->OnExtensionUnloaded(browser_context(),
152                               extension.get(),
153                               UnloadedExtensionInfo::REASON_DISABLE);
154   WaitForSync();
155 }
156
157 std::string AppWindowGeometryCacheTest::AddExtensionWithPrefs(
158     const std::string& name) {
159   // Generate the extension with a path based on the name so that extensions
160   // with different names will have different IDs.
161   base::FilePath path =
162       browser_context()->GetPath().AppendASCII("Extensions").AppendASCII(name);
163   scoped_refptr<Extension> extension =
164       ExtensionBuilder()
165           .SetManifest(
166                DictionaryBuilder().Set("name", "test").Set("version", "0.1"))
167           .SetPath(path)
168           .Build();
169
170   extension_prefs_->OnExtensionInstalled(
171       extension.get(),
172       Extension::ENABLED,
173       syncer::StringOrdinal::CreateInitialOrdinal(),
174       std::string());
175   return extension->id();
176 }
177
178 // Test getting geometry from an empty store.
179 TEST_F(AppWindowGeometryCacheTest, GetGeometryEmptyStore) {
180   const std::string extension_id = AddExtensionWithPrefs("ext1");
181   ASSERT_FALSE(cache_->GetGeometry(extension_id, kWindowId, NULL, NULL, NULL));
182 }
183
184 // Test getting geometry for an unknown extension.
185 TEST_F(AppWindowGeometryCacheTest, GetGeometryUnkownExtension) {
186   const std::string extension_id1 = AddExtensionWithPrefs("ext1");
187   const std::string extension_id2 = AddExtensionWithPrefs("ext2");
188   AddGeometryAndLoadExtension(extension_id1,
189                               kWindowId,
190                               gfx::Rect(4, 5, 31, 43),
191                               gfx::Rect(0, 0, 1600, 900),
192                               ui::SHOW_STATE_NORMAL);
193   ASSERT_FALSE(cache_->GetGeometry(extension_id2, kWindowId, NULL, NULL, NULL));
194 }
195
196 // Test getting geometry for an unknown window in a known extension.
197 TEST_F(AppWindowGeometryCacheTest, GetGeometryUnkownWindow) {
198   const std::string extension_id = AddExtensionWithPrefs("ext1");
199   AddGeometryAndLoadExtension(extension_id,
200                               kWindowId,
201                               gfx::Rect(4, 5, 31, 43),
202                               gfx::Rect(0, 0, 1600, 900),
203                               ui::SHOW_STATE_NORMAL);
204   ASSERT_FALSE(cache_->GetGeometry(extension_id, kWindowId2, NULL, NULL, NULL));
205 }
206
207 // Test that loading geometry, screen_bounds and state from the store works
208 // correctly.
209 TEST_F(AppWindowGeometryCacheTest, GetGeometryAndStateFromStore) {
210   const std::string extension_id = AddExtensionWithPrefs("ext1");
211   gfx::Rect bounds(4, 5, 31, 43);
212   gfx::Rect screen_bounds(0, 0, 1600, 900);
213   ui::WindowShowState state = ui::SHOW_STATE_NORMAL;
214   AddGeometryAndLoadExtension(
215       extension_id, kWindowId, bounds, screen_bounds, state);
216   gfx::Rect new_bounds;
217   gfx::Rect new_screen_bounds;
218   ui::WindowShowState new_state = ui::SHOW_STATE_DEFAULT;
219   ASSERT_TRUE(cache_->GetGeometry(
220       extension_id, kWindowId, &new_bounds, &new_screen_bounds, &new_state));
221   ASSERT_EQ(bounds, new_bounds);
222   ASSERT_EQ(screen_bounds, new_screen_bounds);
223   ASSERT_EQ(state, new_state);
224 }
225
226 // Test corrupt bounds will not be loaded.
227 TEST_F(AppWindowGeometryCacheTest, CorruptBounds) {
228   const std::string extension_id = AddExtensionWithPrefs("ext1");
229   gfx::Rect bounds;
230   gfx::Rect screen_bounds(0, 0, 1600, 900);
231   ui::WindowShowState state = ui::SHOW_STATE_NORMAL;
232   AddGeometryAndLoadExtension(
233       extension_id, kWindowId, bounds, screen_bounds, state);
234   gfx::Rect new_bounds;
235   gfx::Rect new_screen_bounds;
236   ui::WindowShowState new_state = ui::SHOW_STATE_DEFAULT;
237   ASSERT_FALSE(cache_->GetGeometry(
238       extension_id, kWindowId, &new_bounds, &new_screen_bounds, &new_state));
239   ASSERT_TRUE(new_bounds.IsEmpty());
240   ASSERT_TRUE(new_screen_bounds.IsEmpty());
241   ASSERT_EQ(new_state, ui::SHOW_STATE_DEFAULT);
242 }
243
244 // Test corrupt screen bounds will not be loaded.
245 TEST_F(AppWindowGeometryCacheTest, CorruptScreenBounds) {
246   const std::string extension_id = AddExtensionWithPrefs("ext1");
247   gfx::Rect bounds(4, 5, 31, 43);
248   gfx::Rect screen_bounds;
249   ui::WindowShowState state = ui::SHOW_STATE_NORMAL;
250   AddGeometryAndLoadExtension(
251       extension_id, kWindowId, bounds, screen_bounds, state);
252   gfx::Rect new_bounds;
253   gfx::Rect new_screen_bounds;
254   ui::WindowShowState new_state = ui::SHOW_STATE_DEFAULT;
255   ASSERT_FALSE(cache_->GetGeometry(
256       extension_id, kWindowId, &new_bounds, &new_screen_bounds, &new_state));
257   ASSERT_TRUE(new_bounds.IsEmpty());
258   ASSERT_TRUE(new_screen_bounds.IsEmpty());
259   ASSERT_EQ(new_state, ui::SHOW_STATE_DEFAULT);
260 }
261
262 // Test corrupt state will not be loaded.
263 TEST_F(AppWindowGeometryCacheTest, CorruptState) {
264   const std::string extension_id = AddExtensionWithPrefs("ext1");
265   gfx::Rect bounds(4, 5, 31, 43);
266   gfx::Rect screen_bounds(0, 0, 1600, 900);
267   ui::WindowShowState state = ui::SHOW_STATE_DEFAULT;
268   AddGeometryAndLoadExtension(
269       extension_id, kWindowId, bounds, screen_bounds, state);
270   gfx::Rect new_bounds;
271   gfx::Rect new_screen_bounds;
272   ui::WindowShowState new_state = ui::SHOW_STATE_DEFAULT;
273   ASSERT_FALSE(cache_->GetGeometry(
274       extension_id, kWindowId, &new_bounds, &new_screen_bounds, &new_state));
275   ASSERT_TRUE(new_bounds.IsEmpty());
276   ASSERT_TRUE(new_screen_bounds.IsEmpty());
277   ASSERT_EQ(new_state, ui::SHOW_STATE_DEFAULT);
278 }
279
280 // Test saving geometry, screen_bounds and state to the cache and state store,
281 // and reading it back.
282 TEST_F(AppWindowGeometryCacheTest, SaveGeometryAndStateToStore) {
283   const std::string extension_id = AddExtensionWithPrefs("ext1");
284   const std::string window_id(kWindowId);
285
286   // inform cache of extension
287   LoadExtension(extension_id);
288
289   // update geometry stored in cache
290   gfx::Rect bounds(4, 5, 31, 43);
291   gfx::Rect screen_bounds(0, 0, 1600, 900);
292   ui::WindowShowState state = ui::SHOW_STATE_NORMAL;
293   cache_->SaveGeometry(extension_id, window_id, bounds, screen_bounds, state);
294
295   // make sure that immediately reading back geometry works
296   gfx::Rect new_bounds;
297   gfx::Rect new_screen_bounds;
298   ui::WindowShowState new_state = ui::SHOW_STATE_DEFAULT;
299   ASSERT_TRUE(cache_->GetGeometry(
300       extension_id, window_id, &new_bounds, &new_screen_bounds, &new_state));
301   ASSERT_EQ(bounds, new_bounds);
302   ASSERT_EQ(screen_bounds, new_screen_bounds);
303   ASSERT_EQ(state, new_state);
304
305   // unload extension to force cache to save data to the state store
306   UnloadExtension(extension_id);
307
308   // check if geometry got stored correctly in the state store
309   const base::DictionaryValue* dict =
310       extension_prefs_->GetGeometryCache(extension_id);
311   ASSERT_TRUE(dict);
312
313   ASSERT_TRUE(dict->HasKey(window_id));
314   int v;
315   ASSERT_TRUE(dict->GetInteger(window_id + ".x", &v));
316   ASSERT_EQ(bounds.x(), v);
317   ASSERT_TRUE(dict->GetInteger(window_id + ".y", &v));
318   ASSERT_EQ(bounds.y(), v);
319   ASSERT_TRUE(dict->GetInteger(window_id + ".w", &v));
320   ASSERT_EQ(bounds.width(), v);
321   ASSERT_TRUE(dict->GetInteger(window_id + ".h", &v));
322   ASSERT_EQ(bounds.height(), v);
323   ASSERT_TRUE(dict->GetInteger(window_id + ".screen_bounds_x", &v));
324   ASSERT_EQ(screen_bounds.x(), v);
325   ASSERT_TRUE(dict->GetInteger(window_id + ".screen_bounds_y", &v));
326   ASSERT_EQ(screen_bounds.y(), v);
327   ASSERT_TRUE(dict->GetInteger(window_id + ".screen_bounds_w", &v));
328   ASSERT_EQ(screen_bounds.width(), v);
329   ASSERT_TRUE(dict->GetInteger(window_id + ".screen_bounds_h", &v));
330   ASSERT_EQ(screen_bounds.height(), v);
331   ASSERT_TRUE(dict->GetInteger(window_id + ".state", &v));
332   ASSERT_EQ(state, v);
333
334   // reload extension
335   LoadExtension(extension_id);
336   // and make sure the geometry got reloaded properly too
337   ASSERT_TRUE(cache_->GetGeometry(
338       extension_id, window_id, &new_bounds, &new_screen_bounds, &new_state));
339   ASSERT_EQ(bounds, new_bounds);
340   ASSERT_EQ(screen_bounds, new_screen_bounds);
341   ASSERT_EQ(state, new_state);
342 }
343
344 // Tests that we won't do writes to the state store for SaveGeometry calls
345 // which don't change the state we already have.
346 TEST_F(AppWindowGeometryCacheTest, NoDuplicateWrites) {
347   using testing::_;
348   using testing::Mock;
349
350   const std::string extension_id = AddExtensionWithPrefs("ext1");
351   gfx::Rect bounds1(100, 200, 300, 400);
352   gfx::Rect bounds2(200, 400, 600, 800);
353   gfx::Rect bounds2_duplicate(200, 400, 600, 800);
354
355   gfx::Rect screen_bounds1(0, 0, 1600, 900);
356   gfx::Rect screen_bounds2(0, 0, 1366, 768);
357   gfx::Rect screen_bounds2_duplicate(0, 0, 1366, 768);
358
359   MockPrefChangeCallback observer(pref_service_.get());
360   PrefChangeRegistrar registrar;
361   registrar.Init(pref_service_.get());
362   registrar.Add("extensions.settings", observer.GetCallback());
363
364   // Write the first bounds - it should do > 0 writes.
365   EXPECT_CALL(observer, OnPreferenceChanged(_));
366   cache_->SaveGeometry(
367       extension_id, kWindowId, bounds1, screen_bounds1, ui::SHOW_STATE_NORMAL);
368   WaitForSync();
369   Mock::VerifyAndClearExpectations(&observer);
370
371   // Write a different bounds - it should also do > 0 writes.
372   EXPECT_CALL(observer, OnPreferenceChanged(_));
373   cache_->SaveGeometry(
374       extension_id, kWindowId, bounds2, screen_bounds1, ui::SHOW_STATE_NORMAL);
375   WaitForSync();
376   Mock::VerifyAndClearExpectations(&observer);
377
378   // Write a different screen bounds - it should also do > 0 writes.
379   EXPECT_CALL(observer, OnPreferenceChanged(_));
380   cache_->SaveGeometry(
381       extension_id, kWindowId, bounds2, screen_bounds2, ui::SHOW_STATE_NORMAL);
382   WaitForSync();
383   Mock::VerifyAndClearExpectations(&observer);
384
385   // Write a different state - it should also do > 0 writes.
386   EXPECT_CALL(observer, OnPreferenceChanged(_));
387   cache_->SaveGeometry(extension_id,
388                        kWindowId,
389                        bounds2,
390                        screen_bounds2,
391                        ui::SHOW_STATE_MAXIMIZED);
392   WaitForSync();
393   Mock::VerifyAndClearExpectations(&observer);
394
395   // Write a bounds, screen bounds and state that's a duplicate of what we
396   // already have. This should not do any writes.
397   EXPECT_CALL(observer, OnPreferenceChanged(_)).Times(0);
398   cache_->SaveGeometry(extension_id,
399                        kWindowId,
400                        bounds2_duplicate,
401                        screen_bounds2_duplicate,
402                        ui::SHOW_STATE_MAXIMIZED);
403   WaitForSync();
404   Mock::VerifyAndClearExpectations(&observer);
405 }
406
407 // Tests that no more than kMaxCachedWindows windows will be cached.
408 TEST_F(AppWindowGeometryCacheTest, MaxWindows) {
409   const std::string extension_id = AddExtensionWithPrefs("ext1");
410   // inform cache of extension
411   LoadExtension(extension_id);
412
413   gfx::Rect bounds(4, 5, 31, 43);
414   gfx::Rect screen_bounds(0, 0, 1600, 900);
415   for (size_t i = 0; i < AppWindowGeometryCache::kMaxCachedWindows + 1; ++i) {
416     std::string window_id = "window_" + base::IntToString(i);
417     cache_->SaveGeometry(
418         extension_id, window_id, bounds, screen_bounds, ui::SHOW_STATE_NORMAL);
419   }
420
421   // The first added window should no longer have cached geometry.
422   EXPECT_FALSE(cache_->GetGeometry(extension_id, "window_0", NULL, NULL, NULL));
423   // All other windows should still exist.
424   for (size_t i = 1; i < AppWindowGeometryCache::kMaxCachedWindows + 1; ++i) {
425     std::string window_id = "window_" + base::IntToString(i);
426     EXPECT_TRUE(cache_->GetGeometry(extension_id, window_id, NULL, NULL, NULL));
427   }
428 }
429
430 }  // namespace extensions