"""Tests for IL and C# method extraction from decompiled output.""" from mcilspy.il_parser import extract_csharp_method, extract_il_method # ── IL extraction fixtures ──────────────────────────────────────────── SAMPLE_IL = """\ .class public auto ansi beforefieldinit MyNamespace.MyClass extends [mscorlib]System.Object { .method public hidebysig instance void DoElaborate ( int32 x ) cil managed { .maxstack 8 IL_0000: nop IL_0001: ldarg.1 IL_0002: call instance void MyNamespace.MyClass::Helper(int32) IL_0007: nop IL_0008: ret } // end of method MyClass::DoElaborate .method public hidebysig instance void DoElaborate ( int32 x, string y ) cil managed { .maxstack 8 IL_0000: nop IL_0001: ret } // end of method MyClass::DoElaborate .method private hidebysig instance void Helper ( int32 val ) cil managed { .maxstack 8 IL_0000: nop IL_0001: ret } // end of method MyClass::Helper } // end of class MyNamespace.MyClass """ SAMPLE_CSHARP = """\ namespace MyNamespace { public class MyClass { public void DoElaborate(int x) { Helper(x); } public void DoElaborate(int x, string y) { Console.WriteLine(y); } private void Helper(int val) { // internal helper } public static async Task ProcessAsync( string input, CancellationToken ct) { await Task.Delay(100, ct); return true; } } } """ # ── IL extraction tests ────────────────────────────────────────────── class TestExtractILMethod: """Tests for extract_il_method().""" def test_extracts_single_method(self): """Should extract the Helper method.""" results = extract_il_method(SAMPLE_IL, "Helper") assert len(results) == 1 assert ".method private hidebysig" in results[0] assert "end of method MyClass::Helper" in results[0] def test_extracts_overloaded_methods(self): """Should extract both DoElaborate overloads.""" results = extract_il_method(SAMPLE_IL, "DoElaborate") assert len(results) == 2 # First overload has one param assert "int32 x" in results[0] # Second overload has two params assert "string y" in results[1] def test_returns_empty_for_nonexistent_method(self): """Should return empty list when method doesn't exist.""" results = extract_il_method(SAMPLE_IL, "NonExistent") assert results == [] def test_returns_empty_for_empty_input(self): """Should handle empty inputs gracefully.""" assert extract_il_method("", "Foo") == [] assert extract_il_method(SAMPLE_IL, "") == [] assert extract_il_method("", "") == [] def test_method_name_is_exact_match(self): """Should not match partial method names.""" results = extract_il_method(SAMPLE_IL, "Do") assert results == [] def test_method_name_is_exact_match_helper(self): """Should not match 'Help' when looking for 'Helper'.""" results = extract_il_method(SAMPLE_IL, "Help") assert results == [] def test_preserves_method_body(self): """Extracted method should include the full body.""" results = extract_il_method(SAMPLE_IL, "Helper") assert len(results) == 1 assert ".maxstack 8" in results[0] assert "IL_0000: nop" in results[0] assert "IL_0001: ret" in results[0] # ── C# extraction tests ────────────────────────────────────────────── class TestExtractCSharpMethod: """Tests for extract_csharp_method().""" def test_extracts_single_method(self): """Should extract the Helper method.""" results = extract_csharp_method(SAMPLE_CSHARP, "Helper") assert len(results) == 1 assert "private void Helper" in results[0] assert "// internal helper" in results[0] def test_extracts_overloaded_methods(self): """Should extract both DoElaborate overloads.""" results = extract_csharp_method(SAMPLE_CSHARP, "DoElaborate") assert len(results) == 2 def test_returns_empty_for_nonexistent_method(self): """Should return empty list when method doesn't exist.""" results = extract_csharp_method(SAMPLE_CSHARP, "NonExistent") assert results == [] def test_returns_empty_for_empty_input(self): """Should handle empty inputs gracefully.""" assert extract_csharp_method("", "Foo") == [] assert extract_csharp_method(SAMPLE_CSHARP, "") == [] def test_extracts_multiline_signature(self): """Should handle method signatures that span multiple lines.""" results = extract_csharp_method(SAMPLE_CSHARP, "ProcessAsync") assert len(results) == 1 assert "async Task" in results[0] assert "CancellationToken ct" in results[0] assert "return true;" in results[0] def test_preserves_method_body(self): """Extracted method should include the full body.""" results = extract_csharp_method(SAMPLE_CSHARP, "Helper") assert len(results) == 1 assert "// internal helper" in results[0] def test_method_name_is_exact_match(self): """Should not match partial method names via regex anchoring.""" # "Do" should not match "DoElaborate" because the pattern requires # the name followed by optional generics and opening paren results = extract_csharp_method(SAMPLE_CSHARP, "Do") assert results == [] # ── Edge case tests ─────────────────────────────────────────────────── class TestEdgeCases: """Tests for edge cases in both extractors.""" def test_il_method_with_special_chars_in_name(self): """Methods with dots or special chars in type name should work.""" il = """\ .method public hidebysig instance void Process () cil managed { .maxstack 8 IL_0000: ret } // end of method Outer+Nested::Process """ results = extract_il_method(il, "Process") assert len(results) == 1 def test_csharp_method_with_generic_return(self): """Should handle generic return types.""" cs = """\ public class Foo { public List> Transform(IEnumerable input) { return new(); } } """ results = extract_csharp_method(cs, "Transform") assert len(results) == 1 assert "return new();" in results[0] def test_csharp_method_expression_body(self): """Expression-bodied methods should be extracted (they use => not {}).""" cs = """\ public class Foo { public int GetValue() { return 42; } } """ results = extract_csharp_method(cs, "GetValue") assert len(results) == 1 assert "return 42;" in results[0]