// Copyright 2013 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 "components/autofill/core/common/form_field_data.h"

#include "base/i18n/rtl.h"
#include "base/pickle.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/common/autofill_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace autofill {

namespace {

void FillCommonFields(FormFieldData* data) {
  data->label = base::ASCIIToUTF16("label");
  data->name = base::ASCIIToUTF16("name");
  data->value = base::ASCIIToUTF16("value");
  data->form_control_type = "password";
  data->autocomplete_attribute = "off";
  data->max_length = 200;
  data->is_autofilled = true;
  data->check_status = FormFieldData::CheckStatus::CHECKED;
  data->is_focusable = true;
  data->should_autocomplete = false;
  data->text_direction = base::i18n::RIGHT_TO_LEFT;
  data->option_values.push_back(base::ASCIIToUTF16("First"));
  data->option_values.push_back(base::ASCIIToUTF16("Second"));
  data->option_contents.push_back(base::ASCIIToUTF16("First"));
  data->option_contents.push_back(base::ASCIIToUTF16("Second"));
}

void FillVersion2Fields(FormFieldData* data) {
  data->role = FormFieldData::ROLE_ATTRIBUTE_PRESENTATION;
}

void FillVersion3Fields(FormFieldData* data) {
  data->placeholder = base::ASCIIToUTF16("placeholder");
}

void FillVersion5Fields(FormFieldData* data) {
  data->css_classes = base::ASCIIToUTF16("class1 class2");
}

void FillVersion6Fields(FormFieldData* data) {
  data->properties_mask =
      FieldPropertiesFlags::USER_TYPED | FieldPropertiesFlags::HAD_FOCUS;
}

void FillVersion7Fields(FormFieldData* data) {
  data->id = base::ASCIIToUTF16("id");
}

void WriteSection1(const FormFieldData& data, base::Pickle* pickle) {
  pickle->WriteString16(data.label);
  pickle->WriteString16(data.name);
  pickle->WriteString16(data.value);
  pickle->WriteString(data.form_control_type);
  pickle->WriteString(data.autocomplete_attribute);
  pickle->WriteUInt64(data.max_length);
  pickle->WriteBool(data.is_autofilled);
}

void WriteSection3(const FormFieldData& data, base::Pickle* pickle) {
  pickle->WriteBool(IsChecked(data.check_status));
  pickle->WriteBool(IsCheckable(data.check_status));
}

void WriteSection4(const FormFieldData& data, base::Pickle* pickle) {
  pickle->WriteInt(data.check_status);
}

void WriteSection5(const FormFieldData& data, base::Pickle* pickle) {
  pickle->WriteBool(data.is_focusable);
  pickle->WriteBool(data.should_autocomplete);
}

void WriteSection2(const FormFieldData& data, base::Pickle* pickle) {
  pickle->WriteInt(data.text_direction);
  pickle->WriteInt(static_cast<int>(data.option_values.size()));
  for (auto s : data.option_values)
    pickle->WriteString16(s);
  pickle->WriteInt(static_cast<int>(data.option_contents.size()));
  for (auto s : data.option_contents)
    pickle->WriteString16(s);
}

void WriteVersion2Specific(const FormFieldData& data, base::Pickle* pickle) {
  pickle->WriteInt(data.role);
}

void WriteVersion3Specific(const FormFieldData& data, base::Pickle* pickle) {
  pickle->WriteString16(data.placeholder);
}

void WriteVersion5Specific(const FormFieldData& data, base::Pickle* pickle) {
  pickle->WriteString16(data.css_classes);
}

void WriteVersion6Specific(const FormFieldData& data, base::Pickle* pickle) {
  pickle->WriteUInt32(data.properties_mask);
}

void WriteVersion7Specific(const FormFieldData& data, base::Pickle* pickle) {
  pickle->WriteString16(data.id);
}

void SerializeInVersion1Format(const FormFieldData& data,
                               base::Pickle* pickle) {
  WriteSection1(data, pickle);
  WriteSection3(data, pickle);
  WriteSection5(data, pickle);
  WriteSection2(data, pickle);
}

void SerializeInVersion2Format(const FormFieldData& data,
                               base::Pickle* pickle) {
  WriteSection1(data, pickle);
  WriteSection3(data, pickle);
  WriteSection5(data, pickle);
  WriteVersion2Specific(data, pickle);
  WriteSection2(data, pickle);
}

void SerializeInVersion3Format(const FormFieldData& data,
                               base::Pickle* pickle) {
  WriteSection1(data, pickle);
  WriteSection3(data, pickle);
  WriteSection5(data, pickle);
  WriteVersion2Specific(data, pickle);
  WriteSection2(data, pickle);
  WriteVersion3Specific(data, pickle);
}

void SerializeInVersion4Format(const FormFieldData& data,
                               base::Pickle* pickle) {
  WriteSection1(data, pickle);
  WriteSection4(data, pickle);
  WriteSection5(data, pickle);
  WriteVersion2Specific(data, pickle);
  WriteSection2(data, pickle);
  WriteVersion3Specific(data, pickle);
}

void SerializeInVersion5Format(const FormFieldData& data,
                               base::Pickle* pickle) {
  WriteSection1(data, pickle);
  WriteSection4(data, pickle);
  WriteSection5(data, pickle);
  WriteVersion2Specific(data, pickle);
  WriteSection2(data, pickle);
  WriteVersion3Specific(data, pickle);
  WriteVersion5Specific(data, pickle);
}

void SerializeInVersion6Format(const FormFieldData& data,
                               base::Pickle* pickle) {
  WriteSection1(data, pickle);
  WriteSection4(data, pickle);
  WriteSection5(data, pickle);
  WriteVersion2Specific(data, pickle);
  WriteSection2(data, pickle);
  WriteVersion3Specific(data, pickle);
  WriteVersion5Specific(data, pickle);
  WriteVersion6Specific(data, pickle);
}

void SerializeInVersion7Format(const FormFieldData& data,
                               base::Pickle* pickle) {
  WriteSection1(data, pickle);
  WriteSection4(data, pickle);
  WriteSection5(data, pickle);
  WriteVersion2Specific(data, pickle);
  WriteSection2(data, pickle);
  WriteVersion3Specific(data, pickle);
  WriteVersion5Specific(data, pickle);
  WriteVersion6Specific(data, pickle);
  WriteVersion7Specific(data, pickle);
}

}  // namespace

