Skip to content

fix: prevent substring matching in UID filter causing incorrect test inclusion#4659

Merged
thomhurst merged 2 commits intomainfrom
fix/4656-overlapping-class-name-filter
Feb 4, 2026
Merged

fix: prevent substring matching in UID filter causing incorrect test inclusion#4659
thomhurst merged 2 commits intomainfrom
fix/4656-overlapping-class-name-filter

Conversation

@thomhurst
Copy link
Owner

Summary

Fixes #4656

When filtering tests by UID (e.g., from Visual Studio), class names like ABCV would incorrectly match ABCVC because the filter used string.Contains() for matching. This caused TestDiscoveryContext.AllTests to include tests that shouldn't be there.

Root Cause

In MetadataFilterMatcher.CouldMatchUidFilter(), the code used substring matching:

if (uidValue.Contains(className) && uidValue.Contains(methodName))

This meant that when checking if test ABCV.B2 matches a filter containing UID for ABCVC.B2, it would incorrectly return true because "ABCVC" contains "ABCV" as a substring.

The Fix

Changed to use word-boundary matching:

  • Class names must be bounded by . (namespace/indices), < (generics), or + (nested types)
  • Method names must be preceded by . and followed by ., <, or (

This ensures ABCV only matches the exact class name, not substrings like ABCVC.

Test plan

  • Added reproduction test case in TUnit.TestProject/Bugs/4656/
  • Added integration tests in TUnit.Engine.Tests/OverlappingClassNameFilterTests.cs
  • Tests verify that filtering for ABCVC/B2 runs exactly 2 tests (not 4 if bug exists)
  • Tests verify that filtering for ABCV/* runs exactly 3 tests (not 6 if bug exists)
  • All tests pass in both SourceGenerated and Reflection modes

…inclusion (#4656)

When filtering tests by UID (e.g., from Visual Studio), class names like
"ABCV" would incorrectly match "ABCVC" because the filter used
string.Contains() for matching.

This fix changes CouldMatchUidFilter to use word-boundary matching:
- Class names must be bounded by '.', '<', or '+'
- Method names must be preceded by '.' and followed by '.', '<', or '('

This ensures "ABCV" only matches the exact class name, not substrings
like "ABCVC".

Fixes #4656
Instead of using word-boundary matching for class names, use exact
prefix matching with namespace.classname to ensure we don't match
classes with the same name in different namespaces.
@claude
Copy link
Contributor

claude bot commented Feb 3, 2026

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

The changes correctly fix the substring matching issue while maintaining proper word-boundary semantics for test UIDs. The implementation properly handles the test ID format and adds comprehensive test coverage.

@claude
Copy link
Contributor

claude bot commented Feb 3, 2026

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.


Review Summary

This PR effectively fixes issue #4656 where substring matching in UID filters caused incorrect test inclusion. The implementation is sound:

Correctness: The change from substring matching to prefix and word-boundary matching correctly prevents ABCV from matching ABCVC tests.

CLAUDE.md Compliance:

  • Operates in the unified execution path (post-metadata-collection), so dual-mode requirements do not apply
  • No source generator or public API changes, so snapshot tests are not required
  • Uses modern C# features appropriately (collection expressions, StringComparison.Ordinal)
  • Performance impact is minimal (filter evaluation is not a hot path)

Test Coverage: Comprehensive regression tests in both the engine tests and integration tests validate the fix works correctly in both source-generated and reflection modes.

The architectural approach is particularly elegant—by using namespace-aware prefix matching with proper delimiters, the fix ensures exact matches without needing complex regex patterns.

var backtickIndex = className.IndexOf('`');
if (backtickIndex > 0)
{
classNameForMatching = className.Substring(0, backtickIndex);
Copy link
Contributor

@TimothyMakkison TimothyMakkison Feb 4, 2026

Choose a reason for hiding this comment

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

classNameForMatching = className.AsSpan().Slice(0, backtickIndex); and make ReadOnlySpan<char> classNameForMatching

Could prolly use ValueStringBuilder for expectedClassPrefix and expectedGenericClassPrefix but that's pretty pendanitc

@thomhurst thomhurst merged commit a3d2446 into main Feb 4, 2026
14 of 15 checks passed
@thomhurst thomhurst deleted the fix/4656-overlapping-class-name-filter branch February 4, 2026 22:48
thomhurst pushed a commit that referenced this pull request Feb 5, 2026
…ching

The CouldMatchUidFilter method was failing to match tests when using
VS Test Explorer because:

1. For nested types (e.g., Outer+Inner), Type.Name only returns the
   innermost class name, but UIDs contain the full path with '+' separators

2. For classes with constructor parameters, UIDs contain '(' after the
   class name (e.g., MyClass(String).0.0.Method), but the code only
   checked for '.' or '<'

This fix:
- Adds BuildClassNameForMatching() to construct the full nested type
  hierarchy matching TestIdentifierService.WriteTypeNameWithGenerics
- Validates the character after the class name prefix is a valid
  boundary: '.', '<', or '('
- Maintains protection against substring matching (ABCV vs ABCVC)

Fixes #4656 (follow-up issue after PR #4659)

https://claude.ai/code/session_016KqnWq2pDNM7sDLSn6LvRe
This was referenced Feb 6, 2026
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.

[Bug]: [After(TestDiscovery)] TestDiscoveryContext.AllTests is incorrect

2 participants