Add public injectable IArtifactNamingService to Microsoft.Testing.Platform - #9783

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only
Jul 9, 2026
Merged

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform#9783
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Exposes file-name templating as a public, injectable core service in Microsoft.Testing.Platform, so it can be consumed outside the existing linked-source ([Embedded] helper) pattern.

Changes

  • IArtifactNamingService (public) — single member string ResolveFileName(string template). Expands the standard placeholders ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and sanitizes the leaf file name while preserving any directory portion of the template.
  • ArtifactNamingService (internal) — implementation that wraps the existing ArtifactNamingHelper templating with process name/id/timestamp resolved from ITestApplicationModuleInfo, IEnvironment and IClock.
  • ArtifactFileNameSanitizer (internal) — a core-internal copy of the invalid-char / reserved-name (CON/PRN/AUX/NUL/COM1-9/LPT1-9/CLOCK$) sanitization from the extension-side ReportFileNameSanitizer. The extension copy has shipped InternalAPI entries and is intentionally left in place; a comment marks the two for future consolidation.
  • Registered as a common service in TestHostBuilder.CommonServices.cs and resolvable via a new public ServiceProviderExtensions.GetArtifactNamingService(this IServiceProvider) accessor.
  • Public/internal API tracking files updated.
  • New ArtifactNamingServiceTests cover placeholder expansion, invalid-char sanitization, directory-portion preservation, and unknown-placeholder passthrough.

Verification

Built Microsoft.Testing.Platform.UnitTests and ran --filter "FullyQualifiedName~ArtifactNamingService" — 4/4 passing.

Note

This is one of two PRs split out from #9780 (the other, #9782, aligns the CTRF report writer overwrite behavior).

The pre-existing RS0051 InternalAPI baseline break from #9774 (BaseSerializer.ReadFields/WriteListPayload missing InternalAPI entries) that previously blocked the build has since been fixed on main; this branch was rebased onto that fix, so the two BaseSerializer entries now appear alongside the new service entries in InternalAPI.Unshipped.txt.

@github-actions

This comment has been minimized.

CopilotAI left a comment

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.

Pull request overview

This PR exposes Microsoft.Testing.Platform's artifact file-name templating as a new public, injectable core service, so that consumers can resolve placeholder templates ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and get a sanitized leaf file name without relying on the existing linked-source ([Embedded] helper) pattern. It is one of two PRs split out from #9780 (the companion #9782 handles CTRF overwrite behavior).

The implementation wraps the existing ArtifactNamingHelper templating and mirrors the extension-side sanitization logic. I verified the service is registered as a common service and resolves correctly by interface (the ServiceProvider matches instances via IsInstanceOfType), the constructor dependencies (systemEnvironment, systemClock, _testApplicationModuleInfo) are all in scope at the registration site, IEnvironment.ProcessId/IClock.UtcNow/ArgumentGuard.Ensure all exist as used, and the new internal/public API tracking entries are correctly formatted and placed (InternalAPI tracking is in use in this project — a prior memory to the contrary was outdated and has been down-voted).

Changes:

  • Adds public IArtifactNamingService + ServiceProviderExtensions.GetArtifactNamingService accessor, backed by internal ArtifactNamingService.
  • Adds a core-internal ArtifactFileNameSanitizer copy of the extension-side ReportFileNameSanitizer (intentional duplication, documented for future consolidation).
  • Registers the service in TestHostBuilder.CommonServices.cs, updates Public/Internal API baselines, and adds ArtifactNamingServiceTests.
Show a summary per file
FileDescription
Services/IArtifactNamingService.csNew public interface with a single ResolveFileName(string) member and placeholder docs.
Services/ArtifactNamingService.csInternal implementation resolving process name/id/time and sanitizing the leaf name.
Services/ArtifactFileNameSanitizer.csCore-internal copy of the extension invalid-char/reserved-name sanitizer.
Services/ServiceProviderExtensions.csNew public GetArtifactNamingService accessor.
Hosts/TestHostBuilder.CommonServices.csRegisters ArtifactNamingService as a common service.
PublicAPI/PublicAPI.Unshipped.txtDeclares the new public interface + accessor.
InternalAPI/InternalAPI.Unshipped.txtDeclares the new internal types/members.
Services/ArtifactNamingServiceTests.csTests placeholder expansion, invalid-char sanitization, directory preservation, unknown-placeholder passthrough.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actionsgithub-actionsBot left a comment

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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.


Review Summary

Clean design — exposing the file-name templating as a public injectable service is the right move. The interface is minimal (single member), the implementation correctly delegates to the existing ArtifactNamingHelper, and the sanitizer mirrors the shipped extension-side copy byte-for-byte. Service registration and resolution via IsInstanceOfType will work correctly.

Dimension Verdicts

#DimensionVerdict
1Algorithmic Correctness⚠️ Minor edge case (see inline: Path.GetFileName → empty on trailing separator)
2Public API Design✅ Minimal surface, no init, correctly in PublicAPI.Unshipped.txt
3Backward Compatibility✅ Additive only — new interface, new extension method
4Performance✅ No hot-path concerns; per-call GetStandardReplacements is acceptable for a naming utility
5Cross-TFM CorrectnessGeneratedRegex conditional on NET7_0_OR_GREATER with fallback
6Thread Safety✅ No mutable shared state; each call is self-contained
7Error Handling⚠️ See inline: guard at service boundary for empty leaf name
8LocalizationN/A — no user-facing strings added
9IPC ContractN/A
10Security✅ Path sanitization prevents invalid characters; directory portion preserved as-is (callers control the base directory)
11Testability✅ Constructor-injected dependencies, Moq-based tests
12Test Quality✅ Good coverage of core scenarios; minor gap on reserved-name and {asm}/{tfm}/{arch} paths (hard to mock static reflection)
13Code Style⚠️ Minor: missing blank line in CommonServices.cs (see inline)
14Documentation⚠️ XML doc for {asm}/{tfm} should note "unknown" fallback (see inline)
15InternalAPI Tracking✅ Entries match the actual internal surface
16Scope Discipline✅ Single concern
17Simplicity✅ Clean delegation pattern
18Build InfrastructureN/A
19Analyzer/CodeFixN/A
20Extension Points✅ Registered as common service; extensible via DI replacement
21CLI OptionsN/A
22Agentic WorkflowsN/A

CI Status

Linux builds (Debug & Release) are failing. Please verify the failures are unrelated to this change before merging.

Summary

Three inline comments posted — one style nit, one robustness improvement for an edge case, and one documentation accuracy suggestion. None are blocking. Overall this is a well-structured addition to the platform.

@github-actions

Copy link
Copy Markdown
Contributor

🔴 Build Failure Analysis

Status: 7 errors, all RS0051 (undeclared API)
Root cause: Two new BaseSerializer methods from the base branch commit a946ef7 ("Stabilize extension UIDs and add naming governance") are missing from InternalAPI.Unshipped.txt.

Errors (deduplicated — repeated per TFM: net8.0, net9.0, netstandard2.0)

FileLineSymbol
BaseSerializer.cs359ReadFields(Stream, Func<ushort, int, bool>)
BaseSerializer.cs380WriteListPayload<T>(Stream, ushort, T[]?, Action<Stream, T>)

Why this fails

The project tracks internal API surface via InternalAPI.Unshipped.txt (enforced by the RS0051 Roslyn analyzer). The base commit added ReadFields and WriteListPayload<T> as protected static methods to the internal abstract class BaseSerializer but did not add the corresponding entries. This PR inherits that gap.

Cascading failures

Microsoft.Testing.Extensions.CrashDump.csproj and Microsoft.Testing.Extensions.AzureFoundry.csproj failed because they depend on Microsoft.Testing.Platform.csproj, which failed to compile.

Fix

Add the following two lines to src/Platform/Microsoft.Testing.Platform/InternalAPI/InternalAPI.Unshipped.txt:

static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.ReadFields(System.IO.Stream! stream, System.Func<ushort, int, bool>! tryReadField) -> void
static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.WriteListPayload<T>(System.IO.Stream! stream, ushort fieldId, T[]? list, System.Action<System.IO.Stream!, T>! writeItem) -> void

These should be inserted in alphabetical order among the existing entries.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K · [◷]( · )

@github-actionsgithub-actionsBot left a comment

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.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K ·

CopilotAI review requested due to automatic review settings July 9, 2026 17:55
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from 17095c1 to 425820eCompareJuly 9, 2026 17:55

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 5
  • Review effort level: Low

Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/ArtifactNamingService.cs Outdated
Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/IArtifactNamingService.cs Outdated
@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 18:21
CopilotAI review requested due to automatic review settings July 9, 2026 18:26

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings July 9, 2026 19:05
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from d58afa3 to fe9bf60CompareJuly 9, 2026 19:05
…tform
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from fe9bf60 to 3640da3CompareJuly 9, 2026 19:08

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

CopilotAI review requested due to automatic review settings July 9, 2026 19:10

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9783

GradeTestNotes
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ExpandsStandardPlaceholders
Six assertions (StartsWith, Contains, EndsWith, three DoesNotContain) verify placeholder expansion, output format, and absence of raw tokens — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
LeavesUnknownPlaceholderAsIs
Two Contains checks verify {unknown} is preserved and {pname} is expanded — consider AreEqual("{unknown}_MyApp.json", result) for stronger full-output verification.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PrefixesReservedDeviceName
Precise equality assertion on the reserved-device-name edge case; explanatory comment clarifies the Windows constraint — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PreservesDirectoryPortionAndSanitizesLeaf
Path.Combine equality covers directory preservation and leaf sanitization in one cross-platform check — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
SanitizesInvalidCharactersInExpandedName
Tight equality assertion verifies asterisk-to-underscore sanitization precisely — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ThrowsWhenTemplateHasNoLeafFileName
ThrowsExactly verifies the precise exception type; explanatory comment identifies the invalid-template scenario — no issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 62.3 AIC · ⌖ 11.9 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 20:24
@Evangelink
Amaury Levé (Evangelink) merged commit 1b4496f into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/artifact-naming-service-only branch July 9, 2026 20:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform - #9783

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only
Jul 9, 2026
Merged

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform#9783
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Exposes file-name templating as a public, injectable core service in Microsoft.Testing.Platform, so it can be consumed outside the existing linked-source ([Embedded] helper) pattern.

