119 lines
2.1 KiB
Go
119 lines
2.1 KiB
Go
|
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
|
||
|
}
|