1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
package main
import (
"encoding/xml"
"html"
"os"
"os/exec"
"regexp"
"sort"
"strings"
"testing"
)
// The package diagram is drawn by hand and read by people, so nothing in the
// build notices when it stops describing the code. It started as Turbo Rust's
// and shipped naming internal/rustlang and "the Rust scanner" — an error no
// test could see, because a diagram is a file nothing imports.
//
// These tests hold it to `go list`: the boxes are the packages this module
// actually imports, and the arrows between the two boxes that are ours are the
// imports that really exist.
// diagramFile is the drawio the documentation links to.
const diagramFile = "docs/diagrams/packages.drawio"
// mxFile is as much of drawio's format as these tests need: every cell, with
// its label, and — for an arrow — the two cells it joins.
type mxFile struct {
Host string `xml:"host,attr"`
Cells []mxCell `xml:"diagram>mxGraphModel>root>mxCell"`
}
type mxCell struct {
ID string `xml:"id,attr"`
Value string `xml:"value,attr"`
Edge string `xml:"edge,attr"`
Source string `xml:"source,attr"`
Target string `xml:"target,attr"`
}
// boldLabel is the package name inside a box: drawio stores the label as
// escaped HTML, and the name is the part in bold.
var boldLabel = regexp.MustCompile(`(?s)<b>(.*?)</b>`)
// readDiagram parses the diagram, failing the test rather than returning an
// error — a diagram that will not parse is not a case any caller can handle.
func readDiagram(t *testing.T) mxFile {
t.Helper()
raw, err := os.ReadFile(diagramFile)
if err != nil {
t.Fatalf("reading %s: %v", diagramFile, err)
}
var file mxFile
if err := xml.Unmarshal(raw, &file); err != nil {
t.Fatalf("parsing %s: %v", diagramFile, err)
}
return file
}
// boxes maps each box's package name to the id the arrows use for it.
func boxes(t *testing.T, file mxFile) map[string]string {
t.Helper()
found := map[string]string{}
for _, cell := range file.Cells {
if cell.Edge == "1" || cell.Value == "" {
continue
}
label := html.UnescapeString(cell.Value)
// A box's package name is the part in bold, where there is one; the
// third-party box carries its name plain, with nothing to tell apart
// from it.
if match := boldLabel.FindStringSubmatch(label); match != nil {
label = match[1]
}
found[label] = cell.ID
}
return found
}
// imports asks the toolchain what a package imports, shortened to the names the
// diagram uses: the last element for a turbo-core package, the module-relative
// path for one of ours, and "tcell/v2" for the one third-party dependency.
func imports(t *testing.T, pkg string) []string {
t.Helper()
out, err := exec.Command("go", "list", "-f", `{{join .Imports "\n"}}`, pkg).Output()
if err != nil {
t.Fatalf("go list %s: %v", pkg, err)
}
var names []string
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
switch {
case strings.HasPrefix(line, "rickub.com/turbo-editors/turbo-core/"):
names = append(names, strings.TrimPrefix(line, "rickub.com/turbo-editors/turbo-core/"))
case strings.HasPrefix(line, "rickub.com/turbo-editors/turbo-python/"):
names = append(names, strings.TrimPrefix(line, "rickub.com/turbo-editors/turbo-python/"))
case strings.HasPrefix(line, "github.com/gdamore/tcell/"):
names = append(names, "tcell/v2")
}
}
sort.Strings(names)
return names
}
// The boxes are exactly the packages the two packages of this module import,
// plus the two packages themselves. A box for a package nothing imports is as
// wrong as a missing one: both tell a reader something untrue about the code.
func TestTheDiagramDrawsExactlyThePackagesThisModuleImports(t *testing.T) {
drawn := boxes(t, readDiagram(t))
want := map[string]bool{"main": true, "internal/pythonlang": true}
for _, pkg := range append(imports(t, "."), imports(t, "./internal/pythonlang")...) {
want[pkg] = true
}
for name := range want {
if _, ok := drawn[name]; !ok {
t.Errorf("%s draws no box for %q", diagramFile, name)
}
}
for name := range drawn {
if !want[name] {
t.Errorf("%s draws a box for %q, which nothing in this module imports", diagramFile, name)
}
}
}
// Every arrow leaving one of our two boxes is an import that exists. This is
// the half that caught the copied diagram: an arrow drawn out of a box labelled
// internal/rustlang cannot be checked at all until the box is named right.
func TestEveryArrowOutOfOurPackagesIsARealImport(t *testing.T) {
file := readDiagram(t)
drawn := boxes(t, file)
byID := map[string]string{}
for name, id := range drawn {
byID[id] = name
}
ours := map[string]string{"main": ".", "internal/pythonlang": "./internal/pythonlang"}
for _, cell := range file.Cells {
if cell.Edge != "1" {
continue
}
from, ok := byID[cell.Source]
if !ok {
t.Errorf("%s draws an arrow out of unknown cell %q", diagramFile, cell.Source)
continue
}
pkg, ok := ours[from]
if !ok {
continue
}
to := byID[cell.Target]
if to == "internal/pythonlang" && from == "main" {
continue // main imports it under its full path, already shortened
}
if !slicesContain(imports(t, pkg), to) {
t.Errorf("%s draws %s → %s, but %s imports no such package", diagramFile, from, to, from)
}
}
}
// The file's host attribute names the project it was drawn for. It is the one
// field a reader never sees and a copy always keeps.
func TestTheDiagramSaysWhichProjectItWasDrawnFor(t *testing.T) {
if host := readDiagram(t).Host; host != "turbo-python" {
t.Errorf("%s was drawn for %q, not turbo-python", diagramFile, host)
}
}
// No label anywhere in the diagram names another editor in the family, or the
// language it edits. The copied diagram said "the Rust scanner" in prose that
// no identifier check would have looked at.
func TestNoLabelInTheDiagramNamesAnotherEditorsLanguage(t *testing.T) {
for _, cell := range readDiagram(t).Cells {
label := html.UnescapeString(cell.Value)
for _, other := range []string{"rustlang", "golang", "Rust", "Go ", "turbo-rust", "turbo-go"} {
if strings.Contains(label, other) {
t.Errorf("%s labels a cell %q, which names %q", diagramFile, label, other)
}
}
}
}
// slicesContain says whether a sorted list holds a value. It is here rather
// than from the standard library's slices package so the test reads the same
// way in a checkout of any Go version this module supports.
func slicesContain(list []string, want string) bool {
for _, got := range list {
if got == want {
return true
}
}
return false
}
|