// 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 "components/policy/core/common/android/policy_converter.h"

#include <stddef.h>

#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/json/json_writer.h"
#include "base/types/expected_macros.h"
#include "base/values.h"
#include "components/policy/core/common/schema.h"
#include "testing/gtest/include/gtest/gtest.h"

using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;

namespace policy {
namespace android {

class PolicyConverterTest : public testing::Test {
 public:
  void SetUp() override {
    const char kSchemaTemplate[] =
        R"({
        "type": "object",
        "properties": {
          "string": { "type": "string" },
          "int": { "type": "integer" },
          "bool": { "type": "boolean" },
          "double": { "type": "number" },
          "list": {
            "type": "array",
            "items": { "type": "string" }
          },
          "dict": { "type": "object" }
        }
      })";
    ASSIGN_OR_RETURN(schema_, Schema::Parse(kSchemaTemplate),
                     [](const auto& e) { ADD_FAILURE() << e; });
  }

 protected:
  // Converts the passed in value to the passed in schema, and serializes the
  // result to JSON, to make it easier to compare with EXPECT_EQ.
  std::string Convert(base::Value value, const Schema& value_schema) {
    std::optional<base::Value> converted_value =
        PolicyConverter::ConvertValueToSchema(std::move(value), value_schema);
    EXPECT_TRUE(converted_value.has_value());

    std::string json_string;
    EXPECT_TRUE(base::JSONWriter::Write(converted_value.value(), &json_string));
    return json_string;
  }

  // Uses `PolicyConverter::ConvertJavaStringArrayToListValue` to convert the
  // passed in java array and serializes the result to JSON, to make it easier
  // to compare with `EXPECT_EQ`.
  std::string ConvertJavaStringArrayToListValue(
      JNIEnv* env,
      const JavaRef<JArray<jstring>>& java_array) {
    base::ListValue list =
        PolicyConverter::ConvertJavaStringArrayToListValue(env, java_array);

    std::string json_string;
    EXPECT_TRUE(base::JSONWriter::Write(list, &json_string));

    return json_string;
  }

  // Converts the passed in values to a java string array
  ScopedJavaLocalRef<JArray<jstring>> MakeJavaStringArray(
      JNIEnv* env,
      std::vector<std::string> values) {
    return jni_zero::NewStringArray(env, values);
  }

  Schema schema_;
};

TEST_F(PolicyConverterTest, ConvertToBoolValue) {
  Schema bool_schema = schema_.GetKnownProperty("bool");
  ASSERT_TRUE(bool_schema.valid());

  EXPECT_EQ("true", Convert(base::Value(true), bool_schema));
  EXPECT_EQ("false", Convert(base::Value(false), bool_schema));
  EXPECT_EQ("true", Convert(base::Value("true"), bool_schema));
  EXPECT_EQ("false", Convert(base::Value("false"), bool_schema));
  EXPECT_EQ("\"narf\"", Convert(base::Value("narf"), bool_schema));
  EXPECT_EQ("false", Convert(base::Value(0), bool_schema));
  EXPECT_EQ("true", Convert(base::Value(1), bool_schema));
  EXPECT_EQ("true", Convert(base::Value(42), bool_schema));
  EXPECT_EQ("true", Convert(base::Value(-1), bool_schema));
  EXPECT_EQ("\"1\"", Convert(base::Value("1"), bool_schema));
  EXPECT_EQ("{}", Convert(base::Value(base::Value::Type::DICT), bool_schema));
}

TEST_F(PolicyConverterTest, ConvertToIntValue) {
  Schema int_schema = schema_.GetKnownProperty("int");
  ASSERT_TRUE(int_schema.valid());

  EXPECT_EQ("23", Convert(base::Value(23), int_schema));
  EXPECT_EQ("42", Convert(base::Value("42"), int_schema));
  EXPECT_EQ("-1", Convert(base::Value("-1"), int_schema));
  EXPECT_EQ("\"poit\"", Convert(base::Value("poit"), int_schema));
  EXPECT_EQ("false", Convert(base::Value(false), int_schema));
}

TEST_F(PolicyConverterTest, ConvertToDoubleValue) {
  Schema double_schema = schema_.GetKnownProperty("double");
  ASSERT_TRUE(double_schema.valid());

  EXPECT_EQ("3", Convert(base::Value(3), double_schema));
  EXPECT_EQ("3.14", Convert(base::Value(3.14), double_schema));
  EXPECT_EQ("2.71", Convert(base::Value("2.71"), double_schema));
  EXPECT_EQ("\"zort\"", Convert(base::Value("zort"), double_schema));
  EXPECT_EQ("true", Convert(base::Value(true), double_schema));
}

TEST_F(PolicyConverterTest, ConvertToStringValue) {
  Schema string_schema = schema_.GetKnownProperty("string");
  ASSERT_TRUE(string_schema.valid());

  EXPECT_EQ("\"troz\"", Convert(base::Value("troz"), string_schema));
  EXPECT_EQ("4711", Convert(base::Value(4711), string_schema));
}

TEST_F(PolicyConverterTest, ConvertToListValue) {
  Schema list_schema = schema_.GetKnownProperty("list");
  ASSERT_TRUE(list_schema.valid());

  base::ListValue list;
  list.Append("foo");
  list.Append("bar");
  EXPECT_EQ("[\"foo\",\"bar\"]",
            Convert(base::Value(std::move(list)), list_schema));
  EXPECT_EQ("[\"baz\",\"blurp\"]",
            Convert(base::Value("[\"baz\", \"blurp\"]"), list_schema));
  EXPECT_EQ("[\"hurz\"]", Convert(base::Value("hurz"), list_schema));
  EXPECT_EQ("[\"foo\",\"bar\"]", Convert(base::Value("foo,bar"), list_schema));
  EXPECT_EQ("[\"foo\",\"bar\"]", Convert(base::Value("foo, bar"), list_schema));
  EXPECT_EQ("19", Convert(base::Value(19), list_schema));

  EXPECT_FALSE(
      PolicyConverter::ConvertValueToSchema(base::Value(""), list_schema)
          .has_value());
}

TEST_F(PolicyConverterTest, ConvertFromJavaListToListValue) {
  JNIEnv* env = base::android::AttachCurrentThread();
  EXPECT_EQ("[\"foo\",\"bar\",\"baz\"]",
            ConvertJavaStringArrayToListValue(
                env, MakeJavaStringArray(env, {"foo", "bar", "baz"})));
  EXPECT_EQ("[]", ConvertJavaStringArrayToListValue(
                      env, MakeJavaStringArray(env, {})));
}

TEST_F(PolicyConverterTest, ConvertToDictValue) {
  Schema dict_schema = schema_.GetKnownProperty("dict");
  ASSERT_TRUE(dict_schema.valid());

  base::DictValue dict;
  dict.Set("thx", 1138);
  EXPECT_EQ("{\"thx\":1138}",
            Convert(base::Value(std::move(dict)), dict_schema));
  EXPECT_EQ("{\"moose\":true}",
            Convert(base::Value("{\"moose\": true}"), dict_schema));
  EXPECT_EQ("\"fnord\"", Convert(base::Value("fnord"), dict_schema));
  EXPECT_EQ("1729", Convert(base::Value(1729), dict_schema));

  EXPECT_FALSE(
      PolicyConverter::ConvertValueToSchema(base::Value(""), dict_schema)
          .has_value());
}

}  // namespace android
}  // namespace policy
