File: Windows\Disk\WindowsDiskMetricsTests.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.Diagnostics.Metrics;
using System.Linq;
using System.Runtime.Versioning;
using Microsoft.Extensions.Diagnostics.Metrics.Testing;
using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Test.Helpers;
using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Test;
using Microsoft.Extensions.Logging.Testing;
using Microsoft.Extensions.Time.Testing;
using Microsoft.Shared.Instruments;
using Microsoft.TestUtilities;
using Moq;
using Xunit;
 
namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Windows.Disk.Test;
 
[SupportedOSPlatform("windows")]
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, SkipReason = "Windows specific.")]
public class WindowsDiskMetricsTests
{
    private const string CategoryName = "LogicalDisk";
    private readonly FakeLogger<WindowsDiskMetrics> _fakeLogger = new();
 
    [ConditionalFact]
    public void Creates_Meter_With_Correct_Name()
    {
        using var meterFactory = new TestMeterFactory();
        var performanceCounterFactoryMock = new Mock<IPerformanceCounterFactory>();
        var options = new ResourceMonitoringOptions { EnableDiskIoMetrics = true };
 
        _ = new WindowsDiskMetrics(
            _fakeLogger,
            meterFactory,
            performanceCounterFactoryMock.Object,
            TimeProvider.System,
            Microsoft.Extensions.Options.Options.Create(options));
 
        Meter meter = meterFactory.Meters.Single();
        Assert.Equal(ResourceUtilizationInstruments.MeterName, meter.Name);
    }
 
