mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-10-17 18:19:04 +02:00
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
167 lines
6.4 KiB
Go
167 lines
6.4 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
// Copyright The Prometheus 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.
|
|
|
|
//go:build windows
|
|
|
|
package mi
|
|
|
|
import (
|
|
"math"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
// newField returns a settable reflect.Value holding the zero value of T.
|
|
func newField[T any]() reflect.Value {
|
|
return reflect.New(reflect.TypeFor[T]()).Elem()
|
|
}
|
|
|
|
func TestSetField(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
element Element
|
|
field reflect.Value
|
|
want any
|
|
wantErr bool
|
|
}{
|
|
{"boolean", Element{value: 1, valueType: ValueTypeBOOLEAN}, newField[bool](), true, false},
|
|
{"boolean_into_int", Element{value: 1, valueType: ValueTypeBOOLEAN}, newField[int](), nil, true},
|
|
|
|
{"uint32_into_uint64", Element{value: math.MaxUint32, valueType: ValueTypeUINT32}, newField[uint64](), uint64(math.MaxUint32), false},
|
|
{"uint64_overflows_uint32", Element{value: math.MaxUint32 + 1, valueType: ValueTypeUINT64}, newField[uint32](), nil, true},
|
|
{"uint32_into_string", Element{value: 1, valueType: ValueTypeUINT32}, newField[string](), nil, true},
|
|
|
|
{"sint8_sign_extends", Element{value: 0xFF, valueType: ValueTypeSINT8}, newField[int64](), int64(-1), false},
|
|
{"sint16_sign_extends", Element{value: 0xFFFF, valueType: ValueTypeSINT16}, newField[int64](), int64(-1), false},
|
|
{"sint32_sign_extends", Element{value: 0xFFFFFFFF, valueType: ValueTypeSINT32}, newField[int64](), int64(-1), false},
|
|
{"sint16_positive_into_uint16", Element{value: 42, valueType: ValueTypeSINT16}, newField[uint16](), uint16(42), false},
|
|
{"sint16_negative_into_uint16", Element{value: 0xFFFF, valueType: ValueTypeSINT16}, newField[uint16](), nil, true},
|
|
{"sint32_overflows_int8", Element{value: 1000, valueType: ValueTypeSINT32}, newField[int8](), nil, true},
|
|
|
|
{"real32_into_float32", Element{value: uintptr(math.Float32bits(1.5)), valueType: ValueTypeREAL32}, newField[float32](), float32(1.5), false},
|
|
{"real32_into_float64", Element{value: uintptr(math.Float32bits(-2.25)), valueType: ValueTypeREAL32}, newField[float64](), float64(-2.25), false},
|
|
{"real64_into_float64", Element{value: uintptr(math.Float64bits(9.875)), valueType: ValueTypeREAL64}, newField[float64](), float64(9.875), false},
|
|
{"real64_into_float32", Element{value: uintptr(math.Float64bits(1)), valueType: ValueTypeREAL64}, newField[float32](), nil, true},
|
|
|
|
{"string_null_keeps_zero_value", Element{value: 0, valueType: ValueTypeSTRING}, newField[string](), "", false},
|
|
{"string_into_int", Element{value: 0, valueType: ValueTypeSTRING}, newField[int](), nil, true},
|
|
|
|
{"char16_into_string", Element{value: 'A', valueType: ValueTypeCHAR16}, newField[string](), "A", false},
|
|
{"char16_euro_into_string", Element{value: 0x20AC, valueType: ValueTypeCHAR16}, newField[string](), "€", false},
|
|
{"char16_surrogate_into_string", Element{value: 0xD83D, valueType: ValueTypeCHAR16}, newField[string](), "�", false},
|
|
{"char16_into_uint16", Element{value: 0xD83D, valueType: ValueTypeCHAR16}, newField[uint16](), uint16(0xD83D), false},
|
|
{"char16_overflows_uint8", Element{value: 0x20AC, valueType: ValueTypeCHAR16}, newField[uint8](), nil, true},
|
|
{"char16_into_bool", Element{value: 'A', valueType: ValueTypeCHAR16}, newField[bool](), nil, true},
|
|
|
|
{"null_uint32_keeps_zero_value", Element{value: 42, valueType: ValueTypeUINT32, flags: flagNull}, newField[uint32](), uint32(0), false},
|
|
{"null_real64_keeps_zero_value", Element{value: uintptr(math.Float64bits(9.875)), valueType: ValueTypeREAL64, flags: flagNull}, newField[float64](), float64(0), false},
|
|
{"null_char16_into_string_keeps_zero_value", Element{value: 'A', valueType: ValueTypeCHAR16, flags: flagNull}, newField[string](), "", false},
|
|
{"null_still_rejects_wrong_type", Element{value: 42, valueType: ValueTypeUINT32, flags: flagNull}, newField[string](), nil, true},
|
|
|
|
{"uint16a_into_wrong_slice", Element{value: 0, valueType: ValueTypeUINT16A}, newField[[]uint32](), nil, true},
|
|
{"unsupported_type", Element{value: 0, valueType: ValueTypeDATETIME}, newField[string](), nil, true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
err := setField("Val", tt.field, &tt.element)
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.want, tt.field.Interface())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPrepareUnmarshal(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
type row struct {
|
|
Name string `mi:"Name"`
|
|
Internal int
|
|
ID uint32 `mi:"ProcessId"`
|
|
}
|
|
|
|
t.Run("resets_valid_slice", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dst := []row{{Name: "stale"}}
|
|
|
|
fields, err := prepareUnmarshal(&dst)
|
|
require.NoError(t, err)
|
|
require.Empty(t, dst)
|
|
require.Len(t, fields, 2)
|
|
require.Equal(t, 0, fields[0].index)
|
|
require.Equal(t, "Name", fields[0].tag)
|
|
require.Equal(t, "Name", windows.UTF16PtrToString(fields[0].name))
|
|
require.Equal(t, 2, fields[1].index)
|
|
require.Equal(t, "ProcessId", fields[1].tag)
|
|
require.Equal(t, "ProcessId", windows.UTF16PtrToString(fields[1].name))
|
|
})
|
|
|
|
t.Run("fields_are_cached", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
first, err := prepareUnmarshal(new([]row))
|
|
require.NoError(t, err)
|
|
|
|
second, err := prepareUnmarshal(new([]row))
|
|
require.NoError(t, err)
|
|
|
|
require.Same(t, &first[0], &second[0])
|
|
})
|
|
|
|
t.Run("tag_with_nul", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := prepareUnmarshal(new([]struct {
|
|
Name string `mi:"Na\x00me"`
|
|
}))
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("nil_pointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := prepareUnmarshal((*[]row)(nil))
|
|
require.ErrorIs(t, err, ErrInvalidEntityType)
|
|
})
|
|
|
|
t.Run("slice_of_int", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := prepareUnmarshal(new([]int))
|
|
require.ErrorIs(t, err, ErrInvalidEntityType)
|
|
})
|
|
|
|
t.Run("slice_of_struct_pointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := prepareUnmarshal(new([]*row))
|
|
require.ErrorIs(t, err, ErrInvalidEntityType)
|
|
})
|
|
}
|