...

Source file src/github.com/peterbourgon/ff/v3/fftest/vars.go

Documentation: github.com/peterbourgon/ff/v3/fftest

     1  package fftest
     2  
     3  import (
     4  	"errors"
     5  	"flag"
     6  	"reflect"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  )
    11  
    12  // Pair defines and returns an empty flag set and vars assigned to it.
    13  func Pair() (*flag.FlagSet, *Vars) {
    14  	fs := flag.NewFlagSet("fftest", flag.ContinueOnError)
    15  	vars := DefaultVars(fs)
    16  	return fs, vars
    17  }
    18  
    19  // DefaultVars registers a predefined set of variables to the flag set.
    20  // Tests can call parse on the flag set with a variety of flags, config files,
    21  // and env vars, and check the resulting effect on the vars.
    22  func DefaultVars(fs *flag.FlagSet) *Vars {
    23  	var v Vars
    24  	fs.StringVar(&v.S, "s", "", "string")
    25  	fs.IntVar(&v.I, "i", 0, "int")
    26  	fs.Float64Var(&v.F, "f", 0., "float64")
    27  	fs.BoolVar(&v.B, "b", false, "bool")
    28  	fs.DurationVar(&v.D, "d", 0*time.Second, "time.Duration")
    29  	fs.Var(&v.X, "x", "collection of strings (repeatable)")
    30  	return &v
    31  }
    32  
    33  // NonzeroDefaultVars is like DefaultVars, but provides each primitive flag with
    34  // a nonzero default value. This is useful for tests that explicitly provide a
    35  // zero value for the type.
    36  func NonzeroDefaultVars(fs *flag.FlagSet) *Vars {
    37  	var v Vars
    38  	fs.StringVar(&v.S, "s", "foo", "string")
    39  	fs.IntVar(&v.I, "i", 123, "int")
    40  	fs.Float64Var(&v.F, "f", 9.99, "float64")
    41  	fs.BoolVar(&v.B, "b", true, "bool")
    42  	fs.DurationVar(&v.D, "d", 3*time.Hour, "time.Duration")
    43  	fs.Var(&v.X, "x", "collection of strings (repeatable)")
    44  	return &v
    45  }
    46  
    47  // Vars are a common set of variables used for testing.
    48  type Vars struct {
    49  	S string
    50  	I int
    51  	F float64
    52  	B bool
    53  	D time.Duration
    54  	X StringSlice
    55  
    56  	// ParseError should be assigned as the result of Parse in tests.
    57  	ParseError error
    58  
    59  	// If a test case expects an input to generate a parse error,
    60  	// it can specify that error here. The Compare helper will
    61  	// look for it using errors.Is.
    62  	WantParseErrorIs error
    63  
    64  	// If a test case expects an input to generate a parse error,
    65  	// it can specify part of that error string here. The Compare
    66  	// helper will look for it using strings.Contains.
    67  	WantParseErrorString string
    68  }
    69  
    70  // Compare one set of vars with another
    71  // and t.Error on any difference.
    72  func Compare(t *testing.T, want, have *Vars) {
    73  	t.Helper()
    74  
    75  	if want.WantParseErrorIs != nil || want.WantParseErrorString != "" {
    76  		if want.WantParseErrorIs != nil && have.ParseError == nil {
    77  			t.Errorf("want error (%v), have none", want.WantParseErrorIs)
    78  		}
    79  		if want.WantParseErrorString != "" && have.ParseError == nil {
    80  			t.Errorf("want error (%q), have none", want.WantParseErrorString)
    81  		}
    82  		if want.WantParseErrorIs == nil && want.WantParseErrorString == "" && have.ParseError != nil {
    83  			t.Errorf("want clean parse, have error (%v)", have.ParseError)
    84  		}
    85  		if want.WantParseErrorIs != nil && have.ParseError != nil && !errors.Is(have.ParseError, want.WantParseErrorIs) {
    86  			t.Errorf("want wrapped error (%#+v), have error (%#+v)", want.WantParseErrorIs, have.ParseError)
    87  		}
    88  		if want.WantParseErrorString != "" && have.ParseError != nil && !strings.Contains(have.ParseError.Error(), want.WantParseErrorString) {
    89  			t.Errorf("want error string (%q), have error (%v)", want.WantParseErrorString, have.ParseError)
    90  		}
    91  		return
    92  	}
    93  
    94  	if have.ParseError != nil {
    95  		t.Errorf("error: %v", have.ParseError)
    96  	}
    97  
    98  	if want.S != have.S {
    99  		t.Errorf("var S: want %q, have %q", want.S, have.S)
   100  	}
   101  	if want.I != have.I {
   102  		t.Errorf("var I: want %d, have %d", want.I, have.I)
   103  	}
   104  	if want.F != have.F {
   105  		t.Errorf("var F: want %f, have %f", want.F, have.F)
   106  	}
   107  	if want.B != have.B {
   108  		t.Errorf("var B: want %v, have %v", want.B, have.B)
   109  	}
   110  	if want.D != have.D {
   111  		t.Errorf("var D: want %s, have %s", want.D, have.D)
   112  	}
   113  	if !reflect.DeepEqual(want.X, have.X) {
   114  		t.Errorf("var X: want %v, have %v", want.X, have.X)
   115  	}
   116  }
   117  
   118  // StringSlice is a flag.Value that collects each Set string
   119  // into a slice, allowing for repeated flags.
   120  type StringSlice []string
   121  
   122  // Set implements flag.Value and appends the string to the slice.
   123  func (ss *StringSlice) Set(s string) error {
   124  	(*ss) = append(*ss, s)
   125  	return nil
   126  }
   127  
   128  // String implements flag.Value and returns the list of
   129  // strings, or "..." if no strings have been added.
   130  func (ss *StringSlice) String() string {
   131  	if len(*ss) <= 0 {
   132  		return "..."
   133  	}
   134  	return strings.Join(*ss, ", ")
   135  }
   136  

View as plain text