Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / extensions / browser / app_window / app_window_geometry_cache_unittest.cc
blobe1f4068440298217262576b2c982b80a4972d15f
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.
5 #include "extensions/browser/app_window/app_window_geometry_cache.h"
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"
25 using content::BrowserThread;
27 namespace extensions {
29 namespace {
30 const char kWindowId[] = "windowid";
31 const char kWindowId2[] = "windowid2";
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();
42 } // namespace
44 // Base class for tests.
45 class AppWindowGeometryCacheTest : public ExtensionsTest {
46 public:
47 AppWindowGeometryCacheTest()
48 : ui_thread_(BrowserThread::UI, &ui_message_loop_) {}
50 // testing::Test overrides:
51 void SetUp() override;
52 void TearDown() override;
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);
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();
64 void LoadExtension(const std::string& extension_id);
65 void UnloadExtension(const std::string& extension_id);
67 // Creates and adds an extension with associated prefs. Returns the extension
68 // ID.
69 std::string AddExtensionWithPrefs(const std::string& name);
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_;
80 void AppWindowGeometryCacheTest::SetUp() {
81 ExtensionsTest::SetUp();
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();
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*>()));
102 cache_.reset(
103 new AppWindowGeometryCache(browser_context(), extension_prefs_.get()));
104 cache_->SetSyncDelayForTests(0);
107 void AppWindowGeometryCacheTest::TearDown() {
108 cache_.reset();
109 extension_prefs_.reset();
110 pref_service_.reset();
111 extension_pref_value_map_.reset();
113 ExtensionsTest::TearDown();
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);
138 void AppWindowGeometryCacheTest::WaitForSync() {
139 content::RunAllPendingInMessageLoop();
142 void AppWindowGeometryCacheTest::LoadExtension(
143 const std::string& extension_id) {
144 cache_->LoadGeometryFromStorage(extension_id);
145 WaitForSync();
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();
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();
170 extension_prefs_->OnExtensionInstalled(
171 extension.get(),
172 Extension::ENABLED,
173 syncer::StringOrdinal::CreateInitialOrdinal(),
174 std::string());
175 return extension->id();
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));
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));
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));
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);
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);
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);
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);
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);
286 // inform cache of extension
287 LoadExtension(extension_id);
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);
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);
305 // unload extension to force cache to save data to the state store
306 UnloadExtension(extension_id);
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);
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);
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);
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;
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);
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);
359 MockPrefChangeCallback observer(pref_service_.get());
360 PrefChangeRegistrar registrar;
361 registrar.Init(pref_service_.get());
362 registrar.Add("extensions.settings", observer.GetCallback());
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);
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);
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);
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);
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);
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);
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);
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));
430 } // namespace extensions