Skip to content

✨Add Equals() extension methods for untyped IQuantity #1598

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
54 changes: 8 additions & 46 deletions UnitsNet.Tests/AffineQuantityExtensionsTest.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// 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.Diagnostics.CodeAnalysis;

namespace UnitsNet.Tests;

[SuppressMessage("ReSharper", "InvokeAsExtensionMethod")]
public class AffineQuantityExtensionsTest
{
[Theory]
Expand Down Expand Up @@ -45,7 +48,7 @@ public void Equals_WithToleranceAtTheLimit_ReturnsTrue()
}

[Fact]
public void Equals_Temperature_TemperatureDelta_ThrowsArgumentOutOfRangeException_ForNegativeTolerance()
public void Equals_WithNegativeTolerance_ThrowsArgumentOutOfRangeException()
{
var temperature1 = Temperature.FromDegreesCelsius(25.0);
var temperature2 = Temperature.FromDegreesCelsius(25.0);
Expand All @@ -54,58 +57,17 @@ public void Equals_Temperature_TemperatureDelta_ThrowsArgumentOutOfRangeExceptio
Assert.Throws<ArgumentOutOfRangeException>(() => temperature1.Equals(temperature2, negativeTolerance));
}

[Theory]
[InlineData(25.0, 25.0, 0.1, true)] // Equal values
// [InlineData(25.0, 25.1, 0.1, true)] // Within tolerance (but fails due to rounding)
[InlineData(25.0, 25.1, 0.10001, true)] // Within tolerance
[InlineData(25.0, 25.2, 0.1, false)] // Outside tolerance
[InlineData(25.0, 25.0, 0.0, true)] // Zero tolerance, equal values
[InlineData(25.0, 25.1, 0.0, false)] // Zero tolerance, different values
public void Equals_Temperature_IQuantity(double value1, double value2, double toleranceValue, bool expected)
{
var temperature1 = Temperature.FromDegreesCelsius(value1);
IQuantity temperature2 = Temperature.FromDegreesCelsius(value2);
var tolerance = TemperatureDelta.FromDegreesCelsius(toleranceValue);

var result = temperature1.Equals(temperature2, tolerance);

Assert.Equal(expected, result);
}

[Fact]
public void Equals_Temperature_IQuantity_WithDifferentType_ReturnsFalse()
{
var temperature1 = Temperature.FromDegreesCelsius(25.0);
IQuantity length = Length.From(1, LengthUnit.Meter);
var tolerance = TemperatureDelta.FromDegreesCelsius(1);

var result = temperature1.Equals(length, tolerance);

Assert.False(result);
}

[Fact]
public void Equals_WithNullOther_ReturnsFalse()
{
var quantity = Temperature.FromDegreesCelsius(25.0);
var tolerance = TemperatureDelta.FromDegreesCelsius(25.0);

var result = quantity.Equals(null, tolerance);
var result = AffineQuantityExtensions.Equals(quantity, (Temperature?)null, tolerance);

Assert.False(result);
}

[Fact]
public void Equals_ThrowsArgumentOutOfRangeException_ForNegativeTolerance()
{
var temperature1 = Temperature.FromDegreesCelsius(25.0);
var temperature2 = Temperature.FromDegreesCelsius(25.0);
var negativeTolerance = TemperatureDelta.FromDegreesCelsius(-0.1);

Assert.Throws<ArgumentOutOfRangeException>(() => temperature1.Equals(temperature2, negativeTolerance));
Assert.Throws<ArgumentOutOfRangeException>(() => temperature1.Equals((IQuantity)temperature2, negativeTolerance));
}

[Theory]
[InlineData(new[] { 25.0, 30.0, 35.0 }, 30.0)] // Average of three values
[InlineData(new[] { 25.0 }, 25.0)] // Single value
Expand All @@ -114,7 +76,7 @@ public void Average_Temperature(double[] values, double expectedAverage)
Temperature[] temperatures = Array.ConvertAll(values, value => Temperature.FromDegreesCelsius(value));

Temperature result = temperatures.Average();

Assert.Equal(expectedAverage, result.Value);
Assert.Equal(TemperatureUnit.DegreeCelsius, result.Unit);
}
Expand Down Expand Up @@ -147,7 +109,7 @@ public void Average_Temperature_ThrowsArgumentNullException_ForNullCollection()
public void Average_Temperature_ThrowsInvalidOperationException_ForEmptyCollection()
{
Temperature[] temperatures = [];

Assert.Throws<InvalidOperationException>(() => temperatures.Average());
}

Expand All @@ -163,7 +125,7 @@ public void Average_Temperature_WithUnit(double[] values, TemperatureUnit unit,
Assert.Equal(expectedAverage, result.Value);
Assert.Equal(unit, result.Unit);
}

