Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a few Clipboard tests #10507

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<!-- SYSLIB5005: System.Formats.Nrbf is experimental -->
<NoWarn>$(NoWarn);SYSLIB5005</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TargetFramework Condition="!$(TargetFramework.Contains('windows'))">$(TargetFramework)-windows</TargetFramework>
</PropertyGroup>

<ItemGroup>
Expand All @@ -19,6 +20,7 @@
<ProjectReference Include="$(WpfSourceDir)PresentationCore\PresentationCore.csproj" />
<ProjectReference Include="$(WpfSourceDir)WindowsBase\WindowsBase.csproj" />
<ProjectReference Include="$(WpfSourceDir)System.Xaml\System.Xaml.csproj" />
<ProjectReference Include="$(WpfSourceDir)Extensions\PresentationFramework-SystemDrawing\PresentationFramework-SystemDrawing.csproj" />
</ItemGroup>

<ItemGroup>
Expand All @@ -29,7 +31,8 @@
<PackageReference Include="System.Configuration.ConfigurationManager" Version="$(SystemConfigurationConfigurationManagerPackageVersion)" />
<PackageReference Include="System.Formats.Nrbf" Version="$(SystemFormatsNrbfVersion)" />
<PackageReference Include="System.Runtime.Serialization.Formatters" Version="$(SystemRuntimeSerializationFormattersPackageVersion)" />
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<PackageReference Include="System.Drawing.Common" Version="$(SystemDrawingCommonVersion)" />
<PackageReference Include="System.Private.Windows.Core.TestUtilities" Version="$(SystemPrivateWindowsCoreTestUtilitiesVersion)" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
// 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.Runtime.InteropServices;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Color = System.Windows.Media.Color;

namespace System.Windows;

// Note: each registered Clipboard format is an OS singleton
// and we should not run this test at the same time as other tests using the same format.
[Collection("Sequential")]
[UISettings(MaxAttempts = 3)]
public class ClipboardTests
{
[WpfFact]
public void SetText_InvokeString_GetReturnsExpected()
{
Clipboard.SetText("text");
Clipboard.GetText().Should().Be("text");
Clipboard.ContainsText().Should().BeTrue();
}

[WpfFact]
public void SetAudio_InvokeByteArray_GetReturnsExpected()
{
byte[] audioBytes = [1, 2, 3];
Clipboard.SetAudio(audioBytes);

Clipboard.GetAudioStream().Should().BeOfType<MemoryStream>().Which.ToArray().Should().Equal(audioBytes);
Clipboard.GetData(DataFormats.WaveAudio).Should().BeOfType<MemoryStream>().Which.ToArray().Should().Equal(audioBytes);
Clipboard.ContainsAudio().Should().BeTrue();
Clipboard.ContainsData(DataFormats.WaveAudio).Should().BeTrue();
}

[WpfFact(Skip = "WinForms difference")]
public void SetAudio_InvokeEmptyByteArray_GetReturnsExpected()
{
byte[] audioBytes = Array.Empty<byte>();
Clipboard.SetAudio(audioBytes);

// Currently fails with CLIPBRD_E_BAD_DATA
Clipboard.GetAudioStream().Should().BeNull();
Clipboard.GetData(DataFormats.WaveAudio).Should().BeNull();
Clipboard.ContainsAudio().Should().BeTrue();
Clipboard.ContainsData(DataFormats.WaveAudio).Should().BeTrue();
}

[WpfFact]
public void SetAudio_NullAudioBytes_ThrowsArgumentNullException()
{
Action action = () => Clipboard.SetAudio((byte[])null!);
action.Should().Throw<ArgumentNullException>().WithParameterName("audioBytes");
}

[WpfFact]
public void Clipboard_SetAudio_InvokeStream_GetReturnsExpected()
{
byte[] audioBytes = [1, 2, 3];
using MemoryStream audioStream = new(audioBytes);
Clipboard.SetAudio(audioStream);

Clipboard.GetAudioStream().Should().BeOfType<MemoryStream>().Which.ToArray().Should().Equal(audioBytes);
Clipboard.GetData(DataFormats.WaveAudio).Should().BeOfType<MemoryStream>().Which.ToArray().Should().Equal(audioBytes);
Clipboard.ContainsAudio().Should().BeTrue();
Clipboard.ContainsData(DataFormats.WaveAudio).Should().BeTrue();
}

[WpfFact(Skip = "WinForms difference")]
public void SetAudio_InvokeEmptyStream_GetReturnsExpected()
{
using MemoryStream audioStream = new();
Clipboard.SetAudio(audioStream);

// Currently fails with CLIPBRD_E_BAD_DATA
Clipboard.GetAudioStream().Should().BeNull();
Clipboard.GetData(DataFormats.WaveAudio).Should().BeNull();
Clipboard.ContainsAudio().Should().BeTrue();
Clipboard.ContainsData(DataFormats.WaveAudio).Should().BeTrue();
}

[WpfFact]
public void SetAudio_NullAudioStream_ThrowsArgumentNullException()
{
Action action = () => Clipboard.SetAudio((Stream)null!);
action.Should().Throw<ArgumentNullException>().WithParameterName("audioStream");
}

[WpfTheory]
// These three fail in WinForms, should probably fail in WPF as well.
// [InlineData("")]
// [InlineData(" ")]
// [InlineData("\t")]
[InlineData(null)]
public void SetData_EmptyOrWhitespaceFormat_ThrowsArgumentException(string? format)
{
Action action = () => Clipboard.SetData(format!, "data");
action.Should().Throw<ArgumentException>().WithParameterName("format");
}

[WpfFact]
public void SetData_Null_Throws()
{
Action action = () => Clipboard.SetData("MyData", data: null!);
action.Should().Throw<ArgumentNullException>().WithParameterName("data");
}

[WpfFact]
public void SetData_NullData_ThrowsArgumentNullException()
{
Action action = () => Clipboard.SetData("MyData", data: null!);
action.Should().Throw<ArgumentNullException>().WithParameterName("data");
}

[WpfFact]
public void SetData_Int_GetReturnsExpected()
{
Clipboard.SetData("format", 1);
Clipboard.GetData("format").Should().Be(1);
Clipboard.ContainsData("format").Should().BeTrue();
}

[WpfFact]
public void SetFileDropList_Invoke_GetReturnsExpected()
{
StringCollection filePaths =
[
"filePath",
"filePath2"
];

Clipboard.SetFileDropList(filePaths);

Clipboard.GetFileDropList().Should().BeEquivalentTo(filePaths);
Clipboard.ContainsFileDropList().Should().BeTrue();
}

[WpfFact]
public void SetFileDropList_NullFilePaths_ThrowsArgumentNullException()
{
Action action = () => Clipboard.SetFileDropList(null!);
// Note: The name will change with the WinForms shared code.
action.Should().Throw<ArgumentNullException>().WithParameterName("fileDropList");
}

[WpfFact]
public void SetFileDropList_EmptyFilePaths_ThrowsArgumentException()
{
Action action = static () => Clipboard.SetFileDropList([]);
action.Should().Throw<ArgumentException>();
}

[WpfTheory]
[InlineData("")]
[InlineData("\0")]
public void SetFileDropList_InvalidFileInPaths_ThrowsArgumentException(string filePath)
{
StringCollection filePaths =
[
filePath
];

Action action = () => Clipboard.SetFileDropList(filePaths);
action.Should().Throw<ArgumentException>();
}

[WpfFact]
public unsafe void SetImage_InvokeBitmap_VerifyPixelColor()
{
WriteableBitmap bitmap = new(10, 10, 96, 96, PixelFormats.Bgra32, palette: null);

// Set a specific pixel to a given color (e.g., set pixel at (1, 2) to red)
Color color = Colors.Red;
byte[] colorData = [color.B, color.G, color.R, color.A];
bitmap.WritePixels(new Int32Rect(1, 2, 1, 1), colorData, 4, 0);

Clipboard.SetImage(bitmap);
Clipboard.ContainsImage().Should().BeTrue();
InteropBitmap result = Clipboard.GetImage().Should().BeOfType<InteropBitmap>().Subject;

// Verify the pixel color
byte[] resultColorData = new byte[4];
result.CopyPixels(new Int32Rect(1, 2, 1, 1), resultColorData, 4, 0);
resultColorData.Should().Equal(colorData);

// Set back the image we just got from the clipboard
Clipboard.SetImage(result);
Clipboard.ContainsImage().Should().BeTrue();
result = Clipboard.GetImage().Should().BeOfType<InteropBitmap>().Subject;

// Verify the pixel color
result.CopyPixels(new Int32Rect(1, 2, 1, 1), resultColorData, 4, 0);
resultColorData.Should().Equal(colorData);
}

[WpfTheory]
[BoolData]
public void SetDataObject_WithMultipleData(bool copy)
{
string testData1 = "test data one";
int testData2 = 42;
DataObject data = new();
data.SetData("testData1", testData1);
data.SetData("testData2", testData2);
Clipboard.SetDataObject(data, copy);

object? result1 = Clipboard.GetData("testData1");
result1.Should().Be(testData1);
object? result2 = Clipboard.GetData("testData2");
result2.Should().Be(testData2);
}

[WpfFact]
public void SetData_Text_Format_AllUpper()
{
// The fact that casing on input matters is likely incorrect, but behavior has been this way.
Clipboard.SetData("TEXT", "Hello, World!");
Clipboard.ContainsText().Should().BeTrue();
Clipboard.ContainsData("TEXT").Should().BeTrue();
Clipboard.ContainsData(DataFormats.Text).Should().BeTrue();
Clipboard.ContainsData(DataFormats.UnicodeText).Should().BeTrue();

IDataObject dataObject = Clipboard.GetDataObject().Should().BeAssignableTo<IDataObject>().Subject;
string[] formats = dataObject.GetFormats();
formats.Should().BeEquivalentTo(["System.String", "UnicodeText", "Text"]);

formats = dataObject.GetFormats(autoConvert: false);
formats.Should().BeEquivalentTo(["Text"]);

// CLIPBRD_E_BAD_DATA returned when trying to get clipboard data. This will no longer throw when using
// the shared clipboard code.
Action action = () => Clipboard.GetText().Should().BeEmpty();
action.Should().Throw<ExternalException>();
action = () => Clipboard.GetText(TextDataFormat.Text).Should().BeEmpty();
action.Should().Throw<ExternalException>();
action = () => Clipboard.GetText(TextDataFormat.UnicodeText).Should().BeEmpty();
action.Should().Throw<ExternalException>();

Clipboard.GetData("System.String").Should().BeNull();
action = () => Clipboard.GetData("TEXT").Should().BeNull();
action.Should().Throw<ExternalException>();
}
}

This file was deleted.

This file was deleted.

Loading