1package gtk
2
3// #include <gtk/gtk.h>
4// #include "gtk.go.h"
5import "C"
6import (
7	"errors"
8	"os"
9	"unsafe"
10
11	"github.com/gotk3/gotk3/gdk"
12
13	"github.com/gotk3/gotk3/glib"
14)
15
16// TestFindLabel is a wrapper around gtk_test_find_label().
17// This function will search widget and all its descendants for a GtkLabel widget with a text string matching label_pattern.
18// The labelPattern may contain asterisks “*” and question marks “?” as placeholders, g_pattern_match() is used for the matching.
19func TestFindLabel(widget IWidget, labelPattern string) (IWidget, error) {
20	cstr := C.CString(labelPattern)
21	defer C.free(unsafe.Pointer(cstr))
22	c := C.gtk_test_find_label(widget.toWidget(), (*C.gchar)(cstr))
23	if c == nil {
24		return nil, errors.New("no label with pattern '" + labelPattern + "' found")
25	}
26	obj, err := castWidget(c)
27	if err != nil {
28		return nil, err
29	}
30	return obj, nil
31}
32
33// TestFindSibling is a wrapper around gtk_test_find_sibling().
34// This function will search siblings of base_widget and siblings of its ancestors for all widgets matching widgetType.
35// Of the matching widgets, the one that is geometrically closest to base_widget will be returned.
36func TestFindSibling(baseWidget IWidget, widgetType glib.Type) (IWidget, error) {
37	c := C.gtk_test_find_sibling(baseWidget.toWidget(), C.GType(widgetType))
38	if c == nil {
39		return nil, errors.New("no widget of type '" + widgetType.Name() + "' found")
40	}
41	obj, err := castWidget(c)
42	if err != nil {
43		return nil, err
44	}
45	return obj, nil
46}
47
48// TestFindWidget is a wrapper around gtk_test_find_widget().
49// This function will search the descendants of widget for a widget of type widget_type that has a label matching labelPattern next to it.
50// This is most useful for automated GUI testing, e.g. to find the “OK” button in a dialog and synthesize clicks on it.
51// However see TestFindLabel(), TestFindSibling() and TestWidgetClick() (and their GTK documentation)
52// for possible caveats involving the search of such widgets and synthesizing widget events.
53func TestFindWidget(widget IWidget, labelPattern string, widgetType glib.Type) (IWidget, error) {
54	cstr := C.CString(labelPattern)
55	defer C.free(unsafe.Pointer(cstr))
56	c := C.gtk_test_find_widget(widget.toWidget(), (*C.gchar)(cstr), C.GType(widgetType))
57	if c == nil {
58		return nil, errors.New("no widget with label pattern '" + labelPattern + "' and type '" + widgetType.Name() + "' found")
59	}
60	obj, err := castWidget(c)
61	if err != nil {
62		return nil, err
63	}
64	return obj, nil
65}
66
67/*
68TestInit is a wrapper around gtk_test_init().
69This function is used to initialize a GTK+ test program.
70It will in turn call g_test_init() and gtk_init() to properly initialize the testing framework and graphical toolkit.
71It’ll also set the program’s locale to “C” and prevent loading of rc files and Gtk+ modules.
72This is done to make tets program environments as deterministic as possible.
73
74Like gtk_init() and g_test_init(), any known arguments will be processed and stripped from argc and argv.
75*/
76func TestInit(args *[]string) {
77	if args != nil {
78		argc := C.int(len(*args))
79		argv := C.make_strings(argc)
80		defer C.destroy_strings(argv)
81
82		for i, arg := range *args {
83			cstr := C.CString(arg)
84			C.set_string(argv, C.int(i), (*C.gchar)(cstr))
85		}
86
87		C._gtk_test_init((*C.int)(unsafe.Pointer(&argc)),
88			(***C.char)(unsafe.Pointer(&argv)))
89
90		unhandled := make([]string, argc)
91		for i := 0; i < int(argc); i++ {
92			cstr := C.get_string(argv, C.int(i))
93			unhandled[i] = goString(cstr)
94			C.free(unsafe.Pointer(cstr))
95		}
96		*args = unhandled
97	} else {
98		// gtk_test_init does not take nil, we have to use an empty argument list
99		// (only containing the first arg, which is the executable name)
100		argc := C.int(1)
101		argv := C.make_strings(argc)
102		defer C.destroy_strings(argv)
103
104		// Add first argument
105		cstr := C.CString(os.Args[0])
106		C.set_string(argv, C.int(0), (*C.gchar)(cstr))
107
108		C._gtk_test_init((*C.int)(unsafe.Pointer(&argc)),
109			(***C.char)(unsafe.Pointer(&argv)))
110	}
111}
112
113// TestListAllTypes is a wrapper around gtk_test_list_all_types().
114// Return the type ids that have been registered after calling TestRegisterAllTypes().
115func TestListAllTypes() []glib.Type {
116	var types *C.GType
117	var clen C.guint
118
119	types = C.gtk_test_list_all_types(&clen)
120	defer C.free(unsafe.Pointer(types))
121
122	length := uint(clen)
123
124	typeReturn := make([]glib.Type, length)
125	for i := uint(0); i < length; i++ {
126		current := (*C.GType)(pointerAtOffset(unsafe.Pointer(types), unsafe.Sizeof(*types), i))
127		typeReturn[i] = glib.Type(*current)
128	}
129	return typeReturn
130}
131
132// pointerAtOffset adjusts `arrayPointer` (pointer to the first element of a C array)
133// to point at the offset `i`,
134// to be able to read the value there without having to go through cgo.
135func pointerAtOffset(arrayPointer unsafe.Pointer, elementSize uintptr, offset uint) unsafe.Pointer {
136	return unsafe.Pointer(uintptr(arrayPointer) + elementSize*uintptr(offset))
137}
138
139// TestRegisterAllTypes is a wrapper around gtk_test_register_all_types().
140// Force registration of all core Gtk+ and Gdk object types.
141// This allowes to refer to any of those object types via g_type_from_name() after calling this function.
142func TestRegisterAllTypes() {
143	C.gtk_test_register_all_types()
144}
145
146// TestWidgetSendKey is a wrapper around gtk_test_widget_send_key()
147//
148// This function will generate keyboard press and release events
149// in the middle of the first GdkWindow found that belongs to widget.
150// For windowless widgets like GtkButton (which returns FALSE from gtk_widget_get_has_window()),
151// this will often be an input-only event window.
152// For other widgets, this is usually widget->window.
153//
154// widget: Widget to generate a key press and release on.
155// keyval: A Gdk keyboard value.
156// modifiers: Keyboard modifiers the event is setup with.
157//
158// returns: whether all actions neccessary for the key event simulation were carried out successfully.
159func TestWidgetSendKey(widget IWidget, keyval uint, modifiers gdk.ModifierType) bool {
160	return gobool(C.gtk_test_widget_send_key(widget.toWidget(), C.guint(keyval), C.GdkModifierType(modifiers)))
161}
162