Skip to content
50 changes: 49 additions & 1 deletion UnitsNet.Tests/UnitAbbreviationsCacheTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,29 @@ public void GetUnitAbbreviationsReturnsTheExpectedAbbreviationWhenConstructedWit
Assert.Equal("g", unitAbbreviationCache.GetUnitAbbreviations(MassUnit.Gram, AmericanCulture)[0]);
}

[Fact]
public void UnitInfoOverloads_WithUnitOutsideConfiguredQuantities_ThrowUnitNotFoundException()
{
var unitAbbreviationCache = new UnitAbbreviationsCache([Mass.Info]);
UnitInfo lengthUnitInfo = Length.Info[LengthUnit.Meter];

Assert.Multiple(checks:
[
() => Assert.Throws<UnitNotFoundException>(() => unitAbbreviationCache.GetUnitAbbreviations(lengthUnitInfo, AmericanCulture)),
() => Assert.Throws<UnitNotFoundException>(() => unitAbbreviationCache.GetDefaultAbbreviation(lengthUnitInfo, AmericanCulture)),
() => Assert.Throws<UnitNotFoundException>(() => unitAbbreviationCache.MapUnitToAbbreviation(lengthUnitInfo, AmericanCulture, "m")),
() => Assert.Throws<UnitNotFoundException>(() => unitAbbreviationCache.MapUnitToDefaultAbbreviation(lengthUnitInfo, AmericanCulture, "m"))
]);
}

[Fact]
public void GetDefaultAbbreviationReturnsTheExpectedAbbreviationWhenConstructedWithTheSpecificQuantityInfo()
{
Assert.Multiple(checks:
[
() => { Assert.Equal("g", new UnitAbbreviationsCache([Mass.Info]).GetDefaultAbbreviation(MassUnit.Gram, AmericanCulture)); },
() => { Assert.Equal("g", new UnitAbbreviationsCache([Mass.Info]).GetDefaultAbbreviation(typeof(MassUnit), (int)MassUnit.Gram, AmericanCulture)); }
() => { Assert.Equal("g", new UnitAbbreviationsCache([Mass.Info]).GetDefaultAbbreviation(typeof(MassUnit), (int)MassUnit.Gram, AmericanCulture)); },
() => { Assert.Equal("g", new UnitAbbreviationsCache([Mass.Info]).GetDefaultAbbreviation(Mass.Info[MassUnit.Gram])); }
]);
}

Expand Down Expand Up @@ -133,6 +149,14 @@ public void GetDefaultAbbreviation_WithNullUnitType_ThrowsArgumentNullException(
Assert.Throws<ArgumentNullException>(() => UnitAbbreviationsCache.Default.GetDefaultAbbreviation(null!, 1));
}

[Fact]
public void GetDefaultAbbreviation_WithNullUnitInfo_ThrowsArgumentNullException()
{
var result = Assert.Throws<ArgumentNullException>(() => UnitAbbreviationsCache.Default.GetDefaultAbbreviation((UnitInfo)null!));

Assert.Equal("unitInfo", result.ParamName);
}

[Fact]
public void GetDefaultAbbreviationThrowsUnitNotFoundExceptionIfNoneExist()
{
Expand Down Expand Up @@ -230,6 +254,14 @@ public void MapUnitToDefaultAbbreviation_GivenUnitTypeValueAndCulture_SetsDefaul
Assert.Equal("m^2", cache.GetDefaultAbbreviation(AreaUnit.SquareMeter, AmericanCulture));
}

[Fact]
public void MapUnitToDefaultAbbreviation_WithNullUnitInfo_ThrowsArgumentNullException()
{
var result = Assert.Throws<ArgumentNullException>(() => UnitAbbreviationsCache.Default.MapUnitToDefaultAbbreviation((UnitInfo)null!, AmericanCulture, "x"));

Assert.Equal("unitInfo", result.ParamName);
}

[Fact]
public void MapUnitToDefaultAbbreviation_GivenCustomAbbreviation_SetsAbbreviationUsedByQuantityToString()
{
Expand All @@ -253,6 +285,22 @@ public void MapUnitToAbbreviation_GivenUnitTypeAndValue_AddsTheAbbreviationForUn
Assert.DoesNotContain("zz", cache.GetUnitAbbreviations(MassUnit.Gram, AmericanCulture));
}

