Browse Source

e2e: fix subtests and block parallel unsafe tests

The big mechanical change here is to NOT store `t` as a field on
the `CLI` object (which has been renamed as well to fix the odd
capitalization). The way the tests are structured meant that the
"subtests" were using the _parent_ `*testing.T` instance, which
causes various oddities including confusing messages on failure
about a child test causing a panic in the parent.

Additionally, a few tests have been blocked from running in
parallel because they are sharing `compose.yaml` fixtures and
can fail as a result (e.g. due to a port conflict on the host).
I'll fix these in follow-up PRs but want to focus on correctness
for the tests before optimizing them.

Signed-off-by: Milas Bowman <[email protected]>
Milas Bowman 3 years ago
parent
commit
d9065050fd

+ 9 - 6
pkg/e2e/cancel_test.go

@@ -33,10 +33,10 @@ import (
 )
 
 func TestComposeCancel(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	t.Run("metrics on cancel Compose build", func(t *testing.T) {
-		c.RunDockerComposeCmd("ls")
+		c.RunDockerComposeCmd(t, "ls")
 		buildProjectPath := "fixtures/build-infinite/compose.yaml"
 
 		// require a separate groupID from the process running tests, in order to simulate ctrl+C from a terminal.
@@ -44,19 +44,22 @@ func TestComposeCancel(t *testing.T) {
 		cmd, stdout, stderr, err := StartWithNewGroupID(c.NewDockerCmd("compose", "-f", buildProjectPath, "build", "--progress", "plain"))
 		assert.NilError(t, err)
 
-		c.WaitForCondition(func() (bool, string) {
+		c.WaitForCondition(t, func() (bool, string) {
 			out := stdout.String()
 			errors := stderr.String()
-			return strings.Contains(out, "RUN sleep infinity"), fmt.Sprintf("'RUN sleep infinity' not found in : \n%s\nStderr: \n%s\n", out, errors)
+			return strings.Contains(out,
+					"RUN sleep infinity"), fmt.Sprintf("'RUN sleep infinity' not found in : \n%s\nStderr: \n%s\n", out,
+					errors)
 		}, 30*time.Second, 1*time.Second)
 
 		err = syscall.Kill(-cmd.Process.Pid, syscall.SIGINT) // simulate Ctrl-C : send signal to processGroup, children will have same groupId by default
 
 		assert.NilError(t, err)
-		c.WaitForCondition(func() (bool, string) {
+		c.WaitForCondition(t, func() (bool, string) {
 			out := stdout.String()
 			errors := stderr.String()
-			return strings.Contains(out, "CANCELED"), fmt.Sprintf("'CANCELED' not found in : \n%s\nStderr: \n%s\n", out, errors)
+			return strings.Contains(out, "CANCELED"), fmt.Sprintf("'CANCELED' not found in : \n%s\nStderr: \n%s\n", out,
+				errors)
 		}, 10*time.Second, 1*time.Second)
 	})
 }

+ 5 - 5
pkg/e2e/cascade_stop_test.go

@@ -23,28 +23,28 @@ import (
 )
 
 func TestCascadeStop(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	const projectName = "e2e-cascade-stop"
 
 	t.Run("abort-on-container-exit", func(t *testing.T) {
-		res := c.RunDockerOrExitError("compose", "-f", "./fixtures/cascade-stop-test/compose.yaml", "--project-name", projectName, "up", "--abort-on-container-exit")
+		res := c.RunDockerOrExitError(t, "compose", "-f", "./fixtures/cascade-stop-test/compose.yaml", "--project-name", projectName, "up", "--abort-on-container-exit")
 		res.Assert(t, icmd.Expected{ExitCode: 1, Out: `should_fail-1 exited with code 1`})
 		res.Assert(t, icmd.Expected{ExitCode: 1, Out: `Aborting on container exit...`})
 	})
 
 	t.Run("exit-code-from", func(t *testing.T) {
-		res := c.RunDockerOrExitError("compose", "-f", "./fixtures/cascade-stop-test/compose.yaml", "--project-name", projectName, "up", "--exit-code-from=sleep")
+		res := c.RunDockerOrExitError(t, "compose", "-f", "./fixtures/cascade-stop-test/compose.yaml", "--project-name", projectName, "up", "--exit-code-from=sleep")
 		res.Assert(t, icmd.Expected{ExitCode: 137, Out: `should_fail-1 exited with code 1`})
 		res.Assert(t, icmd.Expected{ExitCode: 137, Out: `Aborting on container exit...`})
 	})
 
 	t.Run("exit-code-from unknown", func(t *testing.T) {
-		res := c.RunDockerOrExitError("compose", "-f", "./fixtures/cascade-stop-test/compose.yaml", "--project-name", projectName, "up", "--exit-code-from=unknown")
+		res := c.RunDockerOrExitError(t, "compose", "-f", "./fixtures/cascade-stop-test/compose.yaml", "--project-name", projectName, "up", "--exit-code-from=unknown")
 		res.Assert(t, icmd.Expected{ExitCode: 1, Err: `no such service: unknown`})
 	})
 
 	t.Run("down", func(t *testing.T) {
-		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
+		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})
 }

+ 46 - 46
pkg/e2e/compose_build_test.go

@@ -28,55 +28,55 @@ import (
 )
 
 func TestLocalComposeBuild(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	t.Run("build named and unnamed images", func(t *testing.T) {
 		// ensure local test run does not reuse previously build image
-		c.RunDockerOrExitError("rmi", "build-test_nginx")
-		c.RunDockerOrExitError("rmi", "custom-nginx")
+		c.RunDockerOrExitError(t, "rmi", "build-test_nginx")
+		c.RunDockerOrExitError(t, "rmi", "custom-nginx")
 
-		res := c.RunDockerComposeCmd("--project-directory", "fixtures/build-test", "build")
+		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "build")
 
 		res.Assert(t, icmd.Expected{Out: "COPY static /usr/share/nginx/html"})
-		c.RunDockerCmd("image", "inspect", "build-test_nginx")
-		c.RunDockerCmd("image", "inspect", "custom-nginx")
+		c.RunDockerCmd(t, "image", "inspect", "build-test_nginx")
+		c.RunDockerCmd(t, "image", "inspect", "custom-nginx")
 	})
 
 	t.Run("build with build-arg", func(t *testing.T) {
 		// ensure local test run does not reuse previously build image
-		c.RunDockerOrExitError("rmi", "build-test_nginx")
-		c.RunDockerOrExitError("rmi", "custom-nginx")
+		c.RunDockerOrExitError(t, "rmi", "build-test_nginx")
+		c.RunDockerOrExitError(t, "rmi", "custom-nginx")
 
-		c.RunDockerComposeCmd("--project-directory", "fixtures/build-test", "build", "--build-arg", "FOO=BAR")
+		c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "build", "--build-arg", "FOO=BAR")
 
-		res := c.RunDockerCmd("image", "inspect", "build-test_nginx")
+		res := c.RunDockerCmd(t, "image", "inspect", "build-test_nginx")
 		res.Assert(t, icmd.Expected{Out: `"FOO": "BAR"`})
 	})
 
 	t.Run("build with build-arg set by env", func(t *testing.T) {
 		// ensure local test run does not reuse previously build image
-		c.RunDockerOrExitError("rmi", "build-test_nginx")
-		c.RunDockerOrExitError("rmi", "custom-nginx")
+		c.RunDockerOrExitError(t, "rmi", "build-test_nginx")
+		c.RunDockerOrExitError(t, "rmi", "custom-nginx")
 
 		icmd.RunCmd(c.NewDockerCmd("compose", "--project-directory", "fixtures/build-test", "build", "--build-arg", "FOO"),
 			func(cmd *icmd.Cmd) {
 				cmd.Env = append(cmd.Env, "FOO=BAR")
 			})
 
-		res := c.RunDockerCmd("image", "inspect", "build-test_nginx")
+		res := c.RunDockerCmd(t, "image", "inspect", "build-test_nginx")
 		res.Assert(t, icmd.Expected{Out: `"FOO": "BAR"`})
 	})
 
 	t.Run("build with multiple build-args ", func(t *testing.T) {
 		// ensure local test run does not reuse previously build image
-		c.RunDockerOrExitError("rmi", "-f", "multi-args_multiargs")
+		c.RunDockerOrExitError(t, "rmi", "-f", "multi-args_multiargs")
 		cmd := c.NewDockerCmd("compose", "--project-directory", "fixtures/build-test/multi-args", "build")
 
 		icmd.RunCmd(cmd, func(cmd *icmd.Cmd) {
 			cmd.Env = append(cmd.Env, "DOCKER_BUILDKIT=0")
 		})
 
-		res := c.RunDockerCmd("image", "inspect", "multi-args_multiargs")
+		res := c.RunDockerCmd(t, "image", "inspect", "multi-args_multiargs")
 		res.Assert(t, icmd.Expected{Out: `"RESULT": "SUCCESS"`})
 	})
 
@@ -86,7 +86,7 @@ func TestLocalComposeBuild(t *testing.T) {
 		os.Unsetenv("SSH_AUTH_SOCK")                         //nolint:errcheck
 		defer os.Setenv("SSH_AUTH_SOCK", defaultSSHAUTHSOCK) //nolint:errcheck
 
-		res := c.RunDockerComposeCmdNoCheck("--project-directory", "fixtures/build-test", "build", "--ssh", "")
+		res := c.RunDockerComposeCmdNoCheck(t, "--project-directory", "fixtures/build-test", "build", "--ssh", "")
 		res.Assert(t, icmd.Expected{
 			ExitCode: 1,
 			Err:      "invalid empty ssh agent socket: make sure SSH_AUTH_SOCK is set",
@@ -95,24 +95,24 @@ func TestLocalComposeBuild(t *testing.T) {
 	})
 
 	t.Run("build succeed with ssh from Compose file", func(t *testing.T) {
-		c.RunDockerOrExitError("rmi", "build-test-ssh")
+		c.RunDockerOrExitError(t, "rmi", "build-test-ssh")
 
-		c.RunDockerComposeCmd("--project-directory", "fixtures/build-test/ssh", "build")
-		c.RunDockerCmd("image", "inspect", "build-test-ssh")
+		c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test/ssh", "build")
+		c.RunDockerCmd(t, "image", "inspect", "build-test-ssh")
 	})
 
 	t.Run("build succeed with ssh from CLI", func(t *testing.T) {
-		c.RunDockerOrExitError("rmi", "build-test-ssh")
+		c.RunDockerOrExitError(t, "rmi", "build-test-ssh")
 
-		c.RunDockerComposeCmd("-f", "fixtures/build-test/ssh/compose-without-ssh.yaml", "--project-directory",
+		c.RunDockerComposeCmd(t, "-f", "fixtures/build-test/ssh/compose-without-ssh.yaml", "--project-directory",
 			"fixtures/build-test/ssh", "build", "--no-cache", "--ssh", "fake-ssh=./fixtures/build-test/ssh/fake_rsa")
-		c.RunDockerCmd("image", "inspect", "build-test-ssh")
+		c.RunDockerCmd(t, "image", "inspect", "build-test-ssh")
 	})
 
 	t.Run("build failed with wrong ssh key id from CLI", func(t *testing.T) {
-		c.RunDockerOrExitError("rmi", "build-test-ssh")
+		c.RunDockerOrExitError(t, "rmi", "build-test-ssh")
 
-		res := c.RunDockerComposeCmdNoCheck("-f", "fixtures/build-test/ssh/compose-without-ssh.yaml",
+		res := c.RunDockerComposeCmdNoCheck(t, "-f", "fixtures/build-test/ssh/compose-without-ssh.yaml",
 			"--project-directory", "fixtures/build-test/ssh", "build", "--no-cache", "--ssh",
 			"wrong-ssh=./fixtures/build-test/ssh/fake_rsa")
 		res.Assert(t, icmd.Expected{
@@ -122,22 +122,22 @@ func TestLocalComposeBuild(t *testing.T) {
 	})
 
 	t.Run("build succeed as part of up with ssh from Compose file", func(t *testing.T) {
-		c.RunDockerOrExitError("rmi", "build-test-ssh")
+		c.RunDockerOrExitError(t, "rmi", "build-test-ssh")
 
-		c.RunDockerComposeCmd("--project-directory", "fixtures/build-test/ssh", "up", "-d", "--build")
+		c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test/ssh", "up", "-d", "--build")
 		t.Cleanup(func() {
-			c.RunDockerComposeCmd("--project-directory", "fixtures/build-test/ssh", "down")
+			c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test/ssh", "down")
 		})
-		c.RunDockerCmd("image", "inspect", "build-test-ssh")
+		c.RunDockerCmd(t, "image", "inspect", "build-test-ssh")
 	})
 
 	t.Run("build as part of up", func(t *testing.T) {
-		c.RunDockerOrExitError("rmi", "build-test_nginx")
-		c.RunDockerOrExitError("rmi", "custom-nginx")
+		c.RunDockerOrExitError(t, "rmi", "build-test_nginx")
+		c.RunDockerOrExitError(t, "rmi", "custom-nginx")
 
-		res := c.RunDockerComposeCmd("--project-directory", "fixtures/build-test", "up", "-d")
+		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "up", "-d")
 		t.Cleanup(func() {
-			c.RunDockerComposeCmd("--project-directory", "fixtures/build-test", "down")
+			c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "down")
 		})
 
 		res.Assert(t, icmd.Expected{Out: "COPY static /usr/share/nginx/html"})
@@ -146,53 +146,53 @@ func TestLocalComposeBuild(t *testing.T) {
 		output := HTTPGetWithRetry(t, "http://localhost:8070", http.StatusOK, 2*time.Second, 20*time.Second)
 		assert.Assert(t, strings.Contains(output, "Hello from Nginx container"))
 
-		c.RunDockerCmd("image", "inspect", "build-test_nginx")
-		c.RunDockerCmd("image", "inspect", "custom-nginx")
+		c.RunDockerCmd(t, "image", "inspect", "build-test_nginx")
+		c.RunDockerCmd(t, "image", "inspect", "custom-nginx")
 	})
 
 	t.Run("no rebuild when up again", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("--project-directory", "fixtures/build-test", "up", "-d")
+		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "up", "-d")
 
 		assert.Assert(t, !strings.Contains(res.Stdout(), "COPY static"), res.Stdout())
 	})
 
 	t.Run("rebuild when up --build", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("--workdir", "fixtures/build-test", "up", "-d", "--build")
+		res := c.RunDockerComposeCmd(t, "--workdir", "fixtures/build-test", "up", "-d", "--build")
 
 		res.Assert(t, icmd.Expected{Out: "COPY static /usr/share/nginx/html"})
 		res.Assert(t, icmd.Expected{Out: "COPY static2 /usr/share/nginx/html"})
 	})
 
 	t.Run("cleanup build project", func(t *testing.T) {
-		c.RunDockerComposeCmd("--project-directory", "fixtures/build-test", "down")
-		c.RunDockerCmd("rmi", "build-test_nginx")
-		c.RunDockerCmd("rmi", "custom-nginx")
+		c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "down")
+		c.RunDockerCmd(t, "rmi", "build-test_nginx")
+		c.RunDockerCmd(t, "rmi", "custom-nginx")
 	})
 }
 
 func TestBuildSecrets(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	t.Run("build with secrets", func(t *testing.T) {
 		// ensure local test run does not reuse previously build image
-		c.RunDockerOrExitError("rmi", "build-test-secret")
+		c.RunDockerOrExitError(t, "rmi", "build-test-secret")
 
-		res := c.RunDockerComposeCmd("--project-directory", "fixtures/build-test/secrets", "build")
+		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test/secrets", "build")
 		res.Assert(t, icmd.Success)
 	})
 }
 
 func TestBuildTags(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	t.Run("build with tags", func(t *testing.T) {
 
 		// ensure local test run does not reuse previously build image
-		c.RunDockerOrExitError("rmi", "build-test-tags")
+		c.RunDockerOrExitError(t, "rmi", "build-test-tags")
 
-		c.RunDockerComposeCmd("--project-directory", "./fixtures/build-test/tags", "build", "--no-cache")
+		c.RunDockerComposeCmd(t, "--project-directory", "./fixtures/build-test/tags", "build", "--no-cache")
 
-		res := c.RunDockerCmd("image", "inspect", "build-test-tags")
+		res := c.RunDockerCmd(t, "image", "inspect", "build-test-tags")
 		expectedOutput := `"RepoTags": [
             "docker/build-test-tags:1.0.0",
             "build-test-tags:latest",

+ 2 - 2
pkg/e2e/compose_down_test.go

@@ -23,12 +23,12 @@ import (
 )
 
 func TestDown(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	const projectName = "e2e-down"
 
 	t.Run("no resource to remove", func(t *testing.T) {
-		res := c.RunDockerOrExitError("compose", "--project-name", projectName, "down")
+		res := c.RunDockerOrExitError(t, "compose", "--project-name", projectName, "down")
 		res.Assert(t, icmd.Expected{ExitCode: 0, Err: `No resource found to remove for project "e2e-down"`})
 	})
 }

+ 26 - 27
pkg/e2e/compose_environment_test.go

@@ -26,13 +26,13 @@ import (
 )
 
 func TestEnvPriority(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	projectDir := "./fixtures/environment/env-priority"
 
 	t.Run("up", func(t *testing.T) {
-		c.RunDockerOrExitError("rmi", "env-compose-priority")
-		c.RunDockerComposeCmd("-f", "./fixtures/environment/env-priority/compose-with-env.yaml",
+		c.RunDockerOrExitError(t, "rmi", "env-compose-priority")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose-with-env.yaml",
 			"--project-directory", projectDir, "up", "-d", "--build")
 	})
 
@@ -46,7 +46,7 @@ func TestEnvPriority(t *testing.T) {
 		os.Setenv("WHEREAMI", "shell") //nolint:errcheck
 		defer os.Unsetenv("WHEREAMI")  //nolint:errcheck
 
-		res := c.RunDockerComposeCmd("-f", "./fixtures/environment/env-priority/compose-with-env.yaml",
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose-with-env.yaml",
 			"--project-directory", projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override",
 			"run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
 
@@ -63,7 +63,7 @@ func TestEnvPriority(t *testing.T) {
 		os.Setenv("WHEREAMI", "shell") //nolint:errcheck
 		defer os.Unsetenv("WHEREAMI")  //nolint:errcheck
 
-		res := c.RunDockerComposeCmd("-f", "./fixtures/environment/env-priority/compose.yaml",
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml",
 			"--project-directory", projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override",
 			"run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
 		assert.Equal(t, strings.TrimSpace(res.Stdout()), "shell")
@@ -76,9 +76,9 @@ func TestEnvPriority(t *testing.T) {
 	// 4. Dockerfile
 	// 5. Variable is not defined
 	t.Run("shell priority from run command", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/environment/env-priority/compose.yaml",
-			"--project-directory", projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override",
-			"run", "--rm", "-e", "WHEREAMI=shell-run", "env-compose-priority")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
+			projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run", "--rm", "-e",
+			"WHEREAMI=shell-run", "env-compose-priority")
 		assert.Equal(t, strings.TrimSpace(res.Stdout()), "shell-run")
 	})
 
@@ -89,9 +89,9 @@ func TestEnvPriority(t *testing.T) {
 	// 4. Dockerfile
 	// 5. Variable is not defined
 	t.Run("override env file", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/environment/env-priority/compose.yaml",
-			"--project-directory", projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override",
-			"run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
+			projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run", "--rm", "-e",
+			"WHEREAMI", "env-compose-priority")
 		assert.Equal(t, strings.TrimSpace(res.Stdout()), "override")
 	})
 
@@ -102,8 +102,8 @@ func TestEnvPriority(t *testing.T) {
 	// 4. Dockerfile
 	// 5. Variable is not defined
 	t.Run("env file", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/environment/env-priority/compose.yaml",
-			"--project-directory", projectDir, "run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
+			projectDir, "run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
 		assert.Equal(t, strings.TrimSpace(res.Stdout()), "Env File")
 	})
 
@@ -114,19 +114,19 @@ func TestEnvPriority(t *testing.T) {
 	// 4. Dockerfile   <-- Result expected
 	// 5. Variable is not defined
 	t.Run("use Dockerfile", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/environment/env-priority/compose.yaml",
-			"--project-directory", projectDir, "--env-file", "./fixtures/environment/env-priority/.env.empty",
-			"run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
+			projectDir, "--env-file", "./fixtures/environment/env-priority/.env.empty", "run", "--rm", "-e", "WHEREAMI",
+			"env-compose-priority")
 		assert.Equal(t, strings.TrimSpace(res.Stdout()), "Dockerfile")
 	})
 
 	t.Run("down", func(t *testing.T) {
-		c.RunDockerComposeCmd("--project-directory", projectDir, "down")
+		c.RunDockerComposeCmd(t, "--project-directory", projectDir, "down")
 	})
 }
 
 func TestEnvInterpolation(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	projectDir := "./fixtures/environment/env-interpolation"
 
@@ -139,7 +139,7 @@ func TestEnvInterpolation(t *testing.T) {
 	t.Run("shell priority from run command", func(t *testing.T) {
 		os.Setenv("WHEREAMI", "shell") //nolint:errcheck
 		defer os.Unsetenv("WHEREAMI")  //nolint:errcheck
-		res := c.RunDockerComposeCmd("-f", "./fixtures/environment/env-interpolation/compose.yaml",
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-interpolation/compose.yaml",
 			"--project-directory", projectDir, "config")
 
 		res.Assert(t, icmd.Expected{Out: `IMAGE: default_env:shell`})
@@ -147,23 +147,22 @@ func TestEnvInterpolation(t *testing.T) {
 }
 
 func TestCommentsInEnvFile(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	projectDir := "./fixtures/environment/env-file-comments"
 
 	t.Run("comments in env files", func(t *testing.T) {
-		c.RunDockerOrExitError("rmi", "env-file-comments")
+		c.RunDockerOrExitError(t, "rmi", "env-file-comments")
 
-		c.RunDockerComposeCmd("-f", "./fixtures/environment/env-file-comments/compose.yaml",
-			"--project-directory", projectDir, "up", "-d", "--build")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-file-comments/compose.yaml", "--project-directory",
+			projectDir, "up", "-d", "--build")
 
-		res := c.RunDockerComposeCmd("-f", "./fixtures/environment/env-file-comments/compose.yaml",
-			"--project-directory", projectDir, "run", "--rm",
-			"-e", "COMMENT", "-e", "NO_COMMENT", "env-file-comments")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-file-comments/compose.yaml",
+			"--project-directory", projectDir, "run", "--rm", "-e", "COMMENT", "-e", "NO_COMMENT", "env-file-comments")
 
 		res.Assert(t, icmd.Expected{Out: `COMMENT=1234`})
 		res.Assert(t, icmd.Expected{Out: `NO_COMMENT=1234#5`})
 
-		c.RunDockerComposeCmd("--project-directory", projectDir, "down", "--rmi", "all")
+		c.RunDockerComposeCmd(t, "--project-directory", projectDir, "down", "--rmi", "all")
 	})
 }

+ 6 - 5
pkg/e2e/compose_exec_test.go

@@ -25,19 +25,20 @@ import (
 )
 
 func TestLocalComposeExec(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	const projectName = "compose-e2e-exec"
 
-	c.RunDockerComposeCmd("--project-directory", "fixtures/simple-composefile", "--project-name", projectName, "up", "-d")
+	c.RunDockerComposeCmd(t, "--project-directory", "fixtures/simple-composefile", "--project-name", projectName, "up",
+		"-d")
 
 	t.Run("exec true", func(t *testing.T) {
-		res := c.RunDockerOrExitError("exec", "compose-e2e-exec-simple-1", "/bin/true")
+		res := c.RunDockerOrExitError(t, "exec", "compose-e2e-exec-simple-1", "/bin/true")
 		res.Assert(t, icmd.Expected{ExitCode: 0})
 	})
 
 	t.Run("exec false", func(t *testing.T) {
-		res := c.RunDockerOrExitError("exec", "compose-e2e-exec-simple-1", "/bin/false")
+		res := c.RunDockerOrExitError(t, "exec", "compose-e2e-exec-simple-1", "/bin/false")
 		res.Assert(t, icmd.Expected{ExitCode: 1})
 	})
 
@@ -50,7 +51,7 @@ func TestLocalComposeExec(t *testing.T) {
 	})
 
 	t.Run("exec without env set", func(t *testing.T) {
-		res := c.RunDockerOrExitError("exec", "-e", "FOO", "compose-e2e-exec-simple-1", "/usr/bin/env")
+		res := c.RunDockerOrExitError(t, "exec", "-e", "FOO", "compose-e2e-exec-simple-1", "/usr/bin/env")
 		res.Assert(t, icmd.Expected{ExitCode: 0})
 		assert.Check(t, !strings.Contains(res.Stdout(), "FOO="))
 	})

+ 25 - 21
pkg/e2e/compose_run_test.go

@@ -26,21 +26,22 @@ import (
 )
 
 func TestLocalComposeRun(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	t.Run("compose run", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "back")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "run", "back")
 		lines := Lines(res.Stdout())
 		assert.Equal(t, lines[len(lines)-1], "Hello there!!", res.Stdout())
 		assert.Assert(t, !strings.Contains(res.Combined(), "orphan"))
-		res = c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "back", "echo", "Hello one more time")
+		res = c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "run", "back", "echo",
+			"Hello one more time")
 		lines = Lines(res.Stdout())
 		assert.Equal(t, lines[len(lines)-1], "Hello one more time", res.Stdout())
 		assert.Assert(t, !strings.Contains(res.Combined(), "orphan"))
 	})
 
 	t.Run("check run container exited", func(t *testing.T) {
-		res := c.RunDockerCmd("ps", "--all")
+		res := c.RunDockerCmd(t, "ps", "--all")
 		lines := Lines(res.Stdout())
 		var runContainerID string
 		var truncatedSlug string
@@ -59,7 +60,7 @@ func TestLocalComposeRun(t *testing.T) {
 			}
 		}
 		assert.Assert(t, runContainerID != "")
-		res = c.RunDockerCmd("inspect", runContainerID)
+		res = c.RunDockerCmd(t, "inspect", runContainerID)
 		res.Assert(t, icmd.Expected{Out: ` "Status": "exited"`})
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.container-number": "1"`})
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project": "run-test"`})
@@ -68,40 +69,43 @@ func TestLocalComposeRun(t *testing.T) {
 	})
 
 	t.Run("compose run --rm", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "--rm", "back", "echo", "Hello again")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "run", "--rm", "back", "echo",
+			"Hello again")
 		lines := Lines(res.Stdout())
 		assert.Equal(t, lines[len(lines)-1], "Hello again", res.Stdout())
 
-		res = c.RunDockerCmd("ps", "--all")
+		res = c.RunDockerCmd(t, "ps", "--all")
 		assert.Assert(t, strings.Contains(res.Stdout(), "run-test_back"), res.Stdout())
 	})
 
 	t.Run("down", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "down")
-		res := c.RunDockerCmd("ps", "--all")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "down")
+		res := c.RunDockerCmd(t, "ps", "--all")
 		assert.Assert(t, !strings.Contains(res.Stdout(), "run-test"), res.Stdout())
 	})
 
 	t.Run("compose run --volumes", func(t *testing.T) {
 		wd, err := os.Getwd()
 		assert.NilError(t, err)
-		res := c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "--volumes", wd+":/foo", "back", "/bin/sh", "-c", "ls /foo")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "run", "--volumes", wd+":/foo",
+			"back", "/bin/sh", "-c", "ls /foo")
 		res.Assert(t, icmd.Expected{Out: "compose_run_test.go"})
 
-		res = c.RunDockerCmd("ps", "--all")
+		res = c.RunDockerCmd(t, "ps", "--all")
 		assert.Assert(t, strings.Contains(res.Stdout(), "run-test_back"), res.Stdout())
 	})
 
 	t.Run("compose run --publish", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "--publish", "8081:80", "-d", "back", "/bin/sh", "-c", "sleep 1")
-		res := c.RunDockerCmd("ps")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "run", "--publish", "8081:80", "-d", "back",
+			"/bin/sh", "-c", "sleep 1")
+		res := c.RunDockerCmd(t, "ps")
 		assert.Assert(t, strings.Contains(res.Stdout(), "8081->80/tcp"), res.Stdout())
 	})
 
 	t.Run("compose run orphan", func(t *testing.T) {
 		// Use different compose files to get an orphan container
-		c.RunDockerComposeCmd("-f", "./fixtures/run-test/orphan.yaml", "run", "simple")
-		res := c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "back", "echo", "Hello")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/orphan.yaml", "run", "simple")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "run", "back", "echo", "Hello")
 		assert.Assert(t, strings.Contains(res.Combined(), "orphan"))
 
 		cmd := c.NewDockerCmd("compose", "-f", "./fixtures/run-test/compose.yaml", "run", "back", "echo", "Hello")
@@ -112,21 +116,21 @@ func TestLocalComposeRun(t *testing.T) {
 	})
 
 	t.Run("down", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "down")
-		c.RunDockerComposeCmd("-f", "./fixtures/run-test/orphan.yaml", "down")
-		res := c.RunDockerCmd("ps", "--all")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "down")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/orphan.yaml", "down")
+		res := c.RunDockerCmd(t, "ps", "--all")
 		assert.Assert(t, !strings.Contains(res.Stdout(), "run-test"), res.Stdout())
 	})
 
 	t.Run("run starts only container and dependencies", func(t *testing.T) {
 		// ensure that even if another service is up run does not start it: https://github.com/docker/compose/issues/9459
-		res := c.RunDockerComposeCmd("-f", "./fixtures/run-test/deps.yaml", "up", "service_b")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/deps.yaml", "up", "service_b")
 		res.Assert(t, icmd.Success)
 
-		res = c.RunDockerComposeCmd("-f", "./fixtures/run-test/deps.yaml", "run", "service_a")
+		res = c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/deps.yaml", "run", "service_a")
 		assert.Assert(t, strings.Contains(res.Combined(), "shared_dep"), res.Combined())
 		assert.Assert(t, !strings.Contains(res.Combined(), "service_b"), res.Combined())
 
-		c.RunDockerComposeCmd("-f", "./fixtures/run-test/deps.yaml", "down", "--remove-orphans")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/deps.yaml", "down", "--remove-orphans")
 	})
 }

+ 45 - 40
pkg/e2e/compose_test.go

@@ -31,31 +31,30 @@ import (
 	"gotest.tools/v3/icmd"
 )
 
-var binDir string
-
 func TestLocalComposeUp(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	// this test shares a fixture with TestCompatibility and can't run at the same time
+	c := NewCLI(t)
 
 	const projectName = "compose-e2e-demo"
 
 	t.Run("up", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/sentences/compose.yaml", "--project-name", projectName, "up", "-d")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/sentences/compose.yaml", "--project-name", projectName, "up", "-d")
 	})
 
 	t.Run("check accessing running app", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-p", projectName, "ps")
+		res := c.RunDockerComposeCmd(t, "-p", projectName, "ps")
 		res.Assert(t, icmd.Expected{Out: `web`})
 
 		endpoint := "http://localhost:90"
 		output := HTTPGetWithRetry(t, endpoint+"/words/noun", http.StatusOK, 2*time.Second, 20*time.Second)
 		assert.Assert(t, strings.Contains(output, `"word":`))
 
-		res = c.RunDockerCmd("network", "ls")
+		res = c.RunDockerCmd(t, "network", "ls")
 		res.Assert(t, icmd.Expected{Out: projectName + "_default"})
 	})
 
 	t.Run("top", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-p", projectName, "top")
+		res := c.RunDockerComposeCmd(t, "-p", projectName, "top")
 		output := res.Stdout()
 		head := []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"}
 		for _, h := range head {
@@ -66,7 +65,7 @@ func TestLocalComposeUp(t *testing.T) {
 	})
 
 	t.Run("check compose labels", func(t *testing.T) {
-		res := c.RunDockerCmd("inspect", projectName+"-web-1")
+		res := c.RunDockerCmd(t, "inspect", projectName+"-web-1")
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.container-number": "1"`})
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project": "compose-e2e-demo"`})
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.oneoff": "False",`})
@@ -76,55 +75,55 @@ func TestLocalComposeUp(t *testing.T) {
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.service": "web"`})
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.version":`})
 
-		res = c.RunDockerCmd("network", "inspect", projectName+"_default")
+		res = c.RunDockerCmd(t, "network", "inspect", projectName+"_default")
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.network": "default"`})
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project": `})
 		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.version": `})
 	})
 
 	t.Run("check user labels", func(t *testing.T) {
-		res := c.RunDockerCmd("inspect", projectName+"-web-1")
+		res := c.RunDockerCmd(t, "inspect", projectName+"-web-1")
 		res.Assert(t, icmd.Expected{Out: `"my-label": "test"`})
 
 	})
 
 	t.Run("check healthcheck output", func(t *testing.T) {
-		c.WaitForCmdResult(c.NewDockerCmd("compose", "-p", projectName, "ps", "--format", "json"),
+		c.WaitForCmdResult(t, c.NewDockerCmd("compose", "-p", projectName, "ps", "--format", "json"),
 			StdoutContains(`"Name":"compose-e2e-demo-web-1","Command":"/dispatcher","Project":"compose-e2e-demo","Service":"web","State":"running","Health":"healthy"`),
 			5*time.Second, 1*time.Second)
 
-		res := c.RunDockerComposeCmd("-p", projectName, "ps")
+		res := c.RunDockerComposeCmd(t, "-p", projectName, "ps")
 		res.Assert(t, icmd.Expected{Out: `NAME                       COMMAND                  SERVICE             STATUS              PORTS`})
 		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-web-1     "/dispatcher"            web                 running (healthy)   0.0.0.0:90->80/tcp`})
 		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-db-1      "docker-entrypoint.s…"   db                  running             5432/tcp`})
 	})
 
 	t.Run("images", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-p", projectName, "images")
+		res := c.RunDockerComposeCmd(t, "-p", projectName, "images")
 		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-db-1      gtardif/sentences-db    latest`})
 		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-web-1     gtardif/sentences-web   latest`})
 		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-words-1   gtardif/sentences-api   latest`})
 	})
 
 	t.Run("down", func(t *testing.T) {
-		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
+		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})
 
 	t.Run("check containers after down", func(t *testing.T) {
-		res := c.RunDockerCmd("ps", "--all")
+		res := c.RunDockerCmd(t, "ps", "--all")
 		assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined())
 	})
 
 	t.Run("check networks after down", func(t *testing.T) {
-		res := c.RunDockerCmd("network", "ls")
+		res := c.RunDockerCmd(t, "network", "ls")
 		assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined())
 	})
 }
 
 func TestComposePull(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
-	res := c.RunDockerOrExitError("compose", "--project-directory", "fixtures/simple-composefile", "pull")
+	res := c.RunDockerOrExitError(t, "compose", "--project-directory", "fixtures/simple-composefile", "pull")
 	output := res.Combined()
 
 	assert.Assert(t, strings.Contains(output, "simple Pulled"))
@@ -132,102 +131,108 @@ func TestComposePull(t *testing.T) {
 }
 
 func TestDownComposefileInParentFolder(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	tmpFolder, err := ioutil.TempDir("fixtures/simple-composefile", "test-tmp")
 	assert.NilError(t, err)
 	defer os.Remove(tmpFolder) // nolint: errcheck
 	projectName := filepath.Base(tmpFolder)
 
-	res := c.RunDockerComposeCmd("--project-directory", tmpFolder, "up", "-d")
+	res := c.RunDockerComposeCmd(t, "--project-directory", tmpFolder, "up", "-d")
 	res.Assert(t, icmd.Expected{Err: "Started", ExitCode: 0})
 
-	res = c.RunDockerComposeCmd("-p", projectName, "down")
+	res = c.RunDockerComposeCmd(t, "-p", projectName, "down")
 	res.Assert(t, icmd.Expected{Err: "Removed", ExitCode: 0})
 }
 
 func TestAttachRestart(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	cmd := c.NewDockerCmd("compose", "--ansi=never", "--project-directory", "./fixtures/attach-restart", "up")
 	res := icmd.StartCmd(cmd)
-	defer c.RunDockerOrExitError("compose", "-p", "attach-restart", "down")
+	defer c.RunDockerOrExitError(t, "compose", "-p", "attach-restart", "down")
 
-	c.WaitForCondition(func() (bool, string) {
+	c.WaitForCondition(t, func() (bool, string) {
 		debug := res.Combined()
-		return strings.Count(res.Stdout(), "failing-1 exited with code 1") == 3, fmt.Sprintf("'failing-1 exited with code 1' not found 3 times in : \n%s\n", debug)
+		return strings.Count(res.Stdout(),
+				"failing-1 exited with code 1") == 3, fmt.Sprintf("'failing-1 exited with code 1' not found 3 times in : \n%s\n",
+				debug)
 	}, 2*time.Minute, 2*time.Second)
 
 	assert.Equal(t, strings.Count(res.Stdout(), "failing-1  | world"), 3, res.Combined())
 }
 
 func TestInitContainer(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
-	res := c.RunDockerOrExitError("compose", "--ansi=never", "--project-directory", "./fixtures/init-container", "up")
-	defer c.RunDockerOrExitError("compose", "-p", "init-container", "down")
+	res := c.RunDockerOrExitError(t, "compose", "--ansi=never", "--project-directory", "./fixtures/init-container", "up")
+	defer c.RunDockerOrExitError(t, "compose", "-p", "init-container", "down")
 	testify.Regexp(t, "foo-1  | hello(?m:.*)bar-1  | world", res.Stdout())
 }
 
 func TestRm(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	const projectName = "compose-e2e-rm"
 
 	t.Run("up", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "up", "-d")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "up", "-d")
 	})
 
 	t.Run("rm -sf", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "rm", "-sf", "simple")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "rm",
+			"-sf", "simple")
 		res.Assert(t, icmd.Expected{Err: "Removed", ExitCode: 0})
 	})
 
 	t.Run("check containers after rm -sf", func(t *testing.T) {
-		res := c.RunDockerCmd("ps", "--all")
+		res := c.RunDockerCmd(t, "ps", "--all")
 		assert.Assert(t, !strings.Contains(res.Combined(), projectName+"_simple"), res.Combined())
 	})
 
 	t.Run("rm -sf <none>", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "rm", "-sf", "simple")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "rm",
+			"-sf", "simple")
 		res.Assert(t, icmd.Expected{ExitCode: 0})
 	})
 
 	t.Run("down", func(t *testing.T) {
-		c.RunDockerComposeCmd("-p", projectName, "down")
+		c.RunDockerComposeCmd(t, "-p", projectName, "down")
 	})
 }
 
 func TestCompatibility(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	// this test shares a fixture with TestLocalComposeUp and can't run at the same time
+	c := NewCLI(t)
 
 	const projectName = "compose-e2e-compatibility"
 
 	t.Run("up", func(t *testing.T) {
-		c.RunDockerComposeCmd("--compatibility", "-f", "./fixtures/sentences/compose.yaml", "--project-name", projectName, "up", "-d")
+		c.RunDockerComposeCmd(t, "--compatibility", "-f", "./fixtures/sentences/compose.yaml", "--project-name",
+			projectName, "up", "-d")
 	})
 
 	t.Run("check container names", func(t *testing.T) {
-		res := c.RunDockerCmd("ps", "--format", "{{.Names}}")
+		res := c.RunDockerCmd(t, "ps", "--format", "{{.Names}}")
 		res.Assert(t, icmd.Expected{Out: "compose-e2e-compatibility_web_1"})
 		res.Assert(t, icmd.Expected{Out: "compose-e2e-compatibility_words_1"})
 		res.Assert(t, icmd.Expected{Out: "compose-e2e-compatibility_db_1"})
 	})
 
 	t.Run("down", func(t *testing.T) {
-		c.RunDockerComposeCmd("-p", projectName, "down")
+		c.RunDockerComposeCmd(t, "-p", projectName, "down")
 	})
 }
 
 func TestConvert(t *testing.T) {
 	const projectName = "compose-e2e-convert"
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	wd, err := os.Getwd()
 	assert.NilError(t, err)
 
 	t.Run("up", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/simple-build-test/compose.yaml", "-p", projectName, "convert")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-build-test/compose.yaml", "-p", projectName, "convert")
 		res.Assert(t, icmd.Expected{Out: fmt.Sprintf(`services:
   nginx:
     build:

+ 23 - 16
pkg/e2e/cp_test.go

@@ -26,12 +26,12 @@ import (
 )
 
 func TestCopy(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	const projectName = "copy_e2e"
 
 	t.Cleanup(func() {
-		c.RunDockerComposeCmd("-f", "./fixtures/cp-test/compose.yaml", "--project-name", projectName, "down")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/cp-test/compose.yaml", "--project-name", projectName, "down")
 
 		os.Remove("./fixtures/cp-test/from-default.txt") //nolint:errcheck
 		os.Remove("./fixtures/cp-test/from-indexed.txt") //nolint:errcheck
@@ -39,41 +39,45 @@ func TestCopy(t *testing.T) {
 	})
 
 	t.Run("start service", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/cp-test/compose.yaml", "--project-name", projectName, "up", "--scale", "nginx=5", "-d")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/cp-test/compose.yaml", "--project-name", projectName, "up",
+			"--scale", "nginx=5", "-d")
 	})
 
 	t.Run("make sure service is running", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-p", projectName, "ps")
+		res := c.RunDockerComposeCmd(t, "-p", projectName, "ps")
 		res.Assert(t, icmd.Expected{Out: `nginx               running`})
 	})
 
 	t.Run("copy to container copies the file to the all containers by default", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "./fixtures/cp-test/cp-me.txt", "nginx:/tmp/default.txt")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp",
+			"./fixtures/cp-test/cp-me.txt", "nginx:/tmp/default.txt")
 		res.Assert(t, icmd.Expected{ExitCode: 0})
 
-		output := c.RunDockerCmd("exec", projectName+"-nginx-1", "cat", "/tmp/default.txt").Stdout()
+		output := c.RunDockerCmd(t, "exec", projectName+"-nginx-1", "cat", "/tmp/default.txt").Stdout()
 		assert.Assert(t, strings.Contains(output, `hello world`), output)
 
-		output = c.RunDockerCmd("exec", projectName+"-nginx-2", "cat", "/tmp/default.txt").Stdout()
+		output = c.RunDockerCmd(t, "exec", projectName+"-nginx-2", "cat", "/tmp/default.txt").Stdout()
 		assert.Assert(t, strings.Contains(output, `hello world`), output)
 
-		output = c.RunDockerCmd("exec", projectName+"-nginx-3", "cat", "/tmp/default.txt").Stdout()
+		output = c.RunDockerCmd(t, "exec", projectName+"-nginx-3", "cat", "/tmp/default.txt").Stdout()
 		assert.Assert(t, strings.Contains(output, `hello world`), output)
 	})
 
 	t.Run("copy to container with a given index copies the file to the given container", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "--index=3", "./fixtures/cp-test/cp-me.txt", "nginx:/tmp/indexed.txt")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "--index=3",
+			"./fixtures/cp-test/cp-me.txt", "nginx:/tmp/indexed.txt")
 		res.Assert(t, icmd.Expected{ExitCode: 0})
 
-		output := c.RunDockerCmd("exec", projectName+"-nginx-3", "cat", "/tmp/indexed.txt").Stdout()
+		output := c.RunDockerCmd(t, "exec", projectName+"-nginx-3", "cat", "/tmp/indexed.txt").Stdout()
 		assert.Assert(t, strings.Contains(output, `hello world`), output)
 
-		res = c.RunDockerOrExitError("exec", projectName+"-nginx-2", "cat", "/tmp/indexed.txt")
+		res = c.RunDockerOrExitError(t, "exec", projectName+"-nginx-2", "cat", "/tmp/indexed.txt")
 		res.Assert(t, icmd.Expected{ExitCode: 1})
 	})
 
 	t.Run("copy from a container copies the file to the host from the first container by default", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "nginx:/tmp/default.txt", "./fixtures/cp-test/from-default.txt")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp",
+			"nginx:/tmp/default.txt", "./fixtures/cp-test/from-default.txt")
 		res.Assert(t, icmd.Expected{ExitCode: 0})
 
 		data, err := os.ReadFile("./fixtures/cp-test/from-default.txt")
@@ -82,7 +86,8 @@ func TestCopy(t *testing.T) {
 	})
 
 	t.Run("copy from a container with a given index copies the file to host", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "--index=3", "nginx:/tmp/indexed.txt", "./fixtures/cp-test/from-indexed.txt")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "--index=3",
+			"nginx:/tmp/indexed.txt", "./fixtures/cp-test/from-indexed.txt")
 		res.Assert(t, icmd.Expected{ExitCode: 0})
 
 		data, err := os.ReadFile("./fixtures/cp-test/from-indexed.txt")
@@ -91,13 +96,15 @@ func TestCopy(t *testing.T) {
 	})
 
 	t.Run("copy to and from a container also work with folder", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "./fixtures/cp-test/cp-folder", "nginx:/tmp")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp",
+			"./fixtures/cp-test/cp-folder", "nginx:/tmp")
 		res.Assert(t, icmd.Expected{ExitCode: 0})
 
-		output := c.RunDockerCmd("exec", projectName+"-nginx-1", "cat", "/tmp/cp-folder/cp-me.txt").Stdout()
+		output := c.RunDockerCmd(t, "exec", projectName+"-nginx-1", "cat", "/tmp/cp-folder/cp-me.txt").Stdout()
 		assert.Assert(t, strings.Contains(output, `hello world from folder`), output)
 
-		res = c.RunDockerComposeCmd("-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "nginx:/tmp/cp-folder", "./fixtures/cp-test/cp-folder2")
+		res = c.RunDockerComposeCmd(t, "-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp",
+			"nginx:/tmp/cp-folder", "./fixtures/cp-test/cp-folder2")
 		res.Assert(t, icmd.Expected{ExitCode: 0})
 
 		data, err := os.ReadFile("./fixtures/cp-test/cp-folder2/cp-me.txt")

+ 13 - 13
pkg/e2e/ddev_test.go

@@ -38,7 +38,7 @@ func TestComposeRunDdev(t *testing.T) {
 	}
 	_ = os.Setenv("DDEV_DEBUG", "true")
 
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 	dir, err := os.MkdirTemp("", t.Name()+"-")
 	assert.NilError(t, err)
 
@@ -48,8 +48,8 @@ func TestComposeRunDdev(t *testing.T) {
 	siteName := filepath.Base(dir)
 
 	t.Cleanup(func() {
-		_ = c.RunCmdInDir(dir, "./ddev", "delete", "-Oy")
-		_ = c.RunCmdInDir(dir, "./ddev", "poweroff")
+		_ = c.RunCmdInDir(t, dir, "./ddev", "delete", "-Oy")
+		_ = c.RunCmdInDir(t, dir, "./ddev", "poweroff")
 		_ = os.RemoveAll(dir)
 	})
 
@@ -59,28 +59,28 @@ func TestComposeRunDdev(t *testing.T) {
 	}
 
 	compressedFilename := fmt.Sprintf("ddev_%s-%s.%s.tar.gz", osName, runtime.GOARCH, ddevVersion)
-	c.RunCmdInDir(dir, "curl", "-LO",
+	c.RunCmdInDir(t, dir, "curl", "-LO",
 		fmt.Sprintf("https://github.com/drud/ddev/releases/download/%s/%s",
 			ddevVersion,
 			compressedFilename))
 
-	c.RunCmdInDir(dir, "tar", "-xzf", compressedFilename)
+	c.RunCmdInDir(t, dir, "tar", "-xzf", compressedFilename)
 
 	// Create a simple index.php we can test against.
-	c.RunCmdInDir(dir, "sh", "-c", "echo '<?php\nprint \"ddev is working\";' >index.php")
+	c.RunCmdInDir(t, dir, "sh", "-c", "echo '<?php\nprint \"ddev is working\";' >index.php")
 
-	c.RunCmdInDir(dir, "./ddev", "config", "--auto")
-	c.RunCmdInDir(dir, "./ddev", "config", "global", "--use-docker-compose-from-path")
-	vRes := c.RunCmdInDir(dir, "./ddev", "version")
+	c.RunCmdInDir(t, dir, "./ddev", "config", "--auto")
+	c.RunCmdInDir(t, dir, "./ddev", "config", "global", "--use-docker-compose-from-path")
+	vRes := c.RunCmdInDir(t, dir, "./ddev", "version")
 	out := vRes.Stdout()
 	fmt.Printf("ddev version: %s\n", out)
 
-	c.RunCmdInDir(dir, "./ddev", "poweroff")
+	c.RunCmdInDir(t, dir, "./ddev", "poweroff")
 
-	c.RunCmdInDir(dir, "./ddev", "start", "-y")
+	c.RunCmdInDir(t, dir, "./ddev", "start", "-y")
 
-	curlRes := c.RunCmdInDir(dir, "curl", "-sSL", fmt.Sprintf("http://%s.ddev.site", siteName))
+	curlRes := c.RunCmdInDir(t, dir, "curl", "-sSL", fmt.Sprintf("http://%s.ddev.site", siteName))
 	out = curlRes.Stdout()
 	fmt.Println(out)
-	assert.Assert(c.test, strings.Contains(out, "ddev is working"), "Could not start project")
+	assert.Assert(t, strings.Contains(out, "ddev is working"), "Could not start project")
 }

+ 40 - 41
pkg/e2e/framework.go

@@ -29,12 +29,13 @@ import (
 	"testing"
 	"time"
 
-	"github.com/docker/compose/v2/cmd/compose"
 	"github.com/pkg/errors"
 	"gotest.tools/v3/assert"
 	is "gotest.tools/v3/assert/cmp"
 	"gotest.tools/v3/icmd"
 	"gotest.tools/v3/poll"
+
+	"github.com/docker/compose/v2/cmd/compose"
 )
 
 var (
@@ -56,21 +57,19 @@ func init() {
 	}
 }
 
-// E2eCLI is used to wrap the CLI for end to end testing
-// nolint stutter
-type E2eCLI struct {
-	BinDir    string
+// CLI is used to wrap the CLI for end to end testing
+type CLI struct {
 	ConfigDir string
-	test      *testing.T
 }
 
-// NewParallelE2eCLI returns a configured TestE2eCLI with t.Parallel() set
-func NewParallelE2eCLI(t *testing.T, binDir string) *E2eCLI {
+// NewParallelCLI returns a configured CLI with t.Parallel() set
+func NewParallelCLI(t *testing.T) *CLI {
 	t.Parallel()
-	return newE2eCLI(t, binDir)
+	return NewCLI(t)
 }
 
-func newE2eCLI(t *testing.T, binDir string) *E2eCLI {
+// NewCLI returns a CLI to use for E2E tests
+func NewCLI(t testing.TB) *CLI {
 	d, err := ioutil.TempDir("", "")
 	assert.Check(t, is.Nil(err))
 
@@ -103,7 +102,7 @@ func newE2eCLI(t *testing.T, binDir string) *E2eCLI {
 		}
 	}
 
-	return &E2eCLI{binDir, d, t}
+	return &CLI{ConfigDir: d}
 }
 
 func dirContents(dir string) []string {
@@ -156,7 +155,7 @@ func CopyFile(sourceFile string, destinationFile string) error {
 }
 
 // NewCmd creates a cmd object configured with the test environment set
-func (c *E2eCLI) NewCmd(command string, args ...string) icmd.Cmd {
+func (c *CLI) NewCmd(command string, args ...string) icmd.Cmd {
 	env := append(os.Environ(),
 		"DOCKER_CONFIG="+c.ConfigDir,
 		"KUBECONFIG=invalid",
@@ -168,7 +167,7 @@ func (c *E2eCLI) NewCmd(command string, args ...string) icmd.Cmd {
 }
 
 // NewCmdWithEnv creates a cmd object configured with the test environment set with additional env vars
-func (c *E2eCLI) NewCmdWithEnv(envvars []string, command string, args ...string) icmd.Cmd {
+func (c *CLI) NewCmdWithEnv(envvars []string, command string, args ...string) icmd.Cmd {
 	env := append(os.Environ(),
 		append(envvars,
 			"DOCKER_CONFIG="+c.ConfigDir,
@@ -181,63 +180,63 @@ func (c *E2eCLI) NewCmdWithEnv(envvars []string, command string, args ...string)
 }
 
 // MetricsSocket get the path where test metrics will be sent
-func (c *E2eCLI) MetricsSocket() string {
+func (c *CLI) MetricsSocket() string {
 	return filepath.Join(c.ConfigDir, "./docker-cli.sock")
 }
 
 // NewDockerCmd creates a docker cmd without running it
-func (c *E2eCLI) NewDockerCmd(args ...string) icmd.Cmd {
+func (c *CLI) NewDockerCmd(args ...string) icmd.Cmd {
 	return c.NewCmd(DockerExecutableName, args...)
 }
 
 // RunDockerOrExitError runs a docker command and returns a result
-func (c *E2eCLI) RunDockerOrExitError(args ...string) *icmd.Result {
-	fmt.Printf("\t[%s] docker %s\n", c.test.Name(), strings.Join(args, " "))
+func (c *CLI) RunDockerOrExitError(t testing.TB, args ...string) *icmd.Result {
+	fmt.Printf("\t[%s] docker %s\n", t.Name(), strings.Join(args, " "))
 	return icmd.RunCmd(c.NewDockerCmd(args...))
 }
 
 // RunCmd runs a command, expects no error and returns a result
-func (c *E2eCLI) RunCmd(args ...string) *icmd.Result {
-	fmt.Printf("\t[%s] %s\n", c.test.Name(), strings.Join(args, " "))
-	assert.Assert(c.test, len(args) >= 1, "require at least one command in parameters")
+func (c *CLI) RunCmd(t testing.TB, args ...string) *icmd.Result {
+	fmt.Printf("\t[%s] %s\n", t.Name(), strings.Join(args, " "))
+	assert.Assert(t, len(args) >= 1, "require at least one command in parameters")
 	res := icmd.RunCmd(c.NewCmd(args[0], args[1:]...))
-	res.Assert(c.test, icmd.Success)
+	res.Assert(t, icmd.Success)
 	return res
 }
 
 // RunCmdInDir runs a command in a given dir, expects no error and returns a result
-func (c *E2eCLI) RunCmdInDir(dir string, args ...string) *icmd.Result {
-	fmt.Printf("\t[%s] %s\n", c.test.Name(), strings.Join(args, " "))
-	assert.Assert(c.test, len(args) >= 1, "require at least one command in parameters")
+func (c *CLI) RunCmdInDir(t testing.TB, dir string, args ...string) *icmd.Result {
+	fmt.Printf("\t[%s] %s\n", t.Name(), strings.Join(args, " "))
+	assert.Assert(t, len(args) >= 1, "require at least one command in parameters")
 	cmd := c.NewCmd(args[0], args[1:]...)
 	cmd.Dir = dir
 	res := icmd.RunCmd(cmd)
-	res.Assert(c.test, icmd.Success)
+	res.Assert(t, icmd.Success)
 	return res
 }
 
 // RunDockerCmd runs a docker command, expects no error and returns a result
-func (c *E2eCLI) RunDockerCmd(args ...string) *icmd.Result {
+func (c *CLI) RunDockerCmd(t testing.TB, args ...string) *icmd.Result {
 	if len(args) > 0 && args[0] == compose.PluginName {
-		c.test.Fatal("This test called 'RunDockerCmd' for 'compose'. Please prefer 'RunDockerComposeCmd' to be able to test as a plugin and standalone")
+		t.Fatal("This test called 'RunDockerCmd' for 'compose'. Please prefer 'RunDockerComposeCmd' to be able to test as a plugin and standalone")
 	}
-	res := c.RunDockerOrExitError(args...)
-	res.Assert(c.test, icmd.Success)
+	res := c.RunDockerOrExitError(t, args...)
+	res.Assert(t, icmd.Success)
 	return res
 }
 
 // RunDockerComposeCmd runs a docker compose command, expects no error and returns a result
-func (c *E2eCLI) RunDockerComposeCmd(args ...string) *icmd.Result {
-	res := c.RunDockerComposeCmdNoCheck(args...)
-	res.Assert(c.test, icmd.Success)
+func (c *CLI) RunDockerComposeCmd(t testing.TB, args ...string) *icmd.Result {
+	res := c.RunDockerComposeCmdNoCheck(t, args...)
+	res.Assert(t, icmd.Success)
 	return res
 }
 
 // RunDockerComposeCmdNoCheck runs a docker compose command, don't presume of any expectation and returns a result
-func (c *E2eCLI) RunDockerComposeCmdNoCheck(args ...string) *icmd.Result {
+func (c *CLI) RunDockerComposeCmdNoCheck(t testing.TB, args ...string) *icmd.Result {
 	if composeStandaloneMode {
 		composeBinary, err := findExecutable(DockerComposeExecutableName, []string{"../../bin", "../../../bin"})
-		assert.NilError(c.test, err)
+		assert.NilError(t, err)
 		return icmd.RunCmd(c.NewCmd(composeBinary, args...))
 	}
 	args = append([]string{"compose"}, args...)
@@ -252,22 +251,22 @@ func StdoutContains(expected string) func(*icmd.Result) bool {
 }
 
 // WaitForCmdResult try to execute a cmd until resulting output matches given predicate
-func (c *E2eCLI) WaitForCmdResult(command icmd.Cmd, predicate func(*icmd.Result) bool, timeout time.Duration, delay time.Duration) {
-	assert.Assert(c.test, timeout.Nanoseconds() > delay.Nanoseconds(), "timeout must be greater than delay")
+func (c *CLI) WaitForCmdResult(t testing.TB, command icmd.Cmd, predicate func(*icmd.Result) bool, timeout time.Duration, delay time.Duration) {
+	assert.Assert(t, timeout.Nanoseconds() > delay.Nanoseconds(), "timeout must be greater than delay")
 	var res *icmd.Result
 	checkStopped := func(logt poll.LogT) poll.Result {
-		fmt.Printf("\t[%s] %s\n", c.test.Name(), strings.Join(command.Command, " "))
+		fmt.Printf("\t[%s] %s\n", t.Name(), strings.Join(command.Command, " "))
 		res = icmd.RunCmd(command)
 		if !predicate(res) {
 			return poll.Continue("Cmd output did not match requirement: %q", res.Combined())
 		}
 		return poll.Success()
 	}
-	poll.WaitOn(c.test, checkStopped, poll.WithDelay(delay), poll.WithTimeout(timeout))
+	poll.WaitOn(t, checkStopped, poll.WithDelay(delay), poll.WithTimeout(timeout))
 }
 
 // WaitForCondition wait for predicate to execute to true
-func (c *E2eCLI) WaitForCondition(predicate func() (bool, string), timeout time.Duration, delay time.Duration) {
+func (c *CLI) WaitForCondition(t testing.TB, predicate func() (bool, string), timeout time.Duration, delay time.Duration) {
 	checkStopped := func(logt poll.LogT) poll.Result {
 		pass, description := predicate()
 		if !pass {
@@ -275,7 +274,7 @@ func (c *E2eCLI) WaitForCondition(predicate func() (bool, string), timeout time.
 		}
 		return poll.Success()
 	}
-	poll.WaitOn(c.test, checkStopped, poll.WithDelay(delay), poll.WithTimeout(timeout))
+	poll.WaitOn(t, checkStopped, poll.WithDelay(delay), poll.WithTimeout(timeout))
 }
 
 // Lines split output into lines
@@ -286,7 +285,7 @@ func Lines(output string) []string {
 // HTTPGetWithRetry performs an HTTP GET on an `endpoint`, using retryDelay also as a request timeout.
 // In the case of an error or the response status is not the expeted one, it retries the same request,
 // returning the response body as a string (empty if we could not reach it)
-func HTTPGetWithRetry(t *testing.T, endpoint string, expectedStatus int, retryDelay time.Duration, timeout time.Duration) string {
+func HTTPGetWithRetry(t testing.TB, endpoint string, expectedStatus int, retryDelay time.Duration, timeout time.Duration) string {
 	var (
 		r   *http.Response
 		err error

+ 10 - 9
pkg/e2e/ipc_test.go

@@ -25,39 +25,40 @@ import (
 )
 
 func TestIPC(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	const projectName = "ipc_e2e"
 	var cid string
 	t.Run("create ipc mode container", func(t *testing.T) {
-		res := c.RunDockerCmd("run", "-d", "--rm", "--ipc=shareable", "--name", "ipc_mode_container", "alpine", "top")
+		res := c.RunDockerCmd(t, "run", "-d", "--rm", "--ipc=shareable", "--name", "ipc_mode_container", "alpine",
+			"top")
 		cid = strings.Trim(res.Stdout(), "\n")
 	})
 
 	t.Run("up", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/ipc-test/compose.yaml", "--project-name", projectName, "up", "-d")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/ipc-test/compose.yaml", "--project-name", projectName, "up", "-d")
 	})
 
 	t.Run("check running project", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-p", projectName, "ps")
+		res := c.RunDockerComposeCmd(t, "-p", projectName, "ps")
 		res.Assert(t, icmd.Expected{Out: `shareable`})
 	})
 
 	t.Run("check ipcmode in container inspect", func(t *testing.T) {
-		res := c.RunDockerCmd("inspect", projectName+"-shareable-1")
+		res := c.RunDockerCmd(t, "inspect", projectName+"-shareable-1")
 		res.Assert(t, icmd.Expected{Out: `"IpcMode": "shareable",`})
 
-		res = c.RunDockerCmd("inspect", projectName+"-service-1")
+		res = c.RunDockerCmd(t, "inspect", projectName+"-service-1")
 		res.Assert(t, icmd.Expected{Out: `"IpcMode": "container:`})
 
-		res = c.RunDockerCmd("inspect", projectName+"-container-1")
+		res = c.RunDockerCmd(t, "inspect", projectName+"-container-1")
 		res.Assert(t, icmd.Expected{Out: fmt.Sprintf(`"IpcMode": "container:%s",`, cid)})
 	})
 
 	t.Run("down", func(t *testing.T) {
-		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
+		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})
 	t.Run("remove ipc mode container", func(t *testing.T) {
-		_ = c.RunDockerCmd("rm", "-f", "ipc_mode_container")
+		_ = c.RunDockerCmd(t, "rm", "-f", "ipc_mode_container")
 	})
 }

+ 6 - 6
pkg/e2e/logs_test.go

@@ -26,33 +26,33 @@ import (
 )
 
 func TestLocalComposeLogs(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	const projectName = "compose-e2e-logs"
 
 	t.Run("up", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/logs-test/compose.yaml", "--project-name", projectName, "up", "-d")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/logs-test/compose.yaml", "--project-name", projectName, "up", "-d")
 	})
 
 	t.Run("logs", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("--project-name", projectName, "logs")
+		res := c.RunDockerComposeCmd(t, "--project-name", projectName, "logs")
 		res.Assert(t, icmd.Expected{Out: `PING localhost (127.0.0.1)`})
 		res.Assert(t, icmd.Expected{Out: `hello`})
 	})
 
 	t.Run("logs ping", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("--project-name", projectName, "logs", "ping")
+		res := c.RunDockerComposeCmd(t, "--project-name", projectName, "logs", "ping")
 		res.Assert(t, icmd.Expected{Out: `PING localhost (127.0.0.1)`})
 		assert.Assert(t, !strings.Contains(res.Stdout(), "hello"))
 	})
 
 	t.Run("logs hello", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("--project-name", projectName, "logs", "hello", "ping")
+		res := c.RunDockerComposeCmd(t, "--project-name", projectName, "logs", "hello", "ping")
 		res.Assert(t, icmd.Expected{Out: `PING localhost (127.0.0.1)`})
 		res.Assert(t, icmd.Expected{Out: `hello`})
 	})
 
 	t.Run("down", func(t *testing.T) {
-		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
+		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})
 }

+ 11 - 11
pkg/e2e/metrics_test.go

@@ -24,32 +24,32 @@ import (
 )
 
 func TestComposeMetrics(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	t.Run("catch specific failure metrics", func(t *testing.T) {
-		res := c.RunDockerOrExitError("compose", "-f", "fixtures/does-not-exist/compose.yaml", "build")
+		res := c.RunDockerOrExitError(t, "compose", "-f", "fixtures/does-not-exist/compose.yaml", "build")
 		expectedErr := "fixtures/does-not-exist/compose.yaml: no such file or directory"
 		if runtime.GOOS == "windows" {
 			expectedErr = "does-not-exist\\compose.yaml: The system cannot find the path specified"
 		}
 		res.Assert(t, icmd.Expected{ExitCode: 14, Err: expectedErr})
-		res = c.RunDockerOrExitError("compose", "-f", "fixtures/wrong-composefile/compose.yaml", "up", "-d")
+		res = c.RunDockerOrExitError(t, "compose", "-f", "fixtures/wrong-composefile/compose.yaml", "up", "-d")
 		res.Assert(t, icmd.Expected{ExitCode: 15, Err: "services.simple Additional property wrongField is not allowed"})
-		res = c.RunDockerOrExitError("compose", "up")
+		res = c.RunDockerOrExitError(t, "compose", "up")
 		res.Assert(t, icmd.Expected{ExitCode: 14, Err: "no configuration file provided: not found"})
-		res = c.RunDockerOrExitError("compose", "up", "-f", "fixtures/wrong-composefile/compose.yaml")
+		res = c.RunDockerOrExitError(t, "compose", "up", "-f", "fixtures/wrong-composefile/compose.yaml")
 		res.Assert(t, icmd.Expected{ExitCode: 16, Err: "unknown shorthand flag: 'f' in -f"})
-		res = c.RunDockerOrExitError("compose", "up", "--file", "fixtures/wrong-composefile/compose.yaml")
+		res = c.RunDockerOrExitError(t, "compose", "up", "--file", "fixtures/wrong-composefile/compose.yaml")
 		res.Assert(t, icmd.Expected{ExitCode: 16, Err: "unknown flag: --file"})
-		res = c.RunDockerOrExitError("compose", "donw", "--file", "fixtures/wrong-composefile/compose.yaml")
+		res = c.RunDockerOrExitError(t, "compose", "donw", "--file", "fixtures/wrong-composefile/compose.yaml")
 		res.Assert(t, icmd.Expected{ExitCode: 16, Err: `unknown docker command: "compose donw"`})
-		res = c.RunDockerOrExitError("compose", "--file", "fixtures/wrong-composefile/build-error.yml", "build")
+		res = c.RunDockerOrExitError(t, "compose", "--file", "fixtures/wrong-composefile/build-error.yml", "build")
 		res.Assert(t, icmd.Expected{ExitCode: 17, Err: `line 17: unknown instruction: WRONG`})
-		res = c.RunDockerOrExitError("compose", "--file", "fixtures/wrong-composefile/build-error.yml", "up")
+		res = c.RunDockerOrExitError(t, "compose", "--file", "fixtures/wrong-composefile/build-error.yml", "up")
 		res.Assert(t, icmd.Expected{ExitCode: 17, Err: `line 17: unknown instruction: WRONG`})
-		res = c.RunDockerOrExitError("compose", "--file", "fixtures/wrong-composefile/unknown-image.yml", "pull")
+		res = c.RunDockerOrExitError(t, "compose", "--file", "fixtures/wrong-composefile/unknown-image.yml", "pull")
 		res.Assert(t, icmd.Expected{ExitCode: 18, Err: `pull access denied for unknownimage, repository does not exist or may require 'docker login'`})
-		res = c.RunDockerOrExitError("compose", "--file", "fixtures/wrong-composefile/unknown-image.yml", "up")
+		res = c.RunDockerOrExitError(t, "compose", "--file", "fixtures/wrong-composefile/unknown-image.yml", "up")
 		res.Assert(t, icmd.Expected{ExitCode: 18, Err: `pull access denied for unknownimage, repository does not exist or may require 'docker login'`})
 	})
 }

+ 36 - 27
pkg/e2e/networks_test.go

@@ -27,126 +27,135 @@ import (
 )
 
 func TestNetworks(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	// fixture is shared with TestNetworkModes and is not safe to run concurrently
+	c := NewCLI(t)
 
 	const projectName = "network_e2e"
 
 	t.Run("ensure we do not reuse previous networks", func(t *testing.T) {
-		c.RunDockerOrExitError("network", "rm", projectName+"_dbnet")
-		c.RunDockerOrExitError("network", "rm", "microservices")
+		c.RunDockerOrExitError(t, "network", "rm", projectName+"_dbnet")
+		c.RunDockerOrExitError(t, "network", "rm", "microservices")
 	})
 
 	t.Run("up", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/network-test/compose.yaml", "--project-name", projectName, "up", "-d")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/network-test/compose.yaml", "--project-name", projectName, "up",
+			"-d")
 	})
 
 	t.Run("check running project", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-p", projectName, "ps")
+		res := c.RunDockerComposeCmd(t, "-p", projectName, "ps")
 		res.Assert(t, icmd.Expected{Out: `web`})
 
 		endpoint := "http://localhost:80"
 		output := HTTPGetWithRetry(t, endpoint+"/words/noun", http.StatusOK, 2*time.Second, 20*time.Second)
 		assert.Assert(t, strings.Contains(output, `"word":`))
 
-		res = c.RunDockerCmd("network", "ls")
+		res = c.RunDockerCmd(t, "network", "ls")
 		res.Assert(t, icmd.Expected{Out: projectName + "_dbnet"})
 		res.Assert(t, icmd.Expected{Out: "microservices"})
 	})
 
 	t.Run("port", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("--project-name", projectName, "port", "words", "8080")
+		res := c.RunDockerComposeCmd(t, "--project-name", projectName, "port", "words", "8080")
 		res.Assert(t, icmd.Expected{Out: `0.0.0.0:8080`})
 	})
 
 	t.Run("down", func(t *testing.T) {
-		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
+		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})
 
 	t.Run("check networks after down", func(t *testing.T) {
-		res := c.RunDockerCmd("network", "ls")
+		res := c.RunDockerCmd(t, "network", "ls")
 		assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined())
 		assert.Assert(t, !strings.Contains(res.Combined(), "microservices"), res.Combined())
 	})
 }
 
-func TestNetworkAliasses(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+func TestNetworkAliases(t *testing.T) {
+	c := NewParallelCLI(t)
 
 	const projectName = "network_alias_e2e"
 
 	t.Run("up", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/network-alias/compose.yaml", "--project-name", projectName, "up", "-d")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/network-alias/compose.yaml", "--project-name", projectName, "up",
+			"-d")
 	})
 
 	t.Run("curl alias", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/network-alias/compose.yaml", "--project-name", projectName, "exec", "-T", "container1", "curl", "http://alias-of-container2/")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/network-alias/compose.yaml", "--project-name", projectName,
+			"exec", "-T", "container1", "curl", "http://alias-of-container2/")
 		assert.Assert(t, strings.Contains(res.Stdout(), "Welcome to nginx!"), res.Stdout())
 	})
 
 	t.Run("curl links", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/network-alias/compose.yaml", "--project-name", projectName, "exec", "-T", "container1", "curl", "http://container/")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/network-alias/compose.yaml", "--project-name", projectName,
+			"exec", "-T", "container1", "curl", "http://container/")
 		assert.Assert(t, strings.Contains(res.Stdout(), "Welcome to nginx!"), res.Stdout())
 	})
 
 	t.Run("down", func(t *testing.T) {
-		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
+		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})
 }
 
 func TestNetworkLinks(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	const projectName = "network_link_e2e"
 
 	t.Run("up", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/network-links/compose.yaml", "--project-name", projectName, "up", "-d")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/network-links/compose.yaml", "--project-name", projectName, "up",
+			"-d")
 	})
 
 	t.Run("curl links in default bridge network", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/network-links/compose.yaml", "--project-name", projectName, "exec", "-T", "container2", "curl", "http://container1/")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/network-links/compose.yaml", "--project-name", projectName,
+			"exec", "-T", "container2", "curl", "http://container1/")
 		assert.Assert(t, strings.Contains(res.Stdout(), "Welcome to nginx!"), res.Stdout())
 	})
 
 	t.Run("down", func(t *testing.T) {
-		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
+		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})
 }
 
 func TestIPAMConfig(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	const projectName = "ipam_e2e"
 
 	t.Run("ensure we do not reuse previous networks", func(t *testing.T) {
-		c.RunDockerOrExitError("network", "rm", projectName+"_default")
+		c.RunDockerOrExitError(t, "network", "rm", projectName+"_default")
 	})
 
 	t.Run("up", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/ipam/compose.yaml", "--project-name", projectName, "up", "-d")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/ipam/compose.yaml", "--project-name", projectName, "up", "-d")
 	})
 
 	t.Run("ensure service get fixed IP assigned", func(t *testing.T) {
-		res := c.RunDockerCmd("inspect", projectName+"-foo-1", "-f", "{{ .NetworkSettings.Networks."+projectName+"_default.IPAddress }}")
+		res := c.RunDockerCmd(t, "inspect", projectName+"-foo-1", "-f",
+			"{{ .NetworkSettings.Networks."+projectName+"_default.IPAddress }}")
 		res.Assert(t, icmd.Expected{Out: "10.1.0.100"})
 	})
 
 	t.Run("down", func(t *testing.T) {
-		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
+		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})
 }
 
 func TestNetworkModes(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	// fixture is shared with TestNetworks and is not safe to run concurrently
+	c := NewCLI(t)
 
 	const projectName = "network_mode_service_run"
 
 	t.Run("run with service mode dependency", func(t *testing.T) {
-		res := c.RunDockerOrExitError("compose", "-f", "./fixtures/network-test/compose.yaml", "--project-name", projectName, "run", "-T", "mydb", "echo", "success")
+		res := c.RunDockerOrExitError(t, "compose", "-f", "./fixtures/network-test/compose.yaml", "--project-name", projectName, "run", "-T", "mydb", "echo", "success")
 		res.Assert(t, icmd.Expected{Out: "success"})
 
 	})
 
 	t.Run("down", func(t *testing.T) {
-		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
+		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})
 }

+ 6 - 5
pkg/e2e/ps_test.go

@@ -27,20 +27,20 @@ import (
 )
 
 func TestPs(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 	const projectName = "e2e-ps"
 
-	res := c.RunDockerComposeCmd("-f", "./fixtures/ps-test/compose.yaml", "--project-name", projectName, "up", "-d")
+	res := c.RunDockerComposeCmd(t, "-f", "./fixtures/ps-test/compose.yaml", "--project-name", projectName, "up", "-d")
 	if assert.NoError(t, res.Error) {
 		t.Cleanup(func() {
-			_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
+			_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 		})
 	}
 
 	assert.Contains(t, res.Combined(), "Container e2e-ps-busybox-1  Started", res.Combined())
 
 	t.Run("pretty", func(t *testing.T) {
-		res = c.RunDockerComposeCmd("-f", "./fixtures/ps-test/compose.yaml", "--project-name", projectName, "ps")
+		res = c.RunDockerComposeCmd(t, "-f", "./fixtures/ps-test/compose.yaml", "--project-name", projectName, "ps")
 		lines := strings.Split(res.Combined(), "\n")
 		assert.Equal(t, 4, len(lines))
 		count := 0
@@ -58,7 +58,8 @@ func TestPs(t *testing.T) {
 	})
 
 	t.Run("json", func(t *testing.T) {
-		res = c.RunDockerComposeCmd("-f", "./fixtures/ps-test/compose.yaml", "--project-name", projectName, "ps", "--format", "json")
+		res = c.RunDockerComposeCmd(t, "-f", "./fixtures/ps-test/compose.yaml", "--project-name", projectName, "ps",
+			"--format", "json")
 		var output []api.ContainerSummary
 		err := json.Unmarshal([]byte(res.Combined()), &output)
 		assert.NoError(t, err)

+ 9 - 10
pkg/e2e/restart_test.go

@@ -27,7 +27,7 @@ import (
 )
 
 func TestRestart(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 	const projectName = "e2e-restart"
 
 	getServiceRegx := func(service string, status string) string {
@@ -38,27 +38,26 @@ func TestRestart(t *testing.T) {
 
 	t.Run("Up a project", func(t *testing.T) {
 		// This is just to ensure the containers do NOT exist
-		c.RunDockerOrExitError("compose", "--project-name", projectName, "down")
+		c.RunDockerOrExitError(t, "compose", "--project-name", projectName, "down")
 
-		res := c.RunDockerOrExitError("compose", "-f", "./fixtures/restart-test/compose.yaml", "--project-name", projectName, "up", "-d")
+		res := c.RunDockerOrExitError(t, "compose", "-f", "./fixtures/restart-test/compose.yaml", "--project-name", projectName, "up", "-d")
 		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-restart-restart-1  Started"), res.Combined())
 
-		c.WaitForCmdResult(c.NewDockerCmd("compose", "--project-name", projectName, "ps", "-a", "--format", "json"),
-			StdoutContains(`"State":"exited"`),
-			10*time.Second, 1*time.Second)
+		c.WaitForCmdResult(t, c.NewDockerCmd("compose", "--project-name", projectName, "ps", "-a", "--format", "json"),
+			StdoutContains(`"State":"exited"`), 10*time.Second, 1*time.Second)
 
-		res = c.RunDockerOrExitError("compose", "--project-name", projectName, "ps", "-a")
+		res = c.RunDockerOrExitError(t, "compose", "--project-name", projectName, "ps", "-a")
 		testify.Regexp(t, getServiceRegx("restart", "exited"), res.Stdout())
 
-		_ = c.RunDockerOrExitError("compose", "-f", "./fixtures/restart-test/compose.yaml", "--project-name", projectName, "restart")
+		_ = c.RunDockerOrExitError(t, "compose", "-f", "./fixtures/restart-test/compose.yaml", "--project-name", projectName, "restart")
 
 		// Give the same time but it must NOT exit
 		time.Sleep(time.Second)
 
-		res = c.RunDockerOrExitError("compose", "--project-name", projectName, "ps")
+		res = c.RunDockerOrExitError(t, "compose", "--project-name", projectName, "ps")
 		testify.Regexp(t, getServiceRegx("restart", "running"), res.Stdout())
 
 		// Clean up
-		c.RunDockerOrExitError("compose", "--project-name", projectName, "down")
+		c.RunDockerOrExitError(t, "compose", "--project-name", projectName, "down")
 	})
 }

+ 19 - 14
pkg/e2e/scan_message_test.go

@@ -30,39 +30,44 @@ import (
 )
 
 func TestDisplayScanMessageAfterBuild(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	// assert docker scan plugin is available
-	c.RunDockerOrExitError("scan", "--help")
+	c.RunDockerOrExitError(t, "scan", "--help")
 
 	t.Run("display on compose build", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test-compose-build", "build")
-		defer c.RunDockerOrExitError("rmi", "-f", "scan-msg-test-compose-build_nginx")
+		res := c.RunDockerComposeCmd(t, "-f", "fixtures/simple-build-test/compose.yaml", "-p",
+			"scan-msg-test-compose-build", "build")
+		defer c.RunDockerOrExitError(t, "rmi", "-f", "scan-msg-test-compose-build_nginx")
 		res.Assert(t, icmd.Expected{Err: utils.ScanSuggestMsg})
 	})
 
 	t.Run("do not display on compose build with quiet flag", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test-quiet", "build", "--quiet")
+		res := c.RunDockerComposeCmd(t, "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test-quiet",
+			"build", "--quiet")
 		assert.Assert(t, !strings.Contains(res.Combined(), "docker scan"), res.Combined())
-		res = c.RunDockerCmd("rmi", "-f", "scan-msg-test-quiet_nginx")
+		res = c.RunDockerCmd(t, "rmi", "-f", "scan-msg-test-quiet_nginx")
 		assert.Assert(t, !strings.Contains(res.Combined(), "No such image"))
 
-		res = c.RunDockerComposeCmd("-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test-q", "build", "-q")
-		defer c.RunDockerOrExitError("rmi", "-f", "scan-msg-test-q_nginx")
+		res = c.RunDockerComposeCmd(t, "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test-q",
+			"build", "-q")
+		defer c.RunDockerOrExitError(t, "rmi", "-f", "scan-msg-test-q_nginx")
 		assert.Assert(t, !strings.Contains(res.Combined(), "docker scan"), res.Combined())
 	})
 
-	_ = c.RunDockerOrExitError("rmi", "scan-msg-test_nginx")
+	_ = c.RunDockerOrExitError(t, "rmi", "scan-msg-test_nginx")
 
 	t.Run("display on compose up if image is built", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test", "up", "-d")
-		defer c.RunDockerOrExitError("compose", "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test", "down")
+		res := c.RunDockerComposeCmd(t, "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test", "up",
+			"-d")
+		defer c.RunDockerOrExitError(t, "compose", "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test", "down")
 		res.Assert(t, icmd.Expected{Err: utils.ScanSuggestMsg})
 	})
 
 	t.Run("do not display on compose up if no image built", func(t *testing.T) { // re-run the same Compose aproject
-		res := c.RunDockerComposeCmd("-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test", "up", "-d")
-		defer c.RunDockerOrExitError("compose", "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test", "down", "--rmi", "all")
+		res := c.RunDockerComposeCmd(t, "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test", "up",
+			"-d")
+		defer c.RunDockerOrExitError(t, "compose", "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test", "down", "--rmi", "all")
 		assert.Assert(t, !strings.Contains(res.Combined(), "docker scan"), res.Combined())
 	})
 
@@ -72,7 +77,7 @@ func TestDisplayScanMessageAfterBuild(t *testing.T) {
 		err := ioutil.WriteFile(scanConfigFile, []byte(`{"optin":true}`), 0644)
 		assert.NilError(t, err)
 
-		res := c.RunDockerCmd("build", "-t", "test-image-scan-msg", "fixtures/simple-build-test/nginx-build")
+		res := c.RunDockerCmd(t, "build", "-t", "test-image-scan-msg", "fixtures/simple-build-test/nginx-build")
 		assert.Assert(t, !strings.Contains(res.Combined(), "docker scan"), res.Combined())
 	})
 }

+ 1 - 1
pkg/e2e/secrets_test.go

@@ -23,7 +23,7 @@ import (
 )
 
 func TestSecretFromEnv(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	t.Run("compose run", func(t *testing.T) {
 		res := icmd.RunCmd(c.NewDockerCmd("compose", "-f", "./fixtures/env-secret/compose.yaml", "run", "foo"),

+ 3 - 3
pkg/e2e/start_fail_test.go

@@ -23,11 +23,11 @@ import (
 )
 
 func TestStartFail(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 	const projectName = "e2e-start-fail"
 
-	res := c.RunDockerOrExitError("compose", "-f", "fixtures/start-fail/compose.yaml", "--project-name", projectName, "up", "-d")
+	res := c.RunDockerOrExitError(t, "compose", "-f", "fixtures/start-fail/compose.yaml", "--project-name", projectName, "up", "-d")
 	res.Assert(t, icmd.Expected{ExitCode: 1, Err: `container for service "fail" is unhealthy`})
 
-	c.RunDockerComposeCmd("--project-name", projectName, "down")
+	c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 }

+ 30 - 27
pkg/e2e/start_stop_test.go

@@ -26,7 +26,7 @@ import (
 )
 
 func TestStartStop(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 	const projectName = "e2e-start-stop-no-dependencies"
 
 	getProjectRegx := func(status string) string {
@@ -42,103 +42,106 @@ func TestStartStop(t *testing.T) {
 	}
 
 	t.Run("Up a project", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "up", "-d")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "up",
+			"-d")
 		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-no-dependencies-simple-1  Started"), res.Combined())
 
-		res = c.RunDockerComposeCmd("ls", "--all")
+		res = c.RunDockerComposeCmd(t, "ls", "--all")
 		testify.Regexp(t, getProjectRegx("running"), res.Stdout())
 
-		res = c.RunDockerComposeCmd("--project-name", projectName, "ps")
+		res = c.RunDockerComposeCmd(t, "--project-name", projectName, "ps")
 		testify.Regexp(t, getServiceRegx("simple", "running"), res.Stdout())
 		testify.Regexp(t, getServiceRegx("another", "running"), res.Stdout())
 	})
 
 	t.Run("stop project", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "stop")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "stop")
 
-		res := c.RunDockerComposeCmd("ls")
+		res := c.RunDockerComposeCmd(t, "ls")
 		assert.Assert(t, !strings.Contains(res.Combined(), "e2e-start-stop-no-dependencies"), res.Combined())
 
-		res = c.RunDockerComposeCmd("ls", "--all")
+		res = c.RunDockerComposeCmd(t, "ls", "--all")
 		testify.Regexp(t, getProjectRegx("exited"), res.Stdout())
 
-		res = c.RunDockerComposeCmd("--project-name", projectName, "ps")
+		res = c.RunDockerComposeCmd(t, "--project-name", projectName, "ps")
 		assert.Assert(t, !strings.Contains(res.Combined(), "e2e-start-stop-no-dependencies-words-1"), res.Combined())
 
-		res = c.RunDockerComposeCmd("--project-name", projectName, "ps", "--all")
+		res = c.RunDockerComposeCmd(t, "--project-name", projectName, "ps", "--all")
 		testify.Regexp(t, getServiceRegx("simple", "exited"), res.Stdout())
 		testify.Regexp(t, getServiceRegx("another", "exited"), res.Stdout())
 	})
 
 	t.Run("start project", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "start")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "start")
 
-		res := c.RunDockerComposeCmd("ls")
+		res := c.RunDockerComposeCmd(t, "ls")
 		testify.Regexp(t, getProjectRegx("running"), res.Stdout())
 	})
 
 	t.Run("pause project", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "pause")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "pause")
 
-		res := c.RunDockerComposeCmd("ls", "--all")
+		res := c.RunDockerComposeCmd(t, "ls", "--all")
 		testify.Regexp(t, getProjectRegx("paused"), res.Stdout())
 	})
 
 	t.Run("unpause project", func(t *testing.T) {
-		c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "unpause")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "unpause")
 
-		res := c.RunDockerComposeCmd("ls")
+		res := c.RunDockerComposeCmd(t, "ls")
 		testify.Regexp(t, getProjectRegx("running"), res.Stdout())
 	})
 
 	t.Run("down", func(t *testing.T) {
-		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
+		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})
 }
 
 func TestStartStopWithDependencies(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 	const projectName = "e2e-start-stop-with-dependencies"
 
-	defer c.RunDockerComposeCmd("--project-name", projectName, "rm", "-fsv")
+	defer c.RunDockerComposeCmd(t, "--project-name", projectName, "rm", "-fsv")
 
 	t.Run("Up", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("-f", "./fixtures/dependencies/compose.yaml", "--project-name", projectName, "up", "-d")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/dependencies/compose.yaml", "--project-name", projectName,
+			"up", "-d")
 		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1  Started"), res.Combined())
 		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1  Started"), res.Combined())
 	})
 
 	t.Run("stop foo", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("--project-name", projectName, "stop", "foo")
+		res := c.RunDockerComposeCmd(t, "--project-name", projectName, "stop", "foo")
 
 		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1  Stopped"), res.Combined())
 
-		res = c.RunDockerComposeCmd("--project-name", projectName, "ps", "--status", "running")
+		res = c.RunDockerComposeCmd(t, "--project-name", projectName, "ps", "--status", "running")
 		assert.Assert(t, strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-bar-1"), res.Combined())
 		assert.Assert(t, !strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-foo-1"), res.Combined())
 	})
 
 	t.Run("start foo", func(t *testing.T) {
-		res := c.RunDockerComposeCmd("--project-name", projectName, "stop")
+		res := c.RunDockerComposeCmd(t, "--project-name", projectName, "stop")
 		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1  Stopped"), res.Combined())
 
-		res = c.RunDockerComposeCmd("--project-name", projectName, "start", "foo")
+		res = c.RunDockerComposeCmd(t, "--project-name", projectName, "start", "foo")
 		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1  Started"), res.Combined())
 		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1  Started"), res.Combined())
 
-		res = c.RunDockerComposeCmd("--project-name", projectName, "ps", "--status", "running")
+		res = c.RunDockerComposeCmd(t, "--project-name", projectName, "ps", "--status", "running")
 		assert.Assert(t, strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-bar-1"), res.Combined())
 		assert.Assert(t, strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-foo-1"), res.Combined())
 	})
 
 	t.Run("Up no-deps links", func(t *testing.T) {
-		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
-		res := c.RunDockerComposeCmd("-f", "./fixtures/links/compose.yaml", "--project-name", projectName, "up", "--no-deps", "-d", "foo")
+		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/links/compose.yaml", "--project-name", projectName, "up",
+			"--no-deps", "-d", "foo")
 		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1  Started"), res.Combined())
 		assert.Assert(t, !strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1  Started"), res.Combined())
 	})
 
 	t.Run("down", func(t *testing.T) {
-		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
+		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})
 }

+ 24 - 23
pkg/e2e/volumes_test.go

@@ -29,16 +29,17 @@ import (
 )
 
 func TestLocalComposeVolume(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 
 	const projectName = "compose-e2e-volume"
 
 	t.Run("up with build and no image name, volume", func(t *testing.T) {
 		// ensure local test run does not reuse previously build image
-		c.RunDockerOrExitError("rmi", "compose-e2e-volume_nginx")
-		c.RunDockerOrExitError("volume", "rm", projectName+"_staticVol")
-		c.RunDockerOrExitError("volume", "rm", "myvolume")
-		c.RunDockerComposeCmd("--project-directory", "fixtures/volume-test", "--project-name", projectName, "up", "-d")
+		c.RunDockerOrExitError(t, "rmi", "compose-e2e-volume_nginx")
+		c.RunDockerOrExitError(t, "volume", "rm", projectName+"_staticVol")
+		c.RunDockerOrExitError(t, "volume", "rm", "myvolume")
+		c.RunDockerComposeCmd(t, "--project-directory", "fixtures/volume-test", "--project-name", projectName, "up",
+			"-d")
 	})
 
 	t.Run("access bind mount data", func(t *testing.T) {
@@ -47,7 +48,7 @@ func TestLocalComposeVolume(t *testing.T) {
 	})
 
 	t.Run("check container volume specs", func(t *testing.T) {
-		res := c.RunDockerCmd("inspect", "compose-e2e-volume-nginx2-1", "--format", "{{ json .Mounts }}")
+		res := c.RunDockerCmd(t, "inspect", "compose-e2e-volume-nginx2-1", "--format", "{{ json .Mounts }}")
 		output := res.Stdout()
 		// nolint
 		assert.Assert(t, strings.Contains(output, `"Destination":"/usr/src/app/node_modules","Driver":"local","Mode":"z","RW":true,"Propagation":""`), output)
@@ -55,17 +56,17 @@ func TestLocalComposeVolume(t *testing.T) {
 	})
 
 	t.Run("check config content", func(t *testing.T) {
-		output := c.RunDockerCmd("exec", "compose-e2e-volume-nginx2-1", "cat", "/myconfig").Stdout()
+		output := c.RunDockerCmd(t, "exec", "compose-e2e-volume-nginx2-1", "cat", "/myconfig").Stdout()
 		assert.Assert(t, strings.Contains(output, `Hello from Nginx container`), output)
 	})
 
 	t.Run("check secrets content", func(t *testing.T) {
-		output := c.RunDockerCmd("exec", "compose-e2e-volume-nginx2-1", "cat", "/run/secrets/mysecret").Stdout()
+		output := c.RunDockerCmd(t, "exec", "compose-e2e-volume-nginx2-1", "cat", "/run/secrets/mysecret").Stdout()
 		assert.Assert(t, strings.Contains(output, `Hello from Nginx container`), output)
 	})
 
 	t.Run("check container bind-mounts specs", func(t *testing.T) {
-		res := c.RunDockerCmd("inspect", "compose-e2e-volume-nginx-1", "--format", "{{ json .Mounts }}")
+		res := c.RunDockerCmd(t, "inspect", "compose-e2e-volume-nginx-1", "--format", "{{ json .Mounts }}")
 		output := res.Stdout()
 		// nolint
 		assert.Assert(t, strings.Contains(output, `"Type":"bind"`))
@@ -73,20 +74,20 @@ func TestLocalComposeVolume(t *testing.T) {
 	})
 
 	t.Run("should inherit anonymous volumes", func(t *testing.T) {
-		c.RunDockerOrExitError("exec", "compose-e2e-volume-nginx2-1", "touch", "/usr/src/app/node_modules/test")
-		c.RunDockerOrExitError("compose", "--project-directory", "fixtures/volume-test", "--project-name", projectName, "up", "--force-recreate", "-d")
-		c.RunDockerOrExitError("exec", "compose-e2e-volume-nginx2-1", "ls", "/usr/src/app/node_modules/test")
+		c.RunDockerOrExitError(t, "exec", "compose-e2e-volume-nginx2-1", "touch", "/usr/src/app/node_modules/test")
+		c.RunDockerOrExitError(t, "compose", "--project-directory", "fixtures/volume-test", "--project-name", projectName, "up", "--force-recreate", "-d")
+		c.RunDockerOrExitError(t, "exec", "compose-e2e-volume-nginx2-1", "ls", "/usr/src/app/node_modules/test")
 	})
 
 	t.Run("should renew anonymous volumes", func(t *testing.T) {
-		c.RunDockerOrExitError("exec", "compose-e2e-volume-nginx2-1", "touch", "/usr/src/app/node_modules/test")
-		c.RunDockerOrExitError("compose", "--project-directory", "fixtures/volume-test", "--project-name", projectName, "up", "--force-recreate", "--renew-anon-volumes", "-d")
-		c.RunDockerOrExitError("exec", "compose-e2e-volume-nginx2-1", "ls", "/usr/src/app/node_modules/test")
+		c.RunDockerOrExitError(t, "exec", "compose-e2e-volume-nginx2-1", "touch", "/usr/src/app/node_modules/test")
+		c.RunDockerOrExitError(t, "compose", "--project-directory", "fixtures/volume-test", "--project-name", projectName, "up", "--force-recreate", "--renew-anon-volumes", "-d")
+		c.RunDockerOrExitError(t, "exec", "compose-e2e-volume-nginx2-1", "ls", "/usr/src/app/node_modules/test")
 	})
 
 	t.Run("cleanup volume project", func(t *testing.T) {
-		c.RunDockerComposeCmd("--project-name", projectName, "down", "--volumes")
-		ls := c.RunDockerCmd("volume", "ls").Stdout()
+		c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--volumes")
+		ls := c.RunDockerCmd(t, "volume", "ls").Stdout()
 		assert.Assert(t, !strings.Contains(ls, projectName+"_staticVol"))
 		assert.Assert(t, !strings.Contains(ls, "myvolume"))
 	})
@@ -96,7 +97,7 @@ func TestProjectVolumeBind(t *testing.T) {
 	if composeStandaloneMode {
 		t.Skip()
 	}
-	c := NewParallelE2eCLI(t, binDir)
+	c := NewParallelCLI(t)
 	const projectName = "compose-e2e-project-volume-bind"
 
 	t.Run("up on project volume with bind specification", func(t *testing.T) {
@@ -104,17 +105,17 @@ func TestProjectVolumeBind(t *testing.T) {
 		assert.NilError(t, err)
 		defer os.RemoveAll(tmpDir) // nolint
 
-		c.RunDockerComposeCmd("--project-name", projectName, "down")
+		c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 
-		c.RunDockerOrExitError("volume", "rm", "-f", projectName+"_project_data").Assert(t, icmd.Success)
+		c.RunDockerOrExitError(t, "volume", "rm", "-f", projectName+"_project_data").Assert(t, icmd.Success)
 		cmd := c.NewCmdWithEnv([]string{"TEST_DIR=" + tmpDir},
 			"docker", "compose", "--project-directory", "fixtures/project-volume-bind-test", "--project-name", projectName, "up", "-d")
 		icmd.RunCmd(cmd).Assert(t, icmd.Success)
-		defer c.RunDockerComposeCmd("--project-name", projectName, "down")
+		defer c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 
-		c.RunCmd("sh", "-c", "echo SUCCESS > "+filepath.Join(tmpDir, "resultfile")).Assert(t, icmd.Success)
+		c.RunCmd(t, "sh", "-c", "echo SUCCESS > "+filepath.Join(tmpDir, "resultfile")).Assert(t, icmd.Success)
 
-		ret := c.RunDockerOrExitError("exec", "frontend", "bash", "-c", "cat /data/resultfile").Assert(t, icmd.Success)
+		ret := c.RunDockerOrExitError(t, "exec", "frontend", "bash", "-c", "cat /data/resultfile").Assert(t, icmd.Success)
 		assert.Assert(t, strings.Contains(ret.Stdout(), "SUCCESS"))
 	})
 }