Something went wrong. Try again.
Simple, intuitive CLI framework for Go pkg.go.dev/go.followtheprocess.codes/cli
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102package flag_test
import ( "bytes" "errors" "net" "net/url" "slices" "testing" "time"
publicflag "go.followtheprocess.codes/cli/flag" "go.followtheprocess.codes/cli/internal/flag" "go.followtheprocess.codes/cli/internal/format" "go.followtheprocess.codes/cli/internal/parse" "go.followtheprocess.codes/test")
func TestFlaggableTypes(t *testing.T) { // We can't do table testing here because Flag[T] is a different type for each test // so we can't do a []Flag[T] which is needed to define the test cases // so strap in for a bunch of copy pasta t.Run("int valid", func(t *testing.T) { var i int
intFlag, err := flag.New(&i, "int", 'i', "Set an int value", flag.Config[int]{}) test.Ok(t, err)
err = intFlag.Set("42") test.Ok(t, err) test.Equal(t, i, 42) test.Equal(t, intFlag.Type(), "int") test.Equal(t, intFlag.String(), "42") test.Equal(t, intFlag.Default(), "42") })
t.Run("int invalid", func(t *testing.T) { var i int
intFlag, err := flag.New(&i, "int", 'i', "Set an int value", flag.Config[int]{}) test.Ok(t, err)
err = intFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("int8 valid", func(t *testing.T) { var i int8
intFlag, err := flag.New(&i, "int", 'i', "Set an int8 value", flag.Config[int8]{}) test.Ok(t, err)
err = intFlag.Set("42") test.Ok(t, err) test.Equal(t, i, int8(42)) test.Equal(t, intFlag.Type(), "int8") test.Equal(t, intFlag.String(), "42") test.Equal(t, intFlag.Default(), "42") })
t.Run("int8 invalid", func(t *testing.T) { var i int8
intFlag, err := flag.New(&i, "int", 'i', "Set an int8 value", flag.Config[int8]{}) test.Ok(t, err)
err = intFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("int16 valid", func(t *testing.T) { var i int16
intFlag, err := flag.New(&i, "int", 'i', "Set an int16 value", flag.Config[int16]{}) test.Ok(t, err)
err = intFlag.Set("42") test.Ok(t, err) test.Equal(t, i, int16(42)) test.Equal(t, intFlag.Type(), "int16") test.Equal(t, intFlag.String(), "42") test.Equal(t, intFlag.Default(), "42") })
t.Run("int16 invalid", func(t *testing.T) { var i int16
intFlag, err := flag.New(&i, "int", 'i', "Set an int16 value", flag.Config[int16]{}) test.Ok(t, err)
err = intFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("int32 valid", func(t *testing.T) { var i int32
intFlag, err := flag.New(&i, "int", 'i', "Set an int32 value", flag.Config[int32]{}) test.Ok(t, err)
err = intFlag.Set("42") test.Ok(t, err) test.Equal(t, i, int32(42)) test.Equal(t, intFlag.Type(), "int32") test.Equal(t, intFlag.String(), "42") })
t.Run("int32 invalid", func(t *testing.T) { var i int32
intFlag, err := flag.New(&i, "int", 'i', "Set an int32 value", flag.Config[int32]{}) test.Ok(t, err)
err = intFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("int64 valid", func(t *testing.T) { var i int64
intFlag, err := flag.New(&i, "int", 'i', "Set an int64 value", flag.Config[int64]{}) test.Ok(t, err)
err = intFlag.Set("42") test.Ok(t, err) test.Equal(t, i, int64(42)) test.Equal(t, intFlag.Type(), "int64") test.Equal(t, intFlag.String(), "42") })
t.Run("int64 invalid", func(t *testing.T) { var i int64
intFlag, err := flag.New(&i, "int", 'i', "Set an int64 value", flag.Config[int64]{}) test.Ok(t, err)
err = intFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("count valid", func(t *testing.T) { var c publicflag.Count
countFlag, err := flag.New(&c, "count", 'c', "Count something", flag.Config[publicflag.Count]{}) test.Ok(t, err)
err = countFlag.Set("1") test.Ok(t, err) test.Equal(t, c, publicflag.Count(1)) test.Equal(t, countFlag.Type(), "count") test.Equal(t, countFlag.String(), "1")
// Setting it again should increment to 2 err = countFlag.Set("1") test.Ok(t, err) test.Equal(t, c, publicflag.Count(2)) test.Equal(t, countFlag.Type(), "count") test.Equal(t, countFlag.String(), "2")
// Should also be able to set an explicit number e.g. --verbosity=3 // so should now be 5 err = countFlag.Set("3") test.Ok(t, err) test.Equal(t, c, publicflag.Count(5)) test.Equal(t, countFlag.Type(), "count") test.Equal(t, countFlag.String(), "5") })
t.Run("count invalid", func(t *testing.T) { var c publicflag.Count
countFlag, err := flag.New(&c, "count", 'c', "Count something", flag.Config[publicflag.Count]{}) test.Ok(t, err)
err = countFlag.Set("a word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("uint valid", func(t *testing.T) { var i uint
intFlag, err := flag.New(&i, "uint", 'i', "Set a uint value", flag.Config[uint]{}) test.Ok(t, err)
err = intFlag.Set("42") test.Ok(t, err) test.Equal(t, i, 42) test.Equal(t, intFlag.Type(), "uint") test.Equal(t, intFlag.String(), "42") })
t.Run("uint invalid", func(t *testing.T) { var i uint
intFlag, err := flag.New(&i, "uint", 'i', "Set a uint value", flag.Config[uint]{}) test.Ok(t, err)
err = intFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("uint8 valid", func(t *testing.T) { var i uint8
intFlag, err := flag.New(&i, "uint", 'i', "Set a uint8 value", flag.Config[uint8]{}) test.Ok(t, err)
err = intFlag.Set("42") test.Ok(t, err) test.Equal(t, i, uint8(42)) test.Equal(t, intFlag.Type(), "uint8") test.Equal(t, intFlag.String(), "42") })
t.Run("uint8 invalid", func(t *testing.T) { var i uint8
intFlag, err := flag.New(&i, "uint", 'i', "Set a uint8 value", flag.Config[uint8]{}) test.Ok(t, err)
err = intFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("uint16 valid", func(t *testing.T) { var i uint16
intFlag, err := flag.New(&i, "uint", 'i', "Set a uint16 value", flag.Config[uint16]{}) test.Ok(t, err)
err = intFlag.Set("42") test.Ok(t, err) test.Equal(t, i, uint16(42)) test.Equal(t, intFlag.Type(), "uint16") test.Equal(t, intFlag.String(), "42") })
t.Run("uint16 invalid", func(t *testing.T) { var i uint16
intFlag, err := flag.New(&i, "uint", 'i', "Set a uint16 value", flag.Config[uint16]{}) test.Ok(t, err)
err = intFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("uint32 valid", func(t *testing.T) { var i uint32
intFlag, err := flag.New(&i, "uint", 'i', "Set a uint32 value", flag.Config[uint32]{}) test.Ok(t, err)
err = intFlag.Set("42") test.Ok(t, err) test.Equal(t, i, uint32(42)) test.Equal(t, intFlag.Type(), "uint32") test.Equal(t, intFlag.String(), "42") })
t.Run("uint32 invalid", func(t *testing.T) { var i uint32
intFlag, err := flag.New(&i, "uint", 'i', "Set a uint32 value", flag.Config[uint32]{}) test.Ok(t, err)
err = intFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("uint64 valid", func(t *testing.T) { var i uint64
intFlag, err := flag.New(&i, "uint", 'i', "Set a uint64 value", flag.Config[uint64]{}) test.Ok(t, err)
err = intFlag.Set("42") test.Ok(t, err) test.Equal(t, i, uint64(42)) test.Equal(t, intFlag.Type(), "uint64") test.Equal(t, intFlag.String(), "42") })
t.Run("uint64 invalid", func(t *testing.T) { var i uint64
intFlag, err := flag.New(&i, "uint", 'i', "Set a uint64 value", flag.Config[uint64]{}) test.Ok(t, err)
err = intFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("uintptr valid", func(t *testing.T) { var i uintptr
intFlag, err := flag.New(&i, "uintptr", 'i', "Set a uintptr value", flag.Config[uintptr]{}) test.Ok(t, err)
err = intFlag.Set("42") test.Ok(t, err) test.Equal(t, i, uintptr(42)) test.Equal(t, intFlag.Type(), "uintptr") test.Equal(t, intFlag.String(), "42") })
t.Run("uintptr invalid", func(t *testing.T) { var i uintptr
intFlag, err := flag.New(&i, "uintptr", 'i', "Set a uintptr value", flag.Config[uintptr]{}) test.Ok(t, err)
err = intFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("float32 valid", func(t *testing.T) { var f float32
floatFlag, err := flag.New(&f, "float", 'f', "Set a float32 value", flag.Config[float32]{}) test.Ok(t, err)
err = floatFlag.Set("3.14159") test.Ok(t, err) test.Equal(t, f, 3.14159) test.Equal(t, floatFlag.Type(), "float32") test.Equal(t, floatFlag.String(), "3.14159") })
t.Run("float32 invalid", func(t *testing.T) { var f float32
floatFlag, err := flag.New(&f, "float", 'f', "Set a float32 value", flag.Config[float32]{}) test.Ok(t, err)
err = floatFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("float64 valid", func(t *testing.T) { var f float64
floatFlag, err := flag.New(&f, "float", 'f', "Set a float64 value", flag.Config[float64]{}) test.Ok(t, err)
err = floatFlag.Set("3.14159") test.Ok(t, err) test.Equal(t, f, 3.14159) test.Equal(t, floatFlag.Type(), "float64") test.Equal(t, floatFlag.String(), "3.14159") })
t.Run("float64 invalid", func(t *testing.T) { var f float64
floatFlag, err := flag.New(&f, "float", 'f', "Set a float64 value", flag.Config[float64]{}) test.Ok(t, err)
err = floatFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("bool valid", func(t *testing.T) { var b bool
boolFlag, err := flag.New(&b, "bool", 'b', "Set a bool value", flag.Config[bool]{}) test.Ok(t, err)
err = boolFlag.Set(format.True) test.Ok(t, err) test.Equal(t, b, true) test.Equal(t, boolFlag.Type(), "bool") test.Equal(t, boolFlag.String(), format.True) })
t.Run("bool help", func(t *testing.T) { var h bool
boolFlag, err := flag.New(&h, "help", 'h', "Show help", flag.Config[bool]{}) test.Ok(t, err)
err = boolFlag.Set(format.True) test.Ok(t, err) test.Equal(t, h, true) test.Equal(t, boolFlag.Type(), "bool") test.Equal(t, boolFlag.String(), format.True) test.Equal(t, boolFlag.Default(), "") // Special case })
t.Run("bool invalid", func(t *testing.T) { var b bool
boolFlag, err := flag.New(&b, "bool", 'b', "Set a bool value", flag.Config[bool]{}) test.Ok(t, err)
err = boolFlag.Set("word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
// No invalid case as all command line args are strings anyway so no real way of // getting an error here t.Run("string", func(t *testing.T) { var str string
strFlag, err := flag.New(&str, "string", 's', "Set a string value", flag.Config[string]{}) test.Ok(t, err)
err = strFlag.Set("newvalue") test.Ok(t, err) test.Equal(t, str, "newvalue") test.Equal(t, strFlag.Type(), "string") test.Equal(t, strFlag.String(), "newvalue") })
t.Run("byte slice valid", func(t *testing.T) { var byt []byte
byteFlag, err := flag.New(&byt, "byte", 'b', "Set a byte slice value", flag.Config[[]byte]{}) test.Ok(t, err)
err = byteFlag.Set("5e") test.Ok(t, err) test.EqualFunc(t, byt, []byte("^"), bytes.Equal) test.Equal(t, byteFlag.Type(), "bytesHex") test.Equal(t, byteFlag.String(), "5e") })
t.Run("byte slice invalid", func(t *testing.T) { var byt []byte
byteFlag, err := flag.New(&byt, "byte", 'b', "Set a byte slice value", flag.Config[[]byte]{}) test.Ok(t, err)
err = byteFlag.Set("0xF") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("time.Time valid", func(t *testing.T) { var tyme time.Time
timeFlag, err := flag.New(&tyme, "time", 't', "Set a time value", flag.Config[time.Time]{}) test.Ok(t, err)
err = timeFlag.Set("2024-07-17T07:38:05Z") test.Ok(t, err)
want, err := time.Parse(time.RFC3339, "2024-07-17T07:38:05Z") test.Ok(t, err) test.Equal(t, tyme, want) test.Equal(t, timeFlag.Type(), "time") test.Equal(t, timeFlag.String(), "2024-07-17T07:38:05Z") })
t.Run("time.Time invalid", func(t *testing.T) { var tyme time.Time
timeFlag, err := flag.New(&tyme, "time", 't', "Set a time value", flag.Config[time.Time]{}) test.Ok(t, err)
err = timeFlag.Set("not a time") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("time.Duration valid", func(t *testing.T) { var duration time.Duration
durationFlag, err := flag.New( &duration, "duration", 'd', "Set a duration value", flag.Config[time.Duration]{}, ) test.Ok(t, err)
err = durationFlag.Set("300ms") test.Ok(t, err)
want, err := time.ParseDuration("300ms") test.Ok(t, err) test.Equal(t, duration, want) test.Equal(t, durationFlag.Type(), "duration") test.Equal(t, durationFlag.String(), "300ms") })
t.Run("time.Duration invalid", func(t *testing.T) { var duration time.Duration
durationFlag, err := flag.New( &duration, "duration", 'd', "Set a duration value", flag.Config[time.Duration]{}, ) test.Ok(t, err)
err = durationFlag.Set("not a duration") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("ip valid", func(t *testing.T) { var ip net.IP
ipFlag, err := flag.New(&ip, "ip", 'i', "Set an IP address", flag.Config[net.IP]{}) test.Ok(t, err)
err = ipFlag.Set("192.0.2.1") test.Ok(t, err) test.DiffBytes(t, ip, net.ParseIP("192.0.2.1")) test.Equal(t, ipFlag.Type(), "ip") test.Equal(t, ipFlag.String(), "192.0.2.1") })
t.Run("ip invalid", func(t *testing.T) { var ip net.IP
ipFlag, err := flag.New(&ip, "ip", 'i', "Set an IP address", flag.Config[net.IP]{}) test.Ok(t, err)
err = ipFlag.Set("not an ip") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("int slice valid", func(t *testing.T) { var slice []int
sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]int]{}) test.Ok(t, err)
err = sliceFlag.Set("1") // Append 1 to the slice test.Ok(t, err)
test.EqualFunc(t, slice, []int{1}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]int") test.Equal(t, sliceFlag.String(), "[1]")
err = sliceFlag.Set("2") // Now 2 test.Ok(t, err)
test.EqualFunc(t, slice, []int{1, 2}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]int") test.Equal(t, sliceFlag.String(), "[1, 2]") })
t.Run("int slice invalid", func(t *testing.T) { var slice []int
sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]int]{}) test.Ok(t, err)
err = sliceFlag.Set("a word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("int8 slice valid", func(t *testing.T) { var slice []int8
sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]int8]{}) test.Ok(t, err)
err = sliceFlag.Set("1") // Append 1 to the slice test.Ok(t, err)
test.EqualFunc(t, slice, []int8{1}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]int8") test.Equal(t, sliceFlag.String(), "[1]")
err = sliceFlag.Set("2") // Now 2 test.Ok(t, err)
test.EqualFunc(t, slice, []int8{1, 2}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]int8") test.Equal(t, sliceFlag.String(), "[1, 2]") })
t.Run("int8 slice invalid", func(t *testing.T) { var slice []int8
sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]int8]{}) test.Ok(t, err)
err = sliceFlag.Set("cheese") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("int16 slice valid", func(t *testing.T) { var slice []int16
sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]int16]{}) test.Ok(t, err)
err = sliceFlag.Set("1") // Append 1 to the slice test.Ok(t, err)
test.EqualFunc(t, slice, []int16{1}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]int16") test.Equal(t, sliceFlag.String(), "[1]")
err = sliceFlag.Set("2") // Now 2 test.Ok(t, err)
test.EqualFunc(t, slice, []int16{1, 2}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]int16") test.Equal(t, sliceFlag.String(), "[1, 2]") })
t.Run("int16 slice invalid", func(t *testing.T) { var slice []int16
sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]int16]{}) test.Ok(t, err)
err = sliceFlag.Set("balls") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("int32 slice valid", func(t *testing.T) { var slice []int32
sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]int32]{}) test.Ok(t, err)
err = sliceFlag.Set("1") // Append 1 to the slice test.Ok(t, err)
test.EqualFunc(t, slice, []int32{1}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]int32") test.Equal(t, sliceFlag.String(), "[1]")
err = sliceFlag.Set("2") // Now 2 test.Ok(t, err)
test.EqualFunc(t, slice, []int32{1, 2}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]int32") test.Equal(t, sliceFlag.String(), "[1, 2]") })
t.Run("int32 slice invalid", func(t *testing.T) { var slice []int32
sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]int32]{}) test.Ok(t, err)
err = sliceFlag.Set("balls") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("int64 slice valid", func(t *testing.T) { var slice []int64
sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]int64]{}) test.Ok(t, err)
err = sliceFlag.Set("1") // Append 1 to the slice test.Ok(t, err)
test.EqualFunc(t, slice, []int64{1}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]int64") test.Equal(t, sliceFlag.String(), "[1]")
err = sliceFlag.Set("2") // Now 2 test.Ok(t, err)
test.EqualFunc(t, slice, []int64{1, 2}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]int64") test.Equal(t, sliceFlag.String(), "[1, 2]") })
t.Run("int64 slice invalid", func(t *testing.T) { var slice []int64
sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]int64]{}) test.Ok(t, err)
err = sliceFlag.Set("balls") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("uint slice valid", func(t *testing.T) { var slice []uint
sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of uints", flag.Config[[]uint]{}) test.Ok(t, err)
err = sliceFlag.Set("1") // Append 1 to the slice test.Ok(t, err)
test.EqualFunc(t, slice, []uint{1}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]uint") test.Equal(t, sliceFlag.String(), "[1]")
err = sliceFlag.Set("2") // Now 2 test.Ok(t, err)
test.EqualFunc(t, slice, []uint{1, 2}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]uint") test.Equal(t, sliceFlag.String(), "[1, 2]") })
t.Run("uint slice invalid", func(t *testing.T) { var slice []uint
sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of unsigned integers", flag.Config[[]uint]{}) test.Ok(t, err)
err = sliceFlag.Set("a word") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("uint16 slice valid", func(t *testing.T) { var slice []uint16
sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]uint16]{}) test.Ok(t, err)
err = sliceFlag.Set("1") // Append 1 to the slice test.Ok(t, err)
test.EqualFunc(t, slice, []uint16{1}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]uint16") test.Equal(t, sliceFlag.String(), "[1]")
err = sliceFlag.Set("2") // Now 2 test.Ok(t, err)
test.EqualFunc(t, slice, []uint16{1, 2}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]uint16") test.Equal(t, sliceFlag.String(), "[1, 2]") })
t.Run("uint16 slice invalid", func(t *testing.T) { var slice []uint16
sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]uint16]{}) test.Ok(t, err)
err = sliceFlag.Set("balls") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("uint32 slice valid", func(t *testing.T) { var slice []uint32
sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]uint32]{}) test.Ok(t, err)
err = sliceFlag.Set("1") // Append 1 to the slice test.Ok(t, err)
test.EqualFunc(t, slice, []uint32{1}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]uint32") test.Equal(t, sliceFlag.String(), "[1]")
err = sliceFlag.Set("2") // Now 2 test.Ok(t, err)
test.EqualFunc(t, slice, []uint32{1, 2}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]uint32") test.Equal(t, sliceFlag.String(), "[1, 2]") })
t.Run("uint32 slice invalid", func(t *testing.T) { var slice []uint32
sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]uint32]{}) test.Ok(t, err)
err = sliceFlag.Set("balls") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("uint64 slice valid", func(t *testing.T) { var slice []uint64
sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of ints", flag.Config[[]uint64]{}) test.Ok(t, err)
err = sliceFlag.Set("1") // Append 1 to the slice test.Ok(t, err)
test.EqualFunc(t, slice, []uint64{1}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]uint64") test.Equal(t, sliceFlag.String(), "[1]")
err = sliceFlag.Set("2") // Now 2 test.Ok(t, err)
test.EqualFunc(t, slice, []uint64{1, 2}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]uint64") test.Equal(t, sliceFlag.String(), "[1, 2]") })
t.Run("uint64 slice invalid", func(t *testing.T) { var slice []uint64
sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of integers", flag.Config[[]uint64]{}) test.Ok(t, err)
err = sliceFlag.Set("balls") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("float32 slice valid", func(t *testing.T) { var slice []float32
sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of floats", flag.Config[[]float32]{}) test.Ok(t, err)
err = sliceFlag.Set("3.14159") // Append pi to the slice test.Ok(t, err)
test.EqualFunc(t, slice, []float32{3.14159}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]float32") test.Equal(t, sliceFlag.String(), "[3.14159]")
err = sliceFlag.Set("2.7128") // Now e test.Ok(t, err)
test.EqualFunc(t, slice, []float32{3.14159, 2.7128}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]float32") test.Equal(t, sliceFlag.String(), "[3.14159, 2.7128]") })
t.Run("float32 slice invalid", func(t *testing.T) { var slice []float32
sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of floats", flag.Config[[]float32]{}) test.Ok(t, err)
err = sliceFlag.Set("balls") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("float64 slice valid", func(t *testing.T) { var slice []float64
sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of floats", flag.Config[[]float64]{}) test.Ok(t, err)
err = sliceFlag.Set("3.14159") // Append pi to the slice test.Ok(t, err)
test.EqualFunc(t, slice, []float64{3.14159}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]float64") test.Equal(t, sliceFlag.String(), "[3.14159]")
err = sliceFlag.Set("2.7128") // Now e test.Ok(t, err)
test.EqualFunc(t, slice, []float64{3.14159, 2.7128}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]float64") test.Equal(t, sliceFlag.String(), "[3.14159, 2.7128]") })
t.Run("float64 slice invalid", func(t *testing.T) { var slice []float64
sliceFlag, err := flag.New(&slice, "slice", 's', "Slice of floats", flag.Config[[]float64]{}) test.Ok(t, err)
err = sliceFlag.Set("balls") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })
t.Run("string slice valid", func(t *testing.T) { // Note: no invalid case for []string because *every* flag value is a string // it's impossible to make a bad one var slice []string
sliceFlag, err := flag.New(&slice, "slice", 's', "Append to a slice of strings", flag.Config[[]string]{}) test.Ok(t, err)
err = sliceFlag.Set("a string") test.Ok(t, err)
test.EqualFunc(t, slice, []string{"a string"}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]string") test.Equal(t, sliceFlag.String(), `["a string"]`)
err = sliceFlag.Set("another string") test.Ok(t, err)
test.EqualFunc(t, slice, []string{"a string", "another string"}, slices.Equal) test.Equal(t, sliceFlag.Type(), "[]string") test.Equal(t, sliceFlag.String(), `["a string", "another string"]`) })
t.Run("url valid", func(t *testing.T) { var u *url.URL
urlFlag, err := flag.New(&u, "url", 'u', "Set a URL", flag.Config[*url.URL]{}) test.Ok(t, err)
err = urlFlag.Set("https://example.com/path") test.Ok(t, err) test.Equal(t, u.Scheme, "https") test.Equal(t, u.Host, "example.com") test.Equal(t, u.Path, "/path") test.Equal(t, urlFlag.Type(), "url") test.Equal(t, urlFlag.String(), "https://example.com/path") })
t.Run("url invalid", func(t *testing.T) { var u *url.URL
urlFlag, err := flag.New(&u, "url", 'u', "Set a URL", flag.Config[*url.URL]{}) test.Ok(t, err)
err = urlFlag.Set("not a url") test.Err(t, err) test.True(t, errors.Is(err, parse.Err)) })}
func TestFlagValidation(t *testing.T) { tests := []struct { name string // Name of the test case flagName string // Input flag name errMsg string // If we wanted an error, what should it say short rune // Flag shorthand wantErr bool // Whether we want an error }{ { name: "short is uppercase", flagName: "delete", short: 'D', wantErr: false, }, { name: "valid short", flagName: "delete", short: 'd', wantErr: false, }, { name: "no shorthand", flagName: "delete", short: publicflag.NoShortHand, wantErr: false, }, { name: "hyphen separated", flagName: "dry-run", short: 'd', wantErr: false, }, { name: "empty name", flagName: "", wantErr: true, errMsg: `invalid flag name "": must not be empty`, }, { name: "whitespace", flagName: " ", wantErr: true, errMsg: `invalid flag name " ": cannot contain whitespace`, }, { name: "mixed case", flagName: "heLlO", wantErr: true, errMsg: `invalid flag name "heLlO": contains upper case character "L"`, }, { name: "underscore", flagName: "set_default", wantErr: true, errMsg: `invalid flag name "set_default": contains non ascii letter: "_"`, }, { name: "digits", flagName: "some-06digit", wantErr: true, errMsg: `invalid flag name "some-06digit": contains non ascii letter: "0"`, }, { name: "just hyphen", flagName: "-", wantErr: true, errMsg: `invalid flag name "-": trailing hyphen`, }, { name: "leading hyphen", flagName: "-something", wantErr: true, errMsg: `invalid flag name "-something": leading hyphen`, }, { name: "trailing hyphen", flagName: "something-", wantErr: true, errMsg: `invalid flag name "something-": trailing hyphen`, }, { name: "non ascii", flagName: "語ç日ð本", wantErr: true, errMsg: `invalid flag name "語ç日ð本": contains non ascii character: "語"`, }, { name: "short is digit", flagName: "delete", short: '7', wantErr: true, errMsg: `invalid shorthand for flag "delete": invalid character, must be a single ASCII letter, got "7"`, }, { name: "short is non ascii", flagName: "delete", short: '本', wantErr: true, errMsg: `invalid shorthand for flag "delete": invalid character, must be a single ASCII letter, got "本"`, }, { name: "short is whitespace", flagName: "delete", short: ' ', wantErr: true, errMsg: `invalid shorthand for flag "delete": cannot contain whitespace`, }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := flag.New(new(string), tt.flagName, tt.short, "Test me", flag.Config[string]{}) test.WantErr(t, err, tt.wantErr)
if err != nil { test.Equal(t, err.Error(), tt.errMsg) } }) }}
func TestFlagNilSafety(t *testing.T) { t.Run("with new", func(t *testing.T) { // Passing a nil target is an error var bang *bool
f, err := flag.New(bang, "bang", 'b', "Nil go bang?", flag.Config[bool]{}) test.Err(t, err) test.Equal(t, f, nil) })
t.Run("composite literal", func(t *testing.T) { // Users doing naughty things, should still be nil safe flag := flag.Flag[bool]{} test.Equal(t, flag.String(), format.Nil) test.Equal(t, flag.Type(), format.Nil)
err := flag.Set(format.True) test.Err(t, err) test.Equal(t, err.Error(), "cannot set value true, flag.value was nil") })}
func BenchmarkFlagSet(b *testing.B) { var count int
flag, err := flag.New(&count, "count", 'c', "Count things", flag.Config[int]{}) test.Ok(b, err)
for b.Loop() { err := flag.Set("42") if err != nil { b.Fatalf("flag.Set returned an error: %v", err) } }}