1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
//go:build !integration
// +build !integration
package shell
import (
"context"
"errors"
"os/exec"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"gitlab.com/gitlab-org/gitlab-runner/common"
"gitlab.com/gitlab-org/gitlab-runner/executors"
"gitlab.com/gitlab-org/gitlab-runner/helpers/process"
"gitlab.com/gitlab-org/gitlab-runner/shells/shellstest"
)
func TestExecutor_Run(t *testing.T) {
var testErr = errors.New("test error")
var exitErr = &exec.ExitError{}
tests := map[string]struct {
commanderAssertions func(*process.MockCommander, chan time.Time)
processKillerAssertions func(*process.MockKillWaiter, chan time.Time)
cancelJob bool
expectedErr error
}{
"canceled job uses new process termination": {
commanderAssertions: func(mCmd *process.MockCommander, waitCalled chan time.Time) {
mCmd.On("Start").Return(nil).Once()
mCmd.On("Wait").Run(func(args mock.Arguments) {
close(waitCalled)
}).Return(nil).Once()
},
processKillerAssertions: func(mProcessKillWaiter *process.MockKillWaiter, waitCalled chan time.Time) {
mProcessKillWaiter.
On("KillAndWait", mock.Anything, mock.Anything).
Return(nil).
WaitUntil(waitCalled)
},
cancelJob: true,
expectedErr: nil,
},
"cmd fails to start": {
commanderAssertions: func(mCmd *process.MockCommander, _ chan time.Time) {
mCmd.On("Start").Return(testErr).Once()
},
processKillerAssertions: func(_ *process.MockKillWaiter, _ chan time.Time) {
},
expectedErr: testErr,
},
"wait returns error": {
commanderAssertions: func(mCmd *process.MockCommander, waitCalled chan time.Time) {
mCmd.On("Start").Return(nil).Once()
mCmd.On("Wait").Run(func(args mock.Arguments) {
close(waitCalled)
}).Return(testErr).Once()
},
processKillerAssertions: func(mProcessKillWaiter *process.MockKillWaiter, waitCalled chan time.Time) {
mProcessKillWaiter.
On("KillAndWait", mock.Anything, mock.Anything).
Return(nil).
WaitUntil(waitCalled)
},
cancelJob: false,
expectedErr: testErr,
},
"wait returns exit error": {
commanderAssertions: func(mCmd *process.MockCommander, waitCalled chan time.Time) {
mCmd.On("Start").Return(nil).Once()
mCmd.On("Wait").Run(func(args mock.Arguments) {
close(waitCalled)
}).Return(exitErr).Once()
},
processKillerAssertions: func(mProcessKillWaiter *process.MockKillWaiter, waitCalled chan time.Time) {
mProcessKillWaiter.
On("KillAndWait", mock.Anything, mock.Anything).
Return(nil).
WaitUntil(waitCalled)
},
cancelJob: false,
expectedErr: &common.BuildError{},
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
shellstest.OnEachShell(t, func(t *testing.T, shell string) {
mProcessKillWaiter, mCmd, cleanup := setupProcessMocks(t)
defer cleanup()
waitCalled := make(chan time.Time)
tt.commanderAssertions(mCmd, waitCalled)
tt.processKillerAssertions(mProcessKillWaiter, waitCalled)
executor := executor{
AbstractExecutor: executors.AbstractExecutor{
Build: &common.Build{
JobResponse: common.JobResponse{},
Runner: &common.RunnerConfig{},
},
BuildShell: &common.ShellConfiguration{
Command: shell,
},
},
}
ctx, cancelJob := context.WithCancel(context.Background())
defer cancelJob()
cmd := common.ExecutorCommand{
Script: "echo hello",
Predefined: false,
Context: ctx,
}
if tt.cancelJob {
cancelJob()
}
err := executor.Run(cmd)
assert.ErrorIs(t, err, tt.expectedErr)
})
})
}
}
func setupProcessMocks(t *testing.T) (*process.MockKillWaiter, *process.MockCommander, func()) {
mProcessKillWaiter := new(process.MockKillWaiter)
defer mProcessKillWaiter.AssertExpectations(t)
mCmd := new(process.MockCommander)
defer mCmd.AssertExpectations(t)
oldNewProcessKillWaiter := newProcessKillWaiter
oldCmd := newCommander
newProcessKillWaiter = func(
logger process.Logger,
gracefulKillTimeout time.Duration,
forceKillTimeout time.Duration,
) process.KillWaiter {
return mProcessKillWaiter
}
newCommander = func(executable string, args []string, options process.CommandOptions) process.Commander {
return mCmd
}
return mProcessKillWaiter, mCmd, func() {
newProcessKillWaiter = oldNewProcessKillWaiter
newCommander = oldCmd
}
}
|