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 "apps/shell_window_geometry_cache.h" | |
6 #include "base/memory/scoped_ptr.h" | |
7 #include "base/prefs/mock_pref_change_callback.h" | |
8 #include "base/strings/string_number_conversions.h" | |
9 #include "chrome/browser/extensions/test_extension_prefs.h" | |
10 #include "chrome/test/base/testing_profile.h" | |
11 #include "content/public/test/test_browser_thread.h" | |
12 #include "content/public/test/test_utils.h" | |
13 #include "extensions/browser/extension_prefs.h" | |
14 #include "testing/gtest/include/gtest/gtest.h" | |
15 | |
16 const char kWindowId[] = "windowid"; | |
17 const char kWindowId2[] = "windowid2"; | |
18 | |
19 using content::BrowserThread; | |
20 | |
21 namespace apps { | |
22 | |
23 // Base class for tests. | |
24 class ShellWindowGeometryCacheTest : public testing::Test { | |
25 public: | |
26 ShellWindowGeometryCacheTest() : | |
27 ui_thread_(BrowserThread::UI, &ui_message_loop_) { | |
28 prefs_.reset(new extensions::TestExtensionPrefs( | |
29 ui_message_loop_.message_loop_proxy().get())); | |
30 cache_.reset(new ShellWindowGeometryCache(&profile_, prefs_->prefs())); | |
31 cache_->SetSyncDelayForTests(0); | |
32 } | |
33 | |
34 void AddGeometryAndLoadExtension( | |
35 const std::string& extension_id, | |
36 const std::string& window_id, | |
37 const gfx::Rect& bounds, | |
38 const gfx::Rect& screen_bounds, | |
39 ui::WindowShowState state); | |
40 | |
41 // Spins the UI threads' message loops to make sure any task | |
42 // posted to sync the geometry to the value store gets a chance to run. | |
43 void WaitForSync(); | |
44 | |
45 void LoadExtension(const std::string& extension_id); | |
46 void UnloadExtension(const std::string& extension_id); | |
47 | |
48 protected: | |
49 TestingProfile profile_; | |
50 base::MessageLoopForUI ui_message_loop_; | |
51 content::TestBrowserThread ui_thread_; | |
52 scoped_ptr<extensions::TestExtensionPrefs> prefs_; | |
53 scoped_ptr<ShellWindowGeometryCache> cache_; | |
54 }; | |
55 | |
56 void ShellWindowGeometryCacheTest::AddGeometryAndLoadExtension( | |
57 const std::string& extension_id, | |
58 const std::string& window_id, | |
59 const gfx::Rect& bounds, | |
60 const gfx::Rect& screen_bounds, | |
61 ui::WindowShowState state) { | |
62 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue); | |
63 base::DictionaryValue* value = new base::DictionaryValue; | |
64 value->SetInteger("x", bounds.x()); | |
65 value->SetInteger("y", bounds.y()); | |
66 value->SetInteger("w", bounds.width()); | |
67 value->SetInteger("h", bounds.height()); | |
68 value->SetInteger("screen_bounds_x", screen_bounds.x()); | |
69 value->SetInteger("screen_bounds_y", screen_bounds.y()); | |
70 value->SetInteger("screen_bounds_w", screen_bounds.width()); | |
71 value->SetInteger("screen_bounds_h", screen_bounds.height()); | |
72 value->SetInteger("state", state); | |
73 dict->SetWithoutPathExpansion(window_id, value); | |
74 prefs_->prefs()->SetGeometryCache(extension_id, dict.Pass()); | |
75 LoadExtension(extension_id); | |
76 } | |
77 | |
78 void ShellWindowGeometryCacheTest::WaitForSync() { | |
79 content::RunAllPendingInMessageLoop(); | |
80 } | |
81 | |
82 void ShellWindowGeometryCacheTest::LoadExtension( | |
83 const std::string& extension_id) { | |
84 cache_->LoadGeometryFromStorage(extension_id); | |
85 WaitForSync(); | |
86 } | |
87 | |
88 void ShellWindowGeometryCacheTest::UnloadExtension( | |
89 const std::string& extension_id) { | |
90 cache_->OnExtensionUnloaded(extension_id); | |
91 WaitForSync(); | |
92 } | |
93 | |
94 // Test getting geometry from an empty store. | |
95 TEST_F(ShellWindowGeometryCacheTest, GetGeometryEmptyStore) { | |
96 const std::string extension_id = prefs_->AddExtensionAndReturnId("ext1"); | |
97 ASSERT_FALSE(cache_->GetGeometry(extension_id, kWindowId, NULL, NULL, NULL)); | |
98 } | |
99 | |
100 // Test getting geometry for an unknown extension. | |
101 TEST_F(ShellWindowGeometryCacheTest, GetGeometryUnkownExtension) { | |
102 const std::string extension_id1 = prefs_->AddExtensionAndReturnId("ext1"); | |
103 const std::string extension_id2 = prefs_->AddExtensionAndReturnId("ext2"); | |
104 AddGeometryAndLoadExtension(extension_id1, kWindowId, | |
105 gfx::Rect(4, 5, 31, 43), | |
106 gfx::Rect(0, 0, 1600, 900), | |
107 ui::SHOW_STATE_NORMAL); | |
108 ASSERT_FALSE(cache_->GetGeometry(extension_id2, kWindowId, NULL, NULL, NULL)); | |
109 } | |
110 | |
111 // Test getting geometry for an unknown window in a known extension. | |
112 TEST_F(ShellWindowGeometryCacheTest, GetGeometryUnkownWindow) { | |
113 const std::string extension_id = prefs_->AddExtensionAndReturnId("ext1"); | |
114 AddGeometryAndLoadExtension(extension_id, kWindowId, | |
115 gfx::Rect(4, 5, 31, 43), | |
116 gfx::Rect(0, 0, 1600, 900), | |
117 ui::SHOW_STATE_NORMAL); | |
118 ASSERT_FALSE(cache_->GetGeometry(extension_id, kWindowId2, NULL, NULL, NULL)); | |
119 } | |
120 | |
121 // Test that loading geometry, screen_bounds and state from the store works | |
122 // correctly. | |
123 TEST_F(ShellWindowGeometryCacheTest, GetGeometryAndStateFromStore) { | |
124 const std::string extension_id = prefs_->AddExtensionAndReturnId("ext1"); | |
125 gfx::Rect bounds(4, 5, 31, 43); | |
126 gfx::Rect screen_bounds(0, 0, 1600, 900); | |
127 ui::WindowShowState state = ui::SHOW_STATE_NORMAL; | |
128 AddGeometryAndLoadExtension(extension_id, kWindowId, bounds, | |
129 screen_bounds, state); | |
130 gfx::Rect new_bounds; | |
131 gfx::Rect new_screen_bounds; | |
132 ui::WindowShowState new_state = ui::SHOW_STATE_DEFAULT; | |
133 ASSERT_TRUE(cache_->GetGeometry( | |
134 extension_id, kWindowId, &new_bounds, &new_screen_bounds, &new_state)); | |
135 ASSERT_EQ(bounds, new_bounds); | |
136 ASSERT_EQ(screen_bounds, new_screen_bounds); | |
137 ASSERT_EQ(state, new_state); | |
138 } | |
139 | |
140 // Test corrupt bounds will not be loaded. | |
141 TEST_F(ShellWindowGeometryCacheTest, CorruptBounds) { | |
142 const std::string extension_id = prefs_->AddExtensionAndReturnId("ext1"); | |
143 gfx::Rect bounds; | |
144 gfx::Rect screen_bounds(0, 0, 1600, 900); | |
145 ui::WindowShowState state = ui::SHOW_STATE_NORMAL; | |
146 AddGeometryAndLoadExtension(extension_id, kWindowId, bounds, | |
147 screen_bounds, state); | |
148 gfx::Rect new_bounds; | |
149 gfx::Rect new_screen_bounds; | |
150 ui::WindowShowState new_state = ui::SHOW_STATE_DEFAULT; | |
151 ASSERT_FALSE(cache_->GetGeometry( | |
152 extension_id, kWindowId, &new_bounds, &new_screen_bounds, &new_state)); | |
153 ASSERT_TRUE(new_bounds.IsEmpty()); | |
154 ASSERT_TRUE(new_screen_bounds.IsEmpty()); | |
155 ASSERT_EQ(new_state, ui::SHOW_STATE_DEFAULT); | |
156 } | |
157 | |
158 // Test corrupt screen bounds will not be loaded. | |
159 TEST_F(ShellWindowGeometryCacheTest, CorruptScreenBounds) { | |
160 const std::string extension_id = prefs_->AddExtensionAndReturnId("ext1"); | |
161 gfx::Rect bounds(4, 5, 31, 43); | |
162 gfx::Rect screen_bounds; | |
163 ui::WindowShowState state = ui::SHOW_STATE_NORMAL; | |
164 AddGeometryAndLoadExtension(extension_id, kWindowId, bounds, | |
165 screen_bounds, state); | |
166 gfx::Rect new_bounds; | |
167 gfx::Rect new_screen_bounds; | |
168 ui::WindowShowState new_state = ui::SHOW_STATE_DEFAULT; | |
169 ASSERT_FALSE(cache_->GetGeometry( | |
170 extension_id, kWindowId, &new_bounds, &new_screen_bounds, &new_state)); | |
171 ASSERT_TRUE(new_bounds.IsEmpty()); | |
172 ASSERT_TRUE(new_screen_bounds.IsEmpty()); | |
173 ASSERT_EQ(new_state, ui::SHOW_STATE_DEFAULT); | |
174 } | |
175 | |
176 // Test corrupt state will not be loaded. | |
177 TEST_F(ShellWindowGeometryCacheTest, CorruptState) { | |
178 const std::string extension_id = prefs_->AddExtensionAndReturnId("ext1"); | |
179 gfx::Rect bounds(4, 5, 31, 43); | |
180 gfx::Rect screen_bounds(0, 0, 1600, 900); | |
181 ui::WindowShowState state = ui::SHOW_STATE_DEFAULT; | |
182 AddGeometryAndLoadExtension(extension_id, kWindowId, bounds, | |
183 screen_bounds, state); | |
184 gfx::Rect new_bounds; | |
185 gfx::Rect new_screen_bounds; | |
186 ui::WindowShowState new_state = ui::SHOW_STATE_DEFAULT; | |
187 ASSERT_FALSE(cache_->GetGeometry( | |
188 extension_id, kWindowId, &new_bounds, &new_screen_bounds, &new_state)); | |
189 ASSERT_TRUE(new_bounds.IsEmpty()); | |
190 ASSERT_TRUE(new_screen_bounds.IsEmpty()); | |
191 ASSERT_EQ(new_state, ui::SHOW_STATE_DEFAULT); | |
192 } | |
193 | |
194 // Test saving geometry, screen_bounds and state to the cache and state store, | |
195 // and reading it back. | |
196 TEST_F(ShellWindowGeometryCacheTest, SaveGeometryAndStateToStore) { | |
197 const std::string extension_id = prefs_->AddExtensionAndReturnId("ext1"); | |
198 const std::string window_id(kWindowId); | |
199 | |
200 // inform cache of extension | |
201 LoadExtension(extension_id); | |
202 | |
203 // update geometry stored in cache | |
204 gfx::Rect bounds(4, 5, 31, 43); | |
205 gfx::Rect screen_bounds(0, 0, 1600, 900); | |
206 ui::WindowShowState state = ui::SHOW_STATE_NORMAL; | |
207 cache_->SaveGeometry(extension_id, window_id, bounds, screen_bounds, state); | |
208 | |
209 // make sure that immediately reading back geometry works | |
210 gfx::Rect new_bounds; | |
211 gfx::Rect new_screen_bounds; | |
212 ui::WindowShowState new_state = ui::SHOW_STATE_DEFAULT; | |
213 ASSERT_TRUE(cache_->GetGeometry( | |
214 extension_id, window_id, &new_bounds, &new_screen_bounds, &new_state)); | |
215 ASSERT_EQ(bounds, new_bounds); | |
216 ASSERT_EQ(screen_bounds, new_screen_bounds); | |
217 ASSERT_EQ(state, new_state); | |
218 | |
219 // unload extension to force cache to save data to the state store | |
220 UnloadExtension(extension_id); | |
221 | |
222 // check if geometry got stored correctly in the state store | |
223 const base::DictionaryValue* dict = | |
224 prefs_->prefs()->GetGeometryCache(extension_id); | |
225 ASSERT_TRUE(dict); | |
226 | |
227 ASSERT_TRUE(dict->HasKey(window_id)); | |
228 int v; | |
229 ASSERT_TRUE(dict->GetInteger(window_id + ".x", &v)); | |
230 ASSERT_EQ(bounds.x(), v); | |
231 ASSERT_TRUE(dict->GetInteger(window_id + ".y", &v)); | |
232 ASSERT_EQ(bounds.y(), v); | |
233 ASSERT_TRUE(dict->GetInteger(window_id + ".w", &v)); | |
234 ASSERT_EQ(bounds.width(), v); | |
235 ASSERT_TRUE(dict->GetInteger(window_id + ".h", &v)); | |
236 ASSERT_EQ(bounds.height(), v); | |
237 ASSERT_TRUE(dict->GetInteger(window_id + ".screen_bounds_x", &v)); | |
238 ASSERT_EQ(screen_bounds.x(), v); | |
239 ASSERT_TRUE(dict->GetInteger(window_id + ".screen_bounds_y", &v)); | |
240 ASSERT_EQ(screen_bounds.y(), v); | |
241 ASSERT_TRUE(dict->GetInteger(window_id + ".screen_bounds_w", &v)); | |
242 ASSERT_EQ(screen_bounds.width(), v); | |
243 ASSERT_TRUE(dict->GetInteger(window_id + ".screen_bounds_h", &v)); | |
244 ASSERT_EQ(screen_bounds.height(), v); | |
245 ASSERT_TRUE(dict->GetInteger(window_id + ".state", &v)); | |
246 ASSERT_EQ(state, v); | |
247 | |
248 // reload extension | |
249 LoadExtension(extension_id); | |
250 // and make sure the geometry got reloaded properly too | |
251 ASSERT_TRUE(cache_->GetGeometry( | |
252 extension_id, window_id, &new_bounds, &new_screen_bounds, &new_state)); | |
253 ASSERT_EQ(bounds, new_bounds); | |
254 ASSERT_EQ(screen_bounds, new_screen_bounds); | |
255 ASSERT_EQ(state, new_state); | |
256 } | |
257 | |
258 // Tests that we won't do writes to the state store for SaveGeometry calls | |
259 // which don't change the state we already have. | |
260 TEST_F(ShellWindowGeometryCacheTest, NoDuplicateWrites) { | |
261 using testing::_; | |
262 using testing::Mock; | |
263 | |
264 const std::string extension_id = prefs_->AddExtensionAndReturnId("ext1"); | |
265 gfx::Rect bounds1(100, 200, 300, 400); | |
266 gfx::Rect bounds2(200, 400, 600, 800); | |
267 gfx::Rect bounds2_duplicate(200, 400, 600, 800); | |
268 | |
269 gfx::Rect screen_bounds1(0, 0, 1600, 900); | |
270 gfx::Rect screen_bounds2(0, 0, 1366, 768); | |
271 gfx::Rect screen_bounds2_duplicate(0, 0, 1366, 768); | |
272 | |
273 MockPrefChangeCallback observer(prefs_->pref_service()); | |
274 PrefChangeRegistrar registrar; | |
275 registrar.Init(prefs_->pref_service()); | |
276 registrar.Add("extensions.settings", observer.GetCallback()); | |
277 | |
278 // Write the first bounds - it should do > 0 writes. | |
279 EXPECT_CALL(observer, OnPreferenceChanged(_)); | |
280 cache_->SaveGeometry(extension_id, kWindowId, bounds1, | |
281 screen_bounds1, ui::SHOW_STATE_NORMAL); | |
282 WaitForSync(); | |
283 Mock::VerifyAndClearExpectations(&observer); | |
284 | |
285 // Write a different bounds - it should also do > 0 writes. | |
286 EXPECT_CALL(observer, OnPreferenceChanged(_)); | |
287 cache_->SaveGeometry(extension_id, kWindowId, bounds2, | |
288 screen_bounds1, ui::SHOW_STATE_NORMAL); | |
289 WaitForSync(); | |
290 Mock::VerifyAndClearExpectations(&observer); | |
291 | |
292 // Write a different screen bounds - it should also do > 0 writes. | |
293 EXPECT_CALL(observer, OnPreferenceChanged(_)); | |
294 cache_->SaveGeometry(extension_id, kWindowId, bounds2, | |
295 screen_bounds2, ui::SHOW_STATE_NORMAL); | |
296 WaitForSync(); | |
297 Mock::VerifyAndClearExpectations(&observer); | |
298 | |
299 // Write a different state - it should also do > 0 writes. | |
300 EXPECT_CALL(observer, OnPreferenceChanged(_)); | |
301 cache_->SaveGeometry(extension_id, kWindowId, bounds2, | |
302 screen_bounds2, ui::SHOW_STATE_MAXIMIZED); | |
303 WaitForSync(); | |
304 Mock::VerifyAndClearExpectations(&observer); | |
305 | |
306 // Write a bounds, screen bounds and state that's a duplicate of what we | |
307 // already have. This should not do any writes. | |
308 EXPECT_CALL(observer, OnPreferenceChanged(_)).Times(0); | |
309 cache_->SaveGeometry(extension_id, kWindowId, bounds2_duplicate, | |
310 screen_bounds2_duplicate, ui::SHOW_STATE_MAXIMIZED); | |
311 WaitForSync(); | |
312 Mock::VerifyAndClearExpectations(&observer); | |
313 } | |
314 | |
315 // Tests that no more than kMaxCachedWindows windows will be cached. | |
316 TEST_F(ShellWindowGeometryCacheTest, MaxWindows) { | |
317 const std::string extension_id = prefs_->AddExtensionAndReturnId("ext1"); | |
318 // inform cache of extension | |
319 LoadExtension(extension_id); | |
320 | |
321 gfx::Rect bounds(4, 5, 31, 43); | |
322 gfx::Rect screen_bounds(0, 0, 1600, 900); | |
323 for (size_t i = 0; i < ShellWindowGeometryCache::kMaxCachedWindows + 1; ++i) { | |
324 std::string window_id = "window_" + base::IntToString(i); | |
325 cache_->SaveGeometry(extension_id, window_id, bounds, | |
326 screen_bounds, ui::SHOW_STATE_NORMAL); | |
327 } | |
328 | |
329 // The first added window should no longer have cached geometry. | |
330 EXPECT_FALSE(cache_->GetGeometry(extension_id, "window_0", NULL, NULL, NULL)); | |
331 // All other windows should still exist. | |
332 for (size_t i = 1; i < ShellWindowGeometryCache::kMaxCachedWindows + 1; ++i) { | |
333 std::string window_id = "window_" + base::IntToString(i); | |
334 EXPECT_TRUE(cache_->GetGeometry(extension_id, window_id, NULL, NULL, NULL)); | |
335 } | |
336 } | |
337 | |
338 } // namespace extensions | |
OLD | NEW |