Skip to content

fix(testing): respect TestRunRequest.exclude when running tests#25743

Open
andravin wants to merge 5 commits into
microsoft:mainfrom
andravin:fix/test-explorer-exclude-filtering
Open

fix(testing): respect TestRunRequest.exclude when running tests#25743
andravin wants to merge 5 commits into
microsoft:mainfrom
andravin:fix/test-explorer-exclude-filtering

Conversation

@andravin

@andravin andravin commented Jan 20, 2026

Copy link
Copy Markdown

The VS Code Test Explorer API documents that TestRunRequest.exclude contains tests the user has marked as excluded (e.g., via filtering). The documentation says,

If given, the extension should run all of the included tests and all their children, excluding any tests
that appear in TestRunRequest.exclude.

Previously, the Python extension ignored request.exclude, so invoking Run Tests would execute all tests even when the Test Explorer view was filtered.

This fix adds exclude handling in WorkspaceTestAdapter.executeTests(): Pre-expand the exclude set to include all descendants, then pass to getTestCaseNodes() which skips excluded nodes with O(1) set lookups during traversal

This change also adds a visited set to getTestCaseNodes() to avoid expanding the same node multiple times when include contains overlapping items.

Fixes the issue where filtering tests in Test Explorer (e.g., by tag) and clicking Run Tests would still run all tests.

Fixes #25742

@andravin
andravin force-pushed the fix/test-explorer-exclude-filtering branch 2 times, most recently from 36418f2 to fc38e1b Compare February 3, 2026 04:17
@andravin

andravin commented Feb 3, 2026

Copy link
Copy Markdown
Author

Tested against main. Unit tests have been added.

andravin added a commit to andravin/vscode-python that referenced this pull request Feb 14, 2026
andravin and others added 5 commits February 17, 2026 22:23
The VS Code Test Explorer API specifies that TestRunRequest.exclude
contains tests the user has marked as excluded (e.g., via filtering).
Per the API contract, "exclusions should apply after inclusions."

Previously, the Python extension ignored request.exclude entirely,
causing "Run Tests" to run all tests even when the user had filtered
the Test Explorer view. This fix adds exclude handling at two levels:

1. In getTestItemsForWorkspace(): Filter out excluded items before
   passing to the test adapter (checks ancestors for top-level items)

2. In WorkspaceTestAdapter.executeTests(): Pre-expand the exclude set
   to include all descendants, then pass to getTestCaseNodes() which
   skips excluded nodes with O(1) set lookups during traversal

Also adds a visited set to getTestCaseNodes() to avoid expanding
the same node multiple times when includes contains overlapping items.

Fixes the issue where filtering tests in Test Explorer (e.g., by tag)
and clicking "Run Tests" would still run all tests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The getTestCaseNodes function was changed from a single-parameter
function returning an array to a 4-parameter function that mutates
the collection array in place (with additional visited and excludeSet
parameters for exclude support). Updated the test stub accordingly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add unit tests for isTestItemExcluded, expandExcludeSet, and
getTestCaseNodes with exclude/visited parameters. Verifies ancestor
chain checking, descendant expansion, and correct node skipping.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…kspace

Remove isTestItemExcluded and exclusion logic from getTestItemsForWorkspace
since exclusions are fully handled downstream via expandExcludeSet and
getTestCaseNodes in WorkspaceTestAdapter.executeTests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…cution

The new project-based test execution path (executeTestsForProjects) was
not respecting TestRunRequest.exclude. This adds exclude filtering by:

- Importing expandExcludeSet and getTestCaseNodes from testItemUtilities
- Creating expanded exclude set from request.exclude in executeTestsForProjects
- Using getTestCaseNodes (with exclude support) instead of getTestCaseNodesRecursive

Also fixes testMocks.ts to create realistic mock TestItems:
- Set canResolveChildren=true when children are present
- Include RunTestTag and DebugTestTag (required for getTestCaseNodes)

Adds tests for exclude filtering in project-based execution including
multi-project scenarios.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

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

Pull request overview

This PR updates the Python extension’s VS Code Test Explorer execution path to honor TestRunRequest.exclude, ensuring tests filtered/excluded by the UI are not executed (including their descendants), aligning behavior with the VS Code Testing API contract.

Changes:

  • Pass request.exclude into WorkspaceTestAdapter.executeTests() and filter excluded nodes during include expansion.
  • Add expandExcludeSet() + visited-node tracking in getTestCaseNodes() to support O(1) exclusion checks and avoid repeated traversal for overlapping includes.
  • Add/adjust unit tests and mocks to cover exclude handling for both single-workspace and project-based execution.
Show a summary per file
File Description
src/client/testing/testController/controller.ts Plumbs request.exclude through to the workspace test adapter execution call.
src/client/testing/testController/workspaceTestAdapter.ts Expands/filters excluded items when collecting leaf test cases for execution.
src/client/testing/testController/common/testItemUtilities.ts Adds expandExcludeSet() and extends getTestCaseNodes() with visited/exclude support.
src/client/testing/testController/common/projectTestExecution.ts Applies exclude handling in project-based execution by filtering leaf expansion.
src/test/testing/testController/workspaceTestAdapter.unit.test.ts Updates stubbing of getTestCaseNodes() to match new signature/behavior.
src/test/testing/testController/testMocks.ts Improves test item mocks (tags + canResolveChildren) to better match real test tree behavior.
src/test/testing/testController/testItemUtilities.unit.test.ts Adds focused unit coverage for exclude expansion and visited/exclude traversal behavior.
src/test/testing/testController/common/projectTestExecution.unit.test.ts Adds coverage to ensure exclude items are not executed in project-based execution (including multi-project).

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 2
  • Review effort level: Low

Comment on lines 62 to 80
const testCaseNodes: TestItem[] = [];
const testCaseIdsSet = new Set<string>();
const visitedNodes = new Set<TestItem>();
const rawExcludeSet = excludes?.length ? new Set(excludes) : undefined;
const excludeSet = expandExcludeSet(rawExcludeSet);
const testCaseIds: string[] = [];
try {
// first fetch all the individual test Items that we necessarily want
// Expand included items to leaf test nodes.
// getTestCaseNodes handles visited tracking and exclusion filtering.
includes.forEach((t) => {
const nodes = getTestCaseNodes(t);
testCaseNodes.push(...nodes);
getTestCaseNodes(t, testCaseNodes, visitedNodes, excludeSet);
});
// iterate through testItems nodes and fetch their unittest runID to pass in as argument
// Collect runIDs for the test nodes to execute.
testCaseNodes.forEach((node) => {
runInstance.started(node); // do the vscode ui test item start here before runtest
runInstance.started(node);
const runId = this.resultResolver.vsIdToRunId.get(node.id);
if (runId) {
testCaseIdsSet.add(runId);
testCaseIds.push(runId);
}
});
Comment on lines +235 to 244
// Mark items as started and collect test IDs
const testCaseIds: string[] = [];
for (const node of testCaseNodes) {
runInstance.started(node);
const runId = project.resultResolver.vsIdToRunId.get(node.id);
if (runId) {
testCaseIds.push(runId);
}
}

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.

Test runs ignore TestRunRequest.exclude (filtered tests still run)

2 participants