func TestParse_Gauges_Delete(t *testing.T) {
	s := NewTestStatsd()
	config := Config{Address: ":8125", DeleteGauges: true}
	s.Config = config
	var err error
	line := "current.users:100|g"
	err = s.parseStatsdLine(line)
	if err != nil {
		t.Errorf("Parsing line %s should not have resulted in an error\n", line)
	}
	err = test_validate_gauge("current.users", 100, s.gauges)
	if err != nil {
		t.Error(err.Error())
	}
	s.Collect()
	err = test_validate_gauge("current.users", 100, s.gauges)
	if err == nil {
		t.Error("current_users_gauge metric should have been deleted")
	}
}
Tests the delete_gauges option
package statsd
import (
	"errors"
	"fmt"
	"path"
	"reflect"
	"testing"
	log "github.com/Sirupsen/logrus"
	"github.com/amonapp/amonagent/internal/testing"
	"github.com/amonapp/amonagent/plugins"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)
func NewTestStatsd() *Statsd {
	s := Statsd{}
	s.done = make(chan struct{})
	s.in = make(chan []byte, s.AllowedPendingMessages)
	s.gauges = make(map[string]cachedgauge)
	s.counters = make(map[string]cachedcounter)
	s.sets = make(map[string]cachedset)
	s.timings = make(map[string]cachedtimings)
	return &s
}
func TestStatsdConfigDefaults(t *testing.T) {
	log.SetLevel(log.WarnLevel)
	plugins.PluginConfigPath = path.Join("/tmp", "plugins-enabled")
	pluginhelper.WritePluginConfig("statsd", "ogusstring")
	s := Statsd{}
	configErr := s.SetConfigDefaults()
	require.NoError(t, configErr)
	assert.True(t, s.Config.DeleteTimings, "Delete Timings is true by default")
	pluginhelper.WritePluginConfig("statsd", "{\"address\": \":8125\"}")
	s2 := Statsd{}
	configErr2 := s2.SetConfigDefaults()
	require.NoError(t, configErr2)
	assert.True(t, s2.Config.DeleteTimings, "Delete Timings, set to true")
}
func TestParse_InvalidLines(t *testing.T) {
	s := NewTestStatsd()
	invalid_lines := []string{
		"i.dont.have.a.pipe:45g",
		"i.dont.have.a.colon45|c",
		"invalid.metric.type:45|e",
		"invalid.plus.minus.non.gauge:+10|c",
		"invalid.plus.minus.non.gauge:+10|s",
		"invalid.plus.minus.non.gauge:+10|ms",
		"invalid.plus.minus.non.gauge:+10|h",
		"invalid.plus.minus.non.gauge:-10|c",
		"invalid.value:foobar|c",
		"invalid.value:d11|c",
		"invalid.value:1d1|c",
	}
	for _, line := range invalid_lines {
		err := s.parseStatsdLine(line)
		if err == nil {
			t.Errorf("Parsing line %s should have resulted in an error\n", line)
		}
	}
}
func TestParse_InvalidSampleRate(t *testing.T) {
	s := NewTestStatsd()
	invalid_lines := []string{
		"invalid.sample.rate:45|c|0.1",
		"invalid.sample.rate.2:45|c|@foo",
		"invalid.sample.rate:45|g|@0.1",
		"invalid.sample.rate:45|s|@0.1",
	}
	for _, line := range invalid_lines {
		err := s.parseStatsdLine(line)
		if err != nil {
			t.Errorf("Parsing line %s should not have resulted in an error\n", line)
		}
	}
	counter_validations := []struct {
		name  string
		value int64
		cache map[string]cachedcounter
	}{
		{
			"invalid.sample.rate",
			45,
			s.counters,
		},
		{
			"invalid.sample.rate.2",
			45,
			s.counters,
		},
	}
	for _, test := range counter_validations {
		err := test_validate_counter(test.name, test.value, test.cache)
		if err != nil {
			t.Error(err.Error())
		}
	}
	err := test_validate_gauge("invalid.sample.rate", 45, s.gauges)
	if err != nil {
		t.Error(err.Error())
	}
	err = test_validate_set("invalid.sample.rate", 1, s.sets)
	if err != nil {
		t.Error(err.Error())
	}
}
func TestParse_DefaultNameParsing(t *testing.T) {
	s := NewTestStatsd()
	valid_lines := []string{
		"valid:1|c",
		"valid.foo-bar:11|c",
	}
	for _, line := range valid_lines {
		err := s.parseStatsdLine(line)
		if err != nil {
			t.Errorf("Parsing line %s should not have resulted in an error\n", line)
		}
	}
	validations := []struct {
		name  string
		value int64
	}{
		{
			"valid",
			1,
		},
		{
			"valid.foo-bar",
			11,
		},
	}
	for _, test := range validations {
		err := test_validate_counter(test.name, test.value, s.counters)
		if err != nil {
			t.Error(err.Error())
		}
	}
}
func TestParse_Fields(t *testing.T) {
	if false {
		t.Errorf("TODO")
	}
}
func TestParseName(t *testing.T) {
	s := NewTestStatsd()
	tests := []struct {
		in_name  string
		out_name string
	}{
		{
			"foobar",
			"foobar",
		},
		{
			"foo.bar",
			"foo.bar",
		},
		{
			"foo.bar-baz",
			"foo.bar-baz",
		},
	}
	for _, test := range tests {
		name, _, _ := s.parseName(test.in_name)
		if name != test.out_name {
			t.Errorf("Expected: %s, got %s", test.out_name, name)
		}
	}
	s.MetricSeparator = "."
	tests = []struct {
		in_name  string
		out_name string
	}{
		{
			"foobar",
			"foobar",
		},
		{
			"foo.bar",
			"foo.bar",
		},
		{
			"foo.bar-baz",
			"foo.bar-baz",
		},
	}
	for _, test := range tests {
		name, _, _ := s.parseName(test.in_name)
		if name != test.out_name {
			t.Errorf("Expected: %s, got %s", test.out_name, name)
		}
	}
}
func TestParse_MeasurementsWithSameName(t *testing.T) {
	s := NewTestStatsd()
	valid_lines := []string{
		"test.counter,host=localhost:1|c",
		"test.counter,host=localhost,region=west:1|c",
	}
	for _, line := range valid_lines {
		err := s.parseStatsdLine(line)
		if err != nil {
			t.Errorf("Parsing line %s should not have resulted in an error\n", line)
		}
	}
	if len(s.counters) != 2 {
		t.Errorf("Expected 2 separate measurements, found %d", len(s.counters))
	}
}
func TestParse_MeasurementsWithMultipleValues(t *testing.T) {
	single_lines := []string{
		"valid.multiple:0|ms|@0.1",
		"valid.multiple:0|ms|",
		"valid.multiple:1|ms",
		"valid.multiple.duplicate:1|c",
		"valid.multiple.duplicate:1|c",
		"valid.multiple.duplicate:2|c",
		"valid.multiple.duplicate:1|c",
		"valid.multiple.duplicate:1|h",
		"valid.multiple.duplicate:1|h",
		"valid.multiple.duplicate:2|h",
		"valid.multiple.duplicate:1|h",
		"valid.multiple.duplicate:1|s",
		"valid.multiple.duplicate:1|s",
		"valid.multiple.duplicate:2|s",
		"valid.multiple.duplicate:1|s",
		"valid.multiple.duplicate:1|g",
		"valid.multiple.duplicate:1|g",
		"valid.multiple.duplicate:2|g",
		"valid.multiple.duplicate:1|g",
		"valid.multiple.mixed:1|c",
		"valid.multiple.mixed:1|ms",
		"valid.multiple.mixed:2|s",
		"valid.multiple.mixed:1|g",
	}
	multiple_lines := []string{
		"valid.multiple:0|ms|@0.1:0|ms|:1|ms",
		"valid.multiple.duplicate:1|c:1|c:2|c:1|c",
		"valid.multiple.duplicate:1|h:1|h:2|h:1|h",
		"valid.multiple.duplicate:1|s:1|s:2|s:1|s",
		"valid.multiple.duplicate:1|g:1|g:2|g:1|g",
		"valid.multiple.mixed:1|c:1|ms:2|s:1|g",
	}
	s_single := NewTestStatsd()
	s_multiple := NewTestStatsd()
	for _, line := range single_lines {
		err := s_single.parseStatsdLine(line)
		if err != nil {
			t.Errorf("Parsing line %s should not have resulted in an error\n", line)
		}
	}
	for _, line := range multiple_lines {
		err := s_multiple.parseStatsdLine(line)
		if err != nil {
			t.Errorf("Parsing line %s should not have resulted in an error\n", line)
		}
	}
	if len(s_single.timings) != 3 {
		t.Errorf("Expected 3 measurement, found %d", len(s_single.timings))
	}
	if cachedtiming, ok := s_single.timings["metric_type=timingvalid.multiple"]; !ok {
		t.Errorf("Expected cached measurement with hash 'metric_type=timingvalid.multiple' not found")
	} else {
		if cachedtiming.name != "valid.multiple" {
			t.Errorf("Expected the name to be 'valid_multiple', got %s", cachedtiming.name)
		}
		if cachedtiming.fields[defaultFieldName].n != 12 {
			t.Errorf("Expected 11 additions, got %d", cachedtiming.fields[defaultFieldName].n)
		}
		if cachedtiming.fields[defaultFieldName].upper != 1 {
			t.Errorf("Expected max input to be 1, got %f", cachedtiming.fields[defaultFieldName].upper)
		}
	}
	if err := test_validate_set("valid.multiple.duplicate", 2, s_single.sets); err != nil {
		t.Error(err.Error())
	}
	if err := test_validate_set("valid.multiple.duplicate", 2, s_multiple.sets); err != nil {
		t.Error(err.Error())
	}
	if err := test_validate_counter("valid.multiple.duplicate", 5, s_single.counters); err != nil {
		t.Error(err.Error())
	}
	if err := test_validate_counter("valid.multiple.duplicate", 5, s_multiple.counters); err != nil {
		t.Error(err.Error())
	}
	if err := test_validate_gauge("valid.multiple.duplicate", 1, s_single.gauges); err != nil {
		t.Error(err.Error())
	}
	if err := test_validate_gauge("valid.multiple.duplicate", 1, s_multiple.gauges); err != nil {
		t.Error(err.Error())
	}
	if err := test_validate_set("valid.multiple.mixed", 1, s_single.sets); err != nil {
		t.Error(err.Error())
	}
	if err := test_validate_set("valid.multiple.mixed", 1, s_multiple.sets); err != nil {
		t.Error(err.Error())
	}
	if err := test_validate_counter("valid.multiple.mixed", 1, s_single.counters); err != nil {
		t.Error(err.Error())
	}
	if err := test_validate_counter("valid.multiple.mixed", 1, s_multiple.counters); err != nil {
		t.Error(err.Error())
	}
	if err := test_validate_gauge("valid.multiple.mixed", 1, s_single.gauges); err != nil {
		t.Error(err.Error())
	}
	if err := test_validate_gauge("valid.multiple.mixed", 1, s_multiple.gauges); err != nil {
		t.Error(err.Error())
	}
}
func TestParse_ValidLines(t *testing.T) {
	s := NewTestStatsd()
	valid_lines := []string{
		"valid:45|c",
		"valid:45|s",
		"valid:45|g",
		"valid.timer:45|ms",
		"valid.timer:45|h",
	}
	for _, line := range valid_lines {
		err := s.parseStatsdLine(line)
		if err != nil {
			t.Errorf("Parsing line %s should not have resulted in an error\n", line)
		}
	}
}
func TestParse_Gauges(t *testing.T) {
	s := NewTestStatsd()
	valid_lines := []string{
		"plus.minus:100|g",
		"plus.minus:-10|g",
		"plus.minus:+30|g",
		"plus.plus:100|g",
		"plus.plus:+100|g",
		"plus.plus:+100|g",
		"minus.minus:100|g",
		"minus.minus:-100|g",
		"minus.minus:-100|g",
		"lone.plus:+100|g",
		"lone.minus:-100|g",
		"overwrite:100|g",
		"overwrite:300|g",
	}
	for _, line := range valid_lines {
		err := s.parseStatsdLine(line)
		if err != nil {
			t.Errorf("Parsing line %s should not have resulted in an error\n", line)
		}
	}
	validations := []struct {
		name  string
		value float64
	}{
		{
			"plus.minus",
			120,
		},
		{
			"plus.plus",
			300,
		},
		{
			"minus.minus",
			-100,
		},
		{
			"lone.plus",
			100,
		},
		{
			"lone.minus",
			-100,
		},
		{
			"overwrite",
			300,
		},
	}
	for _, test := range validations {
		err := test_validate_gauge(test.name, test.value, s.gauges)
		if err != nil {
			t.Error(err.Error())
		}
	}
}
func TestParse_Sets(t *testing.T) {
	s := NewTestStatsd()
	valid_lines := []string{
		"unique.user.ids:100|s",
		"unique.user.ids:100|s",
		"unique.user.ids:100|s",
		"unique.user.ids:100|s",
		"unique.user.ids:100|s",
		"unique.user.ids:101|s",
		"unique.user.ids:102|s",
		"unique.user.ids:102|s",
		"unique.user.ids:123456789|s",
		"oneuser.id:100|s",
		"oneuser.id:100|s",
	}
	for _, line := range valid_lines {
		err := s.parseStatsdLine(line)
		if err != nil {
			t.Errorf("Parsing line %s should not have resulted in an error\n", line)
		}
	}
	validations := []struct {
		name  string
		value int64
	}{
		{
			"unique.user.ids",
			4,
		},
		{
			"oneuser.id",
			1,
		},
	}
	for _, test := range validations {
		err := test_validate_set(test.name, test.value, s.sets)
		if err != nil {
			t.Error(err.Error())
		}
	}
}
func TestParse_Counters(t *testing.T) {
	s := NewTestStatsd()
	valid_lines := []string{
		"small.inc:1|c",
		"big.inc:100|c",
		"big.inc:1|c",
		"big.inc:100000|c",
		"big.inc:1000000|c",
		"small.inc:1|c",
		"zero.init:0|c",
		"sample.rate:1|c|@0.1",
		"sample.rate:1|c",
	}
	for _, line := range valid_lines {
		err := s.parseStatsdLine(line)
		if err != nil {
			t.Errorf("Parsing line %s should not have resulted in an error\n", line)
		}
	}
	validations := []struct {
		name  string
		value int64
	}{
		{
			"small.inc",
			2,
		},
		{
			"big.inc",
			1100101,
		},
		{
			"zero.init",
			0,
		},
		{
			"sample.rate",
			11,
		},
	}
	for _, test := range validations {
		err := test_validate_counter(test.name, test.value, s.counters)
		if err != nil {
			t.Error(err.Error())
		}
	}
}
func TestParse_Timings(t *testing.T) {
	s := NewTestStatsd()
	s.Percentiles = []int{90}
	valid_lines := []string{
		"test.timing:1|ms",
		"test.timing:11|ms",
		"test.timing:1|ms",
		"test.timing:1|ms",
		"test.timing:1|ms",
	}
	for _, line := range valid_lines {
		err := s.parseStatsdLine(line)
		if err != nil {
			t.Errorf("Parsing line %s should not have resulted in an error\n", line)
		}
	}
	result, err := s.Collect()
	require.NoError(t, err)
	resultReflect := reflect.ValueOf(result)
	i := resultReflect.Interface()
	pluginMap := i.(PerformanceStruct)
	validValues := map[string]interface{}{
		"test.timing.90_percentile": float64(11),
		"test.timing.count":         int64(5),
		"test.timing.lower":         float64(1),
		"test.timing.mean":          float64(3),
		"test.timing.deviation":     float64(4),
		"test.timing.upper":         float64(11),
	}
	validKeys := []string{"mean", "deviation", "upper", "lower", "count", "90_percentile"}
	for _, k := range validKeys {
		keyName := "test.timing" + "." + k
		require.NotZero(t, pluginMap.Gauges[keyName])
	}
	assert.Equal(t, pluginMap.Gauges, validValues)
}