-
-
Notifications
You must be signed in to change notification settings - Fork 108
refactor: implement two-phase architecture for xUnit migration code fixer #4477
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…ixer This change introduces a robust two-phase migration architecture that separates semantic analysis from syntax transformation, eliminating semantic model staleness issues that plagued the previous rewriter-based approach. Key changes: **New Two-Phase Architecture:** - Phase 1 (Analysis): Collects all conversion targets while semantic model is valid - Phase 2 (Transformation): Applies pure syntax transformations using annotations - Uses SyntaxAnnotation to track nodes across tree modifications **New Files:** - `Base/TwoPhase/MigrationAnalyzer.cs` - Base class for framework analyzers - `Base/TwoPhase/MigrationTransformer.cs` - Syntax transformation engine - `Base/TwoPhase/ConversionPlan.cs` - Data structures for conversion targets - `TwoPhase/XUnitTwoPhaseAnalyzer.cs` - xUnit-specific analyzer implementation - `Base/MigrationContext.cs` - Error tracking for legacy fallback path **xUnit Migration Improvements:** - Assert.Throws/ThrowsAsync now kept unchanged (TUnit has compatible API) - .Because() message handling for True/False assertions - Proper [Before(Test)]/[After(Test)] attribute generation - TheoryData<T> → IEnumerable<T> conversion - Record.Exception → try-catch block generation - ITestOutputHelper → Console.WriteLine conversion **Code Cleanup:** - Removed 1,762 lines of legacy rewriter code from XUnitMigrationCodeFixProvider - File reduced from 1,849 lines to 87 lines (95% reduction) - Legacy code replaced with simple pass-through implementations All 551 analyzer tests pass.
SummaryThis PR refactors the xUnit migration code fixer to use a two-phase architecture that separates semantic analysis from syntax transformation, reducing code from 1,849 to 87 lines (95% reduction) and eliminating semantic model staleness issues. Critical IssuesNone found ✅ Suggestions1. Exception Handling GranularityThe PR adds broad catch (Exception ex) blocks throughout the two-phase implementation. While this provides robustness for migration failures, consider that some exceptions (e.g., OutOfMemoryException, StackOverflowException) should potentially abort rather than being silently recorded. Impact is low since code fixers are isolated operations. 2. Allocation PatternsMultiple .ToList() calls in analysis phase occur during code fixer execution (not hot paths). This is acceptable since code fixers run on-demand, not during test execution. 3. Testing CoverageThe PR description states all 551 analyzer tests pass. No .verified.txt or .received.txt changes in the diff suggests output is identical to before (expected for refactor). TUnit Rule Compliance✅ Dual-Mode: N/A - Changes are to analyzer/code fixer, not runtime engine Verdict✅ APPROVE - No critical issues This is a well-executed refactoring that eliminates semantic model staleness bugs through proper separation of concerns, reduces complexity dramatically, maintains backward compatibility, and follows modern C# patterns with SyntaxAnnotation for node tracking. |
…ions Implement previously missing xUnit assertion equivalents: - IsStrictlyEqualTo/IsNotStrictlyEqualTo: Uses object.Equals() instead of IEquatable<T> for strict equality comparison - PropertyChanged/PropertyChangedAsync: Monitor INotifyPropertyChanged events - Raises/RaisesAsync/RaisesAny/RaisesAnyAsync: Monitor event raising with type-specific or base EventArgs Update XUnitTwoPhaseAnalyzer to convert Assert.StrictEqual to the new TUnit fluent assertion. PropertyChanged and Raises assertions use the same API as xUnit, so they migrate via namespace change only. Add comprehensive tests for all new assertions.
SummaryThis PR refactors the xUnit migration code fixer to use a two-phase architecture (semantic analysis → syntax transformation), eliminating semantic model staleness issues and reducing XUnitMigrationCodeFixProvider by 95% (1,849 → 87 lines). Critical IssuesNone found ✅ TUnit Rules Compliance✅ No dual-mode requirements - Changes isolated to code fixer infrastructure ObservationsArchitecture improvements:
Code quality:
Minor notes:
Verdict✅ APPROVE - Well-designed refactor with no TUnit rule violations or critical issues detected |
- Add NUnitTwoPhaseAnalyzer and MSTestTwoPhaseAnalyzer for semantic
analysis phase before syntax transformation
- Add NUnitAssertMultipleRewriter to convert Assert.Multiple(() => {})
to using (Assert.Multiple()) {}
- Fix ExpectedResult handling: properly rename TestCase to Arguments
and add System.Threading.Tasks using after post-transformations
- Fix ExpectedException handling: make methods async when wrapping
body in await Assert.ThrowsAsync<T>()
- Fix ref/out parameter handling: use .Wait() instead of await since
C# doesn't allow ref/out parameters in async methods
- Update test expectations to match correct property ordering and
constraint handling
All 130 NUnit migration tests now pass.
- Add test for multiple assertions in ref parameter method - Add test for Assert.Multiple inside ref parameter method Both tests verify that .Wait() is used instead of await when the containing method has ref/out parameters.
SummaryThis PR introduces a two-phase migration architecture for the xUnit code fixer, separating semantic analysis (Phase 1) from syntax transformation (Phase 2) to eliminate semantic model staleness issues. Critical IssuesNone found ✅ Suggestions1. Missing Assertion Tests for New Event AssertionsThe PR includes new event assertion methods (PropertyChanged, PropertyChangedAsync, Raises, RaisesAsync, RaisesAny, RaisesAnyAsync) in TUnit.Assertions/Extensions/Assert.cs but these appear to be primarily tested in EventAssertionTests.cs. Consider whether additional test coverage is needed for edge cases like:
2. New StrictEquality AssertionsThe new IsStrictlyEqualTo and IsNotStrictlyEqualTo assertions (TUnit.Assertions/Assertions/StrictEqualityAssertions.cs:16-24) use object.Equals which may have surprising behavior with value types due to boxing. Consider adding a doc comment warning about this, or examples showing when to use strict vs regular equality. 3. Xunit.Sdk Using RemovalGood catch adding Xunit.Sdk to the usings removal list (TUnit.Analyzers/Migrators/Base/MigrationHelpers.cs:183). This prevents leftover xUnit SDK references. ObservationsArchitecture QualityThe two-phase architecture is well-designed:
Performance Considerations
Error Handling
Test UpdatesTest changes show the migration improvements:
Verdict✅ APPROVE - No critical issues This is a solid refactoring that addresses a real problem (semantic model staleness) with a clean architectural solution. The two-phase approach is well-executed, maintains backward compatibility through the legacy fallback path, and improves code quality significantly (95% reduction in XUnitMigrationCodeFixProvider as noted in the PR description). |
SummaryImplements a robust two-phase architecture for the xUnit migration code fixer, separating semantic analysis from syntax transformation to eliminate semantic model staleness issues. Critical IssuesMissing PublicAPI Snapshot Updates This PR adds 6 new public methods to
According to TUnit Rule #2 (Snapshot Testing), changes to public APIs require running snapshot tests and updating dotnet test TUnit.PublicAPI
# Then review and accept the changes (Linux/macOS)
for f in *.received.txt; do mv "$f" "$\{f%.received.txt\}.verified.txt"; done
git add *.verified.txtSee CLAUDE.md and .claude/docs/mandatory-rules.md for details. SuggestionsNone - the two-phase architecture is well-designed and the code quality is excellent. Verdict |
Add new test coverage inspired by NUnit's extensive test suite: xUnit tests added (7): - ref/out parameter handling (use .Wait() instead of async) - Interface implementation preservation - Nested class migration - Multiple classes in file - Record.Exception DoesNotThrow pattern - Generic test class migration MSTest tests added (3): - ref/out parameter handling - Interface implementation preservation Also fixes MSTest format string and comparer detection in assertions.
SummaryImplements a two-phase architecture for the xUnit migration code fixer, separating semantic analysis from syntax transformation and reducing XUnitMigrationCodeFixProvider by 95% (1,849 → 87 lines). Critical IssuesNone found ✅ Previous Review StatusPrevious review requested PublicAPI snapshot updates - this has been ADDRESSED ✅
TUnit Rules Compliance✅ Dual-Mode: N/A - Changes isolated to code fixer/analyzer infrastructure ObservationsArchitecture Quality:
New Assertion APIs:
Test Coverage:
Verdict✅ APPROVE - Excellent refactoring with no critical issues or TUnit rule violations This is a well-executed architectural improvement that solves real semantic model staleness bugs while dramatically simplifying the codebase. The two-phase design is clean, maintainable, and follows modern C# patterns. |
Summary
This PR introduces a robust two-phase migration architecture for the xUnit code fixer that separates semantic analysis from syntax transformation, eliminating semantic model staleness issues.
Key Improvements
New Files
Base/TwoPhase/MigrationAnalyzer.cs- Base class for framework analyzersBase/TwoPhase/MigrationTransformer.cs- Syntax transformation engineBase/TwoPhase/ConversionPlan.cs- Data structures for conversion targetsTwoPhase/XUnitTwoPhaseAnalyzer.cs- xUnit-specific implementationBase/MigrationContext.cs- Error tracking for legacy fallbackxUnit Conversions Supported
Test plan