1+ namespace NLog . Windows . Forms . Tests
2+ {
3+ using System ;
4+ using System . Linq ;
5+ using System . Text ;
6+ using System . Collections . Generic ;
7+ using System . Reflection ;
8+ using NLog . Config ;
9+ using NLog . LayoutRenderers ;
10+ using Xunit ;
11+
12+ /// <summary>
13+ /// Test the characteristics of the API. Config of the API is tested in <see cref="NLog.UnitTests.Config.ConfigApiTests"/>
14+ /// </summary>
15+ public class ApiTests
16+ {
17+ private readonly Type [ ] allTypes ;
18+ private readonly Assembly nlogExtensionAssembly = typeof ( RichTextBoxTarget ) . Assembly ;
19+ private readonly Dictionary < Type , int > typeUsageCount = new Dictionary < Type , int > ( ) ;
20+
21+ public ApiTests ( )
22+ {
23+ allTypes = nlogExtensionAssembly . GetTypes ( ) ;
24+ }
25+
26+ [ Fact ]
27+ public void PublicEnumsTest ( )
28+ {
29+ foreach ( Type type in allTypes )
30+ {
31+ if ( ! type . IsPublic )
32+ {
33+ continue ;
34+ }
35+
36+ if ( type . IsEnum || type . IsInterface )
37+ {
38+ typeUsageCount [ type ] = 0 ;
39+ }
40+ }
41+
42+ typeUsageCount [ typeof ( IInstallable ) ] = 1 ;
43+
44+ foreach ( Type type in allTypes )
45+ {
46+ if ( type . IsGenericTypeDefinition )
47+ {
48+ continue ;
49+ }
50+
51+ if ( type . BaseType != null )
52+ {
53+ IncrementUsageCount ( type . BaseType ) ;
54+ }
55+
56+ foreach ( var iface in type . GetInterfaces ( ) )
57+ {
58+ IncrementUsageCount ( iface ) ;
59+ }
60+
61+ foreach ( var method in type . GetMethods ( ) )
62+ {
63+ if ( method . IsGenericMethodDefinition )
64+ {
65+ continue ;
66+ }
67+
68+ // Console.WriteLine(" {0}", method.Name);
69+ try
70+ {
71+ IncrementUsageCount ( method . ReturnType ) ;
72+
73+ foreach ( var p in method . GetParameters ( ) )
74+ {
75+ IncrementUsageCount ( p . ParameterType ) ;
76+ }
77+ }
78+ catch ( Exception ex )
79+ {
80+ // this sometimes throws on .NET Compact Framework, but is not fatal
81+ Console . WriteLine ( "EXCEPTION {0}" , ex ) ;
82+ }
83+ }
84+ }
85+
86+ var unusedTypes = new List < Type > ( ) ;
87+ StringBuilder sb = new StringBuilder ( ) ;
88+
89+ foreach ( var kvp in typeUsageCount )
90+ {
91+ if ( kvp . Value == 0 )
92+ {
93+ Console . WriteLine ( "Type '{0}' is not used." , kvp . Key ) ;
94+ unusedTypes . Add ( kvp . Key ) ;
95+ sb . Append ( kvp . Key . FullName ) . Append ( "\n " ) ;
96+ }
97+ }
98+
99+ Assert . Empty ( unusedTypes ) ;
100+ }
101+
102+ private void IncrementUsageCount ( Type type )
103+ {
104+ if ( type . IsArray )
105+ {
106+ type = type . GetElementType ( ) ;
107+ }
108+
109+ if ( type . IsGenericType && ! type . IsGenericTypeDefinition )
110+ {
111+ IncrementUsageCount ( type . GetGenericTypeDefinition ( ) ) ;
112+ foreach ( var parm in type . GetGenericArguments ( ) )
113+ {
114+ IncrementUsageCount ( parm ) ;
115+ }
116+ return ;
117+ }
118+
119+ if ( type . Assembly != nlogExtensionAssembly )
120+ {
121+ return ;
122+ }
123+
124+ if ( typeUsageCount . ContainsKey ( type ) )
125+ {
126+ typeUsageCount [ type ] ++ ;
127+ }
128+ }
129+
130+ [ Fact ]
131+ public void TypesInInternalNamespaceShouldBeInternalTest ( )
132+ {
133+ var notInternalTypes = allTypes
134+ . Where ( t => t . Namespace != null && t . Namespace . Contains ( ".Internal" ) )
135+ . Where ( t => ! t . IsNested && ( t . IsVisible || t . IsPublic ) )
136+ . Select ( t => t . FullName )
137+ . ToList ( ) ;
138+
139+ Assert . Empty ( notInternalTypes ) ;
140+ }
141+
142+ [ Fact ]
143+ public void AppDomainFixedOutput_Attribute_EnsureThreadAgnostic ( )
144+ {
145+ foreach ( Type type in allTypes )
146+ {
147+ var appDomainFixedOutputAttribute = type . GetCustomAttribute < AppDomainFixedOutputAttribute > ( ) ;
148+ if ( appDomainFixedOutputAttribute != null )
149+ {
150+ var threadAgnosticAttribute = type . GetCustomAttribute < ThreadAgnosticAttribute > ( ) ;
151+ Assert . True ( ! ( threadAgnosticAttribute is null ) , $ "{ type . ToString ( ) } is missing [ThreadAgnostic] attribute") ;
152+ }
153+ }
154+ }
155+
156+ [ Fact ]
157+ public void WrapperLayoutRenderer_EnsureThreadAgnostic ( )
158+ {
159+ foreach ( Type type in allTypes )
160+ {
161+ if ( typeof ( NLog . LayoutRenderers . Wrappers . WrapperLayoutRendererBase ) . IsAssignableFrom ( type ) )
162+ {
163+ if ( type . IsAbstract || ! type . IsPublic )
164+ continue ; // skip non-concrete types, enumerations, and private nested types
165+
166+ Assert . True ( type . IsDefined ( typeof ( ThreadAgnosticAttribute ) , true ) , $ "{ type . ToString ( ) } is missing [ThreadAgnostic] attribute.") ;
167+ }
168+ }
169+ }
170+
171+ [ Fact ]
172+ public void RequiredConfigOptionMustBeClass ( )
173+ {
174+ foreach ( Type type in allTypes )
175+ {
176+ var properties = type . GetProperties ( BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance ) ;
177+ foreach ( var prop in properties )
178+ {
179+ var requiredParameter = prop . GetCustomAttribute < NLog . Config . RequiredParameterAttribute > ( ) ;
180+ if ( requiredParameter != null )
181+ {
182+ Assert . True ( prop . PropertyType . IsClass , prop . Name ) ;
183+ }
184+ }
185+ }
186+ }
187+
188+ [ Fact ]
189+ public void SingleDefaultConfigOption ( )
190+ {
191+ string prevDefaultPropertyName = null ;
192+
193+ foreach ( Type type in allTypes )
194+ {
195+ prevDefaultPropertyName = null ;
196+
197+ var properties = type . GetProperties ( BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance ) ;
198+ foreach ( var prop in properties )
199+ {
200+ var defaultParameter = prop . GetCustomAttribute < DefaultParameterAttribute > ( ) ;
201+ if ( defaultParameter != null )
202+ {
203+ Assert . True ( prevDefaultPropertyName == null , prevDefaultPropertyName ? . ToString ( ) ) ;
204+ prevDefaultPropertyName = prop . Name ;
205+ Assert . True ( type . IsSubclassOf ( typeof ( NLog . LayoutRenderers . LayoutRenderer ) ) , type . ToString ( ) ) ;
206+ }
207+ }
208+ }
209+ }
210+
211+ [ Fact ]
212+ public void ValidateLayoutRendererTypeAlias ( )
213+ {
214+ // These class-names should be repaired with next major version bump
215+ // Do NOT add more incorrect class-names to this exlusion-list
216+ HashSet < string > oldFaultyClassNames = new HashSet < string > ( )
217+ {
218+ "RichTextBoxLinkLayoutRenderer" ,
219+ } ;
220+ foreach ( Type type in allTypes )
221+ {
222+ if ( type . IsSubclassOf ( typeof ( LayoutRenderer ) ) )
223+ {
224+ var layoutRendererAttributes = type . GetCustomAttributes < LayoutRendererAttribute > ( ) ? . ToArray ( ) ?? new LayoutRendererAttribute [ 0 ] ;
225+ if ( layoutRendererAttributes . Length == 0 )
226+ {
227+ Assert . True ( type . IsAbstract , $ "{ type } without LayoutRendererAttribute must be abstract") ;
228+ }
229+ else
230+ {
231+ Assert . False ( type . IsAbstract , $ "{ type } with LayoutRendererAttribute cannot be abstract") ;
232+
233+ if ( ! oldFaultyClassNames . Contains ( type . Name ) )
234+ {
235+ var typeAlias = layoutRendererAttributes . First ( ) . Name . Replace ( "-" , "" ) ;
236+ Assert . Equal ( typeAlias + "LayoutRenderer" , type . Name , StringComparer . OrdinalIgnoreCase ) ;
237+ }
238+ }
239+
240+ Assert . Equal ( "NLog.Windows.Forms" , type . Namespace ) ;
241+ }
242+ }
243+ }
244+
245+ [ Fact ]
246+ public void ValidateConfigurationItemFactory ( )
247+ {
248+ ConfigurationItemFactory . Default = null ; // Reset
249+
250+ LogManager . Setup ( ) . SetupExtensions ( ext => ext . RegisterWindowsForms ( ) ) ;
251+
252+ var missingTypes = new List < string > ( ) ;
253+
254+ foreach ( Type type in allTypes )
255+ {
256+ if ( ! type . IsPublic || ! type . IsClass || type . IsAbstract )
257+ continue ;
258+
259+ if ( typeof ( NLog . Targets . Target ) . IsAssignableFrom ( type ) )
260+ {
261+ var configAttribs = type . GetCustomAttributes < NLog . Targets . TargetAttribute > ( false ) ;
262+ Assert . NotEmpty ( configAttribs ) ;
263+
264+ foreach ( var configName in configAttribs )
265+ {
266+ if ( ! ConfigurationItemFactory . Default . TargetFactory . TryCreateInstance ( configName . Name , out var target ) )
267+ {
268+ Console . WriteLine ( configName . Name ) ;
269+ missingTypes . Add ( configName . Name ) ;
270+ }
271+ else if ( type != target . GetType ( ) )
272+ {
273+ Console . WriteLine ( type . Name ) ;
274+ missingTypes . Add ( type . Name ) ;
275+ }
276+ }
277+ }
278+ else if ( typeof ( NLog . Layouts . Layout ) . IsAssignableFrom ( type ) )
279+ {
280+ var configAttribs = type . GetCustomAttributes < NLog . Layouts . LayoutAttribute > ( false ) ;
281+ Assert . NotEmpty ( configAttribs ) ;
282+
283+ foreach ( var configName in configAttribs )
284+ {
285+ if ( ! ConfigurationItemFactory . Default . LayoutFactory . TryCreateInstance ( configName . Name , out var layout ) )
286+ {
287+ Console . WriteLine ( configName . Name ) ;
288+ missingTypes . Add ( configName . Name ) ;
289+ }
290+ else if ( type != layout . GetType ( ) )
291+ {
292+ Console . WriteLine ( type . Name ) ;
293+ missingTypes . Add ( type . Name ) ;
294+ }
295+ }
296+ }
297+ else if ( typeof ( NLog . LayoutRenderers . LayoutRenderer ) . IsAssignableFrom ( type ) )
298+ {
299+ var configAttribs = type . GetCustomAttributes < NLog . LayoutRenderers . LayoutRendererAttribute > ( false ) ;
300+ Assert . NotEmpty ( configAttribs ) ;
301+
302+ foreach ( var configName in configAttribs )
303+ {
304+ if ( ! ConfigurationItemFactory . Default . LayoutRendererFactory . TryCreateInstance ( configName . Name , out var layoutRenderer ) )
305+ {
306+ Console . WriteLine ( configName . Name ) ;
307+ missingTypes . Add ( configName . Name ) ;
308+ }
309+ else if ( type != layoutRenderer . GetType ( ) )
310+ {
311+ Console . WriteLine ( type . Name ) ;
312+ missingTypes . Add ( type . Name ) ;
313+ }
314+ }
315+
316+ if ( typeof ( NLog . LayoutRenderers . Wrappers . WrapperLayoutRendererBase ) . IsAssignableFrom ( type ) )
317+ {
318+ var wrapperAttribs = type . GetCustomAttributes < NLog . LayoutRenderers . AmbientPropertyAttribute > ( false ) ;
319+ if ( wrapperAttribs ? . Any ( ) == true )
320+ {
321+ foreach ( var ambientName in wrapperAttribs )
322+ {
323+ if ( ! ConfigurationItemFactory . Default . AmbientRendererFactory . TryCreateInstance ( ambientName . Name , out var layoutRenderer ) )
324+ {
325+ Console . WriteLine ( ambientName . Name ) ;
326+ missingTypes . Add ( ambientName . Name ) ;
327+ }
328+ else if ( type != layoutRenderer . GetType ( ) )
329+ {
330+ Console . WriteLine ( type . Name ) ;
331+ missingTypes . Add ( type . Name ) ;
332+ }
333+ }
334+ }
335+ }
336+ }
337+ }
338+
339+ Assert . Empty ( missingTypes ) ;
340+ }
341+ }
342+ }
0 commit comments