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

Intrinsic arithmetic function overloads #8710

Merged
merged 29 commits into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
202755b
Support long and double for arithmetic operator functions
jrdodds Apr 26, 2023
d6b87e8
Merge branch 'dotnet:main' into IntrinsicFunctionsOverload
jrdodds Apr 26, 2023
d01d30e
added argument that exceeds the size of long
jrdodds Apr 26, 2023
eb2d53f
use a const char for the decimal separator
jrdodds Apr 28, 2023
68b71d6
Support comparison when the lhs is an integer
jrdodds Apr 28, 2023
a5375f3
Order members by TypeCode; add tests and ChangeWave
jrdodds May 6, 2023
16670c7
Add change wave to fast path
jrdodds May 6, 2023
78ba51f
Merge remote-tracking branch 'upstream/main' into IntrinsicFunctionsO…
jrdodds May 6, 2023
7de86f0
add property in Expander_Tests; rename in other classes
jrdodds May 6, 2023
49b0afd
reduce code repetition; try double if long fails in the same method
jrdodds May 10, 2023
792e61a
modify TryConvertToLong to check long min/max values before convertin…
jrdodds May 11, 2023
333f242
modify TryConvertToInt to check int min/max before converting to double
jrdodds May 11, 2023
278e381
Merge branch 'dotnet:main' into IntrinsicFunctionsOverload
jrdodds May 12, 2023
6369e80
change access to TryConvertToInt, TryConvertToLong, TryConvertToDoubl…
jrdodds May 20, 2023
e1e705f
chnages to rely on TryParse
jrdodds May 20, 2023
0f6ed65
modify test TryConvertToLongGivenDoubleWithLongMaxValue for Apple Sil…
jrdodds May 21, 2023
de36cf0
Merge branch 'dotnet:main' into IntrinsicFunctionsOverload
jrdodds May 21, 2023
158e6e6
cache comparer for arithmetic overloads
jrdodds May 22, 2023
f2cb002
change to use Type.FindMembers and Array.Sort
jrdodds May 22, 2023
824825b
add comment
jrdodds May 23, 2023
f64fa81
update TryParse to use invariant culture
jrdodds May 25, 2023
2339681
change to accept thousands separator and add test for different locale
jrdodds May 29, 2023
3d66331
use InvariantCulture with double.TryParse
jrdodds Jun 7, 2023
22f8684
add comments
jrdodds Jun 7, 2023
e1c7115
fix IDE0005 error that is not reported in local builds
jrdodds Jun 7, 2023
6d15f2f
Merge branch 'dotnet:main' into IntrinsicFunctionsOverload
jrdodds Jun 7, 2023
4f21730
revert unintended whitespace formatting
jrdodds Jun 8, 2023
65cea2f
Merge branch 'dotnet:main' into IntrinsicFunctionsOverload
jrdodds Jun 27, 2023
19b5039
fix nullable errors after merge
jrdodds Jun 28, 2023
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
279 changes: 279 additions & 0 deletions src/Build.UnitTests/Evaluation/ExpanderFunction_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Globalization;
using System.Runtime.InteropServices;
using System.Threading;

using Microsoft.Build.Evaluation;

using Shouldly;

using Xunit;
using Xunit.Abstractions;

