Files
mochi-mqtt/processor_test.go
2019-10-27 11:00:50 +00:00

265 lines
6.6 KiB
Go

package mqtt
import (
"net"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/mochi-co/mqtt/circ"
"github.com/mochi-co/mqtt/packets"
)
func TestNewProcessor(t *testing.T) {
conn := new(MockNetConn)
p := NewProcessor(conn, circ.NewReader(16, 4), circ.NewWriter(16, 4))
require.NotNil(t, p.R)
}
func BenchmarkNewProcessor(b *testing.B) {
conn := new(MockNetConn)
r := circ.NewReader(16, 4)
w := circ.NewWriter(16, 4)
for n := 0; n < b.N; n++ {
NewProcessor(conn, r, w)
}
}
func TestProcessorRefreshDeadline(t *testing.T) {
conn := new(MockNetConn)
p := NewProcessor(conn, circ.NewReader(16, 4), circ.NewWriter(16, 4))
dl := p.Conn.(*MockNetConn).Deadline
p.RefreshDeadline(10)
require.NotEqual(t, dl, p.Conn.(*MockNetConn).Deadline)
}
func BenchmarkProcessorRefreshDeadline(b *testing.B) {
conn := new(MockNetConn)
p := NewProcessor(conn, circ.NewReader(16, 4), circ.NewWriter(16, 4))
for n := 0; n < b.N; n++ {
p.RefreshDeadline(10)
}
}
func TestProcessorReadFixedHeader(t *testing.T) {
conn := new(MockNetConn)
p := NewProcessor(conn, circ.NewReader(16, 4), circ.NewWriter(16, 4))
// Test null data.
fh := new(packets.FixedHeader)
err := p.ReadFixedHeader(fh)
require.Error(t, err)
// Test insufficient peeking.
fh = new(packets.FixedHeader)
p.R.Set([]byte{packets.Connect << 4}, 0, 1)
p.R.SetPos(0, 1)
err = p.ReadFixedHeader(fh)
require.Error(t, err)
fh = new(packets.FixedHeader)
p.R.Set([]byte{packets.Connect << 4, 0x00}, 0, 2)
p.R.SetPos(0, 2)
err = p.ReadFixedHeader(fh)
require.NoError(t, err)
tail, head := p.R.GetPos()
require.Equal(t, int64(2), tail)
require.Equal(t, int64(2), head)
}
func TestProcessorRead(t *testing.T) {
conn := new(MockNetConn)
var fh packets.FixedHeader
p := NewProcessor(conn, circ.NewReader(32, 4), circ.NewWriter(32, 4))
p.R.Set([]byte{
byte(packets.Publish << 4), 18, // Fixed header
0, 5, // Topic Name - LSB+MSB
'a', '/', 'b', '/', 'c', // Topic Name
'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', // Payload
}, 0, 20)
p.R.SetPos(0, 20)
err := p.ReadFixedHeader(&fh)
require.NoError(t, err)
pko, err := p.Read()
require.NoError(t, err)
require.Equal(t, &packets.PublishPacket{
FixedHeader: packets.FixedHeader{
Type: packets.Publish,
Remaining: 18,
},
TopicName: "a/b/c",
Payload: []byte("hello mochi"),
}, pko)
}
func TestProcessorReadFail(t *testing.T) {
conn := new(MockNetConn)
p := NewProcessor(conn, circ.NewReader(32, 4), circ.NewWriter(32, 4))
p.R.Set([]byte{
byte(packets.Publish << 4), 3, // Fixed header
0, 5, // Topic Name - LSB+MSB
'a', '/',
}, 0, 6)
p.R.SetPos(0, 8)
var fh packets.FixedHeader
err := p.ReadFixedHeader(&fh)
require.NoError(t, err)
_, err = p.Read()
require.Error(t, err)
}
// This is a super important test. It checks whether or not subsequent packets
// mutate each other. This happens when you use a single byte buffer for decoding
// multiple packets.
func TestProcessorReadPacketNoOverwrite(t *testing.T) {
conn := new(MockNetConn)
pk1 := []byte{
byte(packets.Publish << 4), 12, // Fixed header
0, 5, // Topic Name - LSB+MSB
'a', '/', 'b', '/', 'c', // Topic Name
'h', 'e', 'l', 'l', 'o', // Payload
}
pk2 := []byte{
byte(packets.Publish << 4), 14, // Fixed header
0, 5, // Topic Name - LSB+MSB
'x', '/', 'y', '/', 'z', // Topic Name
'y', 'a', 'h', 'a', 'l', 'l', 'o', // Payload
}
p := NewProcessor(conn, circ.NewReader(32, 4), circ.NewWriter(32, 4))
p.R.Set(pk1, 0, len(pk1))
p.R.SetPos(0, int64(len(pk1)))
var fh packets.FixedHeader
err := p.ReadFixedHeader(&fh)
require.NoError(t, err)
o1, err := p.Read()
require.NoError(t, err)
require.Equal(t, []byte{'h', 'e', 'l', 'l', 'o'}, o1.(*packets.PublishPacket).Payload)
require.Equal(t, []byte{'h', 'e', 'l', 'l', 'o'}, pk1[9:])
p.R.Set(pk2, 0, len(pk2))
p.R.SetPos(0, int64(len(pk2)))
err = p.ReadFixedHeader(&fh)
require.NoError(t, err)
o2, err := p.Read()
require.NoError(t, err)
require.Equal(t, []byte{'y', 'a', 'h', 'a', 'l', 'l', 'o'}, o2.(*packets.PublishPacket).Payload)
require.Equal(t, []byte{'h', 'e', 'l', 'l', 'o'}, o1.(*packets.PublishPacket).Payload, "o1 payload was mutated")
}
func TestProcessorReadPacketNil(t *testing.T) {
conn := new(MockNetConn)
p := NewProcessor(conn, circ.NewReader(32, 4), circ.NewWriter(32, 4))
var fh packets.FixedHeader
// Check for un-specified packet.
// Create a ping request packet with a false fixedheader type code.
pk := &packets.PingreqPacket{FixedHeader: packets.FixedHeader{Type: packets.Pingreq}}
pk.FixedHeader.Type = 99
p.R.Set([]byte{0, 0}, 0, 2)
p.R.SetPos(0, 2)
err := p.ReadFixedHeader(&fh)
require.NoError(t, err)
_, err = p.Read()
require.Error(t, err)
}
func TestProcessorReadPacketReadOverflow(t *testing.T) {
conn := new(MockNetConn)
p := NewProcessor(conn, circ.NewReader(32, 4), circ.NewWriter(32, 4))
var fh packets.FixedHeader
// Check for un-specified packet.
// Create a ping request packet with a false fixedheader type code.
pk := &packets.PingreqPacket{FixedHeader: packets.FixedHeader{Type: packets.Pingreq}}
pk.FixedHeader.Type = 99
p.R.Set([]byte{byte(packets.Connect << 4), 0}, 0, 2)
p.R.SetPos(0, 2)
err := p.ReadFixedHeader(&fh)
require.NoError(t, err)
p.FixedHeader.Remaining = 999999 // overflow buffer
_, err = p.Read()
require.Error(t, err)
}
// MockNetConn satisfies the net.Conn interface.
type MockNetConn struct {
ID string
Deadline time.Time
}
// Read reads bytes from the net io.reader.
func (m *MockNetConn) Read(b []byte) (n int, err error) {
return 0, nil
}
// Read writes bytes to the net io.writer.
func (m *MockNetConn) Write(b []byte) (n int, err error) {
return 0, nil
}
// Close closes the net.Conn connection.
func (m *MockNetConn) Close() error {
return nil
}
// LocalAddr returns the local address of the request.
func (m *MockNetConn) LocalAddr() net.Addr {
return new(MockNetAddr)
}
// RemoteAddr returns the remove address of the request.
func (m *MockNetConn) RemoteAddr() net.Addr {
return new(MockNetAddr)
}
// SetDeadline sets the request deadline.
func (m *MockNetConn) SetDeadline(t time.Time) error {
m.Deadline = t
return nil
}
// SetReadDeadline sets the read deadline.
func (m *MockNetConn) SetReadDeadline(t time.Time) error {
return nil
}
// SetWriteDeadline sets the write deadline.
func (m *MockNetConn) SetWriteDeadline(t time.Time) error {
return nil
}
// MockNetAddr satisfies net.Addr interface.
type MockNetAddr struct{}
// Network returns the network protocol.
func (m *MockNetAddr) Network() string {
return "tcp"
}
// String returns the network address.
func (m *MockNetAddr) String() string {
return "127.0.0.1"
}