Skip to content

Commit 9869e58

Browse files
authored
Fixed unit tests NFUnitTestConversions (#141)
***NO_CI***
1 parent ae4b543 commit 9869e58

File tree

2 files changed

+182
-41
lines changed

2 files changed

+182
-41
lines changed

Tests/NFUnitTestConversions/UnitTestConvertTests.cs

Lines changed: 181 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ public void Cast_FloatingPoint()
5353
public void Convert_Positive()
5454
{
5555
string number = "12";
56-
int actualNumber = 12;
5756
SByte value_sb = Convert.ToSByte(number);
5857
Assert.Equal(value_sb, (byte)12);
5958
Byte value_b = Convert.ToByte(number);
@@ -76,7 +75,6 @@ public void Convert_Positive()
7675
public void Convert_PositivePlus()
7776
{
7877
string number = "+12";
79-
int actualNumber = 12;
8078
SByte value_sb = Convert.ToSByte(number);
8179
Assert.Equal(value_sb, (byte)12);
8280
Byte value_b = Convert.ToByte(number);
@@ -101,19 +99,18 @@ public void Convert_Negative()
10199
{
102100
string number = "-12";
103101
int actualNumber = -12;
104-
105102
SByte value_sb = Convert.ToSByte(number);
106-
Assert.Equal(value_sb, (sbyte)actualNumber);
107-
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { Byte value_b = Convert.ToByte(number); });
103+
Assert.Equal(value_sb, (sbyte)actualNumber, "Test1");
104+
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { Byte value_b = Convert.ToByte(number); }, "Test2");
108105
Int16 value_s16 = Convert.ToInt16(number);
109-
Assert.Equal(value_s16, (short)actualNumber);
110-
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { UInt16 value_u16 = Convert.ToUInt16(number); });
106+
Assert.Equal(value_s16, (short)actualNumber, "Test3");
107+
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { UInt16 value_u16 = Convert.ToUInt16(number); }, "Test4");
111108
Int32 value_s32 = Convert.ToInt32(number);
112-
Assert.Equal(value_s32, (int)actualNumber);
113-
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { UInt32 value_u32 = Convert.ToUInt32(number); });
109+
Assert.Equal(value_s32, (int)actualNumber, "Test5");
110+
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { UInt32 value_u32 = Convert.ToUInt32(number); }, "Test6");
114111
Int64 value_s64 = Convert.ToInt32(number);
115-
Assert.Equal(value_s64, (long)actualNumber);
116-
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { UInt64 value_u64 = Convert.ToUInt64(number); });
112+
Assert.Equal(value_s64, (long)actualNumber, "Test7");
113+
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { UInt64 value_u64 = Convert.ToUInt64(number); }, "Test8");
117114
}
118115

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

171168
double value_dd = Convert.ToDouble(number);
172169

173-
Assert.Equal(value_dd, actualNumber);
174-
number = "+0.00000000484874758559e-3";
175-
actualNumber = 4.84874758559e-12;
176-
Assert.Equal(actualNumber, Convert.ToDouble(number));
170+
Assert.Equal(value_dd, actualNumber, $"Convert.ToDouble should be {number}"); // this works if the numbers are fairly small - i.e. not e^x sized
171+
172+
// Examples of how we can get differences due to differences between the compiler parser, and the native parser
173+
// And differences caused by the native parsers "fast speed" causing rounding errors
174+
string num1 = "+0.000000004848747585e-3";
175+
string num2 = "4.848747585e-12"; // same number as above, but we've moved the digits over a bit and adjusted the exponent
176+
double dnum1Roslyn = +0.000000004848747585e-3; // Roslyn compiler will parse the value and put it into the double at compile time
177+
double dnum1Native = Convert.ToDouble(num1); // Native code will parse the value and put it into the double at run time
178+
double dnum2Roslyn = 4.848747585e-12; // Compiler
179+
double dnum2Native = Convert.ToDouble(num2); // Native
180+
181+
182+
// Now we will compare some of the ToString values
183+
// 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
184+
Assert.Equal(dnum1Native.ToString(), dnum2Native.ToString(), $"Comparing native parse tostring");
185+
// compare roslyn to roslyn - the roslyn parser is more accurate and that means the double is much more likely to be the same
186+
Assert.Equal(dnum1Roslyn.ToString(), dnum2Roslyn.ToString(), $"Comparing Roslyn parse tostring");
187+
// Now mix things up
188+
Assert.Equal(dnum1Roslyn.ToString(), dnum2Native.ToString(), $"Comparing Roslyn parse and native parse tostring");
189+
Assert.Equal(dnum1Native.ToString(), dnum2Roslyn.ToString(), $"Comparing Roslyn parse and native parse tostring");
190+
Assert.Equal(dnum1Roslyn.ToString(), dnum1Native.ToString(), $"Comparing Roslyn to native using {num1}");
191+
Assert.Equal(dnum2Roslyn.ToString(), dnum2Native.ToString(), $"Comparing Roslyn to natvie using {num2}");
177192
}
178193

