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
23	dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
24)
25
26func listenGaugeStream(vals, result chan float64, done chan struct{}) {
27	var sum float64
28outer:
29	for {
30		select {
31		case <-done:
32			close(vals)
33			for v := range vals {
34				sum += v
35			}
36			break outer
37		case v := <-vals:
38			sum += v
39		}
40	}
41	result <- sum
42	close(result)
43}
44
45func TestGaugeConcurrency(t *testing.T) {
46	it := func(n uint32) bool {
47		mutations := int(n % 10000)
48		concLevel := int(n%15 + 1)
49
50		var start, end sync.WaitGroup
51		start.Add(1)
52		end.Add(concLevel)
53
54		sStream := make(chan float64, mutations*concLevel)
55		result := make(chan float64)
56		done := make(chan struct{})
57
58		go listenGaugeStream(sStream, result, done)
59		go func() {
60			end.Wait()
61			close(done)
62		}()
63
64		gge := NewGauge(GaugeOpts{
65			Name: "test_gauge",
66			Help: "no help can be found here",
67		})
68		for i := 0; i < concLevel; i++ {
69			vals := make([]float64, mutations)
70			for j := 0; j < mutations; j++ {
71				vals[j] = rand.Float64() - 0.5
72			}
73
74			go func(vals []float64) {
75				start.Wait()
76				for _, v := range vals {
77					sStream <- v
78					gge.Add(v)
79				}
80				end.Done()
81			}(vals)
82		}
83		start.Done()
84
85		if expected, got := <-result, math.Float64frombits(gge.(*value).valBits); math.Abs(expected-got) > 0.000001 {
86			t.Fatalf("expected approx. %f, got %f", expected, got)
87			return false
88		}
89		return true
90	}
91
92	if err := quick.Check(it, nil); err != nil {
93		t.Fatal(err)
94	}
95}
96
97func TestGaugeVecConcurrency(t *testing.T) {
98	it := func(n uint32) bool {
99		mutations := int(n % 10000)
100		concLevel := int(n%15 + 1)
101		vecLength := int(n%5 + 1)
102
103		var start, end sync.WaitGroup
104		start.Add(1)
105		end.Add(concLevel)
106
107		sStreams := make([]chan float64, vecLength)
108		results := make([]chan float64, vecLength)
109		done := make(chan struct{})
110
111		for i := 0; i < vecLength; i++ {
112			sStreams[i] = make(chan float64, mutations*concLevel)
113			results[i] = make(chan float64)
114			go listenGaugeStream(sStreams[i], results[i], done)
115		}
116
117		go func() {
118			end.Wait()
119			close(done)
120		}()
121
122		gge := NewGaugeVec(
123			GaugeOpts{
124				Name: "test_gauge",
125				Help: "no help can be found here",
126			},
127			[]string{"label"},
128		)
129		for i := 0; i < concLevel; i++ {
130			vals := make([]float64, mutations)
131			pick := make([]int, mutations)
132			for j := 0; j < mutations; j++ {
133				vals[j] = rand.Float64() - 0.5
134				pick[j] = rand.Intn(vecLength)
135			}
136
137			go func(vals []float64) {
138				start.Wait()
139				for i, v := range vals {
140					sStreams[pick[i]] <- v
141					gge.WithLabelValues(string('A' + pick[i])).Add(v)
142				}
143				end.Done()
144			}(vals)
145		}
146		start.Done()
147
148		for i := range sStreams {
149			if expected, got := <-results[i], math.Float64frombits(gge.WithLabelValues(string('A'+i)).(*value).valBits); math.Abs(expected-got) > 0.000001 {
150				t.Fatalf("expected approx. %f, got %f", expected, got)
151				return false
152			}
153		}
154		return true
155	}
156
157	if err := quick.Check(it, nil); err != nil {
158		t.Fatal(err)
159	}
160}
161
162func TestGaugeFunc(t *testing.T) {
163	gf := NewGaugeFunc(
164		GaugeOpts{
165			Name:        "test_name",
166			Help:        "test help",
167			ConstLabels: Labels{"a": "1", "b": "2"},
168		},
169		func() float64 { return 3.1415 },
170	)
171
172	if expected, got := `Desc{fqName: "test_name", help: "test help", constLabels: {a="1",b="2"}, variableLabels: []}`, gf.Desc().String(); expected != got {
173		t.Errorf("expected %q, got %q", expected, got)
174	}
175
176	m := &dto.Metric{}
177	gf.Write(m)
178
179	if expected, got := `label:<name:"a" value:"1" > label:<name:"b" value:"2" > gauge:<value:3.1415 > `, m.String(); expected != got {
180		t.Errorf("expected %q, got %q", expected, got)
181	}
182}
183