781 lines
18 KiB
Go
781 lines
18 KiB
Go
/*
|
|
Copyright 2018 Google LLC
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package executor
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
|
|
"github.com/GoogleContainerTools/kaniko/pkg/commands"
|
|
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
|
"github.com/GoogleContainerTools/kaniko/pkg/dockerfile"
|
|
"github.com/GoogleContainerTools/kaniko/testutil"
|
|
"github.com/google/go-cmp/cmp"
|
|
v1 "github.com/google/go-containerregistry/pkg/v1"
|
|
"github.com/google/go-containerregistry/pkg/v1/empty"
|
|
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
|
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
|
)
|
|
|
|
func Test_reviewConfig(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
dockerfile string
|
|
originalCmd []string
|
|
originalEntrypoint []string
|
|
expectedCmd []string
|
|
}{
|
|
{
|
|
name: "entrypoint and cmd declared",
|
|
dockerfile: `
|
|
FROM scratch
|
|
CMD ["mycmd"]
|
|
ENTRYPOINT ["myentrypoint"]`,
|
|
originalEntrypoint: []string{"myentrypoint"},
|
|
originalCmd: []string{"mycmd"},
|
|
expectedCmd: []string{"mycmd"},
|
|
},
|
|
{
|
|
name: "only entrypoint declared",
|
|
dockerfile: `
|
|
FROM scratch
|
|
ENTRYPOINT ["myentrypoint"]`,
|
|
originalEntrypoint: []string{"myentrypoint"},
|
|
originalCmd: []string{"mycmd"},
|
|
expectedCmd: nil,
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
config := &v1.Config{
|
|
Cmd: test.originalCmd,
|
|
Entrypoint: test.originalEntrypoint,
|
|
}
|
|
reviewConfig(stage(t, test.dockerfile), config)
|
|
testutil.CheckErrorAndDeepEqual(t, false, nil, test.expectedCmd, config.Cmd)
|
|
})
|
|
}
|
|
}
|
|
|
|
func stage(t *testing.T, d string) config.KanikoStage {
|
|
stages, _, err := dockerfile.Parse([]byte(d))
|
|
if err != nil {
|
|
t.Fatalf("error parsing dockerfile: %v", err)
|
|
}
|
|
return config.KanikoStage{
|
|
Stage: stages[0],
|
|
}
|
|
}
|
|
|
|
type MockCommand struct {
|
|
name string
|
|
}
|
|
|
|
func (m *MockCommand) Name() string {
|
|
return m.name
|
|
}
|
|
|
|
func Test_stageBuilder_shouldTakeSnapshot(t *testing.T) {
|
|
commands := []instructions.Command{
|
|
&MockCommand{name: "command1"},
|
|
&MockCommand{name: "command2"},
|
|
&MockCommand{name: "command3"},
|
|
}
|
|
|
|
stage := instructions.Stage{
|
|
Commands: commands,
|
|
}
|
|
|
|
type fields struct {
|
|
stage config.KanikoStage
|
|
opts *config.KanikoOptions
|
|
}
|
|
type args struct {
|
|
index int
|
|
files []string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
want bool
|
|
}{
|
|
{
|
|
name: "final stage not last command",
|
|
fields: fields{
|
|
stage: config.KanikoStage{
|
|
Final: true,
|
|
Stage: stage,
|
|
},
|
|
},
|
|
args: args{
|
|
index: 1,
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "not final stage last command",
|
|
fields: fields{
|
|
stage: config.KanikoStage{
|
|
Final: false,
|
|
Stage: stage,
|
|
},
|
|
},
|
|
args: args{
|
|
index: len(commands) - 1,
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "not final stage not last command",
|
|
fields: fields{
|
|
stage: config.KanikoStage{
|
|
Final: false,
|
|
Stage: stage,
|
|
},
|
|
},
|
|
args: args{
|
|
index: 0,
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "caching enabled intermediate container",
|
|
fields: fields{
|
|
stage: config.KanikoStage{
|
|
Final: false,
|
|
Stage: stage,
|
|
},
|
|
opts: &config.KanikoOptions{Cache: true},
|
|
},
|
|
args: args{
|
|
index: 0,
|
|
},
|
|
want: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
if tt.fields.opts == nil {
|
|
tt.fields.opts = &config.KanikoOptions{}
|
|
}
|
|
s := &stageBuilder{
|
|
stage: tt.fields.stage,
|
|
opts: tt.fields.opts,
|
|
}
|
|
if got := s.shouldTakeSnapshot(tt.args.index, tt.args.files); got != tt.want {
|
|
t.Errorf("stageBuilder.shouldTakeSnapshot() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCalculateDependencies(t *testing.T) {
|
|
type args struct {
|
|
dockerfile string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want map[int][]string
|
|
}{
|
|
{
|
|
name: "no deps",
|
|
args: args{
|
|
dockerfile: `
|
|
FROM debian as stage1
|
|
RUN foo
|
|
FROM stage1
|
|
RUN bar
|
|
`,
|
|
},
|
|
want: map[int][]string{},
|
|
},
|
|
{
|
|
name: "args",
|
|
args: args{
|
|
dockerfile: `
|
|
ARG myFile=foo
|
|
FROM debian as stage1
|
|
RUN foo
|
|
FROM stage1
|
|
ARG myFile
|
|
COPY --from=stage1 /tmp/$myFile.txt .
|
|
RUN bar
|
|
`,
|
|
},
|
|
want: map[int][]string{
|
|
0: {"/tmp/foo.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "simple deps",
|
|
args: args{
|
|
dockerfile: `
|
|
FROM debian as stage1
|
|
FROM alpine
|
|
COPY --from=stage1 /foo /bar
|
|
`,
|
|
},
|
|
want: map[int][]string{
|
|
0: {"/foo"},
|
|
},
|
|
},
|
|
{
|
|
name: "two sets deps",
|
|
args: args{
|
|
dockerfile: `
|
|
FROM debian as stage1
|
|
FROM ubuntu as stage2
|
|
RUN foo
|
|
COPY --from=stage1 /foo /bar
|
|
FROM alpine
|
|
COPY --from=stage2 /bar /bat
|
|
`,
|
|
},
|
|
want: map[int][]string{
|
|
0: {"/foo"},
|
|
1: {"/bar"},
|
|
},
|
|
},
|
|
{
|
|
name: "double deps",
|
|
args: args{
|
|
dockerfile: `
|
|
FROM debian as stage1
|
|
FROM ubuntu as stage2
|
|
RUN foo
|
|
COPY --from=stage1 /foo /bar
|
|
FROM alpine
|
|
COPY --from=stage1 /baz /bat
|
|
`,
|
|
},
|
|
want: map[int][]string{
|
|
0: {"/foo", "/baz"},
|
|
},
|
|
},
|
|
{
|
|
name: "envs in deps",
|
|
args: args{
|
|
dockerfile: `
|
|
FROM debian as stage1
|
|
FROM ubuntu as stage2
|
|
RUN foo
|
|
ENV key1 val1
|
|
ENV key2 val2
|
|
COPY --from=stage1 /foo/$key1 /foo/$key2 /bar
|
|
FROM alpine
|
|
COPY --from=stage2 /bar /bat
|
|
`,
|
|
},
|
|
want: map[int][]string{
|
|
0: {"/foo/val1", "/foo/val2"},
|
|
1: {"/bar"},
|
|
},
|
|
},
|
|
{
|
|
name: "envs from base image in deps",
|
|
args: args{
|
|
dockerfile: `
|
|
FROM debian as stage1
|
|
ENV key1 baseval1
|
|
FROM stage1 as stage2
|
|
RUN foo
|
|
ENV key2 val2
|
|
COPY --from=stage1 /foo/$key1 /foo/$key2 /bar
|
|
FROM alpine
|
|
COPY --from=stage2 /bar /bat
|
|
`,
|
|
},
|
|
want: map[int][]string{
|
|
0: {"/foo/baseval1", "/foo/val2"},
|
|
1: {"/bar"},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
f, _ := ioutil.TempFile("", "")
|
|
ioutil.WriteFile(f.Name(), []byte(tt.args.dockerfile), 0755)
|
|
opts := &config.KanikoOptions{
|
|
DockerfilePath: f.Name(),
|
|
}
|
|
|
|
got, err := CalculateDependencies(opts)
|
|
if err != nil {
|
|
t.Errorf("got error: %s,", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
diff := cmp.Diff(got, tt.want)
|
|
t.Errorf("CalculateDependencies() = %v, want %v, diff %v", got, tt.want, diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_filesToSave(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
args []string
|
|
want []string
|
|
files []string
|
|
}{
|
|
{
|
|
name: "simple",
|
|
args: []string{"foo"},
|
|
files: []string{"foo"},
|
|
want: []string{"foo"},
|
|
},
|
|
{
|
|
name: "glob",
|
|
args: []string{"foo*"},
|
|
files: []string{"foo", "foo2", "fooooo", "bar"},
|
|
want: []string{"foo", "foo2", "fooooo"},
|
|
},
|
|
{
|
|
name: "complex glob",
|
|
args: []string{"foo*", "bar?"},
|
|
files: []string{"foo", "foo2", "fooooo", "bar", "bar1", "bar2", "bar33"},
|
|
want: []string{"foo", "foo2", "fooooo", "bar1", "bar2"},
|
|
},
|
|
{
|
|
name: "dir",
|
|
args: []string{"foo"},
|
|
files: []string{"foo/bar", "foo/baz", "foo/bat/baz"},
|
|
want: []string{"foo"},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tmpDir, err := ioutil.TempDir("", "")
|
|
if err != nil {
|
|
t.Errorf("error creating tmpdir: %s", err)
|
|
}
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
for _, f := range tt.files {
|
|
p := filepath.Join(tmpDir, f)
|
|
dir := filepath.Dir(p)
|
|
if dir != "." {
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
t.Errorf("error making dir: %s", err)
|
|
}
|
|
}
|
|
fp, err := os.Create(p)
|
|
if err != nil {
|
|
t.Errorf("error making file: %s", err)
|
|
}
|
|
fp.Close()
|
|
}
|
|
|
|
args := []string{}
|
|
for _, arg := range tt.args {
|
|
args = append(args, filepath.Join(tmpDir, arg))
|
|
}
|
|
got, err := filesToSave(args)
|
|
if err != nil {
|
|
t.Errorf("got err: %s", err)
|
|
}
|
|
want := []string{}
|
|
for _, w := range tt.want {
|
|
want = append(want, filepath.Join(tmpDir, w))
|
|
}
|
|
sort.Strings(want)
|
|
sort.Strings(got)
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("filesToSave() = %v, want %v", got, want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInitializeConfig(t *testing.T) {
|
|
tests := []struct {
|
|
description string
|
|
cfg v1.ConfigFile
|
|
expected v1.Config
|
|
}{
|
|
{
|
|
description: "env is not set in the image",
|
|
cfg: v1.ConfigFile{
|
|
Config: v1.Config{
|
|
Image: "test",
|
|
},
|
|
},
|
|
expected: v1.Config{
|
|
Image: "test",
|
|
Env: []string{
|
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
description: "env is set in the image",
|
|
cfg: v1.ConfigFile{
|
|
Config: v1.Config{
|
|
Env: []string{
|
|
"PATH=/usr/local/something",
|
|
},
|
|
},
|
|
},
|
|
expected: v1.Config{
|
|
Env: []string{
|
|
"PATH=/usr/local/something",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
description: "image is empty",
|
|
expected: v1.Config{
|
|
Env: []string{
|
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
img, err := mutate.ConfigFile(empty.Image, &tt.cfg)
|
|
if err != nil {
|
|
t.Errorf("error seen when running test %s", err)
|
|
t.Fail()
|
|
}
|
|
actual, _ := initializeConfig(img)
|
|
testutil.CheckDeepEqual(t, tt.expected, actual.Config)
|
|
}
|
|
}
|
|
|
|
func Test_stageBuilder_optimize(t *testing.T) {
|
|
testCases := []struct {
|
|
opts *config.KanikoOptions
|
|
retrieve bool
|
|
name string
|
|
}{
|
|
{
|
|
name: "cache enabled and layer not present in cache",
|
|
opts: &config.KanikoOptions{Cache: true},
|
|
},
|
|
{
|
|
name: "cache enabled and layer present in cache",
|
|
opts: &config.KanikoOptions{Cache: true},
|
|
retrieve: true,
|
|
},
|
|
{
|
|
name: "cache disabled and layer not present in cache",
|
|
opts: &config.KanikoOptions{Cache: false},
|
|
},
|
|
{
|
|
name: "cache disabled and layer present in cache",
|
|
opts: &config.KanikoOptions{Cache: false},
|
|
retrieve: true,
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
cf := &v1.ConfigFile{}
|
|
snap := fakeSnapShotter{}
|
|
lc := &fakeLayerCache{retrieve: tc.retrieve}
|
|
sb := &stageBuilder{opts: tc.opts, cf: cf, snapshotter: snap, layerCache: lc}
|
|
ck := CompositeCache{}
|
|
file, err := ioutil.TempFile("", "foo")
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
command := MockDockerCommand{
|
|
contextFiles: []string{file.Name()},
|
|
cacheCommand: MockCachedDockerCommand{},
|
|
}
|
|
sb.cmds = []commands.DockerCommand{command}
|
|
err = sb.optimize(ck, cf.Config)
|
|
if err != nil {
|
|
t.Errorf("Expected error to be nil but was %v", err)
|
|
}
|
|
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_stageBuilder_build(t *testing.T) {
|
|
type testcase struct {
|
|
description string
|
|
opts *config.KanikoOptions
|
|
layerCache *fakeLayerCache
|
|
expectedCacheKeys []string
|
|
pushedCacheKeys []string
|
|
commands []commands.DockerCommand
|
|
fileName string
|
|
rootDir string
|
|
image v1.Image
|
|
config *v1.ConfigFile
|
|
}
|
|
// The two copy command test cases use the same filesystem content and should generate the same cache keys. If they don't then something is wrong. They share this variable to help ensure that.
|
|
copyCommandCacheKey := "7263908b66952551d89fd895ffb067e2e30f474be9f38a8f1792af2b6df7c6e3"
|
|
tempDirAndFile := func() (string, string) {
|
|
dir, err := ioutil.TempDir("", "foo")
|
|
if err != nil {
|
|
t.Errorf("could not create temp dir %v", err)
|
|
}
|
|
filename := "bar.txt"
|
|
filepath := filepath.Join(dir, filename)
|
|
err = ioutil.WriteFile(filepath, []byte(`meow`), 0777)
|
|
if err != nil {
|
|
t.Errorf("could not create temp file %v", err)
|
|
}
|
|
buf := bytes.NewBuffer([]byte{})
|
|
writer := tar.NewWriter(buf)
|
|
defer writer.Close()
|
|
|
|
return dir, filename
|
|
}
|
|
testCases := []testcase{
|
|
{
|
|
description: "fake command cache enabled but key not in cache",
|
|
opts: &config.KanikoOptions{Cache: true},
|
|
expectedCacheKeys: []string{"2cd95a0195a42f2873273b7e8c970e3a87970bd0e5d330b3c7d068e7419e5017"},
|
|
pushedCacheKeys: []string{"2cd95a0195a42f2873273b7e8c970e3a87970bd0e5d330b3c7d068e7419e5017"},
|
|
},
|
|
{
|
|
description: "fake command cache enabled and key in cache",
|
|
opts: &config.KanikoOptions{Cache: true},
|
|
layerCache: &fakeLayerCache{
|
|
retrieve: true,
|
|
},
|
|
expectedCacheKeys: []string{"2cd95a0195a42f2873273b7e8c970e3a87970bd0e5d330b3c7d068e7419e5017"},
|
|
pushedCacheKeys: []string{"2cd95a0195a42f2873273b7e8c970e3a87970bd0e5d330b3c7d068e7419e5017"},
|
|
},
|
|
{
|
|
description: "fake command cache disabled and key not in cache",
|
|
opts: &config.KanikoOptions{Cache: false},
|
|
},
|
|
{
|
|
description: "fake command cache disabled and key in cache",
|
|
opts: &config.KanikoOptions{Cache: false},
|
|
layerCache: &fakeLayerCache{
|
|
retrieve: true,
|
|
},
|
|
},
|
|
func() testcase {
|
|
dir, filename := tempDirAndFile()
|
|
filepath := filepath.Join(dir, filename)
|
|
|
|
buf := bytes.NewBuffer([]byte{})
|
|
writer := tar.NewWriter(buf)
|
|
defer writer.Close()
|
|
|
|
info, err := os.Stat(filepath)
|
|
if err != nil {
|
|
t.Errorf("could not get file info for temp file %v", err)
|
|
}
|
|
hdr, err := tar.FileInfoHeader(info, filename)
|
|
if err != nil {
|
|
t.Errorf("could not get tar header for temp file %v", err)
|
|
}
|
|
|
|
if err := writer.WriteHeader(hdr); err != nil {
|
|
t.Errorf("could not write tar header %v", err)
|
|
}
|
|
|
|
content, err := ioutil.ReadFile(filepath)
|
|
if err != nil {
|
|
t.Errorf("could not read tempfile %v", err)
|
|
}
|
|
|
|
if _, err := writer.Write(content); err != nil {
|
|
t.Errorf("could not write file contents to tar")
|
|
}
|
|
tarContent := buf.Bytes()
|
|
return testcase{
|
|
description: "copy command cache enabled and key in cache",
|
|
opts: &config.KanikoOptions{Cache: true},
|
|
layerCache: &fakeLayerCache{
|
|
retrieve: true,
|
|
img: fakeImage{
|
|
ImageLayers: []v1.Layer{
|
|
fakeLayer{
|
|
TarContent: tarContent,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
rootDir: dir,
|
|
expectedCacheKeys: []string{copyCommandCacheKey},
|
|
// CachingCopyCommand is not pushed to the cache
|
|
pushedCacheKeys: []string{},
|
|
commands: func() []commands.DockerCommand {
|
|
cmd, err := commands.GetCommand(
|
|
&instructions.CopyCommand{
|
|
SourcesAndDest: []string{
|
|
filename, "foo.txt",
|
|
},
|
|
},
|
|
dir,
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return []commands.DockerCommand{
|
|
cmd,
|
|
}
|
|
}(),
|
|
fileName: filename,
|
|
}
|
|
}(),
|
|
func() testcase {
|
|
dir, filename := tempDirAndFile()
|
|
|
|
tarContent := []byte{}
|
|
destDir, err := ioutil.TempDir("", "baz")
|
|
if err != nil {
|
|
t.Errorf("could not create temp dir %v", err)
|
|
}
|
|
return testcase{
|
|
description: "copy command cache enabled and key is not in cache",
|
|
opts: &config.KanikoOptions{Cache: true},
|
|
config: &v1.ConfigFile{Config: v1.Config{WorkingDir: destDir}},
|
|
layerCache: &fakeLayerCache{
|
|
retrieve: false,
|
|
},
|
|
image: fakeImage{
|
|
ImageLayers: []v1.Layer{
|
|
fakeLayer{
|
|
TarContent: tarContent,
|
|
},
|
|
},
|
|
},
|
|
rootDir: dir,
|
|
expectedCacheKeys: []string{copyCommandCacheKey},
|
|
pushedCacheKeys: []string{copyCommandCacheKey},
|
|
commands: func() []commands.DockerCommand {
|
|
cmd, err := commands.GetCommand(
|
|
&instructions.CopyCommand{
|
|
SourcesAndDest: []string{
|
|
filename, "foo.txt",
|
|
},
|
|
},
|
|
dir,
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return []commands.DockerCommand{
|
|
cmd,
|
|
}
|
|
}(),
|
|
fileName: filename,
|
|
}
|
|
}(),
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.description, func(t *testing.T) {
|
|
var fileName string
|
|
if tc.commands == nil {
|
|
file, err := ioutil.TempFile("", "foo")
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
command := MockDockerCommand{
|
|
contextFiles: []string{file.Name()},
|
|
cacheCommand: MockCachedDockerCommand{
|
|
contextFiles: []string{file.Name()},
|
|
},
|
|
}
|
|
tc.commands = []commands.DockerCommand{command}
|
|
fileName = file.Name()
|
|
} else {
|
|
fileName = tc.fileName
|
|
}
|
|
|
|
cf := tc.config
|
|
if cf == nil {
|
|
cf = &v1.ConfigFile{
|
|
Config: v1.Config{
|
|
Env: make([]string, 0),
|
|
},
|
|
}
|
|
}
|
|
|
|
snap := fakeSnapShotter{file: fileName}
|
|
lc := tc.layerCache
|
|
if lc == nil {
|
|
lc = &fakeLayerCache{}
|
|
}
|
|
keys := []string{}
|
|
sb := &stageBuilder{
|
|
args: &dockerfile.BuildArgs{}, //required or code will panic
|
|
image: tc.image,
|
|
opts: tc.opts,
|
|
cf: cf,
|
|
snapshotter: snap,
|
|
layerCache: lc,
|
|
pushCache: func(_ *config.KanikoOptions, cacheKey, _, _ string) error {
|
|
keys = append(keys, cacheKey)
|
|
return nil
|
|
},
|
|
}
|
|
sb.cmds = tc.commands
|
|
tmp := commands.RootDir
|
|
if tc.rootDir != "" {
|
|
commands.RootDir = tc.rootDir
|
|
}
|
|
err := sb.build()
|
|
if err != nil {
|
|
t.Errorf("Expected error to be nil but was %v", err)
|
|
}
|
|
|
|
if len(tc.expectedCacheKeys) != len(lc.receivedKeys) {
|
|
t.Errorf("expected to receive %v keys but was %v", len(tc.expectedCacheKeys), len(lc.receivedKeys))
|
|
}
|
|
for _, key := range tc.expectedCacheKeys {
|
|
match := false
|
|
for _, receivedKey := range lc.receivedKeys {
|
|
if key == receivedKey {
|
|
match = true
|
|
break
|
|
}
|
|
}
|
|
if !match {
|
|
t.Errorf("expected received keys to include %v but did not %v", key, lc.receivedKeys)
|
|
}
|
|
}
|
|
if len(tc.pushedCacheKeys) != len(keys) {
|
|
t.Errorf("expected to push %v keys but was %v", len(tc.pushedCacheKeys), len(keys))
|
|
}
|
|
for _, key := range tc.pushedCacheKeys {
|
|
match := false
|
|
for _, pushedKey := range keys {
|
|
if key == pushedKey {
|
|
match = true
|
|
break
|
|
}
|
|
}
|
|
if !match {
|
|
t.Errorf("expected pushed keys to include %v but did not %v", key, keys)
|
|
}
|
|
}
|
|
commands.RootDir = tmp
|
|
|
|
})
|
|
}
|
|
}
|