Changes

  • IArtifactNamingService (public) — single member string ResolveFileName(string template). Expands the standard placeholders ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and sanitizes the leaf file name while preserving any directory portion of the template.
  • ArtifactNamingService (internal) — implementation that wraps the existing ArtifactNamingHelper templating with process name/id/timestamp resolved from ITestApplicationModuleInfo, IEnvironment and IClock.
  • ArtifactFileNameSanitizer (internal) — a core-internal copy of the invalid-char / reserved-name (CON/PRN/AUX/NUL/COM1-9/LPT1-9/CLOCK$) sanitization from the extension-side ReportFileNameSanitizer. The extension copy has shipped InternalAPI entries and is intentionally left in place; a comment marks the two for future consolidation.
  • Registered as a common service in TestHostBuilder.CommonServices.cs and resolvable via a new public ServiceProviderExtensions.GetArtifactNamingService(this IServiceProvider) accessor.
  • Public/internal API tracking files updated.
  • New ArtifactNamingServiceTests cover placeholder expansion, invalid-char sanitization, directory-portion preservation, and unknown-placeholder passthrough.

Verification

Built Microsoft.Testing.Platform.UnitTests and ran --filter "FullyQualifiedName~ArtifactNamingService" — 4/4 passing.

Note

This is one of two PRs split out from #9780 (the other, #9782, aligns the CTRF report writer overwrite behavior).

The pre-existing RS0051 InternalAPI baseline break from #9774 (BaseSerializer.ReadFields/WriteListPayload missing InternalAPI entries) that previously blocked the build has since been fixed on main; this branch was rebased onto that fix, so the two BaseSerializer entries now appear alongside the new service entries in InternalAPI.Unshipped.txt.

@github-actions

This comment has been minimized.

CopilotAI left a comment

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.

Pull request overview

This PR exposes Microsoft.Testing.Platform's artifact file-name templating as a new public, injectable core service, so that consumers can resolve placeholder templates ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and get a sanitized leaf file name without relying on the existing linked-source ([Embedded] helper) pattern. It is one of two PRs split out from #9780 (the companion #9782 handles CTRF overwrite behavior).

The implementation wraps the existing ArtifactNamingHelper templating and mirrors the extension-side sanitization logic. I verified the service is registered as a common service and resolves correctly by interface (the ServiceProvider matches instances via IsInstanceOfType), the constructor dependencies (systemEnvironment, systemClock, _testApplicationModuleInfo) are all in scope at the registration site, IEnvironment.ProcessId/IClock.UtcNow/ArgumentGuard.Ensure all exist as used, and the new internal/public API tracking entries are correctly formatted and placed (InternalAPI tracking is in use in this project — a prior memory to the contrary was outdated and has been down-voted).

Changes:

  • Adds public IArtifactNamingService + ServiceProviderExtensions.GetArtifactNamingService accessor, backed by internal ArtifactNamingService.
  • Adds a core-internal ArtifactFileNameSanitizer copy of the extension-side ReportFileNameSanitizer (intentional duplication, documented for future consolidation).
  • Registers the service in TestHostBuilder.CommonServices.cs, updates Public/Internal API baselines, and adds ArtifactNamingServiceTests.
Show a summary per file
FileDescription
Services/IArtifactNamingService.csNew public interface with a single ResolveFileName(string) member and placeholder docs.
Services/ArtifactNamingService.csInternal implementation resolving process name/id/time and sanitizing the leaf name.
Services/ArtifactFileNameSanitizer.csCore-internal copy of the extension invalid-char/reserved-name sanitizer.
Services/ServiceProviderExtensions.csNew public GetArtifactNamingService accessor.
Hosts/TestHostBuilder.CommonServices.csRegisters ArtifactNamingService as a common service.
PublicAPI/PublicAPI.Unshipped.txtDeclares the new public interface + accessor.
InternalAPI/InternalAPI.Unshipped.txtDeclares the new internal types/members.
Services/ArtifactNamingServiceTests.csTests placeholder expansion, invalid-char sanitization, directory preservation, unknown-placeholder passthrough.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actionsgithub-actionsBot left a comment

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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.


Review Summary

Clean design — exposing the file-name templating as a public injectable service is the right move. The interface is minimal (single member), the implementation correctly delegates to the existing ArtifactNamingHelper, and the sanitizer mirrors the shipped extension-side copy byte-for-byte. Service registration and resolution via IsInstanceOfType will work correctly.

Dimension Verdicts

#DimensionVerdict
1Algorithmic Correctness⚠️ Minor edge case (see inline: Path.GetFileName → empty on trailing separator)
2Public API Design✅ Minimal surface, no init, correctly in PublicAPI.Unshipped.txt
3Backward Compatibility✅ Additive only — new interface, new extension method
4Performance✅ No hot-path concerns; per-call GetStandardReplacements is acceptable for a naming utility
5Cross-TFM CorrectnessGeneratedRegex conditional on NET7_0_OR_GREATER with fallback
6Thread Safety✅ No mutable shared state; each call is self-contained
7Error Handling⚠️ See inline: guard at service boundary for empty leaf name
8LocalizationN/A — no user-facing strings added
9IPC ContractN/A
10Security✅ Path sanitization prevents invalid characters; directory portion preserved as-is (callers control the base directory)
11Testability✅ Constructor-injected dependencies, Moq-based tests
12Test Quality✅ Good coverage of core scenarios; minor gap on reserved-name and {asm}/{tfm}/{arch} paths (hard to mock static reflection)
13Code Style⚠️ Minor: missing blank line in CommonServices.cs (see inline)
14Documentation⚠️ XML doc for {asm}/{tfm} should note "unknown" fallback (see inline)
15InternalAPI Tracking✅ Entries match the actual internal surface
16Scope Discipline✅ Single concern
17Simplicity✅ Clean delegation pattern
18Build InfrastructureN/A
19Analyzer/CodeFixN/A
20Extension Points✅ Registered as common service; extensible via DI replacement
21CLI OptionsN/A
22Agentic WorkflowsN/A

CI Status

Linux builds (Debug & Release) are failing. Please verify the failures are unrelated to this change before merging.

Summary

Three inline comments posted — one style nit, one robustness improvement for an edge case, and one documentation accuracy suggestion. None are blocking. Overall this is a well-structured addition to the platform.

@github-actions

Copy link
Copy Markdown
Contributor

🔴 Build Failure Analysis

Status: 7 errors, all RS0051 (undeclared API)
Root cause: Two new BaseSerializer methods from the base branch commit a946ef7 ("Stabilize extension UIDs and add naming governance") are missing from InternalAPI.Unshipped.txt.

Errors (deduplicated — repeated per TFM: net8.0, net9.0, netstandard2.0)

FileLineSymbol
BaseSerializer.cs359ReadFields(Stream, Func<ushort, int, bool>)
BaseSerializer.cs380WriteListPayload<T>(Stream, ushort, T[]?, Action<Stream, T>)

Why this fails

The project tracks internal API surface via InternalAPI.Unshipped.txt (enforced by the RS0051 Roslyn analyzer). The base commit added ReadFields and WriteListPayload<T> as protected static methods to the internal abstract class BaseSerializer but did not add the corresponding entries. This PR inherits that gap.

Cascading failures

Microsoft.Testing.Extensions.CrashDump.csproj and Microsoft.Testing.Extensions.AzureFoundry.csproj failed because they depend on Microsoft.Testing.Platform.csproj, which failed to compile.

Fix

Add the following two lines to src/Platform/Microsoft.Testing.Platform/InternalAPI/InternalAPI.Unshipped.txt:

static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.ReadFields(System.IO.Stream! stream, System.Func<ushort, int, bool>! tryReadField) -> void
static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.WriteListPayload<T>(System.IO.Stream! stream, ushort fieldId, T[]? list, System.Action<System.IO.Stream!, T>! writeItem) -> void

These should be inserted in alphabetical order among the existing entries.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K · [◷]( · )

@github-actionsgithub-actionsBot left a comment

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.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K ·

CopilotAI review requested due to automatic review settings July 9, 2026 17:55
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from 17095c1 to 425820eCompareJuly 9, 2026 17:55

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 5
  • Review effort level: Low

Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/ArtifactNamingService.cs Outdated
Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/IArtifactNamingService.cs Outdated
@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 18:21
CopilotAI review requested due to automatic review settings July 9, 2026 18:26

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings July 9, 2026 19:05
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from d58afa3 to fe9bf60CompareJuly 9, 2026 19:05
…tform
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from fe9bf60 to 3640da3CompareJuly 9, 2026 19:08

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

CopilotAI review requested due to automatic review settings July 9, 2026 19:10

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9783

GradeTestNotes
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ExpandsStandardPlaceholders
Six assertions (StartsWith, Contains, EndsWith, three DoesNotContain) verify placeholder expansion, output format, and absence of raw tokens — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
LeavesUnknownPlaceholderAsIs
Two Contains checks verify {unknown} is preserved and {pname} is expanded — consider AreEqual("{unknown}_MyApp.json", result) for stronger full-output verification.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PrefixesReservedDeviceName
Precise equality assertion on the reserved-device-name edge case; explanatory comment clarifies the Windows constraint — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PreservesDirectoryPortionAndSanitizesLeaf
Path.Combine equality covers directory preservation and leaf sanitization in one cross-platform check — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
SanitizesInvalidCharactersInExpandedName
Tight equality assertion verifies asterisk-to-underscore sanitization precisely — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ThrowsWhenTemplateHasNoLeafFileName
ThrowsExactly verifies the precise exception type; explanatory comment identifies the invalid-template scenario — no issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 62.3 AIC · ⌖ 11.9 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 20:24
@Evangelink
Amaury Levé (Evangelink) merged commit 1b4496f into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/artifact-naming-service-only branch July 9, 2026 20:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform - #9783

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only
Jul 9, 2026
Merged

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform#9783
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Exposes file-name templating as a public, injectable core service in Microsoft.Testing.Platform, so it can be consumed outside the existing linked-source ([Embedded] helper) pattern.

