Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
38febc9
Migrate from `vstest` to `Microsoft.Testing.Platform` (#43)
Evangelink Oct 11, 2025
3fe7b21
Format
Tyrrrz Oct 11, 2025
a2b5bd6
wip
Tyrrrz Oct 11, 2025
3cd65bd
Format
Tyrrrz Oct 11, 2025
e42c6a6
asd
Tyrrrz Oct 11, 2025
ad398b2
asd
Tyrrrz Oct 11, 2025
d1c23fa
asd
Tyrrrz Oct 11, 2025
09cb57f
asd
Tyrrrz Oct 12, 2025
4e8b5df
Retrofit support for VSTest via an abstraction layer
Tyrrrz Oct 14, 2025
0fa368d
asd
Tyrrrz Oct 14, 2025
ee603f1
asd
Tyrrrz Oct 14, 2025
0ff54ee
asd
Tyrrrz Oct 14, 2025
b0959a1
asd
Tyrrrz Oct 14, 2025
2a04ac6
asd
Tyrrrz Oct 14, 2025
b375377
asd
Tyrrrz Oct 14, 2025
c3baa5e
asd
Tyrrrz Oct 14, 2025
7e73608
asd
Tyrrrz Oct 14, 2025
1ec70f8
asd
Tyrrrz Oct 14, 2025
baa088e
asd
Tyrrrz Oct 14, 2025
e202765
asd
Tyrrrz Oct 14, 2025
05fc4e5
asd
Tyrrrz Oct 15, 2025
3896f9a
asd
Tyrrrz Oct 15, 2025
4555bd6
asd
Tyrrrz Oct 15, 2025
d34bd30
asd
Tyrrrz Oct 15, 2025
86232b4
asd
Tyrrrz Oct 17, 2025
d418098
asd
Tyrrrz Oct 17, 2025
d680c00
asd
Tyrrrz Oct 17, 2025
1084ae2
MTP demo works
Tyrrrz Oct 22, 2025
af06bef
asd
Tyrrrz Oct 25, 2025
23601ff
asd
Tyrrrz Nov 1, 2025
ed6a032
asd
Tyrrrz Nov 1, 2025
201dcae
asd
Tyrrrz Nov 3, 2025
460ef7b
Upgrade to MTP v2
Tyrrrz Nov 11, 2025
77f8f51
asd
Tyrrrz Nov 11, 2025
12e4e65
Merge
Tyrrrz Nov 11, 2025
ab562cc
asd
Tyrrrz Nov 11, 2025
c367f57
Use peer deps
Tyrrrz Nov 12, 2025
f4582b9
tests
Tyrrrz Nov 14, 2025
80ff683
Async github interactions
Tyrrrz Nov 14, 2025
65a9c48
Add MTP tests
Tyrrrz Nov 17, 2025
3c4f5d5
asd
Tyrrrz Nov 17, 2025
29a8325
asd
Tyrrrz Nov 17, 2025
b6a8c73
Migrate demo projects to MSTest
Tyrrrz Nov 17, 2025
b25749c
asd
Tyrrrz Nov 17, 2025
423a9d9
asd
Tyrrrz Nov 17, 2025
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
Prev Previous commit
Next Next commit
Format
  • Loading branch information
Tyrrrz committed Oct 11, 2025
commit 3fe7b21004e9efdbd922e71324738aad0a116384
69 changes: 53 additions & 16 deletions GitHubActionsTestLogger/CliOptionsProvider.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using Microsoft.Testing.Platform.CommandLine;
using Microsoft.Testing.Platform.Extensions;
using Microsoft.Testing.Platform.Extensions.CommandLine;

namespace GitHubActionsTestLogger;

internal sealed class CliOptionsProvider(GitHubTestReporterExtension extension) : ICommandLineOptionsProvider
internal sealed class CliOptionsProvider(GitHubTestReporterExtension extension)
: ICommandLineOptionsProvider
{
public const string ReportGitHubOption = "report-github";
public const string ReportGitHubSummaryOption = "report-github-summary";
Expand All @@ -29,57 +29,94 @@ public static class ReportGitHubSummaryArguments

public IReadOnlyCollection<CommandLineOption> GetCommandLineOptions() =>
[
new(ReportGitHubOption, "Reports test run information to GitHub Actions", ArgumentArity.Zero, isHidden: false),
new(
ReportGitHubOption,
"Reports test run information to GitHub Actions",
ArgumentArity.Zero,
isHidden: false
),
// TODO: Do you prefer multiple zero argument options or a single option with argument?
new(ReportGitHubSummaryOption, "Defines the information to include in the summary", ArgumentArity.OneOrMore, isHidden: false),
new(ReportGitHubTitleOption, "Defines the annotation title format used for reporting test failures", ArgumentArity.ExactlyOne, isHidden: false),
new(ReportGitHubMessageOption, "Defines the annotation message format used for reporting test failures", ArgumentArity.ExactlyOne, isHidden: false),
new(
ReportGitHubSummaryOption,
"Defines the information to include in the summary",
ArgumentArity.OneOrMore,
isHidden: false
),
new(
ReportGitHubTitleOption,
"Defines the annotation title format used for reporting test failures",
ArgumentArity.ExactlyOne,
isHidden: false
),
new(
ReportGitHubMessageOption,
"Defines the annotation message format used for reporting test failures",
ArgumentArity.ExactlyOne,
isHidden: false
),
];

public Task<bool> IsEnabledAsync() => extension.IsEnabledAsync();

// This method is called once after all options are parsed and is used to validate the combination of options.
public Task<ValidationResult> ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions)
public Task<ValidationResult> ValidateCommandLineOptionsAsync(
ICommandLineOptions commandLineOptions
)
{
// We just want to validate that the sub options are set only if the main option is set.
if (commandLineOptions.IsOptionSet(ReportGitHubOption))
{
return ValidationResult.ValidTask;
}

if (commandLineOptions.IsOptionSet(ReportGitHubSummaryOption)
if (
commandLineOptions.IsOptionSet(ReportGitHubSummaryOption)
|| commandLineOptions.IsOptionSet(ReportGitHubTitleOption)
|| commandLineOptions.IsOptionSet(ReportGitHubMessageOption))
|| commandLineOptions.IsOptionSet(ReportGitHubMessageOption)
)
{
return ValidationResult.InvalidTask("The options 'report-github-summary', 'report-github-title', and 'report-github-message' can only be used if 'report-github' is set.");
return ValidationResult.InvalidTask(
"The options 'report-github-summary', 'report-github-title', and 'report-github-message' can only be used if 'report-github' is set."
);
}

return ValidationResult.ValidTask;
}

// This method is called once per option declared and is used to validate the arguments of the given option.
// The arity of the option is checked before this method is called.
public Task<ValidationResult> ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments)
public Task<ValidationResult> ValidateOptionArgumentsAsync(
CommandLineOption commandOption,
string[] arguments
)
{
if (commandOption.Name == ReportGitHubSummaryOption)
{
if (arguments.Length > 3)
{
return ValidationResult.InvalidTask($"The option '{ReportGitHubSummaryOption}' can have at most 3 arguments.");
return ValidationResult.InvalidTask(
$"The option '{ReportGitHubSummaryOption}' can have at most 3 arguments."
);
}

if (arguments.Distinct().Count() != arguments.Length)
{
return ValidationResult.InvalidTask($"The option '{ReportGitHubSummaryOption}' cannot have duplicate arguments.");
return ValidationResult.InvalidTask(
$"The option '{ReportGitHubSummaryOption}' cannot have duplicate arguments."
);
}

for (int i = 0; i < arguments.Length; i++)
{
if (arguments[i] != ReportGitHubSummaryArguments.IncludePassedTests
if (
arguments[i] != ReportGitHubSummaryArguments.IncludePassedTests
&& arguments[i] != ReportGitHubSummaryArguments.IncludeSkippedTests
&& arguments[i] != ReportGitHubSummaryArguments.IncludeNotFoundTests)
&& arguments[i] != ReportGitHubSummaryArguments.IncludeNotFoundTests
)
{
return ValidationResult.InvalidTask($"The option '{ReportGitHubSummaryOption}' can only have the arguments '{ReportGitHubSummaryArguments.IncludePassedTests}', '{ReportGitHubSummaryArguments.IncludeSkippedTests}', and '{ReportGitHubSummaryArguments.IncludeNotFoundTests}'.");
return ValidationResult.InvalidTask(
$"The option '{ReportGitHubSummaryOption}' can only have the arguments '{ReportGitHubSummaryArguments.IncludePassedTests}', '{ReportGitHubSummaryArguments.IncludeSkippedTests}', and '{ReportGitHubSummaryArguments.IncludeNotFoundTests}'."
);
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions GitHubActionsTestLogger/GitHubReportExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ public static void AddGitHubReportProvider(this ITestApplicationBuilder testAppl
{
var extension = new GitHubTestReporterExtension();

var compositeExtension = new CompositeExtensionFactory<GitHubTestReporter>(serviceProvider =>
new GitHubTestReporter(extension, serviceProvider.GetCommandLineOptions()));
var compositeExtension = new CompositeExtensionFactory<GitHubTestReporter>(
serviceProvider => new GitHubTestReporter(
extension,
serviceProvider.GetCommandLineOptions()
)
);
testApplicationBuilder.TestHost.AddDataConsumer(compositeExtension);
testApplicationBuilder.TestHost.AddTestSessionLifetimeHandle(compositeExtension);

Expand Down
35 changes: 23 additions & 12 deletions GitHubActionsTestLogger/GitHubTestReporter.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.Testing.Platform.CommandLine;
using Microsoft.Testing.Platform.Extensions.Messages;
using Microsoft.Testing.Platform.Extensions.TestHost;
Expand All @@ -12,34 +11,46 @@ namespace GitHubActionsTestLogger;
/// <summary>
/// A Microsoft.Testing.Platform extension that reports test run information to GitHub Actions.
/// </summary>
internal sealed class GitHubTestReporter(GitHubTestReporterExtension extension, ICommandLineOptions commandLineOptions) : IDataConsumer, ITestSessionLifetimeHandler
internal sealed class GitHubTestReporter(
GitHubTestReporterExtension extension,
ICommandLineOptions commandLineOptions
) : IDataConsumer, ITestSessionLifetimeHandler
{
private readonly TestReporterContext _context = new(GitHubWorkflow.Default, TestReporterOptions.Resolve(commandLineOptions));
private readonly TestReporterContext _context = new(
GitHubWorkflow.Default,
TestReporterOptions.Resolve(commandLineOptions)
);

public Type[] DataTypesConsumed { get; } =
[
typeof(TestNodeUpdateMessage),
];
public Type[] DataTypesConsumed { get; } = [typeof(TestNodeUpdateMessage)];

public string Uid => extension.Uid;
public string Version => extension.Version;
public string DisplayName => extension.DisplayName;
public string Description => extension.Description;

public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken)
public Task ConsumeAsync(
IDataProducer dataProducer,
IData value,
CancellationToken cancellationToken
)
{
_context.HandleTestResult((TestNodeUpdateMessage)value);
return Task.CompletedTask;
}

public Task<bool> IsEnabledAsync() => extension.IsEnabledAsync();

public Task OnTestSessionFinishingAsync(SessionUid sessionUid, CancellationToken cancellationToken)
public Task OnTestSessionFinishingAsync(
SessionUid sessionUid,
CancellationToken cancellationToken
)
{
_context.HandleTestRunComplete();
return Task.CompletedTask;
}

public Task OnTestSessionStartingAsync(SessionUid sessionUid, CancellationToken cancellationToken)
=> Task.CompletedTask;
}
public Task OnTestSessionStartingAsync(
SessionUid sessionUid,
CancellationToken cancellationToken
) => Task.CompletedTask;
}
1 change: 0 additions & 1 deletion GitHubActionsTestLogger/GitHubTestReporterExtension.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Threading.Tasks;

using Microsoft.Testing.Platform.Extensions;

namespace GitHubActionsTestLogger;
Expand Down
27 changes: 16 additions & 11 deletions GitHubActionsTestLogger/TestReporterContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
using System.Reflection;
using System.Text;
using System.Threading;

using GitHubActionsTestLogger.Utils.Extensions;

using Microsoft.Testing.Platform.Extensions.Messages;

namespace GitHubActionsTestLogger;
Expand All @@ -20,7 +18,11 @@ public class TestReporterContext(GitHubWorkflow github, TestReporterOptions opti

public TestReporterOptions Options { get; } = options;

private string FormatAnnotation(string format, TestNode testNode, TestNodeStateProperty testNodeStateProperty)
private string FormatAnnotation(
string format,
TestNode testNode,
TestNodeStateProperty testNodeStateProperty
)
{
var buffer = new StringBuilder(format);

Expand Down Expand Up @@ -73,17 +75,22 @@ private string FormatAnnotation(string format, TestNode testNode, TestNodeStateP
return buffer.Trim().ToString();
}

private string FormatAnnotationTitle(TestNode testNode, TestNodeStateProperty testNodeStateProperty) =>
FormatAnnotation(Options.AnnotationTitleFormat, testNode, testNodeStateProperty);
private string FormatAnnotationTitle(
TestNode testNode,
TestNodeStateProperty testNodeStateProperty
) => FormatAnnotation(Options.AnnotationTitleFormat, testNode, testNodeStateProperty);

private string FormatAnnotationMessage(TestNode testNode, TestNodeStateProperty testNodeStateProperty) =>
FormatAnnotation(Options.AnnotationMessageFormat, testNode, testNodeStateProperty);
private string FormatAnnotationMessage(
TestNode testNode,
TestNodeStateProperty testNodeStateProperty
) => FormatAnnotation(Options.AnnotationMessageFormat, testNode, testNodeStateProperty);

public void HandleTestResult(TestNodeUpdateMessage testNodeUpdateMessage)
{
using (_lock.EnterScope())
{
var testNodeState = testNodeUpdateMessage.TestNode.Properties.Single<TestNodeStateProperty>();
var testNodeState =
testNodeUpdateMessage.TestNode.Properties.Single<TestNodeStateProperty>();
// Report failed test results to job annotations
if (testNodeState is FailedTestNodeStateProperty or ErrorTestNodeStateProperty)
{
Expand All @@ -108,15 +115,13 @@ public void HandleTestRunStart()
}
}


public void HandleTestRunComplete()
{
using (_lock.EnterScope())
{
_stopwatch.Stop();

var testSuite = Assembly.GetEntryAssembly()?.GetName().Name
?? "Unknown Test Suite";
var testSuite = Assembly.GetEntryAssembly()?.GetName().Name ?? "Unknown Test Suite";

var targetFramework =
// See line 65
Expand Down
30 changes: 18 additions & 12 deletions GitHubActionsTestLogger/TestReporterOptions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System.Linq;

using Microsoft.Testing.Platform.CommandLine;

using static GitHubActionsTestLogger.CliOptionsProvider;

namespace GitHubActionsTestLogger;
Expand All @@ -25,17 +23,25 @@ public partial class TestReporterOptions

public static TestReporterOptions Resolve(ICommandLineOptions commandLineOptions)
{
var annotationTitleFormat =
(commandLineOptions.TryGetOptionArgumentList(ReportGitHubTitleOption, out string[]? arguments)
? arguments[0]
: null)
?? Default.AnnotationTitleFormat;
var annotationTitleFormat =
(
commandLineOptions.TryGetOptionArgumentList(
ReportGitHubTitleOption,
out string[]? arguments
)
? arguments[0]
: null
) ?? Default.AnnotationTitleFormat;

var annotationMessageFormat =
(commandLineOptions.TryGetOptionArgumentList(ReportGitHubMessageOption, out arguments)
? arguments[0]
: null)
?? Default.AnnotationMessageFormat;
(
commandLineOptions.TryGetOptionArgumentList(
ReportGitHubMessageOption,
out arguments
)
? arguments[0]
: null
) ?? Default.AnnotationMessageFormat;

_ = commandLineOptions.TryGetOptionArgumentList(ReportGitHubSummaryOption, out arguments);

Expand All @@ -44,7 +50,7 @@ public static TestReporterOptions Resolve(ICommandLineOptions commandLineOptions
AnnotationTitleFormat = annotationTitleFormat,
AnnotationMessageFormat = annotationMessageFormat,
SummaryIncludePassedTests =
arguments?.Contains(ReportGitHubSummaryArguments.IncludePassedTests) == true
arguments?.Contains(ReportGitHubSummaryArguments.IncludePassedTests) == true
|| Default.SummaryIncludePassedTests,
SummaryIncludeSkippedTests =
arguments?.Contains(ReportGitHubSummaryArguments.IncludeSkippedTests) == true
Expand Down
4 changes: 2 additions & 2 deletions GitHubActionsTestLogger/TestingPlatformBuilderHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ public static class TestingPlatformBuilderHook
/// </summary>
/// <param name="testApplicationBuilder">The test application builder.</param>
/// <param name="_">The command line arguments.</param>
public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] _)
=> testApplicationBuilder.AddGitHubReportProvider();
public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] _) =>
testApplicationBuilder.AddGitHubReportProvider();
}