func GetCounterMetricValue(m metrics.CounterMetric) (float64, error) {
	metricProto := &dto.Metric{}
	if err := m.(metrics.Metric).Write(metricProto); err != nil {
		return 0, fmt.Errorf("error writing m: %v", err)
	}
	return metricProto.Counter.GetValue(), nil
}
GetCounterMetricValue extracts metric value from CounterMetric
package testutil
import (
	"fmt"
	"io"
	"math"
	"reflect"
	"sort"
	"strings"
	dto "github.com/prometheus/client_model/go"
	"github.com/prometheus/common/expfmt"
	"github.com/prometheus/common/model"
	"k8s.io/component-base/metrics"
)
var (
	MetricNameLabel model.LabelName = model.MetricNameLabel
	QuantileLabel model.LabelName = model.QuantileLabel
)
type Metrics map[string]model.Samples
func (m *Metrics) Equal(o Metrics) bool {
	var leftKeySet []string
	var rightKeySet []string
	for k := range *m {
		leftKeySet = append(leftKeySet, k)
	}
	for k := range o {
		rightKeySet = append(rightKeySet, k)
	}
	if !reflect.DeepEqual(leftKeySet, rightKeySet) {
		return false
	}
	for _, k := range leftKeySet {
		if !(*m)[k].Equal(o[k]) {
			return false
		}
	}
	return true
}
func NewMetrics() Metrics {
	result := make(Metrics)
	return result
}
func ParseMetrics(data string, output *Metrics) error {
	dec := expfmt.NewDecoder(strings.NewReader(data), expfmt.FmtText)
	decoder := expfmt.SampleDecoder{
		Dec:  dec,
		Opts: &expfmt.DecodeOptions{},
	}
	for {
		var v model.Vector
		if err := decoder.Decode(&v); err != nil {
			if err == io.EOF {
				return nil
			}
			continue
		}
		for _, metric := range v {
			name := string(metric.Metric[MetricNameLabel])
			(*output)[name] = append((*output)[name], metric)
		}
	}
}
func TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricFamily, error) {
	var textParser expfmt.TextParser
	return textParser.TextToMetricFamilies(in)
}
func PrintSample(sample *model.Sample) string {
	buf := make([]string, 0)
	_, normalContainer := sample.Metric["kubernetes_container_name"]
	for k, v := range sample.Metric {
		if strings.HasPrefix(string(k), "__") {
			continue
		}
		if string(k) == "id" && normalContainer {
			continue
		}
		buf = append(buf, fmt.Sprintf("%v=%v", string(k), v))
	}
	return fmt.Sprintf("[%v] = %v", strings.Join(buf, ","), sample.Value)
}
func ComputeHistogramDelta(before, after model.Samples, label model.LabelName) {
	beforeSamplesMap := make(map[string]*model.Sample)
	for _, bSample := range before {
		beforeSamplesMap[makeKey(bSample.Metric[label], bSample.Metric["le"])] = bSample
	}
	for _, aSample := range after {
		if bSample, found := beforeSamplesMap[makeKey(aSample.Metric[label], aSample.Metric["le"])]; found {
			aSample.Value = aSample.Value - bSample.Value
		}
	}
}
func makeKey(a, b model.LabelValue) string {
	return string(a) + "___" + string(b)
}
func GetMetricValuesForLabel(ms Metrics, metricName, label string) map[string]int64 {
	samples, found := ms[metricName]
	result := make(map[string]int64, len(samples))
	if !found {
		return result
	}
	for _, sample := range samples {
		count := int64(sample.Value)
		dimensionName := string(sample.Metric[model.LabelName(label)])
		result[dimensionName] = count
	}
	return result
}
func ValidateMetrics(metrics Metrics, metricName string, expectedLabels ...string) error {
	samples, ok := metrics[metricName]
	if !ok {
		return fmt.Errorf("metric %q was not found in metrics", metricName)
	}
	for _, sample := range samples {
		for _, l := range expectedLabels {
			if _, ok := sample.Metric[model.LabelName(l)]; !ok {
				return fmt.Errorf("metric %q is missing label %q, sample: %q", metricName, l, sample.String())
			}
		}
	}
	return nil
}
type Histogram struct {
	*dto.Histogram
}
func GetHistogramFromGatherer(gatherer metrics.Gatherer, metricName string) (Histogram, error) {
	var metricFamily *dto.MetricFamily
	m, err := gatherer.Gather()
	if err != nil {
		return Histogram{}, err
	}
	for _, mFamily := range m {
		if mFamily.GetName() == metricName {
			metricFamily = mFamily
			break
		}
	}
	if metricFamily == nil {
		return Histogram{}, fmt.Errorf("metric %q not found", metricName)
	}
	if metricFamily.GetMetric() == nil {
		return Histogram{}, fmt.Errorf("metric %q is empty", metricName)
	}
	if len(metricFamily.GetMetric()) == 0 {
		return Histogram{}, fmt.Errorf("metric %q is empty", metricName)
	}
	return Histogram{
		metricFamily.GetMetric()[0].GetHistogram(),
	}, nil
}
func uint64Ptr(u uint64) *uint64 {
	return &u
}
type bucket struct {
	upperBound float64
	count      float64
}
func bucketQuantile(q float64, buckets []bucket) float64 {
	if q < 0 {
		return math.Inf(-1)
	}
	if q > 1 {
		return math.Inf(+1)
	}
	if len(buckets) < 2 {
		return math.NaN()
	}
	rank := q * buckets[len(buckets)-1].count
	b := sort.Search(len(buckets)-1, func(i int) bool { return buckets[i].count >= rank })
	if b == 0 {
		return buckets[0].upperBound * (rank / buckets[0].count)
	}
	if b == len(buckets)-1 && math.IsInf(buckets[b].upperBound, 1) {
		return buckets[len(buckets)-2].upperBound
	}
	brank := rank - buckets[b-1].count
	bSize := buckets[b].upperBound - buckets[b-1].upperBound
	bCount := buckets[b].count - buckets[b-1].count
	return buckets[b-1].upperBound + bSize*(brank/bCount)
}
func (hist *Histogram) Quantile(q float64) float64 {
	var buckets []bucket
	for _, bckt := range hist.Bucket {
		buckets = append(buckets, bucket{
			count:      float64(bckt.GetCumulativeCount()),
			upperBound: bckt.GetUpperBound(),
		})
	}
	if len(buckets) == 0 || buckets[len(buckets)-1].upperBound != math.Inf(+1) {
		buckets = append(buckets, bucket{
			count:      float64(hist.GetSampleCount()),
			upperBound: math.Inf(+1),
		})
	}
	return bucketQuantile(q, buckets)
}
func (hist *Histogram) Average() float64 {
	return hist.GetSampleSum() / float64(hist.GetSampleCount())
}
func (hist *Histogram) Validate() error {
	if hist.SampleCount == nil || hist.GetSampleCount() == 0 {
		return fmt.Errorf("nil or empty histogram SampleCount")
	}
	if hist.SampleSum == nil || hist.GetSampleSum() == 0 {
		return fmt.Errorf("nil or empty histogram SampleSum")
	}
	for _, bckt := range hist.Bucket {
		if bckt == nil {
			return fmt.Errorf("empty histogram bucket")
		}
		if bckt.UpperBound == nil || bckt.GetUpperBound() < 0 {
			return fmt.Errorf("nil or negative histogram bucket UpperBound")
		}
	}
	return nil
}
func GetGaugeMetricValue(m metrics.GaugeMetric) (float64, error) {
	metricProto := &dto.Metric{}
	if err := m.Write(metricProto); err != nil {
		return 0, fmt.Errorf("error writing m: %v", err)
	}
	return metricProto.Gauge.GetValue(), nil
}