Skip to content

Commit

Permalink
Fixed unit tests NFUnitTestConversions (#141)
Browse files Browse the repository at this point in the history
***NO_CI***
  • Loading branch information
edleno2 authored May 24, 2021
1 parent ae4b543 commit 9869e58
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 41 deletions.
221 changes: 181 additions & 40 deletions Tests/NFUnitTestConversions/UnitTestConvertTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ public void Cast_FloatingPoint()
public void Convert_Positive()
{
string number = "12";
int actualNumber = 12;
SByte value_sb = Convert.ToSByte(number);
Assert.Equal(value_sb, (byte)12);
Byte value_b = Convert.ToByte(number);
Expand All @@ -76,7 +75,6 @@ public void Convert_Positive()
public void Convert_PositivePlus()
{
string number = "+12";
int actualNumber = 12;
SByte value_sb = Convert.ToSByte(number);
Assert.Equal(value_sb, (byte)12);
Byte value_b = Convert.ToByte(number);
Expand All @@ -101,19 +99,18 @@ public void Convert_Negative()
{
string number = "-12";
int actualNumber = -12;

SByte value_sb = Convert.ToSByte(number);
Assert.Equal(value_sb, (sbyte)actualNumber);
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { Byte value_b = Convert.ToByte(number); });
Assert.Equal(value_sb, (sbyte)actualNumber, "Test1");
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { Byte value_b = Convert.ToByte(number); }, "Test2");
Int16 value_s16 = Convert.ToInt16(number);
Assert.Equal(value_s16, (short)actualNumber);
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { UInt16 value_u16 = Convert.ToUInt16(number); });
Assert.Equal(value_s16, (short)actualNumber, "Test3");
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { UInt16 value_u16 = Convert.ToUInt16(number); }, "Test4");
Int32 value_s32 = Convert.ToInt32(number);
Assert.Equal(value_s32, (int)actualNumber);
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { UInt32 value_u32 = Convert.ToUInt32(number); });
Assert.Equal(value_s32, (int)actualNumber, "Test5");
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { UInt32 value_u32 = Convert.ToUInt32(number); }, "Test6");
Int64 value_s64 = Convert.ToInt32(number);
Assert.Equal(value_s64, (long)actualNumber);
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { UInt64 value_u64 = Convert.ToUInt64(number); });
Assert.Equal(value_s64, (long)actualNumber, "Test7");
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { UInt64 value_u64 = Convert.ToUInt64(number); }, "Test8");
}

[TestMethod]
Expand Down Expand Up @@ -166,14 +163,32 @@ public void Convert_Whitespace()
public void Convert_DoubleNormalizeNeg()
{
string number = "-3600030383448481.123456";
double actualNumber = -3600030383448481.123456;
double actualNumber = -3600030383448481.123456; // note: this is the double as calculated by the Roslyn compiler - not the same as the native code routine

double value_dd = Convert.ToDouble(number);

Assert.Equal(value_dd, actualNumber);
number = "+0.00000000484874758559e-3";
actualNumber = 4.84874758559e-12;
Assert.Equal(actualNumber, Convert.ToDouble(number));
Assert.Equal(value_dd, actualNumber, $"Convert.ToDouble should be {number}"); // this works if the numbers are fairly small - i.e. not e^x sized

// Examples of how we can get differences due to differences between the compiler parser, and the native parser
// And differences caused by the native parsers "fast speed" causing rounding errors
string num1 = "+0.000000004848747585e-3";
string num2 = "4.848747585e-12"; // same number as above, but we've moved the digits over a bit and adjusted the exponent
double dnum1Roslyn = +0.000000004848747585e-3; // Roslyn compiler will parse the value and put it into the double at compile time
double dnum1Native = Convert.ToDouble(num1); // Native code will parse the value and put it into the double at run time
double dnum2Roslyn = 4.848747585e-12; // Compiler
double dnum2Native = Convert.ToDouble(num2); // Native


// Now we will compare some of the ToString values
// compare native to native - but parsing e-3 versus e-12 means 9 more loops thru the double multiplication where rounding can occur so this won't work for all numbers
Assert.Equal(dnum1Native.ToString(), dnum2Native.ToString(), $"Comparing native parse tostring");
// compare roslyn to roslyn - the roslyn parser is more accurate and that means the double is much more likely to be the same
Assert.Equal(dnum1Roslyn.ToString(), dnum2Roslyn.ToString(), $"Comparing Roslyn parse tostring");
// Now mix things up
Assert.Equal(dnum1Roslyn.ToString(), dnum2Native.ToString(), $"Comparing Roslyn parse and native parse tostring");
Assert.Equal(dnum1Native.ToString(), dnum2Roslyn.ToString(), $"Comparing Roslyn parse and native parse tostring");
Assert.Equal(dnum1Roslyn.ToString(), dnum1Native.ToString(), $"Comparing Roslyn to native using {num1}");
Assert.Equal(dnum2Roslyn.ToString(), dnum2Native.ToString(), $"Comparing Roslyn to natvie using {num2}");
}

[TestMethod]
Expand All @@ -198,48 +213,128 @@ public void Convert_HexInt()
}

[TestMethod]

public void Convert_BoundaryValues()
{
double valMax = double.MaxValue;
string numMax = valMax.ToString();
double valMin = double.MinValue;
string numMin = valMin.ToString();

Assert.Equal(valMax, Convert.ToDouble(numMax));
Assert.Equal(valMin, Convert.ToDouble(numMin));

valMax = float.MaxValue;
numMax = valMax.ToString();
valMin = float.MinValue;
numMin = valMin.ToString();

Assert.Equal(valMax, Convert.ToDouble(numMax));
Assert.Equal(valMin, Convert.ToDouble(numMin));
//***
//* Boundary tests - tests of the min and max values for double, float and int's.
//* Note for double/float - the ToString() function is limited to a range around 2^64 and 2^-64 - otherwise you get a string of 'oor' or '-oor' (oor = out-of-range)
// Boundary tests for double/float include the numbers that are around the edge of where out-of-range is produced.
//***

const string OUT_OF_RANGE = "oor"; // nanoPrintf can only print up to 2^64-2 as a max value for double/floating
const string OUT_OF_RANGE_NEG = "-oor"; // nanoPrintf can only print down to -2^64+2 as a min value for double/floating

const string DOUBLE_MAX_VAL = "1.7976931348623157E+308"; // will get 'oor' when printed
const string DOUBLE_MAX_HEX = "0x7FEFFFFFFFFFFFFF"; // value from IEEE 574
const string DOUBLE_MIN_VAL = "-1.7976931348623157E+308"; // will get '-oor' when printed
const string DOUBLE_MIN_HEX = "0xFFEFFFFFFFFFFFFF"; // value from IEEE 574
const string DOUBLE_ZERO_HEX = "0x0000000000000000";
const string DOUBLE_LARGEST_PRINT = "1.84467440737095E+19"; // this is the largest number you can ask for ToString() and get a response
const string DOUBLE_LARGESTINVALID_PRINT = "1.8446744073709552E+19"; // first positive value that will get the response 'oor' when printed
const string DOUBLE_SMALLEST_PRINT = "-1.32585973029787E+19"; // this is the smallest number you can ask for ToString() and get a response
const string DOUBLE_SMALLESTINVALID_PRINT = "-1.8446744073709552E+19"; // first negative value that will get the response '-oor' when printed

const string FLOAT_MAX_VAL = "3.40282347E+38";
const string FLOAT_MAX_HEX = "0x7F7FFFFF"; // will get 'oor' when printed
const string FLOAT_MIN_VAL = "-3.40282347E+38";
const string FLOAT_MIN_HEX = "0xFF7FFFFF"; // will get '-oor' when printed
const string FLOAT_ZERO_HEX = "0x00000000";
const string FLOAT_LARGEST_PRINT = "1.844674E+19"; // this is the largest number you can ask for ToString() and get a response
const string FLOAT_LARGESTINVALID_PRINT = "1.8446744E+19"; // first positive value that will get the response 'oor' when printed
const string FLOAT_SMALLEST_PRINT = "-1.844674E+19"; // this is the smallest number you can ask for ToString() and get a response
const string FLOAT_SMALLESTINVALID_PRINT = "-1.8446744E+19"; // first negative value that will get the response '-oor' when printed

// boundary: double max
string time = DateTime.UtcNow.ToString("hh:mm:ss");
double doubleMax = double.MaxValue;
Assert.Equal(doubleMax.ToString(), OUT_OF_RANGE, "nanoPrintf returns oor for double > 2^64-2");
Assert.Equal(DoubleToHex(doubleMax), DOUBLE_MAX_HEX, "Hex value to double max value does not match");
Assert.Equal(DoubleToHex(Convert.ToDouble(DOUBLE_MAX_VAL)), DOUBLE_MAX_HEX, "Parsing double max value does not return correct hex value");

// boundary: double min
double doubleMin = double.MinValue;
Assert.Equal(doubleMin.ToString(), OUT_OF_RANGE_NEG, "nanoPrintf returns oor for double < -2^64+2");
Assert.Equal(DoubleToHex(doubleMin), DOUBLE_MIN_HEX,"Hex value to double min value does not match");
Assert.Equal(DoubleToHex(Convert.ToDouble(DOUBLE_MIN_VAL)), DOUBLE_MIN_HEX, "Parsing double min value does not return correct hex value");

// boundary: double zero
double doubleZero = 0; // test that zero gets a zero exponent and a value like 1023 the exponent bias used in floating point math
Assert.Equal(doubleZero.ToString(), "0", "ToString of a double with zero value formats incorrectly");
Assert.Equal(DoubleToHex(doubleZero), DOUBLE_ZERO_HEX, "Double with zero value returns the wrong hex value");

// boundary: double largest-in-range
double doubleInRange = Convert.ToDouble(DOUBLE_LARGEST_PRINT);
Assert.Equal(doubleInRange.ToString(), DOUBLE_LARGEST_PRINT, "Double.ToString did not return the correct value for largest in range value");

// boundary: double largest-out-of-range
double doubleOutRange = Convert.ToDouble(DOUBLE_LARGESTINVALID_PRINT);
Assert.Equal(doubleOutRange.ToString(), OUT_OF_RANGE, "Double.ToString did not return 'oor' for first out-of-range value");

// boundary: double smallest-in-range
double doubleInRangeNeg = Convert.ToDouble(DOUBLE_SMALLEST_PRINT);
Assert.Equal(doubleInRangeNeg.ToString(), DOUBLE_SMALLEST_PRINT, "Double.ToString did not return the correct value for smallest in range value");

// boundary: double smallest-out-of-range
double doubleOutRangeNeg = Convert.ToDouble(DOUBLE_SMALLESTINVALID_PRINT);
Assert.Equal(doubleOutRangeNeg.ToString(), OUT_OF_RANGE_NEG, "Double.ToString did not return 'oor' for smallest out-of-range value");

// boundary: float max
float floatMax = float.MaxValue;
Assert.Equal(floatMax.ToString(), OUT_OF_RANGE, "nanoPrintf return oor for float > 2^64-2");
Assert.Equal(FloatToHex(floatMax), FLOAT_MAX_HEX, "Hex value to float max values does not match");
Assert.Equal(FloatToHex((float)Convert.ToDouble(FLOAT_MAX_VAL)), FLOAT_MAX_HEX, "Parsing float max value does not return correct hex value");

// boundary: float min
float floatMin = float.MinValue;
Assert.Equal(floatMin.ToString(), OUT_OF_RANGE_NEG, "nanoPrintf returns oor for float < -2^64+2");
Assert.Equal(FloatToHex(floatMin), FLOAT_MIN_HEX, "Hex value to double min value does not match");
Assert.Equal(FloatToHex((float)Convert.ToDouble(FLOAT_MIN_VAL)), FLOAT_MIN_HEX, "Parsing float min value does not return correct hex value");

//boundary: float zero
float floatZero = 0; // test that zero gets a zero exponent and not a value like 1023 the exponent bias used in floating point math
Assert.Equal(floatZero.ToString(), "0", "ToString of a string with zero value formats incorrectly");
Assert.Equal(FloatToHex(floatZero), FLOAT_ZERO_HEX, "Float with zero value returns the wrong hex value");

// boundary: float largest-in-range
float floatInRange = (float)Convert.ToDouble(FLOAT_LARGEST_PRINT);
Assert.Equal(floatInRange.ToString(), FLOAT_LARGEST_PRINT, "Float.ToString did not return the correct value for largest in range value");

// boundary: float largest-out-of-range
float floatOutRange = (float)Convert.ToDouble(FLOAT_LARGESTINVALID_PRINT);
Assert.Equal(floatOutRange.ToString(), OUT_OF_RANGE, "Float.ToString did not return 'oor' for first out-of-range value");

// boundary: float smallest-in-range
float floatInRangeNeg = (float)Convert.ToDouble(FLOAT_SMALLEST_PRINT);
Assert.Equal(floatInRangeNeg.ToString(), FLOAT_SMALLEST_PRINT, "Float.ToString did not return the correct value for smallest in range value");

// boundary: float smallest-out-of-range
float floatOutRangeNeg = (float)Convert.ToDouble(FLOAT_SMALLESTINVALID_PRINT);
Assert.Equal(floatOutRangeNeg.ToString(), OUT_OF_RANGE_NEG, "Float.ToString did not return 'oor' for smallest out-of-range value");

long lMax = long.MaxValue;
numMax = lMax.ToString();
string numMax = lMax.ToString();
long lMin = long.MinValue;
numMin = lMin.ToString();
string numMin = lMin.ToString();

Assert.Equal(lMax, Convert.ToInt64(numMax));
Assert.Equal(lMin, Convert.ToInt64(numMin));
Assert.Equal(lMax, Convert.ToInt64(numMax), "Int64 Max");
Assert.Equal(lMin, Convert.ToInt64(numMin), "Int64 Min");

ulong ulMax = ulong.MaxValue;
numMax = ulMax.ToString();
ulong ulMin = ulong.MinValue;
numMin = ulMin.ToString();

Assert.Equal(ulMax, Convert.ToUInt64(numMax));
Assert.Equal(ulMin, Convert.ToUInt64(numMin));

Assert.Equal(ulMax, Convert.ToUInt64(numMax), "UInt64 Max");
Assert.Equal(ulMin, Convert.ToUInt64(numMin), "UInt64 Min");

long iMax = int.MaxValue;
numMax = iMax.ToString();
long iMin = int.MinValue;
numMin = iMin.ToString();

Assert.Equal(iMax, Convert.ToInt32(numMax));
Assert.Equal(iMin, Convert.ToInt32(numMin));
Assert.Equal(iMax, Convert.ToInt32(numMax), "Int32 Max");
Assert.Equal(iMin, Convert.ToInt32(numMin), "Int32 Min");

uint uiMax = uint.MaxValue;
numMax = uiMax.ToString();
Expand Down Expand Up @@ -270,6 +365,10 @@ public void Convert_BoundaryValues()
sbyte sbMin = sbyte.MinValue;
numMin = sbMin.ToString();

sbMax = Convert.ToSByte(numMax);

sbMin = Convert.ToSByte(numMin);

Assert.Equal(sbMax, Convert.ToSByte(numMax));
Assert.Equal(sbMin, Convert.ToSByte(numMin));

Expand All @@ -280,6 +379,7 @@ public void Convert_BoundaryValues()

Assert.Equal(bMax, Convert.ToByte(numMax));
Assert.Equal(bMin, Convert.ToByte(numMin));

}


