| 1 | //go:build windows |
| 2 | |
| 3 | package appidentity |
| 4 | |
| 5 | import ( |
| 6 | "bytes" |
| 7 | "fmt" |
| 8 | "os" |
| 9 | "path/filepath" |
| 10 | "strings" |
| 11 | "testing" |
| 12 | |
| 13 | "golang.org/x/sys/windows" |
| 14 | ) |
| 15 | |
| 16 | func TestRepairOwnedShortcutsDiscoversPublicAndProductProgramFolders(t *testing.T) { |
| 17 | migrationTestCOM(t) |
| 18 | root := t.TempDir() |
| 19 | launcher := filepath.Join(root, "reasonix-launcher.exe") |
| 20 | migrationTestFile(t, launcher) |
| 21 | studioTarget := filepath.Join(t.TempDir(), "reasonix-studio.exe") |
| 22 | migrationTestFile(t, studioTarget) |
| 23 | |
| 24 | // Every known-folder request is confined to this temporary tree. Unknown |
| 25 | // requests fail instead of falling back to the machine's real shell folders. |
| 26 | shellRoot := t.TempDir() |
| 27 | folders := map[windows.KNOWNFOLDERID]string{ |
| 28 | *windows.FOLDERID_Desktop: filepath.Join(shellRoot, "Desktop"), |
| 29 | *windows.FOLDERID_PublicDesktop: filepath.Join(shellRoot, "PublicDesktop"), |
| 30 | *windows.FOLDERID_Programs: filepath.Join(shellRoot, "Programs"), |
| 31 | *windows.FOLDERID_CommonPrograms: filepath.Join(shellRoot, "CommonPrograms"), |
| 32 | *windows.FOLDERID_RoamingAppData: filepath.Join(shellRoot, "Roaming"), |
| 33 | } |
| 34 | originalKnownFolderPath := knownFolderPath |
| 35 | knownFolderPath = func(id *windows.KNOWNFOLDERID, _ uint32) (string, error) { |
| 36 | if id != nil { |
| 37 | if path, ok := folders[*id]; ok { |
| 38 | return path, nil |
| 39 | } |
| 40 | } |
| 41 | return "", fmt.Errorf("unexpected known-folder request in isolated test") |
| 42 | } |
| 43 | t.Cleanup(func() { knownFolderPath = originalKnownFolderPath }) |
| 44 | |
| 45 | locations := []string{ |
| 46 | folders[*windows.FOLDERID_Desktop], |
| 47 | folders[*windows.FOLDERID_PublicDesktop], |
| 48 | folders[*windows.FOLDERID_Programs], |
| 49 | folders[*windows.FOLDERID_CommonPrograms], |
| 50 | filepath.Join(folders[*windows.FOLDERID_Programs], "Reasonix"), |
| 51 | filepath.Join(folders[*windows.FOLDERID_CommonPrograms], "Reasonix"), |
| 52 | filepath.Join(folders[*windows.FOLDERID_RoamingAppData], "Microsoft", "Internet Explorer", "Quick Launch", "User Pinned", "TaskBar"), |
| 53 | } |
| 54 | var ownedPaths []string |
| 55 | studioBefore := make(map[string][]byte) |
| 56 | for _, location := range locations { |
| 57 | if err := os.MkdirAll(location, 0o755); err != nil { |
| 58 | t.Fatal(err) |
| 59 | } |
| 60 | owned := filepath.Join(location, "Reasonix.lnk") |
| 61 | migrationTestShortcut(t, owned, migrationShortcutState{ |
| 62 | target: launcher, id: "Reasonix", icon: launcher, workingDirectory: root, showCmd: 1, |
| 63 | }) |
| 64 | ownedPaths = append(ownedPaths, owned) |
| 65 | studio := filepath.Join(location, "Reasonix Studio.lnk") |
| 66 | migrationTestShortcut(t, studio, migrationShortcutState{ |
| 67 | target: studioTarget, id: "Reasonix", icon: studioTarget, workingDirectory: filepath.Dir(studioTarget), showCmd: 1, |
| 68 | }) |
| 69 | studioBefore[studio] = migrationReadBytes(t, studio) |
| 70 | } |
| 71 | |
| 72 | candidates, err := shortcutCandidates(root) |
| 73 | if err != nil { |
| 74 | t.Fatal(err) |
| 75 | } |
| 76 | seen := make(map[string]bool) |
| 77 | for _, path := range candidates { |
| 78 | seen[strings.ToLower(filepath.Clean(path))] = true |
| 79 | } |
| 80 | for _, path := range ownedPaths { |
| 81 | if !seen[strings.ToLower(filepath.Clean(path))] { |
| 82 | t.Errorf("owned shortcut was not discovered: %s", path) |
| 83 | } |
| 84 | } |
| 85 | if err := RepairOwnedShortcuts(root); err != nil { |
| 86 | t.Fatal(err) |
| 87 | } |
| 88 | for _, path := range ownedPaths { |
| 89 | got := migrationReadShortcut(t, path) |
| 90 | if got.id != "io.reasonix.desktop" || !migrationSamePath(got.target, launcher) { |
| 91 | t.Errorf("discovered shortcut was not repaired: %s: %+v", path, got) |
| 92 | } |
| 93 | } |
| 94 | for path, before := range studioBefore { |
| 95 | if !bytes.Equal(before, migrationReadBytes(t, path)) { |
| 96 | t.Errorf("discovery repair changed independently installed Studio shortcut: %s", path) |
| 97 | } |
| 98 | } |
| 99 | } |
| 100 |