Skip to content

Initial implementation of IFormattable #599

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Mar 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 73 additions & 73 deletions UnitsNet.Tests/CustomCode/TemperatureTests.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// Licensed under MIT No Attribution, see LICENSE file at the root.
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using Xunit;
using UnitsNet.Units;

namespace UnitsNet.Tests.CustomCode
{
Expand All @@ -27,101 +25,103 @@ public class TemperatureTests : TemperatureTestsBase

protected override double KelvinsInOneKelvin => 1;

public static IEnumerable<object[]> DividedByTemperatureDeltaEqualsTemperatureData { get; } =
new List<object[]>
{
new object[] { Temperature.FromDegreesCelsius(10), 1, Temperature.FromDegreesCelsius(10) },
new object[] { Temperature.FromDegreesCelsius(10), 5, Temperature.FromDegreesCelsius(2) },
new object[] { Temperature.FromDegreesCelsius(10), -10, Temperature.FromDegreesCelsius(-1) },
new object[] { Temperature.FromDegreesFahrenheit(10), 1, Temperature.FromDegreesFahrenheit(10) },
new object[] { Temperature.FromDegreesFahrenheit(10), 5, Temperature.FromDegreesFahrenheit(2) },
new object[] { Temperature.FromDegreesFahrenheit(10), -10, Temperature.FromDegreesFahrenheit(-1) }
};

[SuppressMessage("ReSharper", "ImpureMethodCallOnReadonlyValueField",
Justification = "R# incorrectly identifies method as impure, due to internal method calls.")]
[Theory]
[InlineData(TemperatureUnit.DegreeCelsius, 10, 1, "10 °C")]
[InlineData(TemperatureUnit.DegreeCelsius, 10, 5, "2 °C")]
[InlineData(TemperatureUnit.DegreeCelsius, 10, -10, "-1 °C")]
[InlineData(TemperatureUnit.DegreeFahrenheit, 10, 1, "10 °F")]
[InlineData(TemperatureUnit.DegreeFahrenheit, 10, 5, "2 °F")]
[InlineData(TemperatureUnit.DegreeFahrenheit, 10, -10, "-1 °F")]
public void DividedByTemperatureDeltaEqualsTemperature(TemperatureUnit unit, int temperatureVal, int divisor, string expected)
[MemberData(nameof(DividedByTemperatureDeltaEqualsTemperatureData))]
public void DividedByTemperatureDeltaEqualsTemperature(Temperature temperature, int divisor, Temperature expected)
{
Temperature temperature = Temperature.From(temperatureVal, unit);

// Act
Temperature resultTemp = temperature.Divide(divisor, unit);

string actual = resultTemp.ToUnit(unit).ToString(CultureInfo.InvariantCulture, "{0:0} {1}");
Assert.Equal(expected, actual);
Temperature resultTemp = temperature.Divide(divisor, temperature.Unit);
Assert.True(expected.Equals(resultTemp, 1e-5, ComparisonType.Absolute));
}

public static IEnumerable<object[]> MultiplyByTemperatureDeltaEqualsTemperatureData { get; } =
new List<object[]>
{
new object[] { Temperature.FromDegreesCelsius(10), 0, Temperature.FromDegreesCelsius(0) },
new object[] { Temperature.FromDegreesCelsius(10), 5, Temperature.FromDegreesCelsius(50) },
new object[] { Temperature.FromDegreesCelsius(10), -5, Temperature.FromDegreesCelsius(-50) },
new object[] { Temperature.FromDegreesFahrenheit(10), 0, Temperature.FromDegreesFahrenheit(0) },
new object[] { Temperature.FromDegreesFahrenheit(10), 5, Temperature.FromDegreesFahrenheit(50) },
new object[] { Temperature.FromDegreesFahrenheit(10), -5, Temperature.FromDegreesFahrenheit(-50) }
};

[SuppressMessage("ReSharper", "ImpureMethodCallOnReadonlyValueField",
Justification = "R# incorrectly identifies method as impure, due to internal method calls.")]
[Theory]
[InlineData(TemperatureUnit.DegreeCelsius, 10, 0, "0 °C")]
[InlineData(TemperatureUnit.DegreeCelsius, 10, 5, "50 °C")]
[InlineData(TemperatureUnit.DegreeCelsius, 10, -5, "-50 °C")]
[InlineData(TemperatureUnit.DegreeFahrenheit, 10, 0, "0 °F")]
[InlineData(TemperatureUnit.DegreeFahrenheit, 10, 5, "50 °F")]
[InlineData(TemperatureUnit.DegreeFahrenheit, 10, -5, "-50 °F")]
public void MultiplyByTemperatureDeltaEqualsTemperature(TemperatureUnit unit, int temperatureVal, int factor, string expected)
[MemberData(nameof(MultiplyByTemperatureDeltaEqualsTemperatureData))]
public void MultiplyByTemperatureDeltaEqualsTemperature(Temperature temperature, int factor, Temperature expected)
{
Temperature temperature = Temperature.From(temperatureVal, unit);

// Act
Temperature resultTemp = temperature.Multiply(factor, unit);

string actual = resultTemp.ToUnit(unit).ToString(CultureInfo.InvariantCulture, "{0:0} {1}");
Assert.Equal(expected, actual);
Temperature resultTemp = temperature.Multiply(factor, temperature.Unit);
Assert.True(expected.Equals(resultTemp, 1e-5, ComparisonType.Absolute));
}

public static IEnumerable<object[]> TemperatureDeltaPlusTemperatureEqualsTemperatureData { get; } =
new List<object[]>
{
new object[] { Temperature.FromDegreesCelsius(-10), TemperatureDelta.FromDegreesCelsius(0), Temperature.FromDegreesCelsius(-10) },
new object[] { Temperature.FromDegreesCelsius(-10), TemperatureDelta.FromDegreesCelsius(10), Temperature.FromDegreesCelsius(0) },
new object[] { Temperature.FromDegreesCelsius(-10), TemperatureDelta.FromDegreesCelsius(20), Temperature.FromDegreesCelsius(10) },
new object[] { Temperature.FromDegreesFahrenheit(-10), TemperatureDelta.FromDegreesFahrenheit(0), Temperature.FromDegreesFahrenheit(-10) },
new object[] { Temperature.FromDegreesFahrenheit(-10), TemperatureDelta.FromDegreesFahrenheit(10), Temperature.FromDegreesFahrenheit(0) },
new object[] { Temperature.FromDegreesFahrenheit(-10), TemperatureDelta.FromDegreesFahrenheit(20), Temperature.FromDegreesFahrenheit(10) }
};

[Theory]
[InlineData(TemperatureUnit.DegreeCelsius, -10, 0, "-10 °C")]
[InlineData(TemperatureUnit.DegreeCelsius, -10, 10, "0 °C")]
[InlineData(TemperatureUnit.DegreeCelsius, -10, 20, "10 °C")]
[InlineData(TemperatureUnit.DegreeFahrenheit, -10, 0, "-10 °F")]
[InlineData(TemperatureUnit.DegreeFahrenheit, -10, 10, "0 °F")]
[InlineData(TemperatureUnit.DegreeFahrenheit, -10, 20, "10 °F")]
public void TemperatureDeltaPlusTemperatureEqualsTemperature(TemperatureUnit unit, int deltaVal, int temperatureVal, string expected)
[MemberData(nameof(TemperatureDeltaPlusTemperatureEqualsTemperatureData))]
public void TemperatureDeltaPlusTemperatureEqualsTemperature(Temperature temperature, TemperatureDelta delta, Temperature expected)
{
Temperature temperature = Temperature.From(temperatureVal, unit);
TemperatureDelta delta = TemperatureDelta.From(deltaVal, (TemperatureDeltaUnit)Enum.Parse(typeof(TemperatureDeltaUnit), unit.ToString()));

// Act
Temperature resultTemp = delta + temperature;

string actual = resultTemp.ToUnit(unit).ToString(CultureInfo.InvariantCulture, "{0:0} {1}");
Assert.Equal(expected, actual);
Assert.True(expected.Equals(resultTemp, 1e-5, ComparisonType.Absolute));
}

public static IEnumerable<object[]> TemperatureMinusTemperatureDeltaEqualsTemperatureData { get; } =
new List<object[]>
{
new object[] { Temperature.FromDegreesCelsius(20), TemperatureDelta.FromDegreesCelsius(10), Temperature.FromDegreesCelsius(10) },
new object[] { Temperature.FromDegreesCelsius(20), TemperatureDelta.FromDegreesCelsius(20), Temperature.FromDegreesCelsius(0) },
new object[] { Temperature.FromDegreesCelsius(20), TemperatureDelta.FromDegreesCelsius(30), Temperature.FromDegreesCelsius(-10) },
new object[] { Temperature.FromDegreesFahrenheit(20), TemperatureDelta.FromDegreesFahrenheit(10), Temperature.FromDegreesFahrenheit(10) },
new object[] { Temperature.FromDegreesFahrenheit(20), TemperatureDelta.FromDegreesFahrenheit(20), Temperature.FromDegreesFahrenheit(0) },
new object[] { Temperature.FromDegreesFahrenheit(20), TemperatureDelta.FromDegreesFahrenheit(30), Temperature.FromDegreesFahrenheit(-10) }
};

[Theory]
[InlineData(TemperatureUnit.DegreeCelsius, 20, 10, "10 °C")]
[InlineData(TemperatureUnit.DegreeCelsius, 20, 20, "0 °C")]
[InlineData(TemperatureUnit.DegreeCelsius, 20, 30, "-10 °C")]
[InlineData(TemperatureUnit.DegreeFahrenheit, 20, 10, "10 °F")]
[InlineData(TemperatureUnit.DegreeFahrenheit, 20, 20, "0 °F")]
[InlineData(TemperatureUnit.DegreeFahrenheit, 20, 30, "-10 °F")]
public void TemperatureMinusTemperatureDeltaEqualsTemperature(TemperatureUnit unit, int temperatureVal, int deltaVal, string expected)
[MemberData(nameof(TemperatureMinusTemperatureDeltaEqualsTemperatureData))]
public void TemperatureMinusTemperatureDeltaEqualsTemperature(Temperature temperature, TemperatureDelta delta, Temperature expected)
{
Temperature temperature = Temperature.From(temperatureVal, unit);
TemperatureDelta delta = TemperatureDelta.From(deltaVal, (TemperatureDeltaUnit)Enum.Parse(typeof(TemperatureDeltaUnit), unit.ToString()));

// Act
Temperature resultTemp = temperature - delta;

string actual = resultTemp.ToUnit(unit).ToString(CultureInfo.InvariantCulture, "{0:0} {1}");
Assert.Equal(expected, actual);
Assert.True(expected.Equals(resultTemp, 1e-5, ComparisonType.Absolute));
}

public static IEnumerable<object[]> TemperaturePlusTemperatureDeltaEqualsTemperatureData { get; } =
new List<object[]>
{
new object[] { Temperature.FromDegreesCelsius(-10), TemperatureDelta.FromDegreesCelsius(0), Temperature.FromDegreesCelsius(-10) },
new object[] { Temperature.FromDegreesCelsius(-10), TemperatureDelta.FromDegreesCelsius(10), Temperature.FromDegreesCelsius(0) },
new object[] { Temperature.FromDegreesCelsius(-10), TemperatureDelta.FromDegreesCelsius(20), Temperature.FromDegreesCelsius(10) },
new object[] { Temperature.FromDegreesFahrenheit(-10), TemperatureDelta.FromDegreesFahrenheit(0), Temperature.FromDegreesFahrenheit(-10) },
new object[] { Temperature.FromDegreesFahrenheit(-10), TemperatureDelta.FromDegreesFahrenheit(10), Temperature.FromDegreesFahrenheit(0) },
new object[] { Temperature.FromDegreesFahrenheit(-10), TemperatureDelta.FromDegreesFahrenheit(20), Temperature.FromDegreesFahrenheit(10) }
};

[Theory]
[InlineData(TemperatureUnit.DegreeCelsius, -10, 0, "-10 °C")]
[InlineData(TemperatureUnit.DegreeCelsius, -10, 10, "0 °C")]
[InlineData(TemperatureUnit.DegreeCelsius, -10, 20, "10 °C")]
[InlineData(TemperatureUnit.DegreeFahrenheit, -10, 0, "-10 °F")]
[InlineData(TemperatureUnit.DegreeFahrenheit, -10, 10, "0 °F")]
[InlineData(TemperatureUnit.DegreeFahrenheit, -10, 20, "10 °F")]
public void TemperaturePlusTemperatureDeltaEqualsTemperature(TemperatureUnit unit, int temperatureVal, int deltaVal, string expected)
[MemberData(nameof(TemperaturePlusTemperatureDeltaEqualsTemperatureData))]
public void TemperaturePlusTemperatureDeltaEqualsTemperature(Temperature temperature, TemperatureDelta delta, Temperature expected)
{
Temperature temperature = Temperature.From(temperatureVal, unit);
TemperatureDelta delta = TemperatureDelta.From(deltaVal, (TemperatureDeltaUnit)Enum.Parse(typeof(TemperatureDeltaUnit), unit.ToString()));

// Act
Temperature resultTemp = temperature + delta;

string actual = resultTemp.ToUnit(unit).ToString(CultureInfo.InvariantCulture, "{0:0} {1}");
Assert.Equal(expected, actual);
Assert.True(expected.Equals(resultTemp, 1e-5, ComparisonType.Absolute));
}
}
}
79 changes: 79 additions & 0 deletions UnitsNet.Tests/QuantityIFormattableTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Licensed under MIT No Attribution, see LICENSE file at the root.
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.

