diff options
Diffstat (limited to 'pkg/flipcall/flipcall_test.go')
-rw-r--r-- | pkg/flipcall/flipcall_test.go | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/pkg/flipcall/flipcall_test.go b/pkg/flipcall/flipcall_test.go new file mode 100644 index 000000000..33fd55a44 --- /dev/null +++ b/pkg/flipcall/flipcall_test.go @@ -0,0 +1,405 @@ +// Copyright 2019 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package flipcall + +import ( + "runtime" + "testing" + "time" + + "gvisor.dev/gvisor/pkg/sync" +) + +var testPacketWindowSize = pageSize + +type testConnection struct { + pwa PacketWindowAllocator + clientEP Endpoint + serverEP Endpoint +} + +func newTestConnectionWithOptions(tb testing.TB, clientOpts, serverOpts []EndpointOption) *testConnection { + c := &testConnection{} + if err := c.pwa.Init(); err != nil { + tb.Fatalf("failed to create PacketWindowAllocator: %v", err) + } + pwd, err := c.pwa.Allocate(testPacketWindowSize) + if err != nil { + c.pwa.Destroy() + tb.Fatalf("PacketWindowAllocator.Allocate() failed: %v", err) + } + if err := c.clientEP.Init(ClientSide, pwd, clientOpts...); err != nil { + c.pwa.Destroy() + tb.Fatalf("failed to create client Endpoint: %v", err) + } + if err := c.serverEP.Init(ServerSide, pwd, serverOpts...); err != nil { + c.pwa.Destroy() + c.clientEP.Destroy() + tb.Fatalf("failed to create server Endpoint: %v", err) + } + return c +} + +func newTestConnection(tb testing.TB) *testConnection { + return newTestConnectionWithOptions(tb, nil, nil) +} + +func (c *testConnection) destroy() { + c.pwa.Destroy() + c.clientEP.Destroy() + c.serverEP.Destroy() +} + +func testSendRecv(t *testing.T, c *testConnection) { + // This shared variable is used to confirm that synchronization between + // flipcall endpoints is visible to the Go race detector. + state := 0 + var serverRun sync.WaitGroup + serverRun.Add(1) + go func() { + defer serverRun.Done() + t.Logf("server Endpoint waiting for packet 1") + if _, err := c.serverEP.RecvFirst(); err != nil { + t.Errorf("server Endpoint.RecvFirst() failed: %v", err) + return + } + state++ + if state != 2 { + t.Errorf("shared state counter: got %d, wanted 2", state) + } + t.Logf("server Endpoint got packet 1, sending packet 2 and waiting for packet 3") + if _, err := c.serverEP.SendRecv(0); err != nil { + t.Errorf("server Endpoint.SendRecv() failed: %v", err) + return + } + state++ + if state != 4 { + t.Errorf("shared state counter: got %d, wanted 4", state) + } + t.Logf("server Endpoint got packet 3") + }() + defer func() { + // Ensure that the server goroutine is cleaned up before + // c.serverEP.Destroy(), even if the test fails. + c.serverEP.Shutdown() + serverRun.Wait() + }() + + t.Logf("client Endpoint establishing connection") + if err := c.clientEP.Connect(); err != nil { + t.Fatalf("client Endpoint.Connect() failed: %v", err) + } + state++ + if state != 1 { + t.Errorf("shared state counter: got %d, wanted 1", state) + } + t.Logf("client Endpoint sending packet 1 and waiting for packet 2") + if _, err := c.clientEP.SendRecv(0); err != nil { + t.Fatalf("client Endpoint.SendRecv() failed: %v", err) + } + state++ + if state != 3 { + t.Errorf("shared state counter: got %d, wanted 3", state) + } + t.Logf("client Endpoint got packet 2, sending packet 3") + if err := c.clientEP.SendLast(0); err != nil { + t.Fatalf("client Endpoint.SendLast() failed: %v", err) + } + t.Logf("waiting for server goroutine to complete") + serverRun.Wait() +} + +func TestSendRecv(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testSendRecv(t, c) +} + +func testShutdownBeforeConnect(t *testing.T, c *testConnection, remoteShutdown bool) { + if remoteShutdown { + c.serverEP.Shutdown() + } else { + c.clientEP.Shutdown() + } + if err := c.clientEP.Connect(); err == nil { + t.Errorf("client Endpoint.Connect() succeeded unexpectedly") + } +} + +func TestShutdownBeforeConnectLocal(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownBeforeConnect(t, c, false) +} + +func TestShutdownBeforeConnectRemote(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownBeforeConnect(t, c, true) +} + +func testShutdownDuringConnect(t *testing.T, c *testConnection, remoteShutdown bool) { + var clientRun sync.WaitGroup + clientRun.Add(1) + go func() { + defer clientRun.Done() + if err := c.clientEP.Connect(); err == nil { + t.Errorf("client Endpoint.Connect() succeeded unexpectedly") + } + }() + time.Sleep(time.Second) // to allow c.clientEP.Connect() to block + if remoteShutdown { + c.serverEP.Shutdown() + } else { + c.clientEP.Shutdown() + } + clientRun.Wait() +} + +func TestShutdownDuringConnectLocal(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownDuringConnect(t, c, false) +} + +func TestShutdownDuringConnectRemote(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownDuringConnect(t, c, true) +} + +func testShutdownBeforeRecvFirst(t *testing.T, c *testConnection, remoteShutdown bool) { + if remoteShutdown { + c.clientEP.Shutdown() + } else { + c.serverEP.Shutdown() + } + if _, err := c.serverEP.RecvFirst(); err == nil { + t.Errorf("server Endpoint.RecvFirst() succeeded unexpectedly") + } +} + +func TestShutdownBeforeRecvFirstLocal(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownBeforeRecvFirst(t, c, false) +} + +func TestShutdownBeforeRecvFirstRemote(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownBeforeRecvFirst(t, c, true) +} + +func testShutdownDuringRecvFirstBeforeConnect(t *testing.T, c *testConnection, remoteShutdown bool) { + var serverRun sync.WaitGroup + serverRun.Add(1) + go func() { + defer serverRun.Done() + if _, err := c.serverEP.RecvFirst(); err == nil { + t.Errorf("server Endpoint.RecvFirst() succeeded unexpectedly") + } + }() + time.Sleep(time.Second) // to allow c.serverEP.RecvFirst() to block + if remoteShutdown { + c.clientEP.Shutdown() + } else { + c.serverEP.Shutdown() + } + serverRun.Wait() +} + +func TestShutdownDuringRecvFirstBeforeConnectLocal(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownDuringRecvFirstBeforeConnect(t, c, false) +} + +func TestShutdownDuringRecvFirstBeforeConnectRemote(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownDuringRecvFirstBeforeConnect(t, c, true) +} + +func testShutdownDuringRecvFirstAfterConnect(t *testing.T, c *testConnection, remoteShutdown bool) { + var serverRun sync.WaitGroup + serverRun.Add(1) + go func() { + defer serverRun.Done() + if _, err := c.serverEP.RecvFirst(); err == nil { + t.Errorf("server Endpoint.RecvFirst() succeeded unexpectedly") + } + }() + defer func() { + // Ensure that the server goroutine is cleaned up before + // c.serverEP.Destroy(), even if the test fails. + c.serverEP.Shutdown() + serverRun.Wait() + }() + if err := c.clientEP.Connect(); err != nil { + t.Fatalf("client Endpoint.Connect() failed: %v", err) + } + if remoteShutdown { + c.clientEP.Shutdown() + } else { + c.serverEP.Shutdown() + } + serverRun.Wait() +} + +func TestShutdownDuringRecvFirstAfterConnectLocal(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownDuringRecvFirstAfterConnect(t, c, false) +} + +func TestShutdownDuringRecvFirstAfterConnectRemote(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownDuringRecvFirstAfterConnect(t, c, true) +} + +func testShutdownDuringClientSendRecv(t *testing.T, c *testConnection, remoteShutdown bool) { + var serverRun sync.WaitGroup + serverRun.Add(1) + go func() { + defer serverRun.Done() + if _, err := c.serverEP.RecvFirst(); err != nil { + t.Errorf("server Endpoint.RecvFirst() failed: %v", err) + } + // At this point, the client must be blocked in c.clientEP.SendRecv(). + if remoteShutdown { + c.serverEP.Shutdown() + } else { + c.clientEP.Shutdown() + } + }() + defer func() { + // Ensure that the server goroutine is cleaned up before + // c.serverEP.Destroy(), even if the test fails. + c.serverEP.Shutdown() + serverRun.Wait() + }() + if err := c.clientEP.Connect(); err != nil { + t.Fatalf("client Endpoint.Connect() failed: %v", err) + } + if _, err := c.clientEP.SendRecv(0); err == nil { + t.Errorf("client Endpoint.SendRecv() succeeded unexpectedly") + } +} + +func TestShutdownDuringClientSendRecvLocal(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownDuringClientSendRecv(t, c, false) +} + +func TestShutdownDuringClientSendRecvRemote(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownDuringClientSendRecv(t, c, true) +} + +func testShutdownDuringServerSendRecv(t *testing.T, c *testConnection, remoteShutdown bool) { + var serverRun sync.WaitGroup + serverRun.Add(1) + go func() { + defer serverRun.Done() + if _, err := c.serverEP.RecvFirst(); err != nil { + t.Errorf("server Endpoint.RecvFirst() failed: %v", err) + return + } + if _, err := c.serverEP.SendRecv(0); err == nil { + t.Errorf("server Endpoint.SendRecv() succeeded unexpectedly") + } + }() + defer func() { + // Ensure that the server goroutine is cleaned up before + // c.serverEP.Destroy(), even if the test fails. + c.serverEP.Shutdown() + serverRun.Wait() + }() + if err := c.clientEP.Connect(); err != nil { + t.Fatalf("client Endpoint.Connect() failed: %v", err) + } + if _, err := c.clientEP.SendRecv(0); err != nil { + t.Fatalf("client Endpoint.SendRecv() failed: %v", err) + } + time.Sleep(time.Second) // to allow serverEP.SendRecv() to block + if remoteShutdown { + c.clientEP.Shutdown() + } else { + c.serverEP.Shutdown() + } + serverRun.Wait() +} + +func TestShutdownDuringServerSendRecvLocal(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownDuringServerSendRecv(t, c, false) +} + +func TestShutdownDuringServerSendRecvRemote(t *testing.T) { + c := newTestConnection(t) + defer c.destroy() + testShutdownDuringServerSendRecv(t, c, true) +} + +func benchmarkSendRecv(b *testing.B, c *testConnection) { + var serverRun sync.WaitGroup + serverRun.Add(1) + go func() { + defer serverRun.Done() + if b.N == 0 { + return + } + if _, err := c.serverEP.RecvFirst(); err != nil { + b.Errorf("server Endpoint.RecvFirst() failed: %v", err) + return + } + for i := 1; i < b.N; i++ { + if _, err := c.serverEP.SendRecv(0); err != nil { + b.Errorf("server Endpoint.SendRecv() failed: %v", err) + return + } + } + if err := c.serverEP.SendLast(0); err != nil { + b.Errorf("server Endpoint.SendLast() failed: %v", err) + } + }() + defer func() { + c.serverEP.Shutdown() + serverRun.Wait() + }() + + if err := c.clientEP.Connect(); err != nil { + b.Fatalf("client Endpoint.Connect() failed: %v", err) + } + runtime.GC() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := c.clientEP.SendRecv(0); err != nil { + b.Fatalf("client Endpoint.SendRecv() failed: %v", err) + } + } + b.StopTimer() +} + +func BenchmarkSendRecv(b *testing.B) { + c := newTestConnection(b) + defer c.destroy() + benchmarkSendRecv(b, c) +} |