using SheetMe.Core.Models; using SheetMe.Core.Serialization; namespace SheetMe.Core.Tests; /// /// 레거시 XML 왕복 무결성 테스트 — P0 게이트. /// 실서식 샘플(sheetdesign_sample.xml)을 Read→Write 했을 때 의미론 diff 가 0 이어야 한다. /// [TestClass] public sealed class LegacyXmlRoundTripTests { private static string SamplePath => Path.Combine(AppContext.BaseDirectory, "Assets", "sheetdesign_sample.xml"); /// 실서식 샘플 왕복 — 의미론 diff 0 [TestMethod] public void RoundTrip_RealSample_NoSemanticDiff() { var original = File.ReadAllText(SamplePath); var serializer = new LegacyXmlSerializer(); var document = serializer.Read(original); var rewritten = serializer.Write(document); var diffs = XmlSemanticDiff.Compare(original, rewritten); Assert.AreEqual(0, diffs.Count, "왕복 의미론 diff 발생:\n" + string.Join("\n", diffs)); } /// 실서식 샘플 이중 왕복 — Write 출력이 안정(고정점)이어야 한다 [TestMethod] public void RoundTrip_RealSample_SecondPassIsStable() { var original = File.ReadAllText(SamplePath); var serializer = new LegacyXmlSerializer(); var first = serializer.Write(serializer.Read(original)); var second = serializer.Write(serializer.Read(first)); Assert.AreEqual(first, second, "이중 왕복 결과가 문자열 수준에서 안정적이지 않습니다."); } /// 실서식 샘플 구조 확인 — 페이지/컨트롤 수·타입 매핑 [TestMethod] public void Read_RealSample_ParsesStructure() { var serializer = new LegacyXmlSerializer(); var document = serializer.Read(File.ReadAllText(SamplePath)); Assert.AreEqual(1, document.Pages.Count, "샘플은 단일 페이지여야 합니다."); var page = document.Pages[0]; Assert.AreEqual("MDesignerHost1", page.Root.Id); Assert.AreEqual(823, page.Width, "루트 Size 리프트(823×1250) 실패"); Assert.AreEqual(1250, page.Height); // 샘플 실측: MLine 34, TextBox 33, Label 27, RadioButton 15, Panel 6, MSequence 1, MPictureBox 1 var allControls = Flatten(page.Controls).ToList(); Assert.AreEqual(117, allControls.Count, "전체 컨트롤 수(루트 제외) 불일치"); Assert.AreEqual(34, allControls.Count(c => c.Type == "Line")); Assert.AreEqual(33, allControls.Count(c => c.Type == "TextBox")); Assert.AreEqual(28, allControls.Count(c => c.Type == "Label"), "Label 27 + MSequence 1 = 28"); Assert.AreEqual(15, allControls.Count(c => c.Type == "RadioButton")); Assert.AreEqual(6, allControls.Count(c => c.Type == "Panel")); Assert.AreEqual(1, allControls.Count(c => c.Type == "PictureBox")); Assert.AreEqual(0, allControls.Count(c => c.Type == LegacyTypeCatalog.PlaceholderType), "샘플에는 미지원 타입이 없어야 합니다."); } /// 다중 페이지 병합 규약 — Sheet 루트 1개에 MDesignerHost Object N개 [TestMethod] public void RoundTrip_MultiPage_PreservesPages() { const string xml = "" + "" + "720, 856" + "" + "" + "720, 856" + "" + ""; var serializer = new LegacyXmlSerializer(); var document = serializer.Read(xml); Assert.AreEqual(2, document.Pages.Count); var rewritten = serializer.Write(document); var diffs = XmlSemanticDiff.Compare(xml, rewritten); Assert.AreEqual(0, diffs.Count, string.Join("\n", diffs)); } /// 신규 컨트롤 추가 후 쓰기 — Location/Size/AQN 이 레거시 형식으로 생성 [TestMethod] public void Write_NewControl_EmitsLegacyFormat() { var serializer = new LegacyXmlSerializer(); var document = new FormDocument(); var page = LegacyXmlSerializer.CreateEmptyPage(1); document.Pages.Add(page); var textBox = new ControlElement { Type = "TextBox", Id = "TextBox1", Bounds = new LayoutRect { X = 120, Y = 88, W = 200, H = 24 }, }; textBox.Props.SetText("Text", "환자명"); page.Controls.Add(textBox); var xml = serializer.Write(document); StringAssert.Contains(xml, "M.EMR.UserControl.TextBox, M.EMR.UserControl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); StringAssert.Contains(xml, "120, 88"); StringAssert.Contains(xml, "200, 24"); StringAssert.Contains(xml, "환자명"); // 재읽기 시 동일 구조 var reread = serializer.Read(xml); Assert.AreEqual(1, reread.Pages[0].Controls.Count); Assert.AreEqual("TextBox", reread.Pages[0].Controls[0].Type); Assert.AreEqual(120, reread.Pages[0].Controls[0].Bounds.X); } /// 컨테이너 중첩 시 LocationOnBase 재계산 — 절대좌표(부모 누적) 규약 [TestMethod] public void Write_NestedControl_RecomputesLocationOnBase() { const string xml = "" + "" + "" + "" + "0, 0" + "10, 20" + "100, 22" + "" + "50, 60" + "200, 100" + "" + "720, 856" + "" + ""; var serializer = new LegacyXmlSerializer(); var document = serializer.Read(xml); var rewritten = serializer.Write(document); // Panel(50,60) 안의 TextBox(10,20) → LocationOnBase = 60, 80 (레거시 writer 재계산 규약) StringAssert.Contains(rewritten, "60, 80"); } private static IEnumerable Flatten(IEnumerable controls) { foreach (var control in controls) { yield return control; foreach (var child in Flatten(control.Children)) { yield return child; } } } }