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

Untangle test utils refs #3013

Merged
merged 16 commits into from
Jun 3, 2020
Prev Previous commit
Next Next commit
Fix IPictureTests tests
  • Loading branch information
RussKie committed Jun 3, 2020
commit 30594a1f03fd3f748d56b095705a9b367028a4d9
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using static Interop;
using static Interop.Ole32;

namespace System.Windows.Forms.Primitives.Tests.Interop.Mocks
{
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
internal class MockAxHost
{
private static Guid ipictureDisp_Guid = typeof(IPictureDisp).GUID;
private static Guid ipicture_Guid = typeof(IPicture).GUID;

public MockAxHost(string clsidString)
{
}

public static IPictureDisp GetIPictureDispFromPicture(Image image)
{
PICTDESC desc = GetPICTDESCFromPicture(image);
return (IPictureDisp)OleCreatePictureIndirect(ref desc, ref ipictureDisp_Guid, fOwn: BOOL.TRUE);
}

public static IPicture GetIPictureFromCursor(IntPtr cursorHandle)
{
PICTDESC desc = PICTDESC.FromIcon(Icon.FromHandle(cursorHandle), copy: true);
return (IPicture)OleCreatePictureIndirect(ref desc, ref ipicture_Guid, fOwn: BOOL.TRUE);
}

public static IPicture GetIPictureFromPicture(Image image)
{
PICTDESC desc = GetPICTDESCFromPicture(image);
return (IPicture)OleCreatePictureIndirect(ref desc, ref ipicture_Guid, fOwn: BOOL.TRUE);
}

public static Image GetPictureFromIPicture(object picture)
{
int hPal = default;
IPicture pict = (IPicture)picture;
PICTYPE type = (PICTYPE)pict.Type;
if (type == PICTYPE.BITMAP)
{
try
{
hPal = pict.hPal;
}
catch (COMException)
{
}
}

return GetPictureFromParams(pict.Handle, type, hPal, pict.Width, pict.Height);
}

public static Image GetPictureFromIPictureDisp(object picture)
{
if (picture == null)
{
return null;
}

int hPal = default;
IPictureDisp pict = (IPictureDisp)picture;
PICTYPE type = (PICTYPE)pict.Type;
if (type == PICTYPE.BITMAP)
{
try
{
hPal = pict.hPal;
}
catch (COMException)
{
}
}

Image image = GetPictureFromParams(pict.Handle, type, hPal, pict.Width, pict.Height);
GC.KeepAlive(pict);
return image;
}

private static PICTDESC GetPICTDESCFromPicture(Image image)
{
if (image is Bitmap bmp)
{
return PICTDESC.FromBitmap(bmp);
}

if (image is Metafile mf)
{
return PICTDESC.FromMetafile(mf);
}

throw new ArgumentException("AXUnknownImage", nameof(image));
}

private static Image GetPictureFromParams(
int handle,
PICTYPE type,
int paletteHandle,
int width,
int height)
{
switch (type)
{
case PICTYPE.ICON:
return (Image)Icon.FromHandle((IntPtr)handle).Clone();
case PICTYPE.METAFILE:
WmfPlaceableFileHeader header = new WmfPlaceableFileHeader
{
BboxRight = (short)width,
BboxBottom = (short)height
};

using (var metafile = new Metafile((IntPtr)handle, header, deleteWmf: false))
{
return (Image)metafile.Clone();
}
case PICTYPE.ENHMETAFILE:
using (var metafile = new Metafile((IntPtr)handle, deleteEmf: false))
{
return (Image)metafile.Clone();
}
case PICTYPE.BITMAP:
return Image.FromHbitmap((IntPtr)handle, (IntPtr)paletteHandle);
case PICTYPE.NONE:
// MSDN says this should not be a valid value, but comctl32 returns it...
return null;
case PICTYPE.UNINITIALIZED:
return null;
default:
throw new ArgumentException("AXUnknownImage", nameof(type));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Drawing;
using static Interop.User32;

namespace System.Windows.Forms.Primitives.Tests.Interop.Mocks
{
public class MockCursor : IDisposable
{
private IntPtr _handle = IntPtr.Zero; // handle to loaded image
private bool _ownHandle = true;
private readonly int _resourceId = 0;

public MockCursor(int nResourceId)
{
// We don't delete stock cursors.
_ownHandle = false;
_resourceId = nResourceId;
_handle = LoadCursorW(IntPtr.Zero, (IntPtr)nResourceId);
}

public void Dispose()
{
if (_handle != IntPtr.Zero)
{
if (_ownHandle)
{
DestroyCursor(_handle);
}
_handle = IntPtr.Zero;
}
}

public IntPtr Handle
{
get
{
if (_handle == IntPtr.Zero)
{
throw new ObjectDisposedException(nameof(MockCursor));
}
return _handle;
}
}

public Size Size
{
get => new Size(GetSystemMetrics(SystemMetric.SM_CXCURSOR), GetSystemMetrics(SystemMetric.SM_CYCURSOR));
}
}
}
Original file line number Diff line number Diff line change
@@ -2,93 +2,99 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

// using System.Drawing;
// using Xunit;
// using static Interop.Ole32;

// namespace System.Windows.Forms.Primitives.Tests.Interop.Ole32
// {
// public class IPictureTests
// {
// [Fact]
// public void GetIPictureFromCursor()
// {
// Cursor arrow = Cursors.Arrow;
// IPicture picture = AxHostAccess.GetIPictureFromCursor(arrow);
// Assert.NotNull(picture);
// Assert.Equal(PICTYPE.ICON, (PICTYPE)picture.Type);

// Assert.Equal(arrow.Size.Height, GdiHelper.HimetricToPixelY(picture.Height));
// Assert.Equal(arrow.Size.Width, GdiHelper.HimetricToPixelX(picture.Width));
// }

// [Fact]
// public void GetIPictureFromImage()
// {
// using Icon icon = Icon.FromHandle(Cursors.Arrow.Handle);
// using Bitmap bitmap = icon.ToBitmap();
// IPicture picture = AxHostAccess.GetIPictureFromPicture(bitmap);
// Assert.NotNull(picture);
// Assert.Equal(PICTYPE.BITMAP, (PICTYPE)picture.Type);

// Assert.Equal(bitmap.Size.Height, GdiHelper.HimetricToPixelY(picture.Height));
// Assert.Equal(bitmap.Size.Width, GdiHelper.HimetricToPixelX(picture.Width));
// }

// [Fact]
// public void GetIPictureDispFromImage()
// {
// using Icon icon = SystemIcons.Question;
// using Bitmap bitmap = icon.ToBitmap();
// IPictureDisp picture = AxHostAccess.GetIPictureDispFromPicture(bitmap);
// Assert.NotNull(picture);
// Assert.Equal(PICTYPE.BITMAP, (PICTYPE)picture.Type);

// Assert.Equal(bitmap.Size.Height, GdiHelper.HimetricToPixelY(picture.Height));
// Assert.Equal(bitmap.Size.Width, GdiHelper.HimetricToPixelX(picture.Width));
// }

// [Fact]
// public void GetPictureFromIPicture()
// {
// using Icon icon = SystemIcons.Exclamation;
// using Bitmap bitmap = icon.ToBitmap();
// IPicture picture = AxHostAccess.GetIPictureFromPicture(bitmap);
// Assert.NotNull(picture);
// using Image image = AxHostAccess.GetPictureFromIPicture(picture);
// Assert.NotNull(image);
// Assert.Equal(bitmap.Size, image.Size);
// }

// [Fact]
// public void GetPictureFromIPictureDisp()
// {
// using Bitmap bitmap = new Bitmap(100, 200);
// IPictureDisp picture = AxHostAccess.GetIPictureDispFromPicture(bitmap);
// Assert.NotNull(picture);
// using Image image = AxHostAccess.GetPictureFromIPictureDisp(picture);
// Assert.NotNull(image);
// Assert.Equal(bitmap.Size, image.Size);
// }

// internal class AxHostAccess : AxHost
// {
// private AxHostAccess() : base(string.Empty) { }

// internal new static IPicture GetIPictureFromCursor(Cursor cursor)
// => (IPicture)AxHost.GetIPictureFromCursor(cursor);

// internal new static IPicture GetIPictureFromPicture(Image image)
// => (IPicture)AxHost.GetIPictureFromPicture(image);

// internal new static IPictureDisp GetIPictureDispFromPicture(Image image)
// => (IPictureDisp)AxHost.GetIPictureDispFromPicture(image);

// internal static Image GetPictureFromIPicture(IPicture picture)
// => GetPictureFromIPicture((object)picture);

// internal static Image GetPictureFromIPictureDisp(IPictureDisp picture)
// => GetPictureFromIPictureDisp((object)picture);
// }
// }
// }
using System.Drawing;
using Xunit;
using System.Windows.Forms.Primitives.Tests.Interop.Mocks;
using static Interop.Ole32;
using static Interop.User32;

namespace System.Windows.Forms.Primitives.Tests.Interop.Ole32
{

[Collection("Sequential")]
public class IPictureTests
{
[Fact]
Copy link
Contributor

@weltkante weltkante Jun 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not introduced by the PR, but this seems wrong, GetIPictureFromCursor calls OleCreatePictureIndirect so the test should be WinFormsFact (or at the very least StaFact). Even if this API is robust enough to be called from MTA it doesn't feel right to rely on it, normally all OLE API is required to have OLE initialized.

Also adding [Collection("Sequential")] is probably not required here, its not testing some globally shared resource like clipboard or drag'n'drop, so I don't see why it should coordinate with other tests.

Copy link
Contributor

@weltkante weltkante Jun 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there were a few tests which were intentionally testing for failure when called on the wrong COM apartment, these may need to remain Fact and not turned to WinFormsFact. I don't see any of these tests in this file though.

Copy link
Contributor

@weltkante weltkante Jun 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This being Fact is also tracked in #3271 so if you want you can defer it and not update it in this PR

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe any of IPictureTests, IDispatchTests or ITypeInfoTests test x-thread access, or failures for that matter.

I copied [Collection("Sequential")] from the other two types, I think it ensures that these tests aren't run in parallel.

Copy link
Contributor

@weltkante weltkante Jun 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe any of IPictureTests, IDispatchTests or ITypeInfoTests test x-thread access, or failures for that matter.

There are unexplained E_FAIL returns from OleCreatePictureIndirect on the CI machine (#3271). The only theory I currently have is that OLE isn't initialized and for some reason the CI machine is less robust than running locally, otherwise I have no idea why the initial call already fails with E_FAIL.

I copied [Collection("Sequential")] from the other two types, I think it ensures that these tests aren't run in parallel.

It ensures that the tests are not run in parallel with other tests having that attribute, so it can protect process wide shared resources. xunit docs say that tests on the same class are never run in parallel, only multiple test classes are run in parallel (can look up the doc link if you need it)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, this is what I meant (sorry for not being clearer).
Given that all of the tests from these test classes test more of less the same area we probably don't want them to run in parallel.

public void GetIPictureFromCursor()
{
using MockCursor arrow = new MockCursor(CursorResourceId.IDC_ARROW);

IPicture picture = MockAxHost.GetIPictureFromCursor(arrow.Handle);
Assert.NotNull(picture);
Assert.Equal(PICTYPE.ICON, (PICTYPE)picture.Type);

Assert.Equal(arrow.Size.Height, GdiHelper.HimetricToPixelY(picture.Height));
Assert.Equal(arrow.Size.Width, GdiHelper.HimetricToPixelX(picture.Width));
}

[Fact]
public void GetIPictureFromImage()
{
using MockCursor arrow = new MockCursor(CursorResourceId.IDC_ARROW);
using Icon icon = Icon.FromHandle(arrow.Handle);
using Bitmap bitmap = icon.ToBitmap();
IPicture picture = MockAxHost.GetIPictureFromPicture(bitmap);
Assert.NotNull(picture);
Assert.Equal(PICTYPE.BITMAP, (PICTYPE)picture.Type);

Assert.Equal(bitmap.Size.Height, GdiHelper.HimetricToPixelY(picture.Height));
Assert.Equal(bitmap.Size.Width, GdiHelper.HimetricToPixelX(picture.Width));
}

[Fact]
public void GetIPictureDispFromImage()
{
using Icon icon = SystemIcons.Question;
using Bitmap bitmap = icon.ToBitmap();
IPictureDisp picture = MockAxHost.GetIPictureDispFromPicture(bitmap);
Assert.NotNull(picture);
Assert.Equal(PICTYPE.BITMAP, (PICTYPE)picture.Type);

Assert.Equal(bitmap.Size.Height, GdiHelper.HimetricToPixelY(picture.Height));
Assert.Equal(bitmap.Size.Width, GdiHelper.HimetricToPixelX(picture.Width));
}

[Fact]
public void GetPictureFromIPicture()
{
using Icon icon = SystemIcons.Exclamation;
using Bitmap bitmap = icon.ToBitmap();
IPicture picture = MockAxHost.GetIPictureFromPicture(bitmap);
Assert.NotNull(picture);
using Image image = MockAxHost.GetPictureFromIPicture(picture);
Assert.NotNull(image);
Assert.Equal(bitmap.Size, image.Size);
}

[Fact]
public void GetPictureFromIPictureDisp()
{
using Bitmap bitmap = new Bitmap(100, 200);
IPictureDisp picture = MockAxHost.GetIPictureDispFromPicture(bitmap);
Assert.NotNull(picture);
using Image image = MockAxHost.GetPictureFromIPictureDisp(picture);
Assert.NotNull(image);
Assert.Equal(bitmap.Size, image.Size);
}

//internal class MockAxHost : AxHost
//{
// private MockAxHost() : base(string.Empty) { }

// internal new static IPicture GetIPictureFromCursor(Cursor cursor)
// => (IPicture)AxHost.GetIPictureFromCursor(cursor);

// internal new static IPicture GetIPictureFromPicture(Image image)
// => (IPicture)AxHost.GetIPictureFromPicture(image);

// internal new static IPictureDisp GetIPictureDispFromPicture(Image image)
// => (IPictureDisp)AxHost.GetIPictureDispFromPicture(image);

// internal static Image GetPictureFromIPicture(IPicture picture)
// => GetPictureFromIPicture((object)picture);

// internal static Image GetPictureFromIPictureDisp(IPictureDisp picture)
// => GetPictureFromIPictureDisp((object)picture);
//}
}
}
Loading