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