github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/cmd/juju/main_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "bytes" 8 "flag" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "strings" 15 stdtesting "testing" 16 17 "launchpad.net/gnuflag" 18 gc "launchpad.net/gocheck" 19 20 "launchpad.net/juju-core/cmd" 21 "launchpad.net/juju-core/juju/osenv" 22 _ "launchpad.net/juju-core/provider/dummy" 23 "launchpad.net/juju-core/testing" 24 "launchpad.net/juju-core/version" 25 ) 26 27 func TestPackage(t *stdtesting.T) { 28 testing.MgoTestPackage(t) 29 } 30 31 type MainSuite struct { 32 testing.FakeHomeSuite 33 } 34 35 var _ = gc.Suite(&MainSuite{}) 36 37 var ( 38 flagRunMain = flag.Bool("run-main", false, "Run the application's main function for recursive testing") 39 ) 40 41 // Reentrancy point for testing (something as close as possible to) the juju 42 // tool itself. 43 func TestRunMain(t *stdtesting.T) { 44 if *flagRunMain { 45 Main(flag.Args()) 46 } 47 } 48 49 func badrun(c *gc.C, exit int, args ...string) string { 50 localArgs := append([]string{"-test.run", "TestRunMain", "-run-main", "--", "juju"}, args...) 51 ps := exec.Command(os.Args[0], localArgs...) 52 ps.Env = append(os.Environ(), osenv.JujuHomeEnvKey+"="+osenv.JujuHome()) 53 output, err := ps.CombinedOutput() 54 c.Logf("command output: %q", output) 55 if exit != 0 { 56 c.Assert(err, gc.ErrorMatches, fmt.Sprintf("exit status %d", exit)) 57 } 58 return string(output) 59 } 60 61 func helpText(command cmd.Command, name string) string { 62 buff := &bytes.Buffer{} 63 info := command.Info() 64 info.Name = name 65 f := gnuflag.NewFlagSet(info.Name, gnuflag.ContinueOnError) 66 command.SetFlags(f) 67 buff.Write(info.Help(f)) 68 return buff.String() 69 } 70 71 func deployHelpText() string { 72 return helpText(&DeployCommand{}, "juju deploy") 73 } 74 75 func syncToolsHelpText() string { 76 return helpText(&SyncToolsCommand{}, "juju sync-tools") 77 } 78 79 func (s *MainSuite) TestRunMain(c *gc.C) { 80 defer testing.MakeSampleHome(c).Restore() 81 // The test array structure needs to be inline here as some of the 82 // expected values below use deployHelpText(). This constructs the deploy 83 // command and runs gets the help for it. When the deploy command is 84 // setting the flags (which is needed for the help text) it is accessing 85 // osenv.JujuHome(), which panics if SetJujuHome has not been called. 86 // The FakeHome from testing does this. 87 for i, t := range []struct { 88 summary string 89 args []string 90 code int 91 out string 92 }{{ 93 summary: "no params shows help", 94 args: []string{}, 95 code: 0, 96 out: strings.TrimLeft(helpBasics, "\n"), 97 }, { 98 summary: "juju help is the same as juju", 99 args: []string{"help"}, 100 code: 0, 101 out: strings.TrimLeft(helpBasics, "\n"), 102 }, { 103 summary: "juju --help works too", 104 args: []string{"--help"}, 105 code: 0, 106 out: strings.TrimLeft(helpBasics, "\n"), 107 }, { 108 summary: "juju help basics is the same as juju", 109 args: []string{"help", "basics"}, 110 code: 0, 111 out: strings.TrimLeft(helpBasics, "\n"), 112 }, { 113 summary: "juju help foo doesn't exist", 114 args: []string{"help", "foo"}, 115 code: 1, 116 out: "ERROR unknown command or topic for foo\n", 117 }, { 118 summary: "juju help deploy shows the default help without global options", 119 args: []string{"help", "deploy"}, 120 code: 0, 121 out: deployHelpText(), 122 }, { 123 summary: "juju --help deploy shows the same help as 'help deploy'", 124 args: []string{"--help", "deploy"}, 125 code: 0, 126 out: deployHelpText(), 127 }, { 128 summary: "juju deploy --help shows the same help as 'help deploy'", 129 args: []string{"deploy", "--help"}, 130 code: 0, 131 out: deployHelpText(), 132 }, { 133 summary: "unknown command", 134 args: []string{"discombobulate"}, 135 code: 1, 136 out: "ERROR unrecognized command: juju discombobulate\n", 137 }, { 138 summary: "unknown option before command", 139 args: []string{"--cheese", "bootstrap"}, 140 code: 2, 141 out: "error: flag provided but not defined: --cheese\n", 142 }, { 143 summary: "unknown option after command", 144 args: []string{"bootstrap", "--cheese"}, 145 code: 2, 146 out: "error: flag provided but not defined: --cheese\n", 147 }, { 148 summary: "known option, but specified before command", 149 args: []string{"--environment", "blah", "bootstrap"}, 150 code: 2, 151 out: "error: flag provided but not defined: --environment\n", 152 }, { 153 summary: "juju sync-tools registered properly", 154 args: []string{"sync-tools", "--help"}, 155 code: 0, 156 out: syncToolsHelpText(), 157 }, { 158 summary: "check version command registered properly", 159 args: []string{"version"}, 160 code: 0, 161 out: version.Current.String() + "\n", 162 }, 163 } { 164 c.Logf("test %d: %s", i, t.summary) 165 out := badrun(c, t.code, t.args...) 166 c.Assert(out, gc.Equals, t.out) 167 } 168 } 169 170 var brokenConfig = ` 171 ' 172 ` 173 174 // breakJuju writes a dummy environment with incomplete configuration. 175 // environMethod is called. 176 func breakJuju(c *gc.C, environMethod string) (msg string) { 177 path := osenv.JujuHomePath("environments.yaml") 178 err := ioutil.WriteFile(path, []byte(brokenConfig), 0666) 179 c.Assert(err, gc.IsNil) 180 return `cannot parse "[^"]*": YAML error.*` 181 } 182 183 func (s *MainSuite) TestActualRunJujuArgsBeforeCommand(c *gc.C) { 184 c.Skip("breaks test isolation: lp:1233601") 185 defer testing.MakeFakeHomeNoEnvironments(c, "one").Restore() 186 // Check global args work when specified before command 187 msg := breakJuju(c, "Bootstrap") 188 logpath := filepath.Join(c.MkDir(), "log") 189 out := badrun(c, 1, "--log-file", logpath, "bootstrap") 190 fullmsg := fmt.Sprintf(`(.|\n)*ERROR .*%s\n`, msg) 191 c.Assert(out, gc.Matches, fullmsg) 192 content, err := ioutil.ReadFile(logpath) 193 c.Assert(err, gc.IsNil) 194 c.Assert(string(content), gc.Matches, fullmsg) 195 } 196 197 func (s *MainSuite) TestActualRunJujuArgsAfterCommand(c *gc.C) { 198 c.Skip("breaks test isolation: lp:1233601") 199 defer testing.MakeFakeHomeNoEnvironments(c, "one").Restore() 200 // Check global args work when specified after command 201 msg := breakJuju(c, "Bootstrap") 202 logpath := filepath.Join(c.MkDir(), "log") 203 out := badrun(c, 1, "bootstrap", "--log-file", logpath) 204 fullmsg := fmt.Sprintf(`(.|\n)*ERROR .*%s\n`, msg) 205 c.Assert(out, gc.Matches, fullmsg) 206 content, err := ioutil.ReadFile(logpath) 207 c.Assert(err, gc.IsNil) 208 c.Assert(string(content), gc.Matches, fullmsg) 209 } 210 211 var commandNames = []string{ 212 "add-machine", 213 "add-relation", 214 "add-unit", 215 "api-endpoints", 216 "authorised-keys", 217 "bootstrap", 218 "debug-hooks", 219 "debug-log", 220 "deploy", 221 "destroy-environment", 222 "destroy-machine", 223 "destroy-relation", 224 "destroy-service", 225 "destroy-unit", 226 "env", // alias for switch 227 "expose", 228 "generate-config", // alias for init 229 "get", 230 "get-constraints", 231 "get-env", // alias for get-environment 232 "get-environment", 233 "help", 234 "help-tool", 235 "init", 236 "publish", 237 "remove-machine", // alias for destroy-machine 238 "remove-relation", // alias for destroy-relation 239 "remove-service", // alias for destroy-service 240 "remove-unit", // alias for destroy-unit 241 "resolved", 242 "run", 243 "scp", 244 "set", 245 "set-constraints", 246 "set-env", // alias for set-environment 247 "set-environment", 248 "ssh", 249 "stat", // alias for status 250 "status", 251 "switch", 252 "sync-tools", 253 "terminate-machine", // alias for destroy-machine 254 "unexpose", 255 "unset", 256 "upgrade-charm", 257 "upgrade-juju", 258 "version", 259 } 260 261 func (s *MainSuite) TestHelpCommands(c *gc.C) { 262 // Check that we have correctly registered all the commands 263 // by checking the help output. 264 defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir())) 265 out := badrun(c, 0, "help", "commands") 266 lines := strings.Split(out, "\n") 267 var names []string 268 for _, line := range lines { 269 f := strings.Fields(line) 270 if len(f) == 0 { 271 continue 272 } 273 names = append(names, f[0]) 274 } 275 // The names should be output in alphabetical order, so don't sort. 276 c.Assert(names, gc.DeepEquals, commandNames) 277 } 278 279 var topicNames = []string{ 280 "azure-provider", 281 "basics", 282 "commands", 283 "constraints", 284 "ec2-provider", 285 "global-options", 286 "glossary", 287 "hpcloud-provider", 288 "local-provider", 289 "logging", 290 "openstack-provider", 291 "plugins", 292 "topics", 293 } 294 295 func (s *MainSuite) TestHelpTopics(c *gc.C) { 296 // Check that we have correctly registered all the topics 297 // by checking the help output. 298 defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir())) 299 out := badrun(c, 0, "help", "topics") 300 lines := strings.Split(out, "\n") 301 var names []string 302 for _, line := range lines { 303 f := strings.Fields(line) 304 if len(f) == 0 { 305 continue 306 } 307 names = append(names, f[0]) 308 } 309 // The names should be output in alphabetical order, so don't sort. 310 c.Assert(names, gc.DeepEquals, topicNames) 311 } 312 313 var globalFlags = []string{ 314 "--debug .*", 315 "--description .*", 316 "-h, --help .*", 317 "--log-file .*", 318 "--logging-config .*", 319 "--show-log .*", 320 "-v, --verbose .*", 321 } 322 323 func (s *MainSuite) TestHelpGlobalOptions(c *gc.C) { 324 // Check that we have correctly registered all the topics 325 // by checking the help output. 326 defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir())) 327 out := badrun(c, 0, "help", "global-options") 328 c.Assert(out, gc.Matches, `Global Options 329 330 These options may be used with any command, and may appear in front of any 331 command\.(.|\n)*`) 332 lines := strings.Split(out, "\n") 333 var flags []string 334 for _, line := range lines { 335 f := strings.Fields(line) 336 if len(f) == 0 || line[0] != '-' { 337 continue 338 } 339 flags = append(flags, line) 340 } 341 c.Assert(len(flags), gc.Equals, len(globalFlags)) 342 for i, line := range flags { 343 c.Assert(line, gc.Matches, globalFlags[i]) 344 } 345 }