test: 更新分析功能和迁移测试用例
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
using ClosedXML.Excel;
|
||||
|
||||
namespace Gpulse.WCT.DataAnalyzer.Tests.Commands;
|
||||
|
||||
[TestFixture]
|
||||
@@ -9,24 +11,23 @@ public class AnalyzeCommandTests : CommandTestBase
|
||||
/// 直接指向仓库根的 Resources 目录(gitignored,但本地存在);CI 无此文件则跳过。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Analyze_Ploss_RealVivoFile_ReportsSummary()
|
||||
public async Task Analyze_Ploss_RealVivoFile_GeneratesExcel()
|
||||
{
|
||||
var file = ResolveResourcesFile(
|
||||
"C车-单充快速成型件-A03-V55-vivo iQOD12 Pro-20250519-01.DAT");
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "ploss", "--file", file);
|
||||
var outputDir = NewDir();
|
||||
var outputPath = Path.Combine(outputDir, "result.xlsx");
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync(
|
||||
"--type", "ploss", "--file", file, "--output", outputPath);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
Assert.That(stdout, Does.Contain("=== Ploss Analysis:"));
|
||||
Assert.That(stdout, Does.Contain("Total: 35 (TwoLine: 35, Legacy: 0), FOD: 1, Errors: 0"));
|
||||
Assert.That(stdout, Does.Contain("Normal margin>2000: 34/34 (100.0%)"));
|
||||
Assert.That(stdout, Does.Contain("FOD margin>2000: 1/1 (100.0%)"));
|
||||
Assert.That(stdout, Does.Contain("Identity (delta_p = Tx - Rx - pow_loss - ploss): 35/35"));
|
||||
// FOD 行(第 92 行)反解:delta_p'=18801+2001+(-18447)-1750=605
|
||||
Assert.That(stdout, Does.Contain("delta_p'=605"));
|
||||
Assert.That(stdout, Does.Contain("rewrite: pow_loss = 3394, delta_p = 605"));
|
||||
Assert.That(stdout, Does.Contain("[OK]"));
|
||||
Assert.That(stdout, Does.Contain("Total: 35"));
|
||||
Assert.That(File.Exists(outputPath), Is.True);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -40,28 +41,22 @@ public class AnalyzeCommandTests : CommandTestBase
|
||||
/// 阈值 = (pureMax + foreignMin) / 2 = (5 + -49) / 2 = -22
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Analyze_Qfod_RealXiaomiFiles_ReportsCalibration()
|
||||
public async Task Analyze_Qfod_RealXiaomiFiles_GeneratesExcel()
|
||||
{
|
||||
var pure = ResolveResourcesFile("rapidMold-v1.5-hex2_5-Xiaomi17-qfod_pure-20250817-1.log");
|
||||
var foreign = ResolveResourcesFile("rapidMold-v1.5-hex2_5-Xiaomi17-qfod_foreign-20250817-1.log");
|
||||
|
||||
var outputDir = NewDir();
|
||||
var outputPath = Path.Combine(outputDir, "qfod_result.xlsx");
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync(
|
||||
"--type", "qfod", "--pure", pure, "--foreign", foreign);
|
||||
"--type", "qfod", "--pure", pure, "--foreign", foreign, "--output", outputPath);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
Assert.That(stdout, Does.Contain("=== Qfod Calibration ==="));
|
||||
// Pure: 21 条,min=3 max=5 avg=4.0
|
||||
Assert.That(stdout, Does.Contain("Pure phone"));
|
||||
Assert.That(stdout, Does.Contain("count=21 min=3 max=5 avg=4.0"));
|
||||
// Foreign: 7 条,min=-49 max=-5 avg=-27.7
|
||||
Assert.That(stdout, Does.Contain("Foreign object"));
|
||||
Assert.That(stdout, Does.Contain("count=7 min=-49 max=-5 avg=-27.7"));
|
||||
// Threshold = (5 + -49) / 2 = -22
|
||||
Assert.That(stdout, Does.Contain("Threshold = (pureMax + foreignMin) / 2 = (5 + -49) / 2 = -22"));
|
||||
Assert.That(stdout, Does.Contain("pureMax - threshold = 27"));
|
||||
Assert.That(stdout, Does.Contain("foreignMin - threshold = -27"));
|
||||
Assert.That(stdout, Does.Contain("[OK]"));
|
||||
Assert.That(File.Exists(outputPath), Is.True);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -78,9 +73,14 @@ public class AnalyzeCommandTests : CommandTestBase
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Analyze_UnknownType_PrintsMessage()
|
||||
public async Task Analyze_UnknownType_WithFile_PrintsMessage()
|
||||
{
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "foo");
|
||||
// 确保没有 Datas 文件夹干扰
|
||||
CleanupDatasAndOutput();
|
||||
var dir = NewDir();
|
||||
var file = TestFixtures.WritePlossLog(dir, "test.dat", new TestFixtures.PlossTwoLineRow(Field10: 350));
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "foo", "--file", file);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -90,87 +90,55 @@ public class AnalyzeCommandTests : CommandTestBase
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Analyze_Ploss_MissingFile_PrintsPrompt()
|
||||
public async Task Analyze_Ploss_MissingFile_TriggersAutoScan()
|
||||
{
|
||||
// 不提供文件路径时触发自动扫描
|
||||
CleanupDatasAndOutput();
|
||||
|
||||
var (_, stdout, _) = await Analyze().RunCaptureAsync("--type", "ploss");
|
||||
|
||||
Assert.That(stdout, Does.Contain("Please specify --file for Ploss analysis"));
|
||||
// 没有 Datas 文件夹时报错
|
||||
Assert.That(stdout, Does.Contain("未找到 Datas 文件夹"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Analyze_Ploss_ValidTwoLineFile_ReportsSummary()
|
||||
public async Task Analyze_Ploss_ValidTwoLineFile_GeneratesExcel()
|
||||
{
|
||||
var dir = NewDir();
|
||||
var file = TestFixtures.WritePlossLog(dir, TestFixtures.PlossFileName,
|
||||
// 正常两行:margin = 3000-500 = 2500 > 2000,恒等式成立
|
||||
new TestFixtures.PlossTwoLineRow(),
|
||||
// FOD 两行:ploss=2500 threshold=3000 margin=500,恒等式成立,delta_p'=2500+2001+2500-3000=4001
|
||||
new TestFixtures.PlossTwoLineRow(Field5: 8000, Field9: 2500, Field10: 3000, Field12: 1));
|
||||
// 正常两行:Field10=350(功率段),理论Δp = 6000-800-2200-500=2500
|
||||
new TestFixtures.PlossTwoLineRow(Field10: 350),
|
||||
// FOD 两行:Field10=500(功率段)
|
||||
new TestFixtures.PlossTwoLineRow(Field5: 8000, Field9: 2500, Field10: 500, Field12: 1));
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "ploss", "--file", file);
|
||||
var outputDir = NewDir();
|
||||
var outputPath = Path.Combine(outputDir, "result.xlsx");
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync(
|
||||
"--type", "ploss", "--file", file, "--output", outputPath);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
Assert.That(stdout, Does.Contain("=== Ploss Analysis:"));
|
||||
Assert.That(stdout, Does.Contain("Total: 2 (TwoLine: 2, Legacy: 0), FOD: 1, Errors: 0"));
|
||||
Assert.That(stdout, Does.Contain("Normal margin>2000: 1/1 (100.0%)"));
|
||||
Assert.That(stdout, Does.Contain("FOD margin>2000: 0/1 (0.0%)"));
|
||||
Assert.That(stdout, Does.Contain("Identity (delta_p = Tx - Rx - pow_loss - ploss): 2/2"));
|
||||
Assert.That(stdout, Does.Contain("delta_p'=4001"));
|
||||
Assert.That(stdout, Does.Contain("rewrite: pow_loss = 2200, delta_p = 4001"));
|
||||
Assert.That(stdout, Does.Contain("[OK]"));
|
||||
Assert.That(File.Exists(outputPath), Is.True);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Analyze_Ploss_LegacyFile_CountsLegacyRows()
|
||||
public async Task Analyze_Qfod_MissingArgs_TriggersAutoScan()
|
||||
{
|
||||
var dir = NewDir();
|
||||
var file = TestFixtures.WriteLegacyPlossLog(
|
||||
dir, "singleMold-v1.0-hex2_1-iPhone15-ploss_legacy-20260710-1.log",
|
||||
new (int Ploss, int Threshold, int Fod)[] { (500, 3000, 0), (2500, 3000, 1) });
|
||||
// 不提供文件路径时触发自动扫描
|
||||
CleanupDatasAndOutput();
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "ploss", "--file", file);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
Assert.That(stdout, Does.Contain("Total: 2 (TwoLine: 0, Legacy: 2), FOD: 1, Errors: 0"));
|
||||
Assert.That(stdout, Does.Contain("Normal margin>2000: 1/1 (100.0%)"));
|
||||
Assert.That(stdout, Does.Contain("FOD margin>2000: 0/1 (0.0%)"));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Analyze_Qfod_MissingArgs_PrintsPrompt()
|
||||
{
|
||||
var (_, stdout, _) = await Analyze().RunCaptureAsync("--type", "qfod");
|
||||
|
||||
Assert.That(stdout, Does.Contain("Please specify --pure and --foreign for Qfod calibration"));
|
||||
// 没有 Datas 文件夹时报错
|
||||
Assert.That(stdout, Does.Contain("未找到 Datas 文件夹"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Analyze_Ploss_LegacyFile_HexField1_DecodedCorrectly()
|
||||
{
|
||||
var dir = NewDir();
|
||||
// 旧单行格式 Field1=0C (十六进制) 应被解析为十进制 12
|
||||
var file = TestFixtures.WriteLegacyPlossLog(
|
||||
dir,
|
||||
"singleMold-v1.0-hex2_1-iPhone15-ploss_legacy-20260710-1.log",
|
||||
new (int, int, int)[] { (500, 3000, 0) },
|
||||
field1Hex: "0C");
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "ploss", "--file", file);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
Assert.That(stdout, Does.Contain("Total: 1 (TwoLine: 0, Legacy: 1), FOD: 0, Errors: 0"));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Analyze_Qfod_ValidFiles_ReportsThreshold()
|
||||
public async Task Analyze_Qfod_ValidFiles_GeneratesExcel()
|
||||
{
|
||||
var dir = NewDir();
|
||||
var pure = TestFixtures.WriteQfodLog(
|
||||
@@ -178,19 +146,17 @@ public class AnalyzeCommandTests : CommandTestBase
|
||||
var foreign = TestFixtures.WriteQfodLog(
|
||||
dir, "singleMold-v1.0-hex2_1-iPhone15-qfod_foreign-20260710-1.log", 42, 40, 45);
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "qfod", "--pure", pure, "--foreign", foreign);
|
||||
var outputDir = NewDir();
|
||||
var outputPath = Path.Combine(outputDir, "qfod_result.xlsx");
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync(
|
||||
"--type", "qfod", "--pure", pure, "--foreign", foreign, "--output", outputPath);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
Assert.That(stdout, Does.Contain("=== Qfod Calibration ==="));
|
||||
Assert.That(stdout, Does.Contain("Pure phone"));
|
||||
Assert.That(stdout, Does.Contain("Foreign object"));
|
||||
Assert.That(stdout, Does.Contain("count=3 min=20 max=25 avg=22.0"));
|
||||
Assert.That(stdout, Does.Contain("count=3 min=40 max=45 avg=42.3"));
|
||||
Assert.That(stdout, Does.Contain("Threshold = (pureMax + foreignMin) / 2 = (25 + 40) / 2 = 32"));
|
||||
Assert.That(stdout, Does.Contain("pureMax - threshold = -7"));
|
||||
Assert.That(stdout, Does.Contain("foreignMin - threshold = 8"));
|
||||
Assert.That(stdout, Does.Contain("[OK]"));
|
||||
Assert.That(File.Exists(outputPath), Is.True);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -237,4 +203,309 @@ public class AnalyzeCommandTests : CommandTestBase
|
||||
Assert.Ignore($"真实数据文件不存在(已检索 {candidates.Count} 个候选路径):{fileName}");
|
||||
return null!; // 不会执行
|
||||
}
|
||||
|
||||
#region --auto 自动扫描测试
|
||||
|
||||
/// <summary>
|
||||
/// 测试 --auto 自动扫描 Datas 文件夹并执行 Ploss 分析
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Analyze_Auto_Ploss_ScansDatasFolder()
|
||||
{
|
||||
// 创建 Datas 文件夹和测试文件
|
||||
var datasDir = Path.Combine(AppContext.BaseDirectory, "Datas");
|
||||
Directory.CreateDirectory(datasDir);
|
||||
|
||||
TestFixtures.WritePlossLog(datasDir, "奇瑞-E5-singleMold-A00-V001-iPhone15-无-ploss_test-2026081701.dat",
|
||||
new TestFixtures.PlossTwoLineRow(Field10: 350));
|
||||
|
||||
try
|
||||
{
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "ploss", "--auto");
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
Assert.That(stdout, Does.Contain("扫描 Datas 文件夹"));
|
||||
Assert.That(stdout, Does.Contain("[完成] 已分析"));
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Directory.Exists(datasDir)) Directory.Delete(datasDir, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试 --auto 自动分类纯手机和异物日志
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Analyze_Auto_Qfod_ClassifiesPureAndForeign()
|
||||
{
|
||||
var datasDir = Path.Combine(AppContext.BaseDirectory, "Datas");
|
||||
Directory.CreateDirectory(datasDir);
|
||||
|
||||
// 纯手机日志:异物类型 = 无
|
||||
TestFixtures.WriteQfodLog(datasDir, "奇瑞-E5-singleMold-A00-V001-iPhone15-无-qfod_pure-2026081701.log", 20, 25, 21);
|
||||
// 异物日志:异物类型 = 硬币
|
||||
TestFixtures.WriteQfodLog(datasDir, "奇瑞-E5-singleMold-A00-V001-iPhone15-硬币-qfod_foreign-2026081701.log", -5, -10, -15);
|
||||
|
||||
try
|
||||
{
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "qfod", "--auto");
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
// 验证能识别纯手机和异物
|
||||
Assert.That(stdout, Does.Contain("纯手机:"));
|
||||
Assert.That(stdout, Does.Contain("异物:"));
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Directory.Exists(datasDir)) Directory.Delete(datasDir, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试 --auto 时 Datas 文件夹不存在
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Analyze_Auto_NoDatasFolder_ReportsError()
|
||||
{
|
||||
CleanupDatasAndOutput();
|
||||
|
||||
var (_, stdout, _) = await Analyze().RunCaptureAsync("--type", "ploss", "--auto");
|
||||
|
||||
Assert.That(stdout, Does.Contain("未找到 Datas 文件夹"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 输出目录结构测试
|
||||
|
||||
/// <summary>
|
||||
/// 测试输出目录结构:Output/{车厂}/{车型}/{手机名}/{文件名}.xlsx
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Analyze_Ploss_CreatesOutputDirectoryStructure()
|
||||
{
|
||||
CleanupDatasAndOutput();
|
||||
|
||||
var dir = NewDir();
|
||||
var file = TestFixtures.WritePlossLog(dir, "奇瑞-E5-singleMold-A00-V001-iPhone15-无-ploss_test-2026081701.dat",
|
||||
new TestFixtures.PlossTwoLineRow(Field10: 350));
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "ploss", "--file", file);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
// 验证输出路径包含车厂/车型/手机名
|
||||
Assert.That(stdout, Does.Contain("Output"));
|
||||
Assert.That(stdout, Does.Contain("奇瑞"));
|
||||
Assert.That(stdout, Does.Contain("E5"));
|
||||
Assert.That(stdout, Does.Contain("iPhone15"));
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Qfod ΔQ 绝对值统计测试
|
||||
|
||||
/// <summary>
|
||||
/// 测试 Qfod 统计使用绝对值,阈值用有符号 ΔQ(见使用指南 Threshold=-22 样例)。
|
||||
/// 本用例 pure ΔQ=-20,-25,-21,foreign ΔQ=30,35,40 → pureMax=-20, foreignMin=30,
|
||||
/// 阈值 = (-20 + 30) / 2 = 5(有符号)。断言行7标定Q值单元格 == 5。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Analyze_Qfod_WithNegativeValues_GeneratesExcel()
|
||||
{
|
||||
CleanupDatasAndOutput();
|
||||
|
||||
var dir = NewDir();
|
||||
// 包含负值 ΔQ
|
||||
var pure = TestFixtures.WriteQfodLog(dir, "奇瑞-E5-singleMold-A00-V001-iPhone15-无-qfod_pure-2026081701.log", -20, -25, -21);
|
||||
var foreign = TestFixtures.WriteQfodLog(dir, "奇瑞-E5-singleMold-A00-V001-iPhone15-硬币-qfod_foreign-2026081701.log", 30, 35, 40);
|
||||
|
||||
var outputDir = NewDir();
|
||||
var outputPath = Path.Combine(outputDir, "result.xlsx");
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "qfod", "--pure", pure, "--foreign", foreign, "--output", outputPath);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
Assert.That(stdout, Does.Contain("[OK]"));
|
||||
Assert.That(File.Exists(outputPath), Is.True);
|
||||
});
|
||||
|
||||
// 读回 Excel:行6 标定Q值(Q0 列 col=2)应为有符号阈值 5
|
||||
using var workbook = new XLWorkbook(outputPath);
|
||||
var ws = workbook.Worksheet("Sheet1");
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(ws.Cell(6, 1).GetString(), Is.EqualTo("标定Q值")); // 行6 是标定Q值行
|
||||
// Q0 在行6合并了三列,值在合并后的第一个单元格
|
||||
Assert.That((int)ws.Cell(6, 2).GetDouble(), Is.EqualTo(5));
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ploss 标定值计算测试
|
||||
|
||||
/// <summary>
|
||||
/// 测试 Ploss 标定值计算:标定Δp = 理论Δp最大值 + a
|
||||
/// 功率段 350 的 a 值为 3000。理论Δp = 6000-(800+2200+500) = 2500 → 标定 = 5500。
|
||||
/// 读回 Excel 校验行7标定Δp单元格(350 段 col=2)== 5500。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Analyze_Ploss_CalculatesCalibrationValue()
|
||||
{
|
||||
CleanupDatasAndOutput();
|
||||
|
||||
var dir = NewDir();
|
||||
// 创建功率段 350 的数据
|
||||
// 理论Δp = Field5 - (Field4 + PowLoss + Field9) = 6000 - (800 + 2200 + 500) = 2500
|
||||
var file = TestFixtures.WritePlossLog(dir, "奇瑞-E5-singleMold-A00-V001-iPhone15-无-ploss_test-2026081701.dat",
|
||||
new TestFixtures.PlossTwoLineRow(Field10: 350));
|
||||
|
||||
var outputDir = NewDir();
|
||||
var outputPath = Path.Combine(outputDir, "result.xlsx");
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "ploss", "--file", file, "--output", outputPath);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
Assert.That(File.Exists(outputPath), Is.True);
|
||||
Assert.That(stdout, Does.Contain("Total: 1"));
|
||||
Assert.That(stdout, Does.Contain("Dropped: 0"));
|
||||
});
|
||||
|
||||
// 读回 Excel:行1 列2 含功率段 350;行6 标定Δp值(col=2)= 2500 + 3000 = 5500
|
||||
using var workbook = new XLWorkbook(outputPath);
|
||||
var ws = workbook.Worksheet("Sheet1");
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That((int)ws.Cell(1, 2).GetDouble(), Is.EqualTo(350)); // 行1 是功率段标题行
|
||||
Assert.That(ws.Cell(6, 1).GetString(), Is.EqualTo("标定Δp值")); // 行6 是标定行
|
||||
Assert.That((long)ws.Cell(6, 2).GetDouble(), Is.EqualTo(5500L));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RV-004:Field10 非标准功率段的记录应计入 DroppedCount 并在 stdout 暴露,
|
||||
/// 而非静默丢弃。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Analyze_Ploss_NonStandardPowerSegment_IsDropped()
|
||||
{
|
||||
CleanupDatasAndOutput();
|
||||
|
||||
var dir = NewDir();
|
||||
// Field10=850 不在 9 个标准功率段内 → 被丢弃
|
||||
var file = TestFixtures.WritePlossLog(dir, "奇瑞-E5-singleMold-A00-V001-iPhone15-无-ploss_test-2026081701.dat",
|
||||
new TestFixtures.PlossTwoLineRow(Field10: 850));
|
||||
|
||||
var outputDir = NewDir();
|
||||
var outputPath = Path.Combine(outputDir, "result.xlsx");
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "ploss", "--file", file, "--output", outputPath);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
Assert.That(File.Exists(outputPath), Is.True);
|
||||
Assert.That(stdout, Does.Contain("Total: 1"));
|
||||
Assert.That(stdout, Does.Contain("Dropped: 1"));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RV-002:pure/foreign 日志存在但未提取到 Qfod 行 → 空记录守卫应返回失败而非从 0 起算成功。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Analyze_Qfod_EmptyRecords_ReportsFailure()
|
||||
{
|
||||
CleanupDatasAndOutput();
|
||||
|
||||
var dir = NewDir();
|
||||
// 写一个只有空白行的"空"日志(文件存在但无 Qfod 行)
|
||||
var pure = Path.Combine(dir, "pure_empty.log");
|
||||
var foreign = Path.Combine(dir, "foreign_empty.log");
|
||||
await File.WriteAllTextAsync(pure, "\n\n \n");
|
||||
await File.WriteAllTextAsync(foreign, "\n\n \n");
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "qfod", "--pure", pure, "--foreign", foreign);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
Assert.That(stdout, Does.Contain("FAILED"));
|
||||
Assert.That(stdout, Does.Contain("未提取到 Qfod"));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RV-006:--output 指向不存在的嵌套目录时应自动创建而非崩溃。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Analyze_Ploss_OutputToMissingDir_CreatesDir()
|
||||
{
|
||||
CleanupDatasAndOutput();
|
||||
|
||||
var dir = NewDir();
|
||||
var file = TestFixtures.WritePlossLog(dir, "奇瑞-E5-singleMold-A00-V001-iPhone15-无-ploss_test-2026081701.dat",
|
||||
new TestFixtures.PlossTwoLineRow(Field10: 350));
|
||||
|
||||
var outputRoot = NewDir();
|
||||
var outputPath = Path.Combine(outputRoot, "nested", "deeper", "result.xlsx");
|
||||
Assume.That(Directory.Exists(Path.GetDirectoryName(outputPath)), Is.False);
|
||||
|
||||
var (code, _, _) = await Analyze().RunCaptureAsync("--type", "ploss", "--file", file, "--output", outputPath);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
Assert.That(File.Exists(outputPath), Is.True);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RV-005:--auto 多文件其中一文件损坏(无法解析)不应中止整批;其余文件仍产出 [OK],
|
||||
/// 且 stdout 含 [FAILED] 与最终 [完成] 汇总。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Analyze_Auto_Ploss_OneBadFile_DoesNotAbortBatch()
|
||||
{
|
||||
// 准备 Datas 文件夹:一个好文件 + 一个坏文件(只有 header 无 FOD 行)
|
||||
var dataDir = Path.Combine(AppContext.BaseDirectory, "Datas");
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(dataDir);
|
||||
TestFixtures.WritePlossLog(dataDir, "奇瑞-E5-singleMold-A00-V001-iPhone15-无-ploss_test-2026081701.dat",
|
||||
new TestFixtures.PlossTwoLineRow(Field10: 350));
|
||||
// 坏文件:仅 header 行,无配对 FOD 行 → 解析错误计数
|
||||
var badPath = Path.Combine(dataDir, "奇瑞-E5-singleMold-A00-V001-iPhone15-无-badfile-2026081702.dat");
|
||||
await File.WriteAllTextAsync(badPath, "pow_loss = 2200, delta_p = 2500\n");
|
||||
|
||||
var (code, stdout, _) = await Analyze().RunCaptureAsync("--type", "ploss", "--auto");
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(code, Is.EqualTo(0));
|
||||
Assert.That(stdout, Does.Contain("[OK]"));
|
||||
Assert.That(stdout, Does.Contain("[完成]"));
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Directory.Exists(dataDir)) Directory.Delete(dataDir, true);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user