Expand Down Expand Up @@ -369,5 +469,46 @@ public void Convert_FromBoolean()
Assert.Equals(convTrueIsOne, 1);
Assert.Equals(convFalseIsZero, 0);
}

#region Double/Floating point number helpers

/// <summary>
/// Converts the given double to a hexidecimal display - to be used to test boundary cases.
/// </summary>
/// <param name="d">The double to convert.</param>
/// <returns>"+Infinity", "-Infinity", "NaN" or "0x[16 hex bytes]"</return>
public static string DoubleToHex(double d)
{
if (double.IsPositiveInfinity(d))
return "+Infinity";
if (double.IsNegativeInfinity(d))
return "-Infinity";
if (double.IsNaN(d))
return "NaN";

string returnValue = string.Format("0x{0:X16}", BitConverter.DoubleToInt64Bits(d));
return returnValue;
}

/// <summary>
/// Converts the given double to a hexidecimal display - to be used to test boundary cases.
/// </summary>
/// <param name="f">The single (float) to convert.</param>
/// <returns>"+Infinity", "-Infinity", "NaN" or "0x[8 hex bytes]"</return>
public static string FloatToHex(float f)
{
if (float.IsPositiveInfinity(f))
return "+Infinity";
if (float.IsNegativeInfinity(f))
return "-Infinity";
if (float.IsNaN(f))
return "NaN";
string returnValue = string.Format("0x{0:X8}", BitConverter.ToInt32(BitConverter.GetBytes(f),0)); // CoreLibrary/mscorlib does not implement SingleToInt32Bits
return returnValue;
}


#endregion

}
}
2 changes: 1 addition & 1 deletion nanoFramework.CoreLibrary/System/Convert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ public static long ToInt64(string value, int fromBase = 10)
[CLSCompliant(false)]
public static ulong ToUInt64(string value, int fromBase = 10)
{
return (ulong)NativeToInt64(value.Trim(), true, 0, 0, fromBase);
return (ulong)NativeToInt64(value.Trim(), false, 0, 0, fromBase); // the interface use long for min/max, and uint64 is bigger. Setting min/max to 0/0 will cause the native code to calculate the largest value and return it as Int64 which when cast to UInt64 returns the larger numbers that a UInt64 can reach
}

/// <summary>
Expand Down

0 comments on commit 9869e58

Please sign in to comment.