extension_app_model_builder_unittest.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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/common/extension_set.h" 27#include "extensions/common/manifest.h" 28#include "testing/gtest/include/gtest/gtest.h" 29#include "ui/app_list/app_list_item.h" 30 31namespace { 32 33// Get a string of all apps in |model| joined with ','. 34std::string GetModelContent(app_list::AppListModel* model) { 35 std::string content; 36 for (size_t i = 0; i < model->top_level_item_list()->item_count(); ++i) { 37 if (i > 0) 38 content += ','; 39 content += model->top_level_item_list()->item_at(i)->name(); 40 } 41 return content; 42} 43 44scoped_refptr<extensions::Extension> MakeApp(const std::string& name, 45 const std::string& version, 46 const std::string& url, 47 const std::string& id) { 48 std::string err; 49 base::DictionaryValue value; 50 value.SetString("name", name); 51 value.SetString("version", version); 52 value.SetString("app.launch.web_url", url); 53 scoped_refptr<extensions::Extension> app = 54 extensions::Extension::Create( 55 base::FilePath(), 56 extensions::Manifest::INTERNAL, 57 value, 58 extensions::Extension::WAS_INSTALLED_BY_DEFAULT, 59 id, 60 &err); 61 EXPECT_EQ(err, ""); 62 return app; 63} 64 65class TestAppListControllerDelegate : public AppListControllerDelegate { 66 public: 67 virtual ~TestAppListControllerDelegate() {} 68 virtual void DismissView() OVERRIDE {} 69 virtual gfx::NativeWindow GetAppListWindow() OVERRIDE { return NULL; } 70 virtual gfx::ImageSkia GetWindowIcon() OVERRIDE { return gfx::ImageSkia(); } 71 virtual bool IsAppPinned(const std::string& extension_id) OVERRIDE { 72 return false; 73 } 74 virtual void PinApp(const std::string& extension_id) OVERRIDE {} 75 virtual void UnpinApp(const std::string& extension_id) OVERRIDE {} 76 virtual Pinnable GetPinnable() OVERRIDE { return NO_PIN; } 77 virtual bool CanDoCreateShortcutsFlow() OVERRIDE { return false; } 78 virtual void DoCreateShortcutsFlow(Profile* profile, 79 const std::string& extension_id) OVERRIDE { 80 } 81 virtual bool CanDoShowAppInfoFlow() OVERRIDE { return false; } 82 virtual void DoShowAppInfoFlow(Profile* profile, 83 const std::string& extension_id) OVERRIDE { 84 }; 85 virtual void CreateNewWindow(Profile* profile, bool incognito) OVERRIDE {} 86 virtual void ActivateApp(Profile* profile, 87 const extensions::Extension* extension, 88 AppListSource source, 89 int event_flags) OVERRIDE {} 90 virtual void LaunchApp(Profile* profile, 91 const extensions::Extension* extension, 92 AppListSource source, 93 int event_flags) OVERRIDE {} 94 virtual void ShowForProfileByPath( 95 const base::FilePath& profile_path) OVERRIDE {} 96 virtual bool ShouldShowUserIcon() OVERRIDE { return false; } 97}; 98 99const char kDefaultApps[] = "Packaged App 1,Packaged App 2,Hosted App"; 100const size_t kDefaultAppCount = 3u; 101 102} // namespace 103 104class ExtensionAppModelBuilderTest : public AppListTestBase { 105 public: 106 ExtensionAppModelBuilderTest() {} 107 virtual ~ExtensionAppModelBuilderTest() {} 108 109 virtual void SetUp() OVERRIDE { 110 AppListTestBase::SetUp(); 111 112 CreateBuilder(); 113 } 114 115 virtual void TearDown() OVERRIDE { 116 ResetBuilder(); 117 } 118 119 protected: 120 // Creates a new builder, destroying any existing one. 121 void CreateBuilder() { 122 ResetBuilder(); // Destroy any existing builder in the correct order. 123 124 model_.reset(new app_list::AppListModel); 125 controller_.reset(new TestAppListControllerDelegate); 126 builder_.reset(new ExtensionAppModelBuilder(controller_.get())); 127 builder_->InitializeWithProfile(profile_.get(), model_.get()); 128 } 129 130 void ResetBuilder() { 131 builder_.reset(); 132 controller_.reset(); 133 model_.reset(); 134 } 135 136 scoped_ptr<app_list::AppListModel> model_; 137 scoped_ptr<TestAppListControllerDelegate> controller_; 138 scoped_ptr<ExtensionAppModelBuilder> builder_; 139 140 base::ScopedTempDir second_profile_temp_dir_; 141 142 private: 143 DISALLOW_COPY_AND_ASSIGN(ExtensionAppModelBuilderTest); 144}; 145 146TEST_F(ExtensionAppModelBuilderTest, Build) { 147 // The apps list would have 3 extension apps in the profile. 148 EXPECT_EQ(kDefaultAppCount, model_->top_level_item_list()->item_count()); 149 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 150} 151 152TEST_F(ExtensionAppModelBuilderTest, HideWebStore) { 153 // Install a "web store" app. 154 scoped_refptr<extensions::Extension> store = 155 MakeApp("webstore", 156 "0.0", 157 "http://google.com", 158 std::string(extension_misc::kWebStoreAppId)); 159 service_->AddExtension(store.get()); 160 161 // Install an "enterprise web store" app. 162 scoped_refptr<extensions::Extension> enterprise_store = 163 MakeApp("enterprise_webstore", 164 "0.0", 165 "http://google.com", 166 std::string(extension_misc::kEnterpriseWebStoreAppId)); 167 service_->AddExtension(enterprise_store.get()); 168 169 // Web stores should be present in the AppListModel. 170 app_list::AppListModel model1; 171 ExtensionAppModelBuilder builder1(controller_.get()); 172 builder1.InitializeWithProfile(profile_.get(), &model1); 173 std::string content = GetModelContent(&model1); 174 EXPECT_NE(std::string::npos, content.find("webstore")); 175 EXPECT_NE(std::string::npos, content.find("enterprise_webstore")); 176 177 // Activate the HideWebStoreIcon policy. 178 profile_->GetPrefs()->SetBoolean(prefs::kHideWebStoreIcon, true); 179 180 // Web stores should NOT be in the AppListModel. 181 app_list::AppListModel model2; 182 ExtensionAppModelBuilder builder2(controller_.get()); 183 builder2.InitializeWithProfile(profile_.get(), &model2); 184 content = GetModelContent(&model2); 185 EXPECT_EQ(std::string::npos, content.find("webstore")); 186 EXPECT_EQ(std::string::npos, content.find("enterprise_webstore")); 187} 188 189TEST_F(ExtensionAppModelBuilderTest, DisableAndEnable) { 190 service_->DisableExtension(kHostedAppId, 191 extensions::Extension::DISABLE_NONE); 192 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 193 194 service_->EnableExtension(kHostedAppId); 195 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 196} 197 198TEST_F(ExtensionAppModelBuilderTest, Uninstall) { 199 service_->UninstallExtension(kPackagedApp2Id, false, NULL); 200 EXPECT_EQ(std::string("Packaged App 1,Hosted App"), 201 GetModelContent(model_.get())); 202 203 base::RunLoop().RunUntilIdle(); 204} 205 206TEST_F(ExtensionAppModelBuilderTest, UninstallTerminatedApp) { 207 const extensions::Extension* app = 208 service_->GetInstalledExtension(kPackagedApp2Id); 209 ASSERT_TRUE(app != NULL); 210 211 // Simulate an app termination. 212 service_->TrackTerminatedExtensionForTest(app); 213 214 service_->UninstallExtension(kPackagedApp2Id, false, NULL); 215 EXPECT_EQ(std::string("Packaged App 1,Hosted App"), 216 GetModelContent(model_.get())); 217 218 base::RunLoop().RunUntilIdle(); 219} 220 221TEST_F(ExtensionAppModelBuilderTest, Reinstall) { 222 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 223 224 // Install kPackagedApp1Id again should not create a new entry. 225 extensions::InstallTracker* tracker = 226 extensions::InstallTrackerFactory::GetForProfile(profile_.get()); 227 extensions::InstallObserver::ExtensionInstallParams params( 228 kPackagedApp1Id, "", gfx::ImageSkia(), true, true); 229 tracker->OnBeginExtensionInstall(params); 230 231 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 232} 233 234TEST_F(ExtensionAppModelBuilderTest, OrdinalPrefsChange) { 235 extensions::AppSorting* sorting = 236 extensions::ExtensionPrefs::Get(profile_.get())->app_sorting(); 237 238 syncer::StringOrdinal package_app_page = 239 sorting->GetPageOrdinal(kPackagedApp1Id); 240 sorting->SetPageOrdinal(kHostedAppId, package_app_page.CreateBefore()); 241 // Old behavior: This would be "Hosted App,Packaged App 1,Packaged App 2" 242 // New behavior: Sorting order doesn't change. 243 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 244 245 syncer::StringOrdinal app1_ordinal = 246 sorting->GetAppLaunchOrdinal(kPackagedApp1Id); 247 syncer::StringOrdinal app2_ordinal = 248 sorting->GetAppLaunchOrdinal(kPackagedApp2Id); 249 sorting->SetPageOrdinal(kHostedAppId, package_app_page); 250 sorting->SetAppLaunchOrdinal(kHostedAppId, 251 app1_ordinal.CreateBetween(app2_ordinal)); 252 // Old behavior: This would be "Packaged App 1,Hosted App,Packaged App 2" 253 // New behavior: Sorting order doesn't change. 254 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 255} 256 257TEST_F(ExtensionAppModelBuilderTest, OnExtensionMoved) { 258 extensions::AppSorting* sorting = 259 extensions::ExtensionPrefs::Get(profile_.get())->app_sorting(); 260 sorting->SetPageOrdinal(kHostedAppId, 261 sorting->GetPageOrdinal(kPackagedApp1Id)); 262 263 sorting->OnExtensionMoved(kHostedAppId, kPackagedApp1Id, kPackagedApp2Id); 264 // Old behavior: This would be "Packaged App 1,Hosted App,Packaged App 2" 265 // New behavior: Sorting order doesn't change. 266 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 267 268 sorting->OnExtensionMoved(kHostedAppId, kPackagedApp2Id, std::string()); 269 // Old behavior: This would be restored to the default order. 270 // New behavior: Sorting order still doesn't change. 271 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 272 273 sorting->OnExtensionMoved(kHostedAppId, std::string(), kPackagedApp1Id); 274 // Old behavior: This would be "Hosted App,Packaged App 1,Packaged App 2" 275 // New behavior: Sorting order doesn't change. 276 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 277} 278 279TEST_F(ExtensionAppModelBuilderTest, InvalidOrdinal) { 280 // Creates a no-ordinal case. 281 extensions::AppSorting* sorting = 282 extensions::ExtensionPrefs::Get(profile_.get())->app_sorting(); 283 sorting->ClearOrdinals(kPackagedApp1Id); 284 285 // Creates a corrupted ordinal case. 286 extensions::ExtensionScopedPrefs* scoped_prefs = 287 extensions::ExtensionPrefs::Get(profile_.get()); 288 scoped_prefs->UpdateExtensionPref( 289 kHostedAppId, 290 "page_ordinal", 291 base::Value::CreateStringValue("a corrupted ordinal")); 292 293 // This should not assert or crash. 294 CreateBuilder(); 295} 296 297TEST_F(ExtensionAppModelBuilderTest, OrdinalConfilicts) { 298 // Creates conflict ordinals for app1 and app2. 299 syncer::StringOrdinal conflict_ordinal = 300 syncer::StringOrdinal::CreateInitialOrdinal(); 301 302 extensions::AppSorting* sorting = 303 extensions::ExtensionPrefs::Get(profile_.get())->app_sorting(); 304 sorting->SetPageOrdinal(kHostedAppId, conflict_ordinal); 305 sorting->SetAppLaunchOrdinal(kHostedAppId, conflict_ordinal); 306 307 sorting->SetPageOrdinal(kPackagedApp1Id, conflict_ordinal); 308 sorting->SetAppLaunchOrdinal(kPackagedApp1Id, conflict_ordinal); 309 310 sorting->SetPageOrdinal(kPackagedApp2Id, conflict_ordinal); 311 sorting->SetAppLaunchOrdinal(kPackagedApp2Id, conflict_ordinal); 312 313 // This should not assert or crash. 314 CreateBuilder(); 315 316 // By default, conflicted items are sorted by their app ids (= order added). 317 EXPECT_EQ(std::string("Hosted App,Packaged App 1,Packaged App 2"), 318 GetModelContent(model_.get())); 319} 320 321// This test adds a bookmark app to the app list. 322TEST_F(ExtensionAppModelBuilderTest, BookmarkApp) { 323 const std::string kAppName = "Bookmark App"; 324 const std::string kAppVersion = "2014.1.24.19748"; 325 const std::string kAppUrl = "http://google.com"; 326 const std::string kAppId = "podhdnefolignjhecmjkbimfgioanahm"; 327 std::string err; 328 base::DictionaryValue value; 329 value.SetString("name", kAppName); 330 value.SetString("version", kAppVersion); 331 value.SetString("app.launch.web_url", kAppUrl); 332 scoped_refptr<extensions::Extension> bookmark_app = 333 extensions::Extension::Create( 334 base::FilePath(), 335 extensions::Manifest::INTERNAL, 336 value, 337 extensions::Extension::WAS_INSTALLED_BY_DEFAULT | 338 extensions::Extension::FROM_BOOKMARK, 339 kAppId, 340 &err); 341 EXPECT_TRUE(err.empty()); 342 343 service_->AddExtension(bookmark_app.get()); 344 EXPECT_EQ(kDefaultAppCount + 1, model_->top_level_item_list()->item_count()); 345 EXPECT_NE(std::string::npos, GetModelContent(model_.get()).find(kAppName)); 346} 347