using System;
using Xunit;

namespace UnitsNet.Tests
{
public class QuantityIFormattableTests
{
private static Length length = Length.FromFeet(1.2345678);

[Fact]
public void GFormatStringEqualsToString()
{
Assert.Equal(length.ToString("g"), length.ToString());
}

[Fact]
public void EmptyOrNullFormatStringEqualsGFormat()
{
Assert.Equal(length.ToString("g"), length.ToString(string.Empty));
Assert.Equal(length.ToString("g"), length.ToString((string)null));
}

[Fact]
public void AFormatGetsAbbreviations()
{
Assert.Equal(UnitAbbreviationsCache.Default.GetDefaultAbbreviation(length.Unit), length.ToString("a"));
Assert.Equal(UnitAbbreviationsCache.Default.GetDefaultAbbreviation(length.Unit), length.ToString("a0"));

Assert.Equal(UnitAbbreviationsCache.Default.GetUnitAbbreviations(length.Unit)[1], length.ToString("a1"));
Assert.Equal(UnitAbbreviationsCache.Default.GetUnitAbbreviations(length.Unit)[2], length.ToString("a2"));
}

[Fact]
public void AFormatWithInvalidIndexThrowsFormatException()
{
Assert.Throws<FormatException>(() => length.ToString("a100"));
}

[Fact]
public void VFormatEqualsValueToString()
{
Assert.Equal(length.Value.ToString(), length.ToString("v"));
}

[Fact]
public void QFormatEqualsQuantityName()
{
Assert.Equal(Length.Info.Name, length.ToString("q"));
}

[Theory]
[InlineData("s", "1 ft")]
[InlineData("s1", "1.2 ft")]
[InlineData("s2", "1.23 ft")]
[InlineData("s3", "1.235 ft")]
[InlineData("s4", "1.2346 ft")]
[InlineData("s5", "1.23457 ft")]
[InlineData("s6", "1.234568 ft")]
public void SFormatEqualsSignificantDigits(string sFormatString, string expected)
{
Assert.Equal(expected, length.ToString(sFormatString));
}

[Fact]
public void UFormatEqualsUnitToString()
{
Assert.Equal(length.Unit.ToString(), length.ToString("u"));
}

[Fact]
public void UnsupportedFormatStringThrowsException()
{
Assert.Throws<FormatException>(() => length.ToString("z"));
}
}
}
9 changes: 5 additions & 4 deletions UnitsNet.Tests/QuantityTests.ToString.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed under MIT No Attribution, see LICENSE file at the root.
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.

