Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/unikraft/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ func authTests(t *testing.T, r *testRunner) {
{args: []string{unikraftCmd, "profile", "list"}},
{args: []string{unikraftCmd, "metro", "list"}},
{args: []string{unikraftCmd, "logout"}},
{args: []string{unikraftCmd, "profile", "list"}, allowErr: true},
{args: []string{unikraftCmd, "metro", "list"}, allowErr: true},
{args: []string{unikraftCmd, "profile", "list"}, err: errMaybe},
{args: []string{unikraftCmd, "metro", "list"}, err: errMaybe},
})
})
}
71 changes: 54 additions & 17 deletions cmd/unikraft/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,40 @@ func buildTests(t *testing.T, r *testRunner) {
})
})

var busybox, metroName string
var metroName string
type variant struct {
name string
image string
}
var variants []variant
if r.cfg != nil {
metroName = r.cfg.MetroName

busybox = fmt.Sprintf("%s/busybox-e2e:$UNIQ_IMAGE", r.cfg.Profile.Organization)
// this is what we'd use to test direct push
// busybox := fmt.Sprintf("%s/%s/busybox-e2e:$UNIQ_IMAGE", cfg.Metro.Index().Host, cfg.Profile.Organization)
variants = []variant{
Comment thread
jedevc marked this conversation as resolved.
{
name: "registry",
image: fmt.Sprintf("%s/busybox-e2e:$UNIQ_IMAGE", r.cfg.Profile.Organization),
},
{
name: "direct-push",
image: fmt.Sprintf("%s/%s/busybox-e2e:$UNIQ_IMAGE", r.cfg.Metro.Index().Host, r.cfg.Profile.Organization),
},
}
}

