extension_app_model_builder_unittest.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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_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" 25 26namespace { 27 28const char kHostedAppId[] = "dceacbkfkmllgmjmbhgkpjegnodmildf"; 29const char kPackagedApp1Id[] = "emfkafnhnpcmabnnkckkchdilgeoekbo"; 30const char kPackagedApp2Id[] = "jlklkagmeajbjiobondfhiekepofmljl"; 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->item_list()->item_count(); ++i) { 36 if (i > 0) 37 content += ','; 38 content += model->item_list()->item_at(i)->title(); 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 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 64const char kDefaultApps[] = "Packaged App 1,Packaged App 2,Hosted App"; 65const size_t kDefaultAppCount = 3u; 66 67} // namespace 68 69class ExtensionAppModelBuilderTest : public ExtensionServiceTestBase { 70 public: 71 ExtensionAppModelBuilderTest() {} 72 virtual ~ExtensionAppModelBuilderTest() {} 73 74 virtual void SetUp() OVERRIDE { 75 ExtensionServiceTestBase::SetUp(); 76 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 84 .DirName() 85 .AppendASCII("Preferences"); 86 InitializeInstalledExtensionService(pref_path, source_install_dir); 87 service_->Init(); 88 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()); 92 93 model_.reset(new app_list::AppListModel); 94 } 95 96 virtual void TearDown() OVERRIDE { 97 model_.reset(); 98 } 99 100 protected: 101 scoped_ptr<app_list::AppListModel> model_; 102 103 private: 104 DISALLOW_COPY_AND_ASSIGN(ExtensionAppModelBuilderTest); 105}; 106 107TEST_F(ExtensionAppModelBuilderTest, Build) { 108 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL); 109 110 // The apps list would have 3 extension apps in the profile. 111 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 112} 113 114TEST_F(ExtensionAppModelBuilderTest, HideWebStore) { 115 // Install a "web store" app. 116 scoped_refptr<extensions::Extension> store = 117 MakeApp("webstore", 118 "0.0", 119 "http://google.com", 120 std::string(extension_misc::kWebStoreAppId)); 121 service_->AddExtension(store.get()); 122 123 // Install an "enterprise web store" app. 124 scoped_refptr<extensions::Extension> enterprise_store = 125 MakeApp("enterprise_webstore", 126 "0.0", 127 "http://google.com", 128 std::string(extension_misc::kEnterpriseWebStoreAppId)); 129 service_->AddExtension(enterprise_store.get()); 130 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")); 137 138 // Activate the HideWebStoreIcon policy. 139 profile_->GetPrefs()->SetBoolean(prefs::kHideWebStoreIcon, true); 140 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")); 147} 148 149TEST_F(ExtensionAppModelBuilderTest, DisableAndEnable) { 150 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL); 151 152 service_->DisableExtension(kHostedAppId, 153 extensions::Extension::DISABLE_NONE); 154 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 155 156 service_->EnableExtension(kHostedAppId); 157 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 158} 159 160TEST_F(ExtensionAppModelBuilderTest, Uninstall) { 161 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL); 162 163 service_->UninstallExtension(kPackagedApp2Id, false, NULL); 164 EXPECT_EQ(std::string("Packaged App 1,Hosted App"), 165 GetModelContent(model_.get())); 166 167 base::RunLoop().RunUntilIdle(); 168} 169 170TEST_F(ExtensionAppModelBuilderTest, UninstallTerminatedApp) { 171 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL); 172 173 const extensions::Extension* app = 174 service_->GetInstalledExtension(kPackagedApp2Id); 175 ASSERT_TRUE(app != NULL); 176 177 // Simulate an app termination. 178 service_->TrackTerminatedExtensionForTest(app); 179 180 service_->UninstallExtension(kPackagedApp2Id, false, NULL); 181 EXPECT_EQ(std::string("Packaged App 1,Hosted App"), 182 GetModelContent(model_.get())); 183 184 base::RunLoop().RunUntilIdle(); 185} 186 187TEST_F(ExtensionAppModelBuilderTest, Reinstall) { 188 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL); 189 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 190 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); 196 197 EXPECT_EQ(std::string(kDefaultApps), GetModelContent(model_.get())); 198} 199 200TEST_F(ExtensionAppModelBuilderTest, OrdinalPrefsChange) { 201 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL); 202 203 ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting(); 204 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())); 211 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())); 222} 223 224TEST_F(ExtensionAppModelBuilderTest, OnExtensionMoved) { 225 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL); 226 227 ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting(); 228 sorting->SetPageOrdinal(kHostedAppId, 229 sorting->GetPageOrdinal(kPackagedApp1Id)); 230 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())); 235 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())); 240 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())); 245} 246 247TEST_F(ExtensionAppModelBuilderTest, InvalidOrdinal) { 248 // Creates a no-ordinal case. 249 ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting(); 250 sorting->ClearOrdinals(kPackagedApp1Id); 251 252 // Creates an corrupted ordinal case. 253 ExtensionScopedPrefs* scoped_prefs = service_->extension_prefs(); 254 scoped_prefs->UpdateExtensionPref( 255 kHostedAppId, 256 "page_ordinal", 257 base::Value::CreateStringValue("a corrupted ordinal")); 258 259 // This should not assert or crash. 260 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL); 261} 262 263TEST_F(ExtensionAppModelBuilderTest, OrdinalConfilicts) { 264 // Creates conflict ordinals for app1 and app2. 265 syncer::StringOrdinal conflict_ordinal = 266 syncer::StringOrdinal::CreateInitialOrdinal(); 267 268 ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting(); 269 sorting->SetPageOrdinal(kHostedAppId, conflict_ordinal); 270 sorting->SetAppLaunchOrdinal(kHostedAppId, conflict_ordinal); 271 272 sorting->SetPageOrdinal(kPackagedApp1Id, conflict_ordinal); 273 sorting->SetAppLaunchOrdinal(kPackagedApp1Id, conflict_ordinal); 274 275 sorting->SetPageOrdinal(kPackagedApp2Id, conflict_ordinal); 276 sorting->SetAppLaunchOrdinal(kPackagedApp2Id, conflict_ordinal); 277 278 // This should not assert or crash. 279 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL); 280 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())); 284} 285 286TEST_F(ExtensionAppModelBuilderTest, SwitchProfile) { 287 ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL); 288 EXPECT_EQ(kDefaultAppCount, model_->item_list()->item_count()); 289 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()); 295 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()); 299} 300