using System;
using System.Globalization;
using UnitsNet.Units;
using Xunit;
Expand Down Expand Up @@ -106,7 +107,7 @@ public void FormatsNumberUsingGivenCulture()
try
{
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
Assert.Equal("0.05 m", Length.FromCentimeters(5).ToUnit(LengthUnit.Meter).ToString(null));
Assert.Equal("0.05 m", Length.FromCentimeters(5).ToUnit(LengthUnit.Meter).ToString((IFormatProvider)null));
Assert.Equal("0.05 m", Length.FromCentimeters(5).ToUnit(LengthUnit.Meter).ToString(CultureInfo.InvariantCulture));
Assert.Equal("0,05 m", Length.FromCentimeters(5).ToUnit(LengthUnit.Meter).ToString(new CultureInfo("nb-NO")));
}
Expand All @@ -123,9 +124,9 @@ public void FormatsNumberUsingGivenDigitsAfterRadix()
try
{
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
Assert.Equal("0.05 m", Length.FromCentimeters(5).ToUnit(LengthUnit.Meter).ToString(null, 4));
Assert.Equal("1.97 in", Length.FromCentimeters(5).ToUnit(LengthUnit.Inch).ToString(null, 2));
Assert.Equal("1.9685 in", Length.FromCentimeters(5).ToUnit(LengthUnit.Inch).ToString(null, 4));
Assert.Equal("0.05 m", Length.FromCentimeters(5).ToUnit(LengthUnit.Meter).ToString("s4"));
Assert.Equal("1.97 in", Length.FromCentimeters(5).ToUnit(LengthUnit.Inch).ToString("s2"));
Assert.Equal("1.9685 in", Length.FromCentimeters(5).ToUnit(LengthUnit.Inch).ToString("s4"));
}
finally
{
Expand Down
29 changes: 8 additions & 21 deletions UnitsNet.Tests/UnitAbbreviationsCacheTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,31 +116,18 @@ public void DecimalPointDigitGroupingCultureFormatting(string culture)
Assert.Equal("1.111 m", Length.FromMeters(1111).ToUnit(LengthUnit.Meter).ToString(GetCulture(culture)));
}

