package platform import ( "fmt" "testing" ) func TestInferLiveBootKind(t *testing.T) { t.Parallel() tests := []struct { name string fsType string source string deviceType string transport string want string }{ {name: "ram tmpfs", fsType: "tmpfs", source: "/dev/shm/bee-live", want: "ram"}, {name: "usb disk", source: "/dev/sdb1", deviceType: "disk", transport: "usb", want: "usb"}, {name: "cdrom rom", source: "/dev/sr0", deviceType: "rom", want: "cdrom"}, {name: "disk sata", source: "/dev/nvme0n1p1", deviceType: "disk", transport: "nvme", want: "disk"}, {name: "unknown", source: "overlay", want: "unknown"}, } for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { got := inferLiveBootKind(tc.fsType, tc.source, tc.deviceType, tc.transport) if got != tc.want { t.Fatalf("inferLiveBootKind(%q,%q,%q,%q)=%q want %q", tc.fsType, tc.source, tc.deviceType, tc.transport, got, tc.want) } }) } } func TestVerifyInstallToRAMStatus(t *testing.T) { t.Parallel() dstDir := t.TempDir() if err := verifyInstallToRAMStatus(LiveBootSource{InRAM: true, Kind: "ram", Source: "tmpfs"}, dstDir, false, nil); err != nil { t.Fatalf("expected success for RAM-backed status, got %v", err) } err := verifyInstallToRAMStatus(LiveBootSource{InRAM: false, Kind: "usb", Device: "/dev/sdb1"}, dstDir, false, nil) if err == nil { t.Fatal("expected verification failure when media is still on USB") } if got := err.Error(); got != "install to RAM verification failed: live medium still mounted from USB (/dev/sdb1) and no squashfs found in "+dstDir { t.Fatalf("error=%q", got) } } func TestDescribeLiveBootSource(t *testing.T) { t.Parallel() if got := describeLiveBootSource(LiveBootSource{InRAM: true, Kind: "ram"}); got != "RAM" { t.Fatalf("got %q want RAM", got) } if got := describeLiveBootSource(LiveBootSource{Kind: "unknown", Source: "/run/live/medium"}); got != "/run/live/medium" { t.Fatalf("got %q want /run/live/medium", got) } } func TestEvaluateLiveMediaRAMState(t *testing.T) { t.Parallel() t.Run("in_ram", func(t *testing.T) { state := evaluateLiveMediaRAMState( LiveBootSource{InRAM: true, Kind: "ram", Source: "tmpfs"}, false, nil, nil, ) if state.State != "in_ram" || state.Status != "ok" || state.CanStartCopy { t.Fatalf("state=%+v", state) } }) t.Run("partial_copy_after_cancel", func(t *testing.T) { state := evaluateLiveMediaRAMState( LiveBootSource{InRAM: false, Kind: "usb", Device: "/dev/sdb1"}, false, []string{"/run/live/medium/live/filesystem.squashfs", "/run/live/medium/live/firmware.squashfs"}, []string{"/dev/shm/bee-live/filesystem.squashfs"}, ) if state.State != "partial" || state.Status != "partial" || !state.CanStartCopy { t.Fatalf("state=%+v", state) } if state.CopyComplete { t.Fatalf("CopyComplete=%v want false", state.CopyComplete) } }) t.Run("toram_failed", func(t *testing.T) { state := evaluateLiveMediaRAMState( LiveBootSource{InRAM: false, Kind: "usb", Device: "/dev/sdb1"}, true, nil, nil, ) if state.State != "toram_failed" || state.Status != "failed" || !state.CanStartCopy { t.Fatalf("state=%+v", state) } }) } func TestShouldLogCopyProgress(t *testing.T) { t.Parallel() total := int64(250 * 1024 * 1024) step := int64(100 * 1024 * 1024) if shouldLogCopyProgress(step-1, total, 0) { t.Fatal("progress logged too early") } if !shouldLogCopyProgress(step, total, 0) { t.Fatal("expected log at first 100MB boundary") } if shouldLogCopyProgress(step+16*1024*1024, total, step) { t.Fatal("progress logged again before next 100MB") } if !shouldLogCopyProgress(2*step, total, step) { t.Fatal("expected log at second 100MB boundary") } if !shouldLogCopyProgress(total, total, 2*step) { t.Fatal("expected final completion log") } } func TestTryRemountLiveMedium(t *testing.T) { t.Parallel() orig := runRemountMedium t.Cleanup(func() { runRemountMedium = orig }) t.Run("success", func(t *testing.T) { runRemountMedium = func() ([]byte, error) { return []byte("[10:57:31] Mounted /dev/sr1 on /run/live/medium\n"), nil } var logs []string if err := tryRemountLiveMedium(func(msg string) { logs = append(logs, msg) }); err != nil { t.Fatalf("tryRemountLiveMedium() error = %v", err) } if len(logs) != 1 || logs[0] != "bee-remount-medium: [10:57:31] Mounted /dev/sr1 on /run/live/medium" { t.Fatalf("logs=%v", logs) } }) t.Run("failure", func(t *testing.T) { runRemountMedium = func() ([]byte, error) { return []byte("must be run as root\n"), fmt.Errorf("exit status 1") } var logs []string err := tryRemountLiveMedium(func(msg string) { logs = append(logs, msg) }) if err == nil { t.Fatal("expected error") } if len(logs) != 1 || logs[0] != "bee-remount-medium: must be run as root" { t.Fatalf("logs=%v", logs) } }) } func TestEnsureLiveMediumAvailableRemountsSource(t *testing.T) { t.Parallel() origGlob := liveMediumSquashfsGlob origRemount := runRemountMedium t.Cleanup(func() { liveMediumSquashfsGlob = origGlob runRemountMedium = origRemount }) callCount := 0 liveMediumSquashfsGlob = func() ([]string, error) { callCount++ if callCount == 1 { return nil, nil } return []string{"/run/live/medium/live/filesystem.squashfs"}, nil } runRemountMedium = func() ([]byte, error) { return []byte("Mounted /dev/sr1 on /run/live/medium\n"), nil } var logs []string files, ok := ensureLiveMediumAvailable(func(msg string) { logs = append(logs, msg) }) if !ok { t.Fatal("expected live medium to become available after remount") } if callCount < 2 { t.Fatalf("liveMediumSquashfsGlob called %d times, want at least 2", callCount) } if len(files) != 1 || files[0] != "/run/live/medium/live/filesystem.squashfs" { t.Fatalf("files=%v", files) } found := false for _, msg := range logs { if msg == "Live medium restored after remount scan." { found = true break } } if !found { t.Fatalf("expected remount success log, logs=%v", logs) } } func TestDetachInstallMedium(t *testing.T) { t.Parallel() origUmount := umountLiveMedium origEject := ejectDevice t.Cleanup(func() { umountLiveMedium = origUmount ejectDevice = origEject }) t.Run("success", func(t *testing.T) { var umountCalled bool var ejected string umountLiveMedium = func() error { umountCalled = true return nil } ejectDevice = func(device string) error { ejected = device return nil } var logs []string detachInstallMedium(LiveBootSource{Kind: "cdrom", Device: "/dev/sr1"}, func(msg string) { logs = append(logs, msg) }) if !umountCalled { t.Fatal("expected umountLiveMedium to be called") } if ejected != "/dev/sr1" { t.Fatalf("ejected=%q want /dev/sr1", ejected) } if len(logs) < 3 { t.Fatalf("logs=%v", logs) } }) t.Run("no device", func(t *testing.T) { umountLiveMedium = func() error { return nil } ejectDevice = func(device string) error { t.Fatalf("unexpected eject for %q", device) return nil } var logs []string detachInstallMedium(LiveBootSource{Kind: "ram", Source: "tmpfs"}, func(msg string) { logs = append(logs, msg) }) found := false for _, msg := range logs { if msg == "No block device identified for eject; skipping media eject." { found = true break } } if !found { t.Fatalf("logs=%v", logs) } }) t.Run("eject failure is warning only", func(t *testing.T) { umountLiveMedium = func() error { return nil } ejectDevice = func(device string) error { return fmt.Errorf("exit status 1") } var logs []string detachInstallMedium(LiveBootSource{Kind: "usb", Device: "/dev/sdb1"}, func(msg string) { logs = append(logs, msg) }) found := false for _, msg := range logs { if msg == "Warning: could not eject /dev/sdb1: exit status 1" { found = true break } } if !found { t.Fatalf("logs=%v", logs) } }) }