1 // Copyright 2013 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/extensions/chrome_app_sorting.h" 6 7 #include <map> 8 9 #include "chrome/browser/extensions/./extension_prefs_unittest.h" 10 #include "components/crx_file/id_util.h" 11 #include "extensions/common/constants.h" 12 #include "extensions/common/manifest_constants.h" 13 #include "sync/api/string_ordinal.h" 14 #include "testing/gtest/include/gtest/gtest.h" 15 16 namespace extensions { 17 18 namespace keys = manifest_keys; 19 20 class ChromeAppSortingTest : public ExtensionPrefsTest { 21 protected: 22 ChromeAppSorting* app_sorting() { 23 return static_cast<ChromeAppSorting*>(prefs()->app_sorting()); 24 } 25 }; 26 27 class ChromeAppSortingAppLocation : public ChromeAppSortingTest { 28 public: 29 virtual void Initialize() OVERRIDE { 30 extension_ = prefs_.AddExtension("not_an_app"); 31 // Non-apps should not have any app launch ordinal or page ordinal. 32 prefs()->OnExtensionInstalled(extension_.get(), 33 Extension::ENABLED, 34 syncer::StringOrdinal(), 35 std::string()); 36 } 37 38 virtual void Verify() OVERRIDE { 39 EXPECT_FALSE( 40 app_sorting()->GetAppLaunchOrdinal(extension_->id()).IsValid()); 41 EXPECT_FALSE( 42 app_sorting()->GetPageOrdinal(extension_->id()).IsValid()); 43 } 44 45 private: 46 scoped_refptr<Extension> extension_; 47 }; 48 TEST_F(ChromeAppSortingAppLocation, ChromeAppSortingAppLocation) {} 49 50 class ChromeAppSortingAppLaunchOrdinal : public ChromeAppSortingTest { 51 public: 52 virtual void Initialize() OVERRIDE { 53 // No extensions yet. 54 syncer::StringOrdinal page = syncer::StringOrdinal::CreateInitialOrdinal(); 55 EXPECT_TRUE(syncer::StringOrdinal::CreateInitialOrdinal().Equals( 56 app_sorting()->CreateNextAppLaunchOrdinal(page))); 57 58 extension_ = prefs_.AddApp("on_extension_installed"); 59 EXPECT_FALSE(prefs()->IsExtensionDisabled(extension_->id())); 60 prefs()->OnExtensionInstalled(extension_.get(), 61 Extension::ENABLED, 62 syncer::StringOrdinal(), 63 std::string()); 64 } 65 66 virtual void Verify() OVERRIDE { 67 syncer::StringOrdinal launch_ordinal = 68 app_sorting()->GetAppLaunchOrdinal(extension_->id()); 69 syncer::StringOrdinal page_ordinal = 70 syncer::StringOrdinal::CreateInitialOrdinal(); 71 72 // Extension should have been assigned a valid StringOrdinal. 73 EXPECT_TRUE(launch_ordinal.IsValid()); 74 EXPECT_TRUE(launch_ordinal.LessThan( 75 app_sorting()->CreateNextAppLaunchOrdinal(page_ordinal))); 76 // Set a new launch ordinal of and verify it comes after. 77 app_sorting()->SetAppLaunchOrdinal( 78 extension_->id(), 79 app_sorting()->CreateNextAppLaunchOrdinal(page_ordinal)); 80 syncer::StringOrdinal new_launch_ordinal = 81 app_sorting()->GetAppLaunchOrdinal(extension_->id()); 82 EXPECT_TRUE(launch_ordinal.LessThan(new_launch_ordinal)); 83 84 // This extension doesn't exist, so it should return an invalid 85 // StringOrdinal. 86 syncer::StringOrdinal invalid_app_launch_ordinal = 87 app_sorting()->GetAppLaunchOrdinal("foo"); 88 EXPECT_FALSE(invalid_app_launch_ordinal.IsValid()); 89 EXPECT_EQ(-1, app_sorting()->PageStringOrdinalAsInteger( 90 invalid_app_launch_ordinal)); 91 92 // The second page doesn't have any apps so its next launch ordinal should 93 // be the first launch ordinal. 94 syncer::StringOrdinal next_page = page_ordinal.CreateAfter(); 95 syncer::StringOrdinal next_page_app_launch_ordinal = 96 app_sorting()->CreateNextAppLaunchOrdinal(next_page); 97 EXPECT_TRUE(next_page_app_launch_ordinal.Equals( 98 app_sorting()->CreateFirstAppLaunchOrdinal(next_page))); 99 } 100 101 private: 102 scoped_refptr<Extension> extension_; 103 }; 104 TEST_F(ChromeAppSortingAppLaunchOrdinal, ChromeAppSortingAppLaunchOrdinal) {} 105 106 class ChromeAppSortingPageOrdinal : public ChromeAppSortingTest { 107 public: 108 virtual void Initialize() OVERRIDE { 109 extension_ = prefs_.AddApp("page_ordinal"); 110 // Install with a page preference. 111 first_page_ = syncer::StringOrdinal::CreateInitialOrdinal(); 112 prefs()->OnExtensionInstalled(extension_.get(), 113 Extension::ENABLED, 114 first_page_, 115 std::string()); 116 EXPECT_TRUE(first_page_.Equals( 117 app_sorting()->GetPageOrdinal(extension_->id()))); 118 EXPECT_EQ(0, app_sorting()->PageStringOrdinalAsInteger(first_page_)); 119 120 scoped_refptr<Extension> extension2 = prefs_.AddApp("page_ordinal_2"); 121 // Install without any page preference. 122 prefs()->OnExtensionInstalled(extension2.get(), 123 Extension::ENABLED, 124 syncer::StringOrdinal(), 125 std::string()); 126 EXPECT_TRUE(first_page_.Equals( 127 app_sorting()->GetPageOrdinal(extension2->id()))); 128 } 129 virtual void Verify() OVERRIDE { 130 // Set the page ordinal. 131 syncer::StringOrdinal new_page = first_page_.CreateAfter(); 132 app_sorting()->SetPageOrdinal(extension_->id(), new_page); 133 // Verify the page ordinal. 134 EXPECT_TRUE( 135 new_page.Equals(app_sorting()->GetPageOrdinal(extension_->id()))); 136 EXPECT_EQ(1, app_sorting()->PageStringOrdinalAsInteger(new_page)); 137 138 // This extension doesn't exist, so it should return an invalid 139 // StringOrdinal. 140 EXPECT_FALSE(app_sorting()->GetPageOrdinal("foo").IsValid()); 141 } 142 143 private: 144 syncer::StringOrdinal first_page_; 145 scoped_refptr<Extension> extension_; 146 }; 147 TEST_F(ChromeAppSortingPageOrdinal, ChromeAppSortingPageOrdinal) {} 148 149 // Ensure that ChromeAppSorting is able to properly initialize off a set 150 // of old page and app launch indices and properly convert them. 151 class ChromeAppSortingInitialize : public PrefsPrepopulatedTestBase { 152 public: 153 ChromeAppSortingInitialize() {} 154 virtual ~ChromeAppSortingInitialize() {} 155 156 virtual void Initialize() OVERRIDE { 157 // A preference determining the order of which the apps appear on the NTP. 158 const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index"; 159 // A preference determining the page on which an app appears in the NTP. 160 const char kPrefPageIndexDeprecated[] = "page_index"; 161 162 // Setup the deprecated preferences. 163 ExtensionScopedPrefs* scoped_prefs = 164 static_cast<ExtensionScopedPrefs*>(prefs()); 165 scoped_prefs->UpdateExtensionPref(extension1()->id(), 166 kPrefAppLaunchIndexDeprecated, 167 new base::FundamentalValue(0)); 168 scoped_prefs->UpdateExtensionPref(extension1()->id(), 169 kPrefPageIndexDeprecated, 170 new base::FundamentalValue(0)); 171 172 scoped_prefs->UpdateExtensionPref(extension2()->id(), 173 kPrefAppLaunchIndexDeprecated, 174 new base::FundamentalValue(1)); 175 scoped_prefs->UpdateExtensionPref(extension2()->id(), 176 kPrefPageIndexDeprecated, 177 new base::FundamentalValue(0)); 178 179 scoped_prefs->UpdateExtensionPref(extension3()->id(), 180 kPrefAppLaunchIndexDeprecated, 181 new base::FundamentalValue(0)); 182 scoped_prefs->UpdateExtensionPref(extension3()->id(), 183 kPrefPageIndexDeprecated, 184 new base::FundamentalValue(1)); 185 186 // We insert the ids in reserve order so that we have to deal with the 187 // element on the 2nd page before the 1st page is seen. 188 ExtensionIdList ids; 189 ids.push_back(extension3()->id()); 190 ids.push_back(extension2()->id()); 191 ids.push_back(extension1()->id()); 192 193 prefs()->app_sorting()->Initialize(ids); 194 } 195 virtual void Verify() OVERRIDE { 196 syncer::StringOrdinal first_ordinal = 197 syncer::StringOrdinal::CreateInitialOrdinal(); 198 AppSorting* app_sorting = prefs()->app_sorting(); 199 200 EXPECT_TRUE(first_ordinal.Equals( 201 app_sorting->GetAppLaunchOrdinal(extension1()->id()))); 202 EXPECT_TRUE(first_ordinal.LessThan( 203 app_sorting->GetAppLaunchOrdinal(extension2()->id()))); 204 EXPECT_TRUE(first_ordinal.Equals( 205 app_sorting->GetAppLaunchOrdinal(extension3()->id()))); 206 207 EXPECT_TRUE(first_ordinal.Equals( 208 app_sorting->GetPageOrdinal(extension1()->id()))); 209 EXPECT_TRUE(first_ordinal.Equals( 210 app_sorting->GetPageOrdinal(extension2()->id()))); 211 EXPECT_TRUE(first_ordinal.LessThan( 212 app_sorting->GetPageOrdinal(extension3()->id()))); 213 } 214 }; 215 TEST_F(ChromeAppSortingInitialize, ChromeAppSortingInitialize) {} 216 217 // Make sure that initialization still works when no extensions are present 218 // (i.e. make sure that the web store icon is still loaded into the map). 219 class ChromeAppSortingInitializeWithNoApps : public PrefsPrepopulatedTestBase { 220 public: 221 ChromeAppSortingInitializeWithNoApps() {} 222 virtual ~ChromeAppSortingInitializeWithNoApps() {} 223 224 virtual void Initialize() OVERRIDE { 225 AppSorting* app_sorting = prefs()->app_sorting(); 226 227 // Make sure that the web store has valid ordinals. 228 syncer::StringOrdinal initial_ordinal = 229 syncer::StringOrdinal::CreateInitialOrdinal(); 230 app_sorting->SetPageOrdinal(extensions::kWebStoreAppId, 231 initial_ordinal); 232 app_sorting->SetAppLaunchOrdinal(extensions::kWebStoreAppId, 233 initial_ordinal); 234 235 ExtensionIdList ids; 236 app_sorting->Initialize(ids); 237 } 238 virtual void Verify() OVERRIDE { 239 ChromeAppSorting* app_sorting = 240 static_cast<ChromeAppSorting*>(prefs()->app_sorting()); 241 242 syncer::StringOrdinal page = 243 app_sorting->GetPageOrdinal(extensions::kWebStoreAppId); 244 EXPECT_TRUE(page.IsValid()); 245 246 ChromeAppSorting::PageOrdinalMap::iterator page_it = 247 app_sorting->ntp_ordinal_map_.find(page); 248 EXPECT_TRUE(page_it != app_sorting->ntp_ordinal_map_.end()); 249 250 syncer::StringOrdinal app_launch = 251 app_sorting->GetPageOrdinal(extensions::kWebStoreAppId); 252 EXPECT_TRUE(app_launch.IsValid()); 253 254 ChromeAppSorting::AppLaunchOrdinalMap::iterator app_launch_it = 255 page_it->second.find(app_launch); 256 EXPECT_TRUE(app_launch_it != page_it->second.end()); 257 } 258 }; 259 TEST_F(ChromeAppSortingInitializeWithNoApps, 260 ChromeAppSortingInitializeWithNoApps) {} 261 262 // Tests the application index to ordinal migration code for values that 263 // shouldn't be converted. This should be removed when the migrate code 264 // is taken out. 265 // http://crbug.com/107376 266 class ChromeAppSortingMigrateAppIndexInvalid 267 : public PrefsPrepopulatedTestBase { 268 public: 269 ChromeAppSortingMigrateAppIndexInvalid() {} 270 virtual ~ChromeAppSortingMigrateAppIndexInvalid() {} 271 272 virtual void Initialize() OVERRIDE { 273 // A preference determining the order of which the apps appear on the NTP. 274 const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index"; 275 // A preference determining the page on which an app appears in the NTP. 276 const char kPrefPageIndexDeprecated[] = "page_index"; 277 278 // Setup the deprecated preference. 279 ExtensionScopedPrefs* scoped_prefs = 280 static_cast<ExtensionScopedPrefs*>(prefs()); 281 scoped_prefs->UpdateExtensionPref(extension1()->id(), 282 kPrefAppLaunchIndexDeprecated, 283 new base::FundamentalValue(0)); 284 scoped_prefs->UpdateExtensionPref(extension1()->id(), 285 kPrefPageIndexDeprecated, 286 new base::FundamentalValue(-1)); 287 288 ExtensionIdList ids; 289 ids.push_back(extension1()->id()); 290 291 prefs()->app_sorting()->Initialize(ids); 292 } 293 virtual void Verify() OVERRIDE { 294 // Make sure that the invalid page_index wasn't converted over. 295 EXPECT_FALSE(prefs()->app_sorting()->GetAppLaunchOrdinal( 296 extension1()->id()).IsValid()); 297 } 298 }; 299 TEST_F(ChromeAppSortingMigrateAppIndexInvalid, 300 ChromeAppSortingMigrateAppIndexInvalid) {} 301 302 class ChromeAppSortingFixNTPCollisionsAllCollide 303 : public PrefsPrepopulatedTestBase { 304 public: 305 ChromeAppSortingFixNTPCollisionsAllCollide() {} 306 virtual ~ChromeAppSortingFixNTPCollisionsAllCollide() {} 307 308 virtual void Initialize() OVERRIDE { 309 repeated_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal(); 310 311 AppSorting* app_sorting = prefs()->app_sorting(); 312 313 app_sorting->SetAppLaunchOrdinal(extension1()->id(), 314 repeated_ordinal_); 315 app_sorting->SetPageOrdinal(extension1()->id(), repeated_ordinal_); 316 317 app_sorting->SetAppLaunchOrdinal(extension2()->id(), repeated_ordinal_); 318 app_sorting->SetPageOrdinal(extension2()->id(), repeated_ordinal_); 319 320 app_sorting->SetAppLaunchOrdinal(extension3()->id(), repeated_ordinal_); 321 app_sorting->SetPageOrdinal(extension3()->id(), repeated_ordinal_); 322 323 app_sorting->FixNTPOrdinalCollisions(); 324 } 325 virtual void Verify() OVERRIDE { 326 AppSorting* app_sorting = prefs()->app_sorting(); 327 syncer::StringOrdinal extension1_app_launch = 328 app_sorting->GetAppLaunchOrdinal(extension1()->id()); 329 syncer::StringOrdinal extension2_app_launch = 330 app_sorting->GetAppLaunchOrdinal(extension2()->id()); 331 syncer::StringOrdinal extension3_app_launch = 332 app_sorting->GetAppLaunchOrdinal(extension3()->id()); 333 334 // The overlapping extensions should have be adjusted so that they are 335 // sorted by their id. 336 EXPECT_EQ(extension1()->id() < extension2()->id(), 337 extension1_app_launch.LessThan(extension2_app_launch)); 338 EXPECT_EQ(extension1()->id() < extension3()->id(), 339 extension1_app_launch.LessThan(extension3_app_launch)); 340 EXPECT_EQ(extension2()->id() < extension3()->id(), 341 extension2_app_launch.LessThan(extension3_app_launch)); 342 343 // The page ordinal should be unchanged. 344 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension1()->id()).Equals( 345 repeated_ordinal_)); 346 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension2()->id()).Equals( 347 repeated_ordinal_)); 348 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension3()->id()).Equals( 349 repeated_ordinal_)); 350 } 351 352 private: 353 syncer::StringOrdinal repeated_ordinal_; 354 }; 355 TEST_F(ChromeAppSortingFixNTPCollisionsAllCollide, 356 ChromeAppSortingFixNTPCollisionsAllCollide) {} 357 358 class ChromeAppSortingFixNTPCollisionsSomeCollideAtStart 359 : public PrefsPrepopulatedTestBase { 360 public: 361 ChromeAppSortingFixNTPCollisionsSomeCollideAtStart() {} 362 virtual ~ChromeAppSortingFixNTPCollisionsSomeCollideAtStart() {} 363 364 virtual void Initialize() OVERRIDE { 365 first_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal(); 366 syncer::StringOrdinal second_ordinal = first_ordinal_.CreateAfter(); 367 368 AppSorting* app_sorting = prefs()->app_sorting(); 369 370 // Have the first two extension in the same position, with a third 371 // (non-colliding) extension after. 372 373 app_sorting->SetAppLaunchOrdinal(extension1()->id(), first_ordinal_); 374 app_sorting->SetPageOrdinal(extension1()->id(), first_ordinal_); 375 376 app_sorting->SetAppLaunchOrdinal(extension2()->id(), first_ordinal_); 377 app_sorting->SetPageOrdinal(extension2()->id(), first_ordinal_); 378 379 app_sorting->SetAppLaunchOrdinal(extension3()->id(), second_ordinal); 380 app_sorting->SetPageOrdinal(extension3()->id(), first_ordinal_); 381 382 app_sorting->FixNTPOrdinalCollisions(); 383 } 384 virtual void Verify() OVERRIDE { 385 AppSorting* app_sorting = prefs()->app_sorting(); 386 syncer::StringOrdinal extension1_app_launch = 387 app_sorting->GetAppLaunchOrdinal(extension1()->id()); 388 syncer::StringOrdinal extension2_app_launch = 389 app_sorting->GetAppLaunchOrdinal(extension2()->id()); 390 syncer::StringOrdinal extension3_app_launch = 391 app_sorting->GetAppLaunchOrdinal(extension3()->id()); 392 393 // The overlapping extensions should have be adjusted so that they are 394 // sorted by their id, but they both should be before ext3, which wasn't 395 // overlapping. 396 EXPECT_EQ(extension1()->id() < extension2()->id(), 397 extension1_app_launch.LessThan(extension2_app_launch)); 398 EXPECT_TRUE(extension1_app_launch.LessThan(extension3_app_launch)); 399 EXPECT_TRUE(extension2_app_launch.LessThan(extension3_app_launch)); 400 401 // The page ordinal should be unchanged. 402 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension1()->id()).Equals( 403 first_ordinal_)); 404 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension2()->id()).Equals( 405 first_ordinal_)); 406 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension3()->id()).Equals( 407 first_ordinal_)); 408 } 409 410 private: 411 syncer::StringOrdinal first_ordinal_; 412 }; 413 TEST_F(ChromeAppSortingFixNTPCollisionsSomeCollideAtStart, 414 ChromeAppSortingFixNTPCollisionsSomeCollideAtStart) {} 415 416 class ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd 417 : public PrefsPrepopulatedTestBase { 418 public: 419 ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd() {} 420 virtual ~ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd() {} 421 422 virtual void Initialize() OVERRIDE { 423 first_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal(); 424 syncer::StringOrdinal second_ordinal = first_ordinal_.CreateAfter(); 425 426 AppSorting* app_sorting = prefs()->app_sorting(); 427 428 // Have the first extension in a non-colliding position, followed by two 429 // two extension in the same position. 430 431 app_sorting->SetAppLaunchOrdinal(extension1()->id(), first_ordinal_); 432 app_sorting->SetPageOrdinal(extension1()->id(), first_ordinal_); 433 434 app_sorting->SetAppLaunchOrdinal(extension2()->id(), second_ordinal); 435 app_sorting->SetPageOrdinal(extension2()->id(), first_ordinal_); 436 437 app_sorting->SetAppLaunchOrdinal(extension3()->id(), second_ordinal); 438 app_sorting->SetPageOrdinal(extension3()->id(), first_ordinal_); 439 440 app_sorting->FixNTPOrdinalCollisions(); 441 } 442 virtual void Verify() OVERRIDE { 443 AppSorting* app_sorting = prefs()->app_sorting(); 444 syncer::StringOrdinal extension1_app_launch = 445 app_sorting->GetAppLaunchOrdinal(extension1()->id()); 446 syncer::StringOrdinal extension2_app_launch = 447 app_sorting->GetAppLaunchOrdinal(extension2()->id()); 448 syncer::StringOrdinal extension3_app_launch = 449 app_sorting->GetAppLaunchOrdinal(extension3()->id()); 450 451 // The overlapping extensions should have be adjusted so that they are 452 // sorted by their id, but they both should be after ext1, which wasn't 453 // overlapping. 454 EXPECT_TRUE(extension1_app_launch.LessThan(extension2_app_launch)); 455 EXPECT_TRUE(extension1_app_launch.LessThan(extension3_app_launch)); 456 EXPECT_EQ(extension2()->id() < extension3()->id(), 457 extension2_app_launch.LessThan(extension3_app_launch)); 458 459 // The page ordinal should be unchanged. 460 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension1()->id()).Equals( 461 first_ordinal_)); 462 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension2()->id()).Equals( 463 first_ordinal_)); 464 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension3()->id()).Equals( 465 first_ordinal_)); 466 } 467 468 private: 469 syncer::StringOrdinal first_ordinal_; 470 }; 471 TEST_F(ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd, 472 ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd) {} 473 474 class ChromeAppSortingFixNTPCollisionsTwoCollisions 475 : public PrefsPrepopulatedTestBase { 476 public: 477 ChromeAppSortingFixNTPCollisionsTwoCollisions() {} 478 virtual ~ChromeAppSortingFixNTPCollisionsTwoCollisions() {} 479 480 virtual void Initialize() OVERRIDE { 481 first_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal(); 482 syncer::StringOrdinal second_ordinal = first_ordinal_.CreateAfter(); 483 484 AppSorting* app_sorting = prefs()->app_sorting(); 485 486 // Have two extensions colliding, followed by two more colliding extensions. 487 app_sorting->SetAppLaunchOrdinal(extension1()->id(), first_ordinal_); 488 app_sorting->SetPageOrdinal(extension1()->id(), first_ordinal_); 489 490 app_sorting->SetAppLaunchOrdinal(extension2()->id(), first_ordinal_); 491 app_sorting->SetPageOrdinal(extension2()->id(), first_ordinal_); 492 493 app_sorting->SetAppLaunchOrdinal(extension3()->id(), second_ordinal); 494 app_sorting->SetPageOrdinal(extension3()->id(), first_ordinal_); 495 496 app_sorting->SetAppLaunchOrdinal(extension4()->id(), second_ordinal); 497 app_sorting->SetPageOrdinal(extension4()->id(), first_ordinal_); 498 499 app_sorting->FixNTPOrdinalCollisions(); 500 } 501 virtual void Verify() OVERRIDE { 502 AppSorting* app_sorting = prefs()->app_sorting(); 503 syncer::StringOrdinal extension1_app_launch = 504 app_sorting->GetAppLaunchOrdinal(extension1()->id()); 505 syncer::StringOrdinal extension2_app_launch = 506 app_sorting->GetAppLaunchOrdinal(extension2()->id()); 507 syncer::StringOrdinal extension3_app_launch = 508 app_sorting->GetAppLaunchOrdinal(extension3()->id()); 509 syncer::StringOrdinal extension4_app_launch = 510 app_sorting->GetAppLaunchOrdinal(extension4()->id()); 511 512 // The overlapping extensions should have be adjusted so that they are 513 // sorted by their id, with |ext1| and |ext2| appearing before |ext3| and 514 // |ext4|. 515 EXPECT_TRUE(extension1_app_launch.LessThan(extension3_app_launch)); 516 EXPECT_TRUE(extension1_app_launch.LessThan(extension4_app_launch)); 517 EXPECT_TRUE(extension2_app_launch.LessThan(extension3_app_launch)); 518 EXPECT_TRUE(extension2_app_launch.LessThan(extension4_app_launch)); 519 520 EXPECT_EQ(extension1()->id() < extension2()->id(), 521 extension1_app_launch.LessThan(extension2_app_launch)); 522 EXPECT_EQ(extension3()->id() < extension4()->id(), 523 extension3_app_launch.LessThan(extension4_app_launch)); 524 525 // The page ordinal should be unchanged. 526 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension1()->id()).Equals( 527 first_ordinal_)); 528 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension2()->id()).Equals( 529 first_ordinal_)); 530 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension3()->id()).Equals( 531 first_ordinal_)); 532 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension4()->id()).Equals( 533 first_ordinal_)); 534 } 535 536 private: 537 syncer::StringOrdinal first_ordinal_; 538 }; 539 TEST_F(ChromeAppSortingFixNTPCollisionsTwoCollisions, 540 ChromeAppSortingFixNTPCollisionsTwoCollisions) {} 541 542 class ChromeAppSortingEnsureValidOrdinals 543 : public PrefsPrepopulatedTestBase { 544 public : 545 ChromeAppSortingEnsureValidOrdinals() {} 546 virtual ~ChromeAppSortingEnsureValidOrdinals() {} 547 548 virtual void Initialize() OVERRIDE {} 549 virtual void Verify() OVERRIDE { 550 AppSorting* app_sorting = prefs()->app_sorting(); 551 552 // Give ext1 invalid ordinals and then check that EnsureValidOrdinals fixes 553 // them. 554 app_sorting->SetAppLaunchOrdinal(extension1()->id(), 555 syncer::StringOrdinal()); 556 app_sorting->SetPageOrdinal(extension1()->id(), syncer::StringOrdinal()); 557 558 app_sorting->EnsureValidOrdinals(extension1()->id(), 559 syncer::StringOrdinal()); 560 561 EXPECT_TRUE(app_sorting->GetAppLaunchOrdinal(extension1()->id()).IsValid()); 562 EXPECT_TRUE(app_sorting->GetPageOrdinal(extension1()->id()).IsValid()); 563 } 564 }; 565 TEST_F(ChromeAppSortingEnsureValidOrdinals, 566 ChromeAppSortingEnsureValidOrdinals) {} 567 568 class ChromeAppSortingPageOrdinalMapping : public PrefsPrepopulatedTestBase { 569 public: 570 ChromeAppSortingPageOrdinalMapping() {} 571 virtual ~ChromeAppSortingPageOrdinalMapping() {} 572 573 virtual void Initialize() OVERRIDE {} 574 virtual void Verify() OVERRIDE { 575 std::string ext_1 = "ext_1"; 576 std::string ext_2 = "ext_2"; 577 578 ChromeAppSorting* app_sorting = 579 static_cast<ChromeAppSorting*>(prefs()->app_sorting()); 580 syncer::StringOrdinal first_ordinal = 581 syncer::StringOrdinal::CreateInitialOrdinal(); 582 583 // Ensure attempting to removing a mapping with an invalid page doesn't 584 // modify the map. 585 EXPECT_TRUE(app_sorting->ntp_ordinal_map_.empty()); 586 app_sorting->RemoveOrdinalMapping( 587 ext_1, first_ordinal, first_ordinal); 588 EXPECT_TRUE(app_sorting->ntp_ordinal_map_.empty()); 589 590 // Add new mappings. 591 app_sorting->AddOrdinalMapping(ext_1, first_ordinal, first_ordinal); 592 app_sorting->AddOrdinalMapping(ext_2, first_ordinal, first_ordinal); 593 594 EXPECT_EQ(1U, app_sorting->ntp_ordinal_map_.size()); 595 EXPECT_EQ(2U, app_sorting->ntp_ordinal_map_[first_ordinal].size()); 596 597 ChromeAppSorting::AppLaunchOrdinalMap::iterator it = 598 app_sorting->ntp_ordinal_map_[first_ordinal].find(first_ordinal); 599 EXPECT_EQ(ext_1, it->second); 600 ++it; 601 EXPECT_EQ(ext_2, it->second); 602 603 app_sorting->RemoveOrdinalMapping(ext_1, first_ordinal, first_ordinal); 604 EXPECT_EQ(1U, app_sorting->ntp_ordinal_map_.size()); 605 EXPECT_EQ(1U, app_sorting->ntp_ordinal_map_[first_ordinal].size()); 606 607 it = app_sorting->ntp_ordinal_map_[first_ordinal].find(first_ordinal); 608 EXPECT_EQ(ext_2, it->second); 609 610 // Ensure that attempting to remove an extension with a valid page and app 611 // launch ordinals, but a unused id has no effect. 612 app_sorting->RemoveOrdinalMapping( 613 "invalid_ext", first_ordinal, first_ordinal); 614 EXPECT_EQ(1U, app_sorting->ntp_ordinal_map_.size()); 615 EXPECT_EQ(1U, app_sorting->ntp_ordinal_map_[first_ordinal].size()); 616 617 it = app_sorting->ntp_ordinal_map_[first_ordinal].find(first_ordinal); 618 EXPECT_EQ(ext_2, it->second); 619 } 620 }; 621 TEST_F(ChromeAppSortingPageOrdinalMapping, 622 ChromeAppSortingPageOrdinalMapping) {} 623 624 class ChromeAppSortingPreinstalledAppsBase : public PrefsPrepopulatedTestBase { 625 public: 626 ChromeAppSortingPreinstalledAppsBase() { 627 base::DictionaryValue simple_dict; 628 simple_dict.SetString(keys::kVersion, "1.0.0.0"); 629 simple_dict.SetString(keys::kName, "unused"); 630 simple_dict.SetString(keys::kApp, "true"); 631 simple_dict.SetString(keys::kLaunchLocalPath, "fake.html"); 632 633 std::string error; 634 app1_scoped_ = Extension::Create( 635 prefs_.temp_dir().AppendASCII("app1_"), Manifest::EXTERNAL_PREF, 636 simple_dict, Extension::NO_FLAGS, &error); 637 prefs()->OnExtensionInstalled(app1_scoped_.get(), 638 Extension::ENABLED, 639 syncer::StringOrdinal(), 640 std::string()); 641 642 app2_scoped_ = Extension::Create( 643 prefs_.temp_dir().AppendASCII("app2_"), Manifest::EXTERNAL_PREF, 644 simple_dict, Extension::NO_FLAGS, &error); 645 prefs()->OnExtensionInstalled(app2_scoped_.get(), 646 Extension::ENABLED, 647 syncer::StringOrdinal(), 648 std::string()); 649 650 app1_ = app1_scoped_.get(); 651 app2_ = app2_scoped_.get(); 652 } 653 virtual ~ChromeAppSortingPreinstalledAppsBase() {} 654 655 protected: 656 // Weak references, for convenience. 657 Extension* app1_; 658 Extension* app2_; 659 660 private: 661 scoped_refptr<Extension> app1_scoped_; 662 scoped_refptr<Extension> app2_scoped_; 663 }; 664 665 class ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage 666 : public ChromeAppSortingPreinstalledAppsBase { 667 public: 668 ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage() {} 669 virtual ~ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage() {} 670 671 virtual void Initialize() OVERRIDE {} 672 virtual void Verify() OVERRIDE { 673 syncer::StringOrdinal page = syncer::StringOrdinal::CreateInitialOrdinal(); 674 ChromeAppSorting* app_sorting = 675 static_cast<ChromeAppSorting*>(prefs()->app_sorting()); 676 677 syncer::StringOrdinal min = 678 app_sorting->GetMinOrMaxAppLaunchOrdinalsOnPage( 679 page, 680 ChromeAppSorting::MIN_ORDINAL); 681 syncer::StringOrdinal max = 682 app_sorting->GetMinOrMaxAppLaunchOrdinalsOnPage( 683 page, 684 ChromeAppSorting::MAX_ORDINAL); 685 EXPECT_TRUE(min.IsValid()); 686 EXPECT_TRUE(max.IsValid()); 687 EXPECT_TRUE(min.LessThan(max)); 688 689 // Ensure that the min and max values aren't set for empty pages. 690 min = syncer::StringOrdinal(); 691 max = syncer::StringOrdinal(); 692 syncer::StringOrdinal empty_page = page.CreateAfter(); 693 EXPECT_FALSE(min.IsValid()); 694 EXPECT_FALSE(max.IsValid()); 695 min = app_sorting->GetMinOrMaxAppLaunchOrdinalsOnPage( 696 empty_page, 697 ChromeAppSorting::MIN_ORDINAL); 698 max = app_sorting->GetMinOrMaxAppLaunchOrdinalsOnPage( 699 empty_page, 700 ChromeAppSorting::MAX_ORDINAL); 701 EXPECT_FALSE(min.IsValid()); 702 EXPECT_FALSE(max.IsValid()); 703 } 704 }; 705 TEST_F(ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage, 706 ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage) {} 707 708 // Make sure that empty pages aren't removed from the integer to ordinal 709 // mapping. See http://crbug.com/109802 for details. 710 class ChromeAppSortingKeepEmptyStringOrdinalPages 711 : public ChromeAppSortingPreinstalledAppsBase { 712 public: 713 ChromeAppSortingKeepEmptyStringOrdinalPages() {} 714 virtual ~ChromeAppSortingKeepEmptyStringOrdinalPages() {} 715 716 virtual void Initialize() OVERRIDE { 717 AppSorting* app_sorting = prefs()->app_sorting(); 718 719 syncer::StringOrdinal first_page = 720 syncer::StringOrdinal::CreateInitialOrdinal(); 721 app_sorting->SetPageOrdinal(app1_->id(), first_page); 722 EXPECT_EQ(0, app_sorting->PageStringOrdinalAsInteger(first_page)); 723 724 last_page_ = first_page.CreateAfter(); 725 app_sorting->SetPageOrdinal(app2_->id(), last_page_); 726 EXPECT_EQ(1, app_sorting->PageStringOrdinalAsInteger(last_page_)); 727 728 // Move the second app to create an empty page. 729 app_sorting->SetPageOrdinal(app2_->id(), first_page); 730 EXPECT_EQ(0, app_sorting->PageStringOrdinalAsInteger(first_page)); 731 } 732 virtual void Verify() OVERRIDE { 733 AppSorting* app_sorting = prefs()->app_sorting(); 734 735 // Move the second app to a new empty page at the end, skipping over 736 // the current empty page. 737 last_page_ = last_page_.CreateAfter(); 738 app_sorting->SetPageOrdinal(app2_->id(), last_page_); 739 EXPECT_EQ(2, app_sorting->PageStringOrdinalAsInteger(last_page_)); 740 EXPECT_TRUE(last_page_.Equals(app_sorting->PageIntegerAsStringOrdinal(2))); 741 } 742 743 private: 744 syncer::StringOrdinal last_page_; 745 }; 746 TEST_F(ChromeAppSortingKeepEmptyStringOrdinalPages, 747 ChromeAppSortingKeepEmptyStringOrdinalPages) {} 748 749 class ChromeAppSortingMakesFillerOrdinals 750 : public ChromeAppSortingPreinstalledAppsBase { 751 public: 752 ChromeAppSortingMakesFillerOrdinals() {} 753 virtual ~ChromeAppSortingMakesFillerOrdinals() {} 754 755 virtual void Initialize() OVERRIDE { 756 AppSorting* app_sorting = prefs()->app_sorting(); 757 758 syncer::StringOrdinal first_page = 759 syncer::StringOrdinal::CreateInitialOrdinal(); 760 app_sorting->SetPageOrdinal(app1_->id(), first_page); 761 EXPECT_EQ(0, app_sorting->PageStringOrdinalAsInteger(first_page)); 762 } 763 virtual void Verify() OVERRIDE { 764 AppSorting* app_sorting = prefs()->app_sorting(); 765 766 // Because the UI can add an unlimited number of empty pages without an app 767 // on them, this test simulates dropping of an app on the 1st and 4th empty 768 // pages (3rd and 6th pages by index) to ensure we don't crash and that 769 // filler ordinals are created as needed. See: http://crbug.com/122214 770 syncer::StringOrdinal page_three = 771 app_sorting->PageIntegerAsStringOrdinal(2); 772 app_sorting->SetPageOrdinal(app1_->id(), page_three); 773 EXPECT_EQ(2, app_sorting->PageStringOrdinalAsInteger(page_three)); 774 775 syncer::StringOrdinal page_six = app_sorting->PageIntegerAsStringOrdinal(5); 776 app_sorting->SetPageOrdinal(app1_->id(), page_six); 777 EXPECT_EQ(5, app_sorting->PageStringOrdinalAsInteger(page_six)); 778 } 779 }; 780 TEST_F(ChromeAppSortingMakesFillerOrdinals, 781 ChromeAppSortingMakesFillerOrdinals) {} 782 783 class ChromeAppSortingDefaultOrdinalsBase : public ChromeAppSortingTest { 784 public: 785 ChromeAppSortingDefaultOrdinalsBase() {} 786 virtual ~ChromeAppSortingDefaultOrdinalsBase() {} 787 788 virtual void Initialize() OVERRIDE { 789 app_ = CreateApp("app"); 790 791 InitDefaultOrdinals(); 792 ChromeAppSorting* app_sorting = 793 static_cast<ChromeAppSorting*>(prefs()->app_sorting()); 794 ChromeAppSorting::AppOrdinalsMap& sorting_defaults = 795 app_sorting->default_ordinals_; 796 sorting_defaults[app_->id()].page_ordinal = default_page_ordinal_; 797 sorting_defaults[app_->id()].app_launch_ordinal = 798 default_app_launch_ordinal_; 799 800 SetupUserOrdinals(); 801 InstallApps(); 802 } 803 804 protected: 805 scoped_refptr<Extension> CreateApp(const std::string& name) { 806 base::DictionaryValue simple_dict; 807 simple_dict.SetString(keys::kVersion, "1.0.0.0"); 808 simple_dict.SetString(keys::kName, name); 809 simple_dict.SetString(keys::kApp, "true"); 810 simple_dict.SetString(keys::kLaunchLocalPath, "fake.html"); 811 812 std::string errors; 813 scoped_refptr<Extension> app = Extension::Create( 814 prefs_.temp_dir().AppendASCII(name), Manifest::EXTERNAL_PREF, 815 simple_dict, Extension::NO_FLAGS, &errors); 816 EXPECT_TRUE(app.get()) << errors; 817 EXPECT_TRUE(crx_file::id_util::IdIsValid(app->id())); 818 return app; 819 } 820 821 void InitDefaultOrdinals() { 822 default_page_ordinal_ = 823 syncer::StringOrdinal::CreateInitialOrdinal().CreateAfter(); 824 default_app_launch_ordinal_ = 825 syncer::StringOrdinal::CreateInitialOrdinal().CreateBefore(); 826 } 827 828 virtual void SetupUserOrdinals() {} 829 830 virtual void InstallApps() { 831 prefs()->OnExtensionInstalled(app_.get(), 832 Extension::ENABLED, 833 syncer::StringOrdinal(), 834 std::string()); 835 } 836 837 scoped_refptr<Extension> app_; 838 syncer::StringOrdinal default_page_ordinal_; 839 syncer::StringOrdinal default_app_launch_ordinal_; 840 }; 841 842 // Tests that the app gets its default ordinals. 843 class ChromeAppSortingDefaultOrdinals 844 : public ChromeAppSortingDefaultOrdinalsBase { 845 public: 846 ChromeAppSortingDefaultOrdinals() {} 847 virtual ~ChromeAppSortingDefaultOrdinals() {} 848 849 virtual void Verify() OVERRIDE { 850 AppSorting* app_sorting = prefs()->app_sorting(); 851 EXPECT_TRUE(app_sorting->GetPageOrdinal(app_->id()).Equals( 852 default_page_ordinal_)); 853 EXPECT_TRUE(app_sorting->GetAppLaunchOrdinal(app_->id()).Equals( 854 default_app_launch_ordinal_)); 855 } 856 }; 857 TEST_F(ChromeAppSortingDefaultOrdinals, 858 ChromeAppSortingDefaultOrdinals) {} 859 860 // Tests that the default page ordinal is overridden by install page ordinal. 861 class ChromeAppSortingDefaultOrdinalOverriddenByInstallPage 862 : public ChromeAppSortingDefaultOrdinalsBase { 863 public: 864 ChromeAppSortingDefaultOrdinalOverriddenByInstallPage() {} 865 virtual ~ChromeAppSortingDefaultOrdinalOverriddenByInstallPage() {} 866 867 virtual void Verify() OVERRIDE { 868 AppSorting* app_sorting = prefs()->app_sorting(); 869 870 EXPECT_FALSE(app_sorting->GetPageOrdinal(app_->id()).Equals( 871 default_page_ordinal_)); 872 EXPECT_TRUE(app_sorting->GetPageOrdinal(app_->id()).Equals(install_page_)); 873 } 874 875 protected: 876 virtual void InstallApps() OVERRIDE { 877 install_page_ = default_page_ordinal_.CreateAfter(); 878 prefs()->OnExtensionInstalled(app_.get(), 879 Extension::ENABLED, 880 install_page_, 881 std::string()); 882 } 883 884 private: 885 syncer::StringOrdinal install_page_; 886 }; 887 TEST_F(ChromeAppSortingDefaultOrdinalOverriddenByInstallPage, 888 ChromeAppSortingDefaultOrdinalOverriddenByInstallPage) {} 889 890 // Tests that the default ordinals are overridden by user values. 891 class ChromeAppSortingDefaultOrdinalOverriddenByUserValue 892 : public ChromeAppSortingDefaultOrdinalsBase { 893 public: 894 ChromeAppSortingDefaultOrdinalOverriddenByUserValue() {} 895 virtual ~ChromeAppSortingDefaultOrdinalOverriddenByUserValue() {} 896 897 virtual void Verify() OVERRIDE { 898 AppSorting* app_sorting = prefs()->app_sorting(); 899 900 EXPECT_TRUE(app_sorting->GetPageOrdinal(app_->id()).Equals( 901 user_page_ordinal_)); 902 EXPECT_TRUE(app_sorting->GetAppLaunchOrdinal(app_->id()).Equals( 903 user_app_launch_ordinal_)); 904 } 905 906 protected: 907 virtual void SetupUserOrdinals() OVERRIDE { 908 user_page_ordinal_ = default_page_ordinal_.CreateAfter(); 909 user_app_launch_ordinal_ = default_app_launch_ordinal_.CreateBefore(); 910 911 AppSorting* app_sorting = prefs()->app_sorting(); 912 app_sorting->SetPageOrdinal(app_->id(), user_page_ordinal_); 913 app_sorting->SetAppLaunchOrdinal(app_->id(), user_app_launch_ordinal_); 914 } 915 916 private: 917 syncer::StringOrdinal user_page_ordinal_; 918 syncer::StringOrdinal user_app_launch_ordinal_; 919 }; 920 TEST_F(ChromeAppSortingDefaultOrdinalOverriddenByUserValue, 921 ChromeAppSortingDefaultOrdinalOverriddenByUserValue) {} 922 923 // Tests that the default app launch ordinal is changed to avoid collision. 924 class ChromeAppSortingDefaultOrdinalNoCollision 925 : public ChromeAppSortingDefaultOrdinalsBase { 926 public: 927 ChromeAppSortingDefaultOrdinalNoCollision() {} 928 virtual ~ChromeAppSortingDefaultOrdinalNoCollision() {} 929 930 virtual void Verify() OVERRIDE { 931 AppSorting* app_sorting = prefs()->app_sorting(); 932 933 // Use the default page. 934 EXPECT_TRUE(app_sorting->GetPageOrdinal(app_->id()).Equals( 935 default_page_ordinal_)); 936 // Not using the default app launch ordinal because of the collision. 937 EXPECT_FALSE(app_sorting->GetAppLaunchOrdinal(app_->id()).Equals( 938 default_app_launch_ordinal_)); 939 } 940 941 protected: 942 virtual void SetupUserOrdinals() OVERRIDE { 943 other_app_ = prefs_.AddApp("other_app"); 944 // Creates a collision. 945 AppSorting* app_sorting = prefs()->app_sorting(); 946 app_sorting->SetPageOrdinal(other_app_->id(), default_page_ordinal_); 947 app_sorting->SetAppLaunchOrdinal(other_app_->id(), 948 default_app_launch_ordinal_); 949 950 yet_another_app_ = prefs_.AddApp("yet_aother_app"); 951 app_sorting->SetPageOrdinal(yet_another_app_->id(), default_page_ordinal_); 952 app_sorting->SetAppLaunchOrdinal(yet_another_app_->id(), 953 default_app_launch_ordinal_); 954 } 955 956 private: 957 scoped_refptr<Extension> other_app_; 958 scoped_refptr<Extension> yet_another_app_; 959 }; 960 TEST_F(ChromeAppSortingDefaultOrdinalNoCollision, 961 ChromeAppSortingDefaultOrdinalNoCollision) {} 962 963 // Tests that SetExtensionVisible() correctly hides and unhides extensions. 964 class ChromeAppSortingSetExtensionVisible : public ChromeAppSortingTest { 965 public: 966 ChromeAppSortingSetExtensionVisible() {} 967 virtual ~ChromeAppSortingSetExtensionVisible() {} 968 969 virtual void Initialize() OVERRIDE { 970 first_app_ = prefs_.AddApp("first_app"); 971 second_app_ = prefs_.AddApp("second_app"); 972 } 973 974 virtual void Verify() OVERRIDE { 975 ChromeAppSorting* sorting = app_sorting(); 976 syncer::StringOrdinal page1 = sorting->GetPageOrdinal(first_app_->id()); 977 syncer::StringOrdinal page2 = sorting->GetPageOrdinal(second_app_->id()); 978 EXPECT_TRUE(sorting->GetAppLaunchOrdinal(first_app_->id()).IsValid()); 979 EXPECT_TRUE(sorting->GetAppLaunchOrdinal(second_app_->id()).IsValid()); 980 EXPECT_TRUE(page1.IsValid()); 981 EXPECT_TRUE(page2.IsValid()); 982 EXPECT_TRUE(page1.Equals(page2)); 983 984 sorting->SetExtensionVisible(first_app_->id(), false); 985 EXPECT_EQ( 986 1U, sorting->CountItemsVisibleOnNtp(sorting->ntp_ordinal_map_[page1])); 987 988 sorting->SetExtensionVisible(first_app_->id(), true); 989 EXPECT_EQ( 990 2U, sorting->CountItemsVisibleOnNtp(sorting->ntp_ordinal_map_[page1])); 991 } 992 993 private: 994 scoped_refptr<Extension> first_app_; 995 scoped_refptr<Extension> second_app_; 996 }; 997 TEST_F(ChromeAppSortingSetExtensionVisible, 998 ChromeAppSortingSetExtensionVisible) { 999 } 1000 1001 } // namespace extensions 1002