// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "android_webview/browser/aw_field_trials.h"

#include "android_webview/browser/variations/aw_entropy_providers.h"
#include "components/variations/feature_overrides.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"

namespace android_webview {

namespace {

const char kTestEnabledFeatureName[] = "AwFieldTrialTestEnabledFeature";
const char kTestDisabledFeatureName[] = "AwFieldTrialTestDisabledFeature";
const char kAllowlistedFeatureName[] = "MyTestStudy";

BASE_FEATURE(kTestEnabledFeature,
             kTestEnabledFeatureName,
             base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kTestDisabledFeature,
             kTestDisabledFeatureName,
             base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kAllowlistedFeature,
             kAllowlistedFeatureName,
             base::FEATURE_DISABLED_BY_DEFAULT);

}  // namespace

class AwFieldTrialsTest : public testing::Test {
 public:
  AwFieldTrialsTest() {
    // Without overrides, the states should correspond to the feature defaults.
    EXPECT_TRUE(base::FeatureList::IsEnabled(kTestEnabledFeature));
    EXPECT_FALSE(base::FeatureList::IsEnabled(kTestDisabledFeature));
    original_feature_list_ = base::FeatureList::ClearInstanceForTesting();
  }

  ~AwFieldTrialsTest() override {
    // Restore feature list.
    if (original_feature_list_) {
      base::FeatureList::ClearInstanceForTesting();
      base::FeatureList::RestoreInstanceForTesting(
          std::move(original_feature_list_));
    }
  }

  AwFieldTrialsTest(const AwFieldTrialsTest&) = delete;
  AwFieldTrialsTest& operator=(const AwFieldTrialsTest&) = delete;

  void SetUpFeatureTrial(base::FeatureList* feature_list,
                         const char* feature_name,
                         base::FeatureList::OverrideState override_state) {
    // Set-up a trial enable/disable test feature. An empty limited entropy
    // randomization is used since only default_entropy() will used on the
    // constructed `entropy_providers` instance.
    auto standard_providers = std::make_unique<variations::EntropyProviders>(
        "client_id", variations::ValueInRange{0, 8000},
        /*limited_entropy_randomization_source=*/std::string_view());
    const std::set<std::string_view> kAllowlist{};
    // Note: 0 is a valid nonembedded low entropy source.
    AwEntropyProviders entropy_providers(
        std::move(standard_providers), /*nonembedded_low_entropy_source=*/0,
        std::make_unique<const std::set<std::string_view>>(kAllowlist));
    scoped_refptr<base::FieldTrial> trial(
        base::FieldTrialList::FactoryGetFieldTrial(
            feature_name, /*total_probability=*/1000, "Default",
            entropy_providers.default_entropy()));
    feature_list->RegisterFieldTrialOverride(feature_name, override_state,
                                             trial.get());
  }

