mirror of https://github.com/nealey/vail.git
More unit test junk
This commit is contained in:
parent
db9ca5dc83
commit
d6e6a268a3
|
@ -5,15 +5,20 @@ import (
|
|||
"log"
|
||||
)
|
||||
|
||||
// Book maps names to repeaters
|
||||
//
|
||||
// It ensures that names map 1-1 to repeaters.
|
||||
type Book struct {
|
||||
entries map[string]*Repeater
|
||||
events chan bookEvent
|
||||
entries map[string]*Repeater
|
||||
events chan bookEvent
|
||||
makeRepeater func() *Repeater
|
||||
}
|
||||
|
||||
func NewBook() Book {
|
||||
return Book{
|
||||
entries: make(map[string]*Repeater),
|
||||
events: make(chan bookEvent, 5),
|
||||
entries: make(map[string]*Repeater),
|
||||
events: make(chan bookEvent, 5),
|
||||
makeRepeater: NewRepeater,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,6 +37,7 @@ type bookEvent struct {
|
|||
m Message
|
||||
}
|
||||
|
||||
// Join adds a writer to a named repeater
|
||||
func (b Book) Join(name string, w io.Writer) {
|
||||
b.events <- bookEvent{
|
||||
eventType: joinEvent,
|
||||
|
@ -40,6 +46,7 @@ func (b Book) Join(name string, w io.Writer) {
|
|||
}
|
||||
}
|
||||
|
||||
// Part removes a writer from a named repeater
|
||||
func (b Book) Part(name string, w io.Writer) {
|
||||
b.events <- bookEvent{
|
||||
eventType: partEvent,
|
||||
|
@ -48,6 +55,7 @@ func (b Book) Part(name string, w io.Writer) {
|
|||
}
|
||||
}
|
||||
|
||||
// Send transmits a message to the named repeater
|
||||
func (b Book) Send(name string, m Message) {
|
||||
b.events <- bookEvent{
|
||||
eventType: sendEvent,
|
||||
|
@ -56,6 +64,7 @@ func (b Book) Send(name string, m Message) {
|
|||
}
|
||||
}
|
||||
|
||||
// Run is the endless run loop
|
||||
func (b Book) Run() {
|
||||
for {
|
||||
b.loop()
|
||||
|
@ -69,7 +78,7 @@ func (b Book) loop() {
|
|||
switch event.eventType {
|
||||
case joinEvent:
|
||||
if !ok {
|
||||
repeater = NewRepeater()
|
||||
repeater = b.makeRepeater()
|
||||
b.entries[event.name] = repeater
|
||||
}
|
||||
repeater.Join(event.w)
|
||||
|
|
|
@ -1,69 +1,57 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBook(t *testing.T) {
|
||||
b := NewBook()
|
||||
m := TestMessage{Message{1, 2, []uint8{3, 4}}}
|
||||
b := Book{
|
||||
entries: make(map[string]*Repeater),
|
||||
events: make(chan bookEvent, 5),
|
||||
makeRepeater: NewTestingRepeater,
|
||||
}
|
||||
|
||||
buf1 := bytes.NewBufferString("buf1")
|
||||
buf1Expect := bytes.NewBufferString("buf1")
|
||||
b.Join("moo", buf1)
|
||||
m.Clients = 1
|
||||
c1 := NewTestingClient(t)
|
||||
b.Join("moo", c1)
|
||||
b.loop()
|
||||
if len(b.entries) != 1 {
|
||||
t.Error("Wrong number of entries")
|
||||
}
|
||||
c1.Expect(1)
|
||||
|
||||
// Send to an empty channel
|
||||
b.Send("merf", m.Message)
|
||||
m := Message{0, 0, []uint8{22, 33}}
|
||||
b.Send("merf", m)
|
||||
b.loop()
|
||||
if buf1.String() != buf1Expect.String() {
|
||||
if c1.Len() > 0 {
|
||||
t.Error("Sending to empty channel sent to non-empty channel")
|
||||
}
|
||||
|
||||
// Send to a non-empty channel!
|
||||
b.Send("moo", m.Message)
|
||||
b.Send("moo", m)
|
||||
b.loop()
|
||||
buf1Expect.Write(m.bytes())
|
||||
if buf1.String() != buf1Expect.String() {
|
||||
t.Error("Sending didn't work")
|
||||
}
|
||||
c1.Expect(1, 22, 33)
|
||||
|
||||
// Join another client
|
||||
buf2 := bytes.NewBufferString("buf2")
|
||||
buf2Expect := bytes.NewBufferString("buf2")
|
||||
b.Join("moo", buf2)
|
||||
m.Clients = 2
|
||||
c2 := NewTestingClient(t)
|
||||
b.Join("moo", c2)
|
||||
b.loop()
|
||||
c1.Expect(2)
|
||||
c2.Expect(2)
|
||||
|
||||
// Send to both
|
||||
b.Send("moo", m.Message)
|
||||
m.Duration = append(m.Duration, 44)
|
||||
b.Send("moo", m)
|
||||
b.loop()
|
||||
buf1Expect.Write(m.bytes())
|
||||
buf2Expect.Write(m.bytes())
|
||||
if buf1.String() != buf1Expect.String() {
|
||||
t.Error("Send to 2-member channel busted", buf1)
|
||||
}
|
||||
if buf2.String() != buf2Expect.String() {
|
||||
t.Error("Send to 2-member channel busted", buf2)
|
||||
}
|
||||
c1.Expect(2, 22, 33, 44)
|
||||
c2.Expect(2, 22, 33, 44)
|
||||
|
||||
// Part a client
|
||||
b.Part("moo", buf1)
|
||||
b.Part("moo", c1)
|
||||
b.loop()
|
||||
m.Clients = 1
|
||||
c2.Expect(1)
|
||||
|
||||
b.Send("moo", m.Message)
|
||||
b.Send("moo", m)
|
||||
b.loop()
|
||||
buf2Expect.Write(m.bytes())
|
||||
if buf1.String() != buf1Expect.String() {
|
||||
t.Error("Parted channel but still getting messages", buf1)
|
||||
}
|
||||
if buf2.String() != buf2Expect.String() {
|
||||
t.Error("Someone else parting somehow messed up sends", buf2)
|
||||
}
|
||||
c2.Expect(1, 22, 33, 44)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,21 @@ import (
|
|||
|
||||
var book Book
|
||||
|
||||
// Clock defines an interface for getting the current time.
|
||||
//
|
||||
// We use this in testing to provide a fixed value for the current time, so we
|
||||
// can still compare clocks.
|
||||
type Clock interface {
|
||||
Now() time.Time
|
||||
}
|
||||
|
||||
// WallClock provides the actual time
|
||||
type WallClock struct{}
|
||||
|
||||
func (WallClock) Now() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
repeaterName string
|
||||
}
|
||||
|
@ -22,9 +37,6 @@ func (c Client) Handle(ws *websocket.Conn) {
|
|||
book.Join(c.repeaterName, ws)
|
||||
defer book.Part(c.repeaterName, ws)
|
||||
|
||||
// Tell the client what time we think it is
|
||||
fmt.Fprintf(ws, "[%d]", time.Now().UnixNano()/time.Millisecond.Nanoseconds())
|
||||
|
||||
for {
|
||||
buf := make([]byte, ws.MaxPayloadBytes)
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ type Message struct {
|
|||
Duration []uint8
|
||||
}
|
||||
|
||||
func NewMessage(ts time.Time, durations []time.Duration) Message {
|
||||
func NewMessage(ts time.Time, durations ...time.Duration) Message {
|
||||
msg := Message{
|
||||
Timestamp: ts.UnixNano() / time.Millisecond.Nanoseconds(),
|
||||
Duration: make([]uint8, len(durations)),
|
||||
|
|
|
@ -40,11 +40,9 @@ func TestMessageStruct(t *testing.T) {
|
|||
0,
|
||||
m.Timestamp*time.Millisecond.Nanoseconds(),
|
||||
),
|
||||
[]time.Duration{
|
||||
time.Duration(m.Duration[0]) * time.Millisecond,
|
||||
time.Duration(m.Duration[1]) * time.Millisecond,
|
||||
time.Duration(m.Duration[2]) * time.Millisecond,
|
||||
},
|
||||
time.Duration(m.Duration[0])*time.Millisecond,
|
||||
time.Duration(m.Duration[1])*time.Millisecond,
|
||||
time.Duration(m.Duration[2])*time.Millisecond,
|
||||
)
|
||||
if !m.Equal(m3) {
|
||||
t.Error("NewMessage didn't work", m, m3)
|
||||
|
|
|
@ -3,23 +3,30 @@ package main
|
|||
import (
|
||||
"io"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Repeater is just a list of Writers.
|
||||
type Repeater struct {
|
||||
clock Clock
|
||||
writers []io.Writer
|
||||
}
|
||||
|
||||
// NewRepeater returns a newly-created repeater
|
||||
func NewRepeater() *Repeater {
|
||||
return &Repeater{
|
||||
clock: WallClock{},
|
||||
writers: make([]io.Writer, 0, 20),
|
||||
}
|
||||
}
|
||||
|
||||
// Join joins a writer to this repeater
|
||||
func (r *Repeater) Join(w io.Writer) {
|
||||
r.writers = append(r.writers, w)
|
||||
r.SendMessage()
|
||||
}
|
||||
|
||||
// Part removes a writer from this repeater
|
||||
func (r *Repeater) Part(w io.Writer) {
|
||||
for i, s := range r.writers {
|
||||
if s == w {
|
||||
|
@ -28,8 +35,10 @@ func (r *Repeater) Part(w io.Writer) {
|
|||
r.writers = r.writers[:nsubs-1]
|
||||
}
|
||||
}
|
||||
r.SendMessage()
|
||||
}
|
||||
|
||||
// Send send a message to all connected clients
|
||||
func (r *Repeater) Send(m Message) {
|
||||
m.Clients = uint16(r.Listeners())
|
||||
buf, err := m.MarshalBinary()
|
||||
|
@ -41,6 +50,13 @@ func (r *Repeater) Send(m Message) {
|
|||
}
|
||||
}
|
||||
|
||||
// SendMessage constructs and sends a message
|
||||
func (r *Repeater) SendMessage(durations ...time.Duration) {
|
||||
m := NewMessage(r.clock.Now(), durations...)
|
||||
r.Send(m)
|
||||
}
|
||||
|
||||
// Listeners returns the number of connected clients
|
||||
func (r *Repeater) Listeners() int {
|
||||
return len(r.writers)
|
||||
}
|
||||
|
|
|
@ -2,57 +2,75 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TestMessage struct {
|
||||
Message
|
||||
type FakeClock struct{}
|
||||
|
||||
func (f FakeClock) Now() time.Time {
|
||||
return time.UnixMilli(0)
|
||||
}
|
||||
|
||||
func (m TestMessage) bytes() []byte {
|
||||
b, _ := m.MarshalBinary()
|
||||
return b
|
||||
type TestingClient struct {
|
||||
bytes.Buffer
|
||||
expected bytes.Buffer
|
||||
repeater *Repeater
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func NewTestingClient(t *testing.T) *TestingClient {
|
||||
return &TestingClient{
|
||||
Buffer: bytes.Buffer{},
|
||||
expected: bytes.Buffer{},
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *TestingClient) Expect(clients uint16, payload ...uint8) {
|
||||
m := Message{0, clients, payload}
|
||||
buf, _ := m.MarshalBinary()
|
||||
tc.expected.Write(buf)
|
||||
if tc.String() != tc.expected.String() {
|
||||
tc.t.Errorf("Client buffer mismatch. Wanted %#v, got %#v", tc.expected.String(), tc.String())
|
||||
}
|
||||
tc.Reset()
|
||||
tc.expected.Reset()
|
||||
}
|
||||
|
||||
func NewTestingRepeater() *Repeater {
|
||||
return &Repeater{
|
||||
clock: FakeClock{},
|
||||
writers: make([]io.Writer, 0, 2),
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepeater(t *testing.T) {
|
||||
r := NewRepeater()
|
||||
m := TestMessage{Message{1, 3, []uint8{3, 4}}}
|
||||
r := NewTestingRepeater()
|
||||
|
||||
buf1 := bytes.NewBufferString("buf1")
|
||||
buf1Expect := bytes.NewBufferString("buf1")
|
||||
r.Join(buf1)
|
||||
if r.Listeners() != 1 {
|
||||
t.Error("Joining did nothing")
|
||||
}
|
||||
r.Send(m.Message)
|
||||
m.Clients = 1
|
||||
buf1Expect.Write(m.bytes())
|
||||
if buf1.String() != buf1Expect.String() {
|
||||
t.Error("Client 1 not repeating", buf1)
|
||||
}
|
||||
c1 := NewTestingClient(t)
|
||||
r.Join(c1)
|
||||
c1.Expect(1)
|
||||
|
||||
buf2 := bytes.NewBufferString("buf2")
|
||||
buf2Expect := bytes.NewBufferString("buf2")
|
||||
r.Join(buf2)
|
||||
r.Send(m.Message)
|
||||
m.Clients = 2
|
||||
buf1Expect.Write(m.bytes())
|
||||
buf2Expect.Write(m.bytes())
|
||||
if buf1.String() != buf1Expect.String() {
|
||||
t.Errorf("Client 1 not repeating %#v %#v", buf1, buf1Expect)
|
||||
}
|
||||
if buf2.String() != buf2Expect.String() {
|
||||
t.Error("Client 2 not repeating", buf2)
|
||||
}
|
||||
r.SendMessage(15 * time.Millisecond)
|
||||
c1.Expect(1, 15)
|
||||
|
||||
r.Part(buf1)
|
||||
r.Send(m.Message)
|
||||
m.Clients = 1
|
||||
buf2Expect.Write(m.bytes())
|
||||
if buf1.String() != buf1Expect.String() {
|
||||
t.Error("Client 1 still getting data after part", buf1)
|
||||
}
|
||||
if buf2.String() != buf2Expect.String() {
|
||||
t.Error("Client 2 not getting data after part", buf2)
|
||||
c2 := NewTestingClient(t)
|
||||
r.Join(c2)
|
||||
c1.Expect(2)
|
||||
c2.Expect(2)
|
||||
|
||||
r.SendMessage(58 * time.Millisecond)
|
||||
c1.Expect(2, 58)
|
||||
c2.Expect(2, 58)
|
||||
|
||||
r.Part(c1)
|
||||
c2.Expect(1)
|
||||
|
||||
r.SendMessage(5 * time.Millisecond)
|
||||
c2.Expect(1, 5)
|
||||
if c1.Len() > 0 {
|
||||
t.Error("Client 1 still getting data after part")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue