main.go raw
1 package main
2
3 import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "runtime"
8 "unsafe"
9
10 "github.com/ebitengine/purego"
11 )
12
13 func main() {
14 if len(os.Args) < 2 {
15 fmt.Fprintf(os.Stderr, "usage: %s <path-to-goenv_version.so>\n", os.Args[0])
16 os.Exit(1)
17 }
18 soPath := os.Args[1]
19 if !filepath.IsAbs(soPath) {
20 wd, _ := os.Getwd()
21 soPath = filepath.Join(wd, soPath)
22 }
23
24 lib, err := openLib(soPath)
25 if err != nil {
26 fmt.Fprintf(os.Stderr, "dlopen %s: %v\n", soPath, err)
27 os.Exit(1)
28 }
29
30 var versionParse func(uintptr, int32, uintptr, uintptr, uintptr) int32
31 purego.RegisterLibFunc(&versionParse, lib, "moxie_version_parse")
32
33 var versionCompare func(uintptr, int32, uintptr, int32) int32
34 purego.RegisterLibFunc(&versionCompare, lib, "moxie_version_compare")
35
36 // Test Parse: "mx1.2.19"
37 testParse(versionParse, "mx1.2.19", 1, 2, 19)
38 testParse(versionParse, "go1.24.0", 1, 24, 0)
39 testParse(versionParse, "mx1.3", 1, 3, 0)
40 testParse(versionParse, "devel mx1.4.5 abc", 1, 4, 5)
41
42 // Test Parse: invalid inputs
43 testParseFail(versionParse, "v1.2.3")
44 testParseFail(versionParse, "x")
45 testParseFail(versionParse, "mx")
46
47 // Test Compare
48 testCompare(versionCompare, "mx1.2.19", "mx1.2.19", 0)
49 testCompare(versionCompare, "mx1.2.18", "mx1.2.19", -1)
50 testCompare(versionCompare, "mx1.3.0", "mx1.2.19", 1)
51 testCompare(versionCompare, "mx2.0.0", "mx1.9.9", 1)
52 testCompare(versionCompare, "go1.24.0", "mx1.24.0", 0)
53
54 fmt.Println("\nAll goenv version roundtrip tests passed.")
55 }
56
57 func testParse(fn func(uintptr, int32, uintptr, uintptr, uintptr) int32, version string, wantMaj, wantMin, wantPat int32) {
58 data := []byte(version)
59 var maj, min, pat int32
60 rc := fn(uintptr(unsafe.Pointer(&data[0])), int32(len(data)),
61 uintptr(unsafe.Pointer(&maj)), uintptr(unsafe.Pointer(&min)), uintptr(unsafe.Pointer(&pat)))
62 if rc != 0 {
63 fmt.Fprintf(os.Stderr, "FAIL: parse(%q) returned %d, want 0\n", version, rc)
64 os.Exit(1)
65 }
66 if maj != wantMaj || min != wantMin || pat != wantPat {
67 fmt.Fprintf(os.Stderr, "FAIL: parse(%q) = (%d,%d,%d), want (%d,%d,%d)\n",
68 version, maj, min, pat, wantMaj, wantMin, wantPat)
69 os.Exit(1)
70 }
71 fmt.Printf("PASS: parse(%q) = (%d,%d,%d)\n", version, maj, min, pat)
72 }
73
74 func testParseFail(fn func(uintptr, int32, uintptr, uintptr, uintptr) int32, version string) {
75 data := []byte(version)
76 var maj, min, pat int32
77 rc := fn(uintptr(unsafe.Pointer(&data[0])), int32(len(data)),
78 uintptr(unsafe.Pointer(&maj)), uintptr(unsafe.Pointer(&min)), uintptr(unsafe.Pointer(&pat)))
79 if rc == 0 {
80 fmt.Fprintf(os.Stderr, "FAIL: parse(%q) succeeded, want failure\n", version)
81 os.Exit(1)
82 }
83 fmt.Printf("PASS: parse(%q) correctly rejected\n", version)
84 }
85
86 func testCompare(fn func(uintptr, int32, uintptr, int32) int32, a, b string, want int32) {
87 da := []byte(a)
88 db := []byte(b)
89 got := fn(uintptr(unsafe.Pointer(&da[0])), int32(len(da)),
90 uintptr(unsafe.Pointer(&db[0])), int32(len(db)))
91 if got != want {
92 fmt.Fprintf(os.Stderr, "FAIL: compare(%q, %q) = %d, want %d\n", a, b, got, want)
93 os.Exit(1)
94 }
95 fmt.Printf("PASS: compare(%q, %q) = %d\n", a, b, got)
96 }
97
98 func openLib(path string) (uintptr, error) {
99 switch runtime.GOOS {
100 case "linux", "darwin":
101 return purego.Dlopen(path, purego.RTLD_NOW|purego.RTLD_GLOBAL)
102 default:
103 return 0, fmt.Errorf("unsupported OS: %s", runtime.GOOS)
104 }
105 }
106