github.com/ali-iotechsys/cli@v20.10.0+incompatible/cli/compose/loader/volume_test.go (about) 1 package loader 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/docker/cli/cli/compose/types" 8 "gotest.tools/v3/assert" 9 is "gotest.tools/v3/assert/cmp" 10 ) 11 12 func TestParseVolumeAnonymousVolume(t *testing.T) { 13 for _, path := range []string{"/path", "/path/foo"} { 14 volume, err := ParseVolume(path) 15 expected := types.ServiceVolumeConfig{Type: "volume", Target: path} 16 assert.NilError(t, err) 17 assert.Check(t, is.DeepEqual(expected, volume)) 18 } 19 } 20 21 func TestParseVolumeAnonymousVolumeWindows(t *testing.T) { 22 for _, path := range []string{"C:\\path", "Z:\\path\\foo"} { 23 volume, err := ParseVolume(path) 24 expected := types.ServiceVolumeConfig{Type: "volume", Target: path} 25 assert.NilError(t, err) 26 assert.Check(t, is.DeepEqual(expected, volume)) 27 } 28 } 29 30 func TestParseVolumeTooManyColons(t *testing.T) { 31 _, err := ParseVolume("/foo:/foo:ro:foo") 32 assert.Error(t, err, "invalid spec: /foo:/foo:ro:foo: too many colons") 33 } 34 35 func TestParseVolumeShortVolumes(t *testing.T) { 36 for _, path := range []string{".", "/a"} { 37 volume, err := ParseVolume(path) 38 expected := types.ServiceVolumeConfig{Type: "volume", Target: path} 39 assert.NilError(t, err) 40 assert.Check(t, is.DeepEqual(expected, volume)) 41 } 42 } 43 44 func TestParseVolumeMissingSource(t *testing.T) { 45 for _, spec := range []string{":foo", "/foo::ro"} { 46 _, err := ParseVolume(spec) 47 assert.ErrorContains(t, err, "empty section between colons") 48 } 49 } 50 51 func TestParseVolumeBindMount(t *testing.T) { 52 for _, path := range []string{"./foo", "~/thing", "../other", "/foo", "/home/user"} { 53 volume, err := ParseVolume(path + ":/target") 54 expected := types.ServiceVolumeConfig{ 55 Type: "bind", 56 Source: path, 57 Target: "/target", 58 } 59 assert.NilError(t, err) 60 assert.Check(t, is.DeepEqual(expected, volume)) 61 } 62 } 63 64 func TestParseVolumeRelativeBindMountWindows(t *testing.T) { 65 for _, path := range []string{ 66 "./foo", 67 "~/thing", 68 "../other", 69 "D:\\path", "/home/user", 70 } { 71 volume, err := ParseVolume(path + ":d:\\target") 72 expected := types.ServiceVolumeConfig{ 73 Type: "bind", 74 Source: path, 75 Target: "d:\\target", 76 } 77 assert.NilError(t, err) 78 assert.Check(t, is.DeepEqual(expected, volume)) 79 } 80 } 81 82 func TestParseVolumeWithBindOptions(t *testing.T) { 83 volume, err := ParseVolume("/source:/target:slave") 84 expected := types.ServiceVolumeConfig{ 85 Type: "bind", 86 Source: "/source", 87 Target: "/target", 88 Bind: &types.ServiceVolumeBind{Propagation: "slave"}, 89 } 90 assert.NilError(t, err) 91 assert.Check(t, is.DeepEqual(expected, volume)) 92 } 93 94 func TestParseVolumeWithBindOptionsWindows(t *testing.T) { 95 volume, err := ParseVolume("C:\\source\\foo:D:\\target:ro,rprivate") 96 expected := types.ServiceVolumeConfig{ 97 Type: "bind", 98 Source: "C:\\source\\foo", 99 Target: "D:\\target", 100 ReadOnly: true, 101 Bind: &types.ServiceVolumeBind{Propagation: "rprivate"}, 102 } 103 assert.NilError(t, err) 104 assert.Check(t, is.DeepEqual(expected, volume)) 105 } 106 107 func TestParseVolumeWithInvalidVolumeOptions(t *testing.T) { 108 _, err := ParseVolume("name:/target:bogus") 109 assert.NilError(t, err) 110 } 111 112 func TestParseVolumeWithVolumeOptions(t *testing.T) { 113 volume, err := ParseVolume("name:/target:nocopy") 114 expected := types.ServiceVolumeConfig{ 115 Type: "volume", 116 Source: "name", 117 Target: "/target", 118 Volume: &types.ServiceVolumeVolume{NoCopy: true}, 119 } 120 assert.NilError(t, err) 121 assert.Check(t, is.DeepEqual(expected, volume)) 122 } 123 124 func TestParseVolumeWithReadOnly(t *testing.T) { 125 for _, path := range []string{"./foo", "/home/user"} { 126 volume, err := ParseVolume(path + ":/target:ro") 127 expected := types.ServiceVolumeConfig{ 128 Type: "bind", 129 Source: path, 130 Target: "/target", 131 ReadOnly: true, 132 } 133 assert.NilError(t, err) 134 assert.Check(t, is.DeepEqual(expected, volume)) 135 } 136 } 137 138 func TestParseVolumeWithRW(t *testing.T) { 139 for _, path := range []string{"./foo", "/home/user"} { 140 volume, err := ParseVolume(path + ":/target:rw") 141 expected := types.ServiceVolumeConfig{ 142 Type: "bind", 143 Source: path, 144 Target: "/target", 145 ReadOnly: false, 146 } 147 assert.NilError(t, err) 148 assert.Check(t, is.DeepEqual(expected, volume)) 149 } 150 } 151 152 func TestParseVolumeWindowsNamedPipe(t *testing.T) { 153 volume, err := ParseVolume(`\\.\pipe\docker_engine:\\.\pipe\inside`) 154 assert.NilError(t, err) 155 expected := types.ServiceVolumeConfig{ 156 Type: "bind", 157 Source: `\\.\pipe\docker_engine`, 158 Target: `\\.\pipe\inside`, 159 } 160 assert.Check(t, is.DeepEqual(expected, volume)) 161 } 162 163 func TestIsFilePath(t *testing.T) { 164 assert.Check(t, !isFilePath("a界")) 165 assert.Check(t, !isFilePath("1")) 166 assert.Check(t, !isFilePath("c")) 167 } 168 169 // Preserve the test cases for VolumeSplitN 170 func TestParseVolumeSplitCases(t *testing.T) { 171 for casenumber, x := range []struct { 172 input string 173 n int 174 expected []string 175 }{ 176 {`C:\foo:d:`, -1, []string{`C:\foo`, `d:`}}, 177 {`:C:\foo:d:`, -1, nil}, 178 {`/foo:/bar:ro`, 3, []string{`/foo`, `/bar`, `ro`}}, 179 {`/foo:/bar:ro`, 2, []string{`/foo`, `/bar:ro`}}, 180 {`C:\foo\:/foo`, -1, []string{`C:\foo\`, `/foo`}}, 181 {`d:\`, -1, []string{`d:\`}}, 182 {`d:`, -1, []string{`d:`}}, 183 {`d:\path`, -1, []string{`d:\path`}}, 184 {`d:\path with space`, -1, []string{`d:\path with space`}}, 185 {`d:\pathandmode:rw`, -1, []string{`d:\pathandmode`, `rw`}}, 186 187 {`c:\:d:\`, -1, []string{`c:\`, `d:\`}}, 188 {`c:\windows\:d:`, -1, []string{`c:\windows\`, `d:`}}, 189 {`c:\windows:d:\s p a c e`, -1, []string{`c:\windows`, `d:\s p a c e`}}, 190 {`c:\windows:d:\s p a c e:RW`, -1, []string{`c:\windows`, `d:\s p a c e`, `RW`}}, 191 {`c:\program files:d:\s p a c e i n h o s t d i r`, -1, []string{`c:\program files`, `d:\s p a c e i n h o s t d i r`}}, 192 {`0123456789name:d:`, -1, []string{`0123456789name`, `d:`}}, 193 {`MiXeDcAsEnAmE:d:`, -1, []string{`MiXeDcAsEnAmE`, `d:`}}, 194 {`name:D:`, -1, []string{`name`, `D:`}}, 195 {`name:D::rW`, -1, []string{`name`, `D:`, `rW`}}, 196 {`name:D::RW`, -1, []string{`name`, `D:`, `RW`}}, 197 198 {`c:/:d:/forward/slashes/are/good/too`, -1, []string{`c:/`, `d:/forward/slashes/are/good/too`}}, 199 {`c:\Windows`, -1, []string{`c:\Windows`}}, 200 {`c:\Program Files (x86)`, -1, []string{`c:\Program Files (x86)`}}, 201 {``, -1, nil}, 202 {`.`, -1, []string{`.`}}, 203 {`..\`, -1, []string{`..\`}}, 204 {`c:\:..\`, -1, []string{`c:\`, `..\`}}, 205 {`c:\:d:\:xyzzy`, -1, []string{`c:\`, `d:\`, `xyzzy`}}, 206 // Cover directories with one-character name 207 {`/tmp/x/y:/foo/x/y`, -1, []string{`/tmp/x/y`, `/foo/x/y`}}, 208 } { 209 parsed, _ := ParseVolume(x.input) 210 211 expected := len(x.expected) > 1 212 msg := fmt.Sprintf("Case %d: %s", casenumber, x.input) 213 assert.Check(t, is.Equal(expected, parsed.Source != ""), msg) 214 } 215 } 216 217 func TestParseVolumeInvalidEmptySpec(t *testing.T) { 218 _, err := ParseVolume("") 219 assert.ErrorContains(t, err, "invalid empty volume spec") 220 } 221 222 func TestParseVolumeInvalidSections(t *testing.T) { 223 _, err := ParseVolume("/foo::rw") 224 assert.ErrorContains(t, err, "invalid spec") 225 }