package wire import ( "bytes" "encoding/hex" "fmt" "reflect" "testing" "time" "github.com/stretchr/testify/assert" cmn "github.com/tendermint/tmlibs/common" ) type SimpleStruct struct { String string Bytes []byte Time time.Time } type Animal interface{} // Implements Animal type Cat struct { SimpleStruct } // Implements Animal type Dog struct { SimpleStruct } // Implements Animal type Snake []byte // Implements Animal type Viper struct { Bytes []byte } var _ = RegisterInterface( struct{ Animal }{}, ConcreteType{Cat{}, 0x01}, ConcreteType{Dog{}, 0x02}, ConcreteType{Snake{}, 0x03}, ConcreteType{&Viper{}, 0x04}, ) func TestTime(t *testing.T) { // panic trying to encode times before 1970 panicCases := []time.Time{ time.Time{}, time.Unix(-10, 0), time.Unix(0, -10), } for _, c := range panicCases { n, err := new(int), new(error) buf := new(bytes.Buffer) assert.Panics(t, func() { WriteBinary(c, buf, n, err) }, "expected WriteBinary to panic on times before 1970") } // ensure we can encode/decode a recent time now := time.Now() n, err := new(int), new(error) buf := new(bytes.Buffer) WriteBinary(now, buf, n, err) var thisTime time.Time thisTime = ReadBinary(thisTime, buf, 0, new(int), new(error)).(time.Time) if !thisTime.Truncate(time.Millisecond).Equal(now.Truncate(time.Millisecond)) { t.Fatalf("times dont match. got %v, expected %v", thisTime, now) } // error trying to decode bad times errorCases := []struct { thisTime time.Time err error }{ {time.Time{}, ErrBinaryReadInvalidTimeNegative}, {time.Unix(-10, 0), ErrBinaryReadInvalidTimeNegative}, {time.Unix(0, -10), ErrBinaryReadInvalidTimeNegative}, {time.Unix(0, 10), ErrBinaryReadInvalidTimeSubMillisecond}, {time.Unix(1, 10), ErrBinaryReadInvalidTimeSubMillisecond}, } for _, c := range errorCases { n, err := new(int), new(error) buf := new(bytes.Buffer) timeNano := c.thisTime.UnixNano() WriteInt64(timeNano, buf, n, err) var thisTime time.Time thisTime = ReadBinary(thisTime, buf, 0, n, err).(time.Time) assert.Equal(t, *err, c.err, "expected ReadBinary to throw an error") assert.Equal(t, thisTime, time.Time{}, "expected ReadBinary to return default time") } } func TestEncodeDecode(t *testing.T) { cat := &Cat{SimpleStruct{String: "cat", Time: time.Now()}} n, err := new(int), new(error) buf := new(bytes.Buffer) WriteBinary(cat, buf, n, err) if *err != nil { t.Fatalf("writeBinary:: failed to encode Cat: %v", *err) } cat2 := new(Cat) n, err = new(int), new(error) cat2 = ReadBinary(cat2, buf, 0, n, err).(*Cat) if *err != nil { t.Fatalf("unexpected err: %v", *err) } // NOTE: this fails because []byte{} != []byte(nil) // assert.Equal(t, cat, cat2, "expected cats to match") } func TestUnexportedEmbeddedTypes(t *testing.T) { type unexportedReceiver struct { animal Animal } type exportedReceiver struct { Animal Animal } now := time.Now().Truncate(time.Millisecond) origCat := Cat{SimpleStruct{String: "cat", Time: now}} exportedCat := exportedReceiver{origCat} // this is what we encode writeCat := func() *bytes.Buffer { n, err := new(int), new(error) buf := new(bytes.Buffer) WriteBinary(exportedCat, buf, n, err) if *err != nil { t.Errorf("writeBinary:: failed to encode Cat: %v", *err) } return buf } // try to read into unexportedReceiver (should fail) buf := writeCat() n, err := new(int), new(error) unexp := ReadBinary(unexportedReceiver{}, buf, 0, n, err).(unexportedReceiver) if *err != nil { t.Fatalf("unexpected err: %v", *err) } returnCat, ok := unexp.animal.(Cat) if ok { t.Fatalf("unexpectedly parsed out the Cat type") } // try to read into exportedReceiver (should pass) buf = writeCat() n, err = new(int), new(error) exp := ReadBinary(exportedReceiver{}, buf, 0, n, err).(exportedReceiver) if *err != nil { t.Fatalf("unexpected err: %v", *err) } returnCat, ok = exp.Animal.(Cat) if !ok { t.Fatalf("expected to be able to parse out the Cat type; rrecv: %#v", exp.Animal) } _ = returnCat // NOTE: this fails because []byte{} != []byte(nil) // assert.Equal(t, origCat, returnCat, fmt.Sprintf("cats dont match")) } // TODO: add assertions here ... func TestAnimalInterface(t *testing.T) { var foo Animal // Type of pointer to Animal rt := reflect.TypeOf(&foo) fmt.Printf("rt: %v\n", rt) // Type of Animal itself. // NOTE: normally this is acquired through other means // like introspecting on method signatures, or struct fields. rte := rt.Elem() fmt.Printf("rte: %v\n", rte) // Get a new pointer to the interface // NOTE: calling .Interface() is to get the actual value, // instead of reflection values. ptr := reflect.New(rte).Interface() fmt.Printf("ptr: %v", ptr) // Make a binary byteslice that represents a *snake. foo = Snake([]byte("snake")) snakeBytes := BinaryBytes(foo) snakeReader := bytes.NewReader(snakeBytes) // Now you can read it. n, err := new(int), new(error) it := ReadBinary(foo, snakeReader, 0, n, err).(Animal) fmt.Println(it, reflect.TypeOf(it)) } //------------------------------------- type Constructor func() interface{} type Instantiator func() (o interface{}, ptr interface{}) type Validator func(o interface{}, t *testing.T) type TestCase struct { Constructor Instantiator Validator } //------------------------------------- func constructBasic() interface{} { cat := Cat{ SimpleStruct{ String: "String", Bytes: []byte("Bytes"), Time: time.Unix(123, 456789999), }, } return cat } func instantiateBasic() (interface{}, interface{}) { return Cat{}, &Cat{} } func validateBasic(o interface{}, t *testing.T) { cat := o.(Cat) if cat.String != "String" { t.Errorf("Expected cat.String == 'String', got %v", cat.String) } if string(cat.Bytes) != "Bytes" { t.Errorf("Expected cat.Bytes == 'Bytes', got %X", cat.Bytes) } if cat.Time.UnixNano() != 123456000000 { // Only milliseconds t.Errorf("Expected cat.Time.UnixNano() == 123456000000, got %v", cat.Time.UnixNano()) } } //------------------------------------- type NilTestStruct struct { IntPtr *int CatPtr *Cat Animal Animal } func constructNilTestStruct() interface{} { return NilTestStruct{} } func instantiateNilTestStruct() (interface{}, interface{}) { return NilTestStruct{}, &NilTestStruct{} } func validateNilTestStruct(o interface{}, t *testing.T) { nts := o.(NilTestStruct) if nts.IntPtr != nil { t.Errorf("Expected nts.IntPtr to be nil, got %v", nts.IntPtr) } if nts.CatPtr != nil { t.Errorf("Expected nts.CatPtr to be nil, got %v", nts.CatPtr) } if nts.Animal != nil { t.Errorf("Expected nts.Animal to be nil, got %v", nts.Animal) } } //------------------------------------- type ComplexStruct struct { Name string Animal Animal } func constructComplex() interface{} { c := ComplexStruct{ Name: "Complex", Animal: constructBasic(), } return c } func instantiateComplex() (interface{}, interface{}) { return ComplexStruct{}, &ComplexStruct{} } func validateComplex(o interface{}, t *testing.T) { c2 := o.(ComplexStruct) if cat, ok := c2.Animal.(Cat); ok { validateBasic(cat, t) } else { t.Errorf("Expected c2.Animal to be of type cat, got %v", reflect.ValueOf(c2.Animal).Elem().Type()) } } //------------------------------------- type ComplexStruct2 struct { Cat Cat Dog *Dog Snake Snake Snake2 *Snake Viper Viper Viper2 *Viper } func constructComplex2() interface{} { snake_ := Snake([]byte("hiss")) snakePtr_ := &snake_ c := ComplexStruct2{ Cat: Cat{ SimpleStruct{ String: "String", Bytes: []byte("Bytes"), Time: time.Now(), }, }, Dog: &Dog{ SimpleStruct{ String: "Woof", Bytes: []byte("Bark"), Time: time.Now(), }, }, Snake: Snake([]byte("hiss")), Snake2: snakePtr_, Viper: Viper{Bytes: []byte("hizz")}, Viper2: &Viper{Bytes: []byte("hizz")}, } return c } func instantiateComplex2() (interface{}, interface{}) { return ComplexStruct2{}, &ComplexStruct2{} } func validateComplex2(o interface{}, t *testing.T) { c2 := o.(ComplexStruct2) cat := c2.Cat if cat.String != "String" { t.Errorf("Expected cat.String == 'String', got %v", cat.String) } if string(cat.Bytes) != "Bytes" { t.Errorf("Expected cat.Bytes == 'Bytes', got %X", cat.Bytes) } dog := c2.Dog if dog.String != "Woof" { t.Errorf("Expected dog.String == 'Woof', got %v", dog.String) } if string(dog.Bytes) != "Bark" { t.Errorf("Expected dog.Bytes == 'Bark', got %X", dog.Bytes) } snake := c2.Snake if string(snake) != "hiss" { t.Errorf("Expected string(snake) == 'hiss', got %v", string(snake)) } snake2 := c2.Snake2 if string(*snake2) != "hiss" { t.Errorf("Expected string(snake2) == 'hiss', got %v", string(*snake2)) } viper := c2.Viper if string(viper.Bytes) != "hizz" { t.Errorf("Expected string(viper.Bytes) == 'hizz', got %v", string(viper.Bytes)) } viper2 := c2.Viper2 if string(viper2.Bytes) != "hizz" { t.Errorf("Expected string(viper2.Bytes) == 'hizz', got %v", string(viper2.Bytes)) } } //------------------------------------- type ComplexStructArray struct { Animals []Animal Bytes [5]byte Ints [5]int Array SimpleArray } func constructComplexArray() interface{} { c := ComplexStructArray{ Animals: []Animal{ Cat{ SimpleStruct{ String: "String", Bytes: []byte("Bytes"), Time: time.Now(), }, }, Dog{ SimpleStruct{ String: "Woof", Bytes: []byte("Bark"), Time: time.Now(), }, }, Snake([]byte("hiss")), &Viper{ Bytes: []byte("hizz"), }, }, Bytes: [5]byte{1, 10, 50, 100, 200}, Ints: [5]int{1, 2, 3, 4, 5}, Array: SimpleArray([5]byte{1, 10, 50, 100, 200}), } return c } func instantiateComplexArray() (interface{}, interface{}) { return ComplexStructArray{}, &ComplexStructArray{} } func validateComplexArray(o interface{}, t *testing.T) { c2 := o.(ComplexStructArray) if cat, ok := c2.Animals[0].(Cat); ok { if cat.String != "String" { t.Errorf("Expected cat.String == 'String', got %v", cat.String) } if string(cat.Bytes) != "Bytes" { t.Errorf("Expected cat.Bytes == 'Bytes', got %X", cat.Bytes) } } else { t.Errorf("Expected c2.Animals[0] to be of type cat, got %v", reflect.ValueOf(c2.Animals[0]).Elem().Type()) } if dog, ok := c2.Animals[1].(Dog); ok { if dog.String != "Woof" { t.Errorf("Expected dog.String == 'Woof', got %v", dog.String) } if string(dog.Bytes) != "Bark" { t.Errorf("Expected dog.Bytes == 'Bark', got %X", dog.Bytes) } } else { t.Errorf("Expected c2.Animals[1] to be of type dog, got %v", reflect.ValueOf(c2.Animals[1]).Elem().Type()) } if snake, ok := c2.Animals[2].(Snake); ok { if string(snake) != "hiss" { t.Errorf("Expected string(snake) == 'hiss', got %v", string(snake)) } } else { t.Errorf("Expected c2.Animals[2] to be of type Snake, got %v", reflect.ValueOf(c2.Animals[2]).Elem().Type()) } if viper, ok := c2.Animals[3].(*Viper); ok { if string(viper.Bytes) != "hizz" { t.Errorf("Expected string(viper.Bytes) == 'hizz', got %v", string(viper.Bytes)) } } else { t.Errorf("Expected c2.Animals[3] to be of type *Viper, got %v", reflect.ValueOf(c2.Animals[3]).Elem().Type()) } } //----------------------------------------------------------------------------- var testCases = []TestCase{} func init() { testCases = append(testCases, TestCase{constructBasic, instantiateBasic, validateBasic}) testCases = append(testCases, TestCase{constructComplex, instantiateComplex, validateComplex}) testCases = append(testCases, TestCase{constructComplex2, instantiateComplex2, validateComplex2}) testCases = append(testCases, TestCase{constructComplexArray, instantiateComplexArray, validateComplexArray}) testCases = append(testCases, TestCase{constructNilTestStruct, instantiateNilTestStruct, validateNilTestStruct}) } func TestBinary(t *testing.T) { for i, testCase := range testCases { t.Log(fmt.Sprintf("Running test case %v", i)) // Construct an object o := testCase.Constructor() // Write the object data := BinaryBytes(o) t.Logf("Binary: %X", data) instance, instancePtr := testCase.Instantiator() // Read onto a struct n, err := new(int), new(error) res := ReadBinary(instance, bytes.NewReader(data), 0, n, err) if *err != nil { t.Fatalf("Failed to read into instance: %v", *err) } // Validate object testCase.Validator(res, t) // Read onto a pointer n, err = new(int), new(error) res = ReadBinaryPtr(instancePtr, bytes.NewReader(data), 0, n, err) if *err != nil { t.Fatalf("Failed to read into instance: %v", *err) } if res != instancePtr { t.Errorf("Expected pointer to pass through") } // Validate object testCase.Validator(reflect.ValueOf(res).Elem().Interface(), t) // Read with len(data)-1 limit should fail. instance, _ = testCase.Instantiator() n, err = new(int), new(error) ReadBinary(instance, bytes.NewReader(data), len(data)-1, n, err) if *err != ErrBinaryReadOverflow { t.Fatalf("Expected ErrBinaryReadOverflow") } // Read with len(data) limit should succeed. instance, _ = testCase.Instantiator() n, err = new(int), new(error) ReadBinary(instance, bytes.NewReader(data), len(data), n, err) if *err != nil { t.Fatalf("Failed to read instance with sufficient limit: %v %d %d %s", (*err).Error(), *n, len(data), reflect.TypeOf(instance)) } } } func TestJSON(t *testing.T) { for i, testCase := range testCases { t.Log(fmt.Sprintf("Running test case %v", i)) // Construct an object o := testCase.Constructor() // Write the object data := JSONBytes(o) t.Logf("JSON: %v", string(data)) instance, instancePtr := testCase.Instantiator() // Read onto a struct err := new(error) res := ReadJSON(instance, data, err) if *err != nil { t.Fatalf("Failed to read cat: %v", *err) } // Validate object testCase.Validator(res, t) // Read onto a pointer res = ReadJSON(instancePtr, data, err) if *err != nil { t.Fatalf("Failed to read cat: %v", *err) } if res != instancePtr { t.Errorf("Expected pointer to pass through") } // Validate object testCase.Validator(reflect.ValueOf(res).Elem().Interface(), t) } } //------------------------------------------------------------------------------ type Foo struct { FieldA string `json:"fieldA"` // json field name is "fieldA" FieldB string // json field name is "FieldB" fieldC string // not exported, not serialized. FieldD string `json:",omitempty"` // omit if empty FieldE string `json:",omitempty"` // omit if empty (but won't be) FieldF string `json:"F,omitempty"` // its name is "F", omit if empty FieldG string `json:"G,omitempty"` // its name is "F", omit if empty (but won't be) } func TestJSONFieldNames(t *testing.T) { for i := 0; i < 20; i++ { // Try to ensure deterministic success. foo := Foo{ FieldA: "a", FieldB: "b", fieldC: "c", FieldD: "", // omit because empty FieldE: "e", // no omit, not empty FieldF: "", // omit because empty FieldG: "g", // no omit, not empty } stringified := string(JSONBytes(foo)) expected := `{"fieldA":"a","FieldB":"b","FieldE":"e","G":"g"}` if stringified != expected { t.Fatalf("JSONFieldNames error: expected %v, got %v", expected, stringified) } } } //------------------------------------------------------------------------------ func TestBadAlloc(t *testing.T) { n, err := new(int), new(error) instance := new([]byte) data := cmn.RandBytes(100 * 1024) b := new(bytes.Buffer) // this slice of data claims to be much bigger than it really is WriteUvarint(uint(1<<32-1), b, n, err) b.Write(data) res := ReadBinary(instance, b, 0, n, err) fmt.Println(res, *err) } //------------------------------------------------------------------------------ type SimpleArray [5]byte func TestSimpleArray(t *testing.T) { var foo SimpleArray // Type of pointer to array rt := reflect.TypeOf(&foo) fmt.Printf("rt: %v\n", rt) // *binary.SimpleArray // Type of array itself. // NOTE: normally this is acquired through other means // like introspecting on method signatures, or struct fields. rte := rt.Elem() fmt.Printf("rte: %v\n", rte) // binary.SimpleArray // Get a new pointer to the array // NOTE: calling .Interface() is to get the actual value, // instead of reflection values. ptr := reflect.New(rte).Interface() fmt.Printf("ptr: %v\n", ptr) // &[0 0 0 0 0] // Make a simple int aray fooArray := SimpleArray([5]byte{1, 10, 50, 100, 200}) fooBytes := BinaryBytes(fooArray) fooReader := bytes.NewReader(fooBytes) // Now you can read it. n, err := new(int), new(error) it := ReadBinary(foo, fooReader, 0, n, err).(SimpleArray) if !bytes.Equal(it[:], fooArray[:]) { t.Errorf("Expected %v but got %v", fooArray, it) } } //-------------------------------------------------------------------------------- func TestNilPointerInterface(t *testing.T) { type MyInterface interface{} type MyConcreteStruct1 struct{} type MyConcreteStruct2 struct{} RegisterInterface( struct{ MyInterface }{}, ConcreteType{&MyConcreteStruct1{}, 0x01}, ConcreteType{&MyConcreteStruct2{}, 0x02}, ) type MyStruct struct { MyInterface } myStruct := MyStruct{(*MyConcreteStruct1)(nil)} buf, n, err := new(bytes.Buffer), int(0), error(nil) WriteBinary(myStruct, buf, &n, &err) if err == nil { t.Error("Expected error in writing nil pointer interface") } myStruct = MyStruct{&MyConcreteStruct1{}} buf, n, err = new(bytes.Buffer), int(0), error(nil) WriteBinary(myStruct, buf, &n, &err) if err != nil { t.Error("Unexpected error", err) } } //-------------------------------------------------------------------------------- func TestMultipleInterfaces(t *testing.T) { type MyInterface1 interface{} type MyInterface2 interface{} type Struct1 struct{} type Struct2 struct{} RegisterInterface( struct{ MyInterface1 }{}, ConcreteType{&Struct1{}, 0x01}, ConcreteType{&Struct2{}, 0x02}, ConcreteType{Struct1{}, 0x03}, ConcreteType{Struct2{}, 0x04}, ) RegisterInterface( struct{ MyInterface2 }{}, ConcreteType{&Struct1{}, 0x11}, ConcreteType{&Struct2{}, 0x12}, ConcreteType{Struct1{}, 0x13}, ConcreteType{Struct2{}, 0x14}, ) type MyStruct struct { F1 []MyInterface1 F2 []MyInterface2 } myStruct := MyStruct{ F1: []MyInterface1{ nil, &Struct1{}, &Struct2{}, Struct1{}, Struct2{}, }, F2: []MyInterface2{ nil, &Struct1{}, &Struct2{}, Struct1{}, Struct2{}, }, } buf, n, err := new(bytes.Buffer), int(0), error(nil) WriteBinary(myStruct, buf, &n, &err) if err != nil { t.Error("Unexpected error", err) } if hexStr := hex.EncodeToString(buf.Bytes()); hexStr != "0105"+"0001020304"+"0105"+"0011121314" { t.Error("Unexpected binary bytes", hexStr) } // Now, read myStruct2 := MyStruct{} ReadBinaryPtr(&myStruct2, buf, 0, &n, &err) if err != nil { t.Error("Unexpected error", err) } if len(myStruct2.F1) != 5 { t.Error("Expected F1 to have 5 items") } if myStruct2.F1[0] != nil { t.Error("Expected F1[0] to be nil") } if _, ok := (myStruct2.F1[1]).(*Struct1); !ok { t.Error("Expected F1[1] to be of type *Struct1") } if s, _ := (myStruct2.F1[1]).(*Struct1); s == nil { t.Error("Expected F1[1] to be of type *Struct1 but not nil") } if _, ok := (myStruct2.F1[2]).(*Struct2); !ok { t.Error("Expected F1[2] to be of type *Struct2") } if s, _ := (myStruct2.F1[2]).(*Struct2); s == nil { t.Error("Expected F1[2] to be of type *Struct2 but not nil") } if _, ok := (myStruct2.F1[3]).(Struct1); !ok { t.Error("Expected F1[3] to be of type Struct1") } if _, ok := (myStruct2.F1[4]).(Struct2); !ok { t.Error("Expected F1[4] to be of type Struct2") } if myStruct2.F2[0] != nil { t.Error("Expected F2[0] to be nil") } if _, ok := (myStruct2.F2[1]).(*Struct1); !ok { t.Error("Expected F2[1] to be of type *Struct1") } if s, _ := (myStruct2.F2[1]).(*Struct1); s == nil { t.Error("Expected F2[1] to be of type *Struct1 but not nil") } if _, ok := (myStruct2.F2[2]).(*Struct2); !ok { t.Error("Expected F2[2] to be of type *Struct2") } if s, _ := (myStruct2.F2[2]).(*Struct2); s == nil { t.Error("Expected F2[2] to be of type *Struct2 but not nil") } if _, ok := (myStruct2.F2[3]).(Struct1); !ok { t.Error("Expected F2[3] to be of type Struct1") } if _, ok := (myStruct2.F2[4]).(Struct2); !ok { t.Error("Expected F2[4] to be of type Struct2") } } //-------------------------------------------------------------------------------- func TestPointers(t *testing.T) { type Struct1 struct { Foo int } type MyStruct struct { F1 *Struct1 F2 *Struct1 } myStruct := MyStruct{ F1: nil, F2: &Struct1{8}, } buf, n, err := new(bytes.Buffer), int(0), error(nil) WriteBinary(myStruct, buf, &n, &err) if err != nil { t.Error("Unexpected error", err) } if hexStr := hex.EncodeToString(buf.Bytes()); hexStr != "00"+"010108" { t.Error("Unexpected binary bytes", hexStr) } // Now, read myStruct2 := MyStruct{} ReadBinaryPtr(&myStruct2, buf, 0, &n, &err) if err != nil { t.Error("Unexpected error", err) } if myStruct2.F1 != nil { t.Error("Expected F1 to be nil") } if myStruct2.F2.Foo != 8 { t.Error("Expected F2.Foo to be 8") } } //-------------------------------------------------------------------------------- func TestUnsafe(t *testing.T) { type Struct1 struct { Foo float64 } type Struct2 struct { Foo float64 `wire:"unsafe"` } myStruct := Struct1{5.32} myStruct2 := Struct2{5.32} buf, n, err := new(bytes.Buffer), int(0), error(nil) WriteBinary(myStruct, buf, &n, &err) if err == nil { t.Error("Expected error due to float without `unsafe`") } buf, n, err = new(bytes.Buffer), int(0), error(nil) WriteBinary(myStruct2, buf, &n, &err) if err != nil { t.Error("Unexpected error", err) } var s Struct2 n, err = int(0), error(nil) ReadBinaryPtr(&s, buf, 0, &n, &err) if err != nil { t.Error("Unexpected error", err) } if s.Foo != myStruct2.Foo { t.Error("Expected float values to be the same. Got", s.Foo, "expected", myStruct2.Foo) } } //-------------------------------------------------------------------------------- func TestUnwrap(t *testing.T) { type Result interface{} type ConcreteResult struct{ A int } RegisterInterface( struct{ Result }{}, ConcreteType{&ConcreteResult{}, 0x01}, ) type Struct1 struct { Result Result `json:"unwrap"` other string // this should be ignored, it is unexported Other string `json:"-"` // this should also be ignored } myStruct := Struct1{Result: &ConcreteResult{5}} buf, n, err := new(bytes.Buffer), int(0), error(nil) WriteJSON(myStruct, buf, &n, &err) if err != nil { t.Error("Unexpected error", err) } jsonBytes := buf.Bytes() if string(jsonBytes) != `[1,{"A":5}]` { t.Error("Unexpected jsonBytes", string(jsonBytes)) } var s Struct1 err = error(nil) ReadJSON(&s, jsonBytes, &err) if err != nil { t.Error("Unexpected error", err) } sConcrete, ok := s.Result.(*ConcreteResult) if !ok { t.Error("Expected struct result to be of type ConcreteResult. Got", reflect.TypeOf(s.Result)) } got := sConcrete.A expected := myStruct.Result.(*ConcreteResult).A if got != expected { t.Error("Expected values to match. Got", got, "expected", expected) } }