func Fuzz(data []byte) int { reader := NewReader(bytes.NewReader(data)) value, pos, err := reader.ReadValue(wire.TStruct, 0) if err != nil || pos != int64(len(data)) { return 0 } if err := wire.EvaluateValue(value); err != nil { return 0 } buffer := bytes.Buffer{} writer := BorrowWriter(&buffer) if err := writer.WriteValue(value); err != nil { panic(fmt.Sprintf("error encoding %v: %v", value, err)) } ReturnWriter(writer) if encoded := buffer.Bytes(); !bytes.Equal(data, encoded) { panic(fmt.Sprintf( "encoding mismatch for %v:\n\t %#v (got)\n\t!= %#v (expected)\n", value, encoded, data, )) } return 1 }
func checkEOFError(t *testing.T, typ wire.Type, tests []failureTest) { for _, tt := range tests { value, err := Binary.Decode(bytes.NewReader(tt), typ) if err == nil { // lazy collections need to be fully evaluated for the failure to // propagate err = wire.EvaluateValue(value) } if assert.Error(t, err, "Expected failure parsing %x, got %s", tt, value) { assert.Equal( t, io.ErrUnexpectedEOF, err, "Expected EOF error while parsing %x, got %s", tt, err, ) } } }
func TestListOfBinaryReadNil(t *testing.T) { value := wire.NewValueStruct(wire.Struct{Fields: []wire.Field{{ ID: 1, Value: wire.NewValueList( wire.ValueListFromSlice(wire.TBinary, []wire.Value{ wire.NewValueBinary([]byte("foo")), wire.NewValueBinary(nil), wire.NewValueBinary([]byte("bar")), wire.NewValueBinary([]byte("baz")), }), ), }}}) var c tc.PrimitiveContainers require.NoError(t, c.FromWire(value)) got, err := c.ToWire() require.NoError(t, err) require.NoError(t, wire.EvaluateValue(got)) assert.True(t, wire.ValuesAreEqual(value, got)) }
func TestArgsAndResultValidation(t *testing.T) { tests := []struct { desc string serialize thriftType deserialize wire.Value typ reflect.Type // must be set if serialize is not wantError string }{ { desc: "SetValue: args: value: empty", serialize: tv.KeyValue_SetValue_Helper.Args( (*tv.Key)(stringp("foo")), &tu.ArbitraryValue{}, ), deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueString("foo")}, { ID: 2, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{}}), }, }}), wantError: "ArbitraryValue should have exactly one field: got 0 fields", }, { desc: "SetValueV2: args: missing value", typ: reflect.TypeOf(tv.KeyValue_SetValueV2_Args{}), deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 2, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueBool(true)}, }}), }, }}), wantError: "field Key of KeyValue_SetValueV2_Args is required", }, { desc: "SetValueV2: args: missing key", typ: reflect.TypeOf(tv.KeyValue_SetValueV2_Args{}), deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueString("foo")}, }}), wantError: "field Value of KeyValue_SetValueV2_Args is required", }, { desc: "getValue: result: empty", serialize: &tv.KeyValue_GetValue_Result{}, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{}}), wantError: "KeyValue_GetValue_Result should have exactly one field: got 0 fields", }, { desc: "getValue: result: multiple", serialize: &tv.KeyValue_GetValue_Result{ DoesNotExist: &tx.DoesNotExistException{Key: "foo"}, Success: &tu.ArbitraryValue{BoolValue: boolp(true)}, }, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 0, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueBool(true)}, }}), }, { ID: 1, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueString("foo")}, }}), }, }}), wantError: "KeyValue_GetValue_Result should have exactly one field: got 2 fields", }, { desc: "deleteValue: result: multiple", serialize: &tv.KeyValue_DeleteValue_Result{ DoesNotExist: &tx.DoesNotExistException{Key: "foo"}, InternalError: &tv.InternalError{}, }, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueString("foo")}, }}), }, { ID: 2, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{}}), }, }}), wantError: "KeyValue_DeleteValue_Result should have at most one field: got 2 fields", }, } for _, tt := range tests { var typ reflect.Type if tt.serialize != nil { typ = reflect.TypeOf(tt.serialize).Elem() v, err := tt.serialize.ToWire() if err == nil { err = wire.EvaluateValue(v) } if assert.Error(t, err, "%v: expected failure but got %v", tt.desc, v) { assert.Contains(t, err.Error(), tt.wantError, tt.desc) } } else { typ = tt.typ } if typ == nil { t.Fatalf("invalid test %q: either typ or serialize must be set", tt.desc) } x := reflect.New(typ) args := []reflect.Value{reflect.ValueOf(tt.deserialize)} e := x.MethodByName("FromWire").Call(args)[0].Interface() if assert.NotNil(t, e, "%v: expected failure but got %v", tt.desc, x) { assert.Contains(t, e.(error).Error(), tt.wantError, tt.desc) } } }
func TestContainerValidate(t *testing.T) { tests := []struct { value thriftType wantError string }{ { value: &tc.PrimitiveContainers{ ListOfBinary: [][]byte{ {1, 2, 3}, {}, nil, {4, 5, 6}, }, }, wantError: "invalid [2]: value is nil", }, { value: &ts.Graph{ Edges: []*ts.Edge{ {StartPoint: &ts.Point{X: 1, Y: 2}, EndPoint: &ts.Point{X: 3, Y: 4}}, nil, {StartPoint: &ts.Point{X: 5, Y: 6}, EndPoint: &ts.Point{X: 7, Y: 8}}, }, }, wantError: "invalid [1]: value is nil", }, { value: &tc.ContainersOfContainers{ SetOfLists: [][]string{{}, nil}, }, wantError: "invalid set item: value is nil", }, { value: &tc.MapOfBinaryAndString{ BinaryToString: []struct { Key []byte Value string }{ {Key: []byte("hello"), Value: "world"}, {Key: nil, Value: "foo"}, }, }, wantError: "invalid map key: value is nil", }, { value: &tc.MapOfBinaryAndString{ StringToBinary: map[string][]byte{ "hello": []byte("world"), "foo": nil, }, }, wantError: "invalid [foo]: value is nil", }, } for _, tt := range tests { value, err := tt.value.ToWire() if err == nil { err = wire.EvaluateValue(value) // lazy error } if assert.Error(t, err) { assert.Equal(t, tt.wantError, err.Error()) } } }
func TestStructValidation(t *testing.T) { tests := []struct { desc string serialize thriftType deserialize wire.Value typ reflect.Type // must be set if serialize is not wantError string }{ { desc: "Point: missing X", deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueDouble(42)}, }}), typ: reflect.TypeOf(ts.Point{}), wantError: "field Y of Point is required", }, { desc: "Point: missing Y", deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 2, Value: wire.NewValueDouble(42)}, }}), typ: reflect.TypeOf(ts.Point{}), wantError: "field X of Point is required", }, { desc: "Size: missing width", deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueDouble(42)}, }}), typ: reflect.TypeOf(ts.Size{}), wantError: "field Height of Size is required", }, { desc: "Size: missing height", deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 2, Value: wire.NewValueDouble(42)}, }}), typ: reflect.TypeOf(ts.Size{}), wantError: "field Width of Size is required", }, { desc: "Frame: missing topLeft", serialize: &ts.Frame{Size: &ts.Size{Width: 1, Height: 2}}, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 2, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueDouble(1)}, {ID: 2, Value: wire.NewValueDouble(2)}, }}), }, }}), wantError: "field TopLeft of Frame is required", }, { desc: "Frame: missing Size", serialize: &ts.Frame{TopLeft: &ts.Point{X: 1, Y: 2}}, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueDouble(1)}, {ID: 2, Value: wire.NewValueDouble(2)}, }}), }, }}), wantError: "field Size of Frame is required", }, { desc: "Frame: topLeft: missing y", deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueDouble(1)}, }}), }, { ID: 2, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueDouble(1)}, {ID: 2, Value: wire.NewValueDouble(2)}, }}), }, }}), typ: reflect.TypeOf(ts.Frame{}), wantError: "field Y of Point is required", }, { desc: "Graph: missing edges", serialize: &ts.Graph{Edges: nil}, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{}}), wantError: "field Edges of Graph is required", }, { desc: "Graph: edges: misssing end", serialize: &ts.Graph{ Edges: []*ts.Edge{ {StartPoint: &ts.Point{X: 1, Y: 2}, EndPoint: &ts.Point{X: 3, Y: 4}}, {StartPoint: &ts.Point{X: 5, Y: 6}}, }, }, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueList( wire.ValueListFromSlice(wire.TStruct, []wire.Value{ wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueDouble(1)}, {ID: 2, Value: wire.NewValueDouble(2)}, }}), }, { ID: 2, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueDouble(3)}, {ID: 2, Value: wire.NewValueDouble(4)}, }}), }, }}), wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueDouble(1)}, {ID: 2, Value: wire.NewValueDouble(2)}, }}), }, }}), }), ), }, }}), wantError: "field EndPoint of Edge is required", }, { desc: "User: contact: missing emailAddress", deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueString("hello")}, { ID: 2, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{}}), }, }}), typ: reflect.TypeOf(ts.User{}), wantError: "field EmailAddress of ContactInfo is required", }, { desc: "PrimitiveContainersRequired: missing list", serialize: &tc.PrimitiveContainersRequired{ SetOfInts: map[int32]struct{}{ 1: {}, 2: {}, 3: {}, }, MapOfIntsToDoubles: map[int64]float64{1: 2.3, 4: 5.6}, }, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 2, Value: wire.NewValueSet( wire.ValueListFromSlice(wire.TI32, []wire.Value{ wire.NewValueI32(1), wire.NewValueI32(2), wire.NewValueI32(3), }), ), }, { ID: 3, Value: wire.NewValueMap( wire.MapItemListFromSlice(wire.TI64, wire.TDouble, []wire.MapItem{ { Key: wire.NewValueI64(1), Value: wire.NewValueDouble(2.3), }, { Key: wire.NewValueI64(4), Value: wire.NewValueDouble(5.6), }, }), ), }, }}), wantError: "field ListOfStrings of PrimitiveContainersRequired is required", }, { desc: "PrimitiveContainersRequired: missing set", serialize: &tc.PrimitiveContainersRequired{ ListOfStrings: []string{"hello", "world"}, MapOfIntsToDoubles: map[int64]float64{1: 2.3, 4: 5.6}, }, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueList( wire.ValueListFromSlice(wire.TBinary, []wire.Value{ wire.NewValueString("hello"), wire.NewValueString("world"), }), ), }, { ID: 3, Value: wire.NewValueMap( wire.MapItemListFromSlice(wire.TI64, wire.TDouble, []wire.MapItem{ { Key: wire.NewValueI64(1), Value: wire.NewValueDouble(2.3), }, { Key: wire.NewValueI64(4), Value: wire.NewValueDouble(5.6), }, }), ), }, }}), wantError: "field SetOfInts of PrimitiveContainersRequired is required", }, { desc: "PrimitiveContainersRequired: missing map", serialize: &tc.PrimitiveContainersRequired{ ListOfStrings: []string{"hello", "world"}, SetOfInts: map[int32]struct{}{ 1: {}, 2: {}, 3: {}, }, }, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueList( wire.ValueListFromSlice(wire.TBinary, []wire.Value{ wire.NewValueString("hello"), wire.NewValueString("world"), }), ), }, { ID: 2, Value: wire.NewValueSet( wire.ValueListFromSlice(wire.TI32, []wire.Value{ wire.NewValueI32(1), wire.NewValueI32(2), wire.NewValueI32(3), }), ), }, }}), wantError: "field MapOfIntsToDoubles of PrimitiveContainersRequired is required", }, { desc: "Document: empty", serialize: &tu.Document{}, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{}}), wantError: "Document should have exactly one field: got 0 fields", }, { desc: "Document: multiple", serialize: &tu.Document{ Pdf: td.PDF{}, PlainText: stringp("hello"), }, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueBinary([]byte{}), }, { ID: 2, Value: wire.NewValueString("hello"), }, }}), wantError: "Document should have exactly one field: got 2 fields", }, { desc: "ArbitraryValue: empty", serialize: &tu.ArbitraryValue{}, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{}}), wantError: "ArbitraryValue should have exactly one field: got 0 fields", }, { desc: "ArbitraryValue: primitives", serialize: &tu.ArbitraryValue{ BoolValue: boolp(true), Int64Value: int64p(42), StringValue: stringp(""), }, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueBool(true)}, {ID: 2, Value: wire.NewValueI64(42)}, {ID: 3, Value: wire.NewValueString("")}, }}), wantError: "ArbitraryValue should have exactly one field: got 3 fields", }, { desc: "ArbitraryValue: full", serialize: &tu.ArbitraryValue{ BoolValue: boolp(true), Int64Value: int64p(42), StringValue: stringp(""), ListValue: []*tu.ArbitraryValue{ {BoolValue: boolp(true)}, {Int64Value: int64p(42)}, {StringValue: stringp("")}, }, MapValue: map[string]*tu.ArbitraryValue{ "bool": {BoolValue: boolp(true)}, "int": {Int64Value: int64p(42)}, "string": {StringValue: stringp("")}, }, }, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueBool(true)}, {ID: 2, Value: wire.NewValueI64(42)}, {ID: 3, Value: wire.NewValueString("")}, { ID: 4, Value: wire.NewValueList( wire.ValueListFromSlice(wire.TStruct, []wire.Value{ wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueBool(true)}, }}), wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 2, Value: wire.NewValueI64(42)}, }}), wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 3, Value: wire.NewValueString("")}, }}), }), ), }, { ID: 5, Value: wire.NewValueMap( wire.MapItemListFromSlice(wire.TBinary, wire.TStruct, []wire.MapItem{ { Key: wire.NewValueString("bool"), Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueBool(true)}, }}), }, { Key: wire.NewValueString("int"), Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 2, Value: wire.NewValueI64(42)}, }}), }, { Key: wire.NewValueString("string"), Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 3, Value: wire.NewValueString("")}, }}), }, }), ), }, }}), wantError: "ArbitraryValue should have exactly one field: got 5 fields", }, { desc: "ArbitraryValue: error inside a list", serialize: &tu.ArbitraryValue{ ListValue: []*tu.ArbitraryValue{ {BoolValue: boolp(true)}, {}, }, }, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 4, Value: wire.NewValueList( wire.ValueListFromSlice(wire.TStruct, []wire.Value{ wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueBool(true)}, }}), wire.NewValueStruct(wire.Struct{Fields: []wire.Field{}}), })), }, }}), wantError: "ArbitraryValue should have exactly one field: got 0 fields", }, { desc: "ArbitraryValue: error inside a map value", serialize: &tu.ArbitraryValue{ MapValue: map[string]*tu.ArbitraryValue{ "bool": {BoolValue: boolp(true)}, "empty": {}, }, }, deserialize: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 5, Value: wire.NewValueMap( wire.MapItemListFromSlice(wire.TBinary, wire.TStruct, []wire.MapItem{ { Key: wire.NewValueString("bool"), Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueBool(true)}, }}), }, { Key: wire.NewValueString("empty"), Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{}}), }, }), ), }, }}), wantError: "ArbitraryValue should have exactly one field: got 0 fields", }, { desc: "FrameGroup: error inside a set", serialize: &td.FrameGroup{ &ts.Frame{ TopLeft: &ts.Point{X: 1, Y: 2}, Size: &ts.Size{Width: 3, Height: 4}, }, &ts.Frame{TopLeft: &ts.Point{X: 5, Y: 6}}, }, deserialize: wire.NewValueSet( wire.ValueListFromSlice(wire.TStruct, []wire.Value{ wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueDouble(1), }, { ID: 2, Value: wire.NewValueDouble(2), }, }}), }, { ID: 2, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueDouble(3), }, { ID: 2, Value: wire.NewValueDouble(4), }, }}), }, }}), wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueDouble(5), }, { ID: 2, Value: wire.NewValueDouble(6), }, }}), }, }}), }), ), wantError: "field Size of Frame is required", }, { desc: "EdgeMap: error inside a map key", serialize: &td.EdgeMap{ { Key: &ts.Edge{StartPoint: &ts.Point{X: 1, Y: 2}}, Value: &ts.Edge{StartPoint: &ts.Point{X: 3, Y: 4}, EndPoint: &ts.Point{X: 5, Y: 6}}, }, }, deserialize: wire.NewValueMap( wire.MapItemListFromSlice(wire.TStruct, wire.TStruct, []wire.MapItem{ { Key: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueDouble(1)}, {ID: 2, Value: wire.NewValueDouble(2)}, }}), }, }}), Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ { ID: 1, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueDouble(3)}, {ID: 2, Value: wire.NewValueDouble(4)}, }}), }, { ID: 2, Value: wire.NewValueStruct(wire.Struct{Fields: []wire.Field{ {ID: 1, Value: wire.NewValueDouble(5)}, {ID: 2, Value: wire.NewValueDouble(6)}, }}), }, }}), }, }), ), wantError: "field EndPoint of Edge is required", }, } for _, tt := range tests { var typ reflect.Type if tt.serialize != nil { typ = reflect.TypeOf(tt.serialize).Elem() v, err := tt.serialize.ToWire() if err == nil { err = wire.EvaluateValue(v) } if assert.Error(t, err, "%v: expected failure but got %v", tt.desc, v) { assert.Contains(t, err.Error(), tt.wantError, tt.desc) } } else { typ = tt.typ } if typ == nil { t.Fatalf("invalid test %q: either typ or serialize must be set", tt.desc) } x := reflect.New(typ) args := []reflect.Value{reflect.ValueOf(tt.deserialize)} e := x.MethodByName("FromWire").Call(args)[0].Interface() if assert.NotNil(t, e, "%v: expected failure but got %v", tt.desc, x) { assert.Contains(t, e.(error).Error(), tt.wantError, tt.desc) } } }