返回 DeepSeek-Reasonix
main_test.go
根目录 / desktop / cmd / windows-resource / main_test.go
1 package main
2
3 import (
4 "bytes"
5 "debug/pe"
6 "os"
7 "os/exec"
8 "path/filepath"
9 "runtime"
10 "testing"
11
12 "github.com/tc-hib/winres"
13 "github.com/tc-hib/winres/version"
14 )
15
16 func TestParseNumericVersion(t *testing.T) {
17 for _, test := range []struct {
18 value string
19 want [4]uint16
20 }{
21 {"1.17.13", [4]uint16{1, 17, 13, 0}},
22 {"2.3.4.5", [4]uint16{2, 3, 4, 5}},
23 } {
24 got, err := parseNumericVersion(test.value)
25 if err != nil {
26 t.Fatalf("parseNumericVersion(%q): %v", test.value, err)
27 }
28 if got != test.want {
29 t.Fatalf("parseNumericVersion(%q) = %v, want %v", test.value, got, test.want)
30 }
31 }
32 for _, value := range []string{"", "1.2", "1.2.3-beta", "1.2.70000"} {
33 if _, err := parseNumericVersion(value); err == nil {
34 t.Errorf("parseNumericVersion(%q) unexpectedly succeeded", value)
35 }
36 }
37 }
38
39 func TestStampExecutableSupportsWindowsReleaseArchitectures(t *testing.T) {
40 icon, err := filepath.Abs(filepath.Join("..", "..", "build", "windows", "icon.ico"))
41 if err != nil {
42 t.Fatal(err)
43 }
44 for _, test := range []struct {
45 arch string
46 machine uint16
47 }{
48 {"amd64", pe.IMAGE_FILE_MACHINE_AMD64},
49 {"arm64", pe.IMAGE_FILE_MACHINE_ARM64},
50 } {
51 t.Run(test.arch, func(t *testing.T) {
52 dir := t.TempDir()
53 source := filepath.Join(dir, "main.go")
54 if err := os.WriteFile(source, []byte("package main\nfunc main() {}\n"), 0o600); err != nil {
55 t.Fatal(err)
56 }
57 executable := filepath.Join(dir, "reasonix-launcher.exe")
58 cmd := exec.Command("go", "build", "-trimpath", "-o", executable, source)
59 cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH="+test.arch, "CGO_ENABLED=0")
60 if output, err := cmd.CombinedOutput(); err != nil {
61 t.Fatalf("cross-build %s fixture: %v\n%s", test.arch, err, output)
62 }
63
64 opts := resourceOptions{
65 executable: executable,
66 icon: icon,
67 numericVersion: "1.17.13",
68 fileDescription: "Reasonix Launcher",
69 internalName: "reasonix-launcher",
70 originalName: "reasonix-launcher.exe",
71 }
72 if err := stampExecutable(opts); err != nil {
73 t.Fatal(err)
74 }
75
76 peFile, err := pe.Open(executable)
77 if err != nil {
78 t.Fatal(err)
79 }
80 if peFile.Machine != test.machine {
81 t.Errorf("PE machine = %#x, want %#x", peFile.Machine, test.machine)
82 }
83 peFile.Close()
84
85 data, err := os.ReadFile(executable)
86 if err != nil {
87 t.Fatal(err)
88 }
89 rs, err := winres.LoadFromEXE(bytes.NewReader(data))
90 if err != nil {
91 t.Fatal(err)
92 }
93 if _, err := rs.GetIcon(winres.ID(1)); err != nil {
94 t.Fatalf("embedded icon: %v", err)
95 }
96 info, err := version.FromBytes(rs.Get(winres.RT_VERSION, winres.ID(1), version.LangDefault))
97 if err != nil {
98 t.Fatal(err)
99 }
100 if got := (*info.Table()[version.LangDefault])[version.OriginalFilename]; got != "reasonix-launcher.exe" {
101 t.Errorf("OriginalFilename = %q", got)
102 }
103 manifest := rs.Get(winres.RT_MANIFEST, winres.ID(1), winres.LCIDDefault)
104 if !bytes.Contains(manifest, []byte(`dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,system`)) {
105 t.Errorf("per-monitor-v2 manifest setting is missing: %s", manifest)
106 }
107 })
108 }
109 }
110
111 func TestReplaceFilePreservesMode(t *testing.T) {
112 if runtime.GOOS == "windows" {
113 t.Skip("Windows does not expose Unix executable mode bits")
114 }
115 path := filepath.Join(t.TempDir(), "program.exe")
116 if err := os.WriteFile(path, []byte("old"), 0o751); err != nil {
117 t.Fatal(err)
118 }
119 if err := replaceFile(path, []byte("new"), 0o751); err != nil {
120 t.Fatal(err)
121 }
122 stat, err := os.Stat(path)
123 if err != nil {
124 t.Fatal(err)
125 }
126 if got := stat.Mode().Perm(); got != 0o751 {
127 t.Fatalf("mode = %#o, want 0751", got)
128 }
129 }
130
130 lines GO