main 602baa7

parse_test.go RawHistoryFolder

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}

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