using SheetMe.Core.Catalog; using SheetMe.Core.Serialization; namespace SheetMe.Core.Tests; /// WinForms MaskedTextBox 마스크 → 표시 문자열 변환 테스트. [TestClass] public sealed class LegacyMaskTests { /// 운영 실사용 마스크 상위 항목 — 2026-08-11 활성 디자인 전수 census 근거 [TestMethod] [DataRow("0000년 90월 90일", "____년 __월 __일")] // 198건 — 최다 [DataRow("0000-00-00", "____-__-__")] // 51건 [DataRow("90:90", "__:__")] // 36건 [DataRow("90시90분", "__시__분")] // 22건 [DataRow("0000.00.00.", "____.__.__.")] // 21건 [DataRow("90월 90일", "__월 __일")] // 15건 — 연속 공백 보존 [DataRow("000-9000-0000", "___-____-____")] // 6건 [DataRow("( 90 : 00 )", "( __ : __ )")] [DataRow("####/##/## ##:##", "____/__/__ __:__")] // # 지시자 [DataRow("0000년 90월 90일 90시 90분", "____년 __월 __일 __시 __분")] public void ToPromptDisplay_ProductionMasks(string mask, string expected) { Assert.AreEqual(expected, LegacyMask.ToPromptDisplay(mask)); } /// 이스케이프 — 백슬래시 다음 문자는 리터럴(운영에 '2\000년' 형태가 존재) [TestMethod] [DataRow(@"2\000년", "20__년")] [DataRow(@"2\000년 90월 90일", "20__년 __월 __일")] [DataRow(@"\9\0", "90")] public void ToPromptDisplay_EscapedLiterals(string mask, string expected) { Assert.AreEqual(expected, LegacyMask.ToPromptDisplay(mask)); } /// 문자·영숫자 지시자도 한 자리를 소비한다(운영 미사용이나 규약상 지원) [TestMethod] [DataRow("LLL", "___")] [DataRow("???", "___")] [DataRow("AAA", "___")] [DataRow("aaa", "___")] [DataRow("&&&", "___")] [DataRow("CCC", "___")] public void ToPromptDisplay_CharacterDirectives(string mask, string expected) { Assert.AreEqual(expected, LegacyMask.ToPromptDisplay(mask)); } /// 대소문자 변환자는 자리를 차지하지 않고 표시에도 나오지 않는다 [TestMethod] [DataRow(">LLL", "___")] [DataRow("LL|LL", "____")] public void ToPromptDisplay_CaseModifiersAreInvisible(string mask, string expected) { Assert.AreEqual(expected, LegacyMask.ToPromptDisplay(mask)); } /// PromptChar 지정 — 운영엔 0건이지만 속성이 있으면 반영해야 한다 [TestMethod] public void ToPromptDisplay_HonorsPromptChar() { Assert.AreEqual("0000년 00월 00일", LegacyMask.ToPromptDisplay("0000년 90월 90일", '0')); Assert.AreEqual("####-##-##", LegacyMask.ToPromptDisplay("0000-00-00", '#')); Assert.AreEqual(" - - ", LegacyMask.ToPromptDisplay("0000-00-00", ' ')); } /// 빈 마스크·null 안전 [TestMethod] public void ToPromptDisplay_EmptyInputs() { Assert.AreEqual(string.Empty, LegacyMask.ToPromptDisplay(null)); Assert.AreEqual(string.Empty, LegacyMask.ToPromptDisplay(string.Empty)); } /// 끝에 홀로 남은 백슬래시는 리터럴로 — 인덱스 초과로 죽지 않아야 한다 [TestMethod] public void ToPromptDisplay_TrailingBackslashIsLiteral() { Assert.AreEqual(@"__\", LegacyMask.ToPromptDisplay(@"00\")); } /// /// 표시 길이는 마스크의 시각 길이와 같아야 한다(변환자·이스케이프 제외). /// 길이가 달라지면 캔버스에서 필드 폭이 어긋나 보인다. /// [TestMethod] [DataRow("0000년 90월 90일")] [DataRow("0000.00.00.")] [DataRow("( 90 : 00 )")] public void ToPromptDisplay_PreservesVisibleLength(string mask) { Assert.AreEqual(mask.Length, LegacyMask.ToPromptDisplay(mask).Length); } // ── 시험 입력 미리보기(Apply) ── /// 입력이 마스크 자리를 순서대로 채운다 [TestMethod] [DataRow("0000년 90월 90일", "20260812", "2026년 08월 12일")] [DataRow("0000년 90월 90일", "2026", "2026년 __월 __일")] // 부분 입력 — 나머지는 프롬프트 [DataRow("0000-00-00", "20260812", "2026-08-12")] [DataRow("90:90", "0930", "09:30")] [DataRow("000-9000-0000", "01012345678", "010-1234-5678")] public void Apply_FillsPositionsInOrder(string mask, string input, string expected) { Assert.AreEqual(expected, LegacyMask.Apply(mask, input)); } /// 값만 치든 구분자를 같이 치든 결과가 같아야 한다 [TestMethod] [DataRow("0000-00-00", "2026-08-12", "2026-08-12")] [DataRow("0000-00-00", "20260812", "2026-08-12")] [DataRow("0000년 90월 90일", "2026년 08월 12일", "2026년 08월 12일")] [DataRow("0000년 90월 90일", "20260812", "2026년 08월 12일")] [DataRow("000-9000-0000", "010-1234-5678", "010-1234-5678")] [DataRow("000-9000-0000", "01012345678", "010-1234-5678")] [DataRow("90시90분", "0930", "09시30분")] public void Apply_SeparatorsInInputAreIgnored(string mask, string input, string expected) { Assert.AreEqual(expected, LegacyMask.Apply(mask, input)); } /// 지시자에 맞지 않는 문자는 건너뛴다(WinForms 가 거부하는 것과 같은 결과) [TestMethod] public void Apply_RejectsInvalidCharacters() { Assert.AreEqual("12__", LegacyMask.Apply("0000", "ab12")); // 숫자 자리에 영문 Assert.AreEqual("AB__", LegacyMask.Apply("LLLL", "12AB")); // 영문 자리에 숫자 } /// 대소문자 변환자는 입력에 적용된다 [TestMethod] public void Apply_AppliesCaseModifiers() { Assert.AreEqual("ABC", LegacyMask.Apply(">LLL", "abc")); Assert.AreEqual("abc", LegacyMask.Apply(" /// 이스케이프 리터럴은 입력을 소비하지 않는다. /// 마스크 2\000년 은 리터럴 '2','0' 뒤에 입력 자리 2칸이라 표시가 20__년 이고, /// 편집 가능한 자리(2칸)만 입력하면 된다. /// [TestMethod] public void Apply_EscapedLiteralConsumesNoInput() { Assert.AreEqual("20__년", LegacyMask.ToPromptDisplay(@"2\000년")); Assert.AreEqual("2026년", LegacyMask.Apply(@"2\000년", "26")); Assert.AreEqual("202_년", LegacyMask.Apply(@"2\000년", "2")); } /// /// 입력의 공백은 구분자로 보고 건너뛴다 — '9' 가 공백을 허용해서 생기는 밀림 방지. /// 빈 자리는 프롬프트 문자가 표현하므로 공백을 데이터로 받을 이유가 없다. /// [TestMethod] public void Apply_TreatsInputWhitespaceAsSeparator() { Assert.AreEqual("2026년 08월 12일", LegacyMask.Apply("0000년 90월 90일", "2026 08 12")); Assert.AreEqual("09:30", LegacyMask.Apply("90:90", "09 30")); } /// 입력이 없으면 빈 값 표시와 같아야 한다 [TestMethod] [DataRow("0000년 90월 90일")] [DataRow("000-9000-0000")] public void Apply_WithoutInputEqualsPromptDisplay(string mask) { Assert.AreEqual(LegacyMask.ToPromptDisplay(mask), LegacyMask.Apply(mask, null)); Assert.AreEqual(LegacyMask.ToPromptDisplay(mask), LegacyMask.Apply(mask, string.Empty)); } /// 빈 마스크는 빈 결과 — 자유 입력 필드 [TestMethod] public void Apply_EmptyMask() { Assert.AreEqual(string.Empty, LegacyMask.Apply(null, "abc")); Assert.AreEqual(string.Empty, LegacyMask.Apply(string.Empty, "abc")); } // ── 프리셋 카탈로그 ── /// 프리셋 마스크는 전부 유효해야 한다 — 표시 길이가 보존되고 지시자가 하나 이상 있어야 한다 [TestMethod] public void Presets_AreWellFormed() { foreach (var preset in LegacyMaskCatalog.All) { var display = LegacyMask.ToPromptDisplay(preset.Mask); Assert.AreNotEqual(preset.Mask, display, $"[{preset.Mask}] 에 입력 지시자가 없습니다 — 마스크로 쓸 수 없습니다."); Assert.IsFalse(string.IsNullOrWhiteSpace(preset.Description), $"[{preset.Mask}] 설명 누락"); Assert.IsFalse(string.IsNullOrWhiteSpace(preset.Group), $"[{preset.Mask}] 그룹 누락"); } } /// 프리셋 마스크에 중복이 없어야 한다 [TestMethod] public void Presets_HaveNoDuplicates() { var seen = new HashSet(StringComparer.Ordinal); foreach (var preset in LegacyMaskCatalog.All) { Assert.IsTrue(seen.Add(preset.Mask), $"중복 프리셋: {preset.Mask}"); } } /// 운영 최다 사용 마스크가 프리셋에 있어야 한다(census 근거) [TestMethod] [DataRow("0000년 90월 90일")] // 198건 [DataRow("0000-00-00")] // 51건 [DataRow("90:90")] // 36건 [DataRow("90시90분")] // 22건 [DataRow("0000.00.00.")] // 21건 public void Presets_CoverMostUsedProductionMasks(string mask) { Assert.IsTrue(LegacyMaskCatalog.All.Any(p => p.Mask == mask), $"운영 다빈도 마스크 누락: {mask}"); } }