File: Latency\Internal\LatencyContextTests.cs
Web Access
Project: src\test\Libraries\Microsoft.Extensions.Telemetry.Tests\Microsoft.Extensions.Telemetry.Tests.csproj (Microsoft.Extensions.Telemetry.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.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.Latency.Internal;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
using Microsoft.Shared.Pools;
using Moq;
using Xunit;
 
namespace Microsoft.Extensions.Diagnostics.Latency.Test.Internal;
 
public class LatencyContextTests
{
    private readonly string[] _checkpoints = new[] { "ca", "cb", "lc", "cd" };
    private readonly string[] _tags = new[] { "ta", "tb", "tc", "td" };
    private readonly string[] _measures = new[] { "ma", "mb", "mc", "md" };
 
    [Fact]
    public void Context_Dispose_Stops_Context()
    {
        var latencyContext = GetContext();
        Assert.True(latencyContext.IsRunning);
        latencyContext.Dispose();
        Assert.False(latencyContext.IsRunning);
    }
 
    [Fact]
    public void Context_Dispose_InvokedMulitpleTimes()
    {
        var latencyContext = GetContext();
        Assert.NotNull(latencyContext);
        Assert.False(latencyContext.IsDisposed);
        latencyContext.Dispose();
        Assert.True(latencyContext.IsDisposed);
#pragma warning disable S3966 // Objects should not be disposed more than once
        latencyContext.Dispose();
        latencyContext.Dispose();
#pragma warning restore S3966 // Objects should not be disposed more than once
    }
 
    [Fact]
    public void Context_StopOnlyOnce()
    {
        using var latencyContext = GetContext();
        latencyContext.Freeze();
        Assert.False(latencyContext.IsRunning);
 
        // Subsequent adds become no-ops
        latencyContext.Freeze();
        latencyContext.Freeze();
        Assert.False(latencyContext.IsRunning);
    }
 
    [Fact]
    public void Context_Dispose_ReturnsToPool()
    {
        var r = GetRegistry();
        var li = new LatencyInstrumentProvider(r);
        var lcp = new LatencyContextPool(li);
        var pool = new MockResetOnGet(lcp);
        lcp.Pool = pool;
        var latencyContext = new LatencyContext(lcp);
        Assert.False(latencyContext.IsDisposed);
        latencyContext.Dispose();
        Assert.True(latencyContext.IsDisposed);
        Assert.True(pool.ReturnCalled);
    }
 
    [Fact]
    public void Latency_Context_Is_Not_Adding_Measures_When_Frozen()
    {
        const string TokenName = nameof(TokenName);
 
        using var scope = new ServiceCollection()
            .AddLatencyContext()
            .BuildServiceProvider()
            .CreateScope();
 
        var services = scope.ServiceProvider;
        var context = services.GetRequiredService<ILatencyContextProvider>().CreateContext();
        var tokenIssuer = services.GetRequiredService<ILatencyContextTokenIssuer>();
        var measureToken = tokenIssuer.GetMeasureToken(TokenName);
 
        context.Freeze();
        context.AddMeasure(measureToken, 1);
        context.RecordMeasure(measureToken, 1);
 
        var measures = context.LatencyData.Measures;
 
        Assert.IsAssignableFrom<LatencyContext>(context);
        Assert.False(((LatencyContext)context).IsRunning);
        Assert.Empty(measures.ToArray());
    }
 
    [Fact]
    public void Latency_Context_Is_Adding_Measures_When_Not_Frozen()
    {
        const string TokenName = nameof(TokenName);
 
        using var scope = new ServiceCollection()
            .AddLatencyContext()
            .RegisterMeasureNames(TokenName)
            .BuildServiceProvider()
            .CreateScope();
 
        var services = scope.ServiceProvider;
        var context = services.GetRequiredService<ILatencyContextProvider>().CreateContext();
        var tokenIssuer = services.GetRequiredService<ILatencyContextTokenIssuer>();
        var measureToken = tokenIssuer.GetMeasureToken(TokenName);
 
        context.AddMeasure(measureToken, 1);
 
        var measures = context.LatencyData.Measures;
 
        Assert.IsAssignableFrom<LatencyContext>(context);
        Assert.True(((LatencyContext)context).IsRunning);
        Assert.Single(measures.ToArray());
        Assert.Equal(TokenName, measures[0].Name);
    }
 
    [Fact]
    public void Latency_Context_Is_Recording_Measures_When_Not_Frozen()
    {
        const string TokenName = nameof(TokenName);
 
        using var scope = new ServiceCollection()
            .AddLatencyContext()
            .RegisterMeasureNames(TokenName)
            .BuildServiceProvider()
            .CreateScope();
 
        var services = scope.ServiceProvider;
        var context = services.GetRequiredService<ILatencyContextProvider>().CreateContext();
        var tokenIssuer = services.GetRequiredService<ILatencyContextTokenIssuer>();
        var measureToken = tokenIssuer.GetMeasureToken(TokenName);
 
        context.RecordMeasure(measureToken, 1);
 
        var measures = context.LatencyData.Measures;
 
        Assert.IsAssignableFrom<LatencyContext>(context);
        Assert.True(((LatencyContext)context).IsRunning);
        Assert.Single(measures.ToArray());
        Assert.Equal(TokenName, measures[0].Name);
    }
 
    [Fact]
    public void Latency_Context_Is_Not_Adding_Values_To_Tags_When_Frozen()
    {
        const string TokenName = nameof(TokenName);
        const string Tag = nameof(Tag);
 
        using var scope = new ServiceCollection()
            .AddLatencyContext()
            .RegisterTagNames(TokenName)
            .RegisterCheckpointNames(TokenName)
            .BuildServiceProvider()
            .CreateScope();
 
        var services = scope.ServiceProvider;
        var context = services.GetRequiredService<ILatencyContextProvider>().CreateContext();
        var tokenIssuer = services.GetRequiredService<ILatencyContextTokenIssuer>();
        var tagToken = tokenIssuer.GetTagToken(TokenName);
 
        var tags2 = context.LatencyData.Tags;
 
        context.Freeze();
        context.SetTag(tagToken, Tag);
 
        var tags = context.LatencyData.Tags.ToArray();
 
        Assert.IsAssignableFrom<LatencyContext>(context);
        Assert.False(((LatencyContext)context).IsRunning);
        Assert.Single(tags);
        Assert.Empty(tags[0].Value);
    }
 
    [Fact]
    public void Latency_Context_Is_Adding_Values_To_Tags_Tags_When_Not_Frozen()
    {
        const string TokenName = nameof(TokenName);
        const string Tag = nameof(Tag);
 
        using var scope = new ServiceCollection()
            .AddLatencyContext()
            .RegisterTagNames(TokenName)
            .BuildServiceProvider()
            .CreateScope();
 
        var services = scope.ServiceProvider;
        var context = services.GetRequiredService<ILatencyContextProvider>().CreateContext();
        var tokenIssuer = services.GetRequiredService<ILatencyContextTokenIssuer>();
        var tagToken = tokenIssuer.GetTagToken(TokenName);
 
        context.SetTag(tagToken, Tag);
 
        var tags = context.LatencyData.Tags;
 
        Assert.IsAssignableFrom<LatencyContext>(context);
        Assert.True(((LatencyContext)context).IsRunning);
        Assert.Single(tags.ToArray());
        Assert.Equal(Tag, tags[0].Value);
    }
 
    [Fact]
    public async Task Latency_Context_Is_Returning_Const_Duration_When_Frozen()
    {
        const string TokenName = nameof(TokenName);
        const string Tag = nameof(Tag);
 
        using var scope = new ServiceCollection()
            .AddLatencyContext()
            .BuildServiceProvider()
            .CreateScope();
 
        var services = scope.ServiceProvider;
        var context = services.GetRequiredService<ILatencyContextProvider>().CreateContext();
 
        context.Freeze();
        var afterFreezeDuration = context.LatencyData.DurationTimestamp;
 
        await Task.Delay(1);
 
        var afterDelayDuration = context.LatencyData.DurationTimestamp;
 
        Assert.IsAssignableFrom<LatencyContext>(context);
        Assert.False(((LatencyContext)context).IsRunning);
        Assert.True(afterFreezeDuration.Equals(afterDelayDuration));
    }
 
    [Fact]
    public void Latency_Context_Is_Not_Adding_Checkpoints_When_Frozen()
    {
        const string TokenName = nameof(TokenName);
 
        using var scope = new ServiceCollection()
            .AddLatencyContext()
            .BuildServiceProvider()
            .CreateScope();
 
        var services = scope.ServiceProvider;
        var context = services.GetRequiredService<ILatencyContextProvider>().CreateContext();
        var tokenIssuer = services.GetRequiredService<ILatencyContextTokenIssuer>();
        var checkpointToken = tokenIssuer.GetCheckpointToken(TokenName);
 
        context.Freeze();
        context.AddCheckpoint(checkpointToken);
 
        var checkpoints = context.LatencyData.Checkpoints;
 
        Assert.IsAssignableFrom<LatencyContext>(context);
        Assert.False(((LatencyContext)context).IsRunning);
        Assert.Empty(checkpoints.ToArray());
    }
 
    [Fact]
    public void Latency_Context_Is_Adding_Checkpoints_When_Not_Frozen()
    {
        const string TokenName = nameof(TokenName);
 
        using var scope = new ServiceCollection()
            .AddLatencyContext()
            .RegisterCheckpointNames(TokenName)
            .BuildServiceProvider()
            .CreateScope();
 
        var services = scope.ServiceProvider;
        var context = services.GetRequiredService<ILatencyContextProvider>().CreateContext();
        var tokenIssuer = services.GetRequiredService<ILatencyContextTokenIssuer>();
        var checkpointToken = tokenIssuer.GetCheckpointToken(TokenName);
 
        context.AddCheckpoint(checkpointToken);
 
        var checkpoints = context.LatencyData.Checkpoints;
 
        Assert.IsAssignableFrom<LatencyContext>(context);
        Assert.True(((LatencyContext)context).IsRunning);
        Assert.Single(checkpoints.ToArray());
        Assert.Equal(TokenName, checkpoints[0].Name);
    }
 
    private LatencyContext GetContext()
    {
        var r = GetRegistry();
        var li = new LatencyInstrumentProvider(r);
        var lcp = new LatencyContextPool(li);
        return lcp.Pool.Get();
    }
 
    private LatencyContextRegistrySet GetRegistry()
    {
        var option = MockLatencyContextRegistrationOptions.GetLatencyContextRegistrationOptions(
            _checkpoints, _measures, _tags);
        var lco = new Mock<IOptions<LatencyContextOptions>>();
        lco.Setup(a => a.Value).Returns(new LatencyContextOptions { ThrowOnUnregisteredNames = false });
 
        var r = new LatencyContextRegistrySet(lco.Object, option);
 
        return r;
    }
 
    private class MockResetOnGet : ObjectPool<LatencyContext>
    {
        public bool ReturnCalled;
        private readonly ObjectPool<LatencyContext> _objectPool;
 
        public MockResetOnGet(LatencyContextPool latencyContextPool)
        {
            var policy = new LatencyContextPool.LatencyContextPolicy(latencyContextPool);
            _objectPool = PoolFactory.CreatePool(policy);
        }
 
        public override LatencyContext Get()
        {
            var o = _objectPool.Get();
            _ = o.TryReset();
            return o;
        }
 
        public override void Return(LatencyContext obj)
        {
            ReturnCalled = true;
            _objectPool.Return(obj);
        }
    }
}