コード例 #1
0
ファイル: fuzz.go プロジェクト: thriftrw/thriftrw-go
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
}
コード例 #2
0
ファイル: binary_test.go プロジェクト: thriftrw/thriftrw-go
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,
			)
		}
	}
}
コード例 #3
0
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))
}
コード例 #4
0
ファイル: service_test.go プロジェクト: thriftrw/thriftrw-go
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)
		}
	}
}
コード例 #5
0
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())
		}
	}
}
コード例 #6
0
ファイル: struct_test.go プロジェクト: thriftrw/thriftrw-go
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)
		}
	}
}