Skip to content

Fix HTML logger parallel file collision#15435

Open
nohwnd wants to merge 19 commits intomainfrom
fix/html-logger-parallel-file-collision
Open

Fix HTML logger parallel file collision#15435
nohwnd wants to merge 19 commits intomainfrom
fix/html-logger-parallel-file-collision

Conversation

@nohwnd
Copy link
Copy Markdown
Member

@nohwnd nohwnd commented Mar 4, 2026

Summary

Fixes #15404

When multiple vstest.console processes run in parallel with the HTML logger, temporary XML result files can collide because:

  1. The timestamp format (\yyyyMMdd_HHmmss) only has second-level precision
  2. The in-process \lock (FileCreateLockObject)\ doesn't prevent cross-process races
  3. Two processes finishing in the same second generate identical filenames

Changes

\src/Microsoft.TestPlatform.Extensions.HtmlLogger/HtmlLogger.cs\

  • Add millisecond precision to timestamp - changes format from \HHmmss\ to \HHmmssfff, greatly reducing collision likelihood across parallel processes
  • Use \FileMode.CreateNew\ for atomic file creation - replaces the \File.Exists\ + \File.Create\ pattern with an atomic create-if-not-exists operation that retries on \IOException\
  • *Remove unused \FileCreateLockObject* - the cross-process lock was ineffective

\ est/Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests/HtmlLoggerTests.cs\

  • Added \XmlFilePathShouldContainMillisecondTimestampForCrossProcessUniqueness\ test

Documentation

  • Updated .github/copilot-instructions.md\ with build/test commands, repo structure, and logger architecture
  • Updated .github/skills/vstest-build-test/SKILL.md\ with logger-specific notes

Testing

All 34 HTML logger unit tests pass on both net9.0 and net48.

nohwnd and others added 7 commits March 4, 2026 11:41
Add pre-build environment setup section that detects when .dotnet
contains binaries for the wrong OS and cleans .dotnet, .packages,
and artifacts for a fresh bootstrap.

Update all build/test commands to show both Linux/macOS and Windows
equivalents with a quick-reference table.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sification

- Add PowerShell OS detection alongside bash
- Add guidance for extracting OS-specific commands from tables
- Add placeholder substitution and cross-platform adaptation rules
- Use SQL tracking table instead of markdown table
- Distinguish ENV_ISSUE from ERROR in result classification
- Improve Fix or Flag section with actionable steps per status

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- eng/build.ps1: Forward exit code from Arcade build script using
  \�xit \0\ to ensure non-zero exit codes are propagated
  to the caller
- eng/common/tools.ps1: Add diagnostic log message before exiting so
  the exit code is visible in build output
- .github/skills/vstest-build-test/SKILL.md: Fix Windows test filter
  flag from \-p\ to \-projects\ since \-p\ is ambiguous in
  PowerShell and does not work as expected

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When multiple vstest processes run in parallel with the HTML logger, temporary
XML result files could collide because the timestamp-based filename only has
second-level precision, and the in-process lock doesn't prevent cross-process
races.

Changes:
- Include process ID in the temp XML filename for cross-process uniqueness
- Replace File.Exists + File.Create with atomic FileMode.CreateNew to handle
  any remaining race conditions gracefully via retry
- Remove unused FileCreateLockObject (cross-process lock was ineffective)

Fixes: #15404

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add build commands, test project mappings, repository structure, logger
architecture notes, and analyzer pitfalls to copilot-instructions.md.

Update vstest-build-test skill with logger-specific notes covering TRX and
HTML logger architecture, test patterns, and common analyzer issues.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 4, 2026 14:56
@nohwnd nohwnd marked this pull request as draft March 4, 2026 14:58
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes cross-process temp XML filename collisions in the HTML logger when multiple vstest.console instances run in parallel.

Changes:

  • Adds PID to the temp XML filename and switches to FileMode.CreateNew for atomic “create if not exists”.
  • Adds a unit test to validate the PID is present in the generated XML path.
  • Updates build/doc tooling and authoring docs for repo build/test guidance.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests/HtmlLoggerTests.cs Adds a unit test asserting PID is included in the temp XML filename.
src/Microsoft.TestPlatform.Extensions.HtmlLogger/HtmlLogger.cs Adds PID to filename and uses FileMode.CreateNew for atomic file creation; removes ineffective in-proc lock.
eng/common/tools.ps1 Adds an extra console message when exiting with an exit code.
eng/build.ps1 Attempts to forward the exit code from the invoked Arcade build script.
.github/skills/vstest-build-test/SKILL.md Adds repo-specific build/test instructions and logger notes.
.github/skills/validate-skills/SKILL.md Adds a procedure for validating that documented skill commands work.
.github/skills/creating-skills/SKILL.md Adds guidance for creating skill files and frontmatter.
.github/copilot-instructions.md Adds build/test command guidance and repository structure notes.