    [ConditionalFact]
    public void DiskOperationMetricsTest()
    {
        using var meterFactory = new TestMeterFactory();
        var performanceCounterFactory = new Mock<IPerformanceCounterFactory>();
        var fakeTimeProvider = new FakeTimeProvider();
        var options = new ResourceMonitoringOptions { EnableDiskIoMetrics = true };
 
        // Set up
        const string ReadCounterName = WindowsDiskPerfCounterNames.DiskReadsCounter;
        const string WriteCounterName = WindowsDiskPerfCounterNames.DiskWritesCounter;
        var readCounterC = new FakePerformanceCounter("C:", [0, 1, 1.5f, 2, 2.5f]);
        var readCounterD = new FakePerformanceCounter("D:", [0, 2, 2.5f, 3, 3.5f]);
        performanceCounterFactory.Setup(x => x.Create(CategoryName, ReadCounterName, "C:")).Returns(readCounterC);
        performanceCounterFactory.Setup(x => x.Create(CategoryName, ReadCounterName, "D:")).Returns(readCounterD);
        var writeCounterC = new FakePerformanceCounter("C:", [0, 10, 15, 20, 25]);
        var writeCounterD = new FakePerformanceCounter("D:", [0, 20, 25, 30, 35]);
        performanceCounterFactory.Setup(x => x.Create(CategoryName, WriteCounterName, "C:")).Returns(writeCounterC);
        performanceCounterFactory.Setup(x => x.Create(CategoryName, WriteCounterName, "D:")).Returns(writeCounterD);
        performanceCounterFactory.Setup(x => x.GetCategoryInstances(CategoryName)).Returns(["_Total", "C:", "D:"]);
 
        _ = new WindowsDiskMetrics(
            _fakeLogger,
            meterFactory,
            performanceCounterFactory.Object,
            fakeTimeProvider,
            Options.Options.Create(options));
        Meter meter = meterFactory.Meters.Single();
 
        var readTag = new KeyValuePair<string, object?>("disk.io.direction", "read");
        var writeTag = new KeyValuePair<string, object?>("disk.io.direction", "write");
        var deviceTagC = new KeyValuePair<string, object?>("system.device", "C:");
        var deviceTagD = new KeyValuePair<string, object?>("system.device", "D:");
 
        using var operationCollector = new MetricCollector<long>(meter, ResourceUtilizationInstruments.SystemDiskOperations);
 
        // 1st measurement
        fakeTimeProvider.Advance(TimeSpan.FromMinutes(1));
        operationCollector.RecordObservableInstruments();
        IReadOnlyList<CollectedMeasurement<long>> measurements = operationCollector.GetMeasurementSnapshot();
        Assert.Equal(4, measurements.Count);
        Assert.Equal(60, measurements.Last(x => x.MatchesTags(readTag, deviceTagC)).Value); // 1 * 60 = 60
        Assert.Equal(120, measurements.Last(x => x.MatchesTags(readTag, deviceTagD)).Value); // 2 * 60 = 120
        Assert.Equal(600, measurements.Last(x => x.MatchesTags(writeTag, deviceTagC)).Value); // 10 * 60 = 600
        Assert.Equal(1200, measurements.Last(x => x.MatchesTags(writeTag, deviceTagD)).Value); // 20 * 60 = 1200
 
        // 2nd measurement
        fakeTimeProvider.Advance(TimeSpan.FromMinutes(1));
        operationCollector.RecordObservableInstruments();
        measurements = operationCollector.GetMeasurementSnapshot();
        Assert.Equal(150, measurements.Last(x => x.MatchesTags(readTag, deviceTagC)).Value); // 60 + 1.5 * 60 = 150
        Assert.Equal(270, measurements.Last(x => x.MatchesTags(readTag, deviceTagD)).Value); // 120 + 2.5 * 60 = 270
        Assert.Equal(1500, measurements.Last(x => x.MatchesTags(writeTag, deviceTagC)).Value); // 600 + 15 * 60 = 1500
        Assert.Equal(2700, measurements.Last(x => x.MatchesTags(writeTag, deviceTagD)).Value); // 1200 + 25 * 60 = 2700
 
        // 3rd measurement
        fakeTimeProvider.Advance(TimeSpan.FromSeconds(30));
        operationCollector.RecordObservableInstruments();
        measurements = operationCollector.GetMeasurementSnapshot();
        Assert.Equal(210, measurements.Last(x => x.MatchesTags(readTag, deviceTagC)).Value); // 150 + 2 * 30 = 210
        Assert.Equal(360, measurements.Last(x => x.MatchesTags(readTag, deviceTagD)).Value); // 270 + 3 * 30 = 360
        Assert.Equal(2100, measurements.Last(x => x.MatchesTags(writeTag, deviceTagC)).Value); // 1500 + 20 * 60 = 2100
        Assert.Equal(3600, measurements.Last(x => x.MatchesTags(writeTag, deviceTagD)).Value); // 2700 + 30 * 60 = 3600
 
        // 4th measurement
        fakeTimeProvider.Advance(TimeSpan.FromMinutes(1));
        operationCollector.RecordObservableInstruments();
        measurements = operationCollector.GetMeasurementSnapshot();
        Assert.Equal(360, measurements.Last(x => x.MatchesTags(readTag, deviceTagC)).Value); // 210 + 2.5 * 60 = 360
        Assert.Equal(570, measurements.Last(x => x.MatchesTags(readTag, deviceTagD)).Value); // 360 + 3.5 * 60 = 570
        Assert.Equal(3600, measurements.Last(x => x.MatchesTags(writeTag, deviceTagC)).Value); // 2100 + 25 * 60 = 3600
        Assert.Equal(5700, measurements.Last(x => x.MatchesTags(writeTag, deviceTagD)).Value); // 3600 + 35 * 60 = 5700
    }
 