Changes

  • IArtifactNamingService (public) — single member string ResolveFileName(string template). Expands the standard placeholders ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and sanitizes the leaf file name while preserving any directory portion of the template.
  • ArtifactNamingService (internal) — implementation that wraps the existing ArtifactNamingHelper templating with process name/id/timestamp resolved from ITestApplicationModuleInfo, IEnvironment and IClock.
  • ArtifactFileNameSanitizer (internal) — a core-internal copy of the invalid-char / reserved-name (CON/PRN/AUX/NUL/COM1-9/LPT1-9/CLOCK$) sanitization from the extension-side ReportFileNameSanitizer. The extension copy has shipped InternalAPI entries and is intentionally left in place; a comment marks the two for future consolidation.
  • Registered as a common service in TestHostBuilder.CommonServices.cs and resolvable via a new public ServiceProviderExtensions.GetArtifactNamingService(this IServiceProvider) accessor.
  • Public/internal API tracking files updated.
  • New ArtifactNamingServiceTests cover placeholder expansion, invalid-char sanitization, directory-portion preservation, and unknown-placeholder passthrough.

Verification

Built Microsoft.Testing.Platform.UnitTests and ran --filter "FullyQualifiedName~ArtifactNamingService" — 4/4 passing.

Note

This is one of two PRs split out from #9780 (the other, #9782, aligns the CTRF report writer overwrite behavior).

The pre-existing RS0051 InternalAPI baseline break from #9774 (BaseSerializer.ReadFields/WriteListPayload missing InternalAPI entries) that previously blocked the build has since been fixed on main; this branch was rebased onto that fix, so the two BaseSerializer entries now appear alongside the new service entries in InternalAPI.Unshipped.txt.

@github-actions

This comment has been minimized.

CopilotAI left a comment

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.

Pull request overview

This PR exposes Microsoft.Testing.Platform's artifact file-name templating as a new public, injectable core service, so that consumers can resolve placeholder templates ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and get a sanitized leaf file name without relying on the existing linked-source ([Embedded] helper) pattern. It is one of two PRs split out from #9780 (the companion #9782 handles CTRF overwrite behavior).

The implementation wraps the existing ArtifactNamingHelper templating and mirrors the extension-side sanitization logic. I verified the service is registered as a common service and resolves correctly by interface (the ServiceProvider matches instances via IsInstanceOfType), the constructor dependencies (systemEnvironment, systemClock, _testApplicationModuleInfo) are all in scope at the registration site, IEnvironment.ProcessId/IClock.UtcNow/ArgumentGuard.Ensure all exist as used, and the new internal/public API tracking entries are correctly formatted and placed (InternalAPI tracking is in use in this project — a prior memory to the contrary was outdated and has been down-voted).

Changes:

  • Adds public IArtifactNamingService + ServiceProviderExtensions.GetArtifactNamingService accessor, backed by internal ArtifactNamingService.
  • Adds a core-internal ArtifactFileNameSanitizer copy of the extension-side ReportFileNameSanitizer (intentional duplication, documented for future consolidation).
  • Registers the service in TestHostBuilder.CommonServices.cs, updates Public/Internal API baselines, and adds ArtifactNamingServiceTests.
Show a summary per file
FileDescription
Services/IArtifactNamingService.csNew public interface with a single ResolveFileName(string) member and placeholder docs.
Services/ArtifactNamingService.csInternal implementation resolving process name/id/time and sanitizing the leaf name.
Services/ArtifactFileNameSanitizer.csCore-internal copy of the extension invalid-char/reserved-name sanitizer.
Services/ServiceProviderExtensions.csNew public GetArtifactNamingService accessor.
Hosts/TestHostBuilder.CommonServices.csRegisters ArtifactNamingService as a common service.
PublicAPI/PublicAPI.Unshipped.txtDeclares the new public interface + accessor.
InternalAPI/InternalAPI.Unshipped.txtDeclares the new internal types/members.
Services/ArtifactNamingServiceTests.csTests placeholder expansion, invalid-char sanitization, directory preservation, unknown-placeholder passthrough.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actionsgithub-actionsBot left a comment

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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.


Review Summary

Clean design — exposing the file-name templating as a public injectable service is the right move. The interface is minimal (single member), the implementation correctly delegates to the existing ArtifactNamingHelper, and the sanitizer mirrors the shipped extension-side copy byte-for-byte. Service registration and resolution via IsInstanceOfType will work correctly.

Dimension Verdicts

#DimensionVerdict
1Algorithmic Correctness⚠️ Minor edge case (see inline: Path.GetFileName → empty on trailing separator)
2Public API Design✅ Minimal surface, no init, correctly in PublicAPI.Unshipped.txt
3Backward Compatibility✅ Additive only — new interface, new extension method
4Performance✅ No hot-path concerns; per-call GetStandardReplacements is acceptable for a naming utility
5Cross-TFM CorrectnessGeneratedRegex conditional on NET7_0_OR_GREATER with fallback
6Thread Safety✅ No mutable shared state; each call is self-contained
7Error Handling⚠️ See inline: guard at service boundary for empty leaf name
8LocalizationN/A — no user-facing strings added
9IPC ContractN/A
10Security✅ Path sanitization prevents invalid characters; directory portion preserved as-is (callers control the base directory)
11Testability✅ Constructor-injected dependencies, Moq-based tests
12Test Quality✅ Good coverage of core scenarios; minor gap on reserved-name and {asm}/{tfm}/{arch} paths (hard to mock static reflection)
13Code Style⚠️ Minor: missing blank line in CommonServices.cs (see inline)
14Documentation⚠️ XML doc for {asm}/{tfm} should note "unknown" fallback (see inline)
15InternalAPI Tracking✅ Entries match the actual internal surface
16Scope Discipline✅ Single concern
17Simplicity✅ Clean delegation pattern
18Build InfrastructureN/A
19Analyzer/CodeFixN/A
20Extension Points✅ Registered as common service; extensible via DI replacement
21CLI OptionsN/A
22Agentic WorkflowsN/A

CI Status

Linux builds (Debug & Release) are failing. Please verify the failures are unrelated to this change before merging.

Summary

Three inline comments posted — one style nit, one robustness improvement for an edge case, and one documentation accuracy suggestion. None are blocking. Overall this is a well-structured addition to the platform.

@github-actions

Copy link
Copy Markdown
Contributor

🔴 Build Failure Analysis

Status: 7 errors, all RS0051 (undeclared API)
Root cause: Two new BaseSerializer methods from the base branch commit a946ef7 ("Stabilize extension UIDs and add naming governance") are missing from InternalAPI.Unshipped.txt.

Errors (deduplicated — repeated per TFM: net8.0, net9.0, netstandard2.0)

FileLineSymbol
BaseSerializer.cs359ReadFields(Stream, Func<ushort, int, bool>)
BaseSerializer.cs380WriteListPayload<T>(Stream, ushort, T[]?, Action<Stream, T>)

Why this fails

The project tracks internal API surface via InternalAPI.Unshipped.txt (enforced by the RS0051 Roslyn analyzer). The base commit added ReadFields and WriteListPayload<T> as protected static methods to the internal abstract class BaseSerializer but did not add the corresponding entries. This PR inherits that gap.

Cascading failures

Microsoft.Testing.Extensions.CrashDump.csproj and Microsoft.Testing.Extensions.AzureFoundry.csproj failed because they depend on Microsoft.Testing.Platform.csproj, which failed to compile.

Fix

Add the following two lines to src/Platform/Microsoft.Testing.Platform/InternalAPI/InternalAPI.Unshipped.txt:

static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.ReadFields(System.IO.Stream! stream, System.Func<ushort, int, bool>! tryReadField) -> void
static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.WriteListPayload<T>(System.IO.Stream! stream, ushort fieldId, T[]? list, System.Action<System.IO.Stream!, T>! writeItem) -> void

These should be inserted in alphabetical order among the existing entries.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K · [◷]( · )

@github-actionsgithub-actionsBot left a comment

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.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K ·

CopilotAI review requested due to automatic review settings July 9, 2026 17:55
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from 17095c1 to 425820eCompareJuly 9, 2026 17:55

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 5
  • Review effort level: Low

Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/ArtifactNamingService.cs Outdated
Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/IArtifactNamingService.cs Outdated
@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 18:21
CopilotAI review requested due to automatic review settings July 9, 2026 18:26

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings July 9, 2026 19:05
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from d58afa3 to fe9bf60CompareJuly 9, 2026 19:05
…tform
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from fe9bf60 to 3640da3CompareJuly 9, 2026 19:08

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

CopilotAI review requested due to automatic review settings July 9, 2026 19:10

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9783

GradeTestNotes
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ExpandsStandardPlaceholders
Six assertions (StartsWith, Contains, EndsWith, three DoesNotContain) verify placeholder expansion, output format, and absence of raw tokens — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
LeavesUnknownPlaceholderAsIs
Two Contains checks verify {unknown} is preserved and {pname} is expanded — consider AreEqual("{unknown}_MyApp.json", result) for stronger full-output verification.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PrefixesReservedDeviceName
Precise equality assertion on the reserved-device-name edge case; explanatory comment clarifies the Windows constraint — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PreservesDirectoryPortionAndSanitizesLeaf
Path.Combine equality covers directory preservation and leaf sanitization in one cross-platform check — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
SanitizesInvalidCharactersInExpandedName
Tight equality assertion verifies asterisk-to-underscore sanitization precisely — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ThrowsWhenTemplateHasNoLeafFileName
ThrowsExactly verifies the precise exception type; explanatory comment identifies the invalid-template scenario — no issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 62.3 AIC · ⌖ 11.9 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 20:24
@Evangelink
Amaury Levé (Evangelink) merged commit 1b4496f into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/artifact-naming-service-only branch July 9, 2026 20:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform - #9783

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only
Jul 9, 2026
Merged

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform#9783
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Exposes file-name templating as a public, injectable core service in Microsoft.Testing.Platform, so it can be consumed outside the existing linked-source ([Embedded] helper) pattern.