[Fact]
public void MapUnitToAbbreviation_WithNullUnitInfo_ThrowsArgumentNullException()
{
var result = Assert.Throws<ArgumentNullException>(() => UnitAbbreviationsCache.Default.MapUnitToAbbreviation((UnitInfo)null!, AmericanCulture, "x"));

Assert.Equal("unitInfo", result.ParamName);
}

[Fact]
public void GetUnitAbbreviations_WithNullUnitInfo_ThrowsArgumentNullException()
{
var result = Assert.Throws<ArgumentNullException>(() => UnitAbbreviationsCache.Default.GetUnitAbbreviations((UnitInfo)null!, AmericanCulture));

Assert.Equal("unitInfo", result.ParamName);
}

[Fact]
public void MapUnitToAbbreviation_DoesNotInsertDuplicates()
{
Expand Down
87 changes: 69 additions & 18 deletions UnitsNet/CustomCode/UnitAbbreviationsCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public void MapUnitToAbbreviation<TUnitType>(TUnitType unit, params IEnumerable<
MapUnitToAbbreviation(UnitKey.ForUnit(unit), abbreviations);
}

/// <inheritdoc cref="MapUnitToAbbreviation{TUnitType}(TUnitType,IEnumerable{string})"/>>
/// <inheritdoc cref="MapUnitToAbbreviation{TUnitType}(TUnitType,IEnumerable{string})"/>
/// <param name="unitType">The unit enum type.</param>
/// <param name="unitValue">The unit enum value.</param>
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="CultureInfo.CurrentCulture" /> if null.</param>
Expand All @@ -121,15 +121,15 @@ public void MapUnitToAbbreviation(Type unitType, int unitValue, IFormatProvider?
MapUnitToAbbreviation(UnitKey.Create(unitType, unitValue), formatProvider, abbreviations);
}

/// <inheritdoc cref="MapUnitToAbbreviation{TUnitType}(TUnitType,IEnumerable{string})"/>>
/// <inheritdoc cref="MapUnitToAbbreviation{TUnitType}(TUnitType,IEnumerable{string})"/>
/// <param name="unitKey">The unit key value.</param>
/// <param name="abbreviations">Unit abbreviations to add.</param>
public void MapUnitToAbbreviation(UnitKey unitKey, params IEnumerable<string> abbreviations)
{
MapUnitToAbbreviation(unitKey, CultureInfo.CurrentCulture, abbreviations);
}

/// <inheritdoc cref="MapUnitToAbbreviation{TUnitType}(TUnitType,IEnumerable{string})"/>>
/// <inheritdoc cref="MapUnitToAbbreviation{TUnitType}(TUnitType,IEnumerable{string})"/>
/// <param name="unit">The unit enum value.</param>
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="CultureInfo.CurrentCulture" /> if null.</param>
/// <param name="abbreviations">Unit abbreviations to add.</param>
Expand All @@ -140,13 +140,22 @@ public void MapUnitToAbbreviation<TUnitType>(TUnitType unit, IFormatProvider? fo
MapUnitToAbbreviation(UnitKey.ForUnit(unit), formatProvider, abbreviations);
}

/// <inheritdoc cref="MapUnitToAbbreviation{TUnitType}(TUnitType,IEnumerable{string})"/>>
/// <inheritdoc cref="MapUnitToAbbreviation{TUnitType}(TUnitType,IEnumerable{string})"/>
/// <param name="unitKey">The unit key value.</param>
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="CultureInfo.CurrentCulture" /> if null.</param>
/// <param name="abbreviations">Unit abbreviations to add.</param>
public void MapUnitToAbbreviation(UnitKey unitKey, IFormatProvider? formatProvider, params IEnumerable<string> abbreviations)
{
PerformAbbreviationMapping(unitKey, formatProvider, false, abbreviations);
MapUnitToAbbreviation(Quantities.GetUnitInfo(unitKey), formatProvider, abbreviations);
}

/// <inheritdoc cref="MapUnitToAbbreviation{TUnitType}(TUnitType,IEnumerable{string})"/>
/// <param name="unitInfo">The info representing the unit.</param>
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="CultureInfo.CurrentCulture" /> if null.</param>
/// <param name="abbreviations">Unit abbreviations to add.</param>
public void MapUnitToAbbreviation(UnitInfo unitInfo, IFormatProvider? formatProvider, params IEnumerable<string> abbreviations)
{
AddAbbreviation(GetConfiguredUnitInfo(unitInfo), formatProvider, false, abbreviations);
}

#endregion
Expand All @@ -171,15 +180,15 @@ public void MapUnitToDefaultAbbreviation<TUnitType>(TUnitType unit, string abbre
MapUnitToDefaultAbbreviation(UnitKey.ForUnit(unit), abbreviation);
}

/// <inheritdoc cref="MapUnitToDefaultAbbreviation{TUnitType}(TUnitType,string)"/>>
/// <inheritdoc cref="MapUnitToDefaultAbbreviation{TUnitType}(TUnitType,string)"/>
/// <param name="unitKey">The unit key value.</param>
/// <param name="abbreviation">Unit abbreviations to add as default.</param>
public void MapUnitToDefaultAbbreviation(UnitKey unitKey, string abbreviation)
{
MapUnitToDefaultAbbreviation(unitKey, CultureInfo.CurrentCulture, abbreviation);
}

/// <inheritdoc cref="MapUnitToDefaultAbbreviation{TUnitType}(TUnitType,string)"/>>
/// <inheritdoc cref="MapUnitToDefaultAbbreviation{TUnitType}(TUnitType,string)"/>
/// <param name="unit">The unit enum value.</param>
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="CultureInfo.CurrentCulture" /> if null.</param>
/// <param name="abbreviation">Unit abbreviation to add as default.</param>
Expand All @@ -190,7 +199,7 @@ public void MapUnitToDefaultAbbreviation<TUnitType>(TUnitType unit, IFormatProvi
MapUnitToDefaultAbbreviation(UnitKey.ForUnit(unit), formatProvider, abbreviation);
}

/// <inheritdoc cref="MapUnitToDefaultAbbreviation{TUnitType}(TUnitType,string)"/>>
/// <inheritdoc cref="MapUnitToDefaultAbbreviation{TUnitType}(TUnitType,string)"/>
/// <param name="unitType">The unit enum type.</param>
/// <param name="unitValue">The unit enum value.</param>
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="CultureInfo.CurrentCulture" /> if null.</param>
Expand All @@ -206,22 +215,26 @@ public void MapUnitToDefaultAbbreviation(Type unitType, int unitValue, IFormatPr
MapUnitToDefaultAbbreviation(UnitKey.Create(unitType, unitValue), formatProvider, abbreviation);
}

/// <inheritdoc cref="MapUnitToDefaultAbbreviation{TUnitType}(TUnitType,string)"/>>
/// <inheritdoc cref="MapUnitToDefaultAbbreviation{TUnitType}(TUnitType,string)"/>
/// <param name="unitKey">The unit key value.</param>
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="CultureInfo.CurrentCulture" /> if null.</param>
/// <param name="abbreviation">Unit abbreviation to add as default.</param>
public void MapUnitToDefaultAbbreviation(UnitKey unitKey, IFormatProvider? formatProvider, string abbreviation)
{
PerformAbbreviationMapping(unitKey, formatProvider, true, abbreviation);
MapUnitToDefaultAbbreviation(Quantities.GetUnitInfo(unitKey), formatProvider, abbreviation);
}

#endregion

private void PerformAbbreviationMapping(UnitKey unitKey, IFormatProvider? formatProvider, bool setAsDefault, params IEnumerable<string> abbreviations)
/// <inheritdoc cref="MapUnitToDefaultAbbreviation{TUnitType}(TUnitType,string)"/>
/// <param name="unitInfo">The info representing the unit.</param>
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="CultureInfo.CurrentCulture" /> if null.</param>
/// <param name="abbreviation">Unit abbreviation to add as default.</param>
public void MapUnitToDefaultAbbreviation(UnitInfo unitInfo, IFormatProvider? formatProvider, string abbreviation)
{
AddAbbreviation(Quantities.GetUnitInfo(unitKey), formatProvider, setAsDefault, abbreviations);
AddAbbreviation(GetConfiguredUnitInfo(unitInfo), formatProvider, true, abbreviation);
}


#endregion

/// <summary>
/// Gets the default abbreviation for a given unit type and its numeric enum value.
/// If a unit has more than one abbreviation defined, then it returns the first one.
Expand Down Expand Up @@ -282,10 +295,26 @@ public string GetDefaultAbbreviation(Type unitType, int unitValue, IFormatProvid
/// </exception>
public string GetDefaultAbbreviation(UnitKey unitKey, IFormatProvider? formatProvider = null)
{
IReadOnlyList<string> abbreviations = GetUnitAbbreviations(unitKey, formatProvider);
return GetDefaultAbbreviation(Quantities.GetUnitInfo(unitKey), formatProvider);
}

/// <inheritdoc cref="GetDefaultAbbreviation{TUnitType}" />
/// <param name="unitInfo">The info representing the unit.</param>
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="CultureInfo.CurrentCulture" /> if null.</param>
/// <exception cref="UnitNotFoundException">
/// Thrown when no unit information is found for the specified
/// <paramref name="unitInfo" />.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown when no abbreviations are mapped for the specified unit.
/// </exception>
public string GetDefaultAbbreviation(UnitInfo unitInfo, IFormatProvider? formatProvider = null)
{
unitInfo = GetConfiguredUnitInfo(unitInfo);
IReadOnlyList<string> abbreviations = GetUnitAbbreviations(unitInfo, formatProvider);
if (abbreviations.Count == 0)
{
throw new InvalidOperationException($"No abbreviations were found for {unitKey.UnitEnumType.Name}.{(Enum)unitKey}. Make sure that the unit abbreviations are mapped.");
throw new InvalidOperationException($"No abbreviations were found for {unitInfo.QuantityInfo.Name}.{unitInfo.Name}. Make sure that the unit abbreviations are mapped.");
}

return abbreviations[0];
Expand Down Expand Up @@ -342,12 +371,28 @@ public IReadOnlyList<string> GetUnitAbbreviations(Type unitType, int unitValue,
/// </exception>
public IReadOnlyList<string> GetUnitAbbreviations(UnitKey unitKey, IFormatProvider? formatProvider = null)
{
return GetUnitAbbreviations(Quantities.GetUnitInfo(unitKey), formatProvider);
}

/// <summary>
/// Retrieves the unit abbreviations for a specified unit info and optional format provider.
/// </summary>
/// <param name="unitInfo">The unit info object representing the unit.</param>
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="CultureInfo.CurrentCulture" /> if null.</param>
/// <returns>A read-only collection of unit abbreviation strings.</returns>
/// <exception cref="UnitNotFoundException">
/// Thrown when no unit information is found for the specified
/// <paramref name="unitInfo" />.
/// </exception>
public IReadOnlyList<string> GetUnitAbbreviations(UnitInfo unitInfo, IFormatProvider? formatProvider = null)
{
unitInfo = GetConfiguredUnitInfo(unitInfo);
if (formatProvider is not CultureInfo culture)
{
culture = CultureInfo.CurrentCulture;
}

return GetAbbreviationsWithFallbackCulture(Quantities.GetUnitInfo(unitKey), culture);
return GetAbbreviationsWithFallbackCulture(unitInfo, culture);
}

/// <summary>
Expand Down Expand Up @@ -400,6 +445,12 @@ public IReadOnlyList<string> GetAllUnitAbbreviationsForQuantity(Type unitEnumTyp
return allAbbreviations;
}

private UnitInfo GetConfiguredUnitInfo(UnitInfo unitInfo)
{
if (unitInfo is null) throw new ArgumentNullException(nameof(unitInfo));
return Quantities.GetUnitInfo(unitInfo.UnitKey);
}

/// <summary>
/// Get all abbreviations for the given unit and culture.
/// </summary>
Expand Down