// 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/autofill_regexes.h"

#include <stddef.h>

#include "base/macros.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/common/autofill_regex_constants.h"
#include "testing/gtest/include/gtest/gtest.h"

using base::ASCIIToUTF16;

namespace autofill {

struct InputPatternTestCase {
  const char* const input;
  const char* const pattern;
  };

  class PositiveSampleTest
      : public testing::TestWithParam<InputPatternTestCase> {};

  TEST_P(PositiveSampleTest, SampleRegexes) {
    auto test_case = GetParam();
    SCOPED_TRACE(test_case.input);
    SCOPED_TRACE(test_case.pattern);
    EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input),
                               ASCIIToUTF16(test_case.pattern)));
  }

  INSTANTIATE_TEST_CASE_P(AutofillRegexes,
                          PositiveSampleTest,
                          testing::Values(
                              // Empty pattern
                              InputPatternTestCase{"", ""},
                              InputPatternTestCase{
                                  "Look, ma' -- a non-empty string!", ""},
                              // Substring
                              InputPatternTestCase{"string", "tri"},
                              // Substring at beginning
                              InputPatternTestCase{"string", "str"},
                              InputPatternTestCase{"string", "^str"},
                              // Substring at end
                              InputPatternTestCase{"string", "ring"},
                              InputPatternTestCase{"string", "ring$"},
                              // Case-insensitive
                              InputPatternTestCase{"StRiNg", "string"}));

  class NegativeSampleTest
      : public testing::TestWithParam<InputPatternTestCase> {};

  TEST_P(NegativeSampleTest, SampleRegexes) {
    auto test_case = GetParam();
    SCOPED_TRACE(test_case.input);
    SCOPED_TRACE(test_case.pattern);
    EXPECT_FALSE(MatchesPattern(ASCIIToUTF16(test_case.input),
                                ASCIIToUTF16(test_case.pattern)));
}

INSTANTIATE_TEST_CASE_P(AutofillRegexes,
                        NegativeSampleTest,
                        testing::Values(
                            // Empty string
                            InputPatternTestCase{
                                "", "Look, ma' -- a non-empty pattern!"},
                            // Substring
                            InputPatternTestCase{"string", "trn"},
                            // Substring at beginning
                            InputPatternTestCase{"string", " str"},
                            InputPatternTestCase{"string", "^tri"},
                            // Substring at end
                            InputPatternTestCase{"string", "ring "},
                            InputPatternTestCase{"string", "rin$"}));

