Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b703a2b
Add skill writing skill
nohwnd Mar 4, 2026
a935a8e
Skill writing skill
nohwnd Mar 4, 2026
20b08d7
Make vstest-build-test skill cross-platform with OS mismatch detection
invalid-email-address Mar 4, 2026
7bc6553
Improve validate-skills skill with Windows support and ENV_ISSUE clas…
invalid-email-address Mar 4, 2026
2bd9d56
Fix build exit code propagation and skill documentation
invalid-email-address Mar 4, 2026
a64c258
Fix HTML logger parallel file collision (#15404)
invalid-email-address Mar 4, 2026
ecf5ddb
Update copilot-instructions.md and skill docs with build/test guides
invalid-email-address Mar 4, 2026
3cb268d
Fix HTML logger parallel file collision (#15404)
invalid-email-address Mar 4, 2026
1070912
Merge branch 'main' into fix/html-logger-parallel-file-collision
nohwnd Mar 5, 2026
6e26e63
Apply suggestions from code review
nohwnd Mar 5, 2026
ce0bf3e
Update .github/copilot-instructions.md
nohwnd Mar 5, 2026
4780e0e
Update .github/skills/vstest-build-test/SKILL.md
nohwnd Mar 5, 2026
87a4ba3
Update .github/skills/vstest-build-test/SKILL.md
nohwnd Mar 5, 2026
81df804
Update eng/common/tools.ps1
nohwnd Mar 5, 2026
10bb750
Apply suggestions from code review
nohwnd Mar 24, 2026
2a4f047
Merge branch 'main' into fix/html-logger-parallel-file-collision
nohwnd Mar 24, 2026
4e7ba20
Fix HtmlLogger collision handling
nohwnd Apr 10, 2026
e2f99d1
Merge origin/main into HTML logger fix
nohwnd Apr 10, 2026
4ab2a31
Fix HtmlLogger test assertion
nohwnd Apr 10, 2026
04aaf44
Merge remote-tracking branch 'origin/main' into fix/html-logger-paral…
nohwnd May 13, 2026
0b55ee4
fix: use _fileHelper.Exists instead of File.Exists in GenerateUniqueF…
nohwnd May 13, 2026
8659640
fix: correct mock setup in retry collision test
nohwnd May 13, 2026
9215fdd
Merge branch 'main' into fix/html-logger-parallel-file-collision
nohwnd May 14, 2026
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
12 changes: 7 additions & 5 deletions src/Microsoft.TestPlatform.Extensions.HtmlLogger/HtmlLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ private void PopulateHtmlFile()
{
try
{
var fileName = string.Format(CultureInfo.CurrentCulture, "{0}_{1}_{2}",
var fileName = string.Format(CultureInfo.InvariantCulture, "{0}_{1}_{2}",
Environment.GetEnvironmentVariable("UserName"), Environment.MachineName,
FormatDateTimeForRunName(DateTime.Now));

Expand Down Expand Up @@ -350,17 +350,19 @@ private void PopulateHtmlFile()

private string GenerateUniqueFilePath(string fileName, string fileExtension)
{
string fullFilePath;
for (short i = 0; i < short.MaxValue; i++)
{
var fileNameWithIter = i == 0 ? fileName : Path.GetFileNameWithoutExtension(fileName) + $"[{i}]";
fullFilePath = Path.Combine(TestResultsDirPath!, $"TestResult_{fileNameWithIter}.{fileExtension}");
var fullFilePath = Path.Combine(TestResultsDirPath!, $"TestResult_{fileNameWithIter}.{fileExtension}");

try
{
using var _ = new FileStream(fullFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None);
// Use FileMode.CreateNew for atomic "create if not exists" to avoid
// cross-process race conditions when multiple vstest processes run in parallel.
using var _ = _fileHelper.GetStream(fullFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None);
return fullFilePath;
}
catch (IOException) when (File.Exists(fullFilePath))
catch (IOException) when (_fileHelper.Exists(fullFilePath))
{
Comment on lines +360 to 366
// File already exists (another process created it), try next iteration.
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,35 @@ public void XmlFilePathShouldContainFractionalSecondsForCrossProcessUniqueness()
$"Filename should end with yyyyMMdd_HHmmss.fffffff pattern: {fileName}");
}

[TestMethod]
public void TestCompleteHandlerShouldRetryUniqueFileNameWhenCreateNewCollides()
{
var collisionInjected = false;

_mockFileHelper.Setup(x => x.Exists(It.IsAny<string>())).Returns(true);

_mockFileHelper.Setup(x => x.GetStream(It.IsAny<string>(), FileMode.CreateNew, FileAccess.Write, FileShare.None))
.Returns<string, FileMode, FileAccess, FileShare>((path, _, _, _) =>
{
if (!collisionInjected)
{
Comment thread
nohwnd marked this conversation as resolved.
collisionInjected = true;
throw new IOException("collision");
}

return new Mock<Stream>().Object;
});

_mockFileHelper.Setup(x => x.GetStream(It.IsAny<string>(), FileMode.OpenOrCreate, FileAccess.ReadWrite))
Comment thread
nohwnd marked this conversation as resolved.
.Returns(new Mock<Stream>().Object);

_htmlLogger.TestRunCompleteHandler(new object(), new TestRunCompleteEventArgs(null, false, true, null, null, null, TimeSpan.Zero));

Assert.IsNotNull(_htmlLogger.XmlFilePath);
Assert.Contains("[1].xml", _htmlLogger.XmlFilePath);
_mockFileHelper.Verify(x => x.GetStream(It.IsAny<string>(), FileMode.CreateNew, FileAccess.Write, FileShare.None), Times.AtLeast(2));
}

private static TestCase CreateTestCase(string testCaseName)
{
return new TestCase(testCaseName, new Uri("some://uri"), "DummySourceFileName");
Expand Down