From 92f1af83aee931f5eb86f7f13eb3007053944e7c Mon Sep 17 00:00:00 2001 From: Konrad Hanff Date: Fri, 18 Jun 2021 09:14:07 +0200 Subject: [PATCH] Add tests for codeOceanToRaw and null readers The tests ensure the readers do not return when there is no data available. --- api/websocket_test.go | 64 +++++++++++++++++++++++++++++++++++++++++++ nomad/nomad_test.go | 16 +++++++++++ tests/constants.go | 6 +++- 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/api/websocket_test.go b/api/websocket_test.go index 9db00ea..d6c27ca 100644 --- a/api/websocket_test.go +++ b/api/websocket_test.go @@ -16,11 +16,13 @@ import ( "gitlab.hpi.de/codeocean/codemoon/poseidon/api/dto" "gitlab.hpi.de/codeocean/codemoon/poseidon/nomad" "gitlab.hpi.de/codeocean/codemoon/poseidon/runner" + "gitlab.hpi.de/codeocean/codemoon/poseidon/tests" "gitlab.hpi.de/codeocean/codemoon/poseidon/tests/helpers" "io" "net/http" "net/http/httptest" "net/url" + "strings" "testing" "time" ) @@ -305,8 +307,70 @@ func TestRawToCodeOceanWriter(t *testing.T) { assert.Equal(t, expected, message) } +func TestCodeOceanToRawReaderReturnsOnlyAfterOneByteWasRead(t *testing.T) { + reader := newCodeOceanToRawReader(nil) + + read := make(chan int) + go func() { + p := make([]byte, 10) + n, _ := reader.Read(p) + read <- n + }() + + t.Run("Does not return immediately when there is no data", func(t *testing.T) { + assert.False(t, waitForChannel(read, tests.ShortTimeout)) + }) + + t.Run("Returns when there is data available", func(t *testing.T) { + reader.buffer <- byte(42) + assert.True(t, waitForChannel(read, tests.ShortTimeout)) + }) +} + +func TestCodeOceanToRawReaderReturnsOnlyAfterOneByteWasReadFromConnection(t *testing.T) { + messages := make(chan io.Reader) + + connection := &webSocketConnectionMock{} + connection.On("WriteMessage", mock.AnythingOfType("int"), mock.AnythingOfType("[]uint8")).Return(nil) + connection.On("CloseHandler").Return(nil) + connection.On("SetCloseHandler", mock.Anything).Return() + call := connection.On("NextReader") + call.Run(func(_ mock.Arguments) { + call.Return(websocket.TextMessage, <-messages, nil) + }) + + reader := newCodeOceanToRawReader(connection) + cancel := reader.readInputLoop() + defer cancel() + + read := make(chan int) + message := make([]byte, 10) + go func() { + n, _ := reader.Read(message) + read <- n + }() + + t.Run("Does not return immediately when there is no data", func(t *testing.T) { + assert.False(t, waitForChannel(read, tests.ShortTimeout)) + }) + + t.Run("Returns when there is data available", func(t *testing.T) { + messages <- strings.NewReader("Hello") + assert.True(t, waitForChannel(read, tests.ShortTimeout)) + }) +} + // --- Test suite specific test helpers --- +func waitForChannel(ch chan int, duration time.Duration) bool { + select { + case <-ch: + return true + case <-time.After(duration): + return false + } +} + func newNomadAllocationWithMockedApiClient(runnerId string) (r runner.Runner, mock *nomad.ExecutorAPIMock) { mock = &nomad.ExecutorAPIMock{} r = runner.NewNomadJob(runnerId, mock) diff --git a/nomad/nomad_test.go b/nomad/nomad_test.go index 672fed5..49b0c3c 100644 --- a/nomad/nomad_test.go +++ b/nomad/nomad_test.go @@ -750,3 +750,19 @@ func (s *ExecuteCommandTestSuite) mockExecute(command interface{}, exitCode int, Run(runFunc). Return(exitCode, err) } + +func TestNullReaderDoesNotReturnImmediately(t *testing.T) { + reader := &nullReader{} + readerReturned := make(chan bool) + go func() { + p := make([]byte, 5) + _, _ = reader.Read(p) + close(readerReturned) + }() + + select { + case <-readerReturned: + t.Fatal("Read should not return immediately.") + case <-time.After(tests.ShortTimeout): + } +} diff --git a/tests/constants.go b/tests/constants.go index 2753c6e..52a139e 100644 --- a/tests/constants.go +++ b/tests/constants.go @@ -1,6 +1,9 @@ package tests -import "errors" +import ( + "errors" + "time" +) const ( NonExistingIntegerID = 9999 @@ -21,6 +24,7 @@ const ( AnotherRunnerID = AnotherJobID DefaultExecutionID = "s0m3-3x3cu710n-1d" DefaultMockID = "m0ck-1d" + ShortTimeout = 100 * time.Millisecond ) var (