summaryrefslogtreecommitdiff
path: root/dummyNetConn.go
diff options
context:
space:
mode:
authorjEzEk <[email protected]>2018-10-22 19:12:22 +0200
committerjEzEk <[email protected]>2018-10-25 18:33:32 +0200
commit94eccf489f8ffe493c8a6b98571a207e62663639 (patch)
tree1b37b7be7fb02f16e389c6a71099647d05c8a1ac /dummyNetConn.go
parenta2583299cf9dcb5b8e59d636b0aa8341fab9a3af (diff)
test on dummy X un/checked with/out reply,refactor
Diffstat (limited to 'dummyNetConn.go')
-rw-r--r--dummyNetConn.go261
1 files changed, 261 insertions, 0 deletions
diff --git a/dummyNetConn.go b/dummyNetConn.go
new file mode 100644
index 0000000..91bae4f
--- /dev/null
+++ b/dummyNetConn.go
@@ -0,0 +1,261 @@
+package xgb
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "net"
+ "time"
+)
+
+type dAddr struct {
+ s string
+}
+
+func (_ dAddr) Network() string { return "dummy" }
+func (a dAddr) String() string { return a.s }
+
+var (
+ dNCErrNotImplemented = errors.New("command not implemented")
+ dNCErrClosed = errors.New("server closed")
+ dNCErrWrite = errors.New("server write failed")
+ dNCErrRead = errors.New("server read failed")
+ dNCErrResponse = errors.New("server response error")
+)
+
+type dNCIoResult struct {
+ n int
+ err error
+}
+type dNCIo struct {
+ b []byte
+ result chan dNCIoResult
+}
+
+type dNCCWriteLock struct{}
+type dNCCWriteUnlock struct{}
+type dNCCWriteError struct{}
+type dNCCWriteSuccess struct{}
+type dNCCReadLock struct{}
+type dNCCReadUnlock struct{}
+type dNCCReadError struct{}
+type dNCCReadSuccess struct{}
+
+// dummy net.Conn interface. Needs to be constructed via newDummyNetConn([...]) function.
+type dNC struct {
+ reply func([]byte) []byte
+ addr dAddr
+ in, out chan dNCIo
+ control chan interface{}
+ done chan struct{}
+}
+
+// Results running dummy server, satisfying net.Conn interface for test purposes.
+// 'name' parameter will be returned via (*dNC).Local/RemoteAddr().String()
+// 'reply' parameter function will be runned only on successful (*dNC).Write(b) with 'b' as parameter to 'reply'. The result will be stored in internal buffer and can be retrieved later via (*dNC).Read([...]) method.
+// It is users responsibility to stop and clean up resources with (*dNC).Close, if not needed anymore.
+// By default, the (*dNC).Write([...]) and (*dNC).Read([...]) methods are unlocked and will not result in error.
+//TODO make (*dNC).SetDeadline, (*dNC).SetReadDeadline, (*dNC).SetWriteDeadline work proprely.
+func newDummyNetConn(name string, reply func([]byte) []byte) *dNC {
+
+ s := &dNC{
+ reply,
+ dAddr{name},
+ make(chan dNCIo), make(chan dNCIo),
+ make(chan interface{}),
+ make(chan struct{}),
+ }
+
+ in, out := s.in, chan dNCIo(nil)
+ buf := &bytes.Buffer{}
+ errorRead, errorWrite := false, false
+ lockRead := false
+
+ go func() {
+ defer close(s.done)
+ for {
+ select {
+ case dxsio := <-in:
+ if errorWrite {
+ dxsio.result <- dNCIoResult{0, dNCErrWrite}
+ break
+ }
+
+ response := s.reply(dxsio.b)
+
+ buf.Write(response)
+ dxsio.result <- dNCIoResult{len(dxsio.b), nil}
+
+ if !lockRead && buf.Len() > 0 && out == nil {
+ out = s.out
+ }
+ case dxsio := <-out:
+ if errorRead {
+ dxsio.result <- dNCIoResult{0, dNCErrRead}
+ break
+ }
+
+ n, err := buf.Read(dxsio.b)
+ dxsio.result <- dNCIoResult{n, err}
+
+ if buf.Len() == 0 {
+ out = nil
+ }
+ case ci := <-s.control:
+ if ci == nil {
+ return
+ }
+ switch ci.(type) {
+ case dNCCWriteLock:
+ in = nil
+ case dNCCWriteUnlock:
+ in = s.in
+ case dNCCWriteError:
+ errorWrite = true
+ case dNCCWriteSuccess:
+ errorWrite = false
+ case dNCCReadLock:
+ out = nil
+ lockRead = true
+ case dNCCReadUnlock:
+ lockRead = false
+ if buf.Len() > 0 && out == nil {
+ out = s.out
+ }
+ case dNCCReadError:
+ errorRead = true
+ case dNCCReadSuccess:
+ errorRead = false
+ default:
+ }
+ }
+ }
+ }()
+ return s
+}
+
+// Shuts down dummy net.Conn server. Every blocking or future method calls will do nothing and result in error.
+// Result will be dNCErrClosed if server was allready closed.
+// Server can not be unclosed.
+func (s *dNC) Close() error {
+ select {
+ case s.control <- nil:
+ <-s.done
+ return nil
+ case <-s.done:
+ }
+ return dNCErrClosed
+}
+
+// Performs a write action to server.
+// If not locked by (*dNC).WriteLock, it results in error or success. If locked, this method will block until unlocked, or closed.
+//
+// This method can be set to result in error or success, via (*dNC).WriteError() or (*dNC).WriteSuccess() methods.
+//
+// If setted to result in error, the 'reply' function will NOT be called and internal buffer will NOT increasethe.
+// Result will be (0, dNCErrWrite).
+//
+// If setted to result in success, the 'reply' function will be called and its result will be writen to internal buffer.
+// If there is something in the internal buffer, the (*dNC).Read([...]) will be unblocked (if not previously locked with (*dNC).ReadLock).
+// Result will be (len(b), nil)
+//
+// If server was closed previously, result will be (0, dNCErrClosed).
+func (s *dNC) Write(b []byte) (int, error) {
+ resChan := make(chan dNCIoResult)
+ select {
+ case s.in <- dNCIo{b, resChan}:
+ res := <-resChan
+ return res.n, res.err
+ case <-s.done:
+ }
+ return 0, dNCErrClosed
+}
+
+// Performs a read action from server.
+// If locked by (*dNC).ReadLock(), this method will block until unlocked with (*dNC).ReadUnlock(), or server closes.
+//
+// If not locked, this method can be setted to result imidiatly in error, will block if internal buffer is empty or will perform an read operation from internal buffer.
+//
+// If setted to result in error via (*dNC).ReadError(), the result will be (0, dNCErrWrite).
+//
+// If not locked and not setted to result in error via (*dNC).ReadSuccess(), this method will block until internall buffer is not empty, than it returns the result of the buffer read operation via (*bytes.Buffer).Read([...]).
+// If the internal buffer is empty after this method, all follwing (*dNC).Read([...]), requests will block until internall buffer is filled after successful write requests.
+//
+// If server was closed previously, result will be (0, io.EOF).
+func (s *dNC) Read(b []byte) (int, error) {
+ resChan := make(chan dNCIoResult)
+ select {
+ case s.out <- dNCIo{b, resChan}:
+ res := <-resChan
+ return res.n, res.err
+ case <-s.done:
+ }
+ return 0, io.EOF
+}
+func (s *dNC) LocalAddr() net.Addr { return s.addr }
+func (s *dNC) RemoteAddr() net.Addr { return s.addr }
+func (s *dNC) SetDeadline(t time.Time) error { return dNCErrNotImplemented }
+func (s *dNC) SetReadDeadline(t time.Time) error { return dNCErrNotImplemented }
+func (s *dNC) SetWriteDeadline(t time.Time) error { return dNCErrNotImplemented }
+
+func (s *dNC) Control(i interface{}) error {
+ select {
+ case s.control <- i:
+ return nil
+ case <-s.done:
+ }
+ return dNCErrClosed
+}
+
+// Locks writing. All write requests will be blocked until write is unlocked with (*dNC).WriteUnlock, or server closes.
+func (s *dNC) WriteLock() error {
+ return s.Control(dNCCWriteLock{})
+}
+
+// Unlocks writing. All blocked write requests until now will be accepted.
+func (s *dNC) WriteUnlock() error {
+ return s.Control(dNCCWriteUnlock{})
+}
+
+// Unlocks writing and makes (*dNC).Write to result (0, dNCErrWrite).
+func (s *dNC) WriteError() error {
+ if err := s.WriteUnlock(); err != nil {
+ return err
+ }
+ return s.Control(dNCCWriteError{})
+}
+
+// Unlocks writing and makes (*dNC).Write([...]) not result in error. See (*dNC).Write for details.
+func (s *dNC) WriteSuccess() error {
+ if err := s.WriteUnlock(); err != nil {
+ return err
+ }
+ return s.Control(dNCCWriteSuccess{})
+}
+
+// Locks reading. All read requests will be blocked until read is unlocked with (*dNC).ReadUnlock, or server closes.
+// (*dNC).Read([...]) wil block even after successful write.
+func (s *dNC) ReadLock() error {
+ return s.Control(dNCCReadLock{})
+}
+
+// Unlocks reading. If the internall buffer is not empty, next read will not block.
+func (s *dNC) ReadUnlock() error {
+ return s.Control(dNCCReadUnlock{})
+}
+
+// Unlocks read and makes every blocked and following (*dNC).Read([...]) imidiatly result in error. See (*dNC).Read for details.
+func (s *dNC) ReadError() error {
+ if err := s.ReadUnlock(); err != nil {
+ return err
+ }
+ return s.Control(dNCCReadError{})
+}
+
+// Unlocks read and makes every blocked and following (*dNC).Read([...]) requests be handled, if according to internal buffer. See (*dNC).Read for details.
+func (s *dNC) ReadSuccess() error {
+ if err := s.ReadUnlock(); err != nil {
+ return err
+ }
+ return s.Control(dNCCReadSuccess{})
+}