Skip to content
Merged
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
145 changes: 145 additions & 0 deletions GitHubActionsTestLogger.Tests/MtpSummarySpecs.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using FluentAssertions;
using GitHubActionsTestLogger.Tests.Mtp;
using GitHubActionsTestLogger.Tests.Utils;
using GitHubActionsTestLogger.Tests.Utils.Extensions;
using Microsoft.Testing.Platform.Builder;
using Xunit;
using Xunit.Abstractions;
Expand Down Expand Up @@ -319,4 +321,147 @@ public async Task I_can_use_the_logger_to_produce_a_summary_that_does_not_includ

testOutput.WriteLine(output);
}

[Fact]
public async Task I_can_try_to_use_the_logger_to_produce_a_summary_when_the_output_file_is_nearly_full_and_get_a_truncated_summary()
{
// Arrange
using var testResultsDir = TempDir.Create();
using var summaryFile = TempFile.Create();

// Pre-fill the summary file to within ~1000 bytes of the 1 MiB limit.
// This forces any summary larger than ~1000 bytes to be truncated.
const int prefillSize = 1024 * 1024 - 1000;
File.WriteAllZeroes(summaryFile.Path, prefillSize);

using var commandWriter = new StringWriter();

// Use a file-backed StreamWriter so that the file path is exposed internally
await using var summaryFileStream = File.Open(
Comment thread
Tyrrrz marked this conversation as resolved.
summaryFile.Path,
FileMode.Append,
FileAccess.Write,
FileShare.ReadWrite
);
await using var summaryWriter = new StreamWriter(summaryFileStream);

var builder = await TestApplication.CreateBuilderAsync([
"--results-directory",
testResultsDir.Path,
"--report-github",
"--report-github-summary-include-passed",
]);

builder.RegisterFakeTests(
// Group A
new TestNodeBuilder()
.SetTypeName("GroupA")
.SetDisplayName("TestGroupA_LongTestName_One")
.SetOutcome(Mtp.TestOutcome.Passed)
.Build(),
new TestNodeBuilder()
.SetTypeName("GroupA")
.SetDisplayName("TestGroupA_LongTestName_Two")
.SetOutcome(Mtp.TestOutcome.Failed)
.SetErrorMessage("Expected: something, but got: something else (GroupA)")
.Build(),
// Group B
new TestNodeBuilder()
.SetTypeName("GroupB")
.SetDisplayName("TestGroupB_LongTestName_One")
.SetOutcome(Mtp.TestOutcome.Passed)
.Build(),
new TestNodeBuilder()
.SetTypeName("GroupB")
.SetDisplayName("TestGroupB_LongTestName_Two")
.SetOutcome(Mtp.TestOutcome.Failed)
.SetErrorMessage("Expected: something, but got: something else (GroupB)")
.Build(),
// Group C
new TestNodeBuilder()
.SetTypeName("GroupC")
.SetDisplayName("TestGroupC_LongTestName_One")
.SetOutcome(Mtp.TestOutcome.Passed)
.Build(),
new TestNodeBuilder()
.SetTypeName("GroupC")
.SetDisplayName("TestGroupC_LongTestName_Two")
.SetOutcome(Mtp.TestOutcome.Failed)
.SetErrorMessage("Expected: something, but got: something else (GroupC)")
.Build()
);

builder.AddGitHubActionsReporting(commandWriter, summaryWriter);

// Act
var app = await builder.BuildAsync();
await app.RunAsync();

await summaryWriter.FlushAsync();
Comment thread
Tyrrrz marked this conversation as resolved.

// Assert
var commandOutput = commandWriter.ToString();

var summaryOutput = Encoding.UTF8.GetString(
File.ReadAllBytes(summaryFile.Path, prefillSize)
);

commandOutput.Should().ContainAll("::warning", "truncated");
summaryOutput.Should().NotBeNullOrWhiteSpace();

testOutput.WriteLine("Command output:");
testOutput.WriteLine(commandOutput);
testOutput.WriteLine("Summary output:");
testOutput.WriteLine(summaryOutput);
}

