THRIFT-5511 Full support for the new net6 "nullability" semantics
Client: netstd
Patch: Jens Geyer
This closes #2516
diff --git a/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Impl/Thrift5253/MyService.cs b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Impl/Thrift5253/MyService.cs
index 660b2b7..f423376 100644
--- a/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Impl/Thrift5253/MyService.cs
+++ b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Impl/Thrift5253/MyService.cs
@@ -26,30 +26,30 @@
{
class MyServiceImpl : MyService.IAsync
{
- public Task<AsyncProcessor> AsyncProcessor_(AsyncProcessor input, CancellationToken cancellationToken = default)
+ public Task<AsyncProcessor> AsyncProcessor_(AsyncProcessor? input, CancellationToken cancellationToken = default)
{
- return Task.FromResult(new AsyncProcessor() { Foo = input.Foo });
+ return Task.FromResult(new AsyncProcessor() { Foo = input?.Foo ?? 0 });
}
- public Task<BrokenResult> Broken(BrokenArgs input, CancellationToken cancellationToken = default)
+ public Task<BrokenResult> Broken(BrokenArgs? input, CancellationToken cancellationToken = default)
{
- return Task.FromResult(new BrokenResult() { Foo = input.Foo });
+ return Task.FromResult(new BrokenResult() { Foo = input?.Foo ?? 0 });
}
- public Task<Client> Client_(Client input, CancellationToken cancellationToken = default)
+ public Task<Client> Client_(Client? input, CancellationToken cancellationToken = default)
{
_ = cancellationToken;
- return Task.FromResult(new Client() { Foo = input.Foo });
+ return Task.FromResult(new Client() { Foo = input?.Foo ?? 0 });
}
- public Task<IAsync> IAsync_(IAsync input, CancellationToken cancellationToken = default)
+ public Task<IAsync> IAsync_(IAsync? input, CancellationToken cancellationToken = default)
{
- return Task.FromResult(new IAsync() { Foo = input.Foo });
+ return Task.FromResult(new IAsync() { Foo = input?.Foo ?? 0 });
}
- public Task<InternalStructs> InternalStructs_(InternalStructs input, CancellationToken cancellationToken = default)
+ public Task<InternalStructs> InternalStructs_(InternalStructs? input, CancellationToken cancellationToken = default)
{
- return Task.FromResult(new InternalStructs() { Foo = input.Foo });
+ return Task.FromResult(new InternalStructs() { Foo = input?.Foo ?? 0 });
}
public Task TestAsync(CancellationToken cancellationToken = default)
@@ -62,9 +62,9 @@
return Task.CompletedTask;
}
- public Task<WorksRslt> Works(WorksArrrgs input, CancellationToken cancellationToken = default)
+ public Task<WorksRslt> Works(WorksArrrgs? input, CancellationToken cancellationToken = default)
{
- return Task.FromResult(new WorksRslt() { Foo = input.Foo });
+ return Task.FromResult(new WorksRslt() { Foo = input?.Foo ?? 0 });
}
}
}
diff --git a/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj
index 176e734..42a139c 100644
--- a/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj
+++ b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj
@@ -68,14 +68,14 @@
<Error Condition="$('$(ThriftBinaryVersion)'::StartsWith('$(ThriftVersionOutput)')) == true" Text="Thrift version returned: '$(ThriftBinaryVersion)' is not equal to the projects version '$(ThriftVersionOutput)'." />
<Message Importance="high" Text="Generating tests with thrift binary: '$(PathToThrift)'" />
<!-- Generate the thrift test files -->
- <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial -r ./CassandraTest.thrift" />
- <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial -r ./optional_required_default.thrift" />
- <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial -r ./name_conflicts.thrift" />
- <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial -r ./../../../../test/ThriftTest.thrift" />
- <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial -r ./../../../../contrib/fb303/if/fb303.thrift" />
- <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial -r ./Thrift5253.thrift" />
- <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial -r ./Thrift5320.thrift" />
- <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial -r ./Thrift5382.thrift" />
+ <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ./CassandraTest.thrift" />
+ <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ./optional_required_default.thrift" />
+ <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ./name_conflicts.thrift" />
+ <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ./../../../../test/ThriftTest.thrift" />
+ <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ./../../../../contrib/fb303/if/fb303.thrift" />
+ <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ./Thrift5253.thrift" />
+ <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ./Thrift5320.thrift" />
+ <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ./Thrift5382.thrift" />
</Target>
</Project>
diff --git a/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs b/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs
index 778d24c..49108d1 100644
--- a/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs
+++ b/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs
@@ -83,8 +83,8 @@
[TestMethod]
public void TCollection_Set_Equals_Primitive_Test()
{
- var collection1 = new THashSet<int> {1,2,3};
- var collection2 = new THashSet<int> {1,2,3};
+ var collection1 = new HashSet<int> {1,2,3};
+ var collection2 = new HashSet<int> {1,2,3};
Assert.IsTrue(TCollections.Equals(collection1, collection2));
Assert.IsTrue(collection1.SequenceEqual(collection2));
}
@@ -92,8 +92,8 @@
[TestMethod]
public void TCollection_Set_Equals_Primitive_Different_Test()
{
- var collection1 = new THashSet<int> { 1, 2, 3 };
- var collection2 = new THashSet<int> { 1, 2 };
+ var collection1 = new HashSet<int> { 1, 2, 3 };
+ var collection2 = new HashSet<int> { 1, 2 };
Assert.IsFalse(TCollections.Equals(collection1, collection2));
Assert.IsFalse(collection1.SequenceEqual(collection2));
@@ -105,8 +105,8 @@
[TestMethod]
public void TCollection_Set_Equals_Objects_Test()
{
- var collection1 = new THashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
- var collection2 = new THashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
+ var collection1 = new HashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
+ var collection2 = new HashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
Assert.IsTrue(TCollections.Equals(collection1, collection2));
Assert.IsTrue(collection1.SequenceEqual(collection2));
}
@@ -114,8 +114,8 @@
[TestMethod]
public void TCollection_Set_Set_Equals_Objects_Test()
{
- var collection1 = new THashSet<THashSet<ExampleClass>> { new THashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } } };
- var collection2 = new THashSet<THashSet<ExampleClass>> { new THashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } } };
+ var collection1 = new HashSet<HashSet<ExampleClass>> { new HashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } } };
+ var collection2 = new HashSet<HashSet<ExampleClass>> { new HashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } } };
Assert.IsTrue(TCollections.Equals(collection1, collection2));
Assert.IsFalse(collection1.SequenceEqual(collection2)); // SequenceEqual() calls Equals() of the inner list instead of SequenceEqual()
}
@@ -123,7 +123,7 @@
[TestMethod]
public void TCollection_Set_Equals_OneAndTheSameObject_Test()
{
- var collection1 = new THashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
+ var collection1 = new HashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
var collection2 = collection1; // references to one and the same collection
Assert.IsTrue(TCollections.Equals(collection1, collection2));
Assert.IsTrue(collection1.SequenceEqual(collection2));
diff --git a/lib/netstd/Tests/Thrift.Tests/Collections/THashSetTests.cs b/lib/netstd/Tests/Thrift.Tests/Collections/THashSetTests.cs
index 8de573e..73921ea 100644
--- a/lib/netstd/Tests/Thrift.Tests/Collections/THashSetTests.cs
+++ b/lib/netstd/Tests/Thrift.Tests/Collections/THashSetTests.cs
@@ -1,4 +1,4 @@
-// Licensed to the Apache Software Foundation(ASF) under one
+// Licensed to the Apache Software Foundation(ASF) under one
// or more contributor license agreements.See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.The ASF licenses this file
@@ -22,6 +22,8 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Thrift.Collections;
+#pragma warning disable IDE0063 // simplify using
+
namespace Thrift.Tests.Collections
{
// ReSharper disable once InconsistentNaming
@@ -33,7 +35,7 @@
{
const int value = 1;
- var hashSet = new THashSet<int> {value};
+ var hashSet = new HashSet<int> {value};
Assert.IsTrue(hashSet.Contains(value));
diff --git a/lib/netstd/Tests/Thrift.Tests/DataModel/DeepCopy.cs b/lib/netstd/Tests/Thrift.Tests/DataModel/DeepCopy.cs
index 84fcab8..afffed5 100644
--- a/lib/netstd/Tests/Thrift.Tests/DataModel/DeepCopy.cs
+++ b/lib/netstd/Tests/Thrift.Tests/DataModel/DeepCopy.cs
@@ -24,8 +24,6 @@
using OptReqDefTest;
using Thrift.Collections;
-#nullable disable // this is just test code, we leave it at that
-
namespace Thrift.Tests.DataModel
{
// ReSharper disable once InconsistentNaming
@@ -51,7 +49,7 @@
VerifyIdenticalContent(first, InitializeInstance(new RaceDetails()));
}
- private RaceDetails MakeNestedRaceDetails(int nesting)
+ private RaceDetails? MakeNestedRaceDetails(int nesting)
{
if (++nesting > 1)
return null;
@@ -61,7 +59,7 @@
return instance;
}
- private jack MakeNestedUnion(int nesting)
+ private jack? MakeNestedUnion(int nesting)
{
if (++nesting > 1)
return null;
@@ -88,11 +86,11 @@
instance.Req_one = default;
instance.Req_two = default;
instance.Req_three = default;
- instance.Req_four = default;
- instance.Req_five = default;
+ Assert.IsNotNull(instance.Req_four);
+ Assert.IsNotNull(instance.Req_five);
instance.Req_six = default;
instance.Req_seven = default;;
- instance.Req_eight = default;
+ instance.Req_eight = default;
// leave non-required fields unset again
Assert.IsFalse(instance.__isset.def_one);
@@ -119,15 +117,15 @@
Assert.AreEqual(instance.Req_two_with_value, 2.22);
Assert.AreEqual(instance.Req_three_with_value, 3);
Assert.AreEqual(instance.Req_four_with_value, "four");
- Assert.AreEqual("five", Encoding.UTF8.GetString(instance.Req_five_with_value));
+ Assert.AreEqual("five", Encoding.UTF8.GetString(instance.Req_five_with_value!));
- Assert.IsTrue(instance.Req_six_with_value.Count == 1);
+ Assert.IsTrue(instance.Req_six_with_value!.Count == 1);
Assert.AreEqual(instance.Req_six_with_value[0], 6 );
- Assert.IsTrue(instance.Req_seven_with_value.Count == 1);
+ Assert.IsTrue(instance.Req_seven_with_value!.Count == 1);
Assert.IsTrue(instance.Req_seven_with_value.Contains(7));
- Assert.IsTrue(instance.Req_eight_with_value.Count == 1);
+ Assert.IsTrue(instance.Req_eight_with_value!.Count == 1);
Assert.IsTrue(instance.Req_eight_with_value[8] == 8);
Assert.IsTrue(instance.__isset.def_one_with_value);
@@ -144,12 +142,16 @@
if (nesting < 2)
{
instance.Far_list = new List<Distance>() { Distance.foo, Distance.bar, Distance.baz };
- instance.Far_set = new THashSet<Distance>() { Distance.foo, Distance.bar, Distance.baz };
+ instance.Far_set = new HashSet<Distance>() { Distance.foo, Distance.bar, Distance.baz };
instance.Far_map = new Dictionary<Distance, Distance>() { [Distance.foo] = Distance.foo, [Distance.bar] = Distance.bar, [Distance.baz] = Distance.baz };
- instance.Far_set_list = new THashSet<List<Distance>>() { new List<Distance>() { Distance.foo } };
- instance.Far_list_map_set = new List<Dictionary<sbyte, THashSet<Distance>>>() { new Dictionary<sbyte, THashSet<Distance>>() { [1] = new THashSet<Distance>() { Distance.baz } } };
- instance.Far_map_dist_to_rds = new Dictionary<Distance, List<RaceDetails>>() { [Distance.bar] = new List<RaceDetails>() { MakeNestedRaceDetails(nesting) } };
+ instance.Far_set_list = new HashSet<List<Distance>>() { new List<Distance>() { Distance.foo } };
+ instance.Far_list_map_set = new List<Dictionary<sbyte, HashSet<Distance>>>() { new Dictionary<sbyte, HashSet<Distance>>() { [1] = new HashSet<Distance>() { Distance.baz } } };
+
+ instance.Far_map_dist_to_rds = new Dictionary<Distance, List<RaceDetails>>() { [Distance.bar] = new List<RaceDetails>() };
+ var details = MakeNestedRaceDetails(nesting);
+ if (details != null)
+ instance.Far_map_dist_to_rds[Distance.bar].Add(details);
instance.Req_nested = MakeNestedRaceDetails(nesting);
Assert.IsFalse(instance.__isset.opt_nested);
@@ -245,19 +247,19 @@
instance.Triplesix = ModifyValue(instance.Triplesix);
}
- private jack ModifyValue(jack value, int level)
+ private jack? ModifyValue(jack? value, int level)
{
if (++level > 4)
return value;
if (value == null)
value = MakeNestedUnion(0);
- Debug.Assert(value.As_nested_struct != null);
+ Debug.Assert(value?.As_nested_struct != null);
ModifyInstance(value.As_nested_struct, level);
return value;
}
- private RaceDetails ModifyValue(RaceDetails value, int level)
+ private RaceDetails? ModifyValue(RaceDetails? value, int level)
{
if (++level > 4)
return value;
@@ -268,7 +270,7 @@
return value;
}
- private Dictionary<Distance, List<RaceDetails>> ModifyValue(Dictionary<Distance, List<RaceDetails>> value, int level)
+ private Dictionary<Distance, List<RaceDetails>> ModifyValue(Dictionary<Distance, List<RaceDetails>>? value, int level)
{
if (value == null)
value = new Dictionary<Distance, List<RaceDetails>>();
@@ -283,29 +285,30 @@
if (value.TryGetValue(Distance.bar, out var list) && (list.Count > 0))
{
ModifyInstance(list[0], level);
- list.Add(null);
+ //list.Add(null); -- Thrift does not allow null values in containers
}
- value[Distance.baz] = null;
+ // Thrift does not allow null values in containers
+ //value[Distance.baz] = null;
return value;
}
- private static List<Dictionary<sbyte, THashSet<Distance>>> ModifyValue(List<Dictionary<sbyte, THashSet<Distance>>> value)
+ private static List<Dictionary<sbyte, HashSet<Distance>>> ModifyValue(List<Dictionary<sbyte, HashSet<Distance>>>? value)
{
if (value == null)
- value = new List<Dictionary<sbyte, THashSet<Distance>>>();
+ value = new List<Dictionary<sbyte, HashSet<Distance>>>();
if (value.Count == 0)
- value.Add(new Dictionary<sbyte, THashSet<Distance>>());
- else
- value.Add(null);
+ value.Add(new Dictionary<sbyte, HashSet<Distance>>());
+ //else
+ //value.Add(null); --Thrift does not allow null values in containers
sbyte key = (sbyte)(value[0].Count + 10);
if (value[0].Count == 0)
- value[0].Add(key, new THashSet<Distance>());
- else
- value[0].Add(key, null);
+ value[0].Add(key, new HashSet<Distance>());
+ //else
+ //value[0].Add(key, null); --Thrift does not allow null values in containers
foreach (var entry in value)
{
@@ -327,15 +330,15 @@
return value;
}
- private static THashSet<List<Distance>> ModifyValue(THashSet<List<Distance>> value)
+ private static HashSet<List<Distance>> ModifyValue(HashSet<List<Distance>>? value)
{
if (value == null)
- value = new THashSet<List<Distance>>();
+ value = new HashSet<List<Distance>>();
if (value.Count == 0)
value.Add(new List<Distance>());
- else
- value.Add(null);
+ //else
+ //value.Add(null); -- Thrift does not allow null values in containers
foreach (var entry in value)
if( entry != null)
@@ -344,7 +347,7 @@
return value;
}
- private static Dictionary<Distance, Distance> ModifyValue(Dictionary<Distance, Distance> value)
+ private static Dictionary<Distance, Distance> ModifyValue(Dictionary<Distance, Distance>? value)
{
if (value == null)
value = new Dictionary<Distance, Distance>();
@@ -354,10 +357,10 @@
return value;
}
- private static THashSet<Distance> ModifyValue(THashSet<Distance> value)
+ private static HashSet<Distance> ModifyValue(HashSet<Distance>? value)
{
if (value == null)
- value = new THashSet<Distance>();
+ value = new HashSet<Distance>();
if (value.Contains(Distance.foo))
value.Remove(Distance.foo);
@@ -377,7 +380,7 @@
return value;
}
- private static List<Distance> ModifyValue(List<Distance> value)
+ private static List<Distance> ModifyValue(List<Distance>? value)
{
if (value == null)
value = new List<Distance>();
@@ -392,7 +395,7 @@
return !value;
}
- private static Dictionary<sbyte, short> ModifyValue(Dictionary<sbyte, short> value)
+ private static Dictionary<sbyte, short> ModifyValue(Dictionary<sbyte, short>? value)
{
if (value == null)
value = new Dictionary<sbyte, short>();
@@ -400,15 +403,15 @@
return value;
}
- private static THashSet<long> ModifyValue(THashSet<long> value)
+ private static HashSet<long> ModifyValue(HashSet<long>? value)
{
if (value == null)
- value = new THashSet<long>();
+ value = new HashSet<long>();
value.Add(value.Count+100);
return value;
}
- private static List<int> ModifyValue(List<int> value)
+ private static List<int> ModifyValue(List<int>? value)
{
if (value == null)
value = new List<int>();
@@ -416,16 +419,18 @@
return value;
}
- private static byte[] ModifyValue(byte[] value)
+ private static byte[] ModifyValue(byte[]? value)
{
if (value == null)
value = new byte[1] { 0 };
if (value.Length > 0)
value[0] = (value[0] < 0xFF) ? ++value[0] : (byte)0;
+ else
+ value = new byte[1] { 0 };
return value;
}
- private static string ModifyValue(string value)
+ private static string ModifyValue(string? value)
{
return value + "1";
}
diff --git a/lib/netstd/Tests/Thrift.Tests/DataModel/NullValuesSet.cs b/lib/netstd/Tests/Thrift.Tests/DataModel/NullValuesSet.cs
index 693b68e..ebc1717 100644
--- a/lib/netstd/Tests/Thrift.Tests/DataModel/NullValuesSet.cs
+++ b/lib/netstd/Tests/Thrift.Tests/DataModel/NullValuesSet.cs
@@ -25,13 +25,15 @@
using OptReqDefTest;
using Thrift.Collections;
+#pragma warning disable IDE0017 // init can be simplified - we don't want that here
+
namespace Thrift.Tests.DataModel
{
// ReSharper disable once InconsistentNaming
[TestClass]
public class Thrift_5238
{
- private void CheckInstance(RaceDetails instance)
+ private static void CheckInstance(RaceDetails instance)
{
// object
Assert.IsTrue(instance.__isset.def_nested);
@@ -42,14 +44,14 @@
// string
Assert.IsTrue(instance.__isset.def_four);
Assert.IsTrue(instance.__isset.opt_four);
- Assert.IsNull(instance.Req_four);
+ Assert.IsTrue(string.IsNullOrEmpty(instance.Req_four));
Assert.IsNull(instance.Def_four);
Assert.IsNull(instance.Opt_four);
// byte[]
Assert.IsTrue(instance.__isset.def_five);
Assert.IsTrue(instance.__isset.opt_five);
- Assert.IsNull(instance.Req_five);
+ Assert.IsTrue((instance.Req_five == null) || (instance.Req_five.Length == 0));
Assert.IsNull(instance.Def_five);
Assert.IsNull(instance.Opt_five);
@@ -66,6 +68,9 @@
{
var instance = new OptReqDefTest.RaceDetails();
+ // the following code INTENTIONALLY assigns null to non.nullable reftypes
+ #pragma warning disable CS8625
+
// object
instance.Def_nested = null;
instance.Opt_nested = null;
@@ -85,6 +90,9 @@
instance.Opt_six = null;
instance.Def_six = null;
+ // back to normal
+ #pragma warning restore CS8625
+
// test the setup
CheckInstance(instance);