chromium/chromeos/components/quick_answers/utils/unit_converter_unittest.cc

// 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