    [ConditionalFact]
    public void DiskIoBytesMetricsTest()
    {
        using var meterFactory = new TestMeterFactory();
        var performanceCounterFactory = new Mock<IPerformanceCounterFactory>();
        var fakeTimeProvider = new FakeTimeProvider();
        var options = new ResourceMonitoringOptions { EnableDiskIoMetrics = true };
 
        // Set up
        const string ReadCounterName = WindowsDiskPerfCounterNames.DiskReadBytesCounter;
        const string WriteCounterName = WindowsDiskPerfCounterNames.DiskWriteBytesCounter;
        var readCounterC = new FakePerformanceCounter("C:", [0, 10, 15, 20, 25]);
        var readCounterD = new FakePerformanceCounter("D:", [0, 20, 25, 30, 35]);
        performanceCounterFactory.Setup(x => x.Create(CategoryName, ReadCounterName, "C:")).Returns(readCounterC);
        performanceCounterFactory.Setup(x => x.Create(CategoryName, ReadCounterName, "D:")).Returns(readCounterD);
        var writeCounterC = new FakePerformanceCounter("C:", [0, 100, 150, 200, 250]);
        var writeCounterD = new FakePerformanceCounter("D:", [0, 200, 250, 300, 350]);
        performanceCounterFactory.Setup(x => x.Create(CategoryName, WriteCounterName, "C:")).Returns(writeCounterC);
        performanceCounterFactory.Setup(x => x.Create(CategoryName, WriteCounterName, "D:")).Returns(writeCounterD);
        performanceCounterFactory.Setup(x => x.GetCategoryInstances(CategoryName)).Returns(["_Total", "C:", "D:"]);
 
        _ = new WindowsDiskMetrics(
            _fakeLogger,
            meterFactory,
            performanceCounterFactory.Object,
            fakeTimeProvider,
            Options.Options.Create(options));
        Meter meter = meterFactory.Meters.Single();
 
        var readTag = new KeyValuePair<string, object?>("disk.io.direction", "read");
        var writeTag = new KeyValuePair<string, object?>("disk.io.direction", "write");
        var deviceTagC = new KeyValuePair<string, object?>("system.device", "C:");
        var deviceTagD = new KeyValuePair<string, object?>("system.device", "D:");
 
        using var operationCollector = new MetricCollector<long>(meter, ResourceUtilizationInstruments.SystemDiskIo);
 
        // 1st measurement
        fakeTimeProvider.Advance(TimeSpan.FromMinutes(1));
        operationCollector.RecordObservableInstruments();
        IReadOnlyList<CollectedMeasurement<long>> measurements = operationCollector.GetMeasurementSnapshot();
        Assert.Equal(4, measurements.Count);
        Assert.Equal(600, measurements.Last(x => x.MatchesTags(readTag, deviceTagC)).Value); // 10 * 60 = 600
        Assert.Equal(1200, measurements.Last(x => x.MatchesTags(readTag, deviceTagD)).Value); // 20 * 60 = 1200
        Assert.Equal(6000, measurements.Last(x => x.MatchesTags(writeTag, deviceTagC)).Value); // 100 * 60 = 6000
        Assert.Equal(12000, measurements.Last(x => x.MatchesTags(writeTag, deviceTagD)).Value); // 200 * 60 = 12000
 
        // 2nd measurement
        fakeTimeProvider.Advance(TimeSpan.FromMinutes(1));
        operationCollector.RecordObservableInstruments();
        measurements = operationCollector.GetMeasurementSnapshot();
        Assert.Equal(1500, measurements.Last(x => x.MatchesTags(readTag, deviceTagC)).Value); // 600 + 15 * 60 = 1500
        Assert.Equal(2700, measurements.Last(x => x.MatchesTags(readTag, deviceTagD)).Value); // 1200 + 25 * 60 = 2700
        Assert.Equal(15000, measurements.Last(x => x.MatchesTags(writeTag, deviceTagC)).Value); // 6000 + 150 * 60 = 15000
        Assert.Equal(27000, measurements.Last(x => x.MatchesTags(writeTag, deviceTagD)).Value); // 12000 + 250 * 60 = 27000
 
        // 3rd measurement
        fakeTimeProvider.Advance(TimeSpan.FromSeconds(30));
        operationCollector.RecordObservableInstruments();
        measurements = operationCollector.GetMeasurementSnapshot();
        Assert.Equal(2100, measurements.Last(x => x.MatchesTags(readTag, deviceTagC)).Value); // 1500 + 20 * 30 = 210
        Assert.Equal(3600, measurements.Last(x => x.MatchesTags(readTag, deviceTagD)).Value); // 2700 + 30 * 30 = 360
        Assert.Equal(21000, measurements.Last(x => x.MatchesTags(writeTag, deviceTagC)).Value); // 15000 + 200 * 60 = 21000
        Assert.Equal(36000, measurements.Last(x => x.MatchesTags(writeTag, deviceTagD)).Value); // 27000 + 300 * 60 = 36000
 
        // 4th measurement
        fakeTimeProvider.Advance(TimeSpan.FromMinutes(1));
        operationCollector.RecordObservableInstruments();
        measurements = operationCollector.GetMeasurementSnapshot();
        Assert.Equal(3600, measurements.Last(x => x.MatchesTags(readTag, deviceTagC)).Value); // 2100 + 25 * 60 = 3600
        Assert.Equal(5700, measurements.Last(x => x.MatchesTags(readTag, deviceTagD)).Value); // 3600 + 35 * 60 = 5700
        Assert.Equal(36000, measurements.Last(x => x.MatchesTags(writeTag, deviceTagC)).Value); // 21000 + 250 * 60 = 36000
        Assert.Equal(57000, measurements.Last(x => x.MatchesTags(writeTag, deviceTagD)).Value); // 36000 + 350 * 60 = 57000
    }
}