Release v0.6.26: OpenBao settings synchronization with per-setting conflicts
This commit is contained in:
@@ -15,6 +15,37 @@ struct InputRegressionAccess {
|
||||
static inline bool delivery = true;
|
||||
static inline int notifications = 0;
|
||||
static bool notify(const std::wstring &) { ++notifications; return delivery; }
|
||||
static void sampleConflicts(App &a) {
|
||||
a.conflictConnection=loadSyncConfig(a.data);
|
||||
a.pendingSyncConflicts={{"/autoCorrectMinLength",L"Minimum word length",{'4'},{'5'}}};
|
||||
}
|
||||
static bool staleSync(App &a, bool connectionChanged) {
|
||||
auto before=encodeSettings(a.settings);
|
||||
a.syncConnection=loadSyncConfig(a.data);
|
||||
a.syncSnapshot=before;
|
||||
if(connectionChanged) a.syncConnection.enabled=!a.syncConnection.enabled;
|
||||
else a.syncSnapshot.clear();
|
||||
auto remote=a.settings; remote.recent=!remote.recent;
|
||||
a.syncResult={true,sharedSyncSettings(remote),2,L"test"};
|
||||
a.syncWorker=std::thread([]{});
|
||||
a.syncRunning=true; a.syncDone=true;
|
||||
a.pollSync();
|
||||
return encodeSettings(a.settings)==before && !a.syncRunning &&
|
||||
!std::filesystem::exists(a.data/L"sync-base.bin");
|
||||
}
|
||||
static bool syncBackoff(App &a) {
|
||||
a.syncRetryAfter=GetTickCount64()+15*60000;
|
||||
auto previous=a.settings;
|
||||
auto next=previous;
|
||||
next.toggle=next.casing=next.translit=next.undo=next.clipboard=next.screenshot=L"";
|
||||
next.direct.clear(); next.directIds.clear();
|
||||
// This harness deliberately has no global keyboard hook.
|
||||
a.applySettings(next);
|
||||
bool preserved=a.nextSync==a.syncRetryAfter;
|
||||
a.settings=previous;
|
||||
a.syncRetryAfter=0;
|
||||
return preserved;
|
||||
}
|
||||
static void notificationSetup(App &a) { a.updateNotifier = notify; }
|
||||
static void setup(App &a) { a.hookBindings = {{Toggle,{MOD_CONTROL,VK_F12}}, {Case,{MOD_CONTROL|MOD_SHIFT,'U'}}}; }
|
||||
static bool updateTimer(App &a) {
|
||||
@@ -595,6 +626,55 @@ int main(int argc, char **argv) {
|
||||
fitted=ks::fitWindowRect({2000,2000,2600,2400},{-1920,0,0,1080});
|
||||
require(fitted.left==-600&&fitted.top==680&&fitted.right==0,"negative-coordinate monitor supported");
|
||||
ks::showSettings(app,false);
|
||||
require(GetDlgItem(app.settingsWindow,258)!=nullptr,"sync available without local opt-in");
|
||||
DestroyWindow(app.settingsWindow);
|
||||
ks::saveSyncConfig(app.data,ks::SyncConfig{});
|
||||
ks::showSettings(app,false);
|
||||
require(GetDlgItem(app.settingsWindow,258)!=nullptr,"sync button available after local opt-in");
|
||||
SendMessageW(app.settingsWindow,WM_COMMAND,258,0);
|
||||
auto syncWindow=FindWindowW(L"KSwitcher.Sync",nullptr);
|
||||
require(syncWindow!=nullptr,"private sync dialog opens");
|
||||
ShowWindow(syncWindow,SW_HIDE);
|
||||
require((GetWindowLongPtrW(GetDlgItem(syncWindow,604),GWL_STYLE)&ES_PASSWORD)!=0,
|
||||
"sync password is masked");
|
||||
require(!IsWindowEnabled(app.settingsWindow),"sync dialog owns settings interaction");
|
||||
ks::InputRegressionAccess::sampleConflicts(app);
|
||||
SendMessageW(syncWindow,WM_COMMAND,612,0);
|
||||
auto conflictsWindow=FindWindowW(L"KSwitcher.SyncConflicts",nullptr);
|
||||
require(conflictsWindow!=nullptr,"per-setting conflict dialog opens");
|
||||
ShowWindow(conflictsWindow,SW_HIDE);
|
||||
require(SendDlgItemMessageW(conflictsWindow,620,LB_GETCOUNT,0,0)==1,"conflict shown as separate setting");
|
||||
wchar_t conflictValue[20]{}; GetDlgItemTextW(conflictsWindow,621,conflictValue,20);
|
||||
require(std::wstring(conflictValue)==L"4","local conflict value shown");
|
||||
GetDlgItemTextW(conflictsWindow,622,conflictValue,20);
|
||||
require(std::wstring(conflictValue)==L"5","remote conflict value shown");
|
||||
for(int id:{620,621,622,623,624,IDOK,IDCANCEL}) {
|
||||
RECT control{},client{};
|
||||
GetWindowRect(GetDlgItem(conflictsWindow,id),&control);
|
||||
MapWindowPoints(nullptr,conflictsWindow,reinterpret_cast<POINT*>(&control),2);
|
||||
GetClientRect(conflictsWindow,&client);
|
||||
require(control.left>=0&&control.top>=0&&control.right<=client.right&&control.bottom<=client.bottom,
|
||||
"conflict controls fit dialog");
|
||||
}
|
||||
SendMessageW(conflictsWindow,WM_COMMAND,623,0);
|
||||
require(!IsWindowEnabled(syncWindow),"conflict dialog owns sync interaction");
|
||||
SendMessageW(conflictsWindow,WM_COMMAND,IDCANCEL,0);
|
||||
require(IsWindowEnabled(syncWindow),"closing conflict dialog restores sync window");
|
||||
for(int id:{601,602,603,604,605,606,607,608,609,610,611,612,IDOK,IDCANCEL}) {
|
||||
RECT control{},client{};
|
||||
GetWindowRect(GetDlgItem(syncWindow,id),&control);
|
||||
MapWindowPoints(nullptr,syncWindow,reinterpret_cast<POINT*>(&control),2);
|
||||
GetClientRect(syncWindow,&client);
|
||||
require(control.left>=0&&control.top>=0&&control.right<=client.right&&control.bottom<=client.bottom,
|
||||
"sync controls fit dialog");
|
||||
}
|
||||
SendMessageW(syncWindow,WM_COMMAND,IDOK,0);
|
||||
require(!ks::loadSyncConfig(app.data).enabled,"saving disabled sync does not enable network access");
|
||||
SendMessageW(syncWindow,WM_COMMAND,IDCANCEL,0);
|
||||
require(IsWindowEnabled(app.settingsWindow),"closing sync restores parent");
|
||||
require(ks::InputRegressionAccess::staleSync(app,false),"in-flight sync cannot overwrite newer local edits");
|
||||
require(ks::InputRegressionAccess::staleSync(app,true),"disconnect discards in-flight downloaded profile");
|
||||
require(ks::InputRegressionAccess::syncBackoff(app),"saving settings preserves authentication error backoff");
|
||||
SendMessageW(app.settingsWindow,WM_COMMAND,255,0);
|
||||
auto autoOptions=FindWindowW(L"KSwitcher.AutoOptions",nullptr);
|
||||
require(autoOptions!=nullptr,"auto-correction options open");
|
||||
|
||||
Reference in New Issue
Block a user