github.com/patricebensoussan/go/codec@v1.2.99/shared_test.go (about)

     1  // Copyright (c) 2012-2020 Ugorji Nwoke. All rights reserved.
     2  // Use of this source code is governed by a MIT license found in the LICENSE file.
     3  
     4  package codec
     5  
     6  // This file sets up the variables used, including testInitFns.
     7  // Each file should add initialization that should be performed
     8  // after flags are parsed.
     9  //
    10  // init is a multi-step process:
    11  //   - setup vars (handled by init functions in each file)
    12  //   - parse flags
    13  //   - setup derived vars (handled by pre-init registered functions - registered in init function)
    14  //   - post init (handled by post-init registered functions - registered in init function)
    15  // This way, no one has to manage carefully control the initialization
    16  // using file names, etc.
    17  //
    18  // Tests which require external dependencies need the -tag=x parameter.
    19  // They should be run as:
    20  //    go test -tags=x -run=. <other parameters ...>
    21  // Benchmarks should also take this parameter, to include the sereal, xdr, etc.
    22  // To run against codecgen, etc, make sure you pass extra parameters.
    23  // Example usage:
    24  //    go test "-tags=x codecgen" -bench=. <other parameters ...>
    25  //
    26  // To fully test everything:
    27  //    go test -tags=x -benchtime=100ms -tv -bg -bi  -brw -bu -v -run=. -bench=.
    28  
    29  // Handling flags
    30  // codec_test.go will define a set of global flags for testing, including:
    31  //   - Use Reset
    32  //   - Use IO reader/writer (vs direct bytes)
    33  //   - Set Canonical
    34  //   - Set InternStrings
    35  //   - Use Symbols
    36  //
    37  // This way, we can test them all by running same set of tests with a different
    38  // set of flags.
    39  //
    40  // Following this, all the benchmarks will utilize flags set by codec_test.go
    41  // and will not redefine these "global" flags.
    42  
    43  import (
    44  	"bytes"
    45  	"flag"
    46  	"io"
    47  	"io/ioutil"
    48  	"log"
    49  	"sync"
    50  )
    51  
    52  // __DO_NOT_REMOVE__NEEDED_FOR_REPLACING__IMPORT_PATH__FOR_CODEC_BENCH__
    53  
    54  type testHED struct {
    55  	H Handle
    56  	E *Encoder
    57  	D *Decoder
    58  }
    59  
    60  type ioReaderWrapper struct {
    61  	r io.Reader
    62  }
    63  
    64  func (x ioReaderWrapper) Read(p []byte) (n int, err error) {
    65  	return x.r.Read(p)
    66  }
    67  
    68  type ioWriterWrapper struct {
    69  	w io.Writer
    70  }
    71  
    72  func (x ioWriterWrapper) Write(p []byte) (n int, err error) {
    73  	return x.w.Write(p)
    74  }
    75  
    76  var (
    77  	// testNoopH    = NoopHandle(8)
    78  	testMsgpackH = &MsgpackHandle{}
    79  	testBincH    = &BincHandle{}
    80  	testSimpleH  = &SimpleHandle{}
    81  	testCborH    = &CborHandle{}
    82  	testJsonH    = &JsonHandle{}
    83  
    84  	testHandles     []Handle
    85  	testPreInitFns  []func()
    86  	testPostInitFns []func()
    87  
    88  	testOnce sync.Once
    89  
    90  	testHEDs []testHED
    91  )
    92  
    93  // flag variables used by tests (and bench)
    94  var (
    95  	testVerbose bool
    96  
    97  	//depth of 0 maps to ~400bytes json-encoded string, 1 maps to ~1400 bytes, etc
    98  	//For depth>1, we likely trigger stack growth for encoders, making benchmarking unreliable.
    99  	testDepth int
   100  
   101  	testMaxInitLen int
   102  
   103  	testUseReset    bool
   104  	testUseParallel bool
   105  
   106  	testSkipIntf bool
   107  
   108  	testUseIoEncDec  int
   109  	testUseIoWrapper bool
   110  
   111  	testNumRepeatString int
   112  
   113  	testRpcBufsize       int
   114  	testMapStringKeyOnly bool
   115  
   116  	testBenchmarkNoConfig bool
   117  )
   118  
   119  // variables that are not flags, but which can configure the handles
   120  var (
   121  	testEncodeOptions EncodeOptions
   122  	testDecodeOptions DecodeOptions
   123  	testRPCOptions    RPCOptions
   124  )
   125  
   126  func init() {
   127  	log.SetOutput(ioutil.Discard) // don't allow things log to standard out/err
   128  	testHEDs = make([]testHED, 0, 32)
   129  	testHandles = append(testHandles,
   130  		// testNoopH,
   131  		testMsgpackH, testBincH, testSimpleH, testCborH, testJsonH)
   132  	// JSON should do HTMLCharsAsIs by default
   133  	testJsonH.HTMLCharsAsIs = true
   134  	// testJsonH.InternString = true
   135  	testInitFlags()
   136  	benchInitFlags()
   137  }
   138  
   139  func testInitFlags() {
   140  	var bIgnore bool
   141  	// delete(testDecOpts.ExtFuncs, timeTyp)
   142  	flag.BoolVar(&testVerbose, "tv", false, "Text Extra Verbose Logging if -v if set")
   143  	flag.IntVar(&testUseIoEncDec, "ti", -1, "Use IO Reader/Writer for Marshal/Unmarshal ie >= 0")
   144  	flag.BoolVar(&testUseIoWrapper, "tiw", false, "Wrap the IO Reader/Writer with a base pass-through reader/writer")
   145  
   146  	flag.BoolVar(&testSkipIntf, "tf", false, "Skip Interfaces")
   147  	flag.BoolVar(&testUseReset, "tr", false, "Use Reset")
   148  	flag.BoolVar(&testUseParallel, "tp", false, "Run tests in parallel")
   149  	flag.IntVar(&testNumRepeatString, "trs", 8, "Create string variables by repeating a string N times")
   150  	flag.BoolVar(&bIgnore, "tm", true, "(Deprecated) Use Must(En|De)code")
   151  
   152  	flag.IntVar(&testMaxInitLen, "tx", 0, "Max Init Len")
   153  
   154  	flag.IntVar(&testDepth, "tsd", 0, "Test Struc Depth")
   155  	flag.BoolVar(&testMapStringKeyOnly, "tsk", false, "use maps with string keys only")
   156  }
   157  
   158  func benchInitFlags() {
   159  	flag.BoolVar(&testBenchmarkNoConfig, "bnc", false, "benchmarks: do not make configuration changes for fair benchmarking")
   160  	// flags reproduced here for compatibility (duplicate some in testInitFlags)
   161  	flag.BoolVar(&testMapStringKeyOnly, "bs", false, "benchmarks: use maps with string keys only")
   162  	flag.IntVar(&testDepth, "bd", 1, "Benchmarks: Test Struc Depth")
   163  }
   164  
   165  func testHEDGet(h Handle) *testHED {
   166  	for i := range testHEDs {
   167  		v := &testHEDs[i]
   168  		if v.H == h {
   169  			return v
   170  		}
   171  	}
   172  	testHEDs = append(testHEDs, testHED{h, NewEncoder(nil, h), NewDecoder(nil, h)})
   173  	return &testHEDs[len(testHEDs)-1]
   174  }
   175  
   176  func testReinit() {
   177  	testOnce = sync.Once{}
   178  	testHEDs = nil
   179  }
   180  
   181  func testInitAll() {
   182  	// only parse it once.
   183  	if !flag.Parsed() {
   184  		flag.Parse()
   185  	}
   186  	for _, f := range testPreInitFns {
   187  		f()
   188  	}
   189  	for _, f := range testPostInitFns {
   190  		f()
   191  	}
   192  }
   193  
   194  func testSharedCodecEncode(ts interface{}, bsIn []byte, fn func([]byte) *bytes.Buffer,
   195  	h Handle, bh *BasicHandle, useMust bool) (bs []byte, err error) {
   196  	// bs = make([]byte, 0, approxSize)
   197  	var e *Encoder
   198  	var buf *bytes.Buffer
   199  	if testUseReset && !testUseParallel {
   200  		e = testHEDGet(h).E
   201  	} else {
   202  		e = NewEncoder(nil, h)
   203  	}
   204  	var oldWriteBufferSize int
   205  	if testUseIoEncDec >= 0 {
   206  		buf = fn(bsIn)
   207  		// set the encode options for using a buffer
   208  		oldWriteBufferSize = bh.WriterBufferSize
   209  		bh.WriterBufferSize = testUseIoEncDec
   210  		if testUseIoWrapper {
   211  			e.Reset(ioWriterWrapper{buf})
   212  		} else {
   213  			e.Reset(buf)
   214  		}
   215  	} else {
   216  		bs = bsIn
   217  		e.ResetBytes(&bs)
   218  	}
   219  	if useMust {
   220  		e.MustEncode(ts)
   221  	} else {
   222  		err = e.Encode(ts)
   223  	}
   224  	if testUseIoEncDec >= 0 {
   225  		bs = buf.Bytes()
   226  		bh.WriterBufferSize = oldWriteBufferSize
   227  	}
   228  	return
   229  }
   230  
   231  func testSharedCodecDecoder(bs []byte, h Handle, bh *BasicHandle) (d *Decoder, oldReadBufferSize int) {
   232  	// var buf *bytes.Reader
   233  	if testUseReset && !testUseParallel {
   234  		d = testHEDGet(h).D
   235  	} else {
   236  		d = NewDecoder(nil, h)
   237  	}
   238  	if testUseIoEncDec >= 0 {
   239  		buf := bytes.NewReader(bs)
   240  		oldReadBufferSize = bh.ReaderBufferSize
   241  		bh.ReaderBufferSize = testUseIoEncDec
   242  		if testUseIoWrapper {
   243  			d.Reset(ioReaderWrapper{buf})
   244  		} else {
   245  			d.Reset(buf)
   246  		}
   247  	} else {
   248  		d.ResetBytes(bs)
   249  	}
   250  	return
   251  }
   252  
   253  func testSharedCodecDecoderAfter(d *Decoder, oldReadBufferSize int, bh *BasicHandle) {
   254  	if testUseIoEncDec >= 0 {
   255  		bh.ReaderBufferSize = oldReadBufferSize
   256  	}
   257  }
   258  
   259  func testSharedCodecDecode(bs []byte, ts interface{}, h Handle, bh *BasicHandle, useMust bool) (err error) {
   260  	d, oldReadBufferSize := testSharedCodecDecoder(bs, h, bh)
   261  	if useMust {
   262  		d.MustDecode(ts)
   263  	} else {
   264  		err = d.Decode(ts)
   265  	}
   266  	testSharedCodecDecoderAfter(d, oldReadBufferSize, bh)
   267  	return
   268  }
   269  
   270  // // --- functions below are used by both benchmarks and tests
   271  
   272  // // log message only when testVerbose = true (ie go test ... -- -tv).
   273  // //
   274  // // These are for intormational messages that do not necessarily
   275  // // help with diagnosing a failure, or which are too large.
   276  // func logTv(x interface{}, format string, args ...interface{}) {
   277  // 	if !testVerbose {
   278  // 		return
   279  // 	}
   280  // 	if t, ok := x.(testing.TB); ok { // only available from go 1.9
   281  // 		t.Helper()
   282  // 	}
   283  // 	logT(x, format, args...)
   284  // }
   285  
   286  // // logT logs messages when running as go test -v
   287  // //
   288  // // Use it for diagnostics messages that help diagnost failure,
   289  // // and when the output is not too long ie shorter than like 100 characters.
   290  // //
   291  // // In general, any logT followed by failT should call this.
   292  // func logT(x interface{}, format string, args ...interface{}) {
   293  // 	if x == nil {
   294  // 		if len(format) == 0 || format[len(format)-1] != '\n' {
   295  // 			format = format + "\n"
   296  // 		}
   297  // 		fmt.Printf(format, args...)
   298  // 		return
   299  // 	}
   300  // 	if t, ok := x.(testing.TB); ok { // only available from go 1.9
   301  // 		t.Helper()
   302  // 		t.Logf(format, args...)
   303  // 	}
   304  // }
   305  
   306  // func failTv(x testing.TB, args ...interface{}) {
   307  // 	x.Helper()
   308  // 	if testVerbose {
   309  // 		failTMsg(x, args...)
   310  // 	}
   311  // 	x.FailNow()
   312  // }
   313  
   314  // func failT(x testing.TB, args ...interface{}) {
   315  // 	x.Helper()
   316  // 	failTMsg(x, args...)
   317  // 	x.FailNow()
   318  // }
   319  
   320  // func failTMsg(x testing.TB, args ...interface{}) {
   321  // 	x.Helper()
   322  // 	if len(args) > 0 {
   323  // 		if format, ok := args[0].(string); ok {
   324  // 			logT(x, format, args[1:]...)
   325  // 		} else if len(args) == 1 {
   326  // 			logT(x, "%v", args[0])
   327  // 		} else {
   328  // 			logT(x, "%v", args)
   329  // 		}
   330  // 	}
   331  // }
   332  
   333  // --- functions below are used only by benchmarks alone
   334  
   335  func fnBenchmarkByteBuf(bsIn []byte) (buf *bytes.Buffer) {
   336  	// var buf bytes.Buffer
   337  	// buf.Grow(approxSize)
   338  	buf = bytes.NewBuffer(bsIn)
   339  	buf.Truncate(0)
   340  	return
   341  }
   342  
   343  // func benchFnCodecEncode(ts interface{}, bsIn []byte, h Handle) (bs []byte, err error) {
   344  // 	return testCodecEncode(ts, bsIn, fnBenchmarkByteBuf, h)
   345  // }
   346  
   347  // func benchFnCodecDecode(bs []byte, ts interface{}, h Handle) (err error) {
   348  // 	return testCodecDecode(bs, ts, h)
   349  // }