Skip to content
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

Redesign UnitSystem to support non-SI systems and configurable default units #709

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
40 changes: 20 additions & 20 deletions CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,23 +183,25 @@ private void GenerateInstanceConstructors()
/// <param name=""value"">The numeric value to construct this quantity with.</param>
/// <param name=""unitSystem"">The unit system to create the quantity with.</param>
/// <exception cref=""ArgumentNullException"">The given <see cref=""UnitSystem""/> is null.</exception>
/// <exception cref=""ArgumentException"">No unit was found for the given <see cref=""UnitSystem""/>.</exception>
/// <exception cref=""ArgumentException"">No default unit was found for the given <see cref=""UnitSystem""/>.</exception>
public {_quantity.Name}({_valueType} value, UnitSystem unitSystem)
{{
if(unitSystem == null) throw new ArgumentNullException(nameof(unitSystem));

var unitInfos = Info.GetUnitInfosFor(unitSystem.BaseUnits);
var firstUnitInfo = unitInfos.FirstOrDefault();
if(unitSystem == null) throw new ArgumentNullException(nameof(unitSystem));
");

Writer.WL(_quantity.BaseType == "double"
? @"
_value = Guard.EnsureValidNumber(value, nameof(value));"
_value = Guard.EnsureValidNumber(value, nameof(value));
"
: @"
_value = value;");
Writer.WL(@"
_unit = firstUnitInfo?.Value ?? throw new ArgumentException(""No units were found for the given UnitSystem."", nameof(unitSystem));
}
_value = value;
");

Writer.WL($@"
var defaultUnitInfo = unitSystem.GetDefaultUnitInfo(QuantityType) as UnitInfo<{_unitEnumName}>;

_unit = defaultUnitInfo?.Value ?? throw new ArgumentException(""No default unit was defined for the given UnitSystem."", nameof(unitSystem));
}}
");
}

Expand Down Expand Up @@ -821,13 +823,12 @@ public double As(UnitSystem unitSystem)
if(unitSystem == null)
throw new ArgumentNullException(nameof(unitSystem));

var unitInfos = Info.GetUnitInfosFor(unitSystem.BaseUnits);
var defaultUnitInfo = unitSystem.GetDefaultUnitInfo(QuantityType) as UnitInfo<{_unitEnumName}>;

var firstUnitInfo = unitInfos.FirstOrDefault();
if(firstUnitInfo == null)
throw new ArgumentException(""No units were found for the given UnitSystem."", nameof(unitSystem));
if(defaultUnitInfo == null)
throw new ArgumentException(""No default unit was found for the given UnitSystem."", nameof(unitSystem));

return As(firstUnitInfo.Value);
return As(defaultUnitInfo.Value);
}}

/// <inheritdoc />
Expand Down Expand Up @@ -864,13 +865,12 @@ IQuantity IQuantity.ToUnit(Enum unit)
if(unitSystem == null)
throw new ArgumentNullException(nameof(unitSystem));

var unitInfos = Info.GetUnitInfosFor(unitSystem.BaseUnits);
var defaultUnitInfo = unitSystem.GetDefaultUnitInfo(QuantityType) as UnitInfo<{_unitEnumName}>;

var firstUnitInfo = unitInfos.FirstOrDefault();
if(firstUnitInfo == null)
throw new ArgumentException(""No units were found for the given UnitSystem."", nameof(unitSystem));
if(defaultUnitInfo == null)
throw new ArgumentException(""No default unit was found for the given UnitSystem."", nameof(unitSystem));

return ToUnit(firstUnitInfo.Value);
return ToUnit(defaultUnitInfo.Value);
}}

/// <inheritdoc />
Expand Down
11 changes: 11 additions & 0 deletions UnitsNet.Tests/CustomCode/LengthTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Xunit;
using UnitsNet.Units;
using System;
using System.Linq;

namespace UnitsNet.Tests.CustomCode
{
Expand Down Expand Up @@ -186,6 +187,16 @@ public void Constructor_UnitSystemSI_AssignsSIUnit()
Assert.Equal(LengthUnit.Meter, length.Unit);
}

[Fact]
public void Constructor_UnitSystemWithMillimeters_ConstructsWithMillimeters()
{
var myDefaultLengthUnit = Length.Info.UnitInfos.First(x => x.Value == LengthUnit.Millimeter);
lipchev marked this conversation as resolved.
Show resolved Hide resolved
var myUnitSystem = UnitSystem.SI.WithDefaultUnit(QuantityType.Length, myDefaultLengthUnit);

var length = new Length(1.0, myUnitSystem);
Assert.Equal(LengthUnit.Millimeter, length.Unit);
}

[Fact]
public void Constructor_UnitSystemWithNoMatchingBaseUnits_ThrowsArgumentException()
{
Expand Down
62 changes: 62 additions & 0 deletions UnitsNet.Tests/UnitSystemTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.

using System;
using System.Linq;
using UnitsNet.Units;
using Xunit;

Expand Down Expand Up @@ -149,5 +150,66 @@ public void SIUnitSystemHasCorrectBaseUnits()
Assert.Equal(AmountOfSubstanceUnit.Mole, UnitSystem.SI.BaseUnits.Amount);
Assert.Equal(LuminousIntensityUnit.Candela, UnitSystem.SI.BaseUnits.LuminousIntensity);
}

