From f8a04f43385c77cab5a3746161c318efa20a0aa4 Mon Sep 17 00:00:00 2001 From: Dejan Bratic Date: Fri, 30 Jun 2023 13:05:30 +0200 Subject: [PATCH 1/3] added initial volume implementation; --- src/Nox.Types/Types/Area/Area.cs | 2 +- src/Nox.Types/Types/Distance/Distance.cs | 2 +- src/Nox.Types/Types/Length/Length.cs | 2 +- src/Nox.Types/Types/Volume/Measurement.cs | 76 ++++++ .../Volume/MeasurementConversionFactor.cs | 37 +++ src/Nox.Types/Types/Volume/MeasurementUnit.cs | 53 +++++ src/Nox.Types/Types/Volume/Volume.cs | 46 +++- src/Nox.Types/Types/Volume/VolumeUnit.cs | 11 + .../MeasurementConversionFactorTests.cs | 16 +- .../MeasurementConversionFactorTests.cs | 22 ++ .../Types/Volume/VolumeTests.cs | 218 +++++++++++++++++- .../Types/Volume/VolumeUnitTests.cs | 26 +++ 12 files changed, 496 insertions(+), 15 deletions(-) create mode 100644 src/Nox.Types/Types/Volume/Measurement.cs create mode 100644 src/Nox.Types/Types/Volume/MeasurementConversionFactor.cs create mode 100644 src/Nox.Types/Types/Volume/MeasurementUnit.cs create mode 100644 src/Nox.Types/Types/Volume/VolumeUnit.cs create mode 100644 tests/Nox.Types.Tests/Types/Volume/MeasurementConversionFactorTests.cs create mode 100644 tests/Nox.Types.Tests/Types/Volume/VolumeUnitTests.cs diff --git a/src/Nox.Types/Types/Area/Area.cs b/src/Nox.Types/Types/Area/Area.cs index 883bc76..7786ec8 100644 --- a/src/Nox.Types/Types/Area/Area.cs +++ b/src/Nox.Types/Types/Area/Area.cs @@ -117,7 +117,7 @@ protected override IEnumerable> GetEqualityComponen private QuantityValue GetAreaIn(AreaTypeUnit targetUnit) { - var factor = new MeasurementConversionFactor((MeasurementTypeUnit)Unit, (MeasurementTypeUnit)targetUnit).Value; + var factor = new Common.MeasurementConversionFactor((MeasurementTypeUnit)Unit, (MeasurementTypeUnit)targetUnit).Value; return Round(Value * factor); } diff --git a/src/Nox.Types/Types/Distance/Distance.cs b/src/Nox.Types/Types/Distance/Distance.cs index 57a6580..4525567 100644 --- a/src/Nox.Types/Types/Distance/Distance.cs +++ b/src/Nox.Types/Types/Distance/Distance.cs @@ -135,7 +135,7 @@ public string ToString(IFormatProvider formatProvider) private QuantityValue GetDistanceIn(DistanceTypeUnit targetUnit) { - var factor = new MeasurementConversionFactor((MeasurementTypeUnit)Unit, (MeasurementTypeUnit)targetUnit).Value; + var factor = new Common.MeasurementConversionFactor((MeasurementTypeUnit)Unit, (MeasurementTypeUnit)targetUnit).Value; return Round(Value * factor); } diff --git a/src/Nox.Types/Types/Length/Length.cs b/src/Nox.Types/Types/Length/Length.cs index 6e1307e..f70df33 100644 --- a/src/Nox.Types/Types/Length/Length.cs +++ b/src/Nox.Types/Types/Length/Length.cs @@ -117,7 +117,7 @@ public QuantityValue ToFeet() private QuantityValue GetLengthIn(LengthTypeUnit targetUnit) { - var factor = new MeasurementConversionFactor((MeasurementTypeUnit)Unit, (MeasurementTypeUnit)targetUnit).Value; + var factor = new Common.MeasurementConversionFactor((MeasurementTypeUnit)Unit, (MeasurementTypeUnit)targetUnit).Value; return Round(Value * factor); } diff --git a/src/Nox.Types/Types/Volume/Measurement.cs b/src/Nox.Types/Types/Volume/Measurement.cs new file mode 100644 index 0000000..57cd9b1 --- /dev/null +++ b/src/Nox.Types/Types/Volume/Measurement.cs @@ -0,0 +1,76 @@ +using System; +using System.Globalization; + +namespace Nox.Types; + +public abstract class Measurement : ValueObject + where TValueObject : Measurement, new() + where TUnitType : MeasurementUnit +{ + private const int QuantityValueDecimalPrecision = 6; + + public TUnitType Unit { get; private set; } = default!; + + protected Measurement() : base() { Value = 0; } + + /// + /// Creates a new instance of object with the specified . + /// + /// The value to create the with + /// The to create the with + /// + /// + public static TValueObject From(QuantityValue value, TUnitType unit) + { + var newObject = new TValueObject + { + Value = Round(value), + Unit = unit, + }; + + var validationResult = newObject.Validate(); + + if (!validationResult.IsValid) + { + throw new TypeValidationException(validationResult.Errors); + } + + return newObject; + } + + /// + /// Validates a object. + /// + /// true if the value is valid. + internal override ValidationResult Validate() + { + var result = Value.Validate(); + + if (Value < 0 && !double.IsNaN((double)Value) && !double.IsInfinity((double)Value)) + { + result.Errors.Add(new ValidationFailure(nameof(Value), $"Could not create a Nox {typeof(TValueObject).Name} type as negative {typeof(TValueObject).Name.ToLower()} value {Value} is not allowed.")); + } + + return result; + } + + public override string ToString() + => $"{Value.ToString($"0.{new string('#', QuantityValueDecimalPrecision)}", CultureInfo.InvariantCulture)} {Unit}"; + + /// + /// Returns a string representation of the object using the specified . + /// + /// The format provider for the length value. + /// A string representation of the object with the value formatted using the specified . + public string ToString(IFormatProvider formatProvider) + => $"{Value.ToString(formatProvider)} {Unit}"; + + protected QuantityValue GetMeasurementIn(MeasurementUnit targetUnit) + { + var factor = new MeasurementConversionFactor(Unit, targetUnit).Value; + return Round(Value * factor); + } + + private static QuantityValue Round(QuantityValue value) + => Math.Round((double)value, QuantityValueDecimalPrecision); +} diff --git a/src/Nox.Types/Types/Volume/MeasurementConversionFactor.cs b/src/Nox.Types/Types/Volume/MeasurementConversionFactor.cs new file mode 100644 index 0000000..2e6ea76 --- /dev/null +++ b/src/Nox.Types/Types/Volume/MeasurementConversionFactor.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace Nox.Types; + +public class MeasurementConversionFactor +{ + private static readonly Dictionary<(VolumeUnit, VolumeUnit), double> DefinedVolumeConversionFactors = new() + { + { (VolumeUnit.CubicFoot, VolumeUnit.CubicMeter), 0.0283168466 }, + { (VolumeUnit.CubicMeter, VolumeUnit.CubicFoot), 35.3146667 }, + }; + + public double Value { get; } + + public MeasurementConversionFactor(MeasurementUnit sourceUnit, MeasurementUnit targetUnit) + { + Value = ResolveConversionFactor(sourceUnit, targetUnit); + } + + private static double ResolveConversionFactor(MeasurementUnit sourceUnit, MeasurementUnit targetUnit) + { + + if (sourceUnit is VolumeUnit volumeSourceUnit && targetUnit is VolumeUnit volumeTargetUnit) + { + var conversion = (volumeSourceUnit, volumeTargetUnit); + + if (DefinedVolumeConversionFactors.ContainsKey(conversion)) + return DefinedVolumeConversionFactors[conversion]; + } + + if (sourceUnit == targetUnit) + return 1; + + throw new NotImplementedException($"No conversion defined from {sourceUnit?.Name} to {targetUnit?.Name}."); + } +} diff --git a/src/Nox.Types/Types/Volume/MeasurementUnit.cs b/src/Nox.Types/Types/Volume/MeasurementUnit.cs new file mode 100644 index 0000000..862bf65 --- /dev/null +++ b/src/Nox.Types/Types/Volume/MeasurementUnit.cs @@ -0,0 +1,53 @@ +using System; + +namespace Nox.Types; + +public abstract class MeasurementUnit : IComparable +{ + public int Id { get; } + public string Name { get; } + public string Symbol { get; } + + protected MeasurementUnit(int id, string name, string symbol) + { + Id = id; + Name = name; + Symbol = symbol; + } + + public int CompareTo(object obj) + => Id.CompareTo(((MeasurementUnit)obj).Id); + + public override bool Equals(object obj) + { + if (obj is not MeasurementUnit otherValue) + { + return false; + } + + var isEqualType = GetType().Equals(obj.GetType()); + var isEqualId = Id.Equals(otherValue.Id); + + return isEqualType && isEqualId; + } + + public override int GetHashCode() => Id.GetHashCode(); + + public override string ToString() => Symbol; + + public static bool operator ==(MeasurementUnit? a, MeasurementUnit? b) + { + if (a is null && b is null) + return true; + + if (a is null || b is null) + return false; + + return a.Equals(b); + } + + public static bool operator !=(MeasurementUnit? a, MeasurementUnit? b) + { + return !(a == b); + } +} diff --git a/src/Nox.Types/Types/Volume/Volume.cs b/src/Nox.Types/Types/Volume/Volume.cs index c62acb2..03d7c10 100644 --- a/src/Nox.Types/Types/Volume/Volume.cs +++ b/src/Nox.Types/Types/Volume/Volume.cs @@ -1,9 +1,49 @@ +using System.Collections.Generic; + namespace Nox.Types; +/// +/// Represents a Nox type and value object. +/// +public sealed class Volume : Measurement +{ + /// + /// Creates a new instance of object in cubic feet. + /// + /// The value to create the with + /// + /// + public static Volume FromCubicFeet(QuantityValue value) + => From(value, VolumeUnit.CubicFoot); + + /// + /// Creates a new instance of object in cubic meters. + /// + /// The origin value to create the with + /// + /// + public static Volume FromCubicMeters(QuantityValue value) + => From(value, VolumeUnit.CubicMeter); + /// - /// Represents a Nox type and value object. + /// Creates a new instance of object in cubic meters. /// - /// Placeholder, needs to be implemented - public sealed class Volume : ValueObject + /// The origin value to create the with + /// + /// + public new static Volume From(QuantityValue value) + => From(value, VolumeUnit.CubicMeter); + + private QuantityValue? _cubicFeet; + + public QuantityValue ToCubicFeet() => _cubicFeet ??= GetMeasurementIn(VolumeUnit.CubicFoot); + + private QuantityValue? _cubicMeters; + + public QuantityValue ToCubicMeters() => _cubicMeters ??= GetMeasurementIn(VolumeUnit.CubicMeter); + + protected override IEnumerable> GetEqualityComponents() { + yield return new KeyValuePair(nameof(Value), ToCubicMeters()); } +} \ No newline at end of file diff --git a/src/Nox.Types/Types/Volume/VolumeUnit.cs b/src/Nox.Types/Types/Volume/VolumeUnit.cs new file mode 100644 index 0000000..5e1ac37 --- /dev/null +++ b/src/Nox.Types/Types/Volume/VolumeUnit.cs @@ -0,0 +1,11 @@ +namespace Nox.Types; + +public class VolumeUnit : MeasurementUnit +{ + public static VolumeUnit CubicMeter { get; } = new VolumeUnit(1, "CubicMeter", "m³"); + public static VolumeUnit CubicFoot { get; } = new VolumeUnit(2, "CubicFoot", "ft³"); + + private VolumeUnit(int id, string name, string symbol) : base(id, name, symbol) + { + } +} diff --git a/tests/Nox.Types.Tests/Common/MeasurementConversionFactorTests.cs b/tests/Nox.Types.Tests/Common/MeasurementConversionFactorTests.cs index fa48e1f..132eed4 100644 --- a/tests/Nox.Types.Tests/Common/MeasurementConversionFactorTests.cs +++ b/tests/Nox.Types.Tests/Common/MeasurementConversionFactorTests.cs @@ -8,7 +8,7 @@ public class MeasurementConversionFactorTests [Fact] public void MeasurementUnitConverter_GetConversionFactor_FromFootToMeter_ReturnsValue() { - var factor = new MeasurementConversionFactor(MeasurementTypeUnit.Foot, MeasurementTypeUnit.Meter); + var factor = new Nox.Common.MeasurementConversionFactor(MeasurementTypeUnit.Foot, MeasurementTypeUnit.Meter); factor.Value.Should().Be(0.30480000033); } @@ -16,7 +16,7 @@ public void MeasurementUnitConverter_GetConversionFactor_FromFootToMeter_Returns [Fact] public void MeasurementUnitConverter_GetConversionFactor_FromMeterToFoot_ReturnsValue() { - var factor = new MeasurementConversionFactor(MeasurementTypeUnit.Meter, MeasurementTypeUnit.Foot); + var factor = new Nox.Common.MeasurementConversionFactor(MeasurementTypeUnit.Meter, MeasurementTypeUnit.Foot); factor.Value.Should().Be(3.28083989142); } @@ -24,7 +24,7 @@ public void MeasurementUnitConverter_GetConversionFactor_FromMeterToFoot_Returns [Fact] public void MeasurementUnitConverter_GetConversionFactor_FromKilometerToMile_ReturnsValue() { - var factor = new MeasurementConversionFactor(MeasurementTypeUnit.Kilometer, MeasurementTypeUnit.Mile); + var factor = new Nox.Common.MeasurementConversionFactor(MeasurementTypeUnit.Kilometer, MeasurementTypeUnit.Mile); factor.Value.Should().Be(0.62137119102); } @@ -32,7 +32,7 @@ public void MeasurementUnitConverter_GetConversionFactor_FromKilometerToMile_Ret [Fact] public void MeasurementUnitConverter_GetConversionFactor_FromMileToKilometer_ReturnsValue() { - var factor = new MeasurementConversionFactor(MeasurementTypeUnit.Mile, MeasurementTypeUnit.Kilometer); + var factor = new Nox.Common.MeasurementConversionFactor(MeasurementTypeUnit.Mile, MeasurementTypeUnit.Kilometer); factor.Value.Should().Be(1.60934400315); } @@ -40,7 +40,7 @@ public void MeasurementUnitConverter_GetConversionFactor_FromMileToKilometer_Ret [Fact] public void MeasurementUnitConverter_GetConversionFactor_FromSquareFootToSquareMeter_ReturnsValue() { - var factor = new MeasurementConversionFactor(MeasurementTypeUnit.SquareFoot, MeasurementTypeUnit.SquareMeter); + var factor = new Nox.Common.MeasurementConversionFactor(MeasurementTypeUnit.SquareFoot, MeasurementTypeUnit.SquareMeter); factor.Value.Should().Be(0.09290304); } @@ -48,7 +48,7 @@ public void MeasurementUnitConverter_GetConversionFactor_FromSquareFootToSquareM [Fact] public void MeasurementUnitConverter_GetConversionFactor_FromSquareMeterToSquareFoot_ReturnsValue() { - var factor = new MeasurementConversionFactor(MeasurementTypeUnit.SquareMeter, MeasurementTypeUnit.SquareFoot); + var factor = new Nox.Common.MeasurementConversionFactor(MeasurementTypeUnit.SquareMeter, MeasurementTypeUnit.SquareFoot); factor.Value.Should().Be(10.76391042); } @@ -56,7 +56,7 @@ public void MeasurementUnitConverter_GetConversionFactor_FromSquareMeterToSquare [Fact] public void MeasurementUnitConverter_GetConversionFactor_WithSameSourceAndTargetUnit_ReturnsValue() { - var factor = new MeasurementConversionFactor(MeasurementTypeUnit.Foot, MeasurementTypeUnit.Foot); + var factor = new Nox.Common.MeasurementConversionFactor(MeasurementTypeUnit.Foot, MeasurementTypeUnit.Foot); factor.Value.Should().Be(1); } @@ -64,7 +64,7 @@ public void MeasurementUnitConverter_GetConversionFactor_WithSameSourceAndTarget [Fact] public void MeasurementUnitConverter_GetConversionFactor_WithUnsupportedConversion_ThrowsException() { - var action = () => new MeasurementConversionFactor(MeasurementTypeUnit.SquareMeter, MeasurementTypeUnit.Meter); + var action = () => new Nox.Common.MeasurementConversionFactor(MeasurementTypeUnit.SquareMeter, MeasurementTypeUnit.Meter); action.Should().Throw() .WithMessage("No conversion defined from SquareMeter to Meter."); diff --git a/tests/Nox.Types.Tests/Types/Volume/MeasurementConversionFactorTests.cs b/tests/Nox.Types.Tests/Types/Volume/MeasurementConversionFactorTests.cs new file mode 100644 index 0000000..90693b4 --- /dev/null +++ b/tests/Nox.Types.Tests/Types/Volume/MeasurementConversionFactorTests.cs @@ -0,0 +1,22 @@ +using FluentAssertions; + +namespace Nox.Types.Tests.Types; + +public class MeasurementConversionFactorTests +{ + [Fact] + public void MeasurementConversionFactor_GetConversionFactor_FromCubicFootToCubicMeter_ReturnsValue() + { + var factor = new MeasurementConversionFactor(VolumeUnit.CubicFoot, VolumeUnit.CubicMeter); + + factor.Value.Should().Be(0.0283168466); + } + + [Fact] + public void MeasurementConversionFactor_GetConversionFactor_FromCubicMeterToCubicFoot_ReturnsValue() + { + var factor = new MeasurementConversionFactor(VolumeUnit.CubicMeter, VolumeUnit.CubicFoot); + + factor.Value.Should().Be(35.3146667); + } +} diff --git a/tests/Nox.Types.Tests/Types/Volume/VolumeTests.cs b/tests/Nox.Types.Tests/Types/Volume/VolumeTests.cs index 3d95f15..83b0feb 100644 --- a/tests/Nox.Types.Tests/Types/Volume/VolumeTests.cs +++ b/tests/Nox.Types.Tests/Types/Volume/VolumeTests.cs @@ -1,11 +1,227 @@ // ReSharper disable once CheckNamespace +using FluentAssertions; +using System.Globalization; + namespace Nox.Types.Tests.Types; public class VolumeTests { [Fact] - public void When_Create_Should() + public void Volume_Constructor_ReturnsSameValueAndDefaultUnit() + { + var volume = Volume.From(27.1828); + + volume.Value.Should().Be(27.1828); + volume.Unit.Should().Be(VolumeUnit.CubicMeter); + } + + [Fact] + public void Volume_Constructor_ReturnsRoundedValueAndDefaultUnit() + { + var volume = Volume.From(27.18281828459045); + + volume.Value.Should().Be(27.182818); + volume.Unit.Should().Be(VolumeUnit.CubicMeter); + } + + [Fact] + public void Volume_Constructor_WithUnit_ReturnsSameValueAndUnit() { + var volume = Volume.From(27.1828, VolumeUnit.CubicMeter); + + volume.Value.Should().Be(27.1828); + volume.Unit.Should().Be(VolumeUnit.CubicMeter); + } + + [Fact] + public void Volume_Constructor_WithUnitInCubicMeters_ReturnsSameValueAndUnit() + { + var volume = Volume.FromCubicMeters(27.1828); + + volume.Value.Should().Be(27.1828); + volume.Unit.Should().Be(VolumeUnit.CubicMeter); + } + + [Fact] + public void Volume_Constructor_WithUnitInCubicFeet_ReturnsSameValueAndUnit() + { + var volume = Volume.FromCubicFeet(959.951522); + + volume.Value.Should().Be(959.951522); + volume.Unit.Should().Be(VolumeUnit.CubicFoot); + } + + [Fact] + public void Volume_Constructor_WithNegativeValueInput_ThrowsException() + { + var action = () => Volume.From(-27.1828); + + action.Should().Throw() + .And.Errors.Should().BeEquivalentTo(new[] { new ValidationFailure("Value", "Could not create a Nox Volume type as negative volume value -27.1828 is not allowed.") }); + } + + [Fact] + public void Volume_Constructor_WithNaNValueInput_ThrowsException() + { + var action = () => Volume.From(double.NaN); + + action.Should().Throw() + .And.Errors.Should().BeEquivalentTo(new[] { new ValidationFailure("Value", "Could not create a Nox type as value NaN is not allowed.") }); + } + + [Fact] + public void Volume_Constructor_WithPositiveInfinityValueInput_ThrowsException() + { + var action = () => Volume.From(double.PositiveInfinity); + + action.Should().Throw() + .And.Errors.Should().BeEquivalentTo(new[] { new ValidationFailure("Value", "Could not create a Nox type as value Infinity is not allowed.") }); + } + + [Fact] + public void Volume_Constructor_WithNegativeInfinityValueInput_ThrowsException() + { + var action = () => Volume.From(double.NegativeInfinity); + + action.Should().Throw() + .And.Errors.Should().BeEquivalentTo(new[] { new ValidationFailure("Value", "Could not create a Nox type as value Infinity is not allowed.") }); + } + + [Fact] + public void Volume_ToCubicMeters_ReturnsValue() + { + var volume = Volume.From(27.1828); + + volume.ToCubicMeters().Should().Be(27.1828); + } + + [Fact] + public void Volume_ToCubicFeet_ReturnsValue() + { + var volume = Volume.From(27.1828); + + volume.ToCubicFeet().Should().Be(959.951522); + } + + [Theory] + [InlineData("en-US")] + [InlineData("pt-PT")] + public void Volume_ValueInCubicMeters_ToString_IsCultureIndepdendent(string culture) + { + void Test() + { + var volume = Volume.FromCubicMeters(27.1828); + volume.ToString().Should().Be("27.1828 m³"); + } + + TestUtility.RunInCulture(Test, culture); + } + + [Theory] + [InlineData("en-US", "27.1828 m³")] + [InlineData("pt-PT", "27,1828 m³")] + public void Volume_ValueInCubicMeters_ToString_IsCultureDependent(string culture, string expected) + { + void Test() + { + var volume = Volume.FromCubicMeters(27.1828); + volume.ToString(new CultureInfo(culture)).Should().Be(expected); + } + + TestUtility.RunInCulture(Test, culture); + } + + [Theory] + [InlineData("en-US")] + [InlineData("pt-PT")] + public void Volume_ValueInCubicFeet_ToString_IsCultureIndependent(string culture) + { + void Test() + { + var volume = Volume.FromCubicFeet(959.951522); + volume.ToString().Should().Be("959.951522 ft³"); + } + + TestUtility.RunInCulture(Test, culture); + } + + [Theory] + [InlineData("en-US", "959.951522 ft³")] + [InlineData("pt-PT", "959,951522 ft³")] + public void Volume_ValueInCubicFeet_ToString_IsCultureDependent(string culture, string expected) + { + void Test() + { + var volume = Volume.FromCubicFeet(959.951522); + volume.ToString(new CultureInfo(culture)).Should().Be(expected); + } + + TestUtility.RunInCulture(Test, culture); + } + + [Fact] + public void Volume_Equality_WithSameVolumeUnit_Tests() + { + var volume1 = Volume.From(27.1828, VolumeUnit.CubicMeter); + + var volume2 = Volume.From(27.1828, VolumeUnit.CubicMeter); + + AssertAreEquivalent(volume1, volume2); + } + + [Fact] + public void Volume_Equality_WithDifferentVolumeUnit_Tests() + { + var volume1 = Volume.From(27.1828, VolumeUnit.CubicMeter); + + var volume2 = Volume.From(959.951522, VolumeUnit.CubicFoot); + + AssertAreEquivalent(volume1, volume2); + } + + [Fact] + public void Volume_NonEquality_SpecifyingVolumeUnit_WithSameUnit_Tests() + { + var volume1 = Volume.From(27.1828, VolumeUnit.CubicMeter); + + var volume2 = Volume.From(959.951522, VolumeUnit.CubicMeter); + + AssertAreNotEquivalent(volume1, volume2); + } + + [Fact] + public void Volume_NonEquality_SpecifyingVolumeUnit_WithDifferentUnit_Tests() + { + var volume1 = Volume.From(27.1828, VolumeUnit.CubicMeter); + + var volume2 = Volume.From(27.1828, VolumeUnit.CubicFoot); + + AssertAreNotEquivalent(volume1, volume2); + } + + private static void AssertAreEquivalent(Volume expected, Volume actual) + { + actual.Should().Be(expected); + + expected.Equals(actual).Should().BeTrue(); + + actual.Equals(expected).Should().BeTrue(); + + (expected == actual).Should().BeTrue(); + + (expected != actual).Should().BeFalse(); + } + + private static void AssertAreNotEquivalent(Volume expected, Volume actual) + { + actual.Should().NotBe(expected); + + expected.Equals(actual).Should().BeFalse(); + + actual.Equals(expected).Should().BeFalse(); + + (expected == actual).Should().BeFalse(); + (expected != actual).Should().BeTrue(); } } \ No newline at end of file diff --git a/tests/Nox.Types.Tests/Types/Volume/VolumeUnitTests.cs b/tests/Nox.Types.Tests/Types/Volume/VolumeUnitTests.cs new file mode 100644 index 0000000..311b8d0 --- /dev/null +++ b/tests/Nox.Types.Tests/Types/Volume/VolumeUnitTests.cs @@ -0,0 +1,26 @@ +using FluentAssertions; + +namespace Nox.Types.Tests.Types; + +public class VolumeUnitTests +{ + [Fact] + public void VolumeUnit_CubicMeter_ReturnsValidIdNameAndSymbol() + { + var cubicMeter = VolumeUnit.CubicMeter; + + cubicMeter.Id.Should().Be(1); + cubicMeter.Name.Should().Be("CubicMeter"); + cubicMeter.Symbol.Should().Be("m³"); + } + + [Fact] + public void VolumeUnit_CubicFoot_ReturnsValidIdNameAndSymbol() + { + var cubicMeter = VolumeUnit.CubicFoot; + + cubicMeter.Id.Should().Be(2); + cubicMeter.Name.Should().Be("CubicFoot"); + cubicMeter.Symbol.Should().Be("ft³"); + } +} From f3fc74ae88c3b8341429c8f484b0e2a2298d8e41 Mon Sep 17 00:00:00 2001 From: Dejan Bratic Date: Fri, 30 Jun 2023 15:57:59 +0200 Subject: [PATCH 2/3] measurement conversion factor changed to abstract generic class; added volume conversion factor; --- src/Nox.Types/Types/Volume/Measurement.cs | 6 ++-- .../Volume/MeasurementConversionFactor.cs | 29 ++++++++-------- src/Nox.Types/Types/Volume/Volume.cs | 3 ++ .../Types/Volume/VolumeConversionFactor.cs | 19 +++++++++++ src/Nox.Types/Types/Volume/VolumeUnit.cs | 18 ++++++++++ .../MeasurementConversionFactorTests.cs | 22 ------------ .../Volume/VolumeConversionFactorTests.cs | 34 +++++++++++++++++++ .../Types/Volume/VolumeTests.cs | 3 ++ 8 files changed, 95 insertions(+), 39 deletions(-) create mode 100644 src/Nox.Types/Types/Volume/VolumeConversionFactor.cs delete mode 100644 tests/Nox.Types.Tests/Types/Volume/MeasurementConversionFactorTests.cs create mode 100644 tests/Nox.Types.Tests/Types/Volume/VolumeConversionFactorTests.cs diff --git a/src/Nox.Types/Types/Volume/Measurement.cs b/src/Nox.Types/Types/Volume/Measurement.cs index 57cd9b1..211d2eb 100644 --- a/src/Nox.Types/Types/Volume/Measurement.cs +++ b/src/Nox.Types/Types/Volume/Measurement.cs @@ -65,12 +65,14 @@ public override string ToString() public string ToString(IFormatProvider formatProvider) => $"{Value.ToString(formatProvider)} {Unit}"; - protected QuantityValue GetMeasurementIn(MeasurementUnit targetUnit) + protected QuantityValue GetMeasurementIn(TUnitType targetUnit) { - var factor = new MeasurementConversionFactor(Unit, targetUnit).Value; + var factor = ResolveUnitConversionFactor(Unit, targetUnit); return Round(Value * factor); } private static QuantityValue Round(QuantityValue value) => Math.Round((double)value, QuantityValueDecimalPrecision); + + protected abstract MeasurementConversionFactor ResolveUnitConversionFactor(TUnitType sourceUnit, TUnitType targetUnit); } diff --git a/src/Nox.Types/Types/Volume/MeasurementConversionFactor.cs b/src/Nox.Types/Types/Volume/MeasurementConversionFactor.cs index 2e6ea76..72a196c 100644 --- a/src/Nox.Types/Types/Volume/MeasurementConversionFactor.cs +++ b/src/Nox.Types/Types/Volume/MeasurementConversionFactor.cs @@ -3,35 +3,34 @@ namespace Nox.Types; -public class MeasurementConversionFactor +public abstract class MeasurementConversionFactor where TUnitType : MeasurementUnit { - private static readonly Dictionary<(VolumeUnit, VolumeUnit), double> DefinedVolumeConversionFactors = new() - { - { (VolumeUnit.CubicFoot, VolumeUnit.CubicMeter), 0.0283168466 }, - { (VolumeUnit.CubicMeter, VolumeUnit.CubicFoot), 35.3146667 }, - }; + protected abstract Dictionary<(TUnitType, TUnitType), double> DefinedConversionFactors { get; } public double Value { get; } - public MeasurementConversionFactor(MeasurementUnit sourceUnit, MeasurementUnit targetUnit) + protected MeasurementConversionFactor(TUnitType sourceUnit, TUnitType targetUnit) { Value = ResolveConversionFactor(sourceUnit, targetUnit); } - private static double ResolveConversionFactor(MeasurementUnit sourceUnit, MeasurementUnit targetUnit) + private double ResolveConversionFactor(TUnitType sourceUnit, TUnitType targetUnit) { + var conversion = (sourceUnit, targetUnit); - if (sourceUnit is VolumeUnit volumeSourceUnit && targetUnit is VolumeUnit volumeTargetUnit) - { - var conversion = (volumeSourceUnit, volumeTargetUnit); + if (DefinedConversionFactors.ContainsKey(conversion)) + return DefinedConversionFactors[conversion]; - if (DefinedVolumeConversionFactors.ContainsKey(conversion)) - return DefinedVolumeConversionFactors[conversion]; - } if (sourceUnit == targetUnit) return 1; throw new NotImplementedException($"No conversion defined from {sourceUnit?.Name} to {targetUnit?.Name}."); } -} + + public static QuantityValue operator *(QuantityValue value, MeasurementConversionFactor factor) + => value * factor.Value; + + public static QuantityValue operator *(MeasurementConversionFactor factor, QuantityValue value) + => value * factor; +} \ No newline at end of file diff --git a/src/Nox.Types/Types/Volume/Volume.cs b/src/Nox.Types/Types/Volume/Volume.cs index 03d7c10..dc322ee 100644 --- a/src/Nox.Types/Types/Volume/Volume.cs +++ b/src/Nox.Types/Types/Volume/Volume.cs @@ -46,4 +46,7 @@ protected override IEnumerable> GetEqualityComponen { yield return new KeyValuePair(nameof(Value), ToCubicMeters()); } + + protected override MeasurementConversionFactor ResolveUnitConversionFactor(VolumeUnit sourceUnit, VolumeUnit targetUnit) + => new VolumeConversionFactor(sourceUnit, targetUnit); } \ No newline at end of file diff --git a/src/Nox.Types/Types/Volume/VolumeConversionFactor.cs b/src/Nox.Types/Types/Volume/VolumeConversionFactor.cs new file mode 100644 index 0000000..b983425 --- /dev/null +++ b/src/Nox.Types/Types/Volume/VolumeConversionFactor.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace Nox.Types; + +public class VolumeConversionFactor : MeasurementConversionFactor +{ + private static readonly Dictionary<(VolumeUnit, VolumeUnit), double> _definedVolumeConversionFactors = new() + { + { (VolumeUnit.CubicFoot, VolumeUnit.CubicMeter), 0.0283168466 }, + { (VolumeUnit.CubicMeter, VolumeUnit.CubicFoot), 35.3146667 }, + }; + + protected override Dictionary<(VolumeUnit, VolumeUnit), double> DefinedConversionFactors => _definedVolumeConversionFactors; + + public VolumeConversionFactor(VolumeUnit sourceUnit, VolumeUnit targetUnit) + : base(sourceUnit, targetUnit) + { + } +} \ No newline at end of file diff --git a/src/Nox.Types/Types/Volume/VolumeUnit.cs b/src/Nox.Types/Types/Volume/VolumeUnit.cs index 5e1ac37..a301d4a 100644 --- a/src/Nox.Types/Types/Volume/VolumeUnit.cs +++ b/src/Nox.Types/Types/Volume/VolumeUnit.cs @@ -9,3 +9,21 @@ private VolumeUnit(int id, string name, string symbol) : base(id, name, symbol) { } } + + +public class LengthUnit : MeasurementUnit +{ + public static LengthUnit Meter { get; } = new LengthUnit(1, "Meter", "m"); + public static LengthUnit Foot { get; } = new LengthUnit(2, "Foot", "ft"); + + protected LengthUnit(int id, string name, string symbol) : base(id, name, symbol) + { + } +} + +public class DistanceUnit : LengthUnit +{ + protected DistanceUnit(int id, string name, string symbol) : base(id, name, symbol) + { + } +} \ No newline at end of file diff --git a/tests/Nox.Types.Tests/Types/Volume/MeasurementConversionFactorTests.cs b/tests/Nox.Types.Tests/Types/Volume/MeasurementConversionFactorTests.cs deleted file mode 100644 index 90693b4..0000000 --- a/tests/Nox.Types.Tests/Types/Volume/MeasurementConversionFactorTests.cs +++ /dev/null @@ -1,22 +0,0 @@ -using FluentAssertions; - -namespace Nox.Types.Tests.Types; - -public class MeasurementConversionFactorTests -{ - [Fact] - public void MeasurementConversionFactor_GetConversionFactor_FromCubicFootToCubicMeter_ReturnsValue() - { - var factor = new MeasurementConversionFactor(VolumeUnit.CubicFoot, VolumeUnit.CubicMeter); - - factor.Value.Should().Be(0.0283168466); - } - - [Fact] - public void MeasurementConversionFactor_GetConversionFactor_FromCubicMeterToCubicFoot_ReturnsValue() - { - var factor = new MeasurementConversionFactor(VolumeUnit.CubicMeter, VolumeUnit.CubicFoot); - - factor.Value.Should().Be(35.3146667); - } -} diff --git a/tests/Nox.Types.Tests/Types/Volume/VolumeConversionFactorTests.cs b/tests/Nox.Types.Tests/Types/Volume/VolumeConversionFactorTests.cs new file mode 100644 index 0000000..b2d4c5c --- /dev/null +++ b/tests/Nox.Types.Tests/Types/Volume/VolumeConversionFactorTests.cs @@ -0,0 +1,34 @@ +using FluentAssertions; + +namespace Nox.Types.Tests.Types; + +public class VolumeConversionFactorTests +{ + [Fact] + public void VolumeConversionFactor_FromCubicFootToCubicMeter_ReturnsValue() + { + var factor = new VolumeConversionFactor(VolumeUnit.CubicFoot, VolumeUnit.CubicMeter); + + factor.Value.Should().Be(0.0283168466); + } + + [Fact] + public void VolumeConversionFactor_FromCubicMeterToCubicFoot_ReturnsValue() + { + var factor = new VolumeConversionFactor(VolumeUnit.CubicMeter, VolumeUnit.CubicFoot); + + factor.Value.Should().Be(35.3146667); + } + + [Fact] + public void VolumeConversionFactor_WhenMultipliedByQuantityValue_ReturnsValue() + { + var factor = new VolumeConversionFactor(VolumeUnit.CubicMeter, VolumeUnit.CubicFoot); + + var result1 = (QuantityValue)2.5 * factor; + var result2 = factor * (QuantityValue)2.5; + + result1.Should().Be(result2); + result1.Should().Be(88.28666675); + } +} diff --git a/tests/Nox.Types.Tests/Types/Volume/VolumeTests.cs b/tests/Nox.Types.Tests/Types/Volume/VolumeTests.cs index 83b0feb..f36ebc0 100644 --- a/tests/Nox.Types.Tests/Types/Volume/VolumeTests.cs +++ b/tests/Nox.Types.Tests/Types/Volume/VolumeTests.cs @@ -214,6 +214,9 @@ private static void AssertAreEquivalent(Volume expected, Volume actual) private static void AssertAreNotEquivalent(Volume expected, Volume actual) { + var a =LengthUnit.Foot; + var b = DistanceUnit.Foot; + actual.Should().NotBe(expected); expected.Equals(actual).Should().BeFalse(); From fe70f4cc76a3288ba9f80278ccbbc84eb0b9d645 Mon Sep 17 00:00:00 2001 From: Dejan Bratic Date: Tue, 4 Jul 2023 11:23:45 +0200 Subject: [PATCH 3/3] fixed typos; --- src/Nox.Types/Types/Volume/Measurement.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Nox.Types/Types/Volume/Measurement.cs b/src/Nox.Types/Types/Volume/Measurement.cs index 211d2eb..9faae6f 100644 --- a/src/Nox.Types/Types/Volume/Measurement.cs +++ b/src/Nox.Types/Types/Volume/Measurement.cs @@ -41,7 +41,7 @@ public static TValueObject From(QuantityValue value, TUnitType unit) /// /// Validates a object. /// - /// true if the value is valid. + /// true if the value is valid. internal override ValidationResult Validate() { var result = Value.Validate(); @@ -58,10 +58,10 @@ public override string ToString() => $"{Value.ToString($"0.{new string('#', QuantityValueDecimalPrecision)}", CultureInfo.InvariantCulture)} {Unit}"; /// - /// Returns a string representation of the object using the specified . + /// Returns a string representation of the object using the specified . /// - /// The format provider for the length value. - /// A string representation of the object with the value formatted using the specified . + /// The format provider for the measurement value. + /// A string representation of the object with the value formatted using the specified . public string ToString(IFormatProvider formatProvider) => $"{Value.ToString(formatProvider)} {Unit}";