File: InProcess\AppOfflineIISExpressTests.cs
Web Access
Project: src\src\Servers\IIS\IIS\test\IISExpress.FunctionalTests\IISExpress.FunctionalTests.csproj (IISExpress.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.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.InternalTesting;
using Xunit;
 
namespace Microsoft.AspNetCore.Server.IIS.IISExpress.FunctionalTests;
 
[Collection(PublishedSitesCollection.Name)]
[SkipOnHelix("Unsupported queue", Queues = "Windows.Amd64.VS2022.Pre.Open;")]
public class AppOfflineIISExpressTests : IISFunctionalTestBase
{
    public AppOfflineIISExpressTests(PublishedSitesFixture fixture) : base(fixture)
    {
    }
 
    [ConditionalFact]
    public async Task AppOfflineDroppedWhileSiteStarting_SiteShutsDown_InProcess()
    {
        // This test often hits a race between debug logging and stdout redirection closing the handle
        // we are fine having this race
        using (AppVerifier.Disable(DeployerSelector.ServerType, 0x300))
        {
            var deploymentResult = await DeployApp(HostingModel.InProcess);
 
            for (int i = 0; i < 10; i++)
            {
                // send first request and add app_offline while app is starting
                var runningTask = AssertAppOffline(deploymentResult);
 
                // This test tries to hit a race where we drop app_offline file while
                // in process application is starting, application start takes at least 400ms
                // so we back off for 100ms to allow request to reach request handler
                // Test itself is racy and can result in two scenarios
                //    1. ANCM detects app_offline before it starts the request - if AssertAppOffline succeeds we've hit it
                //    2. Intended scenario where app starts and then shuts down
                // In first case we remove app_offline and try again
                await Task.Delay(RetryDelay);
 
                AddAppOffline(deploymentResult.ContentRoot);
 
                try
                {
                    await runningTask.TimeoutAfter(TimeoutExtensions.DefaultTimeoutValue);
 
                    // if AssertAppOffline succeeded ANCM have picked up app_offline before starting the app
                    // try again
                    RemoveAppOffline(deploymentResult.ContentRoot);
                }
                catch
                {
                    // For IISExpress, we need to catch the exception because IISExpress will not restart a process if it crashed.
                    // RemoveAppOffline will fail due to a bad request exception as the server is down.
                    Assert.Contains(TestSink.Writes, context => context.Message.Contains("Drained all requests, notifying managed."));
                    deploymentResult.AssertWorkerProcessStop();
                    return;
                }
            }
 
            Assert.True(false);
        }
    }
}