Source file src/runtime/metrics/description_test.go

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package metrics_test
     6  
     7  import (
     8  	"bufio"
     9  	"os"
    10  	"regexp"
    11  	"runtime/metrics"
    12  	"strings"
    13  	"testing"
    14  )
    15  
    16  func TestDescriptionNameFormat(t *testing.T) {
    17  	r := regexp.MustCompile("^(?P<name>/[^:]+):(?P<unit>[^:*/]+(?:[*/][^:*/]+)*)$")
    18  	descriptions := metrics.All()
    19  	for _, desc := range descriptions {
    20  		if !r.MatchString(desc.Name) {
    21  			t.Errorf("metrics %q does not match regexp %s", desc.Name, r)
    22  		}
    23  	}
    24  }
    25  
    26  func extractMetricDocs(t *testing.T) map[string]string {
    27  	f, err := os.Open("doc.go")
    28  	if err != nil {
    29  		t.Fatalf("failed to open doc.go in runtime/metrics package: %v", err)
    30  	}
    31  	const (
    32  		stateSearch          = iota // look for list of metrics
    33  		stateNextMetric             // look for next metric
    34  		stateNextDescription        // build description
    35  	)
    36  	state := stateSearch
    37  	s := bufio.NewScanner(f)
    38  	result := make(map[string]string)
    39  	var metric string
    40  	var prevMetric string
    41  	var desc strings.Builder
    42  	for s.Scan() {
    43  		line := strings.TrimSpace(s.Text())
    44  		switch state {
    45  		case stateSearch:
    46  			if line == "Below is the full list of supported metrics, ordered lexicographically." {
    47  				state = stateNextMetric
    48  			}
    49  		case stateNextMetric:
    50  			// Ignore empty lines until we find a non-empty
    51  			// one. This will be our metric name.
    52  			if len(line) != 0 {
    53  				prevMetric = metric
    54  				metric = line
    55  				if prevMetric > metric {
    56  					t.Errorf("metrics %s and %s are out of lexicographical order", prevMetric, metric)
    57  				}
    58  				state = stateNextDescription
    59  			}
    60  		case stateNextDescription:
    61  			if len(line) == 0 || line == `*/` {
    62  				// An empty line means we're done.
    63  				// Write down the description and look
    64  				// for a new metric.
    65  				result[metric] = desc.String()
    66  				desc.Reset()
    67  				state = stateNextMetric
    68  			} else {
    69  				// As long as we're seeing data, assume that's
    70  				// part of the description and append it.
    71  				if desc.Len() != 0 {
    72  					// Turn previous newlines into spaces.
    73  					desc.WriteString(" ")
    74  				}
    75  				desc.WriteString(line)
    76  			}
    77  		}
    78  		if line == `*/` {
    79  			break
    80  		}
    81  	}
    82  	if state == stateSearch {
    83  		t.Fatalf("failed to find supported metrics docs in %s", f.Name())
    84  	}
    85  	return result
    86  }
    87  
    88  func TestDescriptionDocs(t *testing.T) {
    89  	docs := extractMetricDocs(t)
    90  	descriptions := metrics.All()
    91  	for _, d := range descriptions {
    92  		want := d.Description
    93  		got, ok := docs[d.Name]
    94  		if !ok {
    95  			t.Errorf("no docs found for metric %s", d.Name)
    96  			continue
    97  		}
    98  		if got != want {
    99  			t.Errorf("mismatched description and docs for metric %s", d.Name)
   100  			t.Errorf("want: %q, got %q", want, got)
   101  			continue
   102  		}
   103  	}
   104  	if len(docs) > len(descriptions) {
   105  	docsLoop:
   106  		for name, _ := range docs {
   107  			for _, d := range descriptions {
   108  				if name == d.Name {
   109  					continue docsLoop
   110  				}
   111  			}
   112  			t.Errorf("stale documentation for non-existent metric: %s", name)
   113  		}
   114  	}
   115  }
   116  

View as plain text