From c3263c03776401ad1263a9fb8f5a44a8ed44d61b Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Wed, 17 Nov 2021 10:13:06 +0100 Subject: Refactored package structure --- pkg/client/input.go | 26 ++++++++++++++ pkg/client/input_test.go | 60 +++++++++++++++++++++++++++++++ pkg/client/menu.go | 17 +++++++++ pkg/client/menu_test.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ pkg/client/state.go | 23 ++++++++++++ pkg/client/state_test.go | 14 ++++++++ 6 files changed, 232 insertions(+) create mode 100644 pkg/client/input.go create mode 100644 pkg/client/input_test.go create mode 100644 pkg/client/menu.go create mode 100644 pkg/client/menu_test.go create mode 100644 pkg/client/state.go create mode 100644 pkg/client/state_test.go (limited to 'pkg/client') 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") + } +} -- cgit v1.2.3