| // Copyright 2020 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package metrics_test | |
| import ( | |
| "bytes" | |
| "flag" | |
| "fmt" | |
| "go/ast" | |
| "go/doc" | |
| "go/doc/comment" | |
| "go/format" | |
| "go/parser" | |
| "go/token" | |
| "internal/diff" | |
| "os" | |
| "regexp" | |
| "runtime/metrics" | |
| "sort" | |
| "strings" | |
| "testing" | |
| _ "unsafe" | |
| ) | |
| // Implemented in the runtime. | |
| // | |
| //go:linkname runtime_readMetricNames | |
| func runtime_readMetricNames() []string | |
| func TestNames(t *testing.T) { | |
| // Note that this regexp is promised in the package docs for Description. Do not change. | |
| r := regexp.MustCompile("^(?P<name>/[^:]+):(?P<unit>[^:*/]+(?:[*/][^:*/]+)*)$") | |
| all := metrics.All() | |
| for i, d := range all { | |
| if !r.MatchString(d.Name) { | |
| t.Errorf("name %q does not match regexp %#q", d.Name, r) | |
| } | |
| if i > 0 && all[i-1].Name >= all[i].Name { | |
| t.Fatalf("allDesc not sorted: %s ≥ %s", all[i-1].Name, all[i].Name) | |
| } | |
| } | |
| names := runtime_readMetricNames() | |
| sort.Strings(names) | |
| samples := make([]metrics.Sample, len(names)) | |
| for i, name := range names { | |
| samples[i].Name = name | |
| } | |
| metrics.Read(samples) | |
| for _, d := range all { | |
| for len(samples) > 0 && samples[0].Name < d.Name { | |
| t.Errorf("%s: reported by runtime but not listed in All", samples[0].Name) | |
| samples = samples[1:] | |
| } | |
| if len(samples) == 0 || d.Name < samples[0].Name { | |
| t.Errorf("%s: listed in All but not reported by runtime", d.Name) | |
| continue | |
| } | |
| if samples[0].Value.Kind() != d.Kind { | |
| t.Errorf("%s: runtime reports %v but All reports %v", d.Name, samples[0].Value.Kind(), d.Kind) | |
| } | |
| samples = samples[1:] | |
| } | |
| } | |
| func wrap(prefix, text string, width int) string { | |
| doc := &comment.Doc{Content: []comment.Block{&comment.Paragraph{Text: []comment.Text{comment.Plain(text)}}}} | |
| pr := &comment.Printer{TextPrefix: prefix, TextWidth: width} | |
| return string(pr.Text(doc)) | |
| } | |
| func formatDesc(t *testing.T) string { | |
| var b strings.Builder | |
| for i, d := range metrics.All() { | |
| if i > 0 { | |
| fmt.Fprintf(&b, "\n") | |
| } | |
| fmt.Fprintf(&b, "%s\n", d.Name) | |
| fmt.Fprintf(&b, "%s", wrap("\t", d.Description, 80-2*8)) | |
| } | |
| return b.String() | |
| } | |
| var generate = flag.Bool("generate", false, "update doc.go for go generate") | |
| func TestDocs(t *testing.T) { | |
| want := formatDesc(t) | |
| src, err := os.ReadFile("doc.go") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| fset := token.NewFileSet() | |
| f, err := parser.ParseFile(fset, "doc.go", src, parser.ParseComments) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| fdoc := f.Doc | |
| if fdoc == nil { | |
| t.Fatal("no doc comment in doc.go") | |
| } | |
| pkg, err := doc.NewFromFiles(fset, []*ast.File{f}, "runtime/metrics") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if pkg.Doc == "" { | |
| t.Fatal("doc.NewFromFiles lost doc comment") | |
| } | |
| doc := new(comment.Parser).Parse(pkg.Doc) | |
| expectCode := false | |
| foundCode := false | |
| updated := false | |
| for _, block := range doc.Content { | |
| switch b := block.(type) { | |
| case *comment.Heading: | |
| expectCode = false | |
| if b.Text[0] == comment.Plain("Supported metrics") { | |
| expectCode = true | |
| } | |
| case *comment.Code: | |
| if expectCode { | |
| foundCode = true | |
| if b.Text != want { | |
| if !*generate { | |
| t.Fatalf("doc comment out of date; use go generate to rebuild\n%s", diff.Diff("old", []byte(b.Text), "want", []byte(want))) | |
| } | |
| b.Text = want | |
| updated = true | |
| } | |
| } | |
| } | |
| } | |
| if !foundCode { | |
| t.Fatalf("did not find Supported metrics list in doc.go") | |
| } | |
| if updated { | |
| fmt.Fprintf(os.Stderr, "go test -generate: writing new doc.go\n") | |
| var buf bytes.Buffer | |
| buf.Write(src[:fdoc.Pos()-f.FileStart]) | |
| buf.WriteString("/*\n") | |
| buf.Write(new(comment.Printer).Comment(doc)) | |
| buf.WriteString("*/") | |
| buf.Write(src[fdoc.End()-f.FileStart:]) | |
| src, err := format.Source(buf.Bytes()) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := os.WriteFile("doc.go", src, 0666); err != nil { | |
| t.Fatal(err) | |
| } | |
| } else if *generate { | |
| fmt.Fprintf(os.Stderr, "go test -generate: doc.go already up-to-date\n") | |
| } | |
| } |