[Theory]
[InlineData(1, "1.1 m")]
[InlineData(2, "1.12 m")]
[InlineData(3, "1.123 m")]
[InlineData(4, "1.1235 m")]
[InlineData(5, "1.12346 m")]
[InlineData(6, "1.123457 m")]
public void CustomNumberOfSignificantDigitsAfterRadixFormatting(int significantDigitsAfterRadix, string expected)
{
string actual = Length.FromMeters(1.123456789).ToUnit(LengthUnit.Meter).ToString(AmericanCulture, significantDigitsAfterRadix);
Assert.Equal(expected, actual);
}

// Due to rounding, the values will result in the same string representation regardless of the number of significant digits (up to a certain point)
[Theory]
[InlineData(0.819999999999, 2, "0.82 m")]
[InlineData(0.819999999999, 4, "0.82 m")]
[InlineData(0.00299999999, 2, "0.003 m")]
[InlineData(0.00299999999, 4, "0.003 m")]
[InlineData(0.0003000001, 2, "3e-04 m")]
[InlineData(0.0003000001, 4, "3e-04 m")]
[InlineData(0.819999999999, "s2", "0.82 m")]
[InlineData(0.819999999999, "s4", "0.82 m")]
[InlineData(0.00299999999, "s2", "0.003 m")]
[InlineData(0.00299999999, "s4", "0.003 m")]
[InlineData(0.0003000001, "s2", "3e-04 m")]
[InlineData(0.0003000001, "s4", "3e-04 m")]
public void RoundingErrorsWithSignificantDigitsAfterRadixFormatting(double value,
int maxSignificantDigitsAfterRadix, string expected)
string significantDigitsAfterRadixFormatString, string expected)
{
string actual = Length.FromMeters(value).ToUnit(LengthUnit.Meter).ToString(AmericanCulture, maxSignificantDigitsAfterRadix);
string actual = Length.FromMeters(value).ToUnit(LengthUnit.Meter).ToString(significantDigitsAfterRadixFormatString, AmericanCulture);
Assert.Equal(expected, actual);
}

Expand Down
Loading