aboutsummaryrefslogtreecommitdiff
path: root/pkg/client
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/client')
-rw-r--r--pkg/client/input.go26
-rw-r--r--pkg/client/input_test.go60
-rw-r--r--pkg/client/menu.go17
-rw-r--r--pkg/client/menu_test.go92
-rw-r--r--pkg/client/state.go23
-rw-r--r--pkg/client/state_test.go14
6 files changed, 232 insertions, 0 deletions
diff --git a/pkg/client/input.go b/pkg/client/input.go
new file mode 100644
index 0000000..8c814a6
--- /dev/null
+++ b/pkg/client/input.go
@@ -0,0 +1,26 @@
+package client
+
+import (
+ "bufio"
+ "os"
+
+ "github.com/pkg/errors"
+)
+
+// getValidInput returns the selected menu command as a string or an error
+func (s *State) getValidInput() (string, error) {
+ menu := s.config.Menus[s.currentMenu]
+
+ reader := bufio.NewReader(os.Stdin)
+ for {
+ input, err := reader.ReadByte()
+ if err != nil {
+ return "", errors.Wrapf(err, "Could not read byte from stdin")
+ }
+ for _, menuEntry := range menu.MenuEntries {
+ if []byte(menuEntry.Key)[0] == input {
+ return menuEntry.Action, nil
+ }
+ }
+ }
+}
diff --git a/pkg/client/input_test.go b/pkg/client/input_test.go
new file mode 100644
index 0000000..f74a097
--- /dev/null
+++ b/pkg/client/input_test.go
@@ -0,0 +1,60 @@
+package client
+
+import (
+ "os"
+ "shell-game-launcher/pkg/config"
+ "testing"
+)
+
+func TestGetValidInput(t *testing.T) {
+ realStdin := os.Stdin
+ t.Cleanup(func() { os.Stdin = realStdin })
+
+ // Complete menu, no input error
+ state := State{
+ config: &config.Config{
+ Menus: map[string]config.Menu{
+ "test": config.Menu{
+ Banner: "TEST TEST TEST",
+ MenuEntries: []config.MenuEntry{
+ config.MenuEntry{
+ Key: "w",
+ Label: "wait entry",
+ Action: "wait",
+ },
+ config.MenuEntry{
+ Key: "q",
+ Label: "quit entry",
+ Action: "quit",
+ },
+ },
+ },
+ },
+ },
+ currentMenu: "test",
+ login: "",
+ }
+ r, w, _ := os.Pipe()
+ os.Stdin = r
+
+ // Simply test quit entry
+ w.WriteString("q")
+ if cmd, err := state.getValidInput(); err != nil || cmd != "quit" {
+ t.Fatalf("Input handled incorrectly:\nwant: wait\ngot: %s\nerror: %s\n", cmd, err)
+ }
+ // test quit entry after wrong keys
+ w.WriteString("abcdq")
+ if cmd, err := state.getValidInput(); err != nil || cmd != "quit" {
+ t.Fatalf("Input handled incorrectly:\nwant: wait\ngot: %s\nerror: %s\n", cmd, err)
+ }
+ // test wait entry with valid quit after
+ w.WriteString("wq")
+ if cmd, err := state.getValidInput(); err != nil || cmd != "wait" {
+ t.Fatalf("Input handled incorrectly:\nwant: wait\ngot: %s\nerror: %s\n", cmd, err)
+ }
+ // test input error
+ w.Close()
+ if cmd, err := state.getValidInput(); err == nil {
+ t.Fatalf("Input handled incorrectly:\nwant: wait\ngot: %s\nerror: %s\n", cmd, err)
+ }
+}
diff --git a/pkg/client/menu.go b/pkg/client/menu.go
new file mode 100644
index 0000000..f6de082
--- /dev/null
+++ b/pkg/client/menu.go
@@ -0,0 +1,17 @@
+package client
+
+import "fmt"
+
+func (s *State) displayMenu() {
+ menu := s.config.Menus[s.currentMenu]
+ fmt.Print("\033[2J") // clear the screen
+ fmt.Printf("%s\n\n", menu.Banner)
+ if s.login == "" {
+ fmt.Print("Not logged in.\n\n")
+ } else {
+ fmt.Printf("Logged in as: %s\n\n", s.login)
+ }
+ for i := 0; i < len(menu.MenuEntries); i++ {
+ fmt.Printf("%s) %s\n", menu.MenuEntries[i].Key, menu.MenuEntries[i].Label)
+ }
+}
diff --git a/pkg/client/menu_test.go b/pkg/client/menu_test.go
new file mode 100644
index 0000000..35964d8
--- /dev/null
+++ b/pkg/client/menu_test.go
@@ -0,0 +1,92 @@
+package client
+
+import (
+ "io/ioutil"
+ "os"
+ "reflect"
+ "shell-game-launcher/pkg/config"
+ "testing"
+)
+
+func TestDisplayMenu(t *testing.T) {
+ realStdout := os.Stdout
+ t.Cleanup(func() { os.Stdout = realStdout })
+ r, w, _ := os.Pipe()
+ os.Stdout = w
+
+ // Complete menu, while not logged in
+ state := State{
+ config: &config.Config{
+ Menus: map[string]config.Menu{
+ "test": config.Menu{
+ Banner: "TEST TEST TEST",
+ MenuEntries: []config.MenuEntry{
+ config.MenuEntry{
+ Key: "q",
+ Label: "quit entry",
+ Action: "quit",
+ },
+ },
+ },
+ },
+ },
+ currentMenu: "test",
+ login: "",
+ }
+ want := []byte("\033[2J" +
+ "TEST TEST TEST\n" +
+ "\n" +
+ "Not logged in.\n" +
+ "\n" +
+ "q) quit entry\n")
+ state.displayMenu()
+ // back to normal state
+ w.Close()
+ out, _ := ioutil.ReadAll(r)
+ if !reflect.DeepEqual(out, want) {
+ t.Fatalf("menu displayed incorrectly:\nwant:%+v\ngot: %+v", want, out)
+ }
+
+ // Complete menu, while logged in
+ r, w, _ = os.Pipe()
+ os.Stdout = w
+
+ // Complete menu, while not logged in
+ state = State{
+ config: &config.Config{
+ Menus: map[string]config.Menu{
+ "test": config.Menu{
+ Banner: "TEST TEST TEST",
+ MenuEntries: []config.MenuEntry{
+ config.MenuEntry{
+ Key: "w",
+ Label: "wait entry",
+ Action: "wait",
+ },
+ config.MenuEntry{
+ Key: "q",
+ Label: "quit entry",
+ Action: "quit",
+ },
+ },
+ },
+ },
+ },
+ currentMenu: "test",
+ login: "test",
+ }
+ want = []byte("\033[2J" +
+ "TEST TEST TEST\n" +
+ "\n" +
+ "Logged in as: test\n" +
+ "\n" +
+ "w) wait entry\n" +
+ "q) quit entry\n")
+ state.displayMenu()
+ // back to normal state
+ w.Close()
+ out, _ = ioutil.ReadAll(r)
+ if !reflect.DeepEqual(out, want) {
+ t.Fatalf("menu displayed incorrectly:\nwant:%+v\ngot: %+v", want, out)
+ }
+}
diff --git a/pkg/client/state.go b/pkg/client/state.go
new file mode 100644
index 0000000..576721c
--- /dev/null
+++ b/pkg/client/state.go
@@ -0,0 +1,23 @@
+package client
+
+import (
+ "shell-game-launcher/pkg/config"
+)
+
+type State struct {
+ config *config.Config
+ currentMenu string
+ login string
+}
+
+func NewState(config *config.Config, login string) *State {
+ cs := State{
+ config: config,
+ currentMenu: "anonymous",
+ login: login,
+ }
+ if login != "" {
+ cs.currentMenu = "logged_in"
+ }
+ return &cs
+}
diff --git a/pkg/client/state_test.go b/pkg/client/state_test.go
new file mode 100644
index 0000000..917c211
--- /dev/null
+++ b/pkg/client/state_test.go
@@ -0,0 +1,14 @@
+package client
+
+import "testing"
+
+func TestNewState(t *testing.T) {
+ // Empty login
+ if s := NewState(nil, ""); s.currentMenu != "anonymous" {
+ t.Fatal("a new state without login should init to anonymous")
+ }
+ // logged_in
+ if s := NewState(nil, "test"); s.currentMenu != "logged_in" {
+ t.Fatal("a new state with login should init to logged_in")
+ }
+}