1// Copyright (c) 2012-2018 Ugorji Nwoke. All rights reserved.
2// Use of this source code is governed by a MIT license found in the LICENSE file.
3
4package 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
43import (
44	"bytes"
45	"flag"
46	"fmt"
47	"io"
48	"io/ioutil"
49	"log"
50	"sync"
51	"testing"
52)
53
54// __DO_NOT_REMOVE__NEEDED_FOR_REPLACING__IMPORT_PATH__FOR_CODEC_BENCH__
55
56type testHED struct {
57	H Handle
58	E *Encoder
59	D *Decoder
60}
61
62type ioReaderWrapper struct {
63	r io.Reader
64}
65
66func (x ioReaderWrapper) Read(p []byte) (n int, err error) {
67	return x.r.Read(p)
68}
69
70type ioWriterWrapper struct {
71	w io.Writer
72}
73
74func (x ioWriterWrapper) Write(p []byte) (n int, err error) {
75	return x.w.Write(p)
76}
77
78var (
79	// testNoopH    = NoopHandle(8)
80	testMsgpackH = &MsgpackHandle{}
81	testBincH    = &BincHandle{}
82	testSimpleH  = &SimpleHandle{}
83	testCborH    = &CborHandle{}
84	testJsonH    = &JsonHandle{}
85
86	testHandles     []Handle
87	testPreInitFns  []func()
88	testPostInitFns []func()
89
90	testOnce sync.Once
91
92	testHEDs []testHED
93)
94
95// flag variables used by tests (and bench)
96var (
97	testDepth int
98
99	testVerbose       bool
100	testInitDebug     bool
101	testStructToArray bool
102	testCanonical     bool
103	testUseReset      bool
104	testSkipIntf      bool
105	testInternStr     bool
106	testUseMust       bool
107	testCheckCircRef  bool
108
109	testUseIoEncDec  int
110	testUseIoWrapper bool
111
112	testMaxInitLen int
113
114	testNumRepeatString int
115
116	testRpcBufsize int
117)
118
119// variables that are not flags, but which can configure the handles
120var (
121	testEncodeOptions EncodeOptions
122	testDecodeOptions DecodeOptions
123)
124
125// flag variables used by bench
126var (
127	benchDoInitBench      bool
128	benchVerify           bool
129	benchUnscientificRes  bool = false
130	benchMapStringKeyOnly bool
131	//depth of 0 maps to ~400bytes json-encoded string, 1 maps to ~1400 bytes, etc
132	//For depth>1, we likely trigger stack growth for encoders, making benchmarking unreliable.
133	benchDepth     int
134	benchInitDebug bool
135)
136
137func init() {
138	log.SetOutput(ioutil.Discard) // don't allow things log to standard out/err
139	testHEDs = make([]testHED, 0, 32)
140	testHandles = append(testHandles,
141		// testNoopH,
142		testMsgpackH, testBincH, testSimpleH, testCborH, testJsonH)
143	// set ExplicitRelease on each handle
144	testMsgpackH.ExplicitRelease = true
145	testBincH.ExplicitRelease = true
146	testSimpleH.ExplicitRelease = true
147	testCborH.ExplicitRelease = true
148	testJsonH.ExplicitRelease = true
149
150	testInitFlags()
151	benchInitFlags()
152}
153
154func testInitFlags() {
155	// delete(testDecOpts.ExtFuncs, timeTyp)
156	flag.IntVar(&testDepth, "tsd", 0, "Test Struc Depth")
157	flag.BoolVar(&testVerbose, "tv", false, "Test Verbose (no longer used - here for compatibility)")
158	flag.BoolVar(&testInitDebug, "tg", false, "Test Init Debug")
159	flag.IntVar(&testUseIoEncDec, "ti", -1, "Use IO Reader/Writer for Marshal/Unmarshal ie >= 0")
160	flag.BoolVar(&testUseIoWrapper, "tiw", false, "Wrap the IO Reader/Writer with a base pass-through reader/writer")
161	flag.BoolVar(&testStructToArray, "ts", false, "Set StructToArray option")
162	flag.BoolVar(&testCanonical, "tc", false, "Set Canonical option")
163	flag.BoolVar(&testInternStr, "te", false, "Set InternStr option")
164	flag.BoolVar(&testSkipIntf, "tf", false, "Skip Interfaces")
165	flag.BoolVar(&testUseReset, "tr", false, "Use Reset")
166	flag.IntVar(&testNumRepeatString, "trs", 8, "Create string variables by repeating a string N times")
167	flag.IntVar(&testMaxInitLen, "tx", 0, "Max Init Len")
168	flag.BoolVar(&testUseMust, "tm", true, "Use Must(En|De)code")
169	flag.BoolVar(&testCheckCircRef, "tl", false, "Use Check Circular Ref")
170}
171
172func benchInitFlags() {
173	flag.BoolVar(&benchMapStringKeyOnly, "bs", false, "Bench use maps with string keys only")
174	flag.BoolVar(&benchInitDebug, "bg", false, "Bench Debug")
175	flag.IntVar(&benchDepth, "bd", 1, "Bench Depth")
176	flag.BoolVar(&benchDoInitBench, "bi", false, "Run Bench Init")
177	flag.BoolVar(&benchVerify, "bv", false, "Verify Decoded Value during Benchmark")
178	flag.BoolVar(&benchUnscientificRes, "bu", false, "Show Unscientific Results during Benchmark")
179}
180
181func testHEDGet(h Handle) *testHED {
182	for i := range testHEDs {
183		v := &testHEDs[i]
184		if v.H == h {
185			return v
186		}
187	}
188	testHEDs = append(testHEDs, testHED{h, NewEncoder(nil, h), NewDecoder(nil, h)})
189	return &testHEDs[len(testHEDs)-1]
190}
191
192func testReinit() {
193	testOnce = sync.Once{}
194	testHEDs = nil
195}
196
197func testInitAll() {
198	// only parse it once.
199	if !flag.Parsed() {
200		flag.Parse()
201	}
202	for _, f := range testPreInitFns {
203		f()
204	}
205	for _, f := range testPostInitFns {
206		f()
207	}
208}
209
210func sTestCodecEncode(ts interface{}, bsIn []byte, fn func([]byte) *bytes.Buffer,
211	h Handle, bh *BasicHandle) (bs []byte, err error) {
212	// bs = make([]byte, 0, approxSize)
213	var e *Encoder
214	var buf *bytes.Buffer
215	if testUseReset {
216		e = testHEDGet(h).E
217	} else {
218		e = NewEncoder(nil, h)
219	}
220	var oldWriteBufferSize int
221	if testUseIoEncDec >= 0 {
222		buf = fn(bsIn)
223		// set the encode options for using a buffer
224		oldWriteBufferSize = bh.WriterBufferSize
225		bh.WriterBufferSize = testUseIoEncDec
226		if testUseIoWrapper {
227			e.Reset(ioWriterWrapper{buf})
228		} else {
229			e.Reset(buf)
230		}
231	} else {
232		bs = bsIn
233		e.ResetBytes(&bs)
234	}
235	if testUseMust {
236		e.MustEncode(ts)
237	} else {
238		err = e.Encode(ts)
239	}
240	if testUseIoEncDec >= 0 {
241		bs = buf.Bytes()
242		bh.WriterBufferSize = oldWriteBufferSize
243	}
244	if !testUseReset {
245		e.Release()
246	}
247	return
248}
249
250func sTestCodecDecode(bs []byte, ts interface{}, h Handle, bh *BasicHandle) (err error) {
251	var d *Decoder
252	// var buf *bytes.Reader
253	if testUseReset {
254		d = testHEDGet(h).D
255	} else {
256		d = NewDecoder(nil, h)
257	}
258	var oldReadBufferSize int
259	if testUseIoEncDec >= 0 {
260		buf := bytes.NewReader(bs)
261		oldReadBufferSize = bh.ReaderBufferSize
262		bh.ReaderBufferSize = testUseIoEncDec
263		if testUseIoWrapper {
264			d.Reset(ioReaderWrapper{buf})
265		} else {
266			d.Reset(buf)
267		}
268	} else {
269		d.ResetBytes(bs)
270	}
271	if testUseMust {
272		d.MustDecode(ts)
273	} else {
274		err = d.Decode(ts)
275	}
276	if testUseIoEncDec >= 0 {
277		bh.ReaderBufferSize = oldReadBufferSize
278	}
279	if !testUseReset {
280		d.Release()
281	}
282	return
283}
284
285// --- functions below are used by both benchmarks and tests
286
287func logT(x interface{}, format string, args ...interface{}) {
288	if t, ok := x.(*testing.T); ok && t != nil {
289		t.Logf(format, args...)
290	} else if b, ok := x.(*testing.B); ok && b != nil {
291		b.Logf(format, args...)
292	} else { // if testing.Verbose() { // if testVerbose {
293		if len(format) == 0 || format[len(format)-1] != '\n' {
294			format = format + "\n"
295		}
296		fmt.Printf(format, args...)
297	}
298}
299
300// --- functions below are used only by benchmarks alone
301
302func fnBenchmarkByteBuf(bsIn []byte) (buf *bytes.Buffer) {
303	// var buf bytes.Buffer
304	// buf.Grow(approxSize)
305	buf = bytes.NewBuffer(bsIn)
306	buf.Truncate(0)
307	return
308}
309
310// func benchFnCodecEncode(ts interface{}, bsIn []byte, h Handle) (bs []byte, err error) {
311// 	return testCodecEncode(ts, bsIn, fnBenchmarkByteBuf, h)
312// }
313
314// func benchFnCodecDecode(bs []byte, ts interface{}, h Handle) (err error) {
315// 	return testCodecDecode(bs, ts, h)
316// }
317