[ARVADOS] updated: 1.1.2-10-g256e92b

Git user git at public.curoverse.com
Sun Dec 24 16:01:38 EST 2017


Summary of changes:
 lib/cli/get.go           |  2 ++
 lib/cmd/cmd.go           | 33 +++++++++++++++++++++++++----
 lib/cmd/cmd_test.go      | 13 ++++++++++++
 lib/cmdtest/leakcheck.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 98 insertions(+), 4 deletions(-)
 create mode 100644 lib/cmdtest/leakcheck.go

       via  256e92b825af07947ff09954018da0ca0b767acb (commit)
      from  b851f17a590bf423bbe0bce033fa1d40738c96a9 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 256e92b825af07947ff09954018da0ca0b767acb
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date:   Sun Dec 24 14:44:36 2017 -0500

    12868: Fix up error/usage messages.
    
    Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>

diff --git a/lib/cli/get.go b/lib/cli/get.go
index a7adfb6..d3177f3 100644
--- a/lib/cli/get.go
+++ b/lib/cli/get.go
@@ -23,6 +23,8 @@ func Get(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer)
 	}()
 
 	flags := flag.NewFlagSet(prog, flag.ContinueOnError)
+	flags.SetOutput(stderr)
+
 	format := flags.String("format", "json", "output format (json, yaml, or uuid)")
 	flags.StringVar(format, "f", "json", "output format (json, yaml, or uuid)")
 	short := flags.Bool("short", false, "equivalent to --format=uuid")
diff --git a/lib/cmd/cmd.go b/lib/cmd/cmd.go
index a3d5ae8..46d9965 100644
--- a/lib/cmd/cmd.go
+++ b/lib/cmd/cmd.go
@@ -10,14 +10,17 @@ import (
 	"flag"
 	"fmt"
 	"io"
+	"io/ioutil"
+	"sort"
+	"strings"
 )
 
 // A RunFunc runs a command with the given args, and returns an exit
 // code.
 type RunFunc func(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int
 
-// Multi returns a command that looks up its first argument in m, and
-// runs the resulting RunFunc with the remaining args.
+// Multi returns a RunFunc that looks up its first argument in m, and
+// invokes the resulting RunFunc with the remaining args.
 //
 // Example:
 //
@@ -32,11 +35,13 @@ type RunFunc func(prog string, args []string, stdin io.Reader, stdout, stderr io
 func Multi(m map[string]RunFunc) RunFunc {
 	return func(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
 		if len(args) < 1 {
-			fmt.Fprintf(stderr, "usage: %s command [args]", prog)
+			fmt.Fprintf(stderr, "usage: %s command [args]\n", prog)
+			multiUsage(stderr, m)
 			return 2
 		}
 		if cmd, ok := m[args[0]]; !ok {
-			fmt.Fprintf(stderr, "unrecognized command %q", args[0])
+			fmt.Fprintf(stderr, "unrecognized command %q\n", args[0])
+			multiUsage(stderr, m)
 			return 2
 		} else {
 			return cmd(prog+" "+args[0], args[1:], stdin, stdout, stderr)
@@ -44,6 +49,24 @@ func Multi(m map[string]RunFunc) RunFunc {
 	}
 }
 
+func multiUsage(stderr io.Writer, m map[string]RunFunc) {
+	var subcommands []string
+	for sc := range m {
+		if strings.HasPrefix(sc, "-") {
+			// Some subcommands have alternate versions
+			// like "--version" for compatibility. Don't
+			// clutter the subcommand summary with those.
+			continue
+		}
+		subcommands = append(subcommands, sc)
+	}
+	sort.Strings(subcommands)
+	fmt.Fprintf(stderr, "\nAvailable commands:\n")
+	for _, sc := range subcommands {
+		fmt.Fprintf(stderr, "    %s\n", sc)
+	}
+}
+
 // WithLateSubcommand wraps a RunFunc by skipping over some known
 // flags to find a subcommand, and moving that subcommand to the front
 // of the args before calling the wrapped RunFunc. For example:
@@ -62,6 +85,8 @@ func WithLateSubcommand(run RunFunc, argFlags, boolFlags []string) RunFunc {
 		}
 		// Ignore errors. We can't report a useful error
 		// message anyway.
+		flags.SetOutput(ioutil.Discard)
+		flags.Usage = func() {}
 		flags.Parse(args)
 		if flags.NArg() > 0 {
 			// Move the first arg after the recognized
diff --git a/lib/cmd/cmd_test.go b/lib/cmd/cmd_test.go
index fc20bef..2d9228a 100644
--- a/lib/cmd/cmd_test.go
+++ b/lib/cmd/cmd_test.go
@@ -11,6 +11,7 @@ import (
 	"strings"
 	"testing"
 
+	"git.curoverse.com/arvados.git/lib/cmdtest"
 	check "gopkg.in/check.v1"
 )
 
@@ -31,6 +32,7 @@ var testCmd = Multi(map[string]RunFunc{
 })
 
 func (s *CmdSuite) TestHello(c *check.C) {
+	defer cmdtest.LeakCheck(c)()
 	stdout := bytes.NewBuffer(nil)
 	stderr := bytes.NewBuffer(nil)
 	exited := testCmd("prog", []string{"echo", "hello", "world"}, bytes.NewReader(nil), stdout, stderr)
@@ -39,7 +41,18 @@ func (s *CmdSuite) TestHello(c *check.C) {
 	c.Check(stderr.String(), check.Equals, "")
 }
 
+func (s *CmdSuite) TestUsage(c *check.C) {
+	defer cmdtest.LeakCheck(c)()
+	stdout := bytes.NewBuffer(nil)
+	stderr := bytes.NewBuffer(nil)
+	exited := testCmd("prog", []string{"nosuchcommand", "hi"}, bytes.NewReader(nil), stdout, stderr)
+	c.Check(exited, check.Equals, 2)
+	c.Check(stdout.String(), check.Equals, "")
+	c.Check(stderr.String(), check.Matches, `(?ms)^unrecognized command "nosuchcommand"\n.*echo.*\n`)
+}
+
 func (s *CmdSuite) TestWithLateSubcommand(c *check.C) {
+	defer cmdtest.LeakCheck(c)()
 	stdout := bytes.NewBuffer(nil)
 	stderr := bytes.NewBuffer(nil)
 	run := WithLateSubcommand(testCmd, []string{"format", "f"}, []string{"n"})
diff --git a/lib/cmdtest/leakcheck.go b/lib/cmdtest/leakcheck.go
new file mode 100644
index 0000000..c132f1b
--- /dev/null
+++ b/lib/cmdtest/leakcheck.go
@@ -0,0 +1,54 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+// Package cmdtest provides tools for testing command line tools.
+package cmdtest
+
+import (
+	"io"
+	"io/ioutil"
+	"os"
+
+	check "gopkg.in/check.v1"
+)
+
+// LeakCheck tests for output being leaked to os.Stdout and os.Stderr
+// that should be sent elsewhere (e.g., the stdout and stderr streams
+// passed to a cmd.RunFunc).
+//
+// It redirects os.Stdout and os.Stderr to a tempfile, and returns a
+// func, which the caller is expected to defer, that restores os.* and
+// checks that the tempfile is empty.
+//
+// Example:
+//
+//	func (s *Suite) TestSomething(c *check.C) {
+//		defer cmdtest.LeakCheck(c)()
+//		// ... do things that shouldn't print to os.Stderr or os.Stdout
+//	}
+func LeakCheck(c *check.C) func() {
+	tmpfiles := map[string]*os.File{"stdout": nil, "stderr": nil}
+	for i := range tmpfiles {
+		var err error
+		tmpfiles[i], err = ioutil.TempFile("", "")
+		c.Assert(err, check.IsNil)
+		err = os.Remove(tmpfiles[i].Name())
+		c.Assert(err, check.IsNil)
+	}
+
+	stdout, stderr := os.Stdout, os.Stderr
+	os.Stdout, os.Stderr = tmpfiles["stdout"], tmpfiles["stderr"]
+	return func() {
+		os.Stdout, os.Stderr = stdout, stderr
+
+		for i, tmpfile := range tmpfiles {
+			c.Log("checking %s", i)
+			_, err := tmpfile.Seek(0, io.SeekStart)
+			c.Assert(err, check.IsNil)
+			leaked, err := ioutil.ReadAll(tmpfile)
+			c.Assert(err, check.IsNil)
+			c.Check(string(leaked), check.Equals, "")
+		}
+	}
+}

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list