From 042a2cf17704fca5ac8148dce06df2d3563c883e Mon Sep 17 00:00:00 2001 From: koba1t Date: Sat, 12 Jul 2025 03:44:51 +0900 Subject: [PATCH] add testcases for shlexsplit --- api/internal/plugins/execplugin/execplugin.go | 6 +- api/internal/plugins/execplugin/shlex.go | 19 ++ api/internal/plugins/execplugin/shlex_test.go | 177 ++++++++++++++++++ 3 files changed, 198 insertions(+), 4 deletions(-) create mode 100644 api/internal/plugins/execplugin/shlex.go create mode 100644 api/internal/plugins/execplugin/shlex_test.go diff --git a/api/internal/plugins/execplugin/execplugin.go b/api/internal/plugins/execplugin/execplugin.go index 3903d11e2..f6c1dba3c 100644 --- a/api/internal/plugins/execplugin/execplugin.go +++ b/api/internal/plugins/execplugin/execplugin.go @@ -12,8 +12,6 @@ import ( "runtime" "strings" - shlex "github.com/carapace-sh/carapace-shlex" - "sigs.k8s.io/kustomize/api/internal/plugins/utils" "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/kyaml/errors" @@ -95,11 +93,11 @@ func (p *ExecPlugin) processOptionalArgsFields() error { return err } if c.ArgsOneLiner != "" { - argsTolenSlice, err := shlex.Split(c.ArgsOneLiner) + argsTolenSlice, err := ShlexSplit(c.ArgsOneLiner) if err != nil { return fmt.Errorf("failed to parse argsOneLiner: %w", err) } - p.args = argsTolenSlice.Strings() + p.args = argsTolenSlice } if c.ArgsFromFile != "" { content, err := p.h.Loader().Load(c.ArgsFromFile) diff --git a/api/internal/plugins/execplugin/shlex.go b/api/internal/plugins/execplugin/shlex.go new file mode 100644 index 000000000..9e7c5740f --- /dev/null +++ b/api/internal/plugins/execplugin/shlex.go @@ -0,0 +1,19 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package execplugin + +import ( + "fmt" + + shlex "github.com/carapace-sh/carapace-shlex" +) + +func ShlexSplit(s string) ([]string, error) { + // return shlexSplit(s) + tokens, err := shlex.Split(s) + if err != nil { + return nil, fmt.Errorf("shlex split error: %w", err) + } + return tokens.Strings(), nil +} diff --git a/api/internal/plugins/execplugin/shlex_test.go b/api/internal/plugins/execplugin/shlex_test.go new file mode 100644 index 000000000..b8276426c --- /dev/null +++ b/api/internal/plugins/execplugin/shlex_test.go @@ -0,0 +1,177 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package execplugin + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestShlexSplit(t *testing.T) { + testCases := []struct { + name string + input string + expected []string + wantErr bool + }{ + { + name: "basic space separation", + input: `hello world`, + expected: []string{"hello", "world"}, + wantErr: false, + }, + { + name: "double quoted string", + input: `"hello world"`, + expected: []string{"hello world"}, + wantErr: false, + }, + { + name: "single quoted string", + input: `'hello world'`, + expected: []string{"hello world"}, + wantErr: false, + }, + { + name: "mixed quotes and words", + input: `hello "world test"`, + expected: []string{"hello", "world test"}, + wantErr: false, + }, + { + name: "single quotes with spaces", + input: `hello 'world test'`, + expected: []string{"hello", "world test"}, + wantErr: false, + }, + { + name: "nested quotes - single in double", + input: `"hello 'nested' world"`, + expected: []string{"hello 'nested' world"}, + wantErr: false, + }, + { + name: "nested quotes - double in single", + input: `'hello "nested" world'`, + expected: []string{"hello \"nested\" world"}, + wantErr: false, + }, + { + name: "escaped space", + input: `hello\ world`, + expected: []string{"hello world"}, + wantErr: false, + }, + { + name: "escaped quotes in double quotes", + input: `"hello \"world\""`, + expected: []string{"hello \"world\""}, + wantErr: false, + }, + { + name: "single quote in single quotes", + input: `'can'\''t'`, + expected: []string{"can't"}, + wantErr: false, + }, + { + name: "complex argument list", + input: `arg1 "arg 2" 'arg 3' arg4`, + expected: []string{"arg1", "arg 2", "arg 3", "arg4"}, + wantErr: false, + }, + { + name: "echo command with escaped quotes", + input: `echo "Hello, \"World!\""`, + expected: []string{"echo", "Hello, \"World!\""}, + wantErr: false, + }, + { + name: "grep command with quoted search term", + input: `grep -r "search term" /path/to/dir`, + expected: []string{"grep", "-r", "search term", "/path/to/dir"}, + wantErr: false, + }, + { + name: "ls command with quoted filename", + input: `ls -la "file with spaces.txt"`, + expected: []string{"ls", "-la", "file with spaces.txt"}, + wantErr: false, + }, + { + name: "empty string", + input: ``, + expected: []string{""}, + wantErr: false, + }, + { + name: "multiple spaces", + input: ` multiple spaces `, + expected: []string{"multiple", "spaces", ""}, + wantErr: false, + }, + { + name: "with comment string", + input: `echo "Hello, W#orld!" ${USER} # This is a comment`, + expected: []string{"echo", "Hello, W#orld!", "${USER}"}, + wantErr: false, + }, + // may cause an error in shlex at python3 + { + name: "unclosed double quote", + input: `"unclosed quote`, + expected: []string{"unclosed quote"}, + wantErr: false, + }, + { + name: "unclosed single quote", + input: `'unclosed quote`, + expected: []string{"unclosed quote"}, + wantErr: false, + }, + { + name: "mixed unclosed quotes", + input: `"mixed 'quotes`, + expected: []string{"mixed 'quotes"}, + wantErr: false, + }, + { + name: "single quote closed with double quote", + input: `"hello world'`, + expected: []string{"hello world'"}, + wantErr: false, + }, + { + name: "double quote closed with single quote", + input: `'hello world"`, + expected: []string{"hello world\""}, + wantErr: false, + }, + } + + // execute each test case + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // call the ShlexSplit function + result, err := ShlexSplit(tc.input) + + // check for expected error + if tc.wantErr { + if err == nil { + t.Errorf("FAIL: Expected error but got none, Expected: %q\n", tc.expected) + } + return + } + + if assert.NoError(t, err, "FAIL: Unexpected error for input %q", tc.input) { + // check if the result matches the expected output + assert.Equal(t, tc.expected, result, + "FAIL: Result mismatch,Input %q, Expected %q, Got: %q\n", + tc.input, tc.expected, result, + ) + } + }) + } +}