Skip to content

Commit

Permalink
[ads] Add SmartNTT numerical operator condition matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
tmancey committed Oct 29, 2024
1 parent af84212 commit 6f44f2d
Show file tree
Hide file tree
Showing 8 changed files with 372 additions and 0 deletions.
4 changes: 4 additions & 0 deletions components/brave_ads/core/internal/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,10 @@ static_library("internal") {
"serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.h",
"serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.cc",
"serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.h",
"serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.cc",
"serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.h",
"serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.cc",
"serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.h",
"serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.cc",
"serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.h",
"serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.cc",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.h"

#include <cstddef>
#include <string>

#include "base/check.h"
#include "base/logging.h"
#include "base/strings/pattern.h"
#include "base/strings/string_number_conversions.h"

namespace brave_ads {

std::optional<double> ParseNumber(const std::string_view condition) {
CHECK(base::MatchPattern(condition,
kNumericalOperatorConditionMatcherPrefixPattern));

const size_t pos = condition.find(':');
if (pos == std::string::npos || pos + 1 >= condition.size()) {
// Malformed operator.
VLOG(1) << "Malformed numerical operator condition matcher for "
<< condition;
return std::nullopt;
}

double number;
if (!base::StringToDouble(condition.substr(pos + 1), &number)) {
// Malformed number.
VLOG(1) << "Malformed numerical operator condition matcher for "
<< condition;
return std::nullopt;
}

return number;
}

} // namespace brave_ads
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_INTERNAL_NUMERICAL_OPERATOR_CONDITION_MATCHER_UTIL_INTERNAL_H_
#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_INTERNAL_NUMERICAL_OPERATOR_CONDITION_MATCHER_UTIL_INTERNAL_H_

#include <optional>
#include <string_view>

namespace brave_ads {

inline constexpr char kNumericalOperatorConditionMatcherPrefixPattern[] =
"[R?]:*";

// Parses number from condition.
std::optional<double> ParseNumber(std::string_view condition);

} // namespace brave_ads

#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_INTERNAL_NUMERICAL_OPERATOR_CONDITION_MATCHER_UTIL_INTERNAL_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.h"

#include "brave/components/brave_ads/core/internal/common/test/test_base.h"

// npm run test -- brave_unit_tests --filter=BraveAds*

namespace brave_ads {

class BraveAdsNumericalOperatorConditionMatcherUtilInternalTest
: public test::TestBase {};

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilInternalTest,
ParseIntegerNumber) {
// Act & Assert
const std::optional<double> number = ParseNumber("[R=]:1");
ASSERT_TRUE(number);

EXPECT_DOUBLE_EQ(1.0, *number);
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilInternalTest,
ParseDoubleNumber) {
// Act & Assert
const std::optional<double> number = ParseNumber("[R=]:1.0");
ASSERT_TRUE(number);

EXPECT_DOUBLE_EQ(1.0, *number);
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilInternalTest,
DoNotParseMalformedNumber) {
// Act & Assert
EXPECT_FALSE(ParseNumber("[R=]: 1 "));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilInternalTest,
DoNotParseInvalidNumber) {
// Act & Assert
EXPECT_FALSE(ParseNumber("[R=]:one"));
}

} // namespace brave_ads
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.h"

#include <limits>
#include <optional>

#include "base/logging.h"
#include "base/numerics/ranges.h"
#include "base/strings/pattern.h"
#include "base/strings/string_number_conversions.h"
#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal.h"

namespace brave_ads {

namespace {

constexpr char kEqualOperatorConditionMatcherPrefix[] = "[R=]:";
constexpr char kNotEqualOperatorConditionMatcherPrefix[] = "[R≠]:";
constexpr char kGreaterThanOperatorConditionMatcherPrefix[] = "[R>]:";
constexpr char kGreaterThanOrEqualOperatorConditionMatcherPrefix[] = "[R≥]:";
constexpr char kLessThanOperatorConditionMatcherPrefix[] = "[R<]:";
constexpr char kLessThanOrEqualOperatorConditionMatcherPrefix[] = "[R≤]:";

} // namespace

bool MatchNumericalOperator(const std::string_view value,
const std::string_view condition) {
if (!base::MatchPattern(condition,
kNumericalOperatorConditionMatcherPrefixPattern)) {
// Not an operator.
return false;
}

const std::optional<double> number = ParseNumber(condition);
if (!number) {
// Invalid number.
return false;
}

double value_as_double;
if (!base::StringToDouble(value, &value_as_double)) {
// Malformed value.
VLOG(1) << "Malformed numerical operator condition matcher for "
<< condition;
return false;
}

if (condition.starts_with(kEqualOperatorConditionMatcherPrefix)) {
return base::IsApproximatelyEqual(value_as_double, *number,
std::numeric_limits<double>::epsilon());
}

if (condition.starts_with(kNotEqualOperatorConditionMatcherPrefix)) {
return !base::IsApproximatelyEqual(value_as_double, *number,
std::numeric_limits<double>::epsilon());
}

if (condition.starts_with(kGreaterThanOperatorConditionMatcherPrefix)) {
return value_as_double > number;
}

if (condition.starts_with(
kGreaterThanOrEqualOperatorConditionMatcherPrefix)) {
return value_as_double >= number;
}

if (condition.starts_with(kLessThanOperatorConditionMatcherPrefix)) {
return value_as_double < number;
}

if (condition.starts_with(kLessThanOrEqualOperatorConditionMatcherPrefix)) {
return value_as_double <= number;
}

// Unknown operator.
VLOG(1) << "Unknown numerical operator condition matcher for " << condition;
return false;
}

} // namespace brave_ads
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_NUMERICAL_OPERATOR_CONDITION_MATCHER_UTIL_H_
#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_NUMERICAL_OPERATOR_CONDITION_MATCHER_UTIL_H_

