github.com/cloudwego/dynamicgo@v0.2.6-0.20240519101509-707f41b6b834/testdata/baseline_j2p_test.go (about)

     1  package testdata
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	ejson "encoding/json"
     7  	"math"
     8  	"strconv"
     9  	"sync"
    10  	"testing"
    11  
    12  	"github.com/bytedance/sonic"
    13  	"github.com/cloudwego/dynamicgo/conv"
    14  	"github.com/cloudwego/dynamicgo/conv/j2p"
    15  	"github.com/cloudwego/dynamicgo/testdata/kitex_gen/pb/baseline"
    16  	"github.com/stretchr/testify/require"
    17  	"google.golang.org/protobuf/encoding/protowire"
    18  )
    19  
    20  //go:generate kitex -module=github.com/cloudwego/dynamicgo idl/baseline.proto
    21  const (
    22  	protoPath = "testdata/idl/baseline.proto"
    23  )
    24  
    25  var (
    26  	simplePbJSON  = ""
    27  	nestingPbJSON = ""
    28  )
    29  
    30  func init() {
    31  	// build simpleJSON data
    32  	sobj := getPbSimpleValue()
    33  	sout, err := ejson.Marshal(sobj)
    34  	if err != nil {
    35  		panic(err)
    36  	}
    37  	simplePbJSON = string(sout)
    38  	var out bytes.Buffer
    39  	ejson.Indent(&out, sout, "", "")
    40  
    41  	// build nestingJSON data
    42  	nobj := getPbNestingValue()
    43  	nout, err := ejson.Marshal(nobj)
    44  	if err != nil {
    45  		panic(err)
    46  	}
    47  	nestingPbJSON = string(nout)
    48  	out.Reset()
    49  
    50  	psobj := getPartialSimpleValue()
    51  	_, err = ejson.Marshal(psobj)
    52  	if err != nil {
    53  		panic(err)
    54  	}
    55  
    56  	pnobj := getPartialNestingValue()
    57  	_, err = ejson.Marshal(pnobj)
    58  	if err != nil {
    59  		panic(err)
    60  	}
    61  }
    62  
    63  func getPbSimpleValue() *baseline.Simple {
    64  	return &baseline.Simple{
    65  		ByteField:   []byte{math.MaxInt8},
    66  		I64Field:    math.MaxInt64,
    67  		DoubleField: math.MaxFloat64,
    68  		I32Field:    math.MaxInt32,
    69  		StringField: getString(),
    70  		BinaryField: getBytes(),
    71  	}
    72  }
    73  
    74  func getPbPartialSimpleValue() *baseline.PartialSimple {
    75  	return &baseline.PartialSimple{
    76  		ByteField:   []byte{math.MaxInt8},
    77  		DoubleField: math.MaxFloat64,
    78  		BinaryField: getBytes(),
    79  	}
    80  }
    81  
    82  func getPbNestingValue() *baseline.Nesting {
    83  	var ret = &baseline.Nesting{
    84  		String_:         getString(),
    85  		ListSimple:      []*baseline.Simple{},
    86  		Double:          math.MaxFloat64,
    87  		I32:             math.MaxInt32,
    88  		ListI32:         []int32{},
    89  		I64:             math.MaxInt64,
    90  		MapStringString: map[string]string{},
    91  		SimpleStruct:    getPbSimpleValue(),
    92  		MapI32I64:       map[int32]int64{},
    93  		ListString:      []string{},
    94  		Binary:          getBytes(),
    95  		MapI64String:    map[int64]string{},
    96  		ListI64:         []int64{},
    97  		Byte:            []byte{math.MaxInt8},
    98  		MapStringSimple: map[string]*baseline.Simple{},
    99  	}
   100  	for i := 0; i < listCount; i++ {
   101  		ret.ListSimple = append(ret.ListSimple, getPbSimpleValue())
   102  		ret.ListI32 = append(ret.ListI32, math.MinInt32)
   103  		ret.ListI64 = append(ret.ListI64, math.MinInt64)
   104  		ret.ListString = append(ret.ListString, getString())
   105  	}
   106  
   107  	for i := 0; i < mapCount; i++ {
   108  		ret.MapStringString[strconv.Itoa(i)] = getString()
   109  		ret.MapI32I64[int32(i)] = math.MinInt64
   110  		ret.MapI64String[int64(i)] = getString()
   111  		ret.MapStringSimple[strconv.Itoa(i)] = getPbSimpleValue()
   112  	}
   113  
   114  	return ret
   115  }
   116  
   117  func getPbPartialNestingValue() *baseline.PartialNesting {
   118  	var ret = &baseline.PartialNesting{
   119  		ListSimple:      []*baseline.PartialSimple{},
   120  		SimpleStruct:    getPbPartialSimpleValue(),
   121  		MapStringSimple: map[string]*baseline.PartialSimple{},
   122  	}
   123  	for i := 0; i < listCount; i++ {
   124  		ret.ListSimple = append(ret.ListSimple, getPbPartialSimpleValue())
   125  	}
   126  
   127  	for i := 0; i < mapCount; i++ {
   128  		ret.MapStringSimple[strconv.Itoa(i)] = getPbPartialSimpleValue()
   129  	}
   130  
   131  	return ret
   132  }
   133  
   134  func TestJSON2Protobuf_Simple(t *testing.T) {
   135  	_, err := ejson.Marshal(baseline.Simple{})
   136  	if err != nil {
   137  		t.Fatal(err)
   138  	}
   139  	simple := getPbSimpleDesc()
   140  	// unmarshal json to get pb obj
   141  	stru2 := baseline.Simple{}
   142  	if err := sonic.UnmarshalString(simplePbJSON, &stru2); err != nil {
   143  		t.Fatal(err)
   144  	}
   145  	// convert json to pb bytes
   146  	// nj := convertI642StringSimple(simplePbJSON)    // may have error here
   147  	cv := j2p.NewBinaryConv(conv.Options{
   148  		WriteDefaultField: true,
   149  		EnableHttpMapping: false,
   150  	})
   151  	ctx := context.Background()
   152  
   153  	out, err := cv.Do(ctx, simple, []byte(simplePbJSON))
   154  	require.Nil(t, err)
   155  
   156  	// kitex read pb bytes to pb obj
   157  	stru := baseline.Simple{}
   158  	l := 0
   159  	dataLen := len(out)
   160  	for l < dataLen {
   161  		id, wtyp, tagLen := protowire.ConsumeTag(out)
   162  		if tagLen < 0 {
   163  			t.Fatal("proto data error format")
   164  		}
   165  		l += tagLen
   166  		out = out[tagLen:]
   167  		offset, err := stru.FastRead(out, int8(wtyp), int32(id))
   168  		require.Nil(t, err)
   169  		out = out[offset:]
   170  		l += offset
   171  	}
   172  	if len(out) != 0 {
   173  		t.Fatal("proto data error format")
   174  	}
   175  	require.Equal(t, stru2, stru)
   176  }
   177  
   178  func TestJSON2Protobuf_Simple_Parallel(t *testing.T) {
   179  	_, err := ejson.Marshal(baseline.Simple{})
   180  	if err != nil {
   181  		t.Fatal(err)
   182  	}
   183  	simple := getPbSimpleDesc()
   184  	// unmarshal json to get pb obj
   185  	stru2 := baseline.Simple{}
   186  	if err := sonic.UnmarshalString(simplePbJSON, &stru2); err != nil {
   187  		t.Fatal(err)
   188  	}
   189  	// convert json to pb bytes
   190  	// nj := convertI642StringSimple(simplePbJSON)    // may have error here
   191  	cv := j2p.NewBinaryConv(conv.Options{
   192  		WriteDefaultField: true,
   193  		EnableHttpMapping: false,
   194  	})
   195  	wg := sync.WaitGroup{}
   196  	for i := 0; i < Concurrency; i++ {
   197  		wg.Add(1)
   198  		go func(i int) {
   199  			defer func() {
   200  				if r := recover(); r != nil {
   201  					t.Fatalf("panic: %d\n%s", i, simplePbJSON)
   202  				}
   203  			}()
   204  			defer wg.Done()
   205  			ctx := context.Background()
   206  
   207  			out, err := cv.Do(ctx, simple, []byte(simplePbJSON))
   208  			require.Nil(t, err)
   209  
   210  			stru := baseline.Simple{}
   211  			l := 0
   212  			dataLen := len(out)
   213  			for l < dataLen {
   214  				id, wtyp, tagLen := protowire.ConsumeTag(out)
   215  				if tagLen < 0 {
   216  					t.Fatal("proto data error format")
   217  				}
   218  				l += tagLen
   219  				out = out[tagLen:]
   220  				offset, err := stru.FastRead(out, int8(wtyp), int32(id))
   221  				require.Nil(t, err)
   222  				out = out[offset:]
   223  				l += offset
   224  			}
   225  			if len(out) != 0 {
   226  				t.Fatal("proto data error format")
   227  			}
   228  			require.Equal(t, stru2, stru)
   229  		}(i)
   230  	}
   231  	wg.Wait()
   232  }
   233  
   234  func TestJSON2Protobuf_Nesting(t *testing.T) {
   235  	_, err := ejson.Marshal(baseline.Nesting{})
   236  	if err != nil {
   237  		t.Fatal(err)
   238  	}
   239  	nesting := getPbNestingDesc()
   240  	// unmarshal json to get pb obj
   241  	stru2 := baseline.Nesting{}
   242  	if err := sonic.UnmarshalString(nestingPbJSON, &stru2); err != nil {
   243  		t.Fatal(err)
   244  	}
   245  	// convert json to pb bytes
   246  	// nj := convertI642StringSimple(simplePbJSON)    // may have error here
   247  	cv := j2p.NewBinaryConv(conv.Options{
   248  		WriteDefaultField: true,
   249  		EnableHttpMapping: false,
   250  	})
   251  	ctx := context.Background()
   252  	out, err := cv.Do(ctx, nesting, []byte(nestingPbJSON))
   253  	require.Nil(t, err)
   254  
   255  	// kitex read pb bytes to pb obj
   256  	stru := baseline.Nesting{}
   257  	l := 0
   258  	dataLen := len(out)
   259  	for l < dataLen {
   260  		id, wtyp, tagLen := protowire.ConsumeTag(out)
   261  		if tagLen < 0 {
   262  			t.Fatal("proto data error format")
   263  		}
   264  		l += tagLen
   265  		out = out[tagLen:]
   266  		offset, err := stru.FastRead(out, int8(wtyp), int32(id))
   267  		require.Nil(t, err)
   268  		out = out[offset:]
   269  		l += offset
   270  	}
   271  	if len(out) != 0 {
   272  		t.Fatal("proto data error format")
   273  	}
   274  	require.Equal(t, stru2, stru)
   275  }
   276  
   277  func TestJSON2Protobuf_Nesting_Parallel(t *testing.T) {
   278  	_, err := ejson.Marshal(baseline.Nesting{})
   279  	if err != nil {
   280  		t.Fatal(err)
   281  	}
   282  	nesting := getPbNestingDesc()
   283  	// unmarshal json to get pb obj
   284  	stru2 := baseline.Nesting{}
   285  	if err := sonic.UnmarshalString(nestingPbJSON, &stru2); err != nil {
   286  		t.Fatal(err)
   287  	}
   288  	// convert json to pb bytes
   289  	// nj := convertI642StringSimple(simplePbJSON)    // may have error here
   290  	cv := j2p.NewBinaryConv(conv.Options{
   291  		WriteDefaultField: true,
   292  		EnableHttpMapping: false,
   293  	})
   294  
   295  	wg := sync.WaitGroup{}
   296  	for i := 0; i < Concurrency; i++ {
   297  		wg.Add(1)
   298  		go func(i int) {
   299  			defer func() {
   300  				if r := recover(); r != nil {
   301  					t.Fatalf("panic: %d\n%s", i, nestingPbJSON)
   302  				}
   303  			}()
   304  			defer wg.Done()
   305  			ctx := context.Background()
   306  
   307  			out, err := cv.Do(ctx, nesting, []byte(nestingPbJSON))
   308  			require.Nil(t, err)
   309  
   310  			stru := baseline.Nesting{}
   311  			l := 0
   312  			dataLen := len(out)
   313  			for l < dataLen {
   314  				id, wtyp, tagLen := protowire.ConsumeTag(out)
   315  				if tagLen < 0 {
   316  					t.Fatal("proto data error format")
   317  				}
   318  				l += tagLen
   319  				out = out[tagLen:]
   320  				offset, err := stru.FastRead(out, int8(wtyp), int32(id))
   321  				require.Nil(t, err)
   322  				out = out[offset:]
   323  				l += offset
   324  			}
   325  			if len(out) != 0 {
   326  				t.Fatal("proto data error format")
   327  			}
   328  			require.Equal(t, stru2, stru)
   329  		}(i)
   330  	}
   331  	wg.Wait()
   332  }
   333  
   334  func BenchmarkJSON2Protobuf_DynamicGo_Raw(b *testing.B) {
   335  	b.Run("small", func(b *testing.B) {
   336  		simple := getPbSimpleDesc()
   337  		cv := j2p.NewBinaryConv(conv.Options{
   338  			WriteDefaultField:  false,
   339  			EnableValueMapping: true,
   340  		})
   341  		// nj := []byte(convertI642StringSimple(simpleJSON))
   342  		ctx := context.Background()
   343  		out, err := cv.Do(ctx, simple, []byte(simplePbJSON))
   344  		require.Nil(b, err)
   345  
   346  		b.SetBytes(int64(len(out)))
   347  		b.ResetTimer()
   348  		for i := 0; i < b.N; i++ {
   349  			_, _ = cv.Do(ctx, simple, []byte(simplePbJSON))
   350  		}
   351  	})
   352  
   353  	b.Run("medium", func(b *testing.B) {
   354  		nesting := getPbNestingDesc()
   355  		cv := j2p.NewBinaryConv(conv.Options{
   356  			WriteDefaultField:  false,
   357  			EnableValueMapping: true,
   358  		})
   359  		// println(string(nestingPbJSON))
   360  		// nj := []byte(convertI642StringSimple(simpleJSON))
   361  		ctx := context.Background()
   362  
   363  		out, err := cv.Do(ctx, nesting, []byte(nestingPbJSON))
   364  		require.Nil(b, err)
   365  
   366  		b.SetBytes(int64(len(out)))
   367  		b.ResetTimer()
   368  		for i := 0; i < b.N; i++ {
   369  			_, _ = cv.Do(ctx, nesting, []byte(nestingPbJSON))
   370  		}
   371  	})
   372  }
   373  
   374  func BenchmarkJSON2Protobuf_SonicAndKitex(b *testing.B) {
   375  	b.Run("small", func(b *testing.B) {
   376  		v := baseline.Simple{}
   377  		if err := sonic.UnmarshalString(simplePbJSON, &v); err != nil {
   378  			b.Fatal(err)
   379  		}
   380  		var buf = make([]byte, v.Size())
   381  		v.FastWrite(buf)
   382  
   383  		b.SetBytes(int64(len(buf)))
   384  		b.ResetTimer()
   385  		for i := 0; i < b.N; i++ {
   386  			v := baseline.Simple{}
   387  			_ = v.Size()
   388  			_ = sonic.UnmarshalString(simplePbJSON, &v)
   389  			v.FastWrite(buf)
   390  		}
   391  	})
   392  
   393  	b.Run("medium", func(b *testing.B) {
   394  		v := baseline.Nesting{}
   395  		if err := sonic.UnmarshalString(nestingPbJSON, &v); err != nil {
   396  			b.Fatal(err)
   397  		}
   398  		var buf = make([]byte, v.Size())
   399  		v.FastWrite(buf)
   400  
   401  		b.SetBytes(int64(len(buf)))
   402  		b.ResetTimer()
   403  		for i := 0; i < b.N; i++ {
   404  			v := baseline.Nesting{}
   405  			_ = v.Size()
   406  			_ = sonic.UnmarshalString(nestingPbJSON, &v)
   407  			v.FastWrite(buf)
   408  		}
   409  	})
   410  }
   411  
   412  // func BenchmarkJSON2Protobuf_ProtoBufGo(b *testing.B) {
   413  // 	b.Run("small", func(b *testing.B) {
   414  // 		v := baseline.Simple{}
   415  // 		if err := protojson.Unmarshal([]byte(simplePbJSON), v.ProtoReflect().Interface()); err != nil {
   416  // 			b.Fatal(err)
   417  // 		}
   418  // 		b.ResetTimer()
   419  // 		for i := 0; i < b.N; i++ {
   420  // 			_ = protojson.Unmarshal([]byte(simplePbJSON), v.ProtoReflect().Interface())
   421  // 		}
   422  // 	})
   423  
   424  // 	b.Run("medium", func(b *testing.B) {
   425  // 		v := baseline.Nesting{}
   426  // 		if err := protojson.Unmarshal([]byte(nestingPbJSON), v.ProtoReflect().Interface()); err != nil {
   427  // 			b.Fatal(err)
   428  // 		}
   429  // 		b.ResetTimer()
   430  // 		for i := 0; i < b.N; i++ {
   431  // 			_ = protojson.Unmarshal([]byte(nestingPbJSON), v.ProtoReflect().Interface())
   432  // 		}
   433  // 	})
   434  // }