Files
navidrome/utils/shellquote/shellquote_test.go

201 lines
6.7 KiB
Go

package shellquote_test
import (
"testing"
"github.com/navidrome/navidrome/utils/shellquote"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestShellquote(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Shellquote Suite")
}
var _ = Describe("Split", func() {
It("splits simple space-separated words", func() {
words, err := shellquote.Split("a b c")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"a", "b", "c"}))
})
It("handles multiple spaces between words", func() {
words, err := shellquote.Split("a b")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"a", "b"}))
})
It("handles single-quoted strings", func() {
words, err := shellquote.Split("'hello world'")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"hello world"}))
})
It("handles double-quoted strings", func() {
words, err := shellquote.Split(`"hello world"`)
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"hello world"}))
})
It("handles backslash escapes in unquoted mode", func() {
words, err := shellquote.Split(`hello\ world`)
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"hello world"}))
})
It("handles escaped quotes inside double quotes", func() {
words, err := shellquote.Split(`"hello \" world"`)
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{`hello " world`}))
})
It("handles mixed quoting in a single argument", func() {
words, err := shellquote.Split("he'llo wo'rld")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"hello world"}))
})
It("returns empty slice for empty input", func() {
words, err := shellquote.Split("")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(BeEmpty())
})
It("returns empty slice for whitespace-only input", func() {
words, err := shellquote.Split(" ")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(BeEmpty())
})
It("returns error for unterminated single quote", func() {
_, err := shellquote.Split("'hello")
Expect(err).To(MatchError(shellquote.ErrUnterminatedSingleQuote))
})
It("returns error for unterminated double quote", func() {
_, err := shellquote.Split(`"hello`)
Expect(err).To(MatchError(shellquote.ErrUnterminatedDoubleQuote))
})
It("returns error for unterminated escape", func() {
_, err := shellquote.Split(`hello\`)
Expect(err).To(MatchError(shellquote.ErrUnterminatedEscape))
})
It("handles tabs and newlines as delimiters", func() {
words, err := shellquote.Split("a\tb\nc")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"a", "b", "c"}))
})
It("parses the default MPV command template", func() {
words, err := shellquote.Split("mpv --audio-device=%d --no-audio-display --pause %f --input-ipc-server=%s")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(HaveLen(6))
Expect(words).To(Equal([]string{
"mpv",
"--audio-device=%d",
"--no-audio-display",
"--pause",
"%f",
"--input-ipc-server=%s",
}))
})
It("preserves spaces in quoted paths", func() {
words, err := shellquote.Split(`--ao-pcm-file="/audio/my folder/file"`)
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{`--ao-pcm-file=/audio/my folder/file`}))
})
It("handles backslash in double quotes for special chars", func() {
words, err := shellquote.Split(`"hello\\world"`)
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{`hello\world`}))
})
It("preserves backslash in double quotes for non-special chars", func() {
words, err := shellquote.Split(`"hello\nworld"`)
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{`hello\nworld`}))
})
It("handles escaped newline in double quotes", func() {
words, err := shellquote.Split("\"hello\\\nworld\"")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"helloworld"}))
})
// Cases from original go-shellquote test suite
It("handles shell glob characters as literals", func() {
words, err := shellquote.Split("glob* test?")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"glob*", "test?"}))
})
It("handles backslash-escaped special characters", func() {
words, err := shellquote.Split("don\\'t you know the dewey decimal system\\?")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"don't", "you", "know", "the", "dewey", "decimal", "system?"}))
})
It("handles single-quote escape idiom", func() {
// Shell idiom: end single-quote, escaped literal quote, start single-quote again
words, err := shellquote.Split("'don'\\''t you know the dewey decimal system?'")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"don't you know the dewey decimal system?"}))
})
It("handles empty string argument via quotes", func() {
words, err := shellquote.Split("one '' two")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"one", "", "two"}))
})
It("handles backslash-newline joining words in unquoted mode", func() {
words, err := shellquote.Split("text with\\\na backslash-escaped newline")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"text", "witha", "backslash-escaped", "newline"}))
})
It("handles quoted newline inside double quotes", func() {
words, err := shellquote.Split("text \"with\na\" quoted newline")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"text", "with\na", "quoted", "newline"}))
})
It("handles complex double-quoted escapes with backslash-newline", func() {
words, err := shellquote.Split("\"quoted\\d\\\\\\\" text with\\\na backslash-escaped newline\"")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"quoted\\d\\\" text witha backslash-escaped newline"}))
})
It("handles backslash-newline between words", func() {
words, err := shellquote.Split("text with an escaped \\\n newline in the middle")
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"text", "with", "an", "escaped", "newline", "in", "the", "middle"}))
})
It("handles double-quoted substring concatenation", func() {
words, err := shellquote.Split(`foo"bar"baz`)
Expect(err).ToNot(HaveOccurred())
Expect(words).To(Equal([]string{"foobarbaz"}))
})
It("returns error for unterminated quote after escape idiom", func() {
_, err := shellquote.Split("'test'\\''ing")
Expect(err).To(MatchError(shellquote.ErrUnterminatedSingleQuote))
})
It("returns error for unterminated double quote with single quote inside", func() {
_, err := shellquote.Split("\"foo'bar")
Expect(err).To(MatchError(shellquote.ErrUnterminatedDoubleQuote))
})
It("returns error for unterminated escape with leading whitespace", func() {
_, err := shellquote.Split(" \\")
Expect(err).To(MatchError(shellquote.ErrUnterminatedEscape))
})
})