147 lines
4.4 KiB
Go
147 lines
4.4 KiB
Go
package main
|
|
|
|
import "testing"
|
|
|
|
// mkLayout builds a GPT layout: ESP (vfat) + root (ext4) + data (ntfs).
|
|
func mkLayout(diskBytes int64) *DiskLayout {
|
|
d := &DiskLayout{
|
|
DiskPath: "/dev/src", DiskSize: diskBytes, LogicalSector: 512,
|
|
Scheme: "gpt", Label: "gpt", LabelID: "GUID",
|
|
SfdiskDump: gptDump, // any non-empty dump so a script is produced
|
|
Tools: map[string]bool{
|
|
"sfdisk": true, "ntfsclone": true,
|
|
"partclone.extfs": true, "partclone.restore": true, "e2image": true,
|
|
},
|
|
Partitions: []Partition{
|
|
{Num: 1, Start: 2048, Size: 204800, Type: gptESP, FSType: "vfat"}, // 100 MiB
|
|
{Num: 2, Start: 206848, Size: 10 * 2048 * 1024, FSType: "ext4", FSMinBytes: 2 << 30}, // 10 GiB, min 2 GiB
|
|
{Num: 3, Start: 206848 + 10*2048*1024, Size: 10 * 2048 * 1024, FSType: "ntfs", FSMinBytes: 3 << 30}, // 10 GiB, min 3 GiB
|
|
},
|
|
BootRegions: []Region{{Offset: 0, Length: 446, Note: "MBR bootstrap"}},
|
|
}
|
|
return d
|
|
}
|
|
|
|
func lastEndBytes(d *DiskLayout) int64 {
|
|
var e int64
|
|
for _, p := range d.Partitions {
|
|
if x := (p.Start + p.Size) * d.LogicalSector; x > e {
|
|
e = x
|
|
}
|
|
}
|
|
return e + gptTailSectors*d.LogicalSector
|
|
}
|
|
|
|
func TestPlanFitsDeviceKeepsLayout(t *testing.T) {
|
|
d := mkLayout(40 << 30)
|
|
plan, err := planTargetLayout(d, false, 40<<30, planOpts{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(plan.Parts) != 3 {
|
|
t.Fatalf("parts = %d", len(plan.Parts))
|
|
}
|
|
for _, p := range plan.Parts {
|
|
if p.SrcSizeB != p.DstSizeB || p.ShrinkToB != 0 {
|
|
t.Fatalf("p%d resized unexpectedly: %+v", p.Num, p)
|
|
}
|
|
if p.SrcStartB != p.DstStartB {
|
|
t.Fatalf("p%d start moved", p.Num)
|
|
}
|
|
}
|
|
if plan.Parts[0].Method != "raw" { // vfat, no partclone.fat in tool set
|
|
t.Fatalf("p1 method = %s", plan.Parts[0].Method)
|
|
}
|
|
if plan.Parts[1].Method != "fs-image" || plan.Parts[1].Tool != "partclone" {
|
|
t.Fatalf("p2 method/tool = %s/%s", plan.Parts[1].Method, plan.Parts[1].Tool)
|
|
}
|
|
if plan.Parts[2].Method != "fs-image" || plan.Parts[2].Tool != "ntfsclone" {
|
|
t.Fatalf("p3 method/tool = %s/%s", plan.Parts[2].Method, plan.Parts[2].Tool)
|
|
}
|
|
if plan.ESPPart != 1 || plan.RootPart != 2 || !plan.UEFI {
|
|
t.Fatalf("boot roles: esp=%d root=%d uefi=%v", plan.ESPPart, plan.RootPart, plan.UEFI)
|
|
}
|
|
}
|
|
|
|
func TestPlanTrailingFreeSpaceOK(t *testing.T) {
|
|
d := mkLayout(60 << 30)
|
|
// target smaller than the *disk* but larger than what the layout needs
|
|
need := lastEndBytes(d)
|
|
if _, err := planTargetLayout(d, false, need+1<<20, planOpts{}); err != nil {
|
|
t.Fatalf("should fit into %d (need %d): %v", need+1<<20, need, err)
|
|
}
|
|
}
|
|
|
|
func TestPlanNeedsShrinkRequiresFlag(t *testing.T) {
|
|
d := mkLayout(40 << 30)
|
|
need := lastEndBytes(d)
|
|
small := need - (4 << 30) // 4 GiB short; ntfs (min 3 GiB) can give it up
|
|
|
|
if _, err := planTargetLayout(d, false, small, planOpts{}); err == nil {
|
|
t.Fatalf("expected error without --allow-shrink")
|
|
}
|
|
plan, err := planTargetLayout(d, false, small, planOpts{AllowShrink: true})
|
|
if err != nil {
|
|
t.Fatalf("with --allow-shrink: %v", err)
|
|
}
|
|
p3 := plan.Parts[2]
|
|
if p3.ShrinkToB == 0 || p3.ShrinkToB >= p3.SrcSizeB {
|
|
t.Fatalf("p3 should shrink: %+v", p3)
|
|
}
|
|
if p3.Method != "fs-image" {
|
|
t.Fatalf("shrunk partition must be fs-image, got %s", p3.Method)
|
|
}
|
|
}
|
|
|
|
func TestPlanCannotFit(t *testing.T) {
|
|
d := mkLayout(40 << 30)
|
|
// absurdly small: even shrinking ntfs+ext to their minimums can't help
|
|
if _, err := planTargetLayout(d, false, 1<<30, planOpts{AllowShrink: true}); err == nil {
|
|
t.Fatalf("expected cannot-fit error")
|
|
}
|
|
}
|
|
|
|
func TestPlanFileTargetSizing(t *testing.T) {
|
|
d := mkLayout(40 << 30)
|
|
plan, err := planTargetLayout(d, true, 0, planOpts{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
need := lastEndBytes(d)
|
|
if plan.ImageSize < need || plan.ImageSize > need+(2<<20) {
|
|
t.Fatalf("image size %d not ~%d", plan.ImageSize, need)
|
|
}
|
|
if plan.Script == "" {
|
|
t.Fatalf("expected a partition-table script for a file target")
|
|
}
|
|
}
|
|
|
|
func TestPlanNewIDsScript(t *testing.T) {
|
|
d := mkLayout(40 << 30)
|
|
plan, err := planTargetLayout(d, true, 0, planOpts{NewIDs: true})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if plan.Script == "" || containsAny(plan.Script, "label-id:", "uuid=") {
|
|
t.Fatalf("--new-ids script still has ids:\n%s", plan.Script)
|
|
}
|
|
}
|
|
|
|
func containsAny(s string, subs ...string) bool {
|
|
for _, x := range subs {
|
|
if len(x) > 0 && indexOf(s, x) >= 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func indexOf(s, sub string) int {
|
|
for i := 0; i+len(sub) <= len(s); i++ {
|
|
if s[i:i+len(sub)] == sub {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|