func fightN(f1, f2 Factory, n int) []MatchResult { aiA := &aiAdapter{factory: f1, games: make(map[string]gameState)} aiB := &aiAdapter{factory: f2, games: make(map[string]gameState)} a1, a2 := net.Pipe() b1, b2 := net.Pipe() ca1, ca2 := rpc.StreamTransport(a1), rpc.StreamTransport(a2) cb1, cb2 := rpc.StreamTransport(b1), rpc.StreamTransport(b2) // Server-side srvA := botapi.Ai_ServerToClient(aiA) srvB := botapi.Ai_ServerToClient(aiB) serverConnA := rpc.NewConn(ca1, rpc.MainInterface(srvA.Client)) serverConnB := rpc.NewConn(cb1, rpc.MainInterface(srvB.Client)) defer serverConnA.Wait() defer serverConnB.Wait() // Client-side ctx := context.Background() clientConnA := rpc.NewConn(ca2) clientConnB := rpc.NewConn(cb2) defer clientConnA.Close() defer clientConnB.Close() clientA := localAI{botapi.Ai{Client: clientConnA.Bootstrap(ctx)}} clientB := localAI{botapi.Ai{Client: clientConnB.Bootstrap(ctx)}} matchRes := make([]MatchResult, n) // Run the game for i := 0; i < n; i++ { b := engine.EmptyBoard(engine.DefaultConfig) b.InitBoard(engine.DefaultConfig) for !b.IsFinished() { turnCtx, _ := context.WithTimeout(ctx, 30*time.Second) resA, _ := clientA.takeTurn(turnCtx, strconv.Itoa(i), b, engine.P1Faction) resB, _ := clientB.takeTurn(turnCtx, strconv.Itoa(i), b, engine.P2Faction) b.Update(resA, resB) } matchRes[i] = MatchResult{ P1Score: b.BotCount(1), P2Score: b.BotCount(2), } } return matchRes }
func TestCancel(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() p, q := pipetransport.New() if *logMessages { p = logtransport.New(nil, p) } c := rpc.NewConn(p) notify := make(chan struct{}) hanger := testcapnp.Hanger_ServerToClient(Hanger{notify: notify}) d := rpc.NewConn(q, rpc.MainInterface(hanger.Client)) defer d.Wait() defer c.Close() client := testcapnp.Hanger{Client: c.Bootstrap(ctx)} subctx, subcancel := context.WithCancel(ctx) promise := client.Hang(subctx, func(r testcapnp.Hanger_hang_Params) error { return nil }) <-notify subcancel() _, err := promise.Struct() <-notify // test will deadlock if cancel not delivered if err != context.Canceled { t.Errorf("promise.Get() error: %v; want %v", err, context.Canceled) } }
func TestRelease(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() p, q := pipetransport.New() if *logMessages { p = logtransport.New(nil, p) } c := rpc.NewConn(p) hf := new(HandleFactory) d := rpc.NewConn(q, rpc.MainInterface(testcapnp.HandleFactory_ServerToClient(hf).Client)) defer d.Wait() defer c.Close() client := testcapnp.HandleFactory{Client: c.Bootstrap(ctx)} r, err := client.NewHandle(ctx, nil).Struct() if err != nil { t.Fatal("NewHandle:", err) } handle := r.Handle() if n := hf.numHandles(); n != 1 { t.Fatalf("numHandles = %d; want 1", n) } if err := handle.Client.Close(); err != nil { t.Error("handle.Client.Close():", err) } flushConn(ctx, c) if n := hf.numHandles(); n != 0 { t.Errorf("numHandles = %d; want 0", n) } }
func TestIssue3(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() p, q := pipetransport.New() if *logMessages { p = logtransport.New(nil, p) } log := testLogger{t} c := rpc.NewConn(p, rpc.ConnLog(log)) echoSrv := testcapnp.Echoer_ServerToClient(new(SideEffectEchoer)) d := rpc.NewConn(q, rpc.MainInterface(echoSrv.Client), rpc.ConnLog(log)) defer d.Wait() defer c.Close() client := testcapnp.Echoer{Client: c.Bootstrap(ctx)} localCap := testcapnp.CallOrder_ServerToClient(new(CallOrder)) echo := client.Echo(ctx, func(p testcapnp.Echoer_echo_Params) error { return p.SetCap(localCap) }) // This should not deadlock. _, err := echo.Struct() if err != nil { t.Error("Echo error:", err) } }
func TestPromisedCapability(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() p, q := pipetransport.New() if *logMessages { p = logtransport.New(nil, p) } c := rpc.NewConn(p) delay := make(chan struct{}) echoSrv := testcapnp.Echoer_ServerToClient(&DelayEchoer{delay: delay}) d := rpc.NewConn(q, rpc.MainInterface(echoSrv.Client)) defer d.Wait() defer c.Close() client := testcapnp.Echoer{Client: c.Bootstrap(ctx)} echo := client.Echo(ctx, func(p testcapnp.Echoer_echo_Params) error { return p.SetCap(testcapnp.CallOrder{Client: client.Client}) }) pipeline := echo.Cap() call0 := callseq(ctx, pipeline.Client, 0) call1 := callseq(ctx, pipeline.Client, 1) close(delay) check := func(promise testcapnp.CallOrder_getCallSequence_Results_Promise, n uint32) { r, err := promise.Struct() if err != nil { t.Errorf("call%d error: %v", n, err) } if r.N() != n { t.Errorf("call%d = %d; want %d", n, r.N(), n) } } check(call0, 0) check(call1, 1) }
func TestMainInterface(t *testing.T) { main := mockClient() conn, p := newTestConn(t, rpc.MainInterface(main)) defer conn.Close() defer p.Close() bootstrapRoundtrip(t, p) }
func server(c net.Conn) error { // Create a new locally implemented HashFactory. main := hashes.HashFactory_ServerToClient(hashFactory{}) // Listen for calls, using the HashFactory as the bootstrap interface. conn := rpc.NewConn(rpc.StreamTransport(c), rpc.MainInterface(main.Client)) // Wait for connection to abort. err := conn.Wait() return err }
func TestEmbargo(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() p, q := pipetransport.New() if *logMessages { p = logtransport.New(nil, p) } log := testLogger{t} c := rpc.NewConn(p, rpc.ConnLog(log)) echoSrv := testcapnp.Echoer_ServerToClient(new(Echoer)) d := rpc.NewConn(q, rpc.MainInterface(echoSrv.Client), rpc.ConnLog(log)) defer d.Wait() defer c.Close() client := testcapnp.Echoer{Client: c.Bootstrap(ctx)} localCap := testcapnp.CallOrder_ServerToClient(new(CallOrder)) earlyCall := callseq(ctx, client.Client, 0) echo := client.Echo(ctx, func(p testcapnp.Echoer_echo_Params) error { return p.SetCap(localCap) }) pipeline := echo.Cap() call0 := callseq(ctx, pipeline.Client, 0) call1 := callseq(ctx, pipeline.Client, 1) _, err := earlyCall.Struct() if err != nil { t.Errorf("earlyCall error: %v", err) } call2 := callseq(ctx, pipeline.Client, 2) _, err = echo.Struct() if err != nil { t.Errorf("echo.Get() error: %v", err) } call3 := callseq(ctx, pipeline.Client, 3) call4 := callseq(ctx, pipeline.Client, 4) call5 := callseq(ctx, pipeline.Client, 5) check := func(promise testcapnp.CallOrder_getCallSequence_Results_Promise, n uint32) { r, err := promise.Struct() if err != nil { t.Errorf("call%d error: %v", n, err) } if r.N() != n { t.Errorf("call%d = %d; want %d", n, r.N(), n) } } check(call0, 0) check(call1, 1) check(call2, 2) check(call3, 3) check(call4, 4) check(call5, 5) }
func Example() { // Create an in-memory transport. In a real application, you would probably // use a net.TCPConn (for RPC) or an os.Pipe (for IPC). p1, p2 := net.Pipe() t1, t2 := rpc.StreamTransport(p1), rpc.StreamTransport(p2) // Server-side srv := testcapnp.Adder_ServerToClient(AdderServer{}) serverConn := rpc.NewConn(t1, rpc.MainInterface(srv.Client)) defer serverConn.Wait() // Client-side ctx := context.Background() clientConn := rpc.NewConn(t2) defer clientConn.Close() adderClient := testcapnp.Adder{Client: clientConn.Bootstrap(ctx)} // Every client call returns a promise. You can make multiple calls // concurrently. call1 := adderClient.Add(ctx, func(p testcapnp.Adder_add_Params) error { p.SetA(5) p.SetB(2) return nil }) call2 := adderClient.Add(ctx, func(p testcapnp.Adder_add_Params) error { p.SetA(10) p.SetB(20) return nil }) // Calling Struct() on a promise waits until it returns. result1, err := call1.Struct() if err != nil { fmt.Println("Add #1 failed:", err) return } result2, err := call2.Struct() if err != nil { fmt.Println("Add #2 failed:", err) return } fmt.Println("Results:", result1.Result(), result2.Result()) // Output: // Results: 7 30 }
func TestReleaseAlias(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() p, q := pipetransport.New() if *logMessages { p = logtransport.New(nil, p) } c := rpc.NewConn(p) hf := singletonHandleFactory() d := rpc.NewConn(q, rpc.MainInterface(testcapnp.HandleFactory_ServerToClient(hf).Client)) defer d.Wait() defer c.Close() client := testcapnp.HandleFactory{Client: c.Bootstrap(ctx)} r1, err := client.NewHandle(ctx, func(r testcapnp.HandleFactory_newHandle_Params) error { return nil }).Struct() if err != nil { t.Fatal("NewHandle #1:", err) } handle1 := r1.Handle() r2, err := client.NewHandle(ctx, func(r testcapnp.HandleFactory_newHandle_Params) error { return nil }).Struct() if err != nil { t.Fatal("NewHandle #2:", err) } handle2 := r2.Handle() if n := hf.numHandles(); n != 1 { t.Fatalf("after creation, numHandles = %d; want 1", n) } if err := handle1.Client.Close(); err != nil { t.Error("handle1.Client.Close():", err) } flushConn(ctx, c) if n := hf.numHandles(); n != 1 { t.Errorf("after handle1.Client.Close(), numHandles = %d; want 1", n) } if err := handle2.Client.Close(); err != nil { t.Error("handle2.Client.Close():", err) } flushConn(ctx, c) if n := hf.numHandles(); n != 0 { t.Errorf("after handle1.Close() and handle2.Close(), numHandles = %d; want 0", n) } }
// handleConn runs in its own goroutine, started by listen. func (e *aiEndpoint) handleConn(c net.Conn) { aic := &aiConnector{e: e} rc := rpc.NewConn(rpc.StreamTransport(c), rpc.MainInterface(botapi.AiConnector_ServerToClient(aic).Client)) rc.Wait() aic.drop() }
func TestReceiveCallOnExport(t *testing.T) { const questionID = 999 called := false main := stubClient(func(ctx context.Context, params capnp.Struct) (capnp.Struct, error) { msg, s, err := capnp.NewMessage(capnp.SingleSegment(nil)) if err != nil { return capnp.Struct{}, err } result, err := capnp.NewStruct(s, capnp.ObjectSize{}) if err != nil { return capnp.Struct{}, err } called = true if err := msg.SetRoot(result); err != nil { return capnp.Struct{}, err } return result, nil }) conn, p := newTestConn(t, rpc.MainInterface(main)) defer conn.Close() defer p.Close() importID := sendBootstrapAndFinish(t, p) err := sendMessage(context.TODO(), p, func(msg rpccapnp.Message) error { call, err := msg.NewCall() if err != nil { return err } call.SetQuestionId(questionID) call.SetInterfaceId(interfaceID) call.SetMethodId(methodID) target, err := call.NewTarget() if err != nil { return err } target.SetImportedCap(importID) call.SetTarget(target) payload, err := call.NewParams() if err != nil { return err } content, err := capnp.NewStruct(msg.Segment(), capnp.ObjectSize{}) if err != nil { return err } payload.SetContent(content) return nil }) if err != nil { t.Fatal("Call message failed:", err) } retmsg, err := p.RecvMessage(context.TODO()) if err != nil { t.Fatal("Read Call return failed:", err) } if !called { t.Error("interface not called") } if retmsg.Which() != rpccapnp.Message_Which_return { t.Fatalf("Return message is %v; want %v", retmsg.Which(), rpccapnp.Message_Which_return) } ret, err := retmsg.Return() if err != nil { t.Fatal("return error:", err) } if id := ret.AnswerId(); id != questionID { t.Errorf("Return.answerId = %d; want %d", id, questionID) } if ret.Which() == rpccapnp.Return_Which_results { // TODO(light) } else if ret.Which() == rpccapnp.Return_Which_exception { exc, _ := ret.Exception() reason, _ := exc.Reason() t.Error("Return.exception:", reason) } else { t.Errorf("Return.Which() = %v; want %v", ret.Which(), rpccapnp.Return_Which_results) } }