ping/ping_test.go
Cam e33cfb8ae7 Add processPacket unit tests
Add vet and unit test to circleci job
2019-06-04 11:15:55 -07:00

537 lines
12 KiB
Go

package ping
import (
"bytes"
"net"
"runtime/debug"
"testing"
"time"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
)
func TestProcessPacket(t *testing.T) {
pinger := makeTestPinger()
shouldBe1 := 0
// this function should be called
pinger.OnRecv = func(pkt *Packet) {
shouldBe1++
}
data := append(timeToBytes(time.Now()), intToBytes(pinger.Tracker)...)
if remainSize := pinger.Size - timeSliceLength - trackerLength; remainSize > 0 {
data = append(data, bytes.Repeat([]byte{1}, remainSize)...)
}
body := &icmp.Echo{
ID: pinger.id,
Seq: pinger.sequence,
Data: data,
}
msg := &icmp.Message{
Type: ipv4.ICMPTypeEchoReply,
Code: 0,
Body: body,
}
msgBytes, _ := msg.Marshal(nil)
pkt := packet{
nbytes: len(msgBytes),
bytes: msgBytes,
ttl: 24,
}
err := pinger.processPacket(&pkt)
AssertNoError(t, err)
AssertTrue(t, shouldBe1 == 1)
}
func TestProcessPacket_IgnoreNonEchoReplies(t *testing.T) {
pinger := makeTestPinger()
shouldBe0 := 0
// this function should not be called because the tracker is mismatched
pinger.OnRecv = func(pkt *Packet) {
shouldBe0++
}
data := append(timeToBytes(time.Now()), intToBytes(pinger.Tracker)...)
if remainSize := pinger.Size - timeSliceLength - trackerLength; remainSize > 0 {
data = append(data, bytes.Repeat([]byte{1}, remainSize)...)
}
body := &icmp.Echo{
ID: pinger.id,
Seq: pinger.sequence,
Data: data,
}
msg := &icmp.Message{
Type: ipv4.ICMPTypeDestinationUnreachable,
Code: 0,
Body: body,
}
msgBytes, _ := msg.Marshal(nil)
pkt := packet{
nbytes: len(msgBytes),
bytes: msgBytes,
ttl: 24,
}
err := pinger.processPacket(&pkt)
AssertNoError(t, err)
AssertTrue(t, shouldBe0 == 0)
}
func TestProcessPacket_IDMismatch(t *testing.T) {
pinger := makeTestPinger()
pinger.network = "ip" // ID is only checked on "ip" network
shouldBe0 := 0
// this function should not be called because the tracker is mismatched
pinger.OnRecv = func(pkt *Packet) {
shouldBe0++
}
data := append(timeToBytes(time.Now()), intToBytes(pinger.Tracker)...)
if remainSize := pinger.Size - timeSliceLength - trackerLength; remainSize > 0 {
data = append(data, bytes.Repeat([]byte{1}, remainSize)...)
}
body := &icmp.Echo{
ID: 999999,
Seq: pinger.sequence,
Data: data,
}
msg := &icmp.Message{
Type: ipv4.ICMPTypeEchoReply,
Code: 0,
Body: body,
}
msgBytes, _ := msg.Marshal(nil)
pkt := packet{
nbytes: len(msgBytes),
bytes: msgBytes,
ttl: 24,
}
err := pinger.processPacket(&pkt)
AssertNoError(t, err)
AssertTrue(t, shouldBe0 == 0)
}
func TestProcessPacket_TrackerMismatch(t *testing.T) {
pinger := makeTestPinger()
shouldBe0 := 0
// this function should not be called because the tracker is mismatched
pinger.OnRecv = func(pkt *Packet) {
shouldBe0++
}
data := append(timeToBytes(time.Now()), intToBytes(999)...)
if remainSize := pinger.Size - timeSliceLength - trackerLength; remainSize > 0 {
data = append(data, bytes.Repeat([]byte{1}, remainSize)...)
}
body := &icmp.Echo{
ID: pinger.id,
Seq: pinger.sequence,
Data: data,
}
msg := &icmp.Message{
Type: ipv4.ICMPTypeEchoReply,
Code: 0,
Body: body,
}
msgBytes, _ := msg.Marshal(nil)
pkt := packet{
nbytes: len(msgBytes),
bytes: msgBytes,
ttl: 24,
}
err := pinger.processPacket(&pkt)
AssertNoError(t, err)
AssertTrue(t, shouldBe0 == 0)
}
func TestProcessPacket_LargePacket(t *testing.T) {
pinger := makeTestPinger()
pinger.Size = 4096
data := append(timeToBytes(time.Now()), intToBytes(pinger.Tracker)...)
if remainSize := pinger.Size - timeSliceLength - trackerLength; remainSize > 0 {
data = append(data, bytes.Repeat([]byte{1}, remainSize)...)
}
body := &icmp.Echo{
ID: pinger.id,
Seq: pinger.sequence,
Data: data,
}
msg := &icmp.Message{
Type: ipv4.ICMPTypeEchoReply,
Code: 0,
Body: body,
}
msgBytes, _ := msg.Marshal(nil)
pkt := packet{
nbytes: len(msgBytes),
bytes: msgBytes,
ttl: 24,
}
err := pinger.processPacket(&pkt)
AssertNoError(t, err)
}
func TestProcessPacket_PacketTooSmall(t *testing.T) {
pinger := makeTestPinger()
data := []byte("foo")
body := &icmp.Echo{
ID: pinger.id,
Seq: pinger.sequence,
Data: data,
}
msg := &icmp.Message{
Type: ipv4.ICMPTypeEchoReply,
Code: 0,
Body: body,
}
msgBytes, _ := msg.Marshal(nil)
pkt := packet{
nbytes: len(msgBytes),
bytes: msgBytes,
ttl: 24,
}
err := pinger.processPacket(&pkt)
AssertError(t, err, "")
}
func TestNewPingerValid(t *testing.T) {
p, err := NewPinger("www.google.com")
AssertNoError(t, err)
AssertEqualStrings(t, "www.google.com", p.Addr())
// DNS names should resolve into IP addresses
AssertNotEqualStrings(t, "www.google.com", p.IPAddr().String())
AssertTrue(t, isIPv4(p.IPAddr().IP))
AssertFalse(t, p.Privileged())
// Test that SetPrivileged works
p.SetPrivileged(true)
AssertTrue(t, p.Privileged())
// Test setting to ipv4 address
err = p.SetAddr("www.google.com")
AssertNoError(t, err)
AssertTrue(t, isIPv4(p.IPAddr().IP))
// Test setting to ipv6 address
err = p.SetAddr("ipv6.google.com")
AssertNoError(t, err)
AssertTrue(t, isIPv6(p.IPAddr().IP))
p, err = NewPinger("localhost")
AssertNoError(t, err)
AssertEqualStrings(t, "localhost", p.Addr())
// DNS names should resolve into IP addresses
AssertNotEqualStrings(t, "localhost", p.IPAddr().String())
AssertTrue(t, isIPv4(p.IPAddr().IP))
AssertFalse(t, p.Privileged())
// Test that SetPrivileged works
p.SetPrivileged(true)
AssertTrue(t, p.Privileged())
// Test setting to ipv4 address
err = p.SetAddr("www.google.com")
AssertNoError(t, err)
AssertTrue(t, isIPv4(p.IPAddr().IP))
// Test setting to ipv6 address
err = p.SetAddr("ipv6.google.com")
AssertNoError(t, err)
AssertTrue(t, isIPv6(p.IPAddr().IP))
p, err = NewPinger("127.0.0.1")
AssertNoError(t, err)
AssertEqualStrings(t, "127.0.0.1", p.Addr())
AssertTrue(t, isIPv4(p.IPAddr().IP))
AssertFalse(t, p.Privileged())
// Test that SetPrivileged works
p.SetPrivileged(true)
AssertTrue(t, p.Privileged())
// Test setting to ipv4 address
err = p.SetAddr("www.google.com")
AssertNoError(t, err)
AssertTrue(t, isIPv4(p.IPAddr().IP))
// Test setting to ipv6 address
err = p.SetAddr("ipv6.google.com")
AssertNoError(t, err)
AssertTrue(t, isIPv6(p.IPAddr().IP))
p, err = NewPinger("ipv6.google.com")
AssertNoError(t, err)
AssertEqualStrings(t, "ipv6.google.com", p.Addr())
// DNS names should resolve into IP addresses
AssertNotEqualStrings(t, "ipv6.google.com", p.IPAddr().String())
AssertTrue(t, isIPv6(p.IPAddr().IP))
AssertFalse(t, p.Privileged())
// Test that SetPrivileged works
p.SetPrivileged(true)
AssertTrue(t, p.Privileged())
// Test setting to ipv4 address
err = p.SetAddr("www.google.com")
AssertNoError(t, err)
AssertTrue(t, isIPv4(p.IPAddr().IP))
// Test setting to ipv6 address
err = p.SetAddr("ipv6.google.com")
AssertNoError(t, err)
AssertTrue(t, isIPv6(p.IPAddr().IP))
// ipv6 localhost:
p, err = NewPinger("::1")
AssertNoError(t, err)
AssertEqualStrings(t, "::1", p.Addr())
AssertTrue(t, isIPv6(p.IPAddr().IP))
AssertFalse(t, p.Privileged())
// Test that SetPrivileged works
p.SetPrivileged(true)
AssertTrue(t, p.Privileged())
// Test setting to ipv4 address
err = p.SetAddr("www.google.com")
AssertNoError(t, err)
AssertTrue(t, isIPv4(p.IPAddr().IP))
// Test setting to ipv6 address
err = p.SetAddr("ipv6.google.com")
AssertNoError(t, err)
AssertTrue(t, isIPv6(p.IPAddr().IP))
}
func TestNewPingerInvalid(t *testing.T) {
_, err := NewPinger("127.0.0.0.1")
AssertError(t, err, "127.0.0.0.1")
_, err = NewPinger("127..0.0.1")
AssertError(t, err, "127..0.0.1")
_, err = NewPinger("wtf")
AssertError(t, err, "wtf")
_, err = NewPinger(":::1")
AssertError(t, err, ":::1")
_, err = NewPinger("ipv5.google.com")
AssertError(t, err, "ipv5.google.com")
}
func TestSetIPAddr(t *testing.T) {
googleaddr, err := net.ResolveIPAddr("ip", "www.google.com")
if err != nil {
t.Fatal("Can't resolve www.google.com, can't run tests")
}
// Create a localhost ipv4 pinger
p, err := NewPinger("localhost")
AssertNoError(t, err)
AssertEqualStrings(t, "localhost", p.Addr())
// set IPAddr to google
p.SetIPAddr(googleaddr)
AssertEqualStrings(t, googleaddr.String(), p.Addr())
}
func TestStatisticsSunny(t *testing.T) {
// Create a localhost ipv4 pinger
p, err := NewPinger("localhost")
AssertNoError(t, err)
AssertEqualStrings(t, "localhost", p.Addr())
p.PacketsSent = 10
p.PacketsRecv = 10
p.rtts = []time.Duration{
time.Duration(1000),
time.Duration(1000),
time.Duration(1000),
time.Duration(1000),
time.Duration(1000),
time.Duration(1000),
time.Duration(1000),
time.Duration(1000),
time.Duration(1000),
time.Duration(1000),
}
stats := p.Statistics()
if stats.PacketsRecv != 10 {
t.Errorf("Expected %v, got %v", 10, stats.PacketsRecv)
}
if stats.PacketsSent != 10 {
t.Errorf("Expected %v, got %v", 10, stats.PacketsSent)
}
if stats.PacketLoss != 0 {
t.Errorf("Expected %v, got %v", 0, stats.PacketLoss)
}
if stats.MinRtt != time.Duration(1000) {
t.Errorf("Expected %v, got %v", time.Duration(1000), stats.MinRtt)
}
if stats.MaxRtt != time.Duration(1000) {
t.Errorf("Expected %v, got %v", time.Duration(1000), stats.MaxRtt)
}
if stats.AvgRtt != time.Duration(1000) {
t.Errorf("Expected %v, got %v", time.Duration(1000), stats.AvgRtt)
}
if stats.StdDevRtt != time.Duration(0) {
t.Errorf("Expected %v, got %v", time.Duration(0), stats.StdDevRtt)
}
}
func TestStatisticsLossy(t *testing.T) {
// Create a localhost ipv4 pinger
p, err := NewPinger("localhost")
AssertNoError(t, err)
AssertEqualStrings(t, "localhost", p.Addr())
p.PacketsSent = 20
p.PacketsRecv = 10
p.rtts = []time.Duration{
time.Duration(10),
time.Duration(1000),
time.Duration(1000),
time.Duration(10000),
time.Duration(1000),
time.Duration(800),
time.Duration(1000),
time.Duration(40),
time.Duration(100000),
time.Duration(1000),
}
stats := p.Statistics()
if stats.PacketsRecv != 10 {
t.Errorf("Expected %v, got %v", 10, stats.PacketsRecv)
}
if stats.PacketsSent != 20 {
t.Errorf("Expected %v, got %v", 20, stats.PacketsSent)
}
if stats.PacketLoss != 50 {
t.Errorf("Expected %v, got %v", 50, stats.PacketLoss)
}
if stats.MinRtt != time.Duration(10) {
t.Errorf("Expected %v, got %v", time.Duration(10), stats.MinRtt)
}
if stats.MaxRtt != time.Duration(100000) {
t.Errorf("Expected %v, got %v", time.Duration(100000), stats.MaxRtt)
}
if stats.AvgRtt != time.Duration(11585) {
t.Errorf("Expected %v, got %v", time.Duration(11585), stats.AvgRtt)
}
if stats.StdDevRtt != time.Duration(29603) {
t.Errorf("Expected %v, got %v", time.Duration(29603), stats.StdDevRtt)
}
}
// Test helpers
func makeTestPinger() *Pinger {
pinger, _ := NewPinger("127.0.0.1")
pinger.ipv4 = true
pinger.addr = "127.0.0.1"
pinger.network = "ip"
pinger.id = 123
pinger.Tracker = 456
pinger.Size = 0
return pinger
}
func AssertNoError(t *testing.T, err error) {
if err != nil {
t.Errorf("Expected No Error but got %s, Stack:\n%s",
err, string(debug.Stack()))
}
}
func AssertError(t *testing.T, err error, info string) {
if err == nil {
t.Errorf("Expected Error but got %s, %s, Stack:\n%s",
err, info, string(debug.Stack()))
}
}
func AssertEqualStrings(t *testing.T, expected, actual string) {
if expected != actual {
t.Errorf("Expected %s, got %s, Stack:\n%s",
expected, actual, string(debug.Stack()))
}
}
func AssertNotEqualStrings(t *testing.T, expected, actual string) {
if expected == actual {
t.Errorf("Expected %s, got %s, Stack:\n%s",
expected, actual, string(debug.Stack()))
}
}
func AssertTrue(t *testing.T, b bool) {
if !b {
t.Errorf("Expected True, got False, Stack:\n%s", string(debug.Stack()))
}
}
func AssertFalse(t *testing.T, b bool) {
if b {
t.Errorf("Expected False, got True, Stack:\n%s", string(debug.Stack()))
}
}
func BenchmarkProcessPacket(b *testing.B) {
pinger, _ := NewPinger("127.0.0.1")
pinger.ipv4 = true
pinger.addr = "127.0.0.1"
pinger.network = "ip4:icmp"
pinger.id = 123
pinger.Tracker = 456
t := append(timeToBytes(time.Now()), intToBytes(pinger.Tracker)...)
if remainSize := pinger.Size - timeSliceLength - trackerLength; remainSize > 0 {
t = append(t, bytes.Repeat([]byte{1}, remainSize)...)
}
body := &icmp.Echo{
ID: pinger.id,
Seq: pinger.sequence,
Data: t,
}
msg := &icmp.Message{
Type: ipv4.ICMPTypeEchoReply,
Code: 0,
Body: body,
}
msgBytes, _ := msg.Marshal(nil)
pkt := packet{
nbytes: len(msgBytes),
bytes: msgBytes,
ttl: 24,
}
for k := 0; k < b.N; k++ {
pinger.processPacket(&pkt)
}
}