|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Private.Windows;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.Serialization;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Windows.Forms.TestUtilities;
using Moq;
using Windows.Win32.System.Ole;
using Com = Windows.Win32.System.Com;
using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;
using Point = System.Drawing.Point;
using SimpleTestData = System.Windows.Forms.TestUtilities.DataObjectTestHelpers.SimpleTestData;
namespace System.Windows.Forms.Tests;
// NB: doesn't require thread affinity
public partial class DataObjectTests
{
#pragma warning disable WFDEV005 // Type or member is obsolete
private static readonly string[] s_restrictedClipboardFormats =
[
DataFormats.CommaSeparatedValue,
DataFormats.Dib,
DataFormats.Dif,
DataFormats.PenData,
DataFormats.Riff,
DataFormats.Tiff,
DataFormats.WaveAudio,
DataFormats.SymbolicLink,
DataFormats.StringFormat,
DataFormats.Bitmap,
DataFormats.EnhancedMetafile,
DataFormats.FileDrop,
DataFormats.Html,
DataFormats.MetafilePict,
DataFormats.OemText,
DataFormats.Palette,
DataFormats.Rtf,
DataFormats.Text,
DataFormats.UnicodeText,
"FileName",
"FileNameW",
"System.Drawing.Bitmap",
" ", // the last 3 return null and don't process the payload.
string.Empty,
null
];
private static readonly string[] s_unboundedClipboardFormats =
[
DataFormats.Serializable,
"something custom"
];
[Fact]
public void DataObject_ContainsAudio_InvokeDefault_ReturnsFalse()
{
DataObject dataObject = new();
dataObject.ContainsAudio().Should().BeFalse();
}
[Theory]
[BoolData]
public void DataObject_ContainsAudio_InvokeMocked_CallsGetDataPresent(bool result)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.ContainsAudio())
.CallBase();
mockDataObject
.Setup(o => o.GetDataPresent(DataFormats.WaveAudio, false))
.Returns(result)
.Verifiable();
mockDataObject.Object.ContainsAudio().Should().Be(result);
mockDataObject.Verify(o => o.GetDataPresent(DataFormats.WaveAudio, false), Times.Once());
}
[Fact]
public void DataObject_ContainsFileDropList_InvokeDefault_ReturnsFalse()
{
DataObject dataObject = new();
dataObject.ContainsFileDropList().Should().BeFalse();
}
[Theory]
[BoolData]
public void DataObject_ContainsFileDropList_InvokeMocked_CallsGetDataPresent(bool result)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.ContainsFileDropList())
.CallBase();
mockDataObject
.Setup(o => o.GetDataPresent(DataFormats.FileDrop, true))
.Returns(result)
.Verifiable();
mockDataObject.Object.ContainsFileDropList().Should().Be(result);
mockDataObject.Verify(o => o.GetDataPresent(DataFormats.FileDrop, true), Times.Once());
}
[Fact]
public void DataObject_ContainsImage_InvokeDefault_ReturnsFalse()
{
DataObject dataObject = new();
dataObject.ContainsImage().Should().BeFalse();
}
[Theory]
[BoolData]
public void DataObject_ContainsImage_InvokeMocked_CallsGetDataPresent(bool result)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.ContainsImage())
.CallBase();
mockDataObject
.Setup(o => o.GetDataPresent(DataFormats.Bitmap, true))
.Returns(result)
.Verifiable();
mockDataObject.Object.ContainsImage().Should().Be(result);
mockDataObject.Verify(o => o.GetDataPresent(DataFormats.Bitmap, true), Times.Once());
}
[Fact]
public void DataObject_ContainsText_InvokeDefault_ReturnsFalse()
{
DataObject dataObject = new();
dataObject.ContainsText().Should().BeFalse();
}
[Theory]
[BoolData]
public void DataObject_ContainsText_InvokeMocked_CallsGetDataPresent(bool result)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.ContainsText())
.CallBase();
mockDataObject
.Setup(o => o.ContainsText(TextDataFormat.UnicodeText))
.Returns(result)
.Verifiable();
mockDataObject.Object.ContainsText().Should().Be(result);
mockDataObject.Verify(o => o.ContainsText(TextDataFormat.UnicodeText), Times.Once());
}
[Theory]
[EnumData<TextDataFormat>]
public void DataObject_ContainsText_InvokeTextDataFormat_ReturnsFalse(TextDataFormat format)
{
DataObject dataObject = new();
dataObject.ContainsText(format).Should().BeFalse();
}
public static TheoryData<TextDataFormat, string, bool> ContainsText_TextDataFormat_TheoryData()
{
TheoryData<TextDataFormat, string, bool> theoryData = [];
foreach (bool result in new bool[] { true, false })
{
theoryData.Add(TextDataFormat.Text, DataFormats.UnicodeText, result);
theoryData.Add(TextDataFormat.UnicodeText, DataFormats.UnicodeText, result);
theoryData.Add(TextDataFormat.Rtf, DataFormats.Rtf, result);
theoryData.Add(TextDataFormat.Html, DataFormats.Html, result);
theoryData.Add(TextDataFormat.CommaSeparatedValue, DataFormats.CommaSeparatedValue, result);
}
return theoryData;
}
[Theory]
[MemberData(nameof(ContainsText_TextDataFormat_TheoryData))]
public void DataObject_ContainsText_InvokeTextDataFormatMocked_CallsGetDataPresent(TextDataFormat format, string expectedFormat, bool result)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.ContainsText(format))
.CallBase();
mockDataObject
.Setup(o => o.GetDataPresent(expectedFormat, false))
.Returns(result)
.Verifiable();
mockDataObject.Object.ContainsText(format).Should().Be(result);
mockDataObject.Verify(o => o.GetDataPresent(expectedFormat, false), Times.Once());
}
[Theory]
[InvalidEnumData<TextDataFormat>]
public void DataObject_ContainsText_InvokeInvalidTextDataFormat_ThrowsInvalidEnumArgumentException(TextDataFormat format)
{
DataObject dataObject = new();
((Action)(() => dataObject.ContainsText(format))).Should().Throw<InvalidEnumArgumentException>().WithParameterName("format");
}
[Fact]
public void DataObject_GetAudioStream_InvokeDefault_ReturnsNull()
{
DataObject dataObject = new();
dataObject.GetAudioStream().Should().BeNull();
}
public static TheoryData<object, Stream> GetAudioStream_TheoryData()
{
TheoryData<object, Stream> theoryData = new()
{
{ null, null },
{ new(), null }
};
MemoryStream stream = new();
theoryData.Add(stream, stream);
return theoryData;
}
[Theory]
[MemberData(nameof(GetAudioStream_TheoryData))]
public void DataObject_GetAudioStream_InvokeWithData_ReturnsExpected(object result, Stream expected)
{
DataObject dataObject = new();
dataObject.SetData(DataFormats.WaveAudio, result);
dataObject.GetAudioStream().Should().BeSameAs(expected);
}
[Theory]
[MemberData(nameof(GetAudioStream_TheoryData))]
public void DataObject_GetAudioStream_InvokeMocked_ReturnsExpected(object result, Stream expected)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetAudioStream())
.CallBase();
mockDataObject
.Setup(o => o.GetData(DataFormats.WaveAudio, false))
.Returns(result)
.Verifiable();
mockDataObject.Object.GetAudioStream().Should().BeSameAs(expected);
mockDataObject.Verify(o => o.GetData(DataFormats.WaveAudio, false), Times.Once());
}
public static TheoryData<string> GetData_String_TheoryData() => s_restrictedClipboardFormats.ToTheoryData();
public static TheoryData<string> GetData_String_UnboundedFormat_TheoryData() => s_unboundedClipboardFormats.ToTheoryData();
[Theory]
[MemberData(nameof(GetData_String_TheoryData))]
[MemberData(nameof(GetData_String_UnboundedFormat_TheoryData))]
public void DataObject_GetData_InvokeStringDefault_ReturnsNull(string format)
{
DataObject dataObject = new();
dataObject.GetData(format).Should().BeNull();
}
public static TheoryData<string, object> GetData_InvokeStringMocked_TheoryData()
{
TheoryData<string, object> theoryData = [];
foreach (object result in new object[] { new(), null })
{
theoryData.Add("format", result);
theoryData.Add(" ", result);
theoryData.Add(string.Empty, result);
theoryData.Add(null, result);
}
return theoryData;
}
[Theory]
[MemberData(nameof(GetData_InvokeStringMocked_TheoryData))]
public void DataObject_GetData_InvokeStringMocked_CallsGetData(string format, object result)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetData(format))
.CallBase();
mockDataObject
.Setup(o => o.GetData(format, true))
.Returns(result)
.Verifiable();
mockDataObject.Object.GetData(format).Should().BeSameAs(result);
mockDataObject.Verify(o => o.GetData(format, true), Times.Once());
}
[Theory]
[MemberData(nameof(GetData_InvokeStringMocked_TheoryData))]
public void DataObject_GetData_InvokeStringIDataObject_ReturnsExpected(string format, object result)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetData(format, true))
.Returns(result)
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.GetData(format).Should().BeSameAs(result);
mockDataObject.Verify(o => o.GetData(format, true), Times.Once());
}
public static TheoryData<string, bool, object> GetData_StringBoolIDataObject_TheoryData()
{
TheoryData<string, bool, object> theoryData = [];
foreach (bool autoConvert in new bool[] { true, false })
{
foreach (object result in new object[] { new(), null })
{
theoryData.Add("format", autoConvert, result);
theoryData.Add(" ", autoConvert, result);
theoryData.Add(string.Empty, autoConvert, result);
theoryData.Add(null, autoConvert, result);
}
}
return theoryData;
}
[Theory]
[MemberData(nameof(GetData_StringBoolIDataObject_TheoryData))]
public void DataObject_GetData_InvokeStringBoolIDataObject_ReturnsExpected(string format, bool autoConvert, object result)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetData(format, autoConvert))
.Returns(result)
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.GetData(format, autoConvert).Should().BeSameAs(result);
mockDataObject.Verify(o => o.GetData(format, autoConvert), Times.Once());
}
[Theory]
[InlineData(typeof(int))]
[InlineData(null)]
public void DataObject_GetData_InvokeTypeDefault_ReturnsNull(Type format)
{
DataObject dataObject = new();
dataObject.GetData(format).Should().BeNull();
}
public static TheoryData<Type, object, int, object> GetData_InvokeTypeMocked_TheoryData()
{
TheoryData<Type, object, int, object> theoryData = new();
foreach (object result in new object[] { new(), null })
{
theoryData.Add(typeof(int), result, 1, result);
theoryData.Add(null, result, 0, null);
}
return theoryData;
}
[Theory]
[MemberData(nameof(GetData_InvokeTypeMocked_TheoryData))]
public void DataObject_GetData_InvokeTypeMocked_CallsGetData(Type format, object result, int expectedCallCount, object expectedResult)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetData(It.IsAny<Type>()))
.CallBase();
string formatName = format?.FullName ?? "(null)";
mockDataObject
.Setup(o => o.GetData(formatName))
.Returns(result)
.Verifiable();
mockDataObject.Object.GetData(format).Should().BeSameAs(expectedResult);
mockDataObject.Verify(o => o.GetData(formatName), Times.Exactly(expectedCallCount));
}
#nullable enable
internal class DataObjectOverridesTryGetDataCore : DataObject
{
private readonly string _format;
private readonly Func<TypeName, Type>? _resolver;
// true is the default value for general purpose APIs (GetData).
private readonly bool _autoConvert;
public DataObjectOverridesTryGetDataCore(string format, Func<TypeName, Type>? resolver, bool autoConvert) : base()
{
_format = format;
_resolver = resolver;
_autoConvert = autoConvert;
}
public int Count { get; private set; }
public static Type Resolver(TypeName _) => typeof(string);
protected override bool TryGetDataCore<T>(
string format,
Func<TypeName, Type>? resolver,
bool autoConvert,
[NotNullWhen(true), MaybeNullWhen(false)] out T data)
{
format.Should().Be(_format);
resolver.Should().BeEquivalentTo(_resolver);
autoConvert.Should().Be(_autoConvert);
typeof(T).Should().Be(typeof(string));
Count++;
data = default;
return false;
}
}
[Fact]
public void DataObject_TryGetData_InvokeString_CallsTryGetDataCore()
{
DataObjectOverridesTryGetDataCore dataObject = new(typeof(string).FullName!, resolver: null, autoConvert: true);
dataObject.Count.Should().Be(0);
dataObject.TryGetData(out string? data).Should().BeFalse();
data.Should().BeNull();
dataObject.Count.Should().Be(1);
}
public static TheoryData<string> RestrictedAndUnrestrictedFormat =>
[
DataFormats.CommaSeparatedValue,
"something custom"
];
[Theory]
[MemberData(nameof(RestrictedAndUnrestrictedFormat))]
public void TryGetData_InvokeStringString_CallsTryGetDataCore(string format)
{
DataObjectOverridesTryGetDataCore dataObject = new(format, null, autoConvert: true);
dataObject.Count.Should().Be(0);
dataObject.TryGetData(format, out string? data).Should().BeFalse();
data.Should().BeNull();
dataObject.Count.Should().Be(1);
}
[Fact]
public void TryGetData_InvokeStringString_ValidationFails()
{
string format = DataFormats.Bitmap;
DataObjectOverridesTryGetDataCore dataObject = new(format, null, autoConvert: true);
dataObject.Count.Should().Be(0);
// Incompatible format and type.
Action tryGetData = () => dataObject.TryGetData(format, out string? data);
tryGetData.Should().Throw<NotSupportedException>();
dataObject.Count.Should().Be(0);
}
public static TheoryData<string, bool> FormatAndAutoConvert => new()
{
{ DataFormats.CommaSeparatedValue, true },
{ "something custom", true },
{ DataFormats.CommaSeparatedValue, false },
{ "something custom", false }
};
[Theory]
[MemberData(nameof(FormatAndAutoConvert))]
public void TryGetData_InvokeStringBoolString_CallsTryGetDataCore(string format, bool autoConvert)
{
DataObjectOverridesTryGetDataCore dataObject = new(format, resolver: null, autoConvert);
dataObject.Count.Should().Be(0);
dataObject.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]
[BoolData]
public void TryGetData_InvokeStringBoolString_ValidationFails(bool autoConvert)
{
string format = DataFormats.Bitmap;
DataObjectOverridesTryGetDataCore dataObject = new(format, NotSupportedResolver, autoConvert);
dataObject.Count.Should().Be(0);
Action tryGetData = () => dataObject.TryGetData(format, autoConvert, out string? data);
tryGetData.Should().Throw<NotSupportedException>();
dataObject.Count.Should().Be(0);
}
[Theory]
[MemberData(nameof(FormatAndAutoConvert))]
public void DataObject_TryGetData_InvokeStringFuncBoolString_CallsTryGetDataCore(string format, bool autoConvert)
{
DataObjectOverridesTryGetDataCore dataObject = new(format, DataObjectOverridesTryGetDataCore.Resolver, autoConvert);
dataObject.Count.Should().Be(0);
dataObject.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)
{
string format = DataFormats.Bitmap;
DataObjectOverridesTryGetDataCore dataObject = new(format, DataObjectOverridesTryGetDataCore.Resolver, autoConvert);
dataObject.Count.Should().Be(0);
Action tryGetData = () => dataObject.TryGetData(format, DataObjectOverridesTryGetDataCore.Resolver, autoConvert, out string? data);
tryGetData.Should().Throw<NotSupportedException>();
dataObject.Count.Should().Be(0);
}
#nullable disable
[Theory]
[MemberData(nameof(GetData_String_TheoryData))]
[MemberData(nameof(GetData_String_UnboundedFormat_TheoryData))]
public void DataObject_GetDataPresent_InvokeStringDefault_ReturnsFalse(string format)
{
DataObject dataObject = new();
dataObject.GetDataPresent(format).Should().BeFalse();
}
public static TheoryData<string, bool> GetDataPresent_StringMocked_TheoryData()
{
TheoryData<string, bool> theoryData = [];
foreach (bool result in new bool[] { true, false })
{
theoryData.Add("format", result);
theoryData.Add(" ", result);
theoryData.Add(string.Empty, result);
theoryData.Add(null, result);
}
return theoryData;
}
[Theory]
[MemberData(nameof(GetDataPresent_StringMocked_TheoryData))]
public void DataObject_GetDataPresent_InvokeStringMocked_CallsGetDataPresent(string format, bool result)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetDataPresent(format))
.CallBase();
mockDataObject
.Setup(o => o.GetDataPresent(format, true))
.Returns(result)
.Verifiable();
mockDataObject.Object.GetDataPresent(format).Should().Be(result);
mockDataObject.Verify(o => o.GetDataPresent(format, true), Times.Once());
}
[Theory]
[MemberData(nameof(GetDataPresent_StringMocked_TheoryData))]
public void DataObject_GetDataPresent_InvokeStringIDataObject_ReturnsExpected(string format, bool result)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetDataPresent(format, true))
.Returns(result)
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.GetDataPresent(format).Should().Be(result);
mockDataObject.Verify(o => o.GetDataPresent(format, true), Times.Once());
}
public static TheoryData<string, bool, bool> GetDataPresent_StringBoolIDataObject_TheoryData()
{
TheoryData<string, bool, bool> theoryData = [];
foreach (bool autoConvert in new bool[] { true, false })
{
foreach (bool result in new bool[] { true, false })
{
theoryData.Add("format", autoConvert, result);
theoryData.Add(" ", autoConvert, result);
theoryData.Add(string.Empty, autoConvert, result);
theoryData.Add(null, autoConvert, result);
}
}
return theoryData;
}
[Theory]
[MemberData(nameof(GetDataPresent_StringBoolIDataObject_TheoryData))]
public void DataObject_GetDataPresent_InvokeStringBoolIDataObject_ReturnsExpected(string format, bool autoConvert, bool result)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetDataPresent(format, autoConvert))
.Returns(result)
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.GetDataPresent(format, autoConvert).Should().Be(result);
mockDataObject.Verify(o => o.GetDataPresent(format, autoConvert), Times.Once());
}
[Theory]
[InlineData(typeof(int))]
[InlineData(null)]
public void DataObject_GetDataPresentPresent_InvokeTypeDefault_ReturnsFalse(Type format)
{
DataObject dataObject = new();
dataObject.GetDataPresent(format).Should().BeFalse();
}
public static TheoryData<Type, bool, int, bool, string> GetDataPresent_InvokeTypeMocked_TheoryData()
{
TheoryData<Type, bool, int, bool, string> theoryData = [];
foreach (bool result in new bool[] { true, false })
{
theoryData.Add(typeof(int), result, 1, result, typeof(int).FullName);
theoryData.Add(null, result, 0, false, "(null)");
}
return theoryData;
}
[Theory]
[MemberData(nameof(GetDataPresent_InvokeTypeMocked_TheoryData))]
public void DataObject_GetDataPresent_InvokeTypeMocked_CallsGetDataPresent(Type format, bool result, int expectedCallCount, bool expectedResult, string expectedFormatName)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetDataPresent(It.IsAny<Type>()))
.CallBase();
mockDataObject
.Setup(o => o.GetDataPresent(expectedFormatName))
.Returns(result)
.Verifiable();
mockDataObject.Object.GetDataPresent(format).Should().Be(expectedResult);
mockDataObject.Verify(o => o.GetDataPresent(expectedFormatName), Times.Exactly(expectedCallCount));
}
[Fact]
public void DataObject_GetFileDropList_InvokeDefault_ReturnsEmpty()
{
DataObject dataObject = new();
dataObject.GetFileDropList().Cast<string>().Should().BeEmpty();
}
public static TheoryData<object, string[]> GetFileDropList_TheoryData()
{
TheoryData<object, string[]> theoryData = [];
theoryData.Add(null, Array.Empty<string>());
theoryData.Add(new(), Array.Empty<string>());
string[] list = ["a", " ", string.Empty, null];
theoryData.Add(list, list);
return theoryData;
}
[Theory]
[MemberData(nameof(GetFileDropList_TheoryData))]
public void DataObject_GetFileDropList_InvokeWithData_ReturnsExpected(object result, string[] expected)
{
DataObject dataObject = new();
dataObject.SetData(DataFormats.FileDrop, result);
dataObject.GetFileDropList().Cast<string>().Should().BeEquivalentTo(expected);
}
[Theory]
[MemberData(nameof(GetFileDropList_TheoryData))]
public void DataObject_GetFileDropList_InvokeMocked_ReturnsExpected(object result, string[] expected)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetFileDropList())
.CallBase();
mockDataObject
.Setup(o => o.GetData(DataFormats.FileDrop, true))
.Returns(result)
.Verifiable();
mockDataObject.Object.GetFileDropList().Cast<string>().Should().BeEquivalentTo(expected);
mockDataObject.Verify(o => o.GetData(DataFormats.FileDrop, true), Times.Once());
}
[Fact]
public void DataObject_GetFormats_InvokeDefault_ReturnsEmpty()
{
DataObject dataObject = new();
dataObject.GetFormats().Cast<string>().Should().BeEmpty();
}
[WinFormsFact]
public void DataObject_GetFormats_InvokeWithValues_ReturnsExpected()
{
DataObject dataObject = new();
dataObject.SetData("format1", "data1");
dataObject.GetFormats().Should().Equal(["format1"]);
dataObject.SetText("data2");
dataObject.GetFormats().Should().Equal(["format1", "UnicodeText"]);
using Bitmap bitmap1 = new(10, 10);
dataObject.SetData("format2", bitmap1);
dataObject.GetFormats().OrderBy(s => s).Should().Equal(["format1", "format2", "UnicodeText"]);
using Bitmap bitmap2 = new(10, 10);
dataObject.SetData(bitmap2);
dataObject.GetFormats().OrderBy(s => s).Should().Equal(["Bitmap", "format1", "format2", "System.Drawing.Bitmap", "UnicodeText", "WindowsForms10PersistentObject"]);
}
public static TheoryData<string[]> GetFormats_Mocked_TheoryData() => new()
{
{ null },
{ Array.Empty<string>() },
{ new string[] { "a", " ", string.Empty, null } }
};
[Theory]
[MemberData(nameof(GetFormats_Mocked_TheoryData))]
public void DataObject_GetFormats_InvokeMocked_ReturnsExpected(string[] result)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetFormats())
.CallBase();
mockDataObject
.Setup(o => o.GetFormats(true))
.Returns(result)
.Verifiable();
mockDataObject.Object.GetFormats().Should().BeSameAs(result);
mockDataObject.Verify(o => o.GetFormats(true), Times.Once());
}
public static TheoryData<string[]> GetFormats_IDataObject_TheoryData() => new()
{
{ null },
{ Array.Empty<string>() },
{ new string[] { "a", string.Empty, null } },
{ new string[] { "a", " ", string.Empty, null } }
};
[Theory]
[MemberData(nameof(GetFormats_IDataObject_TheoryData))]
public void DataObject_GetFormats_InvokeIDataObject_ReturnsExpected(string[] result)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetFormats(true))
.Returns(result)
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.GetFormats().Should().BeSameAs(result);
mockDataObject.Verify(o => o.GetFormats(true), Times.Once());
}
[Theory]
[BoolData]
public void DataObject_GetFormats_InvokeBoolDefault_ReturnsEmpty(bool autoConvert)
{
DataObject dataObject = new();
dataObject.GetFormats(autoConvert).Should().BeEmpty();
}
[WinFormsFact]
public void DataObject_GetFormats_InvokeBoolWithValues_ReturnsExpected()
{
DataObject dataObject = new();
dataObject.SetData("format1", "data1");
dataObject.GetFormats(autoConvert: true).Should().Equal(["format1"]);
dataObject.GetFormats(autoConvert: false).Should().Equal(["format1"]);
dataObject.SetText("data2");
dataObject.GetFormats(autoConvert: true).Should().Equal(["format1", "UnicodeText"]);
dataObject.GetFormats(autoConvert: false).Should().Equal(["format1", "UnicodeText"]);
using Bitmap bitmap1 = new(10, 10);
dataObject.SetData("format2", bitmap1);
dataObject.GetFormats(autoConvert: true).OrderBy(s => s).Should().Equal(["format1", "format2", "UnicodeText"]);
dataObject.GetFormats(autoConvert: false).OrderBy(s => s).Should().Equal(["format1", "format2", "UnicodeText"]);
using Bitmap bitmap2 = new(10, 10);
dataObject.SetData(bitmap2);
dataObject.GetFormats(autoConvert: true).OrderBy(s => s).Should().Equal(["Bitmap", "format1", "format2", "System.Drawing.Bitmap", "UnicodeText", "WindowsForms10PersistentObject"]);
dataObject.GetFormats(autoConvert: false).OrderBy(s => s).Should().Equal(["format1", "format2", "System.Drawing.Bitmap", "UnicodeText", "WindowsForms10PersistentObject"]);
}
public static TheoryData<bool, string[]> GetFormats_BoolIDataObject_TheoryData() => new()
{
{ true, null },
{ true, Array.Empty<string>() },
{ true, new string[] { "a", " ", string.Empty, null } },
{ false, null },
{ false, Array.Empty<string>() },
{ false, new string[] { "a", " ", string.Empty, null } }
};
[Theory]
[MemberData(nameof(GetFormats_BoolIDataObject_TheoryData))]
public void DataObject_GetFormats_InvokeBoolIDataObject_ReturnsExpected(bool autoConvert, string[] result)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetFormats(autoConvert))
.Returns(result)
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.GetFormats(autoConvert).Should().BeSameAs(result);
mockDataObject.Verify(o => o.GetFormats(autoConvert), Times.Once());
}
[Fact]
public void DataObject_GetImage_InvokeDefault_ReturnsNull()
{
DataObject dataObject = new();
dataObject.GetImage().Should().BeNull();
}
public static TheoryData<object, Image> GetImage_TheoryData()
{
TheoryData<object, Image> theoryData = [];
theoryData.Add(null, null);
theoryData.Add(new(), null);
// This bitmap is not backed up by the GDI handle, we don't have to dispose it.
Bitmap image = new(10, 10);
theoryData.Add(image, image);
return theoryData;
}
[Theory]
[MemberData(nameof(GetImage_TheoryData))]
public void DataObject_GetImage_InvokeWithData_ReturnsExpected(object result, Image expected)
{
DataObject dataObject = new();
dataObject.SetData(DataFormats.Bitmap, result);
dataObject.GetImage().Should().Be(expected);
}
[Theory]
[MemberData(nameof(GetImage_TheoryData))]
public void DataObject_GetImage_InvokeMocked_ReturnsExpected(object result, Image expected)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetImage())
.CallBase();
mockDataObject
.Setup(o => o.GetData(DataFormats.Bitmap, true))
.Returns(result)
.Verifiable();
mockDataObject.Object.GetImage().Should().BeSameAs(expected);
mockDataObject.Verify(o => o.GetData(DataFormats.Bitmap, true), Times.Once());
}
[Fact]
public void DataObject_GetText_InvokeDefault_ReturnsEmpty()
{
DataObject dataObject = new();
dataObject.GetText().Should().BeEmpty();
}
public static TheoryData<string, string> GetText_TheoryData() => new()
{
{ null, string.Empty},
{ string.Empty, string.Empty},
{ " ", " "},
{ "a", "a"}
};
[Theory]
[MemberData(nameof(GetText_TheoryData))]
public void DataObject_GetText_InvokeWithData_ReturnsExpected(string result, string expected)
{
DataObject dataObject = new();
dataObject.SetData(DataFormats.UnicodeText, result);
dataObject.GetText().Should().Be(expected);
}
[Theory]
[StringWithNullData]
public void DataObject_GetText_InvokeMocked_ReturnsExpected(string result)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetText())
.CallBase();
mockDataObject
.Setup(o => o.GetText(TextDataFormat.UnicodeText))
.Returns(result)
.Verifiable();
mockDataObject.Object.GetText().Should().BeSameAs(result);
mockDataObject.Verify(o => o.GetText(TextDataFormat.UnicodeText), Times.Once());
}
[Theory]
[EnumData<TextDataFormat>]
public void DataObject_GetText_InvokeTextDataFormatDefault_ReturnsEmpty(TextDataFormat format)
{
DataObject dataObject = new();
dataObject.GetText(format).Should().BeEmpty();
}
public static TheoryData<TextDataFormat, string, object, string> GetText_TextDataFormat_TheoryData() => new()
{
{ TextDataFormat.Text, DataFormats.UnicodeText, null, string.Empty },
{ TextDataFormat.Text, DataFormats.UnicodeText, new(), string.Empty },
{ TextDataFormat.Text, DataFormats.UnicodeText, string.Empty, string.Empty },
{ TextDataFormat.Text, DataFormats.UnicodeText, " ", " " },
{ TextDataFormat.Text, DataFormats.UnicodeText, "a", "a" },
//
{ TextDataFormat.UnicodeText, DataFormats.UnicodeText, null, string.Empty },
{ TextDataFormat.UnicodeText, DataFormats.UnicodeText, new(), string.Empty },
{ TextDataFormat.UnicodeText, DataFormats.UnicodeText, string.Empty, string.Empty },
{ TextDataFormat.UnicodeText, DataFormats.UnicodeText, " ", " " },
{ TextDataFormat.UnicodeText, DataFormats.UnicodeText, "a", "a" },
//
{ TextDataFormat.Rtf, DataFormats.Rtf, null, string.Empty },
{ TextDataFormat.Rtf, DataFormats.Rtf, new(), string.Empty },
{ TextDataFormat.Rtf, DataFormats.Rtf, string.Empty, string.Empty },
{ TextDataFormat.Rtf, DataFormats.Rtf, " ", " " },
{ TextDataFormat.Rtf, DataFormats.Rtf, "a", "a" },
//
{ TextDataFormat.Html, DataFormats.Html, null, string.Empty },
{ TextDataFormat.Html, DataFormats.Html, new(), string.Empty },
{ TextDataFormat.Html, DataFormats.Html, string.Empty, string.Empty },
{ TextDataFormat.Html, DataFormats.Html, " ", " " },
{ TextDataFormat.Html, DataFormats.Html, "a", "a" },
//
{ TextDataFormat.CommaSeparatedValue, DataFormats.CommaSeparatedValue, null, string.Empty },
{ TextDataFormat.CommaSeparatedValue, DataFormats.CommaSeparatedValue, new(), string.Empty },
{ TextDataFormat.CommaSeparatedValue, DataFormats.CommaSeparatedValue, string.Empty, string.Empty },
{ TextDataFormat.CommaSeparatedValue, DataFormats.CommaSeparatedValue, " ", " " },
{ TextDataFormat.CommaSeparatedValue, DataFormats.CommaSeparatedValue, "a", "a" },
};
[Theory]
[MemberData(nameof(GetText_TextDataFormat_TheoryData))]
public void DataObject_GetText_InvokeTextDataFormatWithData_ReturnsExpected(TextDataFormat format, string expectedFormat, object result, string expected)
{
DataObject dataObject = new();
dataObject.SetData(expectedFormat, result);
dataObject.GetText(format).Should().BeSameAs(expected);
}
[Theory]
[MemberData(nameof(GetText_TextDataFormat_TheoryData))]
public void DataObject_GetText_InvokeTextDataFormatMocked_ReturnsExpected(TextDataFormat format, string expectedFormat, object result, string expected)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetText(format))
.CallBase();
mockDataObject
.Setup(o => o.GetData(expectedFormat, false))
.Returns(result)
.Verifiable();
mockDataObject.Object.GetText(format).Should().BeSameAs(expected);
mockDataObject.Verify(o => o.GetData(expectedFormat, false), Times.Once());
}
[Theory]
[InvalidEnumData<TextDataFormat>]
public void DataObject_GetText_InvokeInvalidFormat_ThrowsInvalidEnumArgumentException(TextDataFormat format)
{
DataObject dataObject = new();
Action action = () => dataObject.GetText(format);
action.Should().Throw<InvalidEnumArgumentException>().WithParameterName("format");
}
public static TheoryData<byte[]> SetAudio_ByteArray_TheoryData() => new()
{
{ Array.Empty<byte>() },
{ new byte[] { 1, 2, 3 } }
};
[Theory]
[MemberData(nameof(SetAudio_ByteArray_TheoryData))]
public void DataObject_SetAudio_InvokeByteArray_GetReturnsExpected(byte[] audioBytes)
{
DataObject dataObject = new();
dataObject.SetAudio(audioBytes);
dataObject.GetAudioStream().Should().BeOfType<MemoryStream>().Subject.ToArray().Should().BeEquivalentTo(audioBytes);
dataObject.GetData(DataFormats.WaveAudio, autoConvert: true).Should().BeOfType<MemoryStream>().Subject.ToArray().Should().Equal(audioBytes);
dataObject.GetData(DataFormats.WaveAudio, autoConvert: false).Should().BeOfType<MemoryStream>().Subject.ToArray().Should().Equal(audioBytes);
dataObject.ContainsAudio().Should().BeTrue();
dataObject.GetDataPresent(DataFormats.WaveAudio, autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent(DataFormats.WaveAudio, autoConvert: false).Should().BeTrue();
}
[Theory]
[MemberData(nameof(SetAudio_ByteArray_TheoryData))]
public void DataObject_SetAudio_InvokeByteArrayMocked_CallsSetAudio(byte[] audioBytes)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetAudio(audioBytes))
.CallBase();
mockDataObject
.Setup(o => o.SetAudio(It.IsAny<MemoryStream>()))
.Verifiable();
mockDataObject.Object.SetAudio(audioBytes);
mockDataObject.Verify(o => o.SetAudio(It.IsAny<MemoryStream>()), Times.Once());
}
[Theory]
[MemberData(nameof(SetAudio_ByteArray_TheoryData))]
public void DataObject_SetAudio_InvokeByteArrayIDataObject_CallsSetData(byte[] audioBytes)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetData(DataFormats.WaveAudio, false, It.IsAny<MemoryStream>()))
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.SetAudio(audioBytes);
mockDataObject.Verify(o => o.SetData(DataFormats.WaveAudio, false, It.IsAny<MemoryStream>()), Times.Once());
}
[Fact]
public void DataObject_SetAudio_NullAudioBytes_ThrowsArgumentNullException()
{
DataObject dataObject = new();
Action action = () => dataObject.SetAudio((byte[])null);
action.Should().Throw<ArgumentNullException>().WithParameterName("audioBytes");
}
public static TheoryData<MemoryStream> SetAudio_Stream_TheoryData() => new()
{
{ new MemoryStream(Array.Empty<byte>()) },
{ new MemoryStream([1, 2, 3]) }
};
[Theory]
[MemberData(nameof(SetAudio_Stream_TheoryData))]
public void DataObject_SetAudio_InvokeStream_GetReturnsExpected(MemoryStream audioStream)
{
DataObject dataObject = new();
dataObject.SetAudio(audioStream);
dataObject.GetAudioStream().Should().BeSameAs(audioStream);
dataObject.GetData(DataFormats.WaveAudio, autoConvert: true).Should().BeSameAs(audioStream);
dataObject.GetData(DataFormats.WaveAudio, autoConvert: false).Should().BeSameAs(audioStream);
dataObject.ContainsAudio().Should().BeTrue();
dataObject.GetDataPresent(DataFormats.WaveAudio, autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent(DataFormats.WaveAudio, autoConvert: false).Should().BeTrue();
}
[Theory]
[MemberData(nameof(SetAudio_Stream_TheoryData))]
public void DataObject_SetAudio_InvokeStreamMocked_CallsSetAudio(MemoryStream audioStream)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetAudio(audioStream))
.CallBase();
mockDataObject
.Setup(o => o.SetData(DataFormats.WaveAudio, false, audioStream))
.Verifiable();
mockDataObject.Object.SetAudio(audioStream);
mockDataObject.Verify(o => o.SetData(DataFormats.WaveAudio, false, audioStream), Times.Once());
}
[Theory]
[MemberData(nameof(SetAudio_Stream_TheoryData))]
public void DataObject_SetAudio_InvokeStreamIDataObject_CallsSetData(MemoryStream audioStream)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetData(DataFormats.WaveAudio, false, audioStream))
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.SetAudio(audioStream);
mockDataObject.Verify(o => o.SetData(DataFormats.WaveAudio, false, audioStream), Times.Once());
}
[Fact]
public void DataObject_SetAudio_NullAudioStream_ThrowsArgumentNullException()
{
DataObject dataObject = new();
Action action = () => dataObject.SetAudio((Stream)null);
action.Should().Throw<ArgumentNullException>().WithParameterName("audioStream");
}
public static TheoryData<object, string> SetData_Object_TheoryData() => new()
{
{ new(), typeof(object).FullName },
{ new Bitmap(10, 10), typeof(Bitmap).FullName },
{ new Mock<ISerializable>(MockBehavior.Strict).Object, DataFormats.Serializable }
};
[Theory]
[MemberData(nameof(SetData_Object_TheoryData))]
public void DataObject_SetData_Object_GetDataReturnsExpected(object data, string format)
{
DataObject dataObject = new();
dataObject.SetData(data);
dataObject.GetData(format, autoConvert: false).Should().BeSameAs(data);
dataObject.GetData(format, autoConvert: true).Should().BeSameAs(data);
dataObject.GetDataPresent(format, autoConvert: false).Should().BeTrue();
dataObject.GetDataPresent(format, autoConvert: true).Should().BeTrue();
}
[Fact]
public void DataObject_SetData_MultipleNonSerializable_GetDataReturnsExpected()
{
object data1 = new();
object data2 = new();
DataObject dataObject = new();
dataObject.SetData(data1);
dataObject.SetData(data2);
string format = data1.GetType().FullName;
dataObject.GetData(format, autoConvert: false).Should().Be(data2);
dataObject.GetData(format, autoConvert: true).Should().Be(data2);
dataObject.GetDataPresent(format, autoConvert: false).Should().BeTrue();
dataObject.GetDataPresent(format, autoConvert: true).Should().BeTrue();
}
[Fact]
public void DataObject_SetData_MultipleSerializable_GetDataReturnsExpected()
{
var data1 = new Mock<ISerializable>(MockBehavior.Strict).Object;
var data2 = new Mock<ISerializable>(MockBehavior.Strict).Object;
DataObject dataObject = new();
dataObject.SetData(data1);
dataObject.SetData(data2);
dataObject.GetData(DataFormats.Serializable, autoConvert: false).Should().Be(data1);
dataObject.GetData(DataFormats.Serializable, autoConvert: true).Should().Be(data1);
dataObject.GetData(data2.GetType().FullName, autoConvert: false).Should().Be(data2);
dataObject.GetData(data2.GetType().FullName, autoConvert: true).Should().Be(data2);
dataObject.GetDataPresent(DataFormats.Serializable, autoConvert: false).Should().BeTrue();
dataObject.GetDataPresent(DataFormats.Serializable, autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent(data2.GetType().FullName, autoConvert: false).Should().BeTrue();
dataObject.GetDataPresent(data2.GetType().FullName, autoConvert: true).Should().BeTrue();
}
public static TheoryData<object> SetData_ObjectIDataObject_TheoryData() => new()
{
{ new() },
{ null }
};
[Theory]
[MemberData(nameof(SetData_ObjectIDataObject_TheoryData))]
public void DataObject_SetData_InvokeObjectIDataObject_CallsSetData(object data)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetData(data))
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.SetData(data);
mockDataObject.Verify(o => o.SetData(data), Times.Once());
}
[Fact]
public void DataObject_SetData_NullData_ThrowsArgumentNullException()
{
DataObject dataObject = new();
((Action)(() => dataObject.SetData(null))).Should().Throw<ArgumentNullException>().WithParameterName("data");
}
public static TheoryData<string, string, bool, bool> SetData_StringObject_TheoryData()
{
TheoryData<string, string, bool, bool> theoryData = new();
foreach (string format in s_restrictedClipboardFormats)
{
if (string.IsNullOrWhiteSpace(format) || format == typeof(Bitmap).FullName || format.StartsWith("FileName", StringComparison.Ordinal))
{
continue;
}
theoryData.Add(format, null, format == DataFormats.FileDrop, format == DataFormats.Bitmap);
theoryData.Add(format, "input", format == DataFormats.FileDrop, format == DataFormats.Bitmap);
}
theoryData.Add(typeof(Bitmap).FullName, null, false, true);
theoryData.Add(typeof(Bitmap).FullName, "input", false, true);
theoryData.Add("FileName", null, true, false);
theoryData.Add("FileName", "input", true, false);
theoryData.Add("FileNameW", null, true, false);
theoryData.Add("FileNameW", "input", true, false);
return theoryData;
}
[Theory]
[MemberData(nameof(SetData_StringObject_TheoryData))]
private void DataObject_SetData_InvokeStringObject_GetReturnsExpected(string format, string input, bool expectedContainsFileDropList, bool expectedContainsImage)
{
DataObject dataObject = new();
dataObject.SetData(format, input);
dataObject.GetDataPresent(format).Should().BeTrue();
dataObject.GetDataPresent(format, autoConvert: false).Should().BeTrue();
dataObject.GetDataPresent(format, autoConvert: true).Should().BeTrue();
dataObject.GetData(format).Should().Be(input);
dataObject.GetData(format, autoConvert: false).Should().Be(input);
dataObject.GetData(format, autoConvert: true).Should().Be(input);
dataObject.ContainsAudio().Should().Be(format == DataFormats.WaveAudio);
dataObject.ContainsFileDropList().Should().Be(expectedContainsFileDropList);
dataObject.ContainsImage().Should().Be(expectedContainsImage);
dataObject.ContainsText().Should().Be(format == DataFormats.UnicodeText);
dataObject.ContainsText(TextDataFormat.Text).Should().Be(format == DataFormats.UnicodeText);
dataObject.ContainsText(TextDataFormat.UnicodeText).Should().Be(format == DataFormats.UnicodeText);
dataObject.ContainsText(TextDataFormat.Rtf).Should().Be(format == DataFormats.Rtf);
dataObject.ContainsText(TextDataFormat.Html).Should().Be(format == DataFormats.Html);
dataObject.ContainsText(TextDataFormat.CommaSeparatedValue).Should().Be(format == DataFormats.CommaSeparatedValue);
}
[Theory]
[InlineData(DataFormats.SerializableConstant, null)]
[InlineData(DataFormats.SerializableConstant, "input")]
[InlineData("something custom", null)]
[InlineData("something custom", "input")]
private void DataObject_SetData_InvokeStringObject_Unbounded_GetReturnsExpected(string format, string input)
{
DataObject dataObject = new();
dataObject.SetData(format, input);
dataObject.GetDataPresent(format).Should().BeTrue();
dataObject.GetDataPresent(format, autoConvert: false).Should().BeTrue();
dataObject.GetDataPresent(format, autoConvert: true).Should().BeTrue();
dataObject.GetData(format).Should().Be(input);
dataObject.GetData(format, autoConvert: false).Should().Be(input);
dataObject.GetData(format, autoConvert: true).Should().Be(input);
_ = dataObject.TryGetData(format, out object _).Should().Be(input is not null);
_ = dataObject.TryGetData(format, autoConvert: false, out object _).Should().Be(input is not null);
_ = dataObject.TryGetData(format, autoConvert: true, out object _).Should().Be(input is not null);
_ = dataObject.TryGetData(format, NotSupportedResolver, autoConvert: true, out object _).Should().Be(input is not null);
_ = dataObject.TryGetData(format, NotSupportedResolver, autoConvert: false, out object _).Should().Be(input is not null);
dataObject.TryGetData(format, NotSupportedResolver, autoConvert: false, out string data).Should().Be(input is not null);
data.Should().Be(input);
dataObject.TryGetData(format, NotSupportedResolver, autoConvert: true, out data).Should().Be(input is not null);
data.Should().Be(input);
dataObject.ContainsAudio().Should().BeFalse();
dataObject.ContainsFileDropList().Should().BeFalse();
dataObject.ContainsImage().Should().BeFalse();
dataObject.ContainsText().Should().BeFalse();
dataObject.ContainsText(TextDataFormat.Text).Should().BeFalse();
dataObject.ContainsText(TextDataFormat.UnicodeText).Should().BeFalse();
dataObject.ContainsText(TextDataFormat.Rtf).Should().BeFalse();
dataObject.ContainsText(TextDataFormat.Html).Should().BeFalse();
dataObject.ContainsText(TextDataFormat.CommaSeparatedValue).Should().BeFalse();
}
[Fact]
public void DataObject_SetData_InvokeStringObjectDibBitmapAutoConvert_GetDataReturnsExpected()
{
using Bitmap image = new(10, 10);
DataObject dataObject = new();
dataObject.SetData(DataFormats.Dib, autoConvert: true, image);
dataObject.GetImage().Should().BeSameAs(image);
dataObject.GetData(DataFormats.Bitmap, autoConvert: true).Should().BeSameAs(image);
dataObject.GetData(DataFormats.Bitmap, autoConvert: false).Should().BeSameAs(image);
dataObject.GetData(typeof(Bitmap).FullName, autoConvert: true).Should().BeSameAs(image);
dataObject.GetData(typeof(Bitmap).FullName, autoConvert: false).Should().BeNull();
dataObject.GetData(DataFormats.Dib, autoConvert: true).Should().BeNull();
dataObject.GetData(DataFormats.Dib, autoConvert: false).Should().BeNull();
dataObject.ContainsImage().Should().BeTrue();
dataObject.GetDataPresent(DataFormats.Bitmap, autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent(DataFormats.Bitmap, autoConvert: false).Should().BeTrue();
dataObject.GetDataPresent(typeof(Bitmap).FullName, autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent(typeof(Bitmap).FullName, autoConvert: false).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.Dib, autoConvert: true).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.Dib, autoConvert: false).Should().BeFalse();
}
public static TheoryData<string, object> SetData_StringObjectIDataObject_TheoryData()
{
TheoryData<string, object> theoryData = [];
foreach (string format in new string[] { "format", " ", string.Empty, null })
{
theoryData.Add(format, null);
theoryData.Add(format, new());
}
return theoryData;
}
[Theory]
[MemberData(nameof(SetData_StringObjectIDataObject_TheoryData))]
public void DataObject_SetData_InvokeStringObjectIDataObject_CallsSetData(string format, object data)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetData(format, data))
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.SetData(format, data);
mockDataObject.Verify(o => o.SetData(format, data), Times.Once());
}
public static TheoryData<string, bool, string, bool, bool> SetData_StringBoolObject_TheoryData()
{
TheoryData<string, bool, string, bool, bool> theoryData = new();
foreach (string format in s_restrictedClipboardFormats)
{
if (string.IsNullOrWhiteSpace(format) || format == typeof(Bitmap).FullName || format.StartsWith("FileName", StringComparison.Ordinal))
{
continue;
}
foreach (bool autoConvert in new bool[] { true, false })
{
theoryData.Add(format, autoConvert, null, format == DataFormats.FileDrop, format == DataFormats.Bitmap);
theoryData.Add(format, autoConvert, "input", format == DataFormats.FileDrop, format == DataFormats.Bitmap);
}
}
theoryData.Add(typeof(Bitmap).FullName, false, null, false, false);
theoryData.Add(typeof(Bitmap).FullName, false, "input", false, false);
theoryData.Add(typeof(Bitmap).FullName, true, null, false, true);
theoryData.Add(typeof(Bitmap).FullName, true, "input", false, true);
theoryData.Add("FileName", false, null, false, false);
theoryData.Add("FileName", false, "input", false, false);
theoryData.Add("FileName", true, null, true, false);
theoryData.Add("FileName", true, "input", true, false);
theoryData.Add("FileNameW", false, null, false, false);
theoryData.Add("FileNameW", false, "input", false, false);
theoryData.Add("FileNameW", true, null, true, false);
theoryData.Add("FileNameW", true, "input", true, false);
return theoryData;
}
[Theory]
[MemberData(nameof(SetData_StringBoolObject_TheoryData))]
private void DataObject_SetData_InvokeStringBoolObject_GetReturnsExpected(string format, bool autoConvert, string input, bool expectedContainsFileDropList, bool expectedContainsImage)
{
DataObject dataObject = new();
dataObject.SetData(format, autoConvert, input);
dataObject.GetData(format, autoConvert: false).Should().Be(input);
dataObject.GetData(format, autoConvert: true).Should().Be(input);
dataObject.GetDataPresent(format, autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent(format, autoConvert: false).Should().BeTrue();
dataObject.ContainsAudio().Should().Be(format == DataFormats.WaveAudio);
dataObject.ContainsFileDropList().Should().Be(expectedContainsFileDropList);
dataObject.ContainsImage().Should().Be(expectedContainsImage);
dataObject.ContainsText().Should().Be(format == DataFormats.UnicodeText);
dataObject.ContainsText(TextDataFormat.Text).Should().Be(format == DataFormats.UnicodeText);
dataObject.ContainsText(TextDataFormat.UnicodeText).Should().Be(format == DataFormats.UnicodeText);
dataObject.ContainsText(TextDataFormat.Rtf).Should().Be(format == DataFormats.Rtf);
dataObject.ContainsText(TextDataFormat.Html).Should().Be(format == DataFormats.Html);
dataObject.ContainsText(TextDataFormat.CommaSeparatedValue).Should().Be(format == DataFormats.CommaSeparatedValue);
}
[Theory]
[InlineData("something custom", false, "input")]
[InlineData("something custom", false, null)]
[InlineData("something custom", true, "input")]
[InlineData("something custom", true, null)]
[InlineData(DataFormats.SerializableConstant, false, "input")]
[InlineData(DataFormats.SerializableConstant, false, null)]
[InlineData(DataFormats.SerializableConstant, true, "input")]
[InlineData(DataFormats.SerializableConstant, true, null)]
private void DataObject_SetData_InvokeStringBoolObject_Unbounded(string format, bool autoConvert, string input)
{
DataObject dataObject = new();
dataObject.SetData(format, autoConvert, input);
dataObject.GetData(format, autoConvert: false).Should().Be(input);
dataObject.GetData(format, autoConvert: true).Should().Be(input);
dataObject.TryGetData(format, out string data).Should().Be(input is not null);
data.Should().Be(input);
dataObject.TryGetData(format, autoConvert: false, out data).Should().Be(input is not null);
data.Should().Be(input);
dataObject.TryGetData(format, autoConvert: true, out data).Should().Be(input is not null);
data.Should().Be(input);
dataObject.TryGetData(format, NotSupportedResolver, autoConvert: false, out data).Should().Be(input is not null);
data.Should().Be(input);
dataObject.TryGetData(format, NotSupportedResolver, autoConvert: true, out data).Should().Be(input is not null);
data.Should().Be(input);
dataObject.GetDataPresent(format, autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent(format, autoConvert: false).Should().BeTrue();
dataObject.ContainsAudio().Should().Be(format == DataFormats.WaveAudio);
dataObject.ContainsFileDropList().Should().BeFalse();
dataObject.ContainsImage().Should().BeFalse();
dataObject.ContainsText().Should().BeFalse();
dataObject.ContainsText(TextDataFormat.Text).Should().BeFalse();
dataObject.ContainsText(TextDataFormat.UnicodeText).Should().BeFalse();
dataObject.ContainsText(TextDataFormat.Rtf).Should().BeFalse();
dataObject.ContainsText(TextDataFormat.Html).Should().BeFalse();
dataObject.ContainsText(TextDataFormat.CommaSeparatedValue).Should().BeFalse();
}
[Fact]
public void DataObject_SetData_InvokeStringBoolObjectDibBitmapAutoConvert_GetDataReturnsExpected()
{
using Bitmap image = new(10, 10);
DataObject dataObject = new();
dataObject.SetData(DataFormats.Dib, autoConvert: true, image);
dataObject.GetImage().Should().BeSameAs(image);
dataObject.GetData(DataFormats.Bitmap, autoConvert: true).Should().BeSameAs(image);
dataObject.GetData(DataFormats.Bitmap, autoConvert: false).Should().BeSameAs(image);
dataObject.GetData(typeof(Bitmap).FullName, autoConvert: true).Should().BeSameAs(image);
dataObject.GetData(typeof(Bitmap).FullName, autoConvert: false).Should().BeNull();
dataObject.GetData(DataFormats.Dib, autoConvert: true).Should().BeNull();
dataObject.GetData(DataFormats.Dib, autoConvert: false).Should().BeNull();
dataObject.ContainsImage().Should().BeTrue();
dataObject.GetDataPresent(DataFormats.Bitmap, autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent(DataFormats.Bitmap, autoConvert: false).Should().BeTrue();
dataObject.GetDataPresent(typeof(Bitmap).FullName, autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent(typeof(Bitmap).FullName, autoConvert: false).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.Dib, autoConvert: true).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.Dib, autoConvert: false).Should().BeFalse();
}
public static TheoryData<string, bool, object> SetData_StringBoolObjectIDataObject_TheoryData()
{
TheoryData<string, bool, object> theoryData = [];
foreach (string format in new string[] { "format", " ", string.Empty, null })
{
foreach (bool autoConvert in new bool[] { true, false })
{
theoryData.Add(format, autoConvert, null);
theoryData.Add(format, autoConvert, new());
}
}
return theoryData;
}
[Theory]
[MemberData(nameof(SetData_StringBoolObjectIDataObject_TheoryData))]
public void DataObject_SetData_InvokeStringBoolObjectIDataObject_CallsSetData(string format, bool autoConvert, object data)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetData(format, autoConvert, data))
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.SetData(format, autoConvert, data);
mockDataObject.Verify(o => o.SetData(format, autoConvert, data), Times.Once());
}
public static TheoryData<Type, object> SetData_TypeObjectIDataObject_TheoryData()
{
TheoryData<Type, object> theoryData = [];
foreach (Type format in new Type[] { typeof(int), null })
{
theoryData.Add(format, null);
theoryData.Add(format, new());
}
return theoryData;
}
[Theory]
[MemberData(nameof(SetData_TypeObjectIDataObject_TheoryData))]
public void DataObject_SetData_InvokeTypeObjectIDataObject_CallsSetData(Type format, object data)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetData(format, data))
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.SetData(format, data);
mockDataObject.Verify(o => o.SetData(format, data), Times.Once());
}
[Fact]
public void DataObject_SetData_NullFormat_ThrowsArgumentNullException()
{
DataObject dataObject = new();
((Action)(() => dataObject.SetData((string)null, new object()))).Should()
.Throw<ArgumentNullException>().WithParameterName("format");
((Action)(() => dataObject.SetData(null, true, new object()))).Should()
.Throw<ArgumentNullException>().WithParameterName("format");
((Action)(() => dataObject.SetData(null, false, new object()))).Should()
.Throw<ArgumentNullException>().WithParameterName("format");
((Action)(() => dataObject.SetData((Type)null, new object()))).Should()
.Throw<ArgumentNullException>().WithParameterName("format");
}
[Theory]
[InlineData("")]
[InlineData(" ")]
public void DataObject_SetData_WhitespaceOrEmptyFormat_ThrowsArgumentException(string format)
{
DataObject dataObject = new();
Action action = () => dataObject.SetData(format, new object());
action.Should().Throw<ArgumentException>().WithParameterName("format");
action = () => dataObject.SetData(format, true, new object());
action.Should().Throw<ArgumentException>().WithParameterName("format");
action = () => dataObject.SetData(format, false, new object());
action.Should().Throw<ArgumentException>().WithParameterName("format");
}
[Fact]
public void DataObject_SetData_DibBitmapNoAutoConvert_ThrowsNotSupportedException()
{
DataObject dataObject = new();
((Action)(() => dataObject.SetData(DataFormats.Dib, false, new Bitmap(10, 10))))
.Should().Throw<NotSupportedException>();
}
public static TheoryData<StringCollection> SetFileDropList_TheoryData() => new()
{
{ new StringCollection() },
{ new StringCollection { "file", " ", string.Empty, null } }
};
[Theory]
[MemberData(nameof(SetFileDropList_TheoryData))]
public void DataObject_SetFileDropList_Invoke_GetReturnsExpected(StringCollection filePaths)
{
DataObject dataObject = new();
dataObject.SetFileDropList(filePaths);
dataObject.GetFileDropList().Should().BeEquivalentTo(filePaths);
dataObject.GetData(DataFormats.FileDrop, autoConvert: true).Should().BeEquivalentTo(filePaths.Cast<string>());
dataObject.GetData(DataFormats.FileDrop, autoConvert: false).Should().BeEquivalentTo(filePaths.Cast<string>());
dataObject.GetData("FileName", autoConvert: true).Should().BeEquivalentTo(filePaths.Cast<string>());
dataObject.GetData("FileName", autoConvert: false).Should().BeNull();
dataObject.GetData("FileNameW", autoConvert: true).Should().BeEquivalentTo(filePaths.Cast<string>());
dataObject.GetData("FileNameW", autoConvert: false).Should().BeNull();
dataObject.ContainsFileDropList().Should().BeTrue();
dataObject.GetDataPresent(DataFormats.FileDrop, autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent(DataFormats.FileDrop, autoConvert: false).Should().BeTrue();
dataObject.GetDataPresent("FileName", autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent("FileName", autoConvert: false).Should().BeFalse();
dataObject.GetDataPresent("FileNameW", autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent("FileNameW", autoConvert: false).Should().BeFalse();
}
[Theory]
[MemberData(nameof(SetFileDropList_TheoryData))]
public void DataObject_SetFileDropList_InvokeMocked_CallsSetFileDropList(StringCollection filePaths)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetFileDropList(filePaths))
.CallBase();
mockDataObject
.Setup(o => o.SetData(DataFormats.FileDrop, true, It.IsAny<string[]>()))
.Verifiable();
mockDataObject.Object.SetFileDropList(filePaths);
mockDataObject.Verify(o => o.SetData(DataFormats.FileDrop, true, It.IsAny<string[]>()), Times.Once());
}
[Theory]
[MemberData(nameof(SetFileDropList_TheoryData))]
public void DataObject_SetFileDropList_InvokeIDataObject_CallsSetData(StringCollection filePaths)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetData(DataFormats.FileDrop, true, It.IsAny<string[]>()))
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.SetFileDropList(filePaths);
mockDataObject.Verify(o => o.SetData(DataFormats.FileDrop, true, It.IsAny<string[]>()), Times.Once());
}
[Fact]
public void DataObject_SetFileDropList_NullFilePaths_ThrowsArgumentNullException()
{
DataObject dataObject = new();
Action action = () => dataObject.SetFileDropList(null);
action.Should().Throw<ArgumentNullException>().WithParameterName("filePaths");
}
[Fact]
public void DataObject_SetImage_Invoke_GetReturnsExpected()
{
using Bitmap image = new(10, 10);
DataObject dataObject = new();
dataObject.SetImage(image);
dataObject.GetImage().Should().BeSameAs(image);
dataObject.GetData(DataFormats.Bitmap, autoConvert: true).Should().BeSameAs(image);
dataObject.GetData(DataFormats.Bitmap, autoConvert: false).Should().BeSameAs(image);
dataObject.GetData(typeof(Bitmap).FullName, autoConvert: true).Should().BeSameAs(image);
dataObject.GetData(typeof(Bitmap).FullName, autoConvert: false).Should().BeNull();
dataObject.GetData(DataFormats.Dib, autoConvert: true).Should().BeNull();
dataObject.GetData(DataFormats.Dib, autoConvert: false).Should().BeNull();
dataObject.ContainsImage().Should().BeTrue();
dataObject.GetDataPresent(DataFormats.Bitmap, autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent(DataFormats.Bitmap, autoConvert: false).Should().BeTrue();
dataObject.GetDataPresent(typeof(Bitmap).FullName, autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent(typeof(Bitmap).FullName, autoConvert: false).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.Dib, autoConvert: true).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.Dib, autoConvert: false).Should().BeFalse();
}
[Fact]
public void DataObject_SetImage_InvokeMocked_CallsSetImage()
{
using Bitmap image = new(10, 10);
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetImage(image))
.CallBase();
mockDataObject
.Setup(o => o.SetData(DataFormats.Bitmap, true, image))
.Verifiable();
mockDataObject.Object.SetImage(image);
mockDataObject.Verify(o => o.SetData(DataFormats.Bitmap, true, image), Times.Once());
}
[Fact]
public void DataObject_SetImage_InvokeIDataObject_CallsSetData()
{
using Bitmap image = new(10, 10);
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetData(DataFormats.Bitmap, true, image))
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.SetImage(image);
mockDataObject.Verify(o => o.SetData(DataFormats.Bitmap, true, image), Times.Once());
}
[Fact]
public void DataObject_SetImage_NullImage_ThrowsArgumentNullException()
{
DataObject dataObject = new();
Action action = () => dataObject.SetImage(null);
action.Should().Throw<ArgumentNullException>().WithParameterName("image");
}
public static TheoryData<string> SetText_String_TheoryData() => new()
{
{ " " },
{ "textData" }
};
[Theory]
[MemberData(nameof(SetText_String_TheoryData))]
public void DataObject_SetText_InvokeString_GetReturnsExpected(string textData)
{
DataObject dataObject = new();
dataObject.SetText(textData);
dataObject.GetText().Should().BeSameAs(textData);
dataObject.GetData(DataFormats.UnicodeText, autoConvert: true).Should().BeSameAs(textData);
dataObject.GetData(DataFormats.UnicodeText, autoConvert: false).Should().BeSameAs(textData);
dataObject.GetData(DataFormats.Text, autoConvert: true).Should().BeSameAs(textData);
dataObject.GetData(DataFormats.Text, autoConvert: false).Should().BeNull();
dataObject.GetData(DataFormats.StringFormat, autoConvert: true).Should().BeSameAs(textData);
dataObject.GetData(DataFormats.StringFormat, autoConvert: false).Should().BeNull();
dataObject.GetData(DataFormats.Rtf, autoConvert: true).Should().BeNull();
dataObject.GetData(DataFormats.Rtf, autoConvert: false).Should().BeNull();
dataObject.GetData(DataFormats.Html, autoConvert: true).Should().BeNull();
dataObject.GetData(DataFormats.Html, autoConvert: false).Should().BeNull();
dataObject.GetData(DataFormats.CommaSeparatedValue, autoConvert: true).Should().BeNull();
dataObject.GetData(DataFormats.CommaSeparatedValue, autoConvert: false).Should().BeNull();
dataObject.ContainsText().Should().BeTrue();
dataObject.GetDataPresent(DataFormats.UnicodeText, autoConvert: true).Should().BeTrue();
dataObject.GetDataPresent(DataFormats.UnicodeText, autoConvert: false).Should().BeTrue();
dataObject.GetDataPresent(DataFormats.Text, autoConvert: true).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.Text, autoConvert: false).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.StringFormat, autoConvert: true).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.StringFormat, autoConvert: false).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.Rtf, autoConvert: true).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.Rtf, autoConvert: false).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.Html, autoConvert: true).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.Html, autoConvert: false).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.CommaSeparatedValue, autoConvert: true).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.CommaSeparatedValue, autoConvert: false).Should().BeFalse();
}
[Theory]
[MemberData(nameof(SetText_String_TheoryData))]
public void DataObject_SetText_InvokeStringMocked_CallsSetText(string textData)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetText(textData))
.CallBase();
mockDataObject
.Setup(o => o.SetText(textData, TextDataFormat.UnicodeText))
.Verifiable();
mockDataObject.Object.SetText(textData);
mockDataObject.Verify(o => o.SetText(textData, TextDataFormat.UnicodeText), Times.Once());
}
[Theory]
[MemberData(nameof(SetText_String_TheoryData))]
public void DataObject_SetText_InvokeStringIDataObject_CallsSetData(string textData)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetData(DataFormats.UnicodeText, false, textData))
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.SetText(textData);
mockDataObject.Verify(o => o.SetData(DataFormats.UnicodeText, false, textData), Times.Once());
}
public static TheoryData<string, TextDataFormat, string, string, string, string> SetText_StringTextDataFormat_TheoryData()
{
TheoryData<string, TextDataFormat, string, string, string, string> theoryData = [];
foreach (string textData in new string[] { "textData", " " })
{
theoryData.Add(textData, TextDataFormat.Text, textData, null, null, null);
theoryData.Add(textData, TextDataFormat.UnicodeText, textData, null, null, null);
theoryData.Add(textData, TextDataFormat.Rtf, null, textData, null, null);
theoryData.Add(textData, TextDataFormat.Html, null, null, textData, null);
theoryData.Add(textData, TextDataFormat.CommaSeparatedValue, null, null, null, textData);
}
return theoryData;
}
[Theory]
[MemberData(nameof(SetText_StringTextDataFormat_TheoryData))]
public void DataObject_SetText_InvokeStringTextDataFormat_GetReturnsExpected(string textData, TextDataFormat format, string expectedUnicodeText, string expectedRtfText, string expectedHtmlText, string expectedCsvText)
{
DataObject dataObject = new();
dataObject.SetText(textData, format);
dataObject.GetText(format).Should().Be(textData);
dataObject.GetData(DataFormats.UnicodeText, autoConvert: true).Should().Be(expectedUnicodeText);
dataObject.GetData(DataFormats.UnicodeText, autoConvert: false).Should().Be(expectedUnicodeText);
dataObject.GetData(DataFormats.Text, autoConvert: true).Should().Be(expectedUnicodeText);
dataObject.GetData(DataFormats.Text, autoConvert: false).Should().BeNull();
dataObject.GetData(DataFormats.StringFormat, autoConvert: true).Should().Be(expectedUnicodeText);
dataObject.GetData(DataFormats.StringFormat, autoConvert: false).Should().BeNull();
dataObject.GetData(DataFormats.Rtf, autoConvert: true).Should().Be(expectedRtfText);
dataObject.GetData(DataFormats.Rtf, autoConvert: false).Should().Be(expectedRtfText);
dataObject.GetData(DataFormats.Html, autoConvert: true).Should().Be(expectedHtmlText);
dataObject.GetData(DataFormats.Html, autoConvert: false).Should().Be(expectedHtmlText);
dataObject.GetData(DataFormats.CommaSeparatedValue, autoConvert: true).Should().Be(expectedCsvText);
dataObject.GetData(DataFormats.CommaSeparatedValue, autoConvert: false).Should().Be(expectedCsvText);
dataObject.ContainsText(format).Should().BeTrue();
dataObject.GetDataPresent(DataFormats.UnicodeText, autoConvert: true).Should().Be(expectedUnicodeText is not null);
dataObject.GetDataPresent(DataFormats.UnicodeText, autoConvert: false).Should().Be(expectedUnicodeText is not null);
dataObject.GetDataPresent(DataFormats.Text, autoConvert: true).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.Text, autoConvert: false).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.StringFormat, autoConvert: true).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.StringFormat, autoConvert: false).Should().BeFalse();
dataObject.GetDataPresent(DataFormats.Rtf, autoConvert: true).Should().Be(expectedRtfText is not null);
dataObject.GetDataPresent(DataFormats.Rtf, autoConvert: false).Should().Be(expectedRtfText is not null);
dataObject.GetDataPresent(DataFormats.Html, autoConvert: true).Should().Be(expectedHtmlText is not null);
dataObject.GetDataPresent(DataFormats.Html, autoConvert: false).Should().Be(expectedHtmlText is not null);
dataObject.GetDataPresent(DataFormats.CommaSeparatedValue, autoConvert: true).Should().Be(expectedCsvText is not null);
dataObject.GetDataPresent(DataFormats.CommaSeparatedValue, autoConvert: false).Should().Be(expectedCsvText is not null);
}
public static TheoryData<string, TextDataFormat, string> SetText_StringTextDataFormatMocked_TheoryData()
{
TheoryData<string, TextDataFormat, string> theoryData = [];
foreach (string textData in new string[] { "textData", " " })
{
theoryData.Add(textData, TextDataFormat.Text, DataFormats.UnicodeText);
theoryData.Add(textData, TextDataFormat.UnicodeText, DataFormats.UnicodeText);
theoryData.Add(textData, TextDataFormat.Rtf, DataFormats.Rtf);
theoryData.Add(textData, TextDataFormat.Html, DataFormats.Html);
theoryData.Add(textData, TextDataFormat.CommaSeparatedValue, DataFormats.CommaSeparatedValue);
}
return theoryData;
}
[Theory]
[MemberData(nameof(SetText_StringTextDataFormatMocked_TheoryData))]
public void DataObject_SetText_InvokeStringTextDataFormatMocked_CallsSetText(string textData, TextDataFormat format, string expectedFormat)
{
Mock<DataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetText(textData, format))
.CallBase();
mockDataObject
.Setup(o => o.SetData(expectedFormat, false, textData))
.Verifiable();
mockDataObject.Object.SetText(textData, format);
mockDataObject.Verify(o => o.SetData(expectedFormat, false, textData), Times.Once());
}
[Theory]
[MemberData(nameof(SetText_StringTextDataFormatMocked_TheoryData))]
public void DataObject_SetText_InvokeStringTextDataFormatIDataObject_CallsSetData(string textData, TextDataFormat format, string expectedFormat)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.SetData(expectedFormat, false, textData))
.Verifiable();
DataObject dataObject = new(mockDataObject.Object);
dataObject.SetText(textData, format);
mockDataObject.Verify(o => o.SetData(expectedFormat, false, textData), Times.Once());
}
[Theory]
[NullAndEmptyStringData]
public void DataObject_SetText_NullOrEmptyTextData_ThrowsArgumentNullException(string textData)
{
DataObject dataObject = new();
((Action)(() => dataObject.SetText(textData))).Should()
.Throw<ArgumentNullException>().WithParameterName("textData");
((Action)(() => dataObject.SetText(textData, TextDataFormat.Text))).Should()
.Throw<ArgumentNullException>().WithParameterName("textData");
}
[Theory]
[InvalidEnumData<TextDataFormat>]
public void DataObject_SetText_InvalidFormat_ThrowsInvalidEnumArgumentException(TextDataFormat format)
{
DataObject dataObject = new();
((Action)(() => dataObject.SetText("text", format))).Should()
.Throw<InvalidEnumArgumentException>().WithParameterName("format");
}
[WinFormsFact]
public void DataObject_GetData_EnhancedMetafile_DoesNotTerminateProcess()
{
DataObject data = new(new DataObjectIgnoringStorageMediumForEnhancedMetafile());
// Office ignores the storage medium in GetData(EnhancedMetafile) and always returns a handle,
// even when asked for a stream. This used to crash the process when DataObject interpreted the
// handle as a pointer to a COM IStream without checking the storage medium it retrieved.
data.GetData(DataFormats.EnhancedMetafile).Should().BeNull();
}
private sealed class DataObjectIgnoringStorageMediumForEnhancedMetafile : IComDataObject
{
public void GetData(ref FORMATETC format, out STGMEDIUM medium)
{
Marshal.ThrowExceptionForHR(QueryGetData(ref format));
using var metafile = new Drawing.Imaging.Metafile("bitmaps/milkmateya01.emf");
medium = default;
medium.tymed = TYMED.TYMED_ENHMF;
medium.unionmember = metafile.GetHenhmetafile();
}
public int QueryGetData(ref FORMATETC format)
{
// do not check the requested storage medium, we always return a metafile handle, that's what Office does
if (format.cfFormat != (short)CLIPBOARD_FORMAT.CF_ENHMETAFILE || format.dwAspect != DVASPECT.DVASPECT_CONTENT || format.lindex != -1)
return (int)HRESULT.DV_E_FORMATETC;
return 0;
}
public IEnumFORMATETC EnumFormatEtc(DATADIR direction) => throw new NotImplementedException();
public void GetDataHere(ref FORMATETC format, ref STGMEDIUM medium) => throw new NotImplementedException();
public int GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut) => throw new NotImplementedException();
public void SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release) => throw new NotImplementedException();
public int DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection) => throw new NotImplementedException();
public void DUnadvise(int connection) => throw new NotImplementedException();
public int EnumDAdvise(out IEnumSTATDATA enumAdvise) => throw new NotImplementedException();
}
public static TheoryData<ADVF, IAdviseSink> DAdvise_TheoryData()
{
TheoryData<ADVF, IAdviseSink> theoryData = new()
{
{ ADVF.ADVF_DATAONSTOP, null }
};
Mock<IAdviseSink> mockAdviseSink = new(MockBehavior.Strict);
theoryData.Add(ADVF.ADVF_DATAONSTOP, mockAdviseSink.Object);
return theoryData;
}
[WinFormsTheory]
[MemberData(nameof(DAdvise_TheoryData))]
public void DataObject_DAdvise_InvokeDefault_Success(ADVF advf, IAdviseSink adviseSink)
{
DataObject dataObject = new();
IComDataObject comDataObject = dataObject;
FORMATETC formatetc = default;
((HRESULT)comDataObject.DAdvise(ref formatetc, advf, adviseSink, out int connection)).Should().Be(HRESULT.E_NOTIMPL);
connection.Should().Be(0);
}
private delegate void DAdviseCallback(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection);
[WinFormsTheory]
[MemberData(nameof(DAdvise_TheoryData))]
public void DataObject_DAdvise_InvokeCustomComDataObject_Success(ADVF advf, IAdviseSink adviseSink)
{
Mock<IComDataObject> mockComDataObject = new(MockBehavior.Strict);
mockComDataObject
.Setup(o => o.DAdvise(ref It.Ref<FORMATETC>.IsAny, advf, adviseSink, out It.Ref<int>.IsAny))
.Callback((DAdviseCallback)((ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection) =>
{
pFormatetc.cfFormat = 3;
connection = 2;
}))
.Returns(1);
DataObject dataObject = new(mockComDataObject.Object);
IComDataObject comDataObject = dataObject;
FORMATETC formatetc = default;
comDataObject.DAdvise(ref formatetc, advf, adviseSink, out int connection).Should().Be(1);
connection.Should().Be(2);
formatetc.cfFormat.Should().Be(3);
mockComDataObject.Verify(o => o.DAdvise(ref It.Ref<FORMATETC>.IsAny, advf, adviseSink, out It.Ref<int>.IsAny), Times.Once());
}
[WinFormsTheory]
[InlineData(-1)]
[InlineData(0)]
public void DataObject_DUnadvise_InvokeDefault_Success(int connection)
{
DataObject dataObject = new();
IComDataObject comDataObject = dataObject;
Action action = () => comDataObject.DUnadvise(connection);
action.Should().Throw<NotImplementedException>();
}
[WinFormsTheory]
[InlineData(-1)]
[InlineData(0)]
public void DataObject_DUnadvise_InvokeCustomComDataObject_Success(int connection)
{
Mock<IComDataObject> mockComDataObject = new(MockBehavior.Strict);
mockComDataObject
.Setup(o => o.DUnadvise(connection))
.Verifiable();
DataObject dataObject = new(mockComDataObject.Object);
IComDataObject comDataObject = dataObject;
comDataObject.DUnadvise(connection);
mockComDataObject.Verify(o => o.DUnadvise(connection), Times.Once());
}
[WinFormsFact]
public void DataObject_EnumDAdvise_InvokeDefault_Success()
{
DataObject dataObject = new();
IComDataObject comDataObject = dataObject;
((HRESULT)comDataObject.EnumDAdvise(out IEnumSTATDATA enumAdvise)).Should().Be(HRESULT.OLE_E_ADVISENOTSUPPORTED);
enumAdvise.Should().BeNull();
}
private delegate void EnumDAdviseCallback(out IEnumSTATDATA enumAdvise);
public static TheoryData<IEnumSTATDATA> EnumDAdvise_CustomComDataObject_TheoryData()
{
TheoryData<IEnumSTATDATA> theoryData = [null];
Mock<IEnumSTATDATA> mockEnumStatData = new(MockBehavior.Strict);
theoryData.Add(mockEnumStatData.Object);
return theoryData;
}
[WinFormsTheory]
[MemberData(nameof(EnumDAdvise_CustomComDataObject_TheoryData))]
public void DataObject_EnumDAdvise_InvokeCustomComDataObject_Success(IEnumSTATDATA result)
{
Mock<IComDataObject> mockComDataObject = new(MockBehavior.Strict);
mockComDataObject
.Setup(o => o.EnumDAdvise(out It.Ref<IEnumSTATDATA>.IsAny))
.Callback((EnumDAdviseCallback)((out IEnumSTATDATA enumAdvise) =>
{
enumAdvise = result;
}))
.Returns(1);
DataObject dataObject = new(mockComDataObject.Object);
IComDataObject comDataObject = dataObject;
comDataObject.EnumDAdvise(out IEnumSTATDATA enumStatData).Should().Be(1);
enumStatData.Should().BeSameAs(result);
mockComDataObject.Verify(o => o.EnumDAdvise(out It.Ref<IEnumSTATDATA>.IsAny), Times.Once());
}
public static TheoryData<int> EnumFormatEtc_Default_TheoryData() => new()
{
{ -1 },
{ 0 },
{ 1 },
{ 2 }
};
[WinFormsTheory]
[MemberData(nameof(EnumFormatEtc_Default_TheoryData))]
public void DataObject_EnumFormatEtc_InvokeDefault_Success(int celt)
{
DataObject dataObject = new();
IComDataObject comDataObject = dataObject;
IEnumFORMATETC enumerator = comDataObject.EnumFormatEtc(DATADIR.DATADIR_GET);
enumerator.Should().NotBeNull();
var result = new FORMATETC[1];
int[] fetched = new int[1];
for (int i = 0; i < 2; i++)
{
enumerator.Next(celt, result, fetched).Should().Be((int)HRESULT.S_FALSE);
result[0].cfFormat.Should().Be(0);
fetched[0].Should().Be(0);
enumerator.Next(celt, null, null).Should().Be((int)HRESULT.S_FALSE);
enumerator.Reset().Should().Be((int)HRESULT.S_OK);
}
}
public static TheoryData<string, TYMED> EnumFormatEtc_TheoryData() => new()
{
{ DataFormats.Bitmap, TYMED.TYMED_GDI },
{ DataFormats.CommaSeparatedValue, TYMED.TYMED_HGLOBAL },
{ DataFormats.Dib, TYMED.TYMED_HGLOBAL },
{ DataFormats.Dif, TYMED.TYMED_HGLOBAL },
{ DataFormats.EnhancedMetafile, TYMED.TYMED_HGLOBAL },
{ DataFormats.FileDrop, TYMED.TYMED_HGLOBAL },
{ "FileName", TYMED.TYMED_HGLOBAL },
{ "FileNameW", TYMED.TYMED_HGLOBAL },
{ DataFormats.Html, TYMED.TYMED_HGLOBAL },
{ DataFormats.Locale, TYMED.TYMED_HGLOBAL },
{ DataFormats.MetafilePict, TYMED.TYMED_HGLOBAL },
{ DataFormats.OemText, TYMED.TYMED_HGLOBAL },
{ DataFormats.Palette, TYMED.TYMED_HGLOBAL },
{ DataFormats.PenData, TYMED.TYMED_HGLOBAL },
{ DataFormats.Riff, TYMED.TYMED_HGLOBAL },
{ DataFormats.Rtf, TYMED.TYMED_HGLOBAL },
{ DataFormats.Serializable, TYMED.TYMED_HGLOBAL },
{ DataFormats.StringFormat, TYMED.TYMED_HGLOBAL },
{ DataFormats.SymbolicLink, TYMED.TYMED_HGLOBAL },
{ DataFormats.Text, TYMED.TYMED_HGLOBAL },
{ DataFormats.Tiff, TYMED.TYMED_HGLOBAL },
{ DataFormats.UnicodeText, TYMED.TYMED_HGLOBAL },
{ DataFormats.WaveAudio, TYMED.TYMED_HGLOBAL }
};
[WinFormsTheory]
[MemberData(nameof(EnumFormatEtc_TheoryData))]
public void DataObject_EnumFormatEtc_InvokeWithValues_Success(string format1, TYMED expectedTymed)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetFormats())
.Returns([format1, "Format2"]);
DataObject dataObject = new(mockDataObject.Object);
IComDataObject comDataObject = dataObject;
IEnumFORMATETC enumerator = comDataObject.EnumFormatEtc(DATADIR.DATADIR_GET);
enumerator.Should().NotBeNull();
var result = new FORMATETC[2];
int[] fetched = new int[2];
for (int i = 0; i < 1; i++)
{
// Fetch nothing.
((HRESULT)enumerator.Next(0, result, fetched)).Should().Be(HRESULT.S_FALSE);
fetched[0].Should().Be(0);
((HRESULT)enumerator.Next(0, null, null)).Should().Be(HRESULT.S_FALSE);
// Fetch negative.
((HRESULT)enumerator.Next(-1, result, fetched)).Should().Be(HRESULT.S_FALSE);
fetched[0].Should().Be(0);
((HRESULT)enumerator.Next(-1, null, null)).Should().Be(HRESULT.S_FALSE);
// Null.
((Action)(() => enumerator.Next(1, null, fetched))).Should().Throw<NullReferenceException>();
// Fetch first.
((HRESULT)enumerator.Next(i + 1, result, fetched)).Should().Be(HRESULT.S_OK);
result[0].cfFormat.Should().Be(unchecked((short)(ushort)(DataFormats.GetFormat(format1).Id)));
result[0].dwAspect.Should().Be(DVASPECT.DVASPECT_CONTENT);
result[0].lindex.Should().Be(-1);
result[0].ptd.Should().Be(IntPtr.Zero);
result[0].tymed.Should().Be(expectedTymed);
result[1].cfFormat.Should().Be(0);
fetched[0].Should().Be(1);
// Fetch second.
((HRESULT)enumerator.Next(i + 1, result, fetched)).Should().Be(HRESULT.S_OK);
result[0].cfFormat.Should().NotBe(0);
result[0].dwAspect.Should().Be(DVASPECT.DVASPECT_CONTENT);
result[0].lindex.Should().Be(-1);
result[0].ptd.Should().Be(IntPtr.Zero);
result[0].tymed.Should().Be(TYMED.TYMED_HGLOBAL);
result[1].cfFormat.Should().Be(0);
fetched[0].Should().Be(1);
// Fetch another.
((HRESULT)enumerator.Next(1, null, null)).Should().Be(HRESULT.S_FALSE);
((HRESULT)enumerator.Next(1, null, null)).Should().Be(HRESULT.S_FALSE);
// Reset.
((HRESULT)enumerator.Reset()).Should().Be(HRESULT.S_OK);
}
}
[WinFormsTheory]
[MemberData(nameof(EnumFormatEtc_Default_TheoryData))]
public void DataObject_EnumFormatEtc_InvokeNullFormats_Success(int celt)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetFormats())
.Returns((string[])null);
DataObject dataObject = new(mockDataObject.Object);
IComDataObject comDataObject = dataObject;
IEnumFORMATETC enumerator = comDataObject.EnumFormatEtc(DATADIR.DATADIR_GET);
enumerator.Should().NotBeNull();
var result = new FORMATETC[1];
int[] fetched = new int[1];
for (int i = 0; i < 2; i++)
{
((HRESULT)enumerator.Next(celt, result, fetched)).Should().Be(HRESULT.S_FALSE);
result[0].cfFormat.Should().Be(0);
fetched[0].Should().Be(0);
((HRESULT)enumerator.Next(celt, null, null)).Should().Be(HRESULT.S_FALSE);
((HRESULT)enumerator.Reset()).Should().Be(HRESULT.S_OK);
}
}
[WinFormsTheory]
[InlineData(0)]
[InlineData(1)]
public void DataObject_EnumFormatEtc_SkipDefault_Success(int celt)
{
DataObject dataObject = new();
IComDataObject comDataObject = dataObject;
IEnumFORMATETC enumerator = comDataObject.EnumFormatEtc(DATADIR.DATADIR_GET);
enumerator.Should().NotBeNull();
var result = new FORMATETC[1];
int[] fetched = new int[1];
((HRESULT)enumerator.Skip(celt)).Should().Be(HRESULT.S_FALSE);
((HRESULT)enumerator.Next(1, result, fetched)).Should().Be(HRESULT.S_FALSE);
// Negative.
((HRESULT)enumerator.Skip(-1)).Should().Be(HRESULT.S_OK);
Action action = () => enumerator.Next(1, result, fetched);
action.Should().Throw<ArgumentOutOfRangeException>().WithParameterName("index");
}
[WinFormsFact]
public void DataObject_EnumFormatEtc_SkipCustom_Success()
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetFormats())
.Returns(["Format1", DataFormats.Bitmap, "Format2"]);
DataObject dataObject = new(mockDataObject.Object);
IComDataObject comDataObject = dataObject;
IEnumFORMATETC enumerator = comDataObject.EnumFormatEtc(DATADIR.DATADIR_GET);
enumerator.Should().NotBeNull();
var result = new FORMATETC[1];
int[] fetched = new int[1];
((HRESULT)enumerator.Skip(1)).Should().Be(HRESULT.S_OK);
((HRESULT)enumerator.Next(1, result, fetched)).Should().Be(HRESULT.S_OK);
result[0].cfFormat.Should().Be(2);
result[0].dwAspect.Should().Be(DVASPECT.DVASPECT_CONTENT);
result[0].lindex.Should().Be(-1);
result[0].ptd.Should().Be(IntPtr.Zero);
result[0].tymed.Should().Be(TYMED.TYMED_GDI);
fetched[0].Should().Be(1);
// Skip negative.
((HRESULT)enumerator.Skip(-2)).Should().Be(HRESULT.S_OK);
((HRESULT)enumerator.Next(1, result, fetched)).Should().Be(HRESULT.S_OK);
result[0].cfFormat.Should().Be(unchecked((short)(ushort)(DataFormats.GetFormat("Format1").Id)));
result[0].dwAspect.Should().Be(DVASPECT.DVASPECT_CONTENT);
result[0].lindex.Should().Be(-1);
result[0].ptd.Should().Be(IntPtr.Zero);
result[0].tymed.Should().Be(TYMED.TYMED_HGLOBAL);
fetched[0].Should().Be(1);
// Skip end.
((HRESULT)enumerator.Skip(1)).Should().Be(HRESULT.S_OK);
((HRESULT)enumerator.Next(1, result, fetched)).Should().Be(HRESULT.S_OK);
result[0].cfFormat.Should().Be(unchecked((short)(ushort)(DataFormats.GetFormat("Format2").Id)));
result[0].dwAspect.Should().Be(DVASPECT.DVASPECT_CONTENT);
result[0].lindex.Should().Be(-1);
result[0].ptd.Should().Be(IntPtr.Zero);
result[0].tymed.Should().Be(TYMED.TYMED_HGLOBAL);
fetched[0].Should().Be(1);
((HRESULT)enumerator.Skip(0)).Should().Be(HRESULT.S_FALSE);
((HRESULT)enumerator.Skip(1)).Should().Be(HRESULT.S_FALSE);
// Negative.
((HRESULT)enumerator.Skip(-4)).Should().Be(HRESULT.S_OK);
Action action = () => enumerator.Next(1, result, fetched);
action.Should().Throw<ArgumentOutOfRangeException>().WithParameterName("index");
}
[WinFormsTheory]
[MemberData(nameof(EnumFormatEtc_Default_TheoryData))]
public void DataObject_EnumFormatEtc_CloneDefault_Success(int celt)
{
DataObject dataObject = new();
IComDataObject comDataObject = dataObject;
IEnumFORMATETC source = comDataObject.EnumFormatEtc(DATADIR.DATADIR_GET);
source.Clone(out IEnumFORMATETC enumerator);
enumerator.Should().NotBeNull();
var result = new FORMATETC[1];
int[] fetched = new int[1];
for (int i = 0; i < 2; i++)
{
((HRESULT)enumerator.Next(celt, result, fetched)).Should().Be(HRESULT.S_FALSE);
result[0].cfFormat.Should().Be(0);
fetched[0].Should().Be(0);
((HRESULT)enumerator.Next(celt, null, null)).Should().Be(HRESULT.S_FALSE);
((HRESULT)enumerator.Reset()).Should().Be(HRESULT.S_OK);
}
}
[WinFormsTheory]
[MemberData(nameof(EnumFormatEtc_TheoryData))]
public void DataObject_EnumFormatEtc_CloneWithValues_Success(string format1, TYMED expectedTymed)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetFormats())
.Returns([format1, "Format2"]);
DataObject dataObject = new(mockDataObject.Object);
IComDataObject comDataObject = dataObject;
IEnumFORMATETC source = comDataObject.EnumFormatEtc(DATADIR.DATADIR_GET);
source.Clone(out IEnumFORMATETC enumerator);
enumerator.Should().NotBeNull();
var result = new FORMATETC[2];
int[] fetched = new int[2];
for (int i = 0; i < 1; i++)
{
// Fetch nothing.
((HRESULT)enumerator.Next(0, result, fetched)).Should().Be(HRESULT.S_FALSE);
fetched[0].Should().Be(0);
((HRESULT)enumerator.Next(0, null, null)).Should().Be(HRESULT.S_FALSE);
// Fetch negative.
((HRESULT)enumerator.Next(-1, result, fetched)).Should().Be(HRESULT.S_FALSE);
fetched[0].Should().Be(0);
((HRESULT)enumerator.Next(-1, null, null)).Should().Be(HRESULT.S_FALSE);
// Null.
((Action)(() => enumerator.Next(1, null, fetched))).Should().Throw<NullReferenceException>();
// Fetch first.
((HRESULT)enumerator.Next(i + 1, result, fetched)).Should().Be(HRESULT.S_OK);
result[0].cfFormat.Should().Be(unchecked((short)(ushort)(DataFormats.GetFormat(format1).Id)));
result[0].dwAspect.Should().Be(DVASPECT.DVASPECT_CONTENT);
result[0].lindex.Should().Be(-1);
result[0].ptd.Should().Be(IntPtr.Zero);
result[0].tymed.Should().Be(expectedTymed);
result[1].cfFormat.Should().Be(0);
fetched[0].Should().Be(1);
// Fetch second.
((HRESULT)enumerator.Next(i + 1, result, fetched)).Should().Be(HRESULT.S_OK);
result[0].cfFormat.Should().NotBe(0);
result[0].dwAspect.Should().Be(DVASPECT.DVASPECT_CONTENT);
result[0].lindex.Should().Be(-1);
result[0].ptd.Should().Be(IntPtr.Zero);
result[0].tymed.Should().Be(TYMED.TYMED_HGLOBAL);
result[1].cfFormat.Should().Be(0);
fetched[0].Should().Be(1);
// Fetch another.
((HRESULT)enumerator.Next(1, null, null)).Should().Be(HRESULT.S_FALSE);
((HRESULT)enumerator.Next(1, null, null)).Should().Be(HRESULT.S_FALSE);
// Reset.
((HRESULT)enumerator.Reset()).Should().Be(HRESULT.S_OK);
}
}
[WinFormsTheory]
[MemberData(nameof(EnumFormatEtc_Default_TheoryData))]
public void DataObject_EnumFormatEtc_CloneNullFormats_Success(int celt)
{
Mock<IDataObject> mockDataObject = new(MockBehavior.Strict);
mockDataObject
.Setup(o => o.GetFormats())
.Returns((string[])null);
DataObject dataObject = new(mockDataObject.Object);
IComDataObject comDataObject = dataObject;
IEnumFORMATETC source = comDataObject.EnumFormatEtc(DATADIR.DATADIR_GET);
source.Clone(out IEnumFORMATETC enumerator);
enumerator.Should().NotBeNull();
var result = new FORMATETC[1];
int[] fetched = new int[1];
for (int i = 0; i < 2; i++)
{
((HRESULT)enumerator.Next(celt, result, fetched)).Should().Be(HRESULT.S_FALSE);
result[0].cfFormat.Should().Be(0);
fetched[0].Should().Be(0);
((HRESULT)enumerator.Next(celt, null, null)).Should().Be(HRESULT.S_FALSE);
((HRESULT)enumerator.Reset()).Should().Be(HRESULT.S_OK);
}
}
[WinFormsTheory]
[InlineData(DATADIR.DATADIR_SET)]
[InlineData(DATADIR.DATADIR_GET - 1)]
[InlineData(DATADIR.DATADIR_SET + 1)]
public void DataObject_EnumFormatEtc_InvokeNotGet_ThrowsExternalException(DATADIR dwDirection)
{
DataObject dataObject = new();
IComDataObject comDataObject = dataObject;
((Action)(() => comDataObject.EnumFormatEtc(dwDirection))).Should().Throw<ExternalException>();
}
public static TheoryData<DATADIR, IEnumFORMATETC> EnumFormatEtc_CustomComDataObject_TheoryData()
{
TheoryData<DATADIR, IEnumFORMATETC> theoryData = [];
Mock<IEnumFORMATETC> mockEnumFormatEtc = new(MockBehavior.Strict);
theoryData.Add(DATADIR.DATADIR_GET, mockEnumFormatEtc.Object);
theoryData.Add(DATADIR.DATADIR_SET, mockEnumFormatEtc.Object);
theoryData.Add(DATADIR.DATADIR_GET - 1, mockEnumFormatEtc.Object);
theoryData.Add(DATADIR.DATADIR_SET + 1, mockEnumFormatEtc.Object);
return theoryData;
}
[WinFormsTheory]
[MemberData(nameof(EnumFormatEtc_CustomComDataObject_TheoryData))]
public void DataObject_EnumFormatEtc_InvokeCustomComDataObject_Success(DATADIR dwDirection, IEnumFORMATETC result)
{
Mock<IComDataObject> mockComDataObject = new(MockBehavior.Strict);
mockComDataObject
.Setup(o => o.EnumFormatEtc(dwDirection))
.Returns(result)
.Verifiable();
DataObject dataObject = new(mockComDataObject.Object);
IComDataObject comDataObject = dataObject;
comDataObject.EnumFormatEtc(dwDirection).Should().BeSameAs(result);
mockComDataObject.Verify(o => o.EnumFormatEtc(dwDirection), Times.Once());
}
public static TheoryData<TextDataFormat, short> GetDataHere_Text_TheoryData() => new()
{
{ TextDataFormat.Rtf, (short)DataFormats.GetFormat(DataFormats.Rtf).Id },
{ TextDataFormat.Html, (short)DataFormats.GetFormat(DataFormats.Html).Id }
};
[WinFormsTheory]
[MemberData(nameof(GetDataHere_Text_TheoryData))]
public unsafe void IComDataObjectGetDataHere_Text_Success(TextDataFormat textDataFormat, short cfFormat)
{
DataObject dataObject = new();
dataObject.SetText("text", textDataFormat);
IComDataObject iComDataObject = dataObject;
FORMATETC formatetc = new()
{
tymed = TYMED.TYMED_HGLOBAL,
cfFormat = cfFormat
};
STGMEDIUM stgMedium = new()
{
tymed = TYMED.TYMED_HGLOBAL
};
HGLOBAL handle = PInvokeCore.GlobalAlloc(
GLOBAL_ALLOC_FLAGS.GMEM_MOVEABLE | GLOBAL_ALLOC_FLAGS.GMEM_ZEROINIT,
1);
try
{
stgMedium.unionmember = handle;
iComDataObject.GetDataHere(ref formatetc, ref stgMedium);
sbyte* pChar = *(sbyte**)stgMedium.unionmember;
new string(pChar).Should().Be("text");
}
finally
{
PInvokeCore.GlobalFree(handle);
}
}
public static TheoryData<TextDataFormat, short> GetDataHere_UnicodeText_TheoryData() => new()
{
{ TextDataFormat.Text, (short)CLIPBOARD_FORMAT.CF_UNICODETEXT },
{ TextDataFormat.UnicodeText, (short)CLIPBOARD_FORMAT.CF_UNICODETEXT }
};
[WinFormsTheory]
[MemberData(nameof(GetDataHere_UnicodeText_TheoryData))]
public unsafe void IComDataObjectGetDataHere_UnicodeText_Success(TextDataFormat textDataFormat, short cfFormat)
{
DataObject dataObject = new();
dataObject.SetText("text", textDataFormat);
IComDataObject iComDataObject = dataObject;
FORMATETC formatetc = new()
{
tymed = TYMED.TYMED_HGLOBAL,
cfFormat = cfFormat
};
STGMEDIUM stgMedium = new()
{
tymed = TYMED.TYMED_HGLOBAL
};
HGLOBAL handle = PInvokeCore.GlobalAlloc(
GLOBAL_ALLOC_FLAGS.GMEM_MOVEABLE | GLOBAL_ALLOC_FLAGS.GMEM_ZEROINIT,
1);
try
{
stgMedium.unionmember = handle;
iComDataObject.GetDataHere(ref formatetc, ref stgMedium);
char* pChar = *(char**)stgMedium.unionmember;
new string(pChar).Should().Be("text");
}
finally
{
PInvokeCore.GlobalFree(handle);
}
}
[WinFormsTheory]
[MemberData(nameof(GetDataHere_Text_TheoryData))]
public unsafe void IComDataObjectGetDataHere_TextNoData_ThrowsArgumentException(TextDataFormat textDataFormat, short cfFormat)
{
DataObject dataObject = new();
dataObject.SetText("text", textDataFormat);
IComDataObject iComDataObject = dataObject;
FORMATETC formatetc = new()
{
tymed = TYMED.TYMED_HGLOBAL,
cfFormat = cfFormat
};
STGMEDIUM stgMedium = new()
{
tymed = TYMED.TYMED_HGLOBAL
};
((Action)(() => iComDataObject.GetDataHere(ref formatetc, ref stgMedium))).Should().Throw<ArgumentException>();
}
[WinFormsTheory]
[MemberData(nameof(GetDataHere_UnicodeText_TheoryData))]
public unsafe void IComDataObjectGetDataHere_UnicodeTextNoData_ThrowsArgumentException(TextDataFormat textDataFormat, short cfFormat)
{
DataObject dataObject = new();
dataObject.SetText("text", textDataFormat);
IComDataObject iComDataObject = dataObject;
FORMATETC formatetc = new()
{
tymed = TYMED.TYMED_HGLOBAL,
cfFormat = cfFormat
};
STGMEDIUM stgMedium = new()
{
tymed = TYMED.TYMED_HGLOBAL
};
((Action)(() => iComDataObject.GetDataHere(ref formatetc, ref stgMedium))).Should().Throw<ArgumentException>();
}
[WinFormsFact]
public unsafe void IComDataObjectGetDataHere_FileNames_Success()
{
DataObject dataObject = new();
dataObject.SetFileDropList(["Path1", "Path2"]);
IComDataObject iComDataObject = dataObject;
FORMATETC formatetc = new()
{
tymed = TYMED.TYMED_HGLOBAL,
cfFormat = (short)CLIPBOARD_FORMAT.CF_HDROP
};
STGMEDIUM stgMedium = new()
{
tymed = TYMED.TYMED_HGLOBAL
};
HGLOBAL handle = PInvokeCore.GlobalAlloc(
GLOBAL_ALLOC_FLAGS.GMEM_MOVEABLE | GLOBAL_ALLOC_FLAGS.GMEM_ZEROINIT,
1);
try
{
stgMedium.unionmember = handle;
iComDataObject.GetDataHere(ref formatetc, ref stgMedium);
DROPFILES* pDropFiles = *(DROPFILES**)stgMedium.unionmember;
pDropFiles->pFiles.Should().Be(20u);
pDropFiles->pt.Should().Be(Point.Empty);
pDropFiles->fNC.Should().Be(BOOL.FALSE);
pDropFiles->fWide.Should().Be(BOOL.TRUE);
char* text = (char*)IntPtr.Add((IntPtr)pDropFiles, (int)pDropFiles->pFiles);
new string(text, 0, "Path1".Length + 1 + "Path2".Length + 1 + 1).Should().Be("Path1\0Path2\0\0");
}
finally
{
PInvokeCore.GlobalFree(handle);
}
}
[WinFormsFact]
public unsafe void IComDataObjectGetDataHere_EmptyFileNames_Success()
{
DataObject dataObject = new();
dataObject.SetFileDropList([]);
IComDataObject iComDataObject = dataObject;
FORMATETC formatetc = new()
{
tymed = TYMED.TYMED_HGLOBAL,
cfFormat = (short)CLIPBOARD_FORMAT.CF_HDROP
};
STGMEDIUM stgMedium = new()
{
tymed = TYMED.TYMED_HGLOBAL
};
HGLOBAL handle = PInvokeCore.GlobalAlloc(
GLOBAL_ALLOC_FLAGS.GMEM_MOVEABLE | GLOBAL_ALLOC_FLAGS.GMEM_ZEROINIT,
(uint)sizeof(DROPFILES));
try
{
stgMedium.unionmember = handle;
iComDataObject.GetDataHere(ref formatetc, ref stgMedium);
DROPFILES* pDropFiles = *(DROPFILES**)stgMedium.unionmember;
pDropFiles->pFiles.Should().Be(0u);
pDropFiles->pt.Should().Be(Point.Empty);
pDropFiles->fNC.Should().Be(BOOL.FALSE);
pDropFiles->fWide.Should().Be(BOOL.FALSE);
}
finally
{
PInvokeCore.GlobalFree(handle);
}
}
[WinFormsFact]
public unsafe void IComDataObjectGetDataHere_FileNamesNoData_ThrowsArgumentException()
{
DataObject dataObject = new();
dataObject.SetFileDropList(["Path1", "Path2"]);
IComDataObject iComDataObject = dataObject;
FORMATETC formatetc = new()
{
tymed = TYMED.TYMED_HGLOBAL,
cfFormat = (short)CLIPBOARD_FORMAT.CF_HDROP
};
STGMEDIUM stgMedium = new()
{
tymed = TYMED.TYMED_HGLOBAL
};
((Action)(() => iComDataObject.GetDataHere(ref formatetc, ref stgMedium))).Should().Throw<ArgumentException>();
}
[WinFormsFact]
public unsafe void IComDataObjectGetDataHere_EmptyFileNamesNoData_Success()
{
DataObject dataObject = new();
dataObject.SetFileDropList([]);
IComDataObject iComDataObject = dataObject;
FORMATETC formatetc = new()
{
tymed = TYMED.TYMED_HGLOBAL,
cfFormat = (short)CLIPBOARD_FORMAT.CF_HDROP
};
STGMEDIUM stgMedium = new()
{
tymed = TYMED.TYMED_HGLOBAL
};
iComDataObject.GetDataHere(ref formatetc, ref stgMedium);
}
[WinFormsTheory]
[InlineData(TYMED.TYMED_FILE, TYMED.TYMED_FILE)]
[InlineData(TYMED.TYMED_ISTORAGE, TYMED.TYMED_ISTORAGE)]
[InlineData(TYMED.TYMED_MFPICT, TYMED.TYMED_MFPICT)]
[InlineData(TYMED.TYMED_ENHMF, TYMED.TYMED_ENHMF)]
[InlineData(TYMED.TYMED_NULL, TYMED.TYMED_NULL)]
public void IComDataObjectGetDataHere_InvalidTymed_ThrowsCOMException(TYMED formatetcTymed, TYMED stgMediumTymed)
{
DataObject dataObject = new();
IComDataObject iComDataObject = dataObject;
FORMATETC formatetc = new()
{
tymed = formatetcTymed
};
STGMEDIUM stgMedium = new()
{
tymed = stgMediumTymed
};
((Action)(() => iComDataObject.GetDataHere(ref formatetc, ref stgMedium))).Should().Throw<COMException>()
.Where(e => e.HResult == HRESULT.DV_E_TYMED);
}
[WinFormsTheory]
[InlineData(TYMED.TYMED_HGLOBAL, TYMED.TYMED_HGLOBAL, (short)CLIPBOARD_FORMAT.CF_UNICODETEXT)]
[InlineData(TYMED.TYMED_HGLOBAL, TYMED.TYMED_HGLOBAL, (short)CLIPBOARD_FORMAT.CF_HDROP)]
[InlineData(TYMED.TYMED_ISTREAM, TYMED.TYMED_ISTREAM, (short)CLIPBOARD_FORMAT.CF_UNICODETEXT)]
[InlineData(TYMED.TYMED_ISTREAM, TYMED.TYMED_ISTREAM, (short)CLIPBOARD_FORMAT.CF_HDROP)]
[InlineData(TYMED.TYMED_GDI, TYMED.TYMED_GDI, (short)CLIPBOARD_FORMAT.CF_UNICODETEXT)]
[InlineData(TYMED.TYMED_GDI, TYMED.TYMED_GDI, (short)CLIPBOARD_FORMAT.CF_HDROP)]
public void IComDataObjectGetDataHere_NoDataPresentNoData_ThrowsCOMException(TYMED formatetcTymed, TYMED stgMediumTymed, short cfFormat)
{
DataObject dataObject = new();
IComDataObject iComDataObject = dataObject;
FORMATETC formatetc = new()
{
tymed = formatetcTymed,
cfFormat = cfFormat
};
STGMEDIUM stgMedium = new()
{
tymed = stgMediumTymed
};
((Action)(() => iComDataObject.GetDataHere(ref formatetc, ref stgMedium))).Should().Throw<COMException>()
.Where(e => e.HResult == HRESULT.DV_E_FORMATETC);
}
private class DerivedDataObject : DataObject { }
private class CustomDataObject : IComDataObject, IDataObject
{
public int DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection) => throw new NotImplementedException();
public void DUnadvise(int connection) => throw new NotImplementedException();
public int EnumDAdvise(out IEnumSTATDATA enumAdvise) => throw new NotImplementedException();
public IEnumFORMATETC EnumFormatEtc(DATADIR direction) => throw new NotImplementedException();
public int GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut) => throw new NotImplementedException();
public void GetData(ref FORMATETC format, out STGMEDIUM medium) => throw new NotImplementedException();
public object GetData(string format, bool autoConvert) => throw new NotImplementedException();
public object GetData(string format) => throw new NotImplementedException();
public object GetData(Type format) => throw new NotImplementedException();
public void GetDataHere(ref FORMATETC format, ref STGMEDIUM medium) => throw new NotImplementedException();
public bool GetDataPresent(string format, bool autoConvert) => throw new NotImplementedException();
public bool GetDataPresent(string format) => throw new NotImplementedException();
public bool GetDataPresent(Type format) => throw new NotImplementedException();
public string[] GetFormats(bool autoConvert) => throw new NotImplementedException();
public string[] GetFormats() => throw new NotImplementedException();
public int QueryGetData(ref FORMATETC format) => throw new NotImplementedException();
public void SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release) => throw new NotImplementedException();
public void SetData(string format, bool autoConvert, object data) => throw new NotImplementedException();
public void SetData(string format, object data) => throw new NotImplementedException();
public void SetData(Type format, object data) => throw new NotImplementedException();
public void SetData(object data) => throw new NotImplementedException();
}
public static IEnumerable<object[]> DataObjectMockRoundTripData()
{
yield return new object[] { new DataObject() };
yield return new object[] { new DerivedDataObject() };
yield return new object[] { new CustomDataObject() };
}
[WinFormsTheory]
[MemberData(nameof(DataObjectMockRoundTripData))]
public unsafe void DataObject_MockRoundTrip_OutData_IsSame(object data)
{
dynamic controlAccessor = typeof(Control).TestAccessor().Dynamic;
var dropTargetAccessor = typeof(DropTarget).TestAccessor();
DataObject inData = controlAccessor.CreateRuntimeDataObjectForDrag(data);
if (data is CustomDataObject)
{
inData.Should().NotBeSameAs(data);
}
else
{
inData.Should().BeSameAs(data);
}
using var inDataPtr = ComHelpers.GetComScope<Com.IDataObject>(inData);
IDataObject outData = dropTargetAccessor.CreateDelegate<CreateWinFormsDataObjectForOutgoingDropData>()(inDataPtr);
outData.Should().BeSameAs(data);
}
public static IEnumerable<object[]> DataObjectWithJsonMockRoundTripData()
{
yield return new object[] { new DataObject() };
yield return new object[] { new DerivedDataObject() };
}
[WinFormsTheory]
[MemberData(nameof(DataObjectWithJsonMockRoundTripData))]
public unsafe void DataObject_WithJson_MockRoundTrip_OutData_IsSame(DataObject data)
{
dynamic controlAccessor = typeof(Control).TestAccessor().Dynamic;
var dropTargetAccessor = typeof(DropTarget).TestAccessor();
Point point = new() { X = 1, Y = 1 };
data.SetDataAsJson("point", point);
DataObject inData = controlAccessor.CreateRuntimeDataObjectForDrag(data);
inData.Should().BeSameAs(data);
using var inDataPtr = ComHelpers.GetComScope<Com.IDataObject>(inData);
IDataObject outData = dropTargetAccessor.CreateDelegate<CreateWinFormsDataObjectForOutgoingDropData>()(inDataPtr);
outData.Should().BeSameAs(data);
ITypedDataObject typedOutData = outData.Should().BeAssignableTo<ITypedDataObject>().Subject;
typedOutData.GetDataPresent("point").Should().BeTrue();
typedOutData.TryGetData("point", out Point deserialized).Should().BeTrue();
deserialized.Should().BeEquivalentTo(point);
}
[WinFormsFact]
public unsafe void DataObject_StringData_MockRoundTrip_IsWrapped()
{
string testString = "Test";
dynamic accessor = typeof(Control).TestAccessor().Dynamic;
var dropTargetAccessor = typeof(DropTarget).TestAccessor();
DataObject inData = accessor.CreateRuntimeDataObjectForDrag(testString);
inData.Should().BeAssignableTo<DataObject>();
using var inDataPtr = ComHelpers.GetComScope<Com.IDataObject>(inData);
IDataObject outData = dropTargetAccessor.CreateDelegate<CreateWinFormsDataObjectForOutgoingDropData>()(inDataPtr);
outData.Should().BeSameAs(inData);
outData.GetData(typeof(string)).Should().Be(testString);
}
[WinFormsFact]
public unsafe void DataObject_IDataObject_MockRoundTrip_IsWrapped()
{
CustomIDataObject data = new();
dynamic accessor = typeof(Control).TestAccessor().Dynamic;
var dropTargetAccessor = typeof(DropTarget).TestAccessor();
DataObject inData = accessor.CreateRuntimeDataObjectForDrag(data);
inData.Should().BeAssignableTo<DataObject>();
inData.Should().NotBeSameAs(data);
using var inDataPtr = ComHelpers.GetComScope<Com.IDataObject>(inData);
IDataObject outData = dropTargetAccessor.CreateDelegate<CreateWinFormsDataObjectForOutgoingDropData>()(inDataPtr);
outData.Should().BeSameAs(data);
}
[WinFormsFact]
public unsafe void DataObject_ComTypesIDataObject_MockRoundTrip_IsWrapped()
{
CustomComTypesDataObject data = new();
dynamic accessor = typeof(Control).TestAccessor().Dynamic;
var dropTargetAccessor = typeof(DropTarget).TestAccessor();
DataObject inData = accessor.CreateRuntimeDataObjectForDrag(data);
inData.Should().NotBeSameAs(data);
inData.Should().BeAssignableTo<DataObject>();
using var inDataPtr = ComHelpers.GetComScope<Com.IDataObject>(inData);
IDataObject outData = dropTargetAccessor.CreateDelegate<CreateWinFormsDataObjectForOutgoingDropData>()(inDataPtr);
outData.Should().BeSameAs(inData);
}
[WinFormsFact]
public unsafe void DataObject_Native_GetFormats_ReturnsExpected()
{
DataObject native = new();
using Bitmap bitmap = new(10, 10);
native.SetImage(bitmap);
string customFormat = "customFormat";
native.SetData(customFormat, "custom");
// Simulate receiving DataObject from native.
DataObject data = new(ComHelpers.GetComPointer<Com.IDataObject>(native));
data.GetDataPresent(typeof(Bitmap)).Should().BeTrue();
data.GetDataPresent(customFormat).Should().BeTrue();
data.GetDataPresent("notExist").Should().BeFalse();
data.GetFormats().Should().BeEquivalentTo([typeof(Bitmap).FullName, nameof(Bitmap), customFormat]);
}
[WinFormsFact]
public unsafe void DataObject_Native_GetData_SerializationFailure()
{
using Font value = new("Arial", 10);
using BinaryFormatterScope scope = new(enable: true);
// We are blocking managed font from being serialized as a Locale format.
DataObject native = new(DataFormats.Locale, value);
// Simulate receiving DataObject from native.
// Clipboard.SetDataObject(native, copy: true);
var comDataObject = ComHelpers.GetComPointer<Com.IDataObject>(native);
Com.FORMATETC formatetc = new()
{
tymed = (uint)TYMED.TYMED_HGLOBAL,
cfFormat = (ushort)CLIPBOARD_FORMAT.CF_LOCALE
};
comDataObject->GetData(formatetc, out Com.STGMEDIUM medium);
// Validate that HGLOBAL had been freed when handling an error.
medium.hGlobal.IsNull.Should().BeTrue();
}
[WinFormsFact]
public void DataObject_SetDataAsJson_DataObject_Throws()
{
string format = "format";
DataObject dataObject = new();
Action action = () => dataObject.SetDataAsJson(format, new DataObject());
action.Should().Throw<InvalidOperationException>();
Action dataObjectSet2 = () => dataObject.SetDataAsJson(format, new DerivedDataObject());
dataObjectSet2.Should().NotThrow();
}
[WinFormsFact]
public void DataObject_SetDataAsJson_ReturnsExpected()
{
SimpleTestData testData = new() { X = 1, Y = 1 };
DataObject dataObject = new();
string format = "testData";
dataObject.SetDataAsJson(format, testData);
dataObject.GetDataPresent(format).Should().BeTrue();
dataObject.TryGetData(format, out SimpleTestData deserialized).Should().BeTrue();
deserialized.Should().BeEquivalentTo(testData);
}
[WinFormsFact]
public void DataObject_SetDataAsJson_Wrapped_ReturnsExpected()
{
SimpleTestData testData = new() { X = 1, Y = 1 };
DataObject dataObject = new();
string format = "testData";
dataObject.SetDataAsJson(format, testData);
DataObject wrapped = new(dataObject);
wrapped.GetDataPresent(format).Should().BeTrue();
wrapped.TryGetData(format, out SimpleTestData deserialized).Should().BeTrue();
deserialized.Should().BeEquivalentTo(testData);
}
[WinFormsFact]
public void DataObject_SetDataAsJson_MultipleData_ReturnsExpected()
{
SimpleTestData testData1 = new() { X = 1, Y = 1 };
SimpleTestData testData2 = new() { Y = 2, X = 2 };
DataObject data = new();
data.SetDataAsJson("testData1", testData1);
data.SetDataAsJson("testData2", testData2);
data.SetData("Mystring", "test");
data.TryGetData("testData1", out SimpleTestData deserializedTestData1).Should().BeTrue();
deserializedTestData1.Should().BeEquivalentTo(testData1);
data.TryGetData("testData2", out SimpleTestData deserializedTestData2).Should().BeTrue();
deserializedTestData2.Should().BeEquivalentTo(testData2);
data.TryGetData("Mystring", out string deserializedString).Should().BeTrue();
deserializedString.Should().Be("test");
}
[WinFormsFact]
public void DataObject_SetDataAsJson_CustomJsonConverter_ReturnsExpected()
{
// This test demonstrates one way users can achieve custom JSON serialization behavior if the
// default JSON serialization behavior that is used in SetDataAsJson APIs is not enough for their scenario.
Font font = new("Consolas", emSize: 10);
WeatherForecast forecast = new()
{
Date = DateTimeOffset.Now,
TemperatureCelsius = 25,
Summary = "Hot",
Font = font
};
DataObject dataObject = new();
dataObject.SetDataAsJson("custom", forecast);
dataObject.TryGetData("custom", out WeatherForecast deserialized).Should().BeTrue();
string offsetFormat = "MM/dd/yyyy";
deserialized.Date.ToString(offsetFormat).Should().Be(forecast.Date.ToString(offsetFormat));
deserialized.TemperatureCelsius.Should().Be(forecast.TemperatureCelsius);
deserialized.Summary.Should().Be($"{forecast.Summary} custom!");
deserialized.Font.Should().Be(font);
}
[JsonConverter(typeof(WeatherForecastJsonConverter))]
private class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string Summary { get; set; }
public Font Font { get; set; }
}
private class WeatherForecastJsonConverter : JsonConverter<WeatherForecast>
{
public override WeatherForecast Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
WeatherForecast result = new();
string fontFamily = null;
int size = -1;
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
if (fontFamily is null || size == -1)
{
throw new JsonException();
}
result.Font = new(fontFamily, size);
return result;
}
if (reader.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException();
}
string propertyName = reader.GetString();
reader.Read();
switch (propertyName)
{
case nameof(WeatherForecast.Date):
result.Date = DateTimeOffset.ParseExact(reader.GetString(), "MM/dd/yyyy", null);
break;
case nameof(WeatherForecast.TemperatureCelsius):
result.TemperatureCelsius = reader.GetInt32();
break;
case nameof(WeatherForecast.Summary):
result.Summary = reader.GetString();
break;
case nameof(Font.FontFamily):
fontFamily = reader.GetString();
break;
case nameof(Font.Size):
size = reader.GetInt32();
break;
default:
throw new JsonException();
}
}
throw new JsonException();
}
public override void Write(Utf8JsonWriter writer, WeatherForecast value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteString(nameof(WeatherForecast.Date), value.Date.ToString("MM/dd/yyyy"));
writer.WriteNumber(nameof(WeatherForecast.TemperatureCelsius), value.TemperatureCelsius);
writer.WriteString(nameof(WeatherForecast.Summary), $"{value.Summary} custom!");
writer.WriteString(nameof(Font.FontFamily), value.Font.FontFamily.Name);
writer.WriteNumber(nameof(Font.Size), value.Font.Size);
writer.WriteEndObject();
}
}
[WinFormsFact]
public void DataObject_SetDataAsJson_NullData_Throws()
{
DataObject dataObject = new();
Action dataObjectSet = () => dataObject.SetDataAsJson<string>(null);
dataObjectSet.Should().Throw<ArgumentNullException>();
}
[WinFormsTheory]
[CommonMemberData(typeof(DataObjectTestHelpers), nameof(DataObjectTestHelpers.StringFormat))]
[CommonMemberData(typeof(DataObjectTestHelpers), nameof(DataObjectTestHelpers.BitmapFormat))]
[CommonMemberData(typeof(DataObjectTestHelpers), nameof(DataObjectTestHelpers.UndefinedRestrictedFormat))]
public void DataObject_SetDataAsJson_RestrictedFormats_NotJsonSerialized(string format)
{
DataObject dataObject = new();
dataObject.SetDataAsJson(format, 1);
object storedData = dataObject.TestAccessor().Dynamic._innerData.GetData(format);
storedData.Should().NotBeAssignableTo<IJsonData>();
dataObject.GetData(format).Should().Be(1);
}
[WinFormsTheory]
[CommonMemberData(typeof(DataObjectTestHelpers), nameof(DataObjectTestHelpers.UnboundedFormat))]
public void DataObject_SetDataAsJson_NonRestrictedFormat_NotJsonSerialized(string format)
{
DataObject data = new();
data.SetDataAsJson(format, 1);
object storedData = data.TestAccessor().Dynamic._innerData.GetData(format);
storedData.Should().NotBeAssignableTo<IJsonData>();
data.GetData(format).Should().Be(1);
}
[WinFormsTheory]
[CommonMemberData(typeof(DataObjectTestHelpers), nameof(DataObjectTestHelpers.UnboundedFormat))]
public void DataObject_SetDataAsJson_NonRestrictedFormat_JsonSerialized(string format)
{
DataObject data = new();
SimpleTestData testData = new() { X = 1, Y = 1 };
data.SetDataAsJson(format, testData);
object storedData = data.TestAccessor().Dynamic._innerData.GetData(format);
storedData.Should().BeOfType<JsonData<SimpleTestData>>();
// We don't expose JsonData<T> in public legacy API
data.GetData(format).Should().BeNull();
// For the clipboard, we don't expose JsonData<T> either for in proc scenarios.
Clipboard.SetDataObject(data, copy: false);
Clipboard.GetData(format).Should().BeNull();
}
[WinFormsFact]
public void DataObject_SetDataAsJson_WrongType_ReturnsNull()
{
DataObject dataObject = new();
dataObject.SetDataAsJson("test", new SimpleTestData() { X = 1, Y = 1 });
dataObject.TryGetData("test", out Bitmap data).Should().BeFalse();
data.Should().BeNull();
}
}
|