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