From 90ae5b8136a6c74b230e1cd9e752935140b2f292 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 23 Dec 2025 15:05:37 +0100 Subject: [PATCH 1/7] cli/command: replace reflect for gotest.tools assertion Signed-off-by: Sebastiaan van Stijn --- cli/command/telemetry_utils_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cli/command/telemetry_utils_test.go b/cli/command/telemetry_utils_test.go index b5e3671f45e0..39c1dace604d 100644 --- a/cli/command/telemetry_utils_test.go +++ b/cli/command/telemetry_utils_test.go @@ -4,14 +4,15 @@ import ( "bytes" "context" "io" - "reflect" "strings" "testing" "github.com/docker/cli/cli/streams" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/spf13/cobra" "go.opentelemetry.io/otel/attribute" "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func setupCobraCommands() (*cobra.Command, *cobra.Command, *cobra.Command) { @@ -139,7 +140,7 @@ func TestStdioAttributes(t *testing.T) { cli.Out().SetIsTerminal(tc.stdoutTty) actual := stdioAttributes(cli) - assert.Check(t, reflect.DeepEqual(actual, tc.expected)) + assert.Check(t, is.DeepEqual(actual, tc.expected, cmpopts.EquateComparable(attribute.Value{}))) }) } } @@ -179,7 +180,7 @@ func TestAttributesFromError(t *testing.T) { t.Run(tc.testName, func(t *testing.T) { t.Parallel() actual := attributesFromError(tc.err) - assert.Check(t, reflect.DeepEqual(actual, tc.expected)) + assert.Check(t, is.DeepEqual(actual, tc.expected, cmpopts.EquateComparable(attribute.Value{}))) }) } } From a89b2e19f5723c63ede127f7ee09310b60dd7604 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 23 Dec 2025 15:06:23 +0100 Subject: [PATCH 2/7] cli/command/formatter: rewrite some tests with gotest.tools Signed-off-by: Sebastiaan van Stijn --- cli/command/formatter/reflect_test.go | 30 ++++++++++++--------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/cli/command/formatter/reflect_test.go b/cli/command/formatter/reflect_test.go index 81e71f76bd3b..76ca1a5376c0 100644 --- a/cli/command/formatter/reflect_test.go +++ b/cli/command/formatter/reflect_test.go @@ -4,8 +4,10 @@ package formatter import ( - "reflect" "testing" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) type dummy struct{} @@ -45,24 +47,18 @@ var dummyExpected = map[string]any{ func TestMarshalMap(t *testing.T) { d := dummy{} m, err := marshalMap(&d) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(dummyExpected, m) { - t.Fatalf("expected %+v, got %+v", - dummyExpected, m) - } + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(m, dummyExpected)) } func TestMarshalMapBad(t *testing.T) { - if _, err := marshalMap(nil); err == nil { - t.Fatal("expected an error (argument is nil)") - } - if _, err := marshalMap(dummy{}); err == nil { - t.Fatal("expected an error (argument is non-pointer)") - } + _, err := marshalMap(nil) + assert.Check(t, is.Error(err, "expected a pointer to a struct, got invalid"), "expected an error (argument is nil)") + + _, err = marshalMap(dummy{}) + assert.Check(t, is.Error(err, "expected a pointer to a struct, got struct"), "expected an error (argument is non-pointer)") + x := 42 - if _, err := marshalMap(&x); err == nil { - t.Fatal("expected an error (argument is a pointer to non-struct)") - } + _, err = marshalMap(&x) + assert.Check(t, is.Error(err, "expected a pointer to a struct, got a pointer to int"), "expected an error (argument is a pointer to non-struct)") } From 3811f24f472ef063da24b681741e95875bf4b506 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 23 Dec 2025 15:46:31 +0100 Subject: [PATCH 3/7] cli/connhelper: replace reflect for gotest.tools assertion Signed-off-by: Sebastiaan van Stijn --- cli/connhelper/connhelper_test.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cli/connhelper/connhelper_test.go b/cli/connhelper/connhelper_test.go index 0d9aee0fb2c6..66d2487c3423 100644 --- a/cli/connhelper/connhelper_test.go +++ b/cli/connhelper/connhelper_test.go @@ -1,7 +1,6 @@ package connhelper import ( - "reflect" "testing" "gotest.tools/v3/assert" @@ -27,7 +26,8 @@ func TestSSHFlags(t *testing.T) { } for _, tc := range testCases { - assert.DeepEqual(t, addSSHTimeout(tc.in), tc.out) + result := addSSHTimeout(tc.in) + assert.DeepEqual(t, result, tc.out) } } @@ -57,9 +57,7 @@ func TestDisablePseudoTerminalAllocation(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := disablePseudoTerminalAllocation(tc.sshFlags) - if !reflect.DeepEqual(result, tc.expected) { - t.Errorf("expected %v, got %v", tc.expected, result) - } + assert.DeepEqual(t, result, tc.expected) }) } } From e715dd50761c2f7966da47e856742d459e80e5a9 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 23 Dec 2025 15:48:37 +0100 Subject: [PATCH 4/7] cli/command/volume: remove uses of reflect in test Signed-off-by: Sebastiaan van Stijn --- cli/command/volume/create_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cli/command/volume/create_test.go b/cli/command/volume/create_test.go index cad6ee3f1cdf..8adc3327a748 100644 --- a/cli/command/volume/create_test.go +++ b/cli/command/volume/create_test.go @@ -1,10 +1,13 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package volume import ( "errors" "fmt" "io" - "reflect" + "maps" "sort" "strings" "testing" @@ -124,10 +127,10 @@ func TestVolumeCreateWithFlags(t *testing.T) { if options.Driver != expectedDriver { return client.VolumeCreateResult{}, fmt.Errorf("expected driver %q, got %q", expectedDriver, options.Driver) } - if !reflect.DeepEqual(options.DriverOpts, expectedOpts) { + if !maps.Equal(options.DriverOpts, expectedOpts) { return client.VolumeCreateResult{}, fmt.Errorf("expected drivers opts %v, got %v", expectedOpts, options.DriverOpts) } - if !reflect.DeepEqual(options.Labels, expectedLabels) { + if !maps.Equal(options.Labels, expectedLabels) { return client.VolumeCreateResult{}, fmt.Errorf("expected labels %v, got %v", expectedLabels, options.Labels) } return client.VolumeCreateResult{ From f28565d173bb0677c9d22a450f7a729a97913bf1 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 23 Dec 2025 15:49:19 +0100 Subject: [PATCH 5/7] cli/command/service: replace reflect for gotest.tools assertion Signed-off-by: Sebastiaan van Stijn --- cli/command/service/update_test.go | 34 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/cli/command/service/update_test.go b/cli/command/service/update_test.go index b2731a3aba47..bb346b2e6fb9 100644 --- a/cli/command/service/update_test.go +++ b/cli/command/service/update_test.go @@ -4,8 +4,8 @@ import ( "context" "fmt" "net/netip" - "reflect" "sort" + "strconv" "testing" "time" @@ -368,23 +368,23 @@ func TestUpdateHealthcheckTable(t *testing.T) { err: "--no-healthcheck conflicts with --health-* options", }, } - for i, c := range testCases { - flags := newUpdateCommand(nil).Flags() - for _, flag := range c.flags { - flags.Set(flag[0], flag[1]) - } - cspec := &swarm.ContainerSpec{ - Healthcheck: c.initial, - } - err := updateHealthcheck(flags, cspec) - if c.err != "" { - assert.Error(t, err, c.err) - } else { - assert.NilError(t, err) - if !reflect.DeepEqual(cspec.Healthcheck, c.expected) { - t.Errorf("incorrect result for test %d, expected health config:\n\t%#v\ngot:\n\t%#v", i, c.expected, cspec.Healthcheck) + for i, tc := range testCases { + t.Run(strconv.Itoa(i), func(t *testing.T) { + flags := newUpdateCommand(nil).Flags() + for _, flag := range tc.flags { + assert.Check(t, flags.Set(flag[0], flag[1])) } - } + cspec := &swarm.ContainerSpec{ + Healthcheck: tc.initial, + } + err := updateHealthcheck(flags, cspec) + if tc.err != "" { + assert.Error(t, err, tc.err) + } else { + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(cspec.Healthcheck, tc.expected)) + } + }) } } From 40f052c7e156d2a00c4703f4e18aa5fd6f4de5ca Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 23 Dec 2025 15:51:08 +0100 Subject: [PATCH 6/7] cli/command/container: use reflect IsZero Signed-off-by: Sebastiaan van Stijn --- cli/command/container/opts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/command/container/opts.go b/cli/command/container/opts.go index 853a7f93def3..fc93b59d4b51 100644 --- a/cli/command/container/opts.go +++ b/cli/command/container/opts.go @@ -795,7 +795,7 @@ func parseNetworkOpts(copts *containerOptions) (map[string]*network.EndpointSett // and only a single network is specified, omit the endpoint-configuration // on the client (the daemon will still create it when creating the container) if i == 0 && len(copts.netMode.Value()) == 1 { - if ep == nil || reflect.DeepEqual(*ep, network.EndpointSettings{}) { + if ep == nil || reflect.ValueOf(*ep).IsZero() { continue } } From 8205124d5b6cfeceaa3e8cb91ae0e7c228d930f7 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 23 Dec 2025 15:51:54 +0100 Subject: [PATCH 7/7] cli/command/node: nodeContext: remove uses of reflect Signed-off-by: Sebastiaan van Stijn --- cli/command/node/formatter.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/cli/command/node/formatter.go b/cli/command/node/formatter.go index 68f5d2a9e928..a2eb419c1eaa 100644 --- a/cli/command/node/formatter.go +++ b/cli/command/node/formatter.go @@ -1,9 +1,9 @@ package node import ( + "bytes" "encoding/base64" "fmt" - "reflect" "strings" "github.com/docker/cli/cli/command/formatter" @@ -170,15 +170,23 @@ func (c *nodeContext) ManagerStatus() string { } func (c *nodeContext) TLSStatus() string { - if c.info.Cluster == nil || reflect.DeepEqual(c.info.Cluster.TLSInfo, swarm.TLSInfo{}) || reflect.DeepEqual(c.n.Description.TLSInfo, swarm.TLSInfo{}) { + if c.info.Cluster == nil || isEmptyTLSInfo(c.info.Cluster.TLSInfo) || isEmptyTLSInfo(c.n.Description.TLSInfo) { return "Unknown" } - if reflect.DeepEqual(c.n.Description.TLSInfo, c.info.Cluster.TLSInfo) { + if equalTLSInfo(c.n.Description.TLSInfo, c.info.Cluster.TLSInfo) { return "Ready" } return "Needs Rotation" } +func isEmptyTLSInfo(t swarm.TLSInfo) bool { + return t.TrustRoot == "" && len(t.CertIssuerSubject) == 0 && len(t.CertIssuerPublicKey) == 0 +} + +func equalTLSInfo(t, o swarm.TLSInfo) bool { + return t.TrustRoot == o.TrustRoot && bytes.Equal(t.CertIssuerSubject, o.CertIssuerSubject) && bytes.Equal(t.CertIssuerPublicKey, o.CertIssuerPublicKey) +} + func (c *nodeContext) EngineVersion() string { return c.n.Description.Engine.EngineVersion } @@ -320,8 +328,7 @@ func (ctx *nodeInspectContext) EngineVersion() string { } func (ctx *nodeInspectContext) HasTLSInfo() bool { - tlsInfo := ctx.Node.Description.TLSInfo - return !reflect.DeepEqual(tlsInfo, swarm.TLSInfo{}) + return !isEmptyTLSInfo(ctx.Node.Description.TLSInfo) } func (ctx *nodeInspectContext) TLSInfoTrustRoot() string {