detached 1a6c6c9

spec_test.go RawHistoryFolder

1package spec
2
3import (
4	"crypto/sha256"
5	"encoding/hex"
6	"path"
7	"reflect"
8	"sort"
9	"strings"
10	"testing"
11)
12
13func TestManifestInvariants(t *testing.T) {
14	m, err := Load()
15	if err != nil {
16		t.Fatal(err)
17	}
18	if len(m.Protocols) == 0 {
19		t.Fatal("manifest has no protocols; run `go run ./cmd/wlbsync`")
20	}
21	if len(m.Sources) == 0 {
22		t.Fatal("manifest has no sources")
23	}
24
25	sources := map[string]bool{}
26	for _, s := range m.Sources {
27		if s.ID == "" || s.URL == "" || s.Ref == "" || len(s.Commit) != 40 {
28			t.Errorf("incomplete source %+v", s)
29		}
30		sources[s.ID] = true
31	}
32
33	owners := map[string]string{}
34	parsed := map[string]*Protocol{}
35	listed := map[string]bool{}
36	var pkgs []string
37
38	for _, e := range m.Protocols {
39		pkgs = append(pkgs, e.Package)
40		listed[e.XML] = true
41		if !sources[e.Source] {
42			t.Errorf("%s: unknown source %q", e.Package, e.Source)
43		}
44		if !ValidPackageName(e.Package) {
45			t.Errorf("%s: invalid package name", e.Package)
46		}
47		if e.XML != e.Package+".xml" {
48			t.Errorf("%s: xml file %q, want %q", e.Package, e.XML, e.Package+".xml")
49		}
50		if e.Package != "wayland" && PackageName(path.Base(e.Path)) != e.Package {
51			t.Errorf("%s: PackageName(%q) = %q", e.Package, e.Path, PackageName(path.Base(e.Path)))
52		}
53		wantLicense := "MIT"
54		if e.Package == "serverdecoration" {
55			wantLicense = "LGPL-2.1-or-later"
56		}
57		if e.License != wantLicense {
58			t.Errorf("%s: license %q, want %q", e.Package, e.License, wantLicense)
59		}
60
61		data, err := Open(e.Package)
62		if err != nil {
63			t.Errorf("%s: %v", e.Package, err)
64			continue
65		}
66		sum := sha256.Sum256(data)
67		if got := hex.EncodeToString(sum[:]); got != e.SHA256 {
68			t.Errorf("%s: sha256 %s, manifest has %s", e.Package, got, e.SHA256)
69		}
70		if !strings.Contains(string(data), "<copyright>") {
71			t.Errorf("%s: missing <copyright>", e.Package)
72		}
73		p, err := Parse(data)
74		if err != nil {
75			t.Errorf("%s: %v", e.Package, err)
76			continue
77		}
78		parsed[e.Package] = p
79
80		var want []InterfaceEntry
81		for _, it := range p.Interfaces {
82			want = append(want, InterfaceEntry{Name: it.Name, Version: it.Version})
83			if prev, dup := owners[it.Name]; dup {
84				t.Errorf("interface %s owned by both %s and %s", it.Name, prev, e.Package)
85			}
86			owners[it.Name] = e.Package
87		}
88		sort.Slice(want, func(i, j int) bool { return want[i].Name < want[j].Name })
89		if !reflect.DeepEqual(e.Interfaces, want) {
90			t.Errorf("%s: manifest interfaces %v, XML has %v", e.Package, e.Interfaces, want)
91		}
92	}
93	if !sort.StringsAreSorted(pkgs) {
94		t.Error("manifest protocols are not sorted by package")
95	}
96
97	for _, e := range m.Protocols {
98		p := parsed[e.Package]
99		if p == nil {
100			continue
101		}
102		depSet := map[string]bool{}
103		for _, ref := range p.ReferencedInterfaces() {
104			owner, ok := owners[ref]
105			if !ok {
106				t.Errorf("%s references unknown interface %s", e.Package, ref)
107				continue
108			}
109			if owner != e.Package {
110				depSet[owner] = true
111			}
112		}
113		want := []string{}
114		for d := range depSet {
115			want = append(want, d)
116		}
117		sort.Strings(want)
118		if !reflect.DeepEqual(e.Deps, want) {
119			t.Errorf("%s: deps %v, want %v", e.Package, e.Deps, want)
120		}
121	}
122
123	entries, err := files.ReadDir("xml")
124	if err != nil {
125		t.Fatal(err)
126	}
127	for _, f := range entries {
128		if !listed[f.Name()] {
129			t.Errorf("spec/xml/%s is not in the manifest", f.Name())
130		}
131	}
132	if len(entries) != len(m.Protocols) {
133		t.Errorf("%d files in spec/xml, %d manifest entries", len(entries), len(m.Protocols))
134	}
135}
136
137func TestManifestAPI(t *testing.T) {
138	m, err := Load()
139	if err != nil {
140		t.Fatal(err)
141	}
142	if pkg, ok := m.Lookup("wl_surface"); !ok || pkg != "wayland" {
143		t.Errorf("Lookup(wl_surface) = %q, %v", pkg, ok)
144	}
145	if pkg, ok := m.Lookup("xdg_wm_base"); !ok || pkg != "xdgshell" {
146		t.Errorf("Lookup(xdg_wm_base) = %q, %v", pkg, ok)
147	}
148	if _, ok := m.Lookup("nope"); ok {
149		t.Error("Lookup(nope) succeeded")
150	}
151	if _, err := Open("nope"); err == nil {
152		t.Error("Open(nope) succeeded")
153	}
154	if _, ok := m.Package("xdgshell"); !ok {
155		t.Error("Package(xdgshell) not found")
156	}
157}

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