#include <string_view>

namespace brave_ads {

// Matches a value against a condition using numerical operators. Supports
// equality, greater than, greater than or equal, less than, less than or equal
// and not equal operators.
bool MatchNumericalOperator(std::string_view value, std::string_view condition);

} // namespace brave_ads

#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_MATCHERS_NUMERICAL_OPERATOR_CONDITION_MATCHER_UTIL_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util.h"

#include "brave/components/brave_ads/core/internal/common/test/test_base.h"

// npm run test -- brave_unit_tests --filter=BraveAds*

namespace brave_ads {

class BraveAdsNumericalOperatorConditionMatcherUtilTest
: public test::TestBase {};

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
DoNotMatchNonOperator) {
// Act & Assert
EXPECT_FALSE(MatchNumericalOperator("1", "baz"));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
DoNotMatchMalformedOperator) {
// Act & Assert
EXPECT_FALSE(MatchNumericalOperator("1", "[R=]: 1 "));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest, MatchEqualOperator) {
// Act & Assert
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R=]:1"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R=]:1"));
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R=]:1.0"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R=]:1.0"));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
DoNotMatchEqualOperator) {
// Act & Assert
EXPECT_FALSE(MatchNumericalOperator("1.0", "[R=]:2"));
EXPECT_FALSE(MatchNumericalOperator("1", "[R=]:2"));
EXPECT_FALSE(MatchNumericalOperator("1.0", "[R=]:2.0"));
EXPECT_FALSE(MatchNumericalOperator("1", "[R=]:2.0"));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
MatchNotEqualOperator) {
// Act & Assert
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≠]:2"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R≠]:2"));
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≠]:2.0"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R≠]:2.0"));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
DoNotMatchNotEqualOperator) {
// Act & Assert
EXPECT_FALSE(MatchNumericalOperator("1.0", "[R≠]:1"));
EXPECT_FALSE(MatchNumericalOperator("1", "[R≠]:1"));
EXPECT_FALSE(MatchNumericalOperator("1.0", "[R≠]:1.0"));
EXPECT_FALSE(MatchNumericalOperator("1", "[R≠]:1.0"));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
MatchGreaterThanOperator) {
// Act & Assert
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R>]:0"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R>]:0"));
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R>]:0.0"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R>]:0.0"));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
DoNotMatchGreaterThanOperator) {
// Act & Assert
EXPECT_FALSE(MatchNumericalOperator("1.0", "[R>]:1"));
EXPECT_FALSE(MatchNumericalOperator("1", "[R>]:1"));
EXPECT_FALSE(MatchNumericalOperator("1.0", "[R>]:1.0"));
EXPECT_FALSE(MatchNumericalOperator("1", "[R>]:1.0"));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
MatchGreaterThanOrEqualOperator) {
// Act & Assert
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≥]:0"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R≥]:0"));
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≥]:0.0"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R≥]:0.0"));

EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≥]:1"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R≥]:1"));
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≥]:1.0"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R≥]:1.0"));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
DoNotMatchGreaterThanOrEqualOperator) {
// Act & Assert
EXPECT_FALSE(MatchNumericalOperator("1.0", "[R≥]:2"));
EXPECT_FALSE(MatchNumericalOperator("1", "[R≥]:2"));
EXPECT_FALSE(MatchNumericalOperator("1.0", "[R≥]:2.0"));
EXPECT_FALSE(MatchNumericalOperator("1", "[R≥]:2.0"));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
MatchLessThanOperator) {
// Act & Assert
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R<]:2"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R<]:2"));
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R<]:2.0"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R<]:2.0"));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
DoNotMatchLessThanOperator) {
// Act & Assert
EXPECT_FALSE(MatchNumericalOperator("1.0", "[R<]:1"));
EXPECT_FALSE(MatchNumericalOperator("1", "[R<]:1"));
EXPECT_FALSE(MatchNumericalOperator("1.0", "[R<]:1.0"));
EXPECT_FALSE(MatchNumericalOperator("1", "[R<]:1.0"));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
MatchLessThanOrEqualOperator) {
// Act & Assert
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≤]:1"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R≤]:1"));
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≤]:1.0"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R≤]:1.0"));

EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≤]:2"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R≤]:2"));
EXPECT_TRUE(MatchNumericalOperator("1.0", "[R≤]:2.0"));
EXPECT_TRUE(MatchNumericalOperator("1", "[R≤]:2.0"));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
DoNotMatchLessThanOrEqualOperator) {
// Act & Assert
EXPECT_FALSE(MatchNumericalOperator("1.0", "[R≤]:0"));
EXPECT_FALSE(MatchNumericalOperator("1", "[R≤]:0"));
EXPECT_FALSE(MatchNumericalOperator("1.0", "[R≤]:0.0"));
EXPECT_FALSE(MatchNumericalOperator("1", "[R≤]:0.0"));
}

TEST_F(BraveAdsNumericalOperatorConditionMatcherUtilTest,
DoNotMatchUnknownOperator) {
// Act & Assert
EXPECT_FALSE(MatchNumericalOperator("1", "[_]:2"));
}

} // namespace brave_ads
2 changes: 2 additions & 0 deletions components/brave_ads/core/test/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,8 @@ source_set("brave_ads_unit_tests") {
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/condition_matcher_util_unittest.cc",
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util_unittest.cc",
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal_unittest.cc",
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/numerical_operator_condition_matcher_util_internal_unittest.cc",
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/numerical_operator_condition_matcher_util_unittest.cc",
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util_unittest.cc",
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util_unittest.cc",
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util_unittest.cc",
Expand Down

0 comments on commit 6f44f2d

Please sign in to comment.