event: extract capture handler for re-use

Change-Id: Ic920ade0a49684fa3673f65b14c33dd534e54281
Reviewed-on: https://go-review.googlesource.com/c/exp/+/325573
Trust: Ian Cottrell <iancottrell@google.com>
Run-TryBot: Ian Cottrell <iancottrell@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/event/adapter/eventtest/capture.go b/event/adapter/eventtest/capture.go
new file mode 100644
index 0000000..12a4615
--- /dev/null
+++ b/event/adapter/eventtest/capture.go
@@ -0,0 +1,51 @@
+package eventtest
+
+import (
+	"context"
+
+	"golang.org/x/exp/event"
+)
+
+type CaptureHandler struct {
+	Got []event.Event
+}
+
+func (h *CaptureHandler) Log(ctx context.Context, ev *event.Event) {
+	h.event(ctx, ev)
+}
+
+func (h *CaptureHandler) Metric(ctx context.Context, ev *event.Event) {
+	h.event(ctx, ev)
+}
+
+func (h *CaptureHandler) Annotate(ctx context.Context, ev *event.Event) {
+	h.event(ctx, ev)
+}
+
+func (h *CaptureHandler) Start(ctx context.Context, ev *event.Event) context.Context {
+	h.event(ctx, ev)
+	return ctx
+}
+
+func (h *CaptureHandler) End(ctx context.Context, ev *event.Event) {
+	h.event(ctx, ev)
+}
+
+func (h *CaptureHandler) Reset() {
+	if len(h.Got) > 0 {
+		h.Got = h.Got[:0]
+	}
+}
+
+func (h *CaptureHandler) event(ctx context.Context, ev *event.Event) {
+	h.Got = append(h.Got, *ev)
+	got := &h.Got[len(h.Got)-1]
+	got.Labels = make([]event.Label, len(ev.Labels))
+	copy(got.Labels, ev.Labels)
+}
+
+func NewCapture() (context.Context, *CaptureHandler) {
+	h := &CaptureHandler{}
+	ctx := event.WithExporter(context.Background(), event.NewExporter(h, ExporterOptions()))
+	return ctx, h
+}
diff --git a/event/common_test.go b/event/common_test.go
index 27764df..4066f33 100644
--- a/event/common_test.go
+++ b/event/common_test.go
@@ -7,15 +7,14 @@
 package event_test
 
 import (
-	"context"
 	"testing"
 
 	"golang.org/x/exp/event"
+	"golang.org/x/exp/event/adapter/eventtest"
 )
 
 func TestCommon(t *testing.T) {
-	h := &catchHandler{}
-	ctx := event.WithExporter(context.Background(), event.NewExporter(h, nil))
+	ctx, h := eventtest.NewCapture()
 	m := event.NewCounter("m")
 
 	const simple = "simple message"
@@ -24,18 +23,22 @@
 	event.To(ctx).Log(simple)
 	checkFind(t, h, "Log", event.Message, true, simple)
 	checkFind(t, h, "Log", event.Name, false, "")
+	h.Reset()
 
 	event.To(ctx).Metric(m.Record(3))
 	checkFind(t, h, "Metric", event.Message, false, "")
 	checkFind(t, h, "Metric", event.Name, false, "")
+	h.Reset()
 
 	event.To(ctx).Annotate()
 	checkFind(t, h, "Annotate", event.Message, false, "")
 	checkFind(t, h, "Annotate", event.Name, false, "")
+	h.Reset()
 
 	_, end := event.To(ctx).Start(trace)
 	checkFind(t, h, "Start", event.Message, false, "")
 	checkFind(t, h, "Start", event.Name, true, trace)
+	h.Reset()
 
 	end()
 	checkFind(t, h, "End", event.Message, false, "")
@@ -46,8 +49,12 @@
 	Find(*event.Event) (string, bool)
 }
 
-func checkFind(t *testing.T, h *catchHandler, method string, key finder, match bool, text string) {
-	m, ok := key.Find(&h.ev)
+func checkFind(t *testing.T, h *eventtest.CaptureHandler, method string, key finder, match bool, text string) {
+	if len(h.Got) != 1 {
+		t.Errorf("Got %d events, expected 1", len(h.Got))
+		return
+	}
+	m, ok := key.Find(&h.Got[0])
 	if ok && !match {
 		t.Errorf("%s produced an event with a %v", method, key)
 	}
@@ -58,34 +65,3 @@
 		t.Errorf("Expected event with %v %q from %s got %q", key, text, method, m)
 	}
 }
