Browse code

Refactor test and add coverage to runconfig

Signed-off-by: Vincent Demeester <vincent@sbr.pm>

Vincent Demeester authored on 2015/07/01 17:16:36
Showing 10 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,118 @@
0
+package runconfig
1
+
2
+import (
3
+	"testing"
4
+
5
+	"github.com/docker/docker/pkg/nat"
6
+)
7
+
8
+func TestCompare(t *testing.T) {
9
+	ports1 := make(nat.PortSet)
10
+	ports1[nat.Port("1111/tcp")] = struct{}{}
11
+	ports1[nat.Port("2222/tcp")] = struct{}{}
12
+	ports2 := make(nat.PortSet)
13
+	ports2[nat.Port("3333/tcp")] = struct{}{}
14
+	ports2[nat.Port("4444/tcp")] = struct{}{}
15
+	ports3 := make(nat.PortSet)
16
+	ports3[nat.Port("1111/tcp")] = struct{}{}
17
+	ports3[nat.Port("2222/tcp")] = struct{}{}
18
+	ports3[nat.Port("5555/tcp")] = struct{}{}
19
+	volumes1 := make(map[string]struct{})
20
+	volumes1["/test1"] = struct{}{}
21
+	volumes2 := make(map[string]struct{})
22
+	volumes2["/test2"] = struct{}{}
23
+	volumes3 := make(map[string]struct{})
24
+	volumes3["/test1"] = struct{}{}
25
+	volumes3["/test3"] = struct{}{}
26
+	envs1 := []string{"ENV1=value1", "ENV2=value2"}
27
+	envs2 := []string{"ENV1=value1", "ENV3=value3"}
28
+	entrypoint1 := &Entrypoint{parts: []string{"/bin/sh", "-c"}}
29
+	entrypoint2 := &Entrypoint{parts: []string{"/bin/sh", "-d"}}
30
+	entrypoint3 := &Entrypoint{parts: []string{"/bin/sh", "-c", "echo"}}
31
+	cmd1 := &Command{parts: []string{"/bin/sh", "-c"}}
32
+	cmd2 := &Command{parts: []string{"/bin/sh", "-d"}}
33
+	cmd3 := &Command{parts: []string{"/bin/sh", "-c", "echo"}}
34
+	labels1 := map[string]string{"LABEL1": "value1", "LABEL2": "value2"}
35
+	labels2 := map[string]string{"LABEL1": "value1", "LABEL2": "value3"}
36
+	labels3 := map[string]string{"LABEL1": "value1", "LABEL2": "value2", "LABEL3": "value3"}
37
+
38
+	sameConfigs := map[*Config]*Config{
39
+		// Empty config
40
+		&Config{}: {},
41
+		// Does not compare hostname, domainname & image
42
+		&Config{
43
+			Hostname:   "host1",
44
+			Domainname: "domain1",
45
+			Image:      "image1",
46
+			User:       "user",
47
+		}: {
48
+			Hostname:   "host2",
49
+			Domainname: "domain2",
50
+			Image:      "image2",
51
+			User:       "user",
52
+		},
53
+		// only OpenStdin
54
+		&Config{OpenStdin: false}: {OpenStdin: false},
55
+		// only env
56
+		&Config{Env: envs1}: {Env: envs1},
57
+		// only cmd
58
+		&Config{Cmd: cmd1}: {Cmd: cmd1},
59
+		// only labels
60
+		&Config{Labels: labels1}: {Labels: labels1},
61
+		// only exposedPorts
62
+		&Config{ExposedPorts: ports1}: {ExposedPorts: ports1},
63
+		// only entrypoints
64
+		&Config{Entrypoint: entrypoint1}: {Entrypoint: entrypoint1},
65
+		// only volumes
66
+		&Config{Volumes: volumes1}: {Volumes: volumes1},
67
+	}
68
+	differentConfigs := map[*Config]*Config{
69
+		nil: nil,
70
+		&Config{
71
+			Hostname:   "host1",
72
+			Domainname: "domain1",
73
+			Image:      "image1",
74
+			User:       "user1",
75
+		}: {
76
+			Hostname:   "host1",
77
+			Domainname: "domain1",
78
+			Image:      "image1",
79
+			User:       "user2",
80
+		},
81
+		// only OpenStdin
82
+		&Config{OpenStdin: false}: {OpenStdin: true},
83
+		&Config{OpenStdin: true}:  {OpenStdin: false},
84
+		// only env
85
+		&Config{Env: envs1}: {Env: envs2},
86
+		// only cmd
87
+		&Config{Cmd: cmd1}: {Cmd: cmd2},
88
+		// not the same number of parts
89
+		&Config{Cmd: cmd1}: {Cmd: cmd3},
90
+		// only labels
91
+		&Config{Labels: labels1}: {Labels: labels2},
92
+		// not the same number of labels
93
+		&Config{Labels: labels1}: {Labels: labels3},
94
+		// only exposedPorts
95
+		&Config{ExposedPorts: ports1}: {ExposedPorts: ports2},
96
+		// not the same number of ports
97
+		&Config{ExposedPorts: ports1}: {ExposedPorts: ports3},
98
+		// only entrypoints
99
+		&Config{Entrypoint: entrypoint1}: {Entrypoint: entrypoint2},
100
+		// not the same number of parts
101
+		&Config{Entrypoint: entrypoint1}: {Entrypoint: entrypoint3},
102
+		// only volumes
103
+		&Config{Volumes: volumes1}: {Volumes: volumes2},
104
+		// not the same number of labels
105
+		&Config{Volumes: volumes1}: {Volumes: volumes3},
106
+	}
107
+	for config1, config2 := range sameConfigs {
108
+		if !Compare(config1, config2) {
109
+			t.Fatalf("Compare should be true for [%v] and [%v]", config1, config2)
110
+		}
111
+	}
112
+	for config1, config2 := range differentConfigs {
113
+		if Compare(config1, config2) {
114
+			t.Fatalf("Compare should be false for [%v] and [%v]", config1, config2)
115
+		}
116
+	}
117
+}
... ...
@@ -5,274 +5,109 @@ import (
5 5
 	"encoding/json"
6 6
 	"fmt"
7 7
 	"io/ioutil"
8
-	"strings"
9 8
 	"testing"
10
-
11
-	"github.com/docker/docker/pkg/nat"
12 9
 )
13 10
 
