After commit c018f5fd29 Fenrir is being switched over to go. If you do not want to be part of the testing process, please switch to master.

This commit is contained in:
Storm Dragon committed 2026-10-27 20:30:55 -04:00
1 parent c018f5fd29
commit 0e50de4c76
1047 files changed
+58364 -63747

No files matched your search

+227
View File
@@ -0,0 +1,227 @@
// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package clipboard
import (
"encoding/json"
"errors"
"os"
"path/filepath"
"reflect"
"strings"
"syscall"
"testing"
"time"
)
func TestPersistentHistoryRoundTripResizeAndPrivateModes(t *testing.T) {
path := filepath.Join(t.TempDir(), "private", "terminal.json")
history := New_history(3)
for _, text := range []string{"old", "é界\nsecond line", "🎃"} {
if err := history.Add(text); err != nil {
t.Fatal(err)
}
}
history.Move(1)
store := New_persistence(path, false)
store.Enable(history.Snapshot(), 3)
if err := store.Close(); err != nil {
t.Fatal(err)
}
for file, mode := range map[string]os.FileMode{path: 0600, filepath.Dir(path): 0700} {
info, err := os.Stat(file)
if err != nil || info.Mode().Perm() != mode {
t.Fatal(info, err)
}
}
store = New_persistence(path, true)
if store.LoadErr != nil {
t.Fatal(store.LoadErr)
}
restored := New_history(2)
if err := restored.Restore(store.Initial); err != nil {
t.Fatal(err)
}
if got, _ := restored.Current(); got != "é界\nsecond line" || !restored.At_last() {
t.Fatal(got)
}
if err := store.Close(); err != nil {
t.Fatal(err)
}
restored.Resize(1)
if got, _ := restored.Current(); got != "🎃" {
t.Fatal(got)
}
restored.Resize(4)
restored.Add("new")
if !reflect.DeepEqual(restored.Snapshot().Entries, []string{"new", "🎃"}) {
t.Fatal(restored.Snapshot())
}
}
func TestPersistenceDisabledDoesNotReadOrCreateAndModesAreSeparate(t *testing.T) {
home := t.TempDir()
terminalPath := History_path(home, true)
if !strings.HasPrefix(terminalPath, home+string(os.PathSeparator)) || History_path(home, false) != "/var/lib/fenrir/clipboard/tty.json" || terminalPath == History_path(home, false) {
t.Fatal(terminalPath, History_path(home, false))
}
store := New_persistence(terminalPath, false)
if store.LoadErr != nil || len(store.Initial.Entries) != 0 {
t.Fatal(store)
}
if err := store.Close(); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(filepath.Dir(terminalPath)); !errors.Is(err, os.ErrNotExist) {
t.Fatal("disabled persistence created state", err)
}
// A pre-existing malformed file is also ignored while disabled.
path := filepath.Join(home, "malformed.json")
os.WriteFile(path, []byte("not json"), 0600)
store = New_persistence(path, false)
if store.LoadErr != nil {
t.Fatal("disabled persistence read a file", store.LoadErr)
}
store.Close()
}
func TestPersistenceClearWinsOverQueuedWritesAndQuitFlushes(t *testing.T) {
path := filepath.Join(t.TempDir(), "private", "clipboard.json")
state := Snapshot{Version: 1, Entries: []string{"first"}}
store := New_persistence(path, false)
store.Enable(state, 200)
deadline := time.Now().Add(time.Second)
for {
if _, err := os.Stat(path); err == nil {
break
}
if time.Now().After(deadline) {
t.Fatal("initial write did not finish")
}
time.Sleep(time.Millisecond)
}
for range 10 {
store.Add(strings.Repeat("x", 1<<20), 200)
}
store.Clear()
if err := store.Close(); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(path); !errors.Is(err, os.ErrNotExist) {
t.Fatal("old write resurrected cleared history", err)
}
store = New_persistence(path, false)
store.Enable(state, 200)
if err := store.Close(); err != nil {
t.Fatal(err)
}
got, err := load_history(path)
if err != nil || !reflect.DeepEqual(got, state) {
t.Fatal("quit did not flush newest state", got, err)
}
}
func TestPersistenceRejectsCorruptUnsafeAndUnboundedFiles(t *testing.T) {
root := filepath.Join(t.TempDir(), "private")
if err := os.Mkdir(root, 0700); err != nil {
t.Fatal(err)
}
path := filepath.Join(root, "history.json")
for _, data := range [][]byte{
[]byte("not JSON"), []byte{0xff},
[]byte(`{"version":2,"entries":[],"selected":0}`),
[]byte(`{"version":1,"entries":["word"],"selected":2}`),
[]byte(`{"version":1,"entries":[],"selected":-1}`),
} {
if err := os.WriteFile(path, data, 0600); err != nil {
t.Fatal(err)
}
if _, err := load_history(path); err == nil {
t.Fatal("invalid snapshot accepted")
}
}
state := Snapshot{Version: 1, Entries: []string{"word"}}
if err := save_history(path, state); err != nil {
t.Fatal(err)
}
if err := os.Chmod(path, 0644); err != nil {
t.Fatal(err)
}
if _, err := load_history(path); err == nil {
t.Fatal("public file accepted")
}
os.Chmod(path, 0600)
file, err := os.OpenFile(path, os.O_WRONLY, 0)
if err != nil {
t.Fatal(err)
}
file.Truncate(maxHistoryBytes + 1)
file.Close()
if _, err := load_history(path); err == nil {
t.Fatal("oversized file accepted")
}
if err := save_history(path, state); err != nil {
t.Fatal(err)
}
before, _ := os.ReadFile(path)
if err := save_history(path, Snapshot{Version: 1, Entries: []string{strings.Repeat("x", MaxBytes+1)}}); err == nil {
t.Fatal("oversized entry saved")
}
after, _ := os.ReadFile(path)
if string(before) != string(after) {
t.Fatal("rejected save replaced history")
}
link := filepath.Join(root, "link.json")
if err := os.Symlink(path, link); err != nil {
t.Fatal(err)
}
if _, err := load_history(link); err == nil {
t.Fatal("symlink read")
}
if err := save_history(link, state); err == nil {
t.Fatal("symlink replaced")
}
fifo := filepath.Join(root, "fifo")
if err := syscall.Mkfifo(fifo, 0600); err != nil {
t.Fatal(err)
}
if _, err := load_history(fifo); err == nil {
t.Fatal("FIFO accepted")
}
public := filepath.Join(root, "public")
os.Mkdir(public, 0755)
if err := save_history(filepath.Join(public, "history.json"), state); err == nil {
t.Fatal("public directory accepted")
}
var decoded Snapshot
if err := json.Unmarshal(before, &decoded); err != nil || !reflect.DeepEqual(state, decoded) {
t.Fatal(decoded, err)
}
}
func TestClearRemovesTemporaryHistoryLeftByInterruptedWrite(t *testing.T) {
root := filepath.Join(t.TempDir(), "private")
if err := os.Mkdir(root, 0700); err != nil {
t.Fatal(err)
}
path := filepath.Join(root, "terminal.json")
stale := filepath.Join(root, ".terminal.json-interrupted.tmp")
other := filepath.Join(root, ".other.json-interrupted.tmp")
for _, name := range []string{stale, other} {
if err := os.WriteFile(name, []byte("old clipboard text"), 0600); err != nil {
t.Fatal(err)
}
}
store := New_persistence(path, false)
store.Clear()
if err := store.Close(); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(stale); !errors.Is(err, os.ErrNotExist) {
t.Fatal("clear retained interrupted write", err)
}
if _, err := os.Stat(other); err != nil {
t.Fatal("clear removed another history's file", err)
}
}