From fec33896f62ca4e9056512a1cc18fc4c936c95e4 Mon Sep 17 00:00:00 2001 From: amrali-eg <32075105+amrali-eg@users.noreply.github.com> Date: Thu, 10 Sep 2026 02:25:04 +0300 Subject: [PATCH] Read the status before pressing again, and report what EC actually said Three findings against the cancellation state machine, in the order they matter. The status is now read before a press is attempted. A refusal throws out of the press, so attempting first meant the status was never examined on a refusing iteration: progress depended on the button disappearing rather than on the run reporting. With a button that stays visible and refuses, the previous order runs the full thirty seconds and then claims no Cancel button appeared - while the button was present throughout and the run had already reported. Reading first, the same conditions classify correctly. A conversion that failed is no longer called a cancellation problem. Three outcomes now get three answers: "Conversion stopped" passes, "Conversion complete" says the run finished before it could be stopped, and anything else - "Conversion failed." among them - says the run neither stopped nor completed and this phase proved nothing about cancellation. Reporting EC's own failure as an instrument timing problem would hide a real defect in the phase that exists to check EC's reporting. pressed becomes pressAttempted, and its timeout says "A Cancel press was attempted", because it is set for a press whose delivery is unknown as well as one that succeeded. The old wording asserted a delivery the next clause retracted. For the same reason the other branch of that timeout now says the driver could not find a Cancel button, rather than that none appeared: automation failing to see a control is not evidence about the control. Controls, each against the previous shape for comparison: a button that always refuses now classifies from the status instead of timing out; a reported "Conversion failed." produces the neither-stopped-nor-completed message instead of "Cancellation was not exercised"; and a landed press with no final status reports an attempt. EC is unchanged. Co-Authored-By: Claude Opus 5 --- .../EncodingChecker.GuiSmoke/EcGuiDriver.cs | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/sources/EncodingChecker.GuiSmoke/EcGuiDriver.cs b/sources/EncodingChecker.GuiSmoke/EcGuiDriver.cs index 85a6b23..31c6acd 100644 --- a/sources/EncodingChecker.GuiSmoke/EcGuiDriver.cs +++ b/sources/EncodingChecker.GuiSmoke/EcGuiDriver.cs @@ -274,13 +274,21 @@ internal void ProceedThenCancel(AutomationElement review, Func writingHasB /// private void CancelAndConfirmStopped() { - bool pressed = false; + bool pressAttempted = false; Exception? uncertainPress = null; string? finalStatus = WaitFor( () => { - if (!pressed) + // The status is read first because a refusal throws out of the press + // below, which would end the attempt before the status was ever looked + // at. Progress would then depend on the button disappearing rather than + // on the run reporting, and a button that lingered after the run ended + // would keep being refused with the answer already on screen. + if (StatusLine() is string status && IsFinalConversionStatus(status)) + return status; + + if (!pressAttempted) { AutomationElement? cancel = FindById(MainWindow, "btnCancel"); @@ -289,7 +297,7 @@ private void CancelAndConfirmStopped() try { Invoke(cancel); - pressed = true; + pressAttempted = true; } catch (Exception ex) when ( ex is not ElementNotEnabledException && @@ -298,14 +306,12 @@ or COMException or InvalidOperationException) { uncertainPress = ex; - pressed = true; + pressAttempted = true; } } } - return StatusLine() is string status && IsFinalConversionStatus(status) - ? status - : null; + return null; }, Timeout, out Exception? lastError); @@ -315,19 +321,32 @@ or COMException // Only the uncertain press is added here: Expired already names whatever // the wait was still retrying, which is where a refusal shows up. throw Expired( - (pressed - ? "Cancel was pressed but the run never reported a final status." - : "No Cancel button appeared and the run never reported a final status.") + (pressAttempted + ? "A Cancel press was attempted but the run never reported a final status." + : "The driver could not find a Cancel button and the run never " + + "reported a final status.") + Blame(uncertainPress, null), lastError); } - if (!finalStatus.Contains("Conversion stopped", StringComparison.Ordinal)) + // Three outcomes, and only one of them is about cancellation. Collapsing the + // rest into "cancellation was not exercised" would report a conversion that + // failed - which EC says outright - as a problem with this phase's timing. + if (finalStatus.Contains("Conversion stopped", StringComparison.Ordinal)) + return; + + if (finalStatus.Contains("Conversion complete", StringComparison.Ordinal)) { throw new GuiDriverException( - "Cancellation was not exercised. EC instead reported: " + finalStatus + "Cancellation was not exercised: the run finished before it could be " + + "stopped. EC reported: " + finalStatus + Blame(uncertainPress, lastError)); } + + throw new GuiDriverException( + "The conversion neither stopped nor completed, so this phase proved nothing " + + "about cancellation. EC reported: " + finalStatus + + Blame(uncertainPress, lastError)); } ///