mirror of
https://github.com/Awuqing/BackupX.git
synced 2026-06-21 09:33:40 +08:00
文件备份新增差异模式:仅打包自上次全量以来的变更并记录删除,恢复自动按全量+差异链还原。含基线解析、链式恢复、保留链保护与本机文件任务校验;清单/比对/删除/往返/保留保护单测全覆盖。
80 lines
2.3 KiB
Go
80 lines
2.3 KiB
Go
package backup
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestEncodeDecodeManifestRoundTrip(t *testing.T) {
|
|
m := Manifest{Entries: []ManifestEntry{
|
|
{Path: "src/a.txt", Size: 10, ModTimeNs: 100, Mode: 0o644},
|
|
{Path: "src", Size: 0, ModTimeNs: 50, Mode: 0o755, IsDir: true},
|
|
}}
|
|
data, err := EncodeManifest(m)
|
|
if err != nil {
|
|
t.Fatalf("EncodeManifest: %v", err)
|
|
}
|
|
got, err := DecodeManifest(data)
|
|
if err != nil {
|
|
t.Fatalf("DecodeManifest: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(got, m) {
|
|
t.Fatalf("roundtrip mismatch:\n got %#v\nwant %#v", got, m)
|
|
}
|
|
}
|
|
|
|
func TestDecodeManifestEmpty(t *testing.T) {
|
|
got, err := DecodeManifest(nil)
|
|
if err != nil {
|
|
t.Fatalf("DecodeManifest(nil): %v", err)
|
|
}
|
|
if len(got.Entries) != 0 {
|
|
t.Fatalf("expected empty manifest, got %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestChangedSince(t *testing.T) {
|
|
base := Manifest{Entries: []ManifestEntry{
|
|
{Path: "a.txt", Size: 10, ModTimeNs: 100},
|
|
{Path: "dir", IsDir: true, ModTimeNs: 100},
|
|
}}.index()
|
|
|
|
cases := []struct {
|
|
name string
|
|
cur ManifestEntry
|
|
want bool
|
|
}{
|
|
{"unchanged file", ManifestEntry{Path: "a.txt", Size: 10, ModTimeNs: 100}, false},
|
|
{"size changed", ManifestEntry{Path: "a.txt", Size: 11, ModTimeNs: 100}, true},
|
|
{"mtime changed", ManifestEntry{Path: "a.txt", Size: 10, ModTimeNs: 200}, true},
|
|
{"new file", ManifestEntry{Path: "b.txt", Size: 1, ModTimeNs: 1}, true},
|
|
{"existing dir skipped", ManifestEntry{Path: "dir", IsDir: true, ModTimeNs: 999}, false},
|
|
{"new dir included", ManifestEntry{Path: "newdir", IsDir: true, ModTimeNs: 1}, true},
|
|
}
|
|
for _, tc := range cases {
|
|
if got := changedSince(base, tc.cur); got != tc.want {
|
|
t.Errorf("%s: changedSince=%v want %v", tc.name, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDeletedPaths(t *testing.T) {
|
|
base := Manifest{Entries: []ManifestEntry{
|
|
{Path: "a"}, {Path: "b"}, {Path: "c"},
|
|
}}.index()
|
|
seen := map[string]struct{}{"a": {}, "c": {}}
|
|
got := deletedPaths(base, seen)
|
|
want := []string{"b"}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("deletedPaths=%v want %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestDeletedPathsNoneWhenAllSeen(t *testing.T) {
|
|
base := Manifest{Entries: []ManifestEntry{{Path: "a"}, {Path: "b"}}}.index()
|
|
seen := map[string]struct{}{"a": {}, "b": {}}
|
|
if got := deletedPaths(base, seen); len(got) != 0 {
|
|
t.Fatalf("expected no deletions, got %v", got)
|
|
}
|
|
}
|