Commit 7c7b0ed3 authored by Martynas Sinkievic's avatar Martynas Sinkievic Committed by Commit Bot

[Autofill] Add the pattern configuration parser

Add functions which support validating and parsing the
JSON matching pattern configuration string into a format
supported by |PatternProvider|. Also provide support for
versioning and replacing the configuration in
|PatternProvider|. Due to the nature of the change,
|PatternProvider| was changed to support testing with a
synchronously loaded configuration.

Change-Id: I662ecb933a4067c0ee6d188defe5e982b85469a7
Bug: 1121990
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2359990Reviewed-by: default avatarMatthias Körber <koerber@google.com>
Reviewed-by: default avatarChristoph Schwering <schwering@google.com>
Reviewed-by: default avatarColin Blundell <blundell@chromium.org>
Commit-Queue: Martynas Sinkievič <marsin@google.com>
Cr-Commit-Position: refs/heads/master@{#813097}
parent 0a01915d
......@@ -183,6 +183,8 @@ static_library("browser") {
"metrics/form_event_logger_base.cc",
"metrics/form_event_logger_base.h",
"metrics/form_events.h",
"pattern_provider/pattern_configuration_parser.cc",
"pattern_provider/pattern_configuration_parser.h",
"pattern_provider/pattern_provider.cc",
"pattern_provider/pattern_provider.h",
"payments/account_info_getter.h",
......@@ -390,6 +392,7 @@ static_library("browser") {
"//crypto",
"//google_apis",
"//net",
"//services/data_decoder/public/cpp:cpp",
"//services/metrics/public/cpp:metrics_cpp",
"//services/metrics/public/cpp:ukm_builders",
"//services/network/public/cpp",
......@@ -446,6 +449,8 @@ static_library("test_support") {
"logging/stub_log_manager.h",
"mock_autocomplete_history_manager.cc",
"mock_autocomplete_history_manager.h",
"pattern_provider/test_pattern_provider.cc",
"pattern_provider/test_pattern_provider.h",
"payments/test_authentication_requester.cc",
"payments/test_authentication_requester.h",
"payments/test_credit_card_save_manager.cc",
......@@ -628,6 +633,7 @@ source_set("unit_tests") {
"logging/log_buffer_submitter_unittest.cc",
"logging/log_manager_unittest.cc",
"logging/log_router_unittest.cc",
"pattern_provider/pattern_configuration_parser_unittest.cc",
"pattern_provider/pattern_provider_unittest.cc",
"payments/autofill_offer_manager_unittest.cc",
"payments/credit_card_access_manager_unittest.cc",
......@@ -727,6 +733,7 @@ source_set("unit_tests") {
"//google_apis",
"//google_apis:test_support",
"//net:test_support",
"//services/data_decoder/public/cpp:test_support",
"//services/metrics/public/cpp:ukm_builders",
"//services/network:test_support",
"//services/network/public/cpp",
......
include_rules = [
"+components/grit/components_resources.h",
"+services/data_decoder/public/cpp:cpp",
"+services/data_decoder/public",
]
// Copyright 2020 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/browser/pattern_provider/pattern_configuration_parser.h"
#include "base/bind.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/values.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/grit/components_resources.h"
#include "ui/base/resource/resource_bundle.h"
namespace autofill {
namespace field_type_parsing {
namespace {
const char kPatternIdentifierKey[] = "pattern_identifier";
const char kPositivePatternKey[] = "positive_pattern";
const char kNegativePatternKey[] = "negative_pattern";
const char kPositiveScoreKey[] = "positive_score";
const char kMatchFieldAttributesKey[] = "match_field_attributes";
const char kMatchFieldInputTypesKey[] = "match_field_input_types";
const char kVersionKey[] = "version";
bool ParseMatchingPattern(PatternProvider::Map& patterns,
const std::string& field_type,
const std::string& language,
const base::Value& value) {
if (!value.is_dict())
return false;
const std::string* pattern_identifier =
value.FindStringKey(kPatternIdentifierKey);
const std::string* positive_pattern =
value.FindStringKey(kPositivePatternKey);
const std::string* negative_pattern =
value.FindStringKey(kNegativePatternKey);
base::Optional<double> positive_score =
value.FindDoubleKey(kPositiveScoreKey);
base::Optional<int> match_field_attributes =
value.FindIntKey(kMatchFieldAttributesKey);
base::Optional<int> match_field_input_types =
value.FindIntKey(kMatchFieldInputTypesKey);
if (!pattern_identifier || !positive_pattern || !negative_pattern ||
!positive_score || !match_field_attributes || !match_field_input_types)
return false;
autofill::MatchingPattern new_pattern;
new_pattern.pattern_identifier = *pattern_identifier;
new_pattern.positive_pattern = *positive_pattern;
new_pattern.positive_score = *positive_score;
new_pattern.negative_pattern = *negative_pattern;
new_pattern.match_field_attributes = match_field_attributes.value();
new_pattern.match_field_input_types = match_field_input_types.value();
new_pattern.language = language;
std::vector<MatchingPattern>* pattern_list = &patterns[field_type][language];
pattern_list->push_back(new_pattern);
DVLOG(2) << "Correctly parsed MatchingPattern with identifier |"
<< new_pattern.pattern_identifier << "|.";
return true;
}
// Callback which is used once the JSON is parsed.
// |overwrite_equal_version| should be true when loading a remote
// configuration. If the configuration versions are equal or
// both unspecified (i.e. set to 0) this prioritizes the remote
// configuration over the local one.
void OnJsonParsed(bool overwrite_equal_version,
base::OnceClosure done_callback,
data_decoder::DataDecoder::ValueOrError result) {
// Skip any processing in case of an error.
if (!result.value) {
DVLOG(1) << "Failed to parse PatternProvider configuration JSON string.";
std::move(done_callback).Run();
return;
}
base::Version version = ExtractVersionFromJsonObject(result.value.value());
base::Optional<PatternProvider::Map> patterns =
GetConfigurationFromJsonObject(result.value.value());
if (patterns && version.IsValid()) {
DVLOG(1) << "Successfully parsed PatternProvider configuration.";
PatternProvider& pattern_provider = PatternProvider::GetInstance();
pattern_provider.SetPatterns(std::move(patterns.value()),
std::move(version), overwrite_equal_version);
} else {
DVLOG(1) << "Failed to parse PatternProvider configuration JSON object.";
}
std::move(done_callback).Run();
}
} // namespace
base::Optional<PatternProvider::Map> GetConfigurationFromJsonObject(
const base::Value& root) {
PatternProvider::Map patterns;
if (!root.is_dict()) {
DVLOG(1) << "JSON object is not a dictionary.";
return base::nullopt;
}
for (const auto& kv : root.DictItems()) {
const std::string& field_type = kv.first;
const base::Value* field_type_dict = &kv.second;
if (!field_type_dict->is_dict()) {
DVLOG(1) << "|" << field_type << "| does not contain a dictionary.";
return base::nullopt;
}
for (const auto& value : field_type_dict->DictItems()) {
const std::string& language = value.first;
const base::Value* inner_list = &value.second;
if (!inner_list->is_list()) {
DVLOG(1) << "Language |" << language << "| in |" << field_type
<< "| does not contain a list.";
return base::nullopt;
}
for (const auto& matchingPatternObj : inner_list->GetList()) {
bool success = ParseMatchingPattern(patterns, field_type, language,
matchingPatternObj);
if (!success) {
DVLOG(1) << "Found incorrect |MatchingPattern| object in list |"
<< field_type << "|, language |" << language << "|.";
return base::nullopt;
}
}
}
}
return base::make_optional(patterns);
}
base::Version ExtractVersionFromJsonObject(base::Value& root) {
if (!root.is_dict())
return base::Version("0");
base::Optional<base::Value> version_str = root.ExtractKey(kVersionKey);
if (!version_str || !version_str.value().is_string())
return base::Version("0");
base::Version version = base::Version(version_str.value().GetString());
if (!version.IsValid())
return base::Version("0");
return version;
}
void PopulateFromJsonString(std::string json_string) {
data_decoder::DataDecoder::ParseJsonIsolated(
std::move(json_string),
base::BindOnce(&OnJsonParsed, true, base::DoNothing::Once()));
}
void PopulateFromResourceBundle(base::OnceClosure done_callback) {
if (!ui::ResourceBundle::HasSharedInstance()) {
VLOG(1) << "Resource Bundle unavailable to load Autofill Matching Pattern "
"definitions.";
std::move(done_callback).Run();
return;
}
ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
// Load the string from the Resource Bundle on a worker thread, then
// securely parse the JSON in a separate process and call |OnJsonParsed|
// with the result.
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&ui::ResourceBundle::LoadDataResourceString,
base::Unretained(&bundle), IDR_AUTOFILL_REGEX_JSON),
base::BindOnce(
[](base::OnceClosure done_callback, std::string resource_string) {
data_decoder::DataDecoder::ParseJsonIsolated(
std::move(resource_string),
base::BindOnce(&OnJsonParsed, false, std::move(done_callback)));
},
std::move(done_callback)));
}
base::Optional<PatternProvider::Map>
GetPatternsFromResourceBundleSynchronously() {
if (!ui::ResourceBundle::HasSharedInstance()) {
VLOG(1) << "Resource Bundle unavailable to load Autofill Matching Pattern "
"definitions.";
return base::nullopt;
}
ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
std::string resource_string =
bundle.LoadDataResourceString(IDR_AUTOFILL_REGEX_JSON);
base::Optional<base::Value> json_object =
base::JSONReader::Read(resource_string);
// Discard version, since this is the only getter used in unit tests.
base::Version version = ExtractVersionFromJsonObject(json_object.value());
base::Optional<PatternProvider::Map> configuration_map =
GetConfigurationFromJsonObject(json_object.value());
return configuration_map;
}
} // namespace field_type_parsing
} // namespace autofill
// Copyright 2020 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.
#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_CONFIGURATION_PARSER_H_
#define COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_CONFIGURATION_PARSER_H_
#include <string>
#include "base/json/json_reader.h"
#include "base/version.h"
#include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h"
#include "components/autofill/core/browser/pattern_provider/pattern_provider.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
namespace autofill {
namespace field_type_parsing {
// Tries to extract the configuration version from the JSON base::Value tree.
// This removes the key if found, so that validation is easier later on.
// If not found, default to version 0.
base::Version ExtractVersionFromJsonObject(base::Value& root);
// Transforms the parsed JSON base::Value tree into the map used in
// |PatternProvider|. Requires the version key to already be extracted.
// The root is expected to be a dictionary with keys corresponding to
// strings representing |ServerFieldType|. Then there should be
// second level dictionaries with keys describing the language. These
// should point to a list of objects representing |MatchingPattern|.
// {
// "FIELD_NAME": {
// "language":[
// {MatchingPatternFields}
// ]
// }
// }
// An example can be found in the relative resources folder.
base::Optional<PatternProvider::Map> GetConfigurationFromJsonObject(
const base::Value& root);
// Tries to get and parse the default configuration in the resource bundle
// into a valid map used in |PatternProvider| and swap it in for further use.
// The callback is used as a signal for testing.
void PopulateFromResourceBundle(
base::OnceClosure done_callback = base::DoNothing::Once());
// Tries to parse the given JSON string into a valid map used in the
// |PatternProvider| and swap it in for further use.
void PopulateFromJsonString(std::string json_string);
// Synchronous getter used to set up a test fixture.
base::Optional<PatternProvider::Map>
GetPatternsFromResourceBundleSynchronously();
} // namespace field_type_parsing
} // namespace autofill
#endif
// Copyright 2020 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/browser/pattern_provider/pattern_configuration_parser.h"
#include <stddef.h>
#include "base/json/json_reader.h"
#include "base/test/gtest_util.h"
#include "base/version.h"
#include "components/grit/components_resources.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/resource/resource_bundle.h"
namespace autofill {
namespace field_type_parsing {
// Test that the |base::Value| object of the configuration is
// parsed to the map structure used by |PatternProvider| as
// expected, given the input is valid.
TEST(PatternConfigurationParserTest, WellFormedParsedCorrectly) {
std::string JSON_message = R"(
{
"version": "1.0",
"FULL_NAME": {
"en_us": [
{
"pattern_identifier": "Name_en",
"positive_pattern": "name|full name",
"positive_score": 2.0,
"negative_pattern": "company",
"match_field_attributes": 2,
"match_field_input_types": 3
}
],
"fr": [
{
"pattern_identifier": "Name_fr",
"positive_pattern": "nom|prenom",
"positive_score": 2.0,
"negative_pattern": "compagne",
"match_field_attributes": 2,
"match_field_input_types": 3
}
]
},
"ADDRESS": {
"en_us": [
{
"pattern_identifier": "Address",
"positive_pattern": "address",
"positive_score": 2.0,
"negative_pattern": "email",
"match_field_attributes": 4,
"match_field_input_types": 3
}
]
}
})";
base::Optional<base::Value> JSON_object =
base::JSONReader::Read(JSON_message);
ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string.";
base::Version version = ExtractVersionFromJsonObject(JSON_object.value());
base::Optional<PatternProvider::Map> optional_patterns =
GetConfigurationFromJsonObject(JSON_object.value());
ASSERT_TRUE(version.IsValid());
ASSERT_TRUE(optional_patterns);
ASSERT_EQ(base::Version("1.0"), version);
PatternProvider::Map patterns = optional_patterns.value();
ASSERT_EQ(2U, patterns.size());
ASSERT_TRUE(patterns.count("FULL_NAME"));
ASSERT_EQ(2U, patterns["FULL_NAME"].size());
ASSERT_TRUE(patterns["FULL_NAME"].count("en_us"));
ASSERT_TRUE(patterns["FULL_NAME"].count("fr"));
ASSERT_TRUE(patterns.count("ADDRESS"));
ASSERT_EQ(1U, patterns["ADDRESS"].size());
ASSERT_TRUE(patterns["ADDRESS"].count("en_us"));
// Test one |MatchingPattern| to check that they are parsed correctly.
MatchingPattern* pattern = &patterns["FULL_NAME"]["fr"][0];
ASSERT_EQ("Name_fr", pattern->pattern_identifier);
ASSERT_EQ("nom|prenom", pattern->positive_pattern);
ASSERT_EQ("compagne", pattern->negative_pattern);
ASSERT_EQ("fr", pattern->language);
ASSERT_NEAR(2.0, pattern->positive_score, 1e-6);
ASSERT_EQ(2, pattern->match_field_attributes);
ASSERT_EQ(3, pattern->match_field_input_types);
}
// Test that the parser does not return anything if some |MatchingPattern|
// object is missing a property.
TEST(PatternConfigurationParserTest, MalformedMissingProperty) {
std::string JSON_message = R"(
{
"version": "1.0",
"FULL_NAME": {
"en_us": [
{
"pattern_identifier": "Name_en",
"positive_pattern": "name|full name",
"positive_score": 2.0,
"negative_pattern": "company",
"match_field_attributes": 2,
"match_field_input_types": 3
}
],
"fr": [
{
"pattern_identifier": "Name_fr",
"positive_pattern": "nom|prenom",
"negative_pattern": "compagne",
"match_field_attributes": 2,
"match_field_input_types": 3
}
]
}
})";
base::Optional<base::Value> JSON_object =
base::JSONReader::Read(JSON_message);
ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string.";
base::Optional<PatternProvider::Map> optional_patterns =
GetConfigurationFromJsonObject(JSON_object.value());
ASSERT_FALSE(optional_patterns);
}
// Test that the parser correctly sets the default version if
// it is not present in the configuration.
TEST(PatternConfigurationParserTest, MalformedMissingVersion) {
std::string JSON_message = R"(
{
"FULL_NAME": {
"en_us": [
{
"pattern_identifier": "Name_en",
"positive_pattern": "name|full name",
"positive_score": 2.0,
"negative_pattern": "company",
"match_field_attributes": 2,
"match_field_input_types": 3
}
]
}
})";
base::Optional<base::Value> JSON_object =
base::JSONReader::Read(JSON_message);
ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string.";
base::Version version = ExtractVersionFromJsonObject(JSON_object.value());
ASSERT_EQ(base::Version("0"), version);
}
// Test that the parser does not return anything if the inner key points
// to a single object instead of a list.
TEST(PatternConfigurationParserTest, MalformedNotList) {
std::string JSON_message = R"(
{
"FULL_NAME": {
"en_us": {
"pattern_identifier": "Name_en",
"positive_pattern": "name|full name",
"positive_score": 2.0,
"negative_pattern": "company",
"match_field_attributes": 2,
"match_field_input_types": 3
}
}
})";
base::Optional<base::Value> JSON_object =
base::JSONReader::Read(JSON_message);
ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string.";
base::Optional<PatternProvider::Map> optional_patterns =
GetConfigurationFromJsonObject(JSON_object.value());
ASSERT_FALSE(optional_patterns);
}
} // namespace field_type_parsing
} // namespace autofill
......@@ -8,29 +8,36 @@
#include <iostream>
#include <string>
#include "base/bind.h"
#include "base/no_destructor.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h"
namespace autofill {
PatternProvider::PatternProvider() {
auto& company_patterns = patterns_[AutofillType(COMPANY_NAME).ToString()];
company_patterns["EN"].push_back(GetCompanyPatternEn());
company_patterns["DE"].push_back(GetCompanyPatternDe());
namespace {
PatternProvider* g_pattern_provider = nullptr;
}
PatternProvider::~PatternProvider() {
patterns_.clear();
}
PatternProvider::PatternProvider() = default;
PatternProvider::~PatternProvider() = default;
void PatternProvider::SetPatterns(PatternProvider::Map patterns,
const base::Version version,
const bool overwrite_equal_version) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
void PatternProvider::SetPatterns(
const std::map<std::string,
std::map<std::string, std::vector<MatchingPattern>>>&
patterns) {
patterns_ = patterns;
if (!pattern_version_.IsValid() || pattern_version_ < version ||
(overwrite_equal_version && pattern_version_ == version)) {
patterns_ = patterns;
pattern_version_ = version;
}
}
const std::vector<MatchingPattern>& PatternProvider::GetMatchPatterns(
const std::string& pattern_name,
const std::string& page_language) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return patterns_[pattern_name][page_language];
}
......@@ -41,9 +48,26 @@ const std::vector<MatchingPattern>& PatternProvider::GetMatchPatterns(
return GetMatchPatterns(pattern_name, page_language);
}
PatternProvider* PatternProvider::getInstance() {
static base::NoDestructor<PatternProvider> instance;
return instance.get();
// static.
PatternProvider& PatternProvider::GetInstance() {
if (!g_pattern_provider) {
static base::NoDestructor<PatternProvider> instance;
g_pattern_provider = instance.get();
field_type_parsing::PopulateFromResourceBundle();
}
return *g_pattern_provider;
}
// static.
void PatternProvider::SetPatternProviderForTesting(
PatternProvider* pattern_provider) {
DCHECK(pattern_provider);
g_pattern_provider = pattern_provider;
}
// static.
void PatternProvider::ResetPatternProvider() {
g_pattern_provider = nullptr;
}
} // namespace autofill
......@@ -7,24 +7,33 @@
#include <string>
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/no_destructor.h"
#include "base/sequence_checker.h"
#include "base/version.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h"
#include "components/autofill/core/common/autofill_regex_constants.h"
#include "third_party/re2/src/re2/re2.h"
namespace autofill {
// Base class for the Pattern Provider. This class contains the implementation
// for providing the matching patterns. Different subclasses provide different
// ways to load the data in for further use.
class PatternProvider {
public:
static PatternProvider* getInstance();
// Shorthand for the map structure used to store patterns.
using Map = std::map<std::string,
std::map<std::string, std::vector<MatchingPattern>>>;
// Setter for loaded patterns from external storage.
void SetPatterns(
const std::map<std::string,
std::map<std::string, std::vector<MatchingPattern>>>&
patterns);
// Returns a reference to the global Pattern Provider.
static PatternProvider& GetInstance();
// Setter for loading patterns from external storage.
void SetPatterns(const Map patterns,
const base::Version version,
const bool overwrite_equal_version);
// Provides us with all patterns that can match our field type and page
// language.
......@@ -40,19 +49,38 @@ class PatternProvider {
const std::vector<MatchingPattern>& GetAllPatternsBaseOnType(
ServerFieldType type);
private:
protected:
PatternProvider();
~PatternProvider();
// Local map to store a vector of patterns keyed by field type and
// page language.
Map patterns_;
// Version for keeping track which pattern set is in use.
base::Version pattern_version_;
// Sets a provider to be used for tests.
static void SetPatternProviderForTesting(PatternProvider* pattern_provider);
// Resets the provider pointer if the object behind it gets deleted.
static void ResetPatternProvider();
private:
// Func to sort the incoming map by score.
void SortPatternsByScore(std::vector<MatchingPattern>& patterns);
// Local map to store a vector of patterns keyed by field type and
// page language.
std::map<std::string, std::map<std::string, std::vector<MatchingPattern>>>
patterns_;
// Sequence checker to ensure thread-safety for pattern swapping.
// All functions accessing the |patterns_| member variable are
// expected to be called from the UI thread.
SEQUENCE_CHECKER(sequence_checker_);
FRIEND_TEST_ALL_PREFIXES(AutofillPatternProviderPipelineTest,
TestParsingEquivalent);
FRIEND_TEST_ALL_PREFIXES(AutofillPatternProviderPipelineTest,
DefaultPatternProviderLoads);
friend class base::NoDestructor<PatternProvider>;
};
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_PROVIDER_H_
\ No newline at end of file
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_PROVIDER_H_
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/autofill/core/browser/pattern_provider/pattern_provider.h"
#include <stddef.h>
#include <map>
......@@ -11,10 +9,46 @@
#include <vector>
#include "base/test/gtest_util.h"
#include "base/test/task_environment.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h"
#include "components/autofill/core/browser/pattern_provider/pattern_provider.h"
#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace autofill {
namespace {
// Pattern Provider with custom values set for testing.
class UnitTestPatternProvider : public PatternProvider {
public:
UnitTestPatternProvider();
~UnitTestPatternProvider();
};
UnitTestPatternProvider::UnitTestPatternProvider() {
auto& company_patterns = patterns_[AutofillType(COMPANY_NAME).ToString()];
company_patterns["EN"].push_back(GetCompanyPatternEn());
company_patterns["DE"].push_back(GetCompanyPatternDe());
PatternProvider::SetPatternProviderForTesting(this);
}
UnitTestPatternProvider::~UnitTestPatternProvider() {
PatternProvider::ResetPatternProvider();
}
} // namespace
class AutofillPatternProviderTest : public testing::Test {
protected:
UnitTestPatternProvider pattern_provider_;
~AutofillPatternProviderTest() override = default;
};
bool operator==(const MatchingPattern& mp1, const MatchingPattern& mp2) {
return (mp1.language == mp2.language &&
mp1.match_field_attributes == mp2.match_field_attributes &&
......@@ -25,15 +59,50 @@ bool operator==(const MatchingPattern& mp1, const MatchingPattern& mp2) {
mp1.positive_score == mp2.positive_score);
}
TEST(AutofillPatternProvider, Single_Match) {
TEST_F(AutofillPatternProviderTest, Single_Match) {
MatchingPattern kCompanyPatternEn = GetCompanyPatternEn();
MatchingPattern kCompanyPatternDe = GetCompanyPatternDe();
PatternProvider* pattern_provider = PatternProvider::getInstance();
PatternProvider& pattern_provider = PatternProvider::GetInstance();
ASSERT_TRUE(pattern_provider->GetMatchPatterns("COMPANY_NAME", "EN").size() >
ASSERT_TRUE(pattern_provider.GetMatchPatterns("COMPANY_NAME", "EN").size() >
0);
EXPECT_EQ(pattern_provider->GetMatchPatterns("COMPANY_NAME", "EN")[0],
EXPECT_EQ(pattern_provider.GetMatchPatterns("COMPANY_NAME", "EN")[0],
kCompanyPatternEn);
}
} // namespace autofill
\ No newline at end of file
// Test that the default pattern provider loads without crashing.
TEST(AutofillPatternProviderPipelineTest, DefaultPatternProviderLoads) {
base::test::TaskEnvironment task_environment_;
data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
base::RunLoop run_loop;
field_type_parsing::PopulateFromResourceBundle(run_loop.QuitClosure());
run_loop.Run();
PatternProvider& default_pattern_provider = PatternProvider::GetInstance();
EXPECT_FALSE(default_pattern_provider.patterns_.empty());
// Call the getter to ensure sequence checks work correctly.
default_pattern_provider.GetMatchPatterns("EMAIL_ADDRESS", "en");
}
// Test that the TestPatternProvider class uses a PatternProvider::Map
// equivalent to the DefaultPatternProvider. This is also an example of what is
// needed to test the DefaultPatternProvider. Warning: If this crashes, check
// that no state carried over from other tests using the singleton.
TEST(AutofillPatternProviderPipelineTest, TestParsingEquivalent) {
base::test::TaskEnvironment task_environment_;
data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
base::RunLoop run_loop;
field_type_parsing::PopulateFromResourceBundle(run_loop.QuitClosure());
run_loop.Run();
PatternProvider& default_pattern_provider = PatternProvider::GetInstance();
TestPatternProvider test_pattern_provider;
EXPECT_EQ(default_pattern_provider.patterns_,
test_pattern_provider.patterns_);
}
} // namespace autofill
// Copyright 2020 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/browser/pattern_provider/test_pattern_provider.h"
#include "components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h"
namespace autofill {
TestPatternProvider::TestPatternProvider() {
base::Optional<PatternProvider::Map> patterns =
field_type_parsing::GetPatternsFromResourceBundleSynchronously();
if (patterns)
patterns_ = patterns.value();
PatternProvider::SetPatternProviderForTesting(this);
}
TestPatternProvider::~TestPatternProvider() {
PatternProvider::ResetPatternProvider();
}
} // namespace autofill
// Copyright 2020 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.
#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_TEST_PATTERN_PROVIDER_H_
#define COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_TEST_PATTERN_PROVIDER_H_
#include "components/autofill/core/browser/pattern_provider/pattern_provider.h"
namespace autofill {
// The pattern provider to be used in tests. Loads the MatchingPattern
// configuration synchronously from the Resource Bundle and sets itself as the
// global PatternProvider.
class TestPatternProvider : public PatternProvider {
public:
TestPatternProvider();
~TestPatternProvider();
};
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_TEST_PATTERN_PROVIDER_H_
<?xml version="1.0" encoding="utf-8"?>
<grit-part>
<include name="IDR_AUTOFILL_REGEX_JSON" file="../autofill/core/browser/pattern_provider/resources/regex_patterns.json" type="BINDATA" compress="gzip" />
</grit-part>
......@@ -14,6 +14,7 @@ dev_ui_components_resources.grd.
<release seq="1">
<includes>
<part file="about_ui_resources.grdp" />
<part file="autofill_regex_resources.grdp" />
<part file="dom_distiller_resources.grdp" />
<part file="flags_ui_resources.grdp" />
<part file="management_resources.grdp" />
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment