Add tests for recorder
This commit is contained in:
parent
3ae3eefbac
commit
6ed23aa62d
6 changed files with 262 additions and 6 deletions
|
@ -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)
|
||||||
|
|
|
@ -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,
|
31
internal/pcap/consumers/noop_consumer.go
Normal file
31
internal/pcap/consumers/noop_consumer.go
Normal 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) {
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
210
internal/pcap/recorder_test.go
Normal file
210
internal/pcap/recorder_test.go
Normal 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
|
||||||
|
}
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue