Skip to content

Commit

Permalink
GitHub Summary (#1518)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomhurst authored Jan 9, 2025
1 parent 4c46f2b commit 20f6438
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 0 deletions.
7 changes: 7 additions & 0 deletions TUnit.Engine/Extensions/TestApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using TUnit.Engine.Capabilities;
using TUnit.Engine.CommandLineProviders;
using TUnit.Engine.Framework;
using TUnit.Engine.Reporters;

#pragma warning disable TPEXP

namespace TUnit.Engine.Extensions;
Expand All @@ -14,6 +16,8 @@ internal static class TestApplicationBuilderExtensions
public static void AddTUnit(this ITestApplicationBuilder testApplicationBuilder)
{
TUnitExtension extension = new();

var githubReporter = new GitHubReporter(extension);

testApplicationBuilder.RegisterTestFramework(
serviceProvider => new TestFrameworkCapabilities(new TrxReportCapability(), new BannerCapability(serviceProvider.GetRequiredService<IPlatformInformation>(), serviceProvider.GetCommandLineOptions())),
Expand All @@ -26,5 +30,8 @@ public static void AddTUnit(this ITestApplicationBuilder testApplicationBuilder)
testApplicationBuilder.CommandLine.AddProvider(() => new ParametersCommandProvider(extension));
testApplicationBuilder.CommandLine.AddProvider(() => new FailFastCommandProvider(extension));
testApplicationBuilder.CommandLine.AddProvider(() => new DisableLogoCommandProvider(extension));

testApplicationBuilder.TestHost.AddDataConsumer(_ => githubReporter);
testApplicationBuilder.TestHost.AddTestApplicationLifecycleCallbacks(_ => githubReporter);
}
}
152 changes: 152 additions & 0 deletions TUnit.Engine/Reporters/GitHubReporter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
using System.Collections.Concurrent;
using System.Text;
using Microsoft.Testing.Extensions.TrxReport.Abstractions;
using Microsoft.Testing.Platform.Extensions;
using Microsoft.Testing.Platform.Extensions.Messages;
using Microsoft.Testing.Platform.Extensions.TestHost;

namespace TUnit.Engine.Reporters;

public class GitHubReporter(IExtension extension) : IDataConsumer, ITestApplicationLifecycleCallbacks
{
private string _outputSummaryFilePath = null!;

public async Task<bool> IsEnabledAsync()
{
if (Environment.GetEnvironmentVariable("GITHUB_ACTIONS") is null)
{
return false;
}

if (Environment.GetEnvironmentVariable("OUTPUT_SUMMARY") is not { } fileName
|| !File.Exists(fileName))
{
return false;
}

_outputSummaryFilePath = fileName;

return await extension.IsEnabledAsync();
}

public string Uid => extension.Uid;

public string Version => extension.Version;

public string DisplayName => extension.DisplayName;

public string Description => extension.Description;

private readonly ConcurrentDictionary<string, List<TestNodeUpdateMessage>> _updates = [];

public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken)
{
var testNodeUpdateMessage = (TestNodeUpdateMessage)value;

_updates.GetOrAdd(testNodeUpdateMessage.TestNode.Uid.Value, []).Add(testNodeUpdateMessage);

return Task.CompletedTask;
}

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

public Task BeforeRunAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}

public Task AfterRunAsync(int exitCode, CancellationToken cancellation)
{
var last = _updates.ToDictionary(x => x.Key, x => x.Value.Last());

var passedCount = last.Count(x => x.Value.Properties.AsEnumerable().Any(p => p is PassedTestNodeStateProperty));
var failed = last.Where(x => x.Value.Properties.AsEnumerable().Any(p => p is FailedTestNodeStateProperty or ErrorTestNodeStateProperty)).ToArray();
var cancelled = last.Where(x => x.Value.Properties.AsEnumerable().Any(p => p is CancelledTestNodeStateProperty)).ToArray();
var timeout = last.Where(x => x.Value.Properties.AsEnumerable().Any(p => p is TimeoutTestNodeStateProperty)).ToArray();
var skipped = last.Where(x => x.Value.Properties.AsEnumerable().Any(p => p is SkippedTestNodeStateProperty)).ToArray();
var inProgress = last.Where(x => x.Value.Properties.AsEnumerable().Any(p => p is InProgressTestNodeStateProperty)).ToArray();

var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("# Summary");
stringBuilder.AppendLine();
stringBuilder.AppendLine("| Count | Status |");
stringBuilder.AppendLine("| _ | _ |");
stringBuilder.AppendLine($"| {passedCount} | Passed |");
stringBuilder.AppendLine($"| {failed.Length} | Failed |");

if(skipped.Length > 0)
{
stringBuilder.AppendLine($"| {skipped.Length} | Skipped |");
}

if(timeout.Length > 0)
{
stringBuilder.AppendLine($"| {timeout.Length} | Timed Out |");
}

if(cancelled.Length > 0)
{
stringBuilder.AppendLine($"| {cancelled.Length} | Cancelled |");
}

if(inProgress.Length > 0)
{
stringBuilder.AppendLine($"| {inProgress.Length} | In Progress (never completed) |");
}

stringBuilder.AppendLine();
stringBuilder.AppendLine();
stringBuilder.AppendLine("## Information");
stringBuilder.AppendLine();
stringBuilder.AppendLine("| Test | Status | Start | End | Duration |");
stringBuilder.AppendLine("| _ | _ | _ | _ | _ |");

foreach (var testNodeUpdateMessage in last.Values)
{
var name = testNodeUpdateMessage.TestNode.DisplayName;

var status = GetStatus(testNodeUpdateMessage);

if (status == "Passed")
{
continue;
}

var timingProperty = testNodeUpdateMessage.Properties.AsEnumerable().OfType<TimingProperty>().FirstOrDefault();

var start = timingProperty
?.GlobalTiming.StartTime;

var end = timingProperty
?.GlobalTiming.StartTime;

var duration = timingProperty
?.GlobalTiming.Duration;

stringBuilder.AppendLine($"| {name} | {status} | {start} | {end} | {duration} |");
}

#if NET
return File.WriteAllTextAsync(_outputSummaryFilePath, stringBuilder.ToString(), Encoding.UTF8, cancellation);
#else
File.WriteAllText(_outputSummaryFilePath, stringBuilder.ToString(), Encoding.UTF8);
return Task.CompletedTask;
#endif
}

private static string GetStatus(TestNodeUpdateMessage testNodeUpdateMessage)
{
var stateProperty = testNodeUpdateMessage.Properties.OfType<TestNodeStateProperty>().FirstOrDefault();

return stateProperty switch
{
CancelledTestNodeStateProperty => "Cancelled",
ErrorTestNodeStateProperty or FailedTestNodeStateProperty => "Failed",
InProgressTestNodeStateProperty => "In Progress (never finished)",
PassedTestNodeStateProperty => "Passed",
SkippedTestNodeStateProperty => "Skipped",
TimeoutTestNodeStateProperty => "Timed Out",
_ => "Unknown"
};
}
}

0 comments on commit 20f6438

Please sign in to comment.