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