1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
3
4package purego_test
5
6import (
7 "fmt"
8 "runtime"
9 "testing"
10 "unsafe"
11
12 "github.com/ebitengine/purego"
13 "github.com/ebitengine/purego/internal/load"
14)
15
16func getSystemLibrary() (string, error) {
17 switch runtime.GOOS {
18 case "darwin":
19 return "/usr/lib/libSystem.B.dylib", nil
20 case "linux":
21 return "libc.so.6", nil
22 case "freebsd":
23 return "libc.so.7", nil
24 case "windows":
25 return "ucrtbase.dll", nil
26 default:
27 return "", fmt.Errorf("GOOS=%s is not supported", runtime.GOOS)
28 }
29}
30
31func TestRegisterFunc(t *testing.T) {
32 library, err := getSystemLibrary()
33 if err != nil {
34 t.Fatalf("couldn't get system library: %s", err)
35 }
36 libc, err := load.OpenLibrary(library)
37 if err != nil {
38 t.Fatalf("failed to dlopen: %s", err)
39 }
40 var puts func(string)
41 purego.RegisterLibFunc(&puts, libc, "puts")
42 puts("Calling C from from Go without Cgo!")
43}
44
45func Test_qsort(t *testing.T) {
46 if runtime.GOARCH != "arm64" && runtime.GOARCH != "amd64" {
47 t.Skip("Platform doesn't support Floats")
48 return
49 }
50 library, err := getSystemLibrary()
51 if err != nil {
52 t.Fatalf("couldn't get system library: %s", err)
53 }
54 libc, err := load.OpenLibrary(library)
55 if err != nil {
56 t.Fatalf("failed to dlopen: %s", err)
57 }
58
59 data := []int{88, 56, 100, 2, 25}
60 sorted := []int{2, 25, 56, 88, 100}
61 compare := func(_ purego.CDecl, a, b *int) int {
62 return *a - *b
63 }
64 var qsort func(data []int, nitms uintptr, size uintptr, compar func(_ purego.CDecl, a, b *int) int)
65 purego.RegisterLibFunc(&qsort, libc, "qsort")
66 qsort(data, uintptr(len(data)), unsafe.Sizeof(int(0)), compare)
67 for i := range data {
68 if data[i] != sorted[i] {
69 t.Errorf("got %d wanted %d at %d", data[i], sorted[i], i)
70 }
71 }
72}
73
74func TestRegisterFunc_Floats(t *testing.T) {
75 if runtime.GOARCH != "arm64" && runtime.GOARCH != "amd64" {
76 t.Skip("Platform doesn't support Floats")
77 return
78 }
79 library, err := getSystemLibrary()
80 if err != nil {
81 t.Fatalf("couldn't get system library: %s", err)
82 }
83 libc, err := load.OpenLibrary(library)
84 if err != nil {
85 t.Fatalf("failed to dlopen: %s", err)
86 }
87 {
88 var strtof func(arg string) float32
89 purego.RegisterLibFunc(&strtof, libc, "strtof")
90 const (
91 arg = "2"
92 )
93 got := strtof(arg)
94 expected := float32(2)
95 if got != expected {
96 t.Errorf("strtof failed. got %f but wanted %f", got, expected)
97 }
98 }
99 {
100 var strtod func(arg string, ptr **byte) float64
101 purego.RegisterLibFunc(&strtod, libc, "strtod")
102 const (
103 arg = "1"
104 )
105 got := strtod(arg, nil)
106 expected := float64(1)
107 if got != expected {
108 t.Errorf("strtod failed. got %f but wanted %f", got, expected)
109 }
110 }
111}
112
113func TestRegisterLibFunc_Bool(t *testing.T) {
114 if runtime.GOARCH != "arm64" && runtime.GOARCH != "amd64" {
115 t.Skip("Platform doesn't support callbacks")
116 return
117 }
118 // this callback recreates the state where the return register
119 // contains other information but the least significant byte is false
120 cbFalse := purego.NewCallback(func() uintptr {
121 x := uint64(0x7F5948AE9A00)
122 return uintptr(x)
123 })
124 var runFalse func() bool
125 purego.RegisterFunc(&runFalse, cbFalse)
126 expected := false
127 if got := runFalse(); got != expected {
128 t.Errorf("runFalse failed. got %t but wanted %t", got, expected)
129 }
130}