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

Unified 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, 2 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/extensions/webstore_bundle.cc ('k') | chrome/browser/extensions/webstore_installer.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/extensions/webstore_bundle_browsertest.cc
diff --git a/chrome/browser/extensions/webstore_bundle_browsertest.cc b/chrome/browser/extensions/webstore_bundle_browsertest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..251e7d2b4ec5fac2b0eb329c22d4fd150cc6e0ae
--- /dev/null
+++ b/chrome/browser/extensions/webstore_bundle_browsertest.cc
@@ -0,0 +1,477 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/stringprintf.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/string16.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/webstore_bundle.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/tab_contents/navigation_controller.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/mock_host_resolver.h"
+
+namespace {
+
+// These are just shorter aliases for some of the WebstoreBundle enums.
+WebstoreBundle::ItemMatcher MATCH_ALL = WebstoreBundle::MATCH_ALL;
+WebstoreBundle::ItemMatcher MATCH_APPS = WebstoreBundle::MATCH_APPS;
+WebstoreBundle::ItemMatcher MATCH_EXTENSIONS = WebstoreBundle::MATCH_EXTENSIONS;
+
+WebstoreBundle::Item::State STATE_INITIAL =
+ WebstoreBundle::Item::STATE_INITIAL;
+WebstoreBundle::Item::State STATE_APPROVED =
+ WebstoreBundle::Item::STATE_APPROVED;
+WebstoreBundle::Item::State STATE_INSTALLED =
+ WebstoreBundle::Item::STATE_INSTALLED;
+WebstoreBundle::Item::State STATE_NOT_INSTALLED =
+ WebstoreBundle::Item::STATE_NOT_INSTALLED;
+
+const char kGalleryDomain[] = "cws.com";
+
+struct TestItem {
+ std::string id;
+ std::string localized_name;
+ std::string manifest;
+};
+
+const TestItem kBundleItems[] = {
+ { "pkapffpjmiilhlhbibjhamlmdhfneidj",
+ "Bundle App 1",
+ "{"
+ " \"name\": \"Bundle App 1\","
+ " \"version\": \"1\","
+ " \"app\": {"
+ " \"urls\": [ \"http://www.testapp.com\" ],"
+ " \"launch\": { \"web_url\": \"http://www.testapp.com\" }"
+ " },"
+ " \"permissions\": [ \"clipboardRead\" ]"
+ "}" },
+ { "bmfoocgfinpmkmlbjhcbofejhkhlbchk",
+ "Extension Bundle 1",
+ "{"
+ " \"name\": \"Extension Bundle 1\","
+ " \"version\": \"1\","
+ " \"permissions\": [ \"tabs\" ]"
+ "}" },
+ { "mpneghmdnmaolkljkipbhaienajcflfe",
+ "Extension Bundle 2",
+ "{"
+ " \"name\": \"Extension Bundle 2\","
+ " \"version\": \"1\","
+ " \"permissions\": [\"management\", \"http://google.com\" ],"
+ " \"content_script\": [{"
+ " \"matches\": [ \"http://www.example.com/*\" ],"
+ " \"js\": [ \"content_script.js\" ],"
+ " \"run_at\": \"document_start\""
+ " }]"
+ "}" }
+};
+
+const TestItem k404BundleItems[] = {
+ { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "Extension A",
+ "{"
+ " \"name\": \"Extension A\","
+ " \"version\": \"1\","
+ " \"permissions\": [ \"tabs\" ]"
+ "}" }
+};
+
+const TestItem kBadItems[] = {
+ { "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+ "Extension B",
+ "{"
+ " \"name\": \"Extension B\","
+ " \"version\": \"1\","
+ " \"permissions\" [ \"http://*/*\" ]" // Syntax error on this line.
+ "}" },
+};
+
+const TestItem kBadCrxItems[] = {
+ { "cccccccccccccccccccccccccccccccc",
+ "Extension C",
+ "{"
+ " \"name\": \"Extension C\","
+ " \"version\": \"1\","
+ " \"permissions\": [ \"bookmarks\", \"http://example.org/*\" ]"
+ "}" },
+};
+
+// A WebstoreBundle::Delegate that waits for the callbacks and record the
+// results.
+class WebstoreBundleListener : public WebstoreBundle::Delegate {
+ public:
+ WebstoreBundleListener()
+ : approved_(false),
+ canceled_(false),
+ completed_(false),
+ waiting_(false) {}
+ virtual ~WebstoreBundleListener() {}
+
+ void OnBundleInstallApproved() OVERRIDE;
+ void OnBundleInstallCanceled(bool user_initiated) OVERRIDE;
+ void OnBundleInstallCompleted() OVERRIDE;
+
+ void ResetResults();
+ void Wait();
+
+ bool approved() { return approved_; }
+ bool canceled() { return canceled_; }
+ bool completed() { return completed_; }
+
+ private:
+ void QuitMessageLoopIfWaiting();
+
+ bool approved_;
+ bool canceled_;
+ bool completed_;
+ bool waiting_;
+};
+
+void WebstoreBundleListener::OnBundleInstallApproved() {
+ approved_ = true;
+ QuitMessageLoopIfWaiting();
+}
+
+void WebstoreBundleListener::OnBundleInstallCanceled(bool user_initiated) {
+ canceled_ = true;
+ QuitMessageLoopIfWaiting();
+}
+
+void WebstoreBundleListener::OnBundleInstallCompleted() {
+ completed_ = true;
+ QuitMessageLoopIfWaiting();
+}
+
+void WebstoreBundleListener::ResetResults() {
+ approved_ = false;
+ canceled_ = false;
+ completed_ = false;
+}
+
+void WebstoreBundleListener::Wait() {
+ if (approved_ || canceled_ || completed_)
+ return;
+
+ waiting_ = true;
+ ui_test_utils::RunMessageLoop();
+}
+
+void WebstoreBundleListener::QuitMessageLoopIfWaiting() {
+ if (waiting_) {
+ waiting_ = false;
+ MessageLoopForUI::current()->Quit();
+ }
+}
+
+class WebstoreBundleTest : public ExtensionBrowserTest {
+ public:
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE;
+ virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
+ protected:
+ GURL GetTestServerURL(const std::string& domain, const std::string& filename);
+};
+
+void WebstoreBundleTest::SetUpCommandLine(CommandLine* command_line) {
+ // We start the test server now instead of in SetUpInProcessTestFixture so
+ // we can get its port number.
+ ASSERT_TRUE(test_server()->Start());
+
+ InProcessBrowserTest::SetUpCommandLine(command_line);
+
+ net::HostPortPair host_pair = test_server()->host_port_pair();
+ std::string download_url = base::StringPrintf(
+ "http://%s:%d/files/extensions/api_test/webstore_private/bundle/%s.crx",
+ kGalleryDomain, host_pair.port(), "%s");
+ command_line->AppendSwitchASCII(
+ switches::kAppsGalleryDownloadURL, download_url);
+ command_line->AppendSwitchASCII(
+ switches::kAppsGalleryURL,
+ base::StringPrintf("http://%s", kGalleryDomain));
+}
+
+void WebstoreBundleTest::SetUpInProcessBrowserTestFixture() {
+ host_resolver()->AddRule(kGalleryDomain, "127.0.0.1");
+}
+
+GURL WebstoreBundleTest::GetTestServerURL(const std::string& domain,
+ const std::string& filename) {
+ GURL page_url = test_server()->GetURL(
+ "files/extensions/api_test/webstore_private/" + filename);
+
+ GURL::Replacements replace_host;
+ replace_host.SetHostStr(domain);
+ return page_url.ReplaceComponents(replace_host);
+}
+
+void AddItemsToItemList(WebstoreBundle::ItemList* item_list,
+ const TestItem items[],
+ size_t item_count) {
+ for (size_t i = 0; i < item_count; ++i)
+ item_list->push_back(new WebstoreBundle::Item(
+ items[i].id, items[i].manifest, items[i].localized_name));
+}
+
+} // namespace
+
+// Tests the state transitions and delegate callbacks when the user denies
+// the confirmation prompt.
+IN_PROC_BROWSER_TEST_F(WebstoreBundleTest, ParseAndCancel) {
+ WebstoreBundle::SetAutoApproveForTesting(false);
+
+ Profile* profile = browser()->profile();
+
+ WebstoreBundle::ItemList items;
+ AddItemsToItemList(&items, kBundleItems, ARRAYSIZE_UNSAFE(kBundleItems));
+ AddItemsToItemList(&items, kBadItems, ARRAYSIZE_UNSAFE(kBadItems));
+
+ scoped_refptr<WebstoreBundle> bundle = new WebstoreBundle(profile, &items);
+ scoped_ptr<WebstoreBundleListener> listener(new WebstoreBundleListener());
+
+ // All should start in STATE_INITIAL;
+ EXPECT_EQ(4u, bundle->GetItemsInState(STATE_INITIAL, MATCH_ALL).size());
+
+ bundle->PromptForApproval(listener.get());
+ listener->Wait();
+
+ EXPECT_TRUE(listener->canceled());
+ EXPECT_FALSE(listener->approved());
+
+ // All the extensions should be in STATE_NOT_INSTALLED now.
+ EXPECT_EQ(4u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size());
+}
+
+// Tests the regular flow for approving and installing a bundle.
+IN_PROC_BROWSER_TEST_F(WebstoreBundleTest, InstallSuccess) {
+ WebstoreBundle::SetAutoApproveForTesting(true);
+
+ Profile* profile = browser()->profile();
+ ExtensionService* service = profile->GetExtensionService();
+
+ WebstoreBundle::ItemList items;
+ AddItemsToItemList(&items, kBundleItems, ARRAYSIZE_UNSAFE(kBundleItems));
+
+ scoped_refptr<WebstoreBundle> bundle = new WebstoreBundle(profile, &items);
+ scoped_ptr<WebstoreBundleListener> listener(new WebstoreBundleListener());
+ bundle->PromptForApproval(listener.get());
+ listener->Wait();
+
+ EXPECT_TRUE(listener->approved());
+ EXPECT_FALSE(listener->canceled());
+
+ std::vector<const WebstoreBundle::Item*> approved_items =
+ bundle->GetItemsInState(STATE_APPROVED, MATCH_ALL);
+
+ // All members should have been parsed successfully.
+ EXPECT_EQ(3u, approved_items.size());
+
+ // The items should have had there manifests parsed.
+ for (size_t i = 0; i < approved_items.size(); ++i)
+ EXPECT_TRUE(approved_items[i]->dummy_extension().get());
+
+ // No extensions should have failed.
+ EXPECT_EQ(0u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size());
+
+ // Two extensions and one app were passed in.
+ EXPECT_EQ(1u, bundle->GetItemsInState(STATE_APPROVED, MATCH_APPS).size());
+ EXPECT_EQ(2u, bundle->GetItemsInState(
+ STATE_APPROVED, MATCH_EXTENSIONS).size());
+
+ EXPECT_TRUE(bundle->HasExtensionsInState(STATE_APPROVED));
+ EXPECT_TRUE(bundle->HasAppsInState(STATE_APPROVED));
+
+ listener->ResetResults();
+
+ // This page doesn't exist, but it doesn't matter.
+ ui_test_utils::NavigateToURL(browser(), GetTestServerURL(kGalleryDomain, ""));
+ TabStripModel* tabstrip = browser()->tabstrip_model();
+
+ // Install the bundle.
+ bundle->CompleteInstall(&tabstrip->GetTabContentsAt(0)->controller(),
+ listener.get());
+
+ listener->Wait();
+
+ ASSERT_TRUE(listener->completed());
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kBundleItems); ++i)
+ EXPECT_TRUE(service->GetExtensionById(kBundleItems[i].id, false));
+
+ // 2 apps and 1 extension should have been installed.
+ EXPECT_EQ(3u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_ALL).size());
+ EXPECT_EQ(1u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_APPS).size());
+ EXPECT_EQ(2u, bundle->GetItemsInState(
+ STATE_INSTALLED, MATCH_EXTENSIONS).size());
+
+ // The failed heading should be empty since all succeeded.
+ EXPECT_TRUE(bundle->GetFailedHeadingTextForBubble().empty());
+ EXPECT_TRUE(!bundle->GetInstalledHeadingTextForBubble().empty());
+}
+
+// Tests that the WebstoreBundle handles crx files that fail to install.
+IN_PROC_BROWSER_TEST_F(WebstoreBundleTest, InstallSuccessAndFailure) {
+ WebstoreBundle::SetAutoApproveForTesting(true);
+
+ Profile* profile = browser()->profile();
+ ExtensionService* service = profile->GetExtensionService();
+
+ WebstoreBundle::ItemList items;
+ // Install 2 extensions and 1 app.
+ AddItemsToItemList(&items, kBundleItems, ARRAYSIZE_UNSAFE(kBundleItems));
+ // Fail to parse 1 extension manifest.
+ AddItemsToItemList(&items, kBadItems, ARRAYSIZE_UNSAFE(kBadItems));
+ // Fail to install 1 extension.
+ AddItemsToItemList(
+ &items, kBadCrxItems, ARRAYSIZE_UNSAFE(kBadCrxItems));
+
+ scoped_refptr<WebstoreBundle> bundle = new WebstoreBundle(profile, &items);
+ scoped_ptr<WebstoreBundleListener> listener(new WebstoreBundleListener());
+ bundle->PromptForApproval(listener.get());
+ listener->Wait();
+
+ EXPECT_TRUE(listener->approved());
+ EXPECT_FALSE(listener->canceled());
+
+ std::vector<const WebstoreBundle::Item*> approved_items =
+ bundle->GetItemsInState(STATE_APPROVED, MATCH_ALL);
+
+ // All items except the one with the bad manifest should have been parsed.
+ EXPECT_EQ(4u, approved_items.size());
+ for (size_t i = 0; i < approved_items.size(); ++i)
+ EXPECT_TRUE(approved_items[i]->dummy_extension().get());
+
+ // While the one with the syntax error should have gone to
+ // STATE_NOT_INSTALLED.
+ EXPECT_EQ(1u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size());
+
+ // Three extensions and one app should have been parsed successfully.
+ EXPECT_EQ(1u, bundle->GetItemsInState(STATE_APPROVED, MATCH_APPS).size());
+ EXPECT_EQ(3u, bundle->GetItemsInState(
+ STATE_APPROVED, MATCH_EXTENSIONS).size());
+
+ EXPECT_TRUE(bundle->HasExtensionsInState(STATE_APPROVED));
+ EXPECT_TRUE(bundle->HasAppsInState(STATE_APPROVED));
+
+ listener->ResetResults();
+
+ // This page doesn't exist, but it doesn't matter.
+ ui_test_utils::NavigateToURL(browser(), GetTestServerURL(kGalleryDomain, ""));
+ TabStripModel* tabstrip = browser()->tabstrip_model();
+
+ // Install the bundle.
+ bundle->CompleteInstall(&tabstrip->GetTabContentsAt(0)->controller(),
+ listener.get());
+
+ listener->Wait();
+
+ ASSERT_TRUE(listener->completed());
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kBundleItems); ++i)
+ EXPECT_TRUE(service->GetExtensionById(kBundleItems[i].id, false));
+
+ // Two extensions and one app should have been installed.
+ EXPECT_EQ(3u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_ALL).size());
+ EXPECT_EQ(1u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_APPS).size());
+ EXPECT_EQ(2u, bundle->GetItemsInState(
+ STATE_INSTALLED, MATCH_EXTENSIONS).size());
+
+ // Two extensions failed to be installed -- one at the manifest parse state
+ // and one while unpacking the crx.
+ EXPECT_EQ(2u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size());
+
+ EXPECT_TRUE(!bundle->GetFailedHeadingTextForBubble().empty());
+ EXPECT_TRUE(!bundle->GetInstalledHeadingTextForBubble().empty());
+}
+
+/* The WebstoreInstaller can't handle 404 errors right now.
+IN_PROC_BROWSER_TEST_F(WebstoreBundleTest, FAILS_InstallSuccessAnd404Failure) {
+ WebstoreBundle::SetAutoApproveForTesting(true);
+
+ Profile* profile = browser()->profile();
+ ExtensionService* service = profile->GetExtensionService();
+
+ WebstoreBundle::ItemList items;
+ // kBundleItems will install 2 extensions and 1 app.
+ AddItemsToItemList(&items, kBundleItems, ARRAYSIZE_UNSAFE(kBundleItems));
+
+ // kBadItems will fail parsing of 1 extension.
+ AddItemsToItemList(&items, kBadItems, ARRAYSIZE_UNSAFE(kBadItems));
+
+ // k404BundleItems will 404 when installing 1 extension.
+ AddItemsToItemList(
+ &items, k404BundleItems, ARRAYSIZE_UNSAFE(k404BundleItems));
+
+ scoped_refptr<WebstoreBundle> bundle = new WebstoreBundle(profile, &items);
+ scoped_ptr<WebstoreBundleListener> listener(new WebstoreBundleListener());
+ bundle->PromptForApproval(listener.get());
+ listener->Wait();
+
+ EXPECT_TRUE(listener->approved());
+ EXPECT_FALSE(listener->canceled());
+
+ std::vector<const WebstoreBundle::Item*> approved_items =
+ bundle->GetItemsInState(STATE_APPROVED, MATCH_ALL);
+
+ // All members should have been parsed successfully.
+ EXPECT_EQ(4u, approved_items.size());
+
+ // The items should have had there manifests parsed.
+ for (size_t i = 0; i < approved_items.size(); ++i)
+ EXPECT_TRUE(approved_items[i]->dummy_extension().get());
+
+ // While the one with the syntax error should have gone to
+ // STATE_NOT_INSTALLED.
+ EXPECT_EQ(1u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size());
+
+ // Three extensions and one app should have been parsed successfully.
+ EXPECT_EQ(1u, bundle->GetItemsInState(STATE_APPROVED, MATCH_APPS).size());
+ EXPECT_EQ(3u, bundle->GetItemsInState(
+ STATE_APPROVED, MATCH_EXTENSIONS).size());
+
+ EXPECT_TRUE(bundle->HasExtensionsInState(STATE_APPROVED));
+ EXPECT_TRUE(bundle->HasAppsInState(STATE_APPROVED));
+
+ listener->ResetResults();
+
+ // This page doesn't exist, but it doesn't matter.
+ ui_test_utils::NavigateToURL(browser(), GetTestServerURL(kGalleryDomain, ""));
+ TabStripModel* tabstrip = browser()->tabstrip_model();
+
+ // Install the bundle.
+ bundle->CompleteInstall(&tabstrip->GetTabContentsAt(0)->controller(),
+ listener.get());
+
+ listener->Wait();
+
+ ASSERT_TRUE(listener->completed());
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kBundleItems); ++i)
+ EXPECT_TRUE(service->GetExtensionById(kBundleItems[i].id, false));
+
+ EXPECT_EQ(3u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_ALL).size());
+ EXPECT_EQ(1u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_APPS).size());
+ EXPECT_EQ(2u, bundle->GetItemsInState(
+ STATE_INSTALLED, MATCH_EXTENSIONS).size());
+ EXPECT_EQ(2u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size());
+
+ EXPECT_TRUE(!bundle->GetFailedHeadingTextForBubble().empty());
+ EXPECT_TRUE(!bundle->GetInstalledHeadingTextForBubble().empty());
+}
+*/
« 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