File size: 3,984 Bytes
d456972
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//go:build linux || darwin || freebsd

package motionbricks

import (
	"math"
	"os"
	"testing"
	"unsafe"

	"github.com/ebitengine/purego"
)

const (
	statusOK      = uint32(0)
	deviceAuto    = uint32(0)
	deviceVulkan  = uint32(2)
	abiVersion    = uint32(1)
	errorCapacity = uint64(256)
)

// TestOpaqueABIRoundTrip intentionally mirrors no C struct. Every object is an
// opaque uintptr and every record field is accessed through a function.
func TestOpaqueABIRoundTrip(t *testing.T) {
	library := os.Getenv("MOTIONBRICKS_LIB")
	if library == "" {
		t.Skip("set MOTIONBRICKS_LIB to the motion-bricks.cpp shared library")
	}

	handle, err := purego.Dlopen(library, purego.RTLD_NOW|purego.RTLD_LOCAL)
	if err != nil {
		t.Fatalf("dlopen %s: %v", library, err)
	}
	defer purego.Dlclose(handle)

	var version func() uint32
	var optionsCreate func(unsafe.Pointer, unsafe.Pointer, uint64) uint32
	var optionsFree func(uintptr)
	var optionsSetDevice func(uintptr, uint32, unsafe.Pointer, uint64) uint32
	var optionsGetDevice func(uintptr, unsafe.Pointer, unsafe.Pointer, uint64) uint32
	var commandCreate func(unsafe.Pointer, unsafe.Pointer, uint64) uint32
	var commandFree func(uintptr)
	var commandSetMovement func(uintptr, float32, float32, float32, unsafe.Pointer, uint64) uint32
	var commandGetMovement func(uintptr, unsafe.Pointer, unsafe.Pointer, unsafe.Pointer, unsafe.Pointer, uint64) uint32

	purego.RegisterLibFunc(&version, handle, "mb_abi_version")
	purego.RegisterLibFunc(&optionsCreate, handle, "mb_runtime_options_create")
	purego.RegisterLibFunc(&optionsFree, handle, "mb_runtime_options_free")
	purego.RegisterLibFunc(&optionsSetDevice, handle, "mb_runtime_options_set_device")
	purego.RegisterLibFunc(&optionsGetDevice, handle, "mb_runtime_options_get_device")
	purego.RegisterLibFunc(&commandCreate, handle, "mb_command_create")
	purego.RegisterLibFunc(&commandFree, handle, "mb_command_free")
	purego.RegisterLibFunc(&commandSetMovement, handle, "mb_command_set_movement_direction")
	purego.RegisterLibFunc(&commandGetMovement, handle, "mb_command_get_movement_direction")

	if got := version(); got != abiVersion {
		t.Fatalf("ABI version = %d, want %d", got, abiVersion)
	}

	errorBuffer := make([]byte, errorCapacity)
	errorPointer := unsafe.Pointer(&errorBuffer[0])
	var options uintptr
	if status := optionsCreate(unsafe.Pointer(&options), errorPointer, errorCapacity); status != statusOK {
		t.Fatalf("options create status %d: %q", status, errorBuffer)
	}
	if options == 0 {
		t.Fatal("options create returned a null handle")
	}
	defer optionsFree(options)

	var device uint32 = math.MaxUint32
	if status := optionsGetDevice(options, unsafe.Pointer(&device), errorPointer, errorCapacity); status != statusOK {
		t.Fatalf("get default device status %d", status)
	}
	if device != deviceAuto {
		t.Fatalf("default device = %d, want auto", device)
	}
	if status := optionsSetDevice(options, deviceVulkan, errorPointer, errorCapacity); status != statusOK {
		t.Fatalf("set Vulkan status %d", status)
	}
	if status := optionsGetDevice(options, unsafe.Pointer(&device), errorPointer, errorCapacity); status != statusOK || device != deviceVulkan {
		t.Fatalf("Vulkan round trip status=%d device=%d", status, device)
	}

	var command uintptr
	if status := commandCreate(unsafe.Pointer(&command), errorPointer, errorCapacity); status != statusOK {
		t.Fatalf("command create status %d", status)
	}
	if command == 0 {
		t.Fatal("command create returned a null handle")
	}
	defer commandFree(command)

	if status := commandSetMovement(command, 1.0, 0.0, -0.25, errorPointer, errorCapacity); status != statusOK {
		t.Fatalf("set movement status %d", status)
	}
	var x, y, z float32
	if status := commandGetMovement(command, unsafe.Pointer(&x), unsafe.Pointer(&y), unsafe.Pointer(&z), errorPointer, errorCapacity); status != statusOK {
		t.Fatalf("get movement status %d", status)
	}
	if x != 1.0 || y != 0.0 || z != -0.25 {
		t.Fatalf("movement = (%g, %g, %g)", x, y, z)
	}
}