using ExtractMethod.Tooling; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace BeforeAfter.Tests.ExtractMethod; /// /// Tests for yak em 01: the scaffold (console tool) and the checked-in demo /// fixture. The fixture is DATA (excluded from compilation, copied to the /// output dir), and it is parsed with the tool's own /// so these tests exercise the exact loading path the CLI uses. /// public class DemoFixtureTests { private static readonly string FixturePath = Path.Combine(AppContext.BaseDirectory, "Fixtures", "Demo.cs"); private static (SyntaxTree Tree, CSharpCompilation Compilation) LoadFixture() { Assert.True(File.Exists(FixturePath), $"fixture missing at {FixturePath}"); var tree = CompilationLoader.ParseFile(FixturePath); var compilation = CompilationLoader.CreateCompilation(tree, "DemoFixture"); return (tree, compilation); } // --------------------------------------------------------------------- // (a) The fixture is pristine under the scratch compilation: if this // fails, the fixture (or the TRUSTED_PLATFORM_ASSEMBLIES reference // loading) is broken and every later yak would analyze garbage. // --------------------------------------------------------------------- [Fact] public void Demo_compiles_with_no_diagnostics() { var (_, compilation) = LoadFixture(); Assert.Empty(compilation.GetDiagnostics()); } // --------------------------------------------------------------------- // (b) A known line range snaps to the expected whole statements. // Lines 68..72 of Demo.cs are `int total = 0;` followed by the whole // for-loop (for keyword .. closing brace) inside Summarize — exactly // the "multi-statement range incl. a for-loop" shape of the spec. // --------------------------------------------------------------------- [Fact] public void Known_range_resolves_to_expected_statements() { var (tree, _) = LoadFixture(); var report = SelectionResolver.Resolve(tree, 68, 72); Assert.True(report.Succeeded, report.Error); Assert.Equal(2, report.Count); Assert.Equal( new[] { SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement }, report.Kinds); Assert.Equal("Summarize", report.Method?.Identifier.ValueText); } // --------------------------------------------------------------------- // (c) The semantic model's data-flow analysis works on the fixture's // for-loop node — the foundation every classification in yak 02 // builds on. (AnalyzeDataFlow on the loop itself walks the bound loop // body; Succeeded == false would mean the scratch compilation or the // selected node cannot be bound.) // --------------------------------------------------------------------- [Fact] public void Fixture_for_loop_supports_data_flow_analysis() { var (tree, compilation) = LoadFixture(); var forStatement = tree.GetRoot() .DescendantNodes() .OfType() .Single(n => EnclosingMethodName(n) == "Summarize"); var dataFlow = compilation.GetSemanticModel(tree).AnalyzeDataFlow(forStatement); Assert.True(dataFlow.Succeeded); } // --------------------------------------------------------------------- // Acceptance also demands "clean error otherwise": selecting only the // for-loop header line (line 69) splits the statement and must fail with // a readable reason instead of a crash or a silent wrong count. // --------------------------------------------------------------------- [Fact] public void Range_ending_mid_statement_fails_cleanly() { var (tree, _) = LoadFixture(); var report = SelectionResolver.Resolve(tree, 69, 69); Assert.False(report.Succeeded); Assert.NotNull(report.Error); Assert.Contains("no whole", report.Error); } private static string? EnclosingMethodName(SyntaxNode node) => node.AncestorsAndSelf().OfType().FirstOrDefault()?.Identifier.ValueText; }