Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

namespace Microsoft.Test.E2E.AspNet.OData.Aggregation
{
[Collection("Database")] // Shared static AggregationContext state; serialized with all DB-backed E2E tests in the "Database" collection
public class AggregationTestsEFClassic: AggregationTests
{
public AggregationTestsEFClassic(WebHostTestFixture fixture)
Expand Down Expand Up @@ -58,6 +59,7 @@ public async Task CustomAggregateStdDevWorks(string query)
}

#if NETCORE
[Collection("Database")] // Shared static AggregationContext state; serialized with all DB-backed E2E tests in the "Database" collection
public class AggregationTestsEFCoreInMemory : AggregationTests
{
public AggregationTestsEFCoreInMemory(WebHostTestFixture fixture)
Expand All @@ -72,6 +74,7 @@ protected override void UpdateConfiguration(WebRouteConfiguration configuration)
}
}

[Collection("Database")] // Shared static AggregationContext state; serialized with all DB-backed E2E tests in the "Database" collection
public class AggregationTestsEFCoreSql : AggregationTests
{
public AggregationTestsEFCoreSql(WebHostTestFixture fixture)
Expand All @@ -89,6 +92,7 @@ protected override void UpdateConfiguration(WebRouteConfiguration configuration)


#if !NETCORE
[Collection("Database")] // Shared static AggregationContext state; serialized with all DB-backed E2E tests in the "Database" collection
public class LinqToSqlAggregationTests : WebHostTestBase
{
protected string AggregationTestBaseUrl => "{0}/aggregation/Customers";
Expand Down Expand Up @@ -783,6 +787,7 @@ public async Task ComputeWorks()
}
}

[Collection("Database")] // Shared static AggregationContext state; serialized with all DB-backed E2E tests in the "Database" collection
public class NestedComplexPropertyAggregationTests : WebHostTestBase
{
private string AggregationTestBaseUrl => "{0}/aggregation/Employees";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

namespace Microsoft.Test.E2E.AspNet.OData.Aggregation
{
[Collection("Database")] // Shared static AggregationContext state; serialized with all DB-backed E2E tests in the "Database" collection
public class PagedAggregationTests : WebHostTestBase
{
private const string AggregationTestBaseUrl = "{0}/pagedaggregation/Customers";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

namespace Microsoft.Test.E2E.AspNet.OData.AutoExpand
{
// Shared LocalDB instance: serialized in the "Database" collection to avoid concurrent DROP/CREATE DATABASE deadlock under parallel runs.
[Collection("Database")]
public class AutoExpandTests : WebHostTestBase
{
private const string AutoExpandTestBaseUrl = "{0}/autoexpand/Customers(5)";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ protected override Task<ITestActionResult> CreateEntityAsync(DefaultBatchOrder e
}
}

[Collection("Batch")] // Shared static in-memory data store across servers; run serially under parallel execution
public class DefaultBatchHandlerCUDBatchTests : WebHostTestBase
{
public DefaultBatchHandlerCUDBatchTests(WebHostTestFixture fixture)
Expand Down Expand Up @@ -569,6 +570,7 @@ public async Task CanHandleSingleDeleteInBatch()
}
}

[Collection("Batch")] // Shared static in-memory data store across servers; run serially under parallel execution
public class DefaultBatchHandlerQueryBatchTests : WebHostTestBase
{
public DefaultBatchHandlerQueryBatchTests(WebHostTestFixture fixture)
Expand Down Expand Up @@ -638,6 +640,7 @@ public async Task CanBatchQueriesWithDataServicesClient()
}


[Collection("Batch")] // Shared static in-memory data store across servers; run serially under parallel execution
public class DefaultBatchHandlerErrorsBatchTests : WebHostTestBase
{
public DefaultBatchHandlerErrorsBatchTests(WebHostTestFixture fixture)
Expand Down Expand Up @@ -705,6 +708,7 @@ public async Task SendsIndividualErrorWhenOneOfTheRequestsFails()
}


[Collection("Batch")] // Shared static in-memory data store across servers; run serially under parallel execution
public class DefaultBatchHandlerLinksBatchTests : WebHostTestBase
{
public DefaultBatchHandlerLinksBatchTests(WebHostTestFixture fixture)
Expand Down Expand Up @@ -758,6 +762,7 @@ public async Task CanSetLinksInABatchWithDataServicesClient()
}
}

[Collection("Batch")] // Shared static in-memory data store across servers; run serially under parallel execution
public class DefaultBatchHandlerContinueOnErrorBatchTests : WebHostTestBase
{
public DefaultBatchHandlerContinueOnErrorBatchTests(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ protected override Task<ITestActionResult> CreateEntityAsync(UnbufferedBatchOrde
}
}

[Collection("Batch")] // Shared static in-memory data store across servers; run serially under parallel execution
public class CUDBatchTests : WebHostTestBase
{
public CUDBatchTests(WebHostTestFixture fixture)
Expand Down Expand Up @@ -458,6 +459,7 @@ public async Task CanHandleSingleDeleteInBatch()
}
}

[Collection("Batch")] // Shared static in-memory data store across servers; run serially under parallel execution
public class QueryBatchTests : WebHostTestBase
{
public QueryBatchTests(WebHostTestFixture fixture)
Expand Down Expand Up @@ -522,6 +524,7 @@ public async Task CanBatchQueriesWithDataServicesClient()
}
}

[Collection("Batch")] // Shared static in-memory data store across servers; run serially under parallel execution
public class ErrorsBatchTests : WebHostTestBase
{
public ErrorsBatchTests(WebHostTestFixture fixture)
Expand Down Expand Up @@ -583,6 +586,7 @@ public async Task SendsIndividualErrorWhenOneOfTheRequestsFails()
}
}

