CalibrationManagerTests.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. using System;
  2. using IvfTl.ControlHost.Debug;
  3. using IvfTl.ControlHost.Tests.Fakes;
  4. using Xunit;
  5. namespace IvfTl.ControlHost.Tests
  6. {
  7. /// <summary>
  8. /// D2-02 Task3.2a:CalibrationManager 的 sid 校验路径(不触真实硬件标定)。
  9. /// 真实标定要硬件,这里只断言会话校验/字典管理的纯逻辑分支:
  10. /// · 不存在的 sid → StartCalibrate / GetProgress / Recalibrate 返回 SESSION_EXPIRED;
  11. /// · Stop 未知 sid 幂等返回 Okay;
  12. /// · houseBinOf 委托在 SESSION_EXPIRED 路径上绝不被调用(closure 未进入)。
  13. /// </summary>
  14. public class CalibrationManagerTests
  15. {
  16. private DateTime _now = new DateTime(2026, 6, 25, 0, 0, 0);
  17. private (CalibrationManager calib, DebugSessionManager debug, bool[] houseBinTouched) New()
  18. {
  19. var serial = new FakeSerial();
  20. var gate = new FakeGate(7, serial) { CanAcquire = true };
  21. var debug = new DebugSessionManager(sn => gate, () => _now, ttlMs: 10000, log: _ => { });
  22. var touched = new bool[1];
  23. // houseBinOf:SESSION_EXPIRED 路径下绝不应被调用;被调用则置标志,便于断言。
  24. var calib = new CalibrationManager(debug, sn => { touched[0] = true; return null; }, log: _ => { });
  25. return (calib, debug, touched);
  26. }
  27. [Fact]
  28. public void StartCalibrate_Unknown_Sid_Returns_SESSION_EXPIRED_Without_Touching_HouseBin()
  29. {
  30. var (calib, _, touched) = New();
  31. var r = calib.StartCalibrate("not-a-session", null);
  32. Assert.False(r.Ok);
  33. Assert.Equal("SESSION_EXPIRED", r.Code);
  34. Assert.False(touched[0]); // 会话校验先于取 HouseBin,委托不被触发
  35. }
  36. [Fact]
  37. public void GetProgress_Unknown_Sid_Returns_SESSION_EXPIRED()
  38. {
  39. var (calib, _, _) = New();
  40. var r = calib.GetProgress("nope");
  41. Assert.False(r.Ok);
  42. Assert.Equal("SESSION_EXPIRED", r.Code);
  43. }
  44. [Fact]
  45. public void Recalibrate_Unknown_Sid_Returns_SESSION_EXPIRED()
  46. {
  47. var (calib, _, _) = New();
  48. var r = calib.Recalibrate("nope", 3);
  49. Assert.False(r.Ok);
  50. Assert.Equal("SESSION_EXPIRED", r.Code);
  51. }
  52. [Fact]
  53. public void Stop_Unknown_Sid_Is_Idempotent_Ok()
  54. {
  55. var (calib, _, _) = New();
  56. Assert.True(calib.Stop("nope").Ok);
  57. Assert.True(calib.Stop(null).Ok);
  58. }
  59. // Critical 并发修复:StopAndWait 对未起标定的 sid 是 no-op 且返回 true,
  60. // 让 DebugSessionManager 的"会话即将关闭"回调即便该会话没在标定也能畅通(不阻塞 Dispose)。
  61. [Fact]
  62. public void StopAndWait_Unknown_Sid_Is_NoOp_ReturnsTrue()
  63. {
  64. var (calib, _, _) = New();
  65. Assert.True(calib.StopAndWait("nope", 1000));
  66. Assert.True(calib.StopAndWait(null, 1000));
  67. }
  68. // ── Task3.2b:标定预览帧缓冲的「非标定/无帧」安全分支(推流线程据此决定走 GrabStable) ──
  69. [Fact]
  70. public void IsCalibrating_NoSession_Returns_False()
  71. {
  72. // 无活跃协作器(未起标定)→ IsCalibrating=false → 推流线程走原 GrabStable 分支(行为零变化)。
  73. var (calib, _, _) = New();
  74. Assert.False(calib.IsCalibrating("nope"));
  75. Assert.False(calib.IsCalibrating(null));
  76. }
  77. [Fact]
  78. public void TryGetLatestFrame_NoFrame_Returns_False_And_Empty_Out()
  79. {
  80. // 未标定/无缓冲帧 → TryGetLatestFrame=false 且 out 全空;
  81. // 推流线程标定中遇此会跳过等下一帧、绝不 GrabStable(不争相机锁)。
  82. var (calib, _, _) = New();
  83. Assert.False(calib.TryGetLatestFrame("nope", out var bgr, out var w, out var h));
  84. Assert.Null(bgr);
  85. Assert.Equal(0, w);
  86. Assert.Equal(0, h);
  87. Assert.False(calib.TryGetLatestFrame(null, out _, out _, out _));
  88. }
  89. }
  90. }