 private:
  //  Save the present FeatureList and restore it after test finish.
  std::unique_ptr<base::FeatureList> original_feature_list_;
};

TEST_F(AwFieldTrialsTest, TrialEnableFeatureOverrides) {
  AwFieldTrials aw_field_trials;
  auto feature_list = std::make_unique<base::FeatureList>();
  SetUpFeatureTrial(feature_list.get(), kTestDisabledFeatureName,
                    base::FeatureList::OVERRIDE_ENABLE_FEATURE);
  aw_field_trials.RegisterFeatureOverrides(feature_list.get());
  base::FeatureList::SetInstance(std::move(feature_list));

  EXPECT_TRUE(base::FeatureList::IsEnabled(kTestDisabledFeature));
}

TEST_F(AwFieldTrialsTest, TrialDisableFeatureOverrides) {
  AwFieldTrials aw_field_trials;
  auto feature_list = std::make_unique<base::FeatureList>();
  SetUpFeatureTrial(feature_list.get(), kTestDisabledFeatureName,
                    base::FeatureList::OVERRIDE_DISABLE_FEATURE);
  aw_field_trials.RegisterFeatureOverrides(feature_list.get());
  base::FeatureList::SetInstance(std::move(feature_list));

  EXPECT_FALSE(base::FeatureList::IsEnabled(kTestDisabledFeature));
}

TEST_F(AwFieldTrialsTest, CommandLineEnableFeatureOverrides) {
  AwFieldTrials aw_field_trials;
  auto feature_list = std::make_unique<base::FeatureList>();
  feature_list->InitFromCommandLine(kTestDisabledFeatureName, "");
  aw_field_trials.RegisterFeatureOverrides(feature_list.get());
  base::FeatureList::SetInstance(std::move(feature_list));

  EXPECT_TRUE(base::FeatureList::IsEnabled(kTestDisabledFeature));
}

TEST_F(AwFieldTrialsTest, CommandLineDisableFeatureOverrides) {
  AwFieldTrials aw_field_trials;
  auto feature_list = std::make_unique<base::FeatureList>();
  feature_list->InitFromCommandLine("", kTestDisabledFeatureName);
  aw_field_trials.RegisterFeatureOverrides(feature_list.get());
  base::FeatureList::SetInstance(std::move(feature_list));

  EXPECT_FALSE(base::FeatureList::IsEnabled(kTestDisabledFeature));
}

TEST_F(AwFieldTrialsTest, OnlyRegisterFeatureOverrides) {
  AwFieldTrials aw_field_trials;
  auto feature_list = std::make_unique<base::FeatureList>();
  // Register feature override.
  aw_field_trials.RegisterFeatureOverrides(feature_list.get());
  base::FeatureList::SetInstance(std::move(feature_list));

  EXPECT_FALSE(base::FeatureList::IsEnabled(kTestDisabledFeature));
}

TEST_F(AwFieldTrialsTest, AwFeatureOverrides_NoPreviousOverrides) {
  auto feature_list = std::make_unique<base::FeatureList>();
  {
    variations::FeatureOverrides aw_feature_overrides(*feature_list);
    aw_feature_overrides.DisableFeature(kTestEnabledFeature);
    aw_feature_overrides.EnableFeature(kTestDisabledFeature);
  }
  base::FeatureList::SetInstance(std::move(feature_list));

  // When there are no previous overrides, the state should match what's set up
  // via AwFeatureOverrides.
  EXPECT_FALSE(base::FeatureList::IsEnabled(kTestEnabledFeature));
  EXPECT_TRUE(base::FeatureList::IsEnabled(kTestDisabledFeature));
}

TEST_F(AwFieldTrialsTest, AwFeatureOverrides_WithPreviousOverrides) {
  auto feature_list = std::make_unique<base::FeatureList>();
  SetUpFeatureTrial(feature_list.get(), kTestDisabledFeatureName,
                    base::FeatureList::OVERRIDE_ENABLE_FEATURE);
  SetUpFeatureTrial(feature_list.get(), kTestEnabledFeatureName,
                    base::FeatureList::OVERRIDE_DISABLE_FEATURE);
  {
    variations::FeatureOverrides aw_feature_overrides(*feature_list);
    aw_feature_overrides.DisableFeature(kTestDisabledFeature);
    aw_feature_overrides.EnableFeature(kTestEnabledFeature);
  }
  base::FeatureList::SetInstance(std::move(feature_list));

  // When there are previous overrides (e.g. coming from server-side configs in
  // production), they should take precedence over AwFeatureOverrides.
  EXPECT_TRUE(base::FeatureList::IsEnabled(kTestDisabledFeature));
  EXPECT_FALSE(base::FeatureList::IsEnabled(kTestEnabledFeature));
}

TEST_F(AwFieldTrialsTest, AwFeatureOverrides_WithPreviousUseDefaultOverride) {
  auto feature_list = std::make_unique<base::FeatureList>();
  SetUpFeatureTrial(feature_list.get(), kTestEnabledFeatureName,
                    base::FeatureList::OVERRIDE_USE_DEFAULT);
  {
    variations::FeatureOverrides aw_feature_overrides(*feature_list);
    aw_feature_overrides.DisableFeature(kTestEnabledFeature);
  }
  base::FeatureList::SetInstance(std::move(feature_list));

  // Before querying the feature, the trial shouldn't be active.
  EXPECT_FALSE(base::FieldTrialList::IsTrialActive(kTestEnabledFeatureName));
  // OVERRIDE_USE_DEFAULT entries should be overridden by AwFeatureOverrides.
  EXPECT_FALSE(base::FeatureList::IsEnabled(kTestEnabledFeature));
  // The associated trial should have been activated.
  EXPECT_TRUE(base::FieldTrialList::IsTrialActive(kTestEnabledFeatureName));
}

TEST_F(AwFieldTrialsTest, AllowlistedFeature) {
  // Verify that an allowlisted feature can be created with AwEntropyProviders.

  AwFieldTrials aw_field_trials;
  auto feature_list = std::make_unique<base::FeatureList>();

  // Use a mock entropy source (0) that is different from the default if
  // possible, but here we just ensure it runs without crashing and registers
  // the override.
  SetUpFeatureTrial(feature_list.get(), kAllowlistedFeatureName,
                    base::FeatureList::OVERRIDE_ENABLE_FEATURE);

  aw_field_trials.RegisterFeatureOverrides(feature_list.get());
  base::FeatureList::SetInstance(std::move(feature_list));

  EXPECT_TRUE(base::FeatureList::IsEnabled(kAllowlistedFeature));
}

}  // namespace android_webview
