| // 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 ( |
| "bufio" |
| "os" |
| "path/filepath" |
| "regexp" |
| "runtime" |
| "runtime/metrics" |
| "strings" |
| "testing" |
| ) |
| |
| func TestDescriptionNameFormat(t *testing.T) { |
| r := regexp.MustCompile("^(?P<name>/[^:]+):(?P<unit>[^:*/]+(?:[*/][^:*/]+)*)$") |
| descriptions := metrics.All() |
| for _, desc := range descriptions { |
| if !r.MatchString(desc.Name) { |
| t.Errorf("metrics %q does not match regexp %s", desc.Name, r) |
| } |
| } |
| } |
| |
| func extractMetricDocs(t *testing.T) map[string]string { |
| if runtime.GOOS == "android" { |
| t.Skip("no access to Go source on android") |
| } |
| |
| // Get doc.go. |
| _, filename, _, _ := runtime.Caller(0) |
| filename = filepath.Join(filepath.Dir(filename), "doc.go") |
| |
| f, err := os.Open(filename) |
| if err != nil { |
| t.Fatal(err) |
| } |
| const ( |
| stateSearch = iota // look for list of metrics |
| stateNextMetric // look for next metric |
| stateNextDescription // build description |
| ) |
| state := stateSearch |
| s := bufio.NewScanner(f) |
| result := make(map[string]string) |
| var metric string |
| var prevMetric string |
| var desc strings.Builder |
| for s.Scan() { |
| line := strings.TrimSpace(s.Text()) |
| switch state { |
| case stateSearch: |
| if line == "Supported metrics" { |
| state = stateNextMetric |
| } |
| case stateNextMetric: |
| // Ignore empty lines until we find a non-empty |
| // one. This will be our metric name. |
| if len(line) != 0 { |
| prevMetric = metric |
| metric = line |
| if prevMetric > metric { |
| t.Errorf("metrics %s and %s are out of lexicographical order", prevMetric, metric) |
| } |
| state = stateNextDescription |
| } |
| case stateNextDescription: |
| if len(line) == 0 || line == `*/` { |
| // An empty line means we're done. |
| // Write down the description and look |
| // for a new metric. |
| result[metric] = desc.String() |
| desc.Reset() |
| state = stateNextMetric |
| } else { |
| // As long as we're seeing data, assume that's |
| // part of the description and append it. |
| if desc.Len() != 0 { |
| // Turn previous newlines into spaces. |
| desc.WriteString(" ") |
| } |
| desc.WriteString(line) |
| } |
| } |
| if line == `*/` { |
| break |
| } |
| } |
| if state == stateSearch { |
| t.Fatalf("failed to find supported metrics docs in %s", filename) |
| } |
| return result |
| } |
| |
| func TestDescriptionDocs(t *testing.T) { |
| docs := extractMetricDocs(t) |
| descriptions := metrics.All() |
| for _, d := range descriptions { |
| want := d.Description |
| got, ok := docs[d.Name] |
| if !ok { |
| t.Errorf("no docs found for metric %s", d.Name) |
| continue |
| } |
| if got != want { |
| t.Errorf("mismatched description and docs for metric %s", d.Name) |
| t.Errorf("want: %q, got %q", want, got) |
| continue |
| } |
| } |
| if len(docs) > len(descriptions) { |
| docsLoop: |
| for name, _ := range docs { |
| for _, d := range descriptions { |
| if name == d.Name { |
| continue docsLoop |
| } |
| } |
| t.Errorf("stale documentation for non-existent metric: %s", name) |
| } |
| } |
| } |