Changes

  • IArtifactNamingService (public) — single member string ResolveFileName(string template). Expands the standard placeholders ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and sanitizes the leaf file name while preserving any directory portion of the template.
  • ArtifactNamingService (internal) — implementation that wraps the existing ArtifactNamingHelper templating with process name/id/timestamp resolved from ITestApplicationModuleInfo, IEnvironment and IClock.
  • ArtifactFileNameSanitizer (internal) — a core-internal copy of the invalid-char / reserved-name (CON/PRN/AUX/NUL/COM1-9/LPT1-9/CLOCK$) sanitization from the extension-side ReportFileNameSanitizer. The extension copy has shipped InternalAPI entries and is intentionally left in place; a comment marks the two for future consolidation.
  • Registered as a common service in TestHostBuilder.CommonServices.cs and resolvable via a new public ServiceProviderExtensions.GetArtifactNamingService(this IServiceProvider) accessor.
  • Public/internal API tracking files updated.
  • New ArtifactNamingServiceTests cover placeholder expansion, invalid-char sanitization, directory-portion preservation, and unknown-placeholder passthrough.

Verification

Built Microsoft.Testing.Platform.UnitTests and ran --filter "FullyQualifiedName~ArtifactNamingService" — 4/4 passing.

Note

This is one of two PRs split out from #9780 (the other, #9782, aligns the CTRF report writer overwrite behavior).

The pre-existing RS0051 InternalAPI baseline break from #9774 (BaseSerializer.ReadFields/WriteListPayload missing InternalAPI entries) that previously blocked the build has since been fixed on main; this branch was rebased onto that fix, so the two BaseSerializer entries now appear alongside the new service entries in InternalAPI.Unshipped.txt.

@github-actions

This comment has been minimized.

CopilotAI left a comment

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.

Pull request overview

This PR exposes Microsoft.Testing.Platform's artifact file-name templating as a new public, injectable core service, so that consumers can resolve placeholder templates ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and get a sanitized leaf file name without relying on the existing linked-source ([Embedded] helper) pattern. It is one of two PRs split out from #9780 (the companion #9782 handles CTRF overwrite behavior).

The implementation wraps the existing ArtifactNamingHelper templating and mirrors the extension-side sanitization logic. I verified the service is registered as a common service and resolves correctly by interface (the ServiceProvider matches instances via IsInstanceOfType), the constructor dependencies (systemEnvironment, systemClock, _testApplicationModuleInfo) are all in scope at the registration site, IEnvironment.ProcessId/IClock.UtcNow/ArgumentGuard.Ensure all exist as used, and the new internal/public API tracking entries are correctly formatted and placed (InternalAPI tracking is in use in this project — a prior memory to the contrary was outdated and has been down-voted).

Changes:

  • Adds public IArtifactNamingService + ServiceProviderExtensions.GetArtifactNamingService accessor, backed by internal ArtifactNamingService.
  • Adds a core-internal ArtifactFileNameSanitizer copy of the extension-side ReportFileNameSanitizer (intentional duplication, documented for future consolidation).
  • Registers the service in TestHostBuilder.CommonServices.cs, updates Public/Internal API baselines, and adds ArtifactNamingServiceTests.
Show a summary per file
FileDescription
Services/IArtifactNamingService.csNew public interface with a single ResolveFileName(string) member and placeholder docs.
Services/ArtifactNamingService.csInternal implementation resolving process name/id/time and sanitizing the leaf name.
Services/ArtifactFileNameSanitizer.csCore-internal copy of the extension invalid-char/reserved-name sanitizer.
Services/ServiceProviderExtensions.csNew public GetArtifactNamingService accessor.
Hosts/TestHostBuilder.CommonServices.csRegisters ArtifactNamingService as a common service.
PublicAPI/PublicAPI.Unshipped.txtDeclares the new public interface + accessor.
InternalAPI/InternalAPI.Unshipped.txtDeclares the new internal types/members.
Services/ArtifactNamingServiceTests.csTests placeholder expansion, invalid-char sanitization, directory preservation, unknown-placeholder passthrough.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actionsgithub-actionsBot left a comment

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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.


Review Summary

Clean design — exposing the file-name templating as a public injectable service is the right move. The interface is minimal (single member), the implementation correctly delegates to the existing ArtifactNamingHelper, and the sanitizer mirrors the shipped extension-side copy byte-for-byte. Service registration and resolution via IsInstanceOfType will work correctly.

Dimension Verdicts

#DimensionVerdict
1Algorithmic Correctness⚠️ Minor edge case (see inline: Path.GetFileName → empty on trailing separator)
2Public API Design✅ Minimal surface, no init, correctly in PublicAPI.Unshipped.txt
3Backward Compatibility✅ Additive only — new interface, new extension method
4Performance✅ No hot-path concerns; per-call GetStandardReplacements is acceptable for a naming utility
5Cross-TFM CorrectnessGeneratedRegex conditional on NET7_0_OR_GREATER with fallback
6Thread Safety✅ No mutable shared state; each call is self-contained
7Error Handling⚠️ See inline: guard at service boundary for empty leaf name
8LocalizationN/A — no user-facing strings added
9IPC ContractN/A
10Security✅ Path sanitization prevents invalid characters; directory portion preserved as-is (callers control the base directory)
11Testability✅ Constructor-injected dependencies, Moq-based tests
12Test Quality✅ Good coverage of core scenarios; minor gap on reserved-name and {asm}/{tfm}/{arch} paths (hard to mock static reflection)
13Code Style⚠️ Minor: missing blank line in CommonServices.cs (see inline)
14Documentation⚠️ XML doc for {asm}/{tfm} should note "unknown" fallback (see inline)
15InternalAPI Tracking✅ Entries match the actual internal surface
16Scope Discipline✅ Single concern
17Simplicity✅ Clean delegation pattern
18Build InfrastructureN/A
19Analyzer/CodeFixN/A
20Extension Points✅ Registered as common service; extensible via DI replacement
21CLI OptionsN/A
22Agentic WorkflowsN/A

CI Status

Linux builds (Debug & Release) are failing. Please verify the failures are unrelated to this change before merging.

Summary

Three inline comments posted — one style nit, one robustness improvement for an edge case, and one documentation accuracy suggestion. None are blocking. Overall this is a well-structured addition to the platform.

@github-actions

Copy link
Copy Markdown
Contributor

🔴 Build Failure Analysis

Status: 7 errors, all RS0051 (undeclared API)
Root cause: Two new BaseSerializer methods from the base branch commit a946ef7 ("Stabilize extension UIDs and add naming governance") are missing from InternalAPI.Unshipped.txt.

Errors (deduplicated — repeated per TFM: net8.0, net9.0, netstandard2.0)

FileLineSymbol
BaseSerializer.cs359ReadFields(Stream, Func<ushort, int, bool>)
BaseSerializer.cs380WriteListPayload<T>(Stream, ushort, T[]?, Action<Stream, T>)

Why this fails

The project tracks internal API surface via InternalAPI.Unshipped.txt (enforced by the RS0051 Roslyn analyzer). The base commit added ReadFields and WriteListPayload<T> as protected static methods to the internal abstract class BaseSerializer but did not add the corresponding entries. This PR inherits that gap.

Cascading failures

Microsoft.Testing.Extensions.CrashDump.csproj and Microsoft.Testing.Extensions.AzureFoundry.csproj failed because they depend on Microsoft.Testing.Platform.csproj, which failed to compile.

Fix

Add the following two lines to src/Platform/Microsoft.Testing.Platform/InternalAPI/InternalAPI.Unshipped.txt:

static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.ReadFields(System.IO.Stream! stream, System.Func<ushort, int, bool>! tryReadField) -> void
static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.WriteListPayload<T>(System.IO.Stream! stream, ushort fieldId, T[]? list, System.Action<System.IO.Stream!, T>! writeItem) -> void

These should be inserted in alphabetical order among the existing entries.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K · [◷]( · )

@github-actionsgithub-actionsBot left a comment

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.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K ·

CopilotAI review requested due to automatic review settings July 9, 2026 17:55
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from 17095c1 to 425820eCompareJuly 9, 2026 17:55

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 5
  • Review effort level: Low

Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/ArtifactNamingService.cs Outdated
Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/IArtifactNamingService.cs Outdated
@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 18:21
CopilotAI review requested due to automatic review settings July 9, 2026 18:26

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings July 9, 2026 19:05
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from d58afa3 to fe9bf60CompareJuly 9, 2026 19:05
…tform
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from fe9bf60 to 3640da3CompareJuly 9, 2026 19:08

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

CopilotAI review requested due to automatic review settings July 9, 2026 19:10

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9783

GradeTestNotes
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ExpandsStandardPlaceholders
Six assertions (StartsWith, Contains, EndsWith, three DoesNotContain) verify placeholder expansion, output format, and absence of raw tokens — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
LeavesUnknownPlaceholderAsIs
Two Contains checks verify {unknown} is preserved and {pname} is expanded — consider AreEqual("{unknown}_MyApp.json", result) for stronger full-output verification.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PrefixesReservedDeviceName
Precise equality assertion on the reserved-device-name edge case; explanatory comment clarifies the Windows constraint — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PreservesDirectoryPortionAndSanitizesLeaf
Path.Combine equality covers directory preservation and leaf sanitization in one cross-platform check — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
SanitizesInvalidCharactersInExpandedName
Tight equality assertion verifies asterisk-to-underscore sanitization precisely — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ThrowsWhenTemplateHasNoLeafFileName
ThrowsExactly verifies the precise exception type; explanatory comment identifies the invalid-template scenario — no issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 62.3 AIC · ⌖ 11.9 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 20:24
@Evangelink
Amaury Levé (Evangelink) merged commit 1b4496f into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/artifact-naming-service-only branch July 9, 2026 20:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform - #9783

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only
Jul 9, 2026
Merged

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform#9783
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Exposes file-name templating as a public, injectable core service in Microsoft.Testing.Platform, so it can be consumed outside the existing linked-source ([Embedded] helper) pattern.

Changes

  • IArtifactNamingService (public) — single member string ResolveFileName(string template). Expands the standard placeholders ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and sanitizes the leaf file name while preserving any directory portion of the template.
  • ArtifactNamingService (internal) — implementation that wraps the existing ArtifactNamingHelper templating with process name/id/timestamp resolved from ITestApplicationModuleInfo, IEnvironment and IClock.
  • ArtifactFileNameSanitizer (internal) — a core-internal copy of the invalid-char / reserved-name (CON/PRN/AUX/NUL/COM1-9/LPT1-9/CLOCK$) sanitization from the extension-side ReportFileNameSanitizer. The extension copy has shipped InternalAPI entries and is intentionally left in place; a comment marks the two for future consolidation.
  • Registered as a common service in TestHostBuilder.CommonServices.cs and resolvable via a new public ServiceProviderExtensions.GetArtifactNamingService(this IServiceProvider) accessor.
  • Public/internal API tracking files updated.
  • New ArtifactNamingServiceTests cover placeholder expansion, invalid-char sanitization, directory-portion preservation, and unknown-placeholder passthrough.

Verification

Built Microsoft.Testing.Platform.UnitTests and ran --filter "FullyQualifiedName~ArtifactNamingService" — 4/4 passing.

Note

This is one of two PRs split out from #9780 (the other, #9782, aligns the CTRF report writer overwrite behavior).

The pre-existing RS0051 InternalAPI baseline break from #9774 (BaseSerializer.ReadFields/WriteListPayload missing InternalAPI entries) that previously blocked the build has since been fixed on main; this branch was rebased onto that fix, so the two BaseSerializer entries now appear alongside the new service entries in InternalAPI.Unshipped.txt.

@github-actions

This comment has been minimized.

CopilotAI left a comment

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.

Pull request overview

This PR exposes Microsoft.Testing.Platform's artifact file-name templating as a new public, injectable core service, so that consumers can resolve placeholder templates ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and get a sanitized leaf file name without relying on the existing linked-source ([Embedded] helper) pattern. It is one of two PRs split out from #9780 (the companion #9782 handles CTRF overwrite behavior).

The implementation wraps the existing ArtifactNamingHelper templating and mirrors the extension-side sanitization logic. I verified the service is registered as a common service and resolves correctly by interface (the ServiceProvider matches instances via IsInstanceOfType), the constructor dependencies (systemEnvironment, systemClock, _testApplicationModuleInfo) are all in scope at the registration site, IEnvironment.ProcessId/IClock.UtcNow/ArgumentGuard.Ensure all exist as used, and the new internal/public API tracking entries are correctly formatted and placed (InternalAPI tracking is in use in this project — a prior memory to the contrary was outdated and has been down-voted).

Changes:

  • Adds public IArtifactNamingService + ServiceProviderExtensions.GetArtifactNamingService accessor, backed by internal ArtifactNamingService.
  • Adds a core-internal ArtifactFileNameSanitizer copy of the extension-side ReportFileNameSanitizer (intentional duplication, documented for future consolidation).
  • Registers the service in TestHostBuilder.CommonServices.cs, updates Public/Internal API baselines, and adds ArtifactNamingServiceTests.
Show a summary per file
FileDescription
Services/IArtifactNamingService.csNew public interface with a single ResolveFileName(string) member and placeholder docs.
Services/ArtifactNamingService.csInternal implementation resolving process name/id/time and sanitizing the leaf name.
Services/ArtifactFileNameSanitizer.csCore-internal copy of the extension invalid-char/reserved-name sanitizer.
Services/ServiceProviderExtensions.csNew public GetArtifactNamingService accessor.
Hosts/TestHostBuilder.CommonServices.csRegisters ArtifactNamingService as a common service.
PublicAPI/PublicAPI.Unshipped.txtDeclares the new public interface + accessor.
InternalAPI/InternalAPI.Unshipped.txtDeclares the new internal types/members.
Services/ArtifactNamingServiceTests.csTests placeholder expansion, invalid-char sanitization, directory preservation, unknown-placeholder passthrough.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actionsgithub-actionsBot left a comment

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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.


Review Summary

Clean design — exposing the file-name templating as a public injectable service is the right move. The interface is minimal (single member), the implementation correctly delegates to the existing ArtifactNamingHelper, and the sanitizer mirrors the shipped extension-side copy byte-for-byte. Service registration and resolution via IsInstanceOfType will work correctly.

Dimension Verdicts

#DimensionVerdict
1Algorithmic Correctness⚠️ Minor edge case (see inline: Path.GetFileName → empty on trailing separator)
2Public API Design✅ Minimal surface, no init, correctly in PublicAPI.Unshipped.txt
3Backward Compatibility✅ Additive only — new interface, new extension method
4Performance✅ No hot-path concerns; per-call GetStandardReplacements is acceptable for a naming utility
5Cross-TFM CorrectnessGeneratedRegex conditional on NET7_0_OR_GREATER with fallback
6Thread Safety✅ No mutable shared state; each call is self-contained
7Error Handling⚠️ See inline: guard at service boundary for empty leaf name
8LocalizationN/A — no user-facing strings added
9IPC ContractN/A
10Security✅ Path sanitization prevents invalid characters; directory portion preserved as-is (callers control the base directory)
11Testability✅ Constructor-injected dependencies, Moq-based tests
12Test Quality✅ Good coverage of core scenarios; minor gap on reserved-name and {asm}/{tfm}/{arch} paths (hard to mock static reflection)
13Code Style⚠️ Minor: missing blank line in CommonServices.cs (see inline)
14Documentation⚠️ XML doc for {asm}/{tfm} should note "unknown" fallback (see inline)
15InternalAPI Tracking✅ Entries match the actual internal surface
16Scope Discipline✅ Single concern
17Simplicity✅ Clean delegation pattern
18Build InfrastructureN/A
19Analyzer/CodeFixN/A
20Extension Points✅ Registered as common service; extensible via DI replacement
21CLI OptionsN/A
22Agentic WorkflowsN/A

CI Status

Linux builds (Debug & Release) are failing. Please verify the failures are unrelated to this change before merging.

Summary

Three inline comments posted — one style nit, one robustness improvement for an edge case, and one documentation accuracy suggestion. None are blocking. Overall this is a well-structured addition to the platform.

@github-actions

Copy link
Copy Markdown
Contributor

🔴 Build Failure Analysis

Status: 7 errors, all RS0051 (undeclared API)
Root cause: Two new BaseSerializer methods from the base branch commit a946ef7 ("Stabilize extension UIDs and add naming governance") are missing from InternalAPI.Unshipped.txt.

Errors (deduplicated — repeated per TFM: net8.0, net9.0, netstandard2.0)

FileLineSymbol
BaseSerializer.cs359ReadFields(Stream, Func<ushort, int, bool>)
BaseSerializer.cs380WriteListPayload<T>(Stream, ushort, T[]?, Action<Stream, T>)

Why this fails

The project tracks internal API surface via InternalAPI.Unshipped.txt (enforced by the RS0051 Roslyn analyzer). The base commit added ReadFields and WriteListPayload<T> as protected static methods to the internal abstract class BaseSerializer but did not add the corresponding entries. This PR inherits that gap.

Cascading failures

Microsoft.Testing.Extensions.CrashDump.csproj and Microsoft.Testing.Extensions.AzureFoundry.csproj failed because they depend on Microsoft.Testing.Platform.csproj, which failed to compile.

Fix

Add the following two lines to src/Platform/Microsoft.Testing.Platform/InternalAPI/InternalAPI.Unshipped.txt:

static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.ReadFields(System.IO.Stream! stream, System.Func<ushort, int, bool>! tryReadField) -> void
static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.WriteListPayload<T>(System.IO.Stream! stream, ushort fieldId, T[]? list, System.Action<System.IO.Stream!, T>! writeItem) -> void

These should be inserted in alphabetical order among the existing entries.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K · [◷]( · )

@github-actionsgithub-actionsBot left a comment

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.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K ·

CopilotAI review requested due to automatic review settings July 9, 2026 17:55
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from 17095c1 to 425820eCompareJuly 9, 2026 17:55

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 5
  • Review effort level: Low

Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/ArtifactNamingService.cs Outdated
Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/IArtifactNamingService.cs Outdated
@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 18:21
CopilotAI review requested due to automatic review settings July 9, 2026 18:26

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings July 9, 2026 19:05
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from d58afa3 to fe9bf60CompareJuly 9, 2026 19:05
…tform
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from fe9bf60 to 3640da3CompareJuly 9, 2026 19:08

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

CopilotAI review requested due to automatic review settings July 9, 2026 19:10

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9783

GradeTestNotes
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ExpandsStandardPlaceholders
Six assertions (StartsWith, Contains, EndsWith, three DoesNotContain) verify placeholder expansion, output format, and absence of raw tokens — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
LeavesUnknownPlaceholderAsIs
Two Contains checks verify {unknown} is preserved and {pname} is expanded — consider AreEqual("{unknown}_MyApp.json", result) for stronger full-output verification.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PrefixesReservedDeviceName
Precise equality assertion on the reserved-device-name edge case; explanatory comment clarifies the Windows constraint — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PreservesDirectoryPortionAndSanitizesLeaf
Path.Combine equality covers directory preservation and leaf sanitization in one cross-platform check — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
SanitizesInvalidCharactersInExpandedName
Tight equality assertion verifies asterisk-to-underscore sanitization precisely — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ThrowsWhenTemplateHasNoLeafFileName
ThrowsExactly verifies the precise exception type; explanatory comment identifies the invalid-template scenario — no issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 62.3 AIC · ⌖ 11.9 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 20:24
@Evangelink
Amaury Levé (Evangelink) merged commit 1b4496f into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/artifact-naming-service-only branch July 9, 2026 20:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform - #9783

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only
Jul 9, 2026
Merged

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform#9783
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Exposes file-name templating as a public, injectable core service in Microsoft.Testing.Platform, so it can be consumed outside the existing linked-source ([Embedded] helper) pattern.

