File: Windows\TcpTableInfoTests.cs
Web Access
Project: src\test\Libraries\Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests\Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests.csproj (Microsoft.Extensions.Diagnostics.ResourceMonitoring.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;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Network;
using Microsoft.TestUtilities;
using Xunit;
 
namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Test;
 
[Collection("Tcp Connection Tests")]
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, SkipReason = "Windows specific.")]
public sealed class TcpTableInfoTests
{
    public static readonly TimeSpan DefaultTimeSpan = TimeSpan.FromSeconds(5);
    public static DateTimeOffset StartTimestamp = DateTimeOffset.UtcNow;
    public static DateTimeOffset NextTimestamp = StartTimestamp.Add(DefaultTimeSpan);
 
    // Each MIB_TCPROW needs 20. In experiments, the size should be 20 * MIB_TCPROW_count + 12.
    private const uint FakeSize = 272;
 
    // Add 13 rows more.
    private const uint FakeSize2 = FakeSize + (20 * 13);
    private const uint FakeNumberOfEntries = 13;
    private const uint FakeNumberOfEntries2 = FakeNumberOfEntries * 2;
 
    public static uint FakeGetTcpTableWithUnsuccessfulStatusAllTheTime(IntPtr pTcpTable, ref uint pdwSize, bool bOrder)
    {
        return (uint)NTSTATUS.UnsuccessfulStatus;
    }
 
    public static uint FakeGetTcpTableWithInsufficientBufferAndInvalidParameter(IntPtr pTcpTable, ref uint pdwSize, bool bOrder)
    {
        if (pdwSize < FakeSize)
        {
            pdwSize = FakeSize;
            return (uint)NTSTATUS.InsufficientBuffer;
        }
 
        return (uint)NTSTATUS.InvalidParameter;
    }
 
    public static unsafe uint FakeGetTcpTableWithFakeInformation(IntPtr pTcpTable, ref uint pdwSize, bool bOrder)
    {
        if (DateTimeOffset.UtcNow < NextTimestamp)
        {
            if (pdwSize < FakeSize)
            {
                pdwSize = FakeSize;
                return (uint)NTSTATUS.InsufficientBuffer;
            }
 
            MIB_TCPTABLE fakeTcpTable = new()
            {
                NumberOfEntries = FakeNumberOfEntries
            };
            MIB_TCPROW[] fakeRows = new MIB_TCPROW[FakeNumberOfEntries];
            for (int i = 0; i < 12; ++i)
            {
                fakeRows[i] = new MIB_TCPROW
                {
                    State = (MIB_TCP_STATE)(i + 1),
 
                    // 16_777_343 means 127.0.0.1.
                    LocalAddr = 16_777_343
                };
            }
 
            fakeRows[12] = new MIB_TCPROW
            {
                State = MIB_TCP_STATE.DELETE_TCB,
            };
 
            // True means the result should be sorted.
            if (bOrder)
            {
                fakeRows[12].LocalAddr = 16_777_343 + 1;
            }
            else
            {
                fakeRows[11].LocalAddr = 16_777_343 + 1;
                fakeRows[12].LocalAddr = 16_777_343;
            }
 
            fakeTcpTable.Table = fakeRows[0];
            Marshal.StructureToPtr(fakeTcpTable, pTcpTable, false);
            var offset = Marshal.OffsetOf<MIB_TCPTABLE>(nameof(MIB_TCPTABLE.Table)).ToInt32();
            var fakePtr = IntPtr.Add(pTcpTable, offset);
 
            for (int i = 0; i < fakeTcpTable.NumberOfEntries; ++i)
            {
                Marshal.StructureToPtr(fakeRows[i], fakePtr, false);
                fakePtr = IntPtr.Add(fakePtr, sizeof(MIB_TCPROW));
            }
        }
        else
        {
            if (pdwSize < FakeSize2)
            {
                pdwSize = FakeSize2;
                return (uint)NTSTATUS.InsufficientBuffer;
            }
 
            MIB_TCPTABLE fakeTcpTable = new()
            {
                NumberOfEntries = FakeNumberOfEntries2
            };
            MIB_TCPROW[] fakeRows = new MIB_TCPROW[FakeNumberOfEntries2];
            for (int i = 0; i < 12; ++i)
            {
                fakeRows[i] = new MIB_TCPROW
                {
                    State = (MIB_TCP_STATE)(i + 1),
 
                    // 16_777_343 means 127.0.0.1.
                    LocalAddr = 16_777_343
                };
            }
 
            for (int i = 13; i < 25; ++i)
            {
                fakeRows[i] = new MIB_TCPROW
                {
                    State = (MIB_TCP_STATE)(i + 1 - 13),
 
                    // 16_777_343 means 127.0.0.1.
                    LocalAddr = 16_777_343
                };
            }
 
            fakeRows[12] = new MIB_TCPROW
            {
                State = MIB_TCP_STATE.DELETE_TCB,
            };
            fakeRows[25] = new MIB_TCPROW
            {
                State = MIB_TCP_STATE.DELETE_TCB,
            };
 
            // True means the result should be sorted.
            if (bOrder)
            {
                fakeRows[12].LocalAddr = 16_777_343 + 1;
                fakeRows[25].LocalAddr = 16_777_343 + 1;
            }
            else
            {
                fakeRows[11].LocalAddr = 16_777_343 + 1;
                fakeRows[12].LocalAddr = 16_777_343;
                fakeRows[24].LocalAddr = 16_777_343 + 1;
                fakeRows[25].LocalAddr = 16_777_343;
            }
 
            fakeTcpTable.Table = fakeRows[0];
            Marshal.StructureToPtr(fakeTcpTable, pTcpTable, false);
            var offset = Marshal.OffsetOf<MIB_TCPTABLE>(nameof(MIB_TCPTABLE.Table)).ToInt32();
            var fakePtr = IntPtr.Add(pTcpTable, offset);
 
            for (int i = 0; i < fakeTcpTable.NumberOfEntries; ++i)
            {
                Marshal.StructureToPtr(fakeRows[i], fakePtr, false);
                fakePtr = IntPtr.Add(fakePtr, sizeof(MIB_TCPROW));
            }
        }
 
        return (uint)NTSTATUS.Success;
    }
 