struct InputTestCase {
  const char* const input;
  };

  class ExpirationDate2DigitYearPositive
      : public testing::TestWithParam<InputTestCase> {};

  TEST_P(ExpirationDate2DigitYearPositive, ExpirationDate2DigitYearRegexes) {
    auto test_case = GetParam();
    SCOPED_TRACE(test_case.input);
    const base::string16 pattern = ASCIIToUTF16(kExpirationDate2DigitYearRe);
    EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern));
  }

  INSTANTIATE_TEST_CASE_P(
      AutofillRegexes,
      ExpirationDate2DigitYearPositive,
      testing::Values(InputTestCase{"mm / yy"},
                      InputTestCase{"mm/ yy"},
                      InputTestCase{"mm /yy"},
                      InputTestCase{"mm/yy"},
                      InputTestCase{"mm - yy"},
                      InputTestCase{"mm- yy"},
                      InputTestCase{"mm -yy"},
                      InputTestCase{"mm-yy"},
                      InputTestCase{"mmyy"},
                      // Complex two year cases
                      InputTestCase{"Expiration Date (MM / YY)"},
                      InputTestCase{"Expiration Date (MM/YY)"},
                      InputTestCase{"Expiration Date (MM - YY)"},
                      InputTestCase{"Expiration Date (MM-YY)"},
                      InputTestCase{"Expiration Date MM / YY"},
                      InputTestCase{"Expiration Date MM/YY"},
                      InputTestCase{"Expiration Date MM - YY"},
                      InputTestCase{"Expiration Date MM-YY"},
                      InputTestCase{"expiration date yy"},
                      InputTestCase{"Exp Date     (MM / YY)"}));

  class ExpirationDate2DigitYearNegative
      : public testing::TestWithParam<InputTestCase> {};

  TEST_P(ExpirationDate2DigitYearNegative, ExpirationDate2DigitYearRegexes) {
    auto test_case = GetParam();
    SCOPED_TRACE(test_case.input);
    const base::string16 pattern = ASCIIToUTF16(kExpirationDate2DigitYearRe);
    EXPECT_FALSE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern));
  }

  INSTANTIATE_TEST_CASE_P(
      AutofillRegexes,
      ExpirationDate2DigitYearNegative,
      testing::Values(InputTestCase{""},
                      InputTestCase{"Look, ma' -- an invalid string!"},
                      InputTestCase{"mmfavouritewordyy"},
                      InputTestCase{"mm a yy"},
                      InputTestCase{"mm a yyyy"},
                      // Simple four year cases
                      InputTestCase{"mm / yyyy"},
                      InputTestCase{"mm/ yyyy"},
                      InputTestCase{"mm /yyyy"},
                      InputTestCase{"mm/yyyy"},
                      InputTestCase{"mm - yyyy"},
                      InputTestCase{"mm- yyyy"},
                      InputTestCase{"mm -yyyy"},
                      InputTestCase{"mm-yyyy"},
                      InputTestCase{"mmyyyy"},
                      // Complex four year cases
                      InputTestCase{"Expiration Date (MM / YYYY)"},
                      InputTestCase{"Expiration Date (MM/YYYY)"},
                      InputTestCase{"Expiration Date (MM - YYYY)"},
                      InputTestCase{"Expiration Date (MM-YYYY)"},
                      InputTestCase{"Expiration Date MM / YYYY"},
                      InputTestCase{"Expiration Date MM/YYYY"},
                      InputTestCase{"Expiration Date MM - YYYY"},
                      InputTestCase{"Expiration Date MM-YYYY"},
                      InputTestCase{"expiration date yyyy"},
                      InputTestCase{"Exp Date     (MM / YYYY)"}));

  class ExpirationDate4DigitYearPositive
      : public testing::TestWithParam<InputTestCase> {};

  TEST_P(ExpirationDate4DigitYearPositive, ExpirationDate4DigitYearRegexes) {
    auto test_case = GetParam();
    const base::string16 pattern = ASCIIToUTF16(kExpirationDate4DigitYearRe);
    SCOPED_TRACE(test_case.input);
    EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern));
  }

  INSTANTIATE_TEST_CASE_P(AutofillRegexes,
                          ExpirationDate4DigitYearPositive,
                          testing::Values(
                              // Simple four year cases
                              InputTestCase{"mm / yyyy"},
                              InputTestCase{"mm/ yyyy"},
                              InputTestCase{"mm /yyyy"},
                              InputTestCase{"mm/yyyy"},
                              InputTestCase{"mm - yyyy"},
                              InputTestCase{"mm- yyyy"},
                              InputTestCase{"mm -yyyy"},
                              InputTestCase{"mm-yyyy"},
                              InputTestCase{"mmyyyy"},
                              // Complex four year cases
                              InputTestCase{"Expiration Date (MM / YYYY)"},
                              InputTestCase{"Expiration Date (MM/YYYY)"},
                              InputTestCase{"Expiration Date (MM - YYYY)"},
                              InputTestCase{"Expiration Date (MM-YYYY)"},
                              InputTestCase{"Expiration Date MM / YYYY"},
                              InputTestCase{"Expiration Date MM/YYYY"},
                              InputTestCase{"Expiration Date MM - YYYY"},
                              InputTestCase{"Expiration Date MM-YYYY"},
                              InputTestCase{"expiration date yyyy"},
                              InputTestCase{"Exp Date     (MM / YYYY)"}));

  class ExpirationDate4DigitYearNegative
      : public testing::TestWithParam<InputTestCase> {};

  TEST_P(ExpirationDate4DigitYearNegative, ExpirationDate4DigitYearRegexes) {
    auto test_case = GetParam();
    const base::string16 pattern = ASCIIToUTF16(kExpirationDate4DigitYearRe);
    SCOPED_TRACE(test_case.input);
    EXPECT_FALSE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern));
}

INSTANTIATE_TEST_CASE_P(
    AutofillRegexes,
    ExpirationDate4DigitYearNegative,
    testing::Values(InputTestCase{""},
                    InputTestCase{"Look, ma' -- an invalid string!"},
                    InputTestCase{"mmfavouritewordyy"},
                    InputTestCase{"mm a yy"},
                    InputTestCase{"mm a yyyy"},
                    // Simple two year cases
                    InputTestCase{"mm / yy"},
                    InputTestCase{"mm/ yy"},
                    InputTestCase{"mm /yy"},
                    InputTestCase{"mm/yy"},
                    InputTestCase{"mm - yy"},
                    InputTestCase{"mm- yy"},
                    InputTestCase{"mm -yy"},
                    InputTestCase{"mm-yy"},
                    InputTestCase{"mmyy"},
                    // Complex two year cases
                    InputTestCase{"Expiration Date (MM / YY)"},
                    InputTestCase{"Expiration Date (MM/YY)"},
                    InputTestCase{"Expiration Date (MM - YY)"},
                    InputTestCase{"Expiration Date (MM-YY)"},
                    InputTestCase{"Expiration Date MM / YY"},
                    InputTestCase{"Expiration Date MM/YY"},
                    InputTestCase{"Expiration Date MM - YY"},
                    InputTestCase{"Expiration Date MM-YY"},
                    InputTestCase{"expiration date yy"},
                    InputTestCase{"Exp Date     (MM / YY)"}));

}  // namespace autofill
