github.com/hustcat/docker@v1.3.3-0.20160314103604-901c67a8eeab/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  }