1// Protocol Buffers for Go with Gadgets
2//
3// Copyright (c) 2013, The GoGo Authors. All rights reserved.
4// http://github.com/gogo/protobuf
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10//     * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12//     * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29/*
30The testgen plugin generates Test and Benchmark functions for each message.
31
32Tests are enabled using the following extensions:
33
34  - testgen
35  - testgen_all
36
37Benchmarks are enabled using the following extensions:
38
39  - benchgen
40  - benchgen_all
41
42Let us look at:
43
44  github.com/gogo/protobuf/test/example/example.proto
45
46Btw all the output can be seen at:
47
48  github.com/gogo/protobuf/test/example/*
49
50The following message:
51
52  option (gogoproto.testgen_all) = true;
53  option (gogoproto.benchgen_all) = true;
54
55  message A {
56	optional string Description = 1 [(gogoproto.nullable) = false];
57	optional int64 Number = 2 [(gogoproto.nullable) = false];
58	optional bytes Id = 3 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uuid", (gogoproto.nullable) = false];
59  }
60
61given to the testgen plugin, will generate the following test code:
62
63	func TestAProto(t *testing.T) {
64		popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano()))
65		p := NewPopulatedA(popr, false)
66		dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
67		if err != nil {
68			panic(err)
69		}
70		msg := &A{}
71		if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
72			panic(err)
73		}
74		for i := range dAtA {
75			dAtA[i] = byte(popr.Intn(256))
76		}
77		if err := p.VerboseEqual(msg); err != nil {
78			t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err)
79		}
80		if !p.Equal(msg) {
81			t.Fatalf("%#v !Proto %#v", msg, p)
82		}
83	}
84
85	func BenchmarkAProtoMarshal(b *testing.B) {
86		popr := math_rand.New(math_rand.NewSource(616))
87		total := 0
88		pops := make([]*A, 10000)
89		for i := 0; i < 10000; i++ {
90			pops[i] = NewPopulatedA(popr, false)
91		}
92		b.ResetTimer()
93		for i := 0; i < b.N; i++ {
94			dAtA, err := github_com_gogo_protobuf_proto.Marshal(pops[i%10000])
95			if err != nil {
96				panic(err)
97			}
98			total += len(dAtA)
99		}
100		b.SetBytes(int64(total / b.N))
101	}
102
103	func BenchmarkAProtoUnmarshal(b *testing.B) {
104		popr := math_rand.New(math_rand.NewSource(616))
105		total := 0
106		datas := make([][]byte, 10000)
107		for i := 0; i < 10000; i++ {
108			dAtA, err := github_com_gogo_protobuf_proto.Marshal(NewPopulatedA(popr, false))
109			if err != nil {
110				panic(err)
111			}
112			datas[i] = dAtA
113		}
114		msg := &A{}
115		b.ResetTimer()
116		for i := 0; i < b.N; i++ {
117			total += len(datas[i%10000])
118			if err := github_com_gogo_protobuf_proto.Unmarshal(datas[i%10000], msg); err != nil {
119				panic(err)
120			}
121		}
122		b.SetBytes(int64(total / b.N))
123	}
124
125
126	func TestAJSON(t *testing1.T) {
127		popr := math_rand1.New(math_rand1.NewSource(time1.Now().UnixNano()))
128		p := NewPopulatedA(popr, true)
129		jsondata, err := encoding_json.Marshal(p)
130		if err != nil {
131			panic(err)
132		}
133		msg := &A{}
134		err = encoding_json.Unmarshal(jsondata, msg)
135		if err != nil {
136			panic(err)
137		}
138		if err := p.VerboseEqual(msg); err != nil {
139			t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err)
140		}
141		if !p.Equal(msg) {
142			t.Fatalf("%#v !Json Equal %#v", msg, p)
143		}
144	}
145
146	func TestAProtoText(t *testing2.T) {
147		popr := math_rand2.New(math_rand2.NewSource(time2.Now().UnixNano()))
148		p := NewPopulatedA(popr, true)
149		dAtA := github_com_gogo_protobuf_proto1.MarshalTextString(p)
150		msg := &A{}
151		if err := github_com_gogo_protobuf_proto1.UnmarshalText(dAtA, msg); err != nil {
152			panic(err)
153		}
154		if err := p.VerboseEqual(msg); err != nil {
155			t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err)
156		}
157		if !p.Equal(msg) {
158			t.Fatalf("%#v !Proto %#v", msg, p)
159		}
160	}
161
162	func TestAProtoCompactText(t *testing2.T) {
163		popr := math_rand2.New(math_rand2.NewSource(time2.Now().UnixNano()))
164		p := NewPopulatedA(popr, true)
165		dAtA := github_com_gogo_protobuf_proto1.CompactTextString(p)
166		msg := &A{}
167		if err := github_com_gogo_protobuf_proto1.UnmarshalText(dAtA, msg); err != nil {
168			panic(err)
169		}
170		if err := p.VerboseEqual(msg); err != nil {
171			t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err)
172		}
173		if !p.Equal(msg) {
174			t.Fatalf("%#v !Proto %#v", msg, p)
175		}
176	}
177
178Other registered tests are also generated.
179Tests are registered to this test plugin by calling the following function.
180
181  func RegisterTestPlugin(newFunc NewTestPlugin)
182
183where NewTestPlugin is:
184
185  type NewTestPlugin func(g *generator.Generator) TestPlugin
186
187and TestPlugin is an interface:
188
189  type TestPlugin interface {
190	Generate(imports generator.PluginImports, file *generator.FileDescriptor) (used bool)
191  }
192
193Plugins that use this interface include:
194
195  - populate
196  - gostring
197  - equal
198  - union
199  - and more
200
201Please look at these plugins as examples of how to create your own.
202A good idea is to let each plugin generate its own tests.
203
204*/
205package testgen
206
207import (
208	"github.com/gogo/protobuf/gogoproto"
209	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
210)
211
212type TestPlugin interface {
213	Generate(imports generator.PluginImports, file *generator.FileDescriptor) (used bool)
214}
215
216type NewTestPlugin func(g *generator.Generator) TestPlugin
217
218var testplugins = make([]NewTestPlugin, 0)
219
220func RegisterTestPlugin(newFunc NewTestPlugin) {
221	testplugins = append(testplugins, newFunc)
222}
223
224type plugin struct {
225	*generator.Generator
226	generator.PluginImports
227	tests []TestPlugin
228}
229
230func NewPlugin() *plugin {
231	return &plugin{}
232}
233
234func (p *plugin) Name() string {
235	return "testgen"
236}
237
238func (p *plugin) Init(g *generator.Generator) {
239	p.Generator = g
240	p.tests = make([]TestPlugin, 0, len(testplugins))
241	for i := range testplugins {
242		p.tests = append(p.tests, testplugins[i](g))
243	}
244}
245
246func (p *plugin) Generate(file *generator.FileDescriptor) {
247	p.PluginImports = generator.NewPluginImports(p.Generator)
248	atLeastOne := false
249	for i := range p.tests {
250		used := p.tests[i].Generate(p.PluginImports, file)
251		if used {
252			atLeastOne = true
253		}
254	}
255	if atLeastOne {
256		p.P(`//These tests are generated by github.com/gogo/protobuf/plugin/testgen`)
257	}
258}
259
260type testProto struct {
261	*generator.Generator
262}
263
264func newProto(g *generator.Generator) TestPlugin {
265	return &testProto{g}
266}
267
268func (p *testProto) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
269	used := false
270	testingPkg := imports.NewImport("testing")
271	randPkg := imports.NewImport("math/rand")
272	timePkg := imports.NewImport("time")
273	protoPkg := imports.NewImport("github.com/gogo/protobuf/proto")
274	if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
275		protoPkg = imports.NewImport("github.com/golang/protobuf/proto")
276	}
277	for _, message := range file.Messages() {
278		ccTypeName := generator.CamelCaseSlice(message.TypeName())
279		if message.DescriptorProto.GetOptions().GetMapEntry() {
280			continue
281		}
282		if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
283			used = true
284
285			p.P(`func Test`, ccTypeName, `Proto(t *`, testingPkg.Use(), `.T) {`)
286			p.In()
287			p.P(`seed := `, timePkg.Use(), `.Now().UnixNano()`)
288			p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(seed))`)
289			p.P(`p := NewPopulated`, ccTypeName, `(popr, false)`)
290			p.P(`dAtA, err := `, protoPkg.Use(), `.Marshal(p)`)
291			p.P(`if err != nil {`)
292			p.In()
293			p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
294			p.Out()
295			p.P(`}`)
296			p.P(`msg := &`, ccTypeName, `{}`)
297			p.P(`if err := `, protoPkg.Use(), `.Unmarshal(dAtA, msg); err != nil {`)
298			p.In()
299			p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
300			p.Out()
301			p.P(`}`)
302			p.P(`littlefuzz := make([]byte, len(dAtA))`)
303			p.P(`copy(littlefuzz, dAtA)`)
304			p.P(`for i := range dAtA {`)
305			p.In()
306			p.P(`dAtA[i] = byte(popr.Intn(256))`)
307			p.Out()
308			p.P(`}`)
309			if gogoproto.HasVerboseEqual(file.FileDescriptorProto, message.DescriptorProto) {
310				p.P(`if err := p.VerboseEqual(msg); err != nil {`)
311				p.In()
312				p.P(`t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err)`)
313				p.Out()
314				p.P(`}`)
315			}
316			p.P(`if !p.Equal(msg) {`)
317			p.In()
318			p.P(`t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)`)
319			p.Out()
320			p.P(`}`)
321			p.P(`if len(littlefuzz) > 0 {`)
322			p.In()
323			p.P(`fuzzamount := 100`)
324			p.P(`for i := 0; i < fuzzamount; i++ {`)
325			p.In()
326			p.P(`littlefuzz[popr.Intn(len(littlefuzz))] = byte(popr.Intn(256))`)
327			p.P(`littlefuzz = append(littlefuzz, byte(popr.Intn(256)))`)
328			p.Out()
329			p.P(`}`)
330			p.P(`// shouldn't panic`)
331			p.P(`_ = `, protoPkg.Use(), `.Unmarshal(littlefuzz, msg)`)
332			p.Out()
333			p.P(`}`)
334			p.Out()
335			p.P(`}`)
336			p.P()
337		}
338
339		if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
340			if gogoproto.IsMarshaler(file.FileDescriptorProto, message.DescriptorProto) || gogoproto.IsUnsafeMarshaler(file.FileDescriptorProto, message.DescriptorProto) {
341				p.P(`func Test`, ccTypeName, `MarshalTo(t *`, testingPkg.Use(), `.T) {`)
342				p.In()
343				p.P(`seed := `, timePkg.Use(), `.Now().UnixNano()`)
344				p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(seed))`)
345				p.P(`p := NewPopulated`, ccTypeName, `(popr, false)`)
346				if gogoproto.IsProtoSizer(file.FileDescriptorProto, message.DescriptorProto) {
347					p.P(`size := p.ProtoSize()`)
348				} else {
349					p.P(`size := p.Size()`)
350				}
351				p.P(`dAtA := make([]byte, size)`)
352				p.P(`for i := range dAtA {`)
353				p.In()
354				p.P(`dAtA[i] = byte(popr.Intn(256))`)
355				p.Out()
356				p.P(`}`)
357				p.P(`_, err := p.MarshalTo(dAtA)`)
358				p.P(`if err != nil {`)
359				p.In()
360				p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
361				p.Out()
362				p.P(`}`)
363				p.P(`msg := &`, ccTypeName, `{}`)
364				p.P(`if err := `, protoPkg.Use(), `.Unmarshal(dAtA, msg); err != nil {`)
365				p.In()
366				p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
367				p.Out()
368				p.P(`}`)
369				p.P(`for i := range dAtA {`)
370				p.In()
371				p.P(`dAtA[i] = byte(popr.Intn(256))`)
372				p.Out()
373				p.P(`}`)
374				if gogoproto.HasVerboseEqual(file.FileDescriptorProto, message.DescriptorProto) {
375					p.P(`if err := p.VerboseEqual(msg); err != nil {`)
376					p.In()
377					p.P(`t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err)`)
378					p.Out()
379					p.P(`}`)
380				}
381				p.P(`if !p.Equal(msg) {`)
382				p.In()
383				p.P(`t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)`)
384				p.Out()
385				p.P(`}`)
386				p.Out()
387				p.P(`}`)
388				p.P()
389			}
390		}
391
392		if gogoproto.HasBenchGen(file.FileDescriptorProto, message.DescriptorProto) {
393			used = true
394			p.P(`func Benchmark`, ccTypeName, `ProtoMarshal(b *`, testingPkg.Use(), `.B) {`)
395			p.In()
396			p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(616))`)
397			p.P(`total := 0`)
398			p.P(`pops := make([]*`, ccTypeName, `, 10000)`)
399			p.P(`for i := 0; i < 10000; i++ {`)
400			p.In()
401			p.P(`pops[i] = NewPopulated`, ccTypeName, `(popr, false)`)
402			p.Out()
403			p.P(`}`)
404			p.P(`b.ResetTimer()`)
405			p.P(`for i := 0; i < b.N; i++ {`)
406			p.In()
407			p.P(`dAtA, err := `, protoPkg.Use(), `.Marshal(pops[i%10000])`)
408			p.P(`if err != nil {`)
409			p.In()
410			p.P(`panic(err)`)
411			p.Out()
412			p.P(`}`)
413			p.P(`total += len(dAtA)`)
414			p.Out()
415			p.P(`}`)
416			p.P(`b.SetBytes(int64(total / b.N))`)
417			p.Out()
418			p.P(`}`)
419			p.P()
420
421			p.P(`func Benchmark`, ccTypeName, `ProtoUnmarshal(b *`, testingPkg.Use(), `.B) {`)
422			p.In()
423			p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(616))`)
424			p.P(`total := 0`)
425			p.P(`datas := make([][]byte, 10000)`)
426			p.P(`for i := 0; i < 10000; i++ {`)
427			p.In()
428			p.P(`dAtA, err := `, protoPkg.Use(), `.Marshal(NewPopulated`, ccTypeName, `(popr, false))`)
429			p.P(`if err != nil {`)
430			p.In()
431			p.P(`panic(err)`)
432			p.Out()
433			p.P(`}`)
434			p.P(`datas[i] = dAtA`)
435			p.Out()
436			p.P(`}`)
437			p.P(`msg := &`, ccTypeName, `{}`)
438			p.P(`b.ResetTimer()`)
439			p.P(`for i := 0; i < b.N; i++ {`)
440			p.In()
441			p.P(`total += len(datas[i%10000])`)
442			p.P(`if err := `, protoPkg.Use(), `.Unmarshal(datas[i%10000], msg); err != nil {`)
443			p.In()
444			p.P(`panic(err)`)
445			p.Out()
446			p.P(`}`)
447			p.Out()
448			p.P(`}`)
449			p.P(`b.SetBytes(int64(total / b.N))`)
450			p.Out()
451			p.P(`}`)
452			p.P()
453		}
454	}
455	return used
456}
457
458type testJson struct {
459	*generator.Generator
460}
461
462func newJson(g *generator.Generator) TestPlugin {
463	return &testJson{g}
464}
465
466func (p *testJson) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
467	used := false
468	testingPkg := imports.NewImport("testing")
469	randPkg := imports.NewImport("math/rand")
470	timePkg := imports.NewImport("time")
471	jsonPkg := imports.NewImport("github.com/gogo/protobuf/jsonpb")
472	for _, message := range file.Messages() {
473		ccTypeName := generator.CamelCaseSlice(message.TypeName())
474		if message.DescriptorProto.GetOptions().GetMapEntry() {
475			continue
476		}
477		if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
478			used = true
479			p.P(`func Test`, ccTypeName, `JSON(t *`, testingPkg.Use(), `.T) {`)
480			p.In()
481			p.P(`seed := `, timePkg.Use(), `.Now().UnixNano()`)
482			p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(seed))`)
483			p.P(`p := NewPopulated`, ccTypeName, `(popr, true)`)
484			p.P(`marshaler := `, jsonPkg.Use(), `.Marshaler{}`)
485			p.P(`jsondata, err := marshaler.MarshalToString(p)`)
486			p.P(`if err != nil {`)
487			p.In()
488			p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
489			p.Out()
490			p.P(`}`)
491			p.P(`msg := &`, ccTypeName, `{}`)
492			p.P(`err = `, jsonPkg.Use(), `.UnmarshalString(jsondata, msg)`)
493			p.P(`if err != nil {`)
494			p.In()
495			p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
496			p.Out()
497			p.P(`}`)
498			if gogoproto.HasVerboseEqual(file.FileDescriptorProto, message.DescriptorProto) {
499				p.P(`if err := p.VerboseEqual(msg); err != nil {`)
500				p.In()
501				p.P(`t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err)`)
502				p.Out()
503				p.P(`}`)
504			}
505			p.P(`if !p.Equal(msg) {`)
506			p.In()
507			p.P(`t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)`)
508			p.Out()
509			p.P(`}`)
510			p.Out()
511			p.P(`}`)
512		}
513	}
514	return used
515}
516
517type testText struct {
518	*generator.Generator
519}
520
521func newText(g *generator.Generator) TestPlugin {
522	return &testText{g}
523}
524
525func (p *testText) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
526	used := false
527	testingPkg := imports.NewImport("testing")
528	randPkg := imports.NewImport("math/rand")
529	timePkg := imports.NewImport("time")
530	protoPkg := imports.NewImport("github.com/gogo/protobuf/proto")
531	if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
532		protoPkg = imports.NewImport("github.com/golang/protobuf/proto")
533	}
534	//fmtPkg := imports.NewImport("fmt")
535	for _, message := range file.Messages() {
536		ccTypeName := generator.CamelCaseSlice(message.TypeName())
537		if message.DescriptorProto.GetOptions().GetMapEntry() {
538			continue
539		}
540		if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
541			used = true
542
543			p.P(`func Test`, ccTypeName, `ProtoText(t *`, testingPkg.Use(), `.T) {`)
544			p.In()
545			p.P(`seed := `, timePkg.Use(), `.Now().UnixNano()`)
546			p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(seed))`)
547			p.P(`p := NewPopulated`, ccTypeName, `(popr, true)`)
548			p.P(`dAtA := `, protoPkg.Use(), `.MarshalTextString(p)`)
549			p.P(`msg := &`, ccTypeName, `{}`)
550			p.P(`if err := `, protoPkg.Use(), `.UnmarshalText(dAtA, msg); err != nil {`)
551			p.In()
552			p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
553			p.Out()
554			p.P(`}`)
555			if gogoproto.HasVerboseEqual(file.FileDescriptorProto, message.DescriptorProto) {
556				p.P(`if err := p.VerboseEqual(msg); err != nil {`)
557				p.In()
558				p.P(`t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err)`)
559				p.Out()
560				p.P(`}`)
561			}
562			p.P(`if !p.Equal(msg) {`)
563			p.In()
564			p.P(`t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)`)
565			p.Out()
566			p.P(`}`)
567			p.Out()
568			p.P(`}`)
569			p.P()
570
571			p.P(`func Test`, ccTypeName, `ProtoCompactText(t *`, testingPkg.Use(), `.T) {`)
572			p.In()
573			p.P(`seed := `, timePkg.Use(), `.Now().UnixNano()`)
574			p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(seed))`)
575			p.P(`p := NewPopulated`, ccTypeName, `(popr, true)`)
576			p.P(`dAtA := `, protoPkg.Use(), `.CompactTextString(p)`)
577			p.P(`msg := &`, ccTypeName, `{}`)
578			p.P(`if err := `, protoPkg.Use(), `.UnmarshalText(dAtA, msg); err != nil {`)
579			p.In()
580			p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
581			p.Out()
582			p.P(`}`)
583			if gogoproto.HasVerboseEqual(file.FileDescriptorProto, message.DescriptorProto) {
584				p.P(`if err := p.VerboseEqual(msg); err != nil {`)
585				p.In()
586				p.P(`t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err)`)
587				p.Out()
588				p.P(`}`)
589			}
590			p.P(`if !p.Equal(msg) {`)
591			p.In()
592			p.P(`t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)`)
593			p.Out()
594			p.P(`}`)
595			p.Out()
596			p.P(`}`)
597			p.P()
598
599		}
600	}
601	return used
602}
603
604func init() {
605	RegisterTestPlugin(newProto)
606	RegisterTestPlugin(newJson)
607	RegisterTestPlugin(newText)
608}
609