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