@@ -12,45 +12,73 @@ namespace DataLayerGenerator.Tests.Performance
1212 /// <summary>
1313 /// Performance tests to ensure the generator runs efficiently
1414 /// </summary>
15- public class PerformanceTests : IDisposable
15+ public class PerformanceTests : IAsyncLifetime , IDisposable
1616 {
1717 private readonly DataLayerGeneratorService _service ;
1818 private readonly string _tempDirectory ;
1919 private bool _disposed ;
20+ private static readonly object _warmupLock = new ( ) ;
21+ private static bool _isWarmedUp ;
2022
2123 public PerformanceTests ( )
2224 {
2325 _service = new DataLayerGeneratorService ( ) ;
2426 _tempDirectory = Path . Combine ( Path . GetTempPath ( ) , $ "PerfTests_{ Guid . NewGuid ( ) } ") ;
2527 Directory . CreateDirectory ( _tempDirectory ) ;
28+ }
2629
30+ public async Task InitializeAsync ( )
31+ {
2732 // Warm up Roslyn to avoid measuring initialization in tests
28- WarmUpService ( ) ;
33+ await EnsureWarmedUpAsync ( ) ;
2934 }
3035
31- private void WarmUpService ( )
36+ public Task DisposeAsync ( )
3237 {
33- var warmupCode = "namespace Test { public class Warmup { public int Id { get; set; } } }" ;
34- var warmupFile = CreateTempFile ( warmupCode ) ;
35- try
38+ // No async cleanup needed
39+ return Task . CompletedTask ;
40+ }
41+
42+ private async Task EnsureWarmedUpAsync ( )
43+ {
44+ if ( _isWarmedUp ) return ;
45+
46+ // Use a simple lock for the flag check, but do async work outside the lock
47+ bool shouldWarmup = false ;
48+ lock ( _warmupLock )
3649 {
37- _service . AnalyzeModelsAsync ( warmupFile ) . GetAwaiter ( ) . GetResult ( ) ;
50+ if ( ! _isWarmedUp )
51+ {
52+ shouldWarmup = true ;
53+ _isWarmedUp = true ; // Set it now to prevent other instances from trying
54+ }
55+ }
3856
39- // Also warm up code generation
40- var modelInfo = new ModelInfo
57+ if ( shouldWarmup )
58+ {
59+ var warmupCode = "namespace Test { public class Warmup { public int Id { get; set; } } }" ;
60+ var warmupFile = CreateTempFile ( warmupCode ) ;
61+ try
4162 {
42- ClassName = "Warmup" ,
43- Namespace = "Test" ,
44- HasIdProperty = true ,
45- Properties = new System . Collections . Generic . List < PropertyInfo >
63+ // Async warmup
64+ _ = await _service . AnalyzeModelsAsync ( warmupFile ) ;
65+
66+ // Also warm up code generation
67+ var modelInfo = new ModelInfo
4668 {
47- new PropertyInfo { Name = "Id" , Type = "int" }
48- }
49- } ;
50- _service . GenerateDataLayerClass ( "Warmup" , modelInfo , new DataLayerOptions ( ) ) ;
51- _service . GenerateInterface ( "Warmup" , modelInfo , new DataLayerOptions ( ) ) ;
69+ ClassName = "Warmup" ,
70+ Namespace = "Test" ,
71+ HasIdProperty = true ,
72+ Properties =
73+ [
74+ new ( ) { Name = "Id" , Type = "int" }
75+ ]
76+ } ;
77+ _service . GenerateDataLayerClass ( "Warmup" , modelInfo , new DataLayerOptions ( ) ) ;
78+ _service . GenerateInterface ( "Warmup" , modelInfo , new DataLayerOptions ( ) ) ;
79+ }
80+ catch { /* Ignore warm-up errors */ }
5281 }
53- catch { /* Ignore warm-up errors */ }
5482 }
5583
5684 protected virtual void Dispose ( bool disposing )
@@ -160,7 +188,7 @@ namespace TestApp.Models
160188 stopwatch . ElapsedMilliseconds . Should ( ) . BeLessThan ( 3000 ) ; // Should complete in < 3 seconds
161189 }
162190
163- #endregion
191+ #endregion Model Analysis Performance
164192
165193 #region Code Generation Performance
166194
@@ -173,9 +201,8 @@ public void GenerateDataLayerClass_SimpleModel_CompletesQuickly()
173201 ClassName = "Product" ,
174202 Namespace = "TestApp.Models" ,
175203 HasIdProperty = true ,
176- Properties = Enumerable . Range ( 1 , 10 )
177- . Select ( i => new PropertyInfo { Name = $ "Property{ i } ", Type = "string" } )
178- . ToList ( )
204+ Properties = [ .. Enumerable . Range ( 1 , 10 )
205+ . Select ( i => new PropertyInfo { Name = $ "Property{ i } ", Type = "string" } ) ]
179206 } ;
180207
181208 var options = new DataLayerOptions
@@ -207,9 +234,8 @@ public void GenerateDataLayerClass_LargeModel_CompletesReasonably()
207234 ClassName = "LargeModel" ,
208235 Namespace = "TestApp.Models" ,
209236 HasIdProperty = true ,
210- Properties = Enumerable . Range ( 1 , 100 )
211- . Select ( i => new PropertyInfo { Name = $ "Property{ i } ", Type = "string" } )
212- . ToList ( )
237+ Properties = [ .. Enumerable . Range ( 1 , 100 )
238+ . Select ( i => new PropertyInfo { Name = $ "Property{ i } ", Type = "string" } ) ]
213239 } ;
214240
215241 var options = new DataLayerOptions
@@ -263,7 +289,7 @@ public void GenerateInterface_CompletesQuickly()
263289 stopwatch . ElapsedMilliseconds . Should ( ) . BeLessThan ( 200 ) ; // Should complete in < 0.2 seconds
264290 }
265291
266- #endregion
292+ #endregion Code Generation Performance
267293
268294 #region Batch Processing Performance
269295
@@ -340,15 +366,18 @@ public class Model{i}
340366 stopwatch . ElapsedMilliseconds . Should ( ) . BeLessThan ( 20000 ) ; // Should complete in < 20 seconds
341367 }
342368
343- #endregion
369+ #endregion Batch Processing Performance
344370
345371 #region Memory Usage
346372
347373 [ Fact ]
348374 public async Task MemoryUsage_LargeFile_StaysReasonableAsync ( )
349375 {
350376 // Arrange
377+ // GC.Collect intentionally used for accurate memory measurement in performance test
378+ #pragma warning disable S1215 // "GC.Collect" should not be called
351379 var initialMemory = GC . GetTotalMemory ( true ) ;
380+ #pragma warning restore S1215
352381
353382 var properties = string . Join ( "\n " ,
354383 Enumerable . Range ( 1 , 1000 )
@@ -366,7 +395,7 @@ public class VeryLargeModel
366395
367396 // Act
368397 var result = await _service . AnalyzeModelsAsync ( filePath ) ;
369- var dataLayerCode = _service . GenerateDataLayerClass (
398+ _ = _service . GenerateDataLayerClass (
370399 "VeryLargeModel" ,
371400 result [ 0 ] ,
372401 new DataLayerOptions { GenerateGetAll = true } ) ;
@@ -389,10 +418,13 @@ public class Product { public int Id { get; set; } }
389418}" ;
390419 var filePath = CreateTempFile ( sourceCode ) ;
391420
421+ // GC.Collect intentionally used for accurate memory leak detection in performance test
422+ #pragma warning disable S1215 // "GC.Collect" should not be called
392423 GC . Collect ( ) ;
393424 GC . WaitForPendingFinalizers ( ) ;
394425 GC . Collect ( ) ;
395426 var initialMemory = GC . GetTotalMemory ( true ) ;
427+ #pragma warning restore S1215
396428
397429 // Act - Process same file 100 times
398430 for ( int i = 0 ; i < 100 ; i ++ )
@@ -404,17 +436,21 @@ public class Product { public int Id { get; set; } }
404436 new DataLayerOptions { GenerateGetAll = true } ) ;
405437 }
406438
439+ // GC.Collect intentionally used for accurate memory leak detection in performance test
440+ #pragma warning disable S1215 // "GC.Collect" should not be called
407441 GC . Collect ( ) ;
408442 GC . WaitForPendingFinalizers ( ) ;
409443 GC . Collect ( ) ;
410444 var finalMemory = GC . GetTotalMemory ( true ) ;
445+ #pragma warning restore S1215
446+
411447 var memoryGrowth = ( finalMemory - initialMemory ) / 1024 / 1024 ; // MB
412448
413449 // Assert - Memory shouldn't grow significantly
414450 memoryGrowth . Should ( ) . BeLessThan ( 50 ) ; // Should grow < 50 MB over 100 iterations
415451 }
416452
417- #endregion
453+ #endregion Memory Usage
418454
419455 #region Concurrent Processing
420456
@@ -456,7 +492,7 @@ public class Model{i}
456492 stopwatch . ElapsedMilliseconds . Should ( ) . BeLessThan ( 3000 ) ; // Concurrent should be faster
457493 }
458494
459- #endregion
495+ #endregion Concurrent Processing
460496
461497 #region Comparison Benchmarks
462498
@@ -508,7 +544,7 @@ public class Product
508544 stopwatch . ElapsedMilliseconds . Should ( ) . BeLessThan ( 1000 ) ;
509545 }
510546
511- #endregion
547+ #endregion Comparison Benchmarks
512548
513549 private string CreateTempFile ( string content )
514550 {
0 commit comments