Skip to content

Commit d819b99

Browse files
tmilnthorpangularsen
authored andcommitted
Initial implementation of IFormattable (#599)
* ToString should call formattable with "G" per MSDN docs * Pull IFormattable code into QuantityFormatter * Making format strings case sensitive and making all lower-case for now * Obsolete IQuantity ToString overloads and replace code for obsolete calls. Throw FormatException when invalid abbreviation index is requested and add test. * Converting test to use MemberData rather than rely on strings for checking values * Removing duplicate test and making s format string test data-driven
1 parent c4cab69 commit d819b99

File tree

98 files changed

+3169
-756
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+3169
-756
lines changed
Lines changed: 73 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
// Licensed under MIT No Attribution, see LICENSE file at the root.
22
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.
33

4-
using System;
4+
using System.Collections.Generic;
55
using System.Diagnostics.CodeAnalysis;
6-
using System.Globalization;
76
using Xunit;
8-
using UnitsNet.Units;
97

108
namespace UnitsNet.Tests.CustomCode
119
{
@@ -27,101 +25,103 @@ public class TemperatureTests : TemperatureTestsBase
2725

2826
protected override double KelvinsInOneKelvin => 1;
2927

28+
public static IEnumerable<object[]> DividedByTemperatureDeltaEqualsTemperatureData { get; } =
29+
new List<object[]>
30+
{
31+
new object[] { Temperature.FromDegreesCelsius(10), 1, Temperature.FromDegreesCelsius(10) },
32+
new object[] { Temperature.FromDegreesCelsius(10), 5, Temperature.FromDegreesCelsius(2) },
33+
new object[] { Temperature.FromDegreesCelsius(10), -10, Temperature.FromDegreesCelsius(-1) },
34+
new object[] { Temperature.FromDegreesFahrenheit(10), 1, Temperature.FromDegreesFahrenheit(10) },
35+
new object[] { Temperature.FromDegreesFahrenheit(10), 5, Temperature.FromDegreesFahrenheit(2) },
36+
new object[] { Temperature.FromDegreesFahrenheit(10), -10, Temperature.FromDegreesFahrenheit(-1) }
37+
};
38+
3039
[SuppressMessage("ReSharper", "ImpureMethodCallOnReadonlyValueField",
3140
Justification = "R# incorrectly identifies method as impure, due to internal method calls.")]
3241
[Theory]
33-
[InlineData(TemperatureUnit.DegreeCelsius, 10, 1, "10 °C")]
34-
[InlineData(TemperatureUnit.DegreeCelsius, 10, 5, "2 °C")]
35-
[InlineData(TemperatureUnit.DegreeCelsius, 10, -10, "-1 °C")]
36-
[InlineData(TemperatureUnit.DegreeFahrenheit, 10, 1, "10 °F")]
37-
[InlineData(TemperatureUnit.DegreeFahrenheit, 10, 5, "2 °F")]
38-
[InlineData(TemperatureUnit.DegreeFahrenheit, 10, -10, "-1 °F")]
39-
public void DividedByTemperatureDeltaEqualsTemperature(TemperatureUnit unit, int temperatureVal, int divisor, string expected)
42+
[MemberData(nameof(DividedByTemperatureDeltaEqualsTemperatureData))]
43+
public void DividedByTemperatureDeltaEqualsTemperature(Temperature temperature, int divisor, Temperature expected)
4044
{
41-
Temperature temperature = Temperature.From(temperatureVal, unit);
42-
43-
// Act
44-
Temperature resultTemp = temperature.Divide(divisor, unit);
45-
46-
string actual = resultTemp.ToUnit(unit).ToString(CultureInfo.InvariantCulture, "{0:0} {1}");
47-
Assert.Equal(expected, actual);
45+
Temperature resultTemp = temperature.Divide(divisor, temperature.Unit);
46+
Assert.True(expected.Equals(resultTemp, 1e-5, ComparisonType.Absolute));
4847
}
4948

49+
public static IEnumerable<object[]> MultiplyByTemperatureDeltaEqualsTemperatureData { get; } =
50+
new List<object[]>
51+
{
52+
new object[] { Temperature.FromDegreesCelsius(10), 0, Temperature.FromDegreesCelsius(0) },
53+
new object[] { Temperature.FromDegreesCelsius(10), 5, Temperature.FromDegreesCelsius(50) },
54+
new object[] { Temperature.FromDegreesCelsius(10), -5, Temperature.FromDegreesCelsius(-50) },
55+
new object[] { Temperature.FromDegreesFahrenheit(10), 0, Temperature.FromDegreesFahrenheit(0) },
56+
new object[] { Temperature.FromDegreesFahrenheit(10), 5, Temperature.FromDegreesFahrenheit(50) },
57+
new object[] { Temperature.FromDegreesFahrenheit(10), -5, Temperature.FromDegreesFahrenheit(-50) }
58+
};
59+
5060
[SuppressMessage("ReSharper", "ImpureMethodCallOnReadonlyValueField",
5161
Justification = "R# incorrectly identifies method as impure, due to internal method calls.")]
5262
[Theory]
53-
[InlineData(TemperatureUnit.DegreeCelsius, 10, 0, "0 °C")]
54-
[InlineData(TemperatureUnit.DegreeCelsius, 10, 5, "50 °C")]
55-
[InlineData(TemperatureUnit.DegreeCelsius, 10, -5, "-50 °C")]
56-
[InlineData(TemperatureUnit.DegreeFahrenheit, 10, 0, "0 °F")]
57-
[InlineData(TemperatureUnit.DegreeFahrenheit, 10, 5, "50 °F")]
58-
[InlineData(TemperatureUnit.DegreeFahrenheit, 10, -5, "-50 °F")]
59-
public void MultiplyByTemperatureDeltaEqualsTemperature(TemperatureUnit unit, int temperatureVal, int factor, string expected)
63+
[MemberData(nameof(MultiplyByTemperatureDeltaEqualsTemperatureData))]
64+
public void MultiplyByTemperatureDeltaEqualsTemperature(Temperature temperature, int factor, Temperature expected)
6065
{
61-
Temperature temperature = Temperature.From(temperatureVal, unit);
62-
63-
// Act
64-
Temperature resultTemp = temperature.Multiply(factor, unit);
65-
66-
string actual = resultTemp.ToUnit(unit).ToString(CultureInfo.InvariantCulture, "{0:0} {1}");
67-
Assert.Equal(expected, actual);
66+
Temperature resultTemp = temperature.Multiply(factor, temperature.Unit);
67+
Assert.True(expected.Equals(resultTemp, 1e-5, ComparisonType.Absolute));
6868
}
6969

70+
public static IEnumerable<object[]> TemperatureDeltaPlusTemperatureEqualsTemperatureData { get; } =
71+
new List<object[]>
72+
{
73+
new object[] { Temperature.FromDegreesCelsius(-10), TemperatureDelta.FromDegreesCelsius(0), Temperature.FromDegreesCelsius(-10) },
74+
new object[] { Temperature.FromDegreesCelsius(-10), TemperatureDelta.FromDegreesCelsius(10), Temperature.FromDegreesCelsius(0) },
75+
new object[] { Temperature.FromDegreesCelsius(-10), TemperatureDelta.FromDegreesCelsius(20), Temperature.FromDegreesCelsius(10) },
76+
new object[] { Temperature.FromDegreesFahrenheit(-10), TemperatureDelta.FromDegreesFahrenheit(0), Temperature.FromDegreesFahrenheit(-10) },
77+
new object[] { Temperature.FromDegreesFahrenheit(-10), TemperatureDelta.FromDegreesFahrenheit(10), Temperature.FromDegreesFahrenheit(0) },
78+
new object[] { Temperature.FromDegreesFahrenheit(-10), TemperatureDelta.FromDegreesFahrenheit(20), Temperature.FromDegreesFahrenheit(10) }
79+
};
80+
7081
[Theory]
71-
[InlineData(TemperatureUnit.DegreeCelsius, -10, 0, "-10 °C")]
72-
[InlineData(TemperatureUnit.DegreeCelsius, -10, 10, "0 °C")]
73-
[InlineData(TemperatureUnit.DegreeCelsius, -10, 20, "10 °C")]
74-
[InlineData(TemperatureUnit.DegreeFahrenheit, -10, 0, "-10 °F")]
75-
[InlineData(TemperatureUnit.DegreeFahrenheit, -10, 10, "0 °F")]
76-
[InlineData(TemperatureUnit.DegreeFahrenheit, -10, 20, "10 °F")]
77-
public void TemperatureDeltaPlusTemperatureEqualsTemperature(TemperatureUnit unit, int deltaVal, int temperatureVal, string expected)
82+
[MemberData(nameof(TemperatureDeltaPlusTemperatureEqualsTemperatureData))]
83+
public void TemperatureDeltaPlusTemperatureEqualsTemperature(Temperature temperature, TemperatureDelta delta, Temperature expected)
7884
{
79-
Temperature temperature = Temperature.From(temperatureVal, unit);
80-
TemperatureDelta delta = TemperatureDelta.From(deltaVal, (TemperatureDeltaUnit)Enum.Parse(typeof(TemperatureDeltaUnit), unit.ToString()));
81-
82-
// Act
8385
Temperature resultTemp = delta + temperature;
84-
85-
string actual = resultTemp.ToUnit(unit).ToString(CultureInfo.InvariantCulture, "{0:0} {1}");
86-
Assert.Equal(expected, actual);
86+
Assert.True(expected.Equals(resultTemp, 1e-5, ComparisonType.Absolute));
8787
}
8888

89+
public static IEnumerable<object[]> TemperatureMinusTemperatureDeltaEqualsTemperatureData { get; } =
90+
new List<object[]>
91+
{
92+
new object[] { Temperature.FromDegreesCelsius(20), TemperatureDelta.FromDegreesCelsius(10), Temperature.FromDegreesCelsius(10) },
93+
new object[] { Temperature.FromDegreesCelsius(20), TemperatureDelta.FromDegreesCelsius(20), Temperature.FromDegreesCelsius(0) },
94+
new object[] { Temperature.FromDegreesCelsius(20), TemperatureDelta.FromDegreesCelsius(30), Temperature.FromDegreesCelsius(-10) },
95+
new object[] { Temperature.FromDegreesFahrenheit(20), TemperatureDelta.FromDegreesFahrenheit(10), Temperature.FromDegreesFahrenheit(10) },
96+
new object[] { Temperature.FromDegreesFahrenheit(20), TemperatureDelta.FromDegreesFahrenheit(20), Temperature.FromDegreesFahrenheit(0) },
97+
new object[] { Temperature.FromDegreesFahrenheit(20), TemperatureDelta.FromDegreesFahrenheit(30), Temperature.FromDegreesFahrenheit(-10) }
98+
};
99+
89100
[Theory]
90-
[InlineData(TemperatureUnit.DegreeCelsius, 20, 10, "10 °C")]
91-
[InlineData(TemperatureUnit.DegreeCelsius, 20, 20, "0 °C")]
92-
[InlineData(TemperatureUnit.DegreeCelsius, 20, 30, "-10 °C")]
93-
[InlineData(TemperatureUnit.DegreeFahrenheit, 20, 10, "10 °F")]
94-
[InlineData(TemperatureUnit.DegreeFahrenheit, 20, 20, "0 °F")]
95-
[InlineData(TemperatureUnit.DegreeFahrenheit, 20, 30, "-10 °F")]
96-
public void TemperatureMinusTemperatureDeltaEqualsTemperature(TemperatureUnit unit, int temperatureVal, int deltaVal, string expected)
101+
[MemberData(nameof(TemperatureMinusTemperatureDeltaEqualsTemperatureData))]
102+
public void TemperatureMinusTemperatureDeltaEqualsTemperature(Temperature temperature, TemperatureDelta delta, Temperature expected)
97103
{
98-
Temperature temperature = Temperature.From(temperatureVal, unit);
99-
TemperatureDelta delta = TemperatureDelta.From(deltaVal, (TemperatureDeltaUnit)Enum.Parse(typeof(TemperatureDeltaUnit), unit.ToString()));
100-
101-
// Act
102104
Temperature resultTemp = temperature - delta;
103-
104-
string actual = resultTemp.ToUnit(unit).ToString(CultureInfo.InvariantCulture, "{0:0} {1}");
105-
Assert.Equal(expected, actual);
105+
Assert.True(expected.Equals(resultTemp, 1e-5, ComparisonType.Absolute));
106106
}
107107

108+
public static IEnumerable<object[]> TemperaturePlusTemperatureDeltaEqualsTemperatureData { get; } =
109+
new List<object[]>
110+
{
111+
new object[] { Temperature.FromDegreesCelsius(-10), TemperatureDelta.FromDegreesCelsius(0), Temperature.FromDegreesCelsius(-10) },
112+
new object[] { Temperature.FromDegreesCelsius(-10), TemperatureDelta.FromDegreesCelsius(10), Temperature.FromDegreesCelsius(0) },
113+
new object[] { Temperature.FromDegreesCelsius(-10), TemperatureDelta.FromDegreesCelsius(20), Temperature.FromDegreesCelsius(10) },
114+
new object[] { Temperature.FromDegreesFahrenheit(-10), TemperatureDelta.FromDegreesFahrenheit(0), Temperature.FromDegreesFahrenheit(-10) },
115+
new object[] { Temperature.FromDegreesFahrenheit(-10), TemperatureDelta.FromDegreesFahrenheit(10), Temperature.FromDegreesFahrenheit(0) },
116+
new object[] { Temperature.FromDegreesFahrenheit(-10), TemperatureDelta.FromDegreesFahrenheit(20), Temperature.FromDegreesFahrenheit(10) }
117+
};
118+
108119
[Theory]
109-
[InlineData(TemperatureUnit.DegreeCelsius, -10, 0, "-10 °C")]
110-
[InlineData(TemperatureUnit.DegreeCelsius, -10, 10, "0 °C")]
111-
[InlineData(TemperatureUnit.DegreeCelsius, -10, 20, "10 °C")]
112-
[InlineData(TemperatureUnit.DegreeFahrenheit, -10, 0, "-10 °F")]
113-
[InlineData(TemperatureUnit.DegreeFahrenheit, -10, 10, "0 °F")]
114-
[InlineData(TemperatureUnit.DegreeFahrenheit, -10, 20, "10 °F")]
115-
public void TemperaturePlusTemperatureDeltaEqualsTemperature(TemperatureUnit unit, int temperatureVal, int deltaVal, string expected)
120+
[MemberData(nameof(TemperaturePlusTemperatureDeltaEqualsTemperatureData))]
121+
public void TemperaturePlusTemperatureDeltaEqualsTemperature(Temperature temperature, TemperatureDelta delta, Temperature expected)
116122
{
117-
Temperature temperature = Temperature.From(temperatureVal, unit);
118-
TemperatureDelta delta = TemperatureDelta.From(deltaVal, (TemperatureDeltaUnit)Enum.Parse(typeof(TemperatureDeltaUnit), unit.ToString()));
119-
120-
// Act
121123
Temperature resultTemp = temperature + delta;
122-
123-
string actual = resultTemp.ToUnit(unit).ToString(CultureInfo.InvariantCulture, "{0:0} {1}");
124-
Assert.Equal(expected, actual);
124+
Assert.True(expected.Equals(resultTemp, 1e-5, ComparisonType.Absolute));
125125
}
126126
}
127127
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Licensed under MIT No Attribution, see LICENSE file at the root.
2+
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.
3+
4+
using System;
5+
using Xunit;
6+
7+
namespace UnitsNet.Tests
8+
{
9+
public class QuantityIFormattableTests
10+
{
11+
private static Length length = Length.FromFeet(1.2345678);
12+
13+
[Fact]
14+
public void GFormatStringEqualsToString()
15+
{
16+
Assert.Equal(length.ToString("g"), length.ToString());
17+
}
18+
19+
[Fact]
20+
public void EmptyOrNullFormatStringEqualsGFormat()
21+
{
22+
Assert.Equal(length.ToString("g"), length.ToString(string.Empty));
23+
Assert.Equal(length.ToString("g"), length.ToString((string)null));
24+
}
25+
26+
[Fact]
27+
public void AFormatGetsAbbreviations()
28+
{
29+
Assert.Equal(UnitAbbreviationsCache.Default.GetDefaultAbbreviation(length.Unit), length.ToString("a"));
30+
Assert.Equal(UnitAbbreviationsCache.Default.GetDefaultAbbreviation(length.Unit), length.ToString("a0"));
31+
32+
Assert.Equal(UnitAbbreviationsCache.Default.GetUnitAbbreviations(length.Unit)[1], length.ToString("a1"));
33+
Assert.Equal(UnitAbbreviationsCache.Default.GetUnitAbbreviations(length.Unit)[2], length.ToString("a2"));
34+
}
35+
36+
[Fact]
37+
public void AFormatWithInvalidIndexThrowsFormatException()
38+
{
39+
Assert.Throws<FormatException>(() => length.ToString("a100"));
40+
}
41+
42+
[Fact]
43+
public void VFormatEqualsValueToString()
44+
{
45+
Assert.Equal(length.Value.ToString(), length.ToString("v"));
46+
}
47+
48+
[Fact]
49+
public void QFormatEqualsQuantityName()
50+
{
51+
Assert.Equal(Length.Info.Name, length.ToString("q"));
52+
}
53+
54+
[Theory]
55+
[InlineData("s", "1 ft")]
56+
[InlineData("s1", "1.2 ft")]
57+
[InlineData("s2", "1.23 ft")]
58+
[InlineData("s3", "1.235 ft")]
59+
[InlineData("s4", "1.2346 ft")]
60+
[InlineData("s5", "1.23457 ft")]
61+
[InlineData("s6", "1.234568 ft")]
62+
public void SFormatEqualsSignificantDigits(string sFormatString, string expected)
63+
{
64+
Assert.Equal(expected, length.ToString(sFormatString));
65+
}
66+
67+
[Fact]
68+
public void UFormatEqualsUnitToString()
69+
{
70+
Assert.Equal(length.Unit.ToString(), length.ToString("u"));
71+
}
72+
73+
[Fact]
74+
public void UnsupportedFormatStringThrowsException()
75+
{
76+
Assert.Throws<FormatException>(() => length.ToString("z"));
77+
}
78+
}
79+
}

UnitsNet.Tests/QuantityTests.ToString.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed under MIT No Attribution, see LICENSE file at the root.
22
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.
33

4+
using System;
45
using System.Globalization;
56
using UnitsNet.Units;
67
using Xunit;
@@ -106,7 +107,7 @@ public void FormatsNumberUsingGivenCulture()
106107
try
107108
{
108109
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
109-
Assert.Equal("0.05 m", Length.FromCentimeters(5).ToUnit(LengthUnit.Meter).ToString(null));
110+
Assert.Equal("0.05 m", Length.FromCentimeters(5).ToUnit(LengthUnit.Meter).ToString((IFormatProvider)null));
110111
Assert.Equal("0.05 m", Length.FromCentimeters(5).ToUnit(LengthUnit.Meter).ToString(CultureInfo.InvariantCulture));
111112
Assert.Equal("0,05 m", Length.FromCentimeters(5).ToUnit(LengthUnit.Meter).ToString(new CultureInfo("nb-NO")));
112113
}
@@ -123,9 +124,9 @@ public void FormatsNumberUsingGivenDigitsAfterRadix()
123124
try
124125
{
125126
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
126-
Assert.Equal("0.05 m", Length.FromCentimeters(5).ToUnit(LengthUnit.Meter).ToString(null, 4));
127-
Assert.Equal("1.97 in", Length.FromCentimeters(5).ToUnit(LengthUnit.Inch).ToString(null, 2));
128-
Assert.Equal("1.9685 in", Length.FromCentimeters(5).ToUnit(LengthUnit.Inch).ToString(null, 4));
127+
Assert.Equal("0.05 m", Length.FromCentimeters(5).ToUnit(LengthUnit.Meter).ToString("s4"));
128+
Assert.Equal("1.97 in", Length.FromCentimeters(5).ToUnit(LengthUnit.Inch).ToString("s2"));
129+
Assert.Equal("1.9685 in", Length.FromCentimeters(5).ToUnit(LengthUnit.Inch).ToString("s4"));
129130
}
130131
finally
131132
{

UnitsNet.Tests/UnitAbbreviationsCacheTests.cs

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -116,31 +116,18 @@ public void DecimalPointDigitGroupingCultureFormatting(string culture)
116116
Assert.Equal("1.111 m", Length.FromMeters(1111).ToUnit(LengthUnit.Meter).ToString(GetCulture(culture)));
117117
}
118118

119-
[Theory]
120-
[InlineData(1, "1.1 m")]
121-
[InlineData(2, "1.12 m")]
122-
[InlineData(3, "1.123 m")]
123-
[InlineData(4, "1.1235 m")]
124-
[InlineData(5, "1.12346 m")]
125-
[InlineData(6, "1.123457 m")]
126-
public void CustomNumberOfSignificantDigitsAfterRadixFormatting(int significantDigitsAfterRadix, string expected)
127-
{
128-
string actual = Length.FromMeters(1.123456789).ToUnit(LengthUnit.Meter).ToString(AmericanCulture, significantDigitsAfterRadix);
129-
Assert.Equal(expected, actual);
130-
}
131-
132119
// Due to rounding, the values will result in the same string representation regardless of the number of significant digits (up to a certain point)
133120
[Theory]
134-
[InlineData(0.819999999999, 2, "0.82 m")]
135-
[InlineData(0.819999999999, 4, "0.82 m")]
136-
[InlineData(0.00299999999, 2, "0.003 m")]
137-
[InlineData(0.00299999999, 4, "0.003 m")]
138-
[InlineData(0.0003000001, 2, "3e-04 m")]
139-
[InlineData(0.0003000001, 4, "3e-04 m")]
121+
[InlineData(0.819999999999, "s2", "0.82 m")]
122+
[InlineData(0.819999999999, "s4", "0.82 m")]
123+
[InlineData(0.00299999999, "s2", "0.003 m")]
124+
[InlineData(0.00299999999, "s4", "0.003 m")]
125+
[InlineData(0.0003000001, "s2", "3e-04 m")]
126+
[InlineData(0.0003000001, "s4", "3e-04 m")]
140127
public void RoundingErrorsWithSignificantDigitsAfterRadixFormatting(double value,
141-
int maxSignificantDigitsAfterRadix, string expected)
128+
string significantDigitsAfterRadixFormatString, string expected)
142129
{
143-
string actual = Length.FromMeters(value).ToUnit(LengthUnit.Meter).ToString(AmericanCulture, maxSignificantDigitsAfterRadix);
130+
string actual = Length.FromMeters(value).ToUnit(LengthUnit.Meter).ToString(significantDigitsAfterRadixFormatString, AmericanCulture);
144131
Assert.Equal(expected, actual);
145132
}
146133

0 commit comments

Comments
 (0)