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 "base/memory/ref_counted.h"
6#include "chrome/browser/extensions/extension_error_controller.h"
7#include "chrome/browser/extensions/extension_error_ui.h"
8#include "chrome/browser/extensions/extension_service.h"
9#include "chrome/browser/extensions/extension_service_test_base.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/test/base/testing_profile.h"
12#include "extensions/browser/extension_prefs.h"
13#include "extensions/browser/extension_registry.h"
14#include "extensions/common/extension.h"
15#include "extensions/common/extension_builder.h"
16#include "extensions/common/value_builder.h"
17
18namespace extensions {
19
20namespace {
21
22// Create a mock for the UI component of the error alert that is shown for
23// blacklisted extensions. This allows us to test which extensions the alert
24// is showing, and also eliminates the UI component (since this is a unit
25// test).
26class MockExtensionErrorUI : public ExtensionErrorUI {
27 public:
28  explicit MockExtensionErrorUI(ExtensionErrorUI::Delegate* delegate);
29  virtual ~MockExtensionErrorUI();
30
31  // Wrappers around the similar methods in ExtensionErrorUI.
32  void CloseUI();
33  void Accept();
34  void Details();
35
36  ExtensionErrorUI::Delegate* delegate() { return delegate_; }
37
38 private:
39  // ExtensionErrorUI implementation.
40  virtual bool ShowErrorInBubbleView() OVERRIDE;
41  virtual void ShowExtensions() OVERRIDE;
42  virtual void Close() OVERRIDE;
43
44  // Keep a copy of the delegate around for ourselves.
45  ExtensionErrorUI::Delegate* delegate_;
46};
47
48// We use this as a slight hack to get the created Error UI, if any. We should
49// only ever have one (since this is a single-profile test), and this avoids
50// the need for any kind of accessor to the ErrorController from
51// ExtensionService.
52MockExtensionErrorUI* g_error_ui = NULL;
53
54MockExtensionErrorUI::MockExtensionErrorUI(
55    ExtensionErrorUI::Delegate* delegate)
56    : ExtensionErrorUI(delegate),
57      delegate_(delegate) {
58  // We should never make more than one of these in a test.
59  DCHECK(!g_error_ui);
60  g_error_ui = this;
61}
62
63MockExtensionErrorUI::~MockExtensionErrorUI() {
64  g_error_ui = NULL;
65}
66
67void MockExtensionErrorUI::CloseUI() {
68  BubbleViewDidClose();
69}
70
71void MockExtensionErrorUI::Accept() {
72  BubbleViewAcceptButtonPressed();
73}
74
75void MockExtensionErrorUI::Details() {
76  BubbleViewCancelButtonPressed();
77}
78
79bool MockExtensionErrorUI::ShowErrorInBubbleView() {
80  return true;
81}
82
83void MockExtensionErrorUI::ShowExtensions() {}
84
85void MockExtensionErrorUI::Close() {
86  CloseUI();
87}
88
89ExtensionErrorUI* CreateMockUI(ExtensionErrorUI::Delegate* delegate) {
90  return new MockExtensionErrorUI(delegate);
91}
92
93// Builds and returns a simple extension.
94scoped_refptr<const Extension> BuildExtension() {
95  return ExtensionBuilder()
96      .SetManifest(DictionaryBuilder().Set("name", "My Wonderful Extension")
97                                      .Set("version", "0.1.1.0")
98                                      .Set("manifest_version", 2)
99                                      .Build())
100      .Build();
101}
102
103}  // namespace
104
105class ExtensionErrorControllerUnitTest : public ExtensionServiceTestBase {
106 protected:
107  virtual void SetUp() OVERRIDE;
108
109  // Add an extension to chrome, and mark it as blacklisted in the prefs.
110  testing::AssertionResult AddBlacklistedExtension(const Extension* extension);
111
112  // Return the ExtensionPrefs associated with the test.
113  ExtensionPrefs* GetPrefs();
114
115  Profile* profile() { return profile_.get(); }
116};
117
118void ExtensionErrorControllerUnitTest::SetUp() {
119  ExtensionServiceTestBase::SetUp();
120  // Make sure we use the mock UI instead of the real UI.
121  ExtensionErrorController::SetUICreateMethodForTesting(CreateMockUI);
122
123  // We don't want a first-run ExtensionService, since we ignore warnings
124  // for new profiles.
125  ExtensionServiceInitParams params = CreateDefaultInitParams();
126  params.is_first_run = false;
127  InitializeExtensionService(params);
128}
129
130testing::AssertionResult
131ExtensionErrorControllerUnitTest::AddBlacklistedExtension(
132    const Extension* extension) {
133  GetPrefs()->SetExtensionBlacklisted(extension->id(), true);
134  service_->AddExtension(extension);
135
136  // Make sure the extension is added to the blacklisted set.
137  if (!ExtensionRegistry::Get(profile())->blacklisted_extensions()
138          .Contains(extension->id())) {
139    return testing::AssertionFailure()
140        << "Failed to add blacklisted extension.";
141  }
142
143  return testing::AssertionSuccess();
144}
145
146ExtensionPrefs* ExtensionErrorControllerUnitTest::GetPrefs() {
147  return ExtensionPrefs::Get(profile());
148}
149
150// Test that closing the extension alert for blacklisted extensions counts
151// as acknowledging them in the prefs.
152TEST_F(ExtensionErrorControllerUnitTest, ClosingAcknowledgesBlacklisted) {
153  // Add a blacklisted extension.
154  scoped_refptr<const Extension> extension = BuildExtension();
155  ASSERT_TRUE(AddBlacklistedExtension(extension.get()));
156
157  service_->Init();
158
159  // Make sure that we created an error "ui" to warn about the blacklisted
160  // extension.
161  ASSERT_TRUE(g_error_ui);
162  ExtensionErrorUI::Delegate* delegate = g_error_ui->delegate();
163  ASSERT_TRUE(delegate);
164
165  // Make sure that the blacklisted extension is reported (and that no other
166  // extensions are).
167  const ExtensionSet& delegate_blacklisted_extensions =
168      delegate->GetBlacklistedExtensions();
169  EXPECT_EQ(1u, delegate_blacklisted_extensions.size());
170  EXPECT_TRUE(delegate_blacklisted_extensions.Contains(extension->id()));
171
172  // Close, and verify that the extension ids now acknowledged.
173  g_error_ui->CloseUI();
174  EXPECT_TRUE(GetPrefs()->IsBlacklistedExtensionAcknowledged(extension->id()));
175  // Verify we cleaned up after ourselves.
176  EXPECT_FALSE(g_error_ui);
177}
178
179// Test that clicking "accept" on the extension alert counts as acknowledging
180// blacklisted extensions.
181TEST_F(ExtensionErrorControllerUnitTest, AcceptingAcknowledgesBlacklisted) {
182  // Add a blacklisted extension.
183  scoped_refptr<const Extension> extension = BuildExtension();
184  ASSERT_TRUE(AddBlacklistedExtension(extension.get()));
185
186  service_->Init();
187
188  // Make sure that we created an error "ui" to warn about the blacklisted
189  // extension.
190  ASSERT_TRUE(g_error_ui);
191
192  // Accept, and verify that the extension ids now acknowledged.
193  g_error_ui->Accept();
194  EXPECT_TRUE(GetPrefs()->IsBlacklistedExtensionAcknowledged(extension->id()));
195  // Verify we cleaned up after ourselves.
196  EXPECT_FALSE(g_error_ui);
197}
198
199// Test that we don't warn for extensions which are blacklisted, but have
200// already been acknowledged.
201TEST_F(ExtensionErrorControllerUnitTest, DontWarnForAcknowledgedBlacklisted) {
202  scoped_refptr<const Extension> extension = BuildExtension();
203  ASSERT_TRUE(AddBlacklistedExtension(extension.get()));
204
205  GetPrefs()->AcknowledgeBlacklistedExtension(extension->id());
206
207  service_->Init();
208
209  // We should never have made an alert, because the extension should already
210  // be acknowledged.
211  ASSERT_FALSE(g_error_ui);
212}
213
214}  // namespace extensions
215