Skip to content
This repository has been archived by the owner on Dec 14, 2020. It is now read-only.

Commit

Permalink
Add tests for ConnOptions and LinkOptions
Browse files Browse the repository at this point in the history
Resolves #37
  • Loading branch information
vcabbage committed Feb 17, 2018
1 parent 3ecab7c commit 3c439be
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 86 deletions.
60 changes: 36 additions & 24 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ func (s *Session) NewReceiver(opts ...LinkOption) (*Receiver, error) {
maxCredit: DefaultLinkCredit,
}

l, err := newLink(s, r, opts)
l, err := attachLink(s, r, opts)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -350,7 +350,7 @@ func (s *Sender) Close() error {

// NewSender opens a new sender link on the session.
func (s *Session) NewSender(opts ...LinkOption) (*Sender, error) {
l, err := newLink(s, nil, opts)
l, err := attachLink(s, nil, opts)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -691,30 +691,15 @@ type link struct {
err error // err returned on Close()
}

// newLink is used by Receiver and Sender to create new links
func newLink(s *Session, r *Receiver, opts []LinkOption) (*link, error) {
l := &link{
name: string(randBytes(40)),
session: s,
receiver: r,
close: make(chan struct{}),
done: make(chan struct{}),
// TODO: this is excessive, especially on 64-bit platforms
// default to a more reasonable max and allow users to
// change via LinkOption
maxMessageSize: maxSliceLen,
// attachLink is used by Receiver and Sender to create new links
func attachLink(s *Session, r *Receiver, opts []LinkOption) (*link, error) {
l, err := newLink(s, r, opts)
if err != nil {
return nil, err
}

isReceiver := r != nil

// configure options
for _, o := range opts {
err := o(l)
if err != nil {
return nil, err
}
}

// buffer rx to linkCredit so that conn.mux won't block
// attempting to send to a slow reader
if isReceiver {
Expand Down Expand Up @@ -814,6 +799,30 @@ func newLink(s *Session, r *Receiver, opts []LinkOption) (*link, error) {
return l, nil
}

func newLink(s *Session, r *Receiver, opts []LinkOption) (*link, error) {
l := &link{
name: string(randBytes(40)),
session: s,
receiver: r,
close: make(chan struct{}),
done: make(chan struct{}),
// TODO: this is excessive, especially on 64-bit platforms
// default to a more reasonable max and allow users to
// change via LinkOption
maxMessageSize: maxSliceLen,
}

// configure options
for _, o := range opts {
err := o(l)
if err != nil {
return nil, err
}
}

return l, nil
}

func (l *link) mux() {
defer l.detach()

Expand Down Expand Up @@ -1201,8 +1210,11 @@ func LinkReceiverSettle(mode ReceiverSettleMode) LinkOption {
// LinkSelectorFilter sets a selector filter (apache.org:selector-filter:string) on the link source.
func LinkSelectorFilter(filter string) LinkOption {
// <descriptor name="apache.org:selector-filter:string" code="0x0000468C:0x00000004"/>
const name = symbol("apache.org:selector-filter:string")
code := binary.BigEndian.Uint64([]byte{0x00, 0x00, 0x46, 0x8C, 0x00, 0x00, 0x00, 0x04})
const (
name = symbol("apache.org:selector-filter:string")
code = uint64(0x0000468C00000004)
)

return func(l *link) error {
if l.source == nil {
l.source = new(source)
Expand Down
47 changes: 47 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package amqp

import (
"encoding/binary"
"testing"
)

func TestLinkOptions(t *testing.T) {
tests := []struct {
label string
opts []LinkOption

wantSource *source
}{
{
label: "no options",
},
{
label: "selector-filter",
opts: []LinkOption{
LinkSelectorFilter("amqp.annotation.x-opt-offset > '100'"),
},

wantSource: &source{
Filter: map[symbol]*describedType{
"apache.org:selector-filter:string": &describedType{
descriptor: binary.BigEndian.Uint64([]byte{0x00, 0x00, 0x46, 0x8C, 0x00, 0x00, 0x00, 0x04}),
value: "amqp.annotation.x-opt-offset > '100'",
},
},
},
},
}

for _, tt := range tests {
t.Run(tt.label, func(t *testing.T) {
got, err := newLink(nil, nil, tt.opts)
if err != nil {
t.Fatal(err)
}

if !testEqual(got.source, tt.wantSource) {
t.Errorf("Properties don't match expected:\n %s", testDiff(got.source, tt.wantSource))
}
})
}
}
44 changes: 44 additions & 0 deletions conn_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package amqp

import (
"testing"
)

func TestConnOptions(t *testing.T) {
tests := []struct {
label string
opts []ConnOption

wantProperties map[symbol]interface{}
}{
{
label: "no options",
},
{
label: "multiple properties",
opts: []ConnOption{
ConnProperty("x-opt-test1", "test1"),
ConnProperty("x-opt-test2", "test2"),
ConnProperty("x-opt-test1", "test3"),
},

wantProperties: map[symbol]interface{}{
"x-opt-test1": "test3",
"x-opt-test2": "test2",
},
},
}

for _, tt := range tests {
t.Run(tt.label, func(t *testing.T) {
got, err := newConn(nil, tt.opts...)
if err != nil {
t.Fatal(err)
}

if !testEqual(got.properties, tt.wantProperties) {
t.Errorf("Properties don't match expected:\n %s", testDiff(got.properties, tt.wantProperties))
}
})
}
}
65 changes: 65 additions & 0 deletions helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package amqp

import (
"reflect"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)

func testEqual(x, y interface{}) bool {
return cmp.Equal(x, y, compareOpts(x, y)...)
}

func testDiff(x, y interface{}) string {
return cmp.Diff(x, y, compareOpts(x, y)...)
}

func compareOpts(x, y interface{}) []cmp.Option {
return cmp.Options{
deepAllowUnexported(x, y),
cmpopts.EquateNaNs(),
}
}

// from https://github.com/google/go-cmp/issues/40
func deepAllowUnexported(vs ...interface{}) cmp.Option {
m := make(map[reflect.Type]struct{})
for _, v := range vs {
structTypes(reflect.ValueOf(v), m)
}
var types []interface{}
for t := range m {
types = append(types, reflect.New(t).Elem().Interface())
}
return cmp.AllowUnexported(types...)
}

func structTypes(v reflect.Value, m map[reflect.Type]struct{}) {
if !v.IsValid() {
return
}
switch v.Kind() {
case reflect.Ptr:
if !v.IsNil() {
structTypes(v.Elem(), m)
}
case reflect.Interface:
if !v.IsNil() {
structTypes(v.Elem(), m)
}
case reflect.Slice, reflect.Array:
for i := 0; i < v.Len(); i++ {
structTypes(v.Index(i), m)
}
case reflect.Map:
for _, k := range v.MapKeys() {
structTypes(v.MapIndex(k), m)
}
case reflect.Struct:
m[v.Type()] = struct{}{}
for i := 0; i < v.NumField(); i++ {
structTypes(v.Field(i), m)
}
}
}
68 changes: 6 additions & 62 deletions marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ import (
"strings"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)

var exampleFrames = []struct {
Expand Down Expand Up @@ -70,11 +67,8 @@ func TestFrameMarshalUnmarshal(t *testing.T) {
if err != nil {
t.Fatalf("%+v", err)
}
cmpOpts := cmp.Options{
DeepAllowUnexported(want.body, payload),
}
if !cmp.Equal(want.body, payload, cmpOpts...) {
t.Errorf("Roundtrip produced different results:\n %s", cmp.Diff(want.body, payload, cmpOpts...))
if !testEqual(want.body, payload) {
t.Errorf("Roundtrip produced different results:\n %s", testDiff(want.body, payload))
}
})
}
Expand Down Expand Up @@ -202,12 +196,8 @@ func TestMarshalUnmarshal(t *testing.T) {
}

cmpType := reflect.Indirect(newType).Interface()
cmpOpts := cmp.Options{
DeepAllowUnexported(type_, cmpType),
cmpopts.EquateNaNs(),
}
if !cmp.Equal(type_, cmpType, cmpOpts...) {
t.Errorf("Roundtrip produced different results:\n %s", cmp.Diff(type_, cmpType, cmpOpts...))
if !testEqual(type_, cmpType) {
t.Errorf("Roundtrip produced different results:\n %s", testDiff(type_, cmpType))
}
})
}
Expand All @@ -227,12 +217,8 @@ func TestReadAny(t *testing.T) {
t.Fatalf("%+v", err)
}

cmpOpts := cmp.Options{
DeepAllowUnexported(type_, got),
cmpopts.EquateNaNs(),
}
if !cmp.Equal(type_, got, cmpOpts...) {
t.Errorf("Roundtrip produced different results:\n %s", cmp.Diff(type_, got, cmpOpts...))
if !testEqual(type_, got) {
t.Errorf("Roundtrip produced different results:\n %s", testDiff(type_, got))
}
})
}
Expand Down Expand Up @@ -622,45 +608,3 @@ func rcvSettle(m ReceiverSettleMode) *ReceiverSettleMode {
func uint32Ptr(u uint32) *uint32 {
return &u
}

// from https://github.com/google/go-cmp/issues/40
func DeepAllowUnexported(vs ...interface{}) cmp.Option {
m := make(map[reflect.Type]struct{})
for _, v := range vs {
structTypes(reflect.ValueOf(v), m)
}
var types []interface{}
for t := range m {
types = append(types, reflect.New(t).Elem().Interface())
}
return cmp.AllowUnexported(types...)
}

func structTypes(v reflect.Value, m map[reflect.Type]struct{}) {
if !v.IsValid() {
return
}
switch v.Kind() {
case reflect.Ptr:
if !v.IsNil() {
structTypes(v.Elem(), m)
}
case reflect.Interface:
if !v.IsNil() {
structTypes(v.Elem(), m)
}
case reflect.Slice, reflect.Array:
for i := 0; i < v.Len(); i++ {
structTypes(v.Index(i), m)
}
case reflect.Map:
for _, k := range v.MapKeys() {
structTypes(v.MapIndex(k), m)
}
case reflect.Struct:
m[v.Type()] = struct{}{}
for i := 0; i < v.NumField(); i++ {
structTypes(v.Field(i), m)
}
}
}

0 comments on commit 3c439be

Please sign in to comment.