// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/chrome_app_sorting.h" #include "chrome/browser/extensions/extension_prefs_unittest.h" #include "components/crx_file/id_util.h" #include "extensions/common/constants.h" #include "extensions/common/manifest_constants.h" #include "sync/api/string_ordinal.h" #include "testing/gtest/include/gtest/gtest.h" namespace extensions { namespace keys = manifest_keys; class ChromeAppSortingAppLocation : public ExtensionPrefsTest { public: void Initialize() override { extension_ = prefs_.AddExtension("not_an_app"); // Non-apps should not have any app launch ordinal or page ordinal. prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED, syncer::StringOrdinal(), std::string()); } void Verify() override { EXPECT_FALSE( app_sorting()->GetAppLaunchOrdinal(extension_->id()).IsValid()); EXPECT_FALSE(app_sorting()->GetPageOrdinal(extension_->id()).IsValid()); } private: scoped_refptr extension_; }; TEST_F(ChromeAppSortingAppLocation, ChromeAppSortingAppLocation) {} class ChromeAppSortingAppLaunchOrdinal : public ExtensionPrefsTest { public: void Initialize() override { // No extensions yet. syncer::StringOrdinal page = syncer::StringOrdinal::CreateInitialOrdinal(); EXPECT_TRUE(syncer::StringOrdinal::CreateInitialOrdinal().Equals( app_sorting()->CreateNextAppLaunchOrdinal(page))); extension_ = prefs_.AddApp("on_extension_installed"); EXPECT_FALSE(prefs()->IsExtensionDisabled(extension_->id())); prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED, syncer::StringOrdinal(), std::string()); } void Verify() override { syncer::StringOrdinal launch_ordinal = app_sorting()->GetAppLaunchOrdinal(extension_->id()); syncer::StringOrdinal page_ordinal = syncer::StringOrdinal::CreateInitialOrdinal(); // Extension should have been assigned a valid StringOrdinal. EXPECT_TRUE(launch_ordinal.IsValid()); EXPECT_TRUE(launch_ordinal.LessThan( app_sorting()->CreateNextAppLaunchOrdinal(page_ordinal))); // Set a new launch ordinal of and verify it comes after. app_sorting()->SetAppLaunchOrdinal( extension_->id(), app_sorting()->CreateNextAppLaunchOrdinal(page_ordinal)); syncer::StringOrdinal new_launch_ordinal = app_sorting()->GetAppLaunchOrdinal(extension_->id()); EXPECT_TRUE(launch_ordinal.LessThan(new_launch_ordinal)); // This extension doesn't exist, so it should return an invalid // StringOrdinal. syncer::StringOrdinal invalid_app_launch_ordinal = app_sorting()->GetAppLaunchOrdinal("foo"); EXPECT_FALSE(invalid_app_launch_ordinal.IsValid()); EXPECT_EQ(-1, app_sorting()->PageStringOrdinalAsInteger( invalid_app_launch_ordinal)); // The second page doesn't have any apps so its next launch ordinal should // be the first launch ordinal. syncer::StringOrdinal next_page = page_ordinal.CreateAfter(); syncer::StringOrdinal next_page_app_launch_ordinal = app_sorting()->CreateNextAppLaunchOrdinal(next_page); EXPECT_TRUE(next_page_app_launch_ordinal.Equals( app_sorting()->CreateFirstAppLaunchOrdinal(next_page))); } private: scoped_refptr extension_; }; TEST_F(ChromeAppSortingAppLaunchOrdinal, ChromeAppSortingAppLaunchOrdinal) {} class ChromeAppSortingPageOrdinal : public ExtensionPrefsTest { public: void Initialize() override { extension_ = prefs_.AddApp("page_ordinal"); // Install with a page preference. first_page_ = syncer::StringOrdinal::CreateInitialOrdinal(); prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED, first_page_, std::string()); EXPECT_TRUE(first_page_.Equals( app_sorting()->GetPageOrdinal(extension_->id()))); EXPECT_EQ(0, app_sorting()->PageStringOrdinalAsInteger(first_page_)); scoped_refptr extension2 = prefs_.AddApp("page_ordinal_2"); // Install without any page preference. prefs()->OnExtensionInstalled(extension2.get(), Extension::ENABLED, syncer::StringOrdinal(), std::string()); EXPECT_TRUE(first_page_.Equals( app_sorting()->GetPageOrdinal(extension2->id()))); } void Verify() override { // Set the page ordinal. syncer::StringOrdinal new_page = first_page_.CreateAfter(); app_sorting()->SetPageOrdinal(extension_->id(), new_page); // Verify the page ordinal. EXPECT_TRUE( new_page.Equals(app_sorting()->GetPageOrdinal(extension_->id()))); EXPECT_EQ(1, app_sorting()->PageStringOrdinalAsInteger(new_page)); // This extension doesn't exist, so it should return an invalid // StringOrdinal. EXPECT_FALSE(app_sorting()->GetPageOrdinal("foo").IsValid()); } private: syncer::StringOrdinal first_page_; scoped_refptr extension_; }; TEST_F(ChromeAppSortingPageOrdinal, ChromeAppSortingPageOrdinal) {} // Ensure that ChromeAppSorting is able to properly initialize off a set // of old page and app launch indices and properly convert them. class ChromeAppSortingInitialize : public PrefsPrepopulatedTestBase { public: ChromeAppSortingInitialize() {} ~ChromeAppSortingInitialize() override {} void Initialize() override { // A preference determining the order of which the apps appear on the NTP. const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index"; // A preference determining the page on which an app appears in the NTP. const char kPrefPageIndexDeprecated[] = "page_index"; // Setup the deprecated preferences. ExtensionScopedPrefs* scoped_prefs = static_cast(prefs()); scoped_prefs->UpdateExtensionPref(extension1()->id(), kPrefAppLaunchIndexDeprecated, new base::FundamentalValue(0)); scoped_prefs->UpdateExtensionPref(extension1()->id(), kPrefPageIndexDeprecated, new base::FundamentalValue(0)); scoped_prefs->UpdateExtensionPref(extension2()->id(), kPrefAppLaunchIndexDeprecated, new base::FundamentalValue(1)); scoped_prefs->UpdateExtensionPref(extension2()->id(), kPrefPageIndexDeprecated, new base::FundamentalValue(0)); scoped_prefs->UpdateExtensionPref(extension3()->id(), kPrefAppLaunchIndexDeprecated, new base::FundamentalValue(0)); scoped_prefs->UpdateExtensionPref(extension3()->id(), kPrefPageIndexDeprecated, new base::FundamentalValue(1)); // We insert the ids in reverse order so that we have to deal with the // element on the 2nd page before the 1st page is seen. ExtensionIdList ids; ids.push_back(extension3()->id()); ids.push_back(extension2()->id()); ids.push_back(extension1()->id()); app_sorting()->MigrateAppIndex(ids); } void Verify() override { syncer::StringOrdinal first_ordinal = syncer::StringOrdinal::CreateInitialOrdinal(); EXPECT_TRUE(first_ordinal.Equals( app_sorting()->GetAppLaunchOrdinal(extension1()->id()))); EXPECT_TRUE(first_ordinal.LessThan( app_sorting()->GetAppLaunchOrdinal(extension2()->id()))); EXPECT_TRUE(first_ordinal.Equals( app_sorting()->GetAppLaunchOrdinal(extension3()->id()))); EXPECT_TRUE(first_ordinal.Equals( app_sorting()->GetPageOrdinal(extension1()->id()))); EXPECT_TRUE(first_ordinal.Equals( app_sorting()->GetPageOrdinal(extension2()->id()))); EXPECT_TRUE(first_ordinal.LessThan( app_sorting()->GetPageOrdinal(extension3()->id()))); } }; TEST_F(ChromeAppSortingInitialize, ChromeAppSortingInitialize) {} // Make sure that initialization still works when no extensions are present // (i.e. make sure that the web store icon is still loaded into the map). class ChromeAppSortingInitializeWithNoApps : public PrefsPrepopulatedTestBase { public: ChromeAppSortingInitializeWithNoApps() {} ~ChromeAppSortingInitializeWithNoApps() override {} void Initialize() override { // Make sure that the web store has valid ordinals. syncer::StringOrdinal initial_ordinal = syncer::StringOrdinal::CreateInitialOrdinal(); app_sorting()->SetPageOrdinal(extensions::kWebStoreAppId, initial_ordinal); app_sorting()->SetAppLaunchOrdinal(extensions::kWebStoreAppId, initial_ordinal); } void Verify() override { syncer::StringOrdinal page = app_sorting()->GetPageOrdinal(extensions::kWebStoreAppId); EXPECT_TRUE(page.IsValid()); ChromeAppSorting::PageOrdinalMap::iterator page_it = app_sorting()->ntp_ordinal_map_.find(page); EXPECT_TRUE(page_it != app_sorting()->ntp_ordinal_map_.end()); syncer::StringOrdinal app_launch = app_sorting()->GetPageOrdinal(extensions::kWebStoreAppId); EXPECT_TRUE(app_launch.IsValid()); ChromeAppSorting::AppLaunchOrdinalMap::iterator app_launch_it = page_it->second.find(app_launch); EXPECT_TRUE(app_launch_it != page_it->second.end()); } }; TEST_F(ChromeAppSortingInitializeWithNoApps, ChromeAppSortingInitializeWithNoApps) {} // Tests the application index to ordinal migration code for values that // shouldn't be converted. This should be removed when the migrate code // is taken out. // http://crbug.com/107376 class ChromeAppSortingMigrateAppIndexInvalid : public PrefsPrepopulatedTestBase { public: ChromeAppSortingMigrateAppIndexInvalid() {} ~ChromeAppSortingMigrateAppIndexInvalid() override {} void Initialize() override { // A preference determining the order of which the apps appear on the NTP. const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index"; // A preference determining the page on which an app appears in the NTP. const char kPrefPageIndexDeprecated[] = "page_index"; // Setup the deprecated preference. ExtensionScopedPrefs* scoped_prefs = static_cast(prefs()); scoped_prefs->UpdateExtensionPref(extension1()->id(), kPrefAppLaunchIndexDeprecated, new base::FundamentalValue(0)); scoped_prefs->UpdateExtensionPref(extension1()->id(), kPrefPageIndexDeprecated, new base::FundamentalValue(-1)); } void Verify() override { // Make sure that the invalid page_index wasn't converted over. EXPECT_FALSE(app_sorting()->GetAppLaunchOrdinal( extension1()->id()).IsValid()); } }; TEST_F(ChromeAppSortingMigrateAppIndexInvalid, ChromeAppSortingMigrateAppIndexInvalid) {} class ChromeAppSortingFixNTPCollisionsAllCollide : public PrefsPrepopulatedTestBase { public: ChromeAppSortingFixNTPCollisionsAllCollide() {} ~ChromeAppSortingFixNTPCollisionsAllCollide() override {} void Initialize() override { repeated_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal(); app_sorting()->SetAppLaunchOrdinal(extension1()->id(), repeated_ordinal_); app_sorting()->SetPageOrdinal(extension1()->id(), repeated_ordinal_); app_sorting()->SetAppLaunchOrdinal(extension2()->id(), repeated_ordinal_); app_sorting()->SetPageOrdinal(extension2()->id(), repeated_ordinal_); app_sorting()->SetAppLaunchOrdinal(extension3()->id(), repeated_ordinal_); app_sorting()->SetPageOrdinal(extension3()->id(), repeated_ordinal_); app_sorting()->FixNTPOrdinalCollisions(); } void Verify() override { syncer::StringOrdinal extension1_app_launch = app_sorting()->GetAppLaunchOrdinal(extension1()->id()); syncer::StringOrdinal extension2_app_launch = app_sorting()->GetAppLaunchOrdinal(extension2()->id()); syncer::StringOrdinal extension3_app_launch = app_sorting()->GetAppLaunchOrdinal(extension3()->id()); // The overlapping extensions should have be adjusted so that they are // sorted by their id. EXPECT_EQ(extension1()->id() < extension2()->id(), extension1_app_launch.LessThan(extension2_app_launch)); EXPECT_EQ(extension1()->id() < extension3()->id(), extension1_app_launch.LessThan(extension3_app_launch)); EXPECT_EQ(extension2()->id() < extension3()->id(), extension2_app_launch.LessThan(extension3_app_launch)); // The page ordinal should be unchanged. EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension1()->id()).Equals( repeated_ordinal_)); EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension2()->id()).Equals( repeated_ordinal_)); EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension3()->id()).Equals( repeated_ordinal_)); } private: syncer::StringOrdinal repeated_ordinal_; }; TEST_F(ChromeAppSortingFixNTPCollisionsAllCollide, ChromeAppSortingFixNTPCollisionsAllCollide) {} class ChromeAppSortingFixNTPCollisionsSomeCollideAtStart : public PrefsPrepopulatedTestBase { public: ChromeAppSortingFixNTPCollisionsSomeCollideAtStart() {} ~ChromeAppSortingFixNTPCollisionsSomeCollideAtStart() override {} void Initialize() override { first_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal(); syncer::StringOrdinal second_ordinal = first_ordinal_.CreateAfter(); // Have the first two extension in the same position, with a third // (non-colliding) extension after. app_sorting()->SetAppLaunchOrdinal(extension1()->id(), first_ordinal_); app_sorting()->SetPageOrdinal(extension1()->id(), first_ordinal_); app_sorting()->SetAppLaunchOrdinal(extension2()->id(), first_ordinal_); app_sorting()->SetPageOrdinal(extension2()->id(), first_ordinal_); app_sorting()->SetAppLaunchOrdinal(extension3()->id(), second_ordinal); app_sorting()->SetPageOrdinal(extension3()->id(), first_ordinal_); app_sorting()->FixNTPOrdinalCollisions(); } void Verify() override { syncer::StringOrdinal extension1_app_launch = app_sorting()->GetAppLaunchOrdinal(extension1()->id()); syncer::StringOrdinal extension2_app_launch = app_sorting()->GetAppLaunchOrdinal(extension2()->id()); syncer::StringOrdinal extension3_app_launch = app_sorting()->GetAppLaunchOrdinal(extension3()->id()); // The overlapping extensions should have be adjusted so that they are // sorted by their id, but they both should be before ext3, which wasn't // overlapping. EXPECT_EQ(extension1()->id() < extension2()->id(), extension1_app_launch.LessThan(extension2_app_launch)); EXPECT_TRUE(extension1_app_launch.LessThan(extension3_app_launch)); EXPECT_TRUE(extension2_app_launch.LessThan(extension3_app_launch)); // The page ordinal should be unchanged. EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension1()->id()).Equals( first_ordinal_)); EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension2()->id()).Equals( first_ordinal_)); EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension3()->id()).Equals( first_ordinal_)); } private: syncer::StringOrdinal first_ordinal_; }; TEST_F(ChromeAppSortingFixNTPCollisionsSomeCollideAtStart, ChromeAppSortingFixNTPCollisionsSomeCollideAtStart) {} class ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd : public PrefsPrepopulatedTestBase { public: ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd() {} ~ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd() override {} void Initialize() override { first_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal(); syncer::StringOrdinal second_ordinal = first_ordinal_.CreateAfter(); // Have the first extension in a non-colliding position, followed by two // two extension in the same position. app_sorting()->SetAppLaunchOrdinal(extension1()->id(), first_ordinal_); app_sorting()->SetPageOrdinal(extension1()->id(), first_ordinal_); app_sorting()->SetAppLaunchOrdinal(extension2()->id(), second_ordinal); app_sorting()->SetPageOrdinal(extension2()->id(), first_ordinal_); app_sorting()->SetAppLaunchOrdinal(extension3()->id(), second_ordinal); app_sorting()->SetPageOrdinal(extension3()->id(), first_ordinal_); app_sorting()->FixNTPOrdinalCollisions(); } void Verify() override { syncer::StringOrdinal extension1_app_launch = app_sorting()->GetAppLaunchOrdinal(extension1()->id()); syncer::StringOrdinal extension2_app_launch = app_sorting()->GetAppLaunchOrdinal(extension2()->id()); syncer::StringOrdinal extension3_app_launch = app_sorting()->GetAppLaunchOrdinal(extension3()->id()); // The overlapping extensions should have be adjusted so that they are // sorted by their id, but they both should be after ext1, which wasn't // overlapping. EXPECT_TRUE(extension1_app_launch.LessThan(extension2_app_launch)); EXPECT_TRUE(extension1_app_launch.LessThan(extension3_app_launch)); EXPECT_EQ(extension2()->id() < extension3()->id(), extension2_app_launch.LessThan(extension3_app_launch)); // The page ordinal should be unchanged. EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension1()->id()).Equals( first_ordinal_)); EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension2()->id()).Equals( first_ordinal_)); EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension3()->id()).Equals( first_ordinal_)); } private: syncer::StringOrdinal first_ordinal_; }; TEST_F(ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd, ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd) {} class ChromeAppSortingFixNTPCollisionsTwoCollisions : public PrefsPrepopulatedTestBase { public: ChromeAppSortingFixNTPCollisionsTwoCollisions() {} ~ChromeAppSortingFixNTPCollisionsTwoCollisions() override {} void Initialize() override { first_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal(); syncer::StringOrdinal second_ordinal = first_ordinal_.CreateAfter(); // Have two extensions colliding, followed by two more colliding extensions. app_sorting()->SetAppLaunchOrdinal(extension1()->id(), first_ordinal_); app_sorting()->SetPageOrdinal(extension1()->id(), first_ordinal_); app_sorting()->SetAppLaunchOrdinal(extension2()->id(), first_ordinal_); app_sorting()->SetPageOrdinal(extension2()->id(), first_ordinal_); app_sorting()->SetAppLaunchOrdinal(extension3()->id(), second_ordinal); app_sorting()->SetPageOrdinal(extension3()->id(), first_ordinal_); app_sorting()->SetAppLaunchOrdinal(extension4()->id(), second_ordinal); app_sorting()->SetPageOrdinal(extension4()->id(), first_ordinal_); app_sorting()->FixNTPOrdinalCollisions(); } void Verify() override { syncer::StringOrdinal extension1_app_launch = app_sorting()->GetAppLaunchOrdinal(extension1()->id()); syncer::StringOrdinal extension2_app_launch = app_sorting()->GetAppLaunchOrdinal(extension2()->id()); syncer::StringOrdinal extension3_app_launch = app_sorting()->GetAppLaunchOrdinal(extension3()->id()); syncer::StringOrdinal extension4_app_launch = app_sorting()->GetAppLaunchOrdinal(extension4()->id()); // The overlapping extensions should have be adjusted so that they are // sorted by their id, with |ext1| and |ext2| appearing before |ext3| and // |ext4|. EXPECT_TRUE(extension1_app_launch.LessThan(extension3_app_launch)); EXPECT_TRUE(extension1_app_launch.LessThan(extension4_app_launch)); EXPECT_TRUE(extension2_app_launch.LessThan(extension3_app_launch)); EXPECT_TRUE(extension2_app_launch.LessThan(extension4_app_launch)); EXPECT_EQ(extension1()->id() < extension2()->id(), extension1_app_launch.LessThan(extension2_app_launch)); EXPECT_EQ(extension3()->id() < extension4()->id(), extension3_app_launch.LessThan(extension4_app_launch)); // The page ordinal should be unchanged. EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension1()->id()).Equals( first_ordinal_)); EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension2()->id()).Equals( first_ordinal_)); EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension3()->id()).Equals( first_ordinal_)); EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension4()->id()).Equals( first_ordinal_)); } private: syncer::StringOrdinal first_ordinal_; }; TEST_F(ChromeAppSortingFixNTPCollisionsTwoCollisions, ChromeAppSortingFixNTPCollisionsTwoCollisions) {} class ChromeAppSortingEnsureValidOrdinals : public PrefsPrepopulatedTestBase { public : ChromeAppSortingEnsureValidOrdinals() {} ~ChromeAppSortingEnsureValidOrdinals() override {} void Initialize() override {} void Verify() override { // Give ext1 invalid ordinals and then check that EnsureValidOrdinals fixes // them. app_sorting()->SetAppLaunchOrdinal(extension1()->id(), syncer::StringOrdinal()); app_sorting()->SetPageOrdinal(extension1()->id(), syncer::StringOrdinal()); app_sorting()->EnsureValidOrdinals(extension1()->id(), syncer::StringOrdinal()); EXPECT_TRUE( app_sorting()->GetAppLaunchOrdinal(extension1()->id()).IsValid()); EXPECT_TRUE(app_sorting()->GetPageOrdinal(extension1()->id()).IsValid()); } }; TEST_F(ChromeAppSortingEnsureValidOrdinals, ChromeAppSortingEnsureValidOrdinals) {} class ChromeAppSortingPageOrdinalMapping : public PrefsPrepopulatedTestBase { public: ChromeAppSortingPageOrdinalMapping() {} ~ChromeAppSortingPageOrdinalMapping() override {} void Initialize() override {} void Verify() override { std::string ext_1 = "ext_1"; std::string ext_2 = "ext_2"; syncer::StringOrdinal first_ordinal = syncer::StringOrdinal::CreateInitialOrdinal(); // Ensure attempting to removing a mapping with an invalid page doesn't // modify the map. EXPECT_TRUE(app_sorting()->ntp_ordinal_map_.empty()); app_sorting()->RemoveOrdinalMapping( ext_1, first_ordinal, first_ordinal); EXPECT_TRUE(app_sorting()->ntp_ordinal_map_.empty()); // Add new mappings. app_sorting()->AddOrdinalMapping(ext_1, first_ordinal, first_ordinal); app_sorting()->AddOrdinalMapping(ext_2, first_ordinal, first_ordinal); EXPECT_EQ(1U, app_sorting()->ntp_ordinal_map_.size()); EXPECT_EQ(2U, app_sorting()->ntp_ordinal_map_[first_ordinal].size()); ChromeAppSorting::AppLaunchOrdinalMap::iterator it = app_sorting()->ntp_ordinal_map_[first_ordinal].find(first_ordinal); EXPECT_EQ(ext_1, it->second); ++it; EXPECT_EQ(ext_2, it->second); app_sorting()->RemoveOrdinalMapping(ext_1, first_ordinal, first_ordinal); EXPECT_EQ(1U, app_sorting()->ntp_ordinal_map_.size()); EXPECT_EQ(1U, app_sorting()->ntp_ordinal_map_[first_ordinal].size()); it = app_sorting()->ntp_ordinal_map_[first_ordinal].find(first_ordinal); EXPECT_EQ(ext_2, it->second); // Ensure that attempting to remove an extension with a valid page and app // launch ordinals, but a unused id has no effect. app_sorting()->RemoveOrdinalMapping( "invalid_ext", first_ordinal, first_ordinal); EXPECT_EQ(1U, app_sorting()->ntp_ordinal_map_.size()); EXPECT_EQ(1U, app_sorting()->ntp_ordinal_map_[first_ordinal].size()); it = app_sorting()->ntp_ordinal_map_[first_ordinal].find(first_ordinal); EXPECT_EQ(ext_2, it->second); } }; TEST_F(ChromeAppSortingPageOrdinalMapping, ChromeAppSortingPageOrdinalMapping) {} class ChromeAppSortingPreinstalledAppsBase : public PrefsPrepopulatedTestBase { public: ChromeAppSortingPreinstalledAppsBase() { base::DictionaryValue simple_dict; simple_dict.SetString(keys::kVersion, "1.0.0.0"); simple_dict.SetString(keys::kName, "unused"); simple_dict.SetString(keys::kApp, "true"); simple_dict.SetString(keys::kLaunchLocalPath, "fake.html"); std::string error; app1_scoped_ = Extension::Create( prefs_.temp_dir().AppendASCII("app1_"), Manifest::EXTERNAL_PREF, simple_dict, Extension::NO_FLAGS, &error); prefs()->OnExtensionInstalled(app1_scoped_.get(), Extension::ENABLED, syncer::StringOrdinal(), std::string()); app2_scoped_ = Extension::Create( prefs_.temp_dir().AppendASCII("app2_"), Manifest::EXTERNAL_PREF, simple_dict, Extension::NO_FLAGS, &error); prefs()->OnExtensionInstalled(app2_scoped_.get(), Extension::ENABLED, syncer::StringOrdinal(), std::string()); app1_ = app1_scoped_.get(); app2_ = app2_scoped_.get(); } ~ChromeAppSortingPreinstalledAppsBase() override {} protected: // Weak references, for convenience. Extension* app1_; Extension* app2_; private: scoped_refptr app1_scoped_; scoped_refptr app2_scoped_; }; class ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage : public ChromeAppSortingPreinstalledAppsBase { public: ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage() {} ~ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage() override {} void Initialize() override {} void Verify() override { syncer::StringOrdinal page = syncer::StringOrdinal::CreateInitialOrdinal(); syncer::StringOrdinal min = app_sorting()->GetMinOrMaxAppLaunchOrdinalsOnPage( page, ChromeAppSorting::MIN_ORDINAL); syncer::StringOrdinal max = app_sorting()->GetMinOrMaxAppLaunchOrdinalsOnPage( page, ChromeAppSorting::MAX_ORDINAL); EXPECT_TRUE(min.IsValid()); EXPECT_TRUE(max.IsValid()); EXPECT_TRUE(min.LessThan(max)); // Ensure that the min and max values aren't set for empty pages. min = syncer::StringOrdinal(); max = syncer::StringOrdinal(); syncer::StringOrdinal empty_page = page.CreateAfter(); EXPECT_FALSE(min.IsValid()); EXPECT_FALSE(max.IsValid()); min = app_sorting()->GetMinOrMaxAppLaunchOrdinalsOnPage( empty_page, ChromeAppSorting::MIN_ORDINAL); max = app_sorting()->GetMinOrMaxAppLaunchOrdinalsOnPage( empty_page, ChromeAppSorting::MAX_ORDINAL); EXPECT_FALSE(min.IsValid()); EXPECT_FALSE(max.IsValid()); } }; TEST_F(ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage, ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage) {} // Make sure that empty pages aren't removed from the integer to ordinal // mapping. See http://crbug.com/109802 for details. class ChromeAppSortingKeepEmptyStringOrdinalPages : public ChromeAppSortingPreinstalledAppsBase { public: ChromeAppSortingKeepEmptyStringOrdinalPages() {} ~ChromeAppSortingKeepEmptyStringOrdinalPages() override {} void Initialize() override { syncer::StringOrdinal first_page = syncer::StringOrdinal::CreateInitialOrdinal(); app_sorting()->SetPageOrdinal(app1_->id(), first_page); EXPECT_EQ(0, app_sorting()->PageStringOrdinalAsInteger(first_page)); last_page_ = first_page.CreateAfter(); app_sorting()->SetPageOrdinal(app2_->id(), last_page_); EXPECT_EQ(1, app_sorting()->PageStringOrdinalAsInteger(last_page_)); // Move the second app to create an empty page. app_sorting()->SetPageOrdinal(app2_->id(), first_page); EXPECT_EQ(0, app_sorting()->PageStringOrdinalAsInteger(first_page)); } void Verify() override { // Move the second app to a new empty page at the end, skipping over // the current empty page. last_page_ = last_page_.CreateAfter(); app_sorting()->SetPageOrdinal(app2_->id(), last_page_); EXPECT_EQ(2, app_sorting()->PageStringOrdinalAsInteger(last_page_)); EXPECT_TRUE( last_page_.Equals(app_sorting()->PageIntegerAsStringOrdinal(2))); } private: syncer::StringOrdinal last_page_; }; TEST_F(ChromeAppSortingKeepEmptyStringOrdinalPages, ChromeAppSortingKeepEmptyStringOrdinalPages) {} class ChromeAppSortingMakesFillerOrdinals : public ChromeAppSortingPreinstalledAppsBase { public: ChromeAppSortingMakesFillerOrdinals() {} ~ChromeAppSortingMakesFillerOrdinals() override {} void Initialize() override { syncer::StringOrdinal first_page = syncer::StringOrdinal::CreateInitialOrdinal(); app_sorting()->SetPageOrdinal(app1_->id(), first_page); EXPECT_EQ(0, app_sorting()->PageStringOrdinalAsInteger(first_page)); } void Verify() override { // Because the UI can add an unlimited number of empty pages without an app // on them, this test simulates dropping of an app on the 1st and 4th empty // pages (3rd and 6th pages by index) to ensure we don't crash and that // filler ordinals are created as needed. See: http://crbug.com/122214 syncer::StringOrdinal page_three = app_sorting()->PageIntegerAsStringOrdinal(2); app_sorting()->SetPageOrdinal(app1_->id(), page_three); EXPECT_EQ(2, app_sorting()->PageStringOrdinalAsInteger(page_three)); syncer::StringOrdinal page_six = app_sorting()->PageIntegerAsStringOrdinal(5); app_sorting()->SetPageOrdinal(app1_->id(), page_six); EXPECT_EQ(5, app_sorting()->PageStringOrdinalAsInteger(page_six)); } }; TEST_F(ChromeAppSortingMakesFillerOrdinals, ChromeAppSortingMakesFillerOrdinals) {} class ChromeAppSortingDefaultOrdinalsBase : public ExtensionPrefsTest { public: ChromeAppSortingDefaultOrdinalsBase() {} ~ChromeAppSortingDefaultOrdinalsBase() override {} void Initialize() override { app_ = CreateApp("app"); InitDefaultOrdinals(); ChromeAppSorting::AppOrdinalsMap& sorting_defaults = app_sorting()->default_ordinals_; sorting_defaults[app_->id()].page_ordinal = default_page_ordinal_; sorting_defaults[app_->id()].app_launch_ordinal = default_app_launch_ordinal_; SetupUserOrdinals(); InstallApps(); } protected: scoped_refptr CreateApp(const std::string& name) { base::DictionaryValue simple_dict; simple_dict.SetString(keys::kVersion, "1.0.0.0"); simple_dict.SetString(keys::kName, name); simple_dict.SetString(keys::kApp, "true"); simple_dict.SetString(keys::kLaunchLocalPath, "fake.html"); std::string errors; scoped_refptr app = Extension::Create( prefs_.temp_dir().AppendASCII(name), Manifest::EXTERNAL_PREF, simple_dict, Extension::NO_FLAGS, &errors); EXPECT_TRUE(app.get()) << errors; EXPECT_TRUE(crx_file::id_util::IdIsValid(app->id())); return app; } void InitDefaultOrdinals() { default_page_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal().CreateAfter(); default_app_launch_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal().CreateBefore(); } virtual void SetupUserOrdinals() {} virtual void InstallApps() { prefs()->OnExtensionInstalled(app_.get(), Extension::ENABLED, syncer::StringOrdinal(), std::string()); } scoped_refptr app_; syncer::StringOrdinal default_page_ordinal_; syncer::StringOrdinal default_app_launch_ordinal_; }; // Tests that the app gets its default ordinals. class ChromeAppSortingDefaultOrdinals : public ChromeAppSortingDefaultOrdinalsBase { public: ChromeAppSortingDefaultOrdinals() {} ~ChromeAppSortingDefaultOrdinals() override {} void Verify() override { EXPECT_TRUE(app_sorting()->GetPageOrdinal(app_->id()).Equals( default_page_ordinal_)); EXPECT_TRUE(app_sorting()->GetAppLaunchOrdinal(app_->id()).Equals( default_app_launch_ordinal_)); } }; TEST_F(ChromeAppSortingDefaultOrdinals, ChromeAppSortingDefaultOrdinals) {} // Tests that the default page ordinal is overridden by install page ordinal. class ChromeAppSortingDefaultOrdinalOverriddenByInstallPage : public ChromeAppSortingDefaultOrdinalsBase { public: ChromeAppSortingDefaultOrdinalOverriddenByInstallPage() {} ~ChromeAppSortingDefaultOrdinalOverriddenByInstallPage() override {} void Verify() override { EXPECT_FALSE(app_sorting()->GetPageOrdinal(app_->id()).Equals( default_page_ordinal_)); EXPECT_TRUE(app_sorting()->GetPageOrdinal(app_->id()).Equals( install_page_)); } protected: void InstallApps() override { install_page_ = default_page_ordinal_.CreateAfter(); prefs()->OnExtensionInstalled(app_.get(), Extension::ENABLED, install_page_, std::string()); } private: syncer::StringOrdinal install_page_; }; TEST_F(ChromeAppSortingDefaultOrdinalOverriddenByInstallPage, ChromeAppSortingDefaultOrdinalOverriddenByInstallPage) {} // Tests that the default ordinals are overridden by user values. class ChromeAppSortingDefaultOrdinalOverriddenByUserValue : public ChromeAppSortingDefaultOrdinalsBase { public: ChromeAppSortingDefaultOrdinalOverriddenByUserValue() {} ~ChromeAppSortingDefaultOrdinalOverriddenByUserValue() override {} void Verify() override { EXPECT_TRUE(app_sorting()->GetPageOrdinal(app_->id()).Equals( user_page_ordinal_)); EXPECT_TRUE(app_sorting()->GetAppLaunchOrdinal(app_->id()).Equals( user_app_launch_ordinal_)); } protected: void SetupUserOrdinals() override { user_page_ordinal_ = default_page_ordinal_.CreateAfter(); user_app_launch_ordinal_ = default_app_launch_ordinal_.CreateBefore(); app_sorting()->SetPageOrdinal(app_->id(), user_page_ordinal_); app_sorting()->SetAppLaunchOrdinal(app_->id(), user_app_launch_ordinal_); } private: syncer::StringOrdinal user_page_ordinal_; syncer::StringOrdinal user_app_launch_ordinal_; }; TEST_F(ChromeAppSortingDefaultOrdinalOverriddenByUserValue, ChromeAppSortingDefaultOrdinalOverriddenByUserValue) {} // Tests that the default app launch ordinal is changed to avoid collision. class ChromeAppSortingDefaultOrdinalNoCollision : public ChromeAppSortingDefaultOrdinalsBase { public: ChromeAppSortingDefaultOrdinalNoCollision() {} ~ChromeAppSortingDefaultOrdinalNoCollision() override {} void Verify() override { // Use the default page. EXPECT_TRUE(app_sorting()->GetPageOrdinal(app_->id()).Equals( default_page_ordinal_)); // Not using the default app launch ordinal because of the collision. EXPECT_FALSE(app_sorting()->GetAppLaunchOrdinal(app_->id()).Equals( default_app_launch_ordinal_)); } protected: void SetupUserOrdinals() override { other_app_ = prefs_.AddApp("other_app"); // Creates a collision. app_sorting()->SetPageOrdinal(other_app_->id(), default_page_ordinal_); app_sorting()->SetAppLaunchOrdinal(other_app_->id(), default_app_launch_ordinal_); yet_another_app_ = prefs_.AddApp("yet_aother_app"); app_sorting()->SetPageOrdinal(yet_another_app_->id(), default_page_ordinal_); app_sorting()->SetAppLaunchOrdinal(yet_another_app_->id(), default_app_launch_ordinal_); } private: scoped_refptr other_app_; scoped_refptr yet_another_app_; }; TEST_F(ChromeAppSortingDefaultOrdinalNoCollision, ChromeAppSortingDefaultOrdinalNoCollision) {} // Tests that SetExtensionVisible() correctly hides and unhides extensions. class ChromeAppSortingSetExtensionVisible : public ExtensionPrefsTest { public: ChromeAppSortingSetExtensionVisible() {} ~ChromeAppSortingSetExtensionVisible() override {} void Initialize() override { first_app_ = prefs_.AddApp("first_app"); second_app_ = prefs_.AddApp("second_app"); } void Verify() override { ChromeAppSorting* sorting = app_sorting(); syncer::StringOrdinal page1 = sorting->GetPageOrdinal(first_app_->id()); syncer::StringOrdinal page2 = sorting->GetPageOrdinal(second_app_->id()); EXPECT_TRUE(sorting->GetAppLaunchOrdinal(first_app_->id()).IsValid()); EXPECT_TRUE(sorting->GetAppLaunchOrdinal(second_app_->id()).IsValid()); EXPECT_TRUE(page1.IsValid()); EXPECT_TRUE(page2.IsValid()); EXPECT_TRUE(page1.Equals(page2)); sorting->SetExtensionVisible(first_app_->id(), false); EXPECT_EQ( 1U, sorting->CountItemsVisibleOnNtp(sorting->ntp_ordinal_map_[page1])); sorting->SetExtensionVisible(first_app_->id(), true); EXPECT_EQ( 2U, sorting->CountItemsVisibleOnNtp(sorting->ntp_ordinal_map_[page1])); } private: scoped_refptr first_app_; scoped_refptr second_app_; }; TEST_F(ChromeAppSortingSetExtensionVisible, ChromeAppSortingSetExtensionVisible) { } } // namespace extensions