mirror of
https://github.com/hikhvar/mqtt2prometheus.git
synced 2026-02-14 09:59:52 +00:00
840 lines
19 KiB
Go
840 lines
19 KiB
Go
package metrics
|
|
|
|
import (
|
|
"os"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hikhvar/mqtt2prometheus/pkg/config"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
var testNowElapsed time.Duration
|
|
|
|
func testNow() time.Time {
|
|
now, err := time.Parse(
|
|
time.RFC3339,
|
|
"2020-11-01T22:08:41+00:00")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
now = now.Add(testNowElapsed)
|
|
return now
|
|
}
|
|
|
|
func TestParser_parseMetric(t *testing.T) {
|
|
stateDir, err := os.MkdirTemp("", "parser_test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(stateDir)
|
|
|
|
now = testNow
|
|
type fields struct {
|
|
metricConfigs map[string][]*config.MetricConfig
|
|
}
|
|
type args struct {
|
|
metricPath string
|
|
deviceID string
|
|
value interface{}
|
|
}
|
|
|
|
var errorValue float64 = 42.44
|
|
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
want Metric
|
|
wantErr bool
|
|
elapseNow time.Duration
|
|
}{
|
|
{
|
|
name: "value without timestamp",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"temperature": {
|
|
{
|
|
PrometheusName: "temperature",
|
|
ValueType: "gauge",
|
|
OmitTimestamp: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "temperature",
|
|
deviceID: "dht22",
|
|
value: 12.6,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("temperature", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 12.6,
|
|
IngestTime: time.Time{},
|
|
Topic: "",
|
|
},
|
|
},
|
|
{
|
|
name: "string value",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"temperature": {
|
|
{
|
|
PrometheusName: "temperature",
|
|
ValueType: "gauge",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "temperature",
|
|
deviceID: "dht22",
|
|
value: "12.6",
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("temperature", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 12.6,
|
|
IngestTime: testNow(),
|
|
Topic: "",
|
|
},
|
|
},
|
|
{
|
|
name: "scaled string value",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"temperature": {
|
|
{
|
|
PrometheusName: "temperature",
|
|
ValueType: "gauge",
|
|
MQTTValueScale: 0.01,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "temperature",
|
|
deviceID: "dht22",
|
|
value: "12.6",
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("temperature", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 0.126,
|
|
IngestTime: testNow(),
|
|
Topic: "",
|
|
},
|
|
},
|
|
{
|
|
name: "string value failure",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"temperature": {
|
|
{
|
|
PrometheusName: "temperature",
|
|
ValueType: "gauge",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "temperature",
|
|
deviceID: "dht22",
|
|
value: "12.6.5",
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "string value failure with errorValue",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"temperature": {
|
|
{
|
|
PrometheusName: "temperature",
|
|
ValueType: "gauge",
|
|
ErrorValue: &errorValue,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "temperature",
|
|
deviceID: "dht22",
|
|
value: "12.6.5",
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("temperature", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: errorValue,
|
|
IngestTime: testNow(),
|
|
Topic: "",
|
|
},
|
|
},
|
|
{
|
|
name: "float value",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"temperature": {
|
|
{
|
|
PrometheusName: "temperature",
|
|
ValueType: "gauge",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "temperature",
|
|
deviceID: "dht22",
|
|
value: 12.6,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("temperature", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 12.6,
|
|
IngestTime: testNow(),
|
|
Topic: "",
|
|
},
|
|
},
|
|
{
|
|
name: "scaled float value",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"humidity": {
|
|
{
|
|
PrometheusName: "humidity",
|
|
ValueType: "gauge",
|
|
MQTTValueScale: 0.01,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "humidity",
|
|
deviceID: "dht22",
|
|
value: 12.6,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("humidity", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 0.126,
|
|
IngestTime: testNow(),
|
|
Topic: "",
|
|
},
|
|
},
|
|
{
|
|
name: "negative scaled float value",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"humidity": {
|
|
{
|
|
PrometheusName: "humidity",
|
|
ValueType: "gauge",
|
|
MQTTValueScale: -2,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "humidity",
|
|
deviceID: "dht22",
|
|
value: 12.6,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("humidity", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: -25.2,
|
|
IngestTime: testNow(),
|
|
Topic: "",
|
|
},
|
|
},
|
|
{
|
|
name: "bool value true",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"enabled": {
|
|
{
|
|
PrometheusName: "enabled",
|
|
ValueType: "gauge",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "enabled",
|
|
deviceID: "dht22",
|
|
value: true,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("enabled", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 1,
|
|
IngestTime: testNow(),
|
|
Topic: "",
|
|
},
|
|
},
|
|
{
|
|
name: "scaled bool value",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"enabled": {
|
|
{
|
|
PrometheusName: "enabled",
|
|
ValueType: "gauge",
|
|
MQTTValueScale: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "enabled",
|
|
deviceID: "dht22",
|
|
value: true,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("enabled", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 0.5,
|
|
IngestTime: testNow(),
|
|
Topic: "",
|
|
},
|
|
},
|
|
{
|
|
name: "bool value false",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"enabled": {
|
|
{
|
|
PrometheusName: "enabled",
|
|
ValueType: "gauge",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "enabled",
|
|
deviceID: "dht22",
|
|
value: false,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("enabled", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 0,
|
|
IngestTime: testNow(),
|
|
Topic: "",
|
|
},
|
|
},
|
|
{
|
|
name: "string mapping value success",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"enabled": {
|
|
{
|
|
PrometheusName: "enabled",
|
|
ValueType: "gauge",
|
|
StringValueMapping: &config.StringValueMappingConfig{
|
|
Map: map[string]float64{
|
|
"foo": 112,
|
|
"bar": 2,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "enabled",
|
|
deviceID: "dht22",
|
|
value: "foo",
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("enabled", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 112,
|
|
IngestTime: testNow(),
|
|
Topic: "",
|
|
},
|
|
},
|
|
{
|
|
name: "string mapping value failure default to error value",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"enabled": {
|
|
{
|
|
PrometheusName: "enabled",
|
|
ValueType: "gauge",
|
|
ErrorValue: floatP(12333),
|
|
StringValueMapping: &config.StringValueMappingConfig{
|
|
Map: map[string]float64{
|
|
"foo": 112,
|
|
"bar": 2,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "enabled",
|
|
deviceID: "dht22",
|
|
value: "asd",
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("enabled", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 12333,
|
|
IngestTime: testNow(),
|
|
Topic: "",
|
|
},
|
|
},
|
|
{
|
|
name: "string mapping value failure no error value",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"enabled": {
|
|
{
|
|
PrometheusName: "enabled",
|
|
ValueType: "gauge",
|
|
StringValueMapping: &config.StringValueMappingConfig{
|
|
Map: map[string]float64{
|
|
"foo": 112,
|
|
"bar": 2,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "enabled",
|
|
deviceID: "dht22",
|
|
value: "asd",
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "metric not configured",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"enabled": {
|
|
{
|
|
PrometheusName: "enabled",
|
|
ValueType: "gauge",
|
|
ErrorValue: floatP(12333),
|
|
StringValueMapping: &config.StringValueMappingConfig{
|
|
Map: map[string]float64{
|
|
"foo": 112,
|
|
"bar": 2,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "enabled1",
|
|
deviceID: "dht22",
|
|
value: "asd",
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "unexpected type",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"enabled": {
|
|
{
|
|
PrometheusName: "enabled",
|
|
ValueType: "gauge",
|
|
StringValueMapping: &config.StringValueMappingConfig{
|
|
Map: map[string]float64{
|
|
"foo": 112,
|
|
"bar": 2,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "enabled",
|
|
deviceID: "dht22",
|
|
value: []int{3},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "monotonic gauge, step 1: initial value",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"aenergy.total": {
|
|
{
|
|
PrometheusName: "total_energy",
|
|
ValueType: "gauge",
|
|
OmitTimestamp: true,
|
|
ForceMonotonicy: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "aenergy.total",
|
|
deviceID: "shellyplus1pm-foo",
|
|
value: 1.0,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("total_energy", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 1.0,
|
|
},
|
|
},
|
|
{
|
|
name: "monotonic gauge, step 2: monotonic increase does not add offset",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"aenergy.total": {
|
|
{
|
|
PrometheusName: "total_energy",
|
|
ValueType: "gauge",
|
|
OmitTimestamp: true,
|
|
ForceMonotonicy: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "aenergy.total",
|
|
deviceID: "shellyplus1pm-foo",
|
|
value: 2.0,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("total_energy", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 2.0,
|
|
},
|
|
},
|
|
{
|
|
name: "monotonic gauge, step 3: raw metric is reset, last value becomes the new offset",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"aenergy.total": {
|
|
{
|
|
PrometheusName: "total_energy",
|
|
ValueType: "gauge",
|
|
OmitTimestamp: true,
|
|
ForceMonotonicy: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "aenergy.total",
|
|
deviceID: "shellyplus1pm-foo",
|
|
value: 0.0,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("total_energy", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 2.0,
|
|
},
|
|
},
|
|
{
|
|
name: "monotonic gauge, step 4: monotonic increase with offset",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"aenergy.total": {
|
|
{
|
|
PrometheusName: "total_energy",
|
|
ValueType: "gauge",
|
|
OmitTimestamp: true,
|
|
ForceMonotonicy: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "aenergy.total",
|
|
deviceID: "shellyplus1pm-foo",
|
|
value: 1.0,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("total_energy", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 3.0,
|
|
},
|
|
},
|
|
{
|
|
name: "integrate positive values using expressions, step 1",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"apower": {
|
|
{
|
|
PrometheusName: "total_energy",
|
|
ValueType: "gauge",
|
|
OmitTimestamp: true,
|
|
Expression: "value > 0 ? last_result + value * elapsed.Hours() : last_result",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
metricPath: "apower",
|
|
deviceID: "shellyplus1pm-foo",
|
|
value: 60.0,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("total_energy", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 0.0, // No elapsed time yet, hence no integration
|
|
},
|
|
},
|
|
{
|
|
name: "integrate positive values using expressions, step 2",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"apower": {
|
|
{
|
|
PrometheusName: "total_energy",
|
|
ValueType: "gauge",
|
|
OmitTimestamp: true,
|
|
Expression: "value > 0 ? last_result + value * elapsed.Hours() : last_result",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
elapseNow: time.Minute,
|
|
args: args{
|
|
metricPath: "apower",
|
|
deviceID: "shellyplus1pm-foo",
|
|
value: 60.0,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("total_energy", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 1.0, // 60 watts for 1 minute = 1 Wh
|
|
},
|
|
},
|
|
{
|
|
name: "integrate positive values using expressions, step 3",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"apower": {
|
|
{
|
|
PrometheusName: "total_energy",
|
|
ValueType: "gauge",
|
|
OmitTimestamp: true,
|
|
Expression: "value > 0 ? last_result + value * elapsed.Hours() : last_result",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
elapseNow: 2 * time.Minute,
|
|
args: args{
|
|
metricPath: "apower",
|
|
deviceID: "shellyplus1pm-foo",
|
|
value: -60.0,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("total_energy", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 1.0, // negative input is ignored
|
|
},
|
|
},
|
|
{
|
|
name: "integrate positive values using expressions, step 4",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"apower": {
|
|
{
|
|
PrometheusName: "total_energy",
|
|
ValueType: "gauge",
|
|
OmitTimestamp: true,
|
|
Expression: "value > 0 ? last_result + value * elapsed.Hours() : last_result",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
elapseNow: 3 * time.Minute,
|
|
args: args{
|
|
metricPath: "apower",
|
|
deviceID: "shellyplus1pm-foo",
|
|
value: 600.0,
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("total_energy", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 11.0, // 600 watts for 1 minute = 10 Wh
|
|
},
|
|
},
|
|
{
|
|
name: "raw expression, step 1",
|
|
fields: fields{
|
|
map[string][]*config.MetricConfig{
|
|
"apower": {
|
|
{
|
|
PrometheusName: "total_energy",
|
|
ValueType: "gauge",
|
|
OmitTimestamp: true,
|
|
RawExpression: `float(join(filter(split(string(raw_value), ""), { # matches "^[0-9\\.]$" }), ""))`,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
elapseNow: 3 * time.Minute,
|
|
args: args{
|
|
metricPath: "apower",
|
|
deviceID: "shellyplus1pm-foo",
|
|
value: "H42Jj.j44",
|
|
},
|
|
want: Metric{
|
|
Description: prometheus.NewDesc("total_energy", "", []string{"sensor", "topic"}, nil),
|
|
ValueType: prometheus.GaugeValue,
|
|
Value: 42.44,
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
testNowElapsed = tt.elapseNow
|
|
defer func() { testNowElapsed = time.Duration(0) }()
|
|
|
|
p := NewParser(nil, config.JsonParsingConfigDefaults.Separator, stateDir)
|
|
p.metricConfigs = tt.fields.metricConfigs
|
|
|
|
// Find a valid metrics config
|
|
configs := p.findMetricConfigs(tt.args.metricPath, tt.args.deviceID)
|
|
if len(configs) != 1 {
|
|
if !tt.wantErr {
|
|
t.Errorf("MetricConfig not found")
|
|
}
|
|
return
|
|
}
|
|
config := configs[0]
|
|
|
|
id := metricID("", tt.args.metricPath, tt.args.deviceID, config.PrometheusName)
|
|
got, err := p.parseMetric(config, id, tt.args.value)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("parseMetric() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("parseMetric() got = %v, want %v", got, tt.want)
|
|
}
|
|
|
|
if config.ForceMonotonicy || config.Expression != "" {
|
|
if err = p.writeMetricState(id, p.states[id]); err != nil {
|
|
t.Errorf("failed to write metric state: %v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func floatP(f float64) *float64 {
|
|
return &f
|
|
}
|
|
|
|
func TestParser_evalExpression(t *testing.T) {
|
|
now = testNow
|
|
testNowElapsed = time.Duration(0)
|
|
id := "metric"
|
|
|
|
tests := []struct {
|
|
expression string
|
|
values []float64
|
|
results []float64
|
|
}{
|
|
{
|
|
expression: "value + value",
|
|
values: []float64{1, 0, -4},
|
|
results: []float64{2, 0, -8},
|
|
},
|
|
{
|
|
expression: "value - last_value",
|
|
values: []float64{1, 2, 5, 7},
|
|
results: []float64{1, 1, 3, 2},
|
|
},
|
|
{
|
|
expression: "last_result + value",
|
|
values: []float64{1, 2, 3, 4},
|
|
results: []float64{1, 3, 6, 10},
|
|
},
|
|
{
|
|
expression: "last_result + elapsed.Milliseconds()",
|
|
values: []float64{0, 0, 0, 0},
|
|
results: []float64{0, 1000, 2000, 3000},
|
|
},
|
|
{
|
|
expression: "now().Unix()",
|
|
values: []float64{0, 0},
|
|
results: []float64{float64(testNow().Unix()), float64(testNow().Unix() + 1)},
|
|
},
|
|
{
|
|
expression: "int(1.1) + int(1.9)",
|
|
values: []float64{0},
|
|
results: []float64{2},
|
|
},
|
|
{
|
|
expression: "float(elapsed)",
|
|
values: []float64{0, 0},
|
|
results: []float64{0, float64(time.Second)},
|
|
},
|
|
{
|
|
expression: "round(value)",
|
|
values: []float64{1.1, 2.5, 3.9},
|
|
results: []float64{1, 3, 4},
|
|
},
|
|
{
|
|
expression: "ceil(value)",
|
|
values: []float64{1.1, 2.9, 4.0},
|
|
results: []float64{2, 3, 4},
|
|
},
|
|
{
|
|
expression: "floor(value)",
|
|
values: []float64{1.1, 2.9, 4.0},
|
|
results: []float64{1, 2, 4},
|
|
},
|
|
{
|
|
expression: "abs(value)",
|
|
values: []float64{0, 1, -2},
|
|
results: []float64{0, 1, 2},
|
|
},
|
|
{
|
|
expression: "min(value, 0)",
|
|
values: []float64{1, -2, 3, -4},
|
|
results: []float64{0, -2, 0, -4},
|
|
},
|
|
{
|
|
expression: "max(value, 0)",
|
|
values: []float64{1, -2, 3, -4},
|
|
results: []float64{1, 0, 3, 0},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.expression, func(t *testing.T) {
|
|
stateDir, err := os.MkdirTemp("", "parser_test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(stateDir)
|
|
defer func() { testNowElapsed = time.Duration(0) }()
|
|
|
|
p := NewParser(nil, ".", stateDir)
|
|
for i, value := range tt.values {
|
|
got, err := p.evalExpressionValue(id, tt.expression, value, value)
|
|
want := tt.results[i]
|
|
if err != nil {
|
|
t.Errorf("evaluating the %dth value '%v' failed: %v", i, value, err)
|
|
}
|
|
if got != want {
|
|
t.Errorf("unexpected result for %dth value, got %v, want %v", i, got, want)
|
|
}
|
|
// Advance the clock by one second for every sample
|
|
testNowElapsed = testNowElapsed + time.Second
|
|
}
|
|
})
|
|
}
|
|
}
|