TEST(FormFieldDataTest, SerializeAndDeserialize) {
  FormFieldData data;
  FillCommonFields(&data);
  FillVersion2Fields(&data);
  FillVersion3Fields(&data);
  FillVersion5Fields(&data);
  FillVersion6Fields(&data);
  FillVersion7Fields(&data);

  base::Pickle pickle;
  SerializeFormFieldData(data, &pickle);

  base::PickleIterator iter(pickle);
  FormFieldData actual;
  EXPECT_TRUE(DeserializeFormFieldData(&iter, &actual));

  EXPECT_TRUE(actual.SameFieldAs(data));
}

TEST(FormFieldDataTest, DeserializeVersion1) {
  FormFieldData data;
  FillCommonFields(&data);

  base::Pickle pickle;
  pickle.WriteInt(1);
  SerializeInVersion1Format(data, &pickle);

  base::PickleIterator iter(pickle);
  FormFieldData actual;
  EXPECT_TRUE(DeserializeFormFieldData(&iter, &actual));

  EXPECT_TRUE(actual.SameFieldAs(data));
}

TEST(FormFieldDataTest, DeserializeVersion2) {
  FormFieldData data;
  FillCommonFields(&data);
  FillVersion2Fields(&data);

  base::Pickle pickle;
  pickle.WriteInt(2);
  SerializeInVersion2Format(data, &pickle);

  base::PickleIterator iter(pickle);
  FormFieldData actual;
  EXPECT_TRUE(DeserializeFormFieldData(&iter, &actual));

  EXPECT_TRUE(actual.SameFieldAs(data));
}

