/************************************************************************ * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ************************************************************************/ #include #include "Base/Exception.h" #include "Base/Tools.h" #include "Base/Unit.h" #include "Base/Quantity.h" #include "Base/UnitsApi.h" #include "Base/UnitsSchemasData.h" #include "Base/UnitsSchemas.h" #include #include #include using Base::Quantity; using Base::QuantityFormat; using Base::RuntimeError; using Base::Tools; using Base::Unit; using Base::UnitsApi; using Base::UnitsSchema; using Base::UnitsSchemas; class SchemaTest: public testing::Test { protected: void SetUp() override { const QLocale loc(QLocale::C); QLocale::setDefault(loc); } void TearDown() override {} static std::string set(const std::string& schemaName, const Unit unit, const double value) // NOLINT { UnitsApi::setSchema(schemaName); const auto quantity = Quantity {value, unit}; return quantity.getSafeUserString(); } static std::string setWithPrecision( const std::string& name, const double value, const Unit unit, const int precision ) { UnitsApi::setSchema(name); Quantity quantity {value, unit}; QuantityFormat format = quantity.getFormat(); format.setPrecision(precision); quantity.setFormat(format); return quantity.getSafeUserString(); } static std::string setWithDenominator( const std::string& name, const double value, const Unit unit, const int denominator ) { UnitsApi::setSchema(name); Quantity quantity {value, unit}; QuantityFormat format = quantity.getFormat(); format.setDenominator(denominator); quantity.setFormat(format); return quantity.getSafeUserString(); } std::unique_ptr schemas; // NOLINT }; TEST_F(SchemaTest, meter_decimal_1_mm_precision_6) { const std::string result = setWithPrecision("MeterDecimal", 1.0, Unit::Length, 6); const auto expect {"0.001000 m"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, meter_decimal_15_mm2_precision_6) { const std::string result = setWithPrecision("MeterDecimal", 15.0, Unit::Area, 6); const auto expect {"0.000015 m^2"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, meter_decimal_123456000_mm3_precision_6) { const std::string result = setWithPrecision("MeterDecimal", 123456000.0, Unit::Volume, 6); const auto expect {"0.123456 m^3"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, meter_decimal_123456000_W_precision_6) { const std::string result = setWithPrecision("MeterDecimal", 123456000.0, Unit::Power, 6); const auto expect {"123.456000 W"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, meter_decimal_123456000_V_precision_6) { const std::string result = setWithPrecision("MeterDecimal", 123456000.0, Unit::ElectricPotential, 6); const auto expect {"123.456000 V"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, meter_decimal_123456000_W_m2_precision_6) { const std::string result = setWithPrecision("MeterDecimal", 123.456, Unit::HeatFlux, 6); const auto expect {"123.456000 W/m^2"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, meter_decimal_123456000_m_s_precision_6) { const std::string result = setWithPrecision("MeterDecimal", 123.456, Unit::Velocity, 6); const auto expect {"0.123456 m/s"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, mks_1_mm_precision_6) { const std::string result = setWithPrecision("MKS", 1.0, Unit::Length, 6); const auto expect {"1.000000 mm"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, mks_15_mm2_precision_6) { const std::string result = setWithPrecision("MKS", 15.0, Unit::Area, 6); const auto expect {"15.000000 mm^2"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, mks_123456000_mm3_precision_6) { const std::string result = setWithPrecision("MKS", 123456000.0, Unit::Volume, 6); const auto expect {"123.456000 l"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, mks_123456000_W_precision_6) { const std::string result = setWithPrecision("MKS", 123456000.0, Unit::Power, 6); const auto expect {"123.456000 W"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, mks_123456000_V_precision_6) { const std::string result = setWithPrecision("MKS", 123456000.0, Unit::ElectricPotential, 6); const auto expect {"123.456000 V"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, mks_123456000_W_m2_precision_6) { const std::string result = setWithPrecision("MKS", 123.456, Unit::HeatFlux, 6); const auto expect {"123.456000 W/m^2"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, mks_123456000_m_s_precision_6) { const std::string result = setWithPrecision("MKS", 123.456, Unit::Velocity, 6); const auto expect {"0.123456 m/s"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, internal_1_mm_precision_0) { const std::string result = setWithPrecision("Internal", 1.0, Unit::Length, 0); const auto expect {"1 mm"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, internal_100_mm_precision_0) { const std::string result = setWithPrecision("Internal", 100.0, Unit::Length, 0); const auto expect {"100 mm"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, internal_100_mm_precision_1) { const std::string result = setWithPrecision("Internal", 100.0, Unit::Length, 1); const auto expect {"100.0 mm"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, internal_20000_mm_precision_2) { const std::string result = setWithPrecision("Internal", 20000.0, Unit::Length, 2); const auto expect {"20.00 m"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_decimal_1_mm_precision_0) { const std::string result = setWithPrecision("ImperialDecimal", 1.0, Unit::Length, 0); const auto expect {"1 mm"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_decimal_10_mm_precision_0) { const std::string result = setWithPrecision("ImperialDecimal", 10.0, Unit::Length, 0); const auto expect {"10 mm"}; // https://github.com/FreeCAD/FreeCAD/commit/569154b73f818c6a88b010def687d5e684ce64c2 EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_decimal_20_mm_precision_0) { const std::string result = setWithPrecision("ImperialDecimal", 20.0, Unit::Length, 0); const auto expect {"1 in"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_1_mm_precision_0) { const std::string result = setWithPrecision("Imperial", 1.0, Unit::Length, 0); const auto expect {"39 thou"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_0_mm_precision_0) { const std::string result = setWithPrecision("Imperial", 0.0, Unit::Length, 0); const auto expect {"0 in"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_0_mm_precision_1) { const std::string result = setWithPrecision("Imperial", 0.0, Unit::Length, 1); const auto expect {"0.0 in"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_decimal_0_mm_precision_0) { const std::string result = setWithPrecision("ImperialDecimal", 0.0, Unit::Length, 0); const auto expect {"0 in"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_decimal_0_mm_precision_1) { const std::string result = setWithPrecision("ImperialDecimal", 0.0, Unit::Length, 1); const auto expect {"0.0 in"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_civil_0_mm_precision_0) { const std::string result = setWithPrecision("ImperialCivil", 0.0, Unit::Length, 0); const auto expect {"0 ft"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_civil_0_mm_precision_1) { const std::string result = setWithPrecision("ImperialCivil", 0.0, Unit::Length, 1); const auto expect {"0.0 ft"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_building_0_mm_precision_0) { const std::string result = setWithPrecision("ImperialBuilding", 0.0, Unit::Length, 0); const auto expect {"0"}; // don't know why EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_building_0_mm_precision_1) { const std::string result = setWithPrecision("ImperialBuilding", 0.0, Unit::Length, 1); const auto expect {"0"}; // don't know why EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_decimal_1_mm_precision_1) { const std::string result = setWithPrecision("ImperialDecimal", 1.0, Unit::Length, 1); const auto expect {"1 mm"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_decimal_100_mm_precision_0) { const std::string result = setWithPrecision("ImperialDecimal", 100.0, Unit::Length, 0); const auto expect {"4 in"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_decimal_100_mm_precision_1) { const std::string result = setWithPrecision("ImperialDecimal", 100.0, Unit::Length, 1); const auto expect {"3.9 in"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_decimal_100_mm_precision_2) { const std::string result = setWithPrecision("ImperialDecimal", 100.0, Unit::Length, 2); const auto expect {"3.94 in"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_decimal_1_mm_precision_2) { const std::string result = setWithPrecision("ImperialDecimal", 1.0, Unit::Length, 2); const auto expect {"0.04 in"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_decimal_1_mm_precision_4) { const std::string result = setWithPrecision("ImperialDecimal", 1.0, Unit::Length, 4); const auto expect {"0.0394 in"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_safe_user_str_same) { constexpr auto val {304.8}; const auto result = setWithPrecision("Imperial", val, Unit::Length, 2); const auto expect = Tools::escapeQuotesFromString("1.00'"); EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_safe_user_str_more) { constexpr auto val {310.0}; const auto result = setWithPrecision("Imperial", val, Unit::Length, 2); const auto expect = Tools::escapeQuotesFromString("1.02'"); EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_safe_user_str_less) { constexpr auto val {300.0}; const auto result = setWithPrecision("Imperial", val, Unit::Length, 2); const auto expect = Tools::escapeQuotesFromString("11.81\""); EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_safe_user_str_one_inch) { constexpr auto val {25.4}; const auto result = setWithPrecision("Imperial", val, Unit::Length, 2); const auto expect = Tools::escapeQuotesFromString("1.00\""); EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_building_special_function_length_inch) { constexpr auto val {25.4}; const auto result = set("ImperialBuilding", Unit::Length, val); const auto expect = Tools::escapeQuotesFromString("1\""); EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_building_special_function_length_foot) { constexpr auto val {25.4 * 12}; const auto result = set("ImperialBuilding", Unit::Length, val); const auto expect = Tools::escapeQuotesFromString("1'"); EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_building_special_function_zero_length) { const auto result = set("ImperialBuilding", Unit::Length, 0.0); const auto expect = Tools::escapeQuotesFromString("0"); EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_building_special_function_length_negative_fraction_only) { constexpr auto val {(-1.0 / 8.0) * 25.4}; // -1/8 inch in mm const auto result = setWithDenominator("ImperialBuilding", val, Unit::Length, 8); const auto expect = Tools::escapeQuotesFromString("-1/8\""); EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_building_special_function_negative_inches_and_fraction) { constexpr auto val {-2.5 * 25.4}; // -2.5 inches in mm const auto result = set("ImperialBuilding", Unit::Length, val); const auto expect = Tools::escapeQuotesFromString("-2\" - 1/2\""); EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_building_special_function_high_precision_rounding) { constexpr auto val {25.396}; // Very close to exactly 1 inch const auto result = setWithDenominator("ImperialBuilding", val, Unit::Length, 8); const auto expect = Tools::escapeQuotesFromString("1\""); EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_building_special_function_length) { GTEST_SKIP() << "QuantityParser::yyparse() is crashing on the >>1' 2\" + 1/4\"<< input, " "so disable this test"; constexpr auto val {360.6}; const auto result = set("ImperialBuilding", Unit::Length, val); const auto expect = Tools::escapeQuotesFromString("1' 2\" + 1/4\""); EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_building_special_function_length_neg) { constexpr auto val {-360.6}; const auto result = setWithDenominator("ImperialBuilding", val, Unit::Length, 8); const auto expect = Tools::escapeQuotesFromString("-1' 2\" - 1/4\""); EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_civil_special_function_angle_degrees) { constexpr auto val {180}; const auto result = set("ImperialCivil", Unit::Angle, val); const auto expect {"180°"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_civil_special_function_angle_minutes) { constexpr auto val {180.5}; const auto result = set("ImperialCivil", Unit::Angle, val); const auto expect {"180°30′"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_civil_special_function_angle_seconds) { constexpr auto val {180.11}; const auto result = set("ImperialCivil", Unit::Angle, val); const auto expect {"180°6′36″"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, imperial_civil_special_function_angle_no_degrees) { constexpr auto val {0.11}; const auto result = set("ImperialCivil", Unit::Angle, val); const auto expect {"0°6′36″"}; EXPECT_EQ(result, expect); } TEST_F(SchemaTest, unknown_schema_name_throws) { EXPECT_THROW(UnitsApi::setSchema("Unknown"), RuntimeError); } TEST_F(SchemaTest, round_trip_test) { const auto units = std::to_array({ Unit::Length, Unit::Mass, Unit::Area, Unit::Density, Unit::Volume, Unit::TimeSpan, Unit::Frequency, Unit::Velocity, Unit::Acceleration, Unit::Temperature, Unit::CurrentDensity, Unit::ElectricCurrent, Unit::ElectricPotential, Unit::ElectricCharge, Unit::SurfaceChargeDensity, Unit::MagneticFieldStrength, Unit::MagneticFlux, Unit::MagneticFluxDensity, Unit::Magnetization, Unit::ElectricalCapacitance, Unit::ElectricalInductance, Unit::ElectricalConductance, Unit::ElectricalResistance, Unit::ElectricalConductivity, Unit::ElectromagneticPotential, Unit::AmountOfSubstance, Unit::LuminousIntensity, Unit::CompressiveStrength, Unit::Pressure, Unit::ShearModulus, Unit::Stress, Unit::UltimateTensileStrength, Unit::YieldStrength, Unit::YoungsModulus, Unit::Stiffness, Unit::StiffnessDensity, Unit::Force, Unit::Work, Unit::Power, Unit::Moment, Unit::SpecificEnergy, Unit::ThermalConductivity, Unit::ThermalExpansionCoefficient, Unit::VolumetricThermalExpansionCoefficient, Unit::SpecificHeat, Unit::ThermalTransferCoefficient, Unit::HeatFlux, Unit::DynamicViscosity, Unit::KinematicViscosity, Unit::VacuumPermittivity, Unit::VolumeFlowRate, Unit::DissipationRate, Unit::InverseLength, Unit::InverseArea, Unit::InverseVolume, }); std::array values = {0.01, 0.1, 1.0, 10.0, 100.0}; double factor {}; std::string unitString; UnitsApi::setDecimals(16); UnitsApi::setSchema("Internal"); for (auto unit : units) { for (double value : values) { Quantity q1 {value, unit}; std::string result = UnitsApi::schemaTranslate(q1, factor, unitString); Quantity q2 = Quantity::parse(result); EXPECT_DOUBLE_EQ(q2.getValue(), value); } } UnitsApi::setSchema("MKS"); for (auto unit : units) { for (double value : values) { Quantity q1 {value, unit}; std::string result = UnitsApi::schemaTranslate(q1, factor, unitString); Quantity q2 = Quantity::parse(result); EXPECT_DOUBLE_EQ(q2.getValue(), value); } } UnitsApi::setSchema("Imperial"); for (auto unit : units) { for (double value : values) { Quantity q1 {value, unit}; std::string result = UnitsApi::schemaTranslate(q1, factor, unitString); Quantity q2 = Quantity::parse(result); EXPECT_NEAR(q2.getValue(), value, 0.001); } } UnitsApi::setSchema("ImperialDecimal"); for (auto unit : units) { for (double value : values) { Quantity q1 {value, unit}; std::string result = UnitsApi::schemaTranslate(q1, factor, unitString); Quantity q2 = Quantity::parse(result); EXPECT_NEAR(q2.getValue(), value, 0.001); } } UnitsApi::setSchema("Centimeter"); for (auto unit : units) { for (double value : values) { Quantity q1 {value, unit}; std::string result = UnitsApi::schemaTranslate(q1, factor, unitString); Quantity q2 = Quantity::parse(result); EXPECT_DOUBLE_EQ(q2.getValue(), value); } } UnitsApi::setSchema("MmMin"); for (auto unit : units) { for (double value : values) { Quantity q1 {value, unit}; std::string result = UnitsApi::schemaTranslate(q1, factor, unitString); Quantity q2 = Quantity::parse(result); EXPECT_DOUBLE_EQ(q2.getValue(), value); } } UnitsApi::setSchema("ImperialCivil"); for (auto unit : units) { for (double value : values) { Quantity q1 {value, unit}; std::string result = UnitsApi::schemaTranslate(q1, factor, unitString); Quantity q2 = Quantity::parse(result); EXPECT_NEAR(q2.getValue(), value, 0.001); } } UnitsApi::setSchema("FEM"); for (auto unit : units) { for (double value : values) { Quantity q1 {value, unit}; std::string result = UnitsApi::schemaTranslate(q1, factor, unitString); Quantity q2 = Quantity::parse(result); EXPECT_DOUBLE_EQ(q2.getValue(), value); } } UnitsApi::setSchema("MeterDecimal"); for (auto unit : units) { for (double value : values) { Quantity q1 {value, unit}; std::string result = UnitsApi::schemaTranslate(q1, factor, unitString); Quantity q2 = Quantity::parse(result); EXPECT_DOUBLE_EQ(q2.getValue(), value); } } }