Files

85 lines
2.8 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package speech
import (
"bufio"
"context"
"errors"
"fmt"
"net"
"testing"
"time"
)
func TestContinuousSpeechWaitsForEndAndSurvivesCancellation(t *testing.T) {
for _, interrupted := range []bool{false, true} {
t.Run(fmt.Sprint(interrupted), func(t *testing.T) {
conn, server := net.Pipe()
control, controlServer := net.Pipe()
client := connectedClient(conn)
client.cancelConn, client.cancelReader, client.clientID = control, bufio.NewReader(control), 42
t.Cleanup(func() { server.Close(); controlServer.Close(); client.Close(context.Background()) })
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
began := make(chan struct{}, 1)
finished := make(chan error, 1)
go func() { finished <- client.SpeakAndWait(ctx, "First line", func() { began <- struct{}{} }) }()
server.SetDeadline(time.Now().Add(2 * time.Second))
reader := bufio.NewReader(server)
if err := exchangeSSIP(reader, server, "SET SELF NOTIFICATION ALL on\r\n", "200 OK\r\n"); err != nil {
t.Fatal(err)
}
for _, want := range []string{"SPEAK\r\n", "First line\r\n", ".\r\n"} {
got, err := reader.ReadString('\n')
if err != nil || got != want {
t.Fatalf("speech block %q, %v; want %q", got, err, want)
}
}
fmt.Fprint(server, "230 OK\r\n225-1\r\n225 QUEUED\r\n")
select {
case <-began:
t.Fatal("queue acknowledgement advanced reading position")
case <-finished:
t.Fatal("queue acknowledgement completed speech")
default:
}
fmt.Fprint(server, "701-1\r\n701-42\r\n701 BEGIN\r\n")
select {
case <-began:
case <-time.After(time.Second):
t.Fatal("BEGIN did not update reading position")
}
if interrupted {
cancel()
controlServer.SetDeadline(time.Now().Add(time.Second))
if err := exchangeSSIP(bufio.NewReader(controlServer), controlServer, "CANCEL 42\r\n", "200 OK\r\n"); err != nil {
t.Fatal(err)
}
fmt.Fprint(server, "703-1\r\n703-42\r\n703 CANCELED\r\n")
} else {
fmt.Fprint(server, "702-1\r\n702-42\r\n702 END\r\n")
}
if err := exchangeSSIP(reader, server, "SET SELF NOTIFICATION ALL off\r\n", "200 OK\r\n"); err != nil {
t.Fatal(err)
}
if err := <-finished; interrupted && !errors.Is(err, context.Canceled) || !interrupted && err != nil {
t.Fatalf("completion = %v", err)
}
// The cancellation notification must not become the next SPEAK reply.
go func() { finished <- client.Speak(context.Background(), "After") }()
for range 3 {
if _, err := reader.ReadString('\n'); err != nil {
t.Fatal(err)
}
}
fmt.Fprint(server, "230 OK\r\n225-2\r\n225 QUEUED\r\n")
if err := <-finished; err != nil {
t.Fatalf("speech after read all: %v", err)
}
})
}
}