172 lines
4.8 KiB
Go
172 lines
4.8 KiB
Go
package nvda2cthulhu
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
type recordingHandler struct {
|
|
mutex sync.Mutex
|
|
spoken []string
|
|
interrupt []bool
|
|
braille []string
|
|
canceled int
|
|
}
|
|
|
|
func (handler *recordingHandler) SpeakText(text string, interrupt bool) bool {
|
|
handler.mutex.Lock()
|
|
defer handler.mutex.Unlock()
|
|
handler.spoken = append(handler.spoken, text)
|
|
handler.interrupt = append(handler.interrupt, interrupt)
|
|
return true
|
|
}
|
|
|
|
func (handler *recordingHandler) BrailleMessage(text string) bool {
|
|
handler.mutex.Lock()
|
|
defer handler.mutex.Unlock()
|
|
handler.braille = append(handler.braille, text)
|
|
return true
|
|
}
|
|
|
|
func (handler *recordingHandler) CancelSpeech() bool {
|
|
handler.mutex.Lock()
|
|
defer handler.mutex.Unlock()
|
|
handler.canceled++
|
|
return true
|
|
}
|
|
|
|
func TestAllowedOriginAcceptsNativeAndLoopbackClients(t *testing.T) {
|
|
for _, test := range []struct {
|
|
origin string
|
|
want bool
|
|
}{
|
|
{"", true},
|
|
{"http://127.0.0.1:3457", true},
|
|
{"http://localhost:3457", true},
|
|
{"https://[::1]:3457", true},
|
|
{"https://example.com", false},
|
|
{"null", false},
|
|
} {
|
|
request := httptest.NewRequest(http.MethodGet, "http://127.0.0.1:3457/", nil)
|
|
if test.origin != "" {
|
|
request.Header.Set("Origin", test.origin)
|
|
}
|
|
if got := allowedOrigin(request); got != test.want {
|
|
t.Errorf("allowedOrigin(%q) = %v, want %v", test.origin, got, test.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func liveSocketTest(t *testing.T) {
|
|
t.Helper()
|
|
handler := &recordingHandler{}
|
|
server := New(nil, handler)
|
|
httpTest := httptest.NewServer(http.HandlerFunc(server.handleHTTP))
|
|
defer httpTest.Close()
|
|
url := "ws" + strings.TrimPrefix(httpTest.URL, "http")
|
|
connection, _, err := websocket.DefaultDialer.Dial(url, nil)
|
|
if err != nil {
|
|
t.Fatalf("dial WebSocket: %v", err)
|
|
}
|
|
defer connection.Close()
|
|
|
|
if err := connection.WriteMessage(websocket.TextMessage, []byte("Accessibility mod loaded.")); err != nil {
|
|
t.Fatalf("write speech text: %v", err)
|
|
}
|
|
if err := connection.WriteMessage(websocket.BinaryMessage, []byte{0x81, 0xab, 'B', 'r', 'a', 'i', 'l', 'l', 'e', 'T', 'e', 'x', 't', 0xa2, 'O', 'K'}); err != nil {
|
|
t.Fatalf("write braille text: %v", err)
|
|
}
|
|
if err := connection.WriteMessage(websocket.TextMessage, []byte("CancelSpeech")); err != nil {
|
|
t.Fatalf("write cancellation: %v", err)
|
|
}
|
|
|
|
waitForHandlerCalls(t, handler)
|
|
handler.mutex.Lock()
|
|
defer handler.mutex.Unlock()
|
|
if len(handler.spoken) != 1 || handler.spoken[0] != "Accessibility mod loaded." || len(handler.interrupt) != 1 || !handler.interrupt[0] {
|
|
t.Fatalf("speech calls = %v, interrupts = %v", handler.spoken, handler.interrupt)
|
|
}
|
|
if len(handler.braille) != 1 || handler.braille[0] != "OK" {
|
|
t.Fatalf("braille calls = %v", handler.braille)
|
|
}
|
|
if handler.canceled != 1 {
|
|
t.Fatalf("cancellations = %d, want 1", handler.canceled)
|
|
}
|
|
}
|
|
|
|
func TestServerHandlesWebSocketRequests(t *testing.T) {
|
|
if os.Getenv("CTHULHU_LIVE_SOCKET_TEST") != "1" {
|
|
t.Skip("set CTHULHU_LIVE_SOCKET_TEST=1 to allow a local TCP listener")
|
|
}
|
|
liveSocketTest(t)
|
|
}
|
|
|
|
func TestStartStopClosesActiveConnection(t *testing.T) {
|
|
if os.Getenv("CTHULHU_LIVE_SOCKET_TEST") != "1" {
|
|
t.Skip("set CTHULHU_LIVE_SOCKET_TEST=1 to allow a local TCP listener")
|
|
}
|
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatalf("find port: %v", err)
|
|
}
|
|
port := listener.Addr().(*net.TCPAddr).Port
|
|
if err := listener.Close(); err != nil {
|
|
t.Fatalf("release port: %v", err)
|
|
}
|
|
t.Setenv(hostEnv, fmt.Sprintf("http://127.0.0.1:%d", port))
|
|
|
|
server := New(nil, &recordingHandler{})
|
|
if err := server.Start(context.Background()); err != nil {
|
|
t.Fatalf("start server: %v", err)
|
|
}
|
|
connection, _, err := websocket.DefaultDialer.Dial(fmt.Sprintf("ws://127.0.0.1:%d", port), nil)
|
|
if err != nil {
|
|
t.Fatalf("dial server: %v", err)
|
|
}
|
|
defer connection.Close()
|
|
if err := server.Stop(context.Background()); err != nil {
|
|
t.Fatalf("stop server: %v", err)
|
|
}
|
|
if _, _, err := connection.ReadMessage(); err == nil {
|
|
t.Fatal("active connection remained open after Stop")
|
|
}
|
|
}
|
|
|
|
func waitForHandlerCalls(t *testing.T, handler *recordingHandler) {
|
|
t.Helper()
|
|
deadline := time.Now().Add(2 * time.Second)
|
|
for time.Now().Before(deadline) {
|
|
handler.mutex.Lock()
|
|
done := len(handler.spoken) == 1 && len(handler.braille) == 1 && handler.canceled == 1
|
|
handler.mutex.Unlock()
|
|
if done {
|
|
return
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
}
|
|
|
|
func TestBindAddressUsesPythonDefault(t *testing.T) {
|
|
t.Setenv(hostEnv, "http://127.0.0.1")
|
|
if address := bindAddress(); address != "127.0.0.1:3457" {
|
|
t.Fatalf("default address = %q", address)
|
|
}
|
|
}
|
|
|
|
func TestStopWithoutStartSucceeds(t *testing.T) {
|
|
server := New(nil, nil)
|
|
if err := server.Stop(context.Background()); err != nil {
|
|
t.Fatalf("stop unstarted server: %v", err)
|
|
}
|
|
}
|