Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extensible Filters + AggregateFilter #4200

Draft
wants to merge 41 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
dedd0dc
Extensible + Aggregate Filters
msm-tomlonghurst Nov 29, 2024
f262c40
BuildFilterAsync
msm-tomlonghurst Nov 29, 2024
161cc8e
IsAvailable
thomhurst Nov 29, 2024
d362a3b
TreeNodeFilterTests fixes
thomhurst Nov 29, 2024
816cb2f
Whoops
thomhurst Nov 29, 2024
753d61f
RegisterTestExecutionFilter
thomhurst Nov 29, 2024
9c3aba6
Tidy usings
thomhurst Nov 29, 2024
480df01
Update public API
thomhurst Nov 29, 2024
73b585b
Update public API
thomhurst Nov 29, 2024
b5c1dec
PublicAPI.Unshipped.txt
thomhurst Nov 29, 2024
f6f4a1e
Fix
thomhurst Nov 30, 2024
96dbb6d
FiltersTests
thomhurst Nov 30, 2024
fbc8297
Fix warning
thomhurst Nov 30, 2024
142d747
Fix warning
thomhurst Nov 30, 2024
550b1f0
TestNodeUids.Length > 0
thomhurst Nov 30, 2024
8464f3c
Merge branch 'main' into feature/extensible-filters
thomhurst Nov 30, 2024
9ee34e9
Merge branch 'main' into feature/extensible-filters
thomhurst Nov 30, 2024
fbb722a
Comment failing tests
thomhurst Nov 30, 2024
a93f793
Merge remote-tracking branch 'origin/feature/extensible-filters' into…
thomhurst Nov 30, 2024
888e14b
Fix warning
thomhurst Nov 30, 2024
ff5dd33
Fix
thomhurst Nov 30, 2024
c8726bf
InnerExceptions
thomhurst Nov 30, 2024
b26c5ad
Update public API
thomhurst Nov 30, 2024
ada8e77
Fix Public API
thomhurst Nov 30, 2024
ae622cf
Fix Public API
thomhurst Nov 30, 2024
cf0eb8f
Fix Public API
thomhurst Nov 30, 2024
59ffe61
Fix Public API
thomhurst Nov 30, 2024
14e554e
Merge branch 'main' into feature/extensible-filters
thomhurst Dec 2, 2024
3c72755
ISupportsFilterCapability.cs
thomhurst Dec 2, 2024
7f34897
Public API fixes
thomhurst Dec 2, 2024
9104f79
Merge branch 'feature/extensible-filters' of https://github.com/thomh…
thomhurst Dec 2, 2024
01f28d7
Merge main
thomhurst Dec 2, 2024
dfcdcce
Fix Public API
thomhurst Dec 2, 2024
d6d5958
Merge
thomhurst Dec 12, 2024
51d5e92
MatchesFilter interface method
thomhurst Dec 12, 2024
800c276
Update public api
thomhurst Dec 12, 2024
76a000c
Fix VSTestTestExecutionFilter
thomhurst Dec 12, 2024
ea37b30
Fixes
thomhurst Dec 12, 2024
43df0ce
public API tweaks
thomhurst Dec 12, 2024
b88aeaf
Remove SupportsFilterCapability.cs
thomhurst Dec 12, 2024
e1164e8
public API
thomhurst Dec 12, 2024
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
2 changes: 0 additions & 2 deletions samples/Playground/ServerMode/v1.0.0/ClientInfo.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Text.Json;

using Newtonsoft.Json;

namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100;
Expand Down
2 changes: 0 additions & 2 deletions samples/Playground/ServerMode/v1.0.0/InitializeRequest.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Text.Json;

using Newtonsoft.Json;

namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100;
Expand Down
2 changes: 0 additions & 2 deletions samples/Playground/ServerMode/v1.0.0/ServerInfo.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Text.Json;

using Newtonsoft.Json;

namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Operations;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequest
Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequest.VSTestRunTestExecutionRequest(Microsoft.Testing.Platform.TestHost.TestSessionContext! session, Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestTestExecutionFilter! executionFilter, string![]! assemblyPaths, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IRunContext! runContext, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IFrameworkHandle! frameworkHandle) -> void
Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequestFactory
Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestTestExecutionFilter
Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestTestExecutionFilter.TestCases.get -> System.Collections.Immutable.ImmutableArray<Microsoft.VisualStudio.TestPlatform.ObjectModel.TestCase!>?
Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework
Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.Dispose() -> void
Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.SynchronizedSingleSessionVSTestBridgedTestFramework(Microsoft.Testing.Platform.Extensions.IExtension! extension, System.Func<System.Collections.Generic.IEnumerable<System.Reflection.Assembly!>!>! getTestAssemblies, System.IServiceProvider! serviceProvider, Microsoft.Testing.Platform.Capabilities.TestFramework.ITestFrameworkCapabilities! capabilities) -> void
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
#nullable enable
Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestTestExecutionFilter.IsAvailable.get -> bool
Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestTestExecutionFilter.MatchesFilter(Microsoft.Testing.Platform.Extensions.Messages.TestNode! testNode) -> bool
Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestTestExecutionFilter.TestCases.get -> System.Collections.Immutable.ImmutableArray<Microsoft.VisualStudio.TestPlatform.ObjectModel.TestCase!>
static Microsoft.Testing.Extensions.VSTestBridge.Helpers.TestApplicationBuilderExtensions.AddRunSettingsEnvironmentVariableProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder, Microsoft.Testing.Platform.Extensions.IExtension! extension) -> void
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Immutable;

using Microsoft.Testing.Platform.Extensions.Messages;
using Microsoft.Testing.Platform.Requests;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;

