Skip to content

Commit 74bc215

Browse files
committed
Refactor and enhance performance tests
Updated PerformanceTests to use IAsyncLifetime for async warmup, improved Roslyn warmup logic with thread safety, and modernized collection initializations. Added pragma directives for GC.Collect usage in memory tests and clarified region names for better code organization.
1 parent 9897b9e commit 74bc215

File tree

1 file changed

+68
-32
lines changed

1 file changed

+68
-32
lines changed

DataLayerGenerator.Tests/Performance/PerformanceTests.cs

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)