Changes

  • IArtifactNamingService (public) — single member string ResolveFileName(string template). Expands the standard placeholders ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and sanitizes the leaf file name while preserving any directory portion of the template.
  • ArtifactNamingService (internal) — implementation that wraps the existing ArtifactNamingHelper templating with process name/id/timestamp resolved from ITestApplicationModuleInfo, IEnvironment and IClock.
  • ArtifactFileNameSanitizer (internal) — a core-internal copy of the invalid-char / reserved-name (CON/PRN/AUX/NUL/COM1-9/LPT1-9/CLOCK$) sanitization from the extension-side ReportFileNameSanitizer. The extension copy has shipped InternalAPI entries and is intentionally left in place; a comment marks the two for future consolidation.
  • Registered as a common service in TestHostBuilder.CommonServices.cs and resolvable via a new public ServiceProviderExtensions.GetArtifactNamingService(this IServiceProvider) accessor.
  • Public/internal API tracking files updated.
  • New ArtifactNamingServiceTests cover placeholder expansion, invalid-char sanitization, directory-portion preservation, and unknown-placeholder passthrough.

Verification

Built Microsoft.Testing.Platform.UnitTests and ran --filter "FullyQualifiedName~ArtifactNamingService" — 4/4 passing.

Note

This is one of two PRs split out from #9780 (the other, #9782, aligns the CTRF report writer overwrite behavior).

The pre-existing RS0051 InternalAPI baseline break from #9774 (BaseSerializer.ReadFields/WriteListPayload missing InternalAPI entries) that previously blocked the build has since been fixed on main; this branch was rebased onto that fix, so the two BaseSerializer entries now appear alongside the new service entries in InternalAPI.Unshipped.txt.

@github-actions

This comment has been minimized.

CopilotAI left a comment

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.

Pull request overview

This PR exposes Microsoft.Testing.Platform's artifact file-name templating as a new public, injectable core service, so that consumers can resolve placeholder templates ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and get a sanitized leaf file name without relying on the existing linked-source ([Embedded] helper) pattern. It is one of two PRs split out from #9780 (the companion #9782 handles CTRF overwrite behavior).

The implementation wraps the existing ArtifactNamingHelper templating and mirrors the extension-side sanitization logic. I verified the service is registered as a common service and resolves correctly by interface (the ServiceProvider matches instances via IsInstanceOfType), the constructor dependencies (systemEnvironment, systemClock, _testApplicationModuleInfo) are all in scope at the registration site, IEnvironment.ProcessId/IClock.UtcNow/ArgumentGuard.Ensure all exist as used, and the new internal/public API tracking entries are correctly formatted and placed (InternalAPI tracking is in use in this project — a prior memory to the contrary was outdated and has been down-voted).

Changes:

  • Adds public IArtifactNamingService + ServiceProviderExtensions.GetArtifactNamingService accessor, backed by internal ArtifactNamingService.
  • Adds a core-internal ArtifactFileNameSanitizer copy of the extension-side ReportFileNameSanitizer (intentional duplication, documented for future consolidation).
  • Registers the service in TestHostBuilder.CommonServices.cs, updates Public/Internal API baselines, and adds ArtifactNamingServiceTests.
Show a summary per file
FileDescription
Services/IArtifactNamingService.csNew public interface with a single ResolveFileName(string) member and placeholder docs.
Services/ArtifactNamingService.csInternal implementation resolving process name/id/time and sanitizing the leaf name.
Services/ArtifactFileNameSanitizer.csCore-internal copy of the extension invalid-char/reserved-name sanitizer.
Services/ServiceProviderExtensions.csNew public GetArtifactNamingService accessor.
Hosts/TestHostBuilder.CommonServices.csRegisters ArtifactNamingService as a common service.
PublicAPI/PublicAPI.Unshipped.txtDeclares the new public interface + accessor.
InternalAPI/InternalAPI.Unshipped.txtDeclares the new internal types/members.
Services/ArtifactNamingServiceTests.csTests placeholder expansion, invalid-char sanitization, directory preservation, unknown-placeholder passthrough.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actionsgithub-actionsBot left a comment

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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.


Review Summary

Clean design — exposing the file-name templating as a public injectable service is the right move. The interface is minimal (single member), the implementation correctly delegates to the existing ArtifactNamingHelper, and the sanitizer mirrors the shipped extension-side copy byte-for-byte. Service registration and resolution via IsInstanceOfType will work correctly.

Dimension Verdicts

#DimensionVerdict
1Algorithmic Correctness⚠️ Minor edge case (see inline: Path.GetFileName → empty on trailing separator)
2Public API Design✅ Minimal surface, no init, correctly in PublicAPI.Unshipped.txt
3Backward Compatibility✅ Additive only — new interface, new extension method
4Performance✅ No hot-path concerns; per-call GetStandardReplacements is acceptable for a naming utility
5Cross-TFM CorrectnessGeneratedRegex conditional on NET7_0_OR_GREATER with fallback
6Thread Safety✅ No mutable shared state; each call is self-contained
7Error Handling⚠️ See inline: guard at service boundary for empty leaf name
8LocalizationN/A — no user-facing strings added
9IPC ContractN/A
10Security✅ Path sanitization prevents invalid characters; directory portion preserved as-is (callers control the base directory)
11Testability✅ Constructor-injected dependencies, Moq-based tests
12Test Quality✅ Good coverage of core scenarios; minor gap on reserved-name and {asm}/{tfm}/{arch} paths (hard to mock static reflection)
13Code Style⚠️ Minor: missing blank line in CommonServices.cs (see inline)
14Documentation⚠️ XML doc for {asm}/{tfm} should note "unknown" fallback (see inline)
15InternalAPI Tracking✅ Entries match the actual internal surface
16Scope Discipline✅ Single concern
17Simplicity✅ Clean delegation pattern
18Build InfrastructureN/A
19Analyzer/CodeFixN/A
20Extension Points✅ Registered as common service; extensible via DI replacement
21CLI OptionsN/A
22Agentic WorkflowsN/A

CI Status

Linux builds (Debug & Release) are failing. Please verify the failures are unrelated to this change before merging.

Summary

Three inline comments posted — one style nit, one robustness improvement for an edge case, and one documentation accuracy suggestion. None are blocking. Overall this is a well-structured addition to the platform.

@github-actions

Copy link
Copy Markdown
Contributor

🔴 Build Failure Analysis

Status: 7 errors, all RS0051 (undeclared API)
Root cause: Two new BaseSerializer methods from the base branch commit a946ef7 ("Stabilize extension UIDs and add naming governance") are missing from InternalAPI.Unshipped.txt.

Errors (deduplicated — repeated per TFM: net8.0, net9.0, netstandard2.0)

FileLineSymbol
BaseSerializer.cs359ReadFields(Stream, Func<ushort, int, bool>)
BaseSerializer.cs380WriteListPayload<T>(Stream, ushort, T[]?, Action<Stream, T>)

Why this fails

The project tracks internal API surface via InternalAPI.Unshipped.txt (enforced by the RS0051 Roslyn analyzer). The base commit added ReadFields and WriteListPayload<T> as protected static methods to the internal abstract class BaseSerializer but did not add the corresponding entries. This PR inherits that gap.

Cascading failures

Microsoft.Testing.Extensions.CrashDump.csproj and Microsoft.Testing.Extensions.AzureFoundry.csproj failed because they depend on Microsoft.Testing.Platform.csproj, which failed to compile.

Fix

Add the following two lines to src/Platform/Microsoft.Testing.Platform/InternalAPI/InternalAPI.Unshipped.txt:

static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.ReadFields(System.IO.Stream! stream, System.Func<ushort, int, bool>! tryReadField) -> void
static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.WriteListPayload<T>(System.IO.Stream! stream, ushort fieldId, T[]? list, System.Action<System.IO.Stream!, T>! writeItem) -> void

These should be inserted in alphabetical order among the existing entries.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K · [◷]( · )

@github-actionsgithub-actionsBot left a comment

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.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K ·

CopilotAI review requested due to automatic review settings July 9, 2026 17:55
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from 17095c1 to 425820eCompareJuly 9, 2026 17:55

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 5
  • Review effort level: Low

Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/ArtifactNamingService.cs Outdated
Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/IArtifactNamingService.cs Outdated
@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 18:21
CopilotAI review requested due to automatic review settings July 9, 2026 18:26

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings July 9, 2026 19:05
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from d58afa3 to fe9bf60CompareJuly 9, 2026 19:05
…tform
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from fe9bf60 to 3640da3CompareJuly 9, 2026 19:08

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

CopilotAI review requested due to automatic review settings July 9, 2026 19:10

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9783

GradeTestNotes
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ExpandsStandardPlaceholders
Six assertions (StartsWith, Contains, EndsWith, three DoesNotContain) verify placeholder expansion, output format, and absence of raw tokens — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
LeavesUnknownPlaceholderAsIs
Two Contains checks verify {unknown} is preserved and {pname} is expanded — consider AreEqual("{unknown}_MyApp.json", result) for stronger full-output verification.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PrefixesReservedDeviceName
Precise equality assertion on the reserved-device-name edge case; explanatory comment clarifies the Windows constraint — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PreservesDirectoryPortionAndSanitizesLeaf
Path.Combine equality covers directory preservation and leaf sanitization in one cross-platform check — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
SanitizesInvalidCharactersInExpandedName
Tight equality assertion verifies asterisk-to-underscore sanitization precisely — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ThrowsWhenTemplateHasNoLeafFileName
ThrowsExactly verifies the precise exception type; explanatory comment identifies the invalid-template scenario — no issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 62.3 AIC · ⌖ 11.9 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 20:24
@Evangelink
Amaury Levé (Evangelink) merged commit 1b4496f into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/artifact-naming-service-only branch July 9, 2026 20:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform - #9783

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only
Jul 9, 2026
Merged

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform#9783
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Exposes file-name templating as a public, injectable core service in Microsoft.Testing.Platform, so it can be consumed outside the existing linked-source ([Embedded] helper) pattern.

