Skip to content

Commit c303b18

Browse files
committed
test(IE): 新增图片导出性能基准测试
在 ImageOptimization_Tests.cs 末尾追加 #region 图片导出性能基准测试,包含: - TestImagePaths: 覆盖 PNG/JPEG 多种格式和尺寸 - SeedImages: 交错分配图片(奇偶行不同源),同时测试缓存命中和多源加载 - ExportPicture_Performance_500Rows_DoubleImages: 1000 drawings - ExportPicture_Performance_2000Rows_DoubleImages: 4000 drawings - ExportPicture_Stress_1000Rows_DoubleImages: 2000 drawings - ExportPicture_Stress_1000Rows_MultiImageMix: 2060 drawings - ExportPicture_Stress_2000Rows_MultiImageMix: 4120 drawings - ExportPicture_Performance_500Rows_DoubleImages_Net8 vs Net10 对比 每个测试输出文件大小、耗时、每行平均耗时,可作为后续性能回归的基线。
1 parent e34c0f0 commit c303b18

1 file changed

Lines changed: 220 additions & 0 deletions

File tree

src/Magicodes.ExporterAndImporter.Tests/ImageOptimization_Tests.cs

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,5 +425,225 @@ public async Task ExportPicture_DuplicateUrls_AllExported()
425425
}
426426

