using AcDream.App.UI.Layout; namespace AcDream.App.Tests.UI.Layout; /// /// Conformance for — the exact port of /// retail's string-table escape codec /// (StringTableMetaLanguage::UnescapeString @ 0x0067BDC0 / /// EscapeString @ 0x0067BBC0, character tables /// GetUnEscapedChar @ 0x0067B750 / GetEscapedChar @ 0x0067B6C0). /// The metalanguage character set is byte-verified against the PDB-paired /// 2013 binary (file offset 0x3FE178: []!{}#\|^$). /// public sealed class RetailStringEscapesTests { [Theory] [InlineData("line one\\nline two", "line one\nline two")] [InlineData("a\\tb", "a\tb")] [InlineData("a\\rb", "a\rb")] [InlineData("say \\qhi\\q", "say \"hi\"")] public void Unescape_DecodesTheFourCharacterEscapes( string raw, string expected) => Assert.Equal(expected, RetailStringEscapes.Unescape(raw)); [Theory] [InlineData("\\[", "[")] [InlineData("\\]", "]")] [InlineData("\\!", "!")] [InlineData("\\{", "{")] [InlineData("\\}", "}")] [InlineData("\\#", "#")] [InlineData("\\\\", "\\")] [InlineData("\\|", "|")] [InlineData("\\^", "^")] [InlineData("\\$", "$")] public void Unescape_DecodesEveryMetalanguageSelfEscape( string raw, string expected) => Assert.Equal(expected, RetailStringEscapes.Unescape(raw)); /// /// GetUnEscapedChar returns 0 for anything else — retail keeps the /// backslash verbatim (UnescapeString's else-branch), including a /// trailing backslash whose "next" character is the terminator. /// [Theory] [InlineData("\\z", "\\z")] [InlineData("C:\\path\\dir", "C:\\path\\dir")] [InlineData("ends with \\", "ends with \\")] [InlineData("\\N upper is not an escape", "\\N upper is not an escape")] public void Unescape_KeepsUnrecognizedPairsVerbatim( string raw, string expected) => Assert.Equal(expected, RetailStringEscapes.Unescape(raw)); /// /// The double-decode hazard the 2026-08-17 systemic round exists to /// close: an authored escaped backslash before an 'n' decodes ONCE to /// the literal two characters backslash+n — a second decode pass (the /// retired per-consumer copies) would corrupt it into a line break. /// [Fact] public void Unescape_EscapedBackslashBeforeN_YieldsLiteralPair() => Assert.Equal("\\n", RetailStringEscapes.Unescape("\\\\n")); [Fact] public void Unescape_EmptyString_IsEmpty() => Assert.Equal(string.Empty, RetailStringEscapes.Unescape(string.Empty)); /// No backslash → no allocation: the same instance returns. [Fact] public void Unescape_NoEscapes_ReturnsTheSameInstance() { const string plain = "Please Wait"; Assert.Same(plain, RetailStringEscapes.Unescape(plain)); } [Theory] [InlineData("line one\nline two", "line one\\nline two")] [InlineData("a\tb", "a\\tb")] [InlineData("a\rb", "a\\rb")] [InlineData("say \"hi\"", "say \\qhi\\q")] [InlineData("[x]", "\\[x\\]")] [InlineData("back\\slash", "back\\\\slash")] [InlineData("plain", "plain")] public void Escape_IsTheStorageInverse(string plain, string expected) => Assert.Equal(expected, RetailStringEscapes.Escape(plain)); /// /// Retail's template-variable round trip /// (AddVariable_String @ 0x0042E6C0 escapes on insert; /// InqString @ 0x0042E490 unescapes the composed whole): variable /// content must come out verbatim. /// [Theory] [InlineData("plain name")] [InlineData("Odd\\Name")] [InlineData("multi\nline")] [InlineData("tabs\tand \"quotes\"")] [InlineData("[]!{}#\\|^$")] [InlineData("")] public void UnescapeOfEscape_RoundTripsVerbatim(string value) => Assert.Equal(value, RetailStringEscapes.Unescape( RetailStringEscapes.Escape(value))); }