14
-func parse(t *testing.T, args string) (*Config, *HostConfig, error) {
15
-	config, hostConfig, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
16
-	return config, hostConfig, err
17
-}
18
-
19
-func mustParse(t *testing.T, args string) (*Config, *HostConfig) {
20
-	config, hostConfig, err := parse(t, args)
21
-	if err != nil {
22
-		t.Fatal(err)
11
+func TestEntrypointMarshalJSON(t *testing.T) {
12
+	entrypoints := map[*Entrypoint]string{
13
+		nil:                                            "",
14
+		&Entrypoint{}:                                  "null",
15
+		&Entrypoint{[]string{"/bin/sh", "-c", "echo"}}: `["/bin/sh","-c","echo"]`,
23 16
 	}
24
-	return config, hostConfig
25
-}
26 17
 
27
-// check if (a == c && b == d) || (a == d && b == c)
28
-// because maps are randomized
29
-func compareRandomizedStrings(a, b, c, d string) error {
30
-	if a == c && b == d {
31
-		return nil
32
-	}
33
-	if a == d && b == c {
34
-		return nil
35
-	}
36
-	return fmt.Errorf("strings don't match")
37
-}
38
-
39
-func TestParseRunLinks(t *testing.T) {
40
-	if _, hostConfig := mustParse(t, "--link a:b"); len(hostConfig.Links) == 0 || hostConfig.Links[0] != "a:b" {
41
-		t.Fatalf("Error parsing links. Expected []string{\"a:b\"}, received: %v", hostConfig.Links)
42
-	}
43
-	if _, hostConfig := mustParse(t, "--link a:b --link c:d"); len(hostConfig.Links) < 2 || hostConfig.Links[0] != "a:b" || hostConfig.Links[1] != "c:d" {
44
-		t.Fatalf("Error parsing links. Expected []string{\"a:b\", \"c:d\"}, received: %v", hostConfig.Links)
45
-	}
46
-	if _, hostConfig := mustParse(t, ""); len(hostConfig.Links) != 0 {
47
-		t.Fatalf("Error parsing links. No link expected, received: %v", hostConfig.Links)
18
+	for entrypoint, expected := range entrypoints {
19
+		data, err := entrypoint.MarshalJSON()
20
+		if err != nil {
21
+			t.Fatal(err)
22
+		}
23
+		if string(data) != expected {
24
+			t.Fatalf("Expected %v, got %v", expected, string(data))
25
+		}
48 26
 	}
49 27
 }
50 28
 
51
-func TestParseRunAttach(t *testing.T) {
52
-	if config, _ := mustParse(t, "-a stdin"); !config.AttachStdin || config.AttachStdout || config.AttachStderr {
53
-		t.Fatalf("Error parsing attach flags. Expect only Stdin enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
54
-	}
55
-	if config, _ := mustParse(t, "-a stdin -a stdout"); !config.AttachStdin || !config.AttachStdout || config.AttachStderr {
56
-		t.Fatalf("Error parsing attach flags. Expect only Stdin and Stdout enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
57
-	}
58
-	if config, _ := mustParse(t, "-a stdin -a stdout -a stderr"); !config.AttachStdin || !config.AttachStdout || !config.AttachStderr {
59
-		t.Fatalf("Error parsing attach flags. Expect all attach enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
60
-	}
61
-	if config, _ := mustParse(t, ""); config.AttachStdin || !config.AttachStdout || !config.AttachStderr {
62
-		t.Fatalf("Error parsing attach flags. Expect Stdin disabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
29
+func TestEntrypointUnmarshalJSON(t *testing.T) {
30
+	parts := map[string][]string{
31
+		"":   {"default", "values"},
32
+		"[]": {},
33
+		`["/bin/sh","-c","echo"]`: {"/bin/sh", "-c", "echo"},
63 34
 	}
35
+	for json, expectedParts := range parts {
36
+		entrypoint := &Entrypoint{
37
+			[]string{"default", "values"},
38
+		}
39
+		if err := entrypoint.UnmarshalJSON([]byte(json)); err != nil {
40
+			t.Fatal(err)
41
+		}
64 42
 
65
-	if _, _, err := parse(t, "-a"); err == nil {
66
-		t.Fatalf("Error parsing attach flags, `-a` should be an error but is not")
67
-	}
68
-	if _, _, err := parse(t, "-a invalid"); err == nil {
69
-		t.Fatalf("Error parsing attach flags, `-a invalid` should be an error but is not")
70
-	}
71
-	if _, _, err := parse(t, "-a invalid -a stdout"); err == nil {
72
-		t.Fatalf("Error parsing attach flags, `-a stdout -a invalid` should be an error but is not")
73
-	}
74
-	if _, _, err := parse(t, "-a stdout -a stderr -d"); err == nil {
75
-		t.Fatalf("Error parsing attach flags, `-a stdout -a stderr -d` should be an error but is not")
76
-	}
77
-	if _, _, err := parse(t, "-a stdin -d"); err == nil {
78
-		t.Fatalf("Error parsing attach flags, `-a stdin -d` should be an error but is not")
79
-	}
80
-	if _, _, err := parse(t, "-a stdout -d"); err == nil {
81
-		t.Fatalf("Error parsing attach flags, `-a stdout -d` should be an error but is not")
82
-	}
83
-	if _, _, err := parse(t, "-a stderr -d"); err == nil {
84
-		t.Fatalf("Error parsing attach flags, `-a stderr -d` should be an error but is not")
85
-	}
86
-	if _, _, err := parse(t, "-d --rm"); err == nil {
87
-		t.Fatalf("Error parsing attach flags, `-d --rm` should be an error but is not")
43
+		actualParts := entrypoint.Slice()
44
+		if len(actualParts) != len(expectedParts) {
45
+			t.Fatalf("Expected %v parts, got %v (%v)", len(expectedParts), len(actualParts), expectedParts)
46
+		}
47
+		for index, part := range actualParts {
48
+			if part != expectedParts[index] {
49
+				t.Fatalf("Expected %v, got %v", expectedParts, actualParts)
50
+				break
51
+			}
52
+		}
88 53
 	}
89 54
 }
90 55
 
91
-func TestParseRunVolumes(t *testing.T) {
92
-	if config, hostConfig := mustParse(t, "-v /tmp"); hostConfig.Binds != nil {
93
-		t.Fatalf("Error parsing volume flags, `-v /tmp` should not mount-bind anything. Received %v", hostConfig.Binds)
94
-	} else if _, exists := config.Volumes["/tmp"]; !exists {
95
-		t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Received %v", config.Volumes)
96
-	}
97
-
98
-	if config, hostConfig := mustParse(t, "-v /tmp -v /var"); hostConfig.Binds != nil {
99
-		t.Fatalf("Error parsing volume flags, `-v /tmp -v /var` should not mount-bind anything. Received %v", hostConfig.Binds)
100
-	} else if _, exists := config.Volumes["/tmp"]; !exists {
101
-		t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Received %v", config.Volumes)
102
-	} else if _, exists := config.Volumes["/var"]; !exists {
103
-		t.Fatalf("Error parsing volume flags, `-v /var` is missing from volumes. Received %v", config.Volumes)
104
-	}
105
-
106
-	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp"); hostConfig.Binds == nil || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
107
-		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp` should mount-bind /hostTmp into /containeTmp. Received %v", hostConfig.Binds)
108
-	}
109
-
110
-	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /hostVar:/containerVar"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp", "/hostVar:/containerVar") != nil {
111
-		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /hostVar:/containerVar` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
112
-	}
113
-
114
-	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp:ro", "/hostVar:/containerVar:rw") != nil {
115
-		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
116
-	}
117
-
118
-	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:roZ -v /hostVar:/containerVar:rwZ"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp:roZ", "/hostVar:/containerVar:rwZ") != nil {
119
-		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:roZ -v /hostVar:/containerVar:rwZ` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
120
-	}
121
-
122
-	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:Z -v /hostVar:/containerVar:z"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp:Z", "/hostVar:/containerVar:z") != nil {
123
-		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:Z -v /hostVar:/containerVar:z` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
56
+func TestCommandToString(t *testing.T) {
57
+	commands := map[*Command]string{
58
+		&Command{[]string{""}}:           "",
59
+		&Command{[]string{"one"}}:        "one",
60
+		&Command{[]string{"one", "two"}}: "one two",
124 61
 	}
125
-
126
-	if config, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /containerVar"); hostConfig.Binds == nil || len(hostConfig.Binds) > 1 || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
127
-		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /containerVar` should mount-bind only /hostTmp into /containeTmp. Received %v", hostConfig.Binds)
128
-	} else if _, exists := config.Volumes["/containerVar"]; !exists {
129
-		t.Fatalf("Error parsing volume flags, `-v /containerVar` is missing from volumes. Received %v", config.Volumes)
130
-	}
131
-
132
-	if config, hostConfig := mustParse(t, ""); hostConfig.Binds != nil {
133
-		t.Fatalf("Error parsing volume flags, without volume, nothing should be mount-binded. Received %v", hostConfig.Binds)
134
-	} else if len(config.Volumes) != 0 {
135
-		t.Fatalf("Error parsing volume flags, without volume, no volume should be present. Received %v", config.Volumes)
136
-	}
137
-
138
-	if _, _, err := parse(t, "-v /"); err == nil {
139
-		t.Fatalf("Expected error, but got none")
140
-	}
141
-
142
-	if _, _, err := parse(t, "-v /:/"); err == nil {
143
-		t.Fatalf("Error parsing volume flags, `-v /:/` should fail but didn't")
144
-	}
145
-	if _, _, err := parse(t, "-v"); err == nil {
146
-		t.Fatalf("Error parsing volume flags, `-v` should fail but didn't")
147
-	}
148
-	if _, _, err := parse(t, "-v /tmp:"); err == nil {
149
-		t.Fatalf("Error parsing volume flags, `-v /tmp:` should fail but didn't")
150
-	}
151
-	if _, _, err := parse(t, "-v /tmp:ro"); err == nil {
152
-		t.Fatalf("Error parsing volume flags, `-v /tmp:ro` should fail but didn't")
153
-	}
154
-	if _, _, err := parse(t, "-v /tmp::"); err == nil {
155
-		t.Fatalf("Error parsing volume flags, `-v /tmp::` should fail but didn't")
156
-	}
157
-	if _, _, err := parse(t, "-v :"); err == nil {
158
-		t.Fatalf("Error parsing volume flags, `-v :` should fail but didn't")
159
-	}
160
-	if _, _, err := parse(t, "-v ::"); err == nil {
161
-		t.Fatalf("Error parsing volume flags, `-v ::` should fail but didn't")
162
-	}
163
-	if _, _, err := parse(t, "-v /tmp:/tmp:/tmp:/tmp"); err == nil {
164
-		t.Fatalf("Error parsing volume flags, `-v /tmp:/tmp:/tmp:/tmp` should fail but didn't")
62
+	for command, expected := range commands {
63
+		toString := command.ToString()
64
+		if toString != expected {
65
+			t.Fatalf("Expected %v, got %v", expected, toString)
66
+		}
165 67
 	}
166 68
 }
167 69
 
168
-func TestCompare(t *testing.T) {
169
-	volumes1 := make(map[string]struct{})
170
-	volumes1["/test1"] = struct{}{}
171
-	ports1 := make(nat.PortSet)
172
-	ports1[nat.Port("1111/tcp")] = struct{}{}
173
-	ports1[nat.Port("2222/tcp")] = struct{}{}
174
-	config1 := Config{
175
-		ExposedPorts: ports1,
176
-		Env:          []string{"VAR1=1", "VAR2=2"},
177
-		Volumes:      volumes1,
178
-	}
179
-	ports3 := make(nat.PortSet)
180
-	ports3[nat.Port("0000/tcp")] = struct{}{}
181
-	ports3[nat.Port("2222/tcp")] = struct{}{}
182
-	config3 := Config{
183
-		ExposedPorts: ports3,
184
-		Volumes:      volumes1,
185
-	}
186
-	volumes2 := make(map[string]struct{})
187
-	volumes2["/test2"] = struct{}{}
188
-	config5 := Config{
189
-		Env:     []string{"VAR1=1", "VAR2=2"},
190
-		Volumes: volumes2,
70
+func TestCommandMarshalJSON(t *testing.T) {
71
+	commands := map[*Command]string{
72
+		nil:        "",
73
+		&Command{}: "null",
74
+		&Command{[]string{"/bin/sh", "-c", "echo"}}: `["/bin/sh","-c","echo"]`,
191 75
 	}
192 76
 
193
-	if Compare(&config1, &config3) {
194
-		t.Fatalf("Compare should return false, ExposedPorts are different")
195
-	}
196
-	if Compare(&config1, &config5) {
197
-		t.Fatalf("Compare should return false, Volumes are different")
198
-	}
199
-	if !Compare(&config1, &config1) {
200
-		t.Fatalf("Compare should return true")
77
+	for command, expected := range commands {
78
+		data, err := command.MarshalJSON()
79
+		if err != nil {
80
+			t.Fatal(err)
81
+		}
82
+		if string(data) != expected {
83
+			t.Fatalf("Expected %v, got %v", expected, string(data))
84
+		}
201 85
 	}
202 86
 }
203 87
 
204
-func TestMerge(t *testing.T) {
205
-	volumesImage := make(map[string]struct{})
206
-	volumesImage["/test1"] = struct{}{}
207
-	volumesImage["/test2"] = struct{}{}
208
-	portsImage := make(nat.PortSet)
209
-	portsImage[nat.Port("1111/tcp")] = struct{}{}
210
-	portsImage[nat.Port("2222/tcp")] = struct{}{}
211
-	configImage := &Config{
212
-		ExposedPorts: portsImage,
213
-		Env:          []string{"VAR1=1", "VAR2=2"},
214
-		Volumes:      volumesImage,
215
-	}
216
-
217
-	portsUser := make(nat.PortSet)
218
-	portsUser[nat.Port("2222/tcp")] = struct{}{}
219
-	portsUser[nat.Port("3333/tcp")] = struct{}{}
220
-	volumesUser := make(map[string]struct{})
221
-	volumesUser["/test3"] = struct{}{}
222
-	configUser := &Config{
223
-		ExposedPorts: portsUser,
224
-		Env:          []string{"VAR2=3", "VAR3=3"},
225
-		Volumes:      volumesUser,
88
+func TestCommandUnmarshalJSON(t *testing.T) {
89
+	parts := map[string][]string{
90
+		"":   {"default", "values"},
91
+		"[]": {},
92
+		`["/bin/sh","-c","echo"]`: {"/bin/sh", "-c", "echo"},
226 93
 	}
227
-
228
-	if err := Merge(configUser, configImage); err != nil {
229
-		t.Error(err)
230
-	}
231
-
232
-	if len(configUser.ExposedPorts) != 3 {
233
-		t.Fatalf("Expected 3 ExposedPorts, 1111, 2222 and 3333, found %d", len(configUser.ExposedPorts))
234
-	}
235
-	for portSpecs := range configUser.ExposedPorts {
236
-		if portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
237
-			t.Fatalf("Expected 1111 or 2222 or 3333, found %s", portSpecs)
94
+	for json, expectedParts := range parts {
95
+		command := &Command{
96
+			[]string{"default", "values"},
238 97
 		}
239
-	}
240
-	if len(configUser.Env) != 3 {
241
-		t.Fatalf("Expected 3 env var, VAR1=1, VAR2=3 and VAR3=3, found %d", len(configUser.Env))
242
-	}
243
-	for _, env := range configUser.Env {
244
-		if env != "VAR1=1" && env != "VAR2=3" && env != "VAR3=3" {
245
-			t.Fatalf("Expected VAR1=1 or VAR2=3 or VAR3=3, found %s", env)
98
+		if err := command.UnmarshalJSON([]byte(json)); err != nil {
99
+			t.Fatal(err)
246 100
 		}
247
-	}
248 101
 
249
-	if len(configUser.Volumes) != 3 {
250
-		t.Fatalf("Expected 3 volumes, /test1, /test2 and /test3, found %d", len(configUser.Volumes))
251
-	}
252
-	for v := range configUser.Volumes {
253
-		if v != "/test1" && v != "/test2" && v != "/test3" {
254
-			t.Fatalf("Expected /test1 or /test2 or /test3, found %s", v)
102
+		actualParts := command.Slice()
103
+		if len(actualParts) != len(expectedParts) {
104
+			t.Fatalf("Expected %v parts, got %v (%v)", len(expectedParts), len(actualParts), expectedParts)
255 105
 		}
256
-	}
257
-
258
-	ports, _, err := nat.ParsePortSpecs([]string{"0000"})
259
-	if err != nil {
260
-		t.Error(err)
261
-	}
262
-	configImage2 := &Config{
263
-		ExposedPorts: ports,
264
-	}
265
-
266
-	if err := Merge(configUser, configImage2); err != nil {
267
-		t.Error(err)
268
-	}
269
-
270
-	if len(configUser.ExposedPorts) != 4 {
271
-		t.Fatalf("Expected 4 ExposedPorts, 0000, 1111, 2222 and 3333, found %d", len(configUser.ExposedPorts))
272
-	}
273
-	for portSpecs := range configUser.ExposedPorts {
274
-		if portSpecs.Port() != "0" && portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
275
-			t.Fatalf("Expected %q or %q or %q or %q, found %s", 0, 1111, 2222, 3333, portSpecs)
106
+		for index, part := range actualParts {
107
+			if part != expectedParts[index] {
108
+				t.Fatalf("Expected %v, got %v", expectedParts, actualParts)
109
+				break
110
+			}
276 111
 		}
277 112
 	}
278 113
 }
279 114
new file mode 100644
... ...
@@ -0,0 +1,129 @@
0
+package runconfig
1
+
2
+import (
3
+	"fmt"
4
+	flag "github.com/docker/docker/pkg/mflag"
5
+	"io/ioutil"
6
+	"testing"
7
+)
8
+
9
+type arguments struct {
10
+	args []string
11
+}
12
+
13
+func TestParseExec(t *testing.T) {
14
+	invalids := map[*arguments]error{
15
+		&arguments{[]string{"-unknown"}}: fmt.Errorf("flag provided but not defined: -unknown"),
16
+		&arguments{[]string{"-u"}}:       fmt.Errorf("flag needs an argument: -u"),
17
+		&arguments{[]string{"--user"}}:   fmt.Errorf("flag needs an argument: --user"),
18
+	}
19
+	valids := map[*arguments]*ExecConfig{
20
+		&arguments{
21
+			[]string{"container", "command"},
22
+		}: {
23
+			Container:    "container",
24
+			Cmd:          []string{"command"},
25
+			AttachStdout: true,
26
+			AttachStderr: true,
27
+		},
28
+		&arguments{
29
+			[]string{"container", "command1", "command2"},
30
+		}: {
31
+			Container:    "container",
32
+			Cmd:          []string{"command1", "command2"},
33
+			AttachStdout: true,
34
+			AttachStderr: true,
35
+		},
36
+		&arguments{
37
+			[]string{"-i", "-t", "-u", "uid", "container", "command"},
38
+		}: {
39
+			User:         "uid",
40
+			AttachStdin:  true,
41
+			AttachStdout: true,
42
+			AttachStderr: true,
43
+			Tty:          true,
44
+			Container:    "container",
45
+			Cmd:          []string{"command"},
46
+		},
47
+		&arguments{
48
+			[]string{"-d", "container", "command"},
49
+		}: {
50
+			AttachStdin:  false,
51
+			AttachStdout: false,
52
+			AttachStderr: false,
53
+			Detach:       true,
54
+			Container:    "container",
55
+			Cmd:          []string{"command"},
56
+		},
57
+		&arguments{
58
+			[]string{"-t", "-i", "-d", "container", "command"},
59
+		}: {
60
+			AttachStdin:  false,
61
+			AttachStdout: false,
62
+			AttachStderr: false,
63
+			Detach:       true,
64
+			Tty:          true,
65
+			Container:    "container",
66
+			Cmd:          []string{"command"},
67
+		},
68
+	}
69
+	for invalid, expectedError := range invalids {
70
+		cmd := flag.NewFlagSet("exec", flag.ContinueOnError)
71
+		cmd.ShortUsage = func() {}
72
+		cmd.SetOutput(ioutil.Discard)
73
+		_, err := ParseExec(cmd, invalid.args)
74
+		if err == nil || err.Error() != expectedError.Error() {
75
+			t.Fatalf("Expected an error [%v] for %v, got %v", expectedError, invalid, err)
76
+		}
77
+
78
+	}
79
+	for valid, expectedExecConfig := range valids {
80
+		cmd := flag.NewFlagSet("exec", flag.ContinueOnError)
81
+		cmd.ShortUsage = func() {}
82
+		cmd.SetOutput(ioutil.Discard)
83
+		execConfig, err := ParseExec(cmd, valid.args)
84
+		if err != nil {
85
+			t.Fatal(err)
86
+		}
87
+		if !compareExecConfig(expectedExecConfig, execConfig) {
88
+			t.Fatalf("Expected [%v] for %v, got [%v]", expectedExecConfig, valid, execConfig)
89
+		}
90
+	}
91
+}
92
+
93
+func compareExecConfig(config1 *ExecConfig, config2 *ExecConfig) bool {
94
+	if config1.AttachStderr != config2.AttachStderr {
95
+		return false
96
+	}
97
+	if config1.AttachStdin != config2.AttachStdin {
98
+		return false
99
+	}
100
+	if config1.AttachStdout != config2.AttachStdout {
101
+		return false
102
+	}
103
+	if config1.Container != config2.Container {
104
+		return false
105
+	}
106
+	if config1.Detach != config2.Detach {
107
+		return false
108
+	}
109
+	if config1.Privileged != config2.Privileged {
110
+		return false
111
+	}
112
+	if config1.Tty != config2.Tty {
113
+		return false
114
+	}
115
+	if config1.User != config2.User {
116
+		return false
117
+	}
118
+	if len(config1.Cmd) != len(config2.Cmd) {
119
+		return false
120
+	} else {
121
+		for index, value := range config1.Cmd {
122
+			if value != config2.Cmd[index] {
123
+				return false
124
+			}
125
+		}
126
+	}
127
+	return true
128
+}
0 129
new file mode 100644
... ...
@@ -0,0 +1,18 @@
0
+{
1
+    "Binds": ["/tmp:/tmp"],
2
+    "ContainerIDFile": "",
3
+    "LxcConf": [],
4
+    "Privileged": false,
5
+    "PortBindings": {
6
+        "80/tcp": [
7
+            {
8
+                "HostIp": "0.0.0.0",
9
+                "HostPort": "49153"
10
+            }
11
+        ]
12
+    },
13
+    "Links": ["/name:alias"],
14
+    "PublishAllPorts": false,
15
+    "CapAdd": ["NET_ADMIN"],
16
+    "CapDrop": ["MKNOD"]
17
+}
0 18
new file mode 100644
... ...
@@ -0,0 +1,30 @@
0
+{
1
+    "Binds": ["/tmp:/tmp"],
2
+    "Links": ["redis3:redis"],
3
+    "LxcConf": {"lxc.utsname":"docker"},
4
+    "Memory": 0,
5
+    "MemorySwap": 0,
6
+    "CpuShares": 512,
7
+    "CpuPeriod": 100000,
8
+    "CpusetCpus": "0,1",
9
+    "CpusetMems": "0,1",
10
+    "BlkioWeight": 300,
11
+    "OomKillDisable": false,
12
+    "PortBindings": { "22/tcp": [{ "HostPort": "11022" }] },
13
+    "PublishAllPorts": false,
14
+    "Privileged": false,
15
+    "ReadonlyRootfs": false,
16
+    "Dns": ["8.8.8.8"],
17
+    "DnsSearch": [""],
18
+    "ExtraHosts": null,
19
+    "VolumesFrom": ["parent", "other:ro"],
20
+    "CapAdd": ["NET_ADMIN"],
21
+    "CapDrop": ["MKNOD"],
22
+    "RestartPolicy": { "Name": "", "MaximumRetryCount": 0 },
23
+    "NetworkMode": "bridge",
24
+    "Devices": [],
25
+    "Ulimits": [{}],
26
+    "LogConfig": { "Type": "json-file", "Config": {} },
27
+    "SecurityOpt": [""],
28
+    "CgroupParent": ""
29
+}
0 30
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+ENV1=value1
0 1
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+LABEL1=value1
0 1
new file mode 100644
... ...
@@ -0,0 +1,265 @@
0
+package runconfig
1
+
2
+import (
3
+	"bytes"
4
+	"fmt"
5
+	"io/ioutil"
6
+	"testing"
7
+)
8
+
9
+func TestNetworkModeTest(t *testing.T) {
10
+	networkModes := map[NetworkMode][]bool{
11
+		// private, bridge, host, container, none, default
12
+		"":                         {true, false, false, false, false, false},
13
+		"something:weird":          {true, false, false, false, false, false},
14
+		"bridge":                   {true, true, false, false, false, false},
15
+		DefaultDaemonNetworkMode(): {true, true, false, false, false, false},
16
+		"host":           {false, false, true, false, false, false},
17
+		"container:name": {false, false, false, true, false, false},
18
+		"none":           {true, false, false, false, true, false},
19
+		"default":        {true, false, false, false, false, true},
20
+	}
21
+	networkModeNames := map[NetworkMode]string{
22
+		"":                         "",
23
+		"something:weird":          "",
24
+		"bridge":                   "bridge",
25
+		DefaultDaemonNetworkMode(): "bridge",
26
+		"host":           "host",
27
+		"container:name": "container",
28
+		"none":           "none",
29
+		"default":        "default",
30
+	}
31
+	for networkMode, state := range networkModes {
32
+		if networkMode.IsPrivate() != state[0] {
33
+			t.Fatalf("NetworkMode.IsPrivate for %v should have been %v but was %v", networkMode, state[0], networkMode.IsPrivate())
34
+		}
35
+		if networkMode.IsBridge() != state[1] {
36
+			t.Fatalf("NetworkMode.IsBridge for %v should have been %v but was %v", networkMode, state[1], networkMode.IsBridge())
37
+		}
38
+		if networkMode.IsHost() != state[2] {
39
+			t.Fatalf("NetworkMode.IsHost for %v should have been %v but was %v", networkMode, state[2], networkMode.IsHost())
40
+		}
41
+		if networkMode.IsContainer() != state[3] {
42
+			t.Fatalf("NetworkMode.IsContainer for %v should have been %v but was %v", networkMode, state[3], networkMode.IsContainer())
43
+		}
44
+		if networkMode.IsNone() != state[4] {
45
+			t.Fatalf("NetworkMode.IsNone for %v should have been %v but was %v", networkMode, state[4], networkMode.IsNone())
46
+		}
47
+		if networkMode.IsDefault() != state[5] {
48
+			t.Fatalf("NetworkMode.IsDefault for %v should have been %v but was %v", networkMode, state[5], networkMode.IsDefault())
49
+		}
50
+		if networkMode.NetworkName() != networkModeNames[networkMode] {
51
+			t.Fatalf("Expected name %v, got %v", networkModeNames[networkMode], networkMode.NetworkName())
52
+		}
53
+	}
54
+}
55
+
56
+func TestIpcModeTest(t *testing.T) {
57
+	ipcModes := map[IpcMode][]bool{
58
+		// private, host, container, valid
59
+		"":                         {true, false, false, true},
60
+		"something:weird":          {true, false, false, false},
61
+		":weird":                   {true, false, false, true},
62
+		"host":                     {false, true, false, true},
63
+		"container:name":           {false, false, true, true},
64
+		"container:name:something": {false, false, true, false},
65
+		"container:":               {false, false, true, false},
66
+	}
67
+	for ipcMode, state := range ipcModes {
68
+		if ipcMode.IsPrivate() != state[0] {
69
+			t.Fatalf("IpcMode.IsPrivate for %v should have been %v but was %v", ipcMode, state[0], ipcMode.IsPrivate())
70
+		}
71
+		if ipcMode.IsHost() != state[1] {
72
+			t.Fatalf("IpcMode.IsHost for %v should have been %v but was %v", ipcMode, state[1], ipcMode.IsHost())
73
+		}
74
+		if ipcMode.IsContainer() != state[2] {
75
+			t.Fatalf("IpcMode.IsContainer for %v should have been %v but was %v", ipcMode, state[2], ipcMode.IsContainer())
76
+		}
77
+		if ipcMode.Valid() != state[3] {
78
+			t.Fatalf("IpcMode.Valid for %v should have been %v but was %v", ipcMode, state[3], ipcMode.Valid())
79
+		}
80
+	}
81
+	containerIpcModes := map[IpcMode]string{
82
+		"":                      "",
83
+		"something":             "",
84
+		"something:weird":       "weird",
85
+		"container":             "",
86
+		"container:":            "",
87
+		"container:name":        "name",
88
+		"container:name1:name2": "name1:name2",
89
+	}
90
+	for ipcMode, container := range containerIpcModes {
91
+		if ipcMode.Container() != container {
92
+			t.Fatalf("Expected %v for %v but was %v", container, ipcMode, ipcMode.Container())
93
+		}
94
+	}
95
+}
96
+
97
+func TestUTSModeTest(t *testing.T) {
98
+	utsModes := map[UTSMode][]bool{
99
+		// private, host, valid
100
+		"":                {true, false, true},
101
+		"something:weird": {true, false, false},
102
+		"host":            {false, true, true},
103
+		"host:name":       {true, false, true},
104
+	}
105
+	for utsMode, state := range utsModes {
106
+		if utsMode.IsPrivate() != state[0] {
107
+			t.Fatalf("UtsMode.IsPrivate for %v should have been %v but was %v", utsMode, state[0], utsMode.IsPrivate())
108
+		}
109
+		if utsMode.IsHost() != state[1] {
110
+			t.Fatalf("UtsMode.IsHost for %v should have been %v but was %v", utsMode, state[1], utsMode.IsHost())
111
+		}
112
+		if utsMode.Valid() != state[2] {
113
+			t.Fatalf("UtsMode.Valid for %v should have been %v but was %v", utsMode, state[2], utsMode.Valid())
114
+		}
115
+	}
116
+}
117
+
118
+func TestPidModeTest(t *testing.T) {
119
+	pidModes := map[PidMode][]bool{
120
+		// private, host, valid
121
+		"":                {true, false, true},
122
+		"something:weird": {true, false, false},
123
+		"host":            {false, true, true},
124
+		"host:name":       {true, false, true},
125
+	}
126
+	for pidMode, state := range pidModes {
127
+		if pidMode.IsPrivate() != state[0] {
128
+			t.Fatalf("PidMode.IsPrivate for %v should have been %v but was %v", pidMode, state[0], pidMode.IsPrivate())
129
+		}
130
+		if pidMode.IsHost() != state[1] {
131
+			t.Fatalf("PidMode.IsHost for %v should have been %v but was %v", pidMode, state[1], pidMode.IsHost())
132
+		}
133
+		if pidMode.Valid() != state[2] {
134
+			t.Fatalf("PidMode.Valid for %v should have been %v but was %v", pidMode, state[2], pidMode.Valid())
135
+		}
136
+	}
137
+}
138
+
139
+func TestRestartPolicy(t *testing.T) {
140
+	restartPolicies := map[RestartPolicy][]bool{
141
+		// none, always, failure
142
+		RestartPolicy{}:                {false, false, false},
143
+		RestartPolicy{"something", 0}:  {false, false, false},
144
+		RestartPolicy{"no", 0}:         {true, false, false},
145
+		RestartPolicy{"always", 0}:     {false, true, false},
146
+		RestartPolicy{"on-failure", 0}: {false, false, true},
147
+	}
148
+	for restartPolicy, state := range restartPolicies {
149
+		if restartPolicy.IsNone() != state[0] {
150
+			t.Fatalf("RestartPolicy.IsNone for %v should have been %v but was %v", restartPolicy, state[0], restartPolicy.IsNone())
151
+		}
152
+		if restartPolicy.IsAlways() != state[1] {
153
+			t.Fatalf("RestartPolicy.IsAlways for %v should have been %v but was %v", restartPolicy, state[1], restartPolicy.IsAlways())
154
+		}
155
+		if restartPolicy.IsOnFailure() != state[2] {
156
+			t.Fatalf("RestartPolicy.IsOnFailure for %v should have been %v but was %v", restartPolicy, state[2], restartPolicy.IsOnFailure())
157
+		}
158
+	}
159
+}
160
+
161
+func TestLxcConfigMarshalJSON(t *testing.T) {
162
+	lxcConfigs := map[*LxcConfig]string{
163
+		nil:          "",
164
+		&LxcConfig{}: "null",
165
+		&LxcConfig{
166
+			[]KeyValuePair{{"key1", "value1"}},
167
+		}: `[{"Key":"key1","Value":"value1"}]`,
168
+	}
169
+
170
+	for lxcconfig, expected := range lxcConfigs {
171
+		data, err := lxcconfig.MarshalJSON()
172
+		if err != nil {
173
+			t.Fatal(err)
174
+		}
175
+		if string(data) != expected {
176
+			t.Fatalf("Expected %v, got %v", expected, string(data))
177
+		}
178
+	}
179
+}
180
+
181
+func TestLxcConfigUnmarshalJSON(t *testing.T) {
182
+	keyvaluePairs := map[string][]KeyValuePair{
183
+		"":   {{"key1", "value1"}},
184
+		"[]": {},
185
+		`[{"Key":"key2","Value":"value2"}]`: {{"key2", "value2"}},
186
+	}
187
+	for json, expectedParts := range keyvaluePairs {
188
+		lxcConfig := &LxcConfig{
189
+			[]KeyValuePair{{"key1", "value1"}},
190
+		}
191
+		if err := lxcConfig.UnmarshalJSON([]byte(json)); err != nil {
192
+			t.Fatal(err)
193
+		}
194
+
195
+		actualParts := lxcConfig.Slice()
196
+		if len(actualParts) != len(expectedParts) {
197
+			t.Fatalf("Expected %v keyvaluePairs, got %v (%v)", len(expectedParts), len(actualParts), expectedParts)
198
+		}
199
+		for index, part := range actualParts {
200
+			if part != expectedParts[index] {
201
+				t.Fatalf("Expected %v, got %v", expectedParts, actualParts)
202
+				break
203
+			}
204
+		}
205
+	}
206
+}
207
+
208
+func TestMergeConfigs(t *testing.T) {
209
+	expectedHostname := "hostname"
210
+	expectedContainerIDFile := "containerIdFile"
211
+	config := &Config{
212
+		Hostname: expectedHostname,
213
+	}
214
+	hostConfig := &HostConfig{
215
+		ContainerIDFile: expectedContainerIDFile,
216
+	}
217
+	containerConfigWrapper := MergeConfigs(config, hostConfig)
218
+	if containerConfigWrapper.Config.Hostname != expectedHostname {
219
+		t.Fatalf("containerConfigWrapper config hostname expected %v got %v", expectedHostname, containerConfigWrapper.Config.Hostname)
220
+	}
221
+	if containerConfigWrapper.InnerHostConfig.ContainerIDFile != expectedContainerIDFile {
222
+		t.Fatalf("containerConfigWrapper hostconfig containerIdfile expected %v got %v", expectedContainerIDFile, containerConfigWrapper.InnerHostConfig.ContainerIDFile)
223
+	}
224
+	if containerConfigWrapper.Cpuset != "" {
225
+		t.Fatalf("Expected empty Cpuset, got %v", containerConfigWrapper.Cpuset)
226
+	}
227
+}
228
+
229
+func TestDecodeHostConfig(t *testing.T) {
230
+	fixtures := []struct {
231
+		file string
232
+	}{
233
+		{"fixtures/container_hostconfig_1_14.json"},
234
+		{"fixtures/container_hostconfig_1_19.json"},
235
+	}
236
+
237
+	for _, f := range fixtures {
238
+		b, err := ioutil.ReadFile(f.file)
239
+		if err != nil {
240
+			t.Fatal(err)
241
+		}
242
+
243
+		c, err := DecodeHostConfig(bytes.NewReader(b))
244
+		if err != nil {
245
+			t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err))
246
+		}
247
+
248
+		if c.Privileged != false {
249
+			t.Fatalf("Expected privileged false, found %s\n", c.Privileged)
250
+		}
251
+
252
+		if len(c.Binds) != 1 {
253
+			t.Fatalf("Expected 1 bind, found %v\n", c.Binds)
254
+		}
255
+
256
+		if len(c.CapAdd) != 1 && c.CapAdd[0] != "NET_ADMIN" {
257
+			t.Fatalf("Expected CapAdd NET_ADMIN, got %v", c.CapAdd)
258
+		}
259
+
260
+		if len(c.CapDrop) != 1 && c.CapDrop[0] != "NET_ADMIN" {
261
+			t.Fatalf("Expected CapDrop MKNOD, got %v", c.CapDrop)
262
+		}
263
+	}
264
+}
0 265
new file mode 100644
... ...
@@ -0,0 +1,83 @@
0
+package runconfig
1
+
2
+import (
3
+	"testing"
4
+
5
+	"github.com/docker/docker/pkg/nat"
6
+)
7
+
8
+func TestMerge(t *testing.T) {
9
+	volumesImage := make(map[string]struct{})
10
+	volumesImage["/test1"] = struct{}{}
11
+	volumesImage["/test2"] = struct{}{}
12
+	portsImage := make(nat.PortSet)
13
+	portsImage[nat.Port("1111/tcp")] = struct{}{}
14
+	portsImage[nat.Port("2222/tcp")] = struct{}{}
15
+	configImage := &Config{
16
+		ExposedPorts: portsImage,
17
+		Env:          []string{"VAR1=1", "VAR2=2"},
18
+		Volumes:      volumesImage,
19
+	}
20
+
21
+	portsUser := make(nat.PortSet)
22
+	portsUser[nat.Port("2222/tcp")] = struct{}{}
23
+	portsUser[nat.Port("3333/tcp")] = struct{}{}
24
+	volumesUser := make(map[string]struct{})
25
+	volumesUser["/test3"] = struct{}{}
26
+	configUser := &Config{
27
+		ExposedPorts: portsUser,
28
+		Env:          []string{"VAR2=3", "VAR3=3"},
29
+		Volumes:      volumesUser,
30
+	}
31
+
32
+	if err := Merge(configUser, configImage); err != nil {
33
+		t.Error(err)
34
+	}
35
+
36
+	if len(configUser.ExposedPorts) != 3 {
37
+		t.Fatalf("Expected 3 ExposedPorts, 1111, 2222 and 3333, found %d", len(configUser.ExposedPorts))
38
+	}
39
+	for portSpecs := range configUser.ExposedPorts {
40
+		if portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
41
+			t.Fatalf("Expected 1111 or 2222 or 3333, found %s", portSpecs)
42
+		}
43
+	}
44
+	if len(configUser.Env) != 3 {
45
+		t.Fatalf("Expected 3 env var, VAR1=1, VAR2=3 and VAR3=3, found %d", len(configUser.Env))
46
+	}
47
+	for _, env := range configUser.Env {
48
+		if env != "VAR1=1" && env != "VAR2=3" && env != "VAR3=3" {
49
+			t.Fatalf("Expected VAR1=1 or VAR2=3 or VAR3=3, found %s", env)
50
+		}
51
+	}
52
+
53
+	if len(configUser.Volumes) != 3 {
54
+		t.Fatalf("Expected 3 volumes, /test1, /test2 and /test3, found %d", len(configUser.Volumes))
55
+	}
56
+	for v := range configUser.Volumes {
57
+		if v != "/test1" && v != "/test2" && v != "/test3" {
58
+			t.Fatalf("Expected /test1 or /test2 or /test3, found %s", v)
59
+		}
60
+	}
61
+
62
+	ports, _, err := nat.ParsePortSpecs([]string{"0000"})
63
+	if err != nil {
64
+		t.Error(err)
65
+	}
66
+	configImage2 := &Config{
67
+		ExposedPorts: ports,
68
+	}
69
+
70
+	if err := Merge(configUser, configImage2); err != nil {
71
+		t.Error(err)
72
+	}
73
+
74
+	if len(configUser.ExposedPorts) != 4 {
75
+		t.Fatalf("Expected 4 ExposedPorts, 0000, 1111, 2222 and 3333, found %d", len(configUser.ExposedPorts))
76
+	}
77
+	for portSpecs := range configUser.ExposedPorts {
78
+		if portSpecs.Port() != "0" && portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
79
+			t.Fatalf("Expected %q or %q or %q or %q, found %s", 0, 1111, 2222, 3333, portSpecs)
80
+		}
81
+	}
82
+}
... ...
@@ -1,10 +1,13 @@
1 1
 package runconfig
2 2
 
3 3
 import (
4
+	"fmt"
4 5
 	"io/ioutil"
6
+	"strings"
5 7
 	"testing"
6 8
 
7 9
 	flag "github.com/docker/docker/pkg/mflag"
10
+	"github.com/docker/docker/pkg/nat"
8 11
 	"github.com/docker/docker/pkg/parsers"
9 12
 )
10 13
 
... ...
@@ -15,6 +18,162 @@ func parseRun(args []string) (*Config, *HostConfig, *flag.FlagSet, error) {
15 15
 	return Parse(cmd, args)
16 16
 }
17 17
 
18
+func parse(t *testing.T, args string) (*Config, *HostConfig, error) {
19
+	config, hostConfig, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
20
+	return config, hostConfig, err
21
+}
22
+
23
+func mustParse(t *testing.T, args string) (*Config, *HostConfig) {
24
+	config, hostConfig, err := parse(t, args)
25
+	if err != nil {
26
+		t.Fatal(err)
27
+	}
28
+	return config, hostConfig
29
+}
30
+
31
+// check if (a == c && b == d) || (a == d && b == c)
32
+// because maps are randomized
33
+func compareRandomizedStrings(a, b, c, d string) error {
34
+	if a == c && b == d {
35
+		return nil
36
+	}
37
+	if a == d && b == c {
38
+		return nil
39
+	}
40
+	return fmt.Errorf("strings don't match")
41
+}
42
+func TestParseRunLinks(t *testing.T) {
43
+	if _, hostConfig := mustParse(t, "--link a:b"); len(hostConfig.Links) == 0 || hostConfig.Links[0] != "a:b" {
44
+		t.Fatalf("Error parsing links. Expected []string{\"a:b\"}, received: %v", hostConfig.Links)
45
+	}
46
+	if _, hostConfig := mustParse(t, "--link a:b --link c:d"); len(hostConfig.Links) < 2 || hostConfig.Links[0] != "a:b" || hostConfig.Links[1] != "c:d" {
47
+		t.Fatalf("Error parsing links. Expected []string{\"a:b\", \"c:d\"}, received: %v", hostConfig.Links)
48
+	}
49
+	if _, hostConfig := mustParse(t, ""); len(hostConfig.Links) != 0 {
50
+		t.Fatalf("Error parsing links. No link expected, received: %v", hostConfig.Links)
51
+	}
52
+}
53
+
54
+func TestParseRunAttach(t *testing.T) {
55
+	if config, _ := mustParse(t, "-a stdin"); !config.AttachStdin || config.AttachStdout || config.AttachStderr {
56
+		t.Fatalf("Error parsing attach flags. Expect only Stdin enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
57
+	}
58
+	if config, _ := mustParse(t, "-a stdin -a stdout"); !config.AttachStdin || !config.AttachStdout || config.AttachStderr {
59
+		t.Fatalf("Error parsing attach flags. Expect only Stdin and Stdout enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
60
+	}
61
+	if config, _ := mustParse(t, "-a stdin -a stdout -a stderr"); !config.AttachStdin || !config.AttachStdout || !config.AttachStderr {
62
+		t.Fatalf("Error parsing attach flags. Expect all attach enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
63
+	}
64
+	if config, _ := mustParse(t, ""); config.AttachStdin || !config.AttachStdout || !config.AttachStderr {
65
+		t.Fatalf("Error parsing attach flags. Expect Stdin disabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
66
+	}
67
+	if config, _ := mustParse(t, "-i"); !config.AttachStdin || !config.AttachStdout || !config.AttachStderr {
68
+		t.Fatalf("Error parsing attach flags. Expect Stdin enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
69
+	}
70
+
71
+	if _, _, err := parse(t, "-a"); err == nil {
72
+		t.Fatalf("Error parsing attach flags, `-a` should be an error but is not")
73
+	}
74
+	if _, _, err := parse(t, "-a invalid"); err == nil {
75
+		t.Fatalf("Error parsing attach flags, `-a invalid` should be an error but is not")
76
+	}
77
+	if _, _, err := parse(t, "-a invalid -a stdout"); err == nil {
78
+		t.Fatalf("Error parsing attach flags, `-a stdout -a invalid` should be an error but is not")
79
+	}
80
+	if _, _, err := parse(t, "-a stdout -a stderr -d"); err == nil {
81
+		t.Fatalf("Error parsing attach flags, `-a stdout -a stderr -d` should be an error but is not")
82
+	}
83
+	if _, _, err := parse(t, "-a stdin -d"); err == nil {
84
+		t.Fatalf("Error parsing attach flags, `-a stdin -d` should be an error but is not")
85
+	}
86
+	if _, _, err := parse(t, "-a stdout -d"); err == nil {
87
+		t.Fatalf("Error parsing attach flags, `-a stdout -d` should be an error but is not")
88
+	}
89
+	if _, _, err := parse(t, "-a stderr -d"); err == nil {
90
+		t.Fatalf("Error parsing attach flags, `-a stderr -d` should be an error but is not")
91
+	}
92
+	if _, _, err := parse(t, "-d --rm"); err == nil {
93
+		t.Fatalf("Error parsing attach flags, `-d --rm` should be an error but is not")
94
+	}
95
+}
96
+
97
+func TestParseRunVolumes(t *testing.T) {
98
+	if config, hostConfig := mustParse(t, "-v /tmp"); hostConfig.Binds != nil {
99
+		t.Fatalf("Error parsing volume flags, `-v /tmp` should not mount-bind anything. Received %v", hostConfig.Binds)
100
+	} else if _, exists := config.Volumes["/tmp"]; !exists {
101
+		t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Received %v", config.Volumes)
102
+	}
103
+
104
+	if config, hostConfig := mustParse(t, "-v /tmp -v /var"); hostConfig.Binds != nil {
105
+		t.Fatalf("Error parsing volume flags, `-v /tmp -v /var` should not mount-bind anything. Received %v", hostConfig.Binds)
106
+	} else if _, exists := config.Volumes["/tmp"]; !exists {
107
+		t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Received %v", config.Volumes)
108
+	} else if _, exists := config.Volumes["/var"]; !exists {
109
+		t.Fatalf("Error parsing volume flags, `-v /var` is missing from volumes. Received %v", config.Volumes)
110
+	}
111
+
112
+	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp"); hostConfig.Binds == nil || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
113
+		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp` should mount-bind /hostTmp into /containeTmp. Received %v", hostConfig.Binds)
114
+	}
115
+
116
+	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /hostVar:/containerVar"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp", "/hostVar:/containerVar") != nil {
117
+		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /hostVar:/containerVar` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
118
+	}
119
+
120
+	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp:ro", "/hostVar:/containerVar:rw") != nil {
121
+		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
122
+	}
123
+
124
+	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:roZ -v /hostVar:/containerVar:rwZ"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp:roZ", "/hostVar:/containerVar:rwZ") != nil {
125
+		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:roZ -v /hostVar:/containerVar:rwZ` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
126
+	}
127
+
128
+	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:Z -v /hostVar:/containerVar:z"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp:Z", "/hostVar:/containerVar:z") != nil {
129
+		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:Z -v /hostVar:/containerVar:z` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
130
+	}
131
+
132
+	if config, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /containerVar"); hostConfig.Binds == nil || len(hostConfig.Binds) > 1 || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
133
+		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /containerVar` should mount-bind only /hostTmp into /containeTmp. Received %v", hostConfig.Binds)
134
+	} else if _, exists := config.Volumes["/containerVar"]; !exists {
135
+		t.Fatalf("Error parsing volume flags, `-v /containerVar` is missing from volumes. Received %v", config.Volumes)
136
+	}
137
+
138
+	if config, hostConfig := mustParse(t, ""); hostConfig.Binds != nil {
139
+		t.Fatalf("Error parsing volume flags, without volume, nothing should be mount-binded. Received %v", hostConfig.Binds)
140
+	} else if len(config.Volumes) != 0 {
141
+		t.Fatalf("Error parsing volume flags, without volume, no volume should be present. Received %v", config.Volumes)
142
+	}
143
+
144
+	if _, _, err := parse(t, "-v /"); err == nil {
145
+		t.Fatalf("Expected error, but got none")
146
+	}
147
+
148
+	if _, _, err := parse(t, "-v /:/"); err == nil {
149
+		t.Fatalf("Error parsing volume flags, `-v /:/` should fail but didn't")
150
+	}
151
+	if _, _, err := parse(t, "-v"); err == nil {
152
+		t.Fatalf("Error parsing volume flags, `-v` should fail but didn't")
153
+	}
154
+	if _, _, err := parse(t, "-v /tmp:"); err == nil {
155
+		t.Fatalf("Error parsing volume flags, `-v /tmp:` should fail but didn't")
156
+	}
157
+	if _, _, err := parse(t, "-v /tmp:ro"); err == nil {
158
+		t.Fatalf("Error parsing volume flags, `-v /tmp:ro` should fail but didn't")
159
+	}
160
+	if _, _, err := parse(t, "-v /tmp::"); err == nil {
161
+		t.Fatalf("Error parsing volume flags, `-v /tmp::` should fail but didn't")
162
+	}
163
+	if _, _, err := parse(t, "-v :"); err == nil {
164
+		t.Fatalf("Error parsing volume flags, `-v :` should fail but didn't")
165
+	}
166
+	if _, _, err := parse(t, "-v ::"); err == nil {
167
+		t.Fatalf("Error parsing volume flags, `-v ::` should fail but didn't")
168
+	}
169
+	if _, _, err := parse(t, "-v /tmp:/tmp:/tmp:/tmp"); err == nil {
170
+		t.Fatalf("Error parsing volume flags, `-v /tmp:/tmp:/tmp:/tmp` should fail but didn't")
171
+	}
172
+}
173
+
18 174
 func TestParseLxcConfOpt(t *testing.T) {
19 175
 	opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
20 176
 
... ...
@@ -30,6 +189,18 @@ func TestParseLxcConfOpt(t *testing.T) {
30 30
 			t.Fail()
31 31
 		}
32 32
 	}
33
+
34
+	// With parseRun too
35
+	_, hostconfig, _, err := parseRun([]string{"lxc.utsname=docker", "lxc.utsname = docker ", "img", "cmd"})
36
+	if err != nil {
37
+		t.Fatal(err)
38
+	}
39
+	for _, lxcConf := range hostconfig.LxcConf.Slice() {
40
+		if lxcConf.Key != "lxc.utsname" || lxcConf.Value != "docker" {
41
+			t.Fail()
42
+		}
43
+	}
44
+
33 45
 }
34 46
 
35 47
 func TestNetHostname(t *testing.T) {
... ...
@@ -56,10 +227,335 @@ func TestNetHostname(t *testing.T) {
56 56
 	if _, _, _, err := parseRun([]string{"-h=name", "--net=container:other", "img", "cmd"}); err != ErrConflictNetworkHostname {
57 57
 		t.Fatalf("Expected error ErrConflictNetworkHostname, got: %s", err)
58 58
 	}
59
+	if _, _, _, err := parseRun([]string{"--net=container", "img", "cmd"}); err == nil || err.Error() != "--net: invalid net mode: invalid container format container:<name|id>" {
60
+		t.Fatalf("Expected error with --net=container, got : %v", err)
61
+	}
62
+	if _, _, _, err := parseRun([]string{"--net=weird", "img", "cmd"}); err == nil || err.Error() != "--net: invalid net mode: invalid --net: weird" {
63
+		t.Fatalf("Expected error with --net=weird, got: %s", err)
64
+	}
59 65
 }
60 66
 
61 67
 func TestConflictContainerNetworkAndLinks(t *testing.T) {
62 68
 	if _, _, _, err := parseRun([]string{"--net=container:other", "--link=zip:zap", "img", "cmd"}); err != ErrConflictContainerNetworkAndLinks {
63 69
 		t.Fatalf("Expected error ErrConflictContainerNetworkAndLinks, got: %s", err)
64 70
 	}
71
+	if _, _, _, err := parseRun([]string{"--net=host", "--link=zip:zap", "img", "cmd"}); err != ErrConflictHostNetworkAndLinks {
72
+		t.Fatalf("Expected error ErrConflictHostNetworkAndLinks, got: %s", err)
73
+	}
74
+}
75
+
76
+func TestConflictNetworkModeAndOptions(t *testing.T) {
77
+	if _, _, _, err := parseRun([]string{"--net=host", "--dns=8.8.8.8", "img", "cmd"}); err != ErrConflictNetworkAndDns {
78
+		t.Fatalf("Expected error ErrConflictNetworkAndDns, got %s", err)
79
+	}
80
+	if _, _, _, err := parseRun([]string{"--net=container:other", "--dns=8.8.8.8", "img", "cmd"}); err != ErrConflictNetworkAndDns {
81
+		t.Fatalf("Expected error ErrConflictNetworkAndDns, got %s", err)
82
+	}
83
+	if _, _, _, err := parseRun([]string{"--net=host", "--add-host=name:8.8.8.8", "img", "cmd"}); err != ErrConflictNetworkHosts {
84
+		t.Fatalf("Expected error ErrConflictNetworkAndDns, got %s", err)
85
+	}
86
+	if _, _, _, err := parseRun([]string{"--net=container:other", "--add-host=name:8.8.8.8", "img", "cmd"}); err != ErrConflictNetworkHosts {
87
+		t.Fatalf("Expected error ErrConflictNetworkAndDns, got %s", err)
88
+	}
89
+	if _, _, _, err := parseRun([]string{"--net=host", "--mac-address=92:d0:c6:0a:29:33", "img", "cmd"}); err != ErrConflictContainerNetworkAndMac {
90
+		t.Fatalf("Expected error ErrConflictContainerNetworkAndMac, got %s", err)
91
+	}
92
+	if _, _, _, err := parseRun([]string{"--net=container:other", "--mac-address=92:d0:c6:0a:29:33", "img", "cmd"}); err != ErrConflictContainerNetworkAndMac {
93
+		t.Fatalf("Expected error ErrConflictContainerNetworkAndMac, got %s", err)
94
+	}
95
+	if _, _, _, err := parseRun([]string{"--net=container:other", "-P", "img", "cmd"}); err != ErrConflictNetworkPublishPorts {
96
+		t.Fatalf("Expected error ErrConflictNetworkPublishPorts, got %s", err)
97
+	}
98
+	if _, _, _, err := parseRun([]string{"--net=container:other", "-p", "8080", "img", "cmd"}); err != ErrConflictNetworkPublishPorts {
99
+		t.Fatalf("Expected error ErrConflictNetworkPublishPorts, got %s", err)
100
+	}
101
+	if _, _, _, err := parseRun([]string{"--net=container:other", "--expose", "8000-9000", "img", "cmd"}); err != ErrConflictNetworkExposePorts {
102
+		t.Fatalf("Expected error ErrConflictNetworkExposePorts, got %s", err)
103
+	}
104
+}
105
+
106
+// Simple parse with MacAddress validatation
107
+func TestParseWithMacAddress(t *testing.T) {
108
+	invalidMacAddress := "--mac-address=invalidMacAddress"
109
+	validMacAddress := "--mac-address=92:d0:c6:0a:29:33"
110
+	if _, _, _, err := parseRun([]string{invalidMacAddress, "img", "cmd"}); err != nil && err.Error() != "invalidMacAddress is not a valid mac address" {
111
+		t.Fatalf("Expected an error with %v mac-address, got %v", invalidMacAddress, err)
112
+	}
113
+	if config, _ := mustParse(t, validMacAddress); config.MacAddress != "92:d0:c6:0a:29:33" {
114
+		t.Fatalf("Expected the config to have '92:d0:c6:0a:29:33' as MacAddress, got '%v'", config.MacAddress)
115
+	}
116
+}
117
+
118
+func TestParseWithMemory(t *testing.T) {
119
+	invalidMemory := "--memory=invalid"
120
+	validMemory := "--memory=1G"
121
+	if _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err != nil && err.Error() != "invalid size: 'invalid'" {
122
+		t.Fatalf("Expected an error with '%v' Memory, got '%v'", invalidMemory, err)
123
+	}
124
+	if _, hostconfig := mustParse(t, validMemory); hostconfig.Memory != 1073741824 {
125
+		t.Fatalf("Expected the config to have '1G' as Memory, got '%v'", hostconfig.Memory)
126
+	}
127
+}
128
+
129
+func TestParseWithMemorySwap(t *testing.T) {
130
+	invalidMemory := "--memory-swap=invalid"
131
+	validMemory := "--memory-swap=1G"
132
+	anotherValidMemory := "--memory-swap=-1"
133
+	if _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err == nil || err.Error() != "invalid size: 'invalid'" {
134
+		t.Fatalf("Expected an error with '%v' MemorySwap, got '%v'", invalidMemory, err)
135
+	}
136
+	if _, hostconfig := mustParse(t, validMemory); hostconfig.MemorySwap != 1073741824 {
137
+		t.Fatalf("Expected the config to have '1073741824' as MemorySwap, got '%v'", hostconfig.MemorySwap)
138
+	}
139
+	if _, hostconfig := mustParse(t, anotherValidMemory); hostconfig.MemorySwap != -1 {
140
+		t.Fatalf("Expected the config to have '-1' as MemorySwap, got '%v'", hostconfig.MemorySwap)
141
+	}
142
+}
143
+
144
+func TestParseHostname(t *testing.T) {
145
+	hostname := "--hostname=hostname"
146
+	hostnameWithDomain := "--hostname=hostname.domainname"
147
+	hostnameWithDomainTld := "--hostname=hostname.domainname.tld"
148
+	if config, _ := mustParse(t, hostname); config.Hostname != "hostname" && config.Domainname != "" {
149
+		t.Fatalf("Expected the config to have 'hostname' as hostname, got '%v'", config.Hostname)
150
+	}
151
+	if config, _ := mustParse(t, hostnameWithDomain); config.Hostname != "hostname" && config.Domainname != "domainname" {
152
+		t.Fatalf("Expected the config to have 'hostname' as hostname, got '%v'", config.Hostname)
153
+	}
154
+	if config, _ := mustParse(t, hostnameWithDomainTld); config.Hostname != "hostname" && config.Domainname != "domainname.tld" {
155
+		t.Fatalf("Expected the config to have 'hostname' as hostname, got '%v'", config.Hostname)
156
+	}
157
+}
158
+
159
+func TestParseWithExpose(t *testing.T) {
160
+	invalids := map[string]string{
161
+		":":                   "Invalid port format for --expose: :",
162
+		"8080:9090":           "Invalid port format for --expose: 8080:9090",
163
+		"/tcp":                "Invalid range format for --expose: /tcp, error: Empty string specified for ports.",
164
+		"/udp":                "Invalid range format for --expose: /udp, error: Empty string specified for ports.",
165
+		"NaN/tcp":             `Invalid range format for --expose: NaN/tcp, error: strconv.ParseUint: parsing "NaN": invalid syntax`,
166
+		"NaN-NaN/tcp":         `Invalid range format for --expose: NaN-NaN/tcp, error: strconv.ParseUint: parsing "NaN": invalid syntax`,
167
+		"8080-NaN/tcp":        `Invalid range format for --expose: 8080-NaN/tcp, error: strconv.ParseUint: parsing "NaN": invalid syntax`,
168
+		"1234567890-8080/tcp": `Invalid range format for --expose: 1234567890-8080/tcp, error: strconv.ParseUint: parsing "1234567890": value out of range`,
169
+	}
170
+	valids := map[string][]nat.Port{
171
+		"8080/tcp":      {"8080/tcp"},
172
+		"8080/udp":      {"8080/udp"},
173
+		"8080/ncp":      {"8080/ncp"},
174
+		"8080-8080/udp": {"8080/udp"},
175
+		"8080-8082/tcp": {"8080/tcp", "8081/tcp", "8082/tcp"},
176
+	}
177
+	for expose, expectedError := range invalids {
178
+		if _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"}); err == nil || err.Error() != expectedError {
179
+			t.Fatalf("Expected error '%v' with '--expose=%v', got '%v'", expectedError, expose, err)
180
+		}
181
+	}
182
+	for expose, exposedPorts := range valids {
183
+		config, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"})
184
+		if err != nil {
185
+			t.Fatal(err)
186
+		}
187
+		if len(config.ExposedPorts) != len(exposedPorts) {
188
+			t.Fatalf("Expected %v exposed port, got %v", len(exposedPorts), len(config.ExposedPorts))
189
+		}
190
+		for _, port := range exposedPorts {
191
+			if _, ok := config.ExposedPorts[port]; !ok {
192
+				t.Fatalf("Expected %v, got %v", exposedPorts, config.ExposedPorts)
193
+			}
194
+		}
195
+	}
196
+	// Merge with actual published port
197
+	config, _, _, err := parseRun([]string{"--publish=80", "--expose=80-81/tcp", "img", "cmd"})
198
+	if err != nil {
199
+		t.Fatal(err)
200
+	}
201
+	if len(config.ExposedPorts) != 2 {
202
+		t.Fatalf("Expected 2 exposed ports, got %v", config.ExposedPorts)
203
+	}
204
+	ports := []nat.Port{"80/tcp", "81/tcp"}
205
+	for _, port := range ports {
206
+		if _, ok := config.ExposedPorts[port]; !ok {
207
+			t.Fatalf("Expected %v, got %v", ports, config.ExposedPorts)
208
+		}
209
+	}
210
+}
211
+
212
+func TestParseDevice(t *testing.T) {
213
+	valids := map[string]DeviceMapping{
214
+		"/dev/snd": {
215
+			PathOnHost:        "/dev/snd",
216
+			PathInContainer:   "/dev/snd",
217
+			CgroupPermissions: "rwm",
218
+		},
219
+		"/dev/snd:/something": {
220
+			PathOnHost:        "/dev/snd",
221
+			PathInContainer:   "/something",
222
+			CgroupPermissions: "rwm",
223
+		},
224
+		"/dev/snd:/something:ro": {
225
+			PathOnHost:        "/dev/snd",
226
+			PathInContainer:   "/something",
227
+			CgroupPermissions: "ro",
228
+		},
229
+	}
230
+	for device, deviceMapping := range valids {
231
+		_, hostconfig, _, err := parseRun([]string{fmt.Sprintf("--device=%v", device), "img", "cmd"})
232
+		if err != nil {
233
+			t.Fatal(err)
234
+		}
235
+		if len(hostconfig.Devices) != 1 {
236
+			t.Fatalf("Expected 1 devices, got %v", hostconfig.Devices)
237
+		}
238
+		if hostconfig.Devices[0] != deviceMapping {
239
+			t.Fatalf("Expected %v, got %v", deviceMapping, hostconfig.Devices)
240
+		}
241
+	}
242
+
243
+}
244
+
245
+func TestParseModes(t *testing.T) {
246
+	// ipc ko
247
+	if _, _, _, err := parseRun([]string{"--ipc=container:", "img", "cmd"}); err == nil || err.Error() != "--ipc: invalid IPC mode" {
248
+		t.Fatalf("Expected an error with message '--ipc: invalid IPC mode', got %v", err)
249
+	}
250
+	// ipc ok
251
+	_, hostconfig, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
252
+	if err != nil {
253
+		t.Fatal(err)
254
+	}
255
+	if !hostconfig.IpcMode.Valid() {
256
+		t.Fatalf("Expected a valid IpcMode, got %v", hostconfig.IpcMode)
257
+	}
258
+	// pid ko
259
+	if _, _, _, err := parseRun([]string{"--pid=container:", "img", "cmd"}); err == nil || err.Error() != "--pid: invalid PID mode" {
260
+		t.Fatalf("Expected an error with message '--pid: invalid PID mode', got %v", err)
261
+	}
262
+	// pid ok
263
+	_, hostconfig, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
264
+	if err != nil {
265
+		t.Fatal(err)
266
+	}
267
+	if !hostconfig.PidMode.Valid() {
268
+		t.Fatalf("Expected a valid PidMode, got %v", hostconfig.PidMode)
269
+	}
270
+	// uts ko
271
+	if _, _, _, err := parseRun([]string{"--uts=container:", "img", "cmd"}); err == nil || err.Error() != "--uts: invalid UTS mode" {
272
+		t.Fatalf("Expected an error with message '--uts: invalid UTS mode', got %v", err)
273
+	}
274
+	// uts ok
275
+	_, hostconfig, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
276
+	if err != nil {
277
+		t.Fatal(err)
278
+	}
279
+	if !hostconfig.UTSMode.Valid() {
280
+		t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
281
+	}
282
+}
283
+
284
+func TestParseRestartPolicy(t *testing.T) {
285
+	invalids := map[string]string{
286
+		"something":          "invalid restart policy something",
287
+		"always:2":           "maximum restart count not valid with restart policy of \"always\"",
288
+		"on-failure:invalid": `strconv.ParseInt: parsing "invalid": invalid syntax`,
289
+	}
290
+	valids := map[string]RestartPolicy{
291
+		"": {},
292
+		// FIXME This feels not right
293
+		"always:1:2": {
294
+			Name:              "always",
295
+			MaximumRetryCount: 0,
296
+		},
297
+		"on-failure:1": {
298
+			Name:              "on-failure",
299
+			MaximumRetryCount: 1,
300
+		},
301
+		// FIXME This doesn't feel right
302
+		"on-failure:1:2": {
303
+			Name:              "on-failure",
304
+			MaximumRetryCount: 0,
305
+		},
306
+	}
307
+	for restart, expectedError := range invalids {
308
+		if _, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%s", restart), "img", "cmd"}); err == nil || err.Error() != expectedError {
309
+			t.Fatalf("Expected an error with message '%v' for %v, got %v", expectedError, restart, err)
310
+		}
311
+	}
312
+	for restart, expected := range valids {
313
+		_, hostconfig, _, err := parseRun([]string{fmt.Sprintf("--restart=%v", restart), "img", "cmd"})
314
+		if err != nil {
315
+			t.Fatal(err)
316
+		}
317
+		if hostconfig.RestartPolicy != expected {
318
+			t.Fatalf("Expected %v, got %v", expected, hostconfig.RestartPolicy)
319
+		}
320
+	}
321
+}
322
+
323
+func TestParseLoggingOpts(t *testing.T) {
324
+	// logging opts ko
325
+	if _, _, _, err := parseRun([]string{"--log-driver=none", "--log-opt=anything", "img", "cmd"}); err == nil || err.Error() != "Invalid logging opts for driver none" {
326
+		t.Fatalf("Expected an error with message 'Invalid logging opts for driver none', got %v", err)
327
+	}
328
+	// logging opts ok
329
+	_, hostconfig, _, err := parseRun([]string{"--log-driver=syslog", "--log-opt=something", "img", "cmd"})
330
+	if err != nil {
331
+		t.Fatal(err)
332
+	}
333
+	if hostconfig.LogConfig.Type != "syslog" || len(hostconfig.LogConfig.Config) != 1 {
334
+		t.Fatalf("Expected a 'syslog' LogConfig with one config, got %v", hostconfig.RestartPolicy)
335
+	}
336
+}
337
+
338
+func TestParseEnvfileVariables(t *testing.T) {
339
+	// env ko
340
+	if _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != "open nonexistent: no such file or directory" {
341
+		t.Fatalf("Expected an error with message 'open nonexistent: no such file or directory', got %v", err)
342
+	}
343
+	// env ok
344
+	config, _, _, err := parseRun([]string{"--env-file=fixtures/valid.env", "img", "cmd"})
345
+	if err != nil {
346
+		t.Fatal(err)
347
+	}
348
+	if len(config.Env) != 1 || config.Env[0] != "ENV1=value1" {
349
+		t.Fatalf("Expected a a config with [ENV1=value1], got %v", config.Env)
350
+	}
351
+	config, _, _, err = parseRun([]string{"--env-file=fixtures/valid.env", "--env=ENV2=value2", "img", "cmd"})
352
+	if err != nil {
353
+		t.Fatal(err)
354
+	}
355
+	if len(config.Env) != 2 || config.Env[0] != "ENV1=value1" || config.Env[1] != "ENV2=value2" {
356
+		t.Fatalf("Expected a a config with [ENV1=value1 ENV2=value2], got %v", config.Env)
357
+	}
358
+}
359
+
360
+func TestParseLabelfileVariables(t *testing.T) {
361
+	// label ko
362
+	if _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != "open nonexistent: no such file or directory" {
363
+		t.Fatalf("Expected an error with message 'open nonexistent: no such file or directory', got %v", err)
364
+	}
365
+	// label ok
366
+	config, _, _, err := parseRun([]string{"--label-file=fixtures/valid.label", "img", "cmd"})
367
+	if err != nil {
368
+		t.Fatal(err)
369
+	}
370
+	if len(config.Labels) != 1 || config.Labels["LABEL1"] != "value1" {
371
+		t.Fatalf("Expected a a config with [LABEL1:value1], got %v", config.Labels)
372
+	}
373
+	config, _, _, err = parseRun([]string{"--label-file=fixtures/valid.label", "--label=LABEL2=value2", "img", "cmd"})
374
+	if err != nil {
375
+		t.Fatal(err)
376
+	}
377
+	if len(config.Labels) != 2 || config.Labels["LABEL1"] != "value1" || config.Labels["LABEL2"] != "value2" {
378
+		t.Fatalf("Expected a a config with [LABEL1:value1 LABEL2:value2], got %v", config.Labels)
379
+	}
380
+}
381
+
382
+func TestParseEntryPoint(t *testing.T) {
383
+	config, _, _, err := parseRun([]string{"--entrypoint=anything", "cmd", "img"})
384
+	if err != nil {
385
+		t.Fatal(err)
386
+	}
387
+	if config.Entrypoint.Len() != 1 && config.Entrypoint.parts[0] != "anything" {
388
+		t.Fatalf("Expected entrypoint 'anything', got %v", config.Entrypoint)
389
+	}
65 390
 }