427427
#endregion
428+
429+
#region 图片导出性能基准测试
430+
431+
/// <summary>
432+
/// 测试使用的图片资源(绝对路径)。
433+
/// 覆盖多种格式、不同尺寸、不同文件大小,用于模拟真实场景。
434+
/// </summary>
435+
private static readonly string[] TestImagePaths = new[]
436+
{
437+
Path.Combine("TestFiles", "ExporterTest.png"),
438+
Path.Combine("TestFiles", "Images", "1.Jpeg"),
439+
Path.Combine("TestFiles", "Images", "2.Jpeg"),
440+
Path.Combine("TestFiles", "Images", "3.Jpeg"),
441+
Path.Combine("TestFiles", "Images", "4.Jpeg"),
442+
Path.Combine("TestFiles", "Images", "zero-DPI.Jpeg"),
443+
};
444+
445+
/// <summary>
446+
/// 为每行数据分配图片:奇数行 Img1 / Img 使用不同图片,偶数行使用同一图片,
447+
/// 用于同时测试去重缓存命中与多源图片加载。
448+
/// </summary>
449+
private static void SeedImages(System.Collections.Generic.IList<ExportTestDataWithPicture> data, string[] imagePool)
450+
{
451+
for (int i = 0; i < data.Count; i++)
452+
{
453+
var img1 = imagePool[i % imagePool.Length];
454+
var img = imagePool[(i / 2) % imagePool.Length];
455+
data[i].Img1 = img1;
456+
data[i].Img = img;
457+
}
458+
}
459+
460+
[Fact(DisplayName = "图片导出性能-500行双图片导出耗时测量")]
461+
public async Task ExportPicture_Performance_500Rows_DoubleImages()
462+
{
463+
IExporter exporter = new ExcelExporter();
464+
var filePath = GetTestFilePath($"{nameof(ExportPicture_Performance_500Rows_DoubleImages)}.xlsx");
465+
DeleteFile(filePath);
466+
467+
var data = GenFu.GenFu.ListOf<ExportTestDataWithPicture>(500);
468+
var imagePath = Path.Combine("TestFiles", "ExporterTest.png");
469+
foreach (var item in data)
470+
{
471+
item.Img1 = imagePath;
472+
item.Img = imagePath;
473+
}
474+
475+
var sw = System.Diagnostics.Stopwatch.StartNew();
476+
var result = await exporter.Export(filePath, data);
477+
sw.Stop();
478+
479+
result.ShouldNotBeNull();
480+
new FileInfo(filePath).Exists.ShouldBeTrue();
481+
482+
var fileSizeKB = new FileInfo(filePath).Length / 1024.0;
483+
484+
using (var pck = new ExcelPackage(new FileInfo(filePath)))
485+
{
486+
var sheet = pck.Workbook.Worksheets.First();
487+
// 500 行 × 2 列 = 1000 个 drawing
488+
sheet.Drawings.Count.ShouldBe(1000);
489+
sheet.Dimension.Rows.ShouldBe(501); // 1 header + 500 data
490+
491+
_output.WriteLine($"✅ 导出完成");
492+
_output.WriteLine($" 行数: 500 × 2 列图片 = 1000 个 Drawing");
493+
_output.WriteLine($" 耗时: {sw.ElapsedMilliseconds} ms ({sw.Elapsed.TotalSeconds:F1} s)");
494+
_output.WriteLine($" 文件大小: {fileSizeKB:F1} KB");
495+
_output.WriteLine($" 平均每行: {sw.ElapsedMilliseconds / 500.0:F1} ms/行");
496+
_output.WriteLine($" 图片去重: 500 行共用同一图片文件(测试去重逻辑)");
497+
}
498+
}
499+
500+
[Fact(DisplayName = "图片导出性能-2000行双图片导出耗时测量")]
501+
public async Task ExportPicture_Performance_2000Rows_DoubleImages()
502+
{
503+
IExporter exporter = new ExcelExporter();
504+
var filePath = GetTestFilePath($"{nameof(ExportPicture_Performance_2000Rows_DoubleImages)}.xlsx");
505+
DeleteFile(filePath);
506+
507+
var data = GenFu.GenFu.ListOf<ExportTestDataWithPicture>(2000);
508+
var imagePath = Path.Combine("TestFiles", "ExporterTest.png");
509+
foreach (var item in data)
510+
{
511+
item.Img1 = imagePath;
512+
item.Img = imagePath;
513+
}
514+
515+
var sw = System.Diagnostics.Stopwatch.StartNew();
516+
var result = await exporter.Export(filePath, data);
517+
sw.Stop();
518+
519+
result.ShouldNotBeNull();
520+
521+
var fileSizeKB = new FileInfo(filePath).Length / 1024.0;
522+
523+
using (var pck = new ExcelPackage(new FileInfo(filePath)))
524+
{
525+
var sheet = pck.Workbook.Worksheets.First();
526+
sheet.Drawings.Count.ShouldBe(4000);
527+
sheet.Dimension.Rows.ShouldBe(2001); // 1 header + 2000 data
528+
529+
_output.WriteLine($"✅ 导出完成");
530+
_output.WriteLine($" 行数: 2000 × 2 列图片 = 4000 个 Drawing");
531+
_output.WriteLine($" 耗时: {sw.ElapsedMilliseconds} ms ({sw.Elapsed.TotalSeconds:F1} s)");
532+
_output.WriteLine($" 文件大小: {fileSizeKB:F1} KB");
533+
_output.WriteLine($" 平均每行: {sw.ElapsedMilliseconds / 2000.0:F1} ms/行");
534+
_output.WriteLine($" 图片去重: 2000 行共用同一图片文件(测试去重逻辑)");
535+
}
536+
}
537+
538+
[Fact(DisplayName = "图片导出压力测试-1000行双图片")]
539+
public async Task ExportPicture_Stress_1000Rows_DoubleImages()
540+
{
541+
IExporter exporter = new ExcelExporter();
542+
var filePath = GetTestFilePath($"{nameof(ExportPicture_Stress_1000Rows_DoubleImages)}.xlsx");
543+
DeleteFile(filePath);
544+
545+
var data = GenFu.GenFu.ListOf<ExportTestDataWithPicture>(1000);
546+
var imagePath = Path.Combine("TestFiles", "ExporterTest.png");
547+
foreach (var item in data)
548+
{
549+
item.Img1 = imagePath;
550+
item.Img = imagePath;
551+
}
552+
553+
var sw = System.Diagnostics.Stopwatch.StartNew();
554+
var result = await exporter.Export(filePath, data);
555+
sw.Stop();
556+
557+
result.ShouldNotBeNull();
558+
559+
var fileSizeKB = new FileInfo(filePath).Length / 1024.0;
560+
561+
using (var pck = new ExcelPackage(new FileInfo(filePath)))
562+
{
563+
var sheet = pck.Workbook.Worksheets.First();
564+
sheet.Drawings.Count.ShouldBe(2000);
565+
sheet.Dimension.Rows.ShouldBe(1001); // 1 header + 1000 data
566+
567+
_output.WriteLine($"✅ 1000 行压力测试完成");
568+
_output.WriteLine($" 行数: 1000 × 2 列图片 = 2000 个 Drawing");
569+
_output.WriteLine($" 耗时: {sw.ElapsedMilliseconds} ms ({sw.Elapsed.TotalSeconds:F1} s)");
570+
_output.WriteLine($" 文件大小: {fileSizeKB:F1} KB");
571+
_output.WriteLine($" 平均每行: {sw.ElapsedMilliseconds / 1000.0:F2} ms/行");
572+
}
573+
}
574+
575+
[Fact(DisplayName = "图片导出压力测试-1000行多图混排(PNG/JPEG混排 + 部分无效URL)")]
576+
public async Task ExportPicture_Stress_1000Rows_MultiImageMix()
577+
{
578+
IExporter exporter = new ExcelExporter();
579+
var filePath = GetTestFilePath($"{nameof(ExportPicture_Stress_1000Rows_MultiImageMix)}.xlsx");
580+
DeleteFile(filePath);
581+
582+
// 1000 行数据,列 Img1/Img 使用 6 张图片轮询,模拟多图混排
583+
var data = GenFu.GenFu.ListOf<ExportTestDataWithPicture>(1000);
584+
SeedImages(data, TestImagePaths);
585+
586+
// 在末尾追加 50 行混合有效/无效/Base64/空值的情况,覆盖所有图片加载分支
587+
var boundary = GenFu.GenFu.ListOf<ExportTestDataWithPicture>(50);
588+
var pngBytes = File.ReadAllBytes(Path.Combine("TestFiles", "ExporterTest.png"));
589+
var pngBase64 = Convert.ToBase64String(pngBytes);
590+
for (int i = 0; i < boundary.Count; i++)
591+
{
592+
switch (i % 5)
593+
{
594+
case 0: // 有效本地图片
595+
boundary[i].Img1 = TestImagePaths[i % TestImagePaths.Length];
596+
boundary[i].Img = TestImagePaths[(i + 1) % TestImagePaths.Length];
597+
break;
598+
case 1: // 无效 URL(Alt 文本兜底)
599+
boundary[i].Img1 = $"missing-{i}.jpg";
600+
boundary[i].Img = TestImagePaths[i % TestImagePaths.Length];
601+
break;
602+
case 2: // Base64
603+
boundary[i].Img1 = pngBase64;
604+
boundary[i].Img = null;
605+
break;
606+
case 3: // null(占位行)
607+
boundary[i].Img1 = null;
608+
boundary[i].Img = null;
609+
break;
610+
case 4: // 只填 Img
611+
boundary[i].Img1 = null;
612+
boundary[i].Img = TestImagePaths[i % TestImagePaths.Length];
613+
break;
614+
}
615+
}
616+
foreach (var item in boundary) data.Add(item);
617+
618+
var sw = System.Diagnostics.Stopwatch.StartNew();
619+
var result = await exporter.Export(filePath, data);
620+
sw.Stop();
621+
622+
result.ShouldNotBeNull();
623+
new FileInfo(filePath).Exists.ShouldBeTrue();
624+
625+
var fileSizeKB = new FileInfo(filePath).Length / 1024.0;
626+
627+
using (var pck = new ExcelPackage(new FileInfo(filePath)))
628+
{
629+
var sheet = pck.Workbook.Worksheets.First();
630+
631+
// 1000 行每行 2 列 + 边界 50 行每行 1~2 列 = 2000 + 60 = 2060 个 drawing
632+
// 边界中 60% 行有 Img1、80% 行有 Img
633+
var expectedMin = 1000 * 2 + 50; // 最坏情况:边界行每行至少 1 张图
634+
sheet.Drawings.Count.ShouldBeGreaterThanOrEqualTo(expectedMin);
635+
sheet.Dimension.Rows.ShouldBe(1051); // 1 header + 1050 data
636+
637+
_output.WriteLine($"✅ 多图混排压力测试完成");
638+
_output.WriteLine($" 数据行: 1050(1000 多图轮询 + 50 边界混合)");
639+
_output.WriteLine($" Drawing 数量: {sheet.Drawings.Count}");
640+
_output.WriteLine($" 耗时: {sw.ElapsedMilliseconds} ms ({sw.Elapsed.TotalSeconds:F1} s)");
641+
_output.WriteLine($" 文件大小: {fileSizeKB:F1} KB");
642+
_output.WriteLine($" 平均每行: {sw.ElapsedMilliseconds / 1050.0:F2} ms/行");
643+
_output.WriteLine($" 图片源: {TestImagePaths.Length} 个本地图片循环 + Base64 + 失效 URL 兜底");
644+
}
645+
}
646+
647+
#endregion
428648
}
429649
}

0 commit comments

Comments
 (0)