github.com/damirazo/docker@v1.9.0/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/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 := os.Environ()
    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, ec, err := runCommandWithOutput(helpCmd)
    54  		if err != nil || ec != 0 {
    55  			c.Fatalf("docker help should have worked\nout:%s\nec:%d", out, ec)
    56  		}
    57  		lines := strings.Split(out, "\n")
    58  		for _, line := range lines {
    59  			if len(line) > 80 {
    60  				c.Fatalf("Line is too long(%d chars):\n%s", len(line), line)
    61  			}
    62  
    63  			// All lines should not end with a space
    64  			if strings.HasSuffix(line, " ") {
    65  				c.Fatalf("Line should not end with a space: %s", line)
    66  			}
    67  
    68  			if scanForHome && strings.Contains(line, `=`+home) {
    69  				c.Fatalf("Line should use '%q' instead of %q:\n%s", homedir.GetShortcutString(), home, line)
    70  			}
    71  			if runtime.GOOS != "windows" {
    72  				i := strings.Index(line, homedir.GetShortcutString())
    73  				if i >= 0 && i != len(line)-1 && line[i+1] != '/' {
    74  					c.Fatalf("Main help should not have used home shortcut:\n%s", line)
    75  				}
    76  			}
    77  		}
    78  
    79  		// Make sure each cmd's help text fits within 90 chars and that
    80  		// on non-windows system we use ~ when possible (to shorten things).
    81  		// Pull the list of commands from the "Commands:" section of docker help
    82  		helpCmd = exec.Command(dockerBinary, "help")
    83  		helpCmd.Env = newEnvs
    84  		out, ec, err = runCommandWithOutput(helpCmd)
    85  		if err != nil || ec != 0 {
    86  			c.Fatalf("docker help should have worked\nout:%s\nec:%d", out, ec)
    87  		}
    88  		i := strings.Index(out, "Commands:")
    89  		if i < 0 {
    90  			c.Fatalf("Missing 'Commands:' in:\n%s", out)
    91  		}
    92  
    93  		cmds := []string{}
    94  		// Grab all chars starting at "Commands:"
    95  		helpOut := strings.Split(out[i:], "\n")
    96  		// First line is just "Commands:"
    97  		if isLocalDaemon {
    98  			// Replace first line with "daemon" command since it's not part of the list of commands.
    99  			helpOut[0] = " daemon"
   100  		} else {
   101  			// Skip first line
   102  			helpOut = helpOut[1:]
   103  		}
   104  
   105  		// Create the list of commands we want to test
   106  		cmdsToTest := []string{}
   107  		for _, cmd := range helpOut {
   108  			// Stop on blank line or non-idented line
   109  			if cmd == "" || !unicode.IsSpace(rune(cmd[0])) {
   110  				break
   111  			}
   112  
   113  			// Grab just the first word of each line
   114  			cmd = strings.Split(strings.TrimSpace(cmd), " ")[0]
   115  			cmds = append(cmds, cmd) // Saving count for later
   116  
   117  			cmdsToTest = append(cmdsToTest, cmd)
   118  		}
   119  
   120  		// Add some 'two word' commands - would be nice to automatically
   121  		// calculate this list - somehow
   122  		cmdsToTest = append(cmdsToTest, "volume create")
   123  		cmdsToTest = append(cmdsToTest, "volume inspect")
   124  		cmdsToTest = append(cmdsToTest, "volume ls")
   125  		cmdsToTest = append(cmdsToTest, "volume rm")
   126  
   127  		for _, cmd := range cmdsToTest {
   128  			var stderr string
   129  
   130  			args := strings.Split(cmd+" --help", " ")
   131  
   132  			// Check the full usage text
   133  			helpCmd := exec.Command(dockerBinary, args...)
   134  			helpCmd.Env = newEnvs
   135  			out, stderr, ec, err = runCommandWithStdoutStderr(helpCmd)
   136  			if len(stderr) != 0 {
   137  				c.Fatalf("Error on %q help. non-empty stderr:%q", cmd, stderr)
   138  			}
   139  			if strings.HasSuffix(out, "\n\n") {
   140  				c.Fatalf("Should not have blank line on %q\nout:%q", cmd, out)
   141  			}
   142  			if !strings.Contains(out, "--help=false") {
   143  				c.Fatalf("Should show full usage on %q\nout:%q", cmd, out)
   144  			}
   145  			if err != nil || ec != 0 {
   146  				c.Fatalf("Error on %q help: %s\nexit code:%d", cmd, out, ec)
   147  			}
   148  
   149  			// Check each line for lots of stuff
   150  			lines := strings.Split(out, "\n")
   151  			for _, line := range lines {
   152  				if len(line) > 90 {
   153  					c.Fatalf("Help for %q is too long(%d chars):\n%s", cmd,
   154  						len(line), line)
   155  				}
   156  
   157  				if scanForHome && strings.Contains(line, `"`+home) {
   158  					c.Fatalf("Help for %q should use ~ instead of %q on:\n%s",
   159  						cmd, home, line)
   160  				}
   161  				i := strings.Index(line, "~")
   162  				if i >= 0 && i != len(line)-1 && line[i+1] != '/' {
   163  					c.Fatalf("Help for %q should not have used ~:\n%s", cmd, line)
   164  				}
   165  
   166  				// If a line starts with 4 spaces then assume someone
   167  				// added a multi-line description for an option and we need
   168  				// to flag it
   169  				if strings.HasPrefix(line, "    ") {
   170  					c.Fatalf("Help for %q should not have a multi-line option: %s", cmd, line)
   171  				}
   172  
   173  				// Options should NOT end with a period
   174  				if strings.HasPrefix(line, "  -") && strings.HasSuffix(line, ".") {
   175  					c.Fatalf("Help for %q should not end with a period: %s", cmd, line)
   176  				}
   177  
   178  				// Options should NOT end with a space
   179  				if strings.HasSuffix(line, " ") {
   180  					c.Fatalf("Help for %q should not end with a space: %s", cmd, line)
   181  				}
   182  
   183  			}
   184  
   185  			// For each command make sure we generate an error
   186  			// if we give a bad arg
   187  			args = strings.Split(cmd+" --badArg", " ")
   188  
   189  			dCmd := exec.Command(dockerBinary, args...)
   190  			out, stderr, ec, err = runCommandWithStdoutStderr(dCmd)
   191  			if len(out) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
   192  				c.Fatalf("Bad results from 'docker %s --badArg'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", cmd, ec, out, stderr, err)
   193  			}
   194  			// Be really picky
   195  			if strings.HasSuffix(stderr, "\n\n") {
   196  				c.Fatalf("Should not have a blank line at the end of 'docker rm'\n%s", stderr)
   197  			}
   198  
   199  			// Now make sure that each command will print a short-usage
   200  			// (not a full usage - meaning no opts section) if we
   201  			// are missing a required arg or pass in a bad arg
   202  
   203  			// These commands will never print a short-usage so don't test
   204  			noShortUsage := map[string]string{
   205  				"images":  "",
   206  				"login":   "",
   207  				"logout":  "",
   208  				"network": "",
   209  			}
   210  
   211  			if _, ok := noShortUsage[cmd]; !ok {
   212  				// For each command run it w/o any args. It will either return
   213  				// valid output or print a short-usage
   214  				var dCmd *exec.Cmd
   215  				var stdout, stderr string
   216  				var args []string
   217  
   218  				// skipNoArgs are ones that we don't want to try w/o
   219  				// any args. Either because it'll hang the test or
   220  				// lead to incorrect test result (like false negative).
   221  				// Whatever the reason, skip trying to run w/o args and
   222  				// jump to trying with a bogus arg.
   223  				skipNoArgs := map[string]struct{}{
   224  					"daemon": {},
   225  					"events": {},
   226  					"load":   {},
   227  				}
   228  
   229  				ec = 0
   230  				if _, ok := skipNoArgs[cmd]; !ok {
   231  					args = strings.Split(cmd, " ")
   232  					dCmd = exec.Command(dockerBinary, args...)
   233  					stdout, stderr, ec, err = runCommandWithStdoutStderr(dCmd)
   234  				}
   235  
   236  				// If its ok w/o any args then try again with an arg
   237  				if ec == 0 {
   238  					args = strings.Split(cmd+" badArg", " ")
   239  					dCmd = exec.Command(dockerBinary, args...)
   240  					stdout, stderr, ec, err = runCommandWithStdoutStderr(dCmd)
   241  				}
   242  
   243  				if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
   244  					c.Fatalf("Bad output from %q\nstdout:%q\nstderr:%q\nec:%d\nerr:%q", args, stdout, stderr, ec, err)
   245  				}
   246  				// Should have just short usage
   247  				if !strings.Contains(stderr, "\nUsage:\t") {
   248  					c.Fatalf("Missing short usage on %q\nstderr:%q", args, stderr)
   249  				}
   250  				// But shouldn't have full usage
   251  				if strings.Contains(stderr, "--help=false") {
   252  					c.Fatalf("Should not have full usage on %q\nstderr:%q", args, stderr)
   253  				}
   254  				if strings.HasSuffix(stderr, "\n\n") {
   255  					c.Fatalf("Should not have a blank line on %q\nstderr:%q", args, stderr)
   256  				}
   257  			}
   258  
   259  		}
   260  
   261  		// Number of commands for standard release and experimental release
   262  		standard := 40
   263  		experimental := 1
   264  		expected := standard + experimental
   265  		if isLocalDaemon {
   266  			expected++ // for the daemon command
   267  		}
   268  		if len(cmds) > expected {
   269  			c.Fatalf("Wrong # of cmds(%d), it should be: %d\nThe list:\n%q",
   270  				len(cmds), expected, cmds)
   271  		}
   272  	}
   273  
   274  }
   275  
   276  func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
   277  	testRequires(c, DaemonIsLinux)
   278  	// Test to make sure the exit code and output (stdout vs stderr) of
   279  	// various good and bad cases are what we expect
   280  
   281  	// docker : stdout=all, stderr=empty, rc=0
   282  	cmd := exec.Command(dockerBinary)
   283  	stdout, stderr, ec, err := runCommandWithStdoutStderr(cmd)
   284  	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
   285  		c.Fatalf("Bad results from 'docker'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
   286  	}
   287  	// Be really pick
   288  	if strings.HasSuffix(stdout, "\n\n") {
   289  		c.Fatalf("Should not have a blank line at the end of 'docker'\n%s", stdout)
   290  	}
   291  
   292  	// docker help: stdout=all, stderr=empty, rc=0
   293  	cmd = exec.Command(dockerBinary, "help")
   294  	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
   295  	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
   296  		c.Fatalf("Bad results from 'docker help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
   297  	}
   298  	// Be really pick
   299  	if strings.HasSuffix(stdout, "\n\n") {
   300  		c.Fatalf("Should not have a blank line at the end of 'docker help'\n%s", stdout)
   301  	}
   302  
   303  	// docker --help: stdout=all, stderr=empty, rc=0
   304  	cmd = exec.Command(dockerBinary, "--help")
   305  	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
   306  	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
   307  		c.Fatalf("Bad results from 'docker --help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
   308  	}
   309  	// Be really pick
   310  	if strings.HasSuffix(stdout, "\n\n") {
   311  		c.Fatalf("Should not have a blank line at the end of 'docker --help'\n%s", stdout)
   312  	}
   313  
   314  	// docker inspect busybox: stdout=all, stderr=empty, rc=0
   315  	// Just making sure stderr is empty on valid cmd
   316  	cmd = exec.Command(dockerBinary, "inspect", "busybox")
   317  	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
   318  	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
   319  		c.Fatalf("Bad results from 'docker inspect busybox'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
   320  	}
   321  	// Be really pick
   322  	if strings.HasSuffix(stdout, "\n\n") {
   323  		c.Fatalf("Should not have a blank line at the end of 'docker inspect busyBox'\n%s", stdout)
   324  	}
   325  
   326  	// docker rm: stdout=empty, stderr=all, rc!=0
   327  	// testing the min arg error msg
   328  	cmd = exec.Command(dockerBinary, "rm")
   329  	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
   330  	if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
   331  		c.Fatalf("Bad results from 'docker rm'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
   332  	}
   333  	// Should not contain full help text but should contain info about
   334  	// # of args and Usage line
   335  	if !strings.Contains(stderr, "requires a minimum") {
   336  		c.Fatalf("Missing # of args text from 'docker rm'\nstderr:%s", stderr)
   337  	}
   338  
   339  	// docker rm NoSuchContainer: stdout=empty, stderr=all, rc=0
   340  	// testing to make sure no blank line on error
   341  	cmd = exec.Command(dockerBinary, "rm", "NoSuchContainer")
   342  	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
   343  	if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
   344  		c.Fatalf("Bad results from 'docker rm NoSuchContainer'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
   345  	}
   346  	// Be really picky
   347  	if strings.HasSuffix(stderr, "\n\n") {
   348  		c.Fatalf("Should not have a blank line at the end of 'docker rm'\n%s", stderr)
   349  	}
   350  
   351  	// docker BadCmd: stdout=empty, stderr=all, rc=0
   352  	cmd = exec.Command(dockerBinary, "BadCmd")
   353  	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
   354  	if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
   355  		c.Fatalf("Bad results from 'docker BadCmd'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
   356  	}
   357  	if stderr != "docker: 'BadCmd' is not a docker command.\nSee 'docker --help'.\n" {
   358  		c.Fatalf("Unexcepted output for 'docker badCmd'\nstderr:%s", stderr)
   359  	}
   360  	// Be really picky
   361  	if strings.HasSuffix(stderr, "\n\n") {
   362  		c.Fatalf("Should not have a blank line at the end of 'docker rm'\n%s", stderr)
   363  	}
   364  
   365  }