1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package pflag
6
7import (
8	"bytes"
9	"fmt"
10	"io"
11	"io/ioutil"
12	"net"
13	"os"
14	"reflect"
15	"sort"
16	"strconv"
17	"strings"
18	"testing"
19	"time"
20)
21
22var (
23	testBool                     = Bool("test_bool", false, "bool value")
24	testInt                      = Int("test_int", 0, "int value")
25	testInt64                    = Int64("test_int64", 0, "int64 value")
26	testUint                     = Uint("test_uint", 0, "uint value")
27	testUint64                   = Uint64("test_uint64", 0, "uint64 value")
28	testString                   = String("test_string", "0", "string value")
29	testFloat                    = Float64("test_float64", 0, "float64 value")
30	testDuration                 = Duration("test_duration", 0, "time.Duration value")
31	testOptionalInt              = Int("test_optional_int", 0, "optional int value")
32	normalizeFlagNameInvocations = 0
33)
34
35func boolString(s string) string {
36	if s == "0" {
37		return "false"
38	}
39	return "true"
40}
41
42func TestEverything(t *testing.T) {
43	m := make(map[string]*Flag)
44	desired := "0"
45	visitor := func(f *Flag) {
46		if len(f.Name) > 5 && f.Name[0:5] == "test_" {
47			m[f.Name] = f
48			ok := false
49			switch {
50			case f.Value.String() == desired:
51				ok = true
52			case f.Name == "test_bool" && f.Value.String() == boolString(desired):
53				ok = true
54			case f.Name == "test_duration" && f.Value.String() == desired+"s":
55				ok = true
56			}
57			if !ok {
58				t.Error("Visit: bad value", f.Value.String(), "for", f.Name)
59			}
60		}
61	}
62	VisitAll(visitor)
63	if len(m) != 9 {
64		t.Error("VisitAll misses some flags")
65		for k, v := range m {
66			t.Log(k, *v)
67		}
68	}
69	m = make(map[string]*Flag)
70	Visit(visitor)
71	if len(m) != 0 {
72		t.Errorf("Visit sees unset flags")
73		for k, v := range m {
74			t.Log(k, *v)
75		}
76	}
77	// Now set all flags
78	Set("test_bool", "true")
79	Set("test_int", "1")
80	Set("test_int64", "1")
81	Set("test_uint", "1")
82	Set("test_uint64", "1")
83	Set("test_string", "1")
84	Set("test_float64", "1")
85	Set("test_duration", "1s")
86	Set("test_optional_int", "1")
87	desired = "1"
88	Visit(visitor)
89	if len(m) != 9 {
90		t.Error("Visit fails after set")
91		for k, v := range m {
92			t.Log(k, *v)
93		}
94	}
95	// Now test they're visited in sort order.
96	var flagNames []string
97	Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) })
98	if !sort.StringsAreSorted(flagNames) {
99		t.Errorf("flag names not sorted: %v", flagNames)
100	}
101}
102
103func TestUsage(t *testing.T) {
104	called := false
105	ResetForTesting(func() { called = true })
106	if GetCommandLine().Parse([]string{"--x"}) == nil {
107		t.Error("parse did not fail for unknown flag")
108	}
109	if called {
110		t.Error("did call Usage while using ContinueOnError")
111	}
112}
113
114func TestAddFlagSet(t *testing.T) {
115	oldSet := NewFlagSet("old", ContinueOnError)
116	newSet := NewFlagSet("new", ContinueOnError)
117
118	oldSet.String("flag1", "flag1", "flag1")
119	oldSet.String("flag2", "flag2", "flag2")
120
121	newSet.String("flag2", "flag2", "flag2")
122	newSet.String("flag3", "flag3", "flag3")
123
124	oldSet.AddFlagSet(newSet)
125
126	if len(oldSet.formal) != 3 {
127		t.Errorf("Unexpected result adding a FlagSet to a FlagSet %v", oldSet)
128	}
129}
130
131func TestAnnotation(t *testing.T) {
132	f := NewFlagSet("shorthand", ContinueOnError)
133
134	if err := f.SetAnnotation("missing-flag", "key", nil); err == nil {
135		t.Errorf("Expected error setting annotation on non-existent flag")
136	}
137
138	f.StringP("stringa", "a", "", "string value")
139	if err := f.SetAnnotation("stringa", "key", nil); err != nil {
140		t.Errorf("Unexpected error setting new nil annotation: %v", err)
141	}
142	if annotation := f.Lookup("stringa").Annotations["key"]; annotation != nil {
143		t.Errorf("Unexpected annotation: %v", annotation)
144	}
145
146	f.StringP("stringb", "b", "", "string2 value")
147	if err := f.SetAnnotation("stringb", "key", []string{"value1"}); err != nil {
148		t.Errorf("Unexpected error setting new annotation: %v", err)
149	}
150	if annotation := f.Lookup("stringb").Annotations["key"]; !reflect.DeepEqual(annotation, []string{"value1"}) {
151		t.Errorf("Unexpected annotation: %v", annotation)
152	}
153
154	if err := f.SetAnnotation("stringb", "key", []string{"value2"}); err != nil {
155		t.Errorf("Unexpected error updating annotation: %v", err)
156	}
157	if annotation := f.Lookup("stringb").Annotations["key"]; !reflect.DeepEqual(annotation, []string{"value2"}) {
158		t.Errorf("Unexpected annotation: %v", annotation)
159	}
160}
161
162func testParse(f *FlagSet, t *testing.T) {
163	if f.Parsed() {
164		t.Error("f.Parse() = true before Parse")
165	}
166	boolFlag := f.Bool("bool", false, "bool value")
167	bool2Flag := f.Bool("bool2", false, "bool2 value")
168	bool3Flag := f.Bool("bool3", false, "bool3 value")
169	intFlag := f.Int("int", 0, "int value")
170	int8Flag := f.Int8("int8", 0, "int value")
171	int16Flag := f.Int16("int16", 0, "int value")
172	int32Flag := f.Int32("int32", 0, "int value")
173	int64Flag := f.Int64("int64", 0, "int64 value")
174	uintFlag := f.Uint("uint", 0, "uint value")
175	uint8Flag := f.Uint8("uint8", 0, "uint value")
176	uint16Flag := f.Uint16("uint16", 0, "uint value")
177	uint32Flag := f.Uint32("uint32", 0, "uint value")
178	uint64Flag := f.Uint64("uint64", 0, "uint64 value")
179	stringFlag := f.String("string", "0", "string value")
180	float32Flag := f.Float32("float32", 0, "float32 value")
181	float64Flag := f.Float64("float64", 0, "float64 value")
182	ipFlag := f.IP("ip", net.ParseIP("127.0.0.1"), "ip value")
183	maskFlag := f.IPMask("mask", ParseIPv4Mask("0.0.0.0"), "mask value")
184	durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
185	optionalIntNoValueFlag := f.Int("optional-int-no-value", 0, "int value")
186	f.Lookup("optional-int-no-value").NoOptDefVal = "9"
187	optionalIntWithValueFlag := f.Int("optional-int-with-value", 0, "int value")
188	f.Lookup("optional-int-no-value").NoOptDefVal = "9"
189	extra := "one-extra-argument"
190	args := []string{
191		"--bool",
192		"--bool2=true",
193		"--bool3=false",
194		"--int=22",
195		"--int8=-8",
196		"--int16=-16",
197		"--int32=-32",
198		"--int64=0x23",
199		"--uint", "24",
200		"--uint8=8",
201		"--uint16=16",
202		"--uint32=32",
203		"--uint64=25",
204		"--string=hello",
205		"--float32=-172e12",
206		"--float64=2718e28",
207		"--ip=10.11.12.13",
208		"--mask=255.255.255.0",
209		"--duration=2m",
210		"--optional-int-no-value",
211		"--optional-int-with-value=42",
212		extra,
213	}
214	if err := f.Parse(args); err != nil {
215		t.Fatal(err)
216	}
217	if !f.Parsed() {
218		t.Error("f.Parse() = false after Parse")
219	}
220	if *boolFlag != true {
221		t.Error("bool flag should be true, is ", *boolFlag)
222	}
223	if v, err := f.GetBool("bool"); err != nil || v != *boolFlag {
224		t.Error("GetBool does not work.")
225	}
226	if *bool2Flag != true {
227		t.Error("bool2 flag should be true, is ", *bool2Flag)
228	}
229	if *bool3Flag != false {
230		t.Error("bool3 flag should be false, is ", *bool2Flag)
231	}
232	if *intFlag != 22 {
233		t.Error("int flag should be 22, is ", *intFlag)
234	}
235	if v, err := f.GetInt("int"); err != nil || v != *intFlag {
236		t.Error("GetInt does not work.")
237	}
238	if *int8Flag != -8 {
239		t.Error("int8 flag should be 0x23, is ", *int8Flag)
240	}
241	if *int16Flag != -16 {
242		t.Error("int16 flag should be -16, is ", *int16Flag)
243	}
244	if v, err := f.GetInt8("int8"); err != nil || v != *int8Flag {
245		t.Error("GetInt8 does not work.")
246	}
247	if v, err := f.GetInt16("int16"); err != nil || v != *int16Flag {
248		t.Error("GetInt16 does not work.")
249	}
250	if *int32Flag != -32 {
251		t.Error("int32 flag should be 0x23, is ", *int32Flag)
252	}
253	if v, err := f.GetInt32("int32"); err != nil || v != *int32Flag {
254		t.Error("GetInt32 does not work.")
255	}
256	if *int64Flag != 0x23 {
257		t.Error("int64 flag should be 0x23, is ", *int64Flag)
258	}
259	if v, err := f.GetInt64("int64"); err != nil || v != *int64Flag {
260		t.Error("GetInt64 does not work.")
261	}
262	if *uintFlag != 24 {
263		t.Error("uint flag should be 24, is ", *uintFlag)
264	}
265	if v, err := f.GetUint("uint"); err != nil || v != *uintFlag {
266		t.Error("GetUint does not work.")
267	}
268	if *uint8Flag != 8 {
269		t.Error("uint8 flag should be 8, is ", *uint8Flag)
270	}
271	if v, err := f.GetUint8("uint8"); err != nil || v != *uint8Flag {
272		t.Error("GetUint8 does not work.")
273	}
274	if *uint16Flag != 16 {
275		t.Error("uint16 flag should be 16, is ", *uint16Flag)
276	}
277	if v, err := f.GetUint16("uint16"); err != nil || v != *uint16Flag {
278		t.Error("GetUint16 does not work.")
279	}
280	if *uint32Flag != 32 {
281		t.Error("uint32 flag should be 32, is ", *uint32Flag)
282	}
283	if v, err := f.GetUint32("uint32"); err != nil || v != *uint32Flag {
284		t.Error("GetUint32 does not work.")
285	}
286	if *uint64Flag != 25 {
287		t.Error("uint64 flag should be 25, is ", *uint64Flag)
288	}
289	if v, err := f.GetUint64("uint64"); err != nil || v != *uint64Flag {
290		t.Error("GetUint64 does not work.")
291	}
292	if *stringFlag != "hello" {
293		t.Error("string flag should be `hello`, is ", *stringFlag)
294	}
295	if v, err := f.GetString("string"); err != nil || v != *stringFlag {
296		t.Error("GetString does not work.")
297	}
298	if *float32Flag != -172e12 {
299		t.Error("float32 flag should be -172e12, is ", *float32Flag)
300	}
301	if v, err := f.GetFloat32("float32"); err != nil || v != *float32Flag {
302		t.Errorf("GetFloat32 returned %v but float32Flag was %v", v, *float32Flag)
303	}
304	if *float64Flag != 2718e28 {
305		t.Error("float64 flag should be 2718e28, is ", *float64Flag)
306	}
307	if v, err := f.GetFloat64("float64"); err != nil || v != *float64Flag {
308		t.Errorf("GetFloat64 returned %v but float64Flag was %v", v, *float64Flag)
309	}
310	if !(*ipFlag).Equal(net.ParseIP("10.11.12.13")) {
311		t.Error("ip flag should be 10.11.12.13, is ", *ipFlag)
312	}
313	if v, err := f.GetIP("ip"); err != nil || !v.Equal(*ipFlag) {
314		t.Errorf("GetIP returned %v but ipFlag was %v", v, *ipFlag)
315	}
316	if (*maskFlag).String() != ParseIPv4Mask("255.255.255.0").String() {
317		t.Error("mask flag should be 255.255.255.0, is ", (*maskFlag).String())
318	}
319	if v, err := f.GetIPv4Mask("mask"); err != nil || v.String() != (*maskFlag).String() {
320		t.Errorf("GetIP returned %v maskFlag was %v error was %v", v, *maskFlag, err)
321	}
322	if *durationFlag != 2*time.Minute {
323		t.Error("duration flag should be 2m, is ", *durationFlag)
324	}
325	if v, err := f.GetDuration("duration"); err != nil || v != *durationFlag {
326		t.Error("GetDuration does not work.")
327	}
328	if _, err := f.GetInt("duration"); err == nil {
329		t.Error("GetInt parsed a time.Duration?!?!")
330	}
331	if *optionalIntNoValueFlag != 9 {
332		t.Error("optional int flag should be the default value, is ", *optionalIntNoValueFlag)
333	}
334	if *optionalIntWithValueFlag != 42 {
335		t.Error("optional int flag should be 42, is ", *optionalIntWithValueFlag)
336	}
337	if len(f.Args()) != 1 {
338		t.Error("expected one argument, got", len(f.Args()))
339	} else if f.Args()[0] != extra {
340		t.Errorf("expected argument %q got %q", extra, f.Args()[0])
341	}
342}
343
344func testParseAll(f *FlagSet, t *testing.T) {
345	if f.Parsed() {
346		t.Error("f.Parse() = true before Parse")
347	}
348	f.BoolP("boola", "a", false, "bool value")
349	f.BoolP("boolb", "b", false, "bool2 value")
350	f.BoolP("boolc", "c", false, "bool3 value")
351	f.BoolP("boold", "d", false, "bool4 value")
352	f.StringP("stringa", "s", "0", "string value")
353	f.StringP("stringz", "z", "0", "string value")
354	f.StringP("stringx", "x", "0", "string value")
355	f.StringP("stringy", "y", "0", "string value")
356	f.Lookup("stringx").NoOptDefVal = "1"
357	args := []string{
358		"-ab",
359		"-cs=xx",
360		"--stringz=something",
361		"-d=true",
362		"-x",
363		"-y",
364		"ee",
365	}
366	want := []string{
367		"boola", "true",
368		"boolb", "true",
369		"boolc", "true",
370		"stringa", "xx",
371		"stringz", "something",
372		"boold", "true",
373		"stringx", "1",
374		"stringy", "ee",
375	}
376	got := []string{}
377	store := func(flag *Flag, value string) error {
378		got = append(got, flag.Name)
379		if len(value) > 0 {
380			got = append(got, value)
381		}
382		return nil
383	}
384	if err := f.ParseAll(args, store); err != nil {
385		t.Errorf("expected no error, got %s", err)
386	}
387	if !f.Parsed() {
388		t.Errorf("f.Parse() = false after Parse")
389	}
390	if !reflect.DeepEqual(got, want) {
391		t.Errorf("f.ParseAll() fail to restore the args")
392		t.Errorf("Got:  %v", got)
393		t.Errorf("Want: %v", want)
394	}
395}
396
397func testParseWithUnknownFlags(f *FlagSet, t *testing.T) {
398	if f.Parsed() {
399		t.Error("f.Parse() = true before Parse")
400	}
401	f.ParseErrorsWhitelist.UnknownFlags = true
402
403	f.BoolP("boola", "a", false, "bool value")
404	f.BoolP("boolb", "b", false, "bool2 value")
405	f.BoolP("boolc", "c", false, "bool3 value")
406	f.BoolP("boold", "d", false, "bool4 value")
407	f.BoolP("boole", "e", false, "bool4 value")
408	f.StringP("stringa", "s", "0", "string value")
409	f.StringP("stringz", "z", "0", "string value")
410	f.StringP("stringx", "x", "0", "string value")
411	f.StringP("stringy", "y", "0", "string value")
412	f.StringP("stringo", "o", "0", "string value")
413	f.Lookup("stringx").NoOptDefVal = "1"
414	args := []string{
415		"-ab",
416		"-cs=xx",
417		"--stringz=something",
418		"--unknown1",
419		"unknown1Value",
420		"-d=true",
421		"-x",
422		"--unknown2=unknown2Value",
423		"-u=unknown3Value",
424		"-p",
425		"unknown4Value",
426		"-q", //another unknown with bool value
427		"-y",
428		"ee",
429		"--unknown7=unknown7value",
430		"--stringo=ovalue",
431		"--unknown8=unknown8value",
432		"--boole",
433		"--unknown6",
434		"",
435		"-uuuuu",
436		"",
437		"--unknown10",
438		"--unknown11",
439	}
440	want := []string{
441		"boola", "true",
442		"boolb", "true",
443		"boolc", "true",
444		"stringa", "xx",
445		"stringz", "something",
446		"boold", "true",
447		"stringx", "1",
448		"stringy", "ee",
449		"stringo", "ovalue",
450		"boole", "true",
451	}
452	got := []string{}
453	store := func(flag *Flag, value string) error {
454		got = append(got, flag.Name)
455		if len(value) > 0 {
456			got = append(got, value)
457		}
458		return nil
459	}
460	if err := f.ParseAll(args, store); err != nil {
461		t.Errorf("expected no error, got %s", err)
462	}
463	if !f.Parsed() {
464		t.Errorf("f.Parse() = false after Parse")
465	}
466	if !reflect.DeepEqual(got, want) {
467		t.Errorf("f.ParseAll() fail to restore the args")
468		t.Errorf("Got:  %v", got)
469		t.Errorf("Want: %v", want)
470	}
471}
472
473func TestShorthand(t *testing.T) {
474	f := NewFlagSet("shorthand", ContinueOnError)
475	if f.Parsed() {
476		t.Error("f.Parse() = true before Parse")
477	}
478	boolaFlag := f.BoolP("boola", "a", false, "bool value")
479	boolbFlag := f.BoolP("boolb", "b", false, "bool2 value")
480	boolcFlag := f.BoolP("boolc", "c", false, "bool3 value")
481	booldFlag := f.BoolP("boold", "d", false, "bool4 value")
482	stringaFlag := f.StringP("stringa", "s", "0", "string value")
483	stringzFlag := f.StringP("stringz", "z", "0", "string value")
484	extra := "interspersed-argument"
485	notaflag := "--i-look-like-a-flag"
486	args := []string{
487		"-ab",
488		extra,
489		"-cs",
490		"hello",
491		"-z=something",
492		"-d=true",
493		"--",
494		notaflag,
495	}
496	f.SetOutput(ioutil.Discard)
497	if err := f.Parse(args); err != nil {
498		t.Error("expected no error, got ", err)
499	}
500	if !f.Parsed() {
501		t.Error("f.Parse() = false after Parse")
502	}
503	if *boolaFlag != true {
504		t.Error("boola flag should be true, is ", *boolaFlag)
505	}
506	if *boolbFlag != true {
507		t.Error("boolb flag should be true, is ", *boolbFlag)
508	}
509	if *boolcFlag != true {
510		t.Error("boolc flag should be true, is ", *boolcFlag)
511	}
512	if *booldFlag != true {
513		t.Error("boold flag should be true, is ", *booldFlag)
514	}
515	if *stringaFlag != "hello" {
516		t.Error("stringa flag should be `hello`, is ", *stringaFlag)
517	}
518	if *stringzFlag != "something" {
519		t.Error("stringz flag should be `something`, is ", *stringzFlag)
520	}
521	if len(f.Args()) != 2 {
522		t.Error("expected one argument, got", len(f.Args()))
523	} else if f.Args()[0] != extra {
524		t.Errorf("expected argument %q got %q", extra, f.Args()[0])
525	} else if f.Args()[1] != notaflag {
526		t.Errorf("expected argument %q got %q", notaflag, f.Args()[1])
527	}
528	if f.ArgsLenAtDash() != 1 {
529		t.Errorf("expected argsLenAtDash %d got %d", f.ArgsLenAtDash(), 1)
530	}
531}
532
533func TestShorthandLookup(t *testing.T) {
534	f := NewFlagSet("shorthand", ContinueOnError)
535	if f.Parsed() {
536		t.Error("f.Parse() = true before Parse")
537	}
538	f.BoolP("boola", "a", false, "bool value")
539	f.BoolP("boolb", "b", false, "bool2 value")
540	args := []string{
541		"-ab",
542	}
543	f.SetOutput(ioutil.Discard)
544	if err := f.Parse(args); err != nil {
545		t.Error("expected no error, got ", err)
546	}
547	if !f.Parsed() {
548		t.Error("f.Parse() = false after Parse")
549	}
550	flag := f.ShorthandLookup("a")
551	if flag == nil {
552		t.Errorf("f.ShorthandLookup(\"a\") returned nil")
553	}
554	if flag.Name != "boola" {
555		t.Errorf("f.ShorthandLookup(\"a\") found %q instead of \"boola\"", flag.Name)
556	}
557	flag = f.ShorthandLookup("")
558	if flag != nil {
559		t.Errorf("f.ShorthandLookup(\"\") did not return nil")
560	}
561	defer func() {
562		recover()
563	}()
564	flag = f.ShorthandLookup("ab")
565	// should NEVER get here. lookup should panic. defer'd func should recover it.
566	t.Errorf("f.ShorthandLookup(\"ab\") did not panic")
567}
568
569func TestParse(t *testing.T) {
570	ResetForTesting(func() { t.Error("bad parse") })
571	testParse(GetCommandLine(), t)
572}
573
574func TestParseAll(t *testing.T) {
575	ResetForTesting(func() { t.Error("bad parse") })
576	testParseAll(GetCommandLine(), t)
577}
578
579func TestIgnoreUnknownFlags(t *testing.T) {
580	ResetForTesting(func() { t.Error("bad parse") })
581	testParseWithUnknownFlags(GetCommandLine(), t)
582}
583
584func TestFlagSetParse(t *testing.T) {
585	testParse(NewFlagSet("test", ContinueOnError), t)
586}
587
588func TestChangedHelper(t *testing.T) {
589	f := NewFlagSet("changedtest", ContinueOnError)
590	f.Bool("changed", false, "changed bool")
591	f.Bool("settrue", true, "true to true")
592	f.Bool("setfalse", false, "false to false")
593	f.Bool("unchanged", false, "unchanged bool")
594
595	args := []string{"--changed", "--settrue", "--setfalse=false"}
596	if err := f.Parse(args); err != nil {
597		t.Error("f.Parse() = false after Parse")
598	}
599	if !f.Changed("changed") {
600		t.Errorf("--changed wasn't changed!")
601	}
602	if !f.Changed("settrue") {
603		t.Errorf("--settrue wasn't changed!")
604	}
605	if !f.Changed("setfalse") {
606		t.Errorf("--setfalse wasn't changed!")
607	}
608	if f.Changed("unchanged") {
609		t.Errorf("--unchanged was changed!")
610	}
611	if f.Changed("invalid") {
612		t.Errorf("--invalid was changed!")
613	}
614	if f.ArgsLenAtDash() != -1 {
615		t.Errorf("Expected argsLenAtDash: %d but got %d", -1, f.ArgsLenAtDash())
616	}
617}
618
619func replaceSeparators(name string, from []string, to string) string {
620	result := name
621	for _, sep := range from {
622		result = strings.Replace(result, sep, to, -1)
623	}
624	// Type convert to indicate normalization has been done.
625	return result
626}
627
628func wordSepNormalizeFunc(f *FlagSet, name string) NormalizedName {
629	seps := []string{"-", "_"}
630	name = replaceSeparators(name, seps, ".")
631	normalizeFlagNameInvocations++
632
633	return NormalizedName(name)
634}
635
636func testWordSepNormalizedNames(args []string, t *testing.T) {
637	f := NewFlagSet("normalized", ContinueOnError)
638	if f.Parsed() {
639		t.Error("f.Parse() = true before Parse")
640	}
641	withDashFlag := f.Bool("with-dash-flag", false, "bool value")
642	// Set this after some flags have been added and before others.
643	f.SetNormalizeFunc(wordSepNormalizeFunc)
644	withUnderFlag := f.Bool("with_under_flag", false, "bool value")
645	withBothFlag := f.Bool("with-both_flag", false, "bool value")
646	if err := f.Parse(args); err != nil {
647		t.Fatal(err)
648	}
649	if !f.Parsed() {
650		t.Error("f.Parse() = false after Parse")
651	}
652	if *withDashFlag != true {
653		t.Error("withDashFlag flag should be true, is ", *withDashFlag)
654	}
655	if *withUnderFlag != true {
656		t.Error("withUnderFlag flag should be true, is ", *withUnderFlag)
657	}
658	if *withBothFlag != true {
659		t.Error("withBothFlag flag should be true, is ", *withBothFlag)
660	}
661}
662
663func TestWordSepNormalizedNames(t *testing.T) {
664	args := []string{
665		"--with-dash-flag",
666		"--with-under-flag",
667		"--with-both-flag",
668	}
669	testWordSepNormalizedNames(args, t)
670
671	args = []string{
672		"--with_dash_flag",
673		"--with_under_flag",
674		"--with_both_flag",
675	}
676	testWordSepNormalizedNames(args, t)
677
678	args = []string{
679		"--with-dash_flag",
680		"--with-under_flag",
681		"--with-both_flag",
682	}
683	testWordSepNormalizedNames(args, t)
684}
685
686func aliasAndWordSepFlagNames(f *FlagSet, name string) NormalizedName {
687	seps := []string{"-", "_"}
688
689	oldName := replaceSeparators("old-valid_flag", seps, ".")
690	newName := replaceSeparators("valid-flag", seps, ".")
691
692	name = replaceSeparators(name, seps, ".")
693	switch name {
694	case oldName:
695		name = newName
696	}
697
698	return NormalizedName(name)
699}
700
701func TestCustomNormalizedNames(t *testing.T) {
702	f := NewFlagSet("normalized", ContinueOnError)
703	if f.Parsed() {
704		t.Error("f.Parse() = true before Parse")
705	}
706
707	validFlag := f.Bool("valid-flag", false, "bool value")
708	f.SetNormalizeFunc(aliasAndWordSepFlagNames)
709	someOtherFlag := f.Bool("some-other-flag", false, "bool value")
710
711	args := []string{"--old_valid_flag", "--some-other_flag"}
712	if err := f.Parse(args); err != nil {
713		t.Fatal(err)
714	}
715
716	if *validFlag != true {
717		t.Errorf("validFlag is %v even though we set the alias --old_valid_falg", *validFlag)
718	}
719	if *someOtherFlag != true {
720		t.Error("someOtherFlag should be true, is ", *someOtherFlag)
721	}
722}
723
724// Every flag we add, the name (displayed also in usage) should normalized
725func TestNormalizationFuncShouldChangeFlagName(t *testing.T) {
726	// Test normalization after addition
727	f := NewFlagSet("normalized", ContinueOnError)
728
729	f.Bool("valid_flag", false, "bool value")
730	if f.Lookup("valid_flag").Name != "valid_flag" {
731		t.Error("The new flag should have the name 'valid_flag' instead of ", f.Lookup("valid_flag").Name)
732	}
733
734	f.SetNormalizeFunc(wordSepNormalizeFunc)
735	if f.Lookup("valid_flag").Name != "valid.flag" {
736		t.Error("The new flag should have the name 'valid.flag' instead of ", f.Lookup("valid_flag").Name)
737	}
738
739	// Test normalization before addition
740	f = NewFlagSet("normalized", ContinueOnError)
741	f.SetNormalizeFunc(wordSepNormalizeFunc)
742
743	f.Bool("valid_flag", false, "bool value")
744	if f.Lookup("valid_flag").Name != "valid.flag" {
745		t.Error("The new flag should have the name 'valid.flag' instead of ", f.Lookup("valid_flag").Name)
746	}
747}
748
749// Related to https://github.com/spf13/cobra/issues/521.
750func TestNormalizationSharedFlags(t *testing.T) {
751	f := NewFlagSet("set f", ContinueOnError)
752	g := NewFlagSet("set g", ContinueOnError)
753	nfunc := wordSepNormalizeFunc
754	testName := "valid_flag"
755	normName := nfunc(nil, testName)
756	if testName == string(normName) {
757		t.Error("TestNormalizationSharedFlags meaningless: the original and normalized flag names are identical:", testName)
758	}
759
760	f.Bool(testName, false, "bool value")
761	g.AddFlagSet(f)
762
763	f.SetNormalizeFunc(nfunc)
764	g.SetNormalizeFunc(nfunc)
765
766	if len(f.formal) != 1 {
767		t.Error("Normalizing flags should not result in duplications in the flag set:", f.formal)
768	}
769	if f.orderedFormal[0].Name != string(normName) {
770		t.Error("Flag name not normalized")
771	}
772	for k := range f.formal {
773		if k != "valid.flag" {
774			t.Errorf("The key in the flag map should have been normalized: wanted \"%s\", got \"%s\" instead", normName, k)
775		}
776	}
777
778	if !reflect.DeepEqual(f.formal, g.formal) || !reflect.DeepEqual(f.orderedFormal, g.orderedFormal) {
779		t.Error("Two flag sets sharing the same flags should stay consistent after being normalized. Original set:", f.formal, "Duplicate set:", g.formal)
780	}
781}
782
783func TestNormalizationSetFlags(t *testing.T) {
784	f := NewFlagSet("normalized", ContinueOnError)
785	nfunc := wordSepNormalizeFunc
786	testName := "valid_flag"
787	normName := nfunc(nil, testName)
788	if testName == string(normName) {
789		t.Error("TestNormalizationSetFlags meaningless: the original and normalized flag names are identical:", testName)
790	}
791
792	f.Bool(testName, false, "bool value")
793	f.Set(testName, "true")
794	f.SetNormalizeFunc(nfunc)
795
796	if len(f.formal) != 1 {
797		t.Error("Normalizing flags should not result in duplications in the flag set:", f.formal)
798	}
799	if f.orderedFormal[0].Name != string(normName) {
800		t.Error("Flag name not normalized")
801	}
802	for k := range f.formal {
803		if k != "valid.flag" {
804			t.Errorf("The key in the flag map should have been normalized: wanted \"%s\", got \"%s\" instead", normName, k)
805		}
806	}
807
808	if !reflect.DeepEqual(f.formal, f.actual) {
809		t.Error("The map of set flags should get normalized. Formal:", f.formal, "Actual:", f.actual)
810	}
811}
812
813// Declare a user-defined flag type.
814type flagVar []string
815
816func (f *flagVar) String() string {
817	return fmt.Sprint([]string(*f))
818}
819
820func (f *flagVar) Set(value string) error {
821	*f = append(*f, value)
822	return nil
823}
824
825func (f *flagVar) Type() string {
826	return "flagVar"
827}
828
829func TestUserDefined(t *testing.T) {
830	var flags FlagSet
831	flags.Init("test", ContinueOnError)
832	var v flagVar
833	flags.VarP(&v, "v", "v", "usage")
834	if err := flags.Parse([]string{"--v=1", "-v2", "-v", "3"}); err != nil {
835		t.Error(err)
836	}
837	if len(v) != 3 {
838		t.Fatal("expected 3 args; got ", len(v))
839	}
840	expect := "[1 2 3]"
841	if v.String() != expect {
842		t.Errorf("expected value %q got %q", expect, v.String())
843	}
844}
845
846func TestSetOutput(t *testing.T) {
847	var flags FlagSet
848	var buf bytes.Buffer
849	flags.SetOutput(&buf)
850	flags.Init("test", ContinueOnError)
851	flags.Parse([]string{"--unknown"})
852	if out := buf.String(); !strings.Contains(out, "--unknown") {
853		t.Logf("expected output mentioning unknown; got %q", out)
854	}
855}
856
857// This tests that one can reset the flags. This still works but not well, and is
858// superseded by FlagSet.
859func TestChangingArgs(t *testing.T) {
860	ResetForTesting(func() { t.Fatal("bad parse") })
861	oldArgs := os.Args
862	defer func() { os.Args = oldArgs }()
863	os.Args = []string{"cmd", "--before", "subcmd"}
864	before := Bool("before", false, "")
865	if err := GetCommandLine().Parse(os.Args[1:]); err != nil {
866		t.Fatal(err)
867	}
868	cmd := Arg(0)
869	os.Args = []string{"subcmd", "--after", "args"}
870	after := Bool("after", false, "")
871	Parse()
872	args := Args()
873
874	if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
875		t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
876	}
877}
878
879// Test that -help invokes the usage message and returns ErrHelp.
880func TestHelp(t *testing.T) {
881	var helpCalled = false
882	fs := NewFlagSet("help test", ContinueOnError)
883	fs.Usage = func() { helpCalled = true }
884	var flag bool
885	fs.BoolVar(&flag, "flag", false, "regular flag")
886	// Regular flag invocation should work
887	err := fs.Parse([]string{"--flag=true"})
888	if err != nil {
889		t.Fatal("expected no error; got ", err)
890	}
891	if !flag {
892		t.Error("flag was not set by --flag")
893	}
894	if helpCalled {
895		t.Error("help called for regular flag")
896		helpCalled = false // reset for next test
897	}
898	// Help flag should work as expected.
899	err = fs.Parse([]string{"--help"})
900	if err == nil {
901		t.Fatal("error expected")
902	}
903	if err != ErrHelp {
904		t.Fatal("expected ErrHelp; got ", err)
905	}
906	if !helpCalled {
907		t.Fatal("help was not called")
908	}
909	// If we define a help flag, that should override.
910	var help bool
911	fs.BoolVar(&help, "help", false, "help flag")
912	helpCalled = false
913	err = fs.Parse([]string{"--help"})
914	if err != nil {
915		t.Fatal("expected no error for defined --help; got ", err)
916	}
917	if helpCalled {
918		t.Fatal("help was called; should not have been for defined help flag")
919	}
920}
921
922func TestNoInterspersed(t *testing.T) {
923	f := NewFlagSet("test", ContinueOnError)
924	f.SetInterspersed(false)
925	f.Bool("true", true, "always true")
926	f.Bool("false", false, "always false")
927	err := f.Parse([]string{"--true", "break", "--false"})
928	if err != nil {
929		t.Fatal("expected no error; got ", err)
930	}
931	args := f.Args()
932	if len(args) != 2 || args[0] != "break" || args[1] != "--false" {
933		t.Fatal("expected interspersed options/non-options to fail")
934	}
935}
936
937func TestTermination(t *testing.T) {
938	f := NewFlagSet("termination", ContinueOnError)
939	boolFlag := f.BoolP("bool", "l", false, "bool value")
940	if f.Parsed() {
941		t.Error("f.Parse() = true before Parse")
942	}
943	arg1 := "ls"
944	arg2 := "-l"
945	args := []string{
946		"--",
947		arg1,
948		arg2,
949	}
950	f.SetOutput(ioutil.Discard)
951	if err := f.Parse(args); err != nil {
952		t.Fatal("expected no error; got ", err)
953	}
954	if !f.Parsed() {
955		t.Error("f.Parse() = false after Parse")
956	}
957	if *boolFlag {
958		t.Error("expected boolFlag=false, got true")
959	}
960	if len(f.Args()) != 2 {
961		t.Errorf("expected 2 arguments, got %d: %v", len(f.Args()), f.Args())
962	}
963	if f.Args()[0] != arg1 {
964		t.Errorf("expected argument %q got %q", arg1, f.Args()[0])
965	}
966	if f.Args()[1] != arg2 {
967		t.Errorf("expected argument %q got %q", arg2, f.Args()[1])
968	}
969	if f.ArgsLenAtDash() != 0 {
970		t.Errorf("expected argsLenAtDash %d got %d", 0, f.ArgsLenAtDash())
971	}
972}
973
974func getDeprecatedFlagSet() *FlagSet {
975	f := NewFlagSet("bob", ContinueOnError)
976	f.Bool("badflag", true, "always true")
977	f.MarkDeprecated("badflag", "use --good-flag instead")
978	return f
979}
980func TestDeprecatedFlagInDocs(t *testing.T) {
981	f := getDeprecatedFlagSet()
982
983	out := new(bytes.Buffer)
984	f.SetOutput(out)
985	f.PrintDefaults()
986
987	if strings.Contains(out.String(), "badflag") {
988		t.Errorf("found deprecated flag in usage!")
989	}
990}
991
992func TestUnHiddenDeprecatedFlagInDocs(t *testing.T) {
993	f := getDeprecatedFlagSet()
994	flg := f.Lookup("badflag")
995	if flg == nil {
996		t.Fatalf("Unable to lookup 'bob' in TestUnHiddenDeprecatedFlagInDocs")
997	}
998	flg.Hidden = false
999
1000	out := new(bytes.Buffer)
1001	f.SetOutput(out)
1002	f.PrintDefaults()
1003
1004	defaults := out.String()
1005	if !strings.Contains(defaults, "badflag") {
1006		t.Errorf("Did not find deprecated flag in usage!")
1007	}
1008	if !strings.Contains(defaults, "use --good-flag instead") {
1009		t.Errorf("Did not find 'use --good-flag instead' in defaults")
1010	}
1011}
1012
1013func TestDeprecatedFlagShorthandInDocs(t *testing.T) {
1014	f := NewFlagSet("bob", ContinueOnError)
1015	name := "noshorthandflag"
1016	f.BoolP(name, "n", true, "always true")
1017	f.MarkShorthandDeprecated("noshorthandflag", fmt.Sprintf("use --%s instead", name))
1018
1019	out := new(bytes.Buffer)
1020	f.SetOutput(out)
1021	f.PrintDefaults()
1022
1023	if strings.Contains(out.String(), "-n,") {
1024		t.Errorf("found deprecated flag shorthand in usage!")
1025	}
1026}
1027
1028func parseReturnStderr(t *testing.T, f *FlagSet, args []string) (string, error) {
1029	oldStderr := os.Stderr
1030	r, w, _ := os.Pipe()
1031	os.Stderr = w
1032
1033	err := f.Parse(args)
1034
1035	outC := make(chan string)
1036	// copy the output in a separate goroutine so printing can't block indefinitely
1037	go func() {
1038		var buf bytes.Buffer
1039		io.Copy(&buf, r)
1040		outC <- buf.String()
1041	}()
1042
1043	w.Close()
1044	os.Stderr = oldStderr
1045	out := <-outC
1046
1047	return out, err
1048}
1049
1050func TestDeprecatedFlagUsage(t *testing.T) {
1051	f := NewFlagSet("bob", ContinueOnError)
1052	f.Bool("badflag", true, "always true")
1053	usageMsg := "use --good-flag instead"
1054	f.MarkDeprecated("badflag", usageMsg)
1055
1056	args := []string{"--badflag"}
1057	out, err := parseReturnStderr(t, f, args)
1058	if err != nil {
1059		t.Fatal("expected no error; got ", err)
1060	}
1061
1062	if !strings.Contains(out, usageMsg) {
1063		t.Errorf("usageMsg not printed when using a deprecated flag!")
1064	}
1065}
1066
1067func TestDeprecatedFlagShorthandUsage(t *testing.T) {
1068	f := NewFlagSet("bob", ContinueOnError)
1069	name := "noshorthandflag"
1070	f.BoolP(name, "n", true, "always true")
1071	usageMsg := fmt.Sprintf("use --%s instead", name)
1072	f.MarkShorthandDeprecated(name, usageMsg)
1073
1074	args := []string{"-n"}
1075	out, err := parseReturnStderr(t, f, args)
1076	if err != nil {
1077		t.Fatal("expected no error; got ", err)
1078	}
1079
1080	if !strings.Contains(out, usageMsg) {
1081		t.Errorf("usageMsg not printed when using a deprecated flag!")
1082	}
1083}
1084
1085func TestDeprecatedFlagUsageNormalized(t *testing.T) {
1086	f := NewFlagSet("bob", ContinueOnError)
1087	f.Bool("bad-double_flag", true, "always true")
1088	f.SetNormalizeFunc(wordSepNormalizeFunc)
1089	usageMsg := "use --good-flag instead"
1090	f.MarkDeprecated("bad_double-flag", usageMsg)
1091
1092	args := []string{"--bad_double_flag"}
1093	out, err := parseReturnStderr(t, f, args)
1094	if err != nil {
1095		t.Fatal("expected no error; got ", err)
1096	}
1097
1098	if !strings.Contains(out, usageMsg) {
1099		t.Errorf("usageMsg not printed when using a deprecated flag!")
1100	}
1101}
1102
1103// Name normalization function should be called only once on flag addition
1104func TestMultipleNormalizeFlagNameInvocations(t *testing.T) {
1105	normalizeFlagNameInvocations = 0
1106
1107	f := NewFlagSet("normalized", ContinueOnError)
1108	f.SetNormalizeFunc(wordSepNormalizeFunc)
1109	f.Bool("with_under_flag", false, "bool value")
1110
1111	if normalizeFlagNameInvocations != 1 {
1112		t.Fatal("Expected normalizeFlagNameInvocations to be 1; got ", normalizeFlagNameInvocations)
1113	}
1114}
1115
1116//
1117func TestHiddenFlagInUsage(t *testing.T) {
1118	f := NewFlagSet("bob", ContinueOnError)
1119	f.Bool("secretFlag", true, "shhh")
1120	f.MarkHidden("secretFlag")
1121
1122	out := new(bytes.Buffer)
1123	f.SetOutput(out)
1124	f.PrintDefaults()
1125
1126	if strings.Contains(out.String(), "secretFlag") {
1127		t.Errorf("found hidden flag in usage!")
1128	}
1129}
1130
1131//
1132func TestHiddenFlagUsage(t *testing.T) {
1133	f := NewFlagSet("bob", ContinueOnError)
1134	f.Bool("secretFlag", true, "shhh")
1135	f.MarkHidden("secretFlag")
1136
1137	args := []string{"--secretFlag"}
1138	out, err := parseReturnStderr(t, f, args)
1139	if err != nil {
1140		t.Fatal("expected no error; got ", err)
1141	}
1142
1143	if strings.Contains(out, "shhh") {
1144		t.Errorf("usage message printed when using a hidden flag!")
1145	}
1146}
1147
1148const defaultOutput = `      --A                         for bootstrapping, allow 'any' type
1149      --Alongflagname             disable bounds checking
1150  -C, --CCC                       a boolean defaulting to true (default true)
1151      --D path                    set relative path for local imports
1152  -E, --EEE num[=1234]            a num with NoOptDefVal (default 4321)
1153      --F number                  a non-zero number (default 2.7)
1154      --G float                   a float that defaults to zero
1155      --IP ip                     IP address with no default
1156      --IPMask ipMask             Netmask address with no default
1157      --IPNet ipNet               IP network with no default
1158      --Ints ints                 int slice with zero default
1159      --N int                     a non-zero int (default 27)
1160      --ND1 string[="bar"]        a string with NoOptDefVal (default "foo")
1161      --ND2 num[=4321]            a num with NoOptDefVal (default 1234)
1162      --StringArray stringArray   string array with zero default
1163      --StringSlice strings       string slice with zero default
1164      --Z int                     an int that defaults to zero
1165      --custom custom             custom Value implementation
1166      --customP custom            a VarP with default (default 10)
1167      --maxT timeout              set timeout for dial
1168  -v, --verbose count             verbosity
1169`
1170
1171// Custom value that satisfies the Value interface.
1172type customValue int
1173
1174func (cv *customValue) String() string { return fmt.Sprintf("%v", *cv) }
1175
1176func (cv *customValue) Set(s string) error {
1177	v, err := strconv.ParseInt(s, 0, 64)
1178	*cv = customValue(v)
1179	return err
1180}
1181
1182func (cv *customValue) Type() string { return "custom" }
1183
1184func TestPrintDefaults(t *testing.T) {
1185	fs := NewFlagSet("print defaults test", ContinueOnError)
1186	var buf bytes.Buffer
1187	fs.SetOutput(&buf)
1188	fs.Bool("A", false, "for bootstrapping, allow 'any' type")
1189	fs.Bool("Alongflagname", false, "disable bounds checking")
1190	fs.BoolP("CCC", "C", true, "a boolean defaulting to true")
1191	fs.String("D", "", "set relative `path` for local imports")
1192	fs.Float64("F", 2.7, "a non-zero `number`")
1193	fs.Float64("G", 0, "a float that defaults to zero")
1194	fs.Int("N", 27, "a non-zero int")
1195	fs.IntSlice("Ints", []int{}, "int slice with zero default")
1196	fs.IP("IP", nil, "IP address with no default")
1197	fs.IPMask("IPMask", nil, "Netmask address with no default")
1198	fs.IPNet("IPNet", net.IPNet{}, "IP network with no default")
1199	fs.Int("Z", 0, "an int that defaults to zero")
1200	fs.Duration("maxT", 0, "set `timeout` for dial")
1201	fs.String("ND1", "foo", "a string with NoOptDefVal")
1202	fs.Lookup("ND1").NoOptDefVal = "bar"
1203	fs.Int("ND2", 1234, "a `num` with NoOptDefVal")
1204	fs.Lookup("ND2").NoOptDefVal = "4321"
1205	fs.IntP("EEE", "E", 4321, "a `num` with NoOptDefVal")
1206	fs.ShorthandLookup("E").NoOptDefVal = "1234"
1207	fs.StringSlice("StringSlice", []string{}, "string slice with zero default")
1208	fs.StringArray("StringArray", []string{}, "string array with zero default")
1209	fs.CountP("verbose", "v", "verbosity")
1210
1211	var cv customValue
1212	fs.Var(&cv, "custom", "custom Value implementation")
1213
1214	cv2 := customValue(10)
1215	fs.VarP(&cv2, "customP", "", "a VarP with default")
1216
1217	fs.PrintDefaults()
1218	got := buf.String()
1219	if got != defaultOutput {
1220		fmt.Println("\n" + got)
1221		fmt.Println("\n" + defaultOutput)
1222		t.Errorf("got %q want %q\n", got, defaultOutput)
1223	}
1224}
1225
1226func TestVisitAllFlagOrder(t *testing.T) {
1227	fs := NewFlagSet("TestVisitAllFlagOrder", ContinueOnError)
1228	fs.SortFlags = false
1229	// https://github.com/spf13/pflag/issues/120
1230	fs.SetNormalizeFunc(func(f *FlagSet, name string) NormalizedName {
1231		return NormalizedName(name)
1232	})
1233
1234	names := []string{"C", "B", "A", "D"}
1235	for _, name := range names {
1236		fs.Bool(name, false, "")
1237	}
1238
1239	i := 0
1240	fs.VisitAll(func(f *Flag) {
1241		if names[i] != f.Name {
1242			t.Errorf("Incorrect order. Expected %v, got %v", names[i], f.Name)
1243		}
1244		i++
1245	})
1246}
1247
1248func TestVisitFlagOrder(t *testing.T) {
1249	fs := NewFlagSet("TestVisitFlagOrder", ContinueOnError)
1250	fs.SortFlags = false
1251	names := []string{"C", "B", "A", "D"}
1252	for _, name := range names {
1253		fs.Bool(name, false, "")
1254		fs.Set(name, "true")
1255	}
1256
1257	i := 0
1258	fs.Visit(func(f *Flag) {
1259		if names[i] != f.Name {
1260			t.Errorf("Incorrect order. Expected %v, got %v", names[i], f.Name)
1261		}
1262		i++
1263	})
1264}
1265