Commit b806a6fc authored by Clemens Arbesser's avatar Clemens Arbesser Committed by Commit Bot

[Autofill Assistant] Added value comparisons to ui framework.

EQUAL is available for all value types. All other comparisons are
currently only supported for strings, integers, and dates.

Bug: b/145043394
Change-Id: I003074fd5437e1f1e83aa7f4b779a3416b707193
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2095113Reviewed-by: default avatarSandro Maggi <sandromaggi@google.com>
Commit-Queue: Clemens Arbesser <arbesser@google.com>
Cr-Commit-Position: refs/heads/master@{#749599}
parent eb0622c9
......@@ -149,6 +149,80 @@ bool ValueToString(UserModel* user_model,
return true;
}
bool Compare(UserModel* user_model,
const std::string& result_model_identifier,
const ValueComparisonProto& proto) {
auto value_a = user_model->GetValue(proto.model_identifier_a());
if (!value_a.has_value()) {
DVLOG(2) << "Error evaluating " << __func__ << ": "
<< proto.model_identifier_a() << " not found in model";
return false;
}
auto value_b = user_model->GetValue(proto.model_identifier_b());
if (!value_b.has_value()) {
DVLOG(2) << "Error evaluating " << __func__ << ": "
<< proto.model_identifier_b() << " not found in model";
return false;
}
if (proto.mode() == ValueComparisonProto::UNDEFINED) {
DVLOG(2) << "Error evaluating " << __func__ << ": mode not set";
return false;
}
if (proto.mode() == ValueComparisonProto::EQUAL) {
user_model->SetValue(result_model_identifier,
SimpleValue(*value_a == *value_b));
return true;
}
// All modes except EQUAL require a size of 1 and a common value type and
// are only supported for a subset of value types.
if (!AreAllValuesOfSize({*value_a, *value_b}, 1)) {
DVLOG(2) << "Error evaluating " << __func__ << ": comparison mode "
<< proto.mode() << "requires all input values to have size 1";
return false;
}
if (!AreAllValuesOfType({*value_a, *value_b}, value_a->kind_case())) {
DVLOG(2) << "Error evaluating " << __func__ << ": comparison mode "
<< proto.mode()
<< "requires all input values to share the same type, but got "
<< value_a->kind_case() << " and " << value_b->kind_case();
return false;
}
if (value_a->kind_case() != ValueProto::kInts &&
value_a->kind_case() != ValueProto::kDates &&
value_a->kind_case() != ValueProto::kStrings) {
DVLOG(2) << "Error evaluating " << __func__
<< ": the selected comparison mode is only supported for "
"integers, strings, and dates";
return false;
}
bool result = false;
switch (proto.mode()) {
case ValueComparisonProto::LESS:
result = *value_a < *value_b;
break;
case ValueComparisonProto::LESS_OR_EQUAL:
result = *value_a < *value_b || value_a == value_b;
break;
case ValueComparisonProto::GREATER_OR_EQUAL:
result = *value_a > *value_b || value_a == value_b;
break;
case ValueComparisonProto::GREATER:
result = *value_a > *value_b;
break;
case ValueComparisonProto::EQUAL:
case ValueComparisonProto::UNDEFINED:
NOTREACHED();
return false;
}
user_model->SetValue(result_model_identifier, SimpleValue(result));
return true;
}
} // namespace
base::WeakPtr<BasicInteractions> BasicInteractions::GetWeakPtr() {
......@@ -208,6 +282,9 @@ bool BasicInteractions::ComputeValue(const ComputeValueProto& proto) {
}
return ValueToString(delegate_->GetUserModel(),
proto.result_model_identifier(), proto.to_string());
case ComputeValueProto::kComparison:
return Compare(delegate_->GetUserModel(), proto.result_model_identifier(),
proto.comparison());
case ComputeValueProto::KIND_NOT_SET:
DVLOG(2) << "Error computing value: kind not set";
return false;
......
......@@ -215,4 +215,102 @@ TEST_F(BasicInteractionsTest, EndActionWithCallbackSucceeds) {
EXPECT_TRUE(basic_interactions_.EndAction(proto));
}
TEST_F(BasicInteractionsTest, ComputeValueCompare) {
user_model_.SetValue("value_a", ValueProto());
user_model_.SetValue("value_b", ValueProto());
ComputeValueProto proto;
proto.mutable_comparison();
// Fields are missing.
EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
proto.mutable_comparison()->set_model_identifier_a("value_a");
EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
proto.mutable_comparison()->set_model_identifier_b("value_b");
EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
proto.set_result_model_identifier("result");
EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
// EQUAL supported for all value types.
proto.mutable_comparison()->set_mode(ValueComparisonProto::EQUAL);
EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
user_model_.SetValue("value_a", SimpleValue(std::string("string_a")));
user_model_.SetValue("value_b", SimpleValue(std::string("string_b")));
EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
user_model_.SetValue("value_a", SimpleValue(true));
user_model_.SetValue("value_b", SimpleValue(false));
EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
user_model_.SetValue("value_a", SimpleValue(1));
user_model_.SetValue("value_b", SimpleValue(2));
EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
user_model_.SetValue("value_a", SimpleValue(CreateDateProto(2020, 8, 7)));
user_model_.SetValue("value_b", SimpleValue(CreateDateProto(2020, 11, 5)));
EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
ValueProto user_actions_value;
user_actions_value.mutable_user_actions();
user_model_.SetValue("value_a", user_actions_value);
user_model_.SetValue("value_b", user_actions_value);
EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
// Some types are not supported for comparison mode != EQUAL.
proto.mutable_comparison()->set_mode(ValueComparisonProto::LESS);
user_model_.SetValue("value_a", ValueProto());
user_model_.SetValue("value_b", ValueProto());
EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
user_model_.SetValue("value_a", SimpleValue(true));
user_model_.SetValue("value_b", SimpleValue(false));
EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
user_model_.SetValue("value_a", user_actions_value);
user_model_.SetValue("value_b", user_actions_value);
EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
// Different types fail for mode != EQUAL.
user_model_.SetValue("value_a", SimpleValue(1));
user_model_.SetValue("value_b", SimpleValue(std::string("a")));
EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
// Size != 1 fails for mode != EQUAL.
ValueProto multi_value;
multi_value.mutable_booleans()->add_values(true);
multi_value.mutable_booleans()->add_values(false);
user_model_.SetValue("value_a", multi_value);
user_model_.SetValue("value_b", multi_value);
EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
// Check comparison results.
proto.mutable_comparison()->set_mode(ValueComparisonProto::LESS);
user_model_.SetValue("value_a", SimpleValue(1));
user_model_.SetValue("value_b", SimpleValue(2));
EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
EXPECT_EQ(user_model_.GetValue("result"), SimpleValue(true));
user_model_.SetValue("value_a", SimpleValue(1));
user_model_.SetValue("value_b", SimpleValue(1));
EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
EXPECT_EQ(user_model_.GetValue("result"), SimpleValue(false));
proto.mutable_comparison()->set_mode(ValueComparisonProto::LESS_OR_EQUAL);
EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
EXPECT_EQ(user_model_.GetValue("result"), SimpleValue(true));
proto.mutable_comparison()->set_mode(ValueComparisonProto::GREATER_OR_EQUAL);
user_model_.SetValue("value_a", SimpleValue(1));
user_model_.SetValue("value_b", SimpleValue(2));
EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
EXPECT_EQ(user_model_.GetValue("result"), SimpleValue(false));
user_model_.SetValue("value_a", SimpleValue(1));
user_model_.SetValue("value_b", SimpleValue(1));
EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
EXPECT_EQ(user_model_.GetValue("result"), SimpleValue(true));
proto.mutable_comparison()->set_mode(ValueComparisonProto::GREATER);
EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
EXPECT_EQ(user_model_.GetValue("result"), SimpleValue(false));
user_model_.SetValue("value_a", SimpleValue(2));
EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
EXPECT_EQ(user_model_.GetValue("result"), SimpleValue(true));
}
} // namespace autofill_assistant
......@@ -84,6 +84,8 @@ message ComputeValueProto {
BooleanNotProto boolean_not = 4;
// Creates a string representation of the specified value.
ToStringProto to_string = 5;
// Compares two values.
ValueComparisonProto comparison = 6;
}
// The model identifier to write the result to.
......@@ -125,6 +127,27 @@ message DateFormatProto {
optional string date_format = 1;
}
// A comparison of two values in the form |value_a| <mode> |value_b|. EQUAL is
// supported for all values. All other comparison modes are only supported for
// single integers, strings, and dates.
message ValueComparisonProto {
enum Mode {
UNDEFINED = 0;
LESS = 1;
LESS_OR_EQUAL = 2;
EQUAL = 3;
GREATER_OR_EQUAL = 4;
GREATER = 5;
}
// The first value to compare.
optional string model_identifier_a = 1;
// The second value to compare.
optional string model_identifier_b = 2;
// The comparison mode.
optional Mode mode = 3;
}
// Displays a standard info popup.
message ShowInfoPopupProto {
optional InfoPopupProto info_popup = 1;
......
......@@ -4,6 +4,8 @@
#include "components/autofill_assistant/browser/value_util.h"
#include <algorithm>
#include "base/i18n/case_conversion.h"
#include "base/strings/utf_string_conversions.h"
namespace autofill_assistant {
......@@ -57,6 +59,36 @@ bool operator==(const ValueProto& value_a, const ValueProto& value_b) {
return true;
}
bool operator<(const ValueProto& value_a, const ValueProto& value_b) {
if (value_a.kind_case() != value_b.kind_case()) {
return false;
}
if (!AreAllValuesOfSize({value_a, value_b}, 1)) {
return false;
}
switch (value_a.kind_case()) {
case ValueProto::kStrings:
return base::i18n::FoldCase(
base::UTF8ToUTF16(value_a.strings().values(0))) <
base::i18n::FoldCase(
base::UTF8ToUTF16(value_b.strings().values(0)));
case ValueProto::kInts:
return value_a.ints().values(0) < value_b.ints().values(0);
case ValueProto::kDates:
return value_a.dates().values(0) < value_b.dates().values(0);
case ValueProto::kUserActions:
case ValueProto::kBooleans:
case ValueProto::KIND_NOT_SET:
NOTREACHED();
return false;
}
return true;
}
bool operator>(const ValueProto& value_a, const ValueProto& value_b) {
return value_b < value_a && !(value_b == value_a);
}
// Compares two |ModelValue| instances and returns true if they exactly match.
bool operator==(const ModelProto::ModelValue& value_a,
const ModelProto::ModelValue& value_b) {
......@@ -97,6 +129,14 @@ bool operator==(const DateProto& value_a, const DateProto& value_b) {
value_a.month() == value_b.month() && value_a.day() == value_b.day();
}
bool operator<(const DateProto& value_a, const DateProto& value_b) {
auto tuple_a =
std::make_tuple(value_a.year(), value_a.month(), value_a.day());
auto tuple_b =
std::make_tuple(value_b.year(), value_b.month(), value_b.day());
return tuple_a < tuple_b;
}
// Intended for debugging. Writes a string representation of |values| to |out|.
template <typename T>
std::ostream& WriteRepeatedField(std::ostream& out, const T& values) {
......
......@@ -13,9 +13,11 @@
namespace autofill_assistant {
// Custom comparison operator for |ValueProto|, because we can't use
// Custom comparison operators for |ValueProto|, because we can't use
// |MessageDifferencer| for protobuf lite and can't rely on serialization.
bool operator==(const ValueProto& value_a, const ValueProto& value_b);
bool operator<(const ValueProto& value_a, const ValueProto& value_b);
bool operator>(const ValueProto& value_a, const ValueProto& value_b);
// Custom comparison operator for |ModelValue|.
bool operator==(const ModelProto::ModelValue& value_a,
......@@ -31,8 +33,9 @@ bool operator==(const DirectActionProto& value_a,
// Custom comparison operator for |UserActionProto|.
bool operator==(const UserActionProto& value_a, const UserActionProto& value_b);
// Custom comparison operator for |DateProto|.
// Custom comparison operators for |DateProto|.
bool operator==(const DateProto& value_a, const DateProto& value_b);
bool operator<(const DateProto& value_a, const DateProto& value_b);
// Intended for debugging.
std::ostream& operator<<(std::ostream& out, const ValueProto& value);
......
......@@ -235,5 +235,55 @@ TEST_F(ValueUtilTest, CombineValues) {
EXPECT_THAT(CombineValues({value_a, value_b, value_c}), expected);
}
TEST_F(ValueUtilTest, SmallerOperatorForValueProto) {
EXPECT_TRUE(SimpleValue(1) < SimpleValue(2));
EXPECT_TRUE(SimpleValue(std::string("a")) < SimpleValue(std::string("b")));
EXPECT_TRUE(SimpleValue(CreateDateProto(2020, 4, 19)) <
SimpleValue(CreateDateProto(2020, 4, 20)));
EXPECT_TRUE(SimpleValue(CreateDateProto(2020, 3, 21)) <
SimpleValue(CreateDateProto(2020, 4, 20)));
EXPECT_TRUE(SimpleValue(CreateDateProto(2019, 5, 21)) <
SimpleValue(CreateDateProto(2020, 4, 20)));
EXPECT_FALSE(SimpleValue(2) < SimpleValue(1));
EXPECT_FALSE(SimpleValue(std::string("b")) < SimpleValue(std::string("a")));
EXPECT_FALSE(SimpleValue(CreateDateProto(2020, 4, 20)) <
SimpleValue(CreateDateProto(2020, 4, 19)));
EXPECT_FALSE(SimpleValue(CreateDateProto(2020, 4, 20)) <
SimpleValue(CreateDateProto(2020, 3, 21)));
EXPECT_FALSE(SimpleValue(CreateDateProto(2020, 4, 20)) <
SimpleValue(CreateDateProto(2019, 5, 21)));
EXPECT_FALSE(SimpleValue(1) < SimpleValue(1));
EXPECT_FALSE(SimpleValue(std::string("a")) < SimpleValue(std::string("a")));
EXPECT_FALSE(SimpleValue(CreateDateProto(2020, 4, 19)) <
SimpleValue(CreateDateProto(2020, 4, 19)));
// Empty values.
ValueProto value_a;
ValueProto value_b;
EXPECT_FALSE(value_a < value_b || value_b < value_a);
// Different types.
value_a = SimpleValue(std::string("a"));
value_b = SimpleValue(1);
EXPECT_FALSE(value_a < value_b || value_b < value_a);
// Size != 1.
value_a = SimpleValue(1);
value_b.mutable_booleans()->add_values(2);
value_b.mutable_booleans()->add_values(3);
EXPECT_FALSE(value_a < value_b || value_b < value_a);
// Unsupported types.
value_a.mutable_user_actions();
value_b.mutable_user_actions();
EXPECT_FALSE(value_a < value_b || value_b < value_a);
value_a.mutable_booleans();
value_b.mutable_booleans();
EXPECT_FALSE(value_a < value_b || value_b < value_a);
}
} // namespace value_util
} // namespace autofill_assistant
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