// +build !windows package ping import ( "errors" "reflect" "runtime" "sort" "testing" "github.com/influxdata/telegraf/testutil" "github.com/stretchr/testify/assert" ) // BSD/Darwin ping output var bsdPingOutput = ` PING www.google.com (216.58.217.36): 56 data bytes 64 bytes from 216.58.217.36: icmp_seq=0 ttl=55 time=15.087 ms 64 bytes from 216.58.217.36: icmp_seq=1 ttl=55 time=21.564 ms 64 bytes from 216.58.217.36: icmp_seq=2 ttl=55 time=27.263 ms 64 bytes from 216.58.217.36: icmp_seq=3 ttl=55 time=18.828 ms 64 bytes from 216.58.217.36: icmp_seq=4 ttl=55 time=18.378 ms --- www.google.com ping statistics --- 5 packets transmitted, 5 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 15.087/20.224/27.263/4.076 ms ` // Linux ping output var linuxPingOutput = ` PING www.google.com (216.58.218.164) 56(84) bytes of data. 64 bytes from host.net (216.58.218.164): icmp_seq=1 ttl=63 time=35.2 ms 64 bytes from host.net (216.58.218.164): icmp_seq=2 ttl=63 time=42.3 ms 64 bytes from host.net (216.58.218.164): icmp_seq=3 ttl=63 time=45.1 ms 64 bytes from host.net (216.58.218.164): icmp_seq=4 ttl=63 time=43.5 ms 64 bytes from host.net (216.58.218.164): icmp_seq=5 ttl=63 time=51.8 ms --- www.google.com ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4010ms rtt min/avg/max/mdev = 35.225/43.628/51.806/5.325 ms ` // Fatal ping output (invalid argument) var fatalPingOutput = ` ping: -i interval too short: Operation not permitted ` // Test that ping command output is processed properly func TestProcessPingOutput(t *testing.T) { trans, rec, avg, ipversion, err := processPingOutput(bsdPingOutput) assert.NoError(t, err) assert.Equal(t, 5, trans, "5 packets were transmitted") assert.Equal(t, 5, rec, "5 packets were transmitted") assert.InDelta(t, 20.224, avg, 0.001) trans, rec, avg, ipversion, err = processPingOutput(linuxPingOutput) assert.NoError(t, err) assert.Equal(t, 5, trans, "5 packets were transmitted") assert.Equal(t, 5, rec, "5 packets were transmitted") assert.InDelta(t, 43.628, avg, 0.001) } // Test that processPingOutput returns an error when 'ping' fails to run, such // as when an invalid argument is provided func TestErrorProcessPingOutput(t *testing.T) { _, _, _, _, err := processPingOutput(fatalPingOutput) assert.Error(t, err, "Error was expected from processPingOutput") } // Test that arg lists and created correctly func TestArgs(t *testing.T) { p := Ping{ Count: 2, } // Actual and Expected arg lists must be sorted for reflect.DeepEqual actual := p.args("www.google.com") expected := []string{"-c", "2", "-n", "-s", "16", "www.google.com"} sort.Strings(actual) sort.Strings(expected) assert.True(t, reflect.DeepEqual(expected, actual), "Expected: %s Actual: %s", expected, actual) p.Interface = "eth0" actual = p.args("www.google.com") expected = []string{"-c", "2", "-n", "-s", "16", "-I", "eth0", "www.google.com"} sort.Strings(actual) sort.Strings(expected) assert.True(t, reflect.DeepEqual(expected, actual), "Expected: %s Actual: %s", expected, actual) p.Timeout = 12.0 actual = p.args("www.google.com") switch runtime.GOOS { case "darwin": expected = []string{"-c", "2", "-n", "-s", "16", "-I", "eth0", "-W", "12000.0", "www.google.com"} case "freebsd": expected = []string{"-c", "2", "-n", "-s", "16", "-I", "eth0", "-t", "12.0", "www.google.com"} default: expected = []string{"-c", "2", "-n", "-s", "16", "-I", "eth0", "-W", "12.0", "www.google.com"} } sort.Strings(actual) sort.Strings(expected) assert.True(t, reflect.DeepEqual(expected, actual), "Expected: %s Actual: %s", expected, actual) p.PingInterval = 1.2 actual = p.args("www.google.com") switch runtime.GOOS { case "darwin": expected = []string{"-c", "2", "-n", "-s", "16", "-I", "eth0", "-W", "12000.0", "-i", "1.2", "www.google.com"} case "freebsd": expected = []string{"-c", "2", "-n", "-s", "16", "-I", "eth0", "-t", "12.0", "-i", "1.2", "www.google.com"} default: expected = []string{"-c", "2", "-n", "-s", "16", "-I", "eth0", "-W", "12.0", "-i", "1.2", "www.google.com"} } sort.Strings(actual) sort.Strings(expected) assert.True(t, reflect.DeepEqual(expected, actual), "Expected: %s Actual: %s", expected, actual) } func mockHostPinger(timeout float64, args ...string) (string, error) { return linuxPingOutput, nil } // Test that Gather function works on a normal ping func TestPingGather(t *testing.T) { var acc testutil.Accumulator p := Ping{ Urls: []string{"www.google.com", "www.reddit.com"}, pingHost: mockHostPinger, } p.Gather(&acc) tags := map[string]string{"url": "www.google.com"} fields := map[string]interface{}{ "packets_transmitted": 5, "packets_received": 5, "percent_packet_loss": 0.0, "average_response_ms": 43.628, } acc.AssertContainsTaggedFields(t, "ping", fields, tags) tags = map[string]string{"url": "www.reddit.com"} acc.AssertContainsTaggedFields(t, "ping", fields, tags) } var lossyPingOutput = ` PING www.google.com (216.58.218.164) 56(84) bytes of data. 64 bytes from host.net (216.58.218.164): icmp_seq=1 ttl=63 time=35.2 ms 64 bytes from host.net (216.58.218.164): icmp_seq=3 ttl=63 time=45.1 ms 64 bytes from host.net (216.58.218.164): icmp_seq=5 ttl=63 time=51.8 ms --- www.google.com ping statistics --- 5 packets transmitted, 3 received, 40% packet loss, time 4010ms rtt min/avg/max/mdev = 35.225/44.033/51.806/5.325 ms ` func mockLossyHostPinger(timeout float64, args ...string) (string, error) { return lossyPingOutput, nil } // Test that Gather works on a ping with lossy packets func TestLossyPingGather(t *testing.T) { var acc testutil.Accumulator p := Ping{ Urls: []string{"www.google.com"}, pingHost: mockLossyHostPinger, } p.Gather(&acc) tags := map[string]string{"url": "www.google.com"} fields := map[string]interface{}{ "packets_transmitted": 5, "packets_received": 3, "percent_packet_loss": 40.0, "average_response_ms": 44.033, } acc.AssertContainsTaggedFields(t, "ping", fields, tags) } var errorPingOutput = ` PING www.amazon.com (176.32.98.166): 56 data bytes Request timeout for icmp_seq 0 --- www.amazon.com ping statistics --- 2 packets transmitted, 0 packets received, 100.0% packet loss ` func mockErrorHostPinger(timeout float64, args ...string) (string, error) { return errorPingOutput, errors.New("No packets received") } // Test that Gather works on a ping with no transmitted packets, even though the // command returns an error func TestBadPingGather(t *testing.T) { var acc testutil.Accumulator p := Ping{ Urls: []string{"www.amazon.com"}, pingHost: mockErrorHostPinger, } p.Gather(&acc) tags := map[string]string{"url": "www.amazon.com"} fields := map[string]interface{}{ "packets_transmitted": 2, "packets_received": 0, "percent_packet_loss": 100.0, } acc.AssertContainsTaggedFields(t, "ping", fields, tags) } func mockFatalHostPinger(timeout float64, args ...string) (string, error) { return fatalPingOutput, errors.New("So very bad") } // Test that a fatal ping command does not gather any statistics. func TestFatalPingGather(t *testing.T) { var acc testutil.Accumulator p := Ping{ Urls: []string{"www.amazon.com"}, pingHost: mockFatalHostPinger, } p.Gather(&acc) assert.False(t, acc.HasMeasurement("packets_transmitted"), "Fatal ping should not have packet measurements") assert.False(t, acc.HasMeasurement("packets_received"), "Fatal ping should not have packet measurements") assert.False(t, acc.HasMeasurement("percent_packet_loss"), "Fatal ping should not have packet measurements") assert.False(t, acc.HasMeasurement("average_response_ms"), "Fatal ping should not have packet measurements") }