105 lines
3.3 KiB
Go
105 lines
3.3 KiB
Go
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/atspi"
|
|
"git.stormux.org/storm/cthulhu/internal/braille"
|
|
"git.stormux.org/storm/cthulhu/internal/logging"
|
|
)
|
|
|
|
// The test executable doubles as a real pipe-protocol peer. Only the explicit
|
|
// child invocation takes this path; the parent runs the ordinary test suite.
|
|
func TestMain(m *testing.M) {
|
|
if os.Getenv("CTHULHU_BRAILLE_TEST_FAIL") == "1" && len(os.Args) == 2 && os.Args[1] == "--client" {
|
|
fmt.Fprintln(os.Stderr, "braille test startup failure")
|
|
os.Exit(1)
|
|
}
|
|
if os.Getenv("CTHULHU_BRAILLE_TEST_CLIENT") == "1" && len(os.Args) == 2 && os.Args[1] == "--client" {
|
|
fmt.Println("ready")
|
|
decoder, encoder := json.NewDecoder(os.Stdin), json.NewEncoder(os.Stdout)
|
|
for {
|
|
var frame braille.Frame
|
|
if err := decoder.Decode(&frame); err != nil {
|
|
os.Exit(0)
|
|
}
|
|
if frame.Text != "" {
|
|
if err := encoder.Encode(braille.RoutingEvent{ID: frame.ID, Offset: 1}); err != nil {
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
func TestBrailleWorkerReportsHelperStartupFailure(t *testing.T) {
|
|
t.Setenv("CTHULHU_BRAILLE_TEST_FAIL", "1")
|
|
path, err := os.Executable()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
worker := &brailleWorker{path: path, logger: slog.New(slog.DiscardHandler)}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
defer cancel()
|
|
if err := worker.session(ctx); err == nil || !strings.Contains(err.Error(), "braille test startup failure") {
|
|
t.Fatalf("helper startup error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestBrailleWorkerHelperProtocolAndRestart(t *testing.T) {
|
|
t.Setenv("CTHULHU_BRAILLE_TEST_CLIENT", "1")
|
|
path, err := os.Executable()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
router := &recordingBrailleRouter{calls: make(chan atspi.BrailleRoute, 8)}
|
|
var output bytes.Buffer
|
|
worker := &brailleWorker{path: path, router: router, logger: logging.New(&output, logging.Options{Level: slog.LevelDebug}), wake: make(chan struct{}, 1)}
|
|
stop := func() {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
defer cancel()
|
|
if err := worker.Stop(ctx); err != nil {
|
|
t.Errorf("stop helper: %v", err)
|
|
}
|
|
}
|
|
defer stop()
|
|
for attempt := 0; attempt < 2; attempt++ {
|
|
if err := worker.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
worker.EnqueueFrame(braille.Frame{Text: "é猫", Cursor: 0, Routes: []braille.TextRoute{{Start: 0, End: 2, TextLength: 2, SourceStart: 10, BusName: ":1.42", Path: "/entry", SourceText: "é猫"}}})
|
|
select {
|
|
case route := <-router.calls:
|
|
if route.Offset != 11 || route.Focus.Path != "/entry" {
|
|
t.Fatalf("route %+v", route)
|
|
}
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("helper route was not delivered")
|
|
}
|
|
stop()
|
|
// Stop joins the writer before inspecting its debug output.
|
|
for _, expected := range []string{`"message":"sent braille frame"`, `"braille_text":"é猫"`, `"cursor_offset":0`} {
|
|
if !strings.Contains(output.String(), expected) {
|
|
t.Fatalf("braille diagnostics missing %s: %s", expected, output.String())
|
|
}
|
|
}
|
|
// A duplicate send can be coalesced by either side; discard already handled
|
|
// keys before checking that the restarted child returns a new event.
|
|
for len(router.calls) > 0 {
|
|
<-router.calls
|
|
}
|
|
if worker.current().Text != "" {
|
|
t.Fatal("stopped worker retained old content")
|
|
}
|
|
}
|
|
}
|