Files

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)
}
}