route/lib/tunnel/tunneltest/state_recorder.go

119 lines
2.1 KiB
Go
Raw Normal View History

2017-01-20 01:27:14 +00:00
package tunneltest
import (
"bytes"
"fmt"
"sync"
"time"
"git.xeserv.us/xena/route/lib/tunnel"
)
var (
recWaitTimeout = 5 * time.Second
recBuffer = 32
)
// States is a sequence of client state changes.
type States []*tunnel.ClientStateChange
func (s States) String() string {
if len(s) == 0 {
return ""
}
var buf bytes.Buffer
fmt.Fprintf(&buf, "[%s", s[0].String())
for _, s := range s[1:] {
fmt.Fprintf(&buf, ",%s", s.String())
}
buf.WriteRune(']')
return buf.String()
}
// StateRecorder saves state changes pushed to StateRecorder.C().
type StateRecorder struct {
mu sync.Mutex
ch chan *tunnel.ClientStateChange
recorded []*tunnel.ClientStateChange
offset int
}
func NewStateRecorder() *StateRecorder {
rec := &StateRecorder{
ch: make(chan *tunnel.ClientStateChange, recBuffer),
}
go rec.record()
return rec
}
func (rec *StateRecorder) record() {
for state := range rec.ch {
rec.mu.Lock()
rec.recorded = append(rec.recorded, state)
rec.mu.Unlock()
}
}
func (rec *StateRecorder) C() chan<- *tunnel.ClientStateChange {
return rec.ch
}
func (rec *StateRecorder) WaitTransitions(states ...tunnel.ClientState) error {
from := states[0]
for _, to := range states[1:] {
if err := rec.WaitTransition(from, to); err != nil {
return err
}
from = to
}
return nil
}
func (rec *StateRecorder) WaitTransition(from, to tunnel.ClientState) error {
timeout := time.After(recWaitTimeout)
var lastStates []*tunnel.ClientStateChange
for {
select {
case <-timeout:
return fmt.Errorf("timed out waiting for %s->%s transition: %v", from, to, States(lastStates))
default:
time.Sleep(50 * time.Millisecond)
lastStates = rec.States()[rec.offset:]
for i, state := range lastStates {
if from != 0 && state.Previous != from {
continue
}
if to != 0 && state.Current != to {
continue
}
rec.offset += i
return nil
}
}
}
}
func (rec *StateRecorder) States() []*tunnel.ClientStateChange {
rec.mu.Lock()
defer rec.mu.Unlock()
states := make([]*tunnel.ClientStateChange, len(rec.recorded))
copy(states, rec.recorded)
return states
}