From 1f8a4815d86fd5983b83da0e442826c7f3208461 Mon Sep 17 00:00:00 2001 From: Johan Lindh Date: Fri, 25 Sep 2026 19:02:30 +0200 Subject: [PATCH] test: make concurrent JsVar batch assertion deterministic --- lib/ui/jsvar_order_test.go | 96 +++++++++++++++----------------------- 1 file changed, 37 insertions(+), 59 deletions(-) diff --git a/lib/ui/jsvar_order_test.go b/lib/ui/jsvar_order_test.go index f822f23b..979b3f64 100644 --- a/lib/ui/jsvar_order_test.go +++ b/lib/ui/jsvar_order_test.go @@ -7,6 +7,7 @@ import ( "io" "sync" "testing" + "testing/synctest" "time" "github.com/linkdata/jaws" @@ -106,73 +107,50 @@ func readJsVarSet(t *testing.T, tr *jawstest.TestRequest) (data string) { } func TestJsVarConcurrentBroadcastsKeepLatestMutation(t *testing.T) { - jsvar, elem, tr := newOrderedJsVar(t) - - firstEntered := make(chan struct{}) - releaseFirst := make(chan struct{}) - firstErr := make(chan error, 1) - go func() { - firstErr <- jsvar.JawsSetPath(elem, "value", orderedMarshalValue{ - Text: "first", - Entered: firstEntered, - Release: releaseFirst, - }) - }() - select { - case <-t.Context().Done(): + synctest.Test(t, func(t *testing.T) { + jsvar, elem, tr := newOrderedJsVar(t) + + firstEntered := make(chan struct{}) + releaseFirst := make(chan struct{}) + setErr := make(chan error, 2) + go func() { + setErr <- jsvar.JawsSetPath(elem, "value", orderedMarshalValue{ + Text: "first", + Entered: firstEntered, + Release: releaseFirst, + }) + }() + <-firstEntered + + secondStarted := make(chan struct{}) + go func() { + close(secondStarted) + setErr <- jsvar.JawsSetPath(elem, "value", orderedMarshalValue{Text: "second"}) + }() + <-secondStarted close(releaseFirst) - t.Fatal("timed out waiting for the first value to enter JSON marshaling") - case <-firstEntered: - } - secondErr := make(chan error, 1) - go func() { - secondErr <- jsvar.JawsSetPath(elem, "value", orderedMarshalValue{Text: "second"}) - }() + for range 2 { + if err := <-setErr; err != nil { + t.Fatal(err) + } + } - secondCompleted := false - timer := time.NewTimer(100 * time.Millisecond) - select { - case err := <-secondErr: - if err != nil { - t.Fatal(err) + synctest.Wait() + time.Sleep(jaws.DefaultUpdateInterval + time.Millisecond) + synctest.Wait() + if got := readJsVarSet(t, tr); got != `value="second"` { + t.Fatalf("broadcast = %q, want latest mutation", got) } - secondCompleted = true - case <-timer.C: - } - if !timer.Stop() { select { - case <-timer.C: + case msg := <-tr.OutCh: + t.Fatalf("unexpected extra broadcast: %#v", msg) default: } - } - close(releaseFirst) - - select { - case <-t.Context().Done(): - t.Fatal("timed out waiting for the first JsVar set") - case err := <-firstErr: - if err != nil { - t.Fatal(err) + if current := jsvar.JawsGet(elem).Value; current != "second" { + t.Fatalf("bound value = %q, want latest mutation %q", current, "second") } - } - if !secondCompleted { - select { - case <-t.Context().Done(): - t.Fatal("timed out waiting for the second JsVar set") - case err := <-secondErr: - if err != nil { - t.Fatal(err) - } - } - } - - if got := readJsVarSet(t, tr); got != `value="second"` { - t.Fatalf("broadcast = %q, want latest mutation", got) - } - if current := jsvar.JawsGet(elem).Value; current != "second" { - t.Fatalf("bound value = %q, want latest mutation %q", current, "second") - } + }) } func TestJsVarMarshalErrorReleasesSetterOrder(t *testing.T) {