func runTest(state *core.BuildState, target *core.BuildTarget) ([]byte, error) { replacedCmd := build.ReplaceTestSequences(target, target.GetTestCommand()) env := core.BuildEnvironment(state, target, true) if len(state.TestArgs) > 0 { args := strings.Join(state.TestArgs, " ") replacedCmd += " " + args env = append(env, "TESTS="+args) } log.Debug("Running test %s\nENVIRONMENT:\n%s\n%s", target.Label, strings.Join(env, "\n"), replacedCmd) _, out, err := core.ExecWithTimeoutShell(target.TestDir(), env, target.TestTimeout, state.Config.Test.Timeout, state.ShowAllOutput, replacedCmd) return out, err }
func runContainerisedTest(state *core.BuildState, target *core.BuildTarget) ([]byte, error) { testDir := path.Join(core.RepoRoot, target.TestDir()) replacedCmd := build.ReplaceTestSequences(target, target.GetTestCommand()) replacedCmd += " " + strings.Join(state.TestArgs, " ") containerName := state.Config.Docker.DefaultImage if target.ContainerSettings != nil && target.ContainerSettings.DockerImage != "" { containerName = target.ContainerSettings.DockerImage } // Gentle hack: remove the absolute path from the command replacedCmd = strings.Replace(replacedCmd, testDir, "/tmp/test", -1) // Fiddly hack follows to handle docker run --rm failing saying "Cannot destroy container..." // "Driver aufs failed to remove root filesystem... device or resource busy" cidfile := path.Join(testDir, ".container_id") // Using C.UTF-8 for LC_ALL because it works. Not sure it's strictly // correct to mix that with LANG=en_GB.UTF-8 command := []string{"docker", "run", "--cidfile", cidfile, "-e", "LC_ALL=C.UTF-8"} if target.ContainerSettings != nil { if target.ContainerSettings.DockerRunArgs != "" { command = append(command, strings.Split(target.ContainerSettings.DockerRunArgs, " ")...) } if target.ContainerSettings.DockerUser != "" { command = append(command, "-u", target.ContainerSettings.DockerUser) } } else { command = append(command, state.Config.Docker.RunArgs...) } for _, env := range core.BuildEnvironment(state, target, true) { command = append(command, "-e", strings.Replace(env, testDir, "/tmp/test", -1)) } replacedCmd = "mkdir -p /tmp/test && cp -r /tmp/test_in/* /tmp/test && cd /tmp/test && " + replacedCmd command = append(command, "-v", testDir+":/tmp/test_in", "-w", "/tmp/test_in", containerName, "bash", "-o", "pipefail", "-c", replacedCmd) log.Debug("Running containerised test %s: %s", target.Label, strings.Join(command, " ")) _, out, err := core.ExecWithTimeout(target.TestDir(), nil, target.TestTimeout, state.Config.Test.Timeout, state.ShowAllOutput, command) retrieveResultsAndRemoveContainer(target, cidfile, err == context.DeadlineExceeded) return out, err }
func ruleHash(target *core.BuildTarget, runtime bool) []byte { h := sha1.New() h.Write([]byte(target.Label.String())) for _, dep := range target.DeclaredDependencies() { h.Write([]byte(dep.String())) } for _, vis := range target.Visibility { h.Write([]byte(vis.String())) // Doesn't strictly affect the output, but best to be safe. } for _, hsh := range target.Hashes { h.Write([]byte(hsh)) } for _, source := range target.AllSources() { h.Write([]byte(source.String())) } for _, out := range target.DeclaredOutputs() { h.Write([]byte(out)) } for _, licence := range target.Licences { h.Write([]byte(licence)) } for _, output := range target.TestOutputs { h.Write([]byte(output)) } for _, output := range target.OptionalOutputs { h.Write([]byte(output)) } for _, label := range target.Labels { h.Write([]byte(label)) } hashBool(h, target.IsBinary) hashBool(h, target.IsTest) // Note that we only hash the current command here; whatever's set in commands that we're not going // to run is uninteresting to us. h.Write([]byte(target.GetCommand())) if runtime { // Similarly, we only hash the current command here again. h.Write([]byte(target.GetTestCommand())) for _, datum := range target.Data { h.Write([]byte(datum.String())) } hashBool(h, target.Containerise) if target.ContainerSettings != nil { e := gob.NewEncoder(h) if err := e.Encode(target.ContainerSettings); err != nil { panic(err) } } if target.Containerise { h.Write(core.State.Hashes.Containerisation) } } hashBool(h, target.NeedsTransitiveDependencies) hashBool(h, target.OutputIsComplete) // Should really not be conditional here, but we don't want adding the new flag to // change the hash of every single other target everywhere. // Might consider removing this the next time we peturb the hashing strategy. if target.Stamp { hashBool(h, target.Stamp) } for _, require := range target.Requires { h.Write([]byte(require)) } // Indeterminate iteration order, yay... languages := []string{} for k := range target.Provides { languages = append(languages, k) } sort.Strings(languages) for _, lang := range languages { h.Write([]byte(lang)) h.Write([]byte(target.Provides[lang].String())) } // Obviously we don't include the code pointer because it's a pointer. h.Write(target.PreBuildHash) h.Write(target.PostBuildHash) return h.Sum(nil) }