m/pkg/event: make type-safe
This is a fairly large change which makes use of Go type parameters
(“generics”) to make the event library (and its memory/etcd
implementations) type safe.
Since we now have the event.Value interface strongly typed, we also move
options which were implementation-specific (like BacklogOnly)
to be part of that interface, instead of the previously type-asserted
specific implementations. Use of options that are not handled by a
particular implementation is a runtime error. Expressing this in the
type system is probably not worth the effort.
We also implement Filter to allow offloading some of the functionality previously implemented in type assertion wrappers into the library itself.
In the end, this ends up removing a bunch of type assertion code, at
the cost of a fairly sweeping change. Unfortunately, some of this is due
to IntelliJ suddenly deciding to reformat comments.
Change-Id: I1ca6d93db1b5c4055a21af3fb9e5e3d425c0d86e
Reviewed-on: https://review.monogon.dev/c/monogon/+/1322
Tested-by: Jenkins CI
Reviewed-by: Leopold Schabel <leo@monogon.tech>
diff --git a/metropolis/pkg/event/memory/memory_test.go b/metropolis/pkg/event/memory/memory_test.go
index f4feb33..41f121e 100644
--- a/metropolis/pkg/event/memory/memory_test.go
+++ b/metropolis/pkg/event/memory/memory_test.go
@@ -28,7 +28,7 @@
// TestAsync exercises the high-level behaviour of a Value, in which a
// watcher is able to catch up to the newest Set value.
func TestAsync(t *testing.T) {
- p := Value{}
+ p := Value[int]{}
p.Set(0)
ctx := context.Background()
@@ -39,7 +39,7 @@
if err != nil {
t.Fatalf("Get: %v", err)
}
- if want, got := 0, val.(int); want != got {
+ if want, got := 0, val; want != got {
t.Fatalf("Value: got %d, wanted %d", got, want)
}
@@ -54,7 +54,7 @@
if err != nil {
t.Fatalf("Get: %v", err)
}
- if want, got := 100, val.(int); want != got {
+ if want, got := 100, val; want != got {
t.Fatalf("Value: got %d, wanted %d", got, want)
}
}
@@ -64,7 +64,7 @@
// This particular test ensures that .Set() calls to a Watcher result in a
// prefect log of updates being transmitted to a watcher.
func TestSync(t *testing.T) {
- p := Value{
+ p := Value[int]{
Sync: true,
}
values := make(chan int, 100)
@@ -79,7 +79,7 @@
if err != nil {
panic(err)
}
- values <- value.(int)
+ values <- value
}
}()
@@ -109,7 +109,7 @@
// This particular test ensures that .Set() calls actually block when a watcher
// is unattended.
func TestSyncBlocks(t *testing.T) {
- p := Value{
+ p := Value[int]{
Sync: true,
}
ctx := context.Background()
@@ -124,7 +124,7 @@
if err != nil {
t.Fatalf("Get: %v", err)
}
- if want, got := 0, value.(int); want != got {
+ if want, got := 0, value; want != got {
t.Fatalf("Got initial value %d, wanted %d", got, want)
}
@@ -160,7 +160,7 @@
t.Fatalf("Set() returned before Get()")
}
- if want, got := 1, value.(int); want != got {
+ if want, got := 1, value; want != got {
t.Fatalf("Wanted value %d, got %d", want, got)
}
@@ -175,7 +175,7 @@
// TestMultipleGets verifies that calling .Get() on a single watcher from two
// goroutines is prevented by returning an error in exactly one of them.
func TestMultipleGets(t *testing.T) {
- p := Value{}
+ p := Value[int]{}
ctx := context.Background()
w := p.Watch()
@@ -204,7 +204,7 @@
func TestConcurrency(t *testing.T) {
ctx := context.Background()
- p := Value{}
+ p := Value[int]{}
p.Set(0)
// Number of watchers to create.
@@ -236,10 +236,10 @@
}
// Ensure monotonicity of received data.
- if val.(int) <= prev {
+ if val <= prev {
done(fmt.Errorf("received out of order data: %d after %d", val, prev))
}
- prev = val.(int)
+ prev = val
// Quit when the final value is received.
if val == final {
@@ -274,7 +274,7 @@
// aborts that particular Get call, but also allows subsequent use of the same
// watcher.
func TestCanceling(t *testing.T) {
- p := Value{
+ p := Value[int]{
Sync: true,
}
@@ -316,7 +316,7 @@
func TestSetAfterWatch(t *testing.T) {
ctx := context.Background()
- p := Value{}
+ p := Value[int]{}
p.Set(0)
watcher := p.Watch()
@@ -326,7 +326,7 @@
if err != nil {
t.Fatalf("Get: %v", err)
}
- if want, got := 1, data.(int); want != got {
+ if want, got := 1, data; want != got {
t.Errorf("Get should've returned %v, got %v", want, got)
}
}