    [ConditionalFact]
    public void Test_TcpTableInfo_Get_UnsuccessfulStatus_All_The_Time()
    {
        var options = new ResourceMonitoringOptions
        {
            SourceIpAddresses = new HashSet<string> { "127.0.0.1" },
            SamplingInterval = DefaultTimeSpan
        };
        WindowsTcpStateInfo tcpTableInfo = new WindowsTcpStateInfo(Options.Options.Create(options));
        tcpTableInfo.SetGetTcpTableDelegate(FakeGetTcpTableWithUnsuccessfulStatusAllTheTime);
        Assert.Throws<InvalidOperationException>(() =>
        {
            var tcpStateInfo = tcpTableInfo.GetpIpV4TcpStateInfo();
        });
    }
 
    [ConditionalFact]
    public void Test_TcpTableInfo_Get_InsufficientBuffer_Then_Get_InvalidParameter()
    {
        var options = new ResourceMonitoringOptions
        {
            SourceIpAddresses = new HashSet<string> { "127.0.0.1" },
            SamplingInterval = DefaultTimeSpan
        };
        WindowsTcpStateInfo tcpTableInfo = new WindowsTcpStateInfo(Options.Options.Create(options));
        tcpTableInfo.SetGetTcpTableDelegate(FakeGetTcpTableWithInsufficientBufferAndInvalidParameter);
        Assert.Throws<InvalidOperationException>(() =>
        {
            var tcpStateInfo = tcpTableInfo.GetpIpV4TcpStateInfo();
        });
    }
 
