| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package rpc | |
| import ( | |
| "http" | |
| "log" | |
| "net" | |
| "once" | |
| "os" | |
| "strings" | |
| "testing" | |
| ) | |
| var serverAddr string | |
| var httpServerAddr string | |
| const second = 1e9 | |
| type Args struct { | |
| A, B int | |
| } | |
| type Reply struct { | |
| C int | |
| } | |
| type Arith int | |
| func (t *Arith) Add(args *Args, reply *Reply) os.Error { | |
| reply.C = args.A + args.B | |
| return nil | |
| } | |
| func (t *Arith) Mul(args *Args, reply *Reply) os.Error { | |
| reply.C = args.A * args.B | |
| return nil | |
| } | |
| func (t *Arith) Div(args *Args, reply *Reply) os.Error { | |
| if args.B == 0 { | |
| return os.ErrorString("divide by zero") | |
| } | |
| reply.C = args.A / args.B | |
| return nil | |
| } | |
| func (t *Arith) Error(args *Args, reply *Reply) os.Error { | |
| panic("ERROR") | |
| } | |
| func startServer() { | |
| Register(new(Arith)) | |
| l, e := net.Listen("tcp", ":0") // any available address | |
| if e != nil { | |
| log.Exitf("net.Listen tcp :0: %v", e) | |
| } | |
| serverAddr = l.Addr().String() | |
| log.Stderr("Test RPC server listening on ", serverAddr) | |
| go Accept(l) | |
| HandleHTTP() | |
| l, e = net.Listen("tcp", ":0") // any available address | |
| if e != nil { | |
| log.Stderrf("net.Listen tcp :0: %v", e) | |
| os.Exit(1) | |
| } | |
| httpServerAddr = l.Addr().String() | |
| log.Stderr("Test HTTP RPC server listening on ", httpServerAddr) | |
| go http.Serve(l, nil) | |
| } | |
| func TestRPC(t *testing.T) { | |
| once.Do(startServer) | |
| client, err := Dial("tcp", serverAddr) | |
| if err != nil { | |
| t.Fatal("dialing", err) | |
| } | |
| // Synchronous calls | |
| args := &Args{7, 8} | |
| reply := new(Reply) | |
| err = client.Call("Arith.Add", args, reply) | |
| if err != nil { | |
| t.Errorf("Add: expected no error but got string %q", err.String()) | |
| } | |
| if reply.C != args.A+args.B { | |
| t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B) | |
| } | |
| args = &Args{7, 8} | |
| reply = new(Reply) | |
| err = client.Call("Arith.Mul", args, reply) | |
| if err != nil { | |
| t.Errorf("Mul: expected no error but got string %q", err.String()) | |
| } | |
| if reply.C != args.A*args.B { | |
| t.Errorf("Mul: expected %d got %d", reply.C, args.A*args.B) | |
| } | |
| // Out of order. | |
| args = &Args{7, 8} | |
| mulReply := new(Reply) | |
| mulCall := client.Go("Arith.Mul", args, mulReply, nil) | |
| addReply := new(Reply) | |
| addCall := client.Go("Arith.Add", args, addReply, nil) | |
| addCall = <-addCall.Done | |
| if addCall.Error != nil { | |
| t.Errorf("Add: expected no error but got string %q", addCall.Error.String()) | |
| } | |
| if addReply.C != args.A+args.B { | |
| t.Errorf("Add: expected %d got %d", addReply.C, args.A+args.B) | |
| } | |
| mulCall = <-mulCall.Done | |
| if mulCall.Error != nil { | |
| t.Errorf("Mul: expected no error but got string %q", mulCall.Error.String()) | |
| } | |
| if mulReply.C != args.A*args.B { | |
| t.Errorf("Mul: expected %d got %d", mulReply.C, args.A*args.B) | |
| } | |
| // Error test | |
| args = &Args{7, 0} | |
| reply = new(Reply) | |
| err = client.Call("Arith.Div", args, reply) | |
| // expect an error: zero divide | |
| if err == nil { | |
| t.Error("Div: expected error") | |
| } else if err.String() != "divide by zero" { | |
| t.Error("Div: expected divide by zero error; got", err) | |
| } | |
| } | |
| func TestHTTPRPC(t *testing.T) { | |
| once.Do(startServer) | |
| client, err := DialHTTP("tcp", httpServerAddr) | |
| if err != nil { | |
| t.Fatal("dialing", err) | |
| } | |
| // Synchronous calls | |
| args := &Args{7, 8} | |
| reply := new(Reply) | |
| err = client.Call("Arith.Add", args, reply) | |
| if err != nil { | |
| t.Errorf("Add: expected no error but got string %q", err.String()) | |
| } | |
| if reply.C != args.A+args.B { | |
| t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B) | |
| } | |
| } | |
| func TestCheckUnknownService(t *testing.T) { | |
| once.Do(startServer) | |
| conn, err := net.Dial("tcp", "", serverAddr) | |
| if err != nil { | |
| t.Fatal("dialing:", err) | |
| } | |
| client := NewClient(conn) | |
| args := &Args{7, 8} | |
| reply := new(Reply) | |
| err = client.Call("Unknown.Add", args, reply) | |
| if err == nil { | |
| t.Error("expected error calling unknown service") | |
| } else if strings.Index(err.String(), "service") < 0 { | |
| t.Error("expected error about service; got", err) | |
| } | |
| } | |
| func TestCheckUnknownMethod(t *testing.T) { | |
| once.Do(startServer) | |
| conn, err := net.Dial("tcp", "", serverAddr) | |
| if err != nil { | |
| t.Fatal("dialing:", err) | |
| } | |
| client := NewClient(conn) | |
| args := &Args{7, 8} | |
| reply := new(Reply) | |
| err = client.Call("Arith.Unknown", args, reply) | |
| if err == nil { | |
| t.Error("expected error calling unknown service") | |
| } else if strings.Index(err.String(), "method") < 0 { | |
| t.Error("expected error about method; got", err) | |
| } | |
| } | |
| func TestCheckBadType(t *testing.T) { | |
| once.Do(startServer) | |
| conn, err := net.Dial("tcp", "", serverAddr) | |
| if err != nil { | |
| t.Fatal("dialing:", err) | |
| } | |
| client := NewClient(conn) | |
| reply := new(Reply) | |
| err = client.Call("Arith.Add", reply, reply) // args, reply would be the correct thing to use | |
| if err == nil { | |
| t.Error("expected error calling Arith.Add with wrong arg type") | |
| } else if strings.Index(err.String(), "type") < 0 { | |
| t.Error("expected error about type; got", err) | |
| } | |
| } | |
| type Bad int | |
| type local struct{} | |
| func (t *Bad) ArgNotPointer(args Args, reply *Reply) os.Error { | |
| return nil | |
| } | |
| func (t *Bad) ArgNotPointerToStruct(args *int, reply *Reply) os.Error { | |
| return nil | |
| } | |
| func (t *Bad) ReplyNotPointer(args *Args, reply Reply) os.Error { | |
| return nil | |
| } | |
| func (t *Bad) ReplyNotPointerToStruct(args *Args, reply *int) os.Error { | |
| return nil | |
| } | |
| func (t *Bad) ArgNotPublic(args *local, reply *Reply) os.Error { | |
| return nil | |
| } | |
| func (t *Bad) ReplyNotPublic(args *Args, reply *local) os.Error { | |
| return nil | |
| } | |
| // Check that registration handles lots of bad methods and a type with no suitable methods. | |
| func TestRegistrationError(t *testing.T) { | |
| err := Register(new(Bad)) | |
| if err == nil { | |
| t.Errorf("expected error registering bad type") | |
| } | |
| } |