1// Copyright 2014 The Prometheus Authors 2// Licensed under the Apache License, Version 2.0 (the "License"); 3// you may not use this file except in compliance with the License. 4// You may obtain a copy of the License at 5// 6// http://www.apache.org/licenses/LICENSE-2.0 7// 8// Unless required by applicable law or agreed to in writing, software 9// distributed under the License is distributed on an "AS IS" BASIS, 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11// See the License for the specific language governing permissions and 12// limitations under the License. 13 14package prometheus 15 16import ( 17 "math" 18 "math/rand" 19 "sync" 20 "testing" 21 "testing/quick" 22 "time" 23 24 dto "github.com/prometheus/client_model/go" 25) 26 27func listenGaugeStream(vals, result chan float64, done chan struct{}) { 28 var sum float64 29outer: 30 for { 31 select { 32 case <-done: 33 close(vals) 34 for v := range vals { 35 sum += v 36 } 37 break outer 38 case v := <-vals: 39 sum += v 40 } 41 } 42 result <- sum 43 close(result) 44} 45 46func TestGaugeConcurrency(t *testing.T) { 47 it := func(n uint32) bool { 48 mutations := int(n % 10000) 49 concLevel := int(n%15 + 1) 50 51 var start, end sync.WaitGroup 52 start.Add(1) 53 end.Add(concLevel) 54 55 sStream := make(chan float64, mutations*concLevel) 56 result := make(chan float64) 57 done := make(chan struct{}) 58 59 go listenGaugeStream(sStream, result, done) 60 go func() { 61 end.Wait() 62 close(done) 63 }() 64 65 gge := NewGauge(GaugeOpts{ 66 Name: "test_gauge", 67 Help: "no help can be found here", 68 }) 69 for i := 0; i < concLevel; i++ { 70 vals := make([]float64, mutations) 71 for j := 0; j < mutations; j++ { 72 vals[j] = rand.Float64() - 0.5 73 } 74 75 go func(vals []float64) { 76 start.Wait() 77 for _, v := range vals { 78 sStream <- v 79 gge.Add(v) 80 } 81 end.Done() 82 }(vals) 83 } 84 start.Done() 85 86 if expected, got := <-result, math.Float64frombits(gge.(*gauge).valBits); math.Abs(expected-got) > 0.000001 { 87 t.Fatalf("expected approx. %f, got %f", expected, got) 88 return false 89 } 90 return true 91 } 92 93 if err := quick.Check(it, nil); err != nil { 94 t.Fatal(err) 95 } 96} 97 98func TestGaugeVecConcurrency(t *testing.T) { 99 it := func(n uint32) bool { 100 mutations := int(n % 10000) 101 concLevel := int(n%15 + 1) 102 vecLength := int(n%5 + 1) 103 104 var start, end sync.WaitGroup 105 start.Add(1) 106 end.Add(concLevel) 107 108 sStreams := make([]chan float64, vecLength) 109 results := make([]chan float64, vecLength) 110 done := make(chan struct{}) 111 112 for i := 0; i < vecLength; i++ { 113 sStreams[i] = make(chan float64, mutations*concLevel) 114 results[i] = make(chan float64) 115 go listenGaugeStream(sStreams[i], results[i], done) 116 } 117 118 go func() { 119 end.Wait() 120 close(done) 121 }() 122 123 gge := NewGaugeVec( 124 GaugeOpts{ 125 Name: "test_gauge", 126 Help: "no help can be found here", 127 }, 128 []string{"label"}, 129 ) 130 for i := 0; i < concLevel; i++ { 131 vals := make([]float64, mutations) 132 pick := make([]int, mutations) 133 for j := 0; j < mutations; j++ { 134 vals[j] = rand.Float64() - 0.5 135 pick[j] = rand.Intn(vecLength) 136 } 137 138 go func(vals []float64) { 139 start.Wait() 140 for i, v := range vals { 141 sStreams[pick[i]] <- v 142 gge.WithLabelValues(string('A' + pick[i])).Add(v) 143 } 144 end.Done() 145 }(vals) 146 } 147 start.Done() 148 149 for i := range sStreams { 150 if expected, got := <-results[i], math.Float64frombits(gge.WithLabelValues(string('A'+i)).(*gauge).valBits); math.Abs(expected-got) > 0.000001 { 151 t.Fatalf("expected approx. %f, got %f", expected, got) 152 return false 153 } 154 } 155 return true 156 } 157 158 if err := quick.Check(it, nil); err != nil { 159 t.Fatal(err) 160 } 161} 162 163func TestGaugeFunc(t *testing.T) { 164 gf := NewGaugeFunc( 165 GaugeOpts{ 166 Name: "test_name", 167 Help: "test help", 168 ConstLabels: Labels{"a": "1", "b": "2"}, 169 }, 170 func() float64 { return 3.1415 }, 171 ) 172 173 if expected, got := `Desc{fqName: "test_name", help: "test help", constLabels: {a="1",b="2"}, variableLabels: []}`, gf.Desc().String(); expected != got { 174 t.Errorf("expected %q, got %q", expected, got) 175 } 176 177 m := &dto.Metric{} 178 gf.Write(m) 179 180 if expected, got := `label:<name:"a" value:"1" > label:<name:"b" value:"2" > gauge:<value:3.1415 > `, m.String(); expected != got { 181 t.Errorf("expected %q, got %q", expected, got) 182 } 183} 184 185func TestGaugeSetCurrentTime(t *testing.T) { 186 g := NewGauge(GaugeOpts{ 187 Name: "test_name", 188 Help: "test help", 189 }) 190 g.SetToCurrentTime() 191 unixTime := float64(time.Now().Unix()) 192 193 m := &dto.Metric{} 194 g.Write(m) 195 196 delta := unixTime - m.GetGauge().GetValue() 197 // This is just a smoke test to make sure SetToCurrentTime is not 198 // totally off. Tests with current time involved are hard... 199 if math.Abs(delta) > 5 { 200 t.Errorf("Gauge set to current time deviates from current time by more than 5s, delta is %f seconds", delta) 201 } 202} 203