[Fact]
public void GetDefaultUnitInfo_GivenUndefinedQuantity_ThrowsArgumentException()
{
Assert.Throws<ArgumentException>(() => UnitSystem.SI.GetDefaultUnitInfo(QuantityType.Undefined));
}

[Fact]
public void GetDefaultUnitInfo_GivenQuantityWithNoDefaultUnits_ReturnsNull()
{
// we cannot simply rely on something like AmplitudeRatio not having a default base unit definition (as this is expected to change soon)
var unitSystemWithNoDefaultLengthUnit = UnitSystem.SI.WithDefaultUnit(QuantityType.Length, null); // we can however force the dissociation

Assert.Null(unitSystemWithNoDefaultLengthUnit.GetDefaultUnitInfo(QuantityType.Length));
}

[Fact]
public void WithDefaultUnit_GivenUndefinedQuantityType_ThrowsArgumentException()
{
var anyUnitInfo = Length.Info.UnitInfos.First();

Assert.Throws<ArgumentException>(() => UnitSystem.SI.WithDefaultUnit(QuantityType.Undefined, anyUnitInfo));
}

[Fact]
public void WithDefaultUnit_GivenIncompatibleUnitAndQuantityType_ThrowsArgumentException()
{
var nonMassUnit = Length.Info.UnitInfos.First();

Assert.Throws<ArgumentException>(() => UnitSystem.SI.WithDefaultUnit(QuantityType.Mass, nonMassUnit));
}

[Fact]
public void WithDefaultUnit_GivenNullForBaseUnits_ReturnsUnitSystemWithOldBaseUnits()
{
var myDefaultLengthUnit = Length.Info.UnitInfos.First(x => x.Value == LengthUnit.Millimeter);

var newSI = UnitSystem.SI.WithDefaultUnit(QuantityType.Length, myDefaultLengthUnit, (BaseUnits) null);

Assert.Equal(UnitSystem.SI, newSI); // currently comparing using BaseUnits
}

[Theory]
[InlineData(LengthUnit.Undefined, MassUnit.Kilogram, DurationUnit.Second, ElectricCurrentUnit.Ampere, TemperatureUnit.Kelvin, AmountOfSubstanceUnit.Mole, LuminousIntensityUnit.Candela)]
[InlineData(LengthUnit.Meter, MassUnit.Undefined, DurationUnit.Second, ElectricCurrentUnit.Ampere, TemperatureUnit.Kelvin, AmountOfSubstanceUnit.Mole, LuminousIntensityUnit.Candela)]
[InlineData(LengthUnit.Meter, MassUnit.Kilogram, DurationUnit.Undefined, ElectricCurrentUnit.Ampere, TemperatureUnit.Kelvin, AmountOfSubstanceUnit.Mole, LuminousIntensityUnit.Candela)]
[InlineData(LengthUnit.Meter, MassUnit.Kilogram, DurationUnit.Second, ElectricCurrentUnit.Undefined, TemperatureUnit.Kelvin, AmountOfSubstanceUnit.Mole, LuminousIntensityUnit.Candela)]
[InlineData(LengthUnit.Meter, MassUnit.Kilogram, DurationUnit.Second, ElectricCurrentUnit.Ampere, TemperatureUnit.Undefined, AmountOfSubstanceUnit.Mole, LuminousIntensityUnit.Candela)]
[InlineData(LengthUnit.Meter, MassUnit.Kilogram, DurationUnit.Second, ElectricCurrentUnit.Ampere, TemperatureUnit.Kelvin, AmountOfSubstanceUnit.Undefined, LuminousIntensityUnit.Candela)]
[InlineData(LengthUnit.Meter, MassUnit.Kilogram, DurationUnit.Second, ElectricCurrentUnit.Ampere, TemperatureUnit.Kelvin, AmountOfSubstanceUnit.Mole, LuminousIntensityUnit.Undefined)]
public void WithDefaultUnit_GivenBaseUnitsNotFullyDefined_ThrowsArgumentException(LengthUnit length, MassUnit mass, DurationUnit time, ElectricCurrentUnit current,
TemperatureUnit temperature, AmountOfSubstanceUnit amount, LuminousIntensityUnit luminousIntensity)
{
var myDefaultLengthUnit = Length.Info.UnitInfos.First(x => x.Value == LengthUnit.Millimeter);

var baseUnits = new BaseUnits(length, mass, time, current, temperature, amount, luminousIntensity);

// BaseUnits(obsolete) kept in order to avoid introducing a breaking change (just yet)
Assert.Throws<ArgumentException>(()=> UnitSystem.SI.WithDefaultUnit(QuantityType.Length, myDefaultLengthUnit, baseUnits));
}

}
}
30 changes: 14 additions & 16 deletions UnitsNet/GeneratedCode/Quantities/Acceleration.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 14 additions & 16 deletions UnitsNet/GeneratedCode/Quantities/AmountOfSubstance.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 14 additions & 16 deletions UnitsNet/GeneratedCode/Quantities/AmplitudeRatio.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading