You've already forked sap-jenkins-library
mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-07-15 01:34:38 +02:00
Refactoring, sanity check for unit-under-test binary (#1827)
- Extracted some methods to structure the code better - Explicitly run the binary with a trivial command first to avoid simple failure reasons (like having a non-linux binary)
This commit is contained in:
@ -17,6 +17,26 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// The functions in this file provide a convenient way to integration test the piper binary in docker containers.
|
||||
// It follows the "given, when, then" approach for structuring tests.
|
||||
// The general concept is that per test one container is started, one piper command is run and outcomes are asserted.
|
||||
// Please note that so far this was only tested with debian/ubuntu based containers.
|
||||
//
|
||||
// Non-exhaustive list of assumptions those functions make:
|
||||
// - Bash is available in the container
|
||||
// - If the option TestDir is not provided, the test project must be in the container image in the directory /project
|
||||
|
||||
// IntegrationTestDockerExecRunnerBundle is used to construct an instance of IntegrationTestDockerExecRunner
|
||||
// This is what a test uses to specify the container it requires
|
||||
type IntegrationTestDockerExecRunnerBundle struct {
|
||||
Image string
|
||||
User string
|
||||
TestDir []string
|
||||
Mounts map[string]string
|
||||
Environment map[string]string
|
||||
Setup []string
|
||||
}
|
||||
|
||||
// IntegrationTestDockerExecRunner keeps the state of an instance of a docker runner
|
||||
type IntegrationTestDockerExecRunner struct {
|
||||
// Runner is the ExecRunner to which all executions are forwarded in the end.
|
||||
@ -30,26 +50,9 @@ type IntegrationTestDockerExecRunner struct {
|
||||
ContainerName string
|
||||
}
|
||||
|
||||
// IntegrationTestDockerExecRunnerBundle is used to construct an instance of IntegrationTestDockerExecRunner
|
||||
type IntegrationTestDockerExecRunnerBundle struct {
|
||||
Image string
|
||||
User string
|
||||
TestDir []string
|
||||
Mounts map[string]string
|
||||
Environment map[string]string
|
||||
Setup []string
|
||||
}
|
||||
|
||||
func givenThisContainer(t *testing.T, bundle IntegrationTestDockerExecRunnerBundle) IntegrationTestDockerExecRunner {
|
||||
runner := command.Command{}
|
||||
|
||||
// Generate a random container name so we can start a new one for each test method
|
||||
// We don't rely on docker's random name generator for two reasons
|
||||
// First, it is easier to save the name here compared to getting it from stdout
|
||||
// Second, the common prefix allows batch stopping/deleting of containers if so desired
|
||||
// The test code will not automatically delete containers as they might be useful for debugging
|
||||
var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
containerName := "piper-integration-test-" + strconv.Itoa(seededRand.Int())
|
||||
containerName := generateContainerName()
|
||||
|
||||
testRunner := IntegrationTestDockerExecRunner{
|
||||
Runner: runner,
|
||||
@ -60,7 +63,6 @@ func givenThisContainer(t *testing.T, bundle IntegrationTestDockerExecRunnerBund
|
||||
ContainerName: containerName,
|
||||
}
|
||||
|
||||
//todo ensure it is a linux binary
|
||||
wd, _ := os.Getwd()
|
||||
localPiper := path.Join(wd, "..", "piper")
|
||||
if localPiper == "" {
|
||||
@ -113,7 +115,27 @@ func givenThisContainer(t *testing.T, bundle IntegrationTestDockerExecRunnerBund
|
||||
}
|
||||
}
|
||||
|
||||
err = testRunner.Runner.RunExecutable("docker", "cp", "piper-command-wrapper.sh", testRunner.ContainerName+":/piper-wrapper")
|
||||
setupPiperBinary(t, testRunner, localPiper)
|
||||
|
||||
return testRunner
|
||||
}
|
||||
|
||||
// generateContainerName creates a name with a common prefix and a random number so we can start a new container for each test method
|
||||
// We don't rely on docker's random name generator for two reasons
|
||||
// First, it is easier to save the name here compared to getting it from stdout
|
||||
// Second, the common prefix allows batch stopping/deleting of containers if so desired
|
||||
// The test code will not automatically delete containers as they might be useful for debugging
|
||||
func generateContainerName() string {
|
||||
var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
return "piper-integration-test-" + strconv.Itoa(seededRand.Int())
|
||||
}
|
||||
|
||||
// setupPiperBinary copies a wrapper script for calling the piper binary into the container and verifies that the piper binary is executable inside the container
|
||||
// The wrapper script (piper-command-wrapper.sh) only calls the piper binary and redirects its output into a file
|
||||
// The purpose of this is to capture piper's stdout/stderr in order to assert on the output
|
||||
// This is not possible via "docker logs", cf https://github.com/moby/moby/issues/8662
|
||||
func setupPiperBinary(t *testing.T, testRunner IntegrationTestDockerExecRunner, localPiper string) {
|
||||
err := testRunner.Runner.RunExecutable("docker", "cp", "piper-command-wrapper.sh", testRunner.ContainerName+":/piper-wrapper")
|
||||
if err != nil {
|
||||
t.Fatalf("Copying command wrapper to container has failed %s", err)
|
||||
}
|
||||
@ -121,7 +143,11 @@ func givenThisContainer(t *testing.T, bundle IntegrationTestDockerExecRunnerBund
|
||||
if err != nil {
|
||||
t.Fatalf("Making command wrapper in container executable has failed %s", err)
|
||||
}
|
||||
return testRunner
|
||||
err = testRunner.Runner.RunExecutable("docker", "exec", testRunner.ContainerName, "/bin/bash", "/piper-wrapper", "/piper", "version")
|
||||
if err != nil {
|
||||
t.Fatalf("Running piper failed. "+
|
||||
"Please check that '%s' is the correct binary, and is compiled for this configuration: 'GOOS=linux GOARCH=amd64'. Error text: %s", localPiper, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *IntegrationTestDockerExecRunner) whenRunningPiperCommand(command string, parameters ...string) error {
|
||||
|
Reference in New Issue
Block a user