// 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 { panicln("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") } }