//go:build brlapi && cgo package braille import ( "bytes" "reflect" "testing" ) // These feasibility checks use real liblouis translation and the helper's // viewport, but never construct or open a BrlAPI backend. The two viewports // model independently owned local and remote sessions, not a session API. func TestVirtualBrailleIndependentWidthsAndRouting(t *testing.T) { frame := Frame{ID: 11, Text: "the cat", Cursor: -1, Contract: true, Table: "en-us-g2.ctb", WordWrap: true, Dots: []byte{0, 0x40, 0, 0, 0, 0, 0}} local, remote := viewport{width: 8}, viewport{width: 4} for _, v := range []*viewport{&local, &remote} { if err := v.setFrame(frame); err != nil { t.Fatal(err) } } localCells, err := local.rawCells(0xc0) if err != nil || !bytes.Equal(localCells, []byte{0x6e, 0, 9, 1, 0x1e, 0, 0, 0}) { t.Fatalf("local=%x error=%v", localCells, err) } remoteCells, err := remote.rawCells(0xc0) if err != nil || !bytes.Equal(remoteCells, []byte{0x6e, 0, 0, 0}) { t.Fatalf("wrapped remote=%x error=%v", remoteCells, err) } if route, ok := remote.route(RouteBase + 1); !ok || route != (RoutingEvent{ID: 11, Offset: 3}) { t.Fatalf("contracted route=%+v ok=%v", route, ok) } if _, ok := remote.route(RouteBase + 2); ok { t.Fatal("wrapped padding routed into hidden text") } if !remote.pan(PanRight) { t.Fatal("remote did not pan") } panned, err := remote.rawCells(0xc0) if err != nil || !bytes.Equal(panned, []byte{9, 1, 0x1e, 0}) { t.Fatalf("panned=%x error=%v", panned, err) } if route, ok := remote.route(RouteBase); !ok || route != (RoutingEvent{ID: 11, Offset: 4}) { t.Fatalf("panned route=%+v ok=%v", route, ok) } if !remote.pan(PanLeft) { t.Fatal("remote did not pan back") } restored, err := remote.rawCells(0xc0) if err != nil || !bytes.Equal(restored, remoteCells) { t.Fatalf("restored=%x error=%v", restored, err) } // A new width gets fresh state. No physical geometry is changed and no // old pan offset or routing map is carried across the replacement. remote = viewport{width: 2} frame.ID = 12 if err := remote.setFrame(frame); err != nil { t.Fatal(err) } if route, ok := remote.route(RouteBase); !ok || route.ID != 12 { t.Fatal("replacement retained an old frame identity") } unchanged, err := local.rawCells(0xc0) if err != nil || !bytes.Equal(unchanged, localCells) || local.offset != 0 || local.width != 8 || local.id != 11 { t.Fatal("remote pan/resize altered local viewport") } } func TestVirtualBrailleCursorUsesTranslatedPosition(t *testing.T) { v := viewport{width: 4} if err := v.setFrame(Frame{Text: "the cat", Cursor: 4, Contract: true, Table: "en-us-g2.ctb"}); err != nil { t.Fatal(err) } cells, err := v.rawCells(0xc0) if err != nil || !bytes.Equal(cells, []byte{0x2e, 0, 0xc9, 1}) { t.Fatalf("translated cursor=%x error=%v", cells, err) } if !v.pan(PanRight) { t.Fatal("did not pan past cursor") } cells, err = v.rawCells(0xc0) if err != nil || !bytes.Equal(cells, []byte{0x1e, 0, 0, 0}) { t.Fatalf("hidden cursor=%x error=%v", cells, err) } if err := v.setFrame(Frame{ID: 14, Text: "the cat", Cursor: 7, Contract: true, Table: "en-us-g2.ctb"}); err != nil { t.Fatal(err) } cells, err = v.rawCells(0xc0) if err != nil || !bytes.Equal(cells, []byte{0x1e, 0xc0, 0, 0}) { t.Fatalf("end cursor=%x error=%v", cells, err) } if route, ok := v.route(RouteBase + 1); !ok || route != (RoutingEvent{ID: 14, Offset: 7}) { t.Fatalf("end route=%+v ok=%v", route, ok) } } func TestVirtualBrailleExplicitComputerTableAndFailure(t *testing.T) { v := viewport{width: 4} // Contract selects the helper's liblouis path; the table determines whether // it contracts. A future virtual-helper API must resolve this distinction. if err := v.setFrame(Frame{ID: 15, Text: "cat", Cursor: -1, Contract: true, Table: "en-us-comp8.ctb"}); err != nil { t.Fatal(err) } cells, err := v.rawCells(0) if err != nil || !bytes.Equal(cells, []byte{9, 1, 0x1e, 0}) { t.Fatalf("computer table=%x error=%v", cells, err) } before := v if err := v.setFrame(Frame{Text: "cat", Cursor: -1, Contract: true, Table: "missing-remote-test-table.ctb"}); err == nil { t.Fatal("missing table accepted") } if !reflect.DeepEqual(v, before) { t.Fatal("failed translation replaced a valid viewport") } }