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 =
"" +
"" +
"" +
"";
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 =
"" +
"" +
"";
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;
}
}
}
}