[Fact]
public async Task I_can_try_to_use_the_logger_to_produce_a_summary_when_the_output_file_is_full_and_get_the_summary_omitted()
{
// Arrange
using var testResultsDir = TempDir.Create();
using var summaryFile = TempFile.Create();

// Pre-fill the summary file to within 1 byte of the 1 MiB limit.
// This leaves no room even for newlines, so the summary must be omitted.
const int prefillSize = 1024 * 1024 - 1;
File.WriteAllZeroes(summaryFile.Path, prefillSize);

using var commandWriter = new StringWriter();

// Use a file-backed StreamWriter so that the file path is exposed internally
await using var summaryFileStream = File.Open(
summaryFile.Path,
FileMode.Append,
FileAccess.Write,
FileShare.ReadWrite
);
await using var summaryWriter = new StreamWriter(summaryFileStream);

var builder = await TestApplication.CreateBuilderAsync([
"--results-directory",
testResultsDir.Path,
"--report-github",
"--report-github-summary-allow-empty",
]);

builder.RegisterFakeTests();
builder.AddGitHubActionsReporting(commandWriter, summaryWriter);

// Act
var app = await builder.BuildAsync();
await app.RunAsync();

await summaryWriter.FlushAsync();

// Assert
var commandOutput = commandWriter.ToString();
var fileLength = new FileInfo(summaryFile.Path).Length;

commandOutput.Should().ContainAll("::warning", "omitted");
fileLength.Should().Be(prefillSize);

testOutput.WriteLine("Command output:");
testOutput.WriteLine(commandOutput);
}
}
38 changes: 38 additions & 0 deletions GitHubActionsTestLogger.Tests/Utils/Extensions/FileExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.IO;

namespace GitHubActionsTestLogger.Tests.Utils.Extensions;

internal static class FileExtensions
{
extension(File)
{
public static void WriteAllZeroes(string path, long count)
{
using var stream = new FileStream(
path,
FileMode.Create,
FileAccess.Write,
FileShare.None,
bufferSize: 1
);

stream.SetLength(count);
}

public static byte[] ReadAllBytes(string path, int offset)
{
using var stream = new FileStream(
path,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite
);
stream.Seek(offset, SeekOrigin.Begin);

var buffer = new byte[stream.Length - offset];
stream.ReadExactly(buffer);

Comment thread
Tyrrrz marked this conversation as resolved.
Comment thread
Tyrrrz marked this conversation as resolved.
return buffer;
}
}
}
37 changes: 37 additions & 0 deletions GitHubActionsTestLogger.Tests/Utils/TempFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.IO;
using System.Reflection;

namespace GitHubActionsTestLogger.Tests.Utils;

internal partial class TempFile(string path) : IDisposable
{
public string Path { get; } = path;

public void Dispose()
{
try
{
File.Delete(Path);
}
catch (FileNotFoundException) { }
}
}

internal partial class TempFile
{
public static TempFile Create()
{
var dirPath = System.IO.Path.Combine(
System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
?? Directory.GetCurrentDirectory(),
"Temp"
);

Directory.CreateDirectory(dirPath);

var filePath = System.IO.Path.Combine(dirPath, Guid.NewGuid() + ".tmp");

return new TempFile(filePath);
}
}
139 changes: 139 additions & 0 deletions GitHubActionsTestLogger.Tests/VsTestSummarySpecs.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using FluentAssertions;
using GitHubActionsTestLogger.Tests.Utils;
using GitHubActionsTestLogger.Tests.Utils.Extensions;
using GitHubActionsTestLogger.Tests.VsTest;
using Xunit;
using Xunit.Abstractions;
Expand Down Expand Up @@ -352,4 +355,140 @@ public void I_can_use_the_logger_to_produce_a_summary_that_does_not_include_empt

testOutput.WriteLine(output);
}

