Skip to content

Commit 76e3610

Browse files
authored
Add tests for perf/cpubench (#56)
* add cpubench test * move exec_wrapper to another common directory * fix linting
1 parent de93713 commit 76e3610

7 files changed

Lines changed: 149 additions & 19 deletions

File tree

pkg/events/events.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ type Processor struct {
7575

7676
func NewProcessor(sub substrate.Manager, cb Callback, state State) *Processor {
7777
return &Processor{
78-
update: make(chan substrate.Manager, 0),
78+
update: make(chan substrate.Manager),
7979
sub: sub,
8080
cb: cb,
8181
state: state,

pkg/perf/cpubench/cpubench_task.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7-
"os/exec"
87

98
"github.com/threefoldtech/zosbase/pkg/perf"
9+
execwrapper "github.com/threefoldtech/zosbase/pkg/perf/exec_wrapper"
1010
"github.com/threefoldtech/zosbase/pkg/stubs"
1111
)
1212

1313
// CPUBenchmarkTask defines CPU benchmark task.
14-
type CPUBenchmarkTask struct{}
14+
type CPUBenchmarkTask struct {
15+
execWrapper execwrapper.ExecWrapper
16+
}
1517

1618
// CPUBenchmarkResult holds CPU benchmark results with the workloads number during the benchmark.
1719
type CPUBenchmarkResult struct {
@@ -25,7 +27,15 @@ var _ perf.Task = (*CPUBenchmarkTask)(nil)
2527

2628
// NewTask returns a new CPU benchmark task.
2729
func NewTask() perf.Task {
28-
return &CPUBenchmarkTask{}
30+
return &CPUBenchmarkTask{
31+
execWrapper: &execwrapper.RealExecWrapper{},
32+
}
33+
}
34+
35+
func NewTaskWithExecWrapper(execWrapper execwrapper.ExecWrapper) perf.Task {
36+
return &CPUBenchmarkTask{
37+
execWrapper: execWrapper,
38+
}
2939
}
3040

3141
// ID returns task ID.
@@ -50,7 +60,8 @@ func (c *CPUBenchmarkTask) Jitter() uint32 {
5060

5161
// Run executes the CPU benchmark.
5262
func (c *CPUBenchmarkTask) Run(ctx context.Context) (interface{}, error) {
53-
cpubenchOut, err := exec.CommandContext(ctx, "cpubench", "-j").CombinedOutput()
63+
cmd := c.execWrapper.CommandContext(ctx, "cpubench", "-j")
64+
cpubenchOut, err := cmd.CombinedOutput()
5465
if err != nil {
5566
return nil, fmt.Errorf("failed to execute cpubench command: %w", err)
5667
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package cpubench
2+
3+
import (
4+
"context"
5+
"encoding/binary"
6+
"errors"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
"github.com/threefoldtech/zbus"
12+
"github.com/threefoldtech/zosbase/pkg/mocks"
13+
"github.com/threefoldtech/zosbase/pkg/perf"
14+
execwrapper "github.com/threefoldtech/zosbase/pkg/perf/exec_wrapper"
15+
"go.uber.org/mock/gomock"
16+
)
17+
18+
func TestCPUBenchmarkTask(t *testing.T) {
19+
t.Run("create new CPU benchmark task", func(t *testing.T) {
20+
task := NewTask()
21+
22+
assert.NotNil(t, task)
23+
assert.Equal(t, "cpu-benchmark", task.ID())
24+
assert.Equal(t, "0 0 */6 * * *", task.Cron()) // Every 6 hours
25+
assert.Contains(t, task.Description(), "CPU")
26+
assert.Equal(t, uint32(0), task.Jitter())
27+
})
28+
29+
t.Run("task implements perf.Task interface", func(t *testing.T) {
30+
var _ perf.Task = NewTask()
31+
})
32+
}
33+
34+
func TestCPUBenchmarkTask_Run(t *testing.T) {
35+
ctrl := gomock.NewController(t)
36+
defer ctrl.Finish()
37+
38+
mockExec := execwrapper.NewMockExecWrapper(ctrl)
39+
mockCmd := execwrapper.NewMockExecCmd(ctrl)
40+
task := NewTaskWithExecWrapper(mockExec).(*CPUBenchmarkTask)
41+
ctx := context.Background()
42+
mockZbus := mocks.NewMockClient(ctrl)
43+
ctx = perf.WithZbusClient(ctx, mockZbus)
44+
45+
mockExec.EXPECT().CommandContext(ctx, "cpubench", "-j").Return(mockCmd).Times(4)
46+
t.Run("successful benchmark execution", func(t *testing.T) {
47+
48+
// Mock successful cpubench execution
49+
expectedOutput := `{
50+
"single": 1542.67,
51+
"multi": 6170.89,
52+
"threads": 4
53+
}`
54+
55+
data := make([]byte, 8)
56+
binary.LittleEndian.PutUint64(data, 5)
57+
response := &zbus.Response{
58+
ID: "test-id",
59+
Output: zbus.Output{
60+
Data: data,
61+
Error: nil,
62+
},
63+
}
64+
65+
mockZbus.EXPECT().
66+
RequestContext(gomock.Any(), "provision", zbus.ObjectID{Name: "statistics", Version: "0.0.1"}, "Workloads").
67+
Return(response, nil)
68+
69+
mockCmd.EXPECT().CombinedOutput().Return([]byte(expectedOutput), nil)
70+
71+
result, err := task.Run(ctx)
72+
require.NoError(t, err)
73+
assert.NotNil(t, result)
74+
75+
// Verify result structure
76+
cpuResult, ok := result.(CPUBenchmarkResult)
77+
require.True(t, ok, "Result should be of type CPUBenchmarkResult")
78+
79+
assert.Equal(t, 1542.67, cpuResult.SingleThreaded)
80+
assert.Equal(t, 6170.89, cpuResult.MultiThreaded)
81+
assert.Equal(t, 4, cpuResult.Threads)
82+
assert.Equal(t, 5, cpuResult.Workloads)
83+
84+
})
85+
86+
t.Run("cpubench command fails", func(t *testing.T) {
87+
// Mock command failure
88+
mockCmd.EXPECT().CombinedOutput().Return([]byte{}, errors.New("command not found"))
89+
90+
result, err := task.Run(ctx)
91+
assert.Error(t, err)
92+
assert.Nil(t, result)
93+
assert.Contains(t, err.Error(), "failed to execute cpubench command")
94+
})
95+
96+
t.Run("invalid JSON output", func(t *testing.T) {
97+
98+
// Mock command with invalid JSON output
99+
invalidJSON := `{invalid json output}`
100+
101+
mockCmd.EXPECT().CombinedOutput().Return([]byte(invalidJSON), nil)
102+
103+
result, err := task.Run(ctx)
104+
assert.Error(t, err)
105+
assert.Nil(t, result)
106+
assert.Contains(t, err.Error(), "failed to parse cpubench output")
107+
})
108+
109+
t.Run("failed to get workloads number", func(t *testing.T) {
110+
111+
mockCmd.EXPECT().CombinedOutput().Return([]byte(`{"single": 1000, "multi": 2000, "threads": 4}`), nil)
112+
// Mock failure in getting workloads number
113+
mockZbus.EXPECT().
114+
RequestContext(gomock.Any(), "provision", zbus.ObjectID{Name: "statistics", Version: "0.0.1"}, "Workloads").
115+
Return(nil, errors.New("failed to get workloads"))
116+
117+
assert.Panics(t, func() {
118+
_, _ = task.Run(ctx)
119+
})
120+
})
121+
}
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package iperf
1+
package execwrapper
22

33
import (
44
"context"
@@ -38,6 +38,3 @@ type RealExecCmd struct {
3838
func (r *RealExecCmd) CombinedOutput() ([]byte, error) {
3939
return r.cmd.CombinedOutput()
4040
}
41-
42-
// Global instance that can be overridden in tests
43-
var execWrapper ExecWrapper = &RealExecWrapper{}

pkg/perf/iperf/mock_exec_wrapper.go renamed to pkg/perf/exec_wrapper/mock_exec_wrapper.go

Lines changed: 2 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/perf/iperf/iperf_task.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/threefoldtech/zosbase/pkg/environment"
1717
"github.com/threefoldtech/zosbase/pkg/network/iperf"
1818
"github.com/threefoldtech/zosbase/pkg/perf"
19+
"github.com/threefoldtech/zosbase/pkg/perf/exec_wrapper"
1920
"github.com/threefoldtech/zosbase/pkg/perf/graphql"
2021
)
2122

@@ -33,7 +34,7 @@ const (
3334
type IperfTest struct {
3435
// Optional dependencies for testing
3536
graphqlClient GraphQLClient
36-
execWrapper ExecWrapper
37+
execWrapper execwrapper.ExecWrapper
3738
}
3839

3940
// IperfResult for iperf test results
@@ -171,7 +172,7 @@ func (t *IperfTest) runIperfTest(ctx context.Context, clientIP string, tcp bool)
171172
opts = append(opts, "--length", "16B", "--udp")
172173
}
173174

174-
execWrap := execWrapper
175+
var execWrap execwrapper.ExecWrapper = &execwrapper.RealExecWrapper{}
175176
if t.execWrapper != nil {
176177
execWrap = t.execWrapper
177178
}
@@ -225,7 +226,7 @@ func (t *IperfTest) runIperfTest(ctx context.Context, clientIP string, tcp bool)
225226
return iperfResult
226227
}
227228

228-
func runIperfCommand(ctx context.Context, opts []string, execWrap ExecWrapper) iperfCommandOutput {
229+
func runIperfCommand(ctx context.Context, opts []string, execWrap execwrapper.ExecWrapper) iperfCommandOutput {
229230
output, err := execWrap.CommandContext(ctx, "iperf", opts...).CombinedOutput()
230231
exitErr := &exec.ExitError{}
231232

pkg/perf/iperf/iperf_task_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"testing"
88

99
"github.com/stretchr/testify/assert"
10+
execwrapper "github.com/threefoldtech/zosbase/pkg/perf/exec_wrapper"
1011
"github.com/threefoldtech/zosbase/pkg/perf/graphql"
1112
"go.uber.org/mock/gomock"
1213
)
@@ -16,8 +17,8 @@ func TestIperfTest_Run_Success(t *testing.T) {
1617
defer ctrl.Finish()
1718

1819
mockGraphQL := NewMockGraphQLClient(ctrl)
19-
mockExec := NewMockExecWrapper(ctrl)
20-
mockCmd := NewMockExecCmd(ctrl)
20+
mockExec := execwrapper.NewMockExecWrapper(ctrl)
21+
mockCmd := execwrapper.NewMockExecCmd(ctrl)
2122

2223
task := &IperfTest{
2324
graphqlClient: mockGraphQL,
@@ -111,7 +112,7 @@ func TestIperfTest_Run_IperfNotFound(t *testing.T) {
111112
defer ctrl.Finish()
112113

113114
mockGraphQL := NewMockGraphQLClient(ctrl)
114-
mockExec := NewMockExecWrapper(ctrl)
115+
mockExec := execwrapper.NewMockExecWrapper(ctrl)
115116

116117
task := &IperfTest{
117118
graphqlClient: mockGraphQL,
@@ -142,7 +143,7 @@ func TestIperfTest_Run_InvalidIPAddress(t *testing.T) {
142143
defer ctrl.Finish()
143144

144145
mockGraphQL := NewMockGraphQLClient(ctrl)
145-
mockExec := NewMockExecWrapper(ctrl)
146+
mockExec := execwrapper.NewMockExecWrapper(ctrl)
146147

147148
task := &IperfTest{
148149
graphqlClient: mockGraphQL,

0 commit comments

Comments
 (0)