@@ -29,28 +29,50 @@ public static partial class OrderIdentityMap
2929 ScenarioExpect . Empty ( result . Diagnostics ) ;
3030 ScenarioExpect . Contains ( "CreateMap()" , ScenarioExpect . Single ( result . GeneratedSources ) ) ;
3131 ScenarioExpect . Contains ( "IdentityMap<global::Demo.Order, string>.Create(SelectKey).Build()" , result . GeneratedSources [ 0 ] ) ;
32+ ScenarioExpect . True ( result . EmitSuccess , string . Join ( Environment . NewLine , result . EmitDiagnostics ) ) ;
3233 } )
3334 . AssertPassed ( ) ;
3435
35- [ Scenario ( "Generator emits internal struct factory in the global namespace " ) ]
36+ [ Scenario ( "Generator emits identity map defaults and host shapes " ) ]
3637 [ Fact ]
37- public Task Generator_Emits_Internal_Struct_Factory_In_The_Global_Namespace ( )
38- => Given ( "a valid internal struct declaration " , ( ) => Compile ( """
38+ public Task Generator_Emits_Identity_Map_Defaults_And_Host_Shapes ( )
39+ => Given ( "valid identity map declarations with default names and host shapes " , ( ) => Compile ( """
3940 using PatternKit.Generators.IdentityMap;
41+ namespace Demo;
4042 public sealed record Order(string Id);
43+
44+ [GenerateIdentityMap(typeof(Order), typeof(string))]
45+ internal abstract partial class AbstractIdentityMap
46+ {
47+ [IdentityMapKeySelector]
48+ private static string SelectKey(Order order) => order.Id;
49+ }
50+
4151 [GenerateIdentityMap(typeof(Order), typeof(string))]
42- internal partial struct OrderIdentityMap
52+ public sealed partial class SealedIdentityMap
53+ {
54+ [IdentityMapKeySelector]
55+ private static string SelectKey(Order order) => order.Id;
56+ }
57+
58+ [GenerateIdentityMap(typeof(Order), typeof(string))]
59+ internal partial struct StructIdentityMap
4360 {
4461 [IdentityMapKeySelector]
4562 private static string SelectKey(Order order) => order.Id;
4663 }
4764 """ ) )
48- . Then ( "generated source keeps the declaration shape and default factory name " , result =>
65+ . Then ( "generated source keeps declaration shapes and default factory names " , result =>
4966 {
5067 ScenarioExpect . Empty ( result . Diagnostics ) ;
51- ScenarioExpect . Contains ( "internal partial struct OrderIdentityMap" , ScenarioExpect . Single ( result . GeneratedSources ) ) ;
52- ScenarioExpect . Contains ( "Create()" , result . GeneratedSources [ 0 ] ) ;
53- ScenarioExpect . DoesNotContain ( "namespace" , result . GeneratedSources [ 0 ] ) ;
68+ ScenarioExpect . Equal ( 3 , result . GeneratedSources . Count ) ;
69+
70+ var combined = string . Join ( "\n " , result . GeneratedSources ) ;
71+ ScenarioExpect . Contains ( "internal abstract partial class AbstractIdentityMap" , combined ) ;
72+ ScenarioExpect . Contains ( "public sealed partial class SealedIdentityMap" , combined ) ;
73+ ScenarioExpect . Contains ( "internal partial struct StructIdentityMap" , combined ) ;
74+ ScenarioExpect . Contains ( "Create()" , combined ) ;
75+ ScenarioExpect . True ( result . EmitSuccess , string . Join ( Environment . NewLine , result . EmitDiagnostics ) ) ;
5476 } )
5577 . AssertPassed ( ) ;
5678
@@ -61,6 +83,8 @@ internal partial struct OrderIdentityMap
6183 [ InlineData ( "public static partial class OrderIdentityMap { [IdentityMapKeySelector] private static string SelectKey(Order order) => order.Id; [IdentityMapKeySelector] private static string SelectAlternate(Order order) => order.Id; }" , "PKIM002" ) ]
6284 [ InlineData ( "public static partial class OrderIdentityMap { [IdentityMapKeySelector] private static int SelectKey(Order order) => 1; }" , "PKIM003" ) ]
6385 [ InlineData ( "public static partial class OrderIdentityMap { [IdentityMapKeySelector] private string SelectKey(Order order) => order.Id; }" , "PKIM003" ) ]
86+ [ InlineData ( "public static partial class OrderIdentityMap { [IdentityMapKeySelector] private static string SelectKey() => string.Empty; }" , "PKIM003" ) ]
87+ [ InlineData ( "public static partial class OrderIdentityMap { [IdentityMapKeySelector] private static string SelectKey(string order) => order; }" , "PKIM003" ) ]
6488 public Task Generator_Reports_Invalid_Identity_Map_Declarations ( string declaration , string diagnosticId )
6589 => Given ( "an invalid identity map declaration" , ( ) => Compile ( $$ """
6690 using PatternKit.Generators.IdentityMap;
@@ -72,18 +96,94 @@ public sealed record Order(string Id);
7296 ScenarioExpect . Contains ( result . Diagnostics , diagnostic => diagnostic . Id == diagnosticId ) )
7397 . AssertPassed ( ) ;
7498
99+ [ Scenario ( "Generator emits nested identity map host wrappers" ) ]
100+ [ Fact ]
101+ public Task Generator_Emits_Nested_Identity_Map_Host_Wrappers ( )
102+ => Given ( "nested identity map declarations" , ( ) => Compile ( """
103+ using PatternKit.Generators.IdentityMap;
104+ namespace Demo;
105+ public sealed record Order(string Id);
106+
107+ public partial class IdentityMapContainer
108+ {
109+ private partial class PrivateHost
110+ {
111+ [GenerateIdentityMap(typeof(Order), typeof(string))]
112+ protected partial class ProtectedIdentityMap
113+ {
114+ [IdentityMapKeySelector]
115+ private static string SelectKey(Order order) => order.Id;
116+ }
117+
118+ [GenerateIdentityMap(typeof(Order), typeof(string))]
119+ private protected partial class PrivateProtectedIdentityMap
120+ {
121+ [IdentityMapKeySelector]
122+ private static string SelectKey(Order order) => order.Id;
123+ }
124+
125+ [GenerateIdentityMap(typeof(Order), typeof(string))]
126+ protected internal partial class ProtectedInternalIdentityMap
127+ {
128+ [IdentityMapKeySelector]
129+ private static string SelectKey(Order order) => order.Id;
130+ }
131+ }
132+ }
133+ """ ) )
134+ . Then ( "generated sources preserve containing partial type wrappers" , result =>
135+ {
136+ ScenarioExpect . Empty ( result . Diagnostics ) ;
137+ ScenarioExpect . Equal ( 3 , result . GeneratedSources . Count ) ;
138+
139+ var combined = string . Join ( "\n " , result . GeneratedSources ) ;
140+ ScenarioExpect . Contains ( "public partial class IdentityMapContainer" , combined ) ;
141+ ScenarioExpect . Contains ( "private partial class PrivateHost" , combined ) ;
142+ ScenarioExpect . Contains ( "protected partial class ProtectedIdentityMap" , combined ) ;
143+ ScenarioExpect . Contains ( "private protected partial class PrivateProtectedIdentityMap" , combined ) ;
144+ ScenarioExpect . Contains ( "protected internal partial class ProtectedInternalIdentityMap" , combined ) ;
145+ ScenarioExpect . True ( result . EmitSuccess , string . Join ( Environment . NewLine , result . EmitDiagnostics ) ) ;
146+ } )
147+ . AssertPassed ( ) ;
148+
149+ [ Scenario ( "Generator skips malformed identity map type arguments" ) ]
150+ [ Theory ]
151+ [ InlineData ( "null!" , "typeof(string)" ) ]
152+ [ InlineData ( "typeof(Order)" , "null!" ) ]
153+ public Task Generator_Skips_Malformed_Identity_Map_Type_Arguments ( string entityType , string keyType )
154+ => Given ( "an identity map declaration with a null type argument" , ( ) => Compile ( $$ """
155+ using PatternKit.Generators.IdentityMap;
156+ public sealed record Order(string Id);
157+ [GenerateIdentityMap({{ entityType }} , {{ keyType }} )]
158+ public static partial class OrderIdentityMap
159+ {
160+ [IdentityMapKeySelector]
161+ private static string SelectKey(Order order) => order.Id;
162+ }
163+ """ ) )
164+ . Then ( "no source is generated" , result =>
165+ ScenarioExpect . Empty ( result . GeneratedSources ) )
166+ . AssertPassed ( ) ;
167+
75168 private static GeneratorResult Compile ( string source )
76169 {
77170 var compilation = RoslynTestHelpers . CreateCompilation (
78171 source ,
79172 "IdentityMapGeneratorTests" ,
80173 extra : MetadataReference . CreateFromFile ( typeof ( IdentityMap < , > ) . Assembly . Location ) ) ;
81- _ = RoslynTestHelpers . Run ( compilation , new IdentityMapGenerator ( ) , out var run , out _ ) ;
174+ _ = RoslynTestHelpers . Run ( compilation , new IdentityMapGenerator ( ) , out var run , out var updated ) ;
82175 var result = run . Results . Single ( ) ;
176+ var emit = updated . Emit ( Stream . Null ) ;
83177 return new GeneratorResult (
84178 result . Diagnostics . ToArray ( ) ,
85- result . GeneratedSources . Select ( static source => source . SourceText . ToString ( ) ) . ToArray ( ) ) ;
179+ result . GeneratedSources . Select ( static source => source . SourceText . ToString ( ) ) . ToArray ( ) ,
180+ emit . Success ,
181+ emit . Diagnostics . Select ( static diagnostic => diagnostic . ToString ( ) ) . ToArray ( ) ) ;
86182 }
87183
88- private sealed record GeneratorResult ( IReadOnlyList < Diagnostic > Diagnostics , IReadOnlyList < string > GeneratedSources ) ;
184+ private sealed record GeneratorResult (
185+ IReadOnlyList < Diagnostic > Diagnostics ,
186+ IReadOnlyList < string > GeneratedSources ,
187+ bool EmitSuccess ,
188+ IReadOnlyList < string > EmitDiagnostics ) ;
89189}
0 commit comments