Newer
Older
TIASShot / TIASshot.Tests / IoUtilCombineSaveFilePathTests.cs
using System;
using System.IO;
using NUnit.Framework;

namespace TIASshot.Tests {
    /// <summary>
    /// IoUtil.CombineSaveFilePath のテスト.
    /// 設定ファイル(config.xml の File/*)由来のファイル名が保存フォルダと結合されるとき,
    /// 絶対パス指定・..\ トラバーサルでも保存先が保存フォルダの外へ出ないこと,および
    /// 同梱 config.xml の既存値では結合結果が従来の Path.Combine と完全一致すること
    /// (挙動不変の特性化)を検証する.
    /// </summary>
    [TestFixture]
    public class IoUtilCombineSaveFilePathTests {

        private readonly string _saveFolder = @"C:\TIAS_Data\2026-08-03\12_00_00-subject";

        /// <summary>
        /// 結合結果が保存フォルダ配下かどうかを判定する(.. は正規化して評価)
        /// </summary>
        private bool IsUnderSaveFolder(string path) {
            var full = Path.GetFullPath(path);
            var baseFull = Path.GetFullPath(_saveFolder)
                .TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
            return full.StartsWith(baseFull, StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>
        /// ファイル名に絶対パスを指定しても保存先が保存フォルダの外に出ない
        /// (Path.Combine は第2引数が絶対パスだと第1引数を破棄する)
        /// </summary>
        [Test]
        public void CombineSaveFilePath_絶対パス指定_保存フォルダ配下に留まる() {
            // Arrange: スタートアップ等の任意の場所への書き込みを試みる設定値
            var filename = @"C:\Users\victim\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\run.csv";

            // Act
            var result = IoUtil.CombineSaveFilePath(_saveFolder, filename);

            // Assert
            Assert.IsTrue(IsUnderSaveFolder(result),
                $"保存先が保存フォルダの外を指している: {Path.GetFullPath(result)}");
        }

        /// <summary>
        /// ファイル名に ..\ トラバーサルを含めても保存先が保存フォルダの外に出ない
        /// </summary>
        [Test]
        public void CombineSaveFilePath_トラバーサル指定_保存フォルダ配下に留まる() {
            // Arrange
            var filename = @"..\..\..\..\x.png";

            // Act
            var result = IoUtil.CombineSaveFilePath(_saveFolder, filename);

            // Assert
            Assert.IsTrue(IsUnderSaveFolder(result),
                $"保存先が保存フォルダの外を指している: {Path.GetFullPath(result)}");
        }

        /// <summary>
        /// 同梱 config.xml の File/* 既存値({CN}/{NO} 置換後の実値)では
        /// 結合結果が従来の Path.Combine と完全一致する(挙動不変の特性化)
        /// </summary>
        [TestCase("Shot0001.png")]          // File/RgbImage({NO} 置換後)
        [TestCase("sRGB17_0001.jpg")]       // File/SrgbImage({CN}/{NO} 置換後)
        [TestCase("Info.csv")]              // File/Info
        [TestCase("TCC.png")]               // File/TccImage
        [TestCase("TCC_ROIs.jpg")]          // File/TccRoisImage
        [TestCase("TCC_RGB.csv")]           // File/TccRgbValues
        [TestCase("TCC_sRGB17.csv")]        // File/TccSrgbValues({CN} 置換後)
        [TestCase("Conv_RGB-sRGB17.csv")]   // File/ConvRgb2Srgb({CN} 置換後)
        [TestCase("Conv_RGB-XYZ17.csv")]    // File/ConvRgb2Xyz({CN} 置換後)
        [TestCase("Conv_sRGB-XYZ17.csv")]   // File/ConvSrgb2Xyz({CN} 置換後)
        [TestCase("Conv_RGB-XYZold.csv")]   // File/ConvRgb2XyzOld
        public void CombineSaveFilePath_同梱configの既存値_従来のPathCombineと一致する(string filename) {
            // Act
            var result = IoUtil.CombineSaveFilePath(_saveFolder, filename);

            // Assert
            Assert.AreEqual(Path.Combine(_saveFolder, filename), result,
                "同梱 config.xml の既存値では結合結果が従来と完全一致すること");
        }
    }
}