1 // Copyright (c) 2012 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.
5 #include "chrome/browser/ui/app_list/extension_app_model_builder.h"
9 #include "base/files/file_path.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/run_loop.h"
13 #include "base/values.h"
14 #include "chrome/browser/extensions/extension_function_test_utils.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/install_tracker.h"
17 #include "chrome/browser/extensions/install_tracker_factory.h"
18 #include "chrome/browser/ui/app_list/app_list_controller_delegate_impl.h"
19 #include "chrome/browser/ui/app_list/app_list_test_util.h"
20 #include "chrome/common/chrome_constants.h"
21 #include "chrome/common/extensions/extension_constants.h"
22 #include "chrome/common/pref_names.h"
23 #include "chrome/test/base/testing_profile.h"
24 #include "extensions/browser/app_sorting.h"
25 #include "extensions/browser/extension_prefs.h"
26 #include "extensions/browser/uninstall_reason.h"
27 #include "extensions/common/extension_set.h"
28 #include "extensions/common/manifest.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "ui/app_list/app_list_item.h"
34 // Get a string of all apps in |model| joined with ','.
35 std::string GetModelContent(app_list::AppListModel* model) {
37 for (size_t i = 0; i < model->top_level_item_list()->item_count(); ++i) {
40 content += model->top_level_item_list()->item_at(i)->name();
45 scoped_refptr<extensions::Extension> MakeApp(const std::string& name,
46 const std::string& version,
47 const std::string& url,
48 const std::string& id) {
50 base::DictionaryValue value;
51 value.SetString("name", name);
52 value.SetString("version", version);
53 value.SetString("app.launch.web_url", url);
54 scoped_refptr<extensions::Extension> app =
55 extensions::Extension::Create(
57 extensions::Manifest::INTERNAL,
59 extensions::Extension::WAS_INSTALLED_BY_DEFAULT,
66 class TestAppListControllerDelegate : public AppListControllerDelegate {
68 virtual ~TestAppListControllerDelegate() {}
69 virtual void DismissView() OVERRIDE {}
70 virtual gfx::NativeWindow GetAppListWindow() OVERRIDE { return NULL; }
71 virtual gfx::ImageSkia GetWindowIcon() OVERRIDE { return gfx::ImageSkia(); }
72 virtual bool IsAppPinned(const std::string& extension_id) OVERRIDE {
75 virtual void PinApp(const std::string& extension_id) OVERRIDE {}
76 virtual void UnpinApp(const std::string& extension_id) OVERRIDE {}
77 virtual Pinnable GetPinnable() OVERRIDE { return NO_PIN; }
78 virtual bool CanDoCreateShortcutsFlow() OVERRIDE { return false; }
79 virtual void DoCreateShortcutsFlow(Profile* profile,
80 const std::string& extension_id) OVERRIDE {
82 virtual bool CanDoShowAppInfoFlow() OVERRIDE { return false; }
83 virtual void DoShowAppInfoFlow(Profile* profile,
84 const std::string& extension_id) OVERRIDE {
86 virtual void CreateNewWindow(Profile* profile, bool incognito) OVERRIDE {}
87 virtual void ActivateApp(Profile* profile,
88 const extensions::Extension* extension,
90 int event_flags) OVERRIDE {}
91 virtual void LaunchApp(Profile* profile,
92 const extensions::Extension* extension,
94 int event_flags) OVERRIDE {}
95 virtual void ShowForProfileByPath(
96 const base::FilePath& profile_path) OVERRIDE {}
97 virtual bool ShouldShowUserIcon() OVERRIDE { return false; }
100 const char kDefaultApps[] = "Packaged App 1,Packaged App 2,Hosted App";
101 const size_t kDefaultAppCount = 3u;
105 class ExtensionAppModelBuilderTest : public AppListTestBase {
107 ExtensionAppModelBuilderTest() {}
108 virtual ~ExtensionAppModelBuilderTest() {}
110 virtual void SetUp() OVERRIDE {
111 AppListTestBase::SetUp();
116 virtual void TearDown() OVERRIDE {
121 // Creates a new builder, destroying any existing one.
122 void CreateBuilder() {
123 ResetBuilder(); // Destroy any existing builder in the correct order.
125 model_.reset(new app_list::AppListModel);
126 controller_.reset(new TestAppListControllerDelegate);
127 builder_.reset(new ExtensionAppModelBuilder(controller_.get()));
128 builder_->InitializeWithProfile(profile_.get(), model_.get());
131 void ResetBuilder() {
137 scoped_ptr<app_list::AppListModel> model_;
138 scoped_ptr<TestAppListControllerDelegate> controller_;
139 scoped_ptr<ExtensionAppModelBuilder> builder_;
141 base::ScopedTempDir second_profile_temp_dir_;
144 DISALLOW_COPY_AND_ASSIGN(ExtensionAppModelBuilderTest);
147 TEST_F(ExtensionAppModelBuilderTest, Build) {
148 // The apps list would have 3 extension apps in the profile.
149 EXPECT_EQ(kDefaultAppCount, model_->top_level_item_list()->item_count());
150 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
153 TEST_F(ExtensionAppModelBuilderTest, HideWebStore) {
154 // Install a "web store" app.
155 scoped_refptr<extensions::Extension> store =
159 std::string(extension_misc::kWebStoreAppId));
160 service_->AddExtension(store.get());
162 // Install an "enterprise web store" app.
163 scoped_refptr<extensions::Extension> enterprise_store =
164 MakeApp("enterprise_webstore",
167 std::string(extension_misc::kEnterpriseWebStoreAppId));
168 service_->AddExtension(enterprise_store.get());
170 // Web stores should be present in the AppListModel.
171 app_list::AppListModel model1;
172 ExtensionAppModelBuilder builder1(controller_.get());
173 builder1.InitializeWithProfile(profile_.get(), &model1);
174 EXPECT_TRUE(model1.FindItem(store->id()));
175 EXPECT_TRUE(model1.FindItem(enterprise_store->id()));
177 // Activate the HideWebStoreIcon policy.
178 profile_->GetPrefs()->SetBoolean(prefs::kHideWebStoreIcon, true);
180 // Now the web stores should not be present anymore.
181 EXPECT_FALSE(model1.FindItem(store->id()));
182 EXPECT_FALSE(model1.FindItem(enterprise_store->id()));
184 // Build a new AppListModel; web stores should NOT be present.
185 app_list::AppListModel model2;
186 ExtensionAppModelBuilder builder2(controller_.get());
187 builder2.InitializeWithProfile(profile_.get(), &model2);
188 EXPECT_FALSE(model2.FindItem(store->id()));
189 EXPECT_FALSE(model2.FindItem(enterprise_store->id()));
191 // Deactivate the HideWebStoreIcon policy again.
192 profile_->GetPrefs()->SetBoolean(prefs::kHideWebStoreIcon, false);
194 // Now the web stores should have appeared.
195 EXPECT_TRUE(model2.FindItem(store->id()));
196 EXPECT_TRUE(model2.FindItem(enterprise_store->id()));
199 TEST_F(ExtensionAppModelBuilderTest, DisableAndEnable) {
200 service_->DisableExtension(kHostedAppId,
201 extensions::Extension::DISABLE_NONE);
202 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
204 service_->EnableExtension(kHostedAppId);
205 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
208 TEST_F(ExtensionAppModelBuilderTest, Uninstall) {
209 service_->UninstallExtension(kPackagedApp2Id,
210 extensions::UNINSTALL_REASON_FOR_TESTING,
211 base::Bind(&base::DoNothing),
213 EXPECT_EQ(std::string("Packaged App 1,Hosted App"),
214 GetModelContent(model_.get()));
216 base::RunLoop().RunUntilIdle();
219 TEST_F(ExtensionAppModelBuilderTest, UninstallTerminatedApp) {
220 const extensions::Extension* app =
221 service_->GetInstalledExtension(kPackagedApp2Id);
222 ASSERT_TRUE(app != NULL);
224 // Simulate an app termination.
225 service_->TrackTerminatedExtensionForTest(app);
227 service_->UninstallExtension(kPackagedApp2Id,
228 extensions::UNINSTALL_REASON_FOR_TESTING,
229 base::Bind(&base::DoNothing),
231 EXPECT_EQ(std::string("Packaged App 1,Hosted App"),
232 GetModelContent(model_.get()));
234 base::RunLoop().RunUntilIdle();
237 TEST_F(ExtensionAppModelBuilderTest, Reinstall) {
238 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
240 // Install kPackagedApp1Id again should not create a new entry.
241 extensions::InstallTracker* tracker =
242 extensions::InstallTrackerFactory::GetForBrowserContext(profile_.get());
243 extensions::InstallObserver::ExtensionInstallParams params(
244 kPackagedApp1Id, "", gfx::ImageSkia(), true, true);
245 tracker->OnBeginExtensionInstall(params);
247 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
250 TEST_F(ExtensionAppModelBuilderTest, OrdinalPrefsChange) {
251 extensions::AppSorting* sorting =
252 extensions::ExtensionPrefs::Get(profile_.get())->app_sorting();
254 syncer::StringOrdinal package_app_page =
255 sorting->GetPageOrdinal(kPackagedApp1Id);
256 sorting->SetPageOrdinal(kHostedAppId, package_app_page.CreateBefore());
257 // Old behavior: This would be "Hosted App,Packaged App 1,Packaged App 2"
258 // New behavior: Sorting order doesn't change.
259 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
261 syncer::StringOrdinal app1_ordinal =
262 sorting->GetAppLaunchOrdinal(kPackagedApp1Id);
263 syncer::StringOrdinal app2_ordinal =
264 sorting->GetAppLaunchOrdinal(kPackagedApp2Id);
265 sorting->SetPageOrdinal(kHostedAppId, package_app_page);
266 sorting->SetAppLaunchOrdinal(kHostedAppId,
267 app1_ordinal.CreateBetween(app2_ordinal));
268 // Old behavior: This would be "Packaged App 1,Hosted App,Packaged App 2"
269 // New behavior: Sorting order doesn't change.
270 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
273 TEST_F(ExtensionAppModelBuilderTest, OnExtensionMoved) {
274 extensions::AppSorting* sorting =
275 extensions::ExtensionPrefs::Get(profile_.get())->app_sorting();
276 sorting->SetPageOrdinal(kHostedAppId,
277 sorting->GetPageOrdinal(kPackagedApp1Id));
279 sorting->OnExtensionMoved(kHostedAppId, kPackagedApp1Id, kPackagedApp2Id);
280 // Old behavior: This would be "Packaged App 1,Hosted App,Packaged App 2"
281 // New behavior: Sorting order doesn't change.
282 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
284 sorting->OnExtensionMoved(kHostedAppId, kPackagedApp2Id, std::string());
285 // Old behavior: This would be restored to the default order.
286 // New behavior: Sorting order still doesn't change.
287 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
289 sorting->OnExtensionMoved(kHostedAppId, std::string(), kPackagedApp1Id);
290 // Old behavior: This would be "Hosted App,Packaged App 1,Packaged App 2"
291 // New behavior: Sorting order doesn't change.
292 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
295 TEST_F(ExtensionAppModelBuilderTest, InvalidOrdinal) {
296 // Creates a no-ordinal case.
297 extensions::AppSorting* sorting =
298 extensions::ExtensionPrefs::Get(profile_.get())->app_sorting();
299 sorting->ClearOrdinals(kPackagedApp1Id);
301 // Creates a corrupted ordinal case.
302 extensions::ExtensionScopedPrefs* scoped_prefs =
303 extensions::ExtensionPrefs::Get(profile_.get());
304 scoped_prefs->UpdateExtensionPref(
307 new base::StringValue("a corrupted ordinal"));
309 // This should not assert or crash.
313 TEST_F(ExtensionAppModelBuilderTest, OrdinalConfilicts) {
314 // Creates conflict ordinals for app1 and app2.
315 syncer::StringOrdinal conflict_ordinal =
316 syncer::StringOrdinal::CreateInitialOrdinal();
318 extensions::AppSorting* sorting =
319 extensions::ExtensionPrefs::Get(profile_.get())->app_sorting();
320 sorting->SetPageOrdinal(kHostedAppId, conflict_ordinal);
321 sorting->SetAppLaunchOrdinal(kHostedAppId, conflict_ordinal);
323 sorting->SetPageOrdinal(kPackagedApp1Id, conflict_ordinal);
324 sorting->SetAppLaunchOrdinal(kPackagedApp1Id, conflict_ordinal);
326 sorting->SetPageOrdinal(kPackagedApp2Id, conflict_ordinal);
327 sorting->SetAppLaunchOrdinal(kPackagedApp2Id, conflict_ordinal);
329 // This should not assert or crash.
332 // By default, conflicted items are sorted by their app ids (= order added).
333 EXPECT_EQ(std::string("Hosted App,Packaged App 1,Packaged App 2"),
334 GetModelContent(model_.get()));
337 // This test adds a bookmark app to the app list.
338 TEST_F(ExtensionAppModelBuilderTest, BookmarkApp) {
339 const std::string kAppName = "Bookmark App";
340 const std::string kAppVersion = "2014.1.24.19748";
341 const std::string kAppUrl = "http://google.com";
342 const std::string kAppId = "podhdnefolignjhecmjkbimfgioanahm";
344 base::DictionaryValue value;
345 value.SetString("name", kAppName);
346 value.SetString("version", kAppVersion);
347 value.SetString("app.launch.web_url", kAppUrl);
348 scoped_refptr<extensions::Extension> bookmark_app =
349 extensions::Extension::Create(
351 extensions::Manifest::INTERNAL,
353 extensions::Extension::WAS_INSTALLED_BY_DEFAULT |
354 extensions::Extension::FROM_BOOKMARK,
357 EXPECT_TRUE(err.empty());
359 service_->AddExtension(bookmark_app.get());
360 EXPECT_EQ(kDefaultAppCount + 1, model_->top_level_item_list()->item_count());
361 EXPECT_NE(std::string::npos, GetModelContent(model_.get()).find(kAppName));