Tizen_4.0 base
[platform/upstream/docker-engine.git] / integration-cli / docker_cli_help_test.go
1 package main
2
3 import (
4         "fmt"
5         "runtime"
6         "strings"
7         "unicode"
8
9         "github.com/docker/docker/integration-cli/checker"
10         "github.com/docker/docker/integration-cli/cli"
11         "github.com/docker/docker/pkg/homedir"
12         icmd "github.com/docker/docker/pkg/testutil/cmd"
13         "github.com/go-check/check"
14 )
15
16 func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
17         // FIXME(vdemeester) should be a unit test, probably using golden files ?
18         testRequires(c, DaemonIsLinux)
19
20         // Make sure main help text fits within 80 chars and that
21         // on non-windows system we use ~ when possible (to shorten things).
22         // Test for HOME set to its default value and set to "/" on linux
23         // Yes on windows setting up an array and looping (right now) isn't
24         // necessary because we just have one value, but we'll need the
25         // array/loop on linux so we might as well set it up so that we can
26         // test any number of home dirs later on and all we need to do is
27         // modify the array - the rest of the testing infrastructure should work
28         homes := []string{homedir.Get()}
29
30         // Non-Windows machines need to test for this special case of $HOME
31         if runtime.GOOS != "windows" {
32                 homes = append(homes, "/")
33         }
34
35         homeKey := homedir.Key()
36         baseEnvs := appendBaseEnv(true)
37
38         // Remove HOME env var from list so we can add a new value later.
39         for i, env := range baseEnvs {
40                 if strings.HasPrefix(env, homeKey+"=") {
41                         baseEnvs = append(baseEnvs[:i], baseEnvs[i+1:]...)
42                         break
43                 }
44         }
45
46         for _, home := range homes {
47
48                 // Dup baseEnvs and add our new HOME value
49                 newEnvs := make([]string, len(baseEnvs)+1)
50                 copy(newEnvs, baseEnvs)
51                 newEnvs[len(newEnvs)-1] = homeKey + "=" + home
52
53                 scanForHome := runtime.GOOS != "windows" && home != "/"
54
55                 // Check main help text to make sure its not over 80 chars
56                 result := icmd.RunCmd(icmd.Cmd{
57                         Command: []string{dockerBinary, "help"},
58                         Env:     newEnvs,
59                 })
60                 result.Assert(c, icmd.Success)
61                 lines := strings.Split(result.Combined(), "\n")
62                 for _, line := range lines {
63                         // All lines should not end with a space
64                         c.Assert(line, checker.Not(checker.HasSuffix), " ", check.Commentf("Line should not end with a space"))
65
66                         if scanForHome && strings.Contains(line, `=`+home) {
67                                 c.Fatalf("Line should use '%q' instead of %q:\n%s", homedir.GetShortcutString(), home, line)
68                         }
69                         if runtime.GOOS != "windows" {
70                                 i := strings.Index(line, homedir.GetShortcutString())
71                                 if i >= 0 && i != len(line)-1 && line[i+1] != '/' {
72                                         c.Fatalf("Main help should not have used home shortcut:\n%s", line)
73                                 }
74                         }
75                 }
76
77                 // Make sure each cmd's help text fits within 90 chars and that
78                 // on non-windows system we use ~ when possible (to shorten things).
79                 // Pull the list of commands from the "Commands:" section of docker help
80                 // FIXME(vdemeester) Why re-run help ?
81                 //helpCmd = exec.Command(dockerBinary, "help")
82                 //helpCmd.Env = newEnvs
83                 //out, _, err = runCommandWithOutput(helpCmd)
84                 //c.Assert(err, checker.IsNil, check.Commentf(out))
85                 i := strings.Index(result.Combined(), "Commands:")
86                 c.Assert(i, checker.GreaterOrEqualThan, 0, check.Commentf("Missing 'Commands:' in:\n%s", result.Combined()))
87
88                 cmds := []string{}
89                 // Grab all chars starting at "Commands:"
90                 helpOut := strings.Split(result.Combined()[i:], "\n")
91                 // Skip first line, it is just "Commands:"
92                 helpOut = helpOut[1:]
93
94                 // Create the list of commands we want to test
95                 cmdsToTest := []string{}
96                 for _, cmd := range helpOut {
97                         // Stop on blank line or non-indented line
98                         if cmd == "" || !unicode.IsSpace(rune(cmd[0])) {
99                                 break
100                         }
101
102                         // Grab just the first word of each line
103                         cmd = strings.Split(strings.TrimSpace(cmd), " ")[0]
104                         cmds = append(cmds, cmd) // Saving count for later
105
106                         cmdsToTest = append(cmdsToTest, cmd)
107                 }
108
109                 // Add some 'two word' commands - would be nice to automatically
110                 // calculate this list - somehow
111                 cmdsToTest = append(cmdsToTest, "volume create")
112                 cmdsToTest = append(cmdsToTest, "volume inspect")
113                 cmdsToTest = append(cmdsToTest, "volume ls")
114                 cmdsToTest = append(cmdsToTest, "volume rm")
115                 cmdsToTest = append(cmdsToTest, "network connect")
116                 cmdsToTest = append(cmdsToTest, "network create")
117                 cmdsToTest = append(cmdsToTest, "network disconnect")
118                 cmdsToTest = append(cmdsToTest, "network inspect")
119                 cmdsToTest = append(cmdsToTest, "network ls")
120                 cmdsToTest = append(cmdsToTest, "network rm")
121
122                 if testEnv.ExperimentalDaemon() {
123                         cmdsToTest = append(cmdsToTest, "checkpoint create")
124                         cmdsToTest = append(cmdsToTest, "checkpoint ls")
125                         cmdsToTest = append(cmdsToTest, "checkpoint rm")
126                 }
127
128                 // Divide the list of commands into go routines and  run the func testcommand on the commands in parallel
129                 // to save runtime of test
130
131                 errChan := make(chan error)
132
133                 for index := 0; index < len(cmdsToTest); index++ {
134                         go func(index int) {
135                                 errChan <- testCommand(cmdsToTest[index], newEnvs, scanForHome, home)
136                         }(index)
137                 }
138
139                 for index := 0; index < len(cmdsToTest); index++ {
140                         err := <-errChan
141                         if err != nil {
142                                 c.Fatal(err)
143                         }
144                 }
145         }
146 }
147
148 func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
149         // Test to make sure the exit code and output (stdout vs stderr) of
150         // various good and bad cases are what we expect
151
152         // docker : stdout=all, stderr=empty, rc=0
153         out := cli.DockerCmd(c).Combined()
154         // Be really pick
155         c.Assert(out, checker.Not(checker.HasSuffix), "\n\n", check.Commentf("Should not have a blank line at the end of 'docker'\n"))
156
157         // docker help: stdout=all, stderr=empty, rc=0
158         out = cli.DockerCmd(c, "help").Combined()
159         // Be really pick
160         c.Assert(out, checker.Not(checker.HasSuffix), "\n\n", check.Commentf("Should not have a blank line at the end of 'docker help'\n"))
161
162         // docker --help: stdout=all, stderr=empty, rc=0
163         out = cli.DockerCmd(c, "--help").Combined()
164         // Be really pick
165         c.Assert(out, checker.Not(checker.HasSuffix), "\n\n", check.Commentf("Should not have a blank line at the end of 'docker --help'\n"))
166
167         // docker inspect busybox: stdout=all, stderr=empty, rc=0
168         // Just making sure stderr is empty on valid cmd
169         out = cli.DockerCmd(c, "inspect", "busybox").Combined()
170         // Be really pick
171         c.Assert(out, checker.Not(checker.HasSuffix), "\n\n", check.Commentf("Should not have a blank line at the end of 'docker inspect busyBox'\n"))
172
173         // docker rm: stdout=empty, stderr=all, rc!=0
174         // testing the min arg error msg
175         cli.Docker(cli.Args("rm")).Assert(c, icmd.Expected{
176                 ExitCode: 1,
177                 Error:    "exit status 1",
178                 Out:      "",
179                 // Should not contain full help text but should contain info about
180                 // # of args and Usage line
181                 Err: "requires at least 1 argument",
182         })
183
184         // docker rm NoSuchContainer: stdout=empty, stderr=all, rc=0
185         // testing to make sure no blank line on error
186         result := cli.Docker(cli.Args("rm", "NoSuchContainer")).Assert(c, icmd.Expected{
187                 ExitCode: 1,
188                 Error:    "exit status 1",
189                 Out:      "",
190         })
191         // Be really picky
192         c.Assert(len(result.Stderr()), checker.Not(checker.Equals), 0)
193         c.Assert(result.Stderr(), checker.Not(checker.HasSuffix), "\n\n", check.Commentf("Should not have a blank line at the end of 'docker rm'\n"))
194
195         // docker BadCmd: stdout=empty, stderr=all, rc=0
196         cli.Docker(cli.Args("BadCmd")).Assert(c, icmd.Expected{
197                 ExitCode: 1,
198                 Error:    "exit status 1",
199                 Out:      "",
200                 Err:      "docker: 'BadCmd' is not a docker command.\nSee 'docker --help'\n",
201         })
202 }
203
204 func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) error {
205
206         args := strings.Split(cmd+" --help", " ")
207
208         // Check the full usage text
209         result := icmd.RunCmd(icmd.Cmd{
210                 Command: append([]string{dockerBinary}, args...),
211                 Env:     newEnvs,
212         })
213         err := result.Error
214         out := result.Stdout()
215         stderr := result.Stderr()
216         if len(stderr) != 0 {
217                 return fmt.Errorf("Error on %q help. non-empty stderr:%q\n", cmd, stderr)
218         }
219         if strings.HasSuffix(out, "\n\n") {
220                 return fmt.Errorf("Should not have blank line on %q\n", cmd)
221         }
222         if !strings.Contains(out, "--help") {
223                 return fmt.Errorf("All commands should mention '--help'. Command '%v' did not.\n", cmd)
224         }
225
226         if err != nil {
227                 return fmt.Errorf(out)
228         }
229
230         // Check each line for lots of stuff
231         lines := strings.Split(out, "\n")
232         for _, line := range lines {
233                 i := strings.Index(line, "~")
234                 if i >= 0 && i != len(line)-1 && line[i+1] != '/' {
235                         return fmt.Errorf("Help for %q should not have used ~:\n%s", cmd, line)
236                 }
237
238                 // Options should NOT end with a period
239                 if strings.HasPrefix(line, "  -") && strings.HasSuffix(line, ".") {
240                         return fmt.Errorf("Help for %q should not end with a period: %s", cmd, line)
241                 }
242
243                 // Options should NOT end with a space
244                 if strings.HasSuffix(line, " ") {
245                         return fmt.Errorf("Help for %q should not end with a space: %s", cmd, line)
246                 }
247
248         }
249
250         // For each command make sure we generate an error
251         // if we give a bad arg
252         args = strings.Split(cmd+" --badArg", " ")
253
254         out, _, err = dockerCmdWithError(args...)
255         if err == nil {
256                 return fmt.Errorf(out)
257         }
258
259         // Be really picky
260         if strings.HasSuffix(stderr, "\n\n") {
261                 return fmt.Errorf("Should not have a blank line at the end of 'docker rm'\n")
262         }
263
264         // Now make sure that each command will print a short-usage
265         // (not a full usage - meaning no opts section) if we
266         // are missing a required arg or pass in a bad arg
267
268         // These commands will never print a short-usage so don't test
269         noShortUsage := map[string]string{
270                 "images":        "",
271                 "login":         "",
272                 "logout":        "",
273                 "network":       "",
274                 "stats":         "",
275                 "volume create": "",
276         }
277
278         if _, ok := noShortUsage[cmd]; !ok {
279                 // skipNoArgs are ones that we don't want to try w/o
280                 // any args. Either because it'll hang the test or
281                 // lead to incorrect test result (like false negative).
282                 // Whatever the reason, skip trying to run w/o args and
283                 // jump to trying with a bogus arg.
284                 skipNoArgs := map[string]struct{}{
285                         "daemon": {},
286                         "events": {},
287                         "load":   {},
288                 }
289
290                 var result *icmd.Result
291                 if _, ok := skipNoArgs[cmd]; !ok {
292                         result = dockerCmdWithResult(strings.Split(cmd, " ")...)
293                 }
294
295                 // If its ok w/o any args then try again with an arg
296                 if result == nil || result.ExitCode == 0 {
297                         result = dockerCmdWithResult(strings.Split(cmd+" badArg", " ")...)
298                 }
299
300                 if err := result.Compare(icmd.Expected{
301                         Out:      icmd.None,
302                         Err:      "\nUsage:",
303                         ExitCode: 1,
304                 }); err != nil {
305                         return err
306                 }
307
308                 stderr := result.Stderr()
309                 // Shouldn't have full usage
310                 if strings.Contains(stderr, "--help=false") {
311                         return fmt.Errorf("Should not have full usage on %q:%v", result.Cmd.Args, stderr)
312                 }
313                 if strings.HasSuffix(stderr, "\n\n") {
314                         return fmt.Errorf("Should not have a blank line on %q\n%v", result.Cmd.Args, stderr)
315                 }
316         }
317
318         return nil
319 }