Added test typing capabilities

This commit is contained in:
diamondburned (Forefront) 2020-07-03 16:53:46 -07:00
parent 04bd23b05a
commit dd6b0669f2
4 changed files with 122 additions and 53 deletions

View File

@ -31,6 +31,7 @@ type Channel struct {
send chan cchat.SendableMessage // ideally this should be another type
edit chan Message // id
del chan MessageHeader
typ chan Author
messageMutex sync.Mutex
messages map[uint32]Message
@ -44,13 +45,14 @@ type Channel struct {
}
var (
_ cchat.Server = (*Channel)(nil)
_ cchat.ServerMessage = (*Channel)(nil)
_ cchat.ServerMessageSender = (*Channel)(nil)
_ cchat.ServerMessageSendCompleter = (*Channel)(nil)
_ cchat.ServerNickname = (*Channel)(nil)
_ cchat.ServerMessageEditor = (*Channel)(nil)
_ cchat.ServerMessageActioner = (*Channel)(nil)
_ cchat.Server = (*Channel)(nil)
_ cchat.ServerMessage = (*Channel)(nil)
_ cchat.ServerMessageSender = (*Channel)(nil)
_ cchat.ServerMessageSendCompleter = (*Channel)(nil)
_ cchat.ServerNickname = (*Channel)(nil)
_ cchat.ServerMessageEditor = (*Channel)(nil)
_ cchat.ServerMessageActioner = (*Channel)(nil)
_ cchat.ServerMessageTypingIndicator = (*Channel)(nil)
)
func (ch *Channel) ID() string {
@ -93,6 +95,7 @@ func (ch *Channel) JoinServer(ctx context.Context, ct cchat.MessagesContainer) (
ch.send = make(chan cchat.SendableMessage)
ch.edit = make(chan Message)
ch.del = make(chan MessageHeader)
ch.typ = make(chan Author)
// Generate the backlog.
for i := 0; i < FetchBacklog; i++ {
@ -330,9 +333,10 @@ func (ch *Channel) SendMessage(msg cchat.SendableMessage) error {
}
const (
DeleteAction = "Delete"
NoopAction = "No-op"
BestTrapAction = "What's the best trap?"
DeleteAction = "Delete"
NoopAction = "No-op"
BestTrapAction = "What's the best trap?"
TriggerTypingAction = "Trigger Typing"
)
func (ch *Channel) MessageActions(id string) []string {
@ -347,7 +351,7 @@ func (ch *Channel) MessageActions(id string) []string {
// takes a container: the frontend should call this in a goroutine.
func (ch *Channel) DoMessageAction(action, messageID string) error {
switch action {
case DeleteAction:
case DeleteAction, TriggerTypingAction:
i, err := strconv.Atoi(messageID)
if err != nil {
return errors.Wrap(err, "Invalid ID")
@ -355,7 +359,14 @@ func (ch *Channel) DoMessageAction(action, messageID string) error {
// Simulate IO.
simulateAustralianInternet()
ch.del <- MessageHeader{uint32(i), time.Now()}
switch action {
case DeleteAction:
ch.del <- MessageHeader{uint32(i), time.Now()}
case TriggerTypingAction:
// Find the message.
ch.typ <- Author{name: ch.messages[uint32(i)].author}
}
case NoopAction:
// do nothing.
@ -439,6 +450,49 @@ func lookbackCheck(words []string, i int, prev, this string) bool {
return strings.HasPrefix(this, words[i]) && i > 0 && words[i-1] == prev
}
// Typing sleeps and returns possibly an error.
func (ch *Channel) Typing() error {
return simulateAustralianInternet()
}
// TypingTimeout returns 5 seconds.
func (ch *Channel) TypingTimeout() time.Duration {
return 5 * time.Second
}
type Typer struct {
Author
time time.Time
}
var _ cchat.Typer = (*Typer)(nil)
func newTyper(a Author) *Typer { return &Typer{a, time.Now()} }
func randomTyper() *Typer { return &Typer{randomAuthor(), time.Now()} }
func (t *Typer) Time() time.Time { return t.time }
func (ch *Channel) TypingSubscribe(ti cchat.TypingIndicator) (stop func(), err error) {
var stopch = make(chan struct{})
go func() {
var ticker = time.NewTicker(8 * time.Second)
defer ticker.Stop()
for {
select {
case <-stopch:
return
case <-ticker.C:
ti.AddTyper(randomTyper())
case author := <-ch.typ:
ti.AddTyper(newTyper(author))
}
}
}()
return func() { close(stopch) }, nil
}
func generateChannels(s *Session, amount int) []cchat.Server {
var channels = make([]cchat.Server, amount)
for i := range channels {
@ -459,30 +513,3 @@ func generateChannels(s *Session, amount int) []cchat.Server {
func randClamp(min, max int) int {
return rand.Intn(max-min) + min
}
// ErrTimedOut is returned when the simulated IO decides to fail.
var ErrTimedOut = errors.New("Australian Internet unsupported.")
// simulate network latency
func simulateAustralianInternet() error {
return simulateAustralianInternetCtx(context.Background())
}
func simulateAustralianInternetCtx(ctx context.Context) (err error) {
var ms = randClamp(internetMinLatency, internetMaxLatency)
select {
case <-time.After(time.Duration(ms) * time.Millisecond):
// noop
case <-ctx.Done():
return ctx.Err()
}
// because australia, drop packet 20% of the time if internetCanFail is
// true.
if internetCanFail && rand.Intn(100) < 20 {
return ErrTimedOut
}
return nil
}

44
internet.go Normal file
View File

@ -0,0 +1,44 @@
package mock
import (
"context"
"math/rand"
"time"
"github.com/pkg/errors"
)
var (
// channel.go @ simulateAustralianInternet
internetCanFail = true
// 500ms ~ 3s
internetMinLatency = 500
internetMaxLatency = 3000
)
// ErrTimedOut is returned when the simulated IO decides to fail.
var ErrTimedOut = errors.New("Australian Internet unsupported.")
// simulate network latency
func simulateAustralianInternet() error {
return simulateAustralianInternetCtx(context.Background())
}
func simulateAustralianInternetCtx(ctx context.Context) (err error) {
var ms = randClamp(internetMinLatency, internetMaxLatency)
select {
case <-time.After(time.Duration(ms) * time.Millisecond):
// noop
case <-ctx.Done():
return ctx.Err()
}
// because australia, drop packet 20% of the time if internetCanFail is
// true.
if internetCanFail && rand.Intn(100) < 20 {
return ErrTimedOut
}
return nil
}

View File

@ -79,12 +79,7 @@ func echoMessage(sendable cchat.SendableMessage, id uint32, author text.Rich) Me
}
func randomMessage(id uint32) Message {
var author = randomdata.SillyName()
return randomMessageWithAuthor(id, text.Rich{
Content: author,
Segments: []text.Segment{segments.NewRandomColored(author)},
})
return randomMessageWithAuthor(id, randomAuthor().name)
}
func randomMessageWithAuthor(id uint32, author text.Rich) Message {
@ -122,6 +117,16 @@ var (
_ cchat.MessageAuthorAvatar = (*Author)(nil)
)
func randomAuthor() Author {
var author = randomdata.SillyName()
return Author{
name: text.Rich{
Content: author,
Segments: []text.Segment{segments.NewRandomColored(author)},
},
}
}
func (a Author) ID() string {
return a.name.Content
}

View File

@ -82,16 +82,9 @@ func (Authenticator) Authenticate(form []string) (cchat.Session, error) {
return newSession(form[0]), nil
}
var (
// channel.go @ simulateAustralianInternet
internetCanFail = true
// 500ms ~ 3s
internetMinLatency = 500
internetMaxLatency = 3000
)
func (s Service) Configuration() (map[string]string, error) {
return map[string]string{
// refer to internet.go
"internet.canFail": strconv.FormatBool(internetCanFail),
"internet.minLatency": strconv.Itoa(internetMinLatency),
"internet.maxLatency": strconv.Itoa(internetMaxLatency),