Context tests and tweaks around lineage traversal

per feedback on #410
This commit is contained in:
Dan Buch 2016-05-17 03:16:26 -04:00
parent bef835d455
commit c720f37e15
No known key found for this signature in database
GPG Key ID: FAEF12936DD3E3EC
2 changed files with 72 additions and 57 deletions

View File

@ -13,12 +13,11 @@ import (
// can be used to retrieve context-specific Args and // can be used to retrieve context-specific Args and
// parsed command-line options. // parsed command-line options.
type Context struct { type Context struct {
App *App App *App
Command Command Command Command
flagSet *flag.FlagSet
setFlags map[string]bool flagSet *flag.FlagSet
globalSetFlags map[string]bool parentContext *Context
parentContext *Context
} }
// NewContext creates a new context. For use in when invoking an App or Command action. // NewContext creates a new context. For use in when invoking an App or Command action.
@ -117,7 +116,7 @@ func (c *Context) Set(name, value string) error {
func (c *Context) IsSet(name string) bool { func (c *Context) IsSet(name string) bool {
if fs := lookupFlagSet(name, c); fs != nil { if fs := lookupFlagSet(name, c); fs != nil {
isSet := false isSet := false
c.flagSet.Visit(func(f *flag.Flag) { fs.Visit(func(f *flag.Flag) {
if f.Name == name { if f.Name == name {
isSet = true isSet = true
} }
@ -146,25 +145,13 @@ func (c *Context) FlagNames() []string {
return names return names
} }
// Parent returns the parent context, if any
func (c *Context) Parent() *Context {
return c.parentContext
}
// Lineage returns *this* context and all of its ancestor contexts in order from // Lineage returns *this* context and all of its ancestor contexts in order from
// child to parent // child to parent
func (c *Context) Lineage() []*Context { func (c *Context) Lineage() []*Context {
lineage := []*Context{} lineage := []*Context{}
cur := c
for { for cur := c; cur != nil; cur = cur.parentContext {
lineage = append(lineage, cur) lineage = append(lineage, cur)
if cur.parentContext == nil {
break
}
cur = cur.parentContext
} }
return lineage return lineage
@ -220,19 +207,6 @@ func (a Args) Swap(from, to int) error {
return nil return nil
} }
func globalContext(ctx *Context) *Context {
if ctx == nil {
return nil
}
for {
if ctx.parentContext == nil {
return ctx
}
ctx = ctx.parentContext
}
}
func lookupFlagSet(name string, ctx *Context) *flag.FlagSet { func lookupFlagSet(name string, ctx *Context) *flag.FlagSet {
for _, c := range ctx.Lineage() { for _, c := range ctx.Lineage() {
if f := c.flagSet.Lookup(name); f != nil { if f := c.flagSet.Lookup(name); f != nil {

View File

@ -84,18 +84,22 @@ func TestContext_NArg(t *testing.T) {
func TestContext_IsSet(t *testing.T) { func TestContext_IsSet(t *testing.T) {
set := flag.NewFlagSet("test", 0) set := flag.NewFlagSet("test", 0)
set.Bool("myflag", false, "doc") set.Bool("one-flag", false, "doc")
set.String("otherflag", "hello world", "doc") set.Bool("two-flag", false, "doc")
globalSet := flag.NewFlagSet("test", 0) set.String("three-flag", "hello world", "doc")
globalSet.Bool("myflagGlobal", true, "doc") parentSet := flag.NewFlagSet("test", 0)
globalCtx := NewContext(nil, globalSet, nil) parentSet.Bool("top-flag", true, "doc")
c := NewContext(nil, set, globalCtx) parentCtx := NewContext(nil, parentSet, nil)
set.Parse([]string{"--myflag", "bat", "baz"}) ctx := NewContext(nil, set, parentCtx)
globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"})
expect(t, c.IsSet("myflag"), true) set.Parse([]string{"--one-flag", "--two-flag", "--three-flag", "frob"})
expect(t, c.IsSet("otherflag"), false) parentSet.Parse([]string{"--top-flag"})
expect(t, c.IsSet("bogusflag"), false)
expect(t, c.IsSet("myflagGlobal"), false) expect(t, ctx.IsSet("one-flag"), true)
expect(t, ctx.IsSet("two-flag"), true)
expect(t, ctx.IsSet("three-flag"), true)
expect(t, ctx.IsSet("top-flag"), true)
expect(t, ctx.IsSet("bogus"), false)
} }
func TestContext_NumFlags(t *testing.T) { func TestContext_NumFlags(t *testing.T) {
@ -124,14 +128,14 @@ func TestContext_LocalFlagNames(t *testing.T) {
set := flag.NewFlagSet("test", 0) set := flag.NewFlagSet("test", 0)
set.Bool("one-flag", false, "doc") set.Bool("one-flag", false, "doc")
set.String("two-flag", "hello world", "doc") set.String("two-flag", "hello world", "doc")
globalSet := flag.NewFlagSet("test", 0) parentSet := flag.NewFlagSet("test", 0)
globalSet.Bool("top-flag", true, "doc") parentSet.Bool("top-flag", true, "doc")
globalCtx := NewContext(nil, globalSet, nil) parentCtx := NewContext(nil, parentSet, nil)
c := NewContext(nil, set, globalCtx) ctx := NewContext(nil, set, parentCtx)
set.Parse([]string{"--one-flag", "--two-flag=foo"}) set.Parse([]string{"--one-flag", "--two-flag=foo"})
globalSet.Parse([]string{"--top-flag"}) parentSet.Parse([]string{"--top-flag"})
actualFlags := c.LocalFlagNames() actualFlags := ctx.LocalFlagNames()
sort.Strings(actualFlags) sort.Strings(actualFlags)
expect(t, actualFlags, []string{"one-flag", "two-flag"}) expect(t, actualFlags, []string{"one-flag", "two-flag"})
@ -141,15 +145,52 @@ func TestContext_FlagNames(t *testing.T) {
set := flag.NewFlagSet("test", 0) set := flag.NewFlagSet("test", 0)
set.Bool("one-flag", false, "doc") set.Bool("one-flag", false, "doc")
set.String("two-flag", "hello world", "doc") set.String("two-flag", "hello world", "doc")
globalSet := flag.NewFlagSet("test", 0) parentSet := flag.NewFlagSet("test", 0)
globalSet.Bool("top-flag", true, "doc") parentSet.Bool("top-flag", true, "doc")
globalCtx := NewContext(nil, globalSet, nil) parentCtx := NewContext(nil, parentSet, nil)
c := NewContext(nil, set, globalCtx) ctx := NewContext(nil, set, parentCtx)
set.Parse([]string{"--one-flag", "--two-flag=foo"}) set.Parse([]string{"--one-flag", "--two-flag=foo"})
globalSet.Parse([]string{"--top-flag"}) parentSet.Parse([]string{"--top-flag"})
actualFlags := c.FlagNames() actualFlags := ctx.FlagNames()
sort.Strings(actualFlags) sort.Strings(actualFlags)
expect(t, actualFlags, []string{"one-flag", "top-flag", "two-flag"}) expect(t, actualFlags, []string{"one-flag", "top-flag", "two-flag"})
} }
func TestContext_Lineage(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Bool("local-flag", false, "doc")
parentSet := flag.NewFlagSet("test", 0)
parentSet.Bool("top-flag", true, "doc")
parentCtx := NewContext(nil, parentSet, nil)
ctx := NewContext(nil, set, parentCtx)
set.Parse([]string{"--local-flag"})
parentSet.Parse([]string{"--top-flag"})
lineage := ctx.Lineage()
expect(t, len(lineage), 2)
expect(t, lineage[0], ctx)
expect(t, lineage[1], parentCtx)
}
func TestContext_lookupFlagSet(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Bool("local-flag", false, "doc")
parentSet := flag.NewFlagSet("test", 0)
parentSet.Bool("top-flag", true, "doc")
parentCtx := NewContext(nil, parentSet, nil)
ctx := NewContext(nil, set, parentCtx)
set.Parse([]string{"--local-flag"})
parentSet.Parse([]string{"--top-flag"})
fs := lookupFlagSet("top-flag", ctx)
expect(t, fs, parentCtx.flagSet)
fs = lookupFlagSet("local-flag", ctx)
expect(t, fs, ctx.flagSet)
if fs := lookupFlagSet("frob", ctx); fs != nil {
t.Fail()
}
}