Fix clipboard history lock recovery and merged selection
This commit is contained in:
1 parent
2c02a79655
commit
0b3ba7fbe7
2 files changed
+63
-2
No files matched your search
@@ -225,3 +225,57 @@ func TestClearRemovesTemporaryHistoryLeftByInterruptedWrite(t *testing.T) {
|
||||
t.Fatal("clear removed another history's file", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPersistenceRetriesAfterHistoryLockTimeout(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "private", "terminal.json")
|
||||
lock, err := acquire_history(path, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer lock.Close()
|
||||
if err := save_history(path, Snapshot{Version: 1, Entries: []string{"shared"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store := New_persistence(path, true)
|
||||
defer store.Close()
|
||||
if !errors.Is(store.LoadErr, syscall.EWOULDBLOCK) {
|
||||
t.Fatalf("expected startup lock contention, got %v", store.LoadErr)
|
||||
}
|
||||
select {
|
||||
case err := <-store.Errors():
|
||||
if !errors.Is(err, syscall.EWOULDBLOCK) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
case <-time.After(4 * time.Second):
|
||||
t.Fatal("refresh did not time out on held lock")
|
||||
}
|
||||
if err := lock.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
select {
|
||||
case update := <-store.Updates():
|
||||
if !reflect.DeepEqual(update.State.Entries, []string{"shared"}) {
|
||||
t.Fatal(update)
|
||||
}
|
||||
case <-time.After(3 * time.Second):
|
||||
t.Fatal("history did not recover after lock release")
|
||||
}
|
||||
if err := store.Close(); err != nil {
|
||||
t.Fatalf("recovered lock failure persisted at close: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableMergePreservesSavedSelection(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "private", "terminal.json")
|
||||
state := Snapshot{Version: 1, Entries: []string{"newer", "selected"}, Selected: 1}
|
||||
if err := save_history(path, state); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
update, err := apply_history(path, historyJob{action: "enable", state: Snapshot{Version: 1, Entries: []string{"retained"}}, limit: 10})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := update.State.Entries[update.State.Selected]; got != "selected" {
|
||||
t.Fatalf("saved selection moved to %q", got)
|
||||
}
|
||||
}
|
||||
@@ -134,6 +134,7 @@ func (p *Persistence) run(path string, watching bool) {
|
||||
defer tick.Stop()
|
||||
last := HistoryUpdate{State: p.Initial, Enabled: p.InitialEnabled}
|
||||
var signature string
|
||||
var retryLock bool
|
||||
var acknowledged uint64
|
||||
for {
|
||||
select {
|
||||
@@ -167,7 +168,7 @@ func (p *Persistence) run(path string, watching bool) {
|
||||
continue
|
||||
}
|
||||
nextSignature := history_signature(path)
|
||||
if nextSignature == signature {
|
||||
if nextSignature == signature && !retryLock {
|
||||
continue
|
||||
}
|
||||
file, err := acquire_history(path, false)
|
||||
@@ -182,16 +183,21 @@ func (p *Persistence) run(path string, watching bool) {
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
// Report a stable failure once, and retry after a file changes.
|
||||
// Report a stable failure once. Lock contention can clear without
|
||||
// changing either file, so it must also be retried on the next tick.
|
||||
if signature != nextSignature {
|
||||
p.report(err)
|
||||
}
|
||||
} else {
|
||||
if retryLock {
|
||||
p.report(nil)
|
||||
}
|
||||
if !reflect.DeepEqual(update, last) {
|
||||
p.publish(update)
|
||||
last = update
|
||||
}
|
||||
}
|
||||
retryLock = errors.Is(err, syscall.EWOULDBLOCK)
|
||||
signature = nextSignature
|
||||
}
|
||||
}
|
||||
@@ -294,6 +300,7 @@ func apply_history(path string, job historyJob) (HistoryUpdate, error) {
|
||||
}
|
||||
if !found {
|
||||
state.Entries = append([]string{text}, state.Entries...)
|
||||
state.Selected++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user