Skip to content

Commit 4738eed

Browse files
authored
Fixed null references and added == and != overloads in Guid (#247)
***NO_CI***
1 parent 5b23ee9 commit 4738eed

File tree

4 files changed

+210
-14
lines changed

4 files changed

+210
-14
lines changed

Tests/NFUnitTestTypes/NFUnitTestTypes.nfproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
</PropertyGroup>
2525
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.props')" />
2626
<ItemGroup>
27+
<Compile Include="UnitTestGuid.cs" />
2728
<Compile Include="UnitTestObjectTypeTests.cs" />
2829
<Compile Include="UnitTestsSpanByte.cs" />
2930
<Compile Include="UnitTestSubTypeTests.cs" />
@@ -47,4 +48,4 @@
4748
<ProjectConfigurationsDeclaredAsItems />
4849
</ProjectCapabilities>
4950
</ProjectExtensions>
50-
</Project>
51+
</Project>
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using nanoFramework.TestFramework;
6+
7+
namespace NFUnitTestTypes
8+
{
9+
[TestClass]
10+
class UnitTestGuid
11+
{
12+
[TestMethod]
13+
public void Guid_Compare_To_Empty()
14+
{
15+
var empty = Guid.Empty;
16+
var notEmpty = Guid.NewGuid();
17+
Assert.IsFalse(empty == notEmpty);
18+
}
19+
20+
[TestMethod]
21+
public void Guid_Empty_IsAllZeros()
22+
{
23+
var empty = Guid.Empty;
24+
var bytes = empty.ToByteArray();
25+
foreach (var b in bytes)
26+
{
27+
Assert.AreEqual((byte)0, b);
28+
}
29+
}
30+
31+
[TestMethod]
32+
public void Guid_Constructor_ByteArray_RoundTrip()
33+
{
34+
var original = Guid.NewGuid();
35+
var bytes = original.ToByteArray();
36+
var roundTrip = new Guid(bytes);
37+
Assert.AreEqual(original, roundTrip);
38+
}
39+
40+
[TestMethod]
41+
public void Guid_Equals_And_Operator()
42+
{
43+
var g1 = Guid.NewGuid();
44+
var g2 = new Guid(g1.ToByteArray());
45+
Assert.IsTrue(g1.Equals(g2));
46+
Assert.IsTrue(g1 == g2);
47+
Assert.IsFalse(g1 != g2);
48+
}
49+
50+
[TestMethod]
51+
public void Guid_NotEquals_And_Operator()
52+
{
53+
var g1 = Guid.NewGuid();
54+
var g2 = Guid.NewGuid();
55+
Assert.IsFalse(g1.Equals(g2));
56+
Assert.IsFalse(g1 == g2);
57+
Assert.IsTrue(g1 != g2);
58+
}
59+
60+
[TestMethod]
61+
public void Guid_ToString_And_Parse()
62+
{
63+
var g1 = Guid.NewGuid();
64+
var str = g1.ToString();
65+
var g2 = new Guid(str);
66+
Assert.AreEqual(g1, g2);
67+
}
68+
69+
[TestMethod]
70+
public void Guid_GetHashCode_Consistent()
71+
{
72+
var g1 = Guid.NewGuid();
73+
var g2 = new Guid(g1.ToByteArray());
74+
Assert.AreEqual(g1.GetHashCode(), g2.GetHashCode());
75+
}
76+
77+
[TestMethod]
78+
public void Guid_CompareTo_Object_And_Self()
79+
{
80+
var g1 = Guid.NewGuid();
81+
var g2 = new Guid(g1.ToByteArray());
82+
Assert.AreEqual(0, g1.CompareTo(g2));
83+
Assert.AreEqual(0, g1.CompareTo((object)g2));
84+
Assert.AreEqual(1, g1.CompareTo(null));
85+
}
86+
87+
[TestMethod]
88+
public void Guid_CompareTo_InvalidType_Throws()
89+
{
90+
var g1 = Guid.NewGuid();
91+
Assert.ThrowsException(typeof(ArgumentException), () =>
92+
{
93+
g1.CompareTo("not a guid");
94+
});
95+
}
96+
97+
[TestMethod]
98+
public void Guid_TryParseGuidWithDashes_Valid()
99+
{
100+
var g1 = Guid.NewGuid();
101+
var str = g1.ToString();
102+
bool parsed = Guid.TryParseGuidWithDashes(str, out var g2);
103+
Assert.IsTrue(parsed);
104+
Assert.AreEqual(g1, g2);
105+
}
106+
107+
[TestMethod]
108+
public void Guid_TryParseGuidWithDashes_Invalid()
109+
{
110+
bool parsed = Guid.TryParseGuidWithDashes("invalid-guid", out var g2);
111+
Assert.IsFalse(parsed);
112+
Assert.AreEqual(Guid.Empty, g2);
113+
}
114+
115+
[TestMethod]
116+
public void Guid_Constructor_String_WithBraces()
117+
{
118+
var g1 = Guid.NewGuid();
119+
var str = "{" + g1.ToString() + "}";
120+
var g2 = new Guid(str);
121+
Assert.AreEqual(g1, g2);
122+
}
123+
124+
[TestMethod]
125+
public void Guid_Constructor_String_Invalid_Throws()
126+
{
127+
Assert.ThrowsException(typeof(ArgumentException), () =>
128+
{
129+
var g = new Guid("invalid-guid");
130+
});
131+
}
132+
133+
[TestMethod]
134+
public void Guid_GetHashCode_Empty()
135+
{
136+
var empty = Guid.Empty;
137+
Assert.AreEqual(0, empty.GetHashCode());
138+
}
139+
}
140+
}

nanoFramework.CoreLibrary/System/AssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;

nanoFramework.CoreLibrary/System/Guid.cs

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ public struct Guid
1818
/// </summary>
1919
public static readonly Guid Empty = new Guid(new byte[16]);
2020

21+
public Guid()
22+
{
23+
// All zeros
24+
_data = new int[4];
25+
}
26+
2127
/// <summary>
2228
/// Initializes a new instance of the <see cref="Guid"/> structure by using the specified integers and bytes.
2329
/// </summary>
@@ -163,26 +169,32 @@ public int CompareTo(object value)
163169
if (value == null)
164170
{
165171
return 1;
166-
}
167172

168-
#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
169-
if (value is not Guid)
173+
}
174+
if (value is not Guid other)
170175
{
171176
throw new ArgumentException();
172177
}
173-
#pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one
174178

175-
int[] other = ((Guid)value)._data;
176-
other ??= new int[4];
179+
return CompareTo(other);
180+
}
177181

182+
/// <summary>
183+
/// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
184+
/// </summary>
185+
/// <param name="other">An object to compare with this instance.</param>
186+
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
187+
public int CompareTo(Guid other)
188+
{
189+
_data ??= new int[4];
190+
other._data ??= new int[4];
178191
for (int i = 0; i < 4; i++)
179192
{
180-
if (_data[i] != other[i])
193+
if (_data[i] != other._data[i])
181194
{
182-
return _data[i] - other[i];
195+
return _data[i] - other._data[i];
183196
}
184197
}
185-
186198
return 0;
187199
}
188200

@@ -201,6 +213,9 @@ public int CompareTo(object value)
201213
/// </remarks>
202214
public byte[] ToByteArray()
203215
{
216+
// Initialize if null (treat as Empty)
217+
_data ??= new int[4];
218+
204219
byte[] buffer = new byte[16];
205220

206221
int index = 0;
@@ -289,10 +304,19 @@ public override bool Equals(object o)
289304
return false;
290305
}
291306

292-
int[] other = ((Guid)o)._data;
293-
other ??= new int[4];
307+
return o is Guid other && Equals(other);
308+
}
294309

295-
return (_data[0] == other[0]) && (_data[1] == other[1]) && (_data[2] == other[2]) && (_data[3] == other[3]);
310+
/// <summary>
311+
/// Indicates whether the current object is equal to another object of the same type.
312+
/// </summary>
313+
/// <param name="other">An object to compare with this object.</param>
314+
/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
315+
public bool Equals(Guid other)
316+
{
317+
_data ??= new int[4];
318+
other._data ??= new int[4];
319+
return (_data[0] == other._data[0]) && (_data[1] == other._data[1]) && (_data[2] == other._data[2]) && (_data[3] == other._data[3]);
296320
}
297321

298322
/// <summary>
@@ -301,6 +325,8 @@ public override bool Equals(object o)
301325
/// <returns>The hash code for this instance.</returns>
302326
public override int GetHashCode()
303327
{
328+
// Initialize if null (treat as Empty)
329+
_data ??= new int[4];
304330
return _data[0] ^ _data[1] ^ _data[2] ^ _data[3];
305331
}
306332

@@ -446,5 +472,34 @@ private static long HexStringToLong(string str, ref int parsePos, int requiredLe
446472
parsePos += requiredLength;
447473
return result;
448474
}
475+
476+
/// <summary>
477+
/// Determines whether two specified instances of <see cref="Guid"/> are equal.
478+
/// </summary>
479+
/// <param name="a">The first <see cref="Guid"/> to compare.</param>
480+
/// <param name="b">The second <see cref="Guid"/> to compare.</param>
481+
/// <returns><see langword="true"/> if <paramref name="a"/> equals <paramref name="b"/>; otherwise, <see langword="false"/>.</returns>
482+
public static bool operator ==(Guid a, Guid b)
483+
{
484+
// Defensive null handling, though _data should always be initialized
485+
a._data ??= new int[4];
486+
b._data ??= new int[4];
487+
488+
return (a._data[0] == b._data[0]) &&
489+
(a._data[1] == b._data[1]) &&
490+
(a._data[2] == b._data[2]) &&
491+
(a._data[3] == b._data[3]);
492+
}
493+
494+
/// <summary>
495+
/// Determines whether two specified instances of <see cref="Guid"/> are not equal.
496+
/// </summary>
497+
/// <param name="a">The first <see cref="Guid"/> to compare.</param>
498+
/// <param name="b">The second <see cref="Guid"/> to compare.</param>
499+
/// <returns><see langword="true"/> if <paramref name="a"/> does not equal <paramref name="b"/>; otherwise, <see langword="false"/>.</returns>
500+
public static bool operator !=(Guid a, Guid b)
501+
{
502+
return !(a == b);
503+
}
449504
}
450505
}

0 commit comments

Comments
 (0)