* Builder: reorganized unit tests for better code reuse, and to test non-empty contexts

This commit is contained in:
Solomon Hykes 2013-06-15 11:07:49 -07:00
parent 38554fc2a7
commit 061f8d12e0
2 changed files with 92 additions and 70 deletions

View File

@ -1,40 +1,75 @@
package docker package docker
import ( import (
"github.com/dotcloud/docker/utils" "io/ioutil"
"testing" "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. // mkTestContext generates a build context from the contents of the provided dockerfile.
// This context is suitable for use as an argument to BuildFile.Build() // This context is suitable for use as an argument to BuildFile.Build()
func mkTestContext(dockerfile string, t *testing.T) Archive { func mkTestContext(dockerfile string, files [][2]string, t *testing.T) Archive {
context, err := mkBuildContext(dockerfile) context, err := mkBuildContext(dockerfile, files)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
return context 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) { func TestBuild(t *testing.T) {
dockerfiles := []string{Dockerfile, DockerfileNoNewLine} for _, ctx := range testContexts {
for _, Dockerfile := range dockerfiles {
runtime, err := newTestRuntime() runtime, err := newTestRuntime()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -43,50 +78,33 @@ func TestBuild(t *testing.T) {
srv := &Server{runtime: runtime} 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
builder := NewBuilder(runtime) builder := NewBuilder(runtime)
container, err := builder.Create( for _, testCmd := range ctx.tests {
&Config{ container, err := builder.Create(
Image: imgID, &Config{
Cmd: []string{"cat", "/tmp/passwd"}, Image: imgID,
}, Cmd: testCmd.cmd,
) },
if err != nil { )
t.Fatal(err) if err != nil {
} t.Fatal(err)
defer runtime.Destroy(container) }
defer runtime.Destroy(container)
output, err := container.Output() output, err := container.Output()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if string(output) != "root:testpass\n" { if string(output) != testCmd.output {
t.Fatalf("Unexpected output. Read '%s', expected '%s'", output, "root:testpass\n") t.Fatalf("Unexpected output. Read '%s', expected '%s'", output, testCmd.output)
} }
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")
} }
} }
} }

View File

@ -132,18 +132,22 @@ func (cli *DockerCli) CmdInsert(args ...string) error {
// mkBuildContext returns an archive of an empty context with the contents // mkBuildContext returns an archive of an empty context with the contents
// of `dockerfile` at the path ./Dockerfile // 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) buf := new(bytes.Buffer)
tw := tar.NewWriter(buf) tw := tar.NewWriter(buf)
hdr := &tar.Header{ files = append(files, [2]string{"Dockerfile", dockerfile})
Name: "Dockerfile", for _, file := range files {
Size: int64(len(content)), name, content := file[0], file[1]
} hdr := &tar.Header{
if err := tw.WriteHeader(hdr); err != nil { Name: name,
return nil, err Size: int64(len(content)),
} }
if _, err := tw.Write([]byte(content)); err != nil { if err := tw.WriteHeader(hdr); err != nil {
return nil, err return nil, err
}
if _, err := tw.Write([]byte(content)); err != nil {
return nil, err
}
} }
if err := tw.Close(); err != nil { if err := tw.Close(); err != nil {
return nil, err return nil, err
@ -174,7 +178,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
if err != nil { if err != nil {
return err return err
} }
context, err = mkBuildContext(string(dockerfile)) context, err = mkBuildContext(string(dockerfile), nil)
} else { } else {
context, err = Tar(cmd.Arg(0), Uncompressed) context, err = Tar(cmd.Arg(0), Uncompressed)
} }