[Fact]
public void I_can_try_to_use_the_logger_to_produce_a_summary_when_the_output_file_is_nearly_full_and_get_a_truncated_summary()
Comment thread
Tyrrrz marked this conversation as resolved.
{
// Arrange
using var summaryFile = TempFile.Create();

// Pre-fill the summary file to within ~1000 bytes of the 1 MiB limit.
// This forces any summary larger than ~1000 bytes to be truncated.
const int prefillSize = 1024 * 1024 - 1000;
File.WriteAllZeroes(summaryFile.Path, prefillSize);

using var commandWriter = new StringWriter();

var events = new FakeTestLoggerEvents();
var logger = new VsTestLogger();

// Use a file-backed StreamWriter so that the file path is exposed internally
using var summaryFileStream = File.Open(
summaryFile.Path,
FileMode.Append,
FileAccess.Write,
FileShare.ReadWrite
);
using (var summaryWriter = new StreamWriter(summaryFileStream))
{
logger.Initialize(
events,
new Dictionary<string, string?> { ["summary-include-passed"] = "true" },
commandWriter,
summaryWriter
);

// Act — simulate a run with multiple test groups to produce a multi-group summary
events.SimulateTestRun(
// Group A
new TestResultBuilder()
.SetDisplayName("TestGroupA_LongTestName_One")
.SetFullyQualifiedName("TestProject.GroupA.TestGroupA_LongTestName_One")
.SetOutcome(TestOutcome.Passed)
.Build(),
new TestResultBuilder()
.SetDisplayName("TestGroupA_LongTestName_Two")
.SetFullyQualifiedName("TestProject.GroupA.TestGroupA_LongTestName_Two")
.SetOutcome(TestOutcome.Failed)
.SetErrorMessage("Expected: something, but got: something else (GroupA)")
.Build(),
// Group B
new TestResultBuilder()
.SetDisplayName("TestGroupB_LongTestName_One")
.SetFullyQualifiedName("TestProject.GroupB.TestGroupB_LongTestName_One")
.SetOutcome(TestOutcome.Passed)
.Build(),
new TestResultBuilder()
.SetDisplayName("TestGroupB_LongTestName_Two")
.SetFullyQualifiedName("TestProject.GroupB.TestGroupB_LongTestName_Two")
.SetOutcome(TestOutcome.Failed)
.SetErrorMessage("Expected: something, but got: something else (GroupB)")
.Build(),
// Group C
new TestResultBuilder()
.SetDisplayName("TestGroupC_LongTestName_One")
.SetFullyQualifiedName("TestProject.GroupC.TestGroupC_LongTestName_One")
.SetOutcome(TestOutcome.Passed)
.Build(),
new TestResultBuilder()
.SetDisplayName("TestGroupC_LongTestName_Two")
.SetFullyQualifiedName("TestProject.GroupC.TestGroupC_LongTestName_Two")
.SetOutcome(TestOutcome.Failed)
.SetErrorMessage("Expected: something, but got: something else (GroupC)")
.Build()
);
} // Dispose writer before reading to ensure content is fully flushed

// Assert
var commandOutput = commandWriter.ToString();

var summaryOutput = Encoding.UTF8.GetString(
File.ReadAllBytes(summaryFile.Path, prefillSize)
);

commandOutput.Should().ContainAll("::warning", "truncated");
summaryOutput.Should().NotBeNullOrWhiteSpace();

testOutput.WriteLine("Command output:");
testOutput.WriteLine(commandOutput);
testOutput.WriteLine("Summary output:");
testOutput.WriteLine(summaryOutput);
}

[Fact]
public void I_can_try_to_use_the_logger_to_produce_a_summary_when_the_output_file_is_full_and_get_the_summary_omitted()
{
// Arrange
using var summaryFile = TempFile.Create();

// Pre-fill the summary file to within 1 byte of the 1 MiB limit.
// This leaves no room even for newlines, so the summary must be omitted.
const int prefillSize = 1024 * 1024 - 1;
File.WriteAllZeroes(summaryFile.Path, prefillSize);

using var commandWriter = new StringWriter();

var events = new FakeTestLoggerEvents();
var logger = new VsTestLogger();

// Use a file-backed StreamWriter so that the file path is exposed internally
using var summaryFileStream = File.Open(
summaryFile.Path,
FileMode.Append,
FileAccess.Write,
FileShare.ReadWrite
);
using (var summaryWriter = new StreamWriter(summaryFileStream))
{
logger.Initialize(
events,
new Dictionary<string, string?> { ["summary-allow-empty"] = "true" },
commandWriter,
summaryWriter
);

// Act
events.SimulateTestRun("TestProject.dll");
} // Dispose writer before reading to ensure content is fully flushed

// Assert
var commandOutput = commandWriter.ToString();
var fileLength = new FileInfo(summaryFile.Path).Length;

commandOutput.Should().ContainAll("::warning", "omitted");
fileLength.Should().Be(prefillSize);

testOutput.WriteLine("Command output:");
testOutput.WriteLine(commandOutput);
}
}
7 changes: 7 additions & 0 deletions GitHubActionsTestLogger/GitHub/GitHubAnnotationKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace GitHubActionsTestLogger.GitHub;

internal enum GitHubAnnotationKind
{
Error,
Warning,
}
Loading
Loading