using Dalamud.Bindings.ImGui; using Dalamud.Game.Text; using Dalamud.Plugin.SelfTest; using HellionChat._Helpers; namespace HellionChat.SelfTests; // B2-3: proves the plugin-disclosure arm-and-hold wires the (otherwise verwaist) // scanner into the REAL send entry InputBar.TrySend. Drives TrySend via the // arm-test-hook with a PUA glyph in the buffer and NotifyPluginDisclosure on: // the first send must ARM and HOLD (no send), so PendingMessage stays the probe // string and the armed flag is set. Arm-case ONLY (seiteneffektfrei): a real // send fires ChatBox.SendMessageUnsafe (a real in-game chat line), so the // second-Enter-sends + ASCII-passthrough legs are in-game smoke only, never // headless. Does NOT call PluginDisclosureScanner.ContainsPrivateUseGlyph in // isolation (the false-green trap — it has no other production caller). internal sealed class DisclosureArmStep : ISelfTestStep { private readonly Plugin plugin; public DisclosureArmStep(Plugin plugin) { this.plugin = plugin; } public string Name => "Hellion Chat - plugin disclosure arm"; public SelfTestStepResult RunStep() { var input = this.plugin.InputBar; if (input is null) { ImGui.Text("Plugin.InputBar is null"); return SelfTestStepResult.Fail; } // The SymbolPicker inserts exactly these FFXIV Private-Use-Area glyphs; // HighQuality is inside PluginDisclosureScanner's PUA range by // construction (the scanner range IS the SeIconChar range). var probe = $"test {SeIconChar.HighQuality.ToIconString()} msg"; var savedPending = input.PendingMessage; var savedNotify = Plugin.Config.NotifyPluginDisclosure; try { Plugin.Config.NotifyPluginDisclosure = true; input.TestResetDisclosureForSelfTest(); input.TestSetPendingMessageForSelfTest(probe); // Precondition guard: refuse to drive the real TrySend unless the // toggle is on AND the scanner sees the probe glyph. If the scanner // regressed, this bails with Fail WITHOUT ever calling TrySend, so a // broken scanner can never leak a real chat line. (The remaining // risk — TrySend not calling the scanner at all — is the wiring this // step exists to catch and is covered by the documented residual-leak // note + the mandatory mid-cycle smoke; see the Step 4.8 warning box.) if ( !Plugin.Config.NotifyPluginDisclosure || !PluginDisclosureScanner.ContainsPrivateUseGlyph(input.PendingMessage) ) { ImGui.Text( "Disclosure precondition not met (toggle off or probe glyph not in the scanner's PUA range) — refusing to drive TrySend to avoid an unintended real send" ); return SelfTestStepResult.Fail; } // First send with a PUA glyph + toggle on must ARM, not send. Pass a // null Tab — the arm branch returns before any channel/send use. var armed = input.TestTryArmDisclosureForSelfTest(null); if (!armed) { ImGui.Text( "First send did not arm disclosure for a PUA-glyph buffer (scanner not wired into TrySend?)" ); return SelfTestStepResult.Fail; } // Buffer must be HELD: TrySend clears _pendingMessage to empty only on // a real send, so an unchanged probe proves nothing was transmitted. if (input.PendingMessage != probe) { ImGui.Text( $"Buffer not held on arm: PendingMessage = '{input.PendingMessage}', expected the unchanged probe (a cleared buffer means it actually sent)" ); return SelfTestStepResult.Fail; } return SelfTestStepResult.Pass; } finally { input.TestResetDisclosureForSelfTest(); input.TestSetPendingMessageForSelfTest(savedPending); Plugin.Config.NotifyPluginDisclosure = savedNotify; } } public void CleanUp() { } }