summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--example_test.go17
-rw-r--r--parse.go31
-rw-r--r--parse_test.go78
-rw-r--r--reflect.go12
-rw-r--r--reflect_test.go21
-rw-r--r--subcommand_test.go58
-rw-r--r--usage.go6
-rw-r--r--usage_test.go187
8 files changed, 338 insertions, 72 deletions
diff --git a/example_test.go b/example_test.go
index 5645156..26f24e7 100644
--- a/example_test.go
+++ b/example_test.go
@@ -95,6 +95,19 @@ func Example_mappings() {
// output: map[john:123 mary:456]
}
+// This example demonstrates arguments with keys and values separated by commas
+func Example_mappingsWithCommas() {
+ // The args you would pass in on the command line
+ os.Args = split("./example --userids john=123 mary=456")
+
+ var args struct {
+ UserIDs map[string]int
+ }
+ MustParse(&args)
+ fmt.Println(args.UserIDs)
+ // output: map[john:123 mary:456]
+}
+
// This eample demonstrates multiple value arguments that can be mixed with
// other arguments.
func Example_multipleMixed() {
@@ -130,6 +143,7 @@ func Example_helpText() {
// This is only necessary when running inside golang's runnable example harness
osExit = func(int) {}
+ stdout = os.Stdout
MustParse(&args)
@@ -162,6 +176,7 @@ func Example_helpPlaceholder() {
// This is only necessary when running inside golang's runnable example harness
osExit = func(int) {}
+ stdout = os.Stdout
MustParse(&args)
@@ -202,6 +217,7 @@ func Example_helpTextWithSubcommand() {
// This is only necessary when running inside golang's runnable example harness
osExit = func(int) {}
+ stdout = os.Stdout
MustParse(&args)
@@ -239,6 +255,7 @@ func Example_helpTextForSubcommand() {
// This is only necessary when running inside golang's runnable example harness
osExit = func(int) {}
+ stdout = os.Stdout
MustParse(&args)
diff --git a/parse.go b/parse.go
index d357d5c..94c0a89 100644
--- a/parse.go
+++ b/parse.go
@@ -13,9 +13,6 @@ import (
scalar "github.com/alexflint/go-scalar"
)
-// to enable monkey-patching during tests
-var osExit = os.Exit
-
// path represents a sequence of steps to find the output location for an
// argument or subcommand in the final destination struct
type path struct {
@@ -80,7 +77,7 @@ var ErrVersion = errors.New("version requested by user")
func MustParse(dest ...interface{}) *Parser {
p, err := NewParser(Config{}, dest...)
if err != nil {
- fmt.Println(err)
+ fmt.Fprintln(stdout, err)
osExit(-1)
return nil // just in case osExit was monkey-patched
}
@@ -88,10 +85,10 @@ func MustParse(dest ...interface{}) *Parser {
err = p.Parse(flags())
switch {
case err == ErrHelp:
- p.writeHelpForCommand(os.Stdout, p.lastCmd)
+ p.writeHelpForCommand(stdout, p.lastCmd)
osExit(0)
case err == ErrVersion:
- fmt.Println(p.version)
+ fmt.Fprintln(stdout, p.version)
osExit(0)
case err != nil:
p.failWithCommand(err.Error(), p.lastCmd)
@@ -688,15 +685,7 @@ func (p *Parser) val(dest path) reflect.Value {
v = v.Elem()
}
- next := v.FieldByIndex(field.Index)
- if !next.IsValid() {
- // it is appropriate to panic here because this can only happen due to
- // an internal bug in this library (since we construct the path ourselves
- // by reflecting on the same struct)
- panic(fmt.Errorf("error resolving path %v: %v has no field named %v",
- dest.fields, v.Type(), field))
- }
- v = next
+ v = v.FieldByIndex(field.Index)
}
return v
}
@@ -723,15 +712,3 @@ func findSubcommand(cmds []*command, name string) *command {
}
return nil
}
-
-// isZero returns true if v contains the zero value for its type
-func isZero(v reflect.Value) bool {
- t := v.Type()
- if t.Kind() == reflect.Slice || t.Kind() == reflect.Map {
- return v.IsNil()
- }
- if !t.Comparable() {
- return false
- }
- return v.Interface() == reflect.Zero(t).Interface()
-}
diff --git a/parse_test.go b/parse_test.go
index d03cbfd..b190e83 100644
--- a/parse_test.go
+++ b/parse_test.go
@@ -1,6 +1,7 @@
package arg
import (
+ "bytes"
"net"
"net/mail"
"os"
@@ -461,7 +462,7 @@ func TestMissingValueAtEnd(t *testing.T) {
assert.Error(t, err)
}
-func TestMissingValueInMIddle(t *testing.T) {
+func TestMissingValueInMiddle(t *testing.T) {
var args struct {
Foo string
Bar string
@@ -546,6 +547,14 @@ func TestNoMoreOptions(t *testing.T) {
assert.Equal(t, []string{"abc", "--foo", "xyz"}, args.Bar)
}
+func TestNoMoreOptionsBeforeHelp(t *testing.T) {
+ var args struct {
+ Foo int
+ }
+ err := parse("not_an_integer -- --help", &args)
+ assert.NotEqual(t, ErrHelp, err)
+}
+
func TestHelpFlag(t *testing.T) {
var args struct {
Foo string
@@ -1299,3 +1308,70 @@ func TestUnexportedFieldsSkipped(t *testing.T) {
_, err := NewParser(Config{}, &args)
require.NoError(t, err)
}
+
+func TestMustParseInvalidParser(t *testing.T) {
+ originalExit := osExit
+ originalStdout := stdout
+ defer func() {
+ osExit = originalExit
+ stdout = originalStdout
+ }()
+
+ var exitCode int
+ osExit = func(code int) { exitCode = code }
+ stdout = &bytes.Buffer{}
+
+ var args struct {
+ CannotParse struct{}
+ }
+ parser := MustParse(&args)
+ assert.Nil(t, parser)
+ assert.Equal(t, -1, exitCode)
+}
+
+func TestMustParsePrintsHelp(t *testing.T) {
+ originalExit := osExit
+ originalStdout := stdout
+ originalArgs := os.Args
+ defer func() {
+ osExit = originalExit
+ stdout = originalStdout
+ os.Args = originalArgs
+ }()
+
+ var exitCode *int
+ osExit = func(code int) { exitCode = &code }
+ os.Args = []string{"someprogram", "--help"}
+ stdout = &bytes.Buffer{}
+
+ var args struct{}
+ parser := MustParse(&args)
+ assert.NotNil(t, parser)
+ require.NotNil(t, exitCode)
+ assert.Equal(t, 0, *exitCode)
+}
+
+func TestMustParsePrintsVersion(t *testing.T) {
+ originalExit := osExit
+ originalStdout := stdout
+ originalArgs := os.Args
+ defer func() {
+ osExit = originalExit
+ stdout = originalStdout
+ os.Args = originalArgs
+ }()
+
+ var exitCode *int
+ osExit = func(code int) { exitCode = &code }
+ os.Args = []string{"someprogram", "--version"}
+
+ var b bytes.Buffer
+ stdout = &b
+
+ var args versioned
+ parser := MustParse(&args)
+ require.NotNil(t, parser)
+ require.NotNil(t, exitCode)
+ assert.Equal(t, 0, *exitCode)
+ assert.Equal(t, "example 3.2.1\n", b.String())
+}
diff --git a/reflect.go b/reflect.go
index 1806973..c719b52 100644
--- a/reflect.go
+++ b/reflect.go
@@ -94,3 +94,15 @@ func isExported(field string) bool {
r, _ := utf8.DecodeRuneInString(field) // returns RuneError for empty string or invalid UTF8
return unicode.IsLetter(r) && unicode.IsUpper(r)
}
+
+// isZero returns true if v contains the zero value for its type
+func isZero(v reflect.Value) bool {
+ t := v.Type()
+ if t.Kind() == reflect.Slice || t.Kind() == reflect.Map {
+ return v.IsNil()
+ }
+ if !t.Comparable() {
+ return false
+ }
+ return v.Interface() == reflect.Zero(t).Interface()
+}
diff --git a/reflect_test.go b/reflect_test.go
index 8d65fd9..10909b3 100644
--- a/reflect_test.go
+++ b/reflect_test.go
@@ -89,3 +89,24 @@ func TestCardinalityString(t *testing.T) {
assert.Equal(t, "unsupported", unsupported.String())
assert.Equal(t, "unknown(42)", cardinality(42).String())
}
+
+func TestIsZero(t *testing.T) {
+ var zero int
+ var notZero = 3
+ var nilSlice []int
+ var nonNilSlice = []int{1, 2, 3}
+ var nilMap map[string]string
+ var nonNilMap = map[string]string{"foo": "bar"}
+ var uncomparable = func() {}
+
+ assert.True(t, isZero(reflect.ValueOf(zero)))
+ assert.False(t, isZero(reflect.ValueOf(notZero)))
+
+ assert.True(t, isZero(reflect.ValueOf(nilSlice)))
+ assert.False(t, isZero(reflect.ValueOf(nonNilSlice)))
+
+ assert.True(t, isZero(reflect.ValueOf(nilMap)))
+ assert.False(t, isZero(reflect.ValueOf(nonNilMap)))
+
+ assert.False(t, isZero(reflect.ValueOf(uncomparable)))
+}
diff --git a/subcommand_test.go b/subcommand_test.go
index c34ab01..2c61dd3 100644
--- a/subcommand_test.go
+++ b/subcommand_test.go
@@ -1,6 +1,7 @@
package arg
import (
+ "reflect"
"testing"
"github.com/stretchr/testify/assert"
@@ -48,6 +49,17 @@ func TestMinimalSubcommand(t *testing.T) {
assert.Equal(t, []string{"list"}, p.SubcommandNames())
}
+func TestSubcommandNamesBeforeParsing(t *testing.T) {
+ type listCmd struct{}
+ var args struct {
+ List *listCmd `arg:"subcommand"`
+ }
+ p, err := NewParser(Config{}, &args)
+ require.NoError(t, err)
+ assert.Nil(t, p.Subcommand())
+ assert.Nil(t, p.SubcommandNames())
+}
+
func TestNoSuchSubcommand(t *testing.T) {
type listCmd struct {
}
@@ -179,6 +191,36 @@ func TestSubcommandsWithOptions(t *testing.T) {
}
}
+func TestSubcommandsWithEnvVars(t *testing.T) {
+ type getCmd struct {
+ Name string `arg:"env"`
+ }
+ type listCmd struct {
+ Limit int `arg:"env"`
+ }
+ type cmd struct {
+ Verbose bool
+ Get *getCmd `arg:"subcommand"`
+ List *listCmd `arg:"subcommand"`
+ }
+
+ {
+ var args cmd
+ setenv(t, "LIMIT", "123")
+ err := parse("list", &args)
+ require.NoError(t, err)
+ require.NotNil(t, args.List)
+ assert.Equal(t, 123, args.List.Limit)
+ }
+
+ {
+ var args cmd
+ setenv(t, "LIMIT", "not_an_integer")
+ err := parse("list", &args)
+ assert.Error(t, err)
+ }
+}
+
func TestNestedSubcommands(t *testing.T) {
type child struct{}
type parent struct {
@@ -353,3 +395,19 @@ func TestSubcommandsWithMultiplePositionals(t *testing.T) {
assert.Equal(t, 5, args.Limit)
}
}
+
+func TestValForNilStruct(t *testing.T) {
+ type subcmd struct{}
+ var cmd struct {
+ Sub *subcmd `arg:"subcommand"`
+ }
+
+ p, err := NewParser(Config{}, &cmd)
+ require.NoError(t, err)
+
+ typ := reflect.TypeOf(cmd)
+ subField, _ := typ.FieldByName("Sub")
+
+ v := p.val(path{fields: []reflect.StructField{subField, subField}})
+ assert.False(t, v.IsValid())
+}
diff --git a/usage.go b/usage.go
index 231476b..c121c45 100644
--- a/usage.go
+++ b/usage.go
@@ -11,7 +11,11 @@ import (
const colWidth = 25
// to allow monkey patching in tests
-var stderr = os.Stderr
+var (
+ stdout io.Writer = os.Stdout
+ stderr io.Writer = os.Stderr
+ osExit = os.Exit
+)
// Fail prints usage information to stderr and exits with non-zero status
func (p *Parser) Fail(msg string) {
diff --git a/usage_test.go b/usage_test.go
index 6dee402..1b6c475 100644
--- a/usage_test.go
+++ b/usage_test.go
@@ -33,9 +33,10 @@ func (n *NameDotName) MarshalText() (text []byte, err error) {
}
func TestWriteUsage(t *testing.T) {
- expectedUsage := "Usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--values VALUES] [--workers WORKERS] [--testenv TESTENV] [--file FILE] INPUT [OUTPUT [OUTPUT ...]]\n"
+ expectedUsage := "Usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--values VALUES] [--workers WORKERS] [--testenv TESTENV] [--file FILE] INPUT [OUTPUT [OUTPUT ...]]"
- expectedHelp := `Usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--values VALUES] [--workers WORKERS] [--testenv TESTENV] [--file FILE] INPUT [OUTPUT [OUTPUT ...]]
+ expectedHelp := `
+Usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--values VALUES] [--workers WORKERS] [--testenv TESTENV] [--file FILE] INPUT [OUTPUT [OUTPUT ...]]
Positional arguments:
INPUT
@@ -56,6 +57,7 @@ Options:
--file FILE, -f FILE File with mandatory extension [default: scratch.txt]
--help, -h display this help and exit
`
+
var args struct {
Input string `arg:"positional"`
Output []string `arg:"positional" help:"list of outputs"`
@@ -79,13 +81,13 @@ Options:
os.Args[0] = "example"
- var usage bytes.Buffer
- p.WriteUsage(&usage)
- assert.Equal(t, expectedUsage, usage.String())
-
var help bytes.Buffer
p.WriteHelp(&help)
- assert.Equal(t, expectedHelp, help.String())
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ var usage bytes.Buffer
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
}
type MyEnum int
@@ -99,7 +101,10 @@ func (n *MyEnum) MarshalText() ([]byte, error) {
}
func TestUsageWithDefaults(t *testing.T) {
- expectedHelp := `Usage: example [--label LABEL] [--content CONTENT]
+ expectedUsage := "Usage: example [--label LABEL] [--content CONTENT]"
+
+ expectedHelp := `
+Usage: example [--label LABEL] [--content CONTENT]
Options:
--label LABEL [default: cat]
@@ -118,7 +123,11 @@ Options:
var help bytes.Buffer
p.WriteHelp(&help)
- assert.Equal(t, expectedHelp, help.String())
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ var usage bytes.Buffer
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
}
func TestUsageCannotMarshalToString(t *testing.T) {
@@ -132,7 +141,10 @@ func TestUsageCannotMarshalToString(t *testing.T) {
}
func TestUsageLongPositionalWithHelp_legacyForm(t *testing.T) {
- expectedHelp := `Usage: example VERYLONGPOSITIONALWITHHELP
+ expectedUsage := "Usage: example VERYLONGPOSITIONALWITHHELP"
+
+ expectedHelp := `
+Usage: example VERYLONGPOSITIONALWITHHELP
Positional arguments:
VERYLONGPOSITIONALWITHHELP
@@ -145,17 +157,23 @@ Options:
VeryLongPositionalWithHelp string `arg:"positional,help:this positional argument is very long but cannot include commas"`
}
- p, err := NewParser(Config{}, &args)
+ p, err := NewParser(Config{Program: "example"}, &args)
require.NoError(t, err)
- os.Args[0] = "example"
var help bytes.Buffer
p.WriteHelp(&help)
- assert.Equal(t, expectedHelp, help.String())
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ var usage bytes.Buffer
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
}
func TestUsageLongPositionalWithHelp_newForm(t *testing.T) {
- expectedHelp := `Usage: example VERYLONGPOSITIONALWITHHELP
+ expectedUsage := "Usage: example VERYLONGPOSITIONALWITHHELP"
+
+ expectedHelp := `
+Usage: example VERYLONGPOSITIONALWITHHELP
Positional arguments:
VERYLONGPOSITIONALWITHHELP
@@ -168,17 +186,23 @@ Options:
VeryLongPositionalWithHelp string `arg:"positional" help:"this positional argument is very long, and includes: commas, colons etc"`
}
- p, err := NewParser(Config{}, &args)
+ p, err := NewParser(Config{Program: "example"}, &args)
require.NoError(t, err)
- os.Args[0] = "example"
var help bytes.Buffer
p.WriteHelp(&help)
- assert.Equal(t, expectedHelp, help.String())
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ var usage bytes.Buffer
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
}
func TestUsageWithProgramName(t *testing.T) {
- expectedHelp := `Usage: myprogram
+ expectedUsage := "Usage: myprogram"
+
+ expectedHelp := `
+Usage: myprogram
Options:
--help, -h display this help and exit
@@ -190,9 +214,14 @@ Options:
require.NoError(t, err)
os.Args[0] = "example"
+
var help bytes.Buffer
p.WriteHelp(&help)
- assert.Equal(t, expectedHelp, help.String())
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ var usage bytes.Buffer
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
}
type versioned struct{}
@@ -203,7 +232,10 @@ func (versioned) Version() string {
}
func TestUsageWithVersion(t *testing.T) {
- expectedHelp := `example 3.2.1
+ expectedUsage := "example 3.2.1\nUsage: example"
+
+ expectedHelp := `
+example 3.2.1
Usage: example
Options:
@@ -216,12 +248,11 @@ Options:
var help bytes.Buffer
p.WriteHelp(&help)
- actual := help.String()
- if expectedHelp != actual {
- t.Logf("Expected:\n%s", expectedHelp)
- t.Logf("Actual:\n%s", actual)
- t.Fail()
- }
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ var usage bytes.Buffer
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
}
type described struct{}
@@ -232,7 +263,10 @@ func (described) Description() string {
}
func TestUsageWithDescription(t *testing.T) {
- expectedHelp := `this program does this and that
+ expectedUsage := "Usage: example"
+
+ expectedHelp := `
+this program does this and that
Usage: example
Options:
@@ -244,16 +278,18 @@ Options:
var help bytes.Buffer
p.WriteHelp(&help)
- actual := help.String()
- if expectedHelp != actual {
- t.Logf("Expected:\n%s", expectedHelp)
- t.Logf("Actual:\n%s", actual)
- t.Fail()
- }
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ var usage bytes.Buffer
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
}
func TestRequiredMultiplePositionals(t *testing.T) {
- expectedHelp := `Usage: example REQUIREDMULTIPLE [REQUIREDMULTIPLE ...]
+ expectedUsage := "Usage: example REQUIREDMULTIPLE [REQUIREDMULTIPLE ...]"
+
+ expectedHelp := `
+Usage: example REQUIREDMULTIPLE [REQUIREDMULTIPLE ...]
Positional arguments:
REQUIREDMULTIPLE required multiple positional
@@ -270,11 +306,18 @@ Options:
var help bytes.Buffer
p.WriteHelp(&help)
- assert.Equal(t, expectedHelp, help.String())
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ var usage bytes.Buffer
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
}
func TestUsageWithNestedSubcommands(t *testing.T) {
- expectedHelp := `Usage: example child nested [--enable] OUTPUT
+ expectedUsage := "Usage: example child nested [--enable] OUTPUT"
+
+ expectedHelp := `
+Usage: example child nested [--enable] OUTPUT
Positional arguments:
OUTPUT
@@ -307,11 +350,18 @@ Global options:
var help bytes.Buffer
p.WriteHelp(&help)
- assert.Equal(t, expectedHelp, help.String())
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ var usage bytes.Buffer
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
}
func TestUsageWithoutLongNames(t *testing.T) {
- expectedHelp := `Usage: example [-a PLACEHOLDER] -b SHORTONLY2
+ expectedUsage := "Usage: example [-a PLACEHOLDER] -b SHORTONLY2"
+
+ expectedHelp := `
+Usage: example [-a PLACEHOLDER] -b SHORTONLY2
Options:
-a PLACEHOLDER some help [default: some val]
@@ -324,13 +374,21 @@ Options:
}
p, err := NewParser(Config{Program: "example"}, &args)
assert.NoError(t, err)
+
var help bytes.Buffer
p.WriteHelp(&help)
- assert.Equal(t, expectedHelp, help.String())
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ var usage bytes.Buffer
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
}
func TestUsageWithShortFirst(t *testing.T) {
- expectedHelp := `Usage: example [-c CAT] [--dog DOG]
+ expectedUsage := "Usage: example [-c CAT] [--dog DOG]"
+
+ expectedHelp := `
+Usage: example [-c CAT] [--dog DOG]
Options:
-c CAT
@@ -343,13 +401,21 @@ Options:
}
p, err := NewParser(Config{Program: "example"}, &args)
assert.NoError(t, err)
+
var help bytes.Buffer
p.WriteHelp(&help)
- assert.Equal(t, expectedHelp, help.String())
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ var usage bytes.Buffer
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
}
func TestUsageWithEnvOptions(t *testing.T) {
- expectedHelp := `Usage: example [-s SHORT]
+ expectedUsage := "Usage: example [-s SHORT]"
+
+ expectedHelp := `
+Usage: example [-s SHORT]
Options:
-s SHORT [env: SHORT]
@@ -363,7 +429,42 @@ Options:
p, err := NewParser(Config{Program: "example"}, &args)
assert.NoError(t, err)
+
var help bytes.Buffer
p.WriteHelp(&help)
- assert.Equal(t, expectedHelp, help.String())
+ assert.Equal(t, expectedHelp[1:], help.String())
+
+ var usage bytes.Buffer
+ p.WriteUsage(&usage)
+ assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
+}
+
+func TestFail(t *testing.T) {
+ originalStderr := stderr
+ originalExit := osExit
+ defer func() {
+ stderr = originalStderr
+ osExit = originalExit
+ }()
+
+ var b bytes.Buffer
+ stderr = &b
+
+ var exitCode int
+ osExit = func(code int) { exitCode = code }
+
+ expectedStdout := `
+Usage: example [--foo FOO]
+error: something went wrong
+`
+
+ var args struct {
+ Foo int
+ }
+ p, err := NewParser(Config{Program: "example"}, &args)
+ require.NoError(t, err)
+ p.Fail("something went wrong")
+
+ assert.Equal(t, expectedStdout[1:], b.String())
+ assert.Equal(t, -1, exitCode)
}