github.com/segmentio/encoding@v0.4.0/thrift/thrift_test.go (about)

     1  package thrift_test
     2  
     3  import (
     4  	"bytes"
     5  	"math"
     6  	"reflect"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/segmentio/encoding/thrift"
    11  )
    12  
    13  var marshalTestValues = [...]struct {
    14  	scenario string
    15  	values   []interface{}
    16  }{
    17  	{
    18  		scenario: "bool",
    19  		values:   []interface{}{false, true},
    20  	},
    21  
    22  	{
    23  		scenario: "int",
    24  		values: []interface{}{
    25  			int(0),
    26  			int(-1),
    27  			int(1),
    28  		},
    29  	},
    30  
    31  	{
    32  		scenario: "int8",
    33  		values: []interface{}{
    34  			int8(0),
    35  			int8(-1),
    36  			int8(1),
    37  			int8(math.MinInt8),
    38  			int8(math.MaxInt8),
    39  		},
    40  	},
    41  
    42  	{
    43  		scenario: "int16",
    44  		values: []interface{}{
    45  			int16(0),
    46  			int16(-1),
    47  			int16(1),
    48  			int16(math.MinInt16),
    49  			int16(math.MaxInt16),
    50  		},
    51  	},
    52  
    53  	{
    54  		scenario: "int32",
    55  		values: []interface{}{
    56  			int32(0),
    57  			int32(-1),
    58  			int32(1),
    59  			int32(math.MinInt32),
    60  			int32(math.MaxInt32),
    61  		},
    62  	},
    63  
    64  	{
    65  		scenario: "int64",
    66  		values: []interface{}{
    67  			int64(0),
    68  			int64(-1),
    69  			int64(1),
    70  			int64(math.MinInt64),
    71  			int64(math.MaxInt64),
    72  		},
    73  	},
    74  
    75  	{
    76  		scenario: "string",
    77  		values: []interface{}{
    78  			"",
    79  			"A",
    80  			"1234567890",
    81  			strings.Repeat("qwertyuiop", 100),
    82  		},
    83  	},
    84  
    85  	{
    86  		scenario: "[]byte",
    87  		values: []interface{}{
    88  			[]byte(""),
    89  			[]byte("A"),
    90  			[]byte("1234567890"),
    91  			bytes.Repeat([]byte("qwertyuiop"), 100),
    92  		},
    93  	},
    94  
    95  	{
    96  		scenario: "[]string",
    97  		values: []interface{}{
    98  			[]string{},
    99  			[]string{"A"},
   100  			[]string{"hello", "world", "!!!"},
   101  			[]string{"0", "1", "3", "4", "5", "6", "7", "8", "9"},
   102  		},
   103  	},
   104  
   105  	{
   106  		scenario: "map[string]int",
   107  		values: []interface{}{
   108  			map[string]int{},
   109  			map[string]int{"A": 1},
   110  			map[string]int{"hello": 1, "world": 2, "answer": 42},
   111  		},
   112  	},
   113  
   114  	{
   115  		scenario: "map[int64]struct{}",
   116  		values: []interface{}{
   117  			map[int64]struct{}{},
   118  			map[int64]struct{}{0: {}, 1: {}, 2: {}},
   119  		},
   120  	},
   121  
   122  	{
   123  		scenario: "[]map[string]struct{}",
   124  		values: []interface{}{
   125  			[]map[string]struct{}{},
   126  			[]map[string]struct{}{{}, {"A": {}, "B": {}, "C": {}}},
   127  		},
   128  	},
   129  
   130  	{
   131  		scenario: "struct{}",
   132  		values:   []interface{}{struct{}{}},
   133  	},
   134  
   135  	{
   136  		scenario: "Point2D",
   137  		values: []interface{}{
   138  			Point2D{},
   139  			Point2D{X: 1},
   140  			Point2D{Y: 2},
   141  			Point2D{X: 3, Y: 4},
   142  		},
   143  	},
   144  
   145  	{
   146  		scenario: "RecursiveStruct",
   147  		values: []interface{}{
   148  			RecursiveStruct{},
   149  			RecursiveStruct{Value: "hello"},
   150  			RecursiveStruct{Value: "hello", Next: &RecursiveStruct{}},
   151  			RecursiveStruct{Value: "hello", Next: &RecursiveStruct{Value: "world", Test: newBool(true)}},
   152  		},
   153  	},
   154  
   155  	{
   156  		scenario: "StructWithEnum",
   157  		values: []interface{}{
   158  			StructWithEnum{},
   159  			StructWithEnum{Enum: 1},
   160  			StructWithEnum{Enum: 2},
   161  		},
   162  	},
   163  
   164  	{
   165  		scenario: "StructWithPointToPointerToBool",
   166  		values: []interface{}{
   167  			StructWithPointerToPointerToBool{
   168  				Test: newBoolPtr(true),
   169  			},
   170  		},
   171  	},
   172  
   173  	{
   174  		scenario: "StructWithEmbeddedStrutPointerWithPointerToPointer",
   175  		values: []interface{}{
   176  			StructWithEmbeddedStrutPointerWithPointerToPointer{
   177  				StructWithPointerToPointerToBool: &StructWithPointerToPointerToBool{
   178  					Test: newBoolPtr(true),
   179  				},
   180  			},
   181  		},
   182  	},
   183  
   184  	{
   185  		scenario: "Union",
   186  		values: []interface{}{
   187  			Union{},
   188  			Union{A: true, F: newBool(true)},
   189  			Union{B: 42, F: newInt(42)},
   190  			Union{C: "hello world!", F: newString("hello world!")},
   191  		},
   192  	},
   193  }
   194  
   195  type Point2D struct {
   196  	X float64 `thrift:"1,required"`
   197  	Y float64 `thrift:"2,required"`
   198  }
   199  
   200  type RecursiveStruct struct {
   201  	Value string           `thrift:"1"`
   202  	Next  *RecursiveStruct `thrift:"2"`
   203  	Test  *bool            `thrift:"3"`
   204  }
   205  
   206  type StructWithEnum struct {
   207  	Enum int8 `thrift:"1,enum"`
   208  }
   209  
   210  type StructWithPointerToPointerToBool struct {
   211  	Test **bool `thrift:"1"`
   212  }
   213  
   214  type StructWithEmbeddedStrutPointerWithPointerToPointer struct {
   215  	*StructWithPointerToPointerToBool
   216  }
   217  
   218  type Union struct {
   219  	A bool        `thrift:"1"`
   220  	B int         `thrift:"2"`
   221  	C string      `thrift:"3"`
   222  	F interface{} `thrift:",union"`
   223  }
   224  
   225  func newBool(b bool) *bool       { return &b }
   226  func newInt(i int) *int          { return &i }
   227  func newString(s string) *string { return &s }
   228  
   229  func newBoolPtr(b bool) **bool {
   230  	p := newBool(b)
   231  	return &p
   232  }
   233  
   234  func TestMarshalUnmarshal(t *testing.T) {
   235  	for _, p := range protocols {
   236  		t.Run(p.name, func(t *testing.T) { testMarshalUnmarshal(t, p.proto) })
   237  	}
   238  }
   239  
   240  func testMarshalUnmarshal(t *testing.T, p thrift.Protocol) {
   241  	for _, test := range marshalTestValues {
   242  		t.Run(test.scenario, func(t *testing.T) {
   243  			for _, value := range test.values {
   244  				b, err := thrift.Marshal(p, value)
   245  				if err != nil {
   246  					t.Fatal("marshal:", err)
   247  				}
   248  
   249  				v := reflect.New(reflect.TypeOf(value))
   250  				if err := thrift.Unmarshal(p, b, v.Interface()); err != nil {
   251  					t.Fatal("unmarshal:", err)
   252  				}
   253  
   254  				if result := v.Elem().Interface(); !reflect.DeepEqual(value, result) {
   255  					t.Errorf("value mismatch:\nwant: %#v\ngot:  %#v", value, result)
   256  				}
   257  			}
   258  		})
   259  	}
   260  }
   261  
   262  func BenchmarkMarshal(b *testing.B) {
   263  	for _, p := range protocols {
   264  		b.Run(p.name, func(b *testing.B) { benchmarkMarshal(b, p.proto) })
   265  	}
   266  }
   267  
   268  type BenchmarkEncodeType struct {
   269  	Name     string               `thrift:"1"`
   270  	Question string               `thrift:"2"`
   271  	Answer   string               `thrift:"3"`
   272  	Sub      *BenchmarkEncodeType `thrift:"4"`
   273  }
   274  
   275  func benchmarkMarshal(b *testing.B, p thrift.Protocol) {
   276  	buf := new(bytes.Buffer)
   277  	enc := thrift.NewEncoder(p.NewWriter(buf))
   278  	val := &BenchmarkEncodeType{
   279  		Name:     "Luke",
   280  		Question: "How are you?",
   281  		Answer:   "42",
   282  		Sub: &BenchmarkEncodeType{
   283  			Name:     "Leia",
   284  			Question: "?",
   285  			Answer:   "whatever",
   286  		},
   287  	}
   288  
   289  	for i := 0; i < b.N; i++ {
   290  		buf.Reset()
   291  		enc.Encode(val)
   292  	}
   293  
   294  	b.SetBytes(int64(buf.Len()))
   295  }
   296  
   297  func BenchmarkUnmarshal(b *testing.B) {
   298  	for _, p := range protocols {
   299  		b.Run(p.name, func(b *testing.B) { benchmarkUnmarshal(b, p.proto) })
   300  	}
   301  }
   302  
   303  type BenchmarkDecodeType struct {
   304  	Name     string               `thrift:"1"`
   305  	Question string               `thrift:"2"`
   306  	Answer   string               `thrift:"3"`
   307  	Sub      *BenchmarkDecodeType `thrift:"4"`
   308  }
   309  
   310  func benchmarkUnmarshal(b *testing.B, p thrift.Protocol) {
   311  	buf, _ := thrift.Marshal(p, &BenchmarkDecodeType{
   312  		Name:     "Luke",
   313  		Question: "How are you?",
   314  		Answer:   "42",
   315  		Sub: &BenchmarkDecodeType{
   316  			Name:     "Leia",
   317  			Question: "?",
   318  			Answer:   "whatever",
   319  		},
   320  	})
   321  
   322  	rb := bytes.NewReader(nil)
   323  	dec := thrift.NewDecoder(p.NewReader(rb))
   324  	val := &BenchmarkDecodeType{}
   325  
   326  	for i := 0; i < b.N; i++ {
   327  		rb.Reset(buf)
   328  		dec.Decode(val)
   329  	}
   330  
   331  	b.SetBytes(int64(len(buf)))
   332  }