detached 1a6c6c9

protocol_test.go RawHistoryFolder

1package spec
2
3import (
4	"os"
5	"reflect"
6	"strings"
7	"testing"
8)
9
10func readTestdata(t *testing.T, name string) []byte {
11	t.Helper()
12	b, err := os.ReadFile("testdata/" + name)
13	if err != nil {
14		t.Fatal(err)
15	}
16	return b
17}
18
19func findInterface(t *testing.T, p *Protocol, name string) Interface {
20	t.Helper()
21	for _, it := range p.Interfaces {
22		if it.Name == name {
23			return it
24		}
25	}
26	t.Fatalf("interface %q not found", name)
27	return Interface{}
28}
29
30func findMessage(t *testing.T, ms []Message, name string) Message {
31	t.Helper()
32	for _, m := range ms {
33		if m.Name == name {
34			return m
35		}
36	}
37	t.Fatalf("message %q not found", name)
38	return Message{}
39}
40
41func findEnum(t *testing.T, it Interface, name string) Enum {
42	t.Helper()
43	for _, e := range it.Enums {
44		if e.Name == name {
45			return e
46		}
47	}
48	t.Fatalf("enum %q not found in %s", name, it.Name)
49	return Enum{}
50}
51
52func TestParseWayland(t *testing.T) {
53	p, err := Parse(readTestdata(t, "wayland.xml"))
54	if err != nil {
55		t.Fatal(err)
56	}
57	if p.Name != "wayland" {
58		t.Errorf("Name = %q", p.Name)
59	}
60	if !strings.Contains(p.Copyright, "Kristian") {
61		t.Errorf("Copyright not preserved: %q", p.Copyright)
62	}
63	if len(p.Interfaces) != 23 {
64		t.Errorf("got %d interfaces, want 23", len(p.Interfaces))
65	}
66	if got := findInterface(t, p, "wl_compositor").Version; got != 7 {
67		t.Errorf("wl_compositor version = %d, want 7", got)
68	}
69
70	// since on a message.
71	release := findMessage(t, findInterface(t, p, "wl_pointer").Requests, "release")
72	if release.Type != "destructor" || release.Since != 3 {
73		t.Errorf("wl_pointer.release = type %q since %d", release.Type, release.Since)
74	}
75
76	// since on an enum.
77	if e := findEnum(t, findInterface(t, p, "wl_data_device_manager"), "dnd_action"); !e.Bitfield || e.Since != 3 {
78		t.Errorf("dnd_action = bitfield %v since %d, want true 3", e.Bitfield, e.Since)
79	}
80
81	// bitfield.
82	seat := findInterface(t, p, "wl_seat")
83	if !findEnum(t, seat, "capability").Bitfield {
84		t.Error("wl_seat.capability should be a bitfield")
85	}
86	if findEnum(t, seat, "error").Bitfield {
87		t.Error("wl_seat.error should not be a bitfield")
88	}
89	if len(findEnum(t, seat, "capability").Entries) == 0 {
90		t.Error("capability has no entries")
91	}
92
93	// allow-null and interface refs.
94	var nullArg, plainArg *Arg
95	for _, m := range findInterface(t, p, "wl_data_device").Requests {
96		for i := range m.Args {
97			a := &m.Args[i]
98			if m.Name == "set_selection" && a.Name == "source" {
99				nullArg = a
100			}
101			if m.Name == "start_drag" && a.Name == "origin" {
102				plainArg = a
103			}
104		}
105	}
106	if nullArg == nil || !nullArg.AllowNull || nullArg.Interface != "wl_data_source" || nullArg.Type != "object" {
107		t.Errorf("set_selection.source = %+v", nullArg)
108	}
109	if plainArg == nil || plainArg.AllowNull {
110		t.Errorf("start_drag.origin = %+v", plainArg)
111	}
112
113	// enum refs, qualified and unqualified.
114	create := findMessage(t, findInterface(t, p, "wl_shm_pool").Requests, "create_buffer")
115	var enumRefs []string
116	for _, a := range create.Args {
117		if a.Enum != "" {
118			enumRefs = append(enumRefs, a.Enum)
119		}
120	}
121	if !reflect.DeepEqual(enumRefs, []string{"wl_shm.format"}) {
122		t.Errorf("create_buffer enum refs = %v", enumRefs)
123	}
124	var found bool
125	for _, a := range findMessage(t, findInterface(t, p, "wl_output").Events, "mode").Args {
126		if a.Name == "flags" && a.Enum == "mode" && a.Summary != "" {
127			found = true
128		}
129	}
130	if !found {
131		t.Error("wl_output.mode flags enum ref/summary not preserved")
132	}
133
134	// Descriptions.
135	if d := findInterface(t, p, "wl_display").Description; d == nil || d.Summary == "" || strings.TrimSpace(d.Text) == "" {
136		t.Errorf("description not parsed: %+v", d)
137	}
138}
139
140func TestParseFixture(t *testing.T) {
141	p, err := Parse(readTestdata(t, "protocol_fixture.xml"))
142	if err != nil {
143		t.Fatal(err)
144	}
145	if p.Name != "wlvision_fixture" || !strings.Contains(p.Copyright, "Synthetic protocol") {
146		t.Errorf("protocol = %q / %q", p.Name, p.Copyright)
147	}
148	if len(p.Interfaces) != 3 {
149		t.Fatalf("got %d interfaces", len(p.Interfaces))
150	}
151	mgr := findInterface(t, p, "zwlr_fixture_manager_v1")
152	if mgr.Version != 2 || len(mgr.Requests) != 10 {
153		t.Errorf("manager version %d requests %d", mgr.Version, len(mgr.Requests))
154	}
155	out := findMessage(t, mgr.Requests, "set_optional_output").Args[0]
156	if !out.AllowNull || out.Interface != "wl_output" {
157		t.Errorf("set_optional_output arg = %+v", out)
158	}
159	thing := findInterface(t, p, "zwlr_fixture_thing_v1")
160	if len(thing.Events) != 7 || len(thing.Enums) != 1 || thing.Enums[0].Entries[1].Value != "1" {
161		t.Errorf("thing = %d events, enums %+v", len(thing.Events), thing.Enums)
162	}
163	want := []string{"wl_seat", "zwlr_fixture_thing_v1", "wl_region", "wl_buffer", "wl_output", "zwlr_fixture_mode_v1", "wl_surface"}
164	if got := p.ReferencedInterfaces(); !reflect.DeepEqual(got, want) {
165		t.Errorf("ReferencedInterfaces = %v, want %v", got, want)
166	}
167}
168
169func TestParseErrors(t *testing.T) {
170	for name, in := range map[string]string{
171		"not xml":   "nope",
172		"no name":   `<protocol></protocol>`,
173		"wrong tag": `<other name="x"/>`,
174	} {
175		if _, err := Parse([]byte(in)); err == nil {
176			t.Errorf("%s: expected error", name)
177		}
178	}
179}

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 1

↑↓ moveenter openesc close