TEST(FormFieldDataTest, DeserializeVersion3) {
  FormFieldData data;
  FillCommonFields(&data);
  FillVersion2Fields(&data);
  FillVersion3Fields(&data);

  base::Pickle pickle;
  pickle.WriteInt(3);
  SerializeInVersion3Format(data, &pickle);

  base::PickleIterator iter(pickle);
  FormFieldData actual;
  EXPECT_TRUE(DeserializeFormFieldData(&iter, &actual));

  EXPECT_TRUE(actual.SameFieldAs(data));
}

TEST(FormFieldDataTest, DeserializeVersion4) {
  FormFieldData data;
  FillCommonFields(&data);
  FillVersion2Fields(&data);
  FillVersion3Fields(&data);

  base::Pickle pickle;
  pickle.WriteInt(4);
  SerializeInVersion4Format(data, &pickle);

  base::PickleIterator iter(pickle);
  FormFieldData actual;
  EXPECT_TRUE(DeserializeFormFieldData(&iter, &actual));

  EXPECT_TRUE(actual.SameFieldAs(data));
}

TEST(FormFieldDataTest, DeserializeVersion5) {
  FormFieldData data;
  FillCommonFields(&data);
  FillVersion2Fields(&data);
  FillVersion3Fields(&data);
  FillVersion5Fields(&data);

  base::Pickle pickle;
  pickle.WriteInt(5);
  SerializeInVersion5Format(data, &pickle);

  base::PickleIterator iter(pickle);
  FormFieldData actual;
  EXPECT_TRUE(DeserializeFormFieldData(&iter, &actual));

  EXPECT_TRUE(actual.SameFieldAs(data));
}

TEST(FormFieldDataTest, DeserializeVersion6) {
  FormFieldData data;
  FillCommonFields(&data);
  FillVersion2Fields(&data);
  FillVersion3Fields(&data);
  FillVersion5Fields(&data);
  FillVersion6Fields(&data);

  base::Pickle pickle;
  pickle.WriteInt(6);
  SerializeInVersion6Format(data, &pickle);

  base::PickleIterator iter(pickle);
  FormFieldData actual;
  EXPECT_TRUE(DeserializeFormFieldData(&iter, &actual));

  EXPECT_TRUE(actual.SameFieldAs(data));
}

TEST(FormFieldDataTest, DeserializeVersion7) {
  FormFieldData data;
  FillCommonFields(&data);
  FillVersion2Fields(&data);
  FillVersion3Fields(&data);
  FillVersion5Fields(&data);
  FillVersion6Fields(&data);
  FillVersion7Fields(&data);

  base::Pickle pickle;
  pickle.WriteInt(7);
  SerializeInVersion7Format(data, &pickle);

  base::PickleIterator iter(pickle);
  FormFieldData actual;
  EXPECT_TRUE(DeserializeFormFieldData(&iter, &actual));

  EXPECT_TRUE(actual.SameFieldAs(data));
}

// Verify that if the data isn't valid, the FormFieldData isn't populated
// during deserialization.
TEST(FormFieldDataTest, DeserializeBadData) {
  base::Pickle pickle;
  pickle.WriteInt(255);
  pickle.WriteString16(base::ASCIIToUTF16("random"));
  pickle.WriteString16(base::ASCIIToUTF16("data"));

  base::PickleIterator iter(pickle);
  FormFieldData actual;
  EXPECT_FALSE(DeserializeFormFieldData(&iter, &actual));
  FormFieldData empty;
  EXPECT_TRUE(actual.SameFieldAs(empty));
}

TEST(FormFieldDataTest, IsTextInputElement) {
  struct TestData {
    const char* form_control_type;
    bool expected;
  } test_data[] = {
      {"text", true},      {"search", true},
      {"tel", true},       {"url", true},
      {"email", true},     {"password", true},
      {"select", false},   {"", false},
      {"checkbox", false}, {"random_string", false},
      {"textarea", false},
  };

  for (const auto& test_case : test_data) {
    FormFieldData data;
    data.form_control_type = test_case.form_control_type;
    EXPECT_EQ(test_case.expected, data.IsTextInputElement());
  }
}

}  // namespace autofill
