解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
3.72 KiB
X
using System;
using System.ComponentModel;
using System.IO;
using NewLife.Buffers;
using NewLife.Data;
using Xunit;

namespace XUnitTest.Buffers;

/// <summary>SpanReader 生命周期测试:流式扩容借出的池缓冲必须能归还、且不影响调用方句柄</summary>
public class SpanReaderLifecycleTests
{
    /// <summary>构造一个需要扩容的流式读取器并读一格,触发从池借缓冲</summary>
    private static SpanReader OpenStreamReader(MemoryStream ms)
    {
        var reader = new SpanReader(ms, null, 1024);
        _ = reader.ReadByte();

        return reader;
    }

    [Fact]
    [DisplayName("流式读取器_释放后缓冲归还池_不再持续借出")]
    public void Dispose_ReturnsBufferToPool()
    {
        var ms = new MemoryStream(new Byte[1024]);

        // 预热:先把池里该桶的数组消耗掉
        for (var i = 0; i < 8; i++)
        {
            var warm = OpenStreamReader(ms);
            warm.Dispose();
            ms.Position = 0;
        }

        const Int32 N = 16;
        var before = GC.GetAllocatedBytesForCurrentThread();
        for (var i = 0; i < N; i++)
        {
            var reader = OpenStreamReader(ms);
            reader.Dispose();
            ms.Position = 0;
        }
        var used = (GC.GetAllocatedBytesForCurrentThread() - before) / N;

        // 归还到位时只剩 OwnerPacket/ArrayOwner 两个小对象(约 80 B);未归还则会每轮再漏一块 1KB 池数组(约 1128 B)
        Assert.True(used < 256, $"每轮分配 {used} 字节,池缓冲疑似未归还(归还到位时约 80 字节)");
    }

    [Fact]
    [DisplayName("流式读取器_using模式可用")]
    public void Dispose_UsingPattern()
    {
        var ms = new MemoryStream(new Byte[64]);

        // ref struct 支持模式化 using:出作用域自动归还扩容借出的池缓冲
        {
            using var reader = new SpanReader(ms, null, 256);
            Assert.Equal(0x00, reader.ReadByte());
        }

        // 二次使用(缓冲来自池复用)不受前一次释放影响
        ms.Position = 0;
        {
            using var reader = new SpanReader(ms, null, 256);
            Assert.Equal(0x00, reader.ReadByte());
        }
    }

    [Fact]
    [DisplayName("视图读取器_释放不影响调用方数据包")]
    public void Dispose_ViewReader_KeepsCallerPacket()
    {
        var packet = new OwnerPacket(8);
        var reader = new SpanReader((IPacket)packet);
        _ = reader.ReadByte();

        reader.Dispose();

        // 构造时传入的数据包不属于读取器:释放只归还自己借的缓冲,不动调用方句柄
        Assert.Equal(1, packet.RefCount);
        Assert.Equal(8, packet.Length);

        packet.Dispose();
    }

    [Fact]
    [DisplayName("释放_重复调用幂等")]
    public void Dispose_Twice_IsIdempotent()
    {
        var ms = new MemoryStream(new Byte[1024]);
        var reader = OpenStreamReader(ms);

        reader.Dispose();
        reader.Dispose();
        reader.Dispose();

        // 重复释放不抛异常;此时读取器已作废
        Assert.True(true);
    }

    [Fact]
    [DisplayName("流式读取器_不释放也不会抛异常_但会漏一块池缓冲")]
    public void WithoutDispose_StillFunctional()
    {
        // 保留旧用法(不显式释放)不抛异常、读取结果正确——只是那块池缓冲回不到池,
        // 故 Dispose 属"建议但非强制"的纪律:走流式扩容的读取器应使用 using
        var ms = new MemoryStream(new Byte[] { 1, 2, 3 });
        var reader = new SpanReader(ms, null, 1024);

        Assert.Equal(1, reader.ReadByte());
        Assert.Equal(2, reader.ReadByte());
        Assert.Equal(3, reader.ReadByte());
    }
}