unittests: Adds winstats unittests

The module pkg/kubelet/winstats has almost no coverage for Windows. This
commit adds unit tests to cover the mentioned module.
This commit is contained in:
Claudiu Belu 2022-06-27 15:03:58 +03:00
parent d34b0275a3
commit e3edf13486
7 changed files with 569 additions and 88 deletions

View File

@ -40,14 +40,14 @@ const (
// networkCounter contains the counters for network adapters. // networkCounter contains the counters for network adapters.
type networkCounter struct { type networkCounter struct {
packetsReceivedPerSecondCounter *perfCounter packetsReceivedPerSecondCounter perfCounter
packetsSentPerSecondCounter *perfCounter packetsSentPerSecondCounter perfCounter
bytesReceivedPerSecondCounter *perfCounter bytesReceivedPerSecondCounter perfCounter
bytesSentPerSecondCounter *perfCounter bytesSentPerSecondCounter perfCounter
packetsReceivedDiscardedCounter *perfCounter packetsReceivedDiscardedCounter perfCounter
packetsReceivedErrorsCounter *perfCounter packetsReceivedErrorsCounter perfCounter
packetsOutboundDiscardedCounter *perfCounter packetsOutboundDiscardedCounter perfCounter
packetsOutboundErrorsCounter *perfCounter packetsOutboundErrorsCounter perfCounter
mu sync.RWMutex mu sync.RWMutex
adapterStats map[string]cadvisorapi.InterfaceStats adapterStats map[string]cadvisorapi.InterfaceStats

View File

@ -0,0 +1,176 @@
//go:build windows
// +build windows
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package winstats
import (
"errors"
"testing"
cadvisorapi "github.com/google/cadvisor/info/v1"
"github.com/stretchr/testify/assert"
)
const fakeAdapterName = "fake-adapter"
type fakePerfCounterImpl struct {
// Returned name.
name string
// Returned value.
value uint64
// If the perfCounter should raise an error.
raiseError bool
}
func (p *fakePerfCounterImpl) getData() (uint64, error) {
if p.raiseError {
return 0, errors.New("Expected getData error.")
}
return p.value, nil
}
func (p *fakePerfCounterImpl) getDataList() (map[string]uint64, error) {
if p.raiseError {
return nil, errors.New("Expected getDataList error.")
}
data := make(map[string]uint64)
data[p.name] = p.value
return data, nil
}
func newFakedNetworkCounters(raiseError bool) *networkCounter {
counters := make([]*fakePerfCounterImpl, 8)
for i := 0; i < 8; i++ {
counters[i] = &fakePerfCounterImpl{
name: fakeAdapterName,
value: 1,
raiseError: raiseError,
}
}
return &networkCounter{
packetsReceivedPerSecondCounter: counters[0],
packetsSentPerSecondCounter: counters[1],
bytesReceivedPerSecondCounter: counters[2],
bytesSentPerSecondCounter: counters[3],
packetsReceivedDiscardedCounter: counters[4],
packetsReceivedErrorsCounter: counters[5],
packetsOutboundDiscardedCounter: counters[6],
packetsOutboundErrorsCounter: counters[7],
adapterStats: map[string]cadvisorapi.InterfaceStats{},
}
}
func TestNewNetworkCounters(t *testing.T) {
_, err := newNetworkCounters()
assert.NoError(t, err)
}
func TestNetworkGetData(t *testing.T) {
netCounter := newFakedNetworkCounters(false)
// Add a net adapter that no longer exists in the adapterStats cache. It will
// have to be cleaned up after processing the data.
netCounter.adapterStats["other-fake-adapter"] = cadvisorapi.InterfaceStats{}
data, err := netCounter.getData()
assert.NoError(t, err)
// Make sure that we only have data from a single net adapter.
expectedStats := cadvisorapi.InterfaceStats{
Name: fakeAdapterName,
RxPackets: 1,
TxPackets: 1,
RxBytes: 1,
TxBytes: 1,
RxDropped: 1,
RxErrors: 1,
TxDropped: 1,
TxErrors: 1,
}
assert.Equal(t, []cadvisorapi.InterfaceStats{expectedStats}, data)
// The returned data is cumulative, so the resulting values should be double on a second call.
data, err = netCounter.getData()
assert.NoError(t, err)
expectedStats = cadvisorapi.InterfaceStats{
Name: fakeAdapterName,
RxPackets: 2,
TxPackets: 2,
RxBytes: 2,
TxBytes: 2,
RxDropped: 1,
RxErrors: 1,
TxDropped: 1,
TxErrors: 1,
}
assert.Equal(t, []cadvisorapi.InterfaceStats{expectedStats}, data)
}
func TestNetworkGetDataFailures(t *testing.T) {
netCounter := newFakedNetworkCounters(true)
_, err := netCounter.getData()
expectedMsg := "Expected getDataList error."
if err == nil || err.Error() != expectedMsg {
t.Fatalf("expected error message `%s` but got `%v`", expectedMsg, err)
}
_, err = netCounter.getData()
netCounter.packetsReceivedPerSecondCounter.(*fakePerfCounterImpl).raiseError = false
if err == nil || err.Error() != expectedMsg {
t.Fatalf("expected error message `%s` but got `%v`", expectedMsg, err)
}
_, err = netCounter.getData()
netCounter.packetsSentPerSecondCounter.(*fakePerfCounterImpl).raiseError = false
if err == nil || err.Error() != expectedMsg {
t.Fatalf("expected error message `%s` but got `%v`", expectedMsg, err)
}
_, err = netCounter.getData()
netCounter.bytesReceivedPerSecondCounter.(*fakePerfCounterImpl).raiseError = false
if err == nil || err.Error() != expectedMsg {
t.Fatalf("expected error message `%s` but got `%v`", expectedMsg, err)
}
_, err = netCounter.getData()
netCounter.bytesSentPerSecondCounter.(*fakePerfCounterImpl).raiseError = false
if err == nil || err.Error() != expectedMsg {
t.Fatalf("expected error message `%s` but got `%v`", expectedMsg, err)
}
_, err = netCounter.getData()
netCounter.packetsReceivedDiscardedCounter.(*fakePerfCounterImpl).raiseError = false
if err == nil || err.Error() != expectedMsg {
t.Fatalf("expected error message `%s` but got `%v`", expectedMsg, err)
}
_, err = netCounter.getData()
netCounter.packetsReceivedErrorsCounter.(*fakePerfCounterImpl).raiseError = false
if err == nil || err.Error() != expectedMsg {
t.Fatalf("expected error message `%s` but got `%v`", expectedMsg, err)
}
_, err = netCounter.getData()
netCounter.packetsOutboundDiscardedCounter.(*fakePerfCounterImpl).raiseError = false
if err == nil || err.Error() != expectedMsg {
t.Fatalf("expected error message `%s` but got `%v`", expectedMsg, err)
}
}

View File

@ -202,9 +202,9 @@ func (p *perfCounterNodeStatsClient) getNodeInfo() nodeInfo {
return p.nodeInfo return p.nodeInfo
} }
func (p *perfCounterNodeStatsClient) collectMetricsData(cpuCounter, memWorkingSetCounter, memCommittedBytesCounter *perfCounter, networkAdapterCounter *networkCounter) { func (p *perfCounterNodeStatsClient) collectMetricsData(cpuCounter, memWorkingSetCounter, memCommittedBytesCounter perfCounter, networkAdapterCounter *networkCounter) {
cpuValue, err := cpuCounter.getData() cpuValue, err := cpuCounter.getData()
cpuCores := runtime.NumCPU() cpuCores := ProcessorCount()
if err != nil { if err != nil {
klog.ErrorS(err, "Unable to get cpu perf counter data") klog.ErrorS(err, "Unable to get cpu perf counter data")
return return

View File

@ -0,0 +1,188 @@
//go:build windows
// +build windows
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package winstats
import (
"os"
"strconv"
"testing"
"time"
cadvisorapi "github.com/google/cadvisor/info/v1"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/util/wait"
)
func TestMonitoring(t *testing.T) {
counterClient, err := NewPerfCounterClient()
assert.NoError(t, err)
// assert that startMonitoring has been called. nodeInfo should be set.
assert.NotNil(t, counterClient.(*StatsClient).client.getNodeInfo())
// Wait until we get a non-zero node metrics.
if pollErr := wait.Poll(100*time.Millisecond, 5*perfCounterUpdatePeriod, func() (bool, error) {
metrics, _ := counterClient.(*StatsClient).client.getNodeMetrics()
if metrics.memoryPrivWorkingSetBytes != 0 {
return true, nil
}
return false, nil
}); pollErr != nil {
t.Fatalf("Encountered error: `%v'", pollErr)
}
}
func TestGetMachineInfo(t *testing.T) {
p := perfCounterNodeStatsClient{
nodeInfo: nodeInfo{
memoryPhysicalCapacityBytes: 100,
},
}
machineInfo, err := p.getMachineInfo()
assert.NoError(t, err)
assert.Equal(t, uint64(100), machineInfo.MemoryCapacity)
hostname, _ := os.Hostname()
assert.Equal(t, hostname, machineInfo.MachineID)
// Check if it's an UUID.
_, err = uuid.Parse(machineInfo.SystemUUID)
assert.NoError(t, err)
id, err := strconv.Atoi(machineInfo.BootID)
assert.NoError(t, err)
assert.NotZero(t, id)
}
func TestGetVersionInfo(t *testing.T) {
client := perfCounterNodeStatsClient{
nodeInfo: nodeInfo{
kernelVersion: "foo",
osImageVersion: "lish",
},
}
info, _ := client.getVersionInfo()
expected := &cadvisorapi.VersionInfo{
KernelVersion: "foo",
ContainerOsVersion: "lish",
}
assert.Equal(t, expected, info)
}
func TestCollectMetricsData(t *testing.T) {
p := perfCounterNodeStatsClient{}
cpuCounter := &fakePerfCounterImpl{
value: 1,
raiseError: true,
}
memWorkingSetCounter := &fakePerfCounterImpl{
value: 2,
raiseError: true,
}
memCommittedBytesCounter := &fakePerfCounterImpl{
value: 3,
raiseError: true,
}
networkAdapterCounter := newFakedNetworkCounters(true)
// Checking the error cases first.
p.collectMetricsData(cpuCounter, memWorkingSetCounter, memCommittedBytesCounter, networkAdapterCounter)
metrics, _ := p.getNodeMetrics()
expectedMetrics := nodeMetrics{}
assert.Equal(t, expectedMetrics, metrics)
cpuCounter.raiseError = false
p.collectMetricsData(cpuCounter, memWorkingSetCounter, memCommittedBytesCounter, networkAdapterCounter)
metrics, _ = p.getNodeMetrics()
assert.Equal(t, expectedMetrics, metrics)
memWorkingSetCounter.raiseError = false
p.collectMetricsData(cpuCounter, memWorkingSetCounter, memCommittedBytesCounter, networkAdapterCounter)
metrics, _ = p.getNodeMetrics()
assert.Equal(t, expectedMetrics, metrics)
memCommittedBytesCounter.raiseError = false
p.collectMetricsData(cpuCounter, memWorkingSetCounter, memCommittedBytesCounter, networkAdapterCounter)
metrics, _ = p.getNodeMetrics()
assert.Equal(t, expectedMetrics, metrics)
networkAdapterCounter = newFakedNetworkCounters(false)
p.collectMetricsData(cpuCounter, memWorkingSetCounter, memCommittedBytesCounter, networkAdapterCounter)
metrics, _ = p.getNodeMetrics()
expectedMetrics = nodeMetrics{
cpuUsageCoreNanoSeconds: uint64(ProcessorCount()) * 1e7,
cpuUsageNanoCores: 0,
memoryPrivWorkingSetBytes: 2,
memoryCommittedBytes: 3,
interfaceStats: networkAdapterCounter.listInterfaceStats(),
timeStamp: time.Now(),
}
assert.Equal(t, expectedMetrics, metrics)
}
func TestConvertCPUValue(t *testing.T) {
testCases := []struct {
cpuValue uint64
expected uint64
}{
{cpuValue: uint64(50), expected: uint64(2000000000)},
{cpuValue: uint64(0), expected: uint64(0)},
{cpuValue: uint64(100), expected: uint64(4000000000)},
}
var cpuCores = 4
for _, tc := range testCases {
p := perfCounterNodeStatsClient{}
newValue := p.convertCPUValue(cpuCores, tc.cpuValue)
assert.Equal(t, tc.expected, newValue)
}
}
func TestGetCPUUsageNanoCores(t *testing.T) {
testCases := []struct {
latestValue uint64
previousValue uint64
expected uint64
}{
{latestValue: uint64(0), previousValue: uint64(0), expected: uint64(0)},
{latestValue: uint64(2000000000), previousValue: uint64(0), expected: uint64(200000000)},
{latestValue: uint64(5000000000), previousValue: uint64(2000000000), expected: uint64(300000000)},
}
for _, tc := range testCases {
p := perfCounterNodeStatsClient{}
p.cpuUsageCoreNanoSecondsCache = cpuUsageCoreNanoSecondsCache{
latestValue: tc.latestValue,
previousValue: tc.previousValue,
}
cpuUsageNanoCores := p.getCPUUsageNanoCores()
assert.Equal(t, tc.expected, cpuUsageNanoCores)
}
}
func testGetPhysicallyInstalledSystemMemoryBytes(t *testing.T) {
totalMemory, err := getPhysicallyInstalledSystemMemoryBytes()
assert.NoError(t, err)
assert.NotZero(t, totalMemory)
}

View File

@ -41,12 +41,17 @@ const (
defaultCachePeriod = 10 * time.Second defaultCachePeriod = 10 * time.Second
) )
type perfCounter struct { type perfCounter interface {
getData() (uint64, error)
getDataList() (map[string]uint64, error)
}
type perfCounterImpl struct {
queryHandle win_pdh.PDH_HQUERY queryHandle win_pdh.PDH_HQUERY
counterHandle win_pdh.PDH_HCOUNTER counterHandle win_pdh.PDH_HCOUNTER
} }
func newPerfCounter(counter string) (*perfCounter, error) { func newPerfCounter(counter string) (perfCounter, error) {
var queryHandle win_pdh.PDH_HQUERY var queryHandle win_pdh.PDH_HQUERY
var counterHandle win_pdh.PDH_HCOUNTER var counterHandle win_pdh.PDH_HCOUNTER
@ -65,35 +70,20 @@ func newPerfCounter(counter string) (*perfCounter, error) {
return nil, fmt.Errorf("unable to collect data from counter. Error code is %x", ret) return nil, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
} }
return &perfCounter{ return &perfCounterImpl{
queryHandle: queryHandle, queryHandle: queryHandle,
counterHandle: counterHandle, counterHandle: counterHandle,
}, nil }, nil
} }
// getData is used for getting data without * in counter name. // getData is used for getting data without * in counter name.
func (p *perfCounter) getData() (uint64, error) { func (p *perfCounterImpl) getData() (uint64, error) {
ret := win_pdh.PdhCollectQueryData(p.queryHandle) filledBuf, bufCount, err := p.getQueriedData()
if ret != win_pdh.ERROR_SUCCESS { if err != nil {
return 0, fmt.Errorf("unable to collect data from counter. Error code is %x", ret) return 0, err
}
var bufSize, bufCount uint32
var size = uint32(unsafe.Sizeof(win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{}))
var emptyBuf [1]win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr.
var data uint64
ret = win_pdh.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &emptyBuf[0])
if ret != win_pdh.PDH_MORE_DATA {
return 0, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
}
filledBuf := make([]win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size)
ret = win_pdh.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &filledBuf[0])
if ret != win_pdh.ERROR_SUCCESS {
return 0, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
} }
var data uint64 = 0
for i := 0; i < int(bufCount); i++ { for i := 0; i < int(bufCount); i++ {
c := filledBuf[i] c := filledBuf[i]
data = uint64(c.FmtValue.DoubleValue) data = uint64(c.FmtValue.DoubleValue)
@ -102,29 +92,14 @@ func (p *perfCounter) getData() (uint64, error) {
return data, nil return data, nil
} }
// getData is used for getting data with * in counter name. // getDataList is used for getting data with * in counter name.
func (p *perfCounter) getDataList() (map[string]uint64, error) { func (p *perfCounterImpl) getDataList() (map[string]uint64, error) {
ret := win_pdh.PdhCollectQueryData(p.queryHandle) filledBuf, bufCount, err := p.getQueriedData()
if ret != win_pdh.ERROR_SUCCESS { if err != nil {
return nil, fmt.Errorf("unable to collect data from counter. Error code is %x", ret) return nil, err
} }
var bufSize, bufCount uint32
var size = uint32(unsafe.Sizeof(win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{}))
var emptyBuf [1]win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr.
data := map[string]uint64{} data := map[string]uint64{}
ret = win_pdh.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &emptyBuf[0])
if ret != win_pdh.PDH_MORE_DATA {
return nil, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
}
filledBuf := make([]win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size)
ret = win_pdh.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &filledBuf[0])
if ret != win_pdh.ERROR_SUCCESS {
return nil, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
}
for i := 0; i < int(bufCount); i++ { for i := 0; i < int(bufCount); i++ {
c := filledBuf[i] c := filledBuf[i]
value := uint64(c.FmtValue.DoubleValue) value := uint64(c.FmtValue.DoubleValue)
@ -134,3 +109,28 @@ func (p *perfCounter) getDataList() (map[string]uint64, error) {
return data, nil return data, nil
} }
// getQueriedData is used for getting data using the given query handle.
func (p *perfCounterImpl) getQueriedData() ([]win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, uint32, error) {
ret := win_pdh.PdhCollectQueryData(p.queryHandle)
if ret != win_pdh.ERROR_SUCCESS {
return nil, 0, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
}
var bufSize, bufCount uint32
var size = uint32(unsafe.Sizeof(win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{}))
var emptyBuf [1]win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr.
ret = win_pdh.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &emptyBuf[0])
if ret != win_pdh.PDH_MORE_DATA {
return nil, 0, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
}
filledBuf := make([]win_pdh.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size)
ret = win_pdh.PdhGetFormattedCounterArrayDouble(p.counterHandle, &bufSize, &bufCount, &filledBuf[0])
if ret != win_pdh.ERROR_SUCCESS {
return nil, 0, fmt.Errorf("unable to collect data from counter. Error code is %x", ret)
}
return filledBuf, bufCount, nil
}

View File

@ -0,0 +1,136 @@
//go:build windows
// +build windows
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package winstats
import (
"testing"
"time"
"k8s.io/apimachinery/pkg/util/wait"
)
func TestPerfCounter(t *testing.T) {
testCases := map[string]struct {
counter string
skipCheck bool
expectErr bool
expectedErrMsg string
}{
"CPU Query": {
counter: cpuQuery,
},
"Memory Prvate Working Set Query": {
counter: memoryPrivWorkingSetQuery,
},
"Memory Committed Bytes Query": {
counter: memoryCommittedBytesQuery,
},
"Net Adapter Packets Received/sec Query": {
counter: packetsReceivedPerSecondQuery,
skipCheck: true,
},
"Net Adapter Packets Sent/sec Query": {
counter: packetsSentPerSecondQuery,
skipCheck: true,
},
"Net Adapter Bytes Received/sec Query": {
counter: bytesReceivedPerSecondQuery,
skipCheck: true,
},
"Net Adapter Bytes Sent/sec Query": {
counter: bytesSentPerSecondQuery,
skipCheck: true,
},
"Net Adapter Packets Received Discarded Query": {
counter: packetsReceivedDiscardedQuery,
skipCheck: true,
},
"Net Adapter Packets Received Errors Query": {
counter: packetsReceivedErrorsQuery,
skipCheck: true,
},
"Net Adapter Packets Outbound Discarded Query": {
counter: packetsOutboundDiscardedQuery,
skipCheck: true,
},
"Net Adapter Packets Outbound Errors Query": {
counter: packetsOutboundErrorsQuery,
skipCheck: true,
},
"Invalid Query": {
counter: "foo",
expectErr: true,
expectedErrMsg: "unable to add process counter: foo. Error code is c0000bc0",
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
counter, err := newPerfCounter(tc.counter)
if tc.expectErr {
if err == nil || err.Error() != tc.expectedErrMsg {
t.Fatalf("expected error message `%s` but got `%v`", tc.expectedErrMsg, err)
}
return
}
// There are some counters that we can't expect to see any non-zero values, like the
// networking-related counters.
if tc.skipCheck {
return
}
// Wait until we get a non-zero perf counter data.
if pollErr := wait.Poll(100*time.Millisecond, 5*perfCounterUpdatePeriod, func() (bool, error) {
data, err := counter.getData()
if err != nil {
return false, err
}
if data != 0 {
return true, nil
}
return false, nil
}); pollErr != nil {
t.Fatalf("Encountered error: `%v'", pollErr)
return
}
// Check that we have at least one non-zero value in the data list.
if pollErr := wait.Poll(100*time.Millisecond, 5*perfCounterUpdatePeriod, func() (bool, error) {
dataList, err := counter.getDataList()
if err != nil {
return false, err
}
for _, value := range dataList {
if value != 0 {
return true, nil
}
}
return false, nil
}); pollErr != nil {
t.Fatalf("Encountered error: `%v'", pollErr)
}
})
}
}

View File

@ -20,6 +20,7 @@ limitations under the License.
package winstats package winstats
import ( import (
"os"
"testing" "testing"
"time" "time"
@ -133,44 +134,24 @@ func TestWinVersionInfo(t *testing.T) {
KernelVersion: "v42"}) KernelVersion: "v42"})
} }
func TestConvertCPUValue(t *testing.T) { func TestGetDirFsInfo(t *testing.T) {
testCases := []struct { c := getClient(t)
cpuValue uint64
expected uint64
}{
{cpuValue: uint64(50), expected: uint64(2000000000)},
{cpuValue: uint64(0), expected: uint64(0)},
{cpuValue: uint64(100), expected: uint64(4000000000)},
}
var cpuCores = 4
for _, tc := range testCases { // Try with a non-existent path.
p := perfCounterNodeStatsClient{} _, err := c.GetDirFsInfo("foo/lish")
newValue := p.convertCPUValue(cpuCores, tc.cpuValue) expectedErrMsg := "The system cannot find the path specified."
assert.Equal(t, tc.expected, newValue) if err == nil || err.Error() != expectedErrMsg {
} t.Fatalf("expected error message `%s` but got `%v`", expectedErrMsg, err)
}
func TestGetCPUUsageNanoCores(t *testing.T) {
testCases := []struct {
latestValue uint64
previousValue uint64
expected uint64
}{
{latestValue: uint64(0), previousValue: uint64(0), expected: uint64(0)},
{latestValue: uint64(2000000000), previousValue: uint64(0), expected: uint64(200000000)},
{latestValue: uint64(5000000000), previousValue: uint64(2000000000), expected: uint64(300000000)},
} }
for _, tc := range testCases { dir, err := os.MkdirTemp("", "fsinfo")
p := perfCounterNodeStatsClient{} assert.NoError(t, err)
p.cpuUsageCoreNanoSecondsCache = cpuUsageCoreNanoSecondsCache{ defer os.RemoveAll(dir)
latestValue: tc.latestValue,
previousValue: tc.previousValue, fsInfo, err := c.GetDirFsInfo(dir)
} assert.NoError(t, err)
cpuUsageNanoCores := p.getCPUUsageNanoCores() assert.NotZero(t, fsInfo.Capacity)
assert.Equal(t, tc.expected, cpuUsageNanoCores) assert.NotZero(t, fsInfo.Available)
}
} }
func getClient(t *testing.T) Client { func getClient(t *testing.T) Client {