Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
d239ca0
fix: add OnTestFinalized event for proper resource management after r…
thomhurst Sep 28, 2025
1f325fd
fix: add EngineTest attribute to Issue3219DataClass for improved test…
thomhurst Sep 28, 2025
fe1b11a
fix: improve error handling during test finalization and clean up cod…
thomhurst Sep 28, 2025
808614e
fix: refactor test setup and disposal logic for improved clarity and …
thomhurst Sep 28, 2025
ee651e0
Merge branch 'main' into bug/3219
thomhurst Sep 28, 2025
d912581
fix: simplify reference counting for property injection
thomhurst Sep 29, 2025
cee7bc9
fix: simplify property injection reference counting
thomhurst Sep 29, 2025
62feadd
feat: Track property-injected ClassDataSource instances during test r…
thomhurst Sep 29, 2025
2469493
Merge branch 'main' into bug/3219
thomhurst Sep 29, 2025
dd25a5c
feat: Add XML configuration files for project settings and migration …
thomhurst Sep 30, 2025
927fed0
Refactor object initialization and registration services
thomhurst Sep 30, 2025
fdf18fb
refactor: Change public interfaces and records to internal visibility
thomhurst Sep 30, 2025
d383406
Merge branch 'main' into bug/3219
thomhurst Sep 30, 2025
e3f75d9
Implement code changes to enhance functionality and improve performance
thomhurst Sep 30, 2025
77611ce
refactor: streamline test result assertion and add failure logging
thomhurst Sep 30, 2025
b6211ce
Merge branch 'main' into bug/3219
thomhurst Sep 30, 2025
688eef7
fix: add null-forgiving operator to methodName and filePath variables
thomhurst Sep 30, 2025
2f092d8
refactor: modify hook execution methods to collect exceptions instead…
thomhurst Sep 30, 2025
8c3b33c
refactor: implement retry logic for test execution and enhance cleanu…
thomhurst Sep 30, 2025
439b0ae
refactor: enhance test scheduling by integrating hook executor and up…
thomhurst Sep 30, 2025
e335086
refactor: remove redundant ownership tracking from property lifecycle…
thomhurst Oct 1, 2025
f9ff3ff
refactor: improve property tracking logic in Reflection and Source Ge…
thomhurst Oct 1, 2025
d21f070
Merge branch 'main' into bug/3219
thomhurst Oct 1, 2025
9cabe1a
feat: enhance test scheduling with After(TestSession) hooks failure t…
thomhurst Oct 1, 2025
6711cbd
fix: improve error logging in RunWithFailureLogging method
thomhurst Oct 1, 2025
1850571
feat: implement TrackableObjectGraphProvider for retrieving trackable…
thomhurst Oct 1, 2025
1930831
Refactor: Remove unused namespaces and code
thomhurst Oct 1, 2025
fe139e6
Implement property initialization framework with data source handling
thomhurst Oct 1, 2025
2a17253
feat: enhance data source resolution by supporting properties and fie…
thomhurst Oct 1, 2025
f381c39
feat: implement ObjectTracker for managing trackable objects and thei…
thomhurst Oct 1, 2025
4a44ecc
feat: implement static property initialization and tracking framework
thomhurst Oct 1, 2025
62625b3
refactor: clean up unused event disposal logic and improve static pro…
thomhurst Oct 1, 2025
0e8cb91
refactor: rename ObjectTracker to StaticPropertyMetadata and update r…
thomhurst Oct 1, 2025
2fa38db
feat: introduce StaticPropertyHandler for managing static property in…
thomhurst Oct 1, 2025
aaa90df
refactor: enhance trackable object retrieval by preventing duplicate …
thomhurst Oct 1, 2025
790918f
feat: enhance object tracking by utilizing tracked objects in context
thomhurst Oct 2, 2025
169da49
refactor: simplify object tracking by removing redundant parameters a…
thomhurst Oct 2, 2025
c1c4bc3
Remove redundant assembly hooks
thomhurst Oct 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions .idea/.idea.TUnit/.idea/.gitignore

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions .idea/.idea.TUnit/.idea/encodings.xml

This file was deleted.

13 changes: 0 additions & 13 deletions .idea/.idea.TUnit/.idea/material_theme_project_new.xml

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/.idea.TUnit/.idea/vcs.xml

This file was deleted.

71 changes: 71 additions & 0 deletions .idea/.idea.TUnit/.idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

196 changes: 95 additions & 101 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,121 +1,115 @@
# TUnit Development Guide

