Skip to content

Optimize formatting for template strings and numbers#2866

Draft
fflaten wants to merge 9 commits into
pester:mainfrom
fflaten:format-templates
Draft

Optimize formatting for template strings and numbers#2866
fflaten wants to merge 9 commits into
pester:mainfrom
fflaten:format-templates

Conversation

@fflaten

@fflaten fflaten commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

PR Summary

Restricts template strings to one level of object recursion in formatting to avoid hangs on large complex objects.

To address the issue above, this PR also:

  • Refactors Format2 to use a decreasing and internally configurable -MaxDepth instead of increasing -Depth with hard limit. Easier to restrict something a max 1 level than start at depth 9.
  • Uses Format-Numbers* for integers and not just decimals. Integers was formatted as ValueType, requring -MaxDepth 2 for formatting simple arrays.
    • Same applies to [char] vs [string] etc, but not fixed for now.

Fix #2865

PR Checklist

  • PR has meaningful title
  • Summary describes changes
  • PR is ready to be merged
    • If not, use the arrow next to Create Pull Request to mark it as a draft. PR can be marked Ready for review when it's ready.
  • Tests are added/update (if required)
  • Documentation is updated/added (if required)

Comment thread src/Format2.ps1 Outdated
Comment on lines +180 to +181
# Limit recursion to simple arrays and hashtable to avoid hangs and complex test names/paths.
Format-Nicely2 -Value $Value -MaxDepth 1

@fflaten fflaten Jul 13, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This will still print a massive multi-line name for something like (Get-Command Invoke-Pester), but at least it doesn't hang. Questions:

  • Should we special case CommandInfo to just return the name like v5? Or let it be so the author will identify a bad practice (not referencing the name property) and fix it.
  • Should tests names expand arrays/dictionaries etc in general?
  • Should tests names allow multi-line at all? Messy console output. Trim or run through [Pester.Formatter]::EscapeControlChars()?

Comment thread src/TypeClass.ps1 Outdated
@fflaten
fflaten force-pushed the format-templates branch from 8e5120e to b224e8e Compare July 14, 2026 09:47
Comment thread src/TypeClass.ps1

function Is-DecimalNumber ($Value) {
$Value -is [float] -or $Value -is [single] -or $Value -is [double] -or $Value -is [decimal]
$Value -is [Single] -or $Value -is [Double] -or $Value -is [Decimal]

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Just cosmetic to get consistent naming in code and tests <Type.Name> for both PS 5.1 and 7+

@fflaten
fflaten force-pushed the format-templates branch from b224e8e to a510080 Compare July 14, 2026 10:06
@fflaten
fflaten requested a review from nohwnd July 14, 2026 10:21
Building on the -MaxDepth budget in this branch, format registered "noisy"
types as a short, representative summary instead of walking their whole property
tree. A CommandInfo referenced whole in a test name (for example <cmd> instead
of <cmd.Name>) otherwise expands into an enormous, deeply nested dump that is so
slow to build it looks like Pester has hung (pester#2865).

Extend Get-DisplayProperty2 to match by base type (so a single entry covers every
subtype, e.g. CommandInfo covers FunctionInfo, CmdletInfo, AliasInfo,
ExternalScriptInfo) and register CommandInfo -> Name. Format-Nicely2 now consults
the registry before expanding an object, so a registered type renders a compact
summary such as FunctionInfo{Name='Invoke-Pester'} in both detailed assertion
messages and shallow test-name templates. Any other complex object referenced
whole in a name collapses to just its type, so an unknown object can never
explode a test name. To tame a new noisy type, add one line to the registry.

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

nohwnd commented Jul 15, 2026

Copy link
Copy Markdown
Member

Hey @fflaten 👋 I pushed a commit onto this branch to iterate on it — hope that's ok, happy to revert if you'd rather take it a different way.

Your -MaxDepth refactor + integer fix are exactly the right foundation, so I built on top rather than starting over. The one thing left over was that a complex object referenced whole in a name (e.g. <cmd> instead of <cmd.Name>, where cmd is a CommandInfo) still expanded one full level — for a CommandInfo that's the entire Definition and ends up ~80k chars, so the name is huge even though it no longer hangs.

The commit finishes the type-map idea that was already sketched in the Get-DisplayProperty2 comment: match by base type (one CommandInfo entry covers FunctionInfo/CmdletInfo/AliasInfo/ExternalScriptInfo/…) and register CommandInfo -> Name. Format-Nicely2 consults the registry before expanding an object, so a registered type renders a compact summary in both assertion messages and test-name templates:

<cmd>  ->  Management.Automation.FunctionInfo{Name='Invoke-Pester'}

Anything not in the registry keeps today's behaviour (full expansion in assertion messages), and in a name it just collapses to [SomeType] so an unknown object can never blow up a test name. Taming a new noisy type is then a one-line registry entry — that's the "least maintenance" part.

Added tests for the base-type match, the compact summary, and an RSpec acceptance test for the #2865 repro. Clean build is green; only the unrelated pre-existing SkipRemainingOnFailureCount failures remain. Let me know what you think!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Name expansion hangs on complex objects like CommandInfo

2 participants