Skip to content

Add artifact naming service with template support for consistent naming across extensions#7856

Open
Evangelink wants to merge 19 commits intomainfrom
dev/amauryleve/artifact-naming-service
Open

Add artifact naming service with template support for consistent naming across extensions#7856
Evangelink wants to merge 19 commits intomainfrom
dev/amauryleve/artifact-naming-service

Conversation

@Evangelink
Copy link
Copy Markdown
Member

Summary

Add IArtifactNamingService — a reusable service that allows extensions to generate consistent artifact file names using template placeholders.

Users can specify templates like:

  • <pname>_<pid>_<id>_hang.dmpMyTests_12345_a1b2c3d4_hang.dmp
  • <asm>_<tfm>_<time>.logMyTests_net9.0_2025-09-22T13-49-34.log

Available Placeholders

Placeholder Description Example
<pname> Process name MyTests
<pid> Process ID 12345
<id> Short random identifier (8 chars) a1b2c3d4
<os> Operating system windows, linux, macos
<asm> Assembly name MyTests
<tfm> Target framework moniker net9.0
<time> Timestamp (1-second precision) 2025-09-22T13-49-34

Key Design Decisions

  • Internal interfaceIArtifactNamingService is internal, registered in the service provider, and available to all extensions
  • Case-insensitive placeholders
  • Custom replacements allow callers to override defaults (e.g., when dumping a child process with a different PID)
  • Unknown placeholders are preserved as-is
  • Backward compatibility — legacy %p pattern handled in the HangDump caller via string.Replace
  • Filesystem-safe time format — uses hyphens (HH-mm-ss) instead of colons

Changes

  • New IArtifactNamingService interface and ArtifactNamingService implementation
  • GetArtifactNamingService() extension method on IServiceProvider
  • Service registered in TestHostBuilder
  • HangDumpProcessLifetimeHandler updated to use the service
  • Version property added to IEnvironment/SystemEnvironment
  • Unit tests and integration test
  • Documentation in docs/ArtifactNamingService.md

Note

This PR revives and fixes the content from #6587 (closed copilot PR), with extensive code review fixes:

  • Fixed build for netstandard2.0 (removed OperatingSystem.IsXxx(), KeyValuePair deconstruction, [..8] range syntax)
  • Fixed non-existent Guard.NotNullOrEmpty API
  • Fixed broken legacy %p handling
  • Removed fragile <root> placeholder (directory tree traversal)
  • Fixed banned Environment API usage (RS0030)
  • Fixed all analyzer warnings

Fixes #6586

Related to #5364, #7345, #6648, #4130, #7126, #6778

Copilot AI and others added 16 commits September 22, 2025 12:04
Co-authored-by: nohwnd <5735905+nohwnd@users.noreply.github.com>
Co-authored-by: nohwnd <5735905+nohwnd@users.noreply.github.com>
…ssing imports

Co-authored-by: nohwnd <5735905+nohwnd@users.noreply.github.com>
Co-authored-by: nohwnd <5735905+nohwnd@users.noreply.github.com>
…ames

Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
# Conflicts:
#	src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs
#	src/Platform/Microsoft.Testing.Platform/Services/ServiceProviderExtensions.cs
- Remove <root> placeholder (fragile directory traversal)
- Fix OperatingSystem.IsWindows/IsLinux/IsMacOS for netstandard2.0 compat (use RuntimeInformation)
- Fix KeyValuePair deconstruction for netstandard2.0 compat
- Fix Guard.NotNullOrEmpty (non-existent API)
- Fix legacy %p pattern handling (move to HangDump caller, not the service)
- Fix time format to use hyphens instead of colons (filesystem safe)
- Remove unnecessary using directives
- Simplify GetOperatingSystemName with ternary expression
- Simplify TFM detection (remove hard-coded version map)
- Fix tests: use Assert.ThrowsExactly, match constructor signature, update assertions
Copilot AI review requested due to automatic review settings April 27, 2026 06:29
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

This PR introduces an internal IArtifactNamingService to centralize artifact filename/path templating across Microsoft.Testing.Platform extensions, and migrates HangDump to use it (while keeping legacy %p support).

Changes:

  • Added IArtifactNamingService + ArtifactNamingService implementation with placeholder-based template resolution.
  • Registered the service in TestHostBuilder and exposed it via IServiceProvider extension method.
  • Updated HangDump to use the service and added/updated unit + integration tests plus documentation.
Show a summary per file
File Description
test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/ArtifactNamingServiceTests.cs Adds unit tests covering placeholder replacement, casing, unknown placeholders, and error cases.
test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs Adds integration coverage for HangDump template-based filenames.
src/Platform/Microsoft.Testing.Platform/Services/ServiceProviderExtensions.cs Adds GetArtifactNamingService() accessor.
src/Platform/Microsoft.Testing.Platform/Services/IArtifactNamingService.cs Introduces the internal artifact naming service contract.
src/Platform/Microsoft.Testing.Platform/Services/ArtifactNamingService.cs Implements regex-based placeholder resolution and default replacement sources.
src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs Registers ArtifactNamingService into the platform service provider.
src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemEnvironment.cs Implements newly added Version property on the environment wrapper.
src/Platform/Microsoft.Testing.Platform/Helpers/System/IEnvironment.cs Adds Version to the environment abstraction.
src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs Uses IArtifactNamingService for dump filename resolution (then applies legacy %p).
src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpExtensions.cs Wires IArtifactNamingService into HangDumpProcessLifetimeHandler construction.
docs/ArtifactNamingService.md Documents placeholders, custom replacements, and HangDump usage.

Copilot's findings

  • Files reviewed: 11/11 changed files
  • Comments generated: 2

Comment thread docs/ArtifactNamingService.md Outdated

private string GetTargetFrameworkMoniker()
{
string frameworkDescription = RuntimeInformation.FrameworkDescription;
Copy link
Copy Markdown
Member

@Youssef1313 Youssef1313 Apr 27, 2026

Choose a reason for hiding this comment

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

This is "best-effort" TFM implementation, and doesn't necessarily map to the real/actual TargetFramework used during build time. I'm not sure if this approach is good, or if calling this "tfm" is a correct naming (even if we have that logic already somewhere for display purposes, we are now using it for something more critical than just display).

/// Service for generating consistent artifact names and paths using template patterns.
/// Supports placeholders like &lt;pname&gt;, &lt;pid&gt;, &lt;id&gt;, &lt;os&gt;, &lt;asm&gt;, &lt;tfm&gt;, &lt;time&gt;.
/// </summary>
internal interface IArtifactNamingService
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would personally avoid relying on IVT, and so I wouldn't register it as a service in core MTP at all. Instead, I think we can start small and have that as a simple static helper class that we link in extensions that need it.

private readonly IClock _clock;
private readonly IProcessHandler _processHandler;

private static readonly Regex TemplateFieldRegex = new(@"<([^>]+)>", RegexOptions.Compiled);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Isn't this equivalent to <(.+?)>? Is one more efficient than the other? I think <(.+?)> is easier to read.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@copilot please review equivalence and efficiency

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Both patterns are functionally equivalent for all relevant cases (single-line artifact template strings without > inside placeholder names).

On efficiency: benchmarking 1M iterations with a compiled regex on a typical template like <pname>_<pid>_<id>_hang.dmp shows <(.+?)> is actually marginally faster (~666ms vs ~755ms) because .NET's compiled regex engine has specific optimizations for lazy quantifiers against single-character anchors — no backtracking is needed once the engine sees >.

Changed to <(.+?)> in commit 8307cf1.

replacements["pname"] = currentProcess.Name;

// OS info
replacements["os"] = GetOperatingSystemName();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure I see this useful. It's best to start as small as possible IMO to address real pain of users.

The only current real pain I see is making TRX file names flexible per the few reports we got (none of which were related to os).

Comment thread src/Platform/Microsoft.Testing.Platform/Services/ArtifactNamingService.cs Outdated
replacements["time"] = _clock.UtcNow.ToString("yyyy-MM-ddTHH-mm-ss", CultureInfo.InvariantCulture);

// Random ID for uniqueness
replacements["id"] = GenerateShortId();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Per the above high precision time suggestion, I think we won't need an id for uniqueness. It's extremely unlikely the above will produce collisions.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

But if you don't use the time, having the id could be useful

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't see why would a user not want to use the time as if it's the "id" for uniqueness.

…lent behavior, slightly faster)

Agent-Logs-Url: https://github.com/microsoft/testfx/sessions/20821b8e-ec78-46cf-a7bd-cfbe8387b93c

Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
…yy-MM-dd_HH-mm-ss.fffffff

Agent-Logs-Url: https://github.com/microsoft/testfx/sessions/c862fbaf-cecd-4c9f-adbc-114c139eff4d

Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 27, 2026 11:36
@Evangelink Evangelink review requested due to automatic review settings April 27, 2026 11:36
Copilot AI review requested due to automatic review settings April 27, 2026 11:37
@Evangelink Evangelink review requested due to automatic review settings April 27, 2026 11:37
@nohwnd
Copy link
Copy Markdown
Member

nohwnd commented Apr 27, 2026

fyi:
been doing the same on VSTest recently, and decided to keep the value in the name if the template is not possible to fill.
The <> were dropped in favor of {} because they caused possible output redirects on copypast and other issues and are not valid filename. [] were invalid because they are wildcard.

In the end I landed on {} because while they can be interpreted by bash they don't require escaping in most places and are valid filename.

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.

Add artifact naming service

5 participants