Repository navigation
[test-improver] Add unit tests for FailedTestHelper.FromFailedTest #11744
Description
Goal and rationale
FailedTestHelper.FromFailedTest (Microsoft.Testing.Platform.MSBuild) builds the MSBuild error-message output shown for a failed test — it picks the file/line location (stack-trace frame → CodeFilePath → target assembly path), chooses the error code (canceled vs failed), and formats either a multiline message (screen output) or a single-line message plus a low-priority message (binlog output, with long-message shortening). This logic directly affects what developers see when a test fails under MSBuild, yet had zero direct unit coverage.
Approach
Added FailedTestHelperTests.cs covering:
- Location resolution: stack-trace frame found,
CodeFilePathfallback (no stack frame location), target-path fallback (neither available). - Canceled vs failed error code selection.
- Multiline-supported path: expected/actual values and stack-trace section included;
lowPriorityMessagestaysnull. - Non-multiline path:
lowPriorityMessagepopulated, single-linemessagehas newlines stripped,placeomitted when absent, and very long messages are shortened per-segment.
Gotcha discovered: FailedTestInfoRequest is a source-linked (<Compile Include>) type compiled into both Microsoft.Testing.Extensions.MSBuild and Microsoft.Testing.Platform.MSBuild. The test project references both assemblies, so referencing the type unaliased caused a CS0433 ambiguous-reference error. Fixed by aliasing the Microsoft.Testing.Platform.MSBuild ProjectReference (Aliases="global,PlatformMSBuild") and using extern alias PlatformMSBuild + a type alias in the new test file to pick the correct copy, without affecting any other existing test file in the project (which still use the reference unaliased via global).
Coverage impact
| Before | After | |
|---|---|---|
FailedTestHelper.FromFailedTest direct tests |
0 | 9 |
Microsoft.Testing.Platform.MSBuild.UnitTests total tests |
139 | 148 |
Trade-offs
Small, focused addition — no production code changes, no new dependencies. The extern alias plumbing adds minor complexity to the csproj/test file, documented with comments at both points.
Reproducibility
./build.sh -restore
export PATH="$PWD/.dotnet:$PATH"
dotnet build test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests.csproj -c Debug
./artifacts/bin/Microsoft.Testing.Platform.MSBuild.UnitTests/Debug/net9.0/Microsoft.Testing.Platform.MSBuild.UnitTestsTest Status
- Build: succeeded, 0 warnings, 0 errors (net8.0 and net9.0).
- Tests:
Microsoft.Testing.Platform.MSBuild.UnitTestsfull suite — net9.0: 148 total, 147 succeeded, 0 failed, 1 skipped (pre-existing, platform-gated); net8.0: identical result. No regressions. dotnet format whitespace --verify-no-changes: clean (only the expected harmless F#-project warning).
Note
This was originally intended as a pull request, but the git push operation failed.
Original error: The process '/usr/bin/git' failed with exit code 128
Workflow Run: View run details and download bundle artifact
The bundle file is available in the agent artifact in the workflow run linked above.
Create the pull request manually
# Download the artifact from the workflow run
gh run download '37241816633' -n agent -D '/tmp/agent-37241816633'
# Resolve the bundle source ref, fetch it into a temporary ref, then create the local branch
bundle_path='/tmp/agent-37241816633/aw-test-assist-msbuild-failed-test-helper-tests.bundle'
temp_ref='refs/bundles/create-pr-test-assist-msbuild-failed-test-helper-tests-140d0f5ca8aa03fd-b424ec96'
target_ref='refs/heads/test-assist/msbuild-failed-test-helper-tests-140d0f5ca8aa03fd'
bundle_source_ref=$(git bundle list-heads "$bundle_path" | awk '$2 ~ /^refs\/heads\// { print $2 }')
if [ -z "$bundle_source_ref" ]; then
bundle_source_ref=$(git bundle list-heads "$bundle_path" | awk '$2 == "HEAD" { print $2 }')
fi
if [ "$(printf '%s\n' "$bundle_source_ref" | sed '/^$/d' | wc -l | tr -d ' ')" != "1" ]; then
echo "Expected exactly one bundle source ref, found: $bundle_source_ref" >&2
exit 1
fi
git fetch "$bundle_path" "${bundle_source_ref}:${temp_ref}"
git update-ref "$target_ref" "$temp_ref"
git checkout 'test-assist/msbuild-failed-test-helper-tests-140d0f5ca8aa03fd'
# Ensure the working tree matches the updated branch
git reset --hard
# Remove the temporary bundle ref
git update-ref -d "$temp_ref"
# Push the branch to the target remote
git push 'origin' 'test-assist/msbuild-failed-test-helper-tests-140d0f5ca8aa03fd'
# Create the pull request
gh pr create --title '[test-improver] Add unit tests for FailedTestHelper.FromFailedTest' --base 'main' --head 'test-assist/msbuild-failed-test-helper-tests-140d0f5ca8aa03fd' --repo 'microsoft/testfx'Warning
Firewall blocked 93 domains
The following domains were blocked by the firewall during workflow execution:
0t3vsblobprodcus362.vsblob.vsassets.io11vvsblobprodcus336.vsblob.vsassets.io1javsblobprodcus364.vsblob.vsassets.io1k9vsblobprodcus379.vsblob.vsassets.io1oavsblobprodcus350.vsblob.vsassets.io1p7vsblobprodcus324.vsblob.vsassets.io1s1vsblobprodcus386.vsblob.vsassets.io2kmvsblobprodcus39.vsblob.vsassets.io2zrvsblobprodcus388.vsblob.vsassets.io37bvsblobprodcus311.vsblob.vsassets.io37cvsblobprodcus359.vsblob.vsassets.io4m6vsblobprodcus384.vsblob.vsassets.io4myvsblobprodcus32.vsblob.vsassets.io4vyvsblobprodcus361.vsblob.vsassets.io4zjvsblobprodcus390.vsblob.vsassets.io51yvsblobprodcus36.vsblob.vsassets.io5dkvsblobprodcus355.vsblob.vsassets.io5rqvsblobprodcus385.vsblob.vsassets.io6s7vsblobprodcus313.vsblob.vsassets.io7devsblobprodcus323.vsblob.vsassets.io7k6vsblobprodcus337.vsblob.vsassets.io7tjvsblobprodcus341.vsblob.vsassets.io80zvsblobprodcus35.vsblob.vsassets.io8xbvsblobprodcus382.vsblob.vsassets.io96bvsblobprodcus338.vsblob.vsassets.ioajhvsblobprodcus363.vsblob.vsassets.iobcnvsblobprodcus378.vsblob.vsassets.iobuilds.dotnet.microsoft.comc50vsblobprodcus330.vsblob.vsassets.ioc78vsblobprodcus322.vsblob.vsassets.iocflvsblobprodcus383.vsblob.vsassets.ioci.dot.netckzvsblobprodcus347.vsblob.vsassets.iod0svsblobprodcus381.vsblob.vsassets.iodata.nuget.orgdlbvsblobprodcus316.vsblob.vsassets.ioe7bvsblobprodcus348.vsblob.vsassets.iofdpvsblobprodcus345.vsblob.vsassets.iofrdvsblobprodcus327.vsblob.vsassets.iogbsvsblobprodcus365.vsblob.vsassets.iogervsblobprodcus329.vsblob.vsassets.ioh6tvsblobprodcus346.vsblob.vsassets.ioi1qvsblobprodcus353.vsblob.vsassets.ioibzvsblobprodcus369.vsblob.vsassets.ioimzvsblobprodcus368.vsblob.vsassets.iojd4vsblobprodcus366.vsblob.vsassets.iojosvsblobprodcus372.vsblob.vsassets.iojrqvsblobprodcus343.vsblob.vsassets.iok0ivsblobprodcus356.vsblob.vsassets.iok4kvsblobprodcus344.vsblob.vsassets.iokgfvsblobprodcus314.vsblob.vsassets.iokh4vsblobprodcus325.vsblob.vsassets.iokijvsblobprodcus387.vsblob.vsassets.iokmuvsblobprodcus389.vsblob.vsassets.iokxqvsblobprodcus376.vsblob.vsassets.iol49vsblobprodcus358.vsblob.vsassets.iol7avsblobprodcus319.vsblob.vsassets.ioljcvsblobprodcus317.vsblob.vsassets.iolylvsblobprodcus31.vsblob.vsassets.iom16vsblobprodcus374.vsblob.vsassets.iom6xvsblobprodcus342.vsblob.vsassets.iom8dvsblobprodcus37.vsblob.vsassets.iomfjvsblobprodcus373.vsblob.vsassets.ion3kvsblobprodcus335.vsblob.vsassets.ionn8vsblobprodcus340.vsblob.vsassets.ioo3svsblobprodcus318.vsblob.vsassets.ioofvvsblobprodcus315.vsblob.vsassets.iop2ovsblobprodcus312.vsblob.vsassets.iopc2vsblobprodcus360.vsblob.vsassets.iopdfvsblobprodcus380.vsblob.vsassets.iope3vsblobprodcus354.vsblob.vsassets.iope4vsblobprodcus351.vsblob.vsassets.iopkgs.dev.azure.compkvvsblobprodcus321.vsblob.vsassets.iorcxvsblobprodcus328.vsblob.vsassets.ios4uvsblobprodcus326.vsblob.vsassets.ios8mvsblobprodcus38.vsblob.vsassets.iose1vsblobprodcus349.vsblob.vsassets.iosqdvsblobprodcus333.vsblob.vsassets.iost8vsblobprodcus339.vsblob.vsassets.iotphvsblobprodcus375.vsblob.vsassets.iou3hvsblobprodcus371.vsblob.vsassets.iou6ovsblobprodcus377.vsblob.vsassets.ioukkvsblobprodcus352.vsblob.vsassets.iouy6vsblobprodcus34.vsblob.vsassets.iov53vsblobprodcus320.vsblob.vsassets.iovb4vsblobprodcus33.vsblob.vsassets.iovwvvsblobprodcus334.vsblob.vsassets.iox3yvsblobprodcus370.vsblob.vsassets.ioxupvsblobprodcus332.vsblob.vsassets.ioyluvsblobprodcus367.vsblob.vsassets.ioyttvsblobprodcus357.vsblob.vsassets.ioytvvsblobprodcus310.vsblob.vsassets.io
To allow these domains, add them to the network.allowed list in your workflow frontmatter:
network:
allowed:
- defaults
- "0t3vsblobprodcus362.vsblob.vsassets.io"
- "11vvsblobprodcus336.vsblob.vsassets.io"
- "1javsblobprodcus364.vsblob.vsassets.io"
- "1k9vsblobprodcus379.vsblob.vsassets.io"
- "1oavsblobprodcus350.vsblob.vsassets.io"
- "1p7vsblobprodcus324.vsblob.vsassets.io"
- "1s1vsblobprodcus386.vsblob.vsassets.io"
- "2kmvsblobprodcus39.vsblob.vsassets.io"
- "2zrvsblobprodcus388.vsblob.vsassets.io"
- "37bvsblobprodcus311.vsblob.vsassets.io"
- "37cvsblobprodcus359.vsblob.vsassets.io"
- "4m6vsblobprodcus384.vsblob.vsassets.io"
- "4myvsblobprodcus32.vsblob.vsassets.io"
- "4vyvsblobprodcus361.vsblob.vsassets.io"
- "4zjvsblobprodcus390.vsblob.vsassets.io"
- "51yvsblobprodcus36.vsblob.vsassets.io"
- "5dkvsblobprodcus355.vsblob.vsassets.io"
- "5rqvsblobprodcus385.vsblob.vsassets.io"
- "6s7vsblobprodcus313.vsblob.vsassets.io"
- "7devsblobprodcus323.vsblob.vsassets.io"
- "7k6vsblobprodcus337.vsblob.vsassets.io"
- "7tjvsblobprodcus341.vsblob.vsassets.io"
- "80zvsblobprodcus35.vsblob.vsassets.io"
- "8xbvsblobprodcus382.vsblob.vsassets.io"
- "96bvsblobprodcus338.vsblob.vsassets.io"
- "ajhvsblobprodcus363.vsblob.vsassets.io"
- "bcnvsblobprodcus378.vsblob.vsassets.io"
- "builds.dotnet.microsoft.com"
- "c50vsblobprodcus330.vsblob.vsassets.io"
- "c78vsblobprodcus322.vsblob.vsassets.io"
- "cflvsblobprodcus383.vsblob.vsassets.io"
- "ci.dot.net"
- "ckzvsblobprodcus347.vsblob.vsassets.io"
- "d0svsblobprodcus381.vsblob.vsassets.io"
- "data.nuget.org"
- "dlbvsblobprodcus316.vsblob.vsassets.io"
- "e7bvsblobprodcus348.vsblob.vsassets.io"
- "fdpvsblobprodcus345.vsblob.vsassets.io"
- "frdvsblobprodcus327.vsblob.vsassets.io"
- "gbsvsblobprodcus365.vsblob.vsassets.io"
- "gervsblobprodcus329.vsblob.vsassets.io"
- "h6tvsblobprodcus346.vsblob.vsassets.io"
- "i1qvsblobprodcus353.vsblob.vsassets.io"
- "ibzvsblobprodcus369.vsblob.vsassets.io"
- "imzvsblobprodcus368.vsblob.vsassets.io"
- "jd4vsblobprodcus366.vsblob.vsassets.io"
- "josvsblobprodcus372.vsblob.vsassets.io"
- "jrqvsblobprodcus343.vsblob.vsassets.io"
- "k0ivsblobprodcus356.vsblob.vsassets.io"
- "k4kvsblobprodcus344.vsblob.vsassets.io"
- "kgfvsblobprodcus314.vsblob.vsassets.io"
- "kh4vsblobprodcus325.vsblob.vsassets.io"
- "kijvsblobprodcus387.vsblob.vsassets.io"
- "kmuvsblobprodcus389.vsblob.vsassets.io"
- "kxqvsblobprodcus376.vsblob.vsassets.io"
- "l49vsblobprodcus358.vsblob.vsassets.io"
- "l7avsblobprodcus319.vsblob.vsassets.io"
- "ljcvsblobprodcus317.vsblob.vsassets.io"
- "lylvsblobprodcus31.vsblob.vsassets.io"
- "m16vsblobprodcus374.vsblob.vsassets.io"
- "m6xvsblobprodcus342.vsblob.vsassets.io"
- "m8dvsblobprodcus37.vsblob.vsassets.io"
- "mfjvsblobprodcus373.vsblob.vsassets.io"
- "n3kvsblobprodcus335.vsblob.vsassets.io"
- "nn8vsblobprodcus340.vsblob.vsassets.io"
- "o3svsblobprodcus318.vsblob.vsassets.io"
- "ofvvsblobprodcus315.vsblob.vsassets.io"
- "p2ovsblobprodcus312.vsblob.vsassets.io"
- "pc2vsblobprodcus360.vsblob.vsassets.io"
- "pdfvsblobprodcus380.vsblob.vsassets.io"
- "pe3vsblobprodcus354.vsblob.vsassets.io"
- "pe4vsblobprodcus351.vsblob.vsassets.io"
- "pkgs.dev.azure.com"
- "pkvvsblobprodcus321.vsblob.vsassets.io"
- "rcxvsblobprodcus328.vsblob.vsassets.io"
- "s4uvsblobprodcus326.vsblob.vsassets.io"
- "s8mvsblobprodcus38.vsblob.vsassets.io"
- "se1vsblobprodcus349.vsblob.vsassets.io"
- "sqdvsblobprodcus333.vsblob.vsassets.io"
- "st8vsblobprodcus339.vsblob.vsassets.io"
- "tphvsblobprodcus375.vsblob.vsassets.io"
- "u3hvsblobprodcus371.vsblob.vsassets.io"
- "u6ovsblobprodcus377.vsblob.vsassets.io"
- "ukkvsblobprodcus352.vsblob.vsassets.io"
- "uy6vsblobprodcus34.vsblob.vsassets.io"
- "v53vsblobprodcus320.vsblob.vsassets.io"
- "vb4vsblobprodcus33.vsblob.vsassets.io"
- "vwvvsblobprodcus334.vsblob.vsassets.io"
- "x3yvsblobprodcus370.vsblob.vsassets.io"
- "xupvsblobprodcus332.vsblob.vsassets.io"
- "yluvsblobprodcus367.vsblob.vsassets.io"
- "yttvsblobprodcus357.vsblob.vsassets.io"
- "ytvvsblobprodcus310.vsblob.vsassets.io"See Network Configuration for more information.
🤖 Automated content by GitHub Copilot. Generated by the Test Improver workflow. · copilot · auto · 540.5 AIC · ⌖ 13.8 AIC · ⊞ 20.5K · [◷]( · ◷)
Comment /test-assist to run again
Add this agentic workflow to your repo
To install this agentic workflow, run
gh aw add githubnext/agentics/workflows/test-improver.md@main
Activity