// SPDX-License-Identifier: GPL-3.0-or-later package apply import ( "os" "path/filepath" "testing" "time" ) func write(t *testing.T, path, content string, mode os.FileMode) string { t.Helper() if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(path, []byte(content), mode); err != nil { t.Fatal(err) } return path } func TestCopyPreservesModeAndModTime(t *testing.T) { dir := t.TempDir() src := write(t, filepath.Join(dir, "a", "x.pdf"), "content", 0o640) old := time.Date(2026, 3, 4, 5, 6, 7, 0, time.UTC) if err := os.Chtimes(src, old, old); err != nil { t.Fatal(err) } dst := filepath.Join(dir, "b", "x.pdf") if err := copyFile(src, dst); err != nil { t.Fatal(err) } fi, err := os.Stat(dst) if err != nil { t.Fatal(err) } if b, _ := os.ReadFile(dst); string(b) != "content" { t.Errorf("content = %q", b) } if fi.Mode().Perm() != 0o640 { t.Errorf("mode = %v, want 0640", fi.Mode().Perm()) } if !fi.ModTime().Equal(old) { t.Errorf("mtime = %v, want %v", fi.ModTime(), old) } if si, _ := os.Stat(src); si == nil { t.Error("copy removed its source") } } func TestCopyLeavesNoTempOnFailure(t *testing.T) { dir := t.TempDir() src := write(t, filepath.Join(dir, "x.pdf"), "content", 0o644) // A destination directory that is really a file: the rename must fail. blocked := write(t, filepath.Join(dir, "blocked"), "not a directory", 0o644) if err := copyFile(src, filepath.Join(blocked, "x.pdf")); err == nil { t.Fatal("copy into a non-directory succeeded") } entries, err := os.ReadDir(dir) if err != nil { t.Fatal(err) } for _, e := range entries { if len(e.Name()) > 6 && e.Name()[:7] == ".krino-" { t.Errorf("a temporary file was left behind: %s", e.Name()) } } } // TestMoveAcrossFilesystems exercises the EXDEV fallback. /dev/shm is a // second filesystem on Linux; the test skips where there is none. func TestMoveAcrossFilesystems(t *testing.T) { other, err := os.MkdirTemp("/dev/shm", "krino-apply-") if err != nil { t.Skip("no second filesystem available:", err) } defer os.RemoveAll(other) src := write(t, filepath.Join(other, "x.pdf"), "across", 0o644) dst := filepath.Join(t.TempDir(), "x.pdf") if err := moveFile(src, dst); err != nil { t.Fatal(err) } if b, err := os.ReadFile(dst); err != nil || string(b) != "across" { t.Errorf("destination = %q, %v", b, err) } if _, err := os.Stat(src); !os.IsNotExist(err) { t.Error("the source survived a cross-filesystem move") } }