File: System\Windows\Forms\AxHost.PropertyBagStreamTests.cs
Web Access
Project: src\src\System.Windows.Forms\tests\UnitTests\System.Windows.Forms.Tests.csproj (System.Windows.Forms.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Runtime.InteropServices;
using Windows.Win32.System.Variant;
using Windows.Win32.System.Com.StructuredStorage;
 
namespace System.Windows.Forms.Tests;
 
public unsafe class AxHost_PropertyBagStreamTests
{
    [Fact]
    public void PropertyBagStream_WriteReadRoundTrip_FormatterEnabled()
    {
        using BinaryFormatterScope formatterScope = new(enable: true);
        AxHost.PropertyBagStream bag = new();
        // cs/deserialization-unexpected-subtypes
        HRESULT hr = bag.Write("Integer", (VARIANT)42); // CodeQL[SM02229] : Testing legacy feature. This is a safe use of VARIANT because the data is trusted and the types are controlled and validated.
        Assert.True(hr.Succeeded);
        NameClass obj = new() { Name = "Hamlet" };
        // cs/deserialization-unexpected-subtypes
        hr = bag.Write("Object", VARIANT.FromObject(obj)); // CodeQL[SM02229] : Testing legacy feature. This is a safe use of VARIANT because the data is trusted and the types are controlled and validated.
        Assert.True(hr.Succeeded);
 
        using MemoryStream stream = new();
        bag.Save(stream);
        Assert.NotEqual(0, stream.Length);
        stream.Position = 0;
 
        AxHost.PropertyBagStream newBag = new(stream);
 
        VARIANT integer = new();
        hr = newBag.Read("Integer", ref integer, null);
        Assert.True(hr.Succeeded);
        Assert.Equal(42, (int)integer);
 
        VARIANT dispatch = new();
        hr = newBag.Read("Object", ref dispatch, null);
        Assert.True(hr.Succeeded);
        Assert.Equal(obj.Name, ((NameClass)dispatch.ToObject()).Name);
    }
 
    [Fact]
    public void PropertyBagStream_WriteReadRoundTrip_FormatterDisabled()
    {
        using BinaryFormatterScope formatterScope = new(enable: false);
        AxHost.PropertyBagStream bag = new();
 
        // cs/deserialization-unexpected-subtypes
        HRESULT hr = bag.Write("Integer", (VARIANT)42); // CodeQL[SM02229] : Testing legacy feature. This is a safe use of VARIANT because the data is trusted and the types are controlled and validated.
        Assert.True(hr.Succeeded);
        NameClass obj = new() { Name = "Hamlet" };
        hr = bag.Write("Object", VARIANT.FromObject(obj));
        Assert.True(hr.Succeeded);
 
        using MemoryStream stream = new();
        Assert.Throws<NotSupportedException>(() => bag.Save(stream));
        Assert.Equal(0, stream.Position);
    }
 
    [Theory]
    [MemberData(nameof(TestData_PrimitiveValues))]
    public void PropertyBagStream_WriteReadRoundTrip_Primitives_FormatterDisabled(object value)
    {
        using BinaryFormatterScope formatterScope = new(enable: false);
 
        AxHost.PropertyBagStream bag = new();
        using VARIANT variant = default;
        Marshal.GetNativeVariantForObject(value, (nint)(void*)&variant);
        string name = value.GetType().FullName!;
 
        // cs/deserialization-unexpected-subtypes
        HRESULT hr = bag.Write(value.GetType().FullName!, variant); // CodeQL[SM02229] : Testing legacy feature. This is a safe use of VARIANT because the data is trusted and the types are controlled and validated.
        Assert.True(hr.Succeeded);
 
        using MemoryStream stream = new();
        bag.Save(stream);
        Assert.NotEqual(0, stream.Length);
        stream.Position = 0;
 
        IPropertyBag.Interface newBag = new AxHost.PropertyBagStream(stream);
        using VARIANT result = new();
        hr = newBag.Read(name, &result, null);
        Assert.True(hr.Succeeded);
        Assert.Equal(variant.ToObject(), result.ToObject());
    }
 
    public static TheoryData<object> TestData_PrimitiveValues => new()
    {
        int.MaxValue,
        uint.MaxValue,
        long.MaxValue,
        ulong.MaxValue,
        short.MaxValue,
        ushort.MaxValue,
        byte.MaxValue,
        sbyte.MaxValue,
        true,
        float.MaxValue,
        double.MaxValue,
        char.MaxValue,
        // TimeSpan has no VARIANT conversion
        // TimeSpan.MaxValue,
        DateTime.MaxValue,
        decimal.MaxValue,
        "RightRound"
    };
 
    [Serializable]
    private class NameClass
    {
        public string Name { get; set; }
    }
}