Messing with the API more, tests pass.

This commit is contained in:
Andrey Petrov 2014-12-21 12:17:01 -08:00
parent bf3cc264e6
commit 137e84db79
6 changed files with 75 additions and 18 deletions

View File

@ -16,7 +16,7 @@ func TestChannel(t *testing.T) {
}
}()
u := NewUser("foo", s)
u := NewUserScreen("foo", s)
ch := NewChannel("", out)
err := ch.Join(u)
@ -26,6 +26,6 @@ func TestChannel(t *testing.T) {
expected := []byte(" * foo joined. (Connected: 1)")
if !reflect.DeepEqual(s.received, expected) {
t.Errorf("Got: `%s`, Expected: `%s`", s.received, expected)
t.Errorf("Got: `%s`; Expected: `%s`", s.received, expected)
}
}

View File

@ -1,7 +1,5 @@
package chat
const messageBuffer = 20
// Host knows about all the commands and channels.
type Host struct {
defaultChannel *Channel

View File

@ -37,6 +37,7 @@ func (m *Message) From(u *User) *Message {
// Render message based on the given theme
func (m *Message) Render(*Theme) string {
// TODO: Return []byte?
// TODO: Render based on theme
// TODO: Cache based on theme
var msg string

View File

@ -5,7 +5,7 @@ import "testing"
func TestSet(t *testing.T) {
var err error
s := NewSet()
u := NewUser("foo", nil)
u := NewUser("foo")
if s.In(u) {
t.Errorf("Set should be empty.")
@ -20,7 +20,7 @@ func TestSet(t *testing.T) {
t.Errorf("Set should contain user.")
}
u2 := NewUser("bar", nil)
u2 := NewUser("bar")
err = s.Add(u2)
if err != nil {
t.Error(err)

View File

@ -1,31 +1,46 @@
package chat
import (
"errors"
"io"
"math/rand"
"time"
)
const messageBuffer = 20
var ErrUserClosed = errors.New("user closed")
// User definition, implemented set Item interface and io.Writer
type User struct {
name string
op bool
colorIdx int
joined time.Time
screen io.Writer
msg chan Message
done chan struct{}
Config UserConfig
}
func NewUser(name string, screen io.Writer) *User {
func NewUser(name string) *User {
u := User{
screen: screen,
joined: time.Now(),
Config: *DefaultUserConfig,
joined: time.Now(),
msg: make(chan Message, messageBuffer),
done: make(chan struct{}, 1),
}
u.SetName(name)
return &u
}
func NewUserScreen(name string, screen io.Writer) *User {
u := NewUser(name)
go u.Consume(screen)
return u
}
// Return unique identifier for user
func (u *User) Id() Id {
return Id(u.name)
@ -52,9 +67,49 @@ func (u *User) SetOp(op bool) {
u.op = op
}
// Write to user's screen
func (u *User) Write(p []byte) (n int, err error) {
return u.screen.Write(p)
// Block until user is closed
func (u *User) Wait() {
<-u.done
}
// Disconnect user, stop accepting messages
func (u *User) Close() {
close(u.done)
close(u.msg)
}
// Consume message buffer into an io.Writer. Will block, should be called in a
// goroutine.
func (u *User) Consume(out io.Writer) {
for m := range u.msg {
u.consumeMsg(m, out)
}
}
// Consume one message and stop, mostly for testing
func (u *User) ConsumeOne(out io.Writer) {
u.consumeMsg(<-u.msg, out)
}
func (u *User) consumeMsg(m Message, out io.Writer) {
s := m.Render(u.Config.Theme)
_, err := out.Write([]byte(s))
if err != nil {
logger.Printf("Write failed to %s, closing: %s", u.Name(), err)
u.Close()
}
}
// Add message to consume by user
func (u *User) Send(m Message) error {
select {
case u.msg <- m:
default:
logger.Printf("Msg buffer full, closing: %s", u.Name())
u.Close()
return ErrUserClosed
}
return nil
}
// Container for per-user configurations.

View File

@ -16,11 +16,14 @@ func (s *MockScreen) Write(data []byte) (n int, err error) {
func TestMakeUser(t *testing.T) {
s := &MockScreen{}
u := NewUser("foo", s)
u := NewUser("foo")
m := NewMessage("hello")
line := []byte("hello")
u.Write(line)
if !reflect.DeepEqual(s.received, line) {
t.Errorf("Expected hello but got: %s", s.received)
defer u.Close()
u.Send(*m)
u.ConsumeOne(s)
if !reflect.DeepEqual(string(s.received), m.String()) {
t.Errorf("Got: `%s`; Expected: `%s`", s.received, m.String())
}
}