PageRenderTime 26ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/middleware/commands_test.go

https://gitlab.com/klauer/caddy
Go | 291 lines | 288 code | 2 blank | 1 comment | 0 complexity | bfd2522c155325a97c099b6a02b3caab MD5 | raw file
  1. package middleware
  2. import (
  3. "fmt"
  4. "runtime"
  5. "strings"
  6. "testing"
  7. )
  8. func TestParseUnixCommand(t *testing.T) {
  9. tests := []struct {
  10. input string
  11. expected []string
  12. }{
  13. // 0 - emtpy command
  14. {
  15. input: ``,
  16. expected: []string{},
  17. },
  18. // 1 - command without arguments
  19. {
  20. input: `command`,
  21. expected: []string{`command`},
  22. },
  23. // 2 - command with single argument
  24. {
  25. input: `command arg1`,
  26. expected: []string{`command`, `arg1`},
  27. },
  28. // 3 - command with multiple arguments
  29. {
  30. input: `command arg1 arg2`,
  31. expected: []string{`command`, `arg1`, `arg2`},
  32. },
  33. // 4 - command with single argument with space character - in quotes
  34. {
  35. input: `command "arg1 arg1"`,
  36. expected: []string{`command`, `arg1 arg1`},
  37. },
  38. // 5 - command with multiple spaces and tab character
  39. {
  40. input: "command arg1 arg2\targ3",
  41. expected: []string{`command`, `arg1`, `arg2`, `arg3`},
  42. },
  43. // 6 - command with single argument with space character - escaped with backspace
  44. {
  45. input: `command arg1\ arg2`,
  46. expected: []string{`command`, `arg1 arg2`},
  47. },
  48. // 7 - single quotes should escape special chars
  49. {
  50. input: `command 'arg1\ arg2'`,
  51. expected: []string{`command`, `arg1\ arg2`},
  52. },
  53. }
  54. for i, test := range tests {
  55. errorPrefix := fmt.Sprintf("Test [%d]: ", i)
  56. errorSuffix := fmt.Sprintf(" Command to parse: [%s]", test.input)
  57. actual, _ := parseUnixCommand(test.input)
  58. if len(actual) != len(test.expected) {
  59. t.Errorf(errorPrefix+"Expected %d parts, got %d: %#v."+errorSuffix, len(test.expected), len(actual), actual)
  60. continue
  61. }
  62. for j := 0; j < len(actual); j++ {
  63. if expectedPart, actualPart := test.expected[j], actual[j]; expectedPart != actualPart {
  64. t.Errorf(errorPrefix+"Expected: %v Actual: %v (index %d)."+errorSuffix, expectedPart, actualPart, j)
  65. }
  66. }
  67. }
  68. }
  69. func TestParseWindowsCommand(t *testing.T) {
  70. tests := []struct {
  71. input string
  72. expected []string
  73. }{
  74. { // 0 - empty command - do not fail
  75. input: ``,
  76. expected: []string{},
  77. },
  78. { // 1 - cmd without args
  79. input: `cmd`,
  80. expected: []string{`cmd`},
  81. },
  82. { // 2 - multiple args
  83. input: `cmd arg1 arg2`,
  84. expected: []string{`cmd`, `arg1`, `arg2`},
  85. },
  86. { // 3 - multiple args with space
  87. input: `cmd "combined arg" arg2`,
  88. expected: []string{`cmd`, `combined arg`, `arg2`},
  89. },
  90. { // 4 - path without spaces
  91. input: `mkdir C:\Windows\foo\bar`,
  92. expected: []string{`mkdir`, `C:\Windows\foo\bar`},
  93. },
  94. { // 5 - command with space in quotes
  95. input: `"command here"`,
  96. expected: []string{`command here`},
  97. },
  98. { // 6 - argument with escaped quotes (two quotes)
  99. input: `cmd ""arg""`,
  100. expected: []string{`cmd`, `"arg"`},
  101. },
  102. { // 7 - argument with escaped quotes (backslash)
  103. input: `cmd \"arg\"`,
  104. expected: []string{`cmd`, `"arg"`},
  105. },
  106. { // 8 - two quotes (escaped) inside an inQuote element
  107. input: `cmd "a ""quoted value"`,
  108. expected: []string{`cmd`, `a "quoted value`},
  109. },
  110. // TODO - see how many quotes are dislayed if we use "", """, """""""
  111. { // 9 - two quotes outside an inQuote element
  112. input: `cmd a ""quoted value`,
  113. expected: []string{`cmd`, `a`, `"quoted`, `value`},
  114. },
  115. { // 10 - path with space in quotes
  116. input: `mkdir "C:\directory name\foobar"`,
  117. expected: []string{`mkdir`, `C:\directory name\foobar`},
  118. },
  119. { // 11 - space without quotes
  120. input: `mkdir C:\ space`,
  121. expected: []string{`mkdir`, `C:\`, `space`},
  122. },
  123. { // 12 - space in quotes
  124. input: `mkdir "C:\ space"`,
  125. expected: []string{`mkdir`, `C:\ space`},
  126. },
  127. { // 13 - UNC
  128. input: `mkdir \\?\C:\Users`,
  129. expected: []string{`mkdir`, `\\?\C:\Users`},
  130. },
  131. { // 14 - UNC with space
  132. input: `mkdir "\\?\C:\Program Files"`,
  133. expected: []string{`mkdir`, `\\?\C:\Program Files`},
  134. },
  135. { // 15 - unclosed quotes - treat as if the path ends with quote
  136. input: `mkdir "c:\Program files`,
  137. expected: []string{`mkdir`, `c:\Program files`},
  138. },
  139. { // 16 - quotes used inside the argument
  140. input: `mkdir "c:\P"rogra"m f"iles`,
  141. expected: []string{`mkdir`, `c:\Program files`},
  142. },
  143. }
  144. for i, test := range tests {
  145. errorPrefix := fmt.Sprintf("Test [%d]: ", i)
  146. errorSuffix := fmt.Sprintf(" Command to parse: [%s]", test.input)
  147. actual := parseWindowsCommand(test.input)
  148. if len(actual) != len(test.expected) {
  149. t.Errorf(errorPrefix+"Expected %d parts, got %d: %#v."+errorSuffix, len(test.expected), len(actual), actual)
  150. continue
  151. }
  152. for j := 0; j < len(actual); j++ {
  153. if expectedPart, actualPart := test.expected[j], actual[j]; expectedPart != actualPart {
  154. t.Errorf(errorPrefix+"Expected: %v Actual: %v (index %d)."+errorSuffix, expectedPart, actualPart, j)
  155. }
  156. }
  157. }
  158. }
  159. func TestSplitCommandAndArgs(t *testing.T) {
  160. // force linux parsing. It's more robust and covers error cases
  161. runtimeGoos = "linux"
  162. defer func() {
  163. runtimeGoos = runtime.GOOS
  164. }()
  165. var parseErrorContent = "error parsing command:"
  166. var noCommandErrContent = "no command contained in"
  167. tests := []struct {
  168. input string
  169. expectedCommand string
  170. expectedArgs []string
  171. expectedErrContent string
  172. }{
  173. // 0 - emtpy command
  174. {
  175. input: ``,
  176. expectedCommand: ``,
  177. expectedArgs: nil,
  178. expectedErrContent: noCommandErrContent,
  179. },
  180. // 1 - command without arguments
  181. {
  182. input: `command`,
  183. expectedCommand: `command`,
  184. expectedArgs: nil,
  185. expectedErrContent: ``,
  186. },
  187. // 2 - command with single argument
  188. {
  189. input: `command arg1`,
  190. expectedCommand: `command`,
  191. expectedArgs: []string{`arg1`},
  192. expectedErrContent: ``,
  193. },
  194. // 3 - command with multiple arguments
  195. {
  196. input: `command arg1 arg2`,
  197. expectedCommand: `command`,
  198. expectedArgs: []string{`arg1`, `arg2`},
  199. expectedErrContent: ``,
  200. },
  201. // 4 - command with unclosed quotes
  202. {
  203. input: `command "arg1 arg2`,
  204. expectedCommand: "",
  205. expectedArgs: nil,
  206. expectedErrContent: parseErrorContent,
  207. },
  208. // 5 - command with unclosed quotes
  209. {
  210. input: `command 'arg1 arg2"`,
  211. expectedCommand: "",
  212. expectedArgs: nil,
  213. expectedErrContent: parseErrorContent,
  214. },
  215. }
  216. for i, test := range tests {
  217. errorPrefix := fmt.Sprintf("Test [%d]: ", i)
  218. errorSuffix := fmt.Sprintf(" Command to parse: [%s]", test.input)
  219. actualCommand, actualArgs, actualErr := SplitCommandAndArgs(test.input)
  220. // test if error matches expectation
  221. if test.expectedErrContent != "" {
  222. if actualErr == nil {
  223. t.Errorf(errorPrefix+"Expected error with content [%s], found no error."+errorSuffix, test.expectedErrContent)
  224. } else if !strings.Contains(actualErr.Error(), test.expectedErrContent) {
  225. t.Errorf(errorPrefix+"Expected error with content [%s], found [%v]."+errorSuffix, test.expectedErrContent, actualErr)
  226. }
  227. } else if actualErr != nil {
  228. t.Errorf(errorPrefix+"Expected no error, found [%v]."+errorSuffix, actualErr)
  229. }
  230. // test if command matches
  231. if test.expectedCommand != actualCommand {
  232. t.Errorf(errorPrefix+"Expected command: [%s], actual: [%s]."+errorSuffix, test.expectedCommand, actualCommand)
  233. }
  234. // test if arguments match
  235. if len(test.expectedArgs) != len(actualArgs) {
  236. t.Errorf(errorPrefix+"Wrong number of arguments! Expected [%v], actual [%v]."+errorSuffix, test.expectedArgs, actualArgs)
  237. } else {
  238. // test args only if the count matches.
  239. for j, actualArg := range actualArgs {
  240. expectedArg := test.expectedArgs[j]
  241. if actualArg != expectedArg {
  242. t.Errorf(errorPrefix+"Argument at position [%d] differ! Expected [%s], actual [%s]"+errorSuffix, j, expectedArg, actualArg)
  243. }
  244. }
  245. }
  246. }
  247. }
  248. func ExampleSplitCommandAndArgs() {
  249. var commandLine string
  250. var command string
  251. var args []string
  252. // just for the test - change GOOS and reset it at the end of the test
  253. runtimeGoos = "windows"
  254. defer func() {
  255. runtimeGoos = runtime.GOOS
  256. }()
  257. commandLine = `mkdir /P "C:\Program Files"`
  258. command, args, _ = SplitCommandAndArgs(commandLine)
  259. fmt.Printf("Windows: %s: %s [%s]\n", commandLine, command, strings.Join(args, ","))
  260. // set GOOS to linux
  261. runtimeGoos = "linux"
  262. commandLine = `mkdir -p /path/with\ space`
  263. command, args, _ = SplitCommandAndArgs(commandLine)
  264. fmt.Printf("Linux: %s: %s [%s]\n", commandLine, command, strings.Join(args, ","))
  265. // Output:
  266. // Windows: mkdir /P "C:\Program Files": mkdir [/P,C:\Program Files]
  267. // Linux: mkdir -p /path/with\ space: mkdir [-p,/path/with space]
  268. }