When multiple vstest processes run in parallel with the HTML logger, temporary
XML result files could collide because the timestamp-based filename only has
second-level precision, and the in-process lock doesn't prevent cross-process
races.

Changes:
- Add millisecond precision to temp XML filename timestamp (HHmmss → HHmmssfff)
  to greatly reduce collision likelihood across parallel processes
- Replace File.Exists + File.Create with atomic FileMode.CreateNew to handle
  any remaining race conditions gracefully via retry
- Remove unused FileCreateLockObject (cross-process lock was ineffective)

Fixes: #15404

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@nohwnd nohwnd force-pushed the fix/html-logger-parallel-file-collision branch from 1d29994 to 3cb268d Compare March 4, 2026 15:04
@nohwnd
Copy link
Copy Markdown
Member Author

nohwnd commented Mar 4, 2026

needs merge of #15434

Copilot AI review requested due to automatic review settings March 5, 2026 14:39
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 5, 2026 15:22
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment on lines +160 to +161
- **Temp XML naming:** `TestResult_{user}_{machine}_{timestamp}_{pid}.xml` — PID ensures cross-process uniqueness
- **File creation:** Uses `FileMode.CreateNew` for atomic creation with retry on collision
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as in copilot-instructions.md: the documented filename pattern includes {pid} but the actual code does not add a PID to the filename. The uniqueness strategy relies on millisecond timestamps plus FileMode.CreateNew with retry. This documentation should be corrected to match the actual implementation.

Suggested change
- **Temp XML naming:** `TestResult_{user}_{machine}_{timestamp}_{pid}.xml`PID ensures cross-process uniqueness
- **File creation:** Uses `FileMode.CreateNew` for atomic creation with retry on collision
- **Temp XML naming:** `TestResult_{user}_{machine}_{timestamp}.xml`timestamp-based name only (no PID)
- **File creation:** Uses `FileMode.CreateNew` for atomic creation with retry on collision; millisecond timestamps + `CreateNew` retries provide uniqueness

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 5, 2026 15:42
nohwnd and others added 3 commits March 5, 2026 16:43
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Youssef Victor <youssefvictor00@gmail.com>
Copilot AI review requested due to automatic review settings March 24, 2026 06:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

@nohwnd nohwnd marked this pull request as ready for review March 24, 2026 07:30
nohwnd and others added 2 commits March 24, 2026 08:32
Tighten the HTML logger collision fix by restoring the intended millisecond timestamp format, keeping atomic create-new file allocation testable through IFileHelper, dropping unrelated docs churn from the PR, and adding coverage for retry-on-collision behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 10, 2026 12:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

using var _ = _fileHelper.GetStream(fullFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None);
return fullFilePath;
}
catch (IOException) when (File.Exists(fullFilePath))
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GenerateUniqueFilePath retries on IOException but uses static File.Exists(fullFilePath) in the catch filter. Since this class already abstracts file operations via IFileHelper, consider using _fileHelper.Exists(fullFilePath) (or checking the specific IOException/HResult for file-exists) so the logic stays consistent and easier to unit test without touching the real filesystem.

Suggested change
catch (IOException) when (File.Exists(fullFilePath))
catch (IOException) when (_fileHelper.Exists(fullFilePath))

Copilot uses AI. Check for mistakes.
Comment on lines +603 to +606
_htmlLogger.TestRunCompleteHandler(new object(), new TestRunCompleteEventArgs(null, false, true, null, null, null, TimeSpan.Zero));

Assert.IsNotNull(_htmlLogger.XmlFilePath);
createdFilePath = _htmlLogger.XmlFilePath;
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XmlFilePathShouldContainMillisecondTimestampForCrossProcessUniqueness calls TestRunCompleteHandler without setting up IFileHelper.GetStream for the new FileMode.CreateNew call, so the mock returns null Streams and the test doesn’t reflect real file creation. Consider setting up GetStream to return a disposable Stream (e.g., MemoryStream) and optionally verifying the CreateNew overload is invoked to keep the test robust.

Copilot uses AI. Check for mistakes.
nohwnd and others added 2 commits April 10, 2026 15:21
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 10, 2026 13:21
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

src/Microsoft.TestPlatform.Extensions.HtmlLogger/HtmlLogger.cs:366

  • GenerateUniqueFilePath uses File.Exists(fullFilePath) in the exception filter, bypassing the injected IFileHelper abstraction. This makes the behavior harder to test/mocks inconsistent (and couples the logger to the physical filesystem even when a custom IFileHelper is provided). Prefer using _fileHelper.Exists(fullFilePath) in the filter, and update the unit test setup accordingly.
                // 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))
            {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTML logger sometimes shows error when using multiple vstest runner in parallel

3 participants