From 061f8d12e04e494fdcbee949facaf598d201c2ec Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Sat, 15 Jun 2013 11:07:49 -0700 Subject: [PATCH] * Builder: reorganized unit tests for better code reuse, and to test non-empty contexts --- buildfile_test.go | 136 ++++++++++++++++++++++++++-------------------- commands.go | 26 +++++---- 2 files changed, 92 insertions(+), 70 deletions(-) diff --git a/buildfile_test.go b/buildfile_test.go index 0432fda3e6..383463ce24 100644 --- a/buildfile_test.go +++ b/buildfile_test.go @@ -1,40 +1,75 @@ package docker import ( - "github.com/dotcloud/docker/utils" + "io/ioutil" "testing" ) -const Dockerfile = ` -# VERSION 0.1 -# DOCKER-VERSION 0.2 - -from ` + unitTestImageName + ` -run sh -c 'echo root:testpass > /tmp/passwd' -run mkdir -p /var/run/sshd -` - -const DockerfileNoNewLine = ` -# VERSION 0.1 -# DOCKER-VERSION 0.2 - -from ` + unitTestImageName + ` -run sh -c 'echo root:testpass > /tmp/passwd' -run mkdir -p /var/run/sshd` - // mkTestContext generates a build context from the contents of the provided dockerfile. // This context is suitable for use as an argument to BuildFile.Build() -func mkTestContext(dockerfile string, t *testing.T) Archive { - context, err := mkBuildContext(dockerfile) +func mkTestContext(dockerfile string, files [][2]string, t *testing.T) Archive { + context, err := mkBuildContext(dockerfile, files) if err != nil { t.Fatal(err) } return context } +// A testContextTemplate describes a build context and how to test it +type testContextTemplate struct { + // Contents of the Dockerfile + dockerfile string + // Additional files in the context, eg [][2]string{"./passwd", "gordon"} + files [][2]string + // Test commands to run in the resulting image + tests []testCommand +} + +// A testCommand describes a command to run in a container, and the exact output required to pass the test +type testCommand struct { + // The command to run, eg. []string{"echo", "hello", "world"} + cmd []string + // The exact output expected, eg. "hello world\n" + output string +} + +// A table of all the contexts to build and test. +// A new docker runtime will be created and torn down for each context. +var testContexts []testContextTemplate = []testContextTemplate{ + { + ` +# VERSION 0.1 +# DOCKER-VERSION 0.2 + +from docker-ut +run sh -c 'echo root:testpass > /tmp/passwd' +run mkdir -p /var/run/sshd +`, + nil, + []testCommand{ + {[]string{"cat", "/tmp/passwd"}, "root:testpass\n"}, + {[]string{"ls", "-d", "/var/run/sshd"}, "/var/run/sshd\n"}, + }, + }, + + { + ` +# VERSION 0.1 +# DOCKER-VERSION 0.2 + +from docker-ut +run sh -c 'echo root:testpass > /tmp/passwd' +run mkdir -p /var/run/sshd`, + nil, + []testCommand{ + {[]string{"cat", "/tmp/passwd"}, "root:testpass\n"}, + {[]string{"ls", "-d", "/var/run/sshd"}, "/var/run/sshd\n"}, + }, + }, +} + func TestBuild(t *testing.T) { - dockerfiles := []string{Dockerfile, DockerfileNoNewLine} - for _, Dockerfile := range dockerfiles { + for _, ctx := range testContexts { runtime, err := newTestRuntime() if err != nil { t.Fatal(err) @@ -43,50 +78,33 @@ func TestBuild(t *testing.T) { srv := &Server{runtime: runtime} - buildfile := NewBuildFile(srv, &utils.NopWriter{}) + buildfile := NewBuildFile(srv, ioutil.Discard) - imgID, err := buildfile.Build(mkTestContext(Dockerfile, t)) + imgID, err := buildfile.Build(mkTestContext(ctx.dockerfile, ctx.files, t)) if err != nil { t.Fatal(err) } builder := NewBuilder(runtime) - container, err := builder.Create( - &Config{ - Image: imgID, - Cmd: []string{"cat", "/tmp/passwd"}, - }, - ) - if err != nil { - t.Fatal(err) - } - defer runtime.Destroy(container) + for _, testCmd := range ctx.tests { + container, err := builder.Create( + &Config{ + Image: imgID, + Cmd: testCmd.cmd, + }, + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container) - output, err := container.Output() - if err != nil { - t.Fatal(err) - } - if string(output) != "root:testpass\n" { - t.Fatalf("Unexpected output. Read '%s', expected '%s'", output, "root:testpass\n") - } - - container2, err := builder.Create( - &Config{ - Image: imgID, - Cmd: []string{"ls", "-d", "/var/run/sshd"}, - }, - ) - if err != nil { - t.Fatal(err) - } - defer runtime.Destroy(container2) - - output, err = container2.Output() - if err != nil { - t.Fatal(err) - } - if string(output) != "/var/run/sshd\n" { - t.Fatal("/var/run/sshd has not been created") + output, err := container.Output() + if err != nil { + t.Fatal(err) + } + if string(output) != testCmd.output { + t.Fatalf("Unexpected output. Read '%s', expected '%s'", output, testCmd.output) + } } } } diff --git a/commands.go b/commands.go index 4441536a95..274ea6fae5 100644 --- a/commands.go +++ b/commands.go @@ -132,18 +132,22 @@ func (cli *DockerCli) CmdInsert(args ...string) error { // mkBuildContext returns an archive of an empty context with the contents // of `dockerfile` at the path ./Dockerfile -func mkBuildContext(content string) (Archive, error) { +func mkBuildContext(dockerfile string, files [][2]string) (Archive, error) { buf := new(bytes.Buffer) tw := tar.NewWriter(buf) - hdr := &tar.Header{ - Name: "Dockerfile", - Size: int64(len(content)), - } - if err := tw.WriteHeader(hdr); err != nil { - return nil, err - } - if _, err := tw.Write([]byte(content)); err != nil { - return nil, err + files = append(files, [2]string{"Dockerfile", dockerfile}) + for _, file := range files { + name, content := file[0], file[1] + hdr := &tar.Header{ + Name: name, + Size: int64(len(content)), + } + if err := tw.WriteHeader(hdr); err != nil { + return nil, err + } + if _, err := tw.Write([]byte(content)); err != nil { + return nil, err + } } if err := tw.Close(); err != nil { return nil, err @@ -174,7 +178,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { if err != nil { return err } - context, err = mkBuildContext(string(dockerfile)) + context, err = mkBuildContext(string(dockerfile), nil) } else { context, err = Tar(cmd.Arg(0), Uncompressed) }