1+ package shellwords
2+
3+ import (
4+ "strings"
5+ "testing"
6+ )
7+
8+ // ParseBacktick=true → an unmatched ‘)’ should be treated as an ERROR (not a literal)
9+ func TestUnmatchedClosingParen_ReturnsError_ParseBacktickEnabled (t * testing.T ) {
10+ p := NewParser ()
11+ p .ParseBacktick = true
12+
13+ _ , err := p .Parse ("))" )
14+ if err == nil {
15+ t .Fatal ("expected error for unmatched ')', got nil" )
16+ }
17+ }
18+
19+ // ParseBacktick=true → Do not execute; must fail
20+ func TestBareClosingParen_NoExecution_ReturnsError (t * testing.T ) {
21+ p := NewParser ()
22+ p .ParseBacktick = true
23+
24+ _ , err := p .Parse ("prefix)echo PWNED)" )
25+ if err == nil {
26+ t .Fatal ("expected error for unmatched ')', got nil" )
27+ }
28+ }
29+
30+ // Default behavior → ‘)’ is a literal; it does not break parsing
31+ func TestBareClosingParen_DefaultParsingLiteral (t * testing.T ) {
32+ out , err := Parse (")a b)c d" )
33+ if err != nil {
34+ t .Fatalf ("unexpected error: %v" , err )
35+ }
36+
37+ want := []string {")a" , "b)c" , "d" }
38+ if len (out ) != len (want ) {
39+ t .Fatalf ("unexpected token count: got %d, want %d, out=%#v" , len (out ), len (want ), out )
40+ }
41+ for i := range want {
42+ if out [i ] != want [i ] {
43+ t .Fatalf ("unexpected token at %d: got %q, want %q, out=%#v" , i , out [i ], want [i ], out )
44+ }
45+ }
46+ }
47+
48+ // ParseBacktick=false → $(...) remains a FULL literal
49+ func TestDollarParenLiteralWhenParseBacktickDisabled (t * testing.T ) {
50+ p := NewParser ()
51+ p .ParseBacktick = false
52+
53+ out , err := p .Parse (`$(echo "foo")` )
54+ if err != nil {
55+ t .Fatalf ("unexpected error: %v" , err )
56+ }
57+
58+ want := []string {`$(echo "foo")` }
59+ if len (out ) != len (want ) {
60+ t .Fatalf ("unexpected token count: got %d, want %d, out=%#v" , len (out ), len (want ), out )
61+ }
62+ if out [0 ] != want [0 ] {
63+ t .Fatalf ("unexpected output: got %q, want %q" , out [0 ], want [0 ])
64+ }
65+ }
66+
67+ // If true → $(...) continues to work with ParseBacktick=true
68+ func TestValidDollarCommandSubstitutionStillWorks (t * testing.T ) {
69+ p := NewParser ()
70+ p .ParseBacktick = true
71+
72+ out , err := p .Parse ("prefix $(printf hello) suffix" )
73+ if err != nil {
74+ t .Fatalf ("unexpected error: %v" , err )
75+ }
76+
77+ got := strings .Join (out , " " )
78+ if ! strings .Contains (got , "hello" ) {
79+ t .Fatalf ("expected valid command substitution to work, got: %q" , got )
80+ }
81+ }
82+
83+ // Error → Unclosed $(
84+ func TestUnclosedDollarCommandSubstitutionReturnsError (t * testing.T ) {
85+ p := NewParser ()
86+ p .ParseBacktick = true
87+
88+ _ , err := p .Parse ("prefix $(echo hello" )
89+ if err == nil {
90+ t .Fatal ("expected error for unclosed command substitution, got nil" )
91+ }
92+ }
0 commit comments