[Fact]
public void Average_TemperaturesWithDifferentUnits_WithTargetUnit()
{
Expand Down
228 changes: 228 additions & 0 deletions UnitsNet.Tests/Extensions/QuantityExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// 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.Diagnostics.CodeAnalysis;

namespace UnitsNet.Tests.Extensions;

[SuppressMessage("ReSharper", "InvokeAsExtensionMethod")]
public class QuantityExtensionsTests
{
[Theory]
[InlineData(25.0, 25.0, 0.1, true)] // Equal values
// [InlineData(25.0, 25.1, 0.1, true)] // Within tolerance (but fails due to rounding)
[InlineData(25.0, 25.1, 0.10001, true)] // Within tolerance
[InlineData(25.0, 25.2, 0.1, false)] // Outside tolerance
[InlineData(25.0, 25.0, 0.0, true)] // Zero tolerance, equal values
[InlineData(25.0, 25.1, 0.0, false)] // Zero tolerance, different values
public void Equals_Affine_IQuantity(double value1, double value2, double toleranceValue, bool expected)
{
var temperature1 = Temperature.FromDegreesCelsius(value1);
IQuantity temperature2 = Temperature.FromDegreesCelsius(value2);
var tolerance = TemperatureDelta.FromDegreesCelsius(toleranceValue);

var result = QuantityExtensions.Equals(temperature1, temperature2, tolerance);

Assert.Equal(expected, result);
}

[Fact]
public void Equals_Affine_IQuantity_WithDifferentType_ReturnsFalse()
{
var temperature1 = Temperature.FromDegreesCelsius(25.0);
IQuantity length = Length.From(1, LengthUnit.Meter); // no other affine quantity to compare with
var tolerance = TemperatureDelta.FromDegreesCelsius(1);

var result = QuantityExtensions.Equals(temperature1, length, tolerance);

Assert.False(result);
}

[Fact]
public void Equals_Linear_IQuantity_WithDifferentType_ReturnsFalse()
{
var mass = Mass.FromKilograms(25.0);
IQuantity length = Length.From(1, LengthUnit.Meter);
var tolerance = Mass.FromKilograms(1);

var result = QuantityExtensions.Equals(mass, length, tolerance);

Assert.False(result);
}

[Fact]
public void Equals_Logarithmic_IQuantity_WithDifferentType_ReturnsFalse()
{
var powerRatio = PowerRatio.FromDecibelWatts(25.0);
IQuantity level = Level.From(1, LevelUnit.Decibel);
var tolerance = PowerRatio.FromDecibelWatts(1);

var result = QuantityExtensions.Equals(powerRatio, level, tolerance);

Assert.False(result);
}

[Fact]
public void Equals_Affine_WithNullOther_ReturnsFalse()
{
var quantity = Temperature.FromDegreesCelsius(25.0);
var tolerance = TemperatureDelta.FromDegreesCelsius(25.0);

var result = QuantityExtensions.Equals(quantity, null, tolerance);

Assert.False(result);
}

[Fact]
public void Equals_Linear_WithNullOther_ReturnsFalse()
{
var quantity = Length.FromMeters(2.0);
var tolerance = Length.FromMeters(0.1);

var result = QuantityExtensions.Equals(quantity, null, tolerance);

Assert.False(result);
}

[Fact]
public void Equals_Logarithmic_WithNullOther_ReturnsFalse()
{
var quantity = PowerRatio.FromDecibelWatts(2.0);
var tolerance = PowerRatio.FromDecibelWatts(0.1);

var result = QuantityExtensions.Equals(quantity, null, tolerance);

Assert.False(result);
}

[Theory]
[InlineData(2.0, 2.0, 0.1, true)]
// [InlineData(2.0, 2.1, 0.1, true)] // should be equal but fails due to rounding
[InlineData(2.0, 2.1, 0.10001, true)]
[InlineData(2.0, 2.2, 0.1, false)]
[InlineData(2.0, 2.0, 0.0, true)]
[InlineData(2.0, 2.1, 0.0, false)]
public void IQuantity_Equals_IQuantity_ReturnsExpectedResult(double value1, double value2, double tolerance, bool expected)
{
IQuantity untypedQuantity1 = Length.FromMeters(value1);
IQuantity untypedQuantity2 = Length.FromMeters(value2);
IQuantity untypedToleranceQuantity = Length.FromMeters(tolerance);

var result = QuantityExtensions.Equals(untypedQuantity1, untypedQuantity2, untypedToleranceQuantity);

Assert.Equal(expected, result);
}

[Theory]
[InlineData(2.0, 2.0, 0.1, true)]
// [InlineData(2.0, 2.1, 0.1, true)] // should be equal but fails due to rounding
[InlineData(2.0, 2.1, 0.10001, true)]
[InlineData(2.0, 2.2, 0.1, false)]
[InlineData(2.0, 2.0, 0.0, true)]
[InlineData(2.0, 2.1, 0.0, false)]
public void Equals_WithUntypedOtherAndTolerance_ReturnsExpectedResult(double value1, double value2, double tolerance, bool expected)
{
var quantity1 = Length.FromMeters(value1);
IQuantity untypedQuantity2 = Length.FromMeters(value2);
var toleranceQuantity = Length.FromMeters(tolerance);

var result = QuantityExtensions.Equals(quantity1, untypedQuantity2, toleranceQuantity);

Assert.Equal(expected, result);
}

[Theory]
[InlineData(2.0, 2.0, 0.1, true)]
// [InlineData(2.0, 2.1, 0.1, true)] // should be equal but fails due to rounding
[InlineData(2.0, 2.1, 0.10001, true)]
[InlineData(2.0, 2.2, 0.1, false)]
[InlineData(2.0, 2.0, 0.0, true)]
[InlineData(2.0, 2.1, 0.0, false)]
public void Equals_IQuantityWithTolerance_ReturnsExpectedResult(double value1, double value2, double tolerance, bool expected)
{
var quantity1 = Length.FromMeters(value1);
IQuantity quantity2 = Length.FromMeters(value2);
var toleranceQuantity = Length.FromMeters(tolerance);

var result = QuantityExtensions.Equals(quantity1, quantity2, toleranceQuantity);

Assert.Equal(expected, result);
}

[Theory]
[InlineData(1, 2)]
[InlineData(100, 110)]
[InlineData(100, 90)]
public void Equals_Logarithmic_IQuantity_ComparesInLinearSpace(double firstValue, double secondValue)
{
var quantity = PowerRatio.FromDecibelWatts(firstValue);
var otherQuantity = PowerRatio.FromDecibelWatts(secondValue);
PowerRatio maxTolerance = quantity > otherQuantity ? quantity - otherQuantity : otherQuantity - quantity;
PowerRatio largerTolerance = maxTolerance * 1.1;
PowerRatio smallerTolerance = maxTolerance / 1.1;
Assert.True(QuantityExtensions.Equals(quantity, (IQuantity)quantity, PowerRatio.Zero));
Assert.True(QuantityExtensions.Equals(quantity, (IQuantity)quantity, maxTolerance));
Assert.True(QuantityExtensions.Equals(quantity, (IQuantity)otherQuantity, largerTolerance));
Assert.False(QuantityExtensions.Equals(quantity, (IQuantity)otherQuantity, smallerTolerance));
// note: it's currently not possible to test this due to the rounding error from (quantity - otherQuantity)
// Assert.True(quantity.Equals((IQuantity)otherQuantity, maxTolerance));
}

[Fact]
public void Equals_Logarithmic_NullQuantity_ReturnsFalse()
{
var quantity = PowerRatio.FromDecibelWatts(1);
var tolerance = PowerRatio.FromDecibelWatts(1);
Assert.False(QuantityExtensions.Equals(quantity, null, tolerance));
}

[Fact(Skip = "Currently throws a NotImplementedException")]
public void Equals_Linear_IQuantity_WithUnknownUnits_ThrowsUnitNotFoundException()
{
var quantity = Length.FromMeters(1);
var invalidQuantity = new Length(1, (LengthUnit)(-1));
Assert.Throws<UnitNotFoundException>(() => invalidQuantity.Equals((IQuantity)quantity, quantity));
Assert.Throws<UnitNotFoundException>(() => quantity.Equals((IQuantity)invalidQuantity, quantity));
Assert.Throws<UnitNotFoundException>(() => quantity.Equals((IQuantity)quantity, invalidQuantity));
}

[Fact(Skip = "Currently throws a NotImplementedException")]
public void Equals_Logarithmic_IQuantity_WithUnknownUnits_ThrowsUnitNotFoundException()
{
var quantity = PowerRatio.FromDecibelWatts(1);
var invalidQuantity = new PowerRatio(1, (PowerRatioUnit)(-1));
Assert.Throws<UnitNotFoundException>(() => QuantityExtensions.Equals(invalidQuantity, (IQuantity)quantity, quantity));
Assert.Throws<UnitNotFoundException>(() => QuantityExtensions.Equals(quantity, (IQuantity)invalidQuantity, quantity));
Assert.Throws<UnitNotFoundException>(() => QuantityExtensions.Equals(quantity, (IQuantity)quantity, invalidQuantity));
}

[Fact]
public void Equals_Affine_WithNegativeTolerance_ThrowsArgumentOutOfRangeException()
{
var quantity = Temperature.FromDegreesCelsius(20);
var other = Temperature.FromDegreesCelsius(21);
var tolerance = TemperatureDelta.FromDegreesCelsius(-0.1);

Assert.Throws<ArgumentOutOfRangeException>(() => quantity.Equals((IQuantity)other, tolerance));
}

[Fact]
public void Equals_Linear_WithNegativeTolerance_ThrowsArgumentOutOfRangeException()
{
var quantity = Length.FromMeters(2.0);
var other = Length.FromMeters(2.0);
var tolerance = Length.FromMeters(-0.1);

Assert.Throws<ArgumentOutOfRangeException>(() => quantity.Equals((IQuantity)other, tolerance));
}

[Fact]
public void Equals_Logarithmic_WithNegativeTolerance_DoesNotThrowArgumentOutOfRangeException()
{
// note: unlike with vector quantities- a small tolerance maybe positive in one unit and negative in another
var quantity = PowerRatio.FromDecibelWatts(1);
var negativeTolerance = PowerRatio.FromDecibelWatts(-1);
Assert.True(QuantityExtensions.Equals(quantity, (IQuantity)quantity, negativeTolerance));
}

}
Loading
Loading