computer-host/internal/daemon/files_test.go
Hari e2f9e54970 host daemon (#2)
* feat: host daemon api scaffold

* fix: use sparse writes

* fix: unix socket length (<108 bytes)
2026-04-08 11:23:19 -04:00

94 lines
2.5 KiB
Go

package daemon
import (
"bytes"
"io"
"os"
"path/filepath"
"syscall"
"testing"
)
func TestCloneFilePreservesSparseDiskUsage(t *testing.T) {
root := t.TempDir()
sourcePath := filepath.Join(root, "source.img")
targetPath := filepath.Join(root, "target.img")
sourceFile, err := os.OpenFile(sourcePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
if err != nil {
t.Fatalf("open source file: %v", err)
}
if _, err := sourceFile.Write([]byte("head")); err != nil {
sourceFile.Close()
t.Fatalf("write source prefix: %v", err)
}
if _, err := sourceFile.Seek(32<<20, io.SeekStart); err != nil {
sourceFile.Close()
t.Fatalf("seek source hole: %v", err)
}
if _, err := sourceFile.Write([]byte("tail")); err != nil {
sourceFile.Close()
t.Fatalf("write source suffix: %v", err)
}
if err := sourceFile.Close(); err != nil {
t.Fatalf("close source file: %v", err)
}
sourceInfo, err := os.Stat(sourcePath)
if err != nil {
t.Fatalf("stat source file: %v", err)
}
sourceUsage, err := allocatedBytes(sourcePath)
if err != nil {
t.Fatalf("allocated bytes for source: %v", err)
}
if sourceUsage >= sourceInfo.Size()/2 {
t.Skip("temp filesystem does not expose sparse allocation savings")
}
if err := cloneFile(sourcePath, targetPath); err != nil {
t.Fatalf("clone sparse file: %v", err)
}
targetInfo, err := os.Stat(targetPath)
if err != nil {
t.Fatalf("stat target file: %v", err)
}
if targetInfo.Size() != sourceInfo.Size() {
t.Fatalf("target size mismatch: got %d want %d", targetInfo.Size(), sourceInfo.Size())
}
targetUsage, err := allocatedBytes(targetPath)
if err != nil {
t.Fatalf("allocated bytes for target: %v", err)
}
if targetUsage >= targetInfo.Size()/2 {
t.Fatalf("target file is not sparse enough: allocated=%d size=%d", targetUsage, targetInfo.Size())
}
targetData, err := os.ReadFile(targetPath)
if err != nil {
t.Fatalf("read target file: %v", err)
}
if !bytes.Equal(targetData[:4], []byte("head")) {
t.Fatalf("target prefix mismatch: %q", string(targetData[:4]))
}
if !bytes.Equal(targetData[len(targetData)-4:], []byte("tail")) {
t.Fatalf("target suffix mismatch: %q", string(targetData[len(targetData)-4:]))
}
if !bytes.Equal(targetData[4:4+(1<<20)], make([]byte, 1<<20)) {
t.Fatal("target hole contents were not zeroed")
}
}
func allocatedBytes(path string) (int64, error) {
info, err := os.Stat(path)
if err != nil {
return 0, err
}
stat, ok := info.Sys().(*syscall.Stat_t)
if !ok {
return 0, syscall.EINVAL
}
return stat.Blocks * 512, nil
}