Skip to content

Commit

Permalink
chore: add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
alxbl committed Apr 30, 2024
1 parent 090655b commit 9dac71c
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package win_perf_counters // import "github.com/open-telemetry/opentelemetry-col

import (
"errors"
"fmt"
"syscall"
"time"
"unicode/utf16"
Expand Down Expand Up @@ -152,14 +151,9 @@ func (m *PerformanceQueryImpl) GetFormattedCounterArrayDouble(hCounter PDH_HCOUN
if ret = PdhGetFormattedCounterArrayDouble(hCounter, &buffSize, &itemCount, &buff[0]); ret == ERROR_SUCCESS {
items := unsafe.Slice((*PDH_FMT_COUNTERVALUE_ITEM_DOUBLE)(unsafe.Pointer(&buff[0])), itemCount)
values := make([]CounterValue, 0, itemCount)
for i, item := range items {
for _, item := range items {
if item.FmtValue.CStatus == PDH_CSTATUS_VALID_DATA || item.FmtValue.CStatus == PDH_CSTATUS_NEW_DATA {
var val CounterValue
if i == 0 { // Be consistent with perfmon output: instance 0 does not display index.
val = CounterValue{UTF16PtrToString(item.SzName), item.FmtValue.DoubleValue}
} else {
val = CounterValue{fmt.Sprintf("%s_%d", UTF16PtrToString(item.SzName), i), item.FmtValue.DoubleValue}
}
val := CounterValue{UTF16PtrToString(item.SzName), item.FmtValue.DoubleValue}

values = append(values, val)
}
Expand Down
40 changes: 29 additions & 11 deletions pkg/winperfcounters/watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (pc *perfCounter) ScrapeData() ([]CounterValue, error) {
return nil, fmt.Errorf("failed to format data for performance counter '%s': %w", pc.path, err)
}

vals = removeTotalIfMultipleValues(vals)
vals = cleanupScrapedValues(vals)
return vals, nil
}

Expand All @@ -132,24 +132,42 @@ func ExpandWildCardPath(counterPath string) ([]string, error) {
return win_perf_counters.ExpandWildCardPath(counterPath)
}

func removeTotalIfMultipleValues(vals []CounterValue) []CounterValue {
// cleanupScrapedValues handles instance name collisions and standardizes names.
// It cleans up the list in-place to avoid unnecessary copies.
func cleanupScrapedValues(vals []CounterValue) []CounterValue {
if len(vals) == 0 {
return vals
}

if len(vals) == 1 {
// if there is only one item & the instance name is "_Total", clear the instance name
if vals[0].InstanceName == totalInstanceName {
vals[0].InstanceName = ""
}
// If there is only one "_Total" instance, clear the instance name.
if len(vals) == 1 && vals[0].InstanceName == totalInstanceName {
vals[0].InstanceName = ""
return vals
}

// if there is more than one item, remove an item that has the instance name "_Total"
for i, val := range vals {
if val.InstanceName == totalInstanceName {
return removeItemAt(vals, i)
occurences := map[string]int{}
totalIndex := -1

for i := range vals {
instanceName := vals[i].InstanceName

if instanceName == totalInstanceName {
// Remember if a "_Total" instance was present.
totalIndex = i
}

if n, ok := occurences[instanceName]; ok {
// Append indices to duplicate instance names.
occurences[instanceName]++
vals[i].InstanceName = fmt.Sprintf("%s_%d", instanceName, n)
} else {
occurences[instanceName] = 1
}
}

// Remove the "_Total" instance, as it can be computed with a sum aggregation.
if totalIndex >= 0 {
return removeItemAt(vals, totalIndex)
}

return vals
Expand Down
139 changes: 139 additions & 0 deletions pkg/winperfcounters/watcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,142 @@ func TestPerfCounter_ScrapeData(t *testing.T) {
})
}
}

func Test_InstanceNameIndexing(t *testing.T) {
type testCase struct {
name string
vals []CounterValue
expected []CounterValue
}

testCases := []testCase{
{
name: "Multiple distinct instances",
vals: []CounterValue{
{
InstanceName: "A",
Value: 1.0,
},
{
InstanceName: "B",
Value: 1.0,
},
{
InstanceName: "C",
Value: 1.0,
},
},
expected: []CounterValue{
{
InstanceName: "A",
Value: 1.0,
},
{
InstanceName: "B",
Value: 1.0,
},
{
InstanceName: "C",
Value: 1.0,
},
},
},
{
name: "Single repeated instance name",
vals: []CounterValue{
{
InstanceName: "A",
Value: 1.0,
},
{
InstanceName: "A",
Value: 1.0,
},
{
InstanceName: "A",
Value: 1.0,
},
},
expected: []CounterValue{
{
InstanceName: "A",
Value: 1.0,
},
{
InstanceName: "A_1",
Value: 1.0,
},
{
InstanceName: "A_2",
Value: 1.0,
},
},
},
{
name: "Multiple repeated instance name",
vals: []CounterValue{
{
InstanceName: "A",
Value: 1.0,
},
{
InstanceName: "B",
Value: 1.0,
},
{
InstanceName: "A",
Value: 1.0,
},
{
InstanceName: "B",
Value: 1.0,
},
{
InstanceName: "B",
Value: 1.0,
},
{
InstanceName: "C",
Value: 1.0,
},
},
expected: []CounterValue{
{
InstanceName: "A",
Value: 1.0,
},
{
InstanceName: "B",
Value: 1.0,
},
{
InstanceName: "A_1",
Value: 1.0,
},
{
InstanceName: "B_1",
Value: 1.0,
},
{
InstanceName: "B_2",
Value: 1.0,
},
{
InstanceName: "C",
Value: 1.0,
},
},
},
}

for _, test := range testCases {
actual := cleanupScrapedValues(test.vals)
t.Run(test.name, func(t *testing.T) {
compareCounterValues(t, test.expected, actual)
})
}
}

func compareCounterValues(t *testing.T, expected []CounterValue, actual []CounterValue) {
assert.EqualValues(t, expected, actual)
}

0 comments on commit 9dac71c

Please sign in to comment.