From 91eae4f03526ccafb1bf3271a816738aaeb3d298 Mon Sep 17 00:00:00 2001 From: Ulysses Souza Date: Thu, 8 Sep 2022 18:55:07 +0200 Subject: [PATCH] Add Codecov Signed-off-by: Ulysses Souza --- .github/workflows/ci.yml | 3 ++ .gitignore | 1 + Makefile | 5 +-- README.md | 1 + pkg/e2e/buffer.go | 66 ---------------------------------- pkg/e2e/cancel_test.go | 28 +++++++++------ pkg/e2e/up_test.go | 34 ++++++++---------- pkg/utils/safebuffer.go | 78 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 117 insertions(+), 99 deletions(-) delete mode 100644 pkg/e2e/buffer.go create mode 100644 pkg/utils/safebuffer.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a4700aff..f8e5283eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -177,6 +177,9 @@ jobs: if: ${{ matrix.mode == 'plugin' }} run: | make e2e-compose + - + name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 - name: Test standalone mode if: ${{ matrix.mode == 'standalone' }} diff --git a/.gitignore b/.gitignore index c04e81c4d..3cb333cac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ bin/ /.vscode/ +coverage.out diff --git a/Makefile b/Makefile index c5913a308..a003ea723 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,8 @@ ifeq ($(DETECTED_OS),Windows) BINARY_EXT=.exe endif -TEST_FLAGS?= +TEST_COVERAGE_FLAGS = -race -coverprofile=coverage.out -covermode=atomic +TEST_FLAGS?= -timeout 15m E2E_TEST?= ifeq ($(E2E_TEST),) else @@ -61,7 +62,7 @@ install: binary .PHONY: e2e-compose e2e-compose: ## Run end to end local tests in plugin mode. Set E2E_TEST=TestName to run a single test docker compose version - go test $(TEST_FLAGS) -count=1 ./pkg/e2e + go test $(TEST_FLAGS) $(TEST_COVERAGE_FLAGS) -count=1 ./pkg/e2e .PHONY: e2e-compose-standalone e2e-compose-standalone: ## Run End to end local tests in standalone mode. Set E2E_TEST=TestName to run a single test diff --git a/README.md b/README.md index 43af9f6df..516eb6f1e 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![PkgGoDev](https://img.shields.io/badge/go.dev-docs-007d9c?style=flat-square&logo=go&logoColor=white)](https://pkg.go.dev/github.com/docker/compose/v2) [![Build Status](https://img.shields.io/github/workflow/status/docker/compose/ci?label=ci&logo=github&style=flat-square)](https://github.com/docker/compose/actions?query=workflow%3Aci) [![Go Report Card](https://goreportcard.com/badge/github.com/docker/compose/v2?style=flat-square)](https://goreportcard.com/report/github.com/docker/compose/v2) +[![Codecov](https://codecov.io/gh/docker/compose/branch/master/graph/badge.svg?token=HP3K4Y4ctu)](https://codecov.io/gh/docker/compose) ![Docker Compose](logo.png?raw=true "Docker Compose Logo") diff --git a/pkg/e2e/buffer.go b/pkg/e2e/buffer.go deleted file mode 100644 index b97b81d01..000000000 --- a/pkg/e2e/buffer.go +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright 2022 Docker Compose CLI 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 e2e - -import ( - "bytes" - "strings" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -type lockedBuffer struct { - mu sync.Mutex - buf bytes.Buffer -} - -func (l *lockedBuffer) Read(p []byte) (n int, err error) { - l.mu.Lock() - defer l.mu.Unlock() - return l.buf.Read(p) -} - -func (l *lockedBuffer) Write(p []byte) (n int, err error) { - l.mu.Lock() - defer l.mu.Unlock() - return l.buf.Write(p) -} - -func (l *lockedBuffer) String() string { - l.mu.Lock() - defer l.mu.Unlock() - return l.buf.String() -} - -func (l *lockedBuffer) RequireEventuallyContains(t testing.TB, v string) { - t.Helper() - var bufContents strings.Builder - require.Eventuallyf(t, func() bool { - l.mu.Lock() - defer l.mu.Unlock() - if _, err := l.buf.WriteTo(&bufContents); err != nil { - require.FailNowf(t, "Failed to copy from buffer", - "Error: %v", err) - } - return strings.Contains(bufContents.String(), v) - }, 5*time.Second, 20*time.Millisecond, - "Buffer did not contain %q\n============\n%s\n============", - v, &bufContents) -} diff --git a/pkg/e2e/cancel_test.go b/pkg/e2e/cancel_test.go index d64305a3d..67ab9fd18 100644 --- a/pkg/e2e/cancel_test.go +++ b/pkg/e2e/cancel_test.go @@ -20,7 +20,7 @@ package e2e import ( - "bytes" + "context" "fmt" "os/exec" "strings" @@ -28,6 +28,7 @@ import ( "testing" "time" + "github.com/docker/compose/v2/pkg/utils" "gotest.tools/v3/assert" "gotest.tools/v3/icmd" ) @@ -41,8 +42,12 @@ func TestComposeCancel(t *testing.T) { // require a separate groupID from the process running tests, in order to simulate ctrl+C from a terminal. // sending kill signal - cmd, stdout, stderr, err := StartWithNewGroupID(c.NewDockerComposeCmd(t, "-f", buildProjectPath, "build", - "--progress", "plain")) + stdout := &utils.SafeBuffer{} + stderr := &utils.SafeBuffer{} + cmd, err := StartWithNewGroupID(context.Background(), + c.NewDockerComposeCmd(t, "-f", buildProjectPath, "build", "--progress", "plain"), + stdout, + stderr) assert.NilError(t, err) c.WaitForCondition(t, func() (bool, string) { @@ -65,15 +70,16 @@ func TestComposeCancel(t *testing.T) { }) } -func StartWithNewGroupID(command icmd.Cmd) (*exec.Cmd, *bytes.Buffer, *bytes.Buffer, error) { - cmd := exec.Command(command.Command[0], command.Command[1:]...) +func StartWithNewGroupID(ctx context.Context, command icmd.Cmd, stdout *utils.SafeBuffer, stderr *utils.SafeBuffer) (*exec.Cmd, error) { + cmd := exec.CommandContext(ctx, command.Command[0], command.Command[1:]...) cmd.Env = command.Env cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - - var stdout bytes.Buffer - var stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr + if stdout != nil { + cmd.Stdout = stdout + } + if stderr != nil { + cmd.Stderr = stderr + } err := cmd.Start() - return cmd, &stdout, &stderr, err + return cmd, err } diff --git a/pkg/e2e/up_test.go b/pkg/e2e/up_test.go index e85982964..87828fbe7 100644 --- a/pkg/e2e/up_test.go +++ b/pkg/e2e/up_test.go @@ -26,6 +26,7 @@ import ( "testing" "time" + "github.com/docker/compose/v2/pkg/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gotest.tools/v3/icmd" @@ -52,9 +53,6 @@ func TestUpDependenciesNotStopped(t *testing.T) { reset() t.Cleanup(reset) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - t.Log("Launching orphan container (background)") c.RunDockerComposeCmd(t, "-f=./fixtures/ups-deps-stop/orphan.yaml", @@ -66,22 +64,18 @@ func TestUpDependenciesNotStopped(t *testing.T) { RequireServiceState(t, c, "orphan", "running") t.Log("Launching app container with implicit dependency") - var upOut lockedBuffer - var upCmd *exec.Cmd - go func() { - testCmd := c.NewDockerComposeCmd(t, - "-f=./fixtures/ups-deps-stop/compose.yaml", - "up", - "app", - ) - cmd := exec.CommandContext(ctx, testCmd.Command[0], testCmd.Command[1:]...) - cmd.Env = testCmd.Env - cmd.Stdout = &upOut - cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + upOut := &utils.SafeBuffer{} + testCmd := c.NewDockerComposeCmd(t, + "-f=./fixtures/ups-deps-stop/compose.yaml", + "up", + "app", + ) - assert.NoError(t, cmd.Start(), "Failed to run compose up") - upCmd = cmd - }() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + cmd, err := StartWithNewGroupID(ctx, testCmd, upOut, nil) + assert.NoError(t, err, "Failed to run compose up") t.Log("Waiting for containers to be in running state") upOut.RequireEventuallyContains(t, "hello app") @@ -89,13 +83,13 @@ func TestUpDependenciesNotStopped(t *testing.T) { RequireServiceState(t, c, "dependency", "running") t.Log("Simulating Ctrl-C") - require.NoError(t, syscall.Kill(-upCmd.Process.Pid, syscall.SIGINT), + require.NoError(t, syscall.Kill(-cmd.Process.Pid, syscall.SIGINT), "Failed to send SIGINT to compose up process") time.AfterFunc(5*time.Second, cancel) t.Log("Waiting for `compose up` to exit") - err := upCmd.Wait() + err = cmd.Wait() if err != nil { exitErr := err.(*exec.ExitError) require.EqualValues(t, exitErr.ExitCode(), 130) diff --git a/pkg/utils/safebuffer.go b/pkg/utils/safebuffer.go new file mode 100644 index 000000000..0545c463c --- /dev/null +++ b/pkg/utils/safebuffer.go @@ -0,0 +1,78 @@ +/* + Copyright 2020 Docker Compose CLI 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 utils + +import ( + "bytes" + "strings" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +// SafeBuffer is a thread safe version of bytes.Buffer +type SafeBuffer struct { + m sync.RWMutex + b bytes.Buffer +} + +// Read is a thread safe version of bytes.Buffer::Read +func (b *SafeBuffer) Read(p []byte) (n int, err error) { + b.m.RLock() + defer b.m.RUnlock() + return b.b.Read(p) +} + +// Write is a thread safe version of bytes.Buffer::Write +func (b *SafeBuffer) Write(p []byte) (n int, err error) { + b.m.Lock() + defer b.m.Unlock() + return b.b.Write(p) +} + +// String is a thread safe version of bytes.Buffer::String +func (b *SafeBuffer) String() string { + b.m.RLock() + defer b.m.RUnlock() + return b.b.String() +} + +// Bytes is a thread safe version of bytes.Buffer::Bytes +func (b *SafeBuffer) Bytes() []byte { + b.m.RLock() + defer b.m.RUnlock() + return b.b.Bytes() +} + +// RequireEventuallyContains is a thread safe eventual checker for the buffer content +func (b *SafeBuffer) RequireEventuallyContains(t testing.TB, v string) { + t.Helper() + var bufContents strings.Builder + require.Eventuallyf(t, func() bool { + b.m.Lock() + defer b.m.Unlock() + if _, err := b.b.WriteTo(&bufContents); err != nil { + require.FailNowf(t, "Failed to copy from buffer", + "Error: %v", err) + } + return strings.Contains(bufContents.String(), v) + }, 2*time.Second, 20*time.Millisecond, + "Buffer did not contain %q\n============\n%s\n============", + v, &bufContents) +}