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