| 1 | package installlayout |
| 2 | |
| 3 | import ( |
| 4 | "os" |
| 5 | "path/filepath" |
| 6 | "runtime" |
| 7 | "testing" |
| 8 | ) |
| 9 | |
| 10 | func TestStableRelaunchPrefersCanonicalWindowsEntry(t *testing.T) { |
| 11 | if runtime.GOOS != "windows" { |
| 12 | t.Skip("Windows installed entry names") |
| 13 | } |
| 14 | root := t.TempDir() |
| 15 | seedVersionedLayout(t, root, "v1.38.9", "old") |
| 16 | canonical := writeTempMember(t, root, "Reasonix.exe", "new") |
| 17 | got, err := StableRelaunchPath(root) |
| 18 | if err != nil || got != canonical { |
| 19 | t.Fatalf("got=%q err=%v", got, err) |
| 20 | } |
| 21 | if err := os.Remove(canonical); err != nil { |
| 22 | t.Fatal(err) |
| 23 | } |
| 24 | got, err = StableRelaunchPath(root) |
| 25 | if err != nil || got != filepath.Join(root, "reasonix-launcher.exe") { |
| 26 | t.Fatalf("fallback=%q err=%v", got, err) |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | func seedVersionedLayout(t *testing.T, root, version, payload string) string { |
| 31 | t.Helper() |
| 32 | src := t.TempDir() |
| 33 | members := make([]Member, 0, len(AllowedVersionMembers())) |
| 34 | for _, name := range AllowedVersionMembers() { |
| 35 | members = append(members, Member{ |
| 36 | Name: name, |
| 37 | Path: writeTempMember(t, src, version+"-"+name, payload+"-"+name), |
| 38 | }) |
| 39 | } |
| 40 | launcherSrc := writeTempMember(t, src, version+"-launcher", payload+"-launcher") |
| 41 | if err := ActivateVersion(ActivationRequest{ |
| 42 | InstallRoot: root, |
| 43 | Version: version, |
| 44 | RequestID: "relaunch-" + version, |
| 45 | Members: members, |
| 46 | RootMembers: []Member{ |
| 47 | {Name: LauncherBinaryName(), Path: launcherSrc}, |
| 48 | }, |
| 49 | RequiredRootNames: []string{LauncherBinaryName()}, |
| 50 | }); err != nil { |
| 51 | t.Fatal(err) |
| 52 | } |
| 53 | desktop, err := ActiveDesktopPath(root) |
| 54 | if err != nil { |
| 55 | t.Fatal(err) |
| 56 | } |
| 57 | return desktop |
| 58 | } |
| 59 | |
| 60 | func TestStableRelaunchPathPrefersLauncherOverVersionedDesktop(t *testing.T) { |
| 61 | root := t.TempDir() |
| 62 | oldDesktop := seedVersionedLayout(t, root, "v1.24.0", "old") |
| 63 | newDesktop := seedVersionedLayout(t, root, "v1.24.1", "new") |
| 64 | if oldDesktop == newDesktop { |
| 65 | t.Fatal("activation did not retain a distinct previous desktop") |
| 66 | } |
| 67 | if _, err := os.Stat(oldDesktop); err != nil { |
| 68 | t.Fatalf("previous desktop should remain on disk: %v", err) |
| 69 | } |
| 70 | got, err := StableRelaunchPath(root) |
| 71 | if err != nil { |
| 72 | t.Fatal(err) |
| 73 | } |
| 74 | want := filepath.Join(root, LauncherBinaryName()) |
| 75 | if got != want { |
| 76 | t.Fatalf("StableRelaunchPath = %s, want launcher %s", got, want) |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | func TestIsActiveDesktopExecutableRejectsRetainedPreviousVersion(t *testing.T) { |
| 81 | root := t.TempDir() |
| 82 | oldDesktop := seedVersionedLayout(t, root, "v1.24.0", "old") |
| 83 | newDesktop := seedVersionedLayout(t, root, "v1.24.1", "new") |
| 84 | |
| 85 | active, err := IsActiveDesktopExecutable(newDesktop) |
| 86 | if err != nil || !active { |
| 87 | t.Fatalf("active desktop: active=%v err=%v", active, err) |
| 88 | } |
| 89 | active, err = IsActiveDesktopExecutable(oldDesktop) |
| 90 | if err != nil { |
| 91 | t.Fatal(err) |
| 92 | } |
| 93 | if active { |
| 94 | t.Fatal("retained previous desktop must not count as the active executable") |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | func TestIsSupersededVersionedDesktop(t *testing.T) { |
| 99 | root := t.TempDir() |
| 100 | oldDesktop := seedVersionedLayout(t, root, "v1.24.0", "old") |
| 101 | newDesktop := seedVersionedLayout(t, root, "v1.24.1", "new") |
| 102 | if !IsSupersededVersionedDesktop(root, oldDesktop) { |
| 103 | t.Fatal("retained previous desktop should be superseded") |
| 104 | } |
| 105 | if IsSupersededVersionedDesktop(root, newDesktop) { |
| 106 | t.Fatal("active desktop must not be superseded") |
| 107 | } |
| 108 | if IsSupersededVersionedDesktop(root, filepath.Join(root, LauncherBinaryName())) { |
| 109 | t.Fatal("launcher is not a superseded desktop") |
| 110 | } |
| 111 | if IsSupersededVersionedDesktop(root, filepath.Join(t.TempDir(), DesktopBinaryName())) { |
| 112 | t.Fatal("desktop outside the install root must not be treated as superseded") |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | func TestIsActiveDesktopExecutableAllowsFlatInstall(t *testing.T) { |
| 117 | root := t.TempDir() |
| 118 | exe := filepath.Join(root, DesktopBinaryName()) |
| 119 | if err := os.WriteFile(exe, []byte("flat"), 0o755); err != nil { |
| 120 | t.Fatal(err) |
| 121 | } |
| 122 | active, err := IsActiveDesktopExecutable(exe) |
| 123 | if err != nil || !active { |
| 124 | t.Fatalf("flat desktop: active=%v err=%v", active, err) |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | func TestStableRelaunchPathFallsBackToActiveDesktopWhenLauncherMissing(t *testing.T) { |
| 129 | root := t.TempDir() |
| 130 | desktop := seedVersionedLayout(t, root, "v1.24.1", "new") |
| 131 | if err := os.Remove(filepath.Join(root, LauncherBinaryName())); err != nil { |
| 132 | t.Fatal(err) |
| 133 | } |
| 134 | got, err := StableRelaunchPath(root) |
| 135 | if err != nil { |
| 136 | t.Fatal(err) |
| 137 | } |
| 138 | if got != desktop { |
| 139 | t.Fatalf("StableRelaunchPath = %s, want active desktop %s", got, desktop) |
| 140 | } |
| 141 | } |
| 142 |