瀏覽代碼

volume/mounts: cleanup tests

- don't use un-keyed structs
- user assert.Check where possible
- use consts for fixed values

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Sebastiaan van Stijn 1 年之前
父節點
當前提交
d69b1fdb72

+ 106 - 26
volume/mounts/lcow_parser_test.go

@@ -1,12 +1,12 @@
 package mounts // import "github.com/docker/docker/volume/mounts"
 
 import (
-	"fmt"
 	"strings"
 	"testing"
 
 	"github.com/docker/docker/api/types/mount"
 	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 func TestLCOWParseMountRaw(t *testing.T) {
@@ -112,18 +112,98 @@ func TestLCOWParseMountRawSplit(t *testing.T) {
 		expRW     bool
 		fail      bool
 	}{
-		{`c:\:/foo`, "local", mount.TypeBind, `/foo`, `c:\`, ``, "", true, false},
-		{`c:\:/foo:ro`, "local", mount.TypeBind, `/foo`, `c:\`, ``, "", false, false},
-		{`c:\:/foo:rw`, "local", mount.TypeBind, `/foo`, `c:\`, ``, "", true, false},
-		{`c:\:/foo:foo`, "local", mount.TypeBind, `/foo`, `c:\`, ``, "", false, true},
-		{`name:/foo:rw`, "local", mount.TypeVolume, `/foo`, ``, `name`, "local", true, false},
-		{`name:/foo`, "local", mount.TypeVolume, `/foo`, ``, `name`, "local", true, false},
-		{`name:/foo:ro`, "local", mount.TypeVolume, `/foo`, ``, `name`, "local", false, false},
-		{`name:/`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
-		{`driver/name:/`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
-		{`\\.\pipe\foo:\\.\pipe\bar`, "local", mount.TypeNamedPipe, `\\.\pipe\bar`, `\\.\pipe\foo`, "", "", true, true},
-		{`\\.\pipe\foo:/data`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
-		{`c:\foo\bar:\\.\pipe\foo`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
+		{
+			bind:      `c:\:/foo`,
+			driver:    "local",
+			expType:   mount.TypeBind,
+			expDest:   `/foo`,
+			expSource: `c:\`,
+			expRW:     true,
+		},
+		{
+			bind:      `c:\:/foo:ro`,
+			driver:    "local",
+			expType:   mount.TypeBind,
+			expDest:   `/foo`,
+			expSource: `c:\`,
+		},
+		{
+			bind:      `c:\:/foo:rw`,
+			driver:    "local",
+			expType:   mount.TypeBind,
+			expDest:   `/foo`,
+			expSource: `c:\`,
+			expRW:     true,
+		},
+		{
+			bind:      `c:\:/foo:foo`,
+			driver:    "local",
+			expType:   mount.TypeBind,
+			expDest:   `/foo`,
+			expSource: `c:\`,
+			fail:      true,
+		},
+		{
+			bind:      `name:/foo:rw`,
+			driver:    "local",
+			expType:   mount.TypeVolume,
+			expDest:   `/foo`,
+			expName:   `name`,
+			expDriver: "local",
+			expRW:     true,
+		},
+		{
+			bind:      `name:/foo`,
+			driver:    "local",
+			expType:   mount.TypeVolume,
+			expDest:   `/foo`,
+			expName:   `name`,
+			expDriver: "local",
+			expRW:     true,
+		},
+		{
+			bind:      `name:/foo:ro`,
+			driver:    "local",
+			expType:   mount.TypeVolume,
+			expDest:   `/foo`,
+			expName:   `name`,
+			expDriver: "local",
+		},
+		{
+			bind:    `name:/`,
+			expType: mount.TypeVolume,
+			expRW:   true,
+			fail:    true,
+		},
+		{
+			bind:    `driver/name:/`,
+			expType: mount.TypeVolume,
+			expRW:   true,
+			fail:    true,
+		},
+		{
+			bind:      `\\.\pipe\foo:\\.\pipe\bar`,
+			driver:    "local",
+			expType:   mount.TypeNamedPipe,
+			expDest:   `\\.\pipe\bar`,
+			expSource: `\\.\pipe\foo`,
+			expRW:     true,
+			fail:      true,
+		},
+		{
+			bind:    `\\.\pipe\foo:/data`,
+			driver:  "local",
+			expType: mount.TypeNamedPipe,
+			expRW:   true,
+			fail:    true,
+		},
+		{
+			bind:    `c:\foo\bar:\\.\pipe\foo`,
+			driver:  "local",
+			expType: mount.TypeNamedPipe,
+			expRW:   true,
+			fail:    true,
+		},
 	}
 
 	parser := NewLCOWParser()
@@ -131,22 +211,22 @@ func TestLCOWParseMountRawSplit(t *testing.T) {
 		p.fi = mockFiProvider{}
 	}
 
-	for i, c := range cases {
-		c := c
-		t.Run(fmt.Sprintf("%d_%s", i, c.bind), func(t *testing.T) {
-			m, err := parser.ParseMountRaw(c.bind, c.driver)
-			if c.fail {
-				assert.ErrorContains(t, err, "", "expected an error")
+	for _, tc := range cases {
+		tc := tc
+		t.Run(tc.bind, func(t *testing.T) {
+			m, err := parser.ParseMountRaw(tc.bind, tc.driver)
+			if tc.fail {
+				assert.Check(t, is.ErrorContains(err, ""), "expected an error")
 				return
 			}
 
-			assert.NilError(t, err)
-			assert.Equal(t, m.Destination, c.expDest)
-			assert.Equal(t, m.Source, c.expSource)
-			assert.Equal(t, m.Name, c.expName)
-			assert.Equal(t, m.Driver, c.expDriver)
-			assert.Equal(t, m.RW, c.expRW)
-			assert.Equal(t, m.Type, c.expType)
+			assert.Check(t, err)
+			assert.Check(t, is.Equal(m.Destination, tc.expDest))
+			assert.Check(t, is.Equal(m.Source, tc.expSource))
+			assert.Check(t, is.Equal(m.Name, tc.expName))
+			assert.Check(t, is.Equal(m.Driver, tc.expDriver))
+			assert.Check(t, is.Equal(m.RW, tc.expRW))
+			assert.Check(t, is.Equal(m.Type, tc.expType))
 		})
 	}
 }

+ 83 - 29
volume/mounts/linux_parser_test.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/docker/docker/api/types/mount"
 	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 func TestLinuxParseMountRaw(t *testing.T) {
@@ -109,15 +110,68 @@ func TestLinuxParseMountRawSplit(t *testing.T) {
 		expRW     bool
 		fail      bool
 	}{
-		{"/tmp:/tmp1", "", mount.TypeBind, "/tmp1", "/tmp", "", "", true, false},
-		{"/tmp:/tmp2:ro", "", mount.TypeBind, "/tmp2", "/tmp", "", "", false, false},
-		{"/tmp:/tmp3:rw", "", mount.TypeBind, "/tmp3", "/tmp", "", "", true, false},
-		{"/tmp:/tmp4:foo", "", mount.TypeBind, "", "", "", "", false, true},
-		{"name:/named1", "", mount.TypeVolume, "/named1", "", "name", "", true, false},
-		{"name:/named2", "external", mount.TypeVolume, "/named2", "", "name", "external", true, false},
-		{"name:/named3:ro", "local", mount.TypeVolume, "/named3", "", "name", "local", false, false},
-		{"local/name:/tmp:rw", "", mount.TypeVolume, "/tmp", "", "local/name", "", true, false},
-		{"/tmp:tmp", "", mount.TypeBind, "", "", "", "", true, true},
+		{
+			bind:      "/tmp:/tmp1",
+			expType:   mount.TypeBind,
+			expDest:   "/tmp1",
+			expSource: "/tmp",
+			expRW:     true,
+		},
+		{
+			bind:      "/tmp:/tmp2:ro",
+			expType:   mount.TypeBind,
+			expDest:   "/tmp2",
+			expSource: "/tmp",
+		},
+		{
+			bind:      "/tmp:/tmp3:rw",
+			expType:   mount.TypeBind,
+			expDest:   "/tmp3",
+			expSource: "/tmp",
+			expRW:     true,
+		},
+		{
+			bind:    "/tmp:/tmp4:foo",
+			expType: mount.TypeBind,
+			fail:    true,
+		},
+		{
+			bind:    "name:/named1",
+			expType: mount.TypeVolume,
+			expDest: "/named1",
+			expName: "name",
+			expRW:   true,
+		},
+		{
+			bind:      "name:/named2",
+			driver:    "external",
+			expType:   mount.TypeVolume,
+			expDest:   "/named2",
+			expName:   "name",
+			expDriver: "external",
+			expRW:     true,
+		},
+		{
+			bind:      "name:/named3:ro",
+			driver:    "local",
+			expType:   mount.TypeVolume,
+			expDest:   "/named3",
+			expName:   "name",
+			expDriver: "local",
+		},
+		{
+			bind:    "local/name:/tmp:rw",
+			expType: mount.TypeVolume,
+			expDest: "/tmp",
+			expName: "local/name",
+			expRW:   true,
+		},
+		{
+			bind:    "/tmp:tmp",
+			expType: mount.TypeBind,
+			expRW:   true,
+			fail:    true,
+		},
 	}
 
 	parser := NewLinuxParser()
@@ -125,22 +179,22 @@ func TestLinuxParseMountRawSplit(t *testing.T) {
 		p.fi = mockFiProvider{}
 	}
 
-	for i, c := range cases {
-		c := c
-		t.Run(fmt.Sprintf("%d_%s", i, c.bind), func(t *testing.T) {
-			m, err := parser.ParseMountRaw(c.bind, c.driver)
-			if c.fail {
-				assert.ErrorContains(t, err, "", "expected an error")
+	for _, tc := range cases {
+		tc := tc
+		t.Run(tc.bind, func(t *testing.T) {
+			m, err := parser.ParseMountRaw(tc.bind, tc.driver)
+			if tc.fail {
+				assert.Check(t, is.ErrorContains(err, ""), "expected an error")
 				return
 			}
 
-			assert.NilError(t, err)
-			assert.Equal(t, m.Destination, c.expDest)
-			assert.Equal(t, m.Source, c.expSource)
-			assert.Equal(t, m.Name, c.expName)
-			assert.Equal(t, m.Driver, c.expDriver)
-			assert.Equal(t, m.RW, c.expRW)
-			assert.Equal(t, m.Type, c.expType)
+			assert.Check(t, err)
+			assert.Check(t, is.Equal(m.Destination, tc.expDest))
+			assert.Check(t, is.Equal(m.Source, tc.expSource))
+			assert.Check(t, is.Equal(m.Name, tc.expName))
+			assert.Check(t, is.Equal(m.Driver, tc.expDriver))
+			assert.Check(t, is.Equal(m.RW, tc.expRW))
+			assert.Check(t, is.Equal(m.Type, tc.expType))
 		})
 	}
 }
@@ -200,21 +254,21 @@ func TestConvertTmpfsOptions(t *testing.T) {
 		},
 	}
 	p := NewLinuxParser()
-	for _, c := range cases {
-		data, err := p.ConvertTmpfsOptions(&c.opt, c.readOnly)
+	for _, tc := range cases {
+		data, err := p.ConvertTmpfsOptions(&tc.opt, tc.readOnly)
 		if err != nil {
 			t.Fatalf("could not convert %+v (readOnly: %v) to string: %v",
-				c.opt, c.readOnly, err)
+				tc.opt, tc.readOnly, err)
 		}
 		t.Logf("data=%q", data)
-		for _, s := range c.expectedSubstrings {
+		for _, s := range tc.expectedSubstrings {
 			if !strings.Contains(data, s) {
-				t.Fatalf("expected substring: %s, got %v (case=%+v)", s, data, c)
+				t.Fatalf("expected substring: %s, got %v (case=%+v)", s, data, tc)
 			}
 		}
-		for _, s := range c.unexpectedSubstrings {
+		for _, s := range tc.unexpectedSubstrings {
 			if strings.Contains(data, s) {
-				t.Fatalf("unexpected substring: %s, got %v (case=%+v)", s, data, c)
+				t.Fatalf("unexpected substring: %s, got %v (case=%+v)", s, data, tc)
 			}
 		}
 	}

+ 39 - 34
volume/mounts/parser_test.go

@@ -5,6 +5,8 @@ import (
 	"testing"
 
 	"github.com/docker/docker/api/types/mount"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 type mockFiProvider struct{}
@@ -50,41 +52,44 @@ func TestParseMountSpec(t *testing.T) {
 		input    mount.Mount
 		expected MountPoint
 	}{
-		{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath, ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: parser.DefaultPropagationMode()}},
-		{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, RW: true, Propagation: parser.DefaultPropagationMode()}},
-		{mount.Mount{Type: mount.TypeBind, Source: testDir + string(os.PathSeparator), Target: testDestinationPath, ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: parser.DefaultPropagationMode()}},
-		{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath + string(os.PathSeparator), ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: parser.DefaultPropagationMode()}},
-		{mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath}, MountPoint{Type: mount.TypeVolume, Destination: testDestinationPath, RW: true, CopyData: parser.DefaultCopyMode()}},
-		{mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath + string(os.PathSeparator)}, MountPoint{Type: mount.TypeVolume, Destination: testDestinationPath, RW: true, CopyData: parser.DefaultCopyMode()}},
+		{
+			input:    mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath, ReadOnly: true},
+			expected: MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: parser.DefaultPropagationMode()},
+		},
+		{
+			input:    mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath},
+			expected: MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, RW: true, Propagation: parser.DefaultPropagationMode()},
+		},
+		{
+			input:    mount.Mount{Type: mount.TypeBind, Source: testDir + string(os.PathSeparator), Target: testDestinationPath, ReadOnly: true},
+			expected: MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: parser.DefaultPropagationMode()},
+		},
+		{
+			input:    mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath + string(os.PathSeparator), ReadOnly: true},
+			expected: MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: parser.DefaultPropagationMode()},
+		},
+		{
+			input:    mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath},
+			expected: MountPoint{Type: mount.TypeVolume, Destination: testDestinationPath, RW: true, CopyData: parser.DefaultCopyMode()},
+		},
+		{
+			input:    mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath + string(os.PathSeparator)},
+			expected: MountPoint{Type: mount.TypeVolume, Destination: testDestinationPath, RW: true, CopyData: parser.DefaultCopyMode()},
+		},
 	}
 
-	for i, c := range cases {
-		t.Logf("case %d", i)
-		mp, err := parser.ParseMountSpec(c.input)
-		if err != nil {
-			t.Error(err)
-		}
-
-		if c.expected.Type != mp.Type {
-			t.Errorf("Expected mount types to match. Expected: '%s', Actual: '%s'", c.expected.Type, mp.Type)
-		}
-		if c.expected.Destination != mp.Destination {
-			t.Errorf("Expected mount destination to match. Expected: '%s', Actual: '%s'", c.expected.Destination, mp.Destination)
-		}
-		if c.expected.Source != mp.Source {
-			t.Errorf("Expected mount source to match. Expected: '%s', Actual: '%s'", c.expected.Source, mp.Source)
-		}
-		if c.expected.RW != mp.RW {
-			t.Errorf("Expected mount writable to match. Expected: '%v', Actual: '%v'", c.expected.RW, mp.RW)
-		}
-		if c.expected.Propagation != mp.Propagation {
-			t.Errorf("Expected mount propagation to match. Expected: '%v', Actual: '%s'", c.expected.Propagation, mp.Propagation)
-		}
-		if c.expected.Driver != mp.Driver {
-			t.Errorf("Expected mount driver to match. Expected: '%v', Actual: '%s'", c.expected.Driver, mp.Driver)
-		}
-		if c.expected.CopyData != mp.CopyData {
-			t.Errorf("Expected mount copy data to match. Expected: '%v', Actual: '%v'", c.expected.CopyData, mp.CopyData)
-		}
+	for _, tc := range cases {
+		tc := tc
+		t.Run("", func(t *testing.T) {
+			mp, err := parser.ParseMountSpec(tc.input)
+			assert.Check(t, err)
+			assert.Check(t, is.Equal(mp.Type, tc.expected.Type))
+			assert.Check(t, is.Equal(mp.Destination, tc.expected.Destination))
+			assert.Check(t, is.Equal(mp.Source, tc.expected.Source))
+			assert.Check(t, is.Equal(mp.RW, tc.expected.RW))
+			assert.Check(t, is.Equal(mp.Propagation, tc.expected.Propagation))
+			assert.Check(t, is.Equal(mp.Driver, tc.expected.Driver))
+			assert.Check(t, is.Equal(mp.CopyData, tc.expected.CopyData))
+		})
 	}
 }

+ 99 - 47
volume/mounts/validate_test.go

@@ -2,71 +2,123 @@ package mounts // import "github.com/docker/docker/volume/mounts"
 
 import (
 	"errors"
-	"os"
 	"runtime"
-	"strings"
 	"testing"
 
 	"github.com/docker/docker/api/types/mount"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 func TestValidateMount(t *testing.T) {
-	testDir, err := os.MkdirTemp("", "test-validate-mount")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(testDir)
+	testDir := t.TempDir()
+	parser := NewParser()
 
-	cases := []struct {
+	tests := []struct {
 		input    mount.Mount
 		expected error
 	}{
-		{mount.Mount{Type: mount.TypeVolume}, errMissingField("Target")},
-		{mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath, Source: "hello"}, nil},
-		{mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath}, nil},
-		{mount.Mount{Type: mount.TypeBind}, errMissingField("Target")},
-		{mount.Mount{Type: mount.TypeBind, Target: testDestinationPath}, errMissingField("Source")},
-		{mount.Mount{Type: mount.TypeBind, Target: testDestinationPath, Source: testSourcePath, VolumeOptions: &mount.VolumeOptions{}}, errExtraField("VolumeOptions")},
+		{
+			input:    mount.Mount{Type: mount.TypeVolume},
+			expected: errMissingField("Target"),
+		},
+		{
+			input: mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath, Source: "hello"},
+		},
+		{
+			input: mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath},
+		},
+		{
+			input:    mount.Mount{Type: mount.TypeBind},
+			expected: errMissingField("Target"),
+		},
+		{
+			input:    mount.Mount{Type: mount.TypeBind, Target: testDestinationPath},
+			expected: errMissingField("Source"),
+		},
+		{
+			input:    mount.Mount{Type: mount.TypeBind, Target: testDestinationPath, Source: testSourcePath, VolumeOptions: &mount.VolumeOptions{}},
+			expected: errExtraField("VolumeOptions"),
+		},
+		{
+			input: mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath},
+		},
+		{
+			input:    mount.Mount{Type: "invalid", Target: testDestinationPath},
+			expected: errors.New("mount type unknown"),
+		},
+		{
+			input:    mount.Mount{Type: mount.TypeBind, Source: testSourcePath, Target: testDestinationPath},
+			expected: errBindSourceDoesNotExist(testSourcePath),
+		},
+	}
+	for _, tc := range tests {
+		tc := tc
+		t.Run("", func(t *testing.T) {
+			err := parser.ValidateMountConfig(&tc.input)
+			if tc.expected != nil {
+				assert.Check(t, is.ErrorContains(err, tc.expected.Error()))
+			} else {
+				assert.Check(t, err)
+			}
+		})
+	}
+}
 
-		{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath}, nil},
-		{mount.Mount{Type: "invalid", Target: testDestinationPath}, errors.New("mount type unknown")},
-		{mount.Mount{Type: mount.TypeBind, Source: testSourcePath, Target: testDestinationPath}, errBindSourceDoesNotExist(testSourcePath)},
+func TestValidateLCOWMount(t *testing.T) {
+	if runtime.GOOS != "windows" {
+		t.Skip("only tested on Windows")
 	}
+	testDir := t.TempDir()
+	parser := NewLCOWParser()
 
-	lcowCases := []struct {
+	tests := []struct {
 		input    mount.Mount
 		expected error
 	}{
-		{mount.Mount{Type: mount.TypeVolume}, errMissingField("Target")},
-		{mount.Mount{Type: mount.TypeVolume, Target: "/foo", Source: "hello"}, nil},
-		{mount.Mount{Type: mount.TypeVolume, Target: "/foo"}, nil},
-		{mount.Mount{Type: mount.TypeBind}, errMissingField("Target")},
-		{mount.Mount{Type: mount.TypeBind, Target: "/foo"}, errMissingField("Source")},
-		{mount.Mount{Type: mount.TypeBind, Target: "/foo", Source: "c:\\foo", VolumeOptions: &mount.VolumeOptions{}}, errExtraField("VolumeOptions")},
-		{mount.Mount{Type: mount.TypeBind, Source: "c:\\foo", Target: "/foo"}, errBindSourceDoesNotExist("c:\\foo")},
-		{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: "/foo"}, nil},
-		{mount.Mount{Type: "invalid", Target: "/foo"}, errors.New("mount type unknown")},
-	}
-	parser := NewParser()
-	for i, x := range cases {
-		err := parser.ValidateMountConfig(&x.input)
-		if err == nil && x.expected == nil {
-			continue
-		}
-		if (err == nil && x.expected != nil) || (x.expected == nil && err != nil) || !strings.Contains(err.Error(), x.expected.Error()) {
-			t.Errorf("expected %q, got %q, case: %d", x.expected, err, i)
-		}
+		{
+			input:    mount.Mount{Type: mount.TypeVolume},
+			expected: errMissingField("Target"),
+		},
+		{
+			input: mount.Mount{Type: mount.TypeVolume, Target: "/foo", Source: "hello"},
+		},
+		{
+			input: mount.Mount{Type: mount.TypeVolume, Target: "/foo"},
+		},
+		{
+			input:    mount.Mount{Type: mount.TypeBind},
+			expected: errMissingField("Target"),
+		},
+		{
+			input:    mount.Mount{Type: mount.TypeBind, Target: "/foo"},
+			expected: errMissingField("Source"),
+		},
+		{
+			input:    mount.Mount{Type: mount.TypeBind, Target: "/foo", Source: "c:\\foo", VolumeOptions: &mount.VolumeOptions{}},
+			expected: errExtraField("VolumeOptions"),
+		},
+		{
+			input:    mount.Mount{Type: mount.TypeBind, Source: "c:\\foo", Target: "/foo"},
+			expected: errBindSourceDoesNotExist("c:\\foo"),
+		},
+		{
+			input: mount.Mount{Type: mount.TypeBind, Source: testDir, Target: "/foo"},
+		},
+		{
+			input:    mount.Mount{Type: "invalid", Target: "/foo"},
+			expected: errors.New("mount type unknown"),
+		},
 	}
-	if runtime.GOOS == "windows" {
-		parser = NewLCOWParser()
-		for i, x := range lcowCases {
-			err := parser.ValidateMountConfig(&x.input)
-			if err == nil && x.expected == nil {
-				continue
-			}
-			if (err == nil && x.expected != nil) || (x.expected == nil && err != nil) || !strings.Contains(err.Error(), x.expected.Error()) {
-				t.Errorf("expected %q, got %q, case: %d", x.expected, err, i)
+	for _, tc := range tests {
+		tc := tc
+		t.Run("", func(t *testing.T) {
+			err := parser.ValidateMountConfig(&tc.input)
+			if tc.expected != nil {
+				assert.Check(t, is.ErrorContains(err, tc.expected.Error()))
+			} else {
+				assert.Check(t, err)
 			}
-		}
+		})
 	}
 }

+ 1 - 1
volume/mounts/validate_unix_test.go

@@ -2,7 +2,7 @@
 
 package mounts // import "github.com/docker/docker/volume/mounts"
 
-var (
+const (
 	testDestinationPath = "/foo"
 	testSourcePath      = "/foo"
 )

+ 1 - 1
volume/mounts/validate_windows_test.go

@@ -1,6 +1,6 @@
 package mounts // import "github.com/docker/docker/volume/mounts"
 
-var (
+const (
 	testDestinationPath = `c:\foo`
 	testSourcePath      = `c:\foo`
 )

+ 113 - 26
volume/mounts/windows_parser_test.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/docker/docker/api/types/mount"
 	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 func TestWindowsParseMountRaw(t *testing.T) {
@@ -118,19 +119,105 @@ func TestWindowsParseMountRawSplit(t *testing.T) {
 		expRW     bool
 		fail      bool
 	}{
-		{`c:\:d:`, "local", mount.TypeBind, `d:`, `c:\`, ``, "", true, false},
-		{`c:\:d:\`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", true, false},
-		{`c:\:d:\:ro`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", false, false},
-		{`c:\:d:\:rw`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", true, false},
-		{`c:\:d:\:foo`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", false, true},
-		{`name:d::rw`, "local", mount.TypeVolume, `d:`, ``, `name`, "local", true, false},
-		{`name:d:`, "local", mount.TypeVolume, `d:`, ``, `name`, "local", true, false},
-		{`name:d::ro`, "local", mount.TypeVolume, `d:`, ``, `name`, "local", false, false},
-		{`name:c:`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
-		{`driver/name:c:`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
-		{`\\.\pipe\foo:\\.\pipe\bar`, "local", mount.TypeNamedPipe, `\\.\pipe\bar`, `\\.\pipe\foo`, "", "", true, false},
-		{`\\.\pipe\foo:c:\foo\bar`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
-		{`c:\foo\bar:\\.\pipe\foo`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
+		{
+			bind:      `c:\:d:`,
+			driver:    "local",
+			expType:   mount.TypeBind,
+			expDest:   `d:`,
+			expSource: `c:\`,
+			expRW:     true,
+		},
+		{
+			bind:      `c:\:d:\`,
+			driver:    "local",
+			expType:   mount.TypeBind,
+			expDest:   `d:\`,
+			expSource: `c:\`,
+			expRW:     true,
+		},
+		{
+			bind:      `c:\:d:\:ro`,
+			driver:    "local",
+			expType:   mount.TypeBind,
+			expDest:   `d:\`,
+			expSource: `c:\`,
+		},
+		{
+			bind:      `c:\:d:\:rw`,
+			driver:    "local",
+			expType:   mount.TypeBind,
+			expDest:   `d:\`,
+			expSource: `c:\`,
+			expRW:     true,
+		},
+		{
+			bind:      `c:\:d:\:foo`,
+			driver:    "local",
+			expType:   mount.TypeBind,
+			expDest:   `d:\`,
+			expSource: `c:\`,
+			fail:      true,
+		},
+		{
+			bind:      `name:d::rw`,
+			driver:    "local",
+			expType:   mount.TypeVolume,
+			expDest:   `d:`,
+			expName:   `name`,
+			expDriver: "local",
+			expRW:     true,
+		},
+		{
+			bind:      `name:d:`,
+			driver:    "local",
+			expType:   mount.TypeVolume,
+			expDest:   `d:`,
+			expName:   `name`,
+			expDriver: "local",
+			expRW:     true,
+		},
+		{
+			bind:      `name:d::ro`,
+			driver:    "local",
+			expType:   mount.TypeVolume,
+			expDest:   `d:`,
+			expName:   `name`,
+			expDriver: "local",
+		},
+		{
+			bind:    `name:c:`,
+			expType: mount.TypeVolume,
+			expRW:   true,
+			fail:    true,
+		},
+		{
+			bind:    `driver/name:c:`,
+			expType: mount.TypeVolume,
+			expRW:   true,
+			fail:    true,
+		},
+		{
+			bind:      `\\.\pipe\foo:\\.\pipe\bar`,
+			driver:    "local",
+			expType:   mount.TypeNamedPipe,
+			expDest:   `\\.\pipe\bar`,
+			expSource: `\\.\pipe\foo`,
+			expRW:     true,
+		},
+		{
+			bind:    `\\.\pipe\foo:c:\foo\bar`,
+			driver:  "local",
+			expType: mount.TypeNamedPipe,
+			expRW:   true,
+			fail:    true,
+		},
+		{
+			bind:    `c:\foo\bar:\\.\pipe\foo`,
+			driver:  "local",
+			expType: mount.TypeNamedPipe,
+			expRW:   true,
+			fail:    true,
+		},
 	}
 
 	parser := NewWindowsParser()
@@ -138,22 +225,22 @@ func TestWindowsParseMountRawSplit(t *testing.T) {
 		p.fi = mockFiProvider{}
 	}
 
-	for i, c := range cases {
-		c := c
-		t.Run(fmt.Sprintf("%d_%s", i, c.bind), func(t *testing.T) {
-			m, err := parser.ParseMountRaw(c.bind, c.driver)
-			if c.fail {
-				assert.ErrorContains(t, err, "", "expected an error")
+	for _, tc := range cases {
+		tc := tc
+		t.Run(tc.bind, func(t *testing.T) {
+			m, err := parser.ParseMountRaw(tc.bind, tc.driver)
+			if tc.fail {
+				assert.Check(t, is.ErrorContains(err, ""), "expected an error")
 				return
 			}
 
-			assert.NilError(t, err)
-			assert.Equal(t, m.Destination, c.expDest)
-			assert.Equal(t, m.Source, c.expSource)
-			assert.Equal(t, m.Name, c.expName)
-			assert.Equal(t, m.Driver, c.expDriver)
-			assert.Equal(t, m.RW, c.expRW)
-			assert.Equal(t, m.Type, c.expType)
+			assert.Check(t, err)
+			assert.Check(t, is.Equal(m.Destination, tc.expDest))
+			assert.Check(t, is.Equal(m.Source, tc.expSource))
+			assert.Check(t, is.Equal(m.Name, tc.expName))
+			assert.Check(t, is.Equal(m.Driver, tc.expDriver))
+			assert.Check(t, is.Equal(m.RW, tc.expRW))
+			assert.Check(t, is.Equal(m.Type, tc.expType))
 		})
 	}
 }