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_unittest.h"
16 #include "chrome/browser/extensions/extension_sorting.h"
17 #include "chrome/browser/extensions/install_tracker.h"
18 #include "chrome/browser/extensions/install_tracker_factory.h"
19 #include "chrome/common/extensions/extension_constants.h"
20 #include "chrome/common/pref_names.h"
21 #include "chrome/test/base/testing_profile.h"
22 #include "extensions/common/manifest.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "ui/app_list/app_list_item_model.h"
28 const char kHostedAppId[] = "dceacbkfkmllgmjmbhgkpjegnodmildf";
29 const char kPackagedApp1Id[] = "emfkafnhnpcmabnnkckkchdilgeoekbo";
30 const char kPackagedApp2Id[] = "jlklkagmeajbjiobondfhiekepofmljl";
32 // Get a string of all apps in |model| joined with ','.
33 std::string GetModelContent(app_list::AppListModel* model) {
35 for (size_t i = 0; i < model->item_list()->item_count(); ++i) {
38 content += model->item_list()->item_at(i)->title();
43 scoped_refptr<extensions::Extension> MakeApp(const std::string& name,
44 const std::string& version,
45 const std::string& url,
46 const std::string& id) {
48 DictionaryValue value;
49 value.SetString("name", name);
50 value.SetString("version", version);
51 value.SetString("app.launch.web_url", url);
52 scoped_refptr<extensions::Extension> app =
53 extensions::Extension::Create(
55 extensions::Manifest::INTERNAL,
57 extensions::Extension::WAS_INSTALLED_BY_DEFAULT,
64 const char kDefaultApps[] = "Packaged App 1,Packaged App 2,Hosted App";
65 const size_t kDefaultAppCount = 3u;
69 class ExtensionAppModelBuilderTest : public ExtensionServiceTestBase {
71 ExtensionAppModelBuilderTest() {}
72 virtual ~ExtensionAppModelBuilderTest() {}
74 virtual void SetUp() OVERRIDE {
75 ExtensionServiceTestBase::SetUp();
77 // Load "app_list" extensions test profile.
78 // The test profile has 4 extensions:
79 // 1 dummy extension, 2 packaged extension apps and 1 hosted extension app.
80 base::FilePath source_install_dir = data_dir_
81 .AppendASCII("app_list")
82 .AppendASCII("Extensions");
83 base::FilePath pref_path = source_install_dir
85 .AppendASCII("Preferences");
86 InitializeInstalledExtensionService(pref_path, source_install_dir);
89 // There should be 4 extensions in the test profile.
90 const ExtensionSet* extensions = service_->extensions();
91 ASSERT_EQ(static_cast<size_t>(4), extensions->size());
93 model_.reset(new app_list::AppListModel);
96 virtual void TearDown() OVERRIDE {
101 scoped_ptr<app_list::AppListModel> model_;
104 DISALLOW_COPY_AND_ASSIGN(ExtensionAppModelBuilderTest);
107 TEST_F(ExtensionAppModelBuilderTest, Build) {
108 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL);
110 // The apps list would have 3 extension apps in the profile.
111 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
114 TEST_F(ExtensionAppModelBuilderTest, HideWebStore) {
115 // Install a "web store" app.
116 scoped_refptr<extensions::Extension> store =
120 std::string(extension_misc::kWebStoreAppId));
121 service_->AddExtension(store.get());
123 // Install an "enterprise web store" app.
124 scoped_refptr<extensions::Extension> enterprise_store =
125 MakeApp("enterprise_webstore",
128 std::string(extension_misc::kEnterpriseWebStoreAppId));
129 service_->AddExtension(enterprise_store.get());
131 // Web stores should be present in the AppListModel.
132 app_list::AppListModel model1;
133 ExtensionAppModelBuilder builder1(profile_.get(), &model1, NULL);
134 std::string content = GetModelContent(&model1);
135 EXPECT_NE(std::string::npos, content.find("webstore"));
136 EXPECT_NE(std::string::npos, content.find("enterprise_webstore"));
138 // Activate the HideWebStoreIcon policy.
139 profile_->GetPrefs()->SetBoolean(prefs::kHideWebStoreIcon, true);
141 // Web stores should NOT be in the AppListModel.
142 app_list::AppListModel model2;
143 ExtensionAppModelBuilder builder2(profile_.get(), &model2, NULL);
144 content = GetModelContent(&model2);
145 EXPECT_EQ(std::string::npos, content.find("webstore"));
146 EXPECT_EQ(std::string::npos, content.find("enterprise_webstore"));
149 TEST_F(ExtensionAppModelBuilderTest, DisableAndEnable) {
150 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL);
152 service_->DisableExtension(kHostedAppId,
153 extensions::Extension::DISABLE_NONE);
154 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
156 service_->EnableExtension(kHostedAppId);
157 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
160 TEST_F(ExtensionAppModelBuilderTest, Uninstall) {
161 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL);
163 service_->UninstallExtension(kPackagedApp2Id, false, NULL);
164 EXPECT_EQ(std::string("Packaged App 1,Hosted App"),
165 GetModelContent(model_.get()));
167 base::RunLoop().RunUntilIdle();
170 TEST_F(ExtensionAppModelBuilderTest, UninstallTerminatedApp) {
171 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL);
173 const extensions::Extension* app =
174 service_->GetInstalledExtension(kPackagedApp2Id);
175 ASSERT_TRUE(app != NULL);
177 // Simulate an app termination.
178 service_->TrackTerminatedExtensionForTest(app);
180 service_->UninstallExtension(kPackagedApp2Id, false, NULL);
181 EXPECT_EQ(std::string("Packaged App 1,Hosted App"),
182 GetModelContent(model_.get()));
184 base::RunLoop().RunUntilIdle();
187 TEST_F(ExtensionAppModelBuilderTest, Reinstall) {
188 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL);
189 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
191 // Install kPackagedApp1Id again should not create a new entry.
192 extensions::InstallTracker* tracker =
193 extensions::InstallTrackerFactory::GetForProfile(profile_.get());
194 tracker->OnBeginExtensionInstall(
195 kPackagedApp1Id, "", gfx::ImageSkia(), true, true);
197 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
200 TEST_F(ExtensionAppModelBuilderTest, OrdinalPrefsChange) {
201 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL);
203 ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting();
205 syncer::StringOrdinal package_app_page =
206 sorting->GetPageOrdinal(kPackagedApp1Id);
207 sorting->SetPageOrdinal(kHostedAppId, package_app_page.CreateBefore());
208 // Old behavior: This would be "Hosted App,Packaged App 1,Packaged App 2"
209 // New behavior: Sorting order doesn't change.
210 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
212 syncer::StringOrdinal app1_ordinal =
213 sorting->GetAppLaunchOrdinal(kPackagedApp1Id);
214 syncer::StringOrdinal app2_ordinal =
215 sorting->GetAppLaunchOrdinal(kPackagedApp2Id);
216 sorting->SetPageOrdinal(kHostedAppId, package_app_page);
217 sorting->SetAppLaunchOrdinal(kHostedAppId,
218 app1_ordinal.CreateBetween(app2_ordinal));
219 // Old behavior: This would be "Packaged App 1,Hosted App,Packaged App 2"
220 // New behavior: Sorting order doesn't change.
221 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
224 TEST_F(ExtensionAppModelBuilderTest, OnExtensionMoved) {
225 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL);
227 ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting();
228 sorting->SetPageOrdinal(kHostedAppId,
229 sorting->GetPageOrdinal(kPackagedApp1Id));
231 service_->OnExtensionMoved(kHostedAppId, kPackagedApp1Id, kPackagedApp2Id);
232 // Old behavior: This would be "Packaged App 1,Hosted App,Packaged App 2"
233 // New behavior: Sorting order doesn't change.
234 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
236 service_->OnExtensionMoved(kHostedAppId, kPackagedApp2Id, std::string());
237 // Old behavior: This would be restored to the default order.
238 // New behavior: Sorting order still doesn't change.
239 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
241 service_->OnExtensionMoved(kHostedAppId, std::string(), kPackagedApp1Id);
242 // Old behavior: This would be "Hosted App,Packaged App 1,Packaged App 2"
243 // New behavior: Sorting order doesn't change.
244 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get()));
247 TEST_F(ExtensionAppModelBuilderTest, InvalidOrdinal) {
248 // Creates a no-ordinal case.
249 ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting();
250 sorting->ClearOrdinals(kPackagedApp1Id);
252 // Creates an corrupted ordinal case.
253 ExtensionScopedPrefs* scoped_prefs = service_->extension_prefs();
254 scoped_prefs->UpdateExtensionPref(
257 base::Value::CreateStringValue("a corrupted ordinal"));
259 // This should not assert or crash.
260 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL);
263 TEST_F(ExtensionAppModelBuilderTest, OrdinalConfilicts) {
264 // Creates conflict ordinals for app1 and app2.
265 syncer::StringOrdinal conflict_ordinal =
266 syncer::StringOrdinal::CreateInitialOrdinal();
268 ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting();
269 sorting->SetPageOrdinal(kHostedAppId, conflict_ordinal);
270 sorting->SetAppLaunchOrdinal(kHostedAppId, conflict_ordinal);
272 sorting->SetPageOrdinal(kPackagedApp1Id, conflict_ordinal);
273 sorting->SetAppLaunchOrdinal(kPackagedApp1Id, conflict_ordinal);
275 sorting->SetPageOrdinal(kPackagedApp2Id, conflict_ordinal);
276 sorting->SetAppLaunchOrdinal(kPackagedApp2Id, conflict_ordinal);
278 // This should not assert or crash.
279 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL);
281 // By default, conflicted items are sorted by their app ids (= order added).
282 EXPECT_EQ(std::string("Hosted App,Packaged App 1,Packaged App 2"),
283 GetModelContent(model_.get()));
286 TEST_F(ExtensionAppModelBuilderTest, SwitchProfile) {
287 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL);
288 EXPECT_EQ(kDefaultAppCount, model_->item_list()->item_count());
290 // Switch to a profile with no apps, ensure all apps are removed.
291 TestingProfile::Builder profile_builder;
292 scoped_ptr<TestingProfile> profile2(profile_builder.Build());
293 builder.SwitchProfile(profile2.get());
294 EXPECT_EQ(0u, model_->item_list()->item_count());
296 // Switch back to the main profile, ensure apps are restored.
297 builder.SwitchProfile(profile_.get());
298 EXPECT_EQ(kDefaultAppCount, model_->item_list()->item_count());