Skip to content

Commit daabb15

Browse files
authored
Merge pull request #5482 from thaJeztah/restart_stop_unit_tests
cli/command/container: add unit tests for container restart and container stop
2 parents bae4b67 + ac502b5 commit daabb15

4 files changed

Lines changed: 189 additions & 0 deletions

File tree

cli/command/container/client_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ type fakeClient struct {
3535
containerExportFunc func(string) (io.ReadCloser, error)
3636
containerExecResizeFunc func(id string, options container.ResizeOptions) error
3737
containerRemoveFunc func(ctx context.Context, containerID string, options container.RemoveOptions) error
38+
containerRestartFunc func(ctx context.Context, containerID string, options container.StopOptions) error
39+
containerStopFunc func(ctx context.Context, containerID string, options container.StopOptions) error
3840
containerKillFunc func(ctx context.Context, containerID, signal string) error
3941
containerPruneFunc func(ctx context.Context, pruneFilters filters.Args) (container.PruneReport, error)
4042
containerAttachFunc func(ctx context.Context, containerID string, options container.AttachOptions) (types.HijackedResponse, error)
@@ -175,6 +177,20 @@ func (f *fakeClient) ContainersPrune(ctx context.Context, pruneFilters filters.A
175177
return container.PruneReport{}, nil
176178
}
177179

180+
func (f *fakeClient) ContainerRestart(ctx context.Context, containerID string, options container.StopOptions) error {
181+
if f.containerRestartFunc != nil {
182+
return f.containerRestartFunc(ctx, containerID, options)
183+
}
184+
return nil
185+
}
186+
187+
func (f *fakeClient) ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error {
188+
if f.containerStopFunc != nil {
189+
return f.containerStopFunc(ctx, containerID, options)
190+
}
191+
return nil
192+
}
193+
178194
func (f *fakeClient) ContainerAttach(ctx context.Context, containerID string, options container.AttachOptions) (types.HijackedResponse, error) {
179195
if f.containerAttachFunc != nil {
180196
return f.containerAttachFunc(ctx, containerID, options)

cli/command/container/restart.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ func runRestart(ctx context.Context, dockerCli command.Cli, opts *restartOptions
5555
if opts.timeoutChanged {
5656
timeout = &opts.timeout
5757
}
58+
59+
// TODO(thaJeztah): consider using parallelOperation for restart, similar to "stop" and "remove"
5860
for _, name := range opts.containers {
5961
err := dockerCli.Client().ContainerRestart(ctx, name, container.StopOptions{
6062
Signal: opts.signal,
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package container
2+
3+
import (
4+
"context"
5+
"io"
6+
"sort"
7+
"sync"
8+
"testing"
9+
10+
"github.com/docker/cli/internal/test"
11+
"github.com/docker/docker/api/types/container"
12+
"github.com/docker/docker/errdefs"
13+
"github.com/pkg/errors"
14+
"gotest.tools/v3/assert"
15+
is "gotest.tools/v3/assert/cmp"
16+
)
17+
18+
func TestRestart(t *testing.T) {
19+
for _, tc := range []struct {
20+
name string
21+
args []string
22+
restarted []string
23+
expectedOpts container.StopOptions
24+
expectedErr string
25+
}{
26+
{
27+
name: "without options",
28+
args: []string{"container-1", "container-2"},
29+
restarted: []string{"container-1", "container-2"},
30+
},
31+
{
32+
name: "with unknown container",
33+
args: []string{"container-1", "nosuchcontainer", "container-2"},
34+
expectedErr: "no such container",
35+
restarted: []string{"container-1", "container-2"},
36+
},
37+
{
38+
name: "with -t",
39+
args: []string{"-t", "2", "container-1"},
40+
expectedOpts: container.StopOptions{Timeout: func(to int) *int { return &to }(2)},
41+
restarted: []string{"container-1"},
42+
},
43+
{
44+
name: "with --time",
45+
args: []string{"--time", "2", "container-1"},
46+
expectedOpts: container.StopOptions{Timeout: func(to int) *int { return &to }(2)},
47+
restarted: []string{"container-1"},
48+
},
49+
} {
50+
tc := tc
51+
t.Run(tc.name, func(t *testing.T) {
52+
var restarted []string
53+
mutex := new(sync.Mutex)
54+
55+
cli := test.NewFakeCli(&fakeClient{
56+
containerRestartFunc: func(ctx context.Context, containerID string, options container.StopOptions) error {
57+
assert.Check(t, is.DeepEqual(options, tc.expectedOpts))
58+
if containerID == "nosuchcontainer" {
59+
return errdefs.NotFound(errors.New("Error: no such container: " + containerID))
60+
}
61+
62+
// TODO(thaJeztah): consider using parallelOperation for restart, similar to "stop" and "remove"
63+
mutex.Lock()
64+
restarted = append(restarted, containerID)
65+
mutex.Unlock()
66+
return nil
67+
},
68+
Version: "1.36",
69+
})
70+
cmd := NewRestartCommand(cli)
71+
cmd.SetOut(io.Discard)
72+
cmd.SetErr(io.Discard)
73+
cmd.SetArgs(tc.args)
74+
75+
err := cmd.Execute()
76+
if tc.expectedErr != "" {
77+
assert.Check(t, is.ErrorContains(err, tc.expectedErr))
78+
} else {
79+
assert.Check(t, is.Nil(err))
80+
}
81+
sort.Strings(restarted)
82+
assert.Check(t, is.DeepEqual(restarted, tc.restarted))
83+
})
84+
}
85+
}

cli/command/container/stop_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package container
2+
3+
import (
4+
"context"
5+
"io"
6+
"sort"
7+
"sync"
8+
"testing"
9+
10+
"github.com/docker/cli/internal/test"
11+
"github.com/docker/docker/api/types/container"
12+
"github.com/docker/docker/errdefs"
13+
"github.com/pkg/errors"
14+
"gotest.tools/v3/assert"
15+
is "gotest.tools/v3/assert/cmp"
16+
)
17+
18+
func TestStop(t *testing.T) {
19+
for _, tc := range []struct {
20+
name string
21+
args []string
22+
stopped []string
23+
expectedOpts container.StopOptions
24+
expectedErr string
25+
}{
26+
{
27+
name: "without options",
28+
args: []string{"container-1", "container-2"},
29+
stopped: []string{"container-1", "container-2"},
30+
},
31+
{
32+
name: "with unknown container",
33+
args: []string{"container-1", "nosuchcontainer", "container-2"},
34+
expectedErr: "no such container",
35+
stopped: []string{"container-1", "container-2"},
36+
},
37+
{
38+
name: "with -t",
39+
args: []string{"-t", "2", "container-1"},
40+
expectedOpts: container.StopOptions{Timeout: func(to int) *int { return &to }(2)},
41+
stopped: []string{"container-1"},
42+
},
43+
{
44+
name: "with --time",
45+
args: []string{"--time", "2", "container-1"},
46+
expectedOpts: container.StopOptions{Timeout: func(to int) *int { return &to }(2)},
47+
stopped: []string{"container-1"},
48+
},
49+
} {
50+
tc := tc
51+
t.Run(tc.name, func(t *testing.T) {
52+
var stopped []string
53+
mutex := new(sync.Mutex)
54+
55+
cli := test.NewFakeCli(&fakeClient{
56+
containerStopFunc: func(ctx context.Context, containerID string, options container.StopOptions) error {
57+
assert.Check(t, is.DeepEqual(options, tc.expectedOpts))
58+
if containerID == "nosuchcontainer" {
59+
return errdefs.NotFound(errors.New("Error: no such container: " + containerID))
60+
}
61+
62+
// containerStopFunc is called in parallel for each container
63+
// so append must be synchronized.
64+
mutex.Lock()
65+
stopped = append(stopped, containerID)
66+
mutex.Unlock()
67+
return nil
68+
},
69+
Version: "1.36",
70+
})
71+
cmd := NewStopCommand(cli)
72+
cmd.SetOut(io.Discard)
73+
cmd.SetErr(io.Discard)
74+
cmd.SetArgs(tc.args)
75+
76+
err := cmd.Execute()
77+
if tc.expectedErr != "" {
78+
assert.Check(t, is.ErrorContains(err, tc.expectedErr))
79+
} else {
80+
assert.Check(t, is.Nil(err))
81+
}
82+
sort.Strings(stopped)
83+
assert.Check(t, is.DeepEqual(stopped, tc.stopped))
84+
})
85+
}
86+
}

0 commit comments

Comments
 (0)