From 5a687c8f4acf67811794ccb2ed322913b70a2757 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Thu, 26 Nov 2015 15:12:03 +0100 Subject: [PATCH] updated tests --- rpc/v2/json.go | 3 +- rpc/v2/json_test.go | 4 +- rpc/v2/server.go | 8 +- rpc/v2/server_test.go | 201 ++++++++++++++++++++++++++++++++++++++++- rpc/v2/types.go | 21 +++-- whisper/whisper_rpc.go | 4 +- 6 files changed, 221 insertions(+), 20 deletions(-) diff --git a/rpc/v2/json.go b/rpc/v2/json.go index fc0e03b863..54f2899e26 100644 --- a/rpc/v2/json.go +++ b/rpc/v2/json.go @@ -23,8 +23,9 @@ import ( "reflect" "strings" "sync/atomic" - "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" ) const ( diff --git a/rpc/v2/json_test.go b/rpc/v2/json_test.go index dc80a863ff..dc8a345d7f 100644 --- a/rpc/v2/json_test.go +++ b/rpc/v2/json_test.go @@ -23,7 +23,7 @@ func TestJSONRequestParsing(t *testing.T) { t.Fatalf("%v", err) } - req := bytes.NewBufferString(`{"id": 1234, "jsonrpc": "2.0", "method": "calc_Add", "params": [11, 22]}`) + req := bytes.NewBufferString(`{"id": 1234, "jsonrpc": "2.0", "method": "calc_add", "params": [11, 22]}`) var str string reply := bytes.NewBufferString(str) rw := &RWC{bufio.NewReadWriter(bufio.NewReader(req), bufio.NewWriter(reply))} @@ -47,7 +47,7 @@ func TestJSONRequestParsing(t *testing.T) { t.Fatalf("Expected service 'calc' but got '%s'", requests[0].service) } - if requests[0].method != "Add" { + if requests[0].method != "add" { t.Fatalf("Expected method 'Add' but got '%s'", requests[0].method) } diff --git a/rpc/v2/server.go b/rpc/v2/server.go index b8414acee4..2fd173c220 100644 --- a/rpc/v2/server.go +++ b/rpc/v2/server.go @@ -20,16 +20,19 @@ import ( "fmt" "reflect" + "runtime" + "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" - "runtime" ) // NewServer will create a new server instance with no registered handlers. func NewServer() *Server { server := &Server{services: make(serviceRegistry), subscriptions: make(subscriptionRegistry)} + // register a default service which will provide meta information about the RPC service such as the services and + // methods it offers. rpcService := &RPCService{server} server.RegisterName("rpc", rpcService) @@ -87,6 +90,9 @@ func (s *Server) register(rcvr interface{}, name string, useName bool) error { if !isExported(sname) && !useName { return fmt.Errorf("%s is not exported", sname) } + if !useName { + sname = formatName(sname) + } // already a previous service register under given sname, merge methods/subscriptions if regsvc, present := s.services[sname]; present { diff --git a/rpc/v2/server_test.go b/rpc/v2/server_test.go index 1ca954923f..ea08c6ccc9 100644 --- a/rpc/v2/server_test.go +++ b/rpc/v2/server_test.go @@ -1,6 +1,12 @@ package v2 -import "testing" +import ( + "encoding/json" + "fmt" + "reflect" + "testing" + "time" +) type Service struct{} @@ -11,7 +17,14 @@ type Args struct { func (s *Service) NoArgsRets() { } -func (s *Service) Args(str string, i int, args *Args) { +type Result struct { + String string + Int int + Args *Args +} + +func (s *Service) Echo(str string, i int, args *Args) Result { + return Result{str, i, args} } func (s *Service) Rets() (string, error) { @@ -30,16 +43,46 @@ func (s *Service) InvalidRets3() (string, string, error) { return "", "", nil } +func (s *Service) Subscription() (Subscription, error) { + return NewSubscription(nil), nil +} + func TestServerRegister(t *testing.T) { server := NewServer() service := new(Service) + if err := server.Register(service); err != nil { + t.Fatalf("%v", err) + } + + if len(server.services) != 2 { + t.Fatalf("Expected 2 services entries, got %d", len(server.services)) + } + + svc, ok := server.services["service"] + if !ok { + t.Fatalf("Unable to locate 'service' service after registration") + } + + if len(svc.callbacks) != 3 { + t.Errorf("Expected 3 callbacks for service 'calc', got %d", len(svc.callbacks)) + } + + if len(svc.subscriptions) != 1 { + t.Errorf("Expected 1 subscription for service 'calc', got %d", len(svc.subscriptions)) + } +} + +func TestServerRegisterName(t *testing.T) { + server := NewServer() + service := new(Service) + if err := server.RegisterName("calc", service); err != nil { t.Fatalf("%v", err) } - if len(server.services) != 1 { - t.Fatalf("Expected 1 service entry but got %d", len(server.services)) + if len(server.services) != 2 { + t.Fatalf("Expected 2 service entries, got %d", len(server.services)) } svc, ok := server.services["calc"] @@ -48,6 +91,154 @@ func TestServerRegister(t *testing.T) { } if len(svc.callbacks) != 3 { - t.Fatalf("Expected 3 callbacks for service 'calc', got %d", len(svc.callbacks)) + t.Errorf("Expected 3 callbacks for service 'calc', got %d", len(svc.callbacks)) + } + + if len(svc.subscriptions) != 1 { + t.Errorf("Expected 1 subscription for service 'calc', got %d", len(svc.subscriptions)) + } +} + +// dummy codec used for testing RPC method execution +type ServerTestCodec struct { + counter int + input []byte + output string + closer chan interface{} +} + +func (c *ServerTestCodec) ReadRequestHeaders() ([]rpcRequest, bool, RPCError) { + c.counter += 1 + + if c.counter == 1 { + var req jsonRequest + json.Unmarshal(c.input, &req) + return []rpcRequest{rpcRequest{id: req.Id, isPubSub: false, service: "test", method: req.Method, params: req.Payload}}, false, nil + } + + // requests are executes in parallel, wait a bit before returning an error so that the previous request has time to + // be executed + timer := time.NewTimer(time.Duration(2) * time.Second) + <-timer.C + + return nil, false, &invalidRequestError{"connection closed"} +} + +func (c *ServerTestCodec) ParseRequestArguments(argTypes []reflect.Type, payload interface{}) ([]reflect.Value, RPCError) { + + args, _ := payload.(json.RawMessage) + + argValues := make([]reflect.Value, len(argTypes)) + params := make([]interface{}, len(argTypes)) + + n, err := countArguments(args) + if err != nil { + return nil, &invalidParamsError{err.Error()} + } + if n != len(argTypes) { + return nil, &invalidParamsError{fmt.Sprintf("insufficient params, want %d have %d", len(argTypes), n)} + + } + + for i, t := range argTypes { + if t.Kind() == reflect.Ptr { + // values must be pointers for the Unmarshal method, reflect. + // Dereference otherwise reflect.New would create **SomeType + argValues[i] = reflect.New(t.Elem()) + params[i] = argValues[i].Interface() + + // when not specified blockNumbers are by default latest (-1) + if blockNumber, ok := params[i].(*BlockNumber); ok { + *blockNumber = BlockNumber(-1) + } + } else { + argValues[i] = reflect.New(t) + params[i] = argValues[i].Interface() + + // when not specified blockNumbers are by default latest (-1) + if blockNumber, ok := params[i].(*BlockNumber); ok { + *blockNumber = BlockNumber(-1) + } + } + } + + if err := json.Unmarshal(args, ¶ms); err != nil { + return nil, &invalidParamsError{err.Error()} + } + + // Convert pointers back to values where necessary + for i, a := range argValues { + if a.Kind() != argTypes[i].Kind() { + argValues[i] = reflect.Indirect(argValues[i]) + } + } + + return argValues, nil +} + +func (c *ServerTestCodec) CreateResponse(id int64, reply interface{}) interface{} { + return &jsonSuccessResponse{Version: jsonRPCVersion, Id: id, Result: reply} +} + +func (c *ServerTestCodec) CreateErrorResponse(id *int64, err RPCError) interface{} { + return &jsonErrResponse{Version: jsonRPCVersion, Id: id, Error: jsonError{Code: err.Code(), Message: err.Error()}} +} + +func (c *ServerTestCodec) CreateErrorResponseWithInfo(id *int64, err RPCError, info interface{}) interface{} { + return &jsonErrResponse{Version: jsonRPCVersion, Id: id, + Error: jsonError{Code: err.Code(), Message: err.Error(), Data: info}} +} + +func (c *ServerTestCodec) CreateNotification(subid string, event interface{}) interface{} { + return &jsonNotification{Version: jsonRPCVersion, Method: notificationMethod, + Params: jsonSubscription{Subscription: subid, Result: event}} +} + +func (c *ServerTestCodec) Write(msg interface{}) error { + if len(c.output) == 0 { // only capture first response + if o, err := json.Marshal(msg); err != nil { + return err + } else { + c.output = string(o) + } + } + + return nil +} + +func (c *ServerTestCodec) Close() { + close(c.closer) +} + +func (c *ServerTestCodec) Closed() <-chan interface{} { + return c.closer +} + +func TestServerMethodExecution(t *testing.T) { + server := NewServer() + service := new(Service) + + if err := server.RegisterName("test", service); err != nil { + t.Fatalf("%v", err) + } + + req := jsonRequest{ + Method: "echo", + Version: "2.0", + Id: 12345, + } + args := []interface{}{"string arg", 1122, &Args{"qwerty"}} + req.Payload, _ = json.Marshal(&args) + + input, _ := json.Marshal(&req) + codec := &ServerTestCodec{input: input, closer: make(chan interface{})} + go server.ServeCodec(codec) + + <-codec.closer + + expected := `{"jsonrpc":"2.0","id":12345,"result":{"String":"string arg","Int":1122,"Args":{"S":"qwerty"}}}` + + if expected != codec.output { + t.Fatalf("expected %s, got %s\n", expected, codec.output) } } diff --git a/rpc/v2/types.go b/rpc/v2/types.go index 38fd5f4d5f..7ab3d91cac 100644 --- a/rpc/v2/types.go +++ b/rpc/v2/types.go @@ -190,8 +190,8 @@ func NewHexNumber(val interface{}) *HexNumber { func (h *HexNumber) UnmarshalJSON(input []byte) error { length := len(input) - if length >= 2 && input[0] == '"' && input[length - 1] == '"' { - input = input[1 : length - 1] + if length >= 2 && input[0] == '"' && input[length-1] == '"' { + input = input[1 : length-1] } hn := (*big.Int)(h) @@ -239,11 +239,12 @@ func (h *HexNumber) BigInt() *big.Int { } type Number int64 + func (n *Number) UnmarshalJSON(data []byte) error { input := strings.TrimSpace(string(data)) - if len(input) >= 2 && input[0] == '"' && input[len(input) - 1] == '"' { - input = input[1 : len(input) - 1] + if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' { + input = input[1 : len(input)-1] } if len(input) == 0 { @@ -271,17 +272,17 @@ func (n *Number) Int64() int64 { } var ( - pendingBlockNumber = big.NewInt(-2) - latestBlockNumber = big.NewInt(-1) + pendingBlockNumber = big.NewInt(-2) + latestBlockNumber = big.NewInt(-1) earliestBlockNumber = big.NewInt(0) - maxBlockNumber = big.NewInt(math.MaxInt64) + maxBlockNumber = big.NewInt(math.MaxInt64) ) type BlockNumber int64 const ( PendingBlockNumber = BlockNumber(-2) - LatestBlockNumber = BlockNumber(-1) + LatestBlockNumber = BlockNumber(-1) ) // UnmarshalJSON parses the given JSON fragement into a BlockNumber. It supports: @@ -294,8 +295,8 @@ const ( func (bn *BlockNumber) UnmarshalJSON(data []byte) error { input := strings.TrimSpace(string(data)) - if len(input) >= 2 && input[0] == '"' && input[len(input) - 1] == '"' { - input = input[1 : len(input) - 1] + if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' { + input = input[1 : len(input)-1] } if len(input) == 0 { diff --git a/whisper/whisper_rpc.go b/whisper/whisper_rpc.go index 7efe10cbb1..4f637e4367 100644 --- a/whisper/whisper_rpc.go +++ b/whisper/whisper_rpc.go @@ -102,7 +102,9 @@ func (s *WhisperService) GetFilterChanges(filterId rpc.HexNumber) []WhisperMessa defer s.messagesMu.RUnlock() if s.messages[filterId.Int()] != nil { - return s.messages[filterId.Int()].retrieve() + if changes := s.messages[filterId.Int()].retrieve(); changes != nil { + return changes + } } return returnWhisperMessages(nil) }