1+ // -----------------------------------------------------------------------
2+ // <copyright file="ShouldNotInvokeContextMaterializerMultipleTimesAnalyzerSpecs.cs" company="Akka.NET Project">
3+ // Copyright (C) 2013-2025 .NET Foundation <https://github.com/akkadotnet/akka.net>
4+ // </copyright>
5+ // -----------------------------------------------------------------------
6+
7+ using Microsoft . CodeAnalysis . Testing ;
8+ using Verify = Akka . Analyzers . Tests . Utility . AkkaVerifier < Akka . Analyzers . ShouldNotInvokeContextMaterializerMultipleTimesAnalyzer > ;
9+
10+ namespace Akka . Analyzers . Tests . Analyzers . AK2000 ;
11+
12+ public class ShouldNotInvokeContextMaterializerMultipleTimesAnalyzerSpecs
13+ {
14+ public static readonly TheoryData < string > SuccessCases = new ( )
15+ {
16+ // Using ActorSystem.Materializer from a non-actor class should not trigger any warnings
17+ """
18+ using System.Linq;
19+ using Akka.Actor;
20+ using Akka.Streams;
21+ using Akka.Streams.Dsl;
22+
23+ public class MyClass
24+ {
25+ public MyClass(ActorSystem system)
26+ {
27+ var mat1 = system.Materializer();
28+ var mat2 = system.Materializer();
29+
30+ var source1 = Source.From(Enumerable.Range(0, 100))
31+ .RunWith(Sink.Ignore<int>(), system.Materializer());
32+ var source2 = Source.From(Enumerable.Range(0, 100))
33+ .RunWith(Sink.Ignore<int>(), system.Materializer());
34+ }
35+ }
36+ """ ,
37+ // Using ActorSystem.Materializer from an actor class should not trigger any warnings
38+ """
39+ using System.Linq;
40+ using Akka.Actor;
41+ using Akka.Streams;
42+ using Akka.Streams.Dsl;
43+
44+ public class MyActor: ReceiveActor
45+ {
46+ public MyActor()
47+ {
48+ var mat1 = Context.System.Materializer();
49+ var mat2 = Context.System.Materializer();
50+
51+ var source1 = Source.From(Enumerable.Range(0, 100))
52+ .RunWith(Sink.Ignore<int>(), Context.System.Materializer());
53+ var source2 = Source.From(Enumerable.Range(0, 100))
54+ .RunWith(Sink.Ignore<int>(), Context.System.Materializer());
55+ }
56+ }
57+ """ ,
58+ // Using ActorSystem.Materializer from an actor class should not trigger any warnings
59+ """
60+ using System.Linq;
61+ using Akka.Actor;
62+ using Akka.Streams;
63+ using Akka.Streams.Dsl;
64+
65+ public class MyActor: ReceiveActor
66+ {
67+ public MyActor()
68+ {
69+ var system = Context.System;
70+ var mat1 = system.Materializer();
71+ var mat2 = system.Materializer();
72+
73+ var source1 = Source.From(Enumerable.Range(0, 100))
74+ .RunWith(Sink.Ignore<int>(), system.Materializer());
75+ var source2 = Source.From(Enumerable.Range(0, 100))
76+ .RunWith(Sink.Ignore<int>(), system.Materializer());
77+ }
78+ }
79+ """ ,
80+ // Using ActorMaterializerExtensions.Materializer() with ActorSystem argument from an actor class should not trigger any warnings
81+ """
82+ using System.Linq;
83+ using Akka.Actor;
84+ using Akka.Streams;
85+ using Akka.Streams.Dsl;
86+
87+ public class MyActor: ReceiveActor
88+ {
89+ public MyActor()
90+ {
91+ var system = Context.System;
92+ var mat1 = ActorMaterializerExtensions.Materializer(system);
93+ var mat2 = ActorMaterializerExtensions.Materializer(system);
94+
95+ var source1 = Source.From(Enumerable.Range(0, 100))
96+ .RunWith(Sink.Ignore<int>(), ActorMaterializerExtensions.Materializer(system));
97+ var source2 = Source.From(Enumerable.Range(0, 100))
98+ .RunWith(Sink.Ignore<int>(), ActorMaterializerExtensions.Materializer(system));
99+ }
100+ }
101+ """ ,
102+ // Cached Context.Materializer should not trigger any warnings
103+ """
104+ using System.Linq;
105+ using Akka.Actor;
106+ using Akka.Streams;
107+ using Akka.Streams.Dsl;
108+
109+ public class MyActor: ReceiveActor
110+ {
111+ public MyActor()
112+ {
113+ var mat = Context.Materializer();
114+
115+ var source1 = Source.From(Enumerable.Range(0, 100))
116+ .RunWith(Sink.Ignore<int>(), mat);
117+ var source2 = Source.From(Enumerable.Range(0, 100))
118+ .RunWith(Sink.Ignore<int>(), mat);
119+ }
120+ }
121+ """ ,
122+ // Cached Context.Materializer should not trigger any warnings
123+ """
124+ using System.Linq;
125+ using Akka.Actor;
126+ using Akka.Streams;
127+ using Akka.Streams.Dsl;
128+
129+ public class MyActor: ReceiveActor
130+ {
131+ private readonly ActorMaterializer _materializer = Context.Materializer();
132+ public MyActor()
133+ {
134+ var source1 = Source.From(Enumerable.Range(0, 100))
135+ .RunWith(Sink.Ignore<int>(), _materializer);
136+ var source2 = Source.From(Enumerable.Range(0, 100))
137+ .RunWith(Sink.Ignore<int>(), _materializer);
138+ }
139+ }
140+ """ ,
141+ // Cached Context.Materializer should not trigger any warnings
142+ """
143+ using System.Linq;
144+ using Akka.Actor;
145+ using Akka.Streams;
146+ using Akka.Streams.Dsl;
147+
148+ public class MyActor: ReceiveActor
149+ {
150+ private ActorMaterializer Materializer { get; } = Context.Materializer();
151+ public MyActor()
152+ {
153+ var source1 = Source.From(Enumerable.Range(0, 100))
154+ .RunWith(Sink.Ignore<int>(), Materializer);
155+ var source2 = Source.From(Enumerable.Range(0, 100))
156+ .RunWith(Sink.Ignore<int>(), Materializer);
157+ }
158+ }
159+ """ ,
160+ } ;
161+
162+ [ Theory ]
163+ [ MemberData ( nameof ( SuccessCases ) ) ]
164+ public Task SuccessCase ( string code )
165+ {
166+ return Verify . VerifyAnalyzer ( code ) ;
167+ }
168+
169+ public static readonly
170+ TheoryData < ( string testData , ( int startLine , int startColumn , int endLine , int endColumn ) [ ] spanData ) >
171+ FailureCases = new ( )
172+ {
173+ (
174+ // Context.Materializer invoked multiple times
175+ """
176+ // 01
177+ using System.Linq;
178+ using Akka.Actor;
179+ using Akka.Streams;
180+ using Akka.Streams.Dsl;
181+
182+ public class MyActor: ReceiveActor
183+ {
184+ public MyActor()
185+ {
186+ var source1 = Source.From(Enumerable.Range(0, 100))
187+ .RunWith(Sink.Ignore<int>(), Context.Materializer());
188+ var source2 = Source.From(Enumerable.Range(0, 100))
189+ .RunWith(Sink.Ignore<int>(), Context.Materializer());
190+ var source3 = Source.From(Enumerable.Range(0, 100))
191+ .RunWith(Sink.Ignore<int>(), Context.Materializer());
192+ }
193+ }
194+ """ , new [ ] { ( 14 , 42 , 14 , 64 ) , ( 16 , 42 , 16 , 64 ) } ) ,
195+ (
196+ // Context.Materializer invoked multiple times
197+ """
198+ // 02
199+ using System.Linq;
200+ using Akka.Actor;
201+ using Akka.Streams;
202+ using Akka.Streams.Dsl;
203+
204+ public class MyActor: ReceiveActor
205+ {
206+ private ActorMaterializer Materializer { get; } = Context.Materializer();
207+ public MyActor()
208+ {
209+ var source1 = Source.From(Enumerable.Range(0, 100))
210+ .RunWith(Sink.Ignore<int>(), Materializer);
211+ var source2 = Source.From(Enumerable.Range(0, 100))
212+ .RunWith(Sink.Ignore<int>(), Context.Materializer());
213+ var source3 = Source.From(Enumerable.Range(0, 100))
214+ .RunWith(Sink.Ignore<int>(), Context.Materializer());
215+ }
216+ }
217+ """ , new [ ] { ( 15 , 42 , 15 , 64 ) , ( 17 , 42 , 17 , 64 ) } ) ,
218+ (
219+ // Context.Materializer invoked multiple times, mixed with ActorSystem.Materializer()
220+ // Should not emit warning on Context.System.Materializer()
221+ """
222+ // 03
223+ using System.Linq;
224+ using Akka.Actor;
225+ using Akka.Streams;
226+ using Akka.Streams.Dsl;
227+
228+ public class MyActor: ReceiveActor
229+ {
230+ public MyActor()
231+ {
232+ var source1 = Source.From(Enumerable.Range(0, 100))
233+ .RunWith(Sink.Ignore<int>(), Context.Materializer());
234+ var source2 = Source.From(Enumerable.Range(0, 100))
235+ .RunWith(Sink.Ignore<int>(), Context.System.Materializer());
236+ var source3 = Source.From(Enumerable.Range(0, 100))
237+ .RunWith(Sink.Ignore<int>(), Context.Materializer());
238+ }
239+ }
240+ """ , new [ ] { ( 16 , 42 , 16 , 64 ) } ) ,
241+ (
242+ // ActorMaterializerExtensions.Materializer() invoked multiple times
243+ """
244+ // 04
245+ using System.Linq;
246+ using Akka.Actor;
247+ using Akka.Streams;
248+ using Akka.Streams.Dsl;
249+
250+ public class MyActor: ReceiveActor
251+ {
252+ public MyActor()
253+ {
254+ var source1 = Source.From(Enumerable.Range(0, 100))
255+ .RunWith(Sink.Ignore<int>(), ActorMaterializerExtensions.Materializer(Context));
256+ var source2 = Source.From(Enumerable.Range(0, 100))
257+ .RunWith(Sink.Ignore<int>(), Context.Materializer());
258+ var source3 = Source.From(Enumerable.Range(0, 100))
259+ .RunWith(Sink.Ignore<int>(), ActorMaterializerExtensions.Materializer(Context));
260+ }
261+ }
262+ """ , new [ ] { ( 14 , 42 , 14 , 64 ) , ( 16 , 42 , 16 , 91 ) } ) ,
263+ } ;
264+
265+ [ Theory ]
266+ [ MemberData ( nameof ( FailureCases ) ) ]
267+ public async Task FailureCase ( ( string testData , ( int startLine , int startColumn , int endLine , int endColumn ) [ ] spanData ) d )
268+ {
269+ var ( testData , spanData ) = d ;
270+ var expectedDiagnostics = new DiagnosticResult [ spanData . Length ] ;
271+ var currentDiagnosticIndex = 0 ;
272+
273+ // there can be multiple violations per test case
274+ foreach ( var ( startLine , startColumn , endLine , endColumn ) in spanData )
275+ {
276+ expectedDiagnostics [ currentDiagnosticIndex ++ ] = Verify . Diagnostic ( ) . WithSpan ( startLine , startColumn , endLine , endColumn ) ;
277+ }
278+
279+ await Verify . VerifyAnalyzer ( testData , expectedDiagnostics ) . ConfigureAwait ( true ) ;
280+ }
281+ }
0 commit comments