blob: 806042b5d31f260aef5f21a07cf6713eabfbcf46 [file] [log] [blame]
// Copyright 2014 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/common/extensions/features/chrome_channel_feature_filter.h"
#include <memory>
#include <string>
#include <utility>
#include "base/macros.h"
#include "base/values.h"
#include "chrome/common/extensions/features/feature_channel.h"
#include "components/version_info/version_info.h"
#include "extensions/common/features/base_feature_provider.h"
#include "extensions/common/features/complex_feature.h"
#include "extensions/common/features/permission_feature.h"
#include "extensions/common/features/simple_feature.h"
#include "extensions/common/value_builder.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
namespace {
template <class FeatureClass>
SimpleFeature* CreateFeature() {
SimpleFeature* feature = new FeatureClass();
feature->AddFilter(std::unique_ptr<SimpleFeatureFilter>(
new ChromeChannelFeatureFilter(feature)));
return feature;
}
Feature::AvailabilityResult IsAvailableInChannel(
const std::string& channel,
version_info::Channel channel_for_testing) {
ScopedCurrentChannel current_channel(channel_for_testing);
SimpleFeature feature;
feature.AddFilter(std::unique_ptr<SimpleFeatureFilter>(
new ChromeChannelFeatureFilter(&feature)));
base::DictionaryValue feature_value;
feature_value.SetString("channel", channel);
feature.Parse(&feature_value);
return feature.IsAvailableToManifest("random-extension",
Manifest::TYPE_UNKNOWN,
Manifest::INVALID_LOCATION,
-1,
Feature::GetCurrentPlatform()).result();
}
} // namespace
class ChromeChannelFeatureFilterTest : public testing::Test {
protected:
ChromeChannelFeatureFilterTest()
: current_channel_(version_info::Channel::UNKNOWN) {}
~ChromeChannelFeatureFilterTest() override {}
private:
ScopedCurrentChannel current_channel_;
DISALLOW_COPY_AND_ASSIGN(ChromeChannelFeatureFilterTest);
};
// Tests that all combinations of feature channel and Chrome channel correctly
// compute feature availability.
TEST_F(ChromeChannelFeatureFilterTest, SupportedChannel) {
// stable supported.
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("stable", version_info::Channel::UNKNOWN));
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("stable", version_info::Channel::CANARY));
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("stable", version_info::Channel::DEV));
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("stable", version_info::Channel::BETA));
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("stable", version_info::Channel::STABLE));
// beta supported.
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("beta", version_info::Channel::UNKNOWN));
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("beta", version_info::Channel::CANARY));
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("beta", version_info::Channel::DEV));
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("beta", version_info::Channel::BETA));
EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL,
IsAvailableInChannel("beta", version_info::Channel::STABLE));
// dev supported.
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("dev", version_info::Channel::UNKNOWN));
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("dev", version_info::Channel::CANARY));
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("dev", version_info::Channel::DEV));
EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL,
IsAvailableInChannel("dev", version_info::Channel::BETA));
EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL,
IsAvailableInChannel("dev", version_info::Channel::STABLE));
// canary supported.
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("canary", version_info::Channel::UNKNOWN));
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("canary", version_info::Channel::CANARY));
EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL,
IsAvailableInChannel("canary", version_info::Channel::DEV));
EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL,
IsAvailableInChannel("canary", version_info::Channel::BETA));
EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL,
IsAvailableInChannel("canary", version_info::Channel::STABLE));
// trunk supported.
EXPECT_EQ(Feature::IS_AVAILABLE,
IsAvailableInChannel("trunk", version_info::Channel::UNKNOWN));
EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL,
IsAvailableInChannel("trunk", version_info::Channel::CANARY));
EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL,
IsAvailableInChannel("trunk", version_info::Channel::DEV));
EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL,
IsAvailableInChannel("trunk", version_info::Channel::BETA));
EXPECT_EQ(Feature::UNSUPPORTED_CHANNEL,
IsAvailableInChannel("trunk", version_info::Channel::STABLE));
}
// Tests the validation of features with channel entries.
TEST_F(ChromeChannelFeatureFilterTest, FeatureValidation) {
std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue());
base::DictionaryValue* feature1 = new base::DictionaryValue();
feature1->SetString("channel", "trunk");
value->Set("feature1", feature1);
base::DictionaryValue* feature2 = new base::DictionaryValue();
feature2->SetString("channel", "trunk");
base::ListValue* extension_types = new base::ListValue();
extension_types->AppendString("extension");
feature2->Set("extension_types", extension_types);
base::ListValue* contexts = new base::ListValue();
contexts->AppendString("blessed_extension");
feature2->Set("contexts", contexts);
value->Set("feature2", feature2);
std::unique_ptr<BaseFeatureProvider> provider(
new BaseFeatureProvider(*value, CreateFeature<PermissionFeature>));
// feature1 won't validate because it lacks an extension type.
EXPECT_FALSE(provider->GetFeature("feature1"));
// If we add one, it works.
feature1->Set("extension_types", extension_types->DeepCopy());
provider.reset(
new BaseFeatureProvider(*value, CreateFeature<PermissionFeature>));
EXPECT_TRUE(provider->GetFeature("feature1"));
// Remove the channel, and feature1 won't validate.
feature1->Remove("channel", NULL);
provider.reset(
new BaseFeatureProvider(*value, CreateFeature<PermissionFeature>));
EXPECT_FALSE(provider->GetFeature("feature1"));
// feature2 won't validate because of the presence of "contexts".
EXPECT_FALSE(provider->GetFeature("feature2"));
// If we remove it, it works.
feature2->Remove("contexts", NULL);
provider.reset(
new BaseFeatureProvider(*value, CreateFeature<PermissionFeature>));
EXPECT_TRUE(provider->GetFeature("feature2"));
}
// Tests simple feature availability across channels.
TEST_F(ChromeChannelFeatureFilterTest, SimpleFeatureAvailability) {
std::unique_ptr<base::DictionaryValue> rule(
DictionaryBuilder()
.Set("feature1",
ListBuilder()
.Append(DictionaryBuilder()
.Set("channel", "beta")
.Set("extension_types",
ListBuilder().Append("extension").Build())
.Build())
.Append(DictionaryBuilder()
.Set("channel", "beta")
.Set("extension_types",
ListBuilder()
.Append("legacy_packaged_app")
.Build())
.Build())
.Build())
.Build());
std::unique_ptr<BaseFeatureProvider> provider(
new BaseFeatureProvider(*rule, CreateFeature<SimpleFeature>));
Feature* feature = provider->GetFeature("feature1");
EXPECT_TRUE(feature);
// Make sure both rules are applied correctly.
{
ScopedCurrentChannel current_channel(version_info::Channel::BETA);
EXPECT_EQ(
Feature::IS_AVAILABLE,
feature->IsAvailableToManifest("1",
Manifest::TYPE_EXTENSION,
Manifest::INVALID_LOCATION,
Feature::UNSPECIFIED_PLATFORM).result());
EXPECT_EQ(
Feature::IS_AVAILABLE,
feature->IsAvailableToManifest("2",
Manifest::TYPE_LEGACY_PACKAGED_APP,
Manifest::INVALID_LOCATION,
Feature::UNSPECIFIED_PLATFORM).result());
}
{
ScopedCurrentChannel current_channel(version_info::Channel::STABLE);
EXPECT_NE(
Feature::IS_AVAILABLE,
feature->IsAvailableToManifest("1",
Manifest::TYPE_EXTENSION,
Manifest::INVALID_LOCATION,
Feature::UNSPECIFIED_PLATFORM).result());
EXPECT_NE(
Feature::IS_AVAILABLE,
feature->IsAvailableToManifest("2",
Manifest::TYPE_LEGACY_PACKAGED_APP,
Manifest::INVALID_LOCATION,
Feature::UNSPECIFIED_PLATFORM).result());
}
}
// Tests complex feature availability across channels.
TEST_F(ChromeChannelFeatureFilterTest, ComplexFeatureAvailability) {
std::unique_ptr<ComplexFeature::FeatureList> features(
new ComplexFeature::FeatureList());
// Rule: "extension", channel trunk.
std::unique_ptr<SimpleFeature> simple_feature(CreateFeature<SimpleFeature>());
std::unique_ptr<base::DictionaryValue> rule(
DictionaryBuilder()
.Set("channel", "trunk")
.Set("extension_types", ListBuilder().Append("extension").Build())
.Build());
simple_feature->Parse(rule.get());
features->push_back(std::move(simple_feature));
// Rule: "legacy_packaged_app", channel stable.
simple_feature.reset(CreateFeature<SimpleFeature>());
rule = DictionaryBuilder()
.Set("channel", "stable")
.Set("extension_types",
ListBuilder().Append("legacy_packaged_app").Build())
.Build();
simple_feature->Parse(rule.get());
features->push_back(std::move(simple_feature));
std::unique_ptr<ComplexFeature> feature(
new ComplexFeature(std::move(features)));
// Test match 1st rule.
{
ScopedCurrentChannel current_channel(version_info::Channel::UNKNOWN);
EXPECT_EQ(
Feature::IS_AVAILABLE,
feature->IsAvailableToManifest("1",
Manifest::TYPE_EXTENSION,
Manifest::INVALID_LOCATION,
Feature::UNSPECIFIED_PLATFORM,
Feature::GetCurrentPlatform()).result());
}
// Test match 2nd rule.
{
ScopedCurrentChannel current_channel(version_info::Channel::BETA);
EXPECT_EQ(
Feature::IS_AVAILABLE,
feature->IsAvailableToManifest("2",
Manifest::TYPE_LEGACY_PACKAGED_APP,
Manifest::INVALID_LOCATION,
Feature::UNSPECIFIED_PLATFORM,
Feature::GetCurrentPlatform()).result());
}
// Test feature not available to extensions above channel unknown.
{
ScopedCurrentChannel current_channel(version_info::Channel::BETA);
EXPECT_NE(
Feature::IS_AVAILABLE,
feature->IsAvailableToManifest("1",
Manifest::TYPE_EXTENSION,
Manifest::INVALID_LOCATION,
Feature::UNSPECIFIED_PLATFORM,
Feature::GetCurrentPlatform()).result());
}
}
} // namespace extensions