Release v0.6.19: retry dropped copies and stabilize snippet integration
This commit is contained in:
+83
-1
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user