1
2
3
4
5 package metrics_test
6
7 import (
8 "bytes"
9 "flag"
10 "fmt"
11 "go/ast"
12 "go/doc"
13 "go/doc/comment"
14 "go/format"
15 "go/parser"
16 "go/token"
17 "internal/diff"
18 "os"
19 "regexp"
20 "runtime/metrics"
21 "slices"
22 "strings"
23 "testing"
24 _ "unsafe"
25 )
26
27
28
29
30 func runtime_readMetricNames() []string
31
32 func TestNames(t *testing.T) {
33
34 r := regexp.MustCompile("^(?P<name>/[^:]+):(?P<unit>[^:*/]+(?:[*/][^:*/]+)*)$")
35 all := metrics.All()
36 for i, d := range all {
37 if !r.MatchString(d.Name) {
38 t.Errorf("name %q does not match regexp %#q", d.Name, r)
39 }
40 if i > 0 && all[i-1].Name >= all[i].Name {
41 t.Fatalf("allDesc not sorted: %s ≥ %s", all[i-1].Name, all[i].Name)
42 }
43 }
44
45 names := runtime_readMetricNames()
46 slices.Sort(names)
47 samples := make([]metrics.Sample, len(names))
48 for i, name := range names {
49 samples[i].Name = name
50 }
51 metrics.Read(samples)
52
53 for _, d := range all {
54 for len(samples) > 0 && samples[0].Name < d.Name {
55 t.Errorf("%s: reported by runtime but not listed in All", samples[0].Name)
56 samples = samples[1:]
57 }
58 if len(samples) == 0 || d.Name < samples[0].Name {
59 t.Errorf("%s: listed in All but not reported by runtime", d.Name)
60 continue
61 }
62 if samples[0].Value.Kind() != d.Kind {
63 t.Errorf("%s: runtime reports %v but All reports %v", d.Name, samples[0].Value.Kind(), d.Kind)
64 }
65 samples = samples[1:]
66 }
67 }
68
69 func wrap(prefix, text string, width int) string {
70 doc := &comment.Doc{Content: []comment.Block{&comment.Paragraph{Text: []comment.Text{comment.Plain(text)}}}}
71 pr := &comment.Printer{TextPrefix: prefix, TextWidth: width}
72 return string(pr.Text(doc))
73 }
74
75 func formatDesc(t *testing.T) string {
76 var b strings.Builder
77 for i, d := range metrics.All() {
78 if i > 0 {
79 fmt.Fprintf(&b, "\n")
80 }
81 fmt.Fprintf(&b, "%s\n", d.Name)
82 fmt.Fprintf(&b, "%s", wrap("\t", d.Description, 80-2*8))
83 }
84 return b.String()
85 }
86
87 var generate = flag.Bool("generate", false, "update doc.go for go generate")
88
89 func TestDocs(t *testing.T) {
90 want := formatDesc(t)
91
92 src, err := os.ReadFile("doc.go")
93 if err != nil {
94 t.Fatal(err)
95 }
96 fset := token.NewFileSet()
97 f, err := parser.ParseFile(fset, "doc.go", src, parser.ParseComments)
98 if err != nil {
99 t.Fatal(err)
100 }
101 fdoc := f.Doc
102 if fdoc == nil {
103 t.Fatal("no doc comment in doc.go")
104 }
105 pkg, err := doc.NewFromFiles(fset, []*ast.File{f}, "runtime/metrics")
106 if err != nil {
107 t.Fatal(err)
108 }
109 if pkg.Doc == "" {
110 t.Fatal("doc.NewFromFiles lost doc comment")
111 }
112 doc := new(comment.Parser).Parse(pkg.Doc)
113 expectCode := false
114 foundCode := false
115 updated := false
116 for _, block := range doc.Content {
117 switch b := block.(type) {
118 case *comment.Heading:
119 expectCode = false
120 if b.Text[0] == comment.Plain("Supported metrics") {
121 expectCode = true
122 }
123 case *comment.Code:
124 if expectCode {
125 foundCode = true
126 if b.Text != want {
127 if !*generate {
128 t.Fatalf("doc comment out of date; use go generate to rebuild\n%s", diff.Diff("old", []byte(b.Text), "want", []byte(want)))
129 }
130 b.Text = want
131 updated = true
132 }
133 }
134 }
135 }
136
137 if !foundCode {
138 t.Fatalf("did not find Supported metrics list in doc.go")
139 }
140 if updated {
141 fmt.Fprintf(os.Stderr, "go test -generate: writing new doc.go\n")
142 var buf bytes.Buffer
143 buf.Write(src[:fdoc.Pos()-f.FileStart])
144 buf.WriteString("/*\n")
145 buf.Write(new(comment.Printer).Comment(doc))
146 buf.WriteString("*/")
147 buf.Write(src[fdoc.End()-f.FileStart:])
148 src, err := format.Source(buf.Bytes())
149 if err != nil {
150 t.Fatal(err)
151 }
152 if err := os.WriteFile("doc.go", src, 0666); err != nil {
153 t.Fatal(err)
154 }
155 } else if *generate {
156 fmt.Fprintf(os.Stderr, "go test -generate: doc.go already up-to-date\n")
157 }
158 }
159
View as plain text