Files

112 lines
3.6 KiB
Go

package braille
import (
"bytes"
"context"
"io"
"strings"
"testing"
"time"
)
type clientBackend struct {
fakeBackend
cancel context.CancelFunc
}
func (backend *clientBackend) Write(text string, cursor int) error {
if err := backend.fakeBackend.Write(text, cursor); err != nil {
return err
}
backend.cancel()
return nil
}
func TestRunClientShowsLiveFrameAndReleasesDisplay(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
backend := &clientBackend{fakeBackend: fakeBackend{geometry: Geometry{20, 1}}, cancel: cancel}
input := strings.NewReader("{\"text\":\"Save, push button\",\"cursor\":-1}\n")
var ready bytes.Buffer
if err := RunClient(ctx, backend, io.NopCloser(input), &ready); err != nil {
t.Fatal(err)
}
if ready.String() != "ready\n" || len(backend.writes) != 1 ||
backend.writes[0] != "Save, push button " || backend.opens != 1 || backend.closes != 1 {
t.Fatalf("client lifecycle: ready=%q backend=%+v", ready.String(), backend)
}
}
func TestRunClientPlacesCursorOnUnicodeCaret(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
backend := &clientBackend{fakeBackend: fakeBackend{geometry: Geometry{4, 1}}, cancel: cancel}
input := strings.NewReader("{\"text\":\"é界abcde\",\"cursor\":5}\n")
if err := RunClient(ctx, backend, io.NopCloser(input), &bytes.Buffer{}); err != nil {
t.Fatal(err)
}
if len(backend.writes) != 1 || backend.writes[0] != "cde " ||
len(backend.cursors) != 1 || backend.cursors[0] != 2 {
t.Fatalf("writes=%q cursors=%v", backend.writes, backend.cursors)
}
}
func TestRunClientRejectsInvalidCursorAndCloses(t *testing.T) {
backend := &fakeBackend{geometry: Geometry{4, 1}}
err := RunClient(context.Background(), backend,
io.NopCloser(strings.NewReader("{\"text\":\"abc\",\"cursor\":4}\n")), &bytes.Buffer{})
if err == nil || backend.opens != 1 || backend.closes != 1 || len(backend.writes) != 0 {
t.Fatalf("error=%v backend=%+v", err, backend)
}
}
func TestOfferLatestFrameReplacesPendingFrame(t *testing.T) {
frames := make(chan Frame, 1)
offerLatestFrame(frames, Frame{Text: "old"})
offerLatestFrame(frames, Frame{Text: "new"})
if frame := <-frames; frame.Text != "new" {
t.Fatalf("retained %q", frame.Text)
}
offerLatestFrame(frames, Frame{Text: "newer"})
if frame := <-frames; frame.Text != "newer" {
t.Fatalf("lost empty-queue update: %q", frame.Text)
}
}
type routingBackend struct{ fakeBackend }
func (b *routingBackend) ReadKey() (Key, error) {
if len(b.writes) == 0 {
return NoKey, nil
}
return b.fakeBackend.ReadKey()
}
func TestRunClientRoutesDisplayedFrameAfterPanningAndJoinsReader(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
input, writer := io.Pipe()
defer writer.Close()
backend := &routingBackend{fakeBackend{geometry: Geometry{4, 1}, keys: []Key{PanRight, RouteBase + 1}, cancel: cancel}}
var output bytes.Buffer
done := make(chan error, 1)
go func() { done <- RunClient(ctx, backend, input, &output) }()
if _, err := io.WriteString(writer, "{\"text\":\"é猫abcdef\",\"cursor\":-1,\"id\":42}\n"); err != nil {
t.Fatal(err)
}
if err := <-done; err != nil {
t.Fatal(err)
}
if output.String() != "ready\n{\"id\":42,\"offset\":5}\n" {
t.Fatalf("helper output %q", output.String())
}
if backend.closes != 1 {
t.Fatalf("close count %d", backend.closes)
}
// The writer has deliberately remained open; return requires the session to
// close the blocked input reader and join it.
if _, err := writer.Write([]byte("x")); err == nil {
t.Fatal("reader still open")
}
}