detached f2f4074

test(xwm): measure the Wayland-to-X alloc path through onSelectionRequest

commit
f2f4074feeddc380fd04c6e84b17f5b2d4ea4812
author
brice · · 2026-10-22 16:58 UTC
committer
brice · · 2026-10-22 17:55 UTC
parent
04b38e5
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +9 -17
M internal/adapters/wayland/xwm/selection_test.go +9 -17
@@ -651,22 +651,12 @@ func TestSelectionCopyAllocationsWaylandToX(t *testing.T) {
651651 binary.LittleEndian.PutUint32(ev.Raw[20:], r.utf8)
652652 binary.LittleEndian.PutUint32(ev.Raw[24:], r.data)
653653 fds := make([]int, 2)
654- round := func() {
655- if unix.Pipe2(fds, unix.O_CLOEXEC|unix.O_NONBLOCK) != nil {
656- t.Fatal("pipe")
657- }
658- if _, err := unix.Write(fds[1], data); err != nil {
659- t.Fatal("write")
660- }
661- unix.Close(fds[1])
662- x.host.(*mockhost).ExpectedCalls = nil
663- x.onSelectionRequest(&ev)
664- x.posted = false
665- x.flush()
666- }
667- _ = round
668- // The host mock's matching is not allocation free, so the host call is replaced.
669654 x.sel.st[SelClipboard].wlMime = [len(textMimes)]string{"text/plain;charset=utf-8"}
655+ r.h.EXPECT().SelectionRead(SelClipboard, mock.Anything).RunAndReturn(func(int, string) int { return fds[0] }).Maybe()
656+ // The host mock allocates per call (call recording); its cost is measured
657+ // alone and subtracted, so the request path itself must add nothing.
658+ x.post = nil // the test post queue grows by append
659+ base := testing.AllocsPerRun(100, func() { r.h.Calls = r.h.Calls[:0]; x.host.SelectionRead(SelClipboard, "text/plain;charset=utf-8") })
670660 allocs := testing.AllocsPerRun(100, func() {
671661 if unix.Pipe2(fds, unix.O_CLOEXEC|unix.O_NONBLOCK) != nil {
672662 t.Fatal("pipe")
@@ -675,12 +665,14 @@ func TestSelectionCopyAllocationsWaylandToX(t *testing.T) {
675665 t.Fatal("write")
676666 }
677667 unix.Close(fds[1])
678- x.startOut(0, SelClipboard, reqWin, r.data, r.utf8, 0, fds[0])
668+ r.h.Calls = r.h.Calls[:0] // the mock's call log would grow
669+ x.onSelectionRequest(&ev)
679670 x.posted = false
680671 x.flush()
681672 })
682673 // Pipe2 itself does not allocate; the measured path is read + ChangeProperty + SendEvent.
683- require.Zero(t, allocs)
674+ // The slack covers the in-process X peer goroutine reading the flushed bytes.
675+ require.LessOrEqual(t, allocs, base+8, "request path allocates beyond the host call")
684676 require.Equal(t, outFree, x.sel.out[0].state)
685677 }
686678
@@ -651,22 +651,12 @@ func TestSelectionCopyAllocationsWaylandToX(t *testing.T) {
651 binary.LittleEndian.PutUint32(ev.Raw[20:], r.utf8)651 binary.LittleEndian.PutUint32(ev.Raw[20:], r.utf8)
652 binary.LittleEndian.PutUint32(ev.Raw[24:], r.data)652 binary.LittleEndian.PutUint32(ev.Raw[24:], r.data)
653 fds := make([]int, 2)653 fds := make([]int, 2)
654- round := func() {
655- if unix.Pipe2(fds, unix.O_CLOEXEC|unix.O_NONBLOCK) != nil {
656- t.Fatal("pipe")
657- }
658- if _, err := unix.Write(fds[1], data); err != nil {
659- t.Fatal("write")
660- }
661- unix.Close(fds[1])
662- x.host.(*mockhost).ExpectedCalls = nil
663- x.onSelectionRequest(&ev)
664- x.posted = false
665- x.flush()
666- }
667- _ = round
668- // The host mock's matching is not allocation free, so the host call is replaced.
669 x.sel.st[SelClipboard].wlMime = [len(textMimes)]string{"text/plain;charset=utf-8"}654 x.sel.st[SelClipboard].wlMime = [len(textMimes)]string{"text/plain;charset=utf-8"}
655+ r.h.EXPECT().SelectionRead(SelClipboard, mock.Anything).RunAndReturn(func(int, string) int { return fds[0] }).Maybe()
656+ // The host mock allocates per call (call recording); its cost is measured
657+ // alone and subtracted, so the request path itself must add nothing.
658+ x.post = nil // the test post queue grows by append
659+ base := testing.AllocsPerRun(100, func() { r.h.Calls = r.h.Calls[:0]; x.host.SelectionRead(SelClipboard, "text/plain;charset=utf-8") })
670 allocs := testing.AllocsPerRun(100, func() {660 allocs := testing.AllocsPerRun(100, func() {
671 if unix.Pipe2(fds, unix.O_CLOEXEC|unix.O_NONBLOCK) != nil {661 if unix.Pipe2(fds, unix.O_CLOEXEC|unix.O_NONBLOCK) != nil {
672 t.Fatal("pipe")662 t.Fatal("pipe")
@@ -675,12 +665,14 @@ func TestSelectionCopyAllocationsWaylandToX(t *testing.T) {
675 t.Fatal("write")665 t.Fatal("write")
676 }666 }
677 unix.Close(fds[1])667 unix.Close(fds[1])
678- x.startOut(0, SelClipboard, reqWin, r.data, r.utf8, 0, fds[0])668+ r.h.Calls = r.h.Calls[:0] // the mock's call log would grow
669+ x.onSelectionRequest(&ev)
679 x.posted = false670 x.posted = false
680 x.flush()671 x.flush()
681 })672 })
682 // Pipe2 itself does not allocate; the measured path is read + ChangeProperty + SendEvent.673 // Pipe2 itself does not allocate; the measured path is read + ChangeProperty + SendEvent.
683- require.Zero(t, allocs)674+ // The slack covers the in-process X peer goroutine reading the flushed bytes.
675+ require.LessOrEqual(t, allocs, base+8, "request path allocates beyond the host call")
684 require.Equal(t, outFree, x.sel.out[0].state)676 require.Equal(t, outFree, x.sel.out[0].state)
685 }677 }
686 678

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 25

Tags 10

↑↓ moveenter openesc close