launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/cmd/supercommand_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package cmd_test
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"strings"
    10  
    11  	"launchpad.net/gnuflag"
    12  	gc "launchpad.net/gocheck"
    13  
    14  	"launchpad.net/juju-core/cmd"
    15  	"launchpad.net/juju-core/testing"
    16  	"launchpad.net/juju-core/testing/testbase"
    17  )
    18  
    19  func initDefenestrate(args []string) (*cmd.SuperCommand, *TestCommand, error) {
    20  	jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest"})
    21  	tc := &TestCommand{Name: "defenestrate"}
    22  	jc.Register(tc)
    23  	return jc, tc, testing.InitCommand(jc, args)
    24  }
    25  
    26  type SuperCommandSuite struct {
    27  	testbase.LoggingSuite
    28  }
    29  
    30  var _ = gc.Suite(&SuperCommandSuite{})
    31  
    32  const helpText = "\n    help\\s+- show help on a command or other topic"
    33  const helpCommandsText = "commands:" + helpText
    34  
    35  func (s *SuperCommandSuite) TestDispatch(c *gc.C) {
    36  	jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest"})
    37  	info := jc.Info()
    38  	c.Assert(info.Name, gc.Equals, "jujutest")
    39  	c.Assert(info.Args, gc.Equals, "<command> ...")
    40  	c.Assert(info.Doc, gc.Matches, helpCommandsText)
    41  
    42  	jc, _, err := initDefenestrate([]string{"discombobulate"})
    43  	c.Assert(err, gc.ErrorMatches, "unrecognized command: jujutest discombobulate")
    44  	info = jc.Info()
    45  	c.Assert(info.Name, gc.Equals, "jujutest")
    46  	c.Assert(info.Args, gc.Equals, "<command> ...")
    47  	c.Assert(info.Doc, gc.Matches, "commands:\n    defenestrate - defenestrate the juju"+helpText)
    48  
    49  	jc, tc, err := initDefenestrate([]string{"defenestrate"})
    50  	c.Assert(err, gc.IsNil)
    51  	c.Assert(tc.Option, gc.Equals, "")
    52  	info = jc.Info()
    53  	c.Assert(info.Name, gc.Equals, "jujutest defenestrate")
    54  	c.Assert(info.Args, gc.Equals, "<something>")
    55  	c.Assert(info.Doc, gc.Equals, "defenestrate-doc")
    56  
    57  	_, tc, err = initDefenestrate([]string{"defenestrate", "--option", "firmly"})
    58  	c.Assert(err, gc.IsNil)
    59  	c.Assert(tc.Option, gc.Equals, "firmly")
    60  
    61  	_, tc, err = initDefenestrate([]string{"defenestrate", "gibberish"})
    62  	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["gibberish"\]`)
    63  
    64  	// --description must be used on it's own.
    65  	_, _, err = initDefenestrate([]string{"--description", "defenestrate"})
    66  	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["defenestrate"\]`)
    67  }
    68  
    69  func (s *SuperCommandSuite) TestRegister(c *gc.C) {
    70  	jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest"})
    71  	jc.Register(&TestCommand{Name: "flip"})
    72  	jc.Register(&TestCommand{Name: "flap"})
    73  	badCall := func() { jc.Register(&TestCommand{Name: "flap"}) }
    74  	c.Assert(badCall, gc.PanicMatches, "command already registered: flap")
    75  }
    76  
    77  func (s *SuperCommandSuite) TestRegisterAlias(c *gc.C) {
    78  	jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest"})
    79  	jc.Register(&TestCommand{Name: "flip", Aliases: []string{"flap", "flop"}})
    80  
    81  	info := jc.Info()
    82  	c.Assert(info.Doc, gc.Equals, `commands:
    83      flap - alias for flip
    84      flip - flip the juju
    85      flop - alias for flip
    86      help - show help on a command or other topic`)
    87  }
    88  
    89  var commandsDoc = `commands:
    90      flapbabble - flapbabble the juju
    91      flip       - flip the juju`
    92  
    93  func (s *SuperCommandSuite) TestInfo(c *gc.C) {
    94  	jc := cmd.NewSuperCommand(cmd.SuperCommandParams{
    95  		Name:    "jujutest",
    96  		Purpose: "to be purposeful",
    97  		Doc:     "doc\nblah\ndoc",
    98  	})
    99  	info := jc.Info()
   100  	c.Assert(info.Name, gc.Equals, "jujutest")
   101  	c.Assert(info.Purpose, gc.Equals, "to be purposeful")
   102  	// info doc starts with the jc.Doc and ends with the help command
   103  	c.Assert(info.Doc, gc.Matches, jc.Doc+"(.|\n)*")
   104  	c.Assert(info.Doc, gc.Matches, "(.|\n)*"+helpCommandsText)
   105  
   106  	jc.Register(&TestCommand{Name: "flip"})
   107  	jc.Register(&TestCommand{Name: "flapbabble"})
   108  	info = jc.Info()
   109  	c.Assert(info.Doc, gc.Matches, jc.Doc+"\n\n"+commandsDoc+helpText)
   110  
   111  	jc.Doc = ""
   112  	info = jc.Info()
   113  	c.Assert(info.Doc, gc.Matches, commandsDoc+helpText)
   114  }
   115  
   116  type testVersionFlagCommand struct {
   117  	cmd.CommandBase
   118  	version string
   119  }
   120  
   121  func (c *testVersionFlagCommand) Info() *cmd.Info {
   122  	return &cmd.Info{Name: "test"}
   123  }
   124  
   125  func (c *testVersionFlagCommand) SetFlags(f *gnuflag.FlagSet) {
   126  	f.StringVar(&c.version, "version", "", "")
   127  }
   128  
   129  func (c *testVersionFlagCommand) Run(_ *cmd.Context) error {
   130  	return nil
   131  }
   132  
   133  func (s *SuperCommandSuite) TestVersionFlag(c *gc.C) {
   134  	jc := cmd.NewSuperCommand(cmd.SuperCommandParams{
   135  		Name:    "jujutest",
   136  		Purpose: "to be purposeful",
   137  		Doc:     "doc\nblah\ndoc",
   138  	})
   139  	testVersionFlagCommand := &testVersionFlagCommand{}
   140  	jc.Register(&cmd.VersionCommand{})
   141  	jc.Register(testVersionFlagCommand)
   142  
   143  	var stdout, stderr bytes.Buffer
   144  	ctx := &cmd.Context{
   145  		Stdout: &stdout,
   146  		Stderr: &stderr,
   147  	}
   148  
   149  	// baseline: juju version
   150  	code := cmd.Main(jc, ctx, []string{"version"})
   151  	c.Check(code, gc.Equals, 0)
   152  	baselineStderr := stderr.String()
   153  	baselineStdout := stdout.String()
   154  	stderr.Reset()
   155  	stdout.Reset()
   156  
   157  	// juju --version output should match that of juju version.
   158  	code = cmd.Main(jc, ctx, []string{"--version"})
   159  	c.Check(code, gc.Equals, 0)
   160  	c.Assert(stderr.String(), gc.Equals, baselineStderr)
   161  	c.Assert(stdout.String(), gc.Equals, baselineStdout)
   162  	stderr.Reset()
   163  	stdout.Reset()
   164  
   165  	// juju test --version should update testVersionFlagCommand.version,
   166  	// and there should be no output. The --version flag on the 'test'
   167  	// subcommand has a different type to the "juju --version" flag.
   168  	code = cmd.Main(jc, ctx, []string{"test", "--version=abc.123"})
   169  	c.Check(code, gc.Equals, 0)
   170  	c.Assert(stderr.String(), gc.Equals, "")
   171  	c.Assert(stdout.String(), gc.Equals, "")
   172  	c.Assert(testVersionFlagCommand.version, gc.Equals, "abc.123")
   173  }
   174  
   175  func (s *SuperCommandSuite) TestLogging(c *gc.C) {
   176  	jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest", Log: &cmd.Log{}})
   177  	jc.Register(&TestCommand{Name: "blah"})
   178  	ctx := testing.Context(c)
   179  	code := cmd.Main(jc, ctx, []string{"blah", "--option", "error", "--debug"})
   180  	c.Assert(code, gc.Equals, 1)
   181  	c.Assert(bufferString(ctx.Stderr), gc.Matches, `^.* ERROR .* BAM!
   182  `)
   183  }
   184  
   185  func (s *SuperCommandSuite) TestDescription(c *gc.C) {
   186  	jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest", Purpose: "blow up the death star"})
   187  	jc.Register(&TestCommand{Name: "blah"})
   188  	ctx := testing.Context(c)
   189  	code := cmd.Main(jc, ctx, []string{"blah", "--description"})
   190  	c.Assert(code, gc.Equals, 0)
   191  	c.Assert(bufferString(ctx.Stdout), gc.Equals, "blow up the death star\n")
   192  }
   193  
   194  func (s *SuperCommandSuite) TestHelp(c *gc.C) {
   195  	jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest"})
   196  	jc.Register(&TestCommand{Name: "blah"})
   197  	ctx := testing.Context(c)
   198  	code := cmd.Main(jc, ctx, []string{"blah", "--help"})
   199  	c.Assert(code, gc.Equals, 0)
   200  	stripped := strings.Replace(bufferString(ctx.Stdout), "\n", "", -1)
   201  	c.Assert(stripped, gc.Matches, ".*usage: jujutest blah.*blah-doc.*")
   202  }
   203  
   204  func (s *SuperCommandSuite) TestHelpWithPrefix(c *gc.C) {
   205  	jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest", UsagePrefix: "juju"})
   206  	jc.Register(&TestCommand{Name: "blah"})
   207  	ctx := testing.Context(c)
   208  	code := cmd.Main(jc, ctx, []string{"blah", "--help"})
   209  	c.Assert(code, gc.Equals, 0)
   210  	stripped := strings.Replace(bufferString(ctx.Stdout), "\n", "", -1)
   211  	c.Assert(stripped, gc.Matches, ".*usage: juju jujutest blah.*blah-doc.*")
   212  }
   213  
   214  func NewSuperWithCallback(callback func(*cmd.Context, string, []string) error) cmd.Command {
   215  	return cmd.NewSuperCommand(cmd.SuperCommandParams{
   216  		Name:            "jujutest",
   217  		Log:             &cmd.Log{},
   218  		MissingCallback: callback,
   219  	})
   220  }
   221  
   222  func (s *SuperCommandSuite) TestMissingCallback(c *gc.C) {
   223  	var calledName string
   224  	var calledArgs []string
   225  
   226  	callback := func(ctx *cmd.Context, subcommand string, args []string) error {
   227  		calledName = subcommand
   228  		calledArgs = args
   229  		return nil
   230  	}
   231  
   232  	code := cmd.Main(
   233  		NewSuperWithCallback(callback),
   234  		testing.Context(c),
   235  		[]string{"foo", "bar", "baz", "--debug"})
   236  	c.Assert(code, gc.Equals, 0)
   237  	c.Assert(calledName, gc.Equals, "foo")
   238  	c.Assert(calledArgs, gc.DeepEquals, []string{"bar", "baz", "--debug"})
   239  }
   240  
   241  func (s *SuperCommandSuite) TestMissingCallbackErrors(c *gc.C) {
   242  	callback := func(ctx *cmd.Context, subcommand string, args []string) error {
   243  		return fmt.Errorf("command not found %q", subcommand)
   244  	}
   245  
   246  	ctx := testing.Context(c)
   247  	code := cmd.Main(NewSuperWithCallback(callback), ctx, []string{"foo"})
   248  	c.Assert(code, gc.Equals, 1)
   249  	c.Assert(testing.Stdout(ctx), gc.Equals, "")
   250  	c.Assert(testing.Stderr(ctx), gc.Equals, "ERROR command not found \"foo\"\n")
   251  }
   252  
   253  func (s *SuperCommandSuite) TestMissingCallbackContextWiredIn(c *gc.C) {
   254  	callback := func(ctx *cmd.Context, subcommand string, args []string) error {
   255  		fmt.Fprintf(ctx.Stdout, "this is std out")
   256  		fmt.Fprintf(ctx.Stderr, "this is std err")
   257  		return nil
   258  	}
   259  
   260  	ctx := testing.Context(c)
   261  	code := cmd.Main(NewSuperWithCallback(callback), ctx, []string{"foo", "bar", "baz", "--debug"})
   262  	c.Assert(code, gc.Equals, 0)
   263  	c.Assert(testing.Stdout(ctx), gc.Equals, "this is std out")
   264  	c.Assert(testing.Stderr(ctx), gc.Equals, "this is std err")
   265  }