t.Run("busybox", func(t *testing.T) {
if r.cfg == nil {
t.Skip("busybox tests require online config")
}
for _, format := range []string{"cpio", "erofs"} {
t.Run(format, func(t *testing.T) {
r.
online().
withCleaners(buildCleaners).
withContext(map[string]string{
"Dockerfile": `
for _, v := range variants {
t.Run(v.name, func(t *testing.T) {
r.
online().
withCleaners(buildCleaners).
withContext(map[string]string{
"Dockerfile": `
FROM busybox:latest
RUN echo "unikraft-e2e" > /etc/unikraft-e2e
COPY <<EOF /entrypoint.sh
Expand All @@ -51,7 +68,7 @@ echo "== END status =="
EOF
RUN chmod +x /entrypoint.sh
`,
"Kraftfile": fmt.Sprintf(`
"Kraftfile": fmt.Sprintf(`
spec: v0.7
name: busybox-e2e
runtime: base-compat:latest
Expand All @@ -60,14 +77,24 @@ rootfs:
source: ./Dockerfile
cmd: ["sh", "/entrypoint.sh"]
`, format),
}).
run(t, []command{
{args: []string{unikraftCmd, "build", ".", "--output", busybox}},
{args: []string{unikraftCmd, "run", "--name", "test-$UNIQ_INST", "--metro", metroName, "--output", "quiet", "--image", busybox}},
{args: []string{unikraftCmd, "instance", "wait", "--until", "state==stopped", "--timeout", "10s", "test-$UNIQ_INST"}},
{args: []string{unikraftCmd, "instance", "logs", "test-$UNIQ_INST"}},
{args: []string{unikraftCmd, "instance", "delete", "test-$UNIQ_INST"}},
}).
run(t, []command{
{args: []string{unikraftCmd, "build", ".", "--output", v.image}},
{args: []string{unikraftCmd, "image", "inspect", v.image}},
{args: []string{unikraftCmd, "image", "ls", v.image, "-okv"}},
{args: []string{unikraftCmd, "run", "--name", "test-$UNIQ_INST", "--metro", metroName, "--output", "quiet", "--image", v.image}},
{args: []string{unikraftCmd, "instance", "wait", "--until", "state==stopped", "--timeout", "10s", "test-$UNIQ_INST"}},
{args: []string{unikraftCmd, "instance", "logs", "test-$UNIQ_INST"}},
{args: []string{unikraftCmd, "instance", "delete", "test-$UNIQ_INST"}},
{args: []string{unikraftCmd, "image", "delete", v.image}},
{args: []string{unikraftCmd, "image", "inspect", v.image}, err: errYes},
// HACK: image ls after delete should fail, but for direct-push
// the image-store still returns the image even after tag
// deletion: https://github.com/unikraft-cloud/agent/pull/521
{args: []string{unikraftCmd, "image", "ls", v.image, "-okv"}, err: errMaybe},
})
})
}
})
}
})
Expand All @@ -79,4 +106,14 @@ var buildCleaners = []cleaner{
pattern: regexp.MustCompile(`\bversion=v\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?\b`),
repl: "version=vX.Y.Z",
},
{
// fractional seconds like ".151719804" in timestamps change between runs
pattern: regexp.MustCompile(`(\d{2}:\d{2}:\d{2})\.\d+`),
repl: "${1}",
},
{
// sizes like "35.55MiB" or "128KiB" change between builds
pattern: regexp.MustCompile(`\b\d+(\.\d+)?(MiB|KiB|GiB)\b`),
repl: "X.XXX${2}",
},
}
12 changes: 6 additions & 6 deletions cmd/unikraft/help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import "testing"
func helpTests(t *testing.T, r *testRunner) {
t.Run("empty", func(t *testing.T) {
r.run(t, []command{
{args: []string{unikraftCmd}, allowErr: true},
{args: []string{unikraftCmd}, err: errYes},
})
})
t.Run("version", func(t *testing.T) {
Expand All @@ -25,19 +25,19 @@ func helpTests(t *testing.T, r *testRunner) {
})
t.Run("invalid/arg", func(t *testing.T) {
r.run(t, []command{
{args: []string{unikraftCmd, "invalid"}, allowErr: true},
{args: []string{unikraftCmd, "invalid"}, err: errYes},
})
})
t.Run("invalid/help", func(t *testing.T) {
r.run(t, []command{
{args: []string{unikraftCmd, "--help", "--bad-flag"}, allowErr: true},
{args: []string{unikraftCmd, "--help", "bad-arg"}, allowErr: true},
{args: []string{unikraftCmd, "--help", "--bad-flag"}, err: errYes},
{args: []string{unikraftCmd, "--help", "bad-arg"}, err: errYes},
})
})
t.Run("invalid/logs", func(t *testing.T) {
r.run(t, []command{
{args: []string{unikraftCmd, "--log-type=json", "invalid"}, allowErr: true},
{args: []string{unikraftCmd, "--log-level=fatal", "invalid"}, allowErr: true},
{args: []string{unikraftCmd, "--log-type=json", "invalid"}, err: errYes},
{args: []string{unikraftCmd, "--log-level=fatal", "invalid"}, err: errYes},
})
})
}
21 changes: 18 additions & 3 deletions cmd/unikraft/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,18 @@ type testRunner struct {
// command represents a single command to execute in a test.
type command struct {
args []string
allowErr bool
err commandErr
captureEnv string
}

type commandErr int

const (
errNo commandErr = iota // command must succeed
errMaybe // command may fail (either outcome is acceptable)
errYes // command must fail
)

// testBuilder provides a fluent interface for configuring and running tests.
type testBuilder struct {
runner *testRunner
Expand Down Expand Up @@ -235,16 +243,18 @@ func (b *testBuilder) run(t *testing.T, commands []command) {
}
var exitErr *exec.ExitError
var exitCode int
if errors.As(err, &exitErr) && command.allowErr {
if errors.As(err, &exitErr) && command.err >= errMaybe {
exitCode = exitErr.ExitCode()
// ignore exit errors for help commands
err = nil
}
require.NoError(t, err, "command %q failed\nstdout:\n%s\nstderr:\n%s",
strings.Join(args, " "),
stdout.String(),
stderr.String(),
)
if command.err == errYes {
require.NotZero(t, exitCode, "command %q was expected to fail but succeeded", strings.Join(args, " "))
}

report := report{
args: command.args,
Expand Down Expand Up @@ -411,6 +421,11 @@ var cleaners = []cleaner{
pattern: regexp.MustCompile(`\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|(\+\d{2}:\d{2}))?\b`),
repl: "YYYY-MM-DDTHH:MM:SSZ",
},
{
// datetimes like "2000-01-02 12:34:56 +0100 BST" change between runs
pattern: regexp.MustCompile(`\b\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}(?:\.\d+)?\s+[+-]\d{4}\s+[A-Z]{1,5}\b`),
repl: "YYYY-MM-DD HH:MM:SS +0000 UTC",
},
{
// kernel log timestamps like "[ 0.065015]" change between runs
pattern: regexp.MustCompile(`\[\s*\d+\.\d+\]`),
Expand Down
51 changes: 0 additions & 51 deletions cmd/unikraft/testdata/TestGolden/build/busybox/cpio

This file was deleted.

103 changes: 103 additions & 0 deletions cmd/unikraft/testdata/TestGolden/build/busybox/cpio/direct-push

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading