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