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  }