Repository navigation
Fixes #3074 - #3080
Fixes #3074#3080
Conversation
…g them The project-file stage of CrossAppGuardCiGateTests evaluated MSBuild by hand: attribute and element spellings of HintPath, Condition-attribute skipping, item expression forms, two resolution bases, and a nearest-wins Directory.Build probe. Each was correct within its bound and each fix exposed the next construct, because the stage was re-implementing a language. dotnet msbuild -getItem -getProperty answers the same question fully evaluated - properties expanded, imports followed, Conditions applied, wildcards expanded, Directory.Build probing performed by MSBuild - offline, building nothing, in about a third of a second per project. The shell-out is floored in the three places it can return nothing without having looked, and the item-type list is floored per type against a synthetic two-SKU fixture because an unrecognised item type answers with an empty array and exit 0. An <Import>'s own path is the one cross-app read the evaluated answer does not carry, so a project that grows one fails loudly rather than being half-scanned. Also drops the stale tool counts from CrossAppMcpToolInventoryPinTests' summary: they narrated why the pin exists rather than asserting anything, and the figures move with every tool added.
…f them There is one project file under each test project today, so taking projectFiles[0] answered correctly - and would have gone on answering about whichever sorted first the moment a second appeared, saying nothing at all about the other. The floors are read per evaluation, and the population pin locates the manifest's own evaluation by name rather than by position. Also drops CrossAppItems' unused project-directory parameter: HintPath resolves against the directory of the file that DECLARED the item, which the evaluated answer carries itself, so the caller's directory was never consulted.
|
Reviewed. This is test-infra-only ( Went through the shell-out path specifically for the things that matter for a CI gate like this:
No correctness, security, or performance concerns beyond that nit. Good test coverage of the failure modes that would matter most for a CI gate (silent-empty-answer scenarios). |
…here MSBuild does Measured, and the two candidate bases diverge: a Reference declared in a Directory.Build.props one directory up, whose relative HintPath names a file beside THAT props file, does not resolve; the same HintPath naming a file beside the consuming project resolves to it. So the base is the project directory, taken from MSBuildProjectFullPath, not the directory of the file that declared the item. The fixture could not tell the two apart - it declared the Reference in the project itself, where both bases are the same directory. Its imported props file now sits ABOVE the project and carries the Reference, so both the relative Include and the relative HintPath resolve out of the fixture tree under the wrong rule.
System.Security was for an XML-escape helper the fixture does not need: it writes its cross-app paths as a relative MSBuild property rather than interpolating an absolute repo path, so there is nothing to escape.
…n out of them The per-type floor exists to prove an item type NAME is real, since a type MSBuild does not recognise returns an empty array with exit 0 - so the evidence is the array length, not how many paths the extraction found in it. The two diverge: one item can yield two paths, a Reference with a HintPath being exactly that, so a derived count read as two Reference items where the fixture declares one. What the extraction found is asserted separately, by what lands in the found set.
|
Reviewed. This is a C#-only test-infrastructure change (no T-SQL, no product code), and the single touched class ( I traced through the new MSBuild-evaluation logic ( One real (if minor) defect:
Didn't flag anything else — no missing-index-style DMV concerns apply here (no T-SQL touched), no secrets/network/file-write boundary issues beyond the already-reviewed shell-out, and I didn't spot a performance regression worth calling out given the PR's own measured ~0.3s/project evaluation cost and the deliberate tradeoff discussion in the description. |
CSharpPaths' summary pointed at MsBuildPaths for the reason it is shared between the walk and the pin that reads it back. That method is gone with the XML parse it belonged to, so the sentence states the reason directly instead. Nothing in the build catches this: GenerateDocumentationFile is off for the project, so the compiler never binds a cref. Building it with documentation ON reports 22 dangling crefs across the compilation, none of them in the files this change touches.
…d-msbuild-getitem
|
Reviewed. This is C# test infrastructure only ( The replacement of the hand-rolled MSBuild XML/regex parser with One inline finding: the timeout/kill path in No missing-index DMV concerns (none present), no secrets/credentials, no network calls introduced. |
Kill(entireProcessTree: true) documents four exceptions, not one. The catch took InvalidOperationException alone, so a remote process, an OS refusal, or a descendant that could not be killed would have replaced "dotnet did not exit within 2 minutes" with a kill-failure stack trace, on the path that is hardest to read from a CI log. The first three match the pattern DarlingDeployRollbackRetentionTests and DarlingDeployStaleFileTests already use for this race; AggregateException is the one Kill's process-tree overload documents for itself. The comment on that catch claimed the exit code below is still read. Nothing below reads it - the branch returns -1 - and the message now says no output was collected rather than leaving an empty stdout to be read as an answer. The deadline was also the only one of the four silent-empty modes with no pin, since the shipped value cannot be waited out in a test. RunDotnet takes it as a parameter defaulting to the shipped value, and the loud-failure pin drives the same probe project - which answers in about a third of a second - against a one-millisecond deadline.
…d-msbuild-getitem # Conflicts: # Lite.Tests/CrossAppGuardCiGateTests.cs
|
Reviewed the Overall this is a solid, carefully-verified rewrite: the process shelling-out avoids shell injection (uses One inline finding: in No security, correctness, or performance issues beyond that. |
The Properties loop called GetString() unguarded while the item metadata reader checks ValueKind first. GetString() raises on a non-string rather than returning null, and an exception out of ParseEvaluation bypasses the Evaluation.Failure floor every caller asserts on - which is the one contract this method has. Reported rather than coerced to the empty string, which is what the metadata reader does: empty is MSBuild's own answer for "there is no Directory.Build.props", so flattening an unexpected shape into one would manufacture that answer instead of admitting the read did not happen. Pinned alongside the other malformed-output shapes, together with an answer missing a property that was asked for - a different failure, and one that would otherwise leave the build-file paths reading as "there are none".
|
Reviewed the full diff (both files) against Parity: No Darling-side counterpart exists for this guard (it's a single Lite.Tests file that scans both Correctness: Traced the Also checked the process-handling in Performance: Each evaluation shells out to No blocking findings. This is a solid, well-verified swap from a hand-rolled XML regex evaluator to real MSBuild evaluation, with a sensible superset/second-opinion check guarding against the class of gap the switch could otherwise introduce. |
Closes #3074.
The project-file stage of
CrossAppGuardCiGateTestsevaluated MSBuild by hand. #3065 fixed seven correctness findings in it during review, all of them real, and the next constructs were already visible —Condition=on an item group,Choose/When, imported props that define the item being read. This replaces the stage withdotnet msbuild -getItem -getProperty, which answers the same question fully evaluated.The
.csstring-literal half is unchanged. This replaces the project-file half only.The invocation, re-measured on this branch
dotnet msbuild <project> -getItem:Compile;None;Content;EmbeddedResource;Reference;ProjectReference -getProperty:MSBuildProjectFullPath;DirectoryBuildPropsPath;DirectoryBuildTargetsPath;DirectoryPackagesPropsPath -p:EnableWindowsTargeting=trueMeasured on a fresh worktree of
origin/devthat had never been built, sandboxed, no network:Lite.Tests), 0.34 s (Darling.Tests)obj/createdItemsandPropertiesin one invocation-getItemreturns{"Properties":{…},"Items":{…}}Two corrections to the issue's own measurements, both verified here:
-p:EnableWindowsTargeting=trueis not required for evaluation.dotnet msbuild -getItemonLite.Tests(net10.0-windows,UseWPF) exits 0 on macOS without it; only a build needs it. It is passed anyway — it is a no-op on the Windows runner and removes a host-dependent difference from a guard that has to answer identically in CI and on a macOS verification run.-preprocessand-getItemdo not combine (-getItemwins silently), so the import list is taken from-getPropertyinstead. That turned out better: see theDirectory.Packages.propsfinding below.The superset proof
Run both routes over the real tree. The project-item stage's contribution to the found set, before and after:
-getItem)Lite.Tests→ DarlingDarling/Darling.Tests/CSharpSourceWalker.csDarling/Darling.Tests/CSharpSourceWalker.csDarling.Tests→ LiteDarling.Tests.csprojnames no file underLiteorLite.Testsat all, so its cross-app project answer is legitimately empty on both sides — which is why the superset pin can only floor that arm on its own-tree answer.And the whole found set, both stages together, is identical. Measured by compiling
origin/dev's version of the file and this branch's version into two harnesses side by side, driving the shippedScanthrough reflection in each, and diffing the results — not by eyeballing a tally:comm -23(in before, not after — a regression if non-empty): emptycomm -13(in after, not before): emptyThe only difference anywhere is the attribution string: the linked compile now reads
Lite.Tests/Lite.Tests.csproj (Compile FullPath)instead of a bare file path, because origin comes from MSBuild'sDefiningProjectFullPath.Nothing is missed, and the comparison is a live assertion rather than a one-time measurement —
TheEvaluatedSet_ContainsEverythingAParseOfTheSameXmlFindskeeps a deliberately crude XML parse as a second opinion and requires the evaluated set to contain it.The one thing the evaluated read does not report, and what happens instead
An
<Import>'s own path. Items an imported file defines arrive with everything else — proven in the fixture — but the imported file's path is not an item and no-getPropertyanswer carries it.-getPropertycovers the build files MSBuild locates itself; an arbitrary hand-written<Import Project="..\Darling\Shared.targets" />is not among them.There is no
<Import>element in any MSBuild file in this repository, and the old route's handling of it was pinned entirely against synthetic XML for that reason. Enumerated rather than tallied: every one of the 24 tracked files matching*.csproj,*.props,*.targets,*.proj,*.sln,*.projitemsor*.shprojwas grepped individually and printed — zero occurrences of<Import, and zero for a case- and whitespace-insensitive<\s*[Ii]mportas well. The 22 hits forSdk=are the SDK-resolution construct, which resolves through a resolver rather than to a path and is deliberately not what the gate matches — asserted as a negative in the gate's known-answers pin. So this is a gate rather than an evaluator:Checkfails loudly if a scanned project — or a build file MSBuild says it imported — grows one. Three lines, versus a rule per construct.A gap the old route had that this closes
Directory.Packages.propsis imported into every project here, and it is a plain MSBuild file: anItemGroupin it becomes an item group of every project. The old route probed upward for exactly two names,Directory.Build.propsandDirectory.Build.targets, so it would never have opened it — and itsImportedBuildProbesfloor asserted it had looked "in the places MSBuild would probe" while omitting one MSBuild does probe.-getProperty:DirectoryPackagesPropsPathreturns it, and any item it defines is in the-getItemanswer regardless.How the shell-out fails loudly
A guard that reports "no cross-app references" because a process never started is worse than the regex it replaced. Four ways of producing an empty answer without looking, each floored:
RunDotnetcatchesWin32Exceptionand returns exit-1with the OS message. (DOTNET_HOST_PATHis not set for a process launched bydotnet run, which is how this suite runs, sodotnetis launched by name fromPATH— measured, not assumed.)Evaluation.ExitCode, asserted on its own.ParseEvaluationis strict — a banner, an error summary, an empty stream or a JSON object with noItemsis a failure, not zero items — and returns the reason asEvaluation.Failure, asserted separately from the exit code.-getItem:Compile;Bogusexits 0 with"Bogus": []. Measured. Key presence proves nothing, so the item-type list is floored per type — and it cannot be floored against this repository, whereContent,EmbeddedResourceandReferenceare all legitimately empty.The per-type count comes from MSBuild's own array lengths, not from the paths the extraction pulled out of them, because those answer different questions: the count is evidence the type name is real, and what the extraction found is asserted separately by the found set. They also genuinely diverge — one item can yield two paths, a
Referencecarrying aHintPathbeing exactly that, so a derived count read as twoReferenceitems where the fixture declares one. Mutations 1 and 2 now fail on the two claims independently: a typo in the list showsReferences=0in the counts, while a brokenHintPathread leavesReference=1and the path missing from the found set.AssertInvocationSucceededcarries (2), (3) and a distinctItems.Count > 0floor, and is read fromCheckas well as from the pins so the two cannot drift. A hang is bounded by a two-minute deadline withKill(entireProcessTree: true); both streams are drained concurrently, because reading one to completion first deadlocks on a half-megabyte answer.The synthetic fixture, which is where
HintPathand the item-type list are provenThere is no
Referenceitem anywhere in this repository, so theHintPathroute to a cross-app assembly has no live example — nor is there aCondition, aChoose, an imported props file, a transform, or a cross-app wildcard.WriteCrossAppFixturewrites a miniature two-SKU tree to a temp directory (consuming project under one SKU's test tree, targets under the other SKU's app tree and its sibling test tree, so #3067's anchor is exercised on this population too) carrying one item of every requested type and one instance of every construct:$(Property)in anInclude=FromProperty.csfoundHintPath, written relatively, on aReferenceHinted.dllfoundCondition=on anItemGroupConditionTrue.csfoundConditionFalse.csabsentChoose/When/OtherwiseChosenWhen.csfoundChosenOtherwise.csabsent.propsFromImport.csfound, attributed toImported.props@(Item->'…')transformTransformed.csfoundGlobbed.xmlfoundNotGlobbed.txtabsentDarlingExtra/Nope.csabsentEach construct is asserted in both directions where it has two, so a fixture that failed to write its tree cannot pass by finding nothing.
Imported.propssits above the consuming project rather than beside it, and carries both theReferenceand one of theCompileitems. That is load-bearing, and it is a defect this PR found in itself:Getting that backwards is a silent miss in exactly this guard's direction: the path resolves to something real-looking, fails the cross-app anchor, and vanishes.
One correction for anyone reading the automated review summary alongside this. It states that
HintPath"now resolves againstDefiningProjectFullPath's directory rather than the consuming project's directory — correct per MSBuild's own resolution rules for metadata declared in an imported file." That is backwards on both counts: it is not what the code does, and it is not MSBuild's rule.ParseEvaluationresolves againstPath.GetDirectoryName(Properties["MSBuildProjectFullPath"]);DefiningProjectFullPathis used only for attribution and never for resolution. The measurement in the table above is what settled it, and mutation 13 is the standing guard — swapping the base back reds the fixture pin. Flagging it here rather than in a comment because the summary is otherwise accurate and this is the one line in it that, if believed, would send someone to reintroduce the defect.Anti-vacuity floors
Check:CSharpFiles > 0,ProjectFiles.Count > 0,Evaluatednon-empty,AssertInvocationSucceededper evaluation,UnreportedImportsempty, and CrossAppGuardCiGateTests anchors on Lite/, so the two Darling.Tests reads of Lite.Tests/ are invisible to it #3067's existing floor on the other SKU's test tree.Compile > 0andProjectReference > 0— the two types a test project cannot honestly be empty of;MSBuildProjectFullPathequal to the project that was asked, which is how "MSBuild found noDirectory.Build.props" is distinguished from "no properties came back"; and the linked compile required by name with its attribution.Check's emptiness assertion holds on this tree whether or not the detector works.Every project file under a test tree is evaluated, not
projectFiles[0]— there is one today, and taking the first would have gone on answering about whichever sorted first the moment a second appeared.Red-proof evidence
Every mutation was applied to a committed tree, rebuilt with the build result checked separately from the run, and restored by
git checkout --with the restore verified by grepping for aMUTANT_MARKERtoken rather than by trusting a diffstat. Every variant compiled (0 Error(s)+Build succeeded) and every one changed the reported outcome, so none is a variant that never ran.And the baseline was re-run LAST, after all seventeen: empty
git status, emptygit diff HEAD, noMUTANT_MARKERanywhere in the worktree, and both harnesses green. A clean baseline beforehand only proves the tree was clean then; this is the one that proves nothing leaked between cases.EvaluatedItemTypes:Reference→Referencesthe fixture declares one References item and MSBuild returned none of that type … Counts: [Compile=5, Content=1, EmbeddedResource=1, None=2, ProjectReference=1, References=0]HintPathmetadata readAssert.Containsfailed onDarling/PerformanceMonitor.Darling.Service/Hinted.dll, withReference=1still in the counts — the extraction claim failing while the type-name claim holdsFixtureGateflipped tonoAssert.DoesNotContainfoundConditionFalse.cs, and the reported set showedChosenOtherwise.cshad also appeared — both condition arms are liveEvaluateswallows a non-zero exit and reports successAssert.NotEqualon the exit code — nothing on the real tree catches this, which is why the pin existsParseEvaluationreturns zero items with no failureEveryCrossAppSourceRead_…the evaluated project read returned no items at all, which no project doesCrossAppItemssilently skipsCompileitemsSet: [],Not found: Darling/Darling.Tests/CSharpSourceWalker.cs; population pin named the missing attribution<Compile Include="..\Darling\…\CSharpSourceWalkerTests.cs" Condition="false" />toLite.Tests.csprojdoes not contain what a parse of the same XML sees: Darling/Darling.Tests/CSharpSourceWalkerTests.cs … Evaluated: [Darling/Darling.Tests/CSharpSourceWalker.cs]— proves the comparison itself, not just its floorParsedProjectXmlPathsyields nothingthe XML parse found no path under Lite.Tests's OWN trees … every comparison below is superset-of-nothingImportElementsnever matchesAssert.Equalcollections differ —Check's emptiness assertion stayed green, which is exactly why the gate needs known answersAssert.EqualonMSBuildProjectFullPathProcessStartInfo("dotnet-not-a-real-executable")exited non-zero … stderr=dotnet could not be started: … No such file or directory— includingEveryCrossAppSourceRead_…, the pin whose success condition is emptinessHintPathagainst the declaring file's directory instead of the project'sAssert.Containsfailed onDarling/PerformanceMonitor.Darling.Service/Hinted.dll. This is the mutation the first fixture could not catch, and the reason the imported props file was moved above the projectImported.propsmoved back beside the projecterror MSB4019: The imported project … "../Imported.props" … was not found— the fixture fails loudly rather than silently losing an item, so its own layout is not a silent assumption(0, "", "")instead of failingAssert.NotEqualon the exit code — the deadline's own claim, which had no pin at all until the second review threadWaitForExitignores the injected deadline and uses the shipped constantValueKindguard on the property readInvalidOperationException : The requested operation requires an element of type 'String', but the target element has type 'Number'raised out ofParseEvaluation— the exact bypass of theFailurefloor that the third review thread identified1, 2, 4, 6b, 8, 13, 15, 16 and 17 each red exactly one test, and that is the point: each names a defect nothing else in the suite can see. 12 is the failure mode the issue called strictly worse than the regex, and it now reds five tests with the command line in the message.
Note on 6b: a
Condition="false"cross-app include makes the crude parse over-read and the pin red. That is deliberate — the failure message says so and asks a human whether that path is a read worth covering, rather than silently taking either route's word for it.Verification
Lite.Testsisnet10.0-windowswithUseWPFand cannot run on macOS, so the actualCrossAppGuardCiGateTests.cswas compiled into a throwawaynet10.0xunit v3 console harness staged inside the gitignoredLite.Tests/bin/, and run there. Green:Total: 10, Errors: 0, Failed: 0, Skipped: 0, Not Run: 0. Microsoft.Testing.Platform prints no per-testPassedlines, so that summary and the 12 mutations above are the evidence, not a grep for failures.The harness reads the edited file rather than a stale binary: the runtime moved from 0.30 s to 3.2 s on the switch (the invocations), the test names changed, and every mutation above changed the outcome.
dotnet build Lite.Tests/Lite.Tests.csproj -p:EnableWindowsTargeting=truesucceeds on macOS:0 Error(s), 9 warnings, none in either changed file. That compiles the whole project including all 300 test files, not just the one under change.Re-run with build output present, so the
bin/-and-obj/-exclusion assertion is exercised against real artifacts: three throwaway.csprojfiles were sitting inLite.Tests/bin/and the population pin'sAssert.DoesNotContain(… "/bin/" …)held. Still green.No
obj/created under either scanned project by the guard, and no fixture temp directory left behind.The
Darling.Testsarm was verified too, and it is the arm I nearly left unchecked. Two guards there read the file this PR changes and cannot run on macOS either:DocCommentHygieneTestsscans every.csfile under the repo root (stacked<summary>blocks, unbalanced doc runs), andCommentFilterAdoptionTestsscans both test projects for an unregistered line-prefix comment filter and compares the result for equality against its own map. Both were compiled into a second net10.0 harness withCSharpSourceWalker.csand run:Total: 42, Errors: 0, Failed: 0, Skipped: 0, Not Run: 0.That harness was red-proofed against this PR's own file, twice, because a whole-repo scanner reporting clean is exactly the shape that reports clean on the wrong tree: injecting a stacked
<summary>intoCrossAppGuardCiGateTests.csredsNoMemberCarriesTwoStackedSummaryBlocks, and injecting aline.TrimStart().StartsWith("//", …)into it redsEveryLinePrefixCommentFilter_StatesTheBoundItRestsOnnamingLite.Tests/CrossAppGuardCiGateTests.csas the unregistered site. So the 42/0 is evidence about this diff rather than about a scan that never reached it.For the record, the shipped code's several
.StartsWithcalls take path prefixes rather than comment prefixes, which is why the adoption scan is silent on them — the detector requires a comment-prefix literal at the call site.Also in this PR
CrossAppMcpToolInventoryPinTests' summary cited a "55-vs-73 drift". The numerals narrate why the pin exists rather than asserting anything the code reads, so per #3072's three-class policy they are deleted rather than corrected, and the sentence now says the same thing without a number.Deleting was the right class on the merits and it is also the one immune to merge order: #3078 adds an MCP tool, which moves the count again. For the record the figures were re-derived by running the shipped
ExtractToolNamesthrough reflection at0f888e90c: 87 Lite, 138 Darling — not the 87/137 quoted to me. Nothing in this PR depends on either figure.Review findings, and one more the review found
Two findings, both correct, both fixed:
using System.Security;was unused. It was for aSecurityElement.Escapecall in an earlier fixture that interpolated the repository's absolute path into the generated XML; the fixture writes relative MSBuild properties instead, so there is nothing to escape. Dropped in8b5122b85, replied in the thread, thread resolved.CSharpPaths' summary still pointed atMsBuildPaths, a method this change deletes. Real rot, and it is worth saying why nothing caught it:GenerateDocumentationFileis off for this project, so the compiler never binds acrefand a deleted member leaves its inbound doc references dangling in silence. Fixed in330f6958fby stating the reason directly instead of cross-referencing.Rather than fix only the one that was reported, I built the project once with
-p:GenerateDocumentationFile=trueand let the compiler enumerate them all. Result: 22 distinctCS1574across the wholeLite.Testscompilation, none in either file this PR touches — so mine is the only one this change introduced, and it is gone. (Roughly half of the 22 look like genuinely stale member references —AppliesTofive times,PayloadColumns,IsVictim,WaitTime— and the rest name types in assemblies Lite does not reference, which acrefcannot resolve by design. The split needs triage rather than a number, and it is pre-existing either way.)Rebased? No — merged twice, and every figure re-derived after each
devmoved five times during review. Two of those touch this PR's files, andorigin/devwas merged in both times rather than rebased, since a rebase means a force-push:Lite.Tests/CrossAppMcpToolInventoryPinTests.cs. Merged at663de9b36. No conflict, and none possible — the numerals were deleted from that summary rather than corrected, so there was no figure left for a+1to the Darling tool count to make wrong.Lite.Tests/CrossAppGuardCiGateTests.cs— the same file, the C# string-literal stage. Merged atdf5f0467c, and this one did conflict, in two hunks. Both resolved to keep both lanes' work: their two new pins (TheCSharpMatchers_StillCannotSeeTheseSpellings,TheBareDirectoryArm_IsLiveForBothSkus) and their two newCSharpPathsdoc paragraphs, alongside my MSBuild pins — while dropping the four evaluator pins this PR deletes, which their side reintroduced simply by being present ondev.A clean merge is the case to distrust, and the second merge is why. #3079 adds a bare-directory arm to
CSharpPaths, which changes the found set: two new references (Lite (directory),Lite.Tests (directory)) and a newDarlingTestsBackstoppedentry for one of them. Nothing textual would have told me that; only re-running the assertions did.Everything re-derived at
df5f0467c:df5f0467cScan, dev's file vs minecommempty both wayscommempty both ways-getItemelapsed<Import>over tracked MSBuild filesCS1574in the two changed filesDocCommentHygieneTests+CommentFilterAdoptionTests, harnessLite.Testsrebuild, macOSThe reference total moving 41 → 43 is attributable rather than mysterious: it is exactly #3079's two bare-directory references, and the superset diff is empty at both bases. The 10 → 12 and 42 → 44 suite deltas are #3079's and #3077's new pins, not mine — which is why the load-bearing evidence here is
Failed: 0plusNot Run: 0plus an empty set difference, all per-run facts, rather than a total.The MCP census moved as expected, and did not matter: 87 Lite / 138 Darling at the original base, 87 / 139 after #2880. Re-derived at each head by running the shipped
ExtractToolNamesthrough reflection, not by grepping. Nothing here depends on either figure.Review findings
Four findings across three review threads, all from the review bot, all correct, all fixed, all threads resolved:
using System.Security;was unused — left from an earlier fixture that interpolated the repository's absolute path into the generated XML and needed escaping. The fixture writes relative MSBuild properties instead.8b5122b85.The kill-after-timeout catch took
InvalidOperationExceptionalone. Right, and it understates the gap:Kill(entireProcessTree: true)documents four —InvalidOperationException,NotSupportedException,Win32Exception, andAggregateExceptionfor a descendant that could not be killed, which the process-tree overload adds for itself. All four are caught now; the first three matchDarlingDeployRollbackRetentionTestsandDarlingDeployStaleFileTests, and the comment says why this site has a fourth. The same thread caught a comment claiming "the exit code below is still read" when the branch returns-1— gone, and the message now says no output was collected rather than leaving an empty stdout to be read as an answer.63b41adf9.That finding also surfaced the one floor of four I had not pinned. The deadline could not be tested because the shipped two minutes cannot be waited out, so
RunDotnetnow takes it as a parameter defaulting to the shipped value, and mutations 15 and 16 below cover it.ParseEvaluationread properties with an unguardedGetString()while the sibling item-metadata reader checksValueKindfirst.GetString()raises on a non-string rather than returning null, and an exception out of that method bypasses theEvaluation.Failurefloor every caller asserts on — which is its only contract. Not unreachable either: the loud-failure pin calls it directly with hand-written JSON. Fixed inb47d4d8ba, and reported rather than coerced to the empty string the way item metadata is, because empty is MSBuild's own answer for "there is noDirectory.Build.props" and flattening an unexpected shape into one would manufacture that answer. Pinned, plus one more the same reasoning exposed: an answer missing a requested property, which would otherwise leave the build-file paths reading as "there are none". Mutation 17.A fourth finding came from the review's own second pass and is worth its own note:
CSharpPaths' summary still carried<see cref="MsBuildPaths"/>, a method this change deletes. Real rot, and nothing in the build catches it —GenerateDocumentationFileis off, so the compiler never binds acref. Fixed in330f6958f. Rather than patch the reported line, I built the project once with-p:GenerateDocumentationFile=trueand let the compiler enumerate every one: 22 distinctCS1574across theLite.Testscompilation, none in either file this PR touches. (Roughly half of the 22 look genuinely stale —AppliesTofive times,PayloadColumns,IsVictim,WaitTime— and the rest name types in assemblies Lite does not reference, which acrefcannot resolve by design. Pre-existing either way; the split needs triage rather than a number.)A stale comment in
Resolvedescribing the deleted wildcard reduction was found the same way and fixed in the merge commit.