File: H2SpecTests.cs
Web Access
Project: src\src\Servers\Kestrel\test\Interop.FunctionalTests\Interop.FunctionalTests.csproj (Interop.FunctionalTests)
// 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.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.InternalTesting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
using Xunit.Abstractions;
 
namespace Interop.FunctionalTests;
 
public class H2SpecTests : LoggedTest
{
 
    [SkipOnArchitecture(Architecture.Arm64, Architecture.X86)] // The h2spec executable is an x64-binary
    [ConditionalTheory]
    [MemberData(nameof(H2SpecTestCases))]
    public async Task RunIndividualTestCase(H2SpecTestCase testCase)
    {
        var hostBuilder = new HostBuilder()
            .ConfigureWebHost(webHostBuilder =>
            {
                webHostBuilder
                    .UseKestrel(options =>
                    {
                        options.Listen(IPAddress.Loopback, 0, listenOptions =>
                        {
                            listenOptions.Protocols = HttpProtocols.Http2;
                            if (testCase.Https)
                            {
                                listenOptions.UseHttps(TestResources.GetTestCertificate());
                            }
                        });
                    })
                .Configure(ConfigureHelloWorld);
            })
            .ConfigureServices(AddTestLogging);
 
        using (var host = hostBuilder.Build())
        {
            await host.StartAsync();
 
            await H2SpecCommands.RunTest(testCase.Id, host.GetPort(), testCase.Https, Logger);
 
            await host.StopAsync();
        }
    }
 
    public static TheoryData<H2SpecTestCase> H2SpecTestCases
    {
        get
        {
            var dataset = new TheoryData<H2SpecTestCase>();
            // { Test name, Skip Reason }
            var toSkip = new Dictionary<string, string>();
 
            var testCases = H2SpecCommands.EnumerateTestCases();
 
            if (testCases == null || !testCases.Any())
            {
                dataset.Add(new H2SpecTestCase()
                {
                    Skip = "Unable to detect test cases on this platform.",
                });
                return dataset;
            }
 
            var supportsAlpn = Utilities.CurrentPlatformSupportsHTTP2OverTls();
 
            foreach (var testcase in testCases)
            {
                string skip = null;
                if (toSkip.TryGetValue(testcase.Item1, out var skipReason))
                {
                    skip = skipReason;
                }
 
                dataset.Add(new H2SpecTestCase
                {
                    Id = testcase.Item1,
                    Description = testcase.Item2,
                    Https = false,
                    Skip = skip,
                });
 
                // https://github.com/dotnet/aspnetcore/issues/11301 We should use Skip but it's broken at the moment.
                if (supportsAlpn)
                {
                    dataset.Add(new H2SpecTestCase
                    {
                        Id = testcase.Item1,
                        Description = testcase.Item2,
                        Https = true,
                        Skip = skip,
                    });
                }
            }
 
            return dataset;
        }
    }
 
    public class H2SpecTestCase : IXunitSerializable
    {
        // For the serializer
        public H2SpecTestCase()
        {
        }
 
        public string Id { get; set; }
        public string Description { get; set; }
        public bool Https { get; set; }
        public string Skip { get; set; }
 
        public void Deserialize(IXunitSerializationInfo info)
        {
            Id = info.GetValue<string>(nameof(Id));
            Description = info.GetValue<string>(nameof(Description));
            Https = info.GetValue<bool>(nameof(Https));
            Skip = info.GetValue<string>(nameof(Skip));
        }
 
        public void Serialize(IXunitSerializationInfo info)
        {
            info.AddValue(nameof(Id), Id, typeof(string));
            info.AddValue(nameof(Description), Description, typeof(string));
            info.AddValue(nameof(Https), Https, typeof(bool));
            info.AddValue(nameof(Skip), Skip, typeof(string));
        }
 
        public override string ToString()
        {
            return $"{Id}, HTTPS:{Https}, {Description}";
        }
    }
 
    private void ConfigureHelloWorld(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            // Read the whole request body to check for errors.
            await context.Request.Body.CopyToAsync(Stream.Null);
            await context.Response.WriteAsync("Hello World");
        });
    }
}