namespace Microsoft.Build.Engine.UnitTests.Evaluation
{
public class ExpanderFunction_Tests
{
private readonly ITestOutputHelper _output;

public ExpanderFunction_Tests(ITestOutputHelper output) => _output = output;

/* Tests for TryConvertToInt */

[Fact]
public void TryConvertToIntGivenNull()
{
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToInt(null, out int actual).ShouldBeFalse();
actual.ShouldBe(0);
}

[Fact]
public void TryConvertToIntGivenDouble()
{
const double value = 10.0;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToInt(value, out int actual).ShouldBeTrue();
actual.ShouldBe(10);
}

[Fact]
public void TryConvertToIntGivenLong()
{
const long value = 10;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToInt(value, out int actual).ShouldBeTrue();
actual.ShouldBe(10);
}

[Fact]
public void TryConvertToIntGivenInt()
{
const int value = 10;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToInt(value, out int actual).ShouldBeTrue();
actual.ShouldBe(10);
}

[Fact]
public void TryConvertToIntGivenString()
{
const string value = "10";
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToInt(value, out int actual).ShouldBeTrue();
actual.ShouldBe(10);
}

[Fact]
public void TryConvertToIntGivenDoubleWithIntMinValue()
{
const int expected = int.MinValue;
const double value = expected;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToInt(value, out int actual).ShouldBeTrue();
actual.ShouldBe(expected);
}

[Fact]
public void TryConvertToIntGivenDoubleWithIntMaxValue()
{
const int expected = int.MaxValue;
const double value = expected;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToInt(value, out int actual).ShouldBeTrue();
actual.ShouldBe(expected);
}

[Fact]
public void TryConvertToIntGivenDoubleWithLessThanIntMinValue()
{
const double value = int.MinValue - 1.0;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToInt(value, out int actual).ShouldBeFalse();
actual.ShouldBe(0);
}

[Fact]
public void TryConvertToIntGivenDoubleWithGreaterThanIntMaxValue()
{
const double value = int.MaxValue + 1.0;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToInt(value, out int actual).ShouldBeFalse();
actual.ShouldBe(0);
}

[Fact]
public void TryConvertToIntGivenLongWithGreaterThanIntMaxValue()
{
const long value = int.MaxValue + 1L;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToInt(value, out int actual).ShouldBeFalse();
actual.ShouldBe(0);
}

/* Tests for TryConvertToLong */

[Fact]
public void TryConvertToLongGivenNull()
{
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToLong(null, out long actual).ShouldBeFalse();
actual.ShouldBe(0);
}

[Fact]
public void TryConvertToLongGivenDouble()
{
const double value = 10.0;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToLong(value, out long actual).ShouldBeTrue();
actual.ShouldBe(10);
}

[Fact]
public void TryConvertToLongGivenLong()
{
const long value = 10;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToLong(value, out long actual).ShouldBeTrue();
actual.ShouldBe(10);
}

[Fact]
public void TryConvertToLongGivenInt()
{
const int value = 10;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToLong(value, out long actual).ShouldBeTrue();
actual.ShouldBe(10);
}

[Fact]
public void TryConvertToLongGivenString()
{
const string value = "10";
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToLong(value, out long actual).ShouldBeTrue();
actual.ShouldBe(10);
}

[Fact]
public void TryConvertToLongGivenDoubleWithLongMinValue()
{
const long expected = long.MinValue;
const double value = expected;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToLong(value, out long actual).ShouldBeTrue();
actual.ShouldBe(expected);
}

[Fact]
public void TryConvertToLongGivenDoubleWithLongMaxValueShouldNotThrow()
{
// An OverflowException should not be thrown from TryConvertToLong().
// Convert.ToInt64(double) has a defect and will throw an OverflowException
// for values >= (long.MaxValue - 511) and <= long.MaxValue.
_ = Should.NotThrow(() => Expander<IProperty, IItem>.Function<IProperty>.TryConvertToLong((double)long.MaxValue, out _));
}

[Fact]
public void TryConvertToLongGivenDoubleWithLongMaxValue()
{
const long longMaxValue = long.MaxValue;
bool result = Expander<IProperty, IItem>.Function<IProperty>.TryConvertToLong((double)longMaxValue, out long actual);
if (RuntimeInformation.OSArchitecture != Architecture.Arm64)
{
// Because of loss of precision, long.MaxValue will not 'round trip' from long to double to long.
result.ShouldBeFalse();
actual.ShouldBe(0);
}
else
{
// Testing on macOS 12 on Apple Silicon M1 Pro produces different result.
result.ShouldBeTrue();
actual.ShouldBe(longMaxValue);
}
}

[Fact]
public void TryConvertToLongGivenDoubleWithVeryLargeLongValue()
{
// Because of loss of precision, veryLargeLong will not 'round trip' but within TryConvertToLong
// the double to long conversion will pass the tolerance test. Return will be true and veryLargeLong != expected.
const long veryLargeLong = long.MaxValue - 512;
const double value = veryLargeLong;
const long expected = 9223372036854774784L;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToLong(value, out long actual).ShouldBeTrue();
actual.ShouldBe(expected);
}

[Fact]
public void TryConvertToLongGivenDoubleWithLessThanLongMinValue()
{
const double value = -92233720368547758081D;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToLong(value, out long actual).ShouldBeFalse();
actual.ShouldBe(0);
}

[Fact]
public void TryConvertToLongGivenDoubleWithGreaterThanLongMaxValue()
{
const double value = (double)long.MaxValue + long.MaxValue;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToLong(value, out long actual).ShouldBeFalse();
actual.ShouldBe(0);
}

/* Tests for TryConvertToDouble */

[Fact]
public void TryConvertToDoubleGivenNull()
{
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToDouble(null, out double actual).ShouldBeFalse();
actual.ShouldBe(0);
}

[Fact]
public void TryConvertToDoubleGivenDouble()
{
const double value = 10.0;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToDouble(value, out double actual).ShouldBeTrue();
actual.ShouldBe(10.0);
}

[Fact]
public void TryConvertToDoubleGivenLong()
{
const long value = 10;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToDouble(value, out double actual).ShouldBeTrue();
actual.ShouldBe(10.0);
}

[Fact]
public void TryConvertToDoubleGivenInt()
{
const int value = 10;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToDouble(value, out double actual).ShouldBeTrue();
actual.ShouldBe(10.0);
}

[Fact]
public void TryConvertToDoubleGivenString()
{
const string value = "10";
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToDouble(value, out double actual).ShouldBeTrue();
actual.ShouldBe(10.0);
}

[Fact]
public void TryConvertToDoubleGivenStringAndLocale()
{
const string value = "1,2";

Thread currentThread = Thread.CurrentThread;
CultureInfo originalCulture = currentThread.CurrentCulture;

try
{
// English South Africa locale uses ',' as decimal separator.
// The invariant culture should be used and "1,2" should be 12.0 not 1.2.
var cultureEnglishSouthAfrica = CultureInfo.CreateSpecificCulture("en-ZA");
currentThread.CurrentCulture = cultureEnglishSouthAfrica;
Expander<IProperty, IItem>.Function<IProperty>.TryConvertToDouble(value, out double actual).ShouldBeTrue();
actual.ShouldBe(12.0);
}
finally
{
// Restore CultureInfo.
currentThread.CurrentCulture = originalCulture;
CultureInfo.CurrentCulture = originalCulture;
CultureInfo.DefaultThreadCurrentCulture = originalCulture;
}
}
}
}
Loading