1package main
2
3import (
4 "path/filepath"
5 "strings"
6 "testing"
7)
8
9func TestParseVendored(t *testing.T) {
10 files, err := filepath.Glob("../../spec/xml/*.xml")
11 if err != nil {
12 t.Fatal(err)
13 }
14 if len(files) != 9 {
15 t.Fatalf("got %d XML files, want 9", len(files))
16 }
17 for _, file := range files {
18 t.Run(filepath.Base(file), func(t *testing.T) {
19 n, err := parseFile(file)
20 if err != nil {
21 t.Fatal(err)
22 }
23 if n.Kind != "xcb" || len(n.Children) == 0 {
24 t.Fatal("missing module AST")
25 }
26 })
27 }
28}
29func TestParseRejects(t *testing.T) {
30 cases := []struct{ name, body, want string }{
31 {"unknown element", "<surprise/>", "unsupported element"},
32 {"unknown attribute", `<xidtype name="WINDOW" surprise="yes"/>`, "unknown attribute"},
33 {"misplaced known element", `<struct name="S"><reply/></struct>`, "unsupported <reply>"},
34 {"sumof", `<request name="R" opcode="1"><list name="a" type="CARD8"><sumof ref="a"/></list></request>`, "sumof"},
35 {"paramref", `<request name="R" opcode="1"><list name="a" type="CARD8"><paramref type="CARD8">a</paramref></list></request>`, "paramref"},
36 {"case", `<request name="R" opcode="1"><switch name="s"><case/></switch></request>`, "case"},
37 {"struct switch", `<struct name="S"><switch name="s"/></struct>`, "unsupported <switch>"},
38 {"reply switch", `<request name="R" opcode="1"><reply><switch name="s"/></reply></request>`, "unsupported <switch>"},
39 {"nested switch", `<request name="R" opcode="1"><switch name="s"><bitcase><switch name="s"/></bitcase></switch></request>`, "unsupported <switch>"},
40 {"fd", `<request name="R" opcode="1"><fd name="fd"/></request>`, "fd"},
41 {"eventstruct", `<eventstruct name="E"/>`, "eventstruct"},
42 {"required alignment", `<struct name="S"><required_start_align align="4"/></struct>`, "required_start_align"},
43 {"forward length", `<struct name="S"><list name="a" type="CARD8"><fieldref>n</fieldref></list><field name="n" type="CARD8"/></struct>`, "later field"},
44 {"xge code", `<event name="E" number="128" xge="true"/>`, "evtype"},
45 {"bad opcode", `<request name="R" opcode="256"/>`, "invalid opcode"},
46 {"bad operator", `<request name="R" opcode="1"><exprfield name="x" type="CARD32"><op op="%"><value>1</value><value>2</value></op></exprfield></request>`, "unsupported operator"},
47 {"wrong operands", `<request name="R" opcode="1"><exprfield name="x" type="CARD32"><op op="+"><value>1</value></op></exprfield></request>`, "two operands"},
48 {"bad pad", `<struct name="S"><pad align="3"/></struct>`, "power of two"},
49 {"namespace", `<xidtype xmlns="other" name="X"/>`, "unsupported element"},
50 {"duplicate attr", `<xidtype name="X" name="Y"/>`, "duplicate attribute"},
51 }
52 for _, tc := range cases {
53 t.Run(tc.name, func(t *testing.T) {
54 _, err := parseXML("bad.xml", strings.NewReader("<xcb header=\"test\">\n"+tc.body+"\n</xcb>"))
55 if err == nil || !strings.Contains(err.Error(), tc.want) || !strings.Contains(err.Error(), "bad.xml:2:") {
56 t.Fatalf("got %v, want file:line and %q", err, tc.want)
57 }
58 })
59 }
60}
61func TestParseAST(t *testing.T) {
62 n, err := parseXML("ok.xml", strings.NewReader(`<xcb header="a"><doc><arbitrary/></doc><request name="R" opcode="1"><field type="CARD32" name="n"/><list type="CARD8" name="data"><op op="*"><fieldref>n</fieldref><value>4</value></op></list></request></xcb>`))
63 if err != nil {
64 t.Fatal(err)
65 }
66 r := n.Children[1]
67 if r.Attr["name"] != "R" || r.Children[1].Children[0].Children[0].Text != "n" {
68 t.Fatal("AST lost order or expression")
69 }
70}
71func FuzzParseXML(f *testing.F) {
72 f.Add(`<xcb header="test"/>`)
73 f.Fuzz(func(t *testing.T, raw string) { _, _ = parseXML("fuzz.xml", strings.NewReader(raw)) })
74}