// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/components/quick_answers/utils/unit_converter.h"
#include <memory>
#include <string>
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chromeos/components/quick_answers/test/test_helpers.h"
#include "chromeos/components/quick_answers/test/unit_conversion_unittest_constants.h"
#include "chromeos/components/quick_answers/utils/quick_answers_utils.h"
#include "chromeos/components/quick_answers/utils/unit_conversion_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace quick_answers {
namespace {
using base::Value;
constexpr char kLengthCategory[] = "Length";
constexpr char kFakeUnitName[] = "FakeUnit";
constexpr double kSamplePreferredRange = 100;
constexpr double kStrictPreferredRange = 1.001;
constexpr double kConvertSouceValue = 100;
} // namespace
class UnitConverterTest : public testing::Test {
public:
UnitConverterTest() = default;
UnitConverterTest(const UnitConverterTest&) = delete;
UnitConverterTest& operator=(const UnitConverterTest&) = delete;
UnitConverter* CreateUnitConverter() {
converter_ = std::make_unique<UnitConverter>(rule_set_);
return converter_.get();
}
void AddConversion(const std::string& category, Value units) {
Value::Dict conversion;
conversion.Set(kCategoryPath, category);
conversion.Set(kUnitsPath, std::move(units));
rule_set_.Append(std::move(conversion));
}
private:
Value::List rule_set_;
std::unique_ptr<UnitConverter> converter_;
};
TEST_F(UnitConverterTest, GetConversionWithEmptyRulesetShouldReturnNullptr) {
auto* converter = CreateUnitConverter();
auto* conversion = converter->GetConversionForCategory(kMassCategory);
EXPECT_EQ(conversion, nullptr);
}
TEST_F(UnitConverterTest, GetConversionWithUnkownCategoryShouldReturnNullptr) {
AddConversion(kMassCategory, Value());
auto* converter = CreateUnitConverter();
auto* conversion = converter->GetConversionForCategory(kLengthCategory);
EXPECT_EQ(conversion, nullptr);
}
TEST_F(UnitConverterTest, GetConversionWithKnownCategoryShouldSucceed) {
AddConversion(kMassCategory, Value());
auto* converter = CreateUnitConverter();
auto* conversion = converter->GetConversionForCategory(kMassCategory);
auto* category_name = conversion->FindStringByDottedPath(kCategoryPath);
EXPECT_EQ(*category_name, kMassCategory);
}
TEST_F(UnitConverterTest, GetPossibleUnitsWithEmptyRulesetShouldReturnNullptr) {
auto* converter = CreateUnitConverter();
auto* units = converter->GetPossibleUnitsForCategory(kMassCategory);
EXPECT_EQ(units, nullptr);
}
TEST_F(UnitConverterTest, GetPossibleUnitsWithKnownCategoryShouldSuccess) {
Value::List input_units;
input_units.Append(CreateUnit(kKilogramName, kKilogramRateA));
AddConversion(kMassCategory, Value(std::move(input_units)));
auto* converter = CreateUnitConverter();
auto* units = converter->GetPossibleUnitsForCategory(kMassCategory);
EXPECT_EQ(units->size(), 1u);
auto& unit = (*units)[0].GetDict();
EXPECT_EQ(unit.FindDoubleByDottedPath(kConversionToSiAPath), kKilogramRateA);
EXPECT_EQ(*unit.FindStringByDottedPath(kNamePath), kKilogramName);
}
TEST_F(UnitConverterTest,
FindProperDestinationUnitWithEmptyRulesetShouldReturnNullptr) {
auto* converter = CreateUnitConverter();
auto* unit = converter->FindProperDestinationUnit(
CreateUnit(kKilogramName, kKilogramRateA,
/*rate_b=*/kInvalidRateTermValue, kMassCategory),
kSamplePreferredRange);
EXPECT_EQ(unit, nullptr);
}
TEST_F(UnitConverterTest,
FindProperDestinationUnitForSameUnitShouldReturnNullptr) {
Value::List input_units;
input_units.Append(CreateUnit(kKilogramName, kKilogramRateA));
AddConversion(kMassCategory, Value(std::move(input_units)));
auto* converter = CreateUnitConverter();
// Should ignore the source unit itself in the ruleset.
auto* unit = converter->FindProperDestinationUnit(
CreateUnit(kKilogramName, kKilogramRateA,
/*rate_b=*/kInvalidRateTermValue, kMassCategory),
kSamplePreferredRange);
EXPECT_EQ(unit, nullptr);
}
TEST_F(UnitConverterTest,
FindProperDestinationUnitWithProperUnitsShouldSuccess) {
Value::List input_units;
input_units.Append(CreateUnit(kKilogramName, kKilogramRateA));
input_units.Append(CreateUnit(kPoundName, kPoundRateA));
AddConversion(kMassCategory, Value(std::move(input_units)));
auto* converter = CreateUnitConverter();
auto* unit = converter->FindProperDestinationUnit(
CreateUnit(kKilogramName, kKilogramRateA,
/*rate_b=*/kInvalidRateTermValue, kMassCategory),
kSamplePreferredRange);
EXPECT_EQ(unit->FindDoubleByDottedPath(kConversionToSiAPath), kPoundRateA);
EXPECT_EQ(*unit->FindStringByDottedPath(kNamePath), kPoundName);
}
TEST_F(UnitConverterTest,
FindProperDestinationUnitForEmptySourceUnitShouldReturnNullptr) {
Value::List input_units;
input_units.Append(CreateUnit(kKilogramName, kKilogramRateA));
input_units.Append(CreateUnit(kPoundName, kPoundRateA));
AddConversion(kMassCategory, Value(std::move(input_units)));
auto* converter = CreateUnitConverter();
// Should find nothing for empty source unit.
auto* unit = converter->FindProperDestinationUnit(Value::Dict(),
kSamplePreferredRange);
EXPECT_EQ(unit, nullptr);
}
TEST_F(UnitConverterTest,
FindProperDestinationUnitForStrictRangeShouldReturnNullptr) {
Value::List input_units;
input_units.Append(CreateUnit(kKilogramName, kKilogramRateA));
input_units.Append(CreateUnit(kPoundName, kPoundRateA));
AddConversion(kMassCategory, Value(std::move(input_units)));
auto* converter = CreateUnitConverter();
// No unit within the preferred conversion rate found.
auto* unit = converter->FindProperDestinationUnit(
CreateUnit(kKilogramName, kKilogramRateA,
/*rate_b=*/kInvalidRateTermValue, kMassCategory),
kStrictPreferredRange);
EXPECT_EQ(unit, nullptr);
}
TEST_F(UnitConverterTest,
FindProperDestinationUnitBetweenMultipleUnitsShouldReturnClosestRate) {
Value::List input_units;
input_units.Append(CreateUnit(kKilogramName, kKilogramRateA));
input_units.Append(CreateUnit(kPoundName, kPoundRateA));
input_units.Append(CreateUnit(kOunceName, kOunceRateA));
AddConversion(kMassCategory, Value(std::move(input_units)));
auto* converter = CreateUnitConverter();
// Should return the unit with closest conversion rate, which is Pound.
auto* unit = converter->FindProperDestinationUnit(
CreateUnit(kKilogramName, kKilogramRateA,
/*rate_b=*/kInvalidRateTermValue, kMassCategory),
100);
EXPECT_EQ(unit->FindDoubleByDottedPath(kConversionToSiAPath), kPoundRateA);
EXPECT_EQ(*unit->FindStringByDottedPath(kNamePath), kPoundName);
}
TEST_F(UnitConverterTest, ConvertWithProperInputShouldSuccess) {
auto* converter = CreateUnitConverter();
auto result = converter->Convert(kConvertSouceValue,
CreateUnit(kKilogramName, kKilogramRateA),
CreateUnit(kPoundName, kPoundRateA));
auto expected_result = BuildUnitConversionResultText(
base::StringPrintf(kResultValueTemplate,
(kKilogramRateA / kPoundRateA) * kConvertSouceValue),
GetUnitDisplayText(kPoundName));
EXPECT_EQ(result, expected_result);
}
TEST_F(UnitConverterTest,
ConvertWithZeroRateSourceUnitShouldReturnEmptyResult) {
auto* converter = CreateUnitConverter();
// Should return empty result if input source unit has 0 conversion rate.
auto result =
converter->Convert(kConvertSouceValue, CreateUnit(kFakeUnitName),
CreateUnit(kPoundName, kPoundRateA));
EXPECT_EQ(result, std::string());
}
TEST_F(UnitConverterTest,
ConvertWithZeroRateDestinationUnitShouldReturnEmptyResult) {
auto* converter = CreateUnitConverter();
// Should return empty result if input destination unit has 0 conversion rate.
auto result = converter->Convert(kConvertSouceValue,
CreateUnit(kPoundName, kPoundRateA),
CreateUnit(kFakeUnitName));
EXPECT_EQ(result, std::string());
}
} // namespace quick_answers