Skip to content

Commit 086b783

Browse files
committed
Add test cases on toolchain command runner
1 parent cfa8cd2 commit 086b783

File tree

4 files changed

+100
-13
lines changed

4 files changed

+100
-13
lines changed

src/engine/tcr.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ func (tcr *TCREngine) RunTCRCycle() {
524524

525525
// AbortCommand triggers interruption of an ongoing TCR cycle operation
526526
func (tcr *TCREngine) AbortCommand() {
527-
tcr.toolchain.AbortExecution()
527+
_ = tcr.toolchain.AbortExecution()
528528
}
529529

530530
func (tcr *TCREngine) createTCREvent(testResult toolchain.TestCommandResult) (event events.TCREvent) {

src/toolchain/command_runner.go

+13-6
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@ import (
2727
"github.com/murex/tcr/report"
2828
"io"
2929
"os/exec"
30+
"sync"
3031
)
3132

3233
type (
3334
// CommandRunner is in charge of managing the lifecycle of a command
3435
CommandRunner struct {
3536
command *exec.Cmd
37+
// commandMutex is here to enforce that commands run in sequence
38+
commandMutex sync.Mutex
3639
}
3740

3841
// CommandStatus is the result status of a Command execution
@@ -64,16 +67,17 @@ const (
6467
)
6568

6669
// commandRunner singleton instance
67-
var commandRunner = getCommandRunner()
70+
var commandRunner = &CommandRunner{
71+
command: nil,
72+
}
6873

6974
func getCommandRunner() *CommandRunner {
70-
return &CommandRunner{
71-
command: nil,
72-
}
75+
return commandRunner
7376
}
7477

7578
// Run launches the execution of the provided command
7679
func (r *CommandRunner) Run(cmd *Command) (result CommandResult) {
80+
commandRunner.commandMutex.Lock()
7781
result = CommandResult{Status: CommandStatusUnknown, Output: ""}
7882
report.PostText(cmd.asCommandLine())
7983

@@ -98,6 +102,7 @@ func (r *CommandRunner) Run(cmd *Command) (result CommandResult) {
98102
// but we need to ensure first that TCR engine can handle it correctly.
99103
result.Status = CommandStatusFail
100104
r.command = nil
105+
commandRunner.commandMutex.Unlock()
101106
return result
102107
}
103108

@@ -110,6 +115,7 @@ func (r *CommandRunner) Run(cmd *Command) (result CommandResult) {
110115
}
111116

112117
r.command = nil
118+
commandRunner.commandMutex.Unlock()
113119
return result
114120
}
115121

@@ -123,13 +129,14 @@ func (*CommandRunner) reportCommandTrace(readCloser io.ReadCloser) {
123129
}
124130

125131
// AbortRunningCommand triggers aborting of any command that is currently running
126-
func (r *CommandRunner) AbortRunningCommand() {
132+
func (r *CommandRunner) AbortRunningCommand() bool {
127133
if r.command == nil || r.command.Process == nil {
128134
report.PostWarning("There is no command running at this time")
129-
return
135+
return false
130136
}
131137
report.PostWarning("Aborting command: \"", r.command.String(), "\"")
132138
_ = r.command.Process.Kill()
133139
// Calling Kill() may be a bit too brutal (may leave children process alive)
134140
//_ = r.command.Process.Signal(os.Kill)
141+
return true
135142
}

src/toolchain/command_runner_test.go

+83-3
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ package toolchain
2525
import (
2626
"fmt"
2727
"github.com/stretchr/testify/assert"
28+
"runtime"
2829
"testing"
30+
"time"
2931
)
3032

3133
func Test_command_result_outcome(t *testing.T) {
32-
33-
testFlags := []struct {
34+
testCases := []struct {
3435
status CommandStatus
3536
expectedPassed bool
3637
expectedFailed bool
@@ -39,11 +40,90 @@ func Test_command_result_outcome(t *testing.T) {
3940
{"fail", false, true},
4041
{"unknown", false, false},
4142
}
42-
for _, tt := range testFlags {
43+
for _, tt := range testCases {
4344
t.Run(fmt.Sprint(tt.status, "_status"), func(t *testing.T) {
4445
result := CommandResult{Status: tt.status}
4546
assert.Equal(t, tt.expectedPassed, result.Passed())
4647
assert.Equal(t, tt.expectedFailed, result.Failed())
4748
})
4849
}
4950
}
51+
52+
func Test_run_command(t *testing.T) {
53+
testCases := []struct {
54+
desc string
55+
command Command
56+
expectedStatus CommandStatus
57+
}{
58+
{
59+
"unknown command",
60+
Command{Path: "unknown-command"},
61+
CommandStatusFail,
62+
},
63+
{
64+
"passing command",
65+
Command{Path: "true"},
66+
CommandStatusPass,
67+
},
68+
{
69+
"failing command",
70+
Command{Path: "false"},
71+
CommandStatusFail,
72+
},
73+
}
74+
75+
for _, tt := range testCases {
76+
t.Run(tt.desc, func(t *testing.T) {
77+
result := getCommandRunner().Run(&tt.command)
78+
assert.Equal(t, tt.expectedStatus, result.Status)
79+
})
80+
}
81+
}
82+
83+
func Test_abort_command(t *testing.T) {
84+
if runtime.GOOS == OsWindows {
85+
t.Skip("this test fails in Windows CI for unexplained reason")
86+
}
87+
testCases := []struct {
88+
desc string
89+
command *Command
90+
expected bool
91+
}{
92+
{
93+
"with no running command",
94+
nil,
95+
false,
96+
},
97+
{
98+
"with running command",
99+
&Command{Path: "sleep", Arguments: []string{"10"}},
100+
true,
101+
},
102+
}
103+
104+
for _, tt := range testCases {
105+
t.Run(tt.desc, func(t *testing.T) {
106+
if tt.command != nil {
107+
logProcess(t, "before run")
108+
go getCommandRunner().Run(tt.command)
109+
logProcess(t, "after run")
110+
// wait for the new command process to start
111+
for getCommandRunner().command == nil {
112+
time.Sleep(10 * time.Millisecond)
113+
}
114+
logProcess(t, "after waiting")
115+
}
116+
logProcess(t, "before abort")
117+
result := getCommandRunner().AbortRunningCommand()
118+
logProcess(t, "after abort")
119+
assert.Equal(t, tt.expected, result)
120+
})
121+
}
122+
}
123+
124+
func logProcess(t *testing.T, stage string) {
125+
t.Log(stage, ": ", getCommandRunner().command)
126+
if getCommandRunner().command != nil {
127+
t.Log("== process: ", getCommandRunner().command.Process)
128+
}
129+
}

src/toolchain/toolchain.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ type (
7070
checkTestCommand() error
7171
runsOnPlatform(osName OsName, archName ArchName) bool
7272
CheckCommandAccess(cmdPath string) (string, error)
73-
AbortExecution()
73+
AbortExecution() bool
7474
}
7575
)
7676

@@ -164,8 +164,8 @@ func (tchn Toolchain) RunTests() TestCommandResult {
164164
}
165165

166166
// AbortExecution asks the toolchain to abort any command currently executing
167-
func (Toolchain) AbortExecution() {
168-
commandRunner.AbortRunningCommand()
167+
func (Toolchain) AbortExecution() bool {
168+
return commandRunner.AbortRunningCommand()
169169
}
170170

171171
// BuildCommandPath returns the build command path for this toolchain

0 commit comments

Comments
 (0)