179194
[TestMethod]
@@ -198,48 +213,128 @@ public void Convert_HexInt()
198213
}
199214

200215
[TestMethod]
216+
201217
public void Convert_BoundaryValues()
202218
{
203-
double valMax = double.MaxValue;
204-
string numMax = valMax.ToString();
205-
double valMin = double.MinValue;
206-
string numMin = valMin.ToString();
207-
208-
Assert.Equal(valMax, Convert.ToDouble(numMax));
209-
Assert.Equal(valMin, Convert.ToDouble(numMin));
210-
211-
valMax = float.MaxValue;
212-
numMax = valMax.ToString();
213-
valMin = float.MinValue;
214-
numMin = valMin.ToString();
215-
216-
Assert.Equal(valMax, Convert.ToDouble(numMax));
217-
Assert.Equal(valMin, Convert.ToDouble(numMin));
219+
//***
220+
//* Boundary tests - tests of the min and max values for double, float and int's.
221+
//* 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)
222+
// Boundary tests for double/float include the numbers that are around the edge of where out-of-range is produced.
223+
//***
224+
225+
const string OUT_OF_RANGE = "oor"; // nanoPrintf can only print up to 2^64-2 as a max value for double/floating
226+
const string OUT_OF_RANGE_NEG = "-oor"; // nanoPrintf can only print down to -2^64+2 as a min value for double/floating
227+
228+
const string DOUBLE_MAX_VAL = "1.7976931348623157E+308"; // will get 'oor' when printed
229+
const string DOUBLE_MAX_HEX = "0x7FEFFFFFFFFFFFFF"; // value from IEEE 574
230+
const string DOUBLE_MIN_VAL = "-1.7976931348623157E+308"; // will get '-oor' when printed
231+
const string DOUBLE_MIN_HEX = "0xFFEFFFFFFFFFFFFF"; // value from IEEE 574
232+
const string DOUBLE_ZERO_HEX = "0x0000000000000000";
233+
const string DOUBLE_LARGEST_PRINT = "1.84467440737095E+19"; // this is the largest number you can ask for ToString() and get a response
234+
const string DOUBLE_LARGESTINVALID_PRINT = "1.8446744073709552E+19"; // first positive value that will get the response 'oor' when printed
235+
const string DOUBLE_SMALLEST_PRINT = "-1.32585973029787E+19"; // this is the smallest number you can ask for ToString() and get a response
236+
const string DOUBLE_SMALLESTINVALID_PRINT = "-1.8446744073709552E+19"; // first negative value that will get the response '-oor' when printed
237+
238+
const string FLOAT_MAX_VAL = "3.40282347E+38";
239+
const string FLOAT_MAX_HEX = "0x7F7FFFFF"; // will get 'oor' when printed
240+
const string FLOAT_MIN_VAL = "-3.40282347E+38";
241+
const string FLOAT_MIN_HEX = "0xFF7FFFFF"; // will get '-oor' when printed
242+
const string FLOAT_ZERO_HEX = "0x00000000";
243+
const string FLOAT_LARGEST_PRINT = "1.844674E+19"; // this is the largest number you can ask for ToString() and get a response
244+
const string FLOAT_LARGESTINVALID_PRINT = "1.8446744E+19"; // first positive value that will get the response 'oor' when printed
245+
const string FLOAT_SMALLEST_PRINT = "-1.844674E+19"; // this is the smallest number you can ask for ToString() and get a response
246+
const string FLOAT_SMALLESTINVALID_PRINT = "-1.8446744E+19"; // first negative value that will get the response '-oor' when printed
247+
248+
// boundary: double max
249+
string time = DateTime.UtcNow.ToString("hh:mm:ss");
250+
double doubleMax = double.MaxValue;
251+
Assert.Equal(doubleMax.ToString(), OUT_OF_RANGE, "nanoPrintf returns oor for double > 2^64-2");
252+
Assert.Equal(DoubleToHex(doubleMax), DOUBLE_MAX_HEX, "Hex value to double max value does not match");
253+
Assert.Equal(DoubleToHex(Convert.ToDouble(DOUBLE_MAX_VAL)), DOUBLE_MAX_HEX, "Parsing double max value does not return correct hex value");
254+
255+
// boundary: double min
256+
double doubleMin = double.MinValue;
257+
Assert.Equal(doubleMin.ToString(), OUT_OF_RANGE_NEG, "nanoPrintf returns oor for double < -2^64+2");
258+
Assert.Equal(DoubleToHex(doubleMin), DOUBLE_MIN_HEX,"Hex value to double min value does not match");
259+
Assert.Equal(DoubleToHex(Convert.ToDouble(DOUBLE_MIN_VAL)), DOUBLE_MIN_HEX, "Parsing double min value does not return correct hex value");
260+
261+
// boundary: double zero
262+
double doubleZero = 0; // test that zero gets a zero exponent and a value like 1023 the exponent bias used in floating point math
263+
Assert.Equal(doubleZero.ToString(), "0", "ToString of a double with zero value formats incorrectly");
264+
Assert.Equal(DoubleToHex(doubleZero), DOUBLE_ZERO_HEX, "Double with zero value returns the wrong hex value");
265+
266+
// boundary: double largest-in-range
267+
double doubleInRange = Convert.ToDouble(DOUBLE_LARGEST_PRINT);
268+
Assert.Equal(doubleInRange.ToString(), DOUBLE_LARGEST_PRINT, "Double.ToString did not return the correct value for largest in range value");
269+
270+
// boundary: double largest-out-of-range
271+
double doubleOutRange = Convert.ToDouble(DOUBLE_LARGESTINVALID_PRINT);
272+
Assert.Equal(doubleOutRange.ToString(), OUT_OF_RANGE, "Double.ToString did not return 'oor' for first out-of-range value");
273+
274+
// boundary: double smallest-in-range
275+
double doubleInRangeNeg = Convert.ToDouble(DOUBLE_SMALLEST_PRINT);
276+
Assert.Equal(doubleInRangeNeg.ToString(), DOUBLE_SMALLEST_PRINT, "Double.ToString did not return the correct value for smallest in range value");
277+
278+
// boundary: double smallest-out-of-range
279+
double doubleOutRangeNeg = Convert.ToDouble(DOUBLE_SMALLESTINVALID_PRINT);
280+
Assert.Equal(doubleOutRangeNeg.ToString(), OUT_OF_RANGE_NEG, "Double.ToString did not return 'oor' for smallest out-of-range value");
281+
282+
// boundary: float max
283+
float floatMax = float.MaxValue;
284+
Assert.Equal(floatMax.ToString(), OUT_OF_RANGE, "nanoPrintf return oor for float > 2^64-2");
285+
Assert.Equal(FloatToHex(floatMax), FLOAT_MAX_HEX, "Hex value to float max values does not match");
286+
Assert.Equal(FloatToHex((float)Convert.ToDouble(FLOAT_MAX_VAL)), FLOAT_MAX_HEX, "Parsing float max value does not return correct hex value");
287+
288+
// boundary: float min
289+
float floatMin = float.MinValue;
290+
Assert.Equal(floatMin.ToString(), OUT_OF_RANGE_NEG, "nanoPrintf returns oor for float < -2^64+2");
291+
Assert.Equal(FloatToHex(floatMin), FLOAT_MIN_HEX, "Hex value to double min value does not match");
292+
Assert.Equal(FloatToHex((float)Convert.ToDouble(FLOAT_MIN_VAL)), FLOAT_MIN_HEX, "Parsing float min value does not return correct hex value");
293+
294+
//boundary: float zero
295+
float floatZero = 0; // test that zero gets a zero exponent and not a value like 1023 the exponent bias used in floating point math
296+
Assert.Equal(floatZero.ToString(), "0", "ToString of a string with zero value formats incorrectly");
297+
Assert.Equal(FloatToHex(floatZero), FLOAT_ZERO_HEX, "Float with zero value returns the wrong hex value");
298+
299+
// boundary: float largest-in-range
300+
float floatInRange = (float)Convert.ToDouble(FLOAT_LARGEST_PRINT);
301+
Assert.Equal(floatInRange.ToString(), FLOAT_LARGEST_PRINT, "Float.ToString did not return the correct value for largest in range value");
302+
303+
// boundary: float largest-out-of-range
304+
float floatOutRange = (float)Convert.ToDouble(FLOAT_LARGESTINVALID_PRINT);
305+
Assert.Equal(floatOutRange.ToString(), OUT_OF_RANGE, "Float.ToString did not return 'oor' for first out-of-range value");
306+
307+
// boundary: float smallest-in-range
308+
float floatInRangeNeg = (float)Convert.ToDouble(FLOAT_SMALLEST_PRINT);
309+
Assert.Equal(floatInRangeNeg.ToString(), FLOAT_SMALLEST_PRINT, "Float.ToString did not return the correct value for smallest in range value");
310+
311+
// boundary: float smallest-out-of-range
312+
float floatOutRangeNeg = (float)Convert.ToDouble(FLOAT_SMALLESTINVALID_PRINT);
313+
Assert.Equal(floatOutRangeNeg.ToString(), OUT_OF_RANGE_NEG, "Float.ToString did not return 'oor' for smallest out-of-range value");
218314

219315
long lMax = long.MaxValue;
220-
numMax = lMax.ToString();
316+
string numMax = lMax.ToString();
221317
long lMin = long.MinValue;
222-
numMin = lMin.ToString();
318+
string numMin = lMin.ToString();
223319

224-
Assert.Equal(lMax, Convert.ToInt64(numMax));
225-
Assert.Equal(lMin, Convert.ToInt64(numMin));
320+
Assert.Equal(lMax, Convert.ToInt64(numMax), "Int64 Max");
321+
Assert.Equal(lMin, Convert.ToInt64(numMin), "Int64 Min");
226322

227323
ulong ulMax = ulong.MaxValue;
228324
numMax = ulMax.ToString();
229325
ulong ulMin = ulong.MinValue;
230326
numMin = ulMin.ToString();
231327

232-
Assert.Equal(ulMax, Convert.ToUInt64(numMax));
233-
Assert.Equal(ulMin, Convert.ToUInt64(numMin));
234-
328+
Assert.Equal(ulMax, Convert.ToUInt64(numMax), "UInt64 Max");
329+
Assert.Equal(ulMin, Convert.ToUInt64(numMin), "UInt64 Min");
235330

236331
long iMax = int.MaxValue;
237332
numMax = iMax.ToString();
238333
long iMin = int.MinValue;
239334
numMin = iMin.ToString();
240335

241-
Assert.Equal(iMax, Convert.ToInt32(numMax));
242-
Assert.Equal(iMin, Convert.ToInt32(numMin));
336+
Assert.Equal(iMax, Convert.ToInt32(numMax), "Int32 Max");
337+
Assert.Equal(iMin, Convert.ToInt32(numMin), "Int32 Min");
243338

244339
uint uiMax = uint.MaxValue;
245340
numMax = uiMax.ToString();
@@ -270,6 +365,10 @@ public void Convert_BoundaryValues()
270365
sbyte sbMin = sbyte.MinValue;
271366
numMin = sbMin.ToString();
272367

368+
sbMax = Convert.ToSByte(numMax);
369+
370+
sbMin = Convert.ToSByte(numMin);
371+
273372
Assert.Equal(sbMax, Convert.ToSByte(numMax));
274373
Assert.Equal(sbMin, Convert.ToSByte(numMin));
275374

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

281380
Assert.Equal(bMax, Convert.ToByte(numMax));
282381
Assert.Equal(bMin, Convert.ToByte(numMin));
382+
283383
}
284384

