Release v0.6.19: retry dropped copies and stabilize snippet integration

This commit is contained in:
2026-10-31 02:03:07 +02:00
parent 0d3b2acae9
commit 6603d28f68
9 changed files with 127 additions and 17 deletions
+83 -1
View File
@@ -25,8 +25,51 @@ bool captured = false, failed = false;
bool conversionOnly = false;
int pasteBehavior = 0;
int enterPresses = 0;
int copyFailuresRemaining = 0, copyRequests = 0;
bool temporaryClipboardExcluded = true;
bool clipboardAccessTimedOut = false;
LRESULT CALLBACK observeEnter(HWND w, UINT message, WPARAM wp, LPARAM lp, UINT_PTR, DWORD_PTR)
{
// This bare EDIT control is our test fixture, not a full editor with
// clipboard retries. Keep clipboard access on its owning UI thread while
// it handles Ctrl+C/Ctrl+V, so unrelated Windows clipboard listeners cannot
// make the fixture silently discard the operation under test.
struct ClipboardAccess
{
bool opened = false;
~ClipboardAccess() { if (opened) CloseClipboard(); }
} access;
if (message == WM_CHAR && (wp == 3 || wp == 22))
{
auto deadline = GetTickCount64() + 1000;
do
{
if (OpenClipboard(w)) { access.opened = true; break; }
Sleep(10);
} while (GetTickCount64() < deadline);
if (!access.opened)
{
clipboardAccessTimedOut = true;
return 0;
}
}
if ((message == WM_CHAR && wp == 3) || message == WM_PASTE)
{
temporaryClipboardExcluded &= IsClipboardFormatAvailable(
RegisterClipboardFormatW(L"ExcludeClipboardContentFromMonitorProcessing")) != FALSE;
if (message == WM_CHAR)
{
++copyRequests;
// Native EDIT silently ignores Ctrl+C when OpenClipboard fails.
// Reproduce that outcome deterministically, without depending on
// the timing of the Windows clipboard-history service.
if (copyFailuresRemaining > 0)
{
--copyFailuresRemaining;
return 0;
}
}
}
if (message == WM_PASTE && pasteBehavior)
{
if (pasteBehavior == 2) SendMessageW(w, EM_REPLACESEL, TRUE, (LPARAM)L"other!");
@@ -51,13 +94,15 @@ std::string diagnosticLog()
size_t diagnosticStart{};
void require(bool ok, const char *name)
{
if (!ok)
if (!ok || clipboardAccessTimedOut)
{
wchar_t observed[2048]{};
GetWindowTextW(field, observed, 2048);
std::cerr << "Editor at failure: " << ks::utf8(observed)
<< "; foregroundSame=" << (GetForegroundWindow() == host)
<< "; inputVersion=" << app->inputVersion.load() << '\n';
if (clipboardAccessTimedOut)
throw std::runtime_error("Test editor could not acquire clipboard within 1000 ms");
throw std::runtime_error(name);
}
std::cout << "PASS " << name << '\n';
@@ -109,6 +154,9 @@ LRESULT CALLBACK procedure(HWND w, UINT msg, WPARAM wp, LPARAM lp)
return 0;
if (msg == WM_TIMER)
{
// Input tests must not start a network update check. Its close veto can
// otherwise leave a completed test running after the editor is gone.
if (app->window) KillTimer(app->window, 5);
try
{
if (GetTickCount64() > deadline)
@@ -257,6 +305,7 @@ LRESULT CALLBACK procedure(HWND w, UINT msg, WPARAM wp, LPARAM lp)
DestroyWindow(field);
field = CreateWindowW(L"EDIT", L"", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_PASSWORD, 20, 20,
590, 30, host, nullptr, GetModuleHandleW(nullptr), nullptr);
SetWindowSubclass(field, observeEnter, 1, 0);
prepare(L"ghbdtn ", false);
record(L"ghbdtn ");
app->transform(ks::AutoCorrect);
@@ -268,6 +317,7 @@ LRESULT CALLBACK procedure(HWND w, UINT msg, WPARAM wp, LPARAM lp)
DestroyWindow(field);
field = CreateWindowW(L"EDIT", L"", WS_CHILD | WS_VISIBLE | WS_BORDER, 20, 20, 590, 30, host,
nullptr, GetModuleHandleW(nullptr), nullptr);
SetWindowSubclass(field, observeEnter, 1, 0);
prepare(L"(ljvf ", false);
record(L"(ljvf ");
app->transform(ks::AutoCorrect);
@@ -347,6 +397,18 @@ LRESULT CALLBACK procedure(HWND w, UINT msg, WPARAM wp, LPARAM lp)
case 30:
case 32:
case 34:
case 37:
// Earlier password/word tests replace the original multiline
// editor with a single-line EDIT. Restore the snippet fixture:
// a single-line control cannot insert the tested Enter action.
if (!(GetWindowLongPtrW(field, GWL_STYLE) & ES_MULTILINE))
{
DestroyWindow(field);
field = CreateWindowW(L"EDIT", L"", WS_CHILD | WS_VISIBLE | WS_BORDER |
ES_MULTILINE | ES_WANTRETURN, 20, 20, 590, 230, host, nullptr,
GetModuleHandleW(nullptr), nullptr);
SetWindowSubclass(field, observeEnter, 1, 0);
}
if (!captured)
{
saved = ks::readClipboard(app->window, 64 * 1024 * 1024);
@@ -355,9 +417,16 @@ LRESULT CALLBACK procedure(HWND w, UINT msg, WPARAM wp, LPARAM lp)
app->ownSequence = ks::writeClipboard(app->window, ks::textClip(sentinel));
app->settings.autoCorrect = stage == 35;
app->settings.autoLanguages = {L"en", L"ru"};
app->settings.cycle = {app->layouts[en].id, app->layouts[ru].id};
app->settings.switchLayout = false;
app->autoCorrector = ks::AutoCorrector{};
app->autoPredictor = ks::AutoCorrector{};
app->autoPredictor.configure(L"dictionaries", app->settings.autoLanguages);
app->settings.snippets = {{L";r", stage == 31 ? L"rider .{cursor}" : L"rider .{cursor}{enter}"}};
enterPresses = 0;
copyRequests = 0;
copyFailuresRemaining = stage == 38 ? 10 : stage == 33 ? 1 : 0;
temporaryClipboardExcluded = true;
ActivateKeyboardLayout(app->layouts[en].handle, 0);
prepare(L"prefix ", false);
{
@@ -384,6 +453,10 @@ LRESULT CALLBACK procedure(HWND w, UINT msg, WPARAM wp, LPARAM lp)
break;
case 33:
require(text() == L"prefix rider .\r\n x", "Enter action precedes buffered typing");
require(copyRequests == 2 && copyFailuresRemaining == 0,
"snippet retries a silently dropped copy without duplicating input");
require(temporaryClipboardExcluded,
"temporary snippet clipboard contents are excluded from Windows history");
require(enterPresses == 1, "Enter marker sends an actual Enter key press");
require(ks::readClipboard(app->window).text() == sentinel, "Enter snippet restores clipboard");
stage = 34;
@@ -407,6 +480,15 @@ LRESULT CALLBACK procedure(HWND w, UINT msg, WPARAM wp, LPARAM lp)
break;
case 36:
require(text() == L"prefix rider .\r\nghbdtn привет ", "automatic correction resumes for subsequent normal typing");
stage = 37;
break;
case 38:
require(copyRequests == 3, "copy retries are bounded to three attempts");
require(text() == L"prefix ;r x" && enterPresses == 0,
"exhausted copy retries preserve trigger and following input without Enter");
require(ks::readClipboard(app->window).text() == sentinel,
"exhausted copy retries restore the clipboard");
copyFailuresRemaining = 0;
finish();
break;
case 20: