Skip to content

Commit

Permalink
Merge pull request #814 from 89luca89/tests/windows_support
Browse files Browse the repository at this point in the history
tests: add Windows E2E testing
  • Loading branch information
pascalbreuninger authored Nov 23, 2023
2 parents d61d366 + 298d7de commit c9a081a
Show file tree
Hide file tree
Showing 12 changed files with 106 additions and 66 deletions.
1 change: 1 addition & 0 deletions .github/workflows/e2e-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ env:
GO111MODULE: on
GOFLAGS: -mod=vendor

# windows blocklist: "up-podman", "integration", "machineprovider", "up"
jobs:
test-e2e:
runs-on: ubuntu-latest
Expand Down
9 changes: 5 additions & 4 deletions e2e/framework/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import (
"io"
"os"
"os/exec"
"path/filepath"
"strings"
)

// ExecCommand executes the command string with the devpod test binary
func (f *Framework) ExecCommandOutput(ctx context.Context, args []string) (string, error) {
var execOut bytes.Buffer

cmd := exec.CommandContext(ctx, f.DevpodBinDir+"/"+f.DevpodBinName, args...)
cmd := exec.CommandContext(ctx, filepath.Join(f.DevpodBinDir, f.DevpodBinName), args...)
cmd.Stdout = io.MultiWriter(os.Stdout, &execOut)
cmd.Stderr = os.Stderr

Expand All @@ -27,7 +28,7 @@ func (f *Framework) ExecCommandOutput(ctx context.Context, args []string) (strin

// ExecCommandStdout executes the command string with the devpod test binary
func (f *Framework) ExecCommandStdout(ctx context.Context, args []string) error {
cmd := exec.CommandContext(ctx, f.DevpodBinDir+"/"+f.DevpodBinName, args...)
cmd := exec.CommandContext(ctx, filepath.Join(f.DevpodBinDir, f.DevpodBinName), args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
Expand All @@ -41,7 +42,7 @@ func (f *Framework) ExecCommandStdout(ctx context.Context, args []string) error
func (f *Framework) ExecCommand(ctx context.Context, captureStdOut, searchForString bool, searchString string, args []string) error {
var execOut bytes.Buffer

cmd := exec.CommandContext(ctx, f.DevpodBinDir+"/"+f.DevpodBinName, args...)
cmd := exec.CommandContext(ctx, filepath.Join(f.DevpodBinDir, f.DevpodBinName), args...)
cmd.Stdout = io.MultiWriter(os.Stdout, &execOut)
cmd.Stderr = os.Stderr

Expand All @@ -65,7 +66,7 @@ func (f *Framework) ExecCommandCapture(ctx context.Context, args []string) (stri
var execOut bytes.Buffer
var execErr bytes.Buffer

cmd := exec.CommandContext(ctx, f.DevpodBinDir+"/"+f.DevpodBinName, args...)
cmd := exec.CommandContext(ctx, filepath.Join(f.DevpodBinDir, f.DevpodBinName), args...)
cmd.Stdout = io.MultiWriter(os.Stdout, &execOut)
cmd.Stderr = io.MultiWriter(os.Stderr, &execErr)

Expand Down
9 changes: 8 additions & 1 deletion e2e/framework/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ type Framework struct {
}

func NewDefaultFramework(path string) *Framework {
var binName = "devpod-"
binName := "devpod-"
switch runtime.GOOS {
case "darwin":
binName = binName + "darwin-"
case "linux":
binName = binName + "linux-"
case "windows":
binName = binName + "windows-"
}

switch runtime.GOARCH {
Expand All @@ -24,5 +26,10 @@ func NewDefaultFramework(path string) *Framework {
case "arm64":
binName = binName + "arm64"
}

if runtime.GOOS == "windows" {
binName = binName + ".exe"
}

return &Framework{DevpodBinDir: path, DevpodBinName: binName}
}
21 changes: 20 additions & 1 deletion e2e/framework/util.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
package framework

import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"time"

"github.com/otiai10/copy"
)

func GetTimeout() time.Duration {
if runtime.GOOS == "windows" {
return 300 * time.Second
}

return 60 * time.Second
}

func CreateTempDir() (string, error) {
// Create temporary directory
dir, err := os.MkdirTemp("", "temp-*")
Expand Down Expand Up @@ -78,8 +90,15 @@ func CopyToTempDir(relativePath string) (string, error) {

func CleanupTempDir(initialDir, tempDir string) {
err := os.RemoveAll(tempDir)
ExpectNoError(err)
if err != nil {
fmt.Println("WARN:", err)
}

err = os.Chdir(initialDir)
ExpectNoError(err)
}

func CleanString(input string) string {
input = strings.ReplaceAll(input, "\\", "")
return strings.ReplaceAll(input, "/", "")
}
6 changes: 6 additions & 0 deletions e2e/tests/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"os"
"path/filepath"
"runtime"

"github.com/loft-sh/devpod/e2e/framework"
"github.com/loft-sh/devpod/pkg/devcontainer/config"
Expand Down Expand Up @@ -157,6 +158,11 @@ var _ = DevPodDescribe("devpod build test suite", func() {
})

ginkgo.It("build kubernetes dockerless", func() {
// skip windows for now
if runtime.GOOS == "windows" {
return
}

ctx := context.Background()

f := framework.NewDefaultFramework(initialDir + "/bin")
Expand Down
4 changes: 2 additions & 2 deletions e2e/tests/machineprovider/machineprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ var _ = DevPodDescribe("devpod machine provider test suite", func() {
// delete workspace
err = f.DevPodWorkspaceDelete(ctx, tempDir)
framework.ExpectNoError(err)
}, ginkgo.SpecTimeout(60*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()))

ginkgo.It("test devpod inactivity timeout", func(ctx context.Context) {
f := framework.NewDefaultFramework(initialDir + "/bin")
Expand Down Expand Up @@ -155,6 +155,6 @@ var _ = DevPodDescribe("devpod machine provider test suite", func() {

time.Sleep(time.Second * 2)
}
}, ginkgo.SpecTimeout(300*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()*5))
})
})
9 changes: 4 additions & 5 deletions e2e/tests/proxyprovider/proxyprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"os"
"path/filepath"
"strings"
"time"

"github.com/loft-sh/devpod/e2e/framework"
"github.com/loft-sh/devpod/pkg/client"
Expand Down Expand Up @@ -81,7 +80,7 @@ var _ = DevPodDescribe("devpod proxy provider test suite", func() {
// delete workspace
err = f.DevPodWorkspaceDelete(ctx, tempDir)
framework.ExpectNoError(err)
}, ginkgo.SpecTimeout(120*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()*2))

ginkgo.It("create & stop workspace via proxy provider", func(ctx context.Context) {
f := framework.NewDefaultFramework(initialDir + "/bin")
Expand Down Expand Up @@ -128,7 +127,7 @@ var _ = DevPodDescribe("devpod proxy provider test suite", func() {
// delete workspace
err = f.DevPodWorkspaceDelete(ctx, tempDir)
framework.ExpectNoError(err)
}, ginkgo.SpecTimeout(120*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()*2))

ginkgo.It("recreate workspace", func(ctx context.Context) {
f := framework.NewDefaultFramework(initialDir + "/bin")
Expand Down Expand Up @@ -176,7 +175,7 @@ var _ = DevPodDescribe("devpod proxy provider test suite", func() {
// delete workspace
err = f.DevPodWorkspaceDelete(ctx, tempDir)
framework.ExpectNoError(err)
}, ginkgo.SpecTimeout(120*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()*2))

ginkgo.It("devcontainer path workspace", func(ctx context.Context) {
f := framework.NewDefaultFramework(initialDir + "/bin")
Expand Down Expand Up @@ -210,6 +209,6 @@ var _ = DevPodDescribe("devpod proxy provider test suite", func() {
// delete workspace
err = f.DevPodWorkspaceDelete(ctx, tempDir)
framework.ExpectNoError(err)
}, ginkgo.SpecTimeout(120*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()*2))
})
})
6 changes: 6 additions & 0 deletions e2e/tests/ssh/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net"
"os"
"os/exec"
"runtime"
"strconv"
"time"

Expand Down Expand Up @@ -82,6 +83,11 @@ var _ = DevPodDescribe("devpod ssh test suite", func() {
})

ginkgo.It("should start a new workspace with a docker provider (default) and forward a port into it", func() {
// skip windows for now
if runtime.GOOS == "windows" {
return
}

tempDir, err := framework.CopyToTempDir("tests/ssh/testdata/forward-test")
framework.ExpectNoError(err)
defer framework.CleanupTempDir(initialDir, tempDir)
Expand Down
31 changes: 16 additions & 15 deletions e2e/tests/up/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"path"
"path/filepath"
"strings"
"time"

"github.com/loft-sh/devpod/e2e/framework"
"github.com/loft-sh/devpod/pkg/devcontainer/config"
Expand Down Expand Up @@ -51,7 +50,7 @@ var _ = DevPodDescribe("devpod up test suite", func() {
// Wait for devpod workspace to come online (deadline: 30s)
err = f.DevPodUp(ctx, tempDir)
framework.ExpectNoError(err)
}, ginkgo.SpecTimeout(60*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()))
ginkgo.It("should start a new workspace and substitute devcontainer.json variables", func(ctx context.Context) {
tempDir, err := framework.CopyToTempDir("tests/up/testdata/docker-variables")
framework.ExpectNoError(err)
Expand Down Expand Up @@ -91,20 +90,22 @@ var _ = DevPodDescribe("devpod up test suite", func() {

localWorkspaceFolder, _, err := f.ExecCommandCapture(ctx, []string{"ssh", "--command", "cat $HOME/local-workspace-folder.out", projectName})
framework.ExpectNoError(err)
gomega.Expect(localWorkspaceFolder).To(gomega.Equal(tempDir))
gomega.Expect(framework.CleanString(localWorkspaceFolder)).To(gomega.Equal(framework.CleanString(tempDir)))

localWorkspaceFolderBasename, _, err := f.ExecCommandCapture(ctx, []string{"ssh", "--command", "cat $HOME/local-workspace-folder-basename.out", projectName})
framework.ExpectNoError(err)
gomega.Expect(localWorkspaceFolderBasename).To(gomega.Equal(filepath.Base(tempDir)))

containerWorkspaceFolder, _, err := f.ExecCommandCapture(ctx, []string{"ssh", "--command", "cat $HOME/container-workspace-folder.out", projectName})
framework.ExpectNoError(err)
gomega.Expect(containerWorkspaceFolder).To(gomega.Equal(filepath.Join("/workspaces", filepath.Base(tempDir))))
gomega.Expect(framework.CleanString(containerWorkspaceFolder)).To(gomega.Equal(
framework.CleanString("workspaces" + filepath.Base(tempDir)),
))

containerWorkspaceFolderBasename, _, err := f.ExecCommandCapture(ctx, []string{"ssh", "--command", "cat $HOME/container-workspace-folder-basename.out", projectName})
framework.ExpectNoError(err)
gomega.Expect(containerWorkspaceFolderBasename).To(gomega.Equal(filepath.Base(tempDir)))
}, ginkgo.SpecTimeout(60*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()))

ginkgo.It("should start a new workspace with mounts", func(ctx context.Context) {
tempDir, err := framework.CopyToTempDir("tests/up/testdata/docker-mounts")
Expand Down Expand Up @@ -138,7 +139,7 @@ var _ = DevPodDescribe("devpod up test suite", func() {
bar, _, err := f.ExecCommandCapture(ctx, []string{"ssh", "--command", "cat $HOME/mnt2/bar.txt", projectName})
framework.ExpectNoError(err)
gomega.Expect(bar).To(gomega.Equal("FOO"))
}, ginkgo.SpecTimeout(60*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()))

ginkgo.It("should start a new workspace with multistage build", func(ctx context.Context) {
tempDir, err := framework.CopyToTempDir("tests/up/testdata/docker-with-multi-stage-build")
Expand All @@ -155,7 +156,7 @@ var _ = DevPodDescribe("devpod up test suite", func() {
// Wait for devpod workspace to come online (deadline: 30s)
err = f.DevPodUp(ctx, tempDir, "--debug")
framework.ExpectNoError(err)
}, ginkgo.SpecTimeout(180*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()*3))

ginkgo.Context("should start a new workspace with features", func() {
ginkgo.It("ensure dependencies installed via features are accessible in lifecycle hooks", func(ctx context.Context) {
Expand All @@ -173,7 +174,7 @@ var _ = DevPodDescribe("devpod up test suite", func() {
// Wait for devpod workspace to come online (deadline: 30s)
err = f.DevPodUp(ctx, tempDir, "--debug")
framework.ExpectNoError(err)
}, ginkgo.SpecTimeout(60*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()))
})
ginkgo.It("should start a new workspace with dotfiles - no install script", func(ctx context.Context) {
tempDir, err := framework.CopyToTempDir("tests/up/testdata/docker")
Expand Down Expand Up @@ -202,7 +203,7 @@ var _ = DevPodDescribe("devpod up test suite", func() {
/home/vscode/.file3
`
framework.ExpectEqual(out, expectedOutput, "should match")
}, ginkgo.SpecTimeout(60*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()))
ginkgo.It("should start a new workspace with dotfiles - install script", func(ctx context.Context) {
tempDir, err := framework.CopyToTempDir("tests/up/testdata/docker")
framework.ExpectNoError(err)
Expand All @@ -228,7 +229,7 @@ var _ = DevPodDescribe("devpod up test suite", func() {
expectedOutput := "/tmp/worked\n"

framework.ExpectEqual(out, expectedOutput, "should match")
}, ginkgo.SpecTimeout(60*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()))

ginkgo.It("should start a new workspace with custom image", func(ctx context.Context) {
tempDir, err := framework.CopyToTempDir("tests/up/testdata/docker")
Expand Down Expand Up @@ -257,7 +258,7 @@ var _ = DevPodDescribe("devpod up test suite", func() {

framework.ExpectEqual(out, expectedOutput, "should match")
framework.ExpectNotEqual(out, unexpectedOutput, "should NOT match")
}, ginkgo.SpecTimeout(60*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()))
ginkgo.It("should start a new workspace with custom image and skip building", func(ctx context.Context) {
tempDir, err := framework.CopyToTempDir("tests/up/testdata/docker-with-multi-stage-build")
framework.ExpectNoError(err)
Expand Down Expand Up @@ -285,7 +286,7 @@ var _ = DevPodDescribe("devpod up test suite", func() {

framework.ExpectEqual(out, expectedOutput, "should match")
framework.ExpectNotEqual(out, unexpectedOutput, "should NOT match")
}, ginkgo.SpecTimeout(60*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()))

ginkgo.Context("should start a workspace from a Dockerfile build", func() {
ginkgo.It("should rebuild image in case of changes in files in build context", func(ctx context.Context) {
Expand Down Expand Up @@ -339,7 +340,7 @@ var _ = DevPodDescribe("devpod up test suite", func() {
image2 := container.Config.LegacyImage

gomega.Expect(image2).ShouldNot(gomega.Equal(image1), "images should be different")
}, ginkgo.SpecTimeout(60*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()))
ginkgo.It("should not rebuild image for changes in files mentioned in .dockerignore", func(ctx context.Context) {
tempDir, err := framework.CopyToTempDir("tests/up/testdata/docker-dockerfile-buildcontext")
framework.ExpectNoError(err)
Expand Down Expand Up @@ -391,7 +392,7 @@ var _ = DevPodDescribe("devpod up test suite", func() {
image2 := container.Config.LegacyImage

gomega.Expect(image2).Should(gomega.Equal(image1), "image should be same")
}, ginkgo.SpecTimeout(60*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()))
})
ginkgo.It("should use http headers to download feature", func(ctx context.Context) {
server := ghttp.NewServer()
Expand Down Expand Up @@ -441,7 +442,7 @@ var _ = DevPodDescribe("devpod up test suite", func() {
err = f.DevPodUp(ctx, tempDir)
framework.ExpectNoError(err)
server.Close()
}, ginkgo.SpecTimeout(60*time.Second))
}, ginkgo.SpecTimeout(framework.GetTimeout()))
})
})
})
Loading

0 comments on commit c9a081a

Please sign in to comment.