1// Copyright 2013 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/command_line.h"
6#include "base/file_util.h"
7#include "base/memory/scoped_ptr.h"
8#include "base/message_loop/message_loop.h"
9#include "base/path_service.h"
10#include "base/prefs/pref_service.h"
11#include "base/strings/utf_string_conversions.h"
12#include "chrome/browser/browser_process.h"
13#include "chrome/browser/extensions/extension_browsertest.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/profiles/profile_manager.h"
16#include "chrome/browser/signin/signin_manager.h"
17#include "chrome/browser/signin/signin_manager_factory.h"
18#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
19#include "chrome/browser/ui/app_list/app_list_service.h"
20#include "chrome/browser/ui/app_list/app_list_syncable_service.h"
21#include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
22#include "chrome/browser/ui/browser.h"
23#include "chrome/browser/ui/browser_finder.h"
24#include "chrome/browser/ui/host_desktop.h"
25#include "chrome/common/chrome_paths.h"
26#include "chrome/common/chrome_switches.h"
27#include "chrome/common/pref_names.h"
28#include "chrome/test/base/in_process_browser_test.h"
29#include "chrome/test/base/ui_test_utils.h"
30#include "ui/app_list/app_list_model.h"
31#include "ui/app_list/search_box_model.h"
32#include "ui/app_list/search_result.h"
33#include "ui/app_list/search_result_observer.h"
34#include "ui/base/models/list_model_observer.h"
35
36namespace {
37
38app_list::AppListModel* GetAppListModel(AppListService* service) {
39  return app_list::AppListSyncableServiceFactory::GetForProfile(
40      service->GetCurrentAppListProfile())->model();
41}
42
43AppListService* GetAppListService() {
44  // TODO(tapted): Consider testing ash explicitly on the win-ash trybot.
45  return AppListService::Get(chrome::GetActiveDesktop());
46}
47
48void SigninProfile(Profile* profile) {
49  SigninManagerFactory::GetForProfile(profile)->
50      SetAuthenticatedUsername("user@example.com");
51}
52
53}  // namespace
54
55// Browser Test for AppListController that runs on all platforms supporting
56// app_list.
57class AppListControllerBrowserTest : public InProcessBrowserTest {
58 public:
59  AppListControllerBrowserTest()
60    : profile2_(NULL) {}
61
62  void InitSecondProfile() {
63    ProfileManager* profile_manager = g_browser_process->profile_manager();
64    base::FilePath temp_profile_dir =
65        profile_manager->user_data_dir().AppendASCII("Profile 1");
66    profile_manager->CreateProfileAsync(
67        temp_profile_dir,
68        base::Bind(&AppListControllerBrowserTest::OnProfileCreated,
69                   this),
70        base::string16(), base::string16(), std::string());
71    content::RunMessageLoop();  // Will stop in OnProfileCreated().
72  }
73
74  void OnProfileCreated(Profile* profile, Profile::CreateStatus status) {
75    if (status == Profile::CREATE_STATUS_INITIALIZED) {
76      profile2_ = profile;
77      base::MessageLoop::current()->Quit();
78    }
79  }
80
81 protected:
82  Profile* profile2_;
83
84 private:
85  DISALLOW_COPY_AND_ASSIGN(AppListControllerBrowserTest);
86};
87
88// TODO(mgiuca): Enable on Linux when supported.
89#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
90#define MAYBE_CreateNewWindow DISABLED_CreateNewWindow
91#else
92#define MAYBE_CreateNewWindow CreateNewWindow
93#endif
94
95// Test the CreateNewWindow function of the controller delegate.
96IN_PROC_BROWSER_TEST_F(AppListControllerBrowserTest, MAYBE_CreateNewWindow) {
97  const chrome::HostDesktopType desktop = chrome::GetActiveDesktop();
98  AppListService* service = GetAppListService();
99  AppListControllerDelegate* controller(service->GetControllerDelegate());
100  ASSERT_TRUE(controller);
101
102  EXPECT_EQ(1U, chrome::GetBrowserCount(browser()->profile(), desktop));
103  EXPECT_EQ(0U, chrome::GetBrowserCount(
104      browser()->profile()->GetOffTheRecordProfile(), desktop));
105
106  controller->CreateNewWindow(browser()->profile(), false);
107  EXPECT_EQ(2U, chrome::GetBrowserCount(browser()->profile(), desktop));
108
109  controller->CreateNewWindow(browser()->profile(), true);
110  EXPECT_EQ(1U, chrome::GetBrowserCount(
111      browser()->profile()->GetOffTheRecordProfile(), desktop));
112}
113
114// TODO(mgiuca): Enable on Linux/ChromeOS when supported.
115#if defined(OS_LINUX)
116#define MAYBE_ShowAndDismiss DISABLED_ShowAndDismiss
117#define MAYBE_SwitchAppListProfiles DISABLED_SwitchAppListProfiles
118#define MAYBE_SwitchAppListProfilesDuringSearch \
119  DISABLED_SwitchAppListProfilesDuringSearch
120#else
121#define MAYBE_ShowAndDismiss ShowAndDismiss
122#define MAYBE_SwitchAppListProfiles SwitchAppListProfiles
123#define MAYBE_SwitchAppListProfilesDuringSearch \
124  SwitchAppListProfilesDuringSearch
125#endif
126
127// Show the app list, then dismiss it.
128IN_PROC_BROWSER_TEST_F(AppListControllerBrowserTest, MAYBE_ShowAndDismiss) {
129  AppListService* service = GetAppListService();
130  ASSERT_FALSE(service->IsAppListVisible());
131  service->ShowForProfile(browser()->profile());
132  ASSERT_TRUE(service->IsAppListVisible());
133  service->DismissAppList();
134  ASSERT_FALSE(service->IsAppListVisible());
135}
136
137IN_PROC_BROWSER_TEST_F(AppListControllerBrowserTest,
138                       MAYBE_SwitchAppListProfiles) {
139  InitSecondProfile();
140  SigninProfile(browser()->profile());
141  SigninProfile(profile2_);
142
143  AppListService* service = GetAppListService();
144  ASSERT_TRUE(service);
145
146  AppListControllerDelegate* controller(service->GetControllerDelegate());
147  ASSERT_TRUE(controller);
148
149  // Open the app list with the browser's profile.
150  ASSERT_FALSE(service->IsAppListVisible());
151  controller->ShowForProfileByPath(browser()->profile()->GetPath());
152  app_list::AppListModel* model = GetAppListModel(service);
153  ASSERT_TRUE(model);
154
155  base::RunLoop().RunUntilIdle();
156
157  ASSERT_TRUE(service->IsAppListVisible());
158  ASSERT_EQ(browser()->profile(), service->GetCurrentAppListProfile());
159
160  // Open the app list with the second profile.
161  controller->ShowForProfileByPath(profile2_->GetPath());
162  model = GetAppListModel(service);
163  ASSERT_TRUE(model);
164  base::RunLoop().RunUntilIdle();
165
166  ASSERT_TRUE(service->IsAppListVisible());
167  ASSERT_EQ(profile2_, service->GetCurrentAppListProfile());
168
169  controller->DismissView();
170}
171
172IN_PROC_BROWSER_TEST_F(AppListControllerBrowserTest,
173                       MAYBE_SwitchAppListProfilesDuringSearch) {
174  InitSecondProfile();
175  SigninProfile(browser()->profile());
176  SigninProfile(profile2_);
177
178  AppListService* service = GetAppListService();
179  ASSERT_TRUE(service);
180
181  AppListControllerDelegate* controller(service->GetControllerDelegate());
182  ASSERT_TRUE(controller);
183
184  // Set a search with original profile.
185  controller->ShowForProfileByPath(browser()->profile()->GetPath());
186  app_list::AppListModel* model = GetAppListModel(service);
187  ASSERT_TRUE(model);
188
189  model->search_box()->SetText(ASCIIToUTF16("minimal"));
190  base::RunLoop().RunUntilIdle();
191
192  // Switch to the second profile.
193  controller->ShowForProfileByPath(profile2_->GetPath());
194  model = GetAppListModel(service);
195  ASSERT_TRUE(model);
196  base::RunLoop().RunUntilIdle();
197
198  // Ensure the search box is empty.
199  ASSERT_TRUE(model->search_box()->text().empty());
200  ASSERT_EQ(profile2_, service->GetCurrentAppListProfile());
201
202  controller->DismissView();
203  ASSERT_FALSE(service->IsAppListVisible());
204}
205
206class ShowAppListBrowserTest : public InProcessBrowserTest {
207 public:
208  ShowAppListBrowserTest() {}
209
210  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
211    command_line->AppendSwitch(switches::kShowAppList);
212  }
213
214 private:
215  DISALLOW_COPY_AND_ASSIGN(ShowAppListBrowserTest);
216};
217
218// See http://crbug.com/315677
219#if defined(OS_WIN)
220#define MAYBE_ShowAppListFlag DISABLED_ShowAppListFlag
221#else
222#define MAYBE_ShowAppListFlag ShowAppListFlag
223#endif
224
225IN_PROC_BROWSER_TEST_F(ShowAppListBrowserTest, MAYBE_ShowAppListFlag) {
226  AppListService* service = GetAppListService();
227  // The app list should already be shown because we passed
228  // switches::kShowAppList.
229  ASSERT_TRUE(service->IsAppListVisible());
230
231  // Create a browser to prevent shutdown when we dismiss the app list.  We
232  // need to do this because switches::kShowAppList suppresses the creation of
233  // any browsers.
234  CreateBrowser(service->GetCurrentAppListProfile());
235  service->DismissAppList();
236}
237
238// Browser Test for AppListController that observes search result changes.
239class AppListControllerSearchResultsBrowserTest
240    : public ExtensionBrowserTest,
241      public app_list::SearchResultObserver,
242      public ui::ListModelObserver {
243 public:
244  AppListControllerSearchResultsBrowserTest()
245      : observed_result_(NULL),
246        item_uninstall_count_(0),
247        observed_results_list_(NULL) {}
248
249  void WatchResultsLookingForItem(
250      app_list::AppListModel::SearchResults* search_results,
251      const std::string& extension_name) {
252    EXPECT_FALSE(observed_results_list_);
253    observed_results_list_ = search_results;
254    observed_results_list_->AddObserver(this);
255    item_to_observe_ = ASCIIToUTF16(extension_name);
256  }
257
258  void StopWatchingResults() {
259    EXPECT_TRUE(observed_results_list_);
260    observed_results_list_->RemoveObserver(this);
261  }
262
263 protected:
264  void AttemptToLocateItem() {
265    if (observed_result_) {
266      observed_result_->RemoveObserver(this);
267      observed_result_ = NULL;
268    }
269
270    for (size_t i = 0; i < observed_results_list_->item_count(); ++i) {
271      if (observed_results_list_->GetItemAt(i)->title() != item_to_observe_)
272        continue;
273
274      // Ensure there is at most one.
275      EXPECT_FALSE(observed_result_);
276      observed_result_ = observed_results_list_->GetItemAt(i);
277    }
278
279    if (observed_result_)
280      observed_result_->AddObserver(this);
281  }
282
283  // Overridden from SearchResultObserver:
284  virtual void OnIconChanged() OVERRIDE {}
285  virtual void OnActionsChanged() OVERRIDE {}
286  virtual void OnIsInstallingChanged() OVERRIDE {}
287  virtual void OnPercentDownloadedChanged() OVERRIDE {}
288  virtual void OnItemInstalled() OVERRIDE {}
289  virtual void OnItemUninstalled() OVERRIDE {
290    ++item_uninstall_count_;
291    EXPECT_TRUE(observed_result_);
292  }
293
294  // Overridden from ui::ListModelObserver:
295  virtual void ListItemsAdded(size_t start, size_t count) OVERRIDE {
296    AttemptToLocateItem();
297  }
298  virtual void ListItemsRemoved(size_t start, size_t count) OVERRIDE {
299    AttemptToLocateItem();
300  }
301  virtual void ListItemMoved(size_t index, size_t target_index) OVERRIDE {}
302  virtual void ListItemsChanged(size_t start, size_t count) OVERRIDE {}
303
304  app_list::SearchResult* observed_result_;
305  int item_uninstall_count_;
306
307 private:
308  base::string16 item_to_observe_;
309  app_list::AppListModel::SearchResults* observed_results_list_;
310
311  DISALLOW_COPY_AND_ASSIGN(AppListControllerSearchResultsBrowserTest);
312};
313
314// TODO(mgiuca): Enable on Linux when supported.
315#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
316#define MAYBE_UninstallSearchResult DISABLED_UninstallSearchResult
317#else
318#define MAYBE_UninstallSearchResult UninstallSearchResult
319#endif
320
321// Test showing search results, and uninstalling one of them while displayed.
322IN_PROC_BROWSER_TEST_F(AppListControllerSearchResultsBrowserTest,
323                       UninstallSearchResult) {
324  SigninProfile(browser()->profile());
325  base::FilePath test_extension_path;
326  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_extension_path));
327  test_extension_path = test_extension_path.AppendASCII("extensions")
328      .AppendASCII("platform_apps")
329      .AppendASCII("minimal");
330  const extensions::Extension* extension =
331      InstallExtension(test_extension_path,
332                       1 /* expected_change: new install */);
333  ASSERT_TRUE(extension);
334
335  AppListService* service = GetAppListService();
336  ASSERT_TRUE(service);
337  service->ShowForProfile(browser()->profile());
338
339  app_list::AppListModel* model = GetAppListModel(service);
340  ASSERT_TRUE(model);
341  WatchResultsLookingForItem(model->results(), extension->name());
342
343  // Ensure a search finds the extension.
344  EXPECT_FALSE(observed_result_);
345  model->search_box()->SetText(ASCIIToUTF16("minimal"));
346  EXPECT_TRUE(observed_result_);
347
348  // Ensure the UI is updated. This is via PostTask in views.
349  base::RunLoop().RunUntilIdle();
350
351  // Now uninstall and ensure this browser test observes it.
352  EXPECT_EQ(0, item_uninstall_count_);
353  UninstallExtension(extension->id());
354  EXPECT_EQ(1, item_uninstall_count_);
355
356  // Results should not be immediately refreshed. When they are, the item should
357  // be removed from the model.
358  EXPECT_TRUE(observed_result_);
359  base::RunLoop().RunUntilIdle();
360  EXPECT_FALSE(observed_result_);
361  StopWatchingResults();
362  service->DismissAppList();
363}
364