event: add Equal methods for Label and Value

Useful for testing.

Change-Id: I77b8109b5df387273170786208af80da62ab64c2
Reviewed-on: https://go-review.googlesource.com/c/exp/+/315529
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
diff --git a/event/label.go b/event/label.go
index 8011a2f..a6819da 100644
--- a/event/label.go
+++ b/event/label.go
@@ -25,6 +25,11 @@
 	Value Value
 }
 
+// Equal reports whether two labels are equal.
+func (l1 Label) Equal(l2 Label) bool {
+	return l1.Name == l2.Name && l1.Value.Equal(l2.Value)
+}
+
 // stringptr is used in untyped when the Value is a string
 type stringptr unsafe.Pointer
 
@@ -53,6 +58,30 @@
 // HasValue returns true if the value is set to any type.
 func (v *Value) HasValue() bool { return v.untyped != nil }
 
+// Equal reports whether two values are equal.
+func (v1 *Value) Equal(v2 Value) bool {
+	if !v1.HasValue() {
+		return !v2.HasValue()
+	}
+	if !v2.HasValue() {
+		return false
+	}
+	switch {
+	case v1.IsString():
+		return v2.IsString() && v1.String() == v2.String()
+	case v1.IsInt64():
+		return v2.IsInt64() && v1.packed == v2.packed
+	case v1.IsUint64():
+		return v2.IsUint64() && v1.packed == v2.packed
+	case v1.IsFloat64():
+		return v2.IsFloat64() && v1.Float64() == v2.Float64()
+	case v1.IsBool():
+		return v2.IsBool() && v1.packed == v2.packed
+	default:
+		return v1.untyped == v2.untyped
+	}
+}
+
 // ValueOf returns a Value for the supplied value.
 func ValueOf(value interface{}) Value {
 	return Value{untyped: value}
diff --git a/event/label_test.go b/event/label_test.go
index 3a5c549..6617f61 100644
--- a/event/label_test.go
+++ b/event/label_test.go
@@ -46,6 +46,32 @@
 	}
 }
 
+func TestEqual(t *testing.T) {
+	var x, y int
+	vals := []event.Value{
+		{},
+		event.Int64Of(1),
+		event.Int64Of(2),
+		event.Uint64Of(3),
+		event.Uint64Of(4),
+		event.Float64Of(3.5),
+		event.Float64Of(3.7),
+		event.BoolOf(true),
+		event.BoolOf(false),
+		event.ValueOf(&x),
+		event.ValueOf(&y),
+	}
+	for i, v1 := range vals {
+		for j, v2 := range vals {
+			got := v1.Equal(v2)
+			want := i == j
+			if got != want {
+				t.Errorf("%v.Equal(%v): got %t, want %t", v1, v2, got, want)
+			}
+		}
+	}
+}
+
 func panics(f func()) (b bool) {
 	defer func() {
 		if x := recover(); x != nil {