    [ConditionalFact]
    public void Test_TcpTableInfo_Get_Correct_Information()
    {
        StartTimestamp = DateTimeOffset.UtcNow;
        NextTimestamp = StartTimestamp.Add(DefaultTimeSpan);
        var options = new ResourceMonitoringOptions
        {
            SourceIpAddresses = new HashSet<string> { "127.0.0.1" },
            SamplingInterval = DefaultTimeSpan
        };
        WindowsTcpStateInfo tcpTableInfo = new WindowsTcpStateInfo(Options.Options.Create(options));
        tcpTableInfo.SetGetTcpTableDelegate(FakeGetTcpTableWithFakeInformation);
        var tcpStateInfo = tcpTableInfo.GetpIpV4TcpStateInfo();
        Assert.NotNull(tcpStateInfo);
        Assert.Equal(1, tcpStateInfo.ClosedCount);
        Assert.Equal(1, tcpStateInfo.ListenCount);
        Assert.Equal(1, tcpStateInfo.SynSentCount);
        Assert.Equal(1, tcpStateInfo.SynRcvdCount);
        Assert.Equal(1, tcpStateInfo.EstabCount);
        Assert.Equal(1, tcpStateInfo.FinWait1Count);
        Assert.Equal(1, tcpStateInfo.FinWait2Count);
        Assert.Equal(1, tcpStateInfo.CloseWaitCount);
        Assert.Equal(1, tcpStateInfo.ClosingCount);
        Assert.Equal(1, tcpStateInfo.LastAckCount);
        Assert.Equal(1, tcpStateInfo.TimeWaitCount);
        Assert.Equal(1, tcpStateInfo.DeleteTcbCount);
 
        // Second calling in a small interval.
        tcpStateInfo = tcpTableInfo.GetpIpV4TcpStateInfo();
        Assert.NotNull(tcpStateInfo);
        Assert.Equal(1, tcpStateInfo.ClosedCount);
        Assert.Equal(1, tcpStateInfo.ListenCount);
        Assert.Equal(1, tcpStateInfo.SynSentCount);
        Assert.Equal(1, tcpStateInfo.SynRcvdCount);
        Assert.Equal(1, tcpStateInfo.EstabCount);
        Assert.Equal(1, tcpStateInfo.FinWait1Count);
        Assert.Equal(1, tcpStateInfo.FinWait2Count);
        Assert.Equal(1, tcpStateInfo.CloseWaitCount);
        Assert.Equal(1, tcpStateInfo.ClosingCount);
        Assert.Equal(1, tcpStateInfo.LastAckCount);
        Assert.Equal(1, tcpStateInfo.TimeWaitCount);
        Assert.Equal(1, tcpStateInfo.DeleteTcbCount);
 
        // Third calling in a long interval.
        Thread.Sleep(6000);
        tcpStateInfo = tcpTableInfo.GetpIpV4TcpStateInfo();
        Assert.NotNull(tcpStateInfo);
        Assert.Equal(2, tcpStateInfo.ClosedCount);
        Assert.Equal(2, tcpStateInfo.ListenCount);
        Assert.Equal(2, tcpStateInfo.SynSentCount);
        Assert.Equal(2, tcpStateInfo.SynRcvdCount);
        Assert.Equal(2, tcpStateInfo.EstabCount);
        Assert.Equal(2, tcpStateInfo.FinWait1Count);
        Assert.Equal(2, tcpStateInfo.FinWait2Count);
        Assert.Equal(2, tcpStateInfo.CloseWaitCount);
        Assert.Equal(2, tcpStateInfo.ClosingCount);
        Assert.Equal(2, tcpStateInfo.LastAckCount);
        Assert.Equal(2, tcpStateInfo.TimeWaitCount);
        Assert.Equal(2, tcpStateInfo.DeleteTcbCount);
    }
 
    [ConditionalFact]
    public void Test_TcpTableInfo_CalculateCount_default_branch()
    {
        TcpStateInfo tcpStateInfo = new();
 
        // Add this case to increase coverage, but 0 will not happen in actual case.
        WindowsTcpStateInfo.CalculateCount(tcpStateInfo, 0);
        Assert.NotNull(tcpStateInfo);
        Assert.Equal(0, tcpStateInfo.ClosedCount);
        Assert.Equal(0, tcpStateInfo.ListenCount);
        Assert.Equal(0, tcpStateInfo.SynSentCount);
        Assert.Equal(0, tcpStateInfo.SynRcvdCount);
        Assert.Equal(0, tcpStateInfo.EstabCount);
        Assert.Equal(0, tcpStateInfo.FinWait1Count);
        Assert.Equal(0, tcpStateInfo.FinWait2Count);
        Assert.Equal(0, tcpStateInfo.CloseWaitCount);
        Assert.Equal(0, tcpStateInfo.ClosingCount);
        Assert.Equal(0, tcpStateInfo.LastAckCount);
        Assert.Equal(0, tcpStateInfo.TimeWaitCount);
        Assert.Equal(0, tcpStateInfo.DeleteTcbCount);
    }
}