Expand All @@ -13,11 +14,13 @@ namespace Microsoft.Testing.Extensions.VSTestBridge.Requests;
/// </summary>
public sealed class VSTestTestExecutionFilter : ITestExecutionFilter
{
internal VSTestTestExecutionFilter()
{
}
internal VSTestTestExecutionFilter() => TestCases = ImmutableArray<TestCase>.Empty;

internal VSTestTestExecutionFilter(ImmutableArray<TestCase> testCases) => TestCases = testCases;

public ImmutableArray<TestCase>? TestCases { get; }
public ImmutableArray<TestCase> TestCases { get; }

public bool IsAvailable => !TestCases.IsDefaultOrEmpty;

public bool MatchesFilter(TestNode testNode) => TestCases.Any(x => x.Id.ToString() == testNode.Uid.Value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using Microsoft.Testing.Platform.Builder;
using Microsoft.Testing.Platform.CommandLine;
using Microsoft.Testing.Platform.Extensions;
using Microsoft.Testing.Platform.Requests;
using Microsoft.Testing.Platform.Services;

namespace Microsoft.Testing.Platform.Helpers;

Expand All @@ -14,7 +16,10 @@ namespace Microsoft.Testing.Platform.Helpers;
public static class TestApplicationBuilderExtensions
{
public static void AddTreeNodeFilterService(this ITestApplicationBuilder testApplicationBuilder, IExtension extension)
=> testApplicationBuilder.CommandLine.AddProvider(() => new TreeNodeFilterCommandLineOptionsProvider(extension));
{
testApplicationBuilder.CommandLine.AddProvider(() => new TreeNodeFilterCommandLineOptionsProvider(extension));
testApplicationBuilder.TestHost.RegisterTestExecutionFilter(sp => new TreeNodeFilter(sp.GetCommandLineOptions()));
}

/// <summary>
/// Registers the command-line options provider for '--maximum-failed-tests'.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ internal sealed class ConsoleTestHost(

private readonly ILogger<ConsoleTestHost> _logger = serviceProvider.GetLoggerFactory().CreateLogger<ConsoleTestHost>();
private readonly IClock _clock = serviceProvider.GetClock();
private readonly Func<TestFrameworkBuilderData, Task<ITestFramework>> _buildTestFrameworkAsync = buildTestFrameworkAsync;

private readonly TestFrameworkManager _testFrameworkManager = testFrameworkManager;
private readonly TestHostManager _testHostManager = testHostManager;

protected override bool RunTestApplicationLifeCycleCallbacks => true;

Expand All @@ -48,24 +44,21 @@ protected override async Task<int> InternalRunAsync()
// Add the ClientInfo service to the service provider
ServiceProvider.TryAddService(ClientInfoService);

// Use user provided filter factory or create console default one.
ITestExecutionFilterFactory testExecutionFilterFactory = ServiceProvider.GetService<ITestExecutionFilterFactory>()
?? new ConsoleTestExecutionFilterFactory(ServiceProvider.GetCommandLineOptions());

// Use user provided filter factory or create console default one.
ITestFrameworkInvoker testAdapterInvoker = ServiceProvider.GetService<ITestFrameworkInvoker>()
?? new TestHostTestFrameworkInvoker(ServiceProvider);

ITestExecutionFilter filter = await testHostManager.BuildFilterAsync(ServiceProvider, []);

ServiceProvider.TryAddService(new Services.TestSessionContext(abortRun));
ITestFramework testFramework = await _buildTestFrameworkAsync(new(
ITestFramework testFramework = await buildTestFrameworkAsync(new TestFrameworkBuilderData(
ServiceProvider,
new ConsoleTestExecutionRequestFactory(ServiceProvider.GetCommandLineOptions(), testExecutionFilterFactory),
new ConsoleTestExecutionRequestFactory(ServiceProvider.GetCommandLineOptions(), filter),
testAdapterInvoker,
testExecutionFilterFactory,
ServiceProvider.GetPlatformOutputDevice(),
[],
_testFrameworkManager,
_testHostManager,
testFrameworkManager,
testHostManager,
new MessageBusProxy(),
ServiceProvider.GetCommandLineOptions().IsOptionSet(PlatformCommandLineProvider.DiscoverTestsOptionKey),
false));
Expand Down
21 changes: 5 additions & 16 deletions src/Platform/Microsoft.Testing.Platform/Hosts/ServerTestHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -447,27 +447,17 @@ private async Task<ResponseArgsBase> ExecuteRequestAsync(RequestArgsBase args, s
// catch and propagated as correct json rpc error
perRequestTestSessionContext.CancellationToken.ThrowIfCancellationRequested();

// Note: Currently the request generation and filtering isn't extensible
// in server mode, we create NoOp services, so that they're always available.
ICollection<TestNode>? testNodes = args.TestNodes;
ITestExecutionFilter executionFilter = await _testSessionManager.BuildFilterAsync(ServiceProvider, testNodes);

ServerTestExecutionRequestFactory requestFactory = new(session =>
{
ICollection<TestNode>? testNodes = args.TestNodes;
string? filter = args.GraphFilter;
ITestExecutionFilter executionFilter = testNodes is not null
? new TestNodeUidListFilter(testNodes.Select(node => node.Uid).ToArray())
: filter is not null
? new TreeNodeFilter(filter)
: new NopFilter();

return method == JsonRpcMethods.TestingRunTests
method == JsonRpcMethods.TestingRunTests
? new RunTestExecutionRequest(session, executionFilter)
: method == JsonRpcMethods.TestingDiscoverTests
? new DiscoverTestExecutionRequest(session, executionFilter)
: throw new NotImplementedException($"Request not implemented '{method}'");
});
: throw new NotImplementedException($"Request not implemented '{method}'"));

// Build the per request objects
ServerTestExecutionFilterFactory filterFactory = new();
TestHostTestFrameworkInvoker invoker = new(perRequestServiceProvider);
PerRequestServerDataConsumer testNodeUpdateProcessor = new(perRequestServiceProvider, this, args.RunId, perRequestServiceProvider.GetTask());

Expand All @@ -485,7 +475,6 @@ private async Task<ResponseArgsBase> ExecuteRequestAsync(RequestArgsBase args, s
perRequestServiceProvider,
requestFactory,
invoker,
filterFactory,
outputDevice.OriginalOutputDevice,
[testNodeUpdateProcessor],
_testFrameworkManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace Microsoft.Testing.Platform.Hosts;

internal sealed class TestFrameworkBuilderData(ServiceProvider serviceProvider, ITestExecutionRequestFactory testExecutionRequestFactory,
ITestFrameworkInvoker testExecutionRequestInvoker, ITestExecutionFilterFactory testExecutionFilterFactory,
ITestFrameworkInvoker testExecutionRequestInvoker,
IPlatformOutputDevice platformOutputDisplayService, IEnumerable<IDataConsumer> serverPerCallConsumers,
TestFrameworkManager testFrameworkManager, TestHostManager testSessionManager, MessageBusProxy messageBusProxy,
bool isForDiscoveryRequest,
Expand All @@ -24,8 +24,6 @@ internal sealed class TestFrameworkBuilderData(ServiceProvider serviceProvider,

public ITestFrameworkInvoker TestExecutionRequestInvoker { get; } = testExecutionRequestInvoker;

public ITestExecutionFilterFactory TestExecutionFilterFactory { get; } = testExecutionFilterFactory;

public IPlatformOutputDevice PlatformOutputDisplayService { get; } = platformOutputDisplayService;

public IEnumerable<IDataConsumer> ServerPerCallConsumers { get; } = serverPerCallConsumers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -486,14 +486,6 @@ await LogTestHostCreatedAsync(
}
else
{
// Add custom ITestExecutionFilterFactory to the service list if available
ActionResult<ITestExecutionFilterFactory> testExecutionFilterFactoryResult = await ((TestHostManager)TestHost).TryBuildTestExecutionFilterFactoryAsync(serviceProvider);
if (testExecutionFilterFactoryResult.IsSuccess)
{
serviceProvider.TryAddService(testExecutionFilterFactoryResult.Result);
}

// Add custom ITestExecutionFilterFactory to the service list if available
ActionResult<ITestFrameworkInvoker> testAdapterInvokerBuilderResult = await ((TestHostManager)TestHost).TryBuildTestAdapterInvokerAsync(serviceProvider);
if (testAdapterInvokerBuilderResult.IsSuccess)
{
Expand Down Expand Up @@ -653,10 +645,9 @@ private async Task<ITestFramework> BuildTestFrameworkAsync(TestFrameworkBuilderD
// creations and we could lose interesting diagnostic information.
List<IDataConsumer> dataConsumersBuilder = [];

await TestHostBuilder.RegisterAsServiceOrConsumerOrBothAsync(testFrameworkBuilderData.PlatformOutputDisplayService, serviceProvider, dataConsumersBuilder);
await TestHostBuilder.RegisterAsServiceOrConsumerOrBothAsync(testFrameworkBuilderData.TestExecutionRequestFactory, serviceProvider, dataConsumersBuilder);
await TestHostBuilder.RegisterAsServiceOrConsumerOrBothAsync(testFrameworkBuilderData.TestExecutionRequestInvoker, serviceProvider, dataConsumersBuilder);
await TestHostBuilder.RegisterAsServiceOrConsumerOrBothAsync(testFrameworkBuilderData.TestExecutionFilterFactory, serviceProvider, dataConsumersBuilder);
await RegisterAsServiceOrConsumerOrBothAsync(testFrameworkBuilderData.PlatformOutputDisplayService, serviceProvider, dataConsumersBuilder);
await RegisterAsServiceOrConsumerOrBothAsync(testFrameworkBuilderData.TestExecutionRequestFactory, serviceProvider, dataConsumersBuilder);
await RegisterAsServiceOrConsumerOrBothAsync(testFrameworkBuilderData.TestExecutionRequestInvoker, serviceProvider, dataConsumersBuilder);

// Create the test framework adapter
ITestFrameworkCapabilities testFrameworkCapabilities = serviceProvider.GetTestFrameworkCapabilities();
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
#nullable enable
Microsoft.Testing.Platform.Requests.AggregateFilter
Microsoft.Testing.Platform.Requests.AggregateFilter.AggregateFilter(params System.Collections.Generic.IReadOnlyList<Microsoft.Testing.Platform.Requests.ITestExecutionFilter!>! innerFilters) -> void
Microsoft.Testing.Platform.Requests.AggregateFilter.InnerFilters.get -> System.Collections.Generic.IReadOnlyList<Microsoft.Testing.Platform.Requests.ITestExecutionFilter!>!
Microsoft.Testing.Platform.Requests.AggregateFilter.IsAvailable.get -> bool
Microsoft.Testing.Platform.Requests.AggregateFilter.MatchesFilter(Microsoft.Testing.Platform.Extensions.Messages.TestNode! testNode) -> bool
Microsoft.Testing.Platform.Requests.ITestExecutionFilter.IsAvailable.get -> bool
Microsoft.Testing.Platform.Requests.ITestExecutionFilter.MatchesFilter(Microsoft.Testing.Platform.Extensions.Messages.TestNode! testNode) -> bool
Microsoft.Testing.Platform.Requests.TestNodeUidListFilter.IsAvailable.get -> bool
Microsoft.Testing.Platform.Requests.TestNodeUidListFilter.MatchesFilter(Microsoft.Testing.Platform.Extensions.Messages.TestNode! testNode) -> bool
Microsoft.Testing.Platform.TestHost.ITestHostManager.RegisterTestExecutionFilter(System.Func<System.IServiceProvider!, Microsoft.Testing.Platform.Requests.ITestExecutionFilter!>! testFilterFactory) -> void
static Microsoft.Testing.Platform.Services.ServiceProviderExtensions.GetServices<TService>(this System.IServiceProvider! provider) -> System.Collections.Generic.IEnumerable<TService!>!
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
#nullable enable
Microsoft.Testing.Platform.Requests.AggregateFilter
Microsoft.Testing.Platform.Requests.AggregateFilter.AggregateFilter(params System.Collections.Generic.IReadOnlyList<Microsoft.Testing.Platform.Requests.ITestExecutionFilter!>! innerFilters) -> void
Microsoft.Testing.Platform.Requests.AggregateFilter.InnerFilters.get -> System.Collections.Generic.IReadOnlyList<Microsoft.Testing.Platform.Requests.ITestExecutionFilter!>!
Microsoft.Testing.Platform.Requests.AggregateFilter.IsAvailable.get -> bool
Microsoft.Testing.Platform.Requests.AggregateFilter.MatchesFilter(Microsoft.Testing.Platform.Extensions.Messages.TestNode! testNode) -> bool
Microsoft.Testing.Platform.Requests.ITestExecutionFilter.IsAvailable.get -> bool
Microsoft.Testing.Platform.Requests.ITestExecutionFilter.MatchesFilter(Microsoft.Testing.Platform.Extensions.Messages.TestNode! testNode) -> bool
Microsoft.Testing.Platform.Requests.TestNodeUidListFilter.IsAvailable.get -> bool
Microsoft.Testing.Platform.Requests.TestNodeUidListFilter.MatchesFilter(Microsoft.Testing.Platform.Extensions.Messages.TestNode! testNode) -> bool
Microsoft.Testing.Platform.TestHost.ITestHostManager.RegisterTestExecutionFilter(System.Func<System.IServiceProvider!, Microsoft.Testing.Platform.Requests.ITestExecutionFilter!>! testFilterFactory) -> void
static Microsoft.Testing.Platform.Services.ServiceProviderExtensions.GetServices<TService>(this System.IServiceProvider! provider) -> System.Collections.Generic.IEnumerable<TService!>!
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.Testing.Platform.Extensions.Messages;

namespace Microsoft.Testing.Platform.Requests;

public sealed class AggregateFilter(params IReadOnlyList<ITestExecutionFilter> innerFilters) : ITestExecutionFilter
{
public IReadOnlyList<ITestExecutionFilter> InnerFilters { get; } = innerFilters;

public bool IsAvailable => true;

public bool MatchesFilter(TestNode testNode) => InnerFilters.All(x => x.MatchesFilter(testNode));
}

This file was deleted.

Loading
Loading