Skip to content

Commit

Permalink
Add support for mocking socket timeouts (#104)
Browse files Browse the repository at this point in the history
* Add support for mocking socket timeouts

* return errors from stop calls

* exercise error case
  • Loading branch information
mkenney authored Jun 2, 2018
1 parent 4593ef0 commit 589826c
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 21 deletions.
8 changes: 1 addition & 7 deletions test/issue-101/Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/issue-101/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func main() {
fmt.Printf(".")
time.Sleep(1 * time.Second)
cnt++
if cnt > 10 {
if cnt > 5 {
break
}
}
Expand Down
3 changes: 2 additions & 1 deletion tot/mock.socket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ func (socket *MockSocket) SendCommand(command socket.Commander) chan *socket.Res
/*
Stop is a Socketer implementation.
*/
func (socket *MockSocket) Stop() {
func (socket *MockSocket) Stop() error {
return nil
}

/*
Expand Down
2 changes: 1 addition & 1 deletion tot/socket/interface.socketer.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type Socketer interface {

// Stop signals the socket read loop to stop listening for data and close
// the websocket connection.
Stop()
Stop() error

// URL returns the URL of the websocket connection.
URL() *url.URL
Expand Down
30 changes: 28 additions & 2 deletions tot/socket/mock.web_socketer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,19 @@ func NewMockWebsocket(socketURL *url.URL) (WebSocketer, error) {

type MockChromeWebSocket struct {
mockResponses []*Response
sleep time.Duration
}

func (socket *MockChromeWebSocket) Close() error { return nil }
func (socket *MockChromeWebSocket) Close() error {
socket.mockResponses = []*Response{
&Response{},
&Response{},
&Response{},
&Response{},
&Response{},
}
return nil
}

/*
This method populates a queue of mock data that will be delivered to the
Expand All @@ -47,6 +57,11 @@ func (socket *MockChromeWebSocket) ReadJSON(v interface{}) error {
var data interface{}
time.Sleep(time.Millisecond * 10)

if socket.sleep > 0 {
time.Sleep(socket.sleep)
socket.sleep = 0
}

if len(socket.mockResponses) > 0 {
data = socket.mockResponses[0]
socket.mockResponses = socket.mockResponses[1:]
Expand All @@ -61,7 +76,18 @@ func (socket *MockChromeWebSocket) ReadJSON(v interface{}) error {
jsonBytes, err := json.Marshal(data)
log.Debugf("Mock ReadJSON(): returning mock data %s", jsonBytes)
err = json.Unmarshal(jsonBytes, &v)
return errs.Wrap(err, fmt.Sprintf("could not unmarshal %s", jsonBytes))
if nil != err {
return errs.Wrap(err, fmt.Sprintf("could not unmarshal %s", jsonBytes))
}
return nil
}

/*
Sleep sets the sleep duration for the next ReadJSON call to replicate
timeouts and delays.
*/
func (socket *MockChromeWebSocket) Sleep(duration time.Duration) {
socket.sleep = duration
}

/*
Expand Down
7 changes: 5 additions & 2 deletions tot/socket/socket.conner.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,14 @@ func (socket *Socket) Disconnect() error {
socket.Stop()
err := socket.conn.Close()
if nil != err {
err = errs.Wrap(err, "disconnect failed")
socket.listenErr.With(err)
}
socket.conn = nil
socket.connected = false
return err
if 0 == len(socket.listenErr) {
return nil
}
return socket.listenErr
}

/*
Expand Down
23 changes: 18 additions & 5 deletions tot/socket/socket.socketer.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,19 @@ func NextSocketID() int {
Socket is a Socketer implementation.
*/
type Socket struct {
commands CommandMapper
commandID int
commandIDMux *sync.Mutex
commands CommandMapper
conn WebSocketer
connected bool
handlers EventHandlerMapper
listenCh chan bool
newSocket func(socketURL *url.URL) (WebSocketer, error)
url *url.URL
socketID int
listenErr errs.Err
listening bool
mux *sync.Mutex
newSocket func(socketURL *url.URL) (WebSocketer, error)
socketID int
url *url.URL

// Protocol interfaces for the API.
accessibility *AccessibilityProtocol
Expand Down Expand Up @@ -302,6 +303,14 @@ func (socket *Socket) listen() error {
err = socket.ReadJSON(&response)
if nil != err {
log.Errorf("socket #%d - %v", socket.socketID, err)
err = errs.Wrap(err, fmt.Sprintf("socket #%d - socket read failed", socket.socketID))
socket.listenErr = err.(errs.Err).With(socket.listenErr)
}
if 0 == response.ID &&
"" == response.Method &&
0 == len(response.Params) &&
0 == len(response.Result) {
log.Errorf("socket #%d - nil response from socket", socket.socketID)
}

if response.ID > 0 {
Expand Down Expand Up @@ -453,7 +462,7 @@ websocket connection.
Stop is a Socketer implementation.
*/
func (socket *Socket) Stop() {
func (socket *Socket) Stop() error {
if socket.listening {
socket.listening = false
select {
Expand All @@ -463,6 +472,10 @@ func (socket *Socket) Stop() {
}
log.Debugf("socket #%d: socket stopped", socket.socketID)
}
if 0 == len(socket.listenErr) {
return nil
}
return socket.listenErr
}

/*
Expand Down
42 changes: 40 additions & 2 deletions tot/socket/socket.socketer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"net/url"
"reflect"
"testing"
"time"
)

func TestNewSocket(t *testing.T) {
Expand All @@ -14,12 +15,49 @@ func TestNewSocket(t *testing.T) {
}
}

func TestCommandNotFound(t *testing.T) {
socketURL, _ := url.Parse("https://test:9222/TestCommandNotFound")
mockSocket := NewMock(socketURL)
mockSocket.Listen()
defer mockSocket.Stop()
mockSocket.Conn().(*MockChromeWebSocket).AddMockData(&Response{
ID: 999,
Error: &Error{},
Method: "Some.methodError",
Result: []byte(`"Mock Command Result"`),
})
time.Sleep(1 * time.Second)
}

func TestSocketStop(t *testing.T) {
socketURL, _ := url.Parse("https://test:9222/TestSocketStop")
mockSocket := NewMock(socketURL)
mockSocket.Listen()
time.Sleep(1 * time.Second)
if err := mockSocket.Stop(); nil != err {
t.Errorf("Expected nil, got error: %v", err)
}
}

func TestSocketDisconnect(t *testing.T) {
socketURL, _ := url.Parse("https://test:9222/TestSocketDisconnect")
socket := NewMock(socketURL)
if err := socket.Disconnect(); nil != err && "*errors.errorString" != reflect.TypeOf(err).String() {
mockSocket := NewMock(socketURL)
if err := mockSocket.Disconnect(); nil != err && "*errors.errorString" != reflect.TypeOf(err).String() && "errors.Err" != reflect.TypeOf(err).String() {
t.Errorf("Socketer.Disconnect() must return an error or nil, %s found", reflect.TypeOf(err).String())
}

// Test the disconnect timeout
mockSocket = NewMock(socketURL)
mockSocket.Listen()
mockSocket.Conn().(*MockChromeWebSocket).Sleep(10 * time.Second)
start := time.Now()
if err := mockSocket.Disconnect(); nil != err && "*errors.errorString" != reflect.TypeOf(err).String() && "errors.Err" != reflect.TypeOf(err).String() {
t.Errorf("Socketer.Disconnect() must return an error or nil, %s found", reflect.TypeOf(err).String())
}
elapsed := time.Since(start)
if elapsed < 1*time.Second {
t.Errorf("Expected disconnect timeout in 1 seconds, %s elapsed", elapsed)
}
}

func TestListenCommand(t *testing.T) {
Expand Down

0 comments on commit 589826c

Please sign in to comment.