146 lines
4.1 KiB
Go
146 lines
4.1 KiB
Go
package runfile
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestWriteReadRoundTrip(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "nested", "running.json")
|
|
want := Info{PID: 4242, Port: 3001, StartedAt: time.Now().UTC().Truncate(time.Second)}
|
|
|
|
if err := Write(path, want); err != nil {
|
|
t.Fatalf("Write: %v", err)
|
|
}
|
|
got, err := Read(path)
|
|
if err != nil {
|
|
t.Fatalf("Read: %v", err)
|
|
}
|
|
if got == nil {
|
|
t.Fatal("Read returned nil for an existing file")
|
|
}
|
|
if got.PID != want.PID || got.Port != want.Port || !got.StartedAt.Equal(want.StartedAt) {
|
|
t.Errorf("round trip mismatch: got %+v, want %+v", *got, want)
|
|
}
|
|
}
|
|
|
|
// TestWriteOverwritesExisting is the cross-platform overwrite check: a stale
|
|
// running.json from a crashed predecessor must be replaced cleanly. POSIX
|
|
// rename(2) handles this natively; Windows needs MoveFileEx with
|
|
// MOVEFILE_REPLACE_EXISTING — atomicReplace gives us both.
|
|
func TestWriteOverwritesExisting(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "running.json")
|
|
|
|
if err := Write(path, Info{PID: 1, Port: 3001}); err != nil {
|
|
t.Fatalf("first Write: %v", err)
|
|
}
|
|
if err := Write(path, Info{PID: 2, Port: 3002}); err != nil {
|
|
t.Fatalf("second Write (overwrite): %v", err)
|
|
}
|
|
|
|
got, err := Read(path)
|
|
if err != nil {
|
|
t.Fatalf("Read: %v", err)
|
|
}
|
|
if got == nil || got.PID != 2 || got.Port != 3002 {
|
|
t.Errorf("after overwrite: got %+v, want PID=2 Port=3002", got)
|
|
}
|
|
}
|
|
|
|
func TestReadMissingIsNotError(t *testing.T) {
|
|
got, err := Read(filepath.Join(t.TempDir(), "absent.json"))
|
|
if err != nil {
|
|
t.Fatalf("Read missing: %v", err)
|
|
}
|
|
if got != nil {
|
|
t.Errorf("Read missing = %+v, want nil", got)
|
|
}
|
|
}
|
|
|
|
func TestRemoveIdempotent(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "running.json")
|
|
if err := Remove(path); err != nil {
|
|
t.Errorf("Remove on missing file: %v", err)
|
|
}
|
|
if err := Write(path, Info{PID: 1, Port: 2}); err != nil {
|
|
t.Fatalf("Write: %v", err)
|
|
}
|
|
if err := Remove(path); err != nil {
|
|
t.Errorf("Remove existing: %v", err)
|
|
}
|
|
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
|
t.Errorf("file still present after Remove")
|
|
}
|
|
}
|
|
|
|
func TestRemoveIfOwnedDoesNotDeleteSuccessorRunfile(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "running.json")
|
|
if err := Write(path, Info{PID: 1, Port: 3001}); err != nil {
|
|
t.Fatalf("Write predecessor: %v", err)
|
|
}
|
|
if err := Write(path, Info{PID: 2, Port: 3002}); err != nil {
|
|
t.Fatalf("Write successor: %v", err)
|
|
}
|
|
if err := RemoveIfOwned(path, 1); err != nil {
|
|
t.Fatalf("RemoveIfOwned predecessor: %v", err)
|
|
}
|
|
got, err := Read(path)
|
|
if err != nil {
|
|
t.Fatalf("Read: %v", err)
|
|
}
|
|
if got == nil || got.PID != 2 || got.Port != 3002 {
|
|
t.Fatalf("successor runfile was removed or changed: %+v", got)
|
|
}
|
|
if err := RemoveIfOwned(path, 2); err != nil {
|
|
t.Fatalf("RemoveIfOwned successor: %v", err)
|
|
}
|
|
if got, err := Read(path); err != nil || got != nil {
|
|
t.Fatalf("after owner removal got=%+v err=%v", got, err)
|
|
}
|
|
}
|
|
|
|
func TestCheckStaleDeadPID(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "running.json")
|
|
// PID 0x7FFFFFFF is effectively guaranteed not to exist.
|
|
if err := Write(path, Info{PID: 0x7FFFFFFF, Port: 3001}); err != nil {
|
|
t.Fatalf("Write: %v", err)
|
|
}
|
|
live, err := CheckStale(path)
|
|
if err != nil {
|
|
t.Fatalf("CheckStale: %v", err)
|
|
}
|
|
if live != nil {
|
|
t.Errorf("CheckStale on dead PID = %+v, want nil (stale, safe to overwrite)", live)
|
|
}
|
|
}
|
|
|
|
func TestCheckStaleLivePID(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "running.json")
|
|
// This test process is unquestionably alive.
|
|
if err := Write(path, Info{PID: os.Getpid(), Port: 3001}); err != nil {
|
|
t.Fatalf("Write: %v", err)
|
|
}
|
|
live, err := CheckStale(path)
|
|
if err != nil {
|
|
t.Fatalf("CheckStale: %v", err)
|
|
}
|
|
if live == nil {
|
|
t.Fatal("CheckStale on live PID = nil, want the live Info")
|
|
}
|
|
if live.PID != os.Getpid() {
|
|
t.Errorf("live.PID = %d, want %d", live.PID, os.Getpid())
|
|
}
|
|
}
|
|
|
|
func TestCheckStaleNoFile(t *testing.T) {
|
|
live, err := CheckStale(filepath.Join(t.TempDir(), "absent.json"))
|
|
if err != nil {
|
|
t.Fatalf("CheckStale: %v", err)
|
|
}
|
|
if live != nil {
|
|
t.Errorf("CheckStale with no file = %+v, want nil", live)
|
|
}
|
|
}
|