-
-type catchHandler struct {
-	ev event.Event
-}
-
-func (h *catchHandler) Log(ctx context.Context, ev *event.Event) {
-	h.event(ctx, ev)
-}
-
-func (h *catchHandler) Metric(ctx context.Context, ev *event.Event) {
-	h.event(ctx, ev)
-}
-
-func (h *catchHandler) Annotate(ctx context.Context, ev *event.Event) {
-	h.event(ctx, ev)
-}
-
-func (h *catchHandler) Start(ctx context.Context, ev *event.Event) context.Context {
-	h.event(ctx, ev)
-	return ctx
-}
-
-func (h *catchHandler) End(ctx context.Context, ev *event.Event) {
-	h.event(ctx, ev)
-}
-
-func (h *catchHandler) event(ctx context.Context, ev *event.Event) {
-	h.ev = *ev
-	h.ev.Labels = make([]event.Label, len(ev.Labels))
-	copy(h.ev.Labels, ev.Labels)
-}
diff --git a/event/logging/egokit/gokit_test.go b/event/logging/egokit/gokit_test.go
index 228240c..d9e5677 100644
--- a/event/logging/egokit/gokit_test.go
+++ b/event/logging/egokit/gokit_test.go
@@ -7,7 +7,6 @@
 package egokit_test
 
 import (
-	"context"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
@@ -15,15 +14,13 @@
 	"golang.org/x/exp/event/adapter/eventtest"
 	"golang.org/x/exp/event/keys"
 	"golang.org/x/exp/event/logging/egokit"
-	"golang.org/x/exp/event/logging/internal"
 )
 
 func Test(t *testing.T) {
 	log := egokit.NewLogger()
-	e, h := internal.NewTestExporter()
-	ctx := event.WithExporter(context.Background(), e)
+	ctx, h := eventtest.NewCapture()
 	log.Log(ctx, "msg", "mess", "level", 1, "name", "n/m", "traceID", 17, "resource", "R")
-	want := &event.Event{
+	want := []event.Event{{
 		At: eventtest.InitialTime,
 		Labels: []event.Label{
 			keys.Value("level").Of(1),
@@ -32,8 +29,8 @@
 			keys.Value("resource").Of("R"),
 			event.Message.Of("mess"),
 		},
-	}
-	if diff := cmp.Diff(want, &h.Got); diff != "" {
+	}}
+	if diff := cmp.Diff(want, h.Got); diff != "" {
 		t.Errorf("mismatch (-want, +got):\n%s", diff)
 	}
 }
diff --git a/event/logging/elogr/logr_test.go b/event/logging/elogr/logr_test.go
index 61936a9..8b1faf2 100644
--- a/event/logging/elogr/logr_test.go
+++ b/event/logging/elogr/logr_test.go
@@ -7,7 +7,6 @@
 package elogr_test
 
 import (
-	"context"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
@@ -19,11 +18,11 @@
 )
 
 func TestInfo(t *testing.T) {
-	e, th := internal.NewTestExporter()
-	log := elogr.NewLogger(event.WithExporter(context.Background(), e), "/").WithName("n").V(3)
+	ctx, th := eventtest.NewCapture()
+	log := elogr.NewLogger(ctx, "/").WithName("n").V(3)
 	log = log.WithName("m")
 	log.Info("mess", "traceID", 17, "resource", "R")
-	want := &event.Event{
+	want := []event.Event{{
 		At: eventtest.InitialTime,
 		Labels: []event.Label{
 			internal.LevelKey.Of(3),
@@ -32,8 +31,8 @@
 			keys.Value("resource").Of("R"),
 			event.Message.Of("mess"),
 		},
-	}
-	if diff := cmp.Diff(want, &th.Got); diff != "" {
+	}}
+	if diff := cmp.Diff(want, th.Got); diff != "" {
 		t.Errorf("mismatch (-want, +got):\n%s", diff)
 	}
 }
diff --git a/event/logging/elogrus/logrus_test.go b/event/logging/elogrus/logrus_test.go
index 7558ec1..ccee07c 100644
--- a/event/logging/elogrus/logrus_test.go
+++ b/event/logging/elogrus/logrus_test.go
@@ -7,7 +7,6 @@
 package elogrus_test
 
 import (
-	"context"
 	"io"
 	"testing"
 
@@ -15,34 +14,32 @@
 	"github.com/google/go-cmp/cmp/cmpopts"
 	"github.com/sirupsen/logrus"
 	"golang.org/x/exp/event"
+	"golang.org/x/exp/event/adapter/eventtest"
 	"golang.org/x/exp/event/keys"
 	"golang.org/x/exp/event/logging/elogrus"
 	"golang.org/x/exp/event/logging/internal"
 )
 
 func Test(t *testing.T) {
-	e, th := internal.NewTestExporter()
+	ctx, th := eventtest.NewCapture()
 	log := logrus.New()
 	log.SetFormatter(elogrus.NewFormatter())
 	log.SetOutput(io.Discard)
 	// adding WithContext panics, because event.FromContext assumes
-	ctx := event.WithExporter(context.Background(), e)
 	log.WithContext(ctx).WithField("traceID", 17).WithField("resource", "R").Info("mess")
 
-	want := &event.Event{
+	want := []event.Event{{
 		Labels: []event.Label{
 			internal.LevelKey.Of(4),
 			keys.Value("traceID").Of(17),
 			keys.Value("resource").Of("R"),
 			event.Message.Of("mess"),
 		},
-	}
-	th.Got.At = want.At
+	}}
 	// logrus fields are stored in a map, so we have to sort to overcome map
 	// iteration indeterminacy.
 	less := func(a, b event.Label) bool { return a.Name < b.Name }
-	if diff := cmp.Diff(want, &th.Got, append([]cmp.Option{cmpopts.SortSlices(less)})...); diff != "" {
+	if diff := cmp.Diff(want, th.Got, cmpopts.SortSlices(less), cmpopts.IgnoreFields(event.Event{}, "At")); diff != "" {
 		t.Errorf("mismatch (-want, +got):\n%s", diff)
 	}
-
 }
diff --git a/event/logging/ezap/zap_test.go b/event/logging/ezap/zap_test.go
index 980180c..a760cdd 100644
--- a/event/logging/ezap/zap_test.go
+++ b/event/logging/ezap/zap_test.go
@@ -7,24 +7,24 @@
 package ezap_test
 
 import (
-	"context"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
 	"go.uber.org/zap"
 	"golang.org/x/exp/event"
+	"golang.org/x/exp/event/adapter/eventtest"
 	"golang.org/x/exp/event/keys"
 	"golang.org/x/exp/event/logging/ezap"
 	"golang.org/x/exp/event/logging/internal"
 )
 
 func Test(t *testing.T) {
-	e, h := internal.NewTestExporter()
-	ctx := event.WithExporter(context.Background(), e)
+	ctx, h := eventtest.NewCapture()
 	log := zap.New(ezap.NewCore(ctx), zap.Fields(zap.Int("traceID", 17), zap.String("resource", "R")))
 	log = log.Named("n/m")
 	log.Info("mess", zap.Float64("pi", 3.14))
-	want := &event.Event{
+	want := []event.Event{{
 		Labels: []event.Label{
 			keys.Int64("traceID").Of(17),
 			keys.String("resource").Of("R"),
@@ -33,9 +33,8 @@
 			keys.Float64("pi").Of(3.14),
 			event.Message.Of("mess"),
 		},
-	}
-	h.Got.At = want.At
-	if diff := cmp.Diff(want, &h.Got); diff != "" {
+	}}
+	if diff := cmp.Diff(want, h.Got, cmpopts.IgnoreFields(event.Event{}, "At")); diff != "" {
 		t.Errorf("mismatch (-want, +got):\n%s", diff)
 	}
 }
diff --git a/event/logging/internal/internal.go b/event/logging/internal/internal.go
index 749b9df..c02a207 100644
--- a/event/logging/internal/internal.go
+++ b/event/logging/internal/internal.go
@@ -1,10 +1,6 @@
 package internal
 
 import (
-	"context"
-
-	"golang.org/x/exp/event"
-	"golang.org/x/exp/event/adapter/eventtest"
 	"golang.org/x/exp/event/keys"
 )
 
@@ -14,19 +10,3 @@
 	NameKey  = keys.String("name")
 	ErrorKey = keys.Value("error")
 )
-
-type TestHandler struct {
-	event.NopHandler
-	Got event.Event
-}
-
-func (h *TestHandler) Log(_ context.Context, ev *event.Event) {
-	h.Got = *ev
-	h.Got.Labels = make([]event.Label, len(ev.Labels))
-	copy(h.Got.Labels, ev.Labels)
-}
-
-func NewTestExporter() (*event.Exporter, *TestHandler) {
-	te := &TestHandler{}
-	return event.NewExporter(te, eventtest.ExporterOptions()), te
-}