285385

@@ -369,5 +469,46 @@ public void Convert_FromBoolean()
369469
Assert.Equals(convTrueIsOne, 1);
370470
Assert.Equals(convFalseIsZero, 0);
371471
}
472+
473+
#region Double/Floating point number helpers
474+
475+
/// <summary>
476+
/// Converts the given double to a hexidecimal display - to be used to test boundary cases.
477+
/// </summary>
478+
/// <param name="d">The double to convert.</param>
479+
/// <returns>"+Infinity", "-Infinity", "NaN" or "0x[16 hex bytes]"</return>
480+
public static string DoubleToHex(double d)
481+
{
482+
if (double.IsPositiveInfinity(d))
483+
return "+Infinity";
484+
if (double.IsNegativeInfinity(d))
485+
return "-Infinity";
486+
if (double.IsNaN(d))
487+
return "NaN";
488+
489+
string returnValue = string.Format("0x{0:X16}", BitConverter.DoubleToInt64Bits(d));
490+
return returnValue;
491+
}
492+
493+
/// <summary>
494+
/// Converts the given double to a hexidecimal display - to be used to test boundary cases.
495+
/// </summary>
496+
/// <param name="f">The single (float) to convert.</param>
497+
/// <returns>"+Infinity", "-Infinity", "NaN" or "0x[8 hex bytes]"</return>
498+
public static string FloatToHex(float f)
499+
{
500+
if (float.IsPositiveInfinity(f))
501+
return "+Infinity";
502+
if (float.IsNegativeInfinity(f))
503+
return "-Infinity";
504+
if (float.IsNaN(f))
505+
return "NaN";
506+
string returnValue = string.Format("0x{0:X8}", BitConverter.ToInt32(BitConverter.GetBytes(f),0)); // CoreLibrary/mscorlib does not implement SingleToInt32Bits
507+
return returnValue;
508+
}
509+
510+
511+
#endregion
512+
372513
}
373514
}

nanoFramework.CoreLibrary/System/Convert.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ public static long ToInt64(string value, int fromBase = 10)
205205
[CLSCompliant(false)]
206206
public static ulong ToUInt64(string value, int fromBase = 10)
207207
{
208-
return (ulong)NativeToInt64(value.Trim(), true, 0, 0, fromBase);
208+
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
209209
}
210210

211211
/// <summary>

0 commit comments

Comments
 (0)