Skip to content

Fix Helix SDK test isolation: current-directory race in XHarness tests - #17571

Merged
mmitche merged 3 commits into
mainfrom
copilot/fix-helix-sdk-test-isolation
Sep 19, 2026
Merged

mmitche merged 3 commits into
mainfrom
copilot/fix-helix-sdk-test-isolation

Conversation

Copilot AI commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

CreateXHarnessAppleWorkItemsTests.ArchivePayloadIsOverwritten fails intermittently: LocalTestResultsReaderTests mutates Environment.CurrentDirectory while XHarness tests resolve relative paths through TaskEnvironment.Fallback, which reads the process current directory on every lookup. When the interleaving is unlucky, PrepareWorkItem resolves apps/System.Foo.app to a path the MockFileSystem never registered, the task logs an error, and InvokeExecute returns false.

Test-only change. Parallel execution is preserved — no nonparallel collection, sleeps, retries, or production behavior changes.

Changes

  • Dropped the global mutation (LocalTestResultsReaderTests): the test already passes a fully qualified path to the reader, so the set/restore of Environment.CurrentDirectory was pure global-state churn.

  • Explicit task environment (CreateXHarnessApple/AndroidWorkItemsTests): each test instance gets its own project directory, so setup registration and production resolution anchor to the same place regardless of interleaving.

    _task = new CreateXHarnessAppleWorkItems()
    {
        BuildEngine = _buildEngine,
        TaskEnvironment = TaskEnvironment.CreateWithProjectDirectoryAndEnvironment(
            Path.Combine(Path.GetTempPath(), "xharness-apple-tests", Guid.NewGuid().ToString("N"))),
    };
  • Failure diagnostics: added MockBuildEngine.ErrorSummary and threaded it through the success assertions, so an unexpected task failure reports the logged error instead of only Expected boolean to be True, but found False.

  • Regression coverage: RelativeAppBundlePathsResolveAgainstTheProjectDirectory / RelativeApkPathsResolveAgainstTheProjectDirectory assert relative payload paths land under TaskEnvironment.ProjectDirectory and reach the archive manager and PayloadArchive metadata unchanged.

To double check:

Copilot AI lite review requested due to automatic review settings September 16, 2026 19:32

Copilot AI 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.

Copilot wasn't able to review any files in this pull request.


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

…ss tests

Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 16, 2026 19:39
Copilot AI changed the title [WIP] Fix Helix SDK test isolation in ArchivePayloadIsOverwritten Fix Helix SDK test isolation: current-directory race in XHarness tests Sep 16, 2026
Copilot AI requested a review from mmitche September 16, 2026 19:40

Copilot AI 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.

🟢 Approval recommended

No unresolved issues were identified that would block approval.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 16, 2026 20:04
Copilot AI requested a review from mmitche September 16, 2026 20:08

Copilot AI 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.

🔵 Needs a closer look

Two moderate filesystem-semantics issues remain unresolved.

Review details

Suppressed comments (5)

src/Common/Microsoft.Arcade.Test.Common/MockFileSystem.cs:70

  • When an existing file is opened with FileMode.Create, this now constructs MockFileStream from the old contents instead of truncating it. A caller that writes a shorter replacement from position 0 leaves stale trailing bytes in Files (for example, ProvisioningProfileProvider uses FileMode.Create), so the mock no longer matches real FileStream semantics. Preserve create/truncate behavior when initializing the writable stream.
        => access == FileAccess.Read && FileExists(path)
            ? new MemoryStream(Encoding.UTF8.GetBytes(Files[path]))
            : new MockFileStream(this, path);

src/Microsoft.DotNet.Helix/JobMonitor/TestResults/LocalTestResultsReader.cs:306

  • File.OpenRead used here before this change opens the file with FileShare.Read, but FileSystem.GetFileStream delegates to new FileStream(path, mode, access), whose default is FileShare.None. That makes the production reader acquire an exclusive read handle, so a second reader of the same result file can now fail with an IOException; preserve the previous sharing semantics in the filesystem abstraction (for example, use FileShare.Read for read-only opens).
        => XmlReader.Create(_fileSystem.GetFileStream(filePath, FileMode.Open, FileAccess.Read), new XmlReaderSettings

src/Microsoft.DotNet.Helix/JobMonitor/TestResults/LocalTestResultsReader.cs:26

  • All reader tests now construct the new three-argument overload with MockFileSystem, so the default constructor used by TestResultProcessor (which creates the real FileSystem) is no longer exercised. A regression in the default IFileSystem wiring or actual FileStream behavior could therefore pass this suite; retain at least one test that uses the normal constructor with a real temporary result file.
        : this(logger, attachmentMode, new FileSystem())

src/Microsoft.DotNet.Helix/Sdk.Tests/Microsoft.DotNet.Helix.Sdk.Tests/CreateXHarnessAndroidWorkItemsTests.cs:149

  • This only checks that PayloadArchive starts with the project directory. It would pass if the task emitted a different archive filename or otherwise changed the metadata while staying under that directory, so it does not verify the stated unchanged payload metadata contract. Assert the exact archive path derived from the relative APK.
        _task.WorkItems.Single().GetMetadata("PayloadArchive").Should().StartWith(projectDirectory);

src/Microsoft.DotNet.Helix/Sdk.Tests/Microsoft.DotNet.Helix.Sdk.Tests/CreateXHarnessAppleWorkItemsTests.cs:174

  • This only checks that PayloadArchive starts with the project directory. It would pass if the task emitted a different archive filename or otherwise changed the metadata while staying under that directory, so it does not verify the stated unchanged payload metadata contract. Assert the exact archive path derived from the relative app bundle.
        _task.WorkItems.Single().GetMetadata("PayloadArchive").Should().StartWith(projectDirectory);
  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@mmitche
mmitche marked this pull request as ready for review September 17, 2026 20:14
@mmitche
mmitche enabled auto-merge (squash) September 18, 2026 18:30
@mmitche
mmitche merged commit 7f17e89 into main Sep 19, 2026
13 checks passed
@mmitche
mmitche deleted the copilot/fix-helix-sdk-test-isolation branch September 19, 2026 17:40
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.

Fix Helix SDK test isolation: current-directory race in ArchivePayloadIsOverwritten

4 participants