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}