File: killer_test.go

package info (click to toggle)
gitlab-ci-multi-runner 14.10.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 31,248 kB
  • sloc: sh: 1,694; makefile: 384; asm: 79; ruby: 68
file content (112 lines) | stat: -rw-r--r-- 2,509 bytes parent folder | download
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
//go:build !integration
// +build !integration

package process

import (
	"errors"
	"os"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

func mockKillerFactory(t *testing.T) (*mockKiller, func()) {
	t.Helper()

	killerMock := new(mockKiller)

	oldNewProcessKiller := newProcessKiller
	cleanup := func() {
		newProcessKiller = oldNewProcessKiller
		killerMock.AssertExpectations(t)
	}

	newProcessKiller = func(logger Logger, cmd Commander) killer {
		return killerMock
	}

	return killerMock, cleanup
}

func TestOSKillWait_KillAndWait(t *testing.T) {
	testProcess := &os.Process{Pid: 1234}
	processStoppedErr := errors.New("process stopped properly")
	killProcessErr := KillProcessError{testProcess.Pid}

	tests := map[string]struct {
		process          *os.Process
		terminateProcess bool
		forceKillProcess bool
		expectedError    error
	}{
		"process is nil": {
			process:       nil,
			expectedError: ErrProcessNotStarted,
		},
		"process terminated": {
			process:          testProcess,
			terminateProcess: true,
			expectedError:    processStoppedErr,
		},
		"process force-killed": {
			process:          testProcess,
			forceKillProcess: true,
			expectedError:    processStoppedErr,
		},
		"process killing failed": {
			process:       testProcess,
			expectedError: &killProcessErr,
		},
	}

	for testName, testCase := range tests {
		t.Run(testName, func(t *testing.T) {
			waitCh := make(chan error, 1)

			killerMock, cleanup := mockKillerFactory(t)
			defer cleanup()

			loggerMock := new(MockLogger)
			defer loggerMock.AssertExpectations(t)

			commanderMock := new(MockCommander)
			defer commanderMock.AssertExpectations(t)

			commanderMock.On("Process").Return(testCase.process)

			if testCase.process != nil {
				loggerMock.
					On("WithFields", mock.Anything).
					Return(loggerMock)

				terminateCall := killerMock.On("Terminate")
				forceKillCall := killerMock.On("ForceKill").Maybe()

				if testCase.terminateProcess {
					terminateCall.Run(func(_ mock.Arguments) {
						waitCh <- processStoppedErr
					})
				}

				if testCase.forceKillProcess {
					forceKillCall.Run(func(_ mock.Arguments) {
						waitCh <- processStoppedErr
					})
				}
			}

			kw := NewOSKillWait(loggerMock, 100*time.Millisecond, 100*time.Millisecond)
			err := kw.KillAndWait(commanderMock, waitCh)

			if testCase.expectedError == nil {
				assert.NoError(t, err)
				return
			}

			assert.ErrorIs(t, testCase.expectedError, err)
		})
	}
}