## Critical Rules
1. **Behavioral parity** between source-generated and reflection modes is mandatory
2. **Run snapshot tests** when changing:
- Source generator output: `dotnet test TUnit.Core.SourceGenerator.Tests`
- Public APIs: `dotnet test TUnit.PublicAPI`
3. **Accept snapshots**: Rename `.received.txt` → `.verified.txt` and commit
4. **Use Microsoft.Testing.Platform**, never VSTest
5. **Performance is paramount** - this framework may be used by millions

## Architecture

### Dual Execution Modes
- **Source Generated**: Compile-time generation for performance
- **Reflection**: Runtime for dynamic scenarios
- Both must produce identical behavior

### Core Components
- **TUnit.Core**: Abstractions and interfaces
- **TUnit.Engine**: Test discovery and execution
- **TUnit.Core.SourceGenerator**: Compile-time generation
- **TUnit.Assertions**: Fluent assertions
- **TUnit.Analyzers**: Compile-time validation

## Coding Standards

### Modern C#
- Collection initializers: `List<string> list = []`
- Pattern matching, records, file-scoped namespaces
- `var` for obvious types
- `ValueTask` for potentially synchronous operations

### Formatting
# TUnit Development Guide for LLM Agents

## MANDATORY RULES - ALWAYS FOLLOW
1. **Dual-mode implementation required**: ALL changes must work identically in both source-generated and reflection modes
2. **Snapshot tests are critical**: After ANY change to source generator or public APIs, you MUST run and accept snapshots
3. **Never use VSTest**: This project uses Microsoft.Testing.Platform exclusively
4. **Performance first**: Optimize for speed - this framework is used by millions

## When You Make Changes

### If you modify source generator code:
1. Run: `dotnet test TUnit.Core.SourceGenerator.Tests`
2. If snapshots differ, rename ALL `*.received.txt` files to `*.verified.txt`
3. Commit the updated `.verified.txt` files

### If you modify public APIs:
1. Run: `dotnet test TUnit.PublicAPI`
2. If snapshots differ, rename ALL `*.received.txt` files to `*.verified.txt`
3. Commit the updated `.verified.txt` files

### If you add a feature:
1. Implement in BOTH `TUnit.Core.SourceGenerator` AND `TUnit.Engine` (reflection path)
2. Verify identical behavior in both modes
3. Add tests covering both execution paths
4. Consider if an analyzer rule would help prevent misuse
5. Test performance impact

### If you fix a bug:
1. Write a failing test first
2. Fix in BOTH execution modes (source-gen and reflection)
3. Verify no performance regression

## Project Structure
- `TUnit.Core`: Abstractions, interfaces, attributes
- `TUnit.Engine`: Test discovery and execution (reflection mode)
- `TUnit.Core.SourceGenerator`: Compile-time code generation (source-gen mode)
- `TUnit.Assertions`: Fluent assertion library
- `TUnit.Analyzers`: Roslyn analyzers for compile-time validation

## Code Style (REQUIRED)
```csharp
// Modern C# syntax - always use
List<string> list = []; // Collection expressions
var result = GetValue(); // var for obvious types

// Always use braces
if (condition)
{
DoSomething(); // Always use braces
DoSomething();
}
```
- PascalCase public, _camelCase private fields
- Expression-bodied members for simple logic
- Meaningful names over comments

### Performance
- Minimize allocations in hot paths
- Object pooling for frequent allocations
- Cache reflection results
- Profile discovery and execution paths

## Development Workflow

### Adding Features
1. Implement in both execution modes
2. Add analyzer rules if applicable
3. Write comprehensive tests
4. Verify performance impact
5. Update documentation
// Naming
public string PublicField; // PascalCase
private string _privateField; // _camelCase

### Fixing Bugs
1. Write failing test
2. Fix in all affected modes
3. Verify no performance regression
// Async
async ValueTask DoWorkAsync(CancellationToken cancellationToken) // ValueTask when possibly sync
```

### Before Submitting
- [ ] Both modes tested
- [ ] Source generator snapshots accepted (if changed)
- [ ] Public API snapshots accepted (if changed)
- [ ] Performance considered
- [ ] No breaking changes
## Performance Guidelines
- Minimize allocations in hot paths (discovery/execution)
- Use object pooling for frequent allocations
- Cache reflection results
- Benchmark critical paths before/after changes

## Quick Commands
## Common Commands
```bash
# Run all tests
# Test everything
dotnet test

