detached 15e9011

hotpath_test.go RawHistoryFolder

1package server
2
3import (
4	"context"
5	"os"
6	"testing"
7	"unsafe"
8
9	"github.com/bnema/purego"
10	"golang.org/x/sys/unix"
11)
12
13// hotResource creates a real wl_client/resource on the display thread without
14// needing a separate client event loop.
15func hotResource(t testing.TB, d *Display, iface *Interface, h Handler) *Resource {
16	t.Helper()
17	lib, err := purego.Dlopen("libwayland-server.so.0", purego.RTLD_NOW)
18	if err != nil {
19		t.Fatal(err)
20	}
21	create, err := purego.Dlsym(lib, "wl_client_create")
22	if err != nil {
23		t.Fatal(err)
24	}
25	fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
26	if err != nil {
27		t.Fatal(err)
28	}
29	t.Cleanup(func() { _ = unix.Close(fds[1]) })
30	var r *Resource
31	if !d.Do(func() {
32		client, _, _ := purego.SyscallN(create, d.c, uintptr(fds[0]))
33		if client == 0 {
34			_ = unix.Close(fds[0])
35			return
36		}
37		r, err = (Client{client}).CreateResource(iface, 1, 2, h)
38	}) {
39		t.Fatal("display stopped")
40	}
41	if err != nil || r == nil {
42		t.Fatalf("create resource: %v", err)
43	}
44	return r
45}
46
47func hotDisplay(t testing.TB, iface *Interface, h Handler) (*Display, *Resource) {
48	t.Helper()
49	if err := NewInterfaces(iface); err != nil {
50		t.Fatal(err)
51	}
52	d, err := NewDisplay()
53	if err != nil {
54		t.Fatal(err)
55	}
56	ctx, cancel := context.WithCancel(context.Background())
57	done := make(chan error, 1)
58	go func() { done <- d.Run(ctx) }()
59	t.Cleanup(func() {
60		cancel()
61		if err := <-done; err != nil {
62			t.Error(err)
63		}
64	})
65	return d, hotResource(t, d, iface, h)
66}
67
68func TestServerAllocatedResourceID(t *testing.T) {
69	iface := &Interface{Name: "server_allocated_id_test", Version: 1}
70	d, existing := hotDisplay(t, iface, nil)
71	if !d.Do(func() {
72		r, err := (Client{existing.client}).CreateResource(iface, 1, 0, nil)
73		if err != nil {
74			t.Errorf("create server-side resource: %v", err)
75			return
76		}
77		if r.ID() == 0 || r.ID() != wlResourceGetID(r.c) {
78			t.Errorf("resource ID = %d, libwayland ID = %d", r.ID(), wlResourceGetID(r.c))
79		}
80	}) {
81		t.Fatal("display stopped")
82	}
83}
84
85func requestMessage(iface *Interface) unsafe.Pointer {
86	return *(*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(&tables.mem[0]), iface.c-tables.base+16))
87}
88
89var hotSink uint32
90
91func hotHandler(_ *Resource, _ uint32, args []Arg) { hotSink = args[0].Uint() }
92
93func TestHotPathAllocs(t *testing.T) {
94	iface := &Interface{Name: "hot_path_test", Version: 1, Requests: []Message{{Name: "request", Signature: "uuu"}}, Events: []Message{{Name: "event", Signature: "uuu"}}}
95	d, r := hotDisplay(t, iface, hotHandler)
96	var post, dispatchAllocs float64
97	if !d.Do(func() {
98		post = testing.AllocsPerRun(100, func() { r.PostEvent(0, Uint(1), Uint(2), Uint(3)) })
99		a := [3]Arg{Uint(1), Uint(2), Uint(3)}
100		dispatchAllocs = testing.AllocsPerRun(100, func() { dispatch(0, r.c, 0, requestMessage(iface), unsafe.Pointer(&a[0])) })
101	}) {
102		t.Fatal("display stopped")
103	}
104	if post != 0 || dispatchAllocs != 0 {
105		t.Fatalf("allocs: PostEvent=%g dispatch=%g, want zero", post, dispatchAllocs)
106	}
107}
108
109func BenchmarkDispatch(b *testing.B) {
110	iface := &Interface{Name: "hot_dispatch_bench", Version: 1, Requests: []Message{{Name: "request", Signature: "uuu"}}}
111	d, r := hotDisplay(b, iface, hotHandler)
112	a := [3]Arg{Uint(1), Uint(2), Uint(3)}
113	if !d.Do(func() {
114		b.ReportAllocs()
115		b.ResetTimer()
116		for i := 0; i < b.N; i++ {
117			dispatch(0, r.c, 0, requestMessage(iface), unsafe.Pointer(&a[0]))
118		}
119		b.StopTimer()
120	}) {
121		b.Fatal("display stopped")
122	}
123}
124func BenchmarkPostEvent(b *testing.B) {
125	iface := &Interface{Name: "hot_event_bench", Version: 1, Events: []Message{{Name: "event", Signature: "uuu"}}}
126	d, r := hotDisplay(b, iface, nil)
127	if !d.Do(func() {
128		b.ReportAllocs()
129		b.ResetTimer()
130		for i := 0; i < b.N; i++ {
131			r.PostEvent(0, Uint(1), Uint(2), Uint(3))
132		}
133		b.StopTimer()
134	}) {
135		b.Fatal("display stopped")
136	}
137}
138
139func TestClientCredentials(t *testing.T) {
140	iface := &Interface{Name: "cred_test", Version: 1, Requests: []Message{{Name: "request", Signature: "uuu"}}, Events: []Message{{Name: "event", Signature: "uuu"}}}
141	d, r := hotDisplay(t, iface, hotHandler)
142	var (
143		got, zero Credentials
144		err, zerr error
145		allocs    float64
146		pid       int
147	)
148	c := r.Client()
149	if !d.Do(func() {
150		got, err = c.Credentials()
151		zero, zerr = Client{}.Credentials()
152		pid = c.PID()
153		allocs = testing.AllocsPerRun(100, func() { _, _ = c.Credentials() })
154	}) {
155		t.Fatal("display stopped")
156	}
157	if err != nil {
158		t.Fatal(err)
159	}
160	if got.PID != os.Getpid() || got.UID != uint32(os.Geteuid()) || got.GID != uint32(os.Getegid()) || pid != got.PID {
161		t.Fatalf("credentials %+v pid %d, want pid=%d uid=%d gid=%d", got, pid, os.Getpid(), os.Geteuid(), os.Getegid())
162	}
163	if zerr == nil || zero != (Credentials{}) || (Client{}).PID() != 0 {
164		t.Fatalf("invalid client: %+v, %v", zero, zerr)
165	}
166	if allocs != 0 {
167		t.Fatalf("Credentials allocs = %g, want 0", allocs)
168	}
169}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 14

↑↓ moveenter openesc close