diff options
author | robliao <robliao@chromium.org> | 2015-03-27 11:25:11 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-03-27 18:26:01 +0000 |
commit | 43eb02c2ffcfa18adfcbf34cd0507a3d7494febe (patch) | |
tree | 97480dd6f2a973a6a187e71ad86aa77bea59de26 /extensions/common/features | |
parent | ddde1d3ec2c03222808b4d93a8c23208b3f4c2ff (diff) | |
download | chromium_src-43eb02c2ffcfa18adfcbf34cd0507a3d7494febe.zip chromium_src-43eb02c2ffcfa18adfcbf34cd0507a3d7494febe.tar.gz chromium_src-43eb02c2ffcfa18adfcbf34cd0507a3d7494febe.tar.bz2 |
Refactor Uses of std::set to std::vector in SimpleFeature
Local profiling suggests that converting std::set to std::vector will save
around 10% of the overall startup time, likely due to the reduced allocs
involved with std::vector.
This change also reduces lookups by traversing the incoming dictionary
and iterating on the available values instead of looking through all
values for all features.
Finally, cleaned up and enforced the testing accessors via code and friend
declarations
BUG=460987
Review URL: https://codereview.chromium.org/1010973013
Cr-Commit-Position: refs/heads/master@{#322609}
Diffstat (limited to 'extensions/common/features')
-rw-r--r-- | extensions/common/features/base_feature_provider_unittest.cc | 28 | ||||
-rw-r--r-- | extensions/common/features/simple_feature.cc | 223 | ||||
-rw-r--r-- | extensions/common/features/simple_feature.h | 141 | ||||
-rw-r--r-- | extensions/common/features/simple_feature_unittest.cc | 194 |
4 files changed, 351 insertions, 235 deletions
diff --git a/extensions/common/features/base_feature_provider_unittest.cc b/extensions/common/features/base_feature_provider_unittest.cc index 8f4a3ba..dc41366 100644 --- a/extensions/common/features/base_feature_provider_unittest.cc +++ b/extensions/common/features/base_feature_provider_unittest.cc @@ -4,9 +4,11 @@ #include "extensions/common/features/base_feature_provider.h" +#include <algorithm> #include <set> #include <string> +#include "base/stl_util.h" #include "extensions/common/extension_builder.h" #include "extensions/common/features/feature.h" #include "extensions/common/features/simple_feature.h" @@ -24,14 +26,16 @@ TEST(BaseFeatureProviderTest, ManifestFeatureTypes) { const SimpleFeature* feature = static_cast<const SimpleFeature*>( FeatureProvider::GetManifestFeature("description")); ASSERT_TRUE(feature); - const std::set<Manifest::Type>* extension_types = feature->extension_types(); + const std::vector<Manifest::Type>* extension_types = + feature->extension_types(); EXPECT_EQ(6u, extension_types->size()); - EXPECT_EQ(1u, extension_types->count(Manifest::TYPE_EXTENSION)); - EXPECT_EQ(1u, extension_types->count(Manifest::TYPE_LEGACY_PACKAGED_APP)); - EXPECT_EQ(1u, extension_types->count(Manifest::TYPE_PLATFORM_APP)); - EXPECT_EQ(1u, extension_types->count(Manifest::TYPE_HOSTED_APP)); - EXPECT_EQ(1u, extension_types->count(Manifest::TYPE_THEME)); - EXPECT_EQ(1u, extension_types->count(Manifest::TYPE_SHARED_MODULE)); + EXPECT_EQ(1, STLCount(*(extension_types), Manifest::TYPE_EXTENSION)); + EXPECT_EQ(1, + STLCount(*(extension_types), Manifest::TYPE_LEGACY_PACKAGED_APP)); + EXPECT_EQ(1, STLCount(*(extension_types), Manifest::TYPE_PLATFORM_APP)); + EXPECT_EQ(1, STLCount(*(extension_types), Manifest::TYPE_HOSTED_APP)); + EXPECT_EQ(1, STLCount(*(extension_types), Manifest::TYPE_THEME)); + EXPECT_EQ(1, STLCount(*(extension_types), Manifest::TYPE_SHARED_MODULE)); } // Tests that real manifest features have the correct availability for an @@ -79,11 +83,13 @@ TEST(BaseFeatureProviderTest, PermissionFeatureTypes) { const SimpleFeature* feature = static_cast<const SimpleFeature*>( BaseFeatureProvider::GetPermissionFeature("power")); ASSERT_TRUE(feature); - const std::set<Manifest::Type>* extension_types = feature->extension_types(); + const std::vector<Manifest::Type>* extension_types = + feature->extension_types(); EXPECT_EQ(3u, extension_types->size()); - EXPECT_EQ(1u, extension_types->count(Manifest::TYPE_EXTENSION)); - EXPECT_EQ(1u, extension_types->count(Manifest::TYPE_LEGACY_PACKAGED_APP)); - EXPECT_EQ(1u, extension_types->count(Manifest::TYPE_PLATFORM_APP)); + EXPECT_EQ(1, STLCount(*(extension_types), Manifest::TYPE_EXTENSION)); + EXPECT_EQ(1, + STLCount(*(extension_types), Manifest::TYPE_LEGACY_PACKAGED_APP)); + EXPECT_EQ(1, STLCount(*(extension_types), Manifest::TYPE_PLATFORM_APP)); } // Tests that real permission features have the correct availability for an app. diff --git a/extensions/common/features/simple_feature.cc b/extensions/common/features/simple_feature.cc index 7d1e06e..6a5bece 100644 --- a/extensions/common/features/simple_feature.cc +++ b/extensions/common/features/simple_feature.cc @@ -4,13 +4,13 @@ #include "extensions/common/features/simple_feature.h" +#include <algorithm> #include <map> #include <vector> #include "base/bind.h" #include "base/command_line.h" #include "base/debug/alias.h" -#include "base/lazy_instance.h" #include "base/sha1.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" @@ -43,56 +43,23 @@ Feature::Availability IsAvailableToContextForBind(const Extension* extension, return feature->IsAvailableToContext(extension, context, url, platform); } -struct Mappings { - Mappings() { - extension_types["extension"] = Manifest::TYPE_EXTENSION; - extension_types["theme"] = Manifest::TYPE_THEME; - extension_types["legacy_packaged_app"] = Manifest::TYPE_LEGACY_PACKAGED_APP; - extension_types["hosted_app"] = Manifest::TYPE_HOSTED_APP; - extension_types["platform_app"] = Manifest::TYPE_PLATFORM_APP; - extension_types["shared_module"] = Manifest::TYPE_SHARED_MODULE; - - contexts["blessed_extension"] = Feature::BLESSED_EXTENSION_CONTEXT; - contexts["unblessed_extension"] = Feature::UNBLESSED_EXTENSION_CONTEXT; - contexts["content_script"] = Feature::CONTENT_SCRIPT_CONTEXT; - contexts["web_page"] = Feature::WEB_PAGE_CONTEXT; - contexts["blessed_web_page"] = Feature::BLESSED_WEB_PAGE_CONTEXT; - contexts["webui"] = Feature::WEBUI_CONTEXT; - - locations["component"] = SimpleFeature::COMPONENT_LOCATION; - locations["external_component"] = - SimpleFeature::EXTERNAL_COMPONENT_LOCATION; - locations["policy"] = SimpleFeature::POLICY_LOCATION; - - platforms["chromeos"] = Feature::CHROMEOS_PLATFORM; - platforms["linux"] = Feature::LINUX_PLATFORM; - platforms["mac"] = Feature::MACOSX_PLATFORM; - platforms["win"] = Feature::WIN_PLATFORM; - } - - std::map<std::string, Manifest::Type> extension_types; - std::map<std::string, Feature::Context> contexts; - std::map<std::string, SimpleFeature::Location> locations; - std::map<std::string, Feature::Platform> platforms; -}; - -base::LazyInstance<Mappings> g_mappings = LAZY_INSTANCE_INITIALIZER; - // TODO(aa): Can we replace all this manual parsing with JSON schema stuff? -void ParseSet(const base::DictionaryValue* value, - const std::string& property, - std::set<std::string>* set) { +void ParseVector(const base::Value* value, + std::vector<std::string>* vector) { const base::ListValue* list_value = NULL; - if (!value->GetList(property, &list_value)) + if (!value->GetAsList(&list_value)) return; - set->clear(); - for (size_t i = 0; i < list_value->GetSize(); ++i) { + vector->clear(); + size_t list_size = list_value->GetSize(); + vector->reserve(list_size); + for (size_t i = 0; i < list_size; ++i) { std::string str_val; - CHECK(list_value->GetString(i, &str_val)) << property << " " << i; - set->insert(str_val); + CHECK(list_value->GetString(i, &str_val)); + vector->push_back(str_val); } + std::sort(vector->begin(), vector->end()); } template<typename T> @@ -124,31 +91,30 @@ void ParseEnum(const base::DictionaryValue* value, } template<typename T> -void ParseEnumSet(const base::DictionaryValue* value, - const std::string& property, - std::set<T>* enum_set, - const std::map<std::string, T>& mapping) { - if (!value->HasKey(property)) - return; - - enum_set->clear(); - +void ParseEnumVector(const base::Value* value, + std::vector<T>* enum_vector, + const std::map<std::string, T>& mapping) { + enum_vector->clear(); std::string property_string; - if (value->GetString(property, &property_string)) { + if (value->GetAsString(&property_string)) { if (property_string == "all") { + enum_vector->reserve(mapping.size()); for (const auto& it : mapping) - enum_set->insert(it.second); + enum_vector->push_back(it.second); } + std::sort(enum_vector->begin(), enum_vector->end()); return; } - std::set<std::string> string_set; - ParseSet(value, property, &string_set); - for (const auto& str : string_set) { + std::vector<std::string> string_vector; + ParseVector(value, &string_vector); + enum_vector->reserve(string_vector.size()); + for (const auto& str : string_vector) { T enum_value = static_cast<T>(0); ParseEnum(str, &enum_value, mapping); - enum_set->insert(enum_value); + enum_vector->push_back(enum_value); } + std::sort(enum_vector->begin(), enum_vector->end()); } void ParseURLPatterns(const base::DictionaryValue* value, @@ -258,6 +224,39 @@ bool IsCommandLineSwitchEnabled(const std::string& switch_name) { } // namespace +struct SimpleFeature::Mappings { + Mappings() { + extension_types["extension"] = Manifest::TYPE_EXTENSION; + extension_types["theme"] = Manifest::TYPE_THEME; + extension_types["legacy_packaged_app"] = Manifest::TYPE_LEGACY_PACKAGED_APP; + extension_types["hosted_app"] = Manifest::TYPE_HOSTED_APP; + extension_types["platform_app"] = Manifest::TYPE_PLATFORM_APP; + extension_types["shared_module"] = Manifest::TYPE_SHARED_MODULE; + + contexts["blessed_extension"] = Feature::BLESSED_EXTENSION_CONTEXT; + contexts["unblessed_extension"] = Feature::UNBLESSED_EXTENSION_CONTEXT; + contexts["content_script"] = Feature::CONTENT_SCRIPT_CONTEXT; + contexts["web_page"] = Feature::WEB_PAGE_CONTEXT; + contexts["blessed_web_page"] = Feature::BLESSED_WEB_PAGE_CONTEXT; + contexts["webui"] = Feature::WEBUI_CONTEXT; + + locations["component"] = SimpleFeature::COMPONENT_LOCATION; + locations["external_component"] = + SimpleFeature::EXTERNAL_COMPONENT_LOCATION; + locations["policy"] = SimpleFeature::POLICY_LOCATION; + + platforms["chromeos"] = Feature::CHROMEOS_PLATFORM; + platforms["linux"] = Feature::LINUX_PLATFORM; + platforms["mac"] = Feature::MACOSX_PLATFORM; + platforms["win"] = Feature::WIN_PLATFORM; + } + + std::map<std::string, Manifest::Type> extension_types; + std::map<std::string, Feature::Context> contexts; + std::map<std::string, SimpleFeature::Location> locations; + std::map<std::string, Feature::Platform> platforms; +}; + SimpleFeature::SimpleFeature() : location_(UNSPECIFIED_LOCATION), min_manifest_version_(0), @@ -274,29 +273,49 @@ void SimpleFeature::AddFilter(scoped_ptr<SimpleFeatureFilter> filter) { filters_.push_back(filter.release()); } -std::string SimpleFeature::Parse(const base::DictionaryValue* value) { - ParseURLPatterns(value, "matches", &matches_); - ParseSet(value, "blacklist", &blacklist_); - ParseSet(value, "whitelist", &whitelist_); - ParseSet(value, "dependencies", &dependencies_); - ParseEnumSet<Manifest::Type>(value, "extension_types", &extension_types_, - g_mappings.Get().extension_types); - ParseEnumSet<Context>(value, "contexts", &contexts_, - g_mappings.Get().contexts); - ParseEnum<Location>(value, "location", &location_, - g_mappings.Get().locations); - ParseEnumSet<Platform>(value, "platforms", &platforms_, - g_mappings.Get().platforms); - value->GetInteger("min_manifest_version", &min_manifest_version_); - value->GetInteger("max_manifest_version", &max_manifest_version_); +std::string SimpleFeature::Parse(const base::DictionaryValue* dictionary) { + static base::LazyInstance<SimpleFeature::Mappings> mappings = + LAZY_INSTANCE_INITIALIZER; no_parent_ = false; - value->GetBoolean("noparent", &no_parent_); - - value->GetBoolean("component_extensions_auto_granted", - &component_extensions_auto_granted_); - - value->GetString("command_line_switch", &command_line_switch_); + for (base::DictionaryValue::Iterator it(*dictionary); + !it.IsAtEnd(); + it.Advance()) { + std::string key = it.key(); + const base::Value* value = &it.value(); + if (key == "matches") { + ParseURLPatterns(dictionary, "matches", &matches_); + } else if (key == "blacklist") { + ParseVector(value, &blacklist_); + } else if (key == "whitelist") { + ParseVector(value, &whitelist_); + } else if (key == "dependencies") { + ParseVector(value, &dependencies_); + } else if (key == "extension_types") { + ParseEnumVector<Manifest::Type>(value, &extension_types_, + mappings.Get().extension_types); + } else if (key == "contexts") { + ParseEnumVector<Context>(value, &contexts_, + mappings.Get().contexts); + } else if (key == "location") { + ParseEnum<Location>(dictionary, "location", &location_, + mappings.Get().locations); + } else if (key == "platforms") { + ParseEnumVector<Platform>(value, &platforms_, + mappings.Get().platforms); + } else if (key == "min_manifest_version") { + dictionary->GetInteger("min_manifest_version", &min_manifest_version_); + } else if (key == "max_manifest_version") { + dictionary->GetInteger("max_manifest_version", &max_manifest_version_); + } else if (key == "noparent") { + dictionary->GetBoolean("noparent", &no_parent_); + } else if (key == "component_extensions_auto_granted") { + dictionary->GetBoolean("component_extensions_auto_granted", + &component_extensions_auto_granted_); + } else if (key == "command_line_switch") { + dictionary->GetString("command_line_switch", &command_line_switch_); + } + } // NOTE: ideally we'd sanity check that "matches" can be specified if and // only if there's a "web_page" or "webui" context, but without @@ -309,7 +328,7 @@ std::string SimpleFeature::Parse(const base::DictionaryValue* value) { std::string result; for (const auto& filter : filters_) { - result = filter->Parse(value); + result = filter->Parse(dictionary); if (!result.empty()) break; } @@ -330,7 +349,7 @@ Feature::Availability SimpleFeature::IsAvailableToManifest( Manifest::Type type_to_check = (type == Manifest::TYPE_USER_SCRIPT) ? Manifest::TYPE_EXTENSION : type; if (!extension_types_.empty() && - !ContainsKey(extension_types_, type_to_check)) { + !ContainsValue(extension_types_, type_to_check)) { return CreateAvailability(INVALID_TYPE, type); } @@ -363,7 +382,7 @@ Feature::Availability SimpleFeature::IsAvailableToManifest( if (!MatchesManifestLocation(location)) return CreateAvailability(INVALID_LOCATION, type); - if (!platforms_.empty() && !ContainsKey(platforms_, platform)) + if (!platforms_.empty() && !ContainsValue(platforms_, platform)) return CreateAvailability(INVALID_PLATFORM, type); if (min_manifest_version_ != 0 && manifest_version < min_manifest_version_) @@ -407,7 +426,7 @@ Feature::Availability SimpleFeature::IsAvailableToContext( return result; } - if (!contexts_.empty() && !ContainsKey(contexts_, context)) + if (!contexts_.empty() && !ContainsValue(contexts_, context)) return CreateAvailability(INVALID_CONTEXT, context); // TODO(kalman): Consider checking |matches_| regardless of context type. @@ -539,17 +558,27 @@ bool SimpleFeature::IsIdInWhitelist(const std::string& extension_id) const { } // static +bool SimpleFeature::IsIdInArray(const std::string& extension_id, + const char* const array[], + size_t array_length) { + if (!IsValidExtensionId(extension_id)) + return false; + + const char* const* start = array; + const char* const* end = array + array_length; + + return ((std::find(start, end, extension_id) != end) || + (std::find(start, end, HashExtensionId(extension_id)) != end)); +} + +// static bool SimpleFeature::IsIdInList(const std::string& extension_id, - const std::set<std::string>& list) { - // Belt-and-suspenders philosophy here. We should be pretty confident by this - // point that we've validated the extension ID format, but in case something - // slips through, we avoid a class of attack where creative ID manipulation - // leads to hash collisions. - if (extension_id.length() != 32) // 128 bits / 4 = 32 mpdecimal characters + const std::vector<std::string>& list) { + if (!IsValidExtensionId(extension_id)) return false; - return (ContainsKey(list, extension_id) || - ContainsKey(list, HashExtensionId(extension_id))); + return (ContainsValue(list, extension_id) || + ContainsValue(list, HashExtensionId(extension_id))); } bool SimpleFeature::MatchesManifestLocation( @@ -583,4 +612,14 @@ Feature::Availability SimpleFeature::CheckDependencies( return CreateAvailability(IS_AVAILABLE); } +// static +bool SimpleFeature::IsValidExtensionId(const std::string& extension_id) { + // Belt-and-suspenders philosophy here. We should be pretty confident by this + // point that we've validated the extension ID format, but in case something + // slips through, we avoid a class of attack where creative ID manipulation + // leads to hash collisions. + // 128 bits / 4 = 32 mpdecimal characters + return (extension_id.length() == 32); +} + } // namespace extensions diff --git a/extensions/common/features/simple_feature.h b/extensions/common/features/simple_feature.h index a5bc270..9d8c539 100644 --- a/extensions/common/features/simple_feature.h +++ b/extensions/common/features/simple_feature.h @@ -11,6 +11,7 @@ #include "base/callback_forward.h" #include "base/gtest_prod_util.h" +#include "base/lazy_instance.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "base/values.h" @@ -21,53 +22,16 @@ namespace extensions { +class BaseFeatureProviderTest; +class ExtensionAPITest; +class ManifestUnitTest; +class SimpleFeatureTest; + class SimpleFeature : public Feature { public: SimpleFeature(); ~SimpleFeature() override; - // Similar to Manifest::Location, these are the classes of locations - // supported in feature files. - // - // This is only public for testing. Production code should never access it, - // nor should it really have any reason to access the SimpleFeature class - // directly, it should be dealing with the Feature interface. - enum Location { - UNSPECIFIED_LOCATION, - COMPONENT_LOCATION, - EXTERNAL_COMPONENT_LOCATION, - POLICY_LOCATION, - }; - - // Accessors defined for testing. See comment above about not directly using - // SimpleFeature in production code. - std::set<std::string>* blacklist() { return &blacklist_; } - const std::set<std::string>* blacklist() const { return &blacklist_; } - std::set<std::string>* whitelist() { return &whitelist_; } - const std::set<std::string>* whitelist() const { return &whitelist_; } - std::set<Manifest::Type>* extension_types() { return &extension_types_; } - const std::set<Manifest::Type>* extension_types() const { - return &extension_types_; - } - std::set<Context>* contexts() { return &contexts_; } - const std::set<Context>* contexts() const { return &contexts_; } - Location location() const { return location_; } - void set_location(Location location) { location_ = location; } - int min_manifest_version() const { return min_manifest_version_; } - void set_min_manifest_version(int min_manifest_version) { - min_manifest_version_ = min_manifest_version; - } - int max_manifest_version() const { return max_manifest_version_; } - void set_max_manifest_version(int max_manifest_version) { - max_manifest_version_ = max_manifest_version; - } - const std::string& command_line_switch() const { - return command_line_switch_; - } - void set_command_line_switch(const std::string& command_line_switch) { - command_line_switch_ = command_line_switch; - } - // Dependency resolution is a property of Features that is preferrably // handled internally to avoid temptation, but FeatureFilters may need // to know if there are any at all. @@ -80,9 +44,7 @@ class SimpleFeature : public Feature { // Unspecified values in the JSON are not modified in the object. This allows // us to implement inheritance by parsing one value after another. Returns // the error found, or an empty string on success. - virtual std::string Parse(const base::DictionaryValue* value); - - std::set<Platform>* platforms() { return &platforms_; } + virtual std::string Parse(const base::DictionaryValue* dictionary); Availability IsAvailableToContext(const Extension* extension, Context context) const { @@ -120,10 +82,51 @@ class SimpleFeature : public Feature { bool IsIdInBlacklist(const std::string& extension_id) const override; bool IsIdInWhitelist(const std::string& extension_id) const override; - static bool IsIdInList(const std::string& extension_id, - const std::set<std::string>& list); + + static bool IsIdInArray(const std::string& extension_id, + const char* const array[], + size_t array_length); protected: + // Similar to Manifest::Location, these are the classes of locations + // supported in feature files. Production code should never directly access + // these. + enum Location { + UNSPECIFIED_LOCATION, + COMPONENT_LOCATION, + EXTERNAL_COMPONENT_LOCATION, + POLICY_LOCATION, + }; + + // Accessors defined for testing. + std::vector<std::string>* blacklist() { return &blacklist_; } + const std::vector<std::string>* blacklist() const { return &blacklist_; } + std::vector<std::string>* whitelist() { return &whitelist_; } + const std::vector<std::string>* whitelist() const { return &whitelist_; } + std::vector<Manifest::Type>* extension_types() { return &extension_types_; } + const std::vector<Manifest::Type>* extension_types() const { + return &extension_types_; + } + std::vector<Context>* contexts() { return &contexts_; } + const std::vector<Context>* contexts() const { return &contexts_; } + std::vector<Platform>* platforms() { return &platforms_; } + Location location() const { return location_; } + void set_location(Location location) { location_ = location; } + int min_manifest_version() const { return min_manifest_version_; } + void set_min_manifest_version(int min_manifest_version) { + min_manifest_version_ = min_manifest_version; + } + int max_manifest_version() const { return max_manifest_version_; } + void set_max_manifest_version(int max_manifest_version) { + max_manifest_version_ = max_manifest_version; + } + const std::string& command_line_switch() const { + return command_line_switch_; + } + void set_command_line_switch(const std::string& command_line_switch) { + command_line_switch_ = command_line_switch; + } + // Handy utilities which construct the correct availability message. Availability CreateAvailability(AvailabilityResult result) const; Availability CreateAvailability(AvailabilityResult result, @@ -134,23 +137,55 @@ class SimpleFeature : public Feature { Context context) const; private: + friend class SimpleFeatureTest; + FRIEND_TEST_ALL_PREFIXES(BaseFeatureProviderTest, ManifestFeatureTypes); + FRIEND_TEST_ALL_PREFIXES(BaseFeatureProviderTest, PermissionFeatureTypes); + FRIEND_TEST_ALL_PREFIXES(ExtensionAPITest, DefaultConfigurationFeatures); + FRIEND_TEST_ALL_PREFIXES(ManifestUnitTest, Extension); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, Blacklist); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, CommandLineSwitch); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, Context); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, HashedIdBlacklist); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, HashedIdWhitelist); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, Inheritance); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, Location); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ManifestVersion); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, PackageType); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParseContexts); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParseLocation); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParseManifestVersion); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParseNull); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParsePackageTypes); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParsePlatforms); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, ParseWhitelist); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, Platform); + FRIEND_TEST_ALL_PREFIXES(SimpleFeatureTest, Whitelist); + + // Holds String to Enum value mappings. + struct Mappings; + + static bool IsIdInList(const std::string& extension_id, + const std::vector<std::string>& list); + bool MatchesManifestLocation(Manifest::Location manifest_location) const; Availability CheckDependencies( const base::Callback<Availability(const Feature*)>& checker) const; + static bool IsValidExtensionId(const std::string& extension_id); + // For clarity and consistency, we handle the default value of each of these // members the same way: it matches everything. It is up to the higher level // code that reads Features out of static data to validate that data and set // sensible defaults. - std::set<std::string> blacklist_; - std::set<std::string> whitelist_; - std::set<std::string> dependencies_; - std::set<Manifest::Type> extension_types_; - std::set<Context> contexts_; + std::vector<std::string> blacklist_; + std::vector<std::string> whitelist_; + std::vector<std::string> dependencies_; + std::vector<Manifest::Type> extension_types_; + std::vector<Context> contexts_; + std::vector<Platform> platforms_; URLPatternSet matches_; Location location_; - std::set<Platform> platforms_; int min_manifest_version_; int max_manifest_version_; bool component_extensions_auto_granted_; diff --git a/extensions/common/features/simple_feature_unittest.cc b/extensions/common/features/simple_feature_unittest.cc index dce5740..528c565 100644 --- a/extensions/common/features/simple_feature_unittest.cc +++ b/extensions/common/features/simple_feature_unittest.cc @@ -7,6 +7,7 @@ #include <string> #include "base/command_line.h" +#include "base/stl_util.h" #include "base/values.h" #include "extensions/common/manifest.h" #include "extensions/common/value_builder.h" @@ -25,19 +26,6 @@ struct IsAvailableTestData { Feature::AvailabilityResult expected_result; }; -bool LocationIsAvailable(SimpleFeature::Location feature_location, - Manifest::Location manifest_location) { - SimpleFeature feature; - feature.set_location(feature_location); - Feature::AvailabilityResult availability_result = - feature.IsAvailableToManifest(std::string(), - Manifest::TYPE_UNKNOWN, - manifest_location, - -1, - Feature::UNSPECIFIED_PLATFORM).result(); - return availability_result == Feature::IS_AVAILABLE; -} - class ScopedCommandLineSwitch { public: explicit ScopedCommandLineSwitch(const std::string& arg) @@ -55,7 +43,23 @@ class ScopedCommandLineSwitch { } // namespace -TEST(SimpleFeatureTest, IsAvailableNullCase) { +class SimpleFeatureTest : public testing::Test { + protected: + bool LocationIsAvailable(SimpleFeature::Location feature_location, + Manifest::Location manifest_location) { + SimpleFeature feature; + feature.set_location(feature_location); + Feature::AvailabilityResult availability_result = + feature.IsAvailableToManifest(std::string(), + Manifest::TYPE_UNKNOWN, + manifest_location, + -1, + Feature::UNSPECIFIED_PLATFORM).result(); + return availability_result == Feature::IS_AVAILABLE; + } +}; + +TEST_F(SimpleFeatureTest, IsAvailableNullCase) { const IsAvailableTestData tests[] = { {"", Manifest::TYPE_UNKNOWN, Manifest::INVALID_LOCATION, Feature::UNSPECIFIED_PLATFORM, -1, Feature::IS_AVAILABLE}, @@ -84,13 +88,13 @@ TEST(SimpleFeatureTest, IsAvailableNullCase) { } } -TEST(SimpleFeatureTest, Whitelist) { +TEST_F(SimpleFeatureTest, Whitelist) { const std::string kIdFoo("fooabbbbccccddddeeeeffffgggghhhh"); const std::string kIdBar("barabbbbccccddddeeeeffffgggghhhh"); const std::string kIdBaz("bazabbbbccccddddeeeeffffgggghhhh"); SimpleFeature feature; - feature.whitelist()->insert(kIdFoo); - feature.whitelist()->insert(kIdBar); + feature.whitelist()->push_back(kIdFoo); + feature.whitelist()->push_back(kIdBar); EXPECT_EQ( Feature::IS_AVAILABLE, @@ -122,7 +126,7 @@ TEST(SimpleFeatureTest, Whitelist) { -1, Feature::UNSPECIFIED_PLATFORM).result()); - feature.extension_types()->insert(Manifest::TYPE_LEGACY_PACKAGED_APP); + feature.extension_types()->push_back(Manifest::TYPE_LEGACY_PACKAGED_APP); EXPECT_EQ( Feature::NOT_FOUND_IN_WHITELIST, feature.IsAvailableToManifest(kIdBaz, @@ -132,14 +136,14 @@ TEST(SimpleFeatureTest, Whitelist) { Feature::UNSPECIFIED_PLATFORM).result()); } -TEST(SimpleFeatureTest, HashedIdWhitelist) { +TEST_F(SimpleFeatureTest, HashedIdWhitelist) { // echo -n "fooabbbbccccddddeeeeffffgggghhhh" | // sha1sum | tr '[:lower:]' '[:upper:]' const std::string kIdFoo("fooabbbbccccddddeeeeffffgggghhhh"); const std::string kIdFooHashed("55BC7228A0D502A2A48C9BB16B07062A01E62897"); SimpleFeature feature; - feature.whitelist()->insert(kIdFooHashed); + feature.whitelist()->push_back(kIdFooHashed); EXPECT_EQ( Feature::IS_AVAILABLE, @@ -171,13 +175,13 @@ TEST(SimpleFeatureTest, HashedIdWhitelist) { Feature::UNSPECIFIED_PLATFORM).result()); } -TEST(SimpleFeatureTest, Blacklist) { +TEST_F(SimpleFeatureTest, Blacklist) { const std::string kIdFoo("fooabbbbccccddddeeeeffffgggghhhh"); const std::string kIdBar("barabbbbccccddddeeeeffffgggghhhh"); const std::string kIdBaz("bazabbbbccccddddeeeeffffgggghhhh"); SimpleFeature feature; - feature.blacklist()->insert(kIdFoo); - feature.blacklist()->insert(kIdBar); + feature.blacklist()->push_back(kIdFoo); + feature.blacklist()->push_back(kIdBar); EXPECT_EQ( Feature::FOUND_IN_BLACKLIST, @@ -210,14 +214,14 @@ TEST(SimpleFeatureTest, Blacklist) { Feature::UNSPECIFIED_PLATFORM).result()); } -TEST(SimpleFeatureTest, HashedIdBlacklist) { +TEST_F(SimpleFeatureTest, HashedIdBlacklist) { // echo -n "fooabbbbccccddddeeeeffffgggghhhh" | // sha1sum | tr '[:lower:]' '[:upper:]' const std::string kIdFoo("fooabbbbccccddddeeeeffffgggghhhh"); const std::string kIdFooHashed("55BC7228A0D502A2A48C9BB16B07062A01E62897"); SimpleFeature feature; - feature.blacklist()->insert(kIdFooHashed); + feature.blacklist()->push_back(kIdFooHashed); EXPECT_EQ( Feature::FOUND_IN_BLACKLIST, @@ -249,10 +253,10 @@ TEST(SimpleFeatureTest, HashedIdBlacklist) { Feature::UNSPECIFIED_PLATFORM).result()); } -TEST(SimpleFeatureTest, PackageType) { +TEST_F(SimpleFeatureTest, PackageType) { SimpleFeature feature; - feature.extension_types()->insert(Manifest::TYPE_EXTENSION); - feature.extension_types()->insert(Manifest::TYPE_LEGACY_PACKAGED_APP); + feature.extension_types()->push_back(Manifest::TYPE_EXTENSION); + feature.extension_types()->push_back(Manifest::TYPE_LEGACY_PACKAGED_APP); EXPECT_EQ( Feature::IS_AVAILABLE, @@ -285,12 +289,12 @@ TEST(SimpleFeatureTest, PackageType) { Feature::UNSPECIFIED_PLATFORM).result()); } -TEST(SimpleFeatureTest, Context) { +TEST_F(SimpleFeatureTest, Context) { SimpleFeature feature; feature.set_name("somefeature"); - feature.contexts()->insert(Feature::BLESSED_EXTENSION_CONTEXT); - feature.extension_types()->insert(Manifest::TYPE_LEGACY_PACKAGED_APP); - feature.platforms()->insert(Feature::CHROMEOS_PLATFORM); + feature.contexts()->push_back(Feature::BLESSED_EXTENSION_CONTEXT); + feature.extension_types()->push_back(Manifest::TYPE_LEGACY_PACKAGED_APP); + feature.platforms()->push_back(Feature::CHROMEOS_PLATFORM); feature.set_min_manifest_version(21); feature.set_max_manifest_version(25); @@ -307,14 +311,14 @@ TEST(SimpleFeatureTest, Context) { EXPECT_EQ("", error); ASSERT_TRUE(extension.get()); - feature.whitelist()->insert("monkey"); + feature.whitelist()->push_back("monkey"); EXPECT_EQ(Feature::NOT_FOUND_IN_WHITELIST, feature.IsAvailableToContext( extension.get(), Feature::BLESSED_EXTENSION_CONTEXT, Feature::CHROMEOS_PLATFORM).result()); feature.whitelist()->clear(); feature.extension_types()->clear(); - feature.extension_types()->insert(Manifest::TYPE_THEME); + feature.extension_types()->push_back(Manifest::TYPE_THEME); { Feature::Availability availability = feature.IsAvailableToContext( extension.get(), Feature::BLESSED_EXTENSION_CONTEXT, @@ -326,10 +330,10 @@ TEST(SimpleFeatureTest, Context) { } feature.extension_types()->clear(); - feature.extension_types()->insert(Manifest::TYPE_LEGACY_PACKAGED_APP); + feature.extension_types()->push_back(Manifest::TYPE_LEGACY_PACKAGED_APP); feature.contexts()->clear(); - feature.contexts()->insert(Feature::UNBLESSED_EXTENSION_CONTEXT); - feature.contexts()->insert(Feature::CONTENT_SCRIPT_CONTEXT); + feature.contexts()->push_back(Feature::UNBLESSED_EXTENSION_CONTEXT); + feature.contexts()->push_back(Feature::CONTENT_SCRIPT_CONTEXT); { Feature::Availability availability = feature.IsAvailableToContext( extension.get(), Feature::BLESSED_EXTENSION_CONTEXT, @@ -340,7 +344,7 @@ TEST(SimpleFeatureTest, Context) { availability.message()); } - feature.contexts()->insert(Feature::WEB_PAGE_CONTEXT); + feature.contexts()->push_back(Feature::WEB_PAGE_CONTEXT); { Feature::Availability availability = feature.IsAvailableToContext( extension.get(), Feature::BLESSED_EXTENSION_CONTEXT, @@ -352,7 +356,7 @@ TEST(SimpleFeatureTest, Context) { } feature.contexts()->clear(); - feature.contexts()->insert(Feature::BLESSED_EXTENSION_CONTEXT); + feature.contexts()->push_back(Feature::BLESSED_EXTENSION_CONTEXT); feature.set_location(SimpleFeature::COMPONENT_LOCATION); EXPECT_EQ(Feature::INVALID_LOCATION, feature.IsAvailableToContext( extension.get(), Feature::BLESSED_EXTENSION_CONTEXT, @@ -376,7 +380,7 @@ TEST(SimpleFeatureTest, Context) { feature.set_max_manifest_version(25); } -TEST(SimpleFeatureTest, Location) { +TEST_F(SimpleFeatureTest, Location) { // Component extensions can access any location. EXPECT_TRUE(LocationIsAvailable(SimpleFeature::COMPONENT_LOCATION, Manifest::COMPONENT)); @@ -423,9 +427,9 @@ TEST(SimpleFeatureTest, Location) { Manifest::EXTERNAL_COMPONENT)); } -TEST(SimpleFeatureTest, Platform) { +TEST_F(SimpleFeatureTest, Platform) { SimpleFeature feature; - feature.platforms()->insert(Feature::CHROMEOS_PLATFORM); + feature.platforms()->push_back(Feature::CHROMEOS_PLATFORM); EXPECT_EQ(Feature::IS_AVAILABLE, feature.IsAvailableToManifest(std::string(), Manifest::TYPE_UNKNOWN, @@ -441,7 +445,7 @@ TEST(SimpleFeatureTest, Platform) { Feature::UNSPECIFIED_PLATFORM).result()); } -TEST(SimpleFeatureTest, ManifestVersion) { +TEST_F(SimpleFeatureTest, ManifestVersion) { SimpleFeature feature; feature.set_min_manifest_version(5); @@ -500,7 +504,7 @@ TEST(SimpleFeatureTest, ManifestVersion) { Feature::UNSPECIFIED_PLATFORM).result()); } -TEST(SimpleFeatureTest, ParseNull) { +TEST_F(SimpleFeatureTest, ParseNull) { scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue()); scoped_ptr<SimpleFeature> feature(new SimpleFeature()); feature->Parse(value.get()); @@ -513,7 +517,7 @@ TEST(SimpleFeatureTest, ParseNull) { EXPECT_EQ(0, feature->max_manifest_version()); } -TEST(SimpleFeatureTest, ParseWhitelist) { +TEST_F(SimpleFeatureTest, ParseWhitelist) { scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue()); base::ListValue* whitelist = new base::ListValue(); whitelist->Append(new base::StringValue("foo")); @@ -522,11 +526,11 @@ TEST(SimpleFeatureTest, ParseWhitelist) { scoped_ptr<SimpleFeature> feature(new SimpleFeature()); feature->Parse(value.get()); EXPECT_EQ(2u, feature->whitelist()->size()); - EXPECT_TRUE(feature->whitelist()->count("foo")); - EXPECT_TRUE(feature->whitelist()->count("bar")); + EXPECT_TRUE(STLCount(*(feature->whitelist()), "foo")); + EXPECT_TRUE(STLCount(*(feature->whitelist()), "bar")); } -TEST(SimpleFeatureTest, ParsePackageTypes) { +TEST_F(SimpleFeatureTest, ParsePackageTypes) { scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue()); base::ListValue* extension_types = new base::ListValue(); extension_types->Append(new base::StringValue("extension")); @@ -539,13 +543,19 @@ TEST(SimpleFeatureTest, ParsePackageTypes) { scoped_ptr<SimpleFeature> feature(new SimpleFeature()); feature->Parse(value.get()); EXPECT_EQ(6u, feature->extension_types()->size()); - EXPECT_TRUE(feature->extension_types()->count(Manifest::TYPE_EXTENSION)); - EXPECT_TRUE(feature->extension_types()->count(Manifest::TYPE_THEME)); - EXPECT_TRUE(feature->extension_types()->count( - Manifest::TYPE_LEGACY_PACKAGED_APP)); - EXPECT_TRUE(feature->extension_types()->count(Manifest::TYPE_HOSTED_APP)); - EXPECT_TRUE(feature->extension_types()->count(Manifest::TYPE_PLATFORM_APP)); - EXPECT_TRUE(feature->extension_types()->count(Manifest::TYPE_SHARED_MODULE)); + EXPECT_TRUE( + STLCount(*(feature->extension_types()), Manifest::TYPE_EXTENSION)); + EXPECT_TRUE( + STLCount(*(feature->extension_types()), Manifest::TYPE_THEME)); + EXPECT_TRUE( + STLCount( + *(feature->extension_types()), Manifest::TYPE_LEGACY_PACKAGED_APP)); + EXPECT_TRUE( + STLCount(*(feature->extension_types()), Manifest::TYPE_HOSTED_APP)); + EXPECT_TRUE( + STLCount(*(feature->extension_types()), Manifest::TYPE_PLATFORM_APP)); + EXPECT_TRUE( + STLCount(*(feature->extension_types()), Manifest::TYPE_SHARED_MODULE)); value->SetString("extension_types", "all"); scoped_ptr<SimpleFeature> feature2(new SimpleFeature()); @@ -553,7 +563,7 @@ TEST(SimpleFeatureTest, ParsePackageTypes) { EXPECT_EQ(*(feature->extension_types()), *(feature2->extension_types())); } -TEST(SimpleFeatureTest, ParseContexts) { +TEST_F(SimpleFeatureTest, ParseContexts) { scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue()); base::ListValue* contexts = new base::ListValue(); contexts->Append(new base::StringValue("blessed_extension")); @@ -566,11 +576,16 @@ TEST(SimpleFeatureTest, ParseContexts) { scoped_ptr<SimpleFeature> feature(new SimpleFeature()); feature->Parse(value.get()); EXPECT_EQ(6u, feature->contexts()->size()); - EXPECT_TRUE(feature->contexts()->count(Feature::BLESSED_EXTENSION_CONTEXT)); - EXPECT_TRUE(feature->contexts()->count(Feature::UNBLESSED_EXTENSION_CONTEXT)); - EXPECT_TRUE(feature->contexts()->count(Feature::CONTENT_SCRIPT_CONTEXT)); - EXPECT_TRUE(feature->contexts()->count(Feature::WEB_PAGE_CONTEXT)); - EXPECT_TRUE(feature->contexts()->count(Feature::BLESSED_WEB_PAGE_CONTEXT)); + EXPECT_TRUE( + STLCount(*(feature->contexts()), Feature::BLESSED_EXTENSION_CONTEXT)); + EXPECT_TRUE( + STLCount(*(feature->contexts()), Feature::UNBLESSED_EXTENSION_CONTEXT)); + EXPECT_TRUE( + STLCount(*(feature->contexts()), Feature::CONTENT_SCRIPT_CONTEXT)); + EXPECT_TRUE( + STLCount(*(feature->contexts()), Feature::WEB_PAGE_CONTEXT)); + EXPECT_TRUE( + STLCount(*(feature->contexts()), Feature::BLESSED_WEB_PAGE_CONTEXT)); value->SetString("contexts", "all"); scoped_ptr<SimpleFeature> feature2(new SimpleFeature()); @@ -578,7 +593,7 @@ TEST(SimpleFeatureTest, ParseContexts) { EXPECT_EQ(*(feature->contexts()), *(feature2->contexts())); } -TEST(SimpleFeatureTest, ParseLocation) { +TEST_F(SimpleFeatureTest, ParseLocation) { scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue()); value->SetString("location", "component"); scoped_ptr<SimpleFeature> feature(new SimpleFeature()); @@ -586,7 +601,7 @@ TEST(SimpleFeatureTest, ParseLocation) { EXPECT_EQ(SimpleFeature::COMPONENT_LOCATION, feature->location()); } -TEST(SimpleFeatureTest, ParsePlatforms) { +TEST_F(SimpleFeatureTest, ParsePlatforms) { scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue()); scoped_ptr<SimpleFeature> feature(new SimpleFeature()); base::ListValue* platforms = new base::ListValue(); @@ -609,15 +624,15 @@ TEST(SimpleFeatureTest, ParsePlatforms) { platforms->AppendString("win"); platforms->AppendString("chromeos"); feature->Parse(value.get()); - std::set<Feature::Platform> expected_platforms; - expected_platforms.insert(Feature::CHROMEOS_PLATFORM); - expected_platforms.insert(Feature::WIN_PLATFORM); + std::vector<Feature::Platform> expected_platforms; + expected_platforms.push_back(Feature::CHROMEOS_PLATFORM); + expected_platforms.push_back(Feature::WIN_PLATFORM); EXPECT_FALSE(feature->platforms()->empty()); EXPECT_EQ(expected_platforms, *feature->platforms()); } -TEST(SimpleFeatureTest, ParseManifestVersion) { +TEST_F(SimpleFeatureTest, ParseManifestVersion) { scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue()); value->SetInteger("min_manifest_version", 1); value->SetInteger("max_manifest_version", 5); @@ -627,13 +642,13 @@ TEST(SimpleFeatureTest, ParseManifestVersion) { EXPECT_EQ(5, feature->max_manifest_version()); } -TEST(SimpleFeatureTest, Inheritance) { +TEST_F(SimpleFeatureTest, Inheritance) { SimpleFeature feature; - feature.whitelist()->insert("foo"); - feature.extension_types()->insert(Manifest::TYPE_THEME); - feature.contexts()->insert(Feature::BLESSED_EXTENSION_CONTEXT); + feature.whitelist()->push_back("foo"); + feature.extension_types()->push_back(Manifest::TYPE_THEME); + feature.contexts()->push_back(Feature::BLESSED_EXTENSION_CONTEXT); feature.set_location(SimpleFeature::COMPONENT_LOCATION); - feature.platforms()->insert(Feature::CHROMEOS_PLATFORM); + feature.platforms()->push_back(Feature::CHROMEOS_PLATFORM); feature.set_min_manifest_version(1); feature.set_max_manifest_version(2); @@ -644,10 +659,10 @@ TEST(SimpleFeatureTest, Inheritance) { EXPECT_EQ(1u, feature.whitelist()->size()); EXPECT_EQ(1u, feature.extension_types()->size()); EXPECT_EQ(1u, feature.contexts()->size()); - EXPECT_EQ(1u, feature.whitelist()->count("foo")); + EXPECT_EQ(1, STLCount(*(feature.whitelist()), "foo")); EXPECT_EQ(SimpleFeature::COMPONENT_LOCATION, feature.location()); EXPECT_EQ(1u, feature.platforms()->size()); - EXPECT_EQ(1u, feature.platforms()->count(Feature::CHROMEOS_PLATFORM)); + EXPECT_EQ(1, STLCount(*(feature.platforms()), Feature::CHROMEOS_PLATFORM)); EXPECT_EQ(1, feature.min_manifest_version()); EXPECT_EQ(2, feature.max_manifest_version()); @@ -668,15 +683,17 @@ TEST(SimpleFeatureTest, Inheritance) { EXPECT_EQ(1u, feature.whitelist()->size()); EXPECT_EQ(1u, feature.extension_types()->size()); EXPECT_EQ(1u, feature.contexts()->size()); - EXPECT_EQ(1u, feature.whitelist()->count("bar")); - EXPECT_EQ(1u, feature.extension_types()->count(Manifest::TYPE_EXTENSION)); - EXPECT_EQ(1u, - feature.contexts()->count(Feature::UNBLESSED_EXTENSION_CONTEXT)); + EXPECT_EQ(1, STLCount(*(feature.whitelist()), "bar")); + EXPECT_EQ(1, + STLCount(*(feature.extension_types()), Manifest::TYPE_EXTENSION)); + EXPECT_EQ(1, + STLCount( + *(feature.contexts()), Feature::UNBLESSED_EXTENSION_CONTEXT)); EXPECT_EQ(2, feature.min_manifest_version()); EXPECT_EQ(3, feature.max_manifest_version()); } -TEST(SimpleFeatureTest, CommandLineSwitch) { +TEST_F(SimpleFeatureTest, CommandLineSwitch) { SimpleFeature feature; feature.set_command_line_switch("laser-beams"); { @@ -710,4 +727,23 @@ TEST(SimpleFeatureTest, CommandLineSwitch) { } } +TEST_F(SimpleFeatureTest, IsIdInArray) { + EXPECT_FALSE(SimpleFeature::IsIdInArray("", {}, 0)); + EXPECT_FALSE(SimpleFeature::IsIdInArray( + "bbbbccccdddddddddeeeeeeffffgghhh", {}, 0)); + + const char* const kIdArray[] = { + "bbbbccccdddddddddeeeeeeffffgghhh", + // aaaabbbbccccddddeeeeffffgggghhhh + "9A0417016F345C934A1A88F55CA17C05014EEEBA" + }; + EXPECT_FALSE(SimpleFeature::IsIdInArray("", kIdArray, arraysize(kIdArray))); + EXPECT_FALSE(SimpleFeature::IsIdInArray( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", kIdArray, arraysize(kIdArray))); + EXPECT_TRUE(SimpleFeature::IsIdInArray( + "bbbbccccdddddddddeeeeeeffffgghhh", kIdArray, arraysize(kIdArray))); + EXPECT_TRUE(SimpleFeature::IsIdInArray( + "aaaabbbbccccddddeeeeffffgggghhhh", kIdArray, arraysize(kIdArray))); +} + } // namespace extensions |