mirror of
https://github.com/shazow/ssh-chat.git
synced 2025-06-07 10:53:07 +03:00
broken: Split UserScreen into PipedScreen, BufferedScreen, HandledScreen
This commit is contained in:
parent
33a76bb7f4
commit
50022e9e44
24
chat/message/config.go
Normal file
24
chat/message/config.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package message
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
// Container for per-user configurations.
|
||||||
|
type UserConfig struct {
|
||||||
|
Highlight *regexp.Regexp
|
||||||
|
Bell bool
|
||||||
|
Quiet bool
|
||||||
|
Theme *Theme
|
||||||
|
Seed int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default user configuration to use
|
||||||
|
var DefaultUserConfig UserConfig
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
DefaultUserConfig = UserConfig{
|
||||||
|
Bell: true,
|
||||||
|
Quiet: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Seed random?
|
||||||
|
}
|
206
chat/message/screen.go
Normal file
206
chat/message/screen.go
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
package message
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
"regexp"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrUserClosed = errors.New("user closed")
|
||||||
|
|
||||||
|
const messageBuffer = 5
|
||||||
|
const messageTimeout = 5 * time.Second
|
||||||
|
|
||||||
|
func BufferedScreen(name string, screen io.WriteCloser) *bufferedScreen {
|
||||||
|
return &bufferedScreen{
|
||||||
|
pipedScreen: PipedScreen(name, screen),
|
||||||
|
msg: make(chan Message, messageBuffer),
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PipedScreen(name string, screen io.WriteCloser) *pipedScreen {
|
||||||
|
return &pipedScreen{
|
||||||
|
baseScreen: &baseScreen{
|
||||||
|
User: NewUser(name),
|
||||||
|
},
|
||||||
|
WriteCloser: screen,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandledScreen(name string, handler func(Message) error) *handledScreen {
|
||||||
|
return &handledScreen{
|
||||||
|
baseScreen: &baseScreen{
|
||||||
|
User: NewUser(name),
|
||||||
|
},
|
||||||
|
handler: handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type handledScreen struct {
|
||||||
|
*baseScreen
|
||||||
|
handler func(Message) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *handledScreen) Send(m Message) error {
|
||||||
|
return u.handler(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Screen that pipes messages to an io.WriteCloser
|
||||||
|
type pipedScreen struct {
|
||||||
|
*baseScreen
|
||||||
|
io.WriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *pipedScreen) Send(m Message) error {
|
||||||
|
r := u.render(m)
|
||||||
|
_, err := u.Write([]byte(r))
|
||||||
|
if err != nil {
|
||||||
|
logger.Printf("Write failed to %s, closing: %s", u.Name(), err)
|
||||||
|
u.Close()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// User container that knows about writing to an IO screen.
|
||||||
|
type baseScreen struct {
|
||||||
|
sync.Mutex
|
||||||
|
*User
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *baseScreen) Config() UserConfig {
|
||||||
|
u.Lock()
|
||||||
|
defer u.Unlock()
|
||||||
|
return u.config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *baseScreen) SetConfig(cfg UserConfig) {
|
||||||
|
u.Lock()
|
||||||
|
u.config = cfg
|
||||||
|
u.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *baseScreen) ID() string {
|
||||||
|
u.Lock()
|
||||||
|
defer u.Unlock()
|
||||||
|
return SanitizeName(u.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *baseScreen) Name() string {
|
||||||
|
u.Lock()
|
||||||
|
defer u.Unlock()
|
||||||
|
return u.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *baseScreen) Joined() time.Time {
|
||||||
|
return u.joined
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename the user with a new Identifier.
|
||||||
|
func (u *baseScreen) SetName(name string) {
|
||||||
|
u.Lock()
|
||||||
|
u.name = name
|
||||||
|
u.config.Seed = rand.Int()
|
||||||
|
u.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplyTo returns the last user that messaged this user.
|
||||||
|
func (u *baseScreen) ReplyTo() Author {
|
||||||
|
u.Lock()
|
||||||
|
defer u.Unlock()
|
||||||
|
return u.replyTo
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReplyTo sets the last user to message this user.
|
||||||
|
func (u *baseScreen) SetReplyTo(user Author) {
|
||||||
|
// TODO: Use UserConfig.ReplyTo string
|
||||||
|
u.Lock()
|
||||||
|
defer u.Unlock()
|
||||||
|
u.replyTo = user
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHighlight sets the highlighting regular expression to match string.
|
||||||
|
func (u *baseScreen) SetHighlight(s string) error {
|
||||||
|
re, err := regexp.Compile(fmt.Sprintf(reHighlight, s))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
u.Lock()
|
||||||
|
u.config.Highlight = re
|
||||||
|
u.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *baseScreen) render(m Message) string {
|
||||||
|
cfg := u.Config()
|
||||||
|
switch m := m.(type) {
|
||||||
|
case PublicMsg:
|
||||||
|
return m.RenderFor(cfg) + Newline
|
||||||
|
case PrivateMsg:
|
||||||
|
u.SetReplyTo(m.From())
|
||||||
|
return m.Render(cfg.Theme) + Newline
|
||||||
|
default:
|
||||||
|
return m.Render(cfg.Theme) + Newline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt renders a theme-colorized prompt string.
|
||||||
|
func (u *baseScreen) Prompt() string {
|
||||||
|
name := u.Name()
|
||||||
|
cfg := u.Config()
|
||||||
|
if cfg.Theme != nil {
|
||||||
|
name = cfg.Theme.ColorName(u)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("[%s] ", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bufferedScreen is a screen that buffers messages on Send using a channel and a consuming goroutine.
|
||||||
|
type bufferedScreen struct {
|
||||||
|
*pipedScreen
|
||||||
|
closeOnce sync.Once
|
||||||
|
msg chan Message
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *bufferedScreen) Close() error {
|
||||||
|
u.closeOnce.Do(func() {
|
||||||
|
close(u.done)
|
||||||
|
})
|
||||||
|
|
||||||
|
return u.pipedScreen.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add message to consume by user
|
||||||
|
func (u *bufferedScreen) Send(m Message) error {
|
||||||
|
select {
|
||||||
|
case <-u.done:
|
||||||
|
return ErrUserClosed
|
||||||
|
case u.msg <- m:
|
||||||
|
case <-time.After(messageTimeout):
|
||||||
|
logger.Printf("Message buffer full, closing: %s", u.Name())
|
||||||
|
u.Close()
|
||||||
|
return ErrUserClosed
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume message buffer into the handler. Will block, should be called in a
|
||||||
|
// goroutine.
|
||||||
|
func (u *bufferedScreen) Consume() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-u.done:
|
||||||
|
return
|
||||||
|
case m, ok := <-u.msg:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Pass on to unbuffered screen.
|
||||||
|
u.pipedScreen.Send(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -52,7 +52,7 @@ func TestTheme(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u := NewUser("foo")
|
u := NewUser("foo")
|
||||||
u.colorIdx = 4
|
u.config.Seed = 4
|
||||||
actual = colorTheme.ColorName(u)
|
actual = colorTheme.ColorName(u)
|
||||||
expected = "\033[38;05;5mfoo\033[0m"
|
expected = "\033[38;05;5mfoo\033[0m"
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
|
@ -1,75 +1,16 @@
|
|||||||
package message
|
package message
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"regexp"
|
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const messageBuffer = 5
|
|
||||||
const messageTimeout = 5 * time.Second
|
|
||||||
const reHighlight = `\b(%s)\b`
|
const reHighlight = `\b(%s)\b`
|
||||||
|
|
||||||
var ErrUserClosed = errors.New("user closed")
|
|
||||||
|
|
||||||
// User container that knows about writing to an IO screen.
|
|
||||||
type UserScreen struct {
|
|
||||||
*User
|
|
||||||
io.WriteCloser
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserScreen) Close() error {
|
|
||||||
u.User.Close()
|
|
||||||
return u.WriteCloser.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleMsg will render the message to the screen, blocking.
|
|
||||||
func (u *UserScreen) HandleMsg(m Message) error {
|
|
||||||
r := u.render(m)
|
|
||||||
_, err := u.Write([]byte(r))
|
|
||||||
if err != nil {
|
|
||||||
logger.Printf("Write failed to %s, closing: %s", u.Name(), err)
|
|
||||||
u.User.Close()
|
|
||||||
u.WriteCloser.Close()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume message buffer into the handler. Will block, should be called in a
|
|
||||||
// goroutine.
|
|
||||||
func (u *UserScreen) Consume() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-u.done:
|
|
||||||
return
|
|
||||||
case m, ok := <-u.msg:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
u.HandleMsg(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume one message and stop, mostly for testing
|
|
||||||
// TODO: Stop using it and remove it.
|
|
||||||
func (u *UserScreen) ConsumeOne() Message {
|
|
||||||
return <-u.msg
|
|
||||||
}
|
|
||||||
|
|
||||||
// User definition, implemented set Item interface and io.Writer
|
// User definition, implemented set Item interface and io.Writer
|
||||||
type User struct {
|
type User struct {
|
||||||
colorIdx int
|
|
||||||
joined time.Time
|
joined time.Time
|
||||||
closeOnce sync.Once
|
|
||||||
msg chan Message
|
|
||||||
done chan struct{}
|
|
||||||
|
|
||||||
mu sync.Mutex
|
|
||||||
name string
|
name string
|
||||||
config UserConfig
|
config UserConfig
|
||||||
replyTo Author // Set when user gets a /msg, for replying.
|
replyTo Author // Set when user gets a /msg, for replying.
|
||||||
@ -80,155 +21,20 @@ func NewUser(name string) *User {
|
|||||||
name: name,
|
name: name,
|
||||||
config: DefaultUserConfig,
|
config: DefaultUserConfig,
|
||||||
joined: time.Now(),
|
joined: time.Now(),
|
||||||
msg: make(chan Message, messageBuffer),
|
|
||||||
done: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
u.setColorIdx(rand.Int())
|
u.config.Seed = rand.Int()
|
||||||
|
|
||||||
return &u
|
return &u
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserScreen(name string, screen io.WriteCloser) *UserScreen {
|
|
||||||
return &UserScreen{
|
|
||||||
User: NewUser(name),
|
|
||||||
WriteCloser: screen,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) Config() UserConfig {
|
|
||||||
u.mu.Lock()
|
|
||||||
defer u.mu.Unlock()
|
|
||||||
return u.config
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) SetConfig(cfg UserConfig) {
|
|
||||||
u.mu.Lock()
|
|
||||||
u.config = cfg
|
|
||||||
u.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) ID() string {
|
|
||||||
u.mu.Lock()
|
|
||||||
defer u.mu.Unlock()
|
|
||||||
return SanitizeName(u.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) Color() int {
|
|
||||||
return u.colorIdx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) Name() string {
|
func (u *User) Name() string {
|
||||||
u.mu.Lock()
|
|
||||||
defer u.mu.Unlock()
|
|
||||||
return u.name
|
return u.name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) Joined() time.Time {
|
func (u *User) Color() int {
|
||||||
return u.joined
|
return u.config.Seed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rename the user with a new Identifier.
|
func (u *User) ID() string {
|
||||||
func (u *User) SetName(name string) {
|
return SanitizeName(u.name)
|
||||||
u.mu.Lock()
|
|
||||||
u.name = name
|
|
||||||
u.setColorIdx(rand.Int())
|
|
||||||
u.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReplyTo returns the last user that messaged this user.
|
|
||||||
func (u *User) ReplyTo() Author {
|
|
||||||
u.mu.Lock()
|
|
||||||
defer u.mu.Unlock()
|
|
||||||
return u.replyTo
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetReplyTo sets the last user to message this user.
|
|
||||||
func (u *User) SetReplyTo(user Author) {
|
|
||||||
// TODO: Use UserConfig.ReplyTo string
|
|
||||||
u.mu.Lock()
|
|
||||||
defer u.mu.Unlock()
|
|
||||||
u.replyTo = user
|
|
||||||
}
|
|
||||||
|
|
||||||
// setColorIdx will set the colorIdx to a specific value, primarily used for
|
|
||||||
// testing.
|
|
||||||
func (u *User) setColorIdx(idx int) {
|
|
||||||
u.colorIdx = idx
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disconnect user, stop accepting messages
|
|
||||||
func (u *User) Close() {
|
|
||||||
u.closeOnce.Do(func() {
|
|
||||||
// close(u.msg) TODO: Close?
|
|
||||||
close(u.done)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHighlight sets the highlighting regular expression to match string.
|
|
||||||
func (u *User) SetHighlight(s string) error {
|
|
||||||
re, err := regexp.Compile(fmt.Sprintf(reHighlight, s))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
u.mu.Lock()
|
|
||||||
u.config.Highlight = re
|
|
||||||
u.mu.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) render(m Message) string {
|
|
||||||
cfg := u.Config()
|
|
||||||
switch m := m.(type) {
|
|
||||||
case PublicMsg:
|
|
||||||
return m.RenderFor(cfg) + Newline
|
|
||||||
case PrivateMsg:
|
|
||||||
u.SetReplyTo(m.From())
|
|
||||||
return m.Render(cfg.Theme) + Newline
|
|
||||||
default:
|
|
||||||
return m.Render(cfg.Theme) + Newline
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add message to consume by user
|
|
||||||
func (u *User) Send(m Message) error {
|
|
||||||
select {
|
|
||||||
case <-u.done:
|
|
||||||
return ErrUserClosed
|
|
||||||
case u.msg <- m:
|
|
||||||
case <-time.After(messageTimeout):
|
|
||||||
logger.Printf("Message buffer full, closing: %s", u.Name())
|
|
||||||
u.Close()
|
|
||||||
return ErrUserClosed
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prompt renders a theme-colorized prompt string.
|
|
||||||
func (u *User) Prompt() string {
|
|
||||||
name := u.Name()
|
|
||||||
cfg := u.Config()
|
|
||||||
if cfg.Theme != nil {
|
|
||||||
name = cfg.Theme.ColorName(u)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("[%s] ", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Container for per-user configurations.
|
|
||||||
type UserConfig struct {
|
|
||||||
Highlight *regexp.Regexp
|
|
||||||
Bell bool
|
|
||||||
Quiet bool
|
|
||||||
Theme *Theme
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default user configuration to use
|
|
||||||
var DefaultUserConfig UserConfig
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
DefaultUserConfig = UserConfig{
|
|
||||||
Bell: true,
|
|
||||||
Quiet: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Seed random?
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,8 @@ func TestMakeUser(t *testing.T) {
|
|||||||
var actual, expected []byte
|
var actual, expected []byte
|
||||||
|
|
||||||
s := &MockScreen{}
|
s := &MockScreen{}
|
||||||
u := NewUserScreen("foo", s)
|
u := PipedScreen("foo", s)
|
||||||
|
|
||||||
m := NewAnnounceMsg("hello")
|
m := NewAnnounceMsg("hello")
|
||||||
|
|
||||||
defer u.Close()
|
defer u.Close()
|
||||||
@ -17,7 +18,6 @@ func TestMakeUser(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to send: %s", err)
|
t.Fatalf("failed to send: %s", err)
|
||||||
}
|
}
|
||||||
u.HandleMsg(<-u.msg)
|
|
||||||
|
|
||||||
s.Read(&actual)
|
s.Read(&actual)
|
||||||
expected = []byte(m.String() + Newline)
|
expected = []byte(m.String() + Newline)
|
||||||
|
@ -88,7 +88,7 @@ func (r *Room) HandleMsg(m message.Message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r.history.Add(m)
|
r.history.Add(m)
|
||||||
r.Members.Each(func(_ string, item set.Item) (err error) {
|
r.Members.Each(func(k string, item set.Item) (err error) {
|
||||||
roomMember := item.Value().(*roomMember)
|
roomMember := item.Value().(*roomMember)
|
||||||
user := roomMember.Member
|
user := roomMember.Member
|
||||||
from := fromMsg.From().(Member)
|
from := fromMsg.From().(Member)
|
||||||
|
@ -57,7 +57,7 @@ func TestIgnore(t *testing.T) {
|
|||||||
users := make([]ScreenedUser, 3)
|
users := make([]ScreenedUser, 3)
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
screen := &MockScreen{}
|
screen := &MockScreen{}
|
||||||
user := message.NewUserScreen(fmt.Sprintf("user%d", i), screen)
|
user := message.NewScreen(fmt.Sprintf("user%d", i), screen)
|
||||||
users[i] = ScreenedUser{
|
users[i] = ScreenedUser{
|
||||||
user: user,
|
user: user,
|
||||||
screen: screen,
|
screen: screen,
|
||||||
@ -162,7 +162,7 @@ func TestRoomJoin(t *testing.T) {
|
|||||||
var expected, actual []byte
|
var expected, actual []byte
|
||||||
|
|
||||||
s := &MockScreen{}
|
s := &MockScreen{}
|
||||||
u := message.NewUserScreen("foo", s)
|
u := message.PipedScreen("foo", s)
|
||||||
|
|
||||||
ch := NewRoom()
|
ch := NewRoom()
|
||||||
go ch.Serve()
|
go ch.Serve()
|
||||||
@ -173,7 +173,6 @@ func TestRoomJoin(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
u.HandleMsg(u.ConsumeOne())
|
|
||||||
expected = []byte(" * foo joined. (Connected: 1)" + message.Newline)
|
expected = []byte(" * foo joined. (Connected: 1)" + message.Newline)
|
||||||
s.Read(&actual)
|
s.Read(&actual)
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
@ -181,7 +180,6 @@ func TestRoomJoin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ch.Send(message.NewSystemMsg("hello", u))
|
ch.Send(message.NewSystemMsg("hello", u))
|
||||||
u.HandleMsg(u.ConsumeOne())
|
|
||||||
expected = []byte("-> hello" + message.Newline)
|
expected = []byte("-> hello" + message.Newline)
|
||||||
s.Read(&actual)
|
s.Read(&actual)
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
@ -189,7 +187,6 @@ func TestRoomJoin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ch.Send(message.ParseInput("/me says hello.", u))
|
ch.Send(message.ParseInput("/me says hello.", u))
|
||||||
u.HandleMsg(u.ConsumeOne())
|
|
||||||
expected = []byte("** foo says hello." + message.Newline)
|
expected = []byte("** foo says hello." + message.Newline)
|
||||||
s.Read(&actual)
|
s.Read(&actual)
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
@ -198,7 +195,11 @@ func TestRoomJoin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRoomDoesntBroadcastAnnounceMessagesWhenQuiet(t *testing.T) {
|
func TestRoomDoesntBroadcastAnnounceMessagesWhenQuiet(t *testing.T) {
|
||||||
u := message.NewUser("foo")
|
msgs := make(chan message.Message)
|
||||||
|
u := message.HandledScreen("foo", func(m message.Message) error {
|
||||||
|
msgs <- m
|
||||||
|
return nil
|
||||||
|
})
|
||||||
u.SetConfig(message.UserConfig{
|
u.SetConfig(message.UserConfig{
|
||||||
Quiet: true,
|
Quiet: true,
|
||||||
})
|
})
|
||||||
@ -211,19 +212,12 @@ func TestRoomDoesntBroadcastAnnounceMessagesWhenQuiet(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drain the initial Join message
|
|
||||||
<-ch.broadcast
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
/*
|
for msg := range msgs {
|
||||||
for {
|
|
||||||
msg := u.ConsumeChan()
|
|
||||||
if _, ok := msg.(*message.AnnounceMsg); ok {
|
if _, ok := msg.(*message.AnnounceMsg); ok {
|
||||||
t.Errorf("Got unexpected `%T`", msg)
|
t.Errorf("Got unexpected `%T`", msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
// XXX: Fix this
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Call with an AnnounceMsg and all the other types
|
// Call with an AnnounceMsg and all the other types
|
||||||
@ -234,10 +228,16 @@ func TestRoomDoesntBroadcastAnnounceMessagesWhenQuiet(t *testing.T) {
|
|||||||
ch.HandleMsg(message.NewSystemMsg("hello", u))
|
ch.HandleMsg(message.NewSystemMsg("hello", u))
|
||||||
ch.HandleMsg(message.NewPrivateMsg("hello", u, u))
|
ch.HandleMsg(message.NewPrivateMsg("hello", u, u))
|
||||||
ch.HandleMsg(message.NewPublicMsg("hello", u))
|
ch.HandleMsg(message.NewPublicMsg("hello", u))
|
||||||
|
// Try an ignored one again just in case
|
||||||
|
ch.HandleMsg(message.NewAnnounceMsg("Once more for fun"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRoomQuietToggleBroadcasts(t *testing.T) {
|
func TestRoomQuietToggleBroadcasts(t *testing.T) {
|
||||||
u := message.NewUser("foo")
|
msgs := make(chan message.Message)
|
||||||
|
u := message.HandledScreen("foo", func(m message.Message) error {
|
||||||
|
msgs <- m
|
||||||
|
return nil
|
||||||
|
})
|
||||||
u.SetConfig(message.UserConfig{
|
u.SetConfig(message.UserConfig{
|
||||||
Quiet: true,
|
Quiet: true,
|
||||||
})
|
})
|
||||||
@ -259,7 +259,7 @@ func TestRoomQuietToggleBroadcasts(t *testing.T) {
|
|||||||
|
|
||||||
expectedMsg := message.NewAnnounceMsg("Ignored")
|
expectedMsg := message.NewAnnounceMsg("Ignored")
|
||||||
ch.HandleMsg(expectedMsg)
|
ch.HandleMsg(expectedMsg)
|
||||||
msg := u.ConsumeOne()
|
msg := <-msgs
|
||||||
if _, ok := msg.(*message.AnnounceMsg); !ok {
|
if _, ok := msg.(*message.AnnounceMsg); !ok {
|
||||||
t.Errorf("Got: `%T`; Expected: `%T`", msg, expectedMsg)
|
t.Errorf("Got: `%T`; Expected: `%T`", msg, expectedMsg)
|
||||||
}
|
}
|
||||||
@ -270,7 +270,7 @@ func TestRoomQuietToggleBroadcasts(t *testing.T) {
|
|||||||
|
|
||||||
ch.HandleMsg(message.NewAnnounceMsg("Ignored"))
|
ch.HandleMsg(message.NewAnnounceMsg("Ignored"))
|
||||||
ch.HandleMsg(message.NewSystemMsg("hello", u))
|
ch.HandleMsg(message.NewSystemMsg("hello", u))
|
||||||
msg = u.ConsumeOne()
|
msg = <-msgs
|
||||||
if _, ok := msg.(*message.AnnounceMsg); ok {
|
if _, ok := msg.(*message.AnnounceMsg); ok {
|
||||||
t.Errorf("Got unexpected `%T`", msg)
|
t.Errorf("Got unexpected `%T`", msg)
|
||||||
}
|
}
|
||||||
@ -280,7 +280,7 @@ func TestQuietToggleDisplayState(t *testing.T) {
|
|||||||
var expected, actual []byte
|
var expected, actual []byte
|
||||||
|
|
||||||
s := &MockScreen{}
|
s := &MockScreen{}
|
||||||
u := message.NewUserScreen("foo", s)
|
u := message.PipedScreen("foo", s)
|
||||||
|
|
||||||
ch := NewRoom()
|
ch := NewRoom()
|
||||||
go ch.Serve()
|
go ch.Serve()
|
||||||
@ -291,7 +291,6 @@ func TestQuietToggleDisplayState(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
u.HandleMsg(u.ConsumeOne())
|
|
||||||
expected = []byte(" * foo joined. (Connected: 1)" + message.Newline)
|
expected = []byte(" * foo joined. (Connected: 1)" + message.Newline)
|
||||||
s.Read(&actual)
|
s.Read(&actual)
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
@ -300,7 +299,6 @@ func TestQuietToggleDisplayState(t *testing.T) {
|
|||||||
|
|
||||||
ch.Send(message.ParseInput("/quiet", u))
|
ch.Send(message.ParseInput("/quiet", u))
|
||||||
|
|
||||||
u.HandleMsg(u.ConsumeOne())
|
|
||||||
expected = []byte("-> Quiet mode is toggled ON" + message.Newline)
|
expected = []byte("-> Quiet mode is toggled ON" + message.Newline)
|
||||||
s.Read(&actual)
|
s.Read(&actual)
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
@ -309,7 +307,6 @@ func TestQuietToggleDisplayState(t *testing.T) {
|
|||||||
|
|
||||||
ch.Send(message.ParseInput("/quiet", u))
|
ch.Send(message.ParseInput("/quiet", u))
|
||||||
|
|
||||||
u.HandleMsg(u.ConsumeOne())
|
|
||||||
expected = []byte("-> Quiet mode is toggled OFF" + message.Newline)
|
expected = []byte("-> Quiet mode is toggled OFF" + message.Newline)
|
||||||
s.Read(&actual)
|
s.Read(&actual)
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
@ -321,7 +318,7 @@ func TestRoomNames(t *testing.T) {
|
|||||||
var expected, actual []byte
|
var expected, actual []byte
|
||||||
|
|
||||||
s := &MockScreen{}
|
s := &MockScreen{}
|
||||||
u := message.NewUserScreen("foo", s)
|
u := message.PipedScreen("foo", s)
|
||||||
|
|
||||||
ch := NewRoom()
|
ch := NewRoom()
|
||||||
go ch.Serve()
|
go ch.Serve()
|
||||||
@ -332,7 +329,6 @@ func TestRoomNames(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
u.HandleMsg(u.ConsumeOne())
|
|
||||||
expected = []byte(" * foo joined. (Connected: 1)" + message.Newline)
|
expected = []byte(" * foo joined. (Connected: 1)" + message.Newline)
|
||||||
s.Read(&actual)
|
s.Read(&actual)
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
@ -341,7 +337,6 @@ func TestRoomNames(t *testing.T) {
|
|||||||
|
|
||||||
ch.Send(message.ParseInput("/names", u))
|
ch.Send(message.ParseInput("/names", u))
|
||||||
|
|
||||||
u.HandleMsg(u.ConsumeOne())
|
|
||||||
expected = []byte("-> 1 connected: foo" + message.Newline)
|
expected = []byte("-> 1 connected: foo" + message.Newline)
|
||||||
s.Read(&actual)
|
s.Read(&actual)
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
2
host.go
2
host.go
@ -75,7 +75,7 @@ func (h *Host) SetMotd(motd string) {
|
|||||||
// Connect a specific Terminal to this host and its room.
|
// Connect a specific Terminal to this host and its room.
|
||||||
func (h *Host) Connect(term *sshd.Terminal) {
|
func (h *Host) Connect(term *sshd.Terminal) {
|
||||||
requestedName := term.Conn.Name()
|
requestedName := term.Conn.Name()
|
||||||
user := message.NewUserScreen(requestedName, term)
|
user := message.NewScreen(requestedName, term)
|
||||||
|
|
||||||
client := h.addClient(user, term.Conn)
|
client := h.addClient(user, term.Conn)
|
||||||
defer h.removeClient(user, client)
|
defer h.removeClient(user, client)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user