diff --git a/src/System.Windows.Forms.Analyzers.CSharp/src/System.Windows.Forms.Analyzers.CSharp.csproj b/src/System.Windows.Forms.Analyzers.CSharp/src/System.Windows.Forms.Analyzers.CSharp.csproj index 52062fff2be..ce1811e1bb7 100644 --- a/src/System.Windows.Forms.Analyzers.CSharp/src/System.Windows.Forms.Analyzers.CSharp.csproj +++ b/src/System.Windows.Forms.Analyzers.CSharp/src/System.Windows.Forms.Analyzers.CSharp.csproj @@ -1,7 +1,7 @@  - System.Windows.Forms.Analyzers + netstandard2.0 Preview enable diff --git a/src/System.Windows.Forms.Analyzers.CodeFixes.CSharp/System.Windows.Forms.Analyzers.CodeFixes.CSharp.csproj b/src/System.Windows.Forms.Analyzers.CodeFixes.CSharp/System.Windows.Forms.Analyzers.CodeFixes.CSharp.csproj index a3eedd84c04..1c578e55e2f 100644 --- a/src/System.Windows.Forms.Analyzers.CodeFixes.CSharp/System.Windows.Forms.Analyzers.CodeFixes.CSharp.csproj +++ b/src/System.Windows.Forms.Analyzers.CodeFixes.CSharp/System.Windows.Forms.Analyzers.CodeFixes.CSharp.csproj @@ -1,7 +1,7 @@  - System.Windows.Forms.Analyzers + netstandard2.0 Preview enable diff --git a/src/System.Windows.Forms.Analyzers.CodeFixes.VisualBasic/System.Windows.Forms.Analyzers.CodeFixes.VisualBasic.vbproj b/src/System.Windows.Forms.Analyzers.CodeFixes.VisualBasic/System.Windows.Forms.Analyzers.CodeFixes.VisualBasic.vbproj index c1505240608..ae6fffd5577 100644 --- a/src/System.Windows.Forms.Analyzers.CodeFixes.VisualBasic/System.Windows.Forms.Analyzers.CodeFixes.VisualBasic.vbproj +++ b/src/System.Windows.Forms.Analyzers.CodeFixes.VisualBasic/System.Windows.Forms.Analyzers.CodeFixes.VisualBasic.vbproj @@ -1,7 +1,7 @@  - System.Windows.Forms.Analyzers + netstandard2.0 true diff --git a/src/System.Windows.Forms.Analyzers.VisualBasic/src/System.Windows.Forms.Analyzers.VisualBasic.vbproj b/src/System.Windows.Forms.Analyzers.VisualBasic/src/System.Windows.Forms.Analyzers.VisualBasic.vbproj index 90c14ba490d..78049c1f249 100644 --- a/src/System.Windows.Forms.Analyzers.VisualBasic/src/System.Windows.Forms.Analyzers.VisualBasic.vbproj +++ b/src/System.Windows.Forms.Analyzers.VisualBasic/src/System.Windows.Forms.Analyzers.VisualBasic.vbproj @@ -1,7 +1,7 @@  - System.Windows.Forms.Analyzers + netstandard2.0 true diff --git a/src/System.Windows.Forms.Analyzers/src/System.Windows.Forms.Analyzers.csproj b/src/System.Windows.Forms.Analyzers/src/System.Windows.Forms.Analyzers.csproj index a953a9164e8..e463bcc2bb3 100644 --- a/src/System.Windows.Forms.Analyzers/src/System.Windows.Forms.Analyzers.csproj +++ b/src/System.Windows.Forms.Analyzers/src/System.Windows.Forms.Analyzers.csproj @@ -2,6 +2,7 @@ netstandard2.0 + Preview enable true diff --git a/src/System.Windows.Forms/tests/TestUtilities/DataObjectTestHelpers.cs b/src/System.Windows.Forms/tests/TestUtilities/Data/DataObjectTestHelpers.cs similarity index 100% rename from src/System.Windows.Forms/tests/TestUtilities/DataObjectTestHelpers.cs rename to src/System.Windows.Forms/tests/TestUtilities/Data/DataObjectTestHelpers.cs diff --git a/src/System.Windows.Forms/tests/TestUtilities/Data/ManagedAndRuntimeDataObject.cs b/src/System.Windows.Forms/tests/TestUtilities/Data/ManagedAndRuntimeDataObject.cs new file mode 100644 index 00000000000..a235b4b5c0a --- /dev/null +++ b/src/System.Windows.Forms/tests/TestUtilities/Data/ManagedAndRuntimeDataObject.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace System.Windows.Forms.TestUtilities; + +internal class ManagedAndRuntimeDataObject : ManagedDataObject, ComTypes.IDataObject +{ + public int DAdvise(ref ComTypes.FORMATETC pFormatetc, ComTypes.ADVF advf, ComTypes.IAdviseSink adviseSink, out int connection) => throw new NotImplementedException(); + public void DUnadvise(int connection) => throw new NotImplementedException(); + public int EnumDAdvise(out ComTypes.IEnumSTATDATA enumAdvise) => throw new NotImplementedException(); + public ComTypes.IEnumFORMATETC EnumFormatEtc(ComTypes.DATADIR direction) => throw new NotImplementedException(); + public int GetCanonicalFormatEtc(ref ComTypes.FORMATETC formatIn, out ComTypes.FORMATETC formatOut) => throw new NotImplementedException(); + public void GetData(ref ComTypes.FORMATETC format, out ComTypes.STGMEDIUM medium) => throw new NotImplementedException(); + public void GetDataHere(ref ComTypes.FORMATETC format, ref ComTypes.STGMEDIUM medium) => throw new NotImplementedException(); + public int QueryGetData(ref ComTypes.FORMATETC format) => throw new NotImplementedException(); + public void SetData(ref ComTypes.FORMATETC formatIn, ref ComTypes.STGMEDIUM medium, bool release) => throw new NotImplementedException(); +} diff --git a/src/System.Windows.Forms/tests/TestUtilities/Data/ManagedDataObject.cs b/src/System.Windows.Forms/tests/TestUtilities/Data/ManagedDataObject.cs new file mode 100644 index 00000000000..e0bbc772e9f --- /dev/null +++ b/src/System.Windows.Forms/tests/TestUtilities/Data/ManagedDataObject.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +namespace System.Windows.Forms.TestUtilities; + +internal class ManagedDataObject : IDataObject +{ + public static string s_format = nameof(SerializableTestData); + protected SerializableTestData? _data; + + public object? GetData(string format, bool autoConvert) => format == s_format ? _data : null; + public object? GetData(string format) => format == s_format ? _data : null; + public object? GetData(Type format) => null; + public bool GetDataPresent(string format, bool autoConvert) => format == s_format && _data is not null; + public bool GetDataPresent(string format) => format == s_format && _data is not null; + public bool GetDataPresent(Type format) => false; + public string[] GetFormats(bool autoConvert) => [s_format]; + public string[] GetFormats() => [s_format]; + public void SetData(string format, bool autoConvert, object? data) + { + if (format == s_format) + { + _data = data as SerializableTestData; + } + } + + public void SetData(string format, object? data) + { + if (format == s_format) + { + _data = data as SerializableTestData; + } + } + + public void SetData(Type format, object? data) => _data = data as SerializableTestData; + public void SetData(object? data) => _data = data as SerializableTestData; +} diff --git a/src/System.Windows.Forms/tests/TestUtilities/Data/SerializableTestData.cs b/src/System.Windows.Forms/tests/TestUtilities/Data/SerializableTestData.cs new file mode 100644 index 00000000000..c91764ad72c --- /dev/null +++ b/src/System.Windows.Forms/tests/TestUtilities/Data/SerializableTestData.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +namespace System.Windows.Forms.TestUtilities; + +[Serializable] +public class SerializableTestData +{ + public string Text { get; } = "a"; +} diff --git a/src/System.Windows.Forms/tests/TestUtilities/Data/TypedAndRuntimeDataObject.cs b/src/System.Windows.Forms/tests/TestUtilities/Data/TypedAndRuntimeDataObject.cs new file mode 100644 index 00000000000..41bb04e15ec --- /dev/null +++ b/src/System.Windows.Forms/tests/TestUtilities/Data/TypedAndRuntimeDataObject.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Metadata; + +namespace System.Windows.Forms.TestUtilities; + +internal class TypedAndRuntimeDataObject : ManagedAndRuntimeDataObject, ITypedDataObject +{ + public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>([MaybeNullWhen(false), NotNullWhen(true)] out T data) => + throw new NotImplementedException(); + public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(string format, [MaybeNullWhen(false), NotNullWhen(true)] out T data) + { + data = default; + if (format == s_format && _data is T t) + { + data = t; + return true; + } + + return false; + } + + public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(string format, bool autoConvert, [MaybeNullWhen(false), NotNullWhen(true)] out T data) => + throw new NotImplementedException(); + public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(string format, Func resolver, bool autoConvert, [MaybeNullWhen(false), NotNullWhen(true)] out T data) => + throw new NotImplementedException(); +} diff --git a/src/System.Windows.Forms/tests/TestUtilities/Data/TypedDataObject.cs b/src/System.Windows.Forms/tests/TestUtilities/Data/TypedDataObject.cs new file mode 100644 index 00000000000..183ff7e36f8 --- /dev/null +++ b/src/System.Windows.Forms/tests/TestUtilities/Data/TypedDataObject.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Metadata; + +namespace System.Windows.Forms.TestUtilities; + +internal class TypedDataObject : ManagedDataObject, ITypedDataObject +{ + public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>([MaybeNullWhen(false), NotNullWhen(true)] out T data) => + throw new NotImplementedException(); + public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(string format, [MaybeNullWhen(false), NotNullWhen(true)] out T data) + { + data = default; + if (format == s_format && _data is T t) + { + data = t; + return true; + } + + return false; + } + + public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(string format, bool autoConvert, [MaybeNullWhen(false), NotNullWhen(true)] out T data) => + throw new NotImplementedException(); + public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(string format, Func resolver, bool autoConvert, [MaybeNullWhen(false), NotNullWhen(true)] out T data) => + throw new NotImplementedException(); +} diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ClipboardTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ClipboardTests.cs index f03e070d94e..c6f643a48f3 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ClipboardTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ClipboardTests.cs @@ -12,6 +12,7 @@ using System.Runtime.InteropServices; using System.Text.Json; using System.Windows.Forms.Primitives; +using System.Windows.Forms.TestUtilities; using Windows.Win32.System.Ole; using static System.Windows.Forms.Tests.BinaryFormatUtilitiesTests; using static System.Windows.Forms.TestUtilities.DataObjectTestHelpers; @@ -23,7 +24,8 @@ namespace System.Windows.Forms.Tests; // Note: each registered Clipboard format is an OS singleton // and we should not run this test at the same time as other tests using the same format. [Collection("Sequential")] -[UISettings(MaxAttempts = 3)] // Try up to 3 times before failing. +// Try up to 3 times before failing. +[UISettings(MaxAttempts = 3)] public class ClipboardTests { #pragma warning disable WFDEV005 // Type or member is obsolete @@ -1110,14 +1112,19 @@ public void Clipboard_CustomDataObject_AvoidBinaryFormatter(bool copy) // Pasting in different process has been simulated. Manual Json deserialization will need to occur. IDataObject received = Clipboard.GetDataObject().Should().BeAssignableTo().Subject; received.Should().NotBe(jsonDataObject); + received.Should().BeAssignableTo(); byte[] jsonBytes = Clipboard.GetData(format).Should().BeOfType().Subject; JsonSerializer.Deserialize(jsonBytes, typeof(SimpleTestData)).Should().BeEquivalentTo(data); + received.TryGetData(format, out byte[]? jsonBytes1).Should().BeTrue(); + jsonBytes1.Should().BeEquivalentTo(jsonBytes); } else { JsonDataObject received = Clipboard.GetDataObject().Should().BeOfType().Subject; received.Should().Be(jsonDataObject); received.Deserialize(format).Should().BeEquivalentTo(data); + Action tryGetData = () => received.TryGetData(format, out byte[]? _); + tryGetData.Should().Throw(); } } @@ -1288,4 +1295,90 @@ public class SomeDataObject : DataObject public override bool GetDataPresent(string format, bool autoConvert) => format == Format || base.GetDataPresent(format, autoConvert); } + + [WinFormsTheory] + [BoolData] + public void Clipboard_RoundTrip_DataObject_SupportsTypedInterface(bool copy) => + CustomDataObject_RoundTrip_SupportsTypedInterface(copy); + + [WinFormsTheory] + [BoolData] + public void Clipboard_RoundTrip_ManagedAndRuntimeDataObject_SupportsTypedInterface(bool copy) => + CustomDataObject_RoundTrip_SupportsTypedInterface(copy); + + [WinFormsTheory] + [BoolData] + public void Clipboard_RoundTrip_TypedAndRuntimeDataObject_SupportsTypedInterface(bool copy) => + CustomDataObject_RoundTrip_SupportsTypedInterface(copy); + + [WinFormsTheory] + [BoolData] + public void Clipboard_RoundTrip_TypedDataObject_SupportsTypedInterface(bool copy) => + CustomDataObject_RoundTrip_SupportsTypedInterface(copy); + + [WinFormsTheory] + [BoolData] + public void Clipboard_RoundTrip_ManagedDataObject_SupportsTypedInterface(bool copy) => + CustomDataObject_RoundTrip_SupportsTypedInterface(copy); + + [WinFormsTheory] + [BoolData] + public void Clipboard_RoundTrip_Object_SupportsTypedInterface(bool copy) + { + SerializableTestData data = new(); + string format = typeof(SerializableTestData).FullName!; + + // Opt-in into access to the binary formatted stream. + using BinaryFormatterInClipboardDragDropScope clipboardScope = new(enable: true); + // We need the BinaryFormatter to flush the data from the managed object to the HGLOBAL + // and to write data to HGLOBAL as a binary formatted stream now if it hadn't been flushed. + using BinaryFormatterScope scope = new(enable: true); + + Clipboard.SetDataObject(data, copy); + + DataObject received = Clipboard.GetDataObject().Should().BeOfType().Subject; + + received.TryGetData(format, out SerializableTestData? result).Should().BeTrue(); + result.Should().BeEquivalentTo(data); + + Clipboard.TryGetData(format, out result).Should().BeTrue(); + result.Should().BeEquivalentTo(data); + } + + private static void CustomDataObject_RoundTrip_SupportsTypedInterface(bool copy) where T : IDataObject, new() + { + SerializableTestData data = new(); + T testDataObject = new(); + string format = ManagedDataObject.s_format; + testDataObject.SetData(format, data); + + // Opt-in into access the binary formatted stream. + using BinaryFormatterInClipboardDragDropScope clipboardScope = new(enable: copy); + // We need the BinaryFormatter to flush the data from the managed object to the HGLOBAL. + using (BinaryFormatterScope scope = new(enable: copy)) + { + Clipboard.SetDataObject(testDataObject, copy); + } + + // copy == true => data was flushed to HGLOBAL and we read it with a WinForms DataObject. + // Otherwise this is the user-implemented ITypedDataObject or the WinForms wrapper. + if (copy || typeof(T).IsAssignableTo(typeof(ITypedDataObject))) + { + ITypedDataObject received = Clipboard.GetDataObject().Should().BeAssignableTo().Subject; + + received.TryGetData(format, out SerializableTestData? result).Should().BeTrue(); + result.Should().BeEquivalentTo(data); + + Clipboard.TryGetData(format, out result).Should().BeTrue(); + result.Should().BeEquivalentTo(data); + } + else + { + T received = Clipboard.GetDataObject().Should().BeOfType().Subject; + received.Should().Be(testDataObject); + // When we are not flushing the data to the HGLOBAL, we are reading from our DataStore or the managed test data object. + Action tryGetData = () => received.TryGetData(format, out SerializableTestData? result); + tryGetData.Should().Throw(); + } + } } diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/DataObjectTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/DataObjectTests.cs index a8311724a16..70de9a38e95 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/DataObjectTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/DataObjectTests.cs @@ -372,7 +372,7 @@ public void DataObject_GetData_InvokeTypeMocked_CallsGetData(Type format, object mockDataObject.Verify(o => o.GetData(formatName), Times.Exactly(expectedCallCount)); } - #nullable enable +#nullable enable internal class DataObjectOverridesTryGetDataCore : DataObject { private readonly string _format; @@ -399,16 +399,17 @@ protected override bool TryGetDataCore( format.Should().Be(_format); resolver.Should().BeEquivalentTo(_resolver); autoConvert.Should().Be(_autoConvert); - typeof(T).Should().Be(typeof(string)); + typeof(T).Should().Be(); Count++; + // This is a mock implementation that never returns anything. data = default; return false; } } [Fact] - public void DataObject_TryGetData_InvokeString_CallsTryGetDataCore() + public void TryGetData_InvokeString_CallsTryGetDataCore() { DataObjectOverridesTryGetDataCore dataObject = new(typeof(string).FullName!, resolver: null, autoConvert: true); dataObject.Count.Should().Be(0); @@ -418,6 +419,18 @@ public void DataObject_TryGetData_InvokeString_CallsTryGetDataCore() dataObject.Count.Should().Be(1); } + [Fact] + public void TryGetData_Wrapper_InvokeString_CallsTryGetDataCore() + { + DataObjectOverridesTryGetDataCore dataObject = new(typeof(string).FullName!, resolver: null, autoConvert: true); + dataObject.Count.Should().Be(0); + DataObject wrapper = new(dataObject); + + wrapper.TryGetData(out string? data).Should().BeFalse(); + data.Should().BeNull(); + dataObject.Count.Should().Be(1); + } + public static TheoryData RestrictedAndUnrestrictedFormat => [ DataFormats.CommaSeparatedValue, @@ -436,6 +449,19 @@ public void TryGetData_InvokeStringString_CallsTryGetDataCore(string format) dataObject.Count.Should().Be(1); } + [Theory] + [MemberData(nameof(RestrictedAndUnrestrictedFormat))] + public void TryGetData_Wrapper_InvokeStringString_CallsTryGetDataCore(string format) + { + DataObjectOverridesTryGetDataCore dataObject = new(format, null, autoConvert: true); + dataObject.Count.Should().Be(0); + DataObject wrapper = new(dataObject); + + wrapper.TryGetData(format, out string? data).Should().BeFalse(); + data.Should().BeNull(); + dataObject.Count.Should().Be(1); + } + [Fact] public void TryGetData_InvokeStringString_ValidationFails() { @@ -449,6 +475,20 @@ public void TryGetData_InvokeStringString_ValidationFails() dataObject.Count.Should().Be(0); } + [Fact] + public void TryGetData_Wrapper_InvokeStringString_ValidationFails() + { + string format = DataFormats.Bitmap; + DataObjectOverridesTryGetDataCore dataObject = new(format, null, autoConvert: true); + dataObject.Count.Should().Be(0); + DataObject wrapper = new(dataObject); + + // Incompatible format and type. + Action tryGetData = () => wrapper.TryGetData(format, out string? data); + tryGetData.Should().Throw(); + dataObject.Count.Should().Be(0); + } + public static TheoryData FormatAndAutoConvert => new() { { DataFormats.CommaSeparatedValue, true }, @@ -469,6 +509,19 @@ public void TryGetData_InvokeStringBoolString_CallsTryGetDataCore(string format, dataObject.Count.Should().Be(1); } + [Theory] + [MemberData(nameof(FormatAndAutoConvert))] + public void TryGetData_Wrapper_InvokeStringBoolString_CallsTryGetDataCore(string format, bool autoConvert) + { + DataObjectOverridesTryGetDataCore dataObject = new(format, resolver: null, autoConvert); + dataObject.Count.Should().Be(0); + DataObject wrapper = new(dataObject); + + wrapper.TryGetData(format, autoConvert, out string? data).Should().BeFalse(); + data.Should().BeNull(); + dataObject.Count.Should().Be(1); + } + private static Type NotSupportedResolver(TypeName typeName) => throw new NotSupportedException(); [Theory] @@ -486,7 +539,7 @@ public void TryGetData_InvokeStringBoolString_ValidationFails(bool autoConvert) [Theory] [MemberData(nameof(FormatAndAutoConvert))] - public void DataObject_TryGetData_InvokeStringFuncBoolString_CallsTryGetDataCore(string format, bool autoConvert) + public void TryGetData_InvokeStringFuncBoolString_CallsTryGetDataCore(string format, bool autoConvert) { DataObjectOverridesTryGetDataCore dataObject = new(format, DataObjectOverridesTryGetDataCore.Resolver, autoConvert); dataObject.Count.Should().Be(0); @@ -496,6 +549,19 @@ public void DataObject_TryGetData_InvokeStringFuncBoolString_CallsTryGetDataCore dataObject.Count.Should().Be(1); } + [Theory] + [MemberData(nameof(FormatAndAutoConvert))] + public void TryGetData_Wrapper_InvokeStringFuncBoolString_CallsTryGetDataCore(string format, bool autoConvert) + { + DataObjectOverridesTryGetDataCore dataObject = new(format, DataObjectOverridesTryGetDataCore.Resolver, autoConvert); + dataObject.Count.Should().Be(0); + DataObject wrapper = new(dataObject); + + wrapper.TryGetData(format, DataObjectOverridesTryGetDataCore.Resolver, autoConvert, out string? data).Should().BeFalse(); + data.Should().BeNull(); + dataObject.Count.Should().Be(1); + } + [Theory] [BoolData] public void TryGetData_InvokeStringFuncBoolString_ValidationFails(bool autoConvert) @@ -508,7 +574,22 @@ public void TryGetData_InvokeStringFuncBoolString_ValidationFails(bool autoConve tryGetData.Should().Throw(); dataObject.Count.Should().Be(0); } - #nullable disable + + [Theory] + [BoolData] + public void TryGetData_Wrapper_InvokeStringFuncBoolString_ValidationFails(bool autoConvert) + { + string format = DataFormats.Bitmap; + DataObjectOverridesTryGetDataCore dataObject = new(format, DataObjectOverridesTryGetDataCore.Resolver, autoConvert); + dataObject.Count.Should().Be(0); + DataObject wrapper = new DataObject(dataObject); + + Action tryGetData = () => wrapper.TryGetData(format, DataObjectOverridesTryGetDataCore.Resolver, autoConvert, out string? data); + tryGetData.Should().Throw(); + dataObject.Count.Should().Be(0); + } + +#nullable disable [Theory] [MemberData(nameof(GetData_String_TheoryData))]