Changes

  • IArtifactNamingService (public) — single member string ResolveFileName(string template). Expands the standard placeholders ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and sanitizes the leaf file name while preserving any directory portion of the template.
  • ArtifactNamingService (internal) — implementation that wraps the existing ArtifactNamingHelper templating with process name/id/timestamp resolved from ITestApplicationModuleInfo, IEnvironment and IClock.
  • ArtifactFileNameSanitizer (internal) — a core-internal copy of the invalid-char / reserved-name (CON/PRN/AUX/NUL/COM1-9/LPT1-9/CLOCK$) sanitization from the extension-side ReportFileNameSanitizer. The extension copy has shipped InternalAPI entries and is intentionally left in place; a comment marks the two for future consolidation.
  • Registered as a common service in TestHostBuilder.CommonServices.cs and resolvable via a new public ServiceProviderExtensions.GetArtifactNamingService(this IServiceProvider) accessor.
  • Public/internal API tracking files updated.
  • New ArtifactNamingServiceTests cover placeholder expansion, invalid-char sanitization, directory-portion preservation, and unknown-placeholder passthrough.

Verification

Built Microsoft.Testing.Platform.UnitTests and ran --filter "FullyQualifiedName~ArtifactNamingService" — 4/4 passing.

Note

This is one of two PRs split out from #9780 (the other, #9782, aligns the CTRF report writer overwrite behavior).

The pre-existing RS0051 InternalAPI baseline break from #9774 (BaseSerializer.ReadFields/WriteListPayload missing InternalAPI entries) that previously blocked the build has since been fixed on main; this branch was rebased onto that fix, so the two BaseSerializer entries now appear alongside the new service entries in InternalAPI.Unshipped.txt.

@github-actions

This comment has been minimized.

CopilotAI left a comment

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.

Pull request overview

This PR exposes Microsoft.Testing.Platform's artifact file-name templating as a new public, injectable core service, so that consumers can resolve placeholder templates ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and get a sanitized leaf file name without relying on the existing linked-source ([Embedded] helper) pattern. It is one of two PRs split out from #9780 (the companion #9782 handles CTRF overwrite behavior).

The implementation wraps the existing ArtifactNamingHelper templating and mirrors the extension-side sanitization logic. I verified the service is registered as a common service and resolves correctly by interface (the ServiceProvider matches instances via IsInstanceOfType), the constructor dependencies (systemEnvironment, systemClock, _testApplicationModuleInfo) are all in scope at the registration site, IEnvironment.ProcessId/IClock.UtcNow/ArgumentGuard.Ensure all exist as used, and the new internal/public API tracking entries are correctly formatted and placed (InternalAPI tracking is in use in this project — a prior memory to the contrary was outdated and has been down-voted).

Changes:

  • Adds public IArtifactNamingService + ServiceProviderExtensions.GetArtifactNamingService accessor, backed by internal ArtifactNamingService.
  • Adds a core-internal ArtifactFileNameSanitizer copy of the extension-side ReportFileNameSanitizer (intentional duplication, documented for future consolidation).
  • Registers the service in TestHostBuilder.CommonServices.cs, updates Public/Internal API baselines, and adds ArtifactNamingServiceTests.
Show a summary per file
FileDescription
Services/IArtifactNamingService.csNew public interface with a single ResolveFileName(string) member and placeholder docs.
Services/ArtifactNamingService.csInternal implementation resolving process name/id/time and sanitizing the leaf name.
Services/ArtifactFileNameSanitizer.csCore-internal copy of the extension invalid-char/reserved-name sanitizer.
Services/ServiceProviderExtensions.csNew public GetArtifactNamingService accessor.
Hosts/TestHostBuilder.CommonServices.csRegisters ArtifactNamingService as a common service.
PublicAPI/PublicAPI.Unshipped.txtDeclares the new public interface + accessor.
InternalAPI/InternalAPI.Unshipped.txtDeclares the new internal types/members.
Services/ArtifactNamingServiceTests.csTests placeholder expansion, invalid-char sanitization, directory preservation, unknown-placeholder passthrough.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actionsgithub-actionsBot left a comment

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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.


Review Summary

Clean design — exposing the file-name templating as a public injectable service is the right move. The interface is minimal (single member), the implementation correctly delegates to the existing ArtifactNamingHelper, and the sanitizer mirrors the shipped extension-side copy byte-for-byte. Service registration and resolution via IsInstanceOfType will work correctly.

Dimension Verdicts

#DimensionVerdict
1Algorithmic Correctness⚠️ Minor edge case (see inline: Path.GetFileName → empty on trailing separator)
2Public API Design✅ Minimal surface, no init, correctly in PublicAPI.Unshipped.txt
3Backward Compatibility✅ Additive only — new interface, new extension method
4Performance✅ No hot-path concerns; per-call GetStandardReplacements is acceptable for a naming utility
5Cross-TFM CorrectnessGeneratedRegex conditional on NET7_0_OR_GREATER with fallback
6Thread Safety✅ No mutable shared state; each call is self-contained
7Error Handling⚠️ See inline: guard at service boundary for empty leaf name
8LocalizationN/A — no user-facing strings added
9IPC ContractN/A
10Security✅ Path sanitization prevents invalid characters; directory portion preserved as-is (callers control the base directory)
11Testability✅ Constructor-injected dependencies, Moq-based tests
12Test Quality✅ Good coverage of core scenarios; minor gap on reserved-name and {asm}/{tfm}/{arch} paths (hard to mock static reflection)
13Code Style⚠️ Minor: missing blank line in CommonServices.cs (see inline)
14Documentation⚠️ XML doc for {asm}/{tfm} should note "unknown" fallback (see inline)
15InternalAPI Tracking✅ Entries match the actual internal surface
16Scope Discipline✅ Single concern
17Simplicity✅ Clean delegation pattern
18Build InfrastructureN/A
19Analyzer/CodeFixN/A
20Extension Points✅ Registered as common service; extensible via DI replacement
21CLI OptionsN/A
22Agentic WorkflowsN/A

CI Status

Linux builds (Debug & Release) are failing. Please verify the failures are unrelated to this change before merging.

Summary

Three inline comments posted — one style nit, one robustness improvement for an edge case, and one documentation accuracy suggestion. None are blocking. Overall this is a well-structured addition to the platform.

@github-actions

Copy link
Copy Markdown
Contributor

🔴 Build Failure Analysis

Status: 7 errors, all RS0051 (undeclared API)
Root cause: Two new BaseSerializer methods from the base branch commit a946ef7 ("Stabilize extension UIDs and add naming governance") are missing from InternalAPI.Unshipped.txt.

Errors (deduplicated — repeated per TFM: net8.0, net9.0, netstandard2.0)

FileLineSymbol
BaseSerializer.cs359ReadFields(Stream, Func<ushort, int, bool>)
BaseSerializer.cs380WriteListPayload<T>(Stream, ushort, T[]?, Action<Stream, T>)

Why this fails

The project tracks internal API surface via InternalAPI.Unshipped.txt (enforced by the RS0051 Roslyn analyzer). The base commit added ReadFields and WriteListPayload<T> as protected static methods to the internal abstract class BaseSerializer but did not add the corresponding entries. This PR inherits that gap.

Cascading failures

Microsoft.Testing.Extensions.CrashDump.csproj and Microsoft.Testing.Extensions.AzureFoundry.csproj failed because they depend on Microsoft.Testing.Platform.csproj, which failed to compile.

Fix

Add the following two lines to src/Platform/Microsoft.Testing.Platform/InternalAPI/InternalAPI.Unshipped.txt:

static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.ReadFields(System.IO.Stream! stream, System.Func<ushort, int, bool>! tryReadField) -> void
static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.WriteListPayload<T>(System.IO.Stream! stream, ushort fieldId, T[]? list, System.Action<System.IO.Stream!, T>! writeItem) -> void

These should be inserted in alphabetical order among the existing entries.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K · [◷]( · )

@github-actionsgithub-actionsBot left a comment

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.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K ·

CopilotAI review requested due to automatic review settings July 9, 2026 17:55
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from 17095c1 to 425820eCompareJuly 9, 2026 17:55

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 5
  • Review effort level: Low

Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/ArtifactNamingService.cs Outdated
Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/IArtifactNamingService.cs Outdated
@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 18:21
CopilotAI review requested due to automatic review settings July 9, 2026 18:26

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings July 9, 2026 19:05
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from d58afa3 to fe9bf60CompareJuly 9, 2026 19:05
…tform
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from fe9bf60 to 3640da3CompareJuly 9, 2026 19:08

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

CopilotAI review requested due to automatic review settings July 9, 2026 19:10

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9783

GradeTestNotes
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ExpandsStandardPlaceholders
Six assertions (StartsWith, Contains, EndsWith, three DoesNotContain) verify placeholder expansion, output format, and absence of raw tokens — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
LeavesUnknownPlaceholderAsIs
Two Contains checks verify {unknown} is preserved and {pname} is expanded — consider AreEqual("{unknown}_MyApp.json", result) for stronger full-output verification.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PrefixesReservedDeviceName
Precise equality assertion on the reserved-device-name edge case; explanatory comment clarifies the Windows constraint — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PreservesDirectoryPortionAndSanitizesLeaf
Path.Combine equality covers directory preservation and leaf sanitization in one cross-platform check — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
SanitizesInvalidCharactersInExpandedName
Tight equality assertion verifies asterisk-to-underscore sanitization precisely — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ThrowsWhenTemplateHasNoLeafFileName
ThrowsExactly verifies the precise exception type; explanatory comment identifies the invalid-template scenario — no issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 62.3 AIC · ⌖ 11.9 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 20:24
@Evangelink
Amaury Levé (Evangelink) merged commit 1b4496f into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/artifact-naming-service-only branch July 9, 2026 20:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform - #9783

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only
Jul 9, 2026
Merged

Add public injectable IArtifactNamingService to Microsoft.Testing.Platform#9783
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/artifact-naming-service-only

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Exposes file-name templating as a public, injectable core service in Microsoft.Testing.Platform, so it can be consumed outside the existing linked-source ([Embedded] helper) pattern.

