From 258eedb38c6e8692565d74a92e3590da477dea6b Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sun, 30 May 2021 15:22:04 +1000 Subject: refactor --- pkg/utils/color.go | 49 ++++++ pkg/utils/formatting.go | 39 +++++ pkg/utils/formatting_test.go | 81 ++++++++++ pkg/utils/io.go | 25 +++ pkg/utils/lines.go | 34 ++++ pkg/utils/lines_test.go | 94 +++++++++++ pkg/utils/slice.go | 118 ++++++++++++++ pkg/utils/slice_test.go | 135 ++++++++++++++++ pkg/utils/template.go | 30 ++++ pkg/utils/template_test.go | 61 ++++++++ pkg/utils/utils.go | 262 ------------------------------- pkg/utils/utils_test.go | 366 ------------------------------------------- 12 files changed, 666 insertions(+), 628 deletions(-) create mode 100644 pkg/utils/color.go create mode 100644 pkg/utils/formatting.go create mode 100644 pkg/utils/formatting_test.go create mode 100644 pkg/utils/io.go create mode 100644 pkg/utils/lines.go create mode 100644 pkg/utils/lines_test.go create mode 100644 pkg/utils/slice.go create mode 100644 pkg/utils/slice_test.go create mode 100644 pkg/utils/template.go create mode 100644 pkg/utils/template_test.go (limited to 'pkg/utils') diff --git a/pkg/utils/color.go b/pkg/utils/color.go new file mode 100644 index 000000000..b38dccf7f --- /dev/null +++ b/pkg/utils/color.go @@ -0,0 +1,49 @@ +package utils + +import ( + "fmt" + "regexp" + + "github.com/fatih/color" +) + +// ColoredString takes a string and a colour attribute and returns a colored +// string with that attribute +func ColoredString(str string, colorAttributes ...color.Attribute) string { + colour := color.New(colorAttributes...) + return ColoredStringDirect(str, colour) +} + +// ColoredStringDirect used for aggregating a few color attributes rather than +// just sending a single one +func ColoredStringDirect(str string, colour *color.Color) string { + return colour.SprintFunc()(fmt.Sprint(str)) +} + +// Decolorise strips a string of color +func Decolorise(str string) string { + re := regexp.MustCompile(`\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]`) + return re.ReplaceAllString(str, "") +} + +func getPadWidths(stringArrays [][]string) []int { + maxWidth := 0 + for _, stringArray := range stringArrays { + if len(stringArray) > maxWidth { + maxWidth = len(stringArray) + } + } + if maxWidth-1 < 0 { + return []int{} + } + padWidths := make([]int, maxWidth-1) + for i := range padWidths { + for _, strings := range stringArrays { + uncoloredString := Decolorise(strings[i]) + if len(uncoloredString) > padWidths[i] { + padWidths[i] = len(uncoloredString) + } + } + } + return padWidths +} diff --git a/pkg/utils/formatting.go b/pkg/utils/formatting.go new file mode 100644 index 000000000..3b4b35bb8 --- /dev/null +++ b/pkg/utils/formatting.go @@ -0,0 +1,39 @@ +package utils + +import "strings" + +// WithPadding pads a string as much as you want +func WithPadding(str string, padding int) string { + uncoloredStr := Decolorise(str) + if padding < len(uncoloredStr) { + return str + } + return str + strings.Repeat(" ", padding-len(uncoloredStr)) +} + +func RenderDisplayStrings(displayStringsArr [][]string) string { + padWidths := getPadWidths(displayStringsArr) + paddedDisplayStrings := getPaddedDisplayStrings(displayStringsArr, padWidths) + + return strings.Join(paddedDisplayStrings, "\n") +} + +func getPaddedDisplayStrings(stringArrays [][]string, padWidths []int) []string { + paddedDisplayStrings := make([]string, len(stringArrays)) + for i, stringArray := range stringArrays { + if len(stringArray) == 0 { + continue + } + for j, padWidth := range padWidths { + if len(stringArray)-1 < j { + continue + } + paddedDisplayStrings[i] += WithPadding(stringArray[j], padWidth) + " " + } + if len(stringArray)-1 < len(padWidths) { + continue + } + paddedDisplayStrings[i] += stringArray[len(padWidths)] + } + return paddedDisplayStrings +} diff --git a/pkg/utils/formatting_test.go b/pkg/utils/formatting_test.go new file mode 100644 index 000000000..3fe3f22f0 --- /dev/null +++ b/pkg/utils/formatting_test.go @@ -0,0 +1,81 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestWithPadding is a function. +func TestWithPadding(t *testing.T) { + type scenario struct { + str string + padding int + expected string + } + + scenarios := []scenario{ + { + "hello world !", + 1, + "hello world !", + }, + { + "hello world !", + 14, + "hello world ! ", + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, s.expected, WithPadding(s.str, s.padding)) + } +} + +// TestGetPaddedDisplayStrings is a function. +func TestGetPaddedDisplayStrings(t *testing.T) { + type scenario struct { + stringArrays [][]string + padWidths []int + expected []string + } + + scenarios := []scenario{ + { + [][]string{{"a", "b"}, {"c", "d"}}, + []int{1}, + []string{"a b", "c d"}, + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, s.expected, getPaddedDisplayStrings(s.stringArrays, s.padWidths)) + } +} + +// TestGetPadWidths is a function. +func TestGetPadWidths(t *testing.T) { + type scenario struct { + stringArrays [][]string + expected []int + } + + scenarios := []scenario{ + { + [][]string{{""}, {""}}, + []int{}, + }, + { + [][]string{{"a"}, {""}}, + []int{}, + }, + { + [][]string{{"aa", "b", "ccc"}, {"c", "d", "e"}}, + []int{2, 1}, + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, s.expected, getPadWidths(s.stringArrays)) + } +} diff --git a/pkg/utils/io.go b/pkg/utils/io.go new file mode 100644 index 000000000..d31c5fc07 --- /dev/null +++ b/pkg/utils/io.go @@ -0,0 +1,25 @@ +package utils + +import ( + "bufio" + "os" +) + +func ForEachLineInFile(path string, f func(string, int)) error { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + reader := bufio.NewReader(file) + for i := 0; true; i++ { + line, err := reader.ReadString('\n') + if err != nil { + break + } + f(line, i) + } + + return nil +} diff --git a/pkg/utils/lines.go b/pkg/utils/lines.go new file mode 100644 index 000000000..4c654d888 --- /dev/null +++ b/pkg/utils/lines.go @@ -0,0 +1,34 @@ +package utils + +import "strings" + +// SplitLines takes a multiline string and splits it on newlines +// currently we are also stripping \r's which may have adverse effects for +// windows users (but no issues have been raised yet) +func SplitLines(multilineString string) []string { + multilineString = strings.Replace(multilineString, "\r", "", -1) + if multilineString == "" || multilineString == "\n" { + return make([]string, 0) + } + lines := strings.Split(multilineString, "\n") + if lines[len(lines)-1] == "" { + return lines[:len(lines)-1] + } + return lines +} + +// TrimTrailingNewline - Trims the trailing newline +// TODO: replace with `chomp` after refactor +func TrimTrailingNewline(str string) string { + if strings.HasSuffix(str, "\n") { + return str[:len(str)-1] + } + return str +} + +// NormalizeLinefeeds - Removes all Windows and Mac style line feeds +func NormalizeLinefeeds(str string) string { + str = strings.Replace(str, "\r\n", "\n", -1) + str = strings.Replace(str, "\r", "", -1) + return str +} diff --git a/pkg/utils/lines_test.go b/pkg/utils/lines_test.go new file mode 100644 index 000000000..6069b8f93 --- /dev/null +++ b/pkg/utils/lines_test.go @@ -0,0 +1,94 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestSplitLines is a function. +func TestSplitLines(t *testing.T) { + type scenario struct { + multilineString string + expected []string + } + + scenarios := []scenario{ + { + "", + []string{}, + }, + { + "\n", + []string{}, + }, + { + "hello world !\nhello universe !\n", + []string{ + "hello world !", + "hello universe !", + }, + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, s.expected, SplitLines(s.multilineString)) + } +} + +// TestTrimTrailingNewline is a function. +func TestTrimTrailingNewline(t *testing.T) { + type scenario struct { + str string + expected string + } + + scenarios := []scenario{ + { + "hello world !\n", + "hello world !", + }, + { + "hello world !", + "hello world !", + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, s.expected, TrimTrailingNewline(s.str)) + } +} + +// TestNormalizeLinefeeds is a function. +func TestNormalizeLinefeeds(t *testing.T) { + type scenario struct { + byteArray []byte + expected []byte + } + var scenarios = []scenario{ + { + // \r\n + []byte{97, 115, 100, 102, 13, 10}, + []byte{97, 115, 100, 102, 10}, + }, + { + // bash\r\nblah + []byte{97, 115, 100, 102, 13, 10, 97, 115, 100, 102}, + []byte{97, 115, 100, 102, 10, 97, 115, 100, 102}, + }, + { + // \r + []byte{97, 115, 100, 102, 13}, + []byte{97, 115, 100, 102}, + }, + { + // \n + []byte{97, 115, 100, 102, 10}, + []byte{97, 115, 100, 102, 10}, + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, string(s.expected), NormalizeLinefeeds(string(s.byteArray))) + } +} diff --git a/pkg/utils/slice.go b/pkg/utils/slice.go new file mode 100644 index 000000000..f536ea056 --- /dev/null +++ b/pkg/utils/slice.go @@ -0,0 +1,118 @@ +package utils + +// IncludesString if the list contains the string +func IncludesString(list []string, a string) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} + +// IncludesInt if the list contains the Int +func IncludesInt(list []int, a int) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} + +// NextIndex returns the index of the element that comes after the given number +func NextIndex(numbers []int, currentNumber int) int { + for index, number := range numbers { + if number > currentNumber { + return index + } + } + return len(numbers) - 1 +} + +// PrevIndex returns the index that comes before the given number, cycling if we reach the end +func PrevIndex(numbers []int, currentNumber int) int { + end := len(numbers) - 1 + for i := end; i >= 0; i-- { + if numbers[i] < currentNumber { + return i + } + } + return 0 +} + +// UnionInt returns the union of two int arrays +func UnionInt(a, b []int) []int { + m := make(map[int]bool) + + for _, item := range a { + m[item] = true + } + + for _, item := range b { + if _, ok := m[item]; !ok { + // this does not mutate the original a slice + // though it does mutate the backing array I believe + // but that doesn't matter because if you later want to append to the + // original a it must see that the backing array has been changed + // and create a new one + a = append(a, item) + } + } + return a +} + +// DifferenceInt returns the difference of two int arrays +func DifferenceInt(a, b []int) []int { + result := []int{} + m := make(map[int]bool) + + for _, item := range b { + m[item] = true + } + + for _, item := range a { + if _, ok := m[item]; !ok { + result = append(result, item) + } + } + return result +} + +// NextIntInCycle returns the next int in a slice, returning to the first index if we've reached the end +func NextIntInCycle(sl []int, current int) int { + for i, val := range sl { + if val == current { + if i == len(sl)-1 { + return sl[0] + } + return sl[i+1] + } + } + return sl[0] +} + +// PrevIntInCycle returns the prev int in a slice, returning to the first index if we've reached the end +func PrevIntInCycle(sl []int, current int) int { + for i, val := range sl { + if val == current { + if i > 0 { + return sl[i-1] + } + return sl[len(sl)-1] + } + } + return sl[len(sl)-1] +} + +func StringArraysOverlap(strArrA []string, strArrB []string) bool { + for _, first := range strArrA { + for _, second := range strArrB { + if first == second { + return true + } + } + } + + return false +} diff --git a/pkg/utils/slice_test.go b/pkg/utils/slice_test.go new file mode 100644 index 000000000..858b1b904 --- /dev/null +++ b/pkg/utils/slice_test.go @@ -0,0 +1,135 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestIncludesString is a function. +func TestIncludesString(t *testing.T) { + type scenario struct { + list []string + element string + expected bool + } + + scenarios := []scenario{ + { + []string{"a", "b"}, + "a", + true, + }, + { + []string{"a", "b"}, + "c", + false, + }, + { + []string{"a", "b"}, + "", + false, + }, + { + []string{""}, + "", + true, + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, s.expected, IncludesString(s.list, s.element)) + } +} + +func TestNextIndex(t *testing.T) { + type scenario struct { + testName string + list []int + element int + expected int + } + + scenarios := []scenario{ + { + // I'm not really fussed about how it behaves here + "no elements", + []int{}, + 1, + -1, + }, + { + "one element", + []int{1}, + 1, + 0, + }, + { + "two elements", + []int{1, 2}, + 1, + 1, + }, + { + "two elements, giving second one", + []int{1, 2}, + 2, + 1, + }, + { + "three elements, giving second one", + []int{1, 2, 3}, + 2, + 2, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + assert.EqualValues(t, s.expected, NextIndex(s.list, s.element)) + }) + } +} + +func TestPrevIndex(t *testing.T) { + type scenario struct { + testName string + list []int + element int + expected int + } + + scenarios := []scenario{ + { + // I'm not really fussed about how it behaves here + "no elements", + []int{}, + 1, + 0, + }, + { + "one element", + []int{1}, + 1, + 0, + }, + { + "two elements", + []int{1, 2}, + 1, + 0, + }, + { + "three elements, giving second one", + []int{1, 2, 3}, + 2, + 0, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + assert.EqualValues(t, s.expected, PrevIndex(s.list, s.element)) + }) + } +} diff --git a/pkg/utils/template.go b/pkg/utils/template.go new file mode 100644 index 000000000..6c147cd38 --- /dev/null +++ b/pkg/utils/template.go @@ -0,0 +1,30 @@ +package utils + +import ( + "bytes" + "strings" + "text/template" +) + +func ResolveTemplate(templateStr string, object interface{}) (string, error) { + tmpl, err := template.New("template").Parse(templateStr) + if err != nil { + return "", err + } + + var buf bytes.Buffer + if err := tmpl.Execute(&buf, object); err != nil { + return "", err + } + + return buf.String(), nil +} + +// ResolvePlaceholderString populates a template with values +func ResolvePlaceholderString(str string, arguments map[string]string) string { + for key, value := range arguments { + str = strings.Replace(str, "{{"+key+"}}", value, -1) + str = strings.Replace(str, "{{."+key+"}}", value, -1) + } + return str +} diff --git a/pkg/utils/template_test.go b/pkg/utils/template_test.go new file mode 100644 index 000000000..f294d115d --- /dev/null +++ b/pkg/utils/template_test.go @@ -0,0 +1,61 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestResolvePlaceholderString is a function. +func TestResolvePlaceholderString(t *testing.T) { + type scenario struct { + templateString string + arguments map[string]string + expected string + } + + scenarios := []scenario{ + { + "", + map[string]string{}, + "", + }, + { + "hello", + map[string]string{}, + "hello", + }, + { + "hello {{arg}}", + map[string]string{}, + "hello {{arg}}", + }, + { + "hello {{arg}}", + map[string]string{"arg": "there"}, + "hello there", + }, + { + "hello", + map[string]string{"arg": "there"}, + "hello", + }, + { + "{{nothing}}", + map[string]string{"nothing": ""}, + "", + }, + { + "{{}} {{ this }} { should not throw}} an {{{{}}}} error", + map[string]string{ + "blah": "blah", + "this": "won't match", + }, + "{{}} {{ this }} { should not throw}} an {{{{}}}} error", + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, s.expected, ResolvePlaceholderString(s.templateString, s.arguments)) + } +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 69fe711ac..4ec1c624c 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -1,7 +1,6 @@ package utils import ( - "bytes" "encoding/json" "fmt" "log" @@ -11,50 +10,11 @@ import ( "runtime" "strconv" "strings" - "text/template" "time" - "github.com/fatih/color" "github.com/jesseduffield/gocui" ) -// SplitLines takes a multiline string and splits it on newlines -// currently we are also stripping \r's which may have adverse effects for -// windows users (but no issues have been raised yet) -func SplitLines(multilineString string) []string { - multilineString = strings.Replace(multilineString, "\r", "", -1) - if multilineString == "" || multilineString == "\n" { - return make([]string, 0) - } - lines := strings.Split(multilineString, "\n") - if lines[len(lines)-1] == "" { - return lines[:len(lines)-1] - } - return lines -} - -// WithPadding pads a string as much as you want -func WithPadding(str string, padding int) string { - uncoloredStr := Decolorise(str) - if padding < len(uncoloredStr) { - return str - } - return str + strings.Repeat(" ", padding-len(uncoloredStr)) -} - -// ColoredString takes a string and a colour attribute and returns a colored -// string with that attribute -func ColoredString(str string, colorAttributes ...color.Attribute) string { - colour := color.New(colorAttributes...) - return ColoredStringDirect(str, colour) -} - -// ColoredStringDirect used for aggregating a few color attributes rather than -// just sending a single one -func ColoredStringDirect(str string, colour *color.Color) string { - return colour.SprintFunc()(fmt.Sprint(str)) -} - // GetCurrentRepoName gets the repo's base name func GetCurrentRepoName() string { pwd, err := os.Getwd() @@ -64,22 +24,6 @@ func GetCurrentRepoName() string { return filepath.Base(pwd) } -// TrimTrailingNewline - Trims the trailing newline -// TODO: replace with `chomp` after refactor -func TrimTrailingNewline(str string) string { - if strings.HasSuffix(str, "\n") { - return str[:len(str)-1] - } - return str -} - -// NormalizeLinefeeds - Removes all Windows and Mac style line feeds -func NormalizeLinefeeds(str string) string { - str = strings.Replace(str, "\r\n", "\n", -1) - str = strings.Replace(str, "\r", "", -1) - return str -} - // GetProjectRoot returns the path to the root of the project. Only to be used // in testing contexts, as with binaries it's unlikely this path will exist on // the machine @@ -100,15 +44,6 @@ func Loader() string { return characters[index : index+1] } -// ResolvePlaceholderString populates a template with values -func ResolvePlaceholderString(str string, arguments map[string]string) string { - for key, value := range arguments { - str = strings.Replace(str, "{{"+key+"}}", value, -1) - str = strings.Replace(str, "{{."+key+"}}", value, -1) - } - return str -} - // Min returns the minimum of two integers func Min(x, y int) int { if x < y { @@ -117,156 +52,11 @@ func Min(x, y int) int { return y } -func RenderDisplayStrings(displayStringsArr [][]string) string { - padWidths := getPadWidths(displayStringsArr) - paddedDisplayStrings := getPaddedDisplayStrings(displayStringsArr, padWidths) - - return strings.Join(paddedDisplayStrings, "\n") -} - -// Decolorise strips a string of color -func Decolorise(str string) string { - re := regexp.MustCompile(`\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]`) - return re.ReplaceAllString(str, "") -} - -func getPadWidths(stringArrays [][]string) []int { - maxWidth := 0 - for _, stringArray := range stringArrays { - if len(stringArray) > maxWidth { - maxWidth = len(stringArray) - } - } - if maxWidth-1 < 0 { - return []int{} - } - padWidths := make([]int, maxWidth-1) - for i := range padWidths { - for _, strings := range stringArrays { - uncoloredString := Decolorise(strings[i]) - if len(uncoloredString) > padWidths[i] { - padWidths[i] = len(uncoloredString) - } - } - } - return padWidths -} - -func getPaddedDisplayStrings(stringArrays [][]string, padWidths []int) []string { - paddedDisplayStrings := make([]string, len(stringArrays)) - for i, stringArray := range stringArrays { - if len(stringArray) == 0 { - continue - } - for j, padWidth := range padWidths { - if len(stringArray)-1 < j { - continue - } - paddedDisplayStrings[i] += WithPadding(stringArray[j], padWidth) + " " - } - if len(stringArray)-1 < len(padWidths) { - continue - } - paddedDisplayStrings[i] += stringArray[len(padWidths)] - } - return paddedDisplayStrings -} - -// displayArraysAligned returns true if every string array returned from our -// list of displayables has the same length -func displayArraysAligned(stringArrays [][]string) bool { - for _, strings := range stringArrays { - if len(strings) != len(stringArrays[0]) { - return false - } - } - return true -} - -// IncludesString if the list contains the string -func IncludesString(list []string, a string) bool { - for _, b := range list { - if b == a { - return true - } - } - return false -} - -// IncludesInt if the list contains the Int -func IncludesInt(list []int, a int) bool { - for _, b := range list { - if b == a { - return true - } - } - return false -} - -// NextIndex returns the index of the element that comes after the given number -func NextIndex(numbers []int, currentNumber int) int { - for index, number := range numbers { - if number > currentNumber { - return index - } - } - return len(numbers) - 1 -} - -// PrevIndex returns the index that comes before the given number, cycling if we reach the end -func PrevIndex(numbers []int, currentNumber int) int { - end := len(numbers) - 1 - for i := end; i >= 0; i-- { - if numbers[i] < currentNumber { - return i - } - } - return 0 -} - func AsJson(i interface{}) string { bytes, _ := json.MarshalIndent(i, "", " ") return string(bytes) } -// UnionInt returns the union of two int arrays -func UnionInt(a, b []int) []int { - m := make(map[int]bool) - - for _, item := range a { - m[item] = true - } - - for _, item := range b { - if _, ok := m[item]; !ok { - // this does not mutate the original a slice - // though it does mutate the backing array I believe - // but that doesn't matter because if you later want to append to the - // original a it must see that the backing array has been changed - // and create a new one - a = append(a, item) - } - } - return a -} - -// DifferenceInt returns the difference of two int arrays -func DifferenceInt(a, b []int) []int { - result := []int{} - m := make(map[int]bool) - - for _, item := range b { - m[item] = true - } - - for _, item := range a { - if _, ok := m[item]; !ok { - result = append(result, item) - } - } - return result -} - // used to keep a number n between 0 and max, allowing for wraparounds func ModuloWithWrap(n, max int) int { if n >= max { @@ -278,32 +68,6 @@ func ModuloWithWrap(n, max int) int { } } -// NextIntInCycle returns the next int in a slice, returning to the first index if we've reached the end -func NextIntInCycle(sl []int, current int) int { - for i, val := range sl { - if val == current { - if i == len(sl)-1 { - return sl[0] - } - return sl[i+1] - } - } - return sl[0] -} - -// PrevIntInCycle returns the prev int in a slice, returning to the first index if we've reached the end -func PrevIntInCycle(sl []int, current int) int { - for i, val := range sl { - if val == current { - if i > 0 { - return sl[i-1] - } - return sl[len(sl)-1] - } - } - return sl[len(sl)-1] -} - // TruncateWithEllipsis returns a string, truncated to a certain length, with an ellipsis func TruncateWithEllipsis(str string, limit int) string { if limit == 1 && len(str) > 1 { @@ -329,18 +93,6 @@ func FindStringSubmatch(str string, regexpStr string) (bool, []string) { return len(match) > 0, match } -func StringArraysOverlap(strArrA []string, strArrB []string) bool { - for _, first := range strArrA { - for _, second := range strArrB { - if first == second { - return true - } - } - } - - return false -} - func MustConvertToInt(s string) int { i, err := strconv.Atoi(s) if err != nil { @@ -349,20 +101,6 @@ func MustConvertToInt(s string) int { return i } -func ResolveTemplate(templateStr string, object interface{}) (string, error) { - tmpl, err := template.New("template").Parse(templateStr) - if err != nil { - return "", err - } - - var buf bytes.Buffer - if err := tmpl.Execute(&buf, object); err != nil { - return "", err - } - - return buf.String(), nil -} - // Safe will close tcell if a panic occurs so that we don't end up in a malformed // terminal state func Safe(f func()) { diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 0e11a028c..c63028d41 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -6,244 +6,6 @@ import ( "github.com/stretchr/testify/assert" ) -// TestSplitLines is a function. -func TestSplitLines(t *testing.T) { - type scenario struct { - multilineString string - expected []string - } - - scenarios := []scenario{ - { - "", - []string{}, - }, - { - "\n", - []string{}, - }, - { - "hello world !\nhello universe !\n", - []string{ - "hello world !", - "hello universe !", - }, - }, - } - - for _, s := range scenarios { - assert.EqualValues(t, s.expected, SplitLines(s.multilineString)) - } -} - -// TestWithPadding is a function. -func TestWithPadding(t *testing.T) { - type scenario struct { - str string - padding int - expected string - } - - scenarios := []scenario{ - { - "hello world !", - 1, - "hello world !", - }, - { - "hello world !", - 14, - "hello world ! ", - }, - } - - for _, s := range scenarios { - assert.EqualValues(t, s.expected, WithPadding(s.str, s.padding)) - } -} - -// TestTrimTrailingNewline is a function. -func TestTrimTrailingNewline(t *testing.T) { - type scenario struct { - str string - expected string - } - - scenarios := []scenario{ - { - "hello world !\n", - "hello world !", - }, - { - "hello world !", - "hello world !", - }, - } - - for _, s := range scenarios { - assert.EqualValues(t, s.expected, TrimTrailingNewline(s.str)) - } -} - -// TestNormalizeLinefeeds is a function. -func TestNormalizeLinefeeds(t *testing.T) { - type scenario struct { - byteArray []byte - expected []byte - } - var scenarios = []scenario{ - { - // \r\n - []byte{97, 115, 100, 102, 13, 10}, - []byte{97, 115, 100, 102, 10}, - }, - { - // bash\r\nblah - []byte{97, 115, 100, 102, 13, 10, 97, 115, 100, 102}, - []byte{97, 115, 100, 102, 10, 97, 115, 100, 102}, - }, - { - // \r - []byte{97, 115, 100, 102, 13}, - []byte{97, 115, 100, 102}, - }, - { - // \n - []byte{97, 115, 100, 102, 10}, - []byte{97, 115, 100, 102, 10}, - }, - } - - for _, s := range scenarios { - assert.EqualValues(t, string(s.expected), NormalizeLinefeeds(string(s.byteArray))) - } -} - -// TestResolvePlaceholderString is a function. -func TestResolvePlaceholderString(t *testing.T) { - type scenario struct { - templateString string - arguments map[string]string - expected string - } - - scenarios := []scenario{ - { - "", - map[string]string{}, - "", - }, - { - "hello", - map[string]string{}, - "hello", - }, - { - "hello {{arg}}", - map[string]string{}, - "hello {{arg}}", - }, - { - "hello {{arg}}", - map[string]string{"arg": "there"}, - "hello there", - }, - { - "hello", - map[string]string{"arg": "there"}, - "hello", - }, - { - "{{nothing}}", - map[string]string{"nothing": ""}, - "", - }, - { - "{{}} {{ this }} { should not throw}} an {{{{}}}} error", - map[string]string{ - "blah": "blah", - "this": "won't match", - }, - "{{}} {{ this }} { should not throw}} an {{{{}}}} error", - }, - } - - for _, s := range scenarios { - assert.EqualValues(t, s.expected, ResolvePlaceholderString(s.templateString, s.arguments)) - } -} - -// TestDisplayArraysAligned is a function. -func TestDisplayArraysAligned(t *testing.T) { - type scenario struct { - input [][]string - expected bool - } - - scenarios := []scenario{ - { - [][]string{{"", ""}, {"", ""}}, - true, - }, - { - [][]string{{""}, {"", ""}}, - false, - }, - } - - for _, s := range scenarios { - assert.EqualValues(t, s.expected, displayArraysAligned(s.input)) - } -} - -// TestGetPaddedDisplayStrings is a function. -func TestGetPaddedDisplayStrings(t *testing.T) { - type scenario struct { - stringArrays [][]string - padWidths []int - expected []string - } - - scenarios := []scenario{ - { - [][]string{{"a", "b"}, {"c", "d"}}, - []int{1}, - []string{"a b", "c d"}, - }, - } - - for _, s := range scenarios { - assert.EqualValues(t, s.expected, getPaddedDisplayStrings(s.stringArrays, s.padWidths)) - } -} - -// TestGetPadWidths is a function. -func TestGetPadWidths(t *testing.T) { - type scenario struct { - stringArrays [][]string - expected []int - } - - scenarios := []scenario{ - { - [][]string{{""}, {""}}, - []int{}, - }, - { - [][]string{{"a"}, {""}}, - []int{}, - }, - { - [][]string{{"aa", "b", "ccc"}, {"c", "d", "e"}}, - []int{2, 1}, - }, - } - - for _, s := range scenarios { - assert.EqualValues(t, s.expected, getPadWidths(s.stringArrays)) - } -} - // TestMin is a function. func TestMin(t *testing.T) { type scenario struct { @@ -275,134 +37,6 @@ func TestMin(t *testing.T) { } } -// TestIncludesString is a function. -func TestIncludesString(t *testing.T) { - type scenario struct { - list []string - element string - expected bool - } - - scenarios := []scenario{ - { - []string{"a", "b"}, - "a", - true, - }, - { - []string{"a", "b"}, - "c", - false, - }, - { - []string{"a", "b"}, - "", - false, - }, - { - []string{""}, - "", - true, - }, - } - - for _, s := range scenarios { - assert.EqualValues(t, s.expected, IncludesString(s.list, s.element)) - } -} - -func TestNextIndex(t *testing.T) { - type scenario struct { - testName string - list []int - element int - expected int - } - - scenarios := []scenario{ - { - // I'm not really fussed about how it behaves here - "no elements", - []int{}, - 1, - -1, - }, - { - "one element", - []int{1}, - 1, - 0, - }, - { - "two elements", - []int{1, 2}, - 1, - 1, - }, - { - "two elements, giving second one", - []int{1, 2}, - 2, - 1, - }, - { - "three elements, giving second one", - []int{1, 2, 3}, - 2, - 2, - }, - } - - for _, s := range scenarios { - t.Run(s.testName, func(t *testing.T) { - assert.EqualValues(t, s.expected, NextIndex(s.list, s.element)) - }) - } -} - -func TestPrevIndex(t *testing.T) { - type scenario struct { - testName string - list []int - element int - expected int - } - - scenarios := []scenario{ - { - // I'm not really fussed about how it behaves here - "no elements", - []int{}, - 1, - 0, - }, - { - "one element", - []int{1}, - 1, - 0, - }, - { - "two elements", - []int{1, 2}, - 1, - 0, - }, - { - "three elements, giving second one", - []int{1, 2, 3}, - 2, - 0, - }, - } - - for _, s := range scenarios { - t.Run(s.testName, func(t *testing.T) { - assert.EqualValues(t, s.expected, PrevIndex(s.list, s.element)) - }) - } -} - func TestAsJson(t *testing.T) { type myStruct struct { a string -- cgit v1.2.3