From d100f6fc24948985a586a332c0e52bcbb4476b25 Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Wed, 20 Feb 2019 23:35:26 -0500 Subject: [PATCH] Switching termdash test to common spinlock utility. And delaying redraws to allow fast widgets to process the input event. --- event/testevent/testevent.go | 32 +++++++++++++++++++++++++++++ termdash.go | 6 ++++++ termdash_test.go | 40 +++++++++++++++--------------------- 3 files changed, 54 insertions(+), 24 deletions(-) create mode 100644 event/testevent/testevent.go diff --git a/event/testevent/testevent.go b/event/testevent/testevent.go new file mode 100644 index 0000000..9235255 --- /dev/null +++ b/event/testevent/testevent.go @@ -0,0 +1,32 @@ +// Package testevent provides utilities for tests that deal with concurrent +// events. +package testevent + +import ( + "context" + "fmt" + "time" +) + +// WaitFor waits until the provided function returns a nil error or the timeout. +// If the function doesn't return a nil error before the timeout expires, +// returns the last returned error. +func WaitFor(timeout time.Duration, fn func() error) error { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + var err error + for { + tick := time.NewTimer(5 * time.Millisecond) + select { + case <-tick.C: + if err = fn(); err != nil { + continue + } + return nil + + case <-ctx.Done(): + return fmt.Errorf("timeout expired, error: %v", err) + } + } +} diff --git a/termdash.go b/termdash.go index c49aa27..f73d427 100644 --- a/termdash.go +++ b/termdash.go @@ -277,6 +277,12 @@ func (td *termdash) redraw() error { func (td *termdash) evRedraw() error { td.mu.Lock() defer td.mu.Unlock() + + // Don't redraw immediately, give widgets that are performing enough time + // to update. + // We don't want to actually synchronize until all widgets update, we are + // purposefully leaving slow widgets behind. + time.Sleep(25 * time.Millisecond) return td.redraw() } diff --git a/termdash_test.go b/termdash_test.go index 8a02287..d32efcc 100644 --- a/termdash_test.go +++ b/termdash_test.go @@ -16,6 +16,7 @@ package termdash import ( "context" + "errors" "fmt" "image" "sync" @@ -26,6 +27,7 @@ import ( "github.com/mum4k/termdash/canvas/testcanvas" "github.com/mum4k/termdash/container" "github.com/mum4k/termdash/event/eventqueue" + "github.com/mum4k/termdash/event/testevent" "github.com/mum4k/termdash/keyboard" "github.com/mum4k/termdash/mouse" "github.com/mum4k/termdash/terminal/faketerm" @@ -388,8 +390,13 @@ func TestRun(t *testing.T) { return } - if err := untilEmpty(5*time.Second, eq); err != nil { - t.Fatalf("untilEmpty => %v", err) + if err := testevent.WaitFor(5*time.Second, func() error { + if !eq.Empty() { + return errors.New("event queue not empty") + } + return nil + }); err != nil { + t.Fatalf("testevent.WaitFor => %v", err) } if tc.after != nil { @@ -569,8 +576,13 @@ func TestController(t *testing.T) { tc.apiEvents(mi) } - if err := untilEmpty(5*time.Second, eq); err != nil { - t.Fatalf("untilEmpty => %v", err) + if err := testevent.WaitFor(5*time.Second, func() error { + if !eq.Empty() { + return errors.New("event queue not empty") + } + return nil + }); err != nil { + t.Fatalf("testevent.WaitFor => %v", err) } if tc.controls != nil { if err := tc.controls(ctrl); err != nil { @@ -585,23 +597,3 @@ func TestController(t *testing.T) { }) } } - -// untilEmpty waits until the queue empties. -// Waits at most the specified duration. -func untilEmpty(timeout time.Duration, q *eventqueue.Unbound) error { - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - for { - tick := time.NewTimer(5 * time.Millisecond) - select { - case <-tick.C: - if q.Empty() { - return nil - } - - case <-ctx.Done(): - return fmt.Errorf("while waiting for the event queue to empty: %v", ctx.Err()) - } - } -}