diff --git a/UnitsNet.Tests/UnitParserTests.cs b/UnitsNet.Tests/UnitParserTests.cs index de3fa48a26..6e66086468 100644 --- a/UnitsNet.Tests/UnitParserTests.cs +++ b/UnitsNet.Tests/UnitParserTests.cs @@ -4,301 +4,391 @@ using System.Globalization; using UnitsNet.Tests.CustomQuantities; -namespace UnitsNet.Tests +namespace UnitsNet.Tests; + +public class UnitParserTests { - public class UnitParserTests + [Fact] + public void Constructor_WithQuantitiesCreatesNewAbbreviationsCacheAndNewQuantityInfoLookup() { - [Fact] - public void Constructor_WithQuantitiesCreatesNewAbbreviationsCacheAndNewQuantityInfoLookup() - { - var unitParser = new UnitParser([Mass.Info]); - Assert.NotNull(unitParser.Abbreviations); - Assert.NotEqual(UnitAbbreviationsCache.Default, unitParser.Abbreviations); - Assert.NotEqual(UnitsNetSetup.Default.QuantityInfoLookup, unitParser.Quantities); - } + var unitParser = new UnitParser([Mass.Info]); + Assert.NotNull(unitParser.Abbreviations); + Assert.NotEqual(UnitAbbreviationsCache.Default, unitParser.Abbreviations); + Assert.NotEqual(UnitsNetSetup.Default.QuantityInfoLookup, unitParser.Quantities); + } - [Fact] - public void Constructor_WithQuantityInfoLookupCreatesNewAbbreviationsCache() - { - var quantities = new QuantityInfoLookup([Mass.Info]); - var unitParser = new UnitParser(quantities); - Assert.NotNull(unitParser.Abbreviations); - Assert.NotEqual(UnitAbbreviationsCache.Default, unitParser.Abbreviations); - Assert.Equal(quantities, unitParser.Quantities); - } + [Fact] + public void Constructor_WithQuantityInfoLookupCreatesNewAbbreviationsCache() + { + var quantities = new QuantityInfoLookup([Mass.Info]); + var unitParser = new UnitParser(quantities); + Assert.NotNull(unitParser.Abbreviations); + Assert.NotEqual(UnitAbbreviationsCache.Default, unitParser.Abbreviations); + Assert.Equal(quantities, unitParser.Quantities); + } - [Fact] - public void CreateDefault_CreatesNewAbbreviationsCacheWithDefaultQuantities() - { - var unitParser = UnitParser.CreateDefault(); - Assert.NotNull(unitParser.Abbreviations); - Assert.NotEqual(UnitAbbreviationsCache.Default, unitParser.Abbreviations); - Assert.Equal(UnitsNetSetup.Default.QuantityInfoLookup, unitParser.Quantities); - } + [Fact] + public void CreateDefault_CreatesNewAbbreviationsCacheWithDefaultQuantities() + { + var unitParser = UnitParser.CreateDefault(); + Assert.NotNull(unitParser.Abbreviations); + Assert.NotEqual(UnitAbbreviationsCache.Default, unitParser.Abbreviations); + Assert.Equal(UnitsNetSetup.Default.QuantityInfoLookup, unitParser.Quantities); + } - [Theory] - [InlineData("m^^2", AreaUnit.SquareMeter)] - [InlineData("cm^^2", AreaUnit.SquareCentimeter)] - public void Parse_ReturnsUnitMappedByCustomAbbreviation(string customAbbreviation, AreaUnit expected) - { - var abbrevCache = new UnitAbbreviationsCache([Area.Info]); - abbrevCache.MapUnitToAbbreviation(expected, customAbbreviation); - var parser = new UnitParser(abbrevCache); + [Theory] + [InlineData("m^^2", AreaUnit.SquareMeter)] + [InlineData("cm^^2", AreaUnit.SquareCentimeter)] + public void Parse_ReturnsUnitMappedByCustomAbbreviation(string customAbbreviation, AreaUnit expected) + { + var abbrevCache = new UnitAbbreviationsCache([Area.Info]); + abbrevCache.MapUnitToAbbreviation(expected, customAbbreviation); + var parser = new UnitParser(abbrevCache); - var actual = parser.Parse(customAbbreviation); + var actual = parser.Parse(customAbbreviation); - Assert.Equal(expected, actual); - } + Assert.Equal(expected, actual); + } - [Fact] - public void Parse_AbbreviationCaseInsensitive_Lowercase_years() - { - var abbreviation = "years"; - var expected = DurationUnit.Year365; - var parser = UnitsNetSetup.Default.UnitParser; + [Fact] + public void Parse_AbbreviationCaseInsensitive_Lowercase_years() + { + var abbreviation = "years"; + var expected = DurationUnit.Year365; + var parser = UnitsNetSetup.Default.UnitParser; - var actual = parser.Parse(abbreviation); + var actual = parser.Parse(abbreviation); - Assert.Equal(expected, actual); - } + Assert.Equal(expected, actual); + } - [Fact] - public void Parse_AbbreviationCaseInsensitive_Uppercase_Years() - { - var abbreviation = "Years"; - var expected = DurationUnit.Year365; - var parser = UnitsNetSetup.Default.UnitParser; + [Fact] + public void Parse_AbbreviationCaseInsensitive_Uppercase_Years() + { + var abbreviation = "Years"; + var expected = DurationUnit.Year365; + var parser = UnitsNetSetup.Default.UnitParser; - var actual = parser.Parse(abbreviation); + var actual = parser.Parse(abbreviation); - Assert.Equal(expected, actual); - } + Assert.Equal(expected, actual); + } - [Fact] - public void Parse_GivenAbbreviationsThatAreAmbiguousWhenLowerCase_ReturnsCorrectUnit() - { - Assert.Equal(PressureUnit.Megabar, Pressure.ParseUnit("Mbar")); - Assert.Equal(PressureUnit.Millibar, Pressure.ParseUnit("mbar")); - } + [Fact] + public void Parse_GivenAbbreviationsThatAreAmbiguousWhenLowerCase_ReturnsCorrectUnit() + { + Assert.Equal(PressureUnit.Megabar, Pressure.ParseUnit("Mbar")); + Assert.Equal(PressureUnit.Millibar, Pressure.ParseUnit("mbar")); + } - [Fact] - public void Parse_NullAbbreviation_Throws_ArgumentNullException() - { - Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse(null!)); - Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse(null!, Length.Info.UnitInfos)); - Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse(null!, typeof(LengthUnit))); - } + [Fact] + public void Parse_NullAbbreviation_Throws_ArgumentNullException() + { + Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse(null!)); + Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse(null!, Length.Info.UnitInfos)); + Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse(null!, typeof(LengthUnit))); + } - [Fact] - public void Parse_UnknownUnitTypeThrowsUnitNotFoundException() - { - Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse("something")); - } + [Fact] + public void Parse_UnknownUnitTypeThrowsUnitNotFoundException() + { + Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse("something")); + } - [Fact] - public void Parse_UnknownAbbreviationThrowsUnitNotFoundException() - { - Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse("nonexistingunit")); - } - - [Theory] - [InlineData("m", typeof(LengthUnit), LengthUnit.Meter)] - [InlineData("m^1", typeof(LengthUnit), LengthUnit.Meter)] - [InlineData("m²", typeof(AreaUnit), AreaUnit.SquareMeter)] - [InlineData("m^2", typeof(AreaUnit), AreaUnit.SquareMeter)] - [InlineData("m³", typeof(VolumeUnit), VolumeUnit.CubicMeter)] - [InlineData("m^3", typeof(VolumeUnit), VolumeUnit.CubicMeter)] - [InlineData("m⁴", typeof(AreaMomentOfInertiaUnit), AreaMomentOfInertiaUnit.MeterToTheFourth)] - [InlineData("m^4", typeof(AreaMomentOfInertiaUnit), AreaMomentOfInertiaUnit.MeterToTheFourth)] - [InlineData("K⁻¹", typeof(CoefficientOfThermalExpansionUnit), CoefficientOfThermalExpansionUnit.PerKelvin)] - [InlineData("K^-1", typeof(CoefficientOfThermalExpansionUnit), CoefficientOfThermalExpansionUnit.PerKelvin)] - [InlineData("kg·s⁻¹·m⁻²", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] - [InlineData("kg·s^-1·m^-2", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] - public void Parse_CanParseUnitsWithPowers(string unitAbbreviation, Type unitType, Enum resultUnitType) - { - Assert.Equal(resultUnitType, UnitsNetSetup.Default.UnitParser.Parse(unitAbbreviation, unitType)); - } - - [Theory] - [InlineData("kg·s⁻¹·m⁻²", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] - [InlineData("kg*s⁻¹*m⁻²", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] - [InlineData("kg·s⁻¹*m⁻²", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] - public void Parse_CanParseMultiplySigns(string unitAbbreviation, Type unitType, Enum resultUnitType) - { - Assert.Equal(resultUnitType, UnitsNetSetup.Default.UnitParser.Parse(unitAbbreviation, unitType)); - } - - [Theory] - [InlineData(" kg·s⁻¹·m⁻²", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] - [InlineData("kg·s⁻¹·m⁻² ", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] - [InlineData("k g · s ⁻ ¹ · m ⁻ ² ", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] - [InlineData(" k g · s ⁻ ¹ · m ⁻ ² ", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] - public void Parse_CanParseWithWhitespacesInUnit(string unitAbbreviation, Type unitType, Enum resultUnitType) - { - Assert.Equal(resultUnitType, UnitsNetSetup.Default.UnitParser.Parse(unitAbbreviation, unitType)); - } + [Fact] + public void Parse_UnknownAbbreviationThrowsUnitNotFoundException() + { + Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse("nonexistingunit")); + } - [Fact] - public void Parse_AmbiguousUnitsThrowsException() - { - // Act 1 - var exception1 = Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse("pt")); - - // Act 2 - var exception2 = Assert.Throws(() => Length.Parse("1 pt", CultureInfo.InvariantCulture)); - - // Assert - Assert.Equal("""Cannot parse "pt" since it matches multiple units: DtpPoint ("pt"), PrinterPoint ("pt").""", exception1.Message); - Assert.Equal("""Cannot parse "pt" since it matches multiple units: DtpPoint ("pt"), PrinterPoint ("pt").""", exception2.Message); - } - - [Theory] - [InlineData("ng", "en-US", MassUnit.Nanogram)] - [InlineData("нг", "ru-RU", MassUnit.Nanogram)] - [InlineData("g", "en-US", MassUnit.Gram)] - [InlineData("г", "ru-RU", MassUnit.Gram)] - [InlineData("kg", "en-US", MassUnit.Kilogram)] - [InlineData("кг", "ru-RU", MassUnit.Kilogram)] - [InlineData("kg", "ru-RU", MassUnit.Kilogram)] // should work with the "FallbackCulture" - public void ParseMassUnit_GivenCulture(string str, string cultureName, Enum expectedUnit) - { - var formatProvider = CultureInfo.GetCultureInfo(cultureName); - UnitParser unitParser = UnitsNetSetup.Default.UnitParser; + [Theory] + [InlineData("m", typeof(LengthUnit), LengthUnit.Meter)] + [InlineData("m^1", typeof(LengthUnit), LengthUnit.Meter)] + [InlineData("m²", typeof(AreaUnit), AreaUnit.SquareMeter)] + [InlineData("m^2", typeof(AreaUnit), AreaUnit.SquareMeter)] + [InlineData("m³", typeof(VolumeUnit), VolumeUnit.CubicMeter)] + [InlineData("m^3", typeof(VolumeUnit), VolumeUnit.CubicMeter)] + [InlineData("m⁴", typeof(AreaMomentOfInertiaUnit), AreaMomentOfInertiaUnit.MeterToTheFourth)] + [InlineData("m^4", typeof(AreaMomentOfInertiaUnit), AreaMomentOfInertiaUnit.MeterToTheFourth)] + [InlineData("K⁻¹", typeof(CoefficientOfThermalExpansionUnit), CoefficientOfThermalExpansionUnit.PerKelvin)] + [InlineData("K^-1", typeof(CoefficientOfThermalExpansionUnit), CoefficientOfThermalExpansionUnit.PerKelvin)] + [InlineData("kg·s⁻¹·m⁻²", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] + [InlineData("kg·s^-1·m^-2", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] + public void Parse_CanParseUnitsWithPowers(string unitAbbreviation, Type unitType, Enum resultUnitType) + { + Assert.Equal(resultUnitType, UnitsNetSetup.Default.UnitParser.Parse(unitAbbreviation, unitType)); + } + + [Theory] + [InlineData("kg·s⁻¹·m⁻²", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] + [InlineData("kg*s⁻¹*m⁻²", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] + [InlineData("kg·s⁻¹*m⁻²", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] + public void Parse_CanParseMultiplySigns(string unitAbbreviation, Type unitType, Enum resultUnitType) + { + Assert.Equal(resultUnitType, UnitsNetSetup.Default.UnitParser.Parse(unitAbbreviation, unitType)); + } + + [Theory] + [InlineData(" kg·s⁻¹·m⁻²", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] + [InlineData("kg·s⁻¹·m⁻² ", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] + [InlineData("k g · s ⁻ ¹ · m ⁻ ² ", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] + [InlineData(" k g · s ⁻ ¹ · m ⁻ ² ", typeof(MassFluxUnit), MassFluxUnit.KilogramPerSecondPerSquareMeter)] + public void Parse_CanParseWithWhitespacesInUnit(string unitAbbreviation, Type unitType, Enum resultUnitType) + { + Assert.Equal(resultUnitType, UnitsNetSetup.Default.UnitParser.Parse(unitAbbreviation, unitType)); + } + + [Fact] + public void Parse_AmbiguousUnitsThrowsException() + { + // Act 1 + var exception1 = Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse("pt")); + + // Act 2 + var exception2 = Assert.Throws(() => Length.Parse("1 pt", CultureInfo.InvariantCulture)); + + // Assert + Assert.Equal("""Cannot parse "pt" since it matches multiple units: DtpPoint ("pt"), PrinterPoint ("pt").""", exception1.Message); + Assert.Equal("""Cannot parse "pt" since it matches multiple units: DtpPoint ("pt"), PrinterPoint ("pt").""", exception2.Message); + } + + [Theory] + [InlineData("ng", "en-US", MassUnit.Nanogram)] + [InlineData("нг", "ru-RU", MassUnit.Nanogram)] + [InlineData("g", "en-US", MassUnit.Gram)] + [InlineData("г", "ru-RU", MassUnit.Gram)] + [InlineData("kg", "en-US", MassUnit.Kilogram)] + [InlineData("кг", "ru-RU", MassUnit.Kilogram)] + [InlineData("kg", "ru-RU", MassUnit.Kilogram)] // should work with the "FallbackCulture" + public void ParseMassUnit_GivenCulture(string str, string cultureName, Enum expectedUnit) + { + var formatProvider = CultureInfo.GetCultureInfo(cultureName); + UnitParser unitParser = UnitsNetSetup.Default.UnitParser; - Assert.Equal(expectedUnit, unitParser.Parse(str, formatProvider)); - } + Assert.Equal(expectedUnit, unitParser.Parse(str, formatProvider)); + } - [Fact] - public void Parse_MappedCustomUnit() - { - var unitAbbreviationsCache = new UnitAbbreviationsCache([HowMuch.Info]); - unitAbbreviationsCache.MapUnitToAbbreviation(HowMuchUnit.Some, "fooh"); - var unitParser = new UnitParser(unitAbbreviationsCache); + [Fact] + public void Parse_MappedCustomUnit() + { + var unitAbbreviationsCache = new UnitAbbreviationsCache([HowMuch.Info]); + unitAbbreviationsCache.MapUnitToAbbreviation(HowMuchUnit.Some, "fooh"); + var unitParser = new UnitParser(unitAbbreviationsCache); - var parsedUnit = unitParser.Parse("fooh"); + var parsedUnit = unitParser.Parse("fooh"); - Assert.Equal(HowMuchUnit.Some, parsedUnit); - } + Assert.Equal(HowMuchUnit.Some, parsedUnit); + } - [Fact] - public void Parse_LengthUnit_MM_ThrowsExceptionDescribingTheAmbiguity() - { - var ex = Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse("MM")); - Assert.Equal("""Cannot parse "MM" since it matches multiple units: Megameter ("Mm"), Millimeter ("mm").""", ex.Message); - } + [Fact] + public void Parse_LengthUnit_MM_ThrowsExceptionDescribingTheAmbiguity() + { + var ex = Assert.Throws(() => UnitsNetSetup.Default.UnitParser.Parse("MM")); + Assert.Equal("""Cannot parse "MM" since it matches multiple units: Megameter ("Mm"), Millimeter ("mm").""", ex.Message); + } - [Fact] - public void TryParse_WithNullAbbreviation_ReturnsFalse() + [Fact] + public void TryParse_WithNullAbbreviation_ReturnsFalse() + { + UnitParser unitParser = UnitsNetSetup.Default.UnitParser; + Assert.Multiple(() => { - UnitParser unitParser = UnitsNetSetup.Default.UnitParser; - Assert.Multiple(() => - { - var success = unitParser.TryParse(null, out LengthUnit unit); - Assert.False(success); - }, () => - { - var success = unitParser.TryParse(null, typeof(LengthUnit), out Enum? _); - Assert.False(success); - }, () => - { - var success = unitParser.TryParse(null, [], null, out UnitInfo? _); - Assert.False(success); - }); - } - - [Fact] - public void TryParse_UnknownUnitType_ReturnsFalse() + var success = unitParser.TryParse(null, out LengthUnit unit); + Assert.False(success); + }, () => { - Assert.False(UnitsNetSetup.Default.UnitParser.TryParse("something", out StringComparison _)); - } - - [Theory] - [InlineData("")] - [InlineData("z^2")] - [InlineData("nonexistingunit")] - public void TryParse_UnknownAbbreviation_ReturnsFalse(string unknownAreaAbbreviation) + var success = unitParser.TryParse(null, typeof(LengthUnit), out Enum? _); + Assert.False(success); + }, () => { - Assert.False(UnitsNetSetup.Default.UnitParser.TryParse(unknownAreaAbbreviation, out AreaUnit _)); - } + var success = unitParser.TryParse(null, [], null, out UnitInfo? _); + Assert.False(success); + }); + } - [Fact] - public void TryParse_WithAmbiguousUnits_ReturnsFalse() - { - UnitParser unitParser = UnitsNetSetup.Default.UnitParser; - Assert.False(unitParser.TryParse("pt", CultureInfo.InvariantCulture, out LengthUnit _)); - Assert.False(unitParser.TryParse("pt", Length.Info.UnitInfos, CultureInfo.InvariantCulture, out UnitInfo? _)); - } - - [Theory] - [InlineData("ng", "en-US", MassUnit.Nanogram)] - [InlineData("нг", "ru-RU", MassUnit.Nanogram)] - [InlineData("g", "en-US", MassUnit.Gram)] - [InlineData("г", "ru-RU", MassUnit.Gram)] - [InlineData("kg", "en-US", MassUnit.Kilogram)] - [InlineData("кг", "ru-RU", MassUnit.Kilogram)] - [InlineData("kg", "ru-RU", MassUnit.Kilogram)] // should work with the "FallbackCulture" - public void TryParseMassUnit_GivenCulture(string str, string cultureName, Enum expectedUnit) - { - var formatProvider = CultureInfo.GetCultureInfo(cultureName); - UnitParser unitParser = UnitsNetSetup.Default.UnitParser; + [Fact] + public void TryParse_UnknownUnitType_ReturnsFalse() + { + Assert.False(UnitsNetSetup.Default.UnitParser.TryParse("something", out StringComparison _)); + } + + [Theory] + [InlineData("")] + [InlineData("z^2")] + [InlineData("nonexistingunit")] + public void TryParse_UnknownAbbreviation_ReturnsFalse(string unknownAreaAbbreviation) + { + Assert.False(UnitsNetSetup.Default.UnitParser.TryParse(unknownAreaAbbreviation, out AreaUnit _)); + } - var success = unitParser.TryParse(str, formatProvider, out MassUnit unitParsed); + [Fact] + public void TryParse_WithAmbiguousUnits_ReturnsFalse() + { + UnitParser unitParser = UnitsNetSetup.Default.UnitParser; + Assert.False(unitParser.TryParse("pt", CultureInfo.InvariantCulture, out LengthUnit _)); + Assert.False(unitParser.TryParse("pt", Length.Info.UnitInfos, CultureInfo.InvariantCulture, out UnitInfo? _)); + } - Assert.True(success); - Assert.Equal(expectedUnit, unitParsed); - } + [Theory] + [InlineData("ng", "en-US", MassUnit.Nanogram)] + [InlineData("нг", "ru-RU", MassUnit.Nanogram)] + [InlineData("g", "en-US", MassUnit.Gram)] + [InlineData("г", "ru-RU", MassUnit.Gram)] + [InlineData("kg", "en-US", MassUnit.Kilogram)] + [InlineData("кг", "ru-RU", MassUnit.Kilogram)] + [InlineData("kg", "ru-RU", MassUnit.Kilogram)] // should work with the "FallbackCulture" + public void TryParseMassUnit_GivenCulture(string str, string cultureName, Enum expectedUnit) + { + var formatProvider = CultureInfo.GetCultureInfo(cultureName); + UnitParser unitParser = UnitsNetSetup.Default.UnitParser; - [Fact] - public void TryGetUnitFromAbbreviation_WithLocalizedUnit_MatchingCulture_ReturnsTrue() - { - var formatProvider = CultureInfo.GetCultureInfo("ru-RU"); - UnitParser unitParser = UnitsNetSetup.Default.UnitParser; + var success = unitParser.TryParse(str, formatProvider, out MassUnit unitParsed); - var success = unitParser.TryGetUnitFromAbbreviation("кг", formatProvider, out UnitInfo? unitInfo); + Assert.True(success); + Assert.Equal(expectedUnit, unitParsed); + } + + [Fact] + public void TryGetUnitFromAbbreviation_WithLocalizedUnit_MatchingCulture_ReturnsTrue() + { + var formatProvider = CultureInfo.GetCultureInfo("ru-RU"); + UnitParser unitParser = UnitsNetSetup.Default.UnitParser; - Assert.True(success); - Assert.Equal(Mass.Info[MassUnit.Kilogram], unitInfo); - } + var success = unitParser.TryGetUnitFromAbbreviation("кг", formatProvider, out UnitInfo? unitInfo); - [Fact] - public void TryGetUnitFromAbbreviation_MatchingFallbackCulture_ReturnsTrue() - { - var formatProvider = CultureInfo.GetCultureInfo("ru-RU"); - UnitParser unitParser = UnitsNetSetup.Default.UnitParser; + Assert.True(success); + Assert.Equal(Mass.Info[MassUnit.Kilogram], unitInfo); + } + + [Fact] + public void TryGetUnitFromAbbreviation_MatchingFallbackCulture_ReturnsTrue() + { + var formatProvider = CultureInfo.GetCultureInfo("ru-RU"); + UnitParser unitParser = UnitsNetSetup.Default.UnitParser; - var success = unitParser.TryGetUnitFromAbbreviation("kg", formatProvider, out UnitInfo? unitInfo); + var success = unitParser.TryGetUnitFromAbbreviation("kg", formatProvider, out UnitInfo? unitInfo); - Assert.True(success); - Assert.Equal(Mass.Info[MassUnit.Kilogram], unitInfo); - } + Assert.True(success); + Assert.Equal(Mass.Info[MassUnit.Kilogram], unitInfo); + } - [Fact] - public void TryGetUnitFromAbbreviation_WithNullString_ReturnsFalse() - { - var success = UnitsNetSetup.Default.UnitParser.TryGetUnitFromAbbreviation(null, CultureInfo.InvariantCulture, out UnitInfo? _); + [Fact] + public void TryGetUnitFromAbbreviation_WithNullString_ReturnsFalse() + { + var success = UnitsNetSetup.Default.UnitParser.TryGetUnitFromAbbreviation(null, CultureInfo.InvariantCulture, out UnitInfo? _); - Assert.False(success); - } + Assert.False(success); + } - [Fact] - public void GetUnitFromAbbreviation_GivenAbbreviationsThatAreAmbiguousWhenLowerCase_ReturnsCorrectUnit() - { - Assert.Equal(PressureUnit.Megabar, UnitParser.Default.GetUnitFromAbbreviation("Mbar", CultureInfo.InvariantCulture).Value); - Assert.Equal(PressureUnit.Millibar, UnitParser.Default.GetUnitFromAbbreviation("mbar", CultureInfo.InvariantCulture).Value); - } + [Fact] + public void GetUnitFromAbbreviation_GivenAbbreviationsThatAreAmbiguousWhenLowerCase_ReturnsCorrectUnit() + { + Assert.Equal(PressureUnit.Megabar, UnitParser.Default.GetUnitFromAbbreviation("Mbar", CultureInfo.InvariantCulture).Value); + Assert.Equal(PressureUnit.Millibar, UnitParser.Default.GetUnitFromAbbreviation("mbar", CultureInfo.InvariantCulture).Value); + } - [Fact] - public void GetUnitFromAbbreviation_NullAbbreviation_Throws_ArgumentNullException() - { - Assert.Throws(() => UnitsNetSetup.Default.UnitParser.GetUnitFromAbbreviation(null!, CultureInfo.InvariantCulture)); - } + [Fact] + public void GetUnitFromAbbreviation_NullAbbreviation_Throws_ArgumentNullException() + { + Assert.Throws(() => UnitParser.Default.GetUnitFromAbbreviation(null!, CultureInfo.InvariantCulture)); + } - [Theory] - [InlineData("z^2")] - [InlineData("nonexistingunit")] - public void GetUnitFromAbbreviation_UnknownAbbreviationThrowsUnitNotFoundException(string unknownAbbreviation) - { - Assert.Throws(() => UnitsNetSetup.Default.UnitParser.GetUnitFromAbbreviation(unknownAbbreviation, CultureInfo.InvariantCulture)); - } + [Theory] + [InlineData("z^2")] + [InlineData("nonexistingunit")] + public void GetUnitFromAbbreviation_UnknownAbbreviationThrowsUnitNotFoundException(string unknownAbbreviation) + { + Assert.Throws(() => UnitParser.Default.GetUnitFromAbbreviation(unknownAbbreviation, CultureInfo.InvariantCulture)); + } + + [Fact] + public void GetUnitFromAbbreviation_WithValidQuantityNameAndAbbreviation_ReturnsTheExpectedUnitInfo() + { + UnitInfo unitInfo = UnitParser.Default.GetUnitFromAbbreviation("Area", "m²", CultureInfo.InvariantCulture); + Assert.Equal(Area.Info[AreaUnit.SquareMeter], unitInfo); + } + + [Fact] + public void GetUnitFromAbbreviation_WithValidQuantityTypeAndAbbreviation_ReturnsTheExpectedUnitInfo() + { + UnitInfo unitInfo = UnitParser.Default.GetUnitFromAbbreviation(typeof(Area), "m²", CultureInfo.InvariantCulture); + Assert.Equal(Area.Info[AreaUnit.SquareMeter], unitInfo); + } + + [Fact] + public void GetUnitFromAbbreviation_WithNullQuantityNameOrAbbreviation_Throws_ArgumentNullException() + { + Assert.Throws(() => UnitParser.Default.GetUnitFromAbbreviation((string)null!, "m²", CultureInfo.InvariantCulture)); + Assert.Throws(() => UnitParser.Default.GetUnitFromAbbreviation("name", null!, CultureInfo.InvariantCulture)); + } + + [Fact] + public void GetUnitFromAbbreviation_WithNullQuantityTypeOrAbbreviation_Throws_ArgumentNullException() + { + Assert.Throws(() => UnitParser.Default.GetUnitFromAbbreviation((Type)null!, "m²", CultureInfo.InvariantCulture)); + Assert.Throws(() => UnitParser.Default.GetUnitFromAbbreviation(typeof(string), null!, CultureInfo.InvariantCulture)); + } + + [Fact] + public void GetUnitFromAbbreviation_WithInvalidQuantityName_Throws_QuantityNotFoundException() + { + Assert.Throws(() => UnitParser.Default.GetUnitFromAbbreviation("InvalidQuantity", "m²", CultureInfo.InvariantCulture)); + } + + [Fact] + public void GetUnitFromAbbreviation_WithInvalidQuantityType_Throws_ArgumentException() + { + ArgumentException ex = Assert.Throws(() => + UnitParser.Default.GetUnitFromAbbreviation(typeof(string), "m²", CultureInfo.InvariantCulture)); + Assert.Equal($"""Type {typeof(string)} must be of type UnitsNet.IQuantity.""", ex.Message); + } + + [Fact] + public void GetUnitFromAbbreviation_WithAmbiguousUnits_ThrowsException() + { + AmbiguousUnitParseException ex = Assert.Throws(() => + UnitParser.Default.GetUnitFromAbbreviation("Length", "pt", CultureInfo.InvariantCulture)); + Assert.Equal("""Cannot parse "pt" since it matches multiple units: DtpPoint ("pt"), PrinterPoint ("pt").""", ex.Message); + } + + [Fact] + public void TryGetUnitFromAbbreviation_WithValidQuantityNameAndAbbreviation_ReturnsTrue() + { + var success = UnitParser.Default.TryGetUnitFromAbbreviation("Area", "m²", CultureInfo.InvariantCulture, out UnitInfo? unitInfo); + Assert.True(success); + Assert.Equal(Area.Info[AreaUnit.SquareMeter], unitInfo); + } + + [Fact] + public void TryGetUnitFromAbbreviation_WithValidQuantityTypeAndAbbreviation_ReturnsTrue() + { + var success = UnitParser.Default.TryGetUnitFromAbbreviation(typeof(Area), "m²", CultureInfo.InvariantCulture, out UnitInfo? unitInfo); + Assert.True(success); + Assert.Equal(Area.Info[AreaUnit.SquareMeter], unitInfo); + } + + [Fact] + public void TryGetUnitFromAbbreviation_WithInvalidQuantityName_ReturnsFalse() + { + var success = UnitParser.Default.TryGetUnitFromAbbreviation("InvalidQuantity", "m²", CultureInfo.InvariantCulture, + out UnitInfo? unitInfo); + Assert.False(success); + Assert.Null(unitInfo); + } + + [Fact] + public void TryGetUnitFromAbbreviation_WithInvalidQuantityType_ReturnsFalse() + { + var success = UnitParser.Default.TryGetUnitFromAbbreviation(typeof(string), "m²", CultureInfo.InvariantCulture, out UnitInfo? unitInfo); + Assert.False(success); + Assert.Null(unitInfo); + } + + [Fact] + public void TryGetUnitFromAbbreviation_WithAmbiguousUnits_ReturnsFalse() + { + var success = UnitParser.Default.TryGetUnitFromAbbreviation("Length", "pt", CultureInfo.InvariantCulture, out UnitInfo? unitInfo); + Assert.False(success); + Assert.Null(unitInfo); } } diff --git a/UnitsNet/CustomCode/UnitParser.cs b/UnitsNet/CustomCode/UnitParser.cs index b2339e9122..089fa3e953 100644 --- a/UnitsNet/CustomCode/UnitParser.cs +++ b/UnitsNet/CustomCode/UnitParser.cs @@ -125,6 +125,63 @@ public Enum Parse(string unitAbbreviation, Type unitType, IFormatProvider? forma return Parse(unitAbbreviation, quantityInfo.UnitInfos, formatProvider).Value; } + /// + /// Retrieves the corresponding to the specified unit abbreviation within a given quantity. + /// + /// The name of the quantity to which the unit belongs. + /// The abbreviation of the unit to retrieve. + /// The format provider to use for lookup. Defaults to if null. + /// + /// The that matches the specified unit abbreviation within the given quantity. + /// + /// The or the is null. + /// + /// Thrown if the specified does not correspond to a known quantity. + /// + /// + /// Thrown if the cannot be resolved to a valid unit for the specified quantity. + /// + /// + /// When a specific is provided, both localized and non-localized units would be compared. + /// Both the and the comparisons are case-insensitive. + /// + public UnitInfo GetUnitFromAbbreviation(string quantityName, string unitAbbreviation, IFormatProvider? formatProvider) + { + if (quantityName == null) throw new ArgumentNullException(nameof(quantityName)); + if (unitAbbreviation == null) throw new ArgumentNullException(nameof(unitAbbreviation)); + + QuantityInfo quantityInfo = Quantities.GetQuantityByName(quantityName); + return Parse(unitAbbreviation, quantityInfo.UnitInfos, formatProvider); + } + + /// + /// Retrieves the corresponding to the specified unit abbreviation within a given quantity. + /// + /// The type of the quantity to which the unit belongs. + /// The abbreviation of the unit to retrieve. + /// The format provider to use for lookup. Defaults to if null. + /// + /// The that matches the specified unit abbreviation within the given quantity. + /// + /// The or the is null. + /// + /// Thrown if the specified does not correspond to a known quantity. + /// + /// No quantity found matching the unit type. + /// No units match the abbreviation. + /// + /// When a specific is provided, both localized and non-localized units would be compared. + /// The comparisons are case-insensitive. + /// + public UnitInfo GetUnitFromAbbreviation(Type quantityType, string unitAbbreviation, IFormatProvider? formatProvider) + { + if (quantityType == null) throw new ArgumentNullException(nameof(quantityType)); + if (unitAbbreviation == null) throw new ArgumentNullException(nameof(unitAbbreviation)); + + QuantityInfo quantityInfo = Quantities.GetQuantityInfo(quantityType); + return Parse(unitAbbreviation, quantityInfo.UnitInfos, formatProvider); + } + /// /// Parses the specified unit abbreviation, such as "kg" or "m", to find the corresponding unit information. /// @@ -314,6 +371,70 @@ internal bool TryParse([NotNullWhen(true)] string? unitAbbreviation, Quan unit = default; return false; } + + /// + /// Attempts to retrieve the corresponding to the specified unit abbreviation within a given + /// quantity. + /// + /// The name of the quantity to which the unit belongs. + /// + /// The abbreviation of the unit to retrieve. Can be null, in which case the method will return false. + /// + /// The format provider to use for lookup. Defaults to if null. + /// + /// When this method returns, contains the that matches the specified unit abbreviation + /// within the given quantity, if the operation succeeds; otherwise, null. + /// + /// + /// true if the unit abbreviation was successfully resolved to a ; otherwise, + /// false. + /// + /// + /// This method does not throw exceptions for invalid input or unresolved unit abbreviations. Instead, it returns + /// false. + /// + public bool TryGetUnitFromAbbreviation(string quantityName, string? unitAbbreviation, IFormatProvider? formatProvider, [NotNullWhen(true)] out UnitInfo? unitInfo) + { + if (unitAbbreviation != null && Quantities.TryGetQuantityByName(quantityName, out QuantityInfo? quantityInfo)) + { + return TryParse(unitAbbreviation, quantityInfo.UnitInfos, formatProvider, out unitInfo); + } + + unitInfo = null; + return false; + } + + /// + /// Attempts to retrieve the corresponding to the specified unit abbreviation within a given + /// quantity. + /// + /// The type of the quantity to which the unit belongs. + /// + /// The abbreviation of the unit to retrieve. Can be null, in which case the method will return false. + /// + /// The format provider to use for lookup. Defaults to if null. + /// + /// When this method returns, contains the that matches the specified unit abbreviation + /// within the given quantity, if the operation succeeds; otherwise, null. + /// + /// + /// true if the unit abbreviation was successfully resolved to a ; otherwise, + /// false. + /// + /// + /// This method does not throw exceptions for invalid input or unresolved unit abbreviations. Instead, it returns + /// false. + /// + public bool TryGetUnitFromAbbreviation(Type quantityType, string? unitAbbreviation, IFormatProvider? formatProvider, [NotNullWhen(true)] out UnitInfo? unitInfo) + { + if (unitAbbreviation != null && Quantities.TryGetQuantityInfo(quantityType, out QuantityInfo? quantityInfo)) + { + return TryParse(unitAbbreviation, quantityInfo.UnitInfos, formatProvider, out unitInfo); + } + + unitInfo = null; + return false; + } /// /// Attempts to match the provided unit abbreviation against the defined abbreviations for the units and returns the