1// Copyright 2014 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/common/extensions/features/chrome_channel_feature_filter.h" 6 7#include <string> 8 9#include "base/memory/scoped_ptr.h" 10#include "base/values.h" 11#include "chrome/common/chrome_version_info.h" 12#include "chrome/common/extensions/features/feature_channel.h" 13#include "extensions/common/features/base_feature_provider.h" 14#include "extensions/common/features/complex_feature.h" 15#include "extensions/common/features/permission_feature.h" 16#include "extensions/common/features/simple_feature.h" 17#include "extensions/common/value_builder.h" 18#include "testing/gtest/include/gtest/gtest.h" 19 20using chrome::VersionInfo; 21 22namespace extensions { 23namespace { 24 25template <class FeatureClass> 26SimpleFeature* CreateFeature() { 27 SimpleFeature* feature = new FeatureClass(); 28 feature->AddFilter( 29 scoped_ptr<SimpleFeatureFilter>(new ChromeChannelFeatureFilter(feature))); 30 return feature; 31} 32 33Feature::AvailabilityResult IsAvailableInChannel( 34 const std::string& channel, 35 VersionInfo::Channel channel_for_testing) { 36 ScopedCurrentChannel current_channel(channel_for_testing); 37 38 SimpleFeature feature; 39 feature.AddFilter(scoped_ptr<SimpleFeatureFilter>( 40 new ChromeChannelFeatureFilter(&feature))); 41 42 base::DictionaryValue feature_value; 43 feature_value.SetString("channel", channel); 44 feature.Parse(&feature_value); 45 46 return feature.IsAvailableToManifest("random-extension", 47 Manifest::TYPE_UNKNOWN, 48 Manifest::INVALID_LOCATION, 49 -1, 50 Feature::GetCurrentPlatform()).result(); 51} 52 53} // namespace 54 55class ChromeChannelFeatureFilterTest : public testing::Test { 56 protected: 57 ChromeChannelFeatureFilterTest() 58 : current_channel_(VersionInfo::CHANNEL_UNKNOWN) {} 59 virtual ~ChromeChannelFeatureFilterTest() {} 60 61 private: 62 ScopedCurrentChannel current_channel_; 63 64 DISALLOW_COPY_AND_ASSIGN(ChromeChannelFeatureFilterTest); 65}; 66 67// Tests that all combinations of feature channel and Chrome channel correctly 68// compute feature availability. 69TEST_F(ChromeChannelFeatureFilterTest, SupportedChannel) { 70 // stable supported. 71 EXPECT_EQ(Feature::IS_AVAILABLE, 72 IsAvailableInChannel("stable", VersionInfo::CHANNEL_UNKNOWN)); 73 EXPECT_EQ(Feature::IS_AVAILABLE, 74 IsAvailableInChannel("stable", VersionInfo::CHANNEL_CANARY)); 75 EXPECT_EQ(Feature::IS_AVAILABLE, 76 IsAvailableInChannel("stable", VersionInfo::CHANNEL_DEV)); 77 EXPECT_EQ(Feature::IS_AVAILABLE, 78 IsAvailableInChannel("stable", VersionInfo::CHANNEL_BETA)); 79 EXPECT_EQ(Feature::IS_AVAILABLE, 80 IsAvailableInChannel("stable", VersionInfo::CHANNEL_STABLE)); 81 82 // beta supported. 83 EXPECT_EQ(Feature::IS_AVAILABLE, 84 IsAvailableInChannel("beta", VersionInfo::CHANNEL_UNKNOWN)); 85 EXPECT_EQ(Feature::IS_AVAILABLE, 86 IsAvailableInChannel("beta", VersionInfo::CHANNEL_CANARY)); 87 EXPECT_EQ(Feature::IS_AVAILABLE, 88 IsAvailableInChannel("beta", VersionInfo::CHANNEL_DEV)); 89 EXPECT_EQ(Feature::IS_AVAILABLE, 90 IsAvailableInChannel("beta", VersionInfo::CHANNEL_BETA)); 91 EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL, 92 IsAvailableInChannel("beta", VersionInfo::CHANNEL_STABLE)); 93 94 // dev supported. 95 EXPECT_EQ(Feature::IS_AVAILABLE, 96 IsAvailableInChannel("dev", VersionInfo::CHANNEL_UNKNOWN)); 97 EXPECT_EQ(Feature::IS_AVAILABLE, 98 IsAvailableInChannel("dev", VersionInfo::CHANNEL_CANARY)); 99 EXPECT_EQ(Feature::IS_AVAILABLE, 100 IsAvailableInChannel("dev", VersionInfo::CHANNEL_DEV)); 101 EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL, 102 IsAvailableInChannel("dev", VersionInfo::CHANNEL_BETA)); 103 EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL, 104 IsAvailableInChannel("dev", VersionInfo::CHANNEL_STABLE)); 105 106 // canary supported. 107 EXPECT_EQ(Feature::IS_AVAILABLE, 108 IsAvailableInChannel("canary", VersionInfo::CHANNEL_UNKNOWN)); 109 EXPECT_EQ(Feature::IS_AVAILABLE, 110 IsAvailableInChannel("canary", VersionInfo::CHANNEL_CANARY)); 111 EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL, 112 IsAvailableInChannel("canary", VersionInfo::CHANNEL_DEV)); 113 EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL, 114 IsAvailableInChannel("canary", VersionInfo::CHANNEL_BETA)); 115 EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL, 116 IsAvailableInChannel("canary", VersionInfo::CHANNEL_STABLE)); 117 118 // trunk supported. 119 EXPECT_EQ(Feature::IS_AVAILABLE, 120 IsAvailableInChannel("trunk", VersionInfo::CHANNEL_UNKNOWN)); 121 EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL, 122 IsAvailableInChannel("trunk", VersionInfo::CHANNEL_CANARY)); 123 EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL, 124 IsAvailableInChannel("trunk", VersionInfo::CHANNEL_DEV)); 125 EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL, 126 IsAvailableInChannel("trunk", VersionInfo::CHANNEL_BETA)); 127 EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL, 128 IsAvailableInChannel("trunk", VersionInfo::CHANNEL_STABLE)); 129} 130 131// Tests the validation of features with channel entries. 132TEST_F(ChromeChannelFeatureFilterTest, FeatureValidation) { 133 scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue()); 134 135 base::DictionaryValue* feature1 = new base::DictionaryValue(); 136 feature1->SetString("channel", "trunk"); 137 value->Set("feature1", feature1); 138 139 base::DictionaryValue* feature2 = new base::DictionaryValue(); 140 feature2->SetString("channel", "trunk"); 141 base::ListValue* extension_types = new base::ListValue(); 142 extension_types->Append(new base::StringValue("extension")); 143 feature2->Set("extension_types", extension_types); 144 base::ListValue* contexts = new base::ListValue(); 145 contexts->Append(new base::StringValue("blessed_extension")); 146 feature2->Set("contexts", contexts); 147 value->Set("feature2", feature2); 148 149 scoped_ptr<BaseFeatureProvider> provider( 150 new BaseFeatureProvider(*value, CreateFeature<PermissionFeature>)); 151 152 // feature1 won't validate because it lacks an extension type. 153 EXPECT_FALSE(provider->GetFeature("feature1")); 154 155 // If we add one, it works. 156 feature1->Set("extension_types", extension_types->DeepCopy()); 157 provider.reset( 158 new BaseFeatureProvider(*value, CreateFeature<PermissionFeature>)); 159 EXPECT_TRUE(provider->GetFeature("feature1")); 160 161 // Remove the channel, and feature1 won't validate. 162 feature1->Remove("channel", NULL); 163 provider.reset( 164 new BaseFeatureProvider(*value, CreateFeature<PermissionFeature>)); 165 EXPECT_FALSE(provider->GetFeature("feature1")); 166 167 // feature2 won't validate because of the presence of "contexts". 168 EXPECT_FALSE(provider->GetFeature("feature2")); 169 170 // If we remove it, it works. 171 feature2->Remove("contexts", NULL); 172 provider.reset( 173 new BaseFeatureProvider(*value, CreateFeature<PermissionFeature>)); 174 EXPECT_TRUE(provider->GetFeature("feature2")); 175} 176 177// Tests simple feature availability across channels. 178TEST_F(ChromeChannelFeatureFilterTest, SimpleFeatureAvailability) { 179 scoped_ptr<base::DictionaryValue> rule( 180 DictionaryBuilder() 181 .Set("feature1", 182 ListBuilder() 183 .Append(DictionaryBuilder().Set("channel", "beta").Set( 184 "extension_types", ListBuilder().Append("extension"))) 185 .Append(DictionaryBuilder().Set("channel", "beta").Set( 186 "extension_types", 187 ListBuilder().Append("legacy_packaged_app")))) 188 .Build()); 189 190 scoped_ptr<BaseFeatureProvider> provider( 191 new BaseFeatureProvider(*rule, CreateFeature<SimpleFeature>)); 192 193 Feature* feature = provider->GetFeature("feature1"); 194 EXPECT_TRUE(feature); 195 196 // Make sure both rules are applied correctly. 197 { 198 ScopedCurrentChannel current_channel(VersionInfo::CHANNEL_BETA); 199 EXPECT_EQ( 200 Feature::IS_AVAILABLE, 201 feature->IsAvailableToManifest("1", 202 Manifest::TYPE_EXTENSION, 203 Manifest::INVALID_LOCATION, 204 Feature::UNSPECIFIED_PLATFORM).result()); 205 EXPECT_EQ( 206 Feature::IS_AVAILABLE, 207 feature->IsAvailableToManifest("2", 208 Manifest::TYPE_LEGACY_PACKAGED_APP, 209 Manifest::INVALID_LOCATION, 210 Feature::UNSPECIFIED_PLATFORM).result()); 211 } 212 { 213 ScopedCurrentChannel current_channel(VersionInfo::CHANNEL_STABLE); 214 EXPECT_NE( 215 Feature::IS_AVAILABLE, 216 feature->IsAvailableToManifest("1", 217 Manifest::TYPE_EXTENSION, 218 Manifest::INVALID_LOCATION, 219 Feature::UNSPECIFIED_PLATFORM).result()); 220 EXPECT_NE( 221 Feature::IS_AVAILABLE, 222 feature->IsAvailableToManifest("2", 223 Manifest::TYPE_LEGACY_PACKAGED_APP, 224 Manifest::INVALID_LOCATION, 225 Feature::UNSPECIFIED_PLATFORM).result()); 226 } 227} 228 229// Tests complex feature availability across channels. 230TEST_F(ChromeChannelFeatureFilterTest, ComplexFeatureAvailability) { 231 scoped_ptr<ComplexFeature::FeatureList> features( 232 new ComplexFeature::FeatureList()); 233 234 // Rule: "extension", channel trunk. 235 scoped_ptr<SimpleFeature> simple_feature(CreateFeature<SimpleFeature>()); 236 scoped_ptr<base::DictionaryValue> rule( 237 DictionaryBuilder() 238 .Set("channel", "trunk") 239 .Set("extension_types", ListBuilder().Append("extension")) 240 .Build()); 241 simple_feature->Parse(rule.get()); 242 features->push_back(simple_feature.release()); 243 244 // Rule: "legacy_packaged_app", channel stable. 245 simple_feature.reset(CreateFeature<SimpleFeature>()); 246 rule = 247 DictionaryBuilder() 248 .Set("channel", "stable") 249 .Set("extension_types", ListBuilder().Append("legacy_packaged_app")) 250 .Build(); 251 simple_feature->Parse(rule.get()); 252 features->push_back(simple_feature.release()); 253 254 scoped_ptr<ComplexFeature> feature(new ComplexFeature(features.Pass())); 255 256 // Test match 1st rule. 257 { 258 ScopedCurrentChannel current_channel(VersionInfo::CHANNEL_UNKNOWN); 259 EXPECT_EQ( 260 Feature::IS_AVAILABLE, 261 feature->IsAvailableToManifest("1", 262 Manifest::TYPE_EXTENSION, 263 Manifest::INVALID_LOCATION, 264 Feature::UNSPECIFIED_PLATFORM, 265 Feature::GetCurrentPlatform()).result()); 266 } 267 268 // Test match 2nd rule. 269 { 270 ScopedCurrentChannel current_channel(VersionInfo::CHANNEL_BETA); 271 EXPECT_EQ( 272 Feature::IS_AVAILABLE, 273 feature->IsAvailableToManifest("2", 274 Manifest::TYPE_LEGACY_PACKAGED_APP, 275 Manifest::INVALID_LOCATION, 276 Feature::UNSPECIFIED_PLATFORM, 277 Feature::GetCurrentPlatform()).result()); 278 } 279 280 // Test feature not available to extensions above channel unknown. 281 { 282 ScopedCurrentChannel current_channel(VersionInfo::CHANNEL_BETA); 283 EXPECT_NE( 284 Feature::IS_AVAILABLE, 285 feature->IsAvailableToManifest("1", 286 Manifest::TYPE_EXTENSION, 287 Manifest::INVALID_LOCATION, 288 Feature::UNSPECIFIED_PLATFORM, 289 Feature::GetCurrentPlatform()).result()); 290 } 291} 292 293} // namespace extensions 294