#if HWTEST
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using NUnit.Framework;
namespace TIASshot.Tests {
/// <summary>
/// 実機テストの判断ロジック(HwTestJudge)のテスト(層①.SDK・UI に依存しない純粋関数).
///
/// 仕様: PLAN_06「検査項目」「守ること」・.claude/verify-profile.md「実機テスト」・HwTestJudge の各関数の契約
/// - preview: フレームレートの下限.close-preview: 所要時間の上限.ウォッチドッグは全体の上限を優先
/// - exposure-gain: 露光は Linear なら ×0.5/×2,Log2Step なら最も近い 2^n から ±1 段(範囲内に収める).
/// ゲインは範囲の幅の ±10%.露光は比(1 段下げ ≦ 0.8・上げ ≧ 1.25,確認した向きはすべて期待どおり),
/// ゲインは差(≧ 3,片側だけでも合格).動かした側が飽和(飽和画素の割合 > 1%)していれば,その向きは確認しない.
/// 両方とも確認できなければ FAIL
/// - 基準探しの判断の順番: 範囲の端 → 測れない → 中間に入った → 飛び越えた → 段数の上限 → 続ける
/// - light: 点灯と消灯の差 ≧ 100.復元: 読み戻しの相対誤差 1%(ゲインは範囲の幅 × 0.1% との大きい方)
/// - チャート不要の検査では校正を止める.終了コード 2/1/0,作業フォルダは keep 指定または 1・3 のとき残す
/// 実測の例は .verify/hwtest/run-20261008-133812/hwtest-result.txt(DFK 33UX178)から採った.
/// 本クラスは HWTEST 構成(Debug|x64)でのみコンパイルされる.
/// </summary>
[TestFixture]
public class HwTestJudgeTests {
private const double Eps = 1e-9;
private static HwTestMeasurement M(double mean, double saturatedFraction) {
return new HwTestMeasurement { Mean = mean, SaturatedFraction = saturatedFraction, MeanSaturatedFraction = saturatedFraction, Count = 8 };
}
private static double NextExp(double current, double min, double max, HwTestValueScale scale, int direction) {
return HwTestJudge.NextExposure(current, min, max, scale, direction,
HwTestThresholds.ExposureLogStep, HwTestThresholds.ExposureDownFactor, HwTestThresholds.ExposureUpFactor);
}
private static HwTestBaselineSearchDecision Decide(double mean, double sat, int direction, bool changed, int stepsDone) {
return HwTestJudge.DecideBaselineSearch(mean, sat, direction, changed, stepsDone, 12, 30, 220, 0.01);
}
// ================================================================
// フレームレート・しきい値
// ================================================================
[Test]
public void FrameRate_フレーム数を秒で割る() {
Assert.That(HwTestJudge.FrameRate(42, TimeSpan.FromSeconds(3)), Is.EqualTo(14.0).Within(Eps));
}
[Test]
public void FrameRate_時間が0_0() {
Assert.That(HwTestJudge.FrameRate(42, TimeSpan.Zero), Is.EqualTo(0.0));
}
[Test]
public void FrameRate_時間が負_0() {
Assert.That(HwTestJudge.FrameRate(42, TimeSpan.FromSeconds(-1)), Is.EqualTo(0.0));
}
[Test]
public void FrameRate_フレーム0_0() {
Assert.That(HwTestJudge.FrameRate(0, TimeSpan.FromSeconds(3)), Is.EqualTo(0.0));
}
[Test]
public void MeetsMinFrameRate_下限ちょうど_合格() {
Assert.That(HwTestJudge.MeetsMinFrameRate(10.0, 10.0), Is.True);
}
[Test]
public void MeetsMinFrameRate_下限未満_不合格() {
Assert.That(HwTestJudge.MeetsMinFrameRate(9.99, 10.0), Is.False);
}
[Test]
public void MeetsMinFrameRate_NaN_不合格() {
Assert.That(HwTestJudge.MeetsMinFrameRate(double.NaN, 10.0), Is.False);
}
[Test]
public void MeetsMinFrameRate_実測13_28fps_下限10で合格() {
// 実測: 13.28 fps(42 フレーム / 3.16 秒)
Assert.That(HwTestJudge.MeetsMinFrameRate(HwTestJudge.FrameRate(42, TimeSpan.FromSeconds(3.16)), HwTestThresholds.MinPreviewFps), Is.True);
}
[Test]
public void WithinLimit_上限ちょうど_以内() {
Assert.That(HwTestJudge.WithinLimit(TimeSpan.FromMilliseconds(1000), TimeSpan.FromMilliseconds(1000)), Is.True);
}
[Test]
public void WithinLimit_上限超え_超過() {
Assert.That(HwTestJudge.WithinLimit(TimeSpan.FromMilliseconds(1001), TimeSpan.FromMilliseconds(1000)), Is.False);
}
[Test]
public void FindOverdue_上限内_None() {
Assert.That(HwTestJudge.FindOverdue(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(10), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(60)),
Is.EqualTo(HwTestOverdue.None));
}
[Test]
public void FindOverdue_上限ちょうど_None() {
Assert.That(HwTestJudge.FindOverdue(TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10), TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(60)),
Is.EqualTo(HwTestOverdue.None));
}
[Test]
public void FindOverdue_項目の上限超え_Item() {
Assert.That(HwTestJudge.FindOverdue(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(10), TimeSpan.FromSeconds(61), TimeSpan.FromSeconds(60)),
Is.EqualTo(HwTestOverdue.Item));
}
[Test]
public void FindOverdue_全体と項目の両方超え_全体を優先() {
Assert.That(HwTestJudge.FindOverdue(TimeSpan.FromMinutes(11), TimeSpan.FromMinutes(10), TimeSpan.FromSeconds(61), TimeSpan.FromSeconds(60)),
Is.EqualTo(HwTestOverdue.Overall));
}
[Test]
public void FindOverdue_実行中の項目なし_項目の上限を見ない() {
Assert.That(HwTestJudge.FindOverdue(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(10), TimeSpan.FromHours(1), null),
Is.EqualTo(HwTestOverdue.None));
}
// ================================================================
// 露光・ゲインの段の計算
// ================================================================
[Test]
public void NextExposure_Linear_下げる_半分() {
Assert.That(NextExp(0.01, 0.0001, 1, HwTestValueScale.Linear, -1), Is.EqualTo(0.005).Within(Eps));
}
[Test]
public void NextExposure_Linear_上げる_2倍() {
Assert.That(NextExp(0.01, 0.0001, 1, HwTestValueScale.Linear, +1), Is.EqualTo(0.02).Within(Eps));
}
[Test]
public void NextExposure_Linear_上げて最大を超える_最大に収める() {
Assert.That(NextExp(0.8, 0.0001, 1, HwTestValueScale.Linear, +1), Is.EqualTo(1.0));
}
[Test]
public void NextExposure_Linear_下げて最小を下回る_最小に収める() {
Assert.That(NextExp(0.0003, 0.0002, 1, HwTestValueScale.Linear, -1), Is.EqualTo(0.0002));
}
[Test]
public void NextExposure_Log2Step_下げる_最も近い2のn乗から1段下() {
// 実測: 露光 0.016667(最も近い 2^n は 2^-6 = 0.015625)→ 下げた 0.007813(2^-7)
Assert.That(NextExp(0.016667, 2e-5, 60, HwTestValueScale.Log2Step, -1), Is.EqualTo(Math.Pow(2, -7)).Within(1e-12));
}
[Test]
public void NextExposure_Log2Step_上げる_最も近い2のn乗から1段上() {
// 実測: 露光 0.016667 → 上げた 0.03125(2^-5)
Assert.That(NextExp(0.016667, 2e-5, 60, HwTestValueScale.Log2Step, +1), Is.EqualTo(Math.Pow(2, -5)).Within(1e-12));
}
[Test]
public void NextExposure_Log2Step_ちょうど2のn乗_そこから1段() {
Assert.That(NextExp(0.015625, 2e-5, 60, HwTestValueScale.Log2Step, +1), Is.EqualTo(0.03125).Within(1e-12));
}
[Test]
public void NextExposure_Log2Step_最大を超える_最大に収める() {
Assert.That(NextExp(0.5, 2e-5, 0.6, HwTestValueScale.Log2Step, +1), Is.EqualTo(0.6));
}
[Test]
public void NextExposure_Log2Step_最小を下回る_最小に収める() {
Assert.That(NextExp(3e-5, 2e-5, 60, HwTestValueScale.Log2Step, -1), Is.EqualTo(2e-5));
}
[Test]
public void NextExposure_向きが0_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => NextExp(0.01, 0.0001, 1, HwTestValueScale.Linear, 0)));
}
[Test]
public void NextExposure_最小が最大より大きい_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => NextExp(0.01, 1, 0.0001, HwTestValueScale.Linear, 1)));
}
[Test]
public void NextExposure_現在値がNaN_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => NextExp(double.NaN, 0.0001, 1, HwTestValueScale.Linear, 1)));
}
[Test]
public void NextExposure_範囲が無限大_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => NextExp(0.01, 0, double.PositiveInfinity, HwTestValueScale.Linear, 1)));
}
[Test]
public void NextExposure_Log2Stepで現在値が0_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => NextExp(0, 0, 1, HwTestValueScale.Log2Step, 1)));
}
[Test]
public void NextExposure_Log2Stepで段の量が0_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() =>
HwTestJudge.NextExposure(0.01, 0, 1, HwTestValueScale.Log2Step, 1, 0, 0.5, 2)));
}
[Test]
public void NextExposure_未知の種類_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => NextExp(0.01, 0, 1, (HwTestValueScale)99, 1)));
}
[Test]
public void AlignExposure_Linear_そのまま() {
Assert.That(HwTestJudge.AlignExposure(0.016667, 2e-5, 60, HwTestValueScale.Linear), Is.EqualTo(0.016667));
}
[Test]
public void AlignExposure_Log2Step_最も近い2のn乗に合わせる() {
// 実測: 元の 0.016667 に最も近い 2^n(0.015625)に合わせた
Assert.That(HwTestJudge.AlignExposure(0.016667, 2e-5, 60, HwTestValueScale.Log2Step), Is.EqualTo(0.015625).Within(1e-12));
}
[Test]
public void AlignExposure_Log2Step_最も近い2のn乗が最小未満_範囲内で最も近い2のn乗() {
// 0.02 に最も近いのは 2^-6(0.015625)だが最小 0.02 未満.範囲 [0.02, 1] の中で最も近いのは 2^-5
Assert.That(HwTestJudge.AlignExposure(0.02, 0.02, 1, HwTestValueScale.Log2Step), Is.EqualTo(0.03125).Within(1e-12));
}
[Test]
public void AlignExposure_Log2Step_最も近い2のn乗が最大超え_範囲内で最も近い2のn乗() {
// 2.9 に最も近いのは 4(2^2)だが最大 2.9 を超える.範囲内で最も近いのは 2(2^1)
Assert.That(HwTestJudge.AlignExposure(2.9, 0.1, 2.9, HwTestValueScale.Log2Step), Is.EqualTo(2.0).Within(1e-12));
}
[Test]
public void AlignExposure_Log2Step_最大がちょうど2のn乗_最大に合わせる() {
// 7 に最も近いのは 8 = 最大(log の丸め誤差で 1 段ずれないこと)
Assert.That(HwTestJudge.AlignExposure(7, 0.1, 8, HwTestValueScale.Log2Step), Is.EqualTo(8.0).Within(1e-12));
}
[Test]
public void AlignExposure_Log2Step_範囲内に2のn乗が無い_そのまま() {
Assert.That(HwTestJudge.AlignExposure(0.35, 0.3, 0.4, HwTestValueScale.Log2Step), Is.EqualTo(0.35));
}
[Test]
public void AlignExposure_Log2Stepで現在値が0_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => HwTestJudge.AlignExposure(0, 0, 1, HwTestValueScale.Log2Step)));
}
[Test]
public void AlignExposure_範囲が不正_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => HwTestJudge.AlignExposure(0.5, 1, 0, HwTestValueScale.Linear)));
}
[Test]
public void ScaleSetting_倍率を掛ける() {
Assert.That(HwTestJudge.ScaleSetting(0.4, 0, 1, 0.5), Is.EqualTo(0.2).Within(Eps));
}
[Test]
public void ScaleSetting_範囲外_範囲内に収める() {
Assert.That(HwTestJudge.ScaleSetting(0.6, 0, 1, 2), Is.EqualTo(1.0));
}
[Test]
public void ScaleSetting_現在値0で上げる_最小から範囲の幅の5パーセント() {
Assert.That(HwTestJudge.ScaleSetting(0, 0, 10, 2), Is.EqualTo(0.5).Within(Eps));
}
[Test]
public void ScaleSetting_現在値0で下げる_変わらない() {
Assert.That(HwTestJudge.ScaleSetting(0, 0, 10, 0.5), Is.EqualTo(0.0));
}
[Test]
public void ScaleSetting_倍率0_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => HwTestJudge.ScaleSetting(0.5, 0, 1, 0)));
}
[Test]
public void ScaleSetting_倍率NaN_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => HwTestJudge.ScaleSetting(0.5, 0, 1, double.NaN)));
}
[Test]
public void StepSetting_範囲の幅の10パーセント上げる() {
// 実測: ゲイン 1(範囲 0〜48)→ 上げた 5.8
Assert.That(HwTestJudge.StepSetting(1, 0, 48, HwTestThresholds.GainStepFraction), Is.EqualTo(5.8).Within(Eps));
}
[Test]
public void StepSetting_下げて最小を下回る_最小に収める() {
// 実測: ゲイン 1(範囲 0〜48)→ 下げた 0
Assert.That(HwTestJudge.StepSetting(1, 0, 48, -HwTestThresholds.GainStepFraction), Is.EqualTo(0.0));
}
[Test]
public void StepSetting_上げて最大を超える_最大に収める() {
Assert.That(HwTestJudge.StepSetting(47, 0, 48, HwTestThresholds.GainStepFraction), Is.EqualTo(48.0));
}
[Test]
public void StepSetting_割合NaN_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => HwTestJudge.StepSetting(1, 0, 48, double.NaN)));
}
[Test]
public void StepSetting_範囲が不正_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => HwTestJudge.StepSetting(1, 48, 0, 0.1)));
}
// ================================================================
// 輝度・飽和
// ================================================================
[Test]
public void FrameStats_BGR_3チャネルの平均() {
var rows = new byte[] { 10, 20, 30, 40, 50, 60 };
var s = HwTestJudge.FrameStats(rows, 1, 2, 6, 3, 1, 254);
Assert.That(s.Mean, Is.EqualTo(35.0).Within(Eps));
}
[Test]
public void FrameStats_飽和なし_割合0() {
var rows = new byte[] { 253, 253, 253, 0, 0, 0 };
var s = HwTestJudge.FrameStats(rows, 1, 2, 6, 3, 1, 254);
Assert.That(s.SaturatedFraction, Is.EqualTo(0.0));
}
[Test]
public void FrameStats_いずれかのチャネルが254以上_飽和画素として数える() {
// 2 画素のうち 1 画素の R だけが 254
var rows = new byte[] { 0, 0, 254, 100, 100, 100 };
var s = HwTestJudge.FrameStats(rows, 1, 2, 6, 3, 1, HwTestThresholds.SaturatedPixelLevel);
Assert.That(s.SaturatedFraction, Is.EqualTo(0.5).Within(Eps));
}
[Test]
public void FrameStats_4チャネル_アルファを読まない() {
var rows = new byte[] { 0, 0, 0, 255, 30, 30, 30, 255 };
var s = HwTestJudge.FrameStats(rows, 1, 2, 8, 4, 1, 254);
Assert.That(new[] { s.Mean, s.SaturatedFraction }, Is.EqualTo(new[] { 15.0, 0.0 }).Within(Eps));
}
[Test]
public void FrameStats_1チャネル_先頭チャネルの平均() {
var rows = new byte[] { 10, 30, 50, 70 };
var s = HwTestJudge.FrameStats(rows, 2, 2, 2, 1, 1, 254);
Assert.That(s.Mean, Is.EqualTo(40.0).Within(Eps));
}
[Test]
public void FrameStats_間引き_指定画素ごとに読む() {
// 1 チャネル・4 画素で 2 画素ごと → 画素 0 と 2 だけを読む
var rows = new byte[] { 10, 200, 30, 200 };
var s = HwTestJudge.FrameStats(rows, 1, 4, 4, 1, 2, 254);
Assert.That(s.Mean, Is.EqualTo(20.0).Within(Eps));
}
[Test]
public void FrameStats_行の詰め物_読まない() {
// 1 行 = 2 画素 × 3 バイト + 詰め物 2 バイト(255)
var rows = new byte[] { 10, 10, 10, 20, 20, 20, 255, 255, 30, 30, 30, 40, 40, 40, 255, 255 };
var s = HwTestJudge.FrameStats(rows, 2, 2, 8, 3, 1, 254);
Assert.That(new[] { s.Mean, s.SaturatedFraction }, Is.EqualTo(new[] { 25.0, 0.0 }).Within(Eps));
}
[Test]
public void FrameStats_rowsがnull_ArgumentException() {
Assert.Throws<ArgumentNullException>((Action)(() => HwTestJudge.FrameStats(null, 1, 1, 3, 3, 1, 254)));
}
[Test]
public void FrameStats_1画素のバイト数が5_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => HwTestJudge.FrameStats(new byte[10], 1, 2, 10, 5, 1, 254)));
}
[Test]
public void FrameStats_1行のバイト数が不足_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => HwTestJudge.FrameStats(new byte[6], 1, 2, 5, 3, 1, 254)));
}
[Test]
public void FrameStats_画素データが不足_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => HwTestJudge.FrameStats(new byte[6], 2, 2, 6, 3, 1, 254)));
}
[Test]
public void FrameStats_間引き0_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => HwTestJudge.FrameStats(new byte[6], 1, 2, 6, 3, 0, 254)));
}
[Test]
public void FrameStats_画素なし_ArgumentException() {
Assert.Throws<ArgumentException>((Action)(() => HwTestJudge.FrameStats(new byte[6], 0, 2, 6, 3, 1, 254)));
}
[Test]
public void IsSaturated_しきい値ちょうど_飽和でない() {
Assert.That(HwTestJudge.IsSaturated(0.01, 0.01), Is.False);
}
[Test]
public void IsSaturated_しきい値超え_飽和() {
Assert.That(HwTestJudge.IsSaturated(0.0101, 0.01), Is.True);
}
[Test]
public void IsSaturated_NaN_飽和でない() {
Assert.That(HwTestJudge.IsSaturated(double.NaN, 0.01), Is.False);
}
[TestCase(30.0)]
[TestCase(125.0)]
[TestCase(220.0)]
public void IsMidBrightness_範囲内と境界_true(double mean) {
Assert.That(HwTestJudge.IsMidBrightness(mean, 30, 220), Is.True);
}
[TestCase(29.99)]
[TestCase(220.01)]
[TestCase(double.NaN)]
public void IsMidBrightness_範囲外とNaN_false(double mean) {
Assert.That(HwTestJudge.IsMidBrightness(mean, 30, 220), Is.False);
}
[Test]
public void IsBaselineCandidate_中間で飽和なし_true() {
// 実測: 露光 0.015625 の平均輝度 158.94・飽和画素の割合 0.00% は基準にできる
Assert.That(HwTestJudge.IsBaselineCandidate(158.94, 0.0, 30, 220, 0.01), Is.True);
}
[Test]
public void IsBaselineCandidate_中間でも飽和_false() {
Assert.That(HwTestJudge.IsBaselineCandidate(200, 0.05, 30, 220, 0.01), Is.False);
}
[Test]
public void IsBaselineCandidate_飽和割合NaN_false() {
Assert.That(HwTestJudge.IsBaselineCandidate(150, double.NaN, 30, 220, 0.01), Is.False);
}
[Test]
public void IsBaselineCandidate_明るすぎ_false() {
Assert.That(HwTestJudge.IsBaselineCandidate(230, 0.0, 30, 220, 0.01), Is.False);
}
[Test]
public void Summarize_平均輝度は平均() {
var m = HwTestJudge.Summarize(new[] { new HwTestFrameStats(100, 0.0), new HwTestFrameStats(110, 0.04) });
Assert.That(m.Mean, Is.EqualTo(105.0).Within(Eps));
}
[Test]
public void Summarize_飽和割合は最大() {
var m = HwTestJudge.Summarize(new[] { new HwTestFrameStats(100, 0.0), new HwTestFrameStats(110, 0.04), new HwTestFrameStats(100, 0.02) });
Assert.That(m.SaturatedFraction, Is.EqualTo(0.04).Within(Eps));
}
[Test]
public void Summarize_飽和割合の平均を参考に持つ() {
var m = HwTestJudge.Summarize(new[] { new HwTestFrameStats(100, 0.0), new HwTestFrameStats(110, 0.04) });
Assert.That(m.MeanSaturatedFraction, Is.EqualTo(0.02).Within(Eps));
}
[Test]
public void Summarize_フレーム数() {
var m = HwTestJudge.Summarize(new[] { new HwTestFrameStats(100, 0.0), new HwTestFrameStats(110, 0.04) });
Assert.That(m.Count, Is.EqualTo(2));
}
[Test]
public void Summarize_空_測定できない() {
var m = HwTestJudge.Summarize(new HwTestFrameStats[0]);
Assert.That(new object[] { double.IsNaN(m.Mean), double.IsNaN(m.SaturatedFraction), m.Count }, Is.EqualTo(new object[] { true, true, 0 }));
}
[Test]
public void Summarize_null_測定できない() {
var m = HwTestJudge.Summarize(null);
Assert.That(new object[] { double.IsNaN(m.Mean), m.Count }, Is.EqualTo(new object[] { true, 0 }));
}
// ================================================================
// 基準探し
// ================================================================
[Test]
public void BaselineSearchDirection_明るすぎ_下げる() {
Assert.That(HwTestJudge.BaselineSearchDirection(230, 0.0, 30, 220, 0.01), Is.EqualTo(-1));
}
[Test]
public void BaselineSearchDirection_中間でも飽和_下げる() {
Assert.That(HwTestJudge.BaselineSearchDirection(200, 0.05, 30, 220, 0.01), Is.EqualTo(-1));
}
[Test]
public void BaselineSearchDirection_暗すぎ_上げる() {
Assert.That(HwTestJudge.BaselineSearchDirection(20, 0.0, 30, 220, 0.01), Is.EqualTo(1));
}
[Test]
public void BaselineSearchDirection_基準にできる明るさ_動かさない() {
Assert.That(HwTestJudge.BaselineSearchDirection(158.94, 0.0, 30, 220, 0.01), Is.EqualTo(0));
}
[Test]
public void BaselineSearchDirection_測定できない_動かさない() {
Assert.That(HwTestJudge.BaselineSearchDirection(double.NaN, double.NaN, 30, 220, 0.01), Is.EqualTo(0));
}
[Test]
public void DecideBaselineSearch_設定が変わらない_範囲の端を最優先() {
// 測定できない・中間に入った,のどちらにも当たる値でも範囲の端が先
Assert.That(Decide(double.NaN, double.NaN, -1, false, 1), Is.EqualTo(HwTestBaselineSearchDecision.StopRangeEnd));
}
[Test]
public void DecideBaselineSearch_設定が変わらない_中間でも範囲の端() {
Assert.That(Decide(150, 0.0, -1, false, 1), Is.EqualTo(HwTestBaselineSearchDecision.StopRangeEnd));
}
[Test]
public void DecideBaselineSearch_平均輝度NaN_測れない() {
Assert.That(Decide(double.NaN, 0.0, -1, true, 1), Is.EqualTo(HwTestBaselineSearchDecision.StopNotMeasured));
}
[Test]
public void DecideBaselineSearch_飽和割合NaN_測れない() {
Assert.That(Decide(150, double.NaN, -1, true, 1), Is.EqualTo(HwTestBaselineSearchDecision.StopNotMeasured));
}
[Test]
public void DecideBaselineSearch_中間に入った_Found() {
Assert.That(Decide(150, 0.0, -1, true, 1), Is.EqualTo(HwTestBaselineSearchDecision.Found));
}
[Test]
public void DecideBaselineSearch_段数の上限で中間に入った_上限よりFoundが先() {
Assert.That(Decide(150, 0.0, -1, true, 12), Is.EqualTo(HwTestBaselineSearchDecision.Found));
}
[Test]
public void DecideBaselineSearch_下げる向きで暗すぎになった_飛び越えた() {
Assert.That(Decide(20, 0.0, -1, true, 1), Is.EqualTo(HwTestBaselineSearchDecision.StopOvershoot));
}
[Test]
public void DecideBaselineSearch_上げる向きで明るすぎになった_飛び越えた() {
Assert.That(Decide(230, 0.0, 1, true, 1), Is.EqualTo(HwTestBaselineSearchDecision.StopOvershoot));
}
[Test]
public void DecideBaselineSearch_上げる向きで飽和した_飛び越えた() {
Assert.That(Decide(200, 0.05, 1, true, 1), Is.EqualTo(HwTestBaselineSearchDecision.StopOvershoot));
}
[Test]
public void DecideBaselineSearch_段数の上限で飛び越えた_上限より飛び越えたが先() {
Assert.That(Decide(20, 0.0, -1, true, 12), Is.EqualTo(HwTestBaselineSearchDecision.StopOvershoot));
}
[Test]
public void DecideBaselineSearch_段数の上限でまだ明るすぎ_上限() {
Assert.That(Decide(230, 0.0, -1, true, 12), Is.EqualTo(HwTestBaselineSearchDecision.StopMaxSteps));
}
[Test]
public void DecideBaselineSearch_まだ明るすぎ_続ける() {
Assert.That(Decide(230, 0.0, -1, true, 3), Is.EqualTo(HwTestBaselineSearchDecision.Continue));
}
[Test]
public void DecideBaselineSearch_まだ暗すぎ_続ける() {
Assert.That(Decide(20, 0.0, 1, true, 3), Is.EqualTo(HwTestBaselineSearchDecision.Continue));
}
[Test]
public void ChooseBaselineIndex_中間に最も近い段() {
var samples = new List<HwTestBaselineSample> {
new HwTestBaselineSample(1, 250, 0), new HwTestBaselineSample(2, 228, 0), new HwTestBaselineSample(3, 10, 0),
};
Assert.That(HwTestJudge.ChooseBaselineIndex(samples, 30, 220, 0.01), Is.EqualTo(1));
}
[Test]
public void ChooseBaselineIndex_飽和していない段を優先() {
// 段 0 は中間の範囲内だが飽和,段 1 は暗すぎるが飽和していない
var samples = new List<HwTestBaselineSample> {
new HwTestBaselineSample(1, 200, 0.05), new HwTestBaselineSample(2, 5, 0),
};
Assert.That(HwTestJudge.ChooseBaselineIndex(samples, 30, 220, 0.01), Is.EqualTo(1));
}
[Test]
public void ChooseBaselineIndex_すべて飽和_その中で中間に最も近い段() {
var samples = new List<HwTestBaselineSample> {
new HwTestBaselineSample(1, 250, 0.5), new HwTestBaselineSample(2, 230, 0.2),
};
Assert.That(HwTestJudge.ChooseBaselineIndex(samples, 30, 220, 0.01), Is.EqualTo(1));
}
[Test]
public void ChooseBaselineIndex_NaNは除く() {
var samples = new List<HwTestBaselineSample> {
new HwTestBaselineSample(1, double.NaN, double.NaN), new HwTestBaselineSample(2, 10, 0),
};
Assert.That(HwTestJudge.ChooseBaselineIndex(samples, 30, 220, 0.01), Is.EqualTo(1));
}
[Test]
public void ChooseBaselineIndex_同じ近さ_先の段() {
var samples = new List<HwTestBaselineSample> {
new HwTestBaselineSample(1, 240, 0), new HwTestBaselineSample(2, 10, 0),
};
Assert.That(HwTestJudge.ChooseBaselineIndex(samples, 30, 220, 0.01), Is.EqualTo(0));
}
[Test]
public void ChooseBaselineIndex_すべてNaN_マイナス1() {
var samples = new List<HwTestBaselineSample> { new HwTestBaselineSample(1, double.NaN, double.NaN) };
Assert.That(HwTestJudge.ChooseBaselineIndex(samples, 30, 220, 0.01), Is.EqualTo(-1));
}
[Test]
public void ChooseBaselineIndex_空_マイナス1() {
Assert.That(HwTestJudge.ChooseBaselineIndex(new List<HwTestBaselineSample>(), 30, 220, 0.01), Is.EqualTo(-1));
}
[Test]
public void ChooseBaselineIndex_null_マイナス1() {
Assert.That(HwTestJudge.ChooseBaselineIndex(null, 30, 220, 0.01), Is.EqualTo(-1));
}
// ================================================================
// 方向の判定(露光: 比・すべて確認した向きが期待どおり)
// ================================================================
[Test]
public void ExposureCriteria_比で見て下げる0_8以下_上げる1_25以上_すべて確認() {
var c = HwTestJudge.ExposureCriteria();
Assert.That(new object[] { c.UseRatio, c.LowerLimit, c.RaiseLimit, c.MaxSaturatedFraction, c.RequireAllChecked },
Is.EqualTo(new object[] { true, 0.8, 1.25, 0.01, true }));
}
[Test]
public void JudgeDirection_露光_実測_下げる側が期待どおりで合格() {
// 実測: 基準 158.92 → 下げた 86.63(比 0.545).上げた 210.14 は飽和割合 70.18%
var r = HwTestJudge.JudgeDirection(M(158.92, 0.0), M(86.63, 0.0), M(210.14, 0.7018), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.Pass, Is.True);
}
[Test]
public void JudgeDirection_露光_実測_下げる側を確認して期待どおり() {
var r = HwTestJudge.JudgeDirection(M(158.92, 0.0), M(86.63, 0.0), M(210.14, 0.7018), true, true, HwTestJudge.ExposureCriteria());
Assert.That(new[] { r.LowerChecked, r.LowerOk }, Is.EqualTo(new[] { true, true }));
}
[Test]
public void JudgeDirection_露光_実測_上げた側が飽和_確認しない() {
var r = HwTestJudge.JudgeDirection(M(158.92, 0.0), M(86.63, 0.0), M(210.14, 0.7018), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.RaiseChecked, Is.False);
}
[Test]
public void JudgeDirection_露光_実測_説明に飽和のため確認しないと飽和割合が出る() {
var r = HwTestJudge.JudgeDirection(M(158.92, 0.0), M(86.63, 0.0), M(210.14, 0.7018), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.Summary, Does.Contain("飽和のため確認しない(飽和割合 70.18%"));
}
[Test]
public void JudgeDirection_露光_比が境界ちょうど_合格() {
var r = HwTestJudge.JudgeDirection(M(100, 0), M(80, 0), M(125, 0), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.Pass, Is.True);
}
[Test]
public void JudgeDirection_露光_下げても暗くならない_不合格() {
var r = HwTestJudge.JudgeDirection(M(100, 0), M(81, 0), M(150, 0), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.Pass, Is.False);
}
[Test]
public void JudgeDirection_露光_上げても明るくならない_不合格() {
var r = HwTestJudge.JudgeDirection(M(100, 0), M(50, 0), M(124, 0), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.Pass, Is.False);
}
[Test]
public void JudgeDirection_露光_上げた側が飽和_比が足りなくても不合格にしない() {
// 飽和で頭打ちになり比 1.1 だが,飽和しているので確認しない(下げる側の合格で合格)
var r = HwTestJudge.JudgeDirection(M(100, 0), M(50, 0), M(110, 0.3), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.Pass, Is.True);
}
[Test]
public void JudgeDirection_露光_上げた側が飽和_比を満たしても期待どおりとしない() {
var r = HwTestJudge.JudgeDirection(M(100, 0), M(50, 0), M(200, 0.3), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.RaiseOk, Is.False);
}
[Test]
public void JudgeDirection_露光_下げた側が飽和_確認しない() {
var r = HwTestJudge.JudgeDirection(M(200, 0), M(150, 0.05), M(255, 0), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.LowerChecked, Is.False);
}
[Test]
public void JudgeDirection_露光_下げた側が飽和_説明に飽和のため確認しないが出る() {
var r = HwTestJudge.JudgeDirection(M(200, 0), M(150, 0.05), M(255, 0), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.Summary, Does.Contain("下げる: 飽和のため確認しない(飽和割合"));
}
[Test]
public void JudgeDirection_露光_飽和割合がしきい値ちょうど_確認する() {
var r = HwTestJudge.JudgeDirection(M(100, 0), M(50, 0), M(150, 0.01), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.RaiseChecked, Is.True);
}
[Test]
public void JudgeDirection_露光_両側とも飽和_不合格() {
var r = HwTestJudge.JudgeDirection(M(200, 0), M(150, 0.05), M(255, 0.5), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.Pass, Is.False);
}
[Test]
public void JudgeDirection_露光_両側とも確認できない_説明に出る() {
var r = HwTestJudge.JudgeDirection(M(200, 0), M(150, 0.05), M(255, 0.5), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.Summary, Does.Contain("どちらの向きも確認できません"));
}
[Test]
public void JudgeDirection_露光_設定が変わらなかった向き_確認しない() {
var r = HwTestJudge.JudgeDirection(M(100, 0), M(100, 0), M(150, 0), false, true, HwTestJudge.ExposureCriteria());
Assert.That(r.LowerChecked, Is.False);
}
[Test]
public void JudgeDirection_露光_片側だけ設定が変わり期待どおり_合格() {
var r = HwTestJudge.JudgeDirection(M(100, 0), M(100, 0), M(150, 0), false, true, HwTestJudge.ExposureCriteria());
Assert.That(r.Pass, Is.True);
}
[Test]
public void JudgeDirection_露光_基準が真っ暗_両側とも確認せず不合格() {
var r = HwTestJudge.JudgeDirection(M(3, 0), M(2, 0), M(8, 0), true, true, HwTestJudge.ExposureCriteria());
Assert.That(new[] { r.LowerChecked, r.RaiseChecked, r.Pass }, Is.EqualTo(new[] { false, false, false }));
}
[Test]
public void JudgeDirection_露光_基準が飽和_上げる向きを確認しない() {
var r = HwTestJudge.JudgeDirection(M(240, 0.2), M(120, 0), M(250, 0.2), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.RaiseChecked, Is.False);
}
[Test]
public void JudgeDirection_露光_下げた側が測定できない_確認しない() {
var r = HwTestJudge.JudgeDirection(M(100, 0), HwTestMeasurement.NotMeasured, M(150, 0), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.LowerChecked, Is.False);
}
[Test]
public void JudgeDirection_基準が測定できない_不合格() {
var r = HwTestJudge.JudgeDirection(HwTestMeasurement.NotMeasured, M(50, 0), M(150, 0), true, true, HwTestJudge.ExposureCriteria());
Assert.That(r.Pass, Is.False);
}
[Test]
public void JudgeDirection_基準がnull_ArgumentNullException() {
Assert.Throws<ArgumentNullException>((Action)(() =>
HwTestJudge.JudgeDirection(M(100, 0), M(50, 0), M(150, 0), true, true, null)));
}
// ================================================================
// 方向の判定(ゲイン: 差・片側だけでも合格)
// ================================================================
[Test]
public void GainCriteria_差で見て3以上_片側で合格() {
var c = HwTestJudge.GainCriteria();
Assert.That(new object[] { c.UseRatio, c.LowerLimit, c.RaiseLimit, c.MaxSaturatedFraction, c.RequireAllChecked },
Is.EqualTo(new object[] { false, 3.0, 3.0, 0.01, false }));
}
[Test]
public void JudgeDirection_ゲイン_実測_上げた側が飽和でも下げる側で合格() {
// 実測: 基準 158.88 → 下げた 143.27(差 -15.6).上げた 206.02 は飽和割合 68.37%
var r = HwTestJudge.JudgeDirection(M(158.88, 0.0), M(143.27, 0.0), M(206.02, 0.6837), true, true, HwTestJudge.GainCriteria());
Assert.That(r.Pass, Is.True);
}
[Test]
public void JudgeDirection_ゲイン_実測_上げた側は確認しない() {
var r = HwTestJudge.JudgeDirection(M(158.88, 0.0), M(143.27, 0.0), M(206.02, 0.6837), true, true, HwTestJudge.GainCriteria());
Assert.That(r.RaiseChecked, Is.False);
}
[Test]
public void JudgeDirection_ゲイン_差が境界ちょうど_期待どおり() {
var r = HwTestJudge.JudgeDirection(M(100, 0), M(97, 0), M(103, 0), true, true, HwTestJudge.GainCriteria());
Assert.That(new[] { r.LowerOk, r.RaiseOk }, Is.EqualTo(new[] { true, true }));
}
[Test]
public void JudgeDirection_ゲイン_下げる側だけ足りない_片側で合格() {
// 範囲の下端で変化が小さい(DFK 33UX178 の実測: 10→0 で差 -3.2 の付近)
var r = HwTestJudge.JudgeDirection(M(100, 0), M(99, 0), M(110, 0), true, true, HwTestJudge.GainCriteria());
Assert.That(r.Pass, Is.True);
}
[Test]
public void JudgeDirection_ゲイン_片側で合格_説明に出る() {
var r = HwTestJudge.JudgeDirection(M(100, 0), M(99, 0), M(110, 0), true, true, HwTestJudge.GainCriteria());
Assert.That(r.Summary, Does.Contain("一方の向きが期待どおりのため合格"));
}
[Test]
public void JudgeDirection_ゲイン_両側とも足りない_不合格() {
var r = HwTestJudge.JudgeDirection(M(100, 0), M(99, 0), M(102, 0), true, true, HwTestJudge.GainCriteria());
Assert.That(r.Pass, Is.False);
}
[Test]
public void JudgeDirection_ゲイン_上げた側が飽和で下げる側が足りない_不合格() {
// 上げた側は差 +50 だが飽和しているので確認しない
var r = HwTestJudge.JudgeDirection(M(100, 0), M(99, 0), M(150, 0.5), true, true, HwTestJudge.GainCriteria());
Assert.That(r.Pass, Is.False);
}
[Test]
public void JudgeDirection_ゲイン_両側とも確認できない_不合格() {
var r = HwTestJudge.JudgeDirection(M(100, 0), M(100, 0), M(150, 0.5), false, true, HwTestJudge.GainCriteria());
Assert.That(r.Pass, Is.False);
}
// ================================================================
// 光源・復元
// ================================================================
[Test]
public void JudgeLight_実測_点灯168_63消灯15_20_合格() {
Assert.That(HwTestJudge.JudgeLight(168.63, 15.20, HwTestThresholds.MinLightDelta), Is.True);
}
[Test]
public void JudgeLight_差が下限ちょうど_合格() {
Assert.That(HwTestJudge.JudgeLight(150, 50, 100), Is.True);
}
[Test]
public void JudgeLight_差が下限未満_不合格() {
Assert.That(HwTestJudge.JudgeLight(149.9, 50, 100), Is.False);
}
[Test]
public void JudgeLight_消灯の方が明るい_不合格() {
Assert.That(HwTestJudge.JudgeLight(50, 200, 100), Is.False);
}
[Test]
public void JudgeLight_NaN_不合格() {
Assert.That(HwTestJudge.JudgeLight(double.NaN, 15, 100), Is.False);
}
[Test]
public void MinLightDelta_100() {
Assert.That(HwTestThresholds.MinLightDelta, Is.EqualTo(100.0));
}
[Test]
public void IsSameSetting_相対誤差1パーセント以内_同じ() {
Assert.That(HwTestJudge.IsSameSetting(0.016667, 0.016667 * 1.005, 0.01, 0), Is.True);
}
[Test]
public void IsSameSetting_相対誤差1パーセント超え_違う() {
Assert.That(HwTestJudge.IsSameSetting(0.016667, 0.016667 * 1.02, 0.01, 0), Is.False);
}
[Test]
public void IsSameSetting_元が0でも絶対誤差の許容内_同じ() {
// ゲイン 0 dB(範囲 0〜48): 絶対誤差の許容 48 × 0.1% = 0.048
Assert.That(HwTestJudge.IsSameSetting(0, 0.04, 0.01, 48 * HwTestThresholds.GainRestoreRangeFraction), Is.True);
}
[Test]
public void IsSameSetting_元が0で絶対誤差の許容超え_違う() {
Assert.That(HwTestJudge.IsSameSetting(0, 0.05, 0.01, 48 * HwTestThresholds.GainRestoreRangeFraction), Is.False);
}
[Test]
public void IsSameSetting_相対と絶対の大きい方を許容にする() {
// 元 1 → 1.04: 相対の許容は約 0.0104 だが,絶対の許容 0.048 の方が大きい
Assert.That(HwTestJudge.IsSameSetting(1, 1.04, 0.01, 0.048), Is.True);
}
[Test]
public void IsSameSetting_NaN_違う() {
Assert.That(HwTestJudge.IsSameSetting(1, double.NaN, 0.01, 0.048), Is.False);
}
[Test]
public void RestoreTolerances_相対1パーセント_ゲインは範囲の0_1パーセント() {
Assert.That(new[] { HwTestThresholds.RestoreRelativeTolerance, HwTestThresholds.GainRestoreRangeFraction },
Is.EqualTo(new[] { 0.01, 0.001 }));
}
[Test]
public void IsRestored_許容内_戻った() {
// 実測: 最初 168.73 → 元に戻した後 168.64
Assert.That(HwTestJudge.IsRestored(168.73, 168.64, HwTestThresholds.RestoreTolerance), Is.True);
}
[Test]
public void IsRestored_許容ちょうど_戻った() {
Assert.That(HwTestJudge.IsRestored(100, 110, 10), Is.True);
}
[Test]
public void IsRestored_許容超え_戻っていない() {
Assert.That(HwTestJudge.IsRestored(100, 110.5, 10), Is.False);
}
[Test]
public void IsRestored_NaN_戻っていない() {
Assert.That(HwTestJudge.IsRestored(100, double.NaN, 10), Is.False);
}
[Test]
public void RelativeError_差を元の絶対値で割る() {
Assert.That(HwTestJudge.RelativeError(-2, -1), Is.EqualTo(0.5).Within(Eps));
}
[Test]
public void RelativeError_NaN_NaN() {
Assert.That(HwTestJudge.RelativeError(double.NaN, 1), Is.NaN);
}
[Test]
public void RelativeError_元も読み戻しも0_0() {
Assert.That(HwTestJudge.RelativeError(0, 0), Is.EqualTo(0.0));
}
[Test]
public void RelativeError_元が0で読み戻しが0でない_正の無限大() {
Assert.That(HwTestJudge.RelativeError(0, 1), Is.EqualTo(double.PositiveInfinity));
}
[Test]
public void DescribeRestoredValue_丸めない値を往復できる書式で出す() {
var v = 1.0 / 60;
Assert.That(HwTestJudge.DescribeRestoredValue("露光", v, v), Does.Contain("読み戻し " + v.ToString("R", CultureInfo.InvariantCulture)));
}
[Test]
public void DescribeRestoredValue_一致_相対誤差0パーセント() {
Assert.That(HwTestJudge.DescribeRestoredValue("露光", 0.5, 0.5), Does.Contain("相対誤差 0%"));
}
[Test]
public void DescribeRestoredValue_元が0_元が0と出る() {
Assert.That(HwTestJudge.DescribeRestoredValue("ゲイン", 0, 0.01), Does.Contain("元が 0"));
}
[Test]
public void DescribeRestoredValue_NaN_測定できないと出る() {
Assert.That(HwTestJudge.DescribeRestoredValue("ゲイン", 1, double.NaN), Does.Contain("測定できない"));
}
[Test]
public void DescribeRestoredValue_小数点がカンマの文化でもピリオドで出す() {
var saved = Thread.CurrentThread.CurrentCulture;
string text;
try {
Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");
text = HwTestJudge.DescribeRestoredValue("露光", 0.25, 0.25);
} finally {
Thread.CurrentThread.CurrentCulture = saved;
}
Assert.That(text, Does.Contain("読み戻し 0.25(元 0.25."));
}
// ================================================================
// 校正の停止
// ================================================================
[Test]
public void ShouldSuppressCalibration_チャート不要の検査_止める() {
Assert.That(HwTestJudge.ShouldSuppressCalibration(false), Is.True);
}
[Test]
public void ShouldSuppressCalibration_チャートが必要な検査_止めない() {
Assert.That(HwTestJudge.ShouldSuppressCalibration(true), Is.False);
}
[Test]
public void IsCalibrationLeaked_止めているのに校正中_漏れ() {
Assert.That(HwTestJudge.IsCalibrationLeaked(true, true, false), Is.True);
}
[Test]
public void IsCalibrationLeaked_止めているのに校正済み_漏れ() {
Assert.That(HwTestJudge.IsCalibrationLeaked(true, false, true), Is.True);
}
[Test]
public void IsCalibrationLeaked_止めていて校正なし_漏れでない() {
Assert.That(HwTestJudge.IsCalibrationLeaked(true, false, false), Is.False);
}
[Test]
public void IsCalibrationLeaked_止めていない_校正中でも漏れでない() {
Assert.That(HwTestJudge.IsCalibrationLeaked(false, true, true), Is.False);
}
// ================================================================
// 終了コードと作業フォルダ
// ================================================================
[Test]
public void DecideExitCode_機種が見つからない_不合格数によらず2() {
Assert.That(HwTestJudge.DecideExitCode(true, 3), Is.EqualTo(2));
}
[Test]
public void DecideExitCode_不合格あり_1() {
Assert.That(HwTestJudge.DecideExitCode(false, 1), Is.EqualTo(1));
}
[Test]
public void DecideExitCode_すべて合格_0() {
Assert.That(HwTestJudge.DecideExitCode(false, 0), Is.EqualTo(0));
}
[Test]
public void ExitCodes_0合格_1不合格_2異常_3タイムアウト() {
Assert.That(new[] { HwTestExitCodes.Pass, HwTestExitCodes.Fail, HwTestExitCodes.Error, HwTestExitCodes.Timeout },
Is.EqualTo(new[] { 0, 1, 2, 3 }));
}
[TestCase(0)]
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
public void ShouldKeepWorkDir_keep指定_常に残す(int exitCode) {
Assert.That(HwTestJudge.ShouldKeepWorkDir(true, exitCode), Is.True);
}
[TestCase(1)]
[TestCase(3)]
public void ShouldKeepWorkDir_不合格とタイムアウト_残す(int exitCode) {
Assert.That(HwTestJudge.ShouldKeepWorkDir(false, exitCode), Is.True);
}
[TestCase(0)]
[TestCase(2)]
public void ShouldKeepWorkDir_合格と前提不足_消す(int exitCode) {
Assert.That(HwTestJudge.ShouldKeepWorkDir(false, exitCode), Is.False);
}
}
}
#endif