Changes

  • IArtifactNamingService (public) — single member string ResolveFileName(string template). Expands the standard placeholders ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and sanitizes the leaf file name while preserving any directory portion of the template.
  • ArtifactNamingService (internal) — implementation that wraps the existing ArtifactNamingHelper templating with process name/id/timestamp resolved from ITestApplicationModuleInfo, IEnvironment and IClock.
  • ArtifactFileNameSanitizer (internal) — a core-internal copy of the invalid-char / reserved-name (CON/PRN/AUX/NUL/COM1-9/LPT1-9/CLOCK$) sanitization from the extension-side ReportFileNameSanitizer. The extension copy has shipped InternalAPI entries and is intentionally left in place; a comment marks the two for future consolidation.
  • Registered as a common service in TestHostBuilder.CommonServices.cs and resolvable via a new public ServiceProviderExtensions.GetArtifactNamingService(this IServiceProvider) accessor.
  • Public/internal API tracking files updated.
  • New ArtifactNamingServiceTests cover placeholder expansion, invalid-char sanitization, directory-portion preservation, and unknown-placeholder passthrough.

Verification

Built Microsoft.Testing.Platform.UnitTests and ran --filter "FullyQualifiedName~ArtifactNamingService" — 4/4 passing.

Note

This is one of two PRs split out from #9780 (the other, #9782, aligns the CTRF report writer overwrite behavior).

The pre-existing RS0051 InternalAPI baseline break from #9774 (BaseSerializer.ReadFields/WriteListPayload missing InternalAPI entries) that previously blocked the build has since been fixed on main; this branch was rebased onto that fix, so the two BaseSerializer entries now appear alongside the new service entries in InternalAPI.Unshipped.txt.

@github-actions

This comment has been minimized.

CopilotAI left a comment

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.

Pull request overview

This PR exposes Microsoft.Testing.Platform's artifact file-name templating as a new public, injectable core service, so that consumers can resolve placeholder templates ({pname}, {pid}, {asm}, {tfm}, {arch}, {time}) and get a sanitized leaf file name without relying on the existing linked-source ([Embedded] helper) pattern. It is one of two PRs split out from #9780 (the companion #9782 handles CTRF overwrite behavior).

The implementation wraps the existing ArtifactNamingHelper templating and mirrors the extension-side sanitization logic. I verified the service is registered as a common service and resolves correctly by interface (the ServiceProvider matches instances via IsInstanceOfType), the constructor dependencies (systemEnvironment, systemClock, _testApplicationModuleInfo) are all in scope at the registration site, IEnvironment.ProcessId/IClock.UtcNow/ArgumentGuard.Ensure all exist as used, and the new internal/public API tracking entries are correctly formatted and placed (InternalAPI tracking is in use in this project — a prior memory to the contrary was outdated and has been down-voted).

Changes:

  • Adds public IArtifactNamingService + ServiceProviderExtensions.GetArtifactNamingService accessor, backed by internal ArtifactNamingService.
  • Adds a core-internal ArtifactFileNameSanitizer copy of the extension-side ReportFileNameSanitizer (intentional duplication, documented for future consolidation).
  • Registers the service in TestHostBuilder.CommonServices.cs, updates Public/Internal API baselines, and adds ArtifactNamingServiceTests.
Show a summary per file
FileDescription
Services/IArtifactNamingService.csNew public interface with a single ResolveFileName(string) member and placeholder docs.
Services/ArtifactNamingService.csInternal implementation resolving process name/id/time and sanitizing the leaf name.
Services/ArtifactFileNameSanitizer.csCore-internal copy of the extension invalid-char/reserved-name sanitizer.
Services/ServiceProviderExtensions.csNew public GetArtifactNamingService accessor.
Hosts/TestHostBuilder.CommonServices.csRegisters ArtifactNamingService as a common service.
PublicAPI/PublicAPI.Unshipped.txtDeclares the new public interface + accessor.
InternalAPI/InternalAPI.Unshipped.txtDeclares the new internal types/members.
Services/ArtifactNamingServiceTests.csTests placeholder expansion, invalid-char sanitization, directory preservation, unknown-placeholder passthrough.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actionsgithub-actionsBot left a comment

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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.


Review Summary

Clean design — exposing the file-name templating as a public injectable service is the right move. The interface is minimal (single member), the implementation correctly delegates to the existing ArtifactNamingHelper, and the sanitizer mirrors the shipped extension-side copy byte-for-byte. Service registration and resolution via IsInstanceOfType will work correctly.

Dimension Verdicts

#DimensionVerdict
1Algorithmic Correctness⚠️ Minor edge case (see inline: Path.GetFileName → empty on trailing separator)
2Public API Design✅ Minimal surface, no init, correctly in PublicAPI.Unshipped.txt
3Backward Compatibility✅ Additive only — new interface, new extension method
4Performance✅ No hot-path concerns; per-call GetStandardReplacements is acceptable for a naming utility
5Cross-TFM CorrectnessGeneratedRegex conditional on NET7_0_OR_GREATER with fallback
6Thread Safety✅ No mutable shared state; each call is self-contained
7Error Handling⚠️ See inline: guard at service boundary for empty leaf name
8LocalizationN/A — no user-facing strings added
9IPC ContractN/A
10Security✅ Path sanitization prevents invalid characters; directory portion preserved as-is (callers control the base directory)
11Testability✅ Constructor-injected dependencies, Moq-based tests
12Test Quality✅ Good coverage of core scenarios; minor gap on reserved-name and {asm}/{tfm}/{arch} paths (hard to mock static reflection)
13Code Style⚠️ Minor: missing blank line in CommonServices.cs (see inline)
14Documentation⚠️ XML doc for {asm}/{tfm} should note "unknown" fallback (see inline)
15InternalAPI Tracking✅ Entries match the actual internal surface
16Scope Discipline✅ Single concern
17Simplicity✅ Clean delegation pattern
18Build InfrastructureN/A
19Analyzer/CodeFixN/A
20Extension Points✅ Registered as common service; extensible via DI replacement
21CLI OptionsN/A
22Agentic WorkflowsN/A

CI Status

Linux builds (Debug & Release) are failing. Please verify the failures are unrelated to this change before merging.

Summary

Three inline comments posted — one style nit, one robustness improvement for an edge case, and one documentation accuracy suggestion. None are blocking. Overall this is a well-structured addition to the platform.

@github-actions

Copy link
Copy Markdown
Contributor

🔴 Build Failure Analysis

Status: 7 errors, all RS0051 (undeclared API)
Root cause: Two new BaseSerializer methods from the base branch commit a946ef7 ("Stabilize extension UIDs and add naming governance") are missing from InternalAPI.Unshipped.txt.

Errors (deduplicated — repeated per TFM: net8.0, net9.0, netstandard2.0)

FileLineSymbol
BaseSerializer.cs359ReadFields(Stream, Func<ushort, int, bool>)
BaseSerializer.cs380WriteListPayload<T>(Stream, ushort, T[]?, Action<Stream, T>)

Why this fails

The project tracks internal API surface via InternalAPI.Unshipped.txt (enforced by the RS0051 Roslyn analyzer). The base commit added ReadFields and WriteListPayload<T> as protected static methods to the internal abstract class BaseSerializer but did not add the corresponding entries. This PR inherits that gap.

Cascading failures

Microsoft.Testing.Extensions.CrashDump.csproj and Microsoft.Testing.Extensions.AzureFoundry.csproj failed because they depend on Microsoft.Testing.Platform.csproj, which failed to compile.

Fix

Add the following two lines to src/Platform/Microsoft.Testing.Platform/InternalAPI/InternalAPI.Unshipped.txt:

static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.ReadFields(System.IO.Stream! stream, System.Func<ushort, int, bool>! tryReadField) -> void
static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.WriteListPayload<T>(System.IO.Stream! stream, ushort fieldId, T[]? list, System.Action<System.IO.Stream!, T>! writeItem) -> void

These should be inserted in alphabetical order among the existing entries.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K · [◷]( · )

@github-actionsgithub-actionsBot left a comment

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.

🤖 Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · 89.8 AIC · ⌖ 6.41 AIC · ⊞ 7.3K ·

CopilotAI review requested due to automatic review settings July 9, 2026 17:55
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from 17095c1 to 425820eCompareJuly 9, 2026 17:55

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 5
  • Review effort level: Low

Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/ArtifactNamingService.cs Outdated
Comment threadsrc/Platform/Microsoft.Testing.Platform/Services/IArtifactNamingService.cs Outdated
@github-actions

This comment has been minimized.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 9, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 18:21
CopilotAI review requested due to automatic review settings July 9, 2026 18:26

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Medium

@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings July 9, 2026 19:05
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from d58afa3 to fe9bf60CompareJuly 9, 2026 19:05
…tform
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/artifact-naming-service-only branch from fe9bf60 to 3640da3CompareJuly 9, 2026 19:08

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

CopilotAI review requested due to automatic review settings July 9, 2026 19:10

CopilotAI left a comment

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.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9783

GradeTestNotes
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ExpandsStandardPlaceholders
Six assertions (StartsWith, Contains, EndsWith, three DoesNotContain) verify placeholder expansion, output format, and absence of raw tokens — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
LeavesUnknownPlaceholderAsIs
Two Contains checks verify {unknown} is preserved and {pname} is expanded — consider AreEqual("{unknown}_MyApp.json", result) for stronger full-output verification.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PrefixesReservedDeviceName
Precise equality assertion on the reserved-device-name edge case; explanatory comment clarifies the Windows constraint — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
PreservesDirectoryPortionAndSanitizesLeaf
Path.Combine equality covers directory preservation and leaf sanitization in one cross-platform check — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
SanitizesInvalidCharactersInExpandedName
Tight equality assertion verifies asterisk-to-underscore sanitization precisely — no issues found.
A (90–100)new ArtifactNamingServiceTests.
ResolveFileName_
ThrowsWhenTemplateHasNoLeafFileName
ThrowsExactly verifies the precise exception type; explanatory comment identifies the invalid-template scenario — no issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 62.3 AIC · ⌖ 11.9 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 9, 2026 20:24
@Evangelink
Amaury Levé (Evangelink) merged commit 1b4496f into mainJul 9, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/artifact-naming-service-only branch July 9, 2026 20:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101