Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(269)

Side by Side Diff: chrome/browser/extensions/webstore_bundle_browsertest.cc

Issue 8391004: Add webstorePrivate API for installing bundles of extensions. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 <string>
6 #include <vector>
7
8 #include "base/command_line.h"
9 #include "base/stringprintf.h"
10 #include "base/basictypes.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop.h"
15 #include "base/string16.h"
16 #include "chrome/browser/extensions/extension_browsertest.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/webstore_bundle.h"
19 #include "chrome/browser/tabs/tab_strip_model.h"
20 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/test/base/testing_profile.h"
24 #include "chrome/test/base/ui_test_utils.h"
25 #include "content/browser/browser_thread.h"
26 #include "content/browser/tab_contents/navigation_controller.h"
27 #include "googleurl/src/gurl.h"
28 #include "net/base/host_port_pair.h"
29 #include "net/base/mock_host_resolver.h"
30
31 namespace {
32
33 // These are just shorter aliases for some of the WebstoreBundle enums.
34 WebstoreBundle::ItemMatcher MATCH_ALL = WebstoreBundle::MATCH_ALL;
35 WebstoreBundle::ItemMatcher MATCH_APPS = WebstoreBundle::MATCH_APPS;
36 WebstoreBundle::ItemMatcher MATCH_EXTENSIONS = WebstoreBundle::MATCH_EXTENSIONS;
37
38 WebstoreBundle::Item::State STATE_INITIAL =
39 WebstoreBundle::Item::STATE_INITIAL;
40 WebstoreBundle::Item::State STATE_APPROVED =
41 WebstoreBundle::Item::STATE_APPROVED;
42 WebstoreBundle::Item::State STATE_INSTALLED =
43 WebstoreBundle::Item::STATE_INSTALLED;
44 WebstoreBundle::Item::State STATE_NOT_INSTALLED =
45 WebstoreBundle::Item::STATE_NOT_INSTALLED;
46
47 const char kGalleryDomain[] = "cws.com";
48
49 struct TestItem {
50 std::string id;
51 std::string localized_name;
52 std::string manifest;
53 };
54
55 const TestItem kBundleItems[] = {
56 { "pkapffpjmiilhlhbibjhamlmdhfneidj",
57 "Bundle App 1",
58 "{"
59 " \"name\": \"Bundle App 1\","
60 " \"version\": \"1\","
61 " \"app\": {"
62 " \"urls\": [ \"http://www.testapp.com\" ],"
63 " \"launch\": { \"web_url\": \"http://www.testapp.com\" }"
64 " },"
65 " \"permissions\": [ \"clipboardRead\" ]"
66 "}" },
67 { "bmfoocgfinpmkmlbjhcbofejhkhlbchk",
68 "Extension Bundle 1",
69 "{"
70 " \"name\": \"Extension Bundle 1\","
71 " \"version\": \"1\","
72 " \"permissions\": [ \"tabs\" ]"
73 "}" },
74 { "mpneghmdnmaolkljkipbhaienajcflfe",
75 "Extension Bundle 2",
76 "{"
77 " \"name\": \"Extension Bundle 2\","
78 " \"version\": \"1\","
79 " \"permissions\": [\"management\", \"http://google.com\" ],"
80 " \"content_script\": [{"
81 " \"matches\": [ \"http://www.example.com/*\" ],"
82 " \"js\": [ \"content_script.js\" ],"
83 " \"run_at\": \"document_start\""
84 " }]"
85 "}" }
86 };
87
88 const TestItem k404BundleItems[] = {
89 { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
90 "Extension A",
91 "{"
92 " \"name\": \"Extension A\","
93 " \"version\": \"1\","
94 " \"permissions\": [ \"tabs\" ]"
95 "}" }
96 };
97
98 const TestItem kBadItems[] = {
99 { "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
100 "Extension B",
101 "{"
102 " \"name\": \"Extension B\","
103 " \"version\": \"1\","
104 " \"permissions\" [ \"http://*/*\" ]" // Syntax error on this line.
105 "}" },
106 };
107
108 const TestItem kBadCrxItems[] = {
109 { "cccccccccccccccccccccccccccccccc",
110 "Extension C",
111 "{"
112 " \"name\": \"Extension C\","
113 " \"version\": \"1\","
114 " \"permissions\": [ \"bookmarks\", \"http://example.org/*\" ]"
115 "}" },
116 };
117
118 // A WebstoreBundle::Delegate that waits for the callbacks and record the
119 // results.
120 class WebstoreBundleListener : public WebstoreBundle::Delegate {
121 public:
122 WebstoreBundleListener()
123 : approved_(false),
124 canceled_(false),
125 completed_(false),
126 waiting_(false) {}
127 virtual ~WebstoreBundleListener() {}
128
129 void OnBundleInstallApproved() OVERRIDE;
130 void OnBundleInstallCanceled(bool user_initiated) OVERRIDE;
131 void OnBundleInstallCompleted() OVERRIDE;
132
133 void ResetResults();
134 void Wait();
135
136 bool approved() { return approved_; }
137 bool canceled() { return canceled_; }
138 bool completed() { return completed_; }
139
140 private:
141 void QuitMessageLoopIfWaiting();
142
143 bool approved_;
144 bool canceled_;
145 bool completed_;
146 bool waiting_;
147 };
148
149 void WebstoreBundleListener::OnBundleInstallApproved() {
150 approved_ = true;
151 QuitMessageLoopIfWaiting();
152 }
153
154 void WebstoreBundleListener::OnBundleInstallCanceled(bool user_initiated) {
155 canceled_ = true;
156 QuitMessageLoopIfWaiting();
157 }
158
159 void WebstoreBundleListener::OnBundleInstallCompleted() {
160 completed_ = true;
161 QuitMessageLoopIfWaiting();
162 }
163
164 void WebstoreBundleListener::ResetResults() {
165 approved_ = false;
166 canceled_ = false;
167 completed_ = false;
168 }
169
170 void WebstoreBundleListener::Wait() {
171 if (approved_ || canceled_ || completed_)
172 return;
173
174 waiting_ = true;
175 ui_test_utils::RunMessageLoop();
176 }
177
178 void WebstoreBundleListener::QuitMessageLoopIfWaiting() {
179 if (waiting_) {
180 waiting_ = false;
181 MessageLoopForUI::current()->Quit();
182 }
183 }
184
185 class WebstoreBundleTest : public ExtensionBrowserTest {
186 public:
187 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE;
188 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
189 protected:
190 GURL GetTestServerURL(const std::string& domain, const std::string& filename);
191 };
192
193 void WebstoreBundleTest::SetUpCommandLine(CommandLine* command_line) {
194 // We start the test server now instead of in SetUpInProcessTestFixture so
195 // we can get its port number.
196 ASSERT_TRUE(test_server()->Start());
197
198 InProcessBrowserTest::SetUpCommandLine(command_line);
199
200 net::HostPortPair host_pair = test_server()->host_port_pair();
201 std::string download_url = base::StringPrintf(
202 "http://%s:%d/files/extensions/api_test/webstore_private/bundle/%s.crx",
203 kGalleryDomain, host_pair.port(), "%s");
204 command_line->AppendSwitchASCII(
205 switches::kAppsGalleryDownloadURL, download_url);
206 command_line->AppendSwitchASCII(
207 switches::kAppsGalleryURL,
208 base::StringPrintf("http://%s", kGalleryDomain));
209 }
210
211 void WebstoreBundleTest::SetUpInProcessBrowserTestFixture() {
212 host_resolver()->AddRule(kGalleryDomain, "127.0.0.1");
213 }
214
215 GURL WebstoreBundleTest::GetTestServerURL(const std::string& domain,
216 const std::string& filename) {
217 GURL page_url = test_server()->GetURL(
218 "files/extensions/api_test/webstore_private/" + filename);
219
220 GURL::Replacements replace_host;
221 replace_host.SetHostStr(domain);
222 return page_url.ReplaceComponents(replace_host);
223 }
224
225 void AddItemsToItemList(WebstoreBundle::ItemList* item_list,
226 const TestItem items[],
227 size_t item_count) {
228 for (size_t i = 0; i < item_count; ++i)
229 item_list->push_back(new WebstoreBundle::Item(
230 items[i].id, items[i].manifest, items[i].localized_name));
231 }
232
233 } // namespace
234
235 // Tests the state transitions and delegate callbacks when the user denies
236 // the confirmation prompt.
237 IN_PROC_BROWSER_TEST_F(WebstoreBundleTest, ParseAndCancel) {
238 WebstoreBundle::SetAutoApproveForTesting(false);
239
240 Profile* profile = browser()->profile();
241
242 WebstoreBundle::ItemList items;
243 AddItemsToItemList(&items, kBundleItems, ARRAYSIZE_UNSAFE(kBundleItems));
244 AddItemsToItemList(&items, kBadItems, ARRAYSIZE_UNSAFE(kBadItems));
245
246 scoped_refptr<WebstoreBundle> bundle = new WebstoreBundle(profile, &items);
247 scoped_ptr<WebstoreBundleListener> listener(new WebstoreBundleListener());
248
249 // All should start in STATE_INITIAL;
250 EXPECT_EQ(4u, bundle->GetItemsInState(STATE_INITIAL, MATCH_ALL).size());
251
252 bundle->PromptForApproval(listener.get());
253 listener->Wait();
254
255 EXPECT_TRUE(listener->canceled());
256 EXPECT_FALSE(listener->approved());
257
258 // All the extensions should be in STATE_NOT_INSTALLED now.
259 EXPECT_EQ(4u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size());
260 }
261
262 // Tests the regular flow for approving and installing a bundle.
263 IN_PROC_BROWSER_TEST_F(WebstoreBundleTest, InstallSuccess) {
264 WebstoreBundle::SetAutoApproveForTesting(true);
265
266 Profile* profile = browser()->profile();
267 ExtensionService* service = profile->GetExtensionService();
268
269 WebstoreBundle::ItemList items;
270 AddItemsToItemList(&items, kBundleItems, ARRAYSIZE_UNSAFE(kBundleItems));
271
272 scoped_refptr<WebstoreBundle> bundle = new WebstoreBundle(profile, &items);
273 scoped_ptr<WebstoreBundleListener> listener(new WebstoreBundleListener());
274 bundle->PromptForApproval(listener.get());
275 listener->Wait();
276
277 EXPECT_TRUE(listener->approved());
278 EXPECT_FALSE(listener->canceled());
279
280 std::vector<const WebstoreBundle::Item*> approved_items =
281 bundle->GetItemsInState(STATE_APPROVED, MATCH_ALL);
282
283 // All members should have been parsed successfully.
284 EXPECT_EQ(3u, approved_items.size());
285
286 // The items should have had there manifests parsed.
287 for (size_t i = 0; i < approved_items.size(); ++i)
288 EXPECT_TRUE(approved_items[i]->dummy_extension().get());
289
290 // No extensions should have failed.
291 EXPECT_EQ(0u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size());
292
293 // Two extensions and one app were passed in.
294 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_APPROVED, MATCH_APPS).size());
295 EXPECT_EQ(2u, bundle->GetItemsInState(
296 STATE_APPROVED, MATCH_EXTENSIONS).size());
297
298 EXPECT_TRUE(bundle->HasExtensionsInState(STATE_APPROVED));
299 EXPECT_TRUE(bundle->HasAppsInState(STATE_APPROVED));
300
301 listener->ResetResults();
302
303 // This page doesn't exist, but it doesn't matter.
304 ui_test_utils::NavigateToURL(browser(), GetTestServerURL(kGalleryDomain, ""));
305 TabStripModel* tabstrip = browser()->tabstrip_model();
306
307 // Install the bundle.
308 bundle->CompleteInstall(&tabstrip->GetTabContentsAt(0)->controller(),
309 listener.get());
310
311 listener->Wait();
312
313 ASSERT_TRUE(listener->completed());
314 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kBundleItems); ++i)
315 EXPECT_TRUE(service->GetExtensionById(kBundleItems[i].id, false));
316
317 // 2 apps and 1 extension should have been installed.
318 EXPECT_EQ(3u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_ALL).size());
319 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_APPS).size());
320 EXPECT_EQ(2u, bundle->GetItemsInState(
321 STATE_INSTALLED, MATCH_EXTENSIONS).size());
322
323 // The failed heading should be empty since all succeeded.
324 EXPECT_TRUE(bundle->GetFailedHeadingTextForBubble().empty());
325 EXPECT_TRUE(!bundle->GetInstalledHeadingTextForBubble().empty());
326 }
327
328 // Tests that the WebstoreBundle handles crx files that fail to install.
329 IN_PROC_BROWSER_TEST_F(WebstoreBundleTest, InstallSuccessAndFailure) {
330 WebstoreBundle::SetAutoApproveForTesting(true);
331
332 Profile* profile = browser()->profile();
333 ExtensionService* service = profile->GetExtensionService();
334
335 WebstoreBundle::ItemList items;
336 // Install 2 extensions and 1 app.
337 AddItemsToItemList(&items, kBundleItems, ARRAYSIZE_UNSAFE(kBundleItems));
338 // Fail to parse 1 extension manifest.
339 AddItemsToItemList(&items, kBadItems, ARRAYSIZE_UNSAFE(kBadItems));
340 // Fail to install 1 extension.
341 AddItemsToItemList(
342 &items, kBadCrxItems, ARRAYSIZE_UNSAFE(kBadCrxItems));
343
344 scoped_refptr<WebstoreBundle> bundle = new WebstoreBundle(profile, &items);
345 scoped_ptr<WebstoreBundleListener> listener(new WebstoreBundleListener());
346 bundle->PromptForApproval(listener.get());
347 listener->Wait();
348
349 EXPECT_TRUE(listener->approved());
350 EXPECT_FALSE(listener->canceled());
351
352 std::vector<const WebstoreBundle::Item*> approved_items =
353 bundle->GetItemsInState(STATE_APPROVED, MATCH_ALL);
354
355 // All items except the one with the bad manifest should have been parsed.
356 EXPECT_EQ(4u, approved_items.size());
357 for (size_t i = 0; i < approved_items.size(); ++i)
358 EXPECT_TRUE(approved_items[i]->dummy_extension().get());
359
360 // While the one with the syntax error should have gone to
361 // STATE_NOT_INSTALLED.
362 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size());
363
364 // Three extensions and one app should have been parsed successfully.
365 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_APPROVED, MATCH_APPS).size());
366 EXPECT_EQ(3u, bundle->GetItemsInState(
367 STATE_APPROVED, MATCH_EXTENSIONS).size());
368
369 EXPECT_TRUE(bundle->HasExtensionsInState(STATE_APPROVED));
370 EXPECT_TRUE(bundle->HasAppsInState(STATE_APPROVED));
371
372 listener->ResetResults();
373
374 // This page doesn't exist, but it doesn't matter.
375 ui_test_utils::NavigateToURL(browser(), GetTestServerURL(kGalleryDomain, ""));
376 TabStripModel* tabstrip = browser()->tabstrip_model();
377
378 // Install the bundle.
379 bundle->CompleteInstall(&tabstrip->GetTabContentsAt(0)->controller(),
380 listener.get());
381
382 listener->Wait();
383
384 ASSERT_TRUE(listener->completed());
385
386 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kBundleItems); ++i)
387 EXPECT_TRUE(service->GetExtensionById(kBundleItems[i].id, false));
388
389 // Two extensions and one app should have been installed.
390 EXPECT_EQ(3u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_ALL).size());
391 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_APPS).size());
392 EXPECT_EQ(2u, bundle->GetItemsInState(
393 STATE_INSTALLED, MATCH_EXTENSIONS).size());
394
395 // Two extensions failed to be installed -- one at the manifest parse state
396 // and one while unpacking the crx.
397 EXPECT_EQ(2u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size());
398
399 EXPECT_TRUE(!bundle->GetFailedHeadingTextForBubble().empty());
400 EXPECT_TRUE(!bundle->GetInstalledHeadingTextForBubble().empty());
401 }
402
403 /* The WebstoreInstaller can't handle 404 errors right now.
404 IN_PROC_BROWSER_TEST_F(WebstoreBundleTest, FAILS_InstallSuccessAnd404Failure) {
405 WebstoreBundle::SetAutoApproveForTesting(true);
406
407 Profile* profile = browser()->profile();
408 ExtensionService* service = profile->GetExtensionService();
409
410 WebstoreBundle::ItemList items;
411 // kBundleItems will install 2 extensions and 1 app.
412 AddItemsToItemList(&items, kBundleItems, ARRAYSIZE_UNSAFE(kBundleItems));
413
414 // kBadItems will fail parsing of 1 extension.
415 AddItemsToItemList(&items, kBadItems, ARRAYSIZE_UNSAFE(kBadItems));
416
417 // k404BundleItems will 404 when installing 1 extension.
418 AddItemsToItemList(
419 &items, k404BundleItems, ARRAYSIZE_UNSAFE(k404BundleItems));
420
421 scoped_refptr<WebstoreBundle> bundle = new WebstoreBundle(profile, &items);
422 scoped_ptr<WebstoreBundleListener> listener(new WebstoreBundleListener());
423 bundle->PromptForApproval(listener.get());
424 listener->Wait();
425
426 EXPECT_TRUE(listener->approved());
427 EXPECT_FALSE(listener->canceled());
428
429 std::vector<const WebstoreBundle::Item*> approved_items =
430 bundle->GetItemsInState(STATE_APPROVED, MATCH_ALL);
431
432 // All members should have been parsed successfully.
433 EXPECT_EQ(4u, approved_items.size());
434
435 // The items should have had there manifests parsed.
436 for (size_t i = 0; i < approved_items.size(); ++i)
437 EXPECT_TRUE(approved_items[i]->dummy_extension().get());
438
439 // While the one with the syntax error should have gone to
440 // STATE_NOT_INSTALLED.
441 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size());
442
443 // Three extensions and one app should have been parsed successfully.
444 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_APPROVED, MATCH_APPS).size());
445 EXPECT_EQ(3u, bundle->GetItemsInState(
446 STATE_APPROVED, MATCH_EXTENSIONS).size());
447
448 EXPECT_TRUE(bundle->HasExtensionsInState(STATE_APPROVED));
449 EXPECT_TRUE(bundle->HasAppsInState(STATE_APPROVED));
450
451 listener->ResetResults();
452
453 // This page doesn't exist, but it doesn't matter.
454 ui_test_utils::NavigateToURL(browser(), GetTestServerURL(kGalleryDomain, ""));
455 TabStripModel* tabstrip = browser()->tabstrip_model();
456
457 // Install the bundle.
458 bundle->CompleteInstall(&tabstrip->GetTabContentsAt(0)->controller(),
459 listener.get());
460
461 listener->Wait();
462
463 ASSERT_TRUE(listener->completed());
464
465 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kBundleItems); ++i)
466 EXPECT_TRUE(service->GetExtensionById(kBundleItems[i].id, false));
467
468 EXPECT_EQ(3u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_ALL).size());
469 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_APPS).size());
470 EXPECT_EQ(2u, bundle->GetItemsInState(
471 STATE_INSTALLED, MATCH_EXTENSIONS).size());
472 EXPECT_EQ(2u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size());
473
474 EXPECT_TRUE(!bundle->GetFailedHeadingTextForBubble().empty());
475 EXPECT_TRUE(!bundle->GetInstalledHeadingTextForBubble().empty());
476 }
477 */
OLDNEW
« no previous file with comments | « chrome/browser/extensions/webstore_bundle.cc ('k') | chrome/browser/extensions/webstore_installer.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698