You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

410 lines
9.2 KiB

  1. package cli
  2. import (
  3. "bytes"
  4. "flag"
  5. "fmt"
  6. "runtime"
  7. "strings"
  8. "testing"
  9. )
  10. func Test_ShowAppHelp_NoAuthor(t *testing.T) {
  11. output := new(bytes.Buffer)
  12. app := NewApp()
  13. app.Writer = output
  14. c := NewContext(app, nil, nil)
  15. ShowAppHelp(c)
  16. if bytes.Index(output.Bytes(), []byte("AUTHOR(S):")) != -1 {
  17. t.Errorf("expected\n%snot to include %s", output.String(), "AUTHOR(S):")
  18. }
  19. }
  20. func Test_ShowAppHelp_NoVersion(t *testing.T) {
  21. output := new(bytes.Buffer)
  22. app := NewApp()
  23. app.Writer = output
  24. app.Version = ""
  25. c := NewContext(app, nil, nil)
  26. ShowAppHelp(c)
  27. if bytes.Index(output.Bytes(), []byte("VERSION:")) != -1 {
  28. t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:")
  29. }
  30. }
  31. func Test_ShowAppHelp_HideVersion(t *testing.T) {
  32. output := new(bytes.Buffer)
  33. app := NewApp()
  34. app.Writer = output
  35. app.HideVersion = true
  36. c := NewContext(app, nil, nil)
  37. ShowAppHelp(c)
  38. if bytes.Index(output.Bytes(), []byte("VERSION:")) != -1 {
  39. t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:")
  40. }
  41. }
  42. func Test_Help_Custom_Flags(t *testing.T) {
  43. oldFlag := HelpFlag
  44. defer func() {
  45. HelpFlag = oldFlag
  46. }()
  47. HelpFlag = BoolFlag{
  48. Name: "help, x",
  49. Usage: "show help",
  50. }
  51. app := App{
  52. Flags: []Flag{
  53. BoolFlag{Name: "foo, h"},
  54. },
  55. Action: func(ctx *Context) error {
  56. if ctx.Bool("h") != true {
  57. t.Errorf("custom help flag not set")
  58. }
  59. return nil
  60. },
  61. }
  62. output := new(bytes.Buffer)
  63. app.Writer = output
  64. app.Run([]string{"test", "-h"})
  65. if output.Len() > 0 {
  66. t.Errorf("unexpected output: %s", output.String())
  67. }
  68. }
  69. func Test_Version_Custom_Flags(t *testing.T) {
  70. oldFlag := VersionFlag
  71. defer func() {
  72. VersionFlag = oldFlag
  73. }()
  74. VersionFlag = BoolFlag{
  75. Name: "version, V",
  76. Usage: "show version",
  77. }
  78. app := App{
  79. Flags: []Flag{
  80. BoolFlag{Name: "foo, v"},
  81. },
  82. Action: func(ctx *Context) error {
  83. if ctx.Bool("v") != true {
  84. t.Errorf("custom version flag not set")
  85. }
  86. return nil
  87. },
  88. }
  89. output := new(bytes.Buffer)
  90. app.Writer = output
  91. app.Run([]string{"test", "-v"})
  92. if output.Len() > 0 {
  93. t.Errorf("unexpected output: %s", output.String())
  94. }
  95. }
  96. func Test_helpCommand_Action_ErrorIfNoTopic(t *testing.T) {
  97. app := NewApp()
  98. set := flag.NewFlagSet("test", 0)
  99. set.Parse([]string{"foo"})
  100. c := NewContext(app, set, nil)
  101. err := helpCommand.Action.(func(*Context) error)(c)
  102. if err == nil {
  103. t.Fatalf("expected error from helpCommand.Action(), but got nil")
  104. }
  105. exitErr, ok := err.(*ExitError)
  106. if !ok {
  107. t.Fatalf("expected ExitError from helpCommand.Action(), but instead got: %v", err.Error())
  108. }
  109. if !strings.HasPrefix(exitErr.Error(), "No help topic for") {
  110. t.Fatalf("expected an unknown help topic error, but got: %v", exitErr.Error())
  111. }
  112. if exitErr.exitCode != 3 {
  113. t.Fatalf("expected exit value = 3, got %d instead", exitErr.exitCode)
  114. }
  115. }
  116. func Test_helpCommand_InHelpOutput(t *testing.T) {
  117. app := NewApp()
  118. output := &bytes.Buffer{}
  119. app.Writer = output
  120. app.Run([]string{"test", "--help"})
  121. s := output.String()
  122. if strings.Contains(s, "\nCOMMANDS:\nGLOBAL OPTIONS:\n") {
  123. t.Fatalf("empty COMMANDS section detected: %q", s)
  124. }
  125. if !strings.Contains(s, "help, h") {
  126. t.Fatalf("missing \"help, h\": %q", s)
  127. }
  128. }
  129. func Test_helpSubcommand_Action_ErrorIfNoTopic(t *testing.T) {
  130. app := NewApp()
  131. set := flag.NewFlagSet("test", 0)
  132. set.Parse([]string{"foo"})
  133. c := NewContext(app, set, nil)
  134. err := helpSubcommand.Action.(func(*Context) error)(c)
  135. if err == nil {
  136. t.Fatalf("expected error from helpCommand.Action(), but got nil")
  137. }
  138. exitErr, ok := err.(*ExitError)
  139. if !ok {
  140. t.Fatalf("expected ExitError from helpCommand.Action(), but instead got: %v", err.Error())
  141. }
  142. if !strings.HasPrefix(exitErr.Error(), "No help topic for") {
  143. t.Fatalf("expected an unknown help topic error, but got: %v", exitErr.Error())
  144. }
  145. if exitErr.exitCode != 3 {
  146. t.Fatalf("expected exit value = 3, got %d instead", exitErr.exitCode)
  147. }
  148. }
  149. func TestShowAppHelp_CommandAliases(t *testing.T) {
  150. app := &App{
  151. Commands: []Command{
  152. {
  153. Name: "frobbly",
  154. Aliases: []string{"fr", "frob"},
  155. Action: func(ctx *Context) error {
  156. return nil
  157. },
  158. },
  159. },
  160. }
  161. output := &bytes.Buffer{}
  162. app.Writer = output
  163. app.Run([]string{"foo", "--help"})
  164. if !strings.Contains(output.String(), "frobbly, fr, frob") {
  165. t.Errorf("expected output to include all command aliases; got: %q", output.String())
  166. }
  167. }
  168. func TestShowCommandHelp_CommandAliases(t *testing.T) {
  169. app := &App{
  170. Commands: []Command{
  171. {
  172. Name: "frobbly",
  173. Aliases: []string{"fr", "frob", "bork"},
  174. Action: func(ctx *Context) error {
  175. return nil
  176. },
  177. },
  178. },
  179. }
  180. output := &bytes.Buffer{}
  181. app.Writer = output
  182. app.Run([]string{"foo", "help", "fr"})
  183. if !strings.Contains(output.String(), "frobbly") {
  184. t.Errorf("expected output to include command name; got: %q", output.String())
  185. }
  186. if strings.Contains(output.String(), "bork") {
  187. t.Errorf("expected output to exclude command aliases; got: %q", output.String())
  188. }
  189. }
  190. func TestShowSubcommandHelp_CommandAliases(t *testing.T) {
  191. app := &App{
  192. Commands: []Command{
  193. {
  194. Name: "frobbly",
  195. Aliases: []string{"fr", "frob", "bork"},
  196. Action: func(ctx *Context) error {
  197. return nil
  198. },
  199. },
  200. },
  201. }
  202. output := &bytes.Buffer{}
  203. app.Writer = output
  204. app.Run([]string{"foo", "help"})
  205. if !strings.Contains(output.String(), "frobbly, fr, frob, bork") {
  206. t.Errorf("expected output to include all command aliases; got: %q", output.String())
  207. }
  208. }
  209. func TestShowCommandHelp_Customtemplate(t *testing.T) {
  210. app := &App{
  211. Commands: []Command{
  212. {
  213. Name: "frobbly",
  214. Action: func(ctx *Context) error {
  215. return nil
  216. },
  217. HelpName: "foo frobbly",
  218. CustomHelpTemplate: `NAME:
  219. {{.HelpName}} - {{.Usage}}
  220. USAGE:
  221. {{.HelpName}} [FLAGS] TARGET [TARGET ...]
  222. FLAGS:
  223. {{range .VisibleFlags}}{{.}}
  224. {{end}}
  225. EXAMPLES:
  226. 1. Frobbly runs with this param locally.
  227. $ {{.HelpName}} wobbly
  228. `,
  229. },
  230. },
  231. }
  232. output := &bytes.Buffer{}
  233. app.Writer = output
  234. app.Run([]string{"foo", "help", "frobbly"})
  235. if strings.Contains(output.String(), "2. Frobbly runs without this param locally.") {
  236. t.Errorf("expected output to exclude \"2. Frobbly runs without this param locally.\"; got: %q", output.String())
  237. }
  238. if !strings.Contains(output.String(), "1. Frobbly runs with this param locally.") {
  239. t.Errorf("expected output to include \"1. Frobbly runs with this param locally.\"; got: %q", output.String())
  240. }
  241. if !strings.Contains(output.String(), "$ foo frobbly wobbly") {
  242. t.Errorf("expected output to include \"$ foo frobbly wobbly\"; got: %q", output.String())
  243. }
  244. }
  245. func TestShowAppHelp_HiddenCommand(t *testing.T) {
  246. app := &App{
  247. Commands: []Command{
  248. {
  249. Name: "frobbly",
  250. Action: func(ctx *Context) error {
  251. return nil
  252. },
  253. },
  254. {
  255. Name: "secretfrob",
  256. Hidden: true,
  257. Action: func(ctx *Context) error {
  258. return nil
  259. },
  260. },
  261. },
  262. }
  263. output := &bytes.Buffer{}
  264. app.Writer = output
  265. app.Run([]string{"app", "--help"})
  266. if strings.Contains(output.String(), "secretfrob") {
  267. t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String())
  268. }
  269. if !strings.Contains(output.String(), "frobbly") {
  270. t.Errorf("expected output to include \"frobbly\"; got: %q", output.String())
  271. }
  272. }
  273. func TestShowAppHelp_CustomAppTemplate(t *testing.T) {
  274. app := &App{
  275. Commands: []Command{
  276. {
  277. Name: "frobbly",
  278. Action: func(ctx *Context) error {
  279. return nil
  280. },
  281. },
  282. {
  283. Name: "secretfrob",
  284. Hidden: true,
  285. Action: func(ctx *Context) error {
  286. return nil
  287. },
  288. },
  289. },
  290. ExtraInfo: func() map[string]string {
  291. platform := fmt.Sprintf("OS: %s | Arch: %s", runtime.GOOS, runtime.GOARCH)
  292. goruntime := fmt.Sprintf("Version: %s | CPUs: %d", runtime.Version(), runtime.NumCPU())
  293. return map[string]string{
  294. "PLATFORM": platform,
  295. "RUNTIME": goruntime,
  296. }
  297. },
  298. CustomAppHelpTemplate: `NAME:
  299. {{.Name}} - {{.Usage}}
  300. USAGE:
  301. {{.Name}} {{if .VisibleFlags}}[FLAGS] {{end}}COMMAND{{if .VisibleFlags}} [COMMAND FLAGS | -h]{{end}} [ARGUMENTS...]
  302. COMMANDS:
  303. {{range .VisibleCommands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
  304. {{end}}{{if .VisibleFlags}}
  305. GLOBAL FLAGS:
  306. {{range .VisibleFlags}}{{.}}
  307. {{end}}{{end}}
  308. VERSION:
  309. 2.0.0
  310. {{"\n"}}{{range $key, $value := ExtraInfo}}
  311. {{$key}}:
  312. {{$value}}
  313. {{end}}`,
  314. }
  315. output := &bytes.Buffer{}
  316. app.Writer = output
  317. app.Run([]string{"app", "--help"})
  318. if strings.Contains(output.String(), "secretfrob") {
  319. t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String())
  320. }
  321. if !strings.Contains(output.String(), "frobbly") {
  322. t.Errorf("expected output to include \"frobbly\"; got: %q", output.String())
  323. }
  324. if !strings.Contains(output.String(), "PLATFORM:") ||
  325. !strings.Contains(output.String(), "OS:") ||
  326. !strings.Contains(output.String(), "Arch:") {
  327. t.Errorf("expected output to include \"PLATFORM:, OS: and Arch:\"; got: %q", output.String())
  328. }
  329. if !strings.Contains(output.String(), "RUNTIME:") ||
  330. !strings.Contains(output.String(), "Version:") ||
  331. !strings.Contains(output.String(), "CPUs:") {
  332. t.Errorf("expected output to include \"RUNTIME:, Version: and CPUs:\"; got: %q", output.String())
  333. }
  334. if !strings.Contains(output.String(), "VERSION:") ||
  335. !strings.Contains(output.String(), "2.0.0") {
  336. t.Errorf("expected output to include \"VERSION:, 2.0.0\"; got: %q", output.String())
  337. }
  338. }