# Source generator tests
# Test source generator specifically
dotnet test TUnit.Core.SourceGenerator.Tests

# Public API tests
# Test public API surface
dotnet test TUnit.PublicAPI

# Accept snapshots (Windows)
# Accept snapshots (Windows - use this after verifying diffs are correct)
for %f in (*.received.txt) do move /Y "%f" "%~nf.verified.txt"

# Accept snapshots (Linux/macOS)
for file in *.received.txt; do mv "$file" "${file%.received.txt}.verified.txt"; done

# Run specific test
# Run specific test by filter
dotnet test -- --treenode-filter "/Assembly/Namespace/ClassName/TestName"
```

## Key Patterns
- **Error Handling**: Specific exceptions with context
- **Async**: `CancellationToken` support throughout
- **Reflection**: AOT-friendly with `[UnconditionalSuppressMessage]`
- **Threading**: Ensure concurrent test safety
- **Disposal**: Proper resource cleanup

## Testing Categories
- Unit tests for components
- Integration for cross-component
- Performance benchmarks for critical paths
- Analyzer tests for compile-time rules
- Snapshot tests for generator and API surface

## Compatibility
- .NET Standard 2.0, .NET 6, 8, 9+
- AOT and trimming support
- Various project configurations

## Common Pitfalls
1. Mode inconsistency between source-gen and reflection
2. Performance regressions in discovery/execution
3. AOT/trimming issues with reflection
4. Thread safety in concurrent execution
5. Resource leaks from improper disposal
6. Forgetting to accept intentional snapshot changes

## Remember
Every change must maintain TUnit's goals: **fast, modern, reliable, and enjoyable to use**.
## AOT/Trimming Compatibility
- Use `[UnconditionalSuppressMessage]` for known-safe reflection
- Test with trimming/AOT enabled projects
- Avoid dynamic code generation at runtime (use source generators instead)

## Threading and Safety
- All test execution must be thread-safe
- Use proper synchronization for shared state
- Dispose resources correctly (implement IDisposable/IAsyncDisposable)

## Critical Mistakes to Avoid
1. ❌ Implementing a feature only in source-gen mode (must do BOTH)
2. ❌ Breaking change to public API without major version bump
3. ❌ Forgetting to accept snapshots after intentional generator changes
4. ❌ Performance regression in discovery or execution
5. ❌ Using reflection in ways incompatible with AOT/trimming

## Verification Checklist
Before completing any task, verify:
- [ ] Works in both source-generated and reflection modes
- [ ] Snapshots accepted if generator/API changed
- [ ] Tests added and passing
- [ ] No performance regression
- [ ] AOT/trimming compatible
- [ ] Thread-safe if touching concurrent code

## Target Frameworks
- .NET Standard 2.0 (library compatibility)
- .NET 6, 8, 9+ (current support)

## Philosophy
TUnit aims to be: **fast, modern, reliable, and enjoyable to use**. Every change should advance these goals.
25 changes: 20 additions & 5 deletions TUnit.Analyzers/TestDataAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ private void CheckMethodDataSource(SymbolAnalysisContext context,
.ToArray()
: Array.Empty<ITypeSymbol>();

// Look for methods first
var methodSymbols = (type as INamedTypeSymbol)?.GetSelfAndBaseTypes()
.SelectMany(x => x.GetMembers())
.OfType<IMethodSymbol>()
Expand All @@ -440,13 +441,27 @@ private void CheckMethodDataSource(SymbolAnalysisContext context,
MatchesParameters(context, argumentForMethodCallTypes, methodSymbol))
?? methodSymbols.FirstOrDefault(x => x.Name == methodName);

// If no method found, check for properties
if (dataSourceMethod is null)
{
context.ReportDiagnostic(
Diagnostic.Create(
Rules.NoMethodFound,
attribute.GetLocation())
);
var propertySymbols = (type as INamedTypeSymbol)?.GetSelfAndBaseTypes()
.SelectMany(x => x.GetMembers())
.OfType<IPropertySymbol>()
.ToArray() ?? Array.Empty<IPropertySymbol>();

var dataSourceProperty = propertySymbols.FirstOrDefault(x => x.Name == methodName);

if (dataSourceProperty is null)
{
context.ReportDiagnostic(
Diagnostic.Create(
Rules.NoMethodFound,
attribute.GetLocation())
);
return;
}

// Properties are valid data sources, no need to check return type for void
return;
}

Expand Down
Loading
Loading