diff --git a/TIASshot.Tests/IoUtilTests.cs b/TIASshot.Tests/IoUtilTests.cs index 5ff291e..d770ef5 100644 --- a/TIASshot.Tests/IoUtilTests.cs +++ b/TIASshot.Tests/IoUtilTests.cs @@ -70,6 +70,114 @@ } /// + /// 再現テスト: 小数点にカンマを使うロケール(de-DE)でもピリオド小数の CSV を正しく解釈する + /// (同梱 tcc_srgb.csv 等はピリオド小数で記述される仕様のため,パースはロケール非依存であること. + /// カルチャ依存だと "96.32301186" が桁区切り扱いで 9632301186 に化け,例外にならず校正が黙って壊れる) + /// + [Test] + [NonParallelizable] + public void LoadMatFromCsv_カンマ小数ロケール_ピリオド小数を正しく解釈する() { + // Arrange + File.WriteAllText(_csvPath, "96.32301186,112.325025,186.5234201\r\n"); + var original = System.Threading.Thread.CurrentThread.CurrentCulture; + try { + System.Threading.Thread.CurrentThread.CurrentCulture = + new System.Globalization.CultureInfo("de-DE"); + + // Act + using (var loaded = IoUtil.LoadMatFromCsv(_csvPath)) { + // Assert + Assert.IsNotNull(loaded); + Assert.AreEqual(96.32301186, loaded.At(0, 0), 1e-6); + Assert.AreEqual(112.325025, loaded.At(0, 1), 1e-6); + Assert.AreEqual(186.5234201, loaded.At(0, 2), 1e-6); + } + } finally { + System.Threading.Thread.CurrentThread.CurrentCulture = original; + } + } + + /// + /// 再現テスト: 小数点にカンマを使うロケール(de-DE)でもピリオド小数で CSV を出力する + /// (カルチャ依存だと小数点がカンマになり,列区切りのカンマと衝突して CSV が壊れる) + /// + [Test] + [NonParallelizable] + public void SaveMatToCsv_カンマ小数ロケール_ピリオド小数で出力する() { + // Arrange + var original = System.Threading.Thread.CurrentThread.CurrentCulture; + try { + System.Threading.Thread.CurrentThread.CurrentCulture = + new System.Globalization.CultureInfo("de-DE"); + using (var src = new Mat(1, 2, MatType.CV_64FC1)) { + src.At(0, 0) = 1.2345678; + src.At(0, 1) = -2.5; + + // Act + IoUtil.SaveMatToCsv(_csvPath, src); + + // Assert + var line = File.ReadAllLines(_csvPath)[0]; + Assert.AreEqual("1.2345678,-2.5000000", line, + "ロケールによらずピリオド小数・カンマ列区切りで出力すること"); + } + } finally { + System.Threading.Thread.CurrentThread.CurrentCulture = original; + } + } + + /// + /// 特性化テスト: ドット小数ロケール(ja-JP)での出力バイト列を固定する + /// (InvariantCulture 固定化の修正で従来ロケールの出力が 1 バイトも変わらない保護) + /// + [Test] + [NonParallelizable] + public void SaveMatToCsv_ドット小数ロケール_出力が従来と同一() { + // Arrange + var original = System.Threading.Thread.CurrentThread.CurrentCulture; + try { + System.Threading.Thread.CurrentThread.CurrentCulture = + new System.Globalization.CultureInfo("ja-JP"); + using (var src = CreateMat2x3()) { + // Act + IoUtil.SaveMatToCsv(_csvPath, src); + + // Assert + var lines = File.ReadAllLines(_csvPath); + Assert.AreEqual("1.2345670,-2.3456780,0.0000010", lines[0]); + Assert.AreEqual("100.0000000,-0.5000000,999.9999990", lines[1]); + } + } finally { + System.Threading.Thread.CurrentThread.CurrentCulture = original; + } + } + + /// + /// 特性化テスト: リポジトリ同梱の tcc_srgb.csv / tcc_xyz.csv が従来どおり読めること + /// (CameraBase の TCC 参照値読み込み経路の保護.先頭要素の値と行列サイズを固定する) + /// + [Test] + public void LoadMatFromCsv_同梱tccCsv_従来どおり読める() { + var repoDir = Path.GetFullPath(Path.Combine( + TestContext.CurrentContext.TestDirectory, + "..", "..", "..", "..", "TIASshot")); + var srgbPath = Path.Combine(repoDir, "tcc_srgb.csv"); + var xyzPath = Path.Combine(repoDir, "tcc_xyz.csv"); + Assert.IsTrue(File.Exists(srgbPath), $"同梱 tcc_srgb.csv が見つからない: {srgbPath}"); + Assert.IsTrue(File.Exists(xyzPath), $"同梱 tcc_xyz.csv が見つからない: {xyzPath}"); + + using (var srgb = IoUtil.LoadMatFromCsv(srgbPath)) + using (var xyz = IoUtil.LoadMatFromCsv(xyzPath)) { + Assert.IsNotNull(srgb, "tcc_srgb.csv が読めること"); + Assert.IsNotNull(xyz, "tcc_xyz.csv が読めること"); + Assert.AreEqual(3, srgb.Cols, "tcc_srgb.csv は 3 列であること"); + Assert.AreEqual(3, xyz.Cols, "tcc_xyz.csv は 3 列であること"); + Assert.AreEqual(96.32301186, srgb.At(0, 0), 1e-6, + "tcc_srgb.csv の先頭要素が従来値で読めること"); + } + } + + /// /// 存在しないファイルをロードしたとき null を返す /// [Test]