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

#include "chrome/browser/extensions/api/declarative_content/content_condition.h"

#include <vector>

#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/test/values_test_util.h"
#include "base/values.h"
#include "extensions/buildflags/buildflags.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

namespace extensions {

namespace {

class TestPredicate : public ContentPredicate {
 public:
  TestPredicate() = default;

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

  ContentPredicateEvaluator* GetEvaluator() const override {
    return nullptr;
  }
};

class TestPredicateFactoryGeneratingError : public ContentPredicateFactory {
 public:
  explicit TestPredicateFactoryGeneratingError(const std::string& error)
      : error_(error) {
  }

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

  std::unique_ptr<const ContentPredicate> CreatePredicate(
      const Extension* extension,
      const base::Value& value,
      std::string* error) override {
    *error = error_;
    return nullptr;
  }

 private:
  const std::string error_;
};

class TestPredicateFactoryGeneratingPredicate : public ContentPredicateFactory {
 public:
  TestPredicateFactoryGeneratingPredicate() = default;

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

  std::unique_ptr<const ContentPredicate> CreatePredicate(
      const Extension* extension,
      const base::Value& value,
      std::string* error) override {
    std::unique_ptr<const ContentPredicate> predicate(new TestPredicate);
    created_predicates_.push_back(predicate.get());
    return predicate;
  }

  const std::vector<raw_ptr<const ContentPredicate, VectorExperimental>>&
  created_predicates() const {
    return created_predicates_;
  }

 private:
  std::vector<raw_ptr<const ContentPredicate, VectorExperimental>>
      created_predicates_;
};

}  // namespace

using testing::HasSubstr;
using testing::UnorderedElementsAre;

TEST(DeclarativeContentConditionTest, UnknownPredicateName) {
  std::string error;
  std::unique_ptr<ContentCondition> condition = CreateContentCondition(
      nullptr, std::map<std::string, ContentPredicateFactory*>(),
      base::test::ParseJson(R"({
          "invalid": "foobar",
          "instanceType": "declarativeContent.PageStateMatcher",
      })"),
      &error);
  EXPECT_THAT(error, HasSubstr("Unknown condition attribute"));
  EXPECT_FALSE(condition);
}

TEST(DeclarativeContentConditionTest,
     PredicateWithErrorProducesEmptyCondition) {
  TestPredicateFactoryGeneratingError factory("error message");
  std::map<std::string, ContentPredicateFactory*> predicate_factories;
  predicate_factories["test_predicate"] = &factory;
  std::string error;
  std::unique_ptr<ContentCondition> condition = CreateContentCondition(
      nullptr, predicate_factories, base::test::ParseJson(R"({
          "test_predicate": "",
          "instanceType": "declarativeContent.PageStateMatcher",
      })"),
      &error);
  EXPECT_EQ("error message", error);
  EXPECT_FALSE(condition);
}

TEST(DeclarativeContentConditionTest, AllSpecifiedPredicatesCreated) {
  TestPredicateFactoryGeneratingPredicate factory1, factory2;
  std::map<std::string, ContentPredicateFactory*> predicate_factories;
  predicate_factories["test_predicate1"] = &factory1;
  predicate_factories["test_predicate2"] = &factory2;
  std::string error;
  std::unique_ptr<ContentCondition> condition = CreateContentCondition(
      nullptr, predicate_factories, base::test::ParseJson(R"({
          "test_predicate1": {},
          "test_predicate2": [],
          "instanceType": "declarativeContent.PageStateMatcher",
      })"),
      &error);
  ASSERT_TRUE(condition);
  ASSERT_EQ(1u, factory1.created_predicates().size());
  ASSERT_EQ(1u, factory2.created_predicates().size());
  std::vector<const ContentPredicate*> predicates;
  for (const auto& predicate : condition->predicates) {
    predicates.push_back(predicate.get());
  }
  EXPECT_THAT(predicates,
              UnorderedElementsAre(factory1.created_predicates()[0],
                                   factory2.created_predicates()[0]));
}

}  // namespace extensions
