extension_app_model_builder_unittest.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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. 4 5#include "chrome/browser/ui/app_list/extension_app_model_builder.h" 6 7#include <string> 8 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" 31 32namespace { 33 34// Get a string of all apps in |model| joined with ','. 35std::string GetModelContent(app_list::AppListModel* model) { 36 std::string content; 37 for (size_t i = 0; i < model->top_level_item_list()->item_count(); ++i) { 38 if (i > 0) 39 content += ','; 40 content += model->top_level_item_list()->item_at(i)->name(); 41 } 42 return content; 43} 44 45scoped_refptr<extensions::Extension> MakeApp(const std::string& name, 46 const std::string& version, 47 const std::string& url, 48 const std::string& id) { 49 std::string err; 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( 56 base::FilePath(), 57 extensions::Manifest::INTERNAL, 58 value, 59 extensions::Extension::WAS_INSTALLED_BY_DEFAULT, 60 id, 61 &err); 62 EXPECT_EQ(err, ""); 63 return app; 64} 65 66class TestAppListControllerDelegate : public AppListControllerDelegate { 67 public: 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 { 73 return false; 74 } 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 { 81 } 82 virtual bool CanDoShowAppInfoFlow() OVERRIDE { return false; } 83 virtual void DoShowAppInfoFlow(Profile* profile, 84 const std::string& extension_id) OVERRIDE { 85 }; 86 virtual void CreateNewWindow(Profile* profile, bool incognito) OVERRIDE {} 87 virtual void ActivateApp(Profile* profile, 88 const extensions::Extension* extension, 89 AppListSource source, 90 int event_flags) OVERRIDE {} 91 virtual void LaunchApp(Profile* profile, 92 const extensions::Extension* extension, 93 AppListSource source, 94 int event_flags) OVERRIDE {} 95 virtual void ShowForProfileByPath( 96 const base::FilePath& profile_path) OVERRIDE {} 97 virtual bool ShouldShowUserIcon() OVERRIDE { return false; } 98}; 99 100const char kDefaultApps[] = "Packaged App 1,Packaged App 2,Hosted App"; 101const size_t kDefaultAppCount = 3u; 102 103} // namespace 104 105class ExtensionAppModelBuilderTest : public AppListTestBase { 106 public: 107 ExtensionAppModelBuilderTest() {} 108 virtual ~ExtensionAppModelBuilderTest() {} 109 110 virtual void SetUp() OVERRIDE { 111 AppListTestBase::SetUp(); 112 113 CreateBuilder(); 114 } 115 116 virtual void TearDown() OVERRIDE { 117 ResetBuilder(); 118 } 119 120 protected: 121 // Creates a new builder, destroying any existing one. 122 void CreateBuilder() { 123 ResetBuilder(); // Destroy any existing builder in the correct order. 124 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()); 129 } 130 131 void ResetBuilder() { 132 builder_.reset(); 133 controller_.reset(); 134 model_.reset(); 135 } 136 137 scoped_ptr<app_list::AppListModel> model_; 138 scoped_ptr<TestAppListControllerDelegate> controller_; 139 scoped_ptr<ExtensionAppModelBuilder> builder_; 140 141 base::ScopedTempDir second_profile_temp_dir_; 142 143 private: 144 DISALLOW_COPY_AND_ASSIGN(ExtensionAppModelBuilderTest); 145}; 146 147TEST_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())); 151} 152 153TEST_F(ExtensionAppModelBuilderTest, HideWebStore) { 154 // Install a "web store" app. 155 scoped_refptr<extensions::Extension> store = 156 MakeApp("webstore", 157 "0.0", 158 "http://google.com", 159 std::string(extension_misc::kWebStoreAppId)); 160 service_->AddExtension(store.get()); 161 162 // Install an "enterprise web store" app. 163 scoped_refptr<extensions::Extension> enterprise_store = 164 MakeApp("enterprise_webstore", 165 "0.0", 166 "http://google.com", 167 std::string(extension_misc::kEnterpriseWebStoreAppId)); 168 service_->AddExtension(enterprise_store.get()); 169 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())); 176 177 // Activate the HideWebStoreIcon policy. 178 profile_->GetPrefs()->SetBoolean(prefs::kHideWebStoreIcon, true); 179 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())); 183 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())); 190 191 // Deactivate the HideWebStoreIcon policy again. 192 profile_->GetPrefs()->SetBoolean(prefs::kHideWebStoreIcon, false); 193 194 // Now the web stores should have appeared. 195 EXPECT_TRUE(model2.FindItem(store->id())); 196 EXPECT_TRUE(model2.FindItem(enterprise_store->id())); 197} 198 199TEST_F(ExtensionAppModelBuilderTest, DisableAndEnable) { 200 service_->DisableExtension(kHostedAppId, 201 extensions::Extension::DISABLE_NONE); 202 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 203 204 service_->EnableExtension(kHostedAppId); 205 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 206} 207 208TEST_F(ExtensionAppModelBuilderTest, Uninstall) { 209 service_->UninstallExtension(kPackagedApp2Id, 210 extensions::UNINSTALL_REASON_FOR_TESTING, 211 base::Bind(&base::DoNothing), 212 NULL); 213 EXPECT_EQ(std::string("Packaged App 1,Hosted App"), 214 GetModelContent(model_.get())); 215 216 base::RunLoop().RunUntilIdle(); 217} 218 219TEST_F(ExtensionAppModelBuilderTest, UninstallTerminatedApp) { 220 const extensions::Extension* app = 221 service_->GetInstalledExtension(kPackagedApp2Id); 222 ASSERT_TRUE(app != NULL); 223 224 // Simulate an app termination. 225 service_->TrackTerminatedExtensionForTest(app); 226 227 service_->UninstallExtension(kPackagedApp2Id, 228 extensions::UNINSTALL_REASON_FOR_TESTING, 229 base::Bind(&base::DoNothing), 230 NULL); 231 EXPECT_EQ(std::string("Packaged App 1,Hosted App"), 232 GetModelContent(model_.get())); 233 234 base::RunLoop().RunUntilIdle(); 235} 236 237TEST_F(ExtensionAppModelBuilderTest, Reinstall) { 238 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 239 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); 246 247 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 248} 249 250TEST_F(ExtensionAppModelBuilderTest, OrdinalPrefsChange) { 251 extensions::AppSorting* sorting = 252 extensions::ExtensionPrefs::Get(profile_.get())->app_sorting(); 253 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())); 260 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())); 271} 272 273TEST_F(ExtensionAppModelBuilderTest, OnExtensionMoved) { 274 extensions::AppSorting* sorting = 275 extensions::ExtensionPrefs::Get(profile_.get())->app_sorting(); 276 sorting->SetPageOrdinal(kHostedAppId, 277 sorting->GetPageOrdinal(kPackagedApp1Id)); 278 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())); 283 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())); 288 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())); 293} 294 295TEST_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); 300 301 // Creates a corrupted ordinal case. 302 extensions::ExtensionScopedPrefs* scoped_prefs = 303 extensions::ExtensionPrefs::Get(profile_.get()); 304 scoped_prefs->UpdateExtensionPref( 305 kHostedAppId, 306 "page_ordinal", 307 new base::StringValue("a corrupted ordinal")); 308 309 // This should not assert or crash. 310 CreateBuilder(); 311} 312 313TEST_F(ExtensionAppModelBuilderTest, OrdinalConfilicts) { 314 // Creates conflict ordinals for app1 and app2. 315 syncer::StringOrdinal conflict_ordinal = 316 syncer::StringOrdinal::CreateInitialOrdinal(); 317 318 extensions::AppSorting* sorting = 319 extensions::ExtensionPrefs::Get(profile_.get())->app_sorting(); 320 sorting->SetPageOrdinal(kHostedAppId, conflict_ordinal); 321 sorting->SetAppLaunchOrdinal(kHostedAppId, conflict_ordinal); 322 323 sorting->SetPageOrdinal(kPackagedApp1Id, conflict_ordinal); 324 sorting->SetAppLaunchOrdinal(kPackagedApp1Id, conflict_ordinal); 325 326 sorting->SetPageOrdinal(kPackagedApp2Id, conflict_ordinal); 327 sorting->SetAppLaunchOrdinal(kPackagedApp2Id, conflict_ordinal); 328 329 // This should not assert or crash. 330 CreateBuilder(); 331 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())); 335} 336 337// This test adds a bookmark app to the app list. 338TEST_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"; 343 std::string err; 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( 350 base::FilePath(), 351 extensions::Manifest::INTERNAL, 352 value, 353 extensions::Extension::WAS_INSTALLED_BY_DEFAULT | 354 extensions::Extension::FROM_BOOKMARK, 355 kAppId, 356 &err); 357 EXPECT_TRUE(err.empty()); 358 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)); 362} 363