Add tests for recorder

This commit is contained in:
Peter 2021-02-17 16:34:14 +01:00
parent 3ae3eefbac
commit 6ed23aa62d
Signed by: prskr
GPG key ID: C1DB5D2E8DB512F9
6 changed files with 262 additions and 6 deletions

View file

@ -2,6 +2,7 @@ package pcap
import ( import (
"context" "context"
"io"
"net" "net"
"time" "time"
@ -29,6 +30,7 @@ type RecordingOptions struct {
} }
type Recorder interface { type Recorder interface {
io.Closer
AvailableDevices() (devices []Device, err error) AvailableDevices() (devices []Device, err error)
Subscriptions() (subscriptions []Subscription) Subscriptions() (subscriptions []Subscription)
StartRecording(ctx context.Context, device string, consumer Consumer) (err error) StartRecording(ctx context.Context, device string, consumer Consumer) (err error)

View file

@ -1,10 +1,11 @@
package pcap package consumers
import ( import (
"io" "io"
"github.com/google/gopacket" "github.com/google/gopacket"
"github.com/google/gopacket/pcapgo" "github.com/google/gopacket/pcapgo"
"gitlab.com/inetmock/inetmock/internal/pcap"
"go.uber.org/multierr" "go.uber.org/multierr"
) )
@ -14,11 +15,11 @@ type writerConsumer struct {
packageWriter *pcapgo.Writer packageWriter *pcapgo.Writer
} }
func (f *writerConsumer) Init(params CaptureParameters) { func (f *writerConsumer) Init(params pcap.CaptureParameters) {
_ = f.packageWriter.WriteFileHeader(65536, params.LinkType) _ = f.packageWriter.WriteFileHeader(65536, params.LinkType)
} }
func NewWriterConsumer(name string, writer io.Writer) (consumer Consumer, err error) { func NewWriterConsumer(name string, writer io.Writer) (consumer pcap.Consumer, err error) {
consumer = &writerConsumer{ consumer = &writerConsumer{
name: name, name: name,
origWriter: writer, origWriter: writer,

View file

@ -0,0 +1,31 @@
package consumers
import (
"github.com/google/gopacket"
"github.com/google/uuid"
"gitlab.com/inetmock/inetmock/internal/pcap"
)
type noopConsumer struct {
name string
}
func NewNoOpConsumer() pcap.Consumer {
return NewNoOpConsumerWithName(uuid.NewString())
}
func NewNoOpConsumerWithName(name string) pcap.Consumer {
return &noopConsumer{
name: name,
}
}
func (n noopConsumer) Name() string {
return n.name
}
func (n noopConsumer) Observe(gopacket.Packet) {
}
func (n noopConsumer) Init(pcap.CaptureParameters) {
}

View file

@ -9,6 +9,7 @@ import (
"time" "time"
_ "github.com/google/gopacket/layers" _ "github.com/google/gopacket/layers"
"go.uber.org/multierr"
) )
const ( const (
@ -37,7 +38,7 @@ type recorder struct {
func (r recorder) Subscriptions() (subscriptions []Subscription) { func (r recorder) Subscriptions() (subscriptions []Subscription) {
r.locker.Lock() r.locker.Lock()
r.locker.Unlock() defer r.locker.Unlock()
for devName, dev := range r.openDevices { for devName, dev := range r.openDevices {
sub := Subscription{ sub := Subscription{
@ -100,7 +101,7 @@ func (r *recorder) StartRecording(ctx context.Context, device string, consumer C
func (r *recorder) StopRecording(consumerKey string) (err error) { func (r *recorder) StopRecording(consumerKey string) (err error) {
r.locker.Lock() r.locker.Lock()
r.locker.Unlock() defer r.locker.Unlock()
var dev deviceConsumer var dev deviceConsumer
var known bool var known bool
@ -114,3 +115,13 @@ func (r *recorder) StopRecording(consumerKey string) (err error) {
return return
} }
func (r recorder) Close() (err error) {
r.locker.Lock()
defer r.locker.Unlock()
for _, consumer := range r.openDevices {
err = multierr.Append(err, consumer.Close())
}
return
}

View file

@ -0,0 +1,210 @@
package pcap_test
import (
"context"
"errors"
"reflect"
"sort"
"testing"
"time"
"gitlab.com/inetmock/inetmock/internal/pcap"
"gitlab.com/inetmock/inetmock/internal/pcap/consumers"
)
func Test_recorder_Subscriptions(t *testing.T) {
type subscriptionRequest struct {
Name string
Device string
}
type testCase struct {
name string
requests []subscriptionRequest
wantSubscriptions []pcap.Subscription
}
tests := []testCase{
{
name: "Emtpy",
},
{
name: "Subscription to loopback",
requests: []subscriptionRequest{
{
Name: "test",
Device: "lo",
},
},
wantSubscriptions: []pcap.Subscription{
{
ConsumerKey: "lo:test",
ConsumerName: "test",
},
},
},
{
name: "Multiple subscriptions to loopback",
requests: []subscriptionRequest{
{
Name: "test",
Device: "lo",
},
{
Name: "test2",
Device: "lo",
},
},
wantSubscriptions: []pcap.Subscription{
{
ConsumerKey: "lo:test",
ConsumerName: "test",
},
{
ConsumerKey: "lo:test2",
ConsumerName: "test2",
},
},
},
}
scenario := func(tt testCase) func(t *testing.T) {
return func(t *testing.T) {
r := pcap.NewRecorder()
t.Cleanup(func() {
_ = r.Close()
})
for _, req := range tt.requests {
if err := r.StartRecording(context.Background(), req.Device, consumers.NewNoOpConsumerWithName(req.Name)); err != nil {
t.Errorf("StartRecording() error = %v", err)
}
}
if gotSubscriptions := sortSubscriptions(r.Subscriptions()); !reflect.DeepEqual(gotSubscriptions, tt.wantSubscriptions) {
t.Errorf("Subscriptions() = %v, want %v", gotSubscriptions, tt.wantSubscriptions)
}
}
}
for _, tt := range tests {
t.Run(tt.name, scenario(tt))
}
}
func Test_recorder_StartRecordingWithOptions(t *testing.T) {
type args struct {
device string
consumer pcap.Consumer
opts pcap.RecordingOptions
}
type testCase struct {
name string
args args
wantErr bool
recorderSetup func() (recorder pcap.Recorder, err error)
}
tests := []testCase{
{
name: "Listen to lo",
recorderSetup: func() (recorder pcap.Recorder, err error) {
recorder = pcap.NewRecorder()
return
},
args: args{
device: "lo",
consumer: consumers.NewNoOpConsumer(),
opts: pcap.RecordingOptions{
Promiscuous: false,
ReadTimeout: 10 * time.Second,
},
},
wantErr: false,
},
{
name: "Listen to lo with existing name",
recorderSetup: func() (recorder pcap.Recorder, err error) {
recorder = pcap.NewRecorder()
err = recorder.StartRecording(context.Background(), "lo", consumers.NewNoOpConsumerWithName("test"))
return
},
args: args{
device: "lo",
consumer: consumers.NewNoOpConsumerWithName("test"),
opts: pcap.RecordingOptions{
Promiscuous: false,
ReadTimeout: 10 * time.Second,
},
},
wantErr: true,
},
}
scenario := func(tt testCase) func(t *testing.T) {
return func(t *testing.T) {
var err error
var recorder pcap.Recorder
if recorder, err = tt.recorderSetup(); err != nil {
t.Fatalf("recorderSetup() error = %v", err)
}
t.Cleanup(func() {
_ = recorder.Close()
})
if err = recorder.StartRecordingWithOptions(context.Background(), tt.args.device, tt.args.consumer, tt.args.opts); (err != nil) != tt.wantErr {
t.Errorf("StartRecordingWithOptions() error = %v, wantErr %v", err, tt.wantErr)
}
}
}
for _, tt := range tests {
t.Run(tt.name, scenario(tt))
}
}
func Test_recorder_AvailableDevices(t *testing.T) {
type testCase struct {
name string
mtacher func(got []pcap.Device) error
wantErr bool
}
tests := []testCase{
{
name: "Expect lo device",
mtacher: func(got []pcap.Device) error {
if len(got) < 1 {
return errors.New("expected at least one interface")
}
foundLoopbackDevice := false
for _, d := range got {
foundLoopbackDevice = foundLoopbackDevice || d.Name == "lo"
}
if !foundLoopbackDevice {
return errors.New("didn't find loopback device")
}
return nil
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
re := pcap.NewRecorder()
gotDevices, err := re.AvailableDevices()
if (err != nil) != tt.wantErr {
t.Errorf("AvailableDevices() error = %v, wantErr %v", err, tt.wantErr)
return
}
if err := tt.mtacher(gotDevices); err != nil {
t.Errorf("AvailableDevices() matcher error = %v", err)
}
})
}
}
func sortSubscriptions(subs []pcap.Subscription) []pcap.Subscription {
sort.Slice(subs, func(i, j int) bool {
return subs[i].ConsumerName < subs[j].ConsumerName
})
return subs
}

View file

@ -9,6 +9,7 @@ import (
"path/filepath" "path/filepath"
"gitlab.com/inetmock/inetmock/internal/pcap" "gitlab.com/inetmock/inetmock/internal/pcap"
"gitlab.com/inetmock/inetmock/internal/pcap/consumers"
"gitlab.com/inetmock/inetmock/pkg/rpc" "gitlab.com/inetmock/inetmock/pkg/rpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@ -61,7 +62,7 @@ func (p pcapServer) StartPCAPFileRecording(_ context.Context, req *rpc.RegisterP
} }
var consumer pcap.Consumer var consumer pcap.Consumer
if consumer, err = pcap.NewWriterConsumer(req.TargetPath, writer); err != nil { if consumer, err = consumers.NewWriterConsumer(req.TargetPath, writer); err != nil {
return nil, status.Error(codes.Unknown, err.Error()) return nil, status.Error(codes.Unknown, err.Error())
} }