[Collection("Batch")] // Shared static in-memory data store across servers; run serially under parallel execution
public class LinksBatchTests : WebHostTestBase
{
public LinksBatchTests(WebHostTestFixture fixture)
Expand Down Expand Up @@ -629,6 +633,7 @@ public virtual async Task CanSetLinksInABatchWithDataServicesClient()
}
}

[Collection("Batch")] // Shared static in-memory data store across servers; run serially under parallel execution
public class ContinueOnErrorBatchTests : WebHostTestBase
{
public ContinueOnErrorBatchTests(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

namespace Microsoft.Test.E2E.AspNet.OData.NestedPaths
{
// Shared LocalDB instance: serialized in the "Database" collection to avoid concurrent DROP/CREATE DATABASE deadlock under parallel runs.
[Collection("Database")]
public class NestedPathEFTests : WebHostTestBase
{
public NestedPathEFTests(WebHostTestFixture fixture)
Expand Down
2 changes: 2 additions & 0 deletions test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Cast/CastTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

namespace Microsoft.Test.E2E.AspNet.OData.Cast
{
// Shared LocalDB instance: serialized in the "Database" collection to avoid concurrent DROP/CREATE DATABASE deadlock under parallel runs.
[Collection("Database")]
public class CastTest : WebHostTestBase
{
private static string[] dataSourceTypes = new string[] { "IM", "EF" };// In Memory and Entity Framework
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ public class PortArranger
public static int Reserve()
{
int attempts = 0;
while (attempts++ < 10)
// Scan a generous number of candidate ports before giving up. Under parallel test
// execution many servers start at once and batch/DoS tests leave numerous sockets in
// TIME_WAIT, so short runs of consecutive ports can all appear busy; a small retry
// budget (the original was 10) intermittently threw "Cannot get an available port".
while (attempts++ < 200)
{
int port = Interlocked.Increment(ref nextPort);
if (port >= 65535)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,31 @@
using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;
#endif

// Parallelism in the test framework is a feature that is new for (Xunit) version 2. However,
// since each test will spin up a number of web servers each with a listening port, disabling the
// parallel test with take a bit long but consume fewer resources with more stable results.
// Each WebHostTestBase subclass spins up its own self-hosted web server (Kestrel/Katana) on a
// dynamically reserved port (see PortArranger.Reserve), so distinct test classes are isolated on the
// wire and can run concurrently. Tests WITHIN a class still run serially against a single shared
// server (IClassFixture<WebHostTestFixture>), which is the behavior the fixture requires.
//
// By default, each test class is a unique test collection. Tests within the same test class will not run
// in parallel against each other. That means that there may be up to # subclasses of WebHostTestBase
// web servers running at any point during the test run, currently ~500. Without this, there would be a
// web server per test case since Xunit 2.0 spawns a new test class instance for each test case.
// By default xUnit uses one test collection per class and runs collections in parallel, so classes
// execute concurrently up to MaxParallelThreads. We cap the degree of parallelism (rather than leaving
// it unbounded / equal to the core count) so we never spin up more than a handful of web servers at
// once: this keeps memory and socket usage bounded while cutting wall-clock time dramatically versus
// fully serial execution.
//
// Using both Disable and Max Threads per this discussion: https://github.com/xunit/xunit/issues/276
// Parallel execution is currently enabled only for the AspNetCore 3.x E2E project (NETCORE && !NETCORE2x),
// which is the configuration validated for concurrency. The classic (.NET Framework) and AspNetCore 2.x
// projects keep the original fully serial behavior until they are similarly hardened.
//
// A moderate cap (4) is deliberate: every EF/database-backed test class shares a single LocalDB instance
// and drops/recreates its database on start, so running many at once serializes on LocalDB DDL and can
// deadlock. Those DB-backed classes (and the process-global TimeZoneInfo tests) are grouped into a single
// serial "Database" xUnit collection so they never run concurrently with one another, while the remaining
// in-memory test classes still parallelize up to the cap.
#if NETCORE && !NETCORE2x
[assembly: CollectionBehavior(MaxParallelThreads = 4)]
#else
[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, MaxParallelThreads = 1)]
#endif

namespace Microsoft.Test.E2E.AspNet.OData.Common.Execution
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

namespace Microsoft.Test.E2E.AspNet.OData.DateAndTimeOfDay
{
[Collection("TimeZoneTests")] // TimeZoneInfo is not thread-safe. Tests in this collection will be executed sequentially
[Collection("Database")] // TimeZoneInfo is not thread-safe; serialized via the shared "Database" collection so timezone + DB-backed tests never overlap
public class DateAndTimeOfDayTest : WebHostTestBase
{
public DateAndTimeOfDayTest(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

namespace Microsoft.Test.E2E.AspNet.OData.DateAndTimeOfDay
{
// Shared LocalDB instance: serialized in the "Database" collection to avoid concurrent DROP/CREATE DATABASE deadlock under parallel runs.
[Collection("Database")]
public class DateAndTimeOfDayWithEfTest : WebHostTestBase
{
public DateAndTimeOfDayWithEfTest(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

namespace Microsoft.Test.E2E.AspNet.OData.DateAndTimeOfDay
{
// Shared LocalDB instance: serialized in the "Database" collection to avoid concurrent DROP/CREATE DATABASE deadlock under parallel runs.
[Collection("Database")]
public class DateWithEfTest : WebHostTestBase
{
public DateWithEfTest(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

namespace Microsoft.Test.E2E.AspNet.OData.DateTimeOffsetSupport
{
// Shared LocalDB instance: serialized in the "Database" collection to avoid concurrent DROP/CREATE DATABASE deadlock under parallel runs.
[Collection("Database")]
public class DateTimeOffsetTest : WebHostTestBase
{
#region Configuration and Static Members
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

namespace Microsoft.Test.E2E.AspNet.OData.DateTimeSupport
{
[Collection("TimeZoneTests")] // TimeZoneInfo is not thread-safe. Tests in this collection will be executed sequentially
[Collection("Database")] // TimeZoneInfo is not thread-safe; serialized via the shared "Database" collection so timezone + DB-backed tests never overlap
public class DateTimeTest : WebHostTestBase
{
public DateTimeTest(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

namespace Microsoft.Test.E2E.AspNet.OData.ETags
{
// Shared LocalDB instance: serialized in the "Database" collection to avoid concurrent DROP/CREATE DATABASE deadlock under parallel runs.
[Collection("Database")]
public class ETagCurrencyTokenEfContextTest : WebHostTestBase
{
public ETagCurrencyTokenEfContextTest(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

namespace Microsoft.Test.E2E.AspNet.OData.EntitySetAggregation
{
// Shared LocalDB instance: serialized in the "Database" collection to avoid concurrent DROP/CREATE DATABASE deadlock under parallel runs.
[Collection("Database")]
public class EntitySetAggregationTests : WebHostTestBase
{
private const string AggregationTestBaseUrl = "{0}/aggregation/Customers";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

namespace Microsoft.Test.E2E.AspNet.OData.ForeignKey
{
// Shared LocalDB instance: serialized in the "Database" collection to avoid concurrent DROP/CREATE DATABASE deadlock under parallel runs.
[Collection("Database")]
public class ForeignKeyTest : WebHostTestBase
{
public ForeignKeyTest(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

namespace Microsoft.Test.E2E.AspNet.OData.Formatter.JsonLight.Metadata
{
[Collection("JsonLightMetadata")] // Shared static random-data table across servers; run serially under parallel execution
public class ActionMetadataTests : WebHostTestBase
{
public ActionMetadataTests(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

namespace Microsoft.Test.E2E.AspNet.OData.Formatter.JsonLight.Metadata
{
[Collection("JsonLightMetadata")] // Shared static random-data table across servers; run serially under parallel execution
public class ComplexTypeTests : WebHostTestBase
{
public ComplexTypeTests(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

namespace Microsoft.Test.E2E.AspNet.OData.Formatter.JsonLight.Metadata
{
[Collection("JsonLightMetadata")] // Shared static random-data table across servers; run serially under parallel execution
public class CustomConventionActionMetadataTests : WebHostTestBase
{
public CustomConventionActionMetadataTests(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

namespace Microsoft.Test.E2E.AspNet.OData.Formatter.JsonLight.Metadata
{
[Collection("JsonLightMetadata")] // Shared static random-data table across servers; run serially under parallel execution
public class EntryMetadataTests : WebHostTestBase
{
public EntryMetadataTests(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

namespace Microsoft.Test.E2E.AspNet.OData.Formatter.JsonLight.Metadata
{
[Collection("JsonLightMetadata")] // Shared static random-data table across servers; run serially under parallel execution
public class FeedMetadataTests : WebHostTestBase
{
public FeedMetadataTests(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

namespace Microsoft.Test.E2E.AspNet.OData.Formatter.JsonLight.Metadata
{
[Collection("JsonLightMetadata")] // Shared static random-data table across servers; run serially under parallel execution
public class MinimalMetadataSpecificTests : WebHostTestBase
{
public MinimalMetadataSpecificTests(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

namespace Microsoft.Test.E2E.AspNet.OData.Formatter.JsonLight.Metadata
{
[Collection("JsonLightMetadata")] // Shared static random-data table across servers; run serially under parallel execution
public class PrimitiveTypesMetadataTests : WebHostTestBase
{
public PrimitiveTypesMetadataTests(WebHostTestFixture fixture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,19 @@ public async Task BigDataServiceVersionHeaderShouldBeRejected()
request.Content = new StringContent(JsonConvert.SerializeObject(model));
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
request.Headers.Add("DataServiceVersion", asb.ToString());
var response = await this.Client.SendAsync(request);

Assert.False(response.IsSuccessStatusCode);
try
{
var response = await this.Client.SendAsync(request);

Assert.False(response.IsSuccessStatusCode);
}
catch (HttpRequestException)
{
// The oversized header can also be rejected by the server resetting the connection,
// which surfaces as an HttpRequestException on the client. That is still a valid
// rejection of the DoS payload (observed under load on Kestrel/AspNetCore).
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class Customer
public string Name { get; set; }

[Expand(ExpandType = SelectExpandType.Disabled)]
[Page(MaxTop = 2)]
public Order Order { get; set; }

public Order AutoExpandOrder { get; set; }
Expand Down Expand Up @@ -50,6 +51,10 @@ public class Customer
[Filter]
[OrderBy("Id", Disabled = true)]
[OrderBy]
// Pin an explicit MaxTop instead of relying on the default. The default $top limit comes from the
// process-global ModelBoundQuerySettings.DefaultModelBoundQuerySettings.MaxTop, which other tests
// mutate concurrently once the E2E suite runs in parallel, making the $top assertions below flaky.
[Page(MaxTop = 2)]
public class Order
{
public int Id { get; set; }
Expand Down Expand Up @@ -108,10 +113,12 @@ public static IEdmModel GetEdmModelByModelBoundAPI(WebRouteConfiguration configu
.Count();
builder.EntityType<Customer>()
.HasOptional(p => p.Order)
.Expand(SelectExpandType.Disabled);
.Expand(SelectExpandType.Disabled)
.Page(2, null);

builder.EntitySet<Order>("Orders")
.EntityType.Expand(6)
.Page(2, null)
.Expand(SelectExpandType.Disabled, "NoExpandCustomers")
.Count()
.Filter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

namespace Microsoft.Test.E2E.AspNet.OData.ModelBoundQuerySettings.PageAttributeTest.SkipTokenTest
{
[Collection("TimeZoneTests")] // TimeZoneInfo is not thread-safe. Tests in this collection will be executed sequentially
[Collection("Database")] // TimeZoneInfo is not thread-safe; serialized via the shared "Database" collection so timezone + DB-backed tests never overlap
public class SkipTokenTest : WebHostTestBase
{
private const string CustomerBaseUrl = "{0}/skiptokentest/Customers";
Expand Down
Loading