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}