mirror of
https://github.com/spf13/cobra
synced 2024-12-28 23:37:09 +00:00
296 lines
6.8 KiB
Go
296 lines
6.8 KiB
Go
// Copyright 2013-2023 The Cobra Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package cobra
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"text/template"
|
|
)
|
|
|
|
func assertNoErr(t *testing.T, e error) {
|
|
if e != nil {
|
|
t.Error(e)
|
|
}
|
|
}
|
|
|
|
func TestAddTemplateFunctions(t *testing.T) {
|
|
AddTemplateFunc("t", func() bool { return true })
|
|
AddTemplateFuncs(template.FuncMap{
|
|
"f": func() bool { return false },
|
|
"h": func() string { return "Hello," },
|
|
"w": func() string { return "world." }})
|
|
|
|
c := &Command{}
|
|
c.SetUsageTemplate(`{{if t}}{{h}}{{end}}{{if f}}{{h}}{{end}} {{w}}`)
|
|
|
|
const expected = "Hello, world."
|
|
if got := c.UsageString(); got != expected {
|
|
t.Errorf("Expected UsageString: %v\nGot: %v", expected, got)
|
|
}
|
|
}
|
|
|
|
func TestLevenshteinDistance(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
s string
|
|
t string
|
|
ignoreCase bool
|
|
expected int
|
|
}{
|
|
{
|
|
name: "Equal strings (case-sensitive)",
|
|
s: "hello",
|
|
t: "hello",
|
|
ignoreCase: false,
|
|
expected: 0,
|
|
},
|
|
{
|
|
name: "Equal strings (case-insensitive)",
|
|
s: "Hello",
|
|
t: "hello",
|
|
ignoreCase: true,
|
|
expected: 0,
|
|
},
|
|
{
|
|
name: "Different strings (case-sensitive)",
|
|
s: "kitten",
|
|
t: "sitting",
|
|
ignoreCase: false,
|
|
expected: 3,
|
|
},
|
|
{
|
|
name: "Different strings (case-insensitive)",
|
|
s: "Kitten",
|
|
t: "Sitting",
|
|
ignoreCase: true,
|
|
expected: 3,
|
|
},
|
|
{
|
|
name: "Empty strings",
|
|
s: "",
|
|
t: "",
|
|
ignoreCase: false,
|
|
expected: 0,
|
|
},
|
|
{
|
|
name: "One empty string",
|
|
s: "abc",
|
|
t: "",
|
|
ignoreCase: false,
|
|
expected: 3,
|
|
},
|
|
{
|
|
name: "Both empty strings",
|
|
s: "",
|
|
t: "",
|
|
ignoreCase: true,
|
|
expected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Act
|
|
got := ld(tt.s, tt.t, tt.ignoreCase)
|
|
|
|
// Assert
|
|
if got != tt.expected {
|
|
t.Errorf("Expected ld: %v\nGot: %v", tt.expected, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringInSlice(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
a string
|
|
list []string
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "String in slice (case-sensitive)",
|
|
a: "apple",
|
|
list: []string{"orange", "banana", "apple", "grape"},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "String not in slice (case-sensitive)",
|
|
a: "pear",
|
|
list: []string{"orange", "banana", "apple", "grape"},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "String in slice (case-insensitive)",
|
|
a: "APPLE",
|
|
list: []string{"orange", "banana", "apple", "grape"},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Empty slice",
|
|
a: "apple",
|
|
list: []string{},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Empty string",
|
|
a: "",
|
|
list: []string{"orange", "banana", "apple", "grape"},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "Empty strings match",
|
|
a: "",
|
|
list: []string{"orange", ""},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "Empty string in empty slice",
|
|
a: "",
|
|
list: []string{},
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Act
|
|
got := stringInSlice(tt.a, tt.list)
|
|
|
|
// Assert
|
|
if got != tt.expected {
|
|
t.Errorf("Expected stringInSlice: %v\nGot: %v", tt.expected, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRpad(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
inputString string
|
|
padding int
|
|
expected string
|
|
}{
|
|
{
|
|
name: "Padding required",
|
|
inputString: "Hello",
|
|
padding: 10,
|
|
expected: "Hello ",
|
|
},
|
|
{
|
|
name: "No padding required",
|
|
inputString: "World",
|
|
padding: 5,
|
|
expected: "World",
|
|
},
|
|
{
|
|
name: "Empty string",
|
|
inputString: "",
|
|
padding: 8,
|
|
expected: " ",
|
|
},
|
|
{
|
|
name: "Zero padding",
|
|
inputString: "cobra",
|
|
padding: 0,
|
|
expected: "cobra",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Act
|
|
got := rpad(tt.inputString, tt.padding)
|
|
|
|
// Assert
|
|
if got != tt.expected {
|
|
t.Errorf("Expected rpad: %v\nGot: %v", tt.expected, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestDeadcodeElimination checks that a simple program using cobra in its
|
|
// default configuration is linked taking full advantage of the linker's
|
|
// deadcode elimination step.
|
|
//
|
|
// If reflect.Value.MethodByName/reflect.Value.Method are reachable the
|
|
// linker will not always be able to prove that exported methods are
|
|
// unreachable, making deadcode elimination less effective. Using
|
|
// text/template and html/template makes reflect.Value.MethodByName
|
|
// reachable.
|
|
// Since cobra can use text/template templates this test checks that in its
|
|
// default configuration that code path can be proven to be unreachable by
|
|
// the linker.
|
|
//
|
|
// See also: https://github.com/spf13/cobra/pull/1956
|
|
func TestDeadcodeElimination(t *testing.T) {
|
|
// check that a simple program using cobra in its default configuration is
|
|
// linked with deadcode elimination enabled.
|
|
const (
|
|
dirname = "test_deadcode"
|
|
progname = "test_deadcode_elimination"
|
|
)
|
|
_ = os.Mkdir(dirname, 0770)
|
|
defer os.RemoveAll(dirname)
|
|
filename := filepath.Join(dirname, progname+".go")
|
|
err := os.WriteFile(filename, []byte(`package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var rootCmd = &cobra.Command{
|
|
Version: "1.0",
|
|
Use: "example_program",
|
|
Short: "example_program - test fixture to check that deadcode elimination is allowed",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
fmt.Println("hello world")
|
|
},
|
|
Aliases: []string{"alias1", "alias2"},
|
|
Example: "stringer --help",
|
|
}
|
|
|
|
func main() {
|
|
if err := rootCmd.Execute(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Whoops. There was an error while executing your CLI '%s'", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
`), 0600)
|
|
if err != nil {
|
|
t.Fatalf("could not write test program: %v", err)
|
|
}
|
|
buf, err := exec.Command("go", "build", filename).CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("could not compile test program: %s", string(buf))
|
|
}
|
|
defer os.Remove(progname)
|
|
buf, err = exec.Command("go", "tool", "nm", progname).CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("could not run go tool nm: %v", err)
|
|
}
|
|
if strings.Contains(string(buf), "MethodByName") {
|
|
t.Error("compiled programs contains MethodByName symbol")
|
|
}
|
|
}
|