The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/3235

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
This is a proof of concept of some unit-testable code that I'd like
to later use for making cmdInit testable (as per #2834).

It's small so should it be easy to review and because I wish to have
some feedback on the approach before moving forward. I hope that the
branch gives a feel of what further branches would look like.

As a general note, I feel it'd be important to improve coverage of the
parts that can be unit tested easily (so basically most higher-level
application logic except for system-level interactions). I think this
can be done incrementally without too much disruption, if we agree.

Signed-off-by: Free Ekanayaka <[email protected]>
From c2745ac8116012dc2a8d115dde51fbb2b8f80af9 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <[email protected]>
Date: Thu, 27 Apr 2017 15:04:46 +0200
Subject: [PATCH] Add a new shared/cmd package with initial command I/O logic

This is a proof of concept of some unit-testable code that I'd like
to later use for making cmdInit testable (as per #2834).

It's small so should it be easy to review and because I wish to have
some feedback on the approach before moving forward. I hope that the
branch gives a feel of what further branches would look like.

As a general note, I feel it'd be important to improve coverage of the
parts that can be unit tested easily (so basically most higher-level
application logic except for system-level interactions). I think this
can be done incrementally without too much disruption, if we agree.

Signed-off-by: Free Ekanayaka <[email protected]>
---
 shared/cmd/context.go      | 50 +++++++++++++++++++++++++++++
 shared/cmd/context_test.go | 80 ++++++++++++++++++++++++++++++++++++++++++++++
 shared/cmd/doc.go          | 11 +++++++
 3 files changed, 141 insertions(+)
 create mode 100644 shared/cmd/context.go
 create mode 100644 shared/cmd/context_test.go
 create mode 100644 shared/cmd/doc.go

diff --git a/shared/cmd/context.go b/shared/cmd/context.go
new file mode 100644
index 0000000..2b7384f
--- /dev/null
+++ b/shared/cmd/context.go
@@ -0,0 +1,50 @@
+package cmd
+
+import (
+       "bufio"
+       "fmt"
+       "io"
+       "strings"
+
+       "github.com/lxc/lxd/shared"
+)
+
+// Context captures the environment the sub-command is being run in,
+// such as in/out/err streams and command line arguments.
+type Context struct {
+       stdin  io.Reader
+       stdout io.Writer
+}
+
+// NewContext creates a new command context with the given parameters.
+func NewContext(stdin io.Reader, stdout io.Writer) *Context {
+       return &Context{
+               stdin:  stdin,
+               stdout: stdout,
+       }
+}
+
+// AskBool asks a question an expect a yes/no answer.
+func (c *Context) AskBool(question string, defaultAnswer string) bool {
+
+       reader := bufio.NewReader(c.stdin)
+
+       for {
+               fmt.Fprintf(c.stdout, question)
+
+               answer, _ := reader.ReadString('\n')
+               answer = strings.TrimSuffix(answer, "\n")
+
+               if answer == "" {
+                       answer = defaultAnswer
+               }
+
+               if shared.StringInSlice(strings.ToLower(answer), 
[]string{"yes", "y"}) {
+                       return true
+               } else if shared.StringInSlice(strings.ToLower(answer), 
[]string{"no", "n"}) {
+                       return false
+               }
+
+               fmt.Fprintf(c.stdout, "Invalid input, try again.\n\n")
+       }
+}
diff --git a/shared/cmd/context_test.go b/shared/cmd/context_test.go
new file mode 100644
index 0000000..162320f
--- /dev/null
+++ b/shared/cmd/context_test.go
@@ -0,0 +1,80 @@
+package cmd_test
+
+import (
+       "bytes"
+       "strings"
+       "testing"
+
+       "github.com/lxc/lxd/shared/cmd"
+)
+
+// The given question is printed on standard output.
+func TestAskBoolPrintQuestion(t *testing.T) {
+       stdin := strings.NewReader("\n")
+       stdout := new(bytes.Buffer)
+       context := cmd.NewContext(stdin, stdout)
+       context.AskBool("Do you like me?", "yes")
+
+       expected := "Do you like me?"
+       if output := stdout.String(); output != expected {
+               t.Errorf("Expected '%s' got '%s'", expected, output)
+       }
+}
+
+// If no input is given (e.g. the user just presses "enter"), the default
+// answer will be returned.
+func TestAskBoolDefault(t *testing.T) {
+       stdin := strings.NewReader("\n")
+       stdout := new(bytes.Buffer)
+       context := cmd.NewContext(stdin, stdout)
+
+       answer := context.AskBool("Do you like me?", "yes")
+
+       if answer != true {
+               t.Errorf("Expected 'true' answer")
+       }
+}
+
+// If "yes" is given, AskBool returns true.
+func TestAskBoolYes(t *testing.T) {
+       stdin := strings.NewReader("yes\n")
+       stdout := new(bytes.Buffer)
+       context := cmd.NewContext(stdin, stdout)
+
+       answer := context.AskBool("Do you like me?", "yes")
+
+       if answer != true {
+               t.Errorf("Expected 'true' answer")
+       }
+}
+
+// If "no" is given, AskBool returns false.
+func TestAskBoolNo(t *testing.T) {
+       stdin := strings.NewReader("no\n")
+       stdout := new(bytes.Buffer)
+       context := cmd.NewContext(stdin, stdout)
+
+       answer := context.AskBool("Do you like me?", "yes")
+
+       if answer != false {
+               t.Errorf("Expected 'false' answer")
+       }
+}
+
+// If an invalid answer is given, the question gets repeated until
+// it succeeds.
+func TestAskBoolInvalid(t *testing.T) {
+       stdin := strings.NewReader("foo\nyes\n")
+       stdout := new(bytes.Buffer)
+       context := cmd.NewContext(stdin, stdout)
+       answer := context.AskBool("Do you like me?", "yes")
+
+       expected := "Do you like me?Invalid input, try again.\n\nDo you like 
me?"
+       if output := stdout.String(); output != expected {
+               t.Errorf("Expected '%s?' got '%s'", expected, output)
+       }
+
+       if answer != true {
+               t.Errorf("Expected 'true' answer")
+       }
+}
diff --git a/shared/cmd/doc.go b/shared/cmd/doc.go
new file mode 100644
index 0000000..02dab72
--- /dev/null
+++ b/shared/cmd/doc.go
@@ -0,0 +1,11 @@
+/*
+
+The package cmd implements a simple abstraction around a "sub-command" for
+a main executable (e.g. "lxd init", where "init" is the sub-command).
+
+It is designed to make unit-testing easier, since OS-specific parts like
+standard in/out can be set in tests.
+
+*/
+
+package cmd
_______________________________________________
lxc-devel mailing list
[email protected]
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to