1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package event_test
6
7import (
8	"context"
9	"io/ioutil"
10	"log"
11	"testing"
12
13	"golang.org/x/tools/internal/event"
14	"golang.org/x/tools/internal/event/core"
15	"golang.org/x/tools/internal/event/export"
16	"golang.org/x/tools/internal/event/keys"
17	"golang.org/x/tools/internal/event/label"
18)
19
20type Hooks struct {
21	A func(ctx context.Context, a int) (context.Context, func())
22	B func(ctx context.Context, b string) (context.Context, func())
23}
24
25var (
26	aValue  = keys.NewInt("a", "")
27	bValue  = keys.NewString("b", "")
28	aCount  = keys.NewInt64("aCount", "Count of time A is called.")
29	aStat   = keys.NewInt("aValue", "A value.")
30	bCount  = keys.NewInt64("B", "Count of time B is called.")
31	bLength = keys.NewInt("BLen", "B length.")
32
33	Baseline = Hooks{
34		A: func(ctx context.Context, a int) (context.Context, func()) {
35			return ctx, func() {}
36		},
37		B: func(ctx context.Context, b string) (context.Context, func()) {
38			return ctx, func() {}
39		},
40	}
41
42	StdLog = Hooks{
43		A: func(ctx context.Context, a int) (context.Context, func()) {
44			log.Printf("A where a=%d", a)
45			return ctx, func() {}
46		},
47		B: func(ctx context.Context, b string) (context.Context, func()) {
48			log.Printf("B where b=%q", b)
49			return ctx, func() {}
50		},
51	}
52
53	Log = Hooks{
54		A: func(ctx context.Context, a int) (context.Context, func()) {
55			core.Log1(ctx, "A", aValue.Of(a))
56			return ctx, func() {}
57		},
58		B: func(ctx context.Context, b string) (context.Context, func()) {
59			core.Log1(ctx, "B", bValue.Of(b))
60			return ctx, func() {}
61		},
62	}
63
64	Trace = Hooks{
65		A: func(ctx context.Context, a int) (context.Context, func()) {
66			return core.Start1(ctx, "A", aValue.Of(a))
67		},
68		B: func(ctx context.Context, b string) (context.Context, func()) {
69			return core.Start1(ctx, "B", bValue.Of(b))
70		},
71	}
72
73	Stats = Hooks{
74		A: func(ctx context.Context, a int) (context.Context, func()) {
75			core.Metric1(ctx, aStat.Of(a))
76			core.Metric1(ctx, aCount.Of(1))
77			return ctx, func() {}
78		},
79		B: func(ctx context.Context, b string) (context.Context, func()) {
80			core.Metric1(ctx, bLength.Of(len(b)))
81			core.Metric1(ctx, bCount.Of(1))
82			return ctx, func() {}
83		},
84	}
85
86	initialList = []int{0, 1, 22, 333, 4444, 55555, 666666, 7777777}
87	stringList  = []string{
88		"A value",
89		"Some other value",
90		"A nice longer value but not too long",
91		"V",
92		"",
93		"ı",
94		"prime count of values",
95	}
96)
97
98type namedBenchmark struct {
99	name string
100	test func(*testing.B)
101}
102
103func Benchmark(b *testing.B) {
104	b.Run("Baseline", Baseline.runBenchmark)
105	b.Run("StdLog", StdLog.runBenchmark)
106	benchmarks := []namedBenchmark{
107		{"Log", Log.runBenchmark},
108		{"Trace", Trace.runBenchmark},
109		{"Stats", Stats.runBenchmark},
110	}
111
112	event.SetExporter(nil)
113	for _, t := range benchmarks {
114		b.Run(t.name+"NoExporter", t.test)
115	}
116
117	event.SetExporter(noopExporter)
118	for _, t := range benchmarks {
119		b.Run(t.name+"Noop", t.test)
120	}
121
122	event.SetExporter(export.Spans(export.LogWriter(ioutil.Discard, false)))
123	for _, t := range benchmarks {
124		b.Run(t.name, t.test)
125	}
126}
127
128func A(ctx context.Context, hooks Hooks, a int) int {
129	ctx, done := hooks.A(ctx, a)
130	defer done()
131	return B(ctx, hooks, a, stringList[a%len(stringList)])
132}
133
134func B(ctx context.Context, hooks Hooks, a int, b string) int {
135	_, done := hooks.B(ctx, b)
136	defer done()
137	return a + len(b)
138}
139
140func (hooks Hooks) runBenchmark(b *testing.B) {
141	ctx := context.Background()
142	b.ReportAllocs()
143	b.ResetTimer()
144	var acc int
145	for i := 0; i < b.N; i++ {
146		for _, value := range initialList {
147			acc += A(ctx, hooks, value)
148		}
149	}
150}
151
152func init() {
153	log.SetOutput(ioutil.Discard)
154}
155
156func noopExporter(ctx context.Context, ev core.Event, lm label.Map) context.Context {
157	return ctx
158}
159