Reduce probability of MultipleAppTests failure (#1365)
- Decrease false-positive failure rate from 1 in 1,000 to 1 in 1,000,000 - Addresses #1350
This commit is contained in:
parent
9b5e34e197
commit
2f4172e7e7
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
@ -25,42 +26,76 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
[ConditionalTheory]
|
[ConditionalTheory]
|
||||||
[InlineData(AncmVersion.AspNetCoreModule)]
|
[InlineData(AncmVersion.AspNetCoreModule)]
|
||||||
[InlineData(AncmVersion.AspNetCoreModuleV2)]
|
[InlineData(AncmVersion.AspNetCoreModuleV2)]
|
||||||
public async Task Startup(AncmVersion ancmVersion)
|
public Task Startup(AncmVersion ancmVersion)
|
||||||
{
|
{
|
||||||
const int numApps = 10;
|
// ANCM v1 currently does *not* retry if an app fails to start the first time due to a port collision.
|
||||||
|
// So, this test is expected to fail on v1 approximately 1 in 1,000 times (probably of at least one collision
|
||||||
|
// when 10 sites choose a random port from the range 1025-48000). Adding one retry should reduce the failure
|
||||||
|
// rate from 1 in 1,000 to 1 in 1,000,000. The previous product code (with "srand(GetTickCount())") should still
|
||||||
|
// fail the test reliably.
|
||||||
|
// https://github.com/aspnet/IISIntegration/issues/1350
|
||||||
|
//
|
||||||
|
// ANCM v2 does retry on port collisions, so no retries should be required.
|
||||||
|
var attempts = (ancmVersion == AncmVersion.AspNetCoreModule) ? 2 : 1;
|
||||||
|
|
||||||
using (var deployers = new DisposableList<ApplicationDeployer>())
|
return Retry(async () =>
|
||||||
{
|
{
|
||||||
var deploymentResults = new List<DeploymentResult>();
|
const int numApps = 10;
|
||||||
|
|
||||||
// Deploy all apps
|
using (var deployers = new DisposableList<ApplicationDeployer>())
|
||||||
for (var i = 0; i < numApps; i++)
|
|
||||||
{
|
{
|
||||||
var deploymentParameters = _fixture.GetBaseDeploymentParameters(hostingModel: IntegrationTesting.HostingModel.OutOfProcess);
|
var deploymentResults = new List<DeploymentResult>();
|
||||||
deploymentParameters.AncmVersion = ancmVersion;
|
|
||||||
|
|
||||||
var deployer = CreateDeployer(deploymentParameters);
|
// Deploy all apps
|
||||||
deployers.Add(deployer);
|
for (var i = 0; i < numApps; i++)
|
||||||
deploymentResults.Add(await deployer.DeployAsync());
|
{
|
||||||
|
var deploymentParameters = _fixture.GetBaseDeploymentParameters(hostingModel: IntegrationTesting.HostingModel.OutOfProcess);
|
||||||
|
deploymentParameters.AncmVersion = ancmVersion;
|
||||||
|
|
||||||
|
var deployer = CreateDeployer(deploymentParameters);
|
||||||
|
deployers.Add(deployer);
|
||||||
|
deploymentResults.Add(await deployer.DeployAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start all requests as quickly as possible, so apps are started as quickly as possible,
|
||||||
|
// to test possible race conditions when multiple apps start at the same time.
|
||||||
|
var requestTasks = new List<Task<HttpResponseMessage>>();
|
||||||
|
foreach (var deploymentResult in deploymentResults)
|
||||||
|
{
|
||||||
|
requestTasks.Add(deploymentResult.HttpClient.GetAsync("/HelloWorld"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify all apps started and return expected response
|
||||||
|
foreach (var requestTask in requestTasks)
|
||||||
|
{
|
||||||
|
var response = await requestTask;
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var responseText = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal("Hello World", responseText);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
attempts: attempts);
|
||||||
|
}
|
||||||
|
|
||||||
// Start all requests as quickly as possible, so apps are started as quickly as possible,
|
private async Task Retry(Func<Task> func, int attempts)
|
||||||
// to test possible race conditions when multiple apps start at the same time.
|
{
|
||||||
var requestTasks = new List<Task<HttpResponseMessage>>();
|
var exceptions = new List<Exception>();
|
||||||
foreach (var deploymentResult in deploymentResults)
|
|
||||||
|
for (var attempt = 0; attempt < attempts; attempt++)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
requestTasks.Add(deploymentResult.HttpClient.GetAsync("/HelloWorld"));
|
await func();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
// Verify all apps started and return expected response
|
|
||||||
foreach (var requestTask in requestTasks)
|
|
||||||
{
|
{
|
||||||
var response = await requestTask;
|
exceptions.Add(e);
|
||||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
||||||
var responseText = await response.Content.ReadAsStringAsync();
|
|
||||||
Assert.Equal("Hello World", responseText);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new AggregateException(exceptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue