77 lines
2.2 KiB
Go
77 lines
2.2 KiB
Go
//go:build !windows
|
|
|
|
package helpers
|
|
|
|
import (
|
|
"os/exec"
|
|
"testing"
|
|
)
|
|
|
|
// TestCrossPlatformCoverageShellQuoteArgRoundTrip 把引用后的串交给真 /bin/sh 求值,证明它被解析回
|
|
// **恰好一个、且内容完全相同**的参数。上面那条表驱动测试只能证明「我以为的形态」,这条证明
|
|
// 「shell 认的形态」——两者不是一回事,恢复命令是给用户复制到 shell 里跑的,后者才是契约。
|
|
//
|
|
// 它同时是注入的负向对照:一旦引用失效,$(id) / `id` 会真的执行、空格与 & 会拆参,argc 或
|
|
// 取回的值必然不等于原值,测试立刻红。用 build tag 排除 Windows(无 POSIX sh),跨平台形态
|
|
// 断言留在 shell_quote_test.go。
|
|
func TestCrossPlatformCoverageShellQuoteArgRoundTrip(t *testing.T) {
|
|
sh, err := exec.LookPath("sh")
|
|
if err != nil {
|
|
t.Skipf("POSIX sh unavailable: %v", err)
|
|
}
|
|
|
|
values := []string{
|
|
"ws-1",
|
|
"https://alidocs.dingtalk.com/i/nodes/x?spaceId=1&type=doc",
|
|
"sp 7",
|
|
"a;id",
|
|
"a&b",
|
|
"a|b",
|
|
"$(id)",
|
|
"`id`",
|
|
"$HOME",
|
|
"*.txt",
|
|
"a>b",
|
|
`a\b`,
|
|
`a"b`,
|
|
"a!b",
|
|
"{a,b}",
|
|
"(a)",
|
|
"~/x",
|
|
"a#b",
|
|
"it's",
|
|
"'",
|
|
"a'b'c",
|
|
"",
|
|
"报表",
|
|
"a\tb",
|
|
"a\nb",
|
|
"--not-a-flag",
|
|
}
|
|
|
|
for _, want := range values {
|
|
t.Run(want, func(t *testing.T) {
|
|
quoted := ShellQuoteArg(want)
|
|
|
|
// 1) 未被拆参也未被吞掉:位置参数个数必须恰好为 1。
|
|
// 引用失效时 "sp 7" 会变 2 个、"" 会变 0 个、$(id) 会变 id 的输出词数。
|
|
argc, err := exec.Command(sh, "-c", "set -- "+quoted+`; printf %s "$#"`).Output()
|
|
if err != nil {
|
|
t.Fatalf("argc probe failed for %q (quoted %s): %v", want, quoted, err)
|
|
}
|
|
if string(argc) != "1" {
|
|
t.Fatalf("argc = %s, want 1 — %q quoted as %s split or vanished", argc, want, quoted)
|
|
}
|
|
|
|
// 2) 内容逐字节相同:展开、命令替换、转义都不得改动值。
|
|
got, err := exec.Command(sh, "-c", "set -- "+quoted+`; printf %s "$1"`).Output()
|
|
if err != nil {
|
|
t.Fatalf("value probe failed for %q (quoted %s): %v", want, quoted, err)
|
|
}
|
|
if string(got) != want {
|
|
t.Fatalf("round-trip mismatch\n input: %q\n quoted: %s\n shell: %q", want, quoted, got)
|
|
}
|
|
})
|
|
}
|
|
}
|