1// Copyright The OpenTelemetry Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// +build windows
16
17package loadscraper
18
19import (
20	"context"
21	"testing"
22	"time"
23
24	"github.com/stretchr/testify/assert"
25	"github.com/stretchr/testify/require"
26	"go.uber.org/zap"
27
28	"go.opentelemetry.io/collector/receiver/hostmetricsreceiver/internal/perfcounters"
29)
30
31func TestStartSampling(t *testing.T) {
32	// override sampling frequency to 2ms
33	samplingFrequency = 2 * time.Millisecond
34
35	// startSampling should set up perf counter and start sampling
36	startSampling(context.Background(), zap.NewNop())
37	assertSamplingUnderway(t)
38
39	// override the processor queue length perf counter with a mock
40	// that will ensure a positive value is returned
41	assert.IsType(t, &perfcounters.PerfLibScraper{}, samplerInstance.perfCounterScraper)
42	samplerInstance.perfCounterScraper = perfcounters.NewMockPerfCounterScraper(map[string]map[string][]int64{
43		system: {processorQueueLength: {100}},
44	})
45
46	// second call to startSampling should succeed, but not do anything
47	startSampling(context.Background(), zap.NewNop())
48	assertSamplingUnderway(t)
49	assert.IsType(t, &perfcounters.MockPerfCounterScraper{}, samplerInstance.perfCounterScraper)
50
51	// ensure that a positive load avg is returned by a call to
52	// "getSampledLoadAverages" which validates the value from the
53	// mock perf counter was used
54	require.Eventually(t, func() bool {
55		avgLoadValues, err := getSampledLoadAverages()
56		assert.NoError(t, err)
57		return avgLoadValues.Load1 > 0 && avgLoadValues.Load5 > 0 && avgLoadValues.Load15 > 0
58	}, time.Second, time.Millisecond, "Load Avg was not set after 1s")
59
60	// sampling should continue after first call to stopSampling since
61	// startSampling was called twice
62	stopSampling(context.Background())
63	assertSamplingUnderway(t)
64
65	// second call to stopSampling should close perf counter, stop
66	// sampling, and clean up the sampler
67	stopSampling(context.Background())
68	assertSamplingStopped(t)
69}
70
71func assertSamplingUnderway(t *testing.T) {
72	assert.NotNil(t, samplerInstance)
73	assert.NotNil(t, samplerInstance.perfCounterScraper)
74
75	select {
76	case <-samplerInstance.done:
77		assert.Fail(t, "Load scraper sampling done channel unexpectedly closed")
78	default:
79	}
80}
81
82func assertSamplingStopped(t *testing.T) {
83	select {
84	case <-samplerInstance.done:
85	default:
86		assert.Fail(t, "Load scraper sampling done channel not closed")
87	}
88}
89
90func TestSampleLoad(t *testing.T) {
91	counterReturnValues := []int64{10, 20, 30, 40, 50}
92	mockPerfCounterScraper := perfcounters.NewMockPerfCounterScraper(map[string]map[string][]int64{
93		system: {processorQueueLength: counterReturnValues},
94	})
95
96	samplerInstance = &sampler{perfCounterScraper: mockPerfCounterScraper}
97
98	for i := 0; i < len(counterReturnValues); i++ {
99		samplerInstance.sampleLoad()
100	}
101
102	assert.Equal(t, calcExpectedLoad(counterReturnValues, loadAvgFactor1m), samplerInstance.loadAvg1m)
103	assert.Equal(t, calcExpectedLoad(counterReturnValues, loadAvgFactor5m), samplerInstance.loadAvg5m)
104	assert.Equal(t, calcExpectedLoad(counterReturnValues, loadAvgFactor15m), samplerInstance.loadAvg15m)
105}
106
107func calcExpectedLoad(scrapedValues []int64, loadAvgFactor float64) float64 {
108	// replicate the calculations that should be performed to determine the exponentially
109	// weighted moving averages based on the specified scraped values
110	var expectedLoad float64
111	for i := 0; i < len(scrapedValues); i++ {
112		expectedLoad = expectedLoad*loadAvgFactor + float64(scrapedValues[i])*(1-loadAvgFactor)
113	}
114	return expectedLoad
115}
116
117func Benchmark_SampleLoad(b *testing.B) {
118	s, _ := newSampler(zap.NewNop())
119
120	b.ResetTimer()
121	for n := 0; n < b.N; n++ {
122		s.sampleLoad()
123	}
124}
125