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 "chrome/browser/signin/signin_error_notifier_ash.h"
6
7#include "ash/test/ash_test_base.h"
8#include "base/memory/scoped_ptr.h"
9#include "chrome/browser/browser_process.h"
10#include "chrome/browser/notifications/notification.h"
11#include "chrome/browser/notifications/notification_ui_manager.h"
12#include "chrome/browser/signin/fake_signin_manager.h"
13#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
14#include "chrome/browser/signin/signin_error_notifier_factory_ash.h"
15#include "chrome/browser/signin/signin_manager_factory.h"
16#include "chrome/test/base/testing_browser_process.h"
17#include "chrome/test/base/testing_profile.h"
18#include "chrome/test/base/testing_profile_manager.h"
19#include "components/signin/core/browser/fake_auth_status_provider.h"
20#include "components/signin/core/browser/profile_oauth2_token_service.h"
21#include "components/signin/core/browser/signin_error_controller.h"
22#include "components/signin/core/browser/signin_manager.h"
23#include "content/public/test/test_browser_thread_bundle.h"
24#include "testing/gtest/include/gtest/gtest.h"
25#include "ui/message_center/notification.h"
26
27#if defined(OS_WIN)
28#include "chrome/browser/ui/ash/ash_util.h"
29#include "ui/aura/test/test_screen.h"
30#include "ui/gfx/screen.h"
31#include "ui/gfx/screen_type_delegate.h"
32#endif
33
34namespace ash {
35namespace test {
36
37namespace {
38
39static const char kTestAccountId[] = "testuser@test.com";
40static const char kTestUsername[] = "testuser@test.com";
41
42// Notification ID corresponding to kProfileSigninNotificationId +
43// kTestAccountId.
44static const std::string kNotificationId =
45    "chrome://settings/signin/testuser@test.com";
46}
47
48#if defined(OS_WIN)
49class ScreenTypeDelegateDesktop : public gfx::ScreenTypeDelegate {
50 public:
51  ScreenTypeDelegateDesktop() {}
52  virtual gfx::ScreenType GetScreenTypeForNativeView(
53      gfx::NativeView view) OVERRIDE {
54    return chrome::IsNativeViewInAsh(view) ?
55        gfx::SCREEN_TYPE_ALTERNATE :
56        gfx::SCREEN_TYPE_NATIVE;
57  }
58 private:
59  DISALLOW_COPY_AND_ASSIGN(ScreenTypeDelegateDesktop);
60};
61#endif
62
63class SigninErrorNotifierTest : public AshTestBase {
64 public:
65  virtual void SetUp() OVERRIDE {
66    // Create a signed-in profile.
67    TestingProfile::Builder builder;
68    builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
69                              FakeSigninManagerBase::Build);
70    profile_ = builder.Build();
71    profile_->set_profile_name(kTestAccountId);
72
73    profile_manager_.reset(
74        new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
75    ASSERT_TRUE(profile_manager_->SetUp());
76
77    TestingBrowserProcess::GetGlobal();
78    AshTestBase::SetUp();
79
80    // Set up screen for Windows.
81#if defined(OS_WIN)
82    test_screen_.reset(aura::TestScreen::Create(gfx::Size()));
83    gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen_.get());
84    gfx::Screen::SetScreenTypeDelegate(new ScreenTypeDelegateDesktop);
85#endif
86
87    error_controller_ =
88        ProfileOAuth2TokenServiceFactory::GetForProfile(profile_.get())->
89            signin_error_controller();
90    SigninErrorNotifierFactory::GetForProfile(profile_.get());
91    notification_ui_manager_ = g_browser_process->notification_ui_manager();
92  }
93
94  virtual void TearDown() OVERRIDE {
95#if defined(OS_WIN)
96    test_screen_.reset();
97#endif
98    profile_manager_.reset();
99
100    AshTestBase::TearDown();
101  }
102
103 protected:
104  void GetMessage(base::string16* message) {
105    const Notification* notification =
106        g_browser_process->notification_ui_manager()->FindById(kNotificationId);
107    ASSERT_FALSE(notification == NULL);
108    *message = notification->message();
109  }
110
111#if defined(OS_WIN)
112  scoped_ptr<gfx::Screen> test_screen_;
113#endif
114  scoped_ptr<TestingProfileManager> profile_manager_;
115  scoped_ptr<TestingProfile> profile_;
116  SigninErrorController* error_controller_;
117  NotificationUIManager* notification_ui_manager_;
118};
119
120TEST_F(SigninErrorNotifierTest, NoErrorAuthStatusProviders) {
121  ASSERT_FALSE(notification_ui_manager_->FindById(kNotificationId));
122  {
123    // Add a provider (removes itself on exiting this scope).
124    FakeAuthStatusProvider provider(error_controller_);
125    ASSERT_FALSE(notification_ui_manager_->FindById(kNotificationId));
126  }
127  ASSERT_FALSE(notification_ui_manager_->FindById(kNotificationId));
128}
129
130#if !defined(OS_WIN)
131// Disabled on Win due to flake. http://crbug.com/372236
132TEST_F(SigninErrorNotifierTest, ErrorAuthStatusProvider) {
133  {
134    FakeAuthStatusProvider provider(error_controller_);
135    ASSERT_FALSE(notification_ui_manager_->FindById(kNotificationId));
136    {
137      FakeAuthStatusProvider error_provider(error_controller_);
138      error_provider.SetAuthError(
139          kTestAccountId,
140          kTestUsername,
141          GoogleServiceAuthError(
142              GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
143      ASSERT_TRUE(notification_ui_manager_->FindById(kNotificationId));
144    }
145    // error_provider is removed now that we've left that scope.
146    ASSERT_FALSE(notification_ui_manager_->FindById(kNotificationId));
147  }
148  // All providers should be removed now.
149  ASSERT_FALSE(notification_ui_manager_->FindById(kNotificationId));
150}
151#endif
152
153#if defined(OS_WIN)
154// Test started crashing on Win 7. http://crbug.com/372277
155#define MAYBE_AuthStatusProviderErrorTransition \
156  DISABLED_AuthStatusProviderErrorTransition
157#else
158#define MAYBE_AuthStatusProviderErrorTransition \
159  AuthStatusProviderErrorTransition
160#endif
161TEST_F(SigninErrorNotifierTest, MAYBE_AuthStatusProviderErrorTransition) {
162  {
163    FakeAuthStatusProvider provider0(error_controller_);
164    FakeAuthStatusProvider provider1(error_controller_);
165    provider0.SetAuthError(
166        kTestAccountId,
167        kTestUsername,
168        GoogleServiceAuthError(
169            GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
170    ASSERT_TRUE(notification_ui_manager_->FindById(kNotificationId));
171
172    base::string16 message;
173    GetMessage(&message);
174    ASSERT_FALSE(message.empty());
175
176    // Now set another auth error and clear the original.
177    provider1.SetAuthError(
178        kTestAccountId,
179        kTestUsername,
180        GoogleServiceAuthError(
181            GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE));
182    provider0.SetAuthError(
183        kTestAccountId,
184        kTestUsername,
185        GoogleServiceAuthError::AuthErrorNone());
186
187    ASSERT_TRUE(notification_ui_manager_->FindById(kNotificationId));
188
189    base::string16 new_message;
190    GetMessage(&new_message);
191    ASSERT_FALSE(new_message.empty());
192
193    ASSERT_NE(new_message, message);
194
195    provider1.SetAuthError(
196        kTestAccountId, kTestUsername, GoogleServiceAuthError::AuthErrorNone());
197    ASSERT_FALSE(notification_ui_manager_->FindById(kNotificationId));
198  }
199}
200
201#if !defined(OS_WIN)
202// Disabled on Win due to flake. http://crbug.com/372236
203// Verify that SigninErrorNotifier ignores certain errors.
204TEST_F(SigninErrorNotifierTest, AuthStatusEnumerateAllErrors) {
205  typedef struct {
206    GoogleServiceAuthError::State error_state;
207    bool is_error;
208  } ErrorTableEntry;
209
210  ErrorTableEntry table[] = {
211    { GoogleServiceAuthError::NONE, false },
212    { GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, true },
213    { GoogleServiceAuthError::USER_NOT_SIGNED_UP, true },
214    { GoogleServiceAuthError::CONNECTION_FAILED, false },
215    { GoogleServiceAuthError::CAPTCHA_REQUIRED, true },
216    { GoogleServiceAuthError::ACCOUNT_DELETED, true },
217    { GoogleServiceAuthError::ACCOUNT_DISABLED, true },
218    { GoogleServiceAuthError::SERVICE_UNAVAILABLE, true },
219    { GoogleServiceAuthError::TWO_FACTOR, true },
220    { GoogleServiceAuthError::REQUEST_CANCELED, true },
221    { GoogleServiceAuthError::HOSTED_NOT_ALLOWED, true },
222    { GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE, true },
223    { GoogleServiceAuthError::SERVICE_ERROR, true },
224  };
225  COMPILE_ASSERT(ARRAYSIZE_UNSAFE(table) == GoogleServiceAuthError::NUM_STATES,
226      kTable_size_does_not_match_number_of_auth_error_types);
227
228  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(table); ++i) {
229    FakeAuthStatusProvider provider(error_controller_);
230    provider.SetAuthError(kTestAccountId,
231                          kTestUsername,
232                          GoogleServiceAuthError(table[i].error_state));
233    const Notification* notification = notification_ui_manager_->
234        FindById(kNotificationId);
235    ASSERT_EQ(table[i].is_error, notification != NULL);
236    if (table[i].is_error) {
237      EXPECT_FALSE(notification->title().empty());
238      EXPECT_FALSE(notification->message().empty());
239      EXPECT_EQ((size_t)1, notification->buttons().size());
240    }
241  }
242}
243#endif
244
245}  // namespace test
246}  // namespace ash
247