Skip to content

test(core): skip environment-dependent tests with a reason instead of failing (#28830) - #28832

Closed
Chirag6722 wants to merge 2 commits into
google-gemini:mainfrom
Chirag6722:fix/28830-guard-windows-env-dependent-tests
Closed

Chirag6722 wants to merge 2 commits into
google-gemini:mainfrom
Chirag6722:fix/28830-guard-windows-env-dependent-tests

Conversation

@Chirag6722

Copy link
Copy Markdown

Closes #28830.

On a clean Windows checkout, npx vitest run in packages/core reports 13 failures before any change is made. None of them indicate a product defect — 8 need a privilege Windows does not grant by default, 4 need PowerShell 7. This makes them skip with a reason instead.

The problem was never that they fail. It is that a contributor cannot tell them from a regression they just caused, and test_windows is gated on github.repository == 'google-gemini/gemini-cli', so it does not run on forks — there is no reference result to diff against.

Capability probes, not platform checks

Both guards ask the host what it can do rather than what it is:

it.skipIf(!canCreateSymlinks())('should resolve symbolic links', ...)
describe.skipIf(!isWindows || !hasPowerShell7())('ShellExecutionService Windows quoting (real shell)', ...)

skipIf(process.platform === 'win32') would have been one line shorter and worse: it would also skip Windows contributors who do have Developer Mode or pwsh installed, and those are exactly the people most likely to be changing this code. canCreateSymlinks() creates one real link in a temp dir and caches the result; its cleanup is wrapped so the probe can never fail a suite it exists only to describe.

For the quoting suite, the precondition was already written down — in prose, in the file's own header:

These tests exercise the full pipeline end-to-end. They pass when gemini-cli selects pwsh.exe from PATH; they fail when the pipeline routes through Windows PowerShell 5.1.

This turns that sentence into the guard.

Verification

Targeted run over the six affected files, before and after:

before:  11 failed
after:    5 passed | 1 skipped (files)
          150 passed | 12 skipped (tests), 0 failed

Full packages/core suite before the change: 14 failed | 392 passed files, 13 failed | 7396 passed tests. After, the count drops to the one item below. prettier and eslint --max-warnings 0 are clean on all changed files (the repo's pre-commit hook re-ran both on the staged set).

One failure I did not fix, and am not claiming

SandboxManager Integration > automatically allows write access to .git when running git command still fails here:

Stderr: 0 [main] sh (21420) C:\Program Files\Git\usr\bin\sh.exe: *** fatal error -
NtCreateDirectoryObject(\BaseNamedObjects\msys-2.0S5-...): 0xC0000022

The sandbox binary shells out to msys sh, which cannot create its shared object — almost certainly because I invoked the suite from Git Bash, so a second msys runtime was already holding it. That is an environment interaction, not a capability a probe can honestly test, and I would rather leave it visible than paper over it with a guard that hides a real failure later. Worth a separate look by someone running from PowerShell or cmd.

I also would not trust a repeat full-suite run I did while another suite was still running — it produced per-test durations of 664,000ms and above, which is machine contention rather than signal. The numbers quoted above come from clean runs.

Placement note

The probes live in packages/core/src/test-utils/ rather than the shared packages/test-utils. I tried the shared package first; importing that barrel from a core test fails with Failed to resolve entry for package "@google/gemini-cli-core", because the barrel re-exports modules that import core. If you would rather they were shared, that resolution issue needs solving first and is bigger than this change.

Third Windows finding, not in this PR

While setting this up: git clone of this repo on Windows without core.longpaths=true leaves the working tree broken. Several snapshot filenames exceed the 260-character limit:

error: cannot stat 'packages/cli/src/ui/components/__snapshots__/
InputPrompt-InputPrompt-Highlighting-and-Cursor-Display-multi-line-scenarios-should-display-
cursor-correctly-at-the-beginning-of-a-line-in-a-multiline-block.snap.svg': Filename too long

git then records them as staged deletions — 3037 dirty entries in a repo that looks cloned. git clone -c core.longpaths=true gives 0 dirty files, which confirms the cause. Nothing in CONTRIBUTING mentions it, and it is a worse first-contact failure than the test noise this PR addresses. Happy to send a docs PR for that separately if useful.

@Chirag6722
Chirag6722 requested review from a team as code owners August 16, 2026 04:03
@google-cla

google-cla Bot commented Aug 16, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@github-actions github-actions Bot added the size/l A large sized PR label Aug 16, 2026
@github-actions

github-actions Bot commented Aug 16, 2026 •

Copy link
Copy Markdown

📊 PR Size: size/L

  • Lines changed: 390
  • Additions: +255
  • Deletions: -135
  • Files changed: 9

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses test failures occurring on Windows environments by implementing capability-based guards instead of broad platform checks. By probing the host environment for necessary features like symlink support and specific shell availability, the test suite now gracefully skips tests that cannot run in a given environment, ensuring that failures are reserved for actual regressions rather than environmental limitations.

Highlights

  • Environment-aware test skipping: Introduced capability probes to dynamically skip tests that fail due to environmental constraints (e.g., lack of symlink permissions or missing PowerShell 7) rather than product defects.
  • New test utilities: Added packages/core/src/test-utils/environment-capabilities.ts to encapsulate logic for checking host capabilities like symlink creation and PowerShell 7 availability.
  • Test suite stability: Updated multiple test files across packages/core to use it.skipIf and describe.skipIf with the new capability probes, significantly reducing noise in local development environments on Windows.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces host capability probes (canCreateSymlinks and hasPowerShell7) in a new environment-capabilities.ts utility file. These probes are used to conditionally skip tests that rely on symbolic links or PowerShell 7, preventing environment-related test failures on hosts (such as default Windows installations) that lack these capabilities. Feedback on the changes suggests sanitizing PATH entries in hasPowerShell7 by removing potential surrounding double quotes to ensure robust path resolution on Windows.

Comment on lines +72 to +81
return (process.env['PATH'] ?? '').split(path.delimiter).some((entry) => {
if (entry === '') {
return false;
}
try {
return fs.existsSync(path.join(entry, 'pwsh.exe'));
} catch {
return false;
}
});

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.

high

On Windows, entries in the PATH environment variable can sometimes be wrapped in double quotes (e.g., if they contain spaces). When path.join is called with a quoted entry, it produces an invalid path (e.g., "C:\\Program Files\\PowerShell\\7"\\pwsh.exe), causing fs.existsSync to return false even if PowerShell 7 is installed. Sanitizing the path entry by removing surrounding quotes ensures robust capability detection.

  return (process.env['PATH'] ?? '').split(path.delimiter).some((entry) => {
    const cleanEntry = entry.replace(/^"|"$/g, '');
    if (cleanEntry === '') {
      return false;
    }
    try {
      return fs.existsSync(path.join(cleanEntry, 'pwsh.exe'));
    } catch {
      return false;
    }
  });

… failing

On a clean Windows checkout `npx vitest run` in packages/core reported 13
failures before any change. None indicated a product defect: 8 build a
symlink fixture, which needs Developer Mode or an elevated shell on
Windows and otherwise throws EPERM, and 4 assert the pwsh quoting
behaviour, which a default Windows install cannot produce because it has
only Windows PowerShell 5.1.

A contributor could not tell those from a regression they had caused, and
the Windows CI job is gated on github.repository so it does not run on
forks, leaving no reference result to compare against.

Both are now capability probes rather than platform checks. Skipping
every win32 host would also skip the contributors who have Developer Mode
or pwsh installed, and they are the ones most likely to be changing this
code. canCreateSymlinks() creates one real link in a temp directory and
caches the answer; hasPowerShell7() looks for pwsh.exe on PATH. The
quoting suite's header already named its precondition in prose, so this
turns that sentence into the guard.

The probes live in packages/core/src/test-utils rather than the shared
test-utils package: importing that barrel from a core test pulls
@google/gemini-cli-core through it and fails to resolve.
@Chirag6722
Chirag6722 force-pushed the fix/28830-guard-windows-env-dependent-tests branch from ddd525f to ccc3b18 Compare August 16, 2026 04:18
@gemini-cli gemini-cli Bot added the area/platform Issues related to Build infra, Release mgmt, Testing, Eval infra, Capacity, Quota mgmt label Aug 16, 2026
…erShell7

Windows stores PATH entries containing spaces quoted in some setups. path.join
then keeps the quote inside the path, so fs.existsSync misses a pwsh that is
actually installed.

The direction of that failure is the one this helper exists to avoid: a false
negative skips the quoting suite on exactly the hosts that could have run it,
which is the same coverage loss as guarding on platform instead of capability.

Verified the mechanism: joining a quoted entry yields a path containing a
literal quote and existsSync returns false, while the unquoted form resolves
identically to the plain path. My own PATH carries no quoted entries, so this
is defensive rather than reproduced end to end here.

Raised by gemini-code-assist on google-gemini#28832.
@Chirag6722

Copy link
Copy Markdown
Author

Good catch, and it is the failure direction this helper exists to avoid — fixed in ddd6829.

A quoted PATH entry makes path.join keep the quote inside the path, so existsSync misses a pwsh that is genuinely installed. That is a false negative, which skips the quoting suite on exactly the hosts that could have run it. Same coverage loss as guarding on platform === 'win32' instead of on capability, which is the thing this PR argues against — so the bug quietly undercut its own rationale.

Verified the mechanism rather than taking it on faith:

quoted joined  -> '"C:\Program Files\PowerShell\7"\pwsh.exe'
quoted exists  -> false
unquoted join  === plain join   ->  true

Being straight about the limit of that check: my own PATH has 0 quoted entries, so I confirmed the mechanism and the equivalence, not an end-to-end repro on a machine that actually stores them quoted. The fix is cheap and strictly widens detection, so I took it on that basis.

Guarded suites still behave: 5 passed | 1 skipped files, 150 passed | 12 skipped tests, 0 failures.

@Shivang9983 Shivang9983 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM on the overall approach!

Using runtime capability probes (canCreateSymlinks(), hasPowerShell7()) is significantly better than a blunt skipIf(process.platform === 'win32').

A blanket OS check unnecessarily drops Windows test coverage for contributors who have Developer Mode or modern PowerShell installed—who are precisely the folks writing and debugging Windows-specific features. Capability checks ensure tests run wherever supported and provide clear, actionable skip reasons when preconditions are missing.

@Chirag6722

Copy link
Copy Markdown
Author

Thanks for actually running it under PowerShell and CMD — that is the measurement that settles it, and it is worth more than the hypothesis was.

Your result narrows #28830 usefully: NtCreateDirectoryObject 0xC0000022 is an MSYS runtime conflict, not a Windows sandbox limitation. That matters for the fix direction, because it means the test is not exercising anything genuinely unsupported on Windows — it is the shell the suite was launched from. A blanket skipIf(process.platform === 'win32') would have written that off as "Windows cannot do this" and permanently dropped the coverage on every Windows host, including the native shells where you just showed it passes.

The fs.symlinkSync EPERM you hit in restricts symlinks to forbidden targets is the cleaner illustration of the same point. It is not a property of Windows; it is a property of that account lacking SeCreateSymbolicLinkPrivilege. Enable Developer Mode or run elevated and it passes. That is precisely the split canCreateSymlinks() probes for, and why the probe reports a reason rather than a bare skip — a contributor who can run the test should run it, and one who cannot should be told which privilege is missing instead of seeing a green suite that silently tested nothing.

On the core.longpaths=true note for CONTRIBUTING.md — please do open that. It is the same failure class one layer earlier: without it the checkout itself is incomplete, so the tests are red for a reason that has nothing to do with the code under test, and the error does not name the cause. Happy to review it.

One thing on this PR for anyone picking it up: the review bot's PATH comment is addressed at head (ddd6829). Quoted PATH entries are unquoted before the existsSync check, test-utils/environment-capabilities.ts:72-77:

return (process.env['PATH'] ?? '').split(path.delimiter).some((entry) => {
  // Windows PATH entries containing spaces are sometimes stored quoted.
  const unquoted = entry.replace(/^"|"$/g, '');

Without that, a quoted entry makes path.join keep the quote inside the path, existsSync misses a pwsh that is genuinely installed, and the quoting suite skips on exactly the hosts that could have run it — a false negative, which is the same coverage loss this PR exists to remove. The thread is just not auto-resolved.

@Shivang9983

Copy link
Copy Markdown

Thanks @Chirag6722! Glad the PowerShell/CMD test runs helped settle the MSYS runtime collision hypothesis and reinforced the need for capability probes in #28832.

I have already submitted the CONTRIBUTING.md PR to document core.longpaths=true and the recovery commands for Windows contributors: #28926. Feel free to review whenever you get a moment!

@gemini-cli gemini-cli Bot added the priority/p2 Important but can be addressed in a future release. label Aug 20, 2026
@gemini-cli

gemini-cli Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Hi there! Thank you for your interest in contributing to Gemini CLI.

To ensure we maintain high code quality and focus on our prioritized roadmap, we only guarantee review and consideration of pull requests for issues that are explicitly labeled as 'help wanted'.

This PR will be closed in 7 days if it remains without that designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding.

@Chirag6722

Copy link
Copy Markdown
Author

Understood on the policy — could a maintainer consider adding help wanted to #28830 so this can be reviewed?

Some context for whoever picks it up. #28830 is already labelled kind/bug, priority/p2 and status/bot-triaged, so it has been through triage and assessed as a real defect; it is only the help wanted designation that is absent.

The failure is also confirmed by someone other than me. @Shivang9983 ran the suite on his own Windows 11 machine under native PowerShell and CMD, where the affected tests pass, which established that NtCreateDirectoryObject 0xC0000022 is an MSYS runtime conflict rather than a Windows limitation. He reviewed this PR and endorsed the approach over a blanket OS check.

That distinction is the reason this PR is worth reviewing rather than closing. The alternative fix — skipIf(process.platform === 'win32') — would make CI green by permanently discarding the coverage on every Windows host, including the ones where the tests demonstrably pass. This replaces the platform checks with runtime capability probes (canCreateSymlinks(), hasPowerShell7()), so a test skips only when the host genuinely lacks the capability and reports which one, instead of a green suite that silently tested nothing.

If the policy is firm and the label is not going to be applied, I would rather know than have it time out — happy to close it myself and leave the diagnosis on #28830 for whoever picks the issue up later.

@Shivang9983

Copy link
Copy Markdown

Hi @maintainers,

Requesting to please consider adding the help wanted label to #28830 so this PR can be reviewed.

I independently tested this suite on a clean Windows 11 setup across native PowerShell and CMD. The findings confirmed that:

  1. The test failures are not actual product regressions but environment-dependent preconditions (MSYS runtime collisions and missing Developer Mode privileges for symlinks).
  2. The runtime capability probes implemented here (canCreateSymlinks(), hasPowerShell7()) allow the test suite to pass reliably across native Windows shells without dropping test coverage via a blanket OS skip.

Given that #28830 is already triaged (kind/bug, priority/p2), having these capability guards in place will significantly improve the Windows contributor experience. Would appreciate a review on this before the stale bot closes it!

@gemini-cli

gemini-cli Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

This pull request is being closed as it has been open for 14 days without a 'help wanted' designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding.

@gemini-cli gemini-cli Bot closed this Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/platform Issues related to Build infra, Release mgmt, Testing, Eval infra, Capacity, Quota mgmt priority/p2 Important but can be addressed in a future release. size/l A large sized PR status/pr-nudge-sent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: 13 core tests fail on a clean checkout from unguarded environment preconditions, and the Windows CI job cannot be used to check

2 participants