detached 908d8c0

utf8_into_test.go RawHistoryFolder

1package xkbcommon
2
3import (
4	"bytes"
5	"errors"
6	"io"
7	"testing"
8
9	"github.com/bnema/purego-xkbcommon/raw"
10)
11
12func TestStateUTF8IntoBoundsAndLifetime(t *testing.T) {
13	c := live(t)
14	s := state(t, rules(t, c, "de"))
15	var scratch [MaxUTF8Buffer]byte
16	n, err := s.UTF8Into(WaylandKeycode(16), scratch[:])
17	if err != nil || !bytes.Equal(scratch[:n], []byte{'q'}) {
18		t.Fatalf("ASCII length=%d err=%v", n, err)
19	}
20	mod, err := rules(t, c, "de").ModIndex("Mod5")
21	if err != nil {
22		t.Fatal(err)
23	}
24	if _, err := s.UpdateMask(1<<mod, 0, 0, 0, 0, 0); err != nil {
25		t.Fatal(err)
26	}
27	n, err = s.UTF8Into(WaylandKeycode(18), scratch[:])
28	if err != nil || !bytes.Equal(scratch[:n], []byte{0xe2, 0x82, 0xac}) {
29		t.Fatalf("multibyte length=%d err=%v", n, err)
30	}
31	short := []byte{0xff, 0xff, 0xff}
32	n, err = s.UTF8Into(WaylandKeycode(18), short)
33	if !errors.Is(err, io.ErrShortBuffer) || n != 3 || !bytes.Equal(short, make([]byte, len(short))) {
34		t.Fatalf("short: n=%d err=%v", n, err)
35	}
36	for _, length := range []int{0, MaxUTF8Buffer + 1} {
37		buf := bytes.Repeat([]byte{0xff}, length)
38		if _, err := s.UTF8Into(WaylandKeycode(18), buf); !errors.Is(err, ErrUTF8Buffer) {
39			t.Fatal(err)
40		}
41		if !bytes.Equal(buf, make([]byte, length)) {
42			t.Fatal("invalid buffer not cleared")
43		}
44	}
45	s.Close()
46	for i := range scratch {
47		scratch[i] = 0xff
48	}
49	if _, err := s.UTF8Into(WaylandKeycode(18), scratch[:]); !errors.Is(err, ErrClosed) {
50		t.Fatal(err)
51	}
52	if !bytes.Equal(scratch[:], make([]byte, len(scratch))) {
53		t.Fatal("closed handle left old bytes")
54	}
55}
56
57func TestComposeUTF8Into(t *testing.T) {
58	c := live(t)
59	table, err := c.NewComposeTable("en_US.UTF-8")
60	if err != nil {
61		t.Fatal(err)
62	}
63	defer table.Close()
64	compose, err := table.NewState()
65	if err != nil {
66		t.Fatal(err)
67	}
68	defer compose.Close()
69	for _, sym := range []uint32{raw.XKB_KEY_dead_circumflex, raw.XKB_KEY_e} {
70		if _, err := compose.Feed(sym); err != nil {
71			t.Fatal(err)
72		}
73	}
74	var buf [MaxUTF8Buffer]byte
75	if n, err := compose.UTF8Into(buf[:]); err != nil || !bytes.Equal(buf[:n], []byte{0xc3, 0xaa}) {
76		t.Fatalf("compose length=%d err=%v", n, err)
77	}
78	if n, err := compose.UTF8Into(buf[:2]); !errors.Is(err, io.ErrShortBuffer) || n != 2 {
79		t.Fatalf("compose short length=%d err=%v", n, err)
80	}
81	if buf[0] != 0 || buf[1] != 0 {
82		t.Fatal("short compose retained bytes")
83	}
84	if err := compose.Reset(); err != nil {
85		t.Fatal(err)
86	}
87	if n, err := compose.UTF8Into(buf[:]); err != nil || n != 0 {
88		t.Fatalf("reset length=%d err=%v", n, err)
89	}
90}
91
92func TestUTF8IntoBoundedAllocations(t *testing.T) {
93	s := state(t, rules(t, live(t), "us"))
94	var buf [MaxUTF8Buffer]byte
95	if n, err := s.UTF8Into(WaylandKeycode(16), buf[:]); err != nil || n != 1 {
96		t.Fatal(n, err)
97	}
98	allocs := testing.AllocsPerRun(100, func() {
99		if _, err := s.UTF8Into(WaylandKeycode(16), buf[:]); err != nil {
100			t.Fatal(err)
101		}
102		clear(buf[:])
103	})
104	// Purego's native dispatch may allocate fixed ABI scratch; the wrapper
105	// must not allocate result-sized buffers or string payloads.
106	// Current purego dispatch plus scoped pinning costs six fixed allocations.
107	if allocs > 6 {
108		t.Fatalf("UTF8Into allocations=%v", allocs)
109	}
110}
111
112func TestUTF8IntoNilAndClosedReceiversClearScratch(t *testing.T) {
113	var nilState *State
114	var nilCompose *ComposeState
115	buf := []byte{0xff, 0xff, 0xff}
116	if n, err := nilState.UTF8Into(WaylandKeycode(16), buf); n != 0 || !errors.Is(err, ErrClosed) {
117		t.Fatal(n, err)
118	}
119	if !bytes.Equal(buf, []byte{0, 0, 0}) {
120		t.Fatal("nil state retained bytes")
121	}
122	for i := range buf {
123		buf[i] = 0xff
124	}
125	if n, err := nilCompose.UTF8Into(buf); n != 0 || !errors.Is(err, ErrClosed) {
126		t.Fatal(n, err)
127	}
128	if !bytes.Equal(buf, []byte{0, 0, 0}) {
129		t.Fatal("nil compose retained bytes")
130	}
131	c := live(t)
132	table, err := c.NewComposeTable("en_US.UTF-8")
133	if err != nil {
134		t.Fatal(err)
135	}
136	defer table.Close()
137	compose, err := table.NewState()
138	if err != nil {
139		t.Fatal(err)
140	}
141	compose.Close()
142	for i := range buf {
143		buf[i] = 0xff
144	}
145	if n, err := compose.UTF8Into(buf); n != 0 || !errors.Is(err, ErrClosed) {
146		t.Fatal(n, err)
147	}
148	if !bytes.Equal(buf, []byte{0, 0, 0}) {
149		t.Fatal("closed compose retained bytes")
150	}
151}

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 2

↑↓ moveenter openesc close