Annotation of pkgsrc/pkgtools/pkglint/files/shtokenizer_test.go, Revision 1.13
1.12 rillig 1: package pkglint
1.1 rillig 2:
1.8 rillig 3: import (
4: "gopkg.in/check.v1"
5: "strings"
6: )
1.1 rillig 7:
8: func (s *Suite) Test_ShTokenizer_ShAtom(c *check.C) {
1.6 rillig 9: t := s.Init(c)
10:
1.13 ! rillig 11: // testRest ensures that the given string is parsed to the expected
1.8 rillig 12: // atoms, and returns the remaining text.
1.13 ! rillig 13: testRest := func(s string, expected ...*ShAtom) string {
1.1 rillig 14: p := NewShTokenizer(dummyLine, s, false)
15: q := shqPlain
16: for _, exp := range expected {
17: c.Check(p.ShAtom(q), deepEquals, exp)
18: q = exp.Quoting
19: }
20: return p.Rest()
21: }
1.8 rillig 22:
1.11 rillig 23: // test ensures that the given string is parsed to the expected
1.8 rillig 24: // atoms, and that the text is completely consumed by the parser.
1.11 rillig 25: test := func(str string, expected ...*ShAtom) {
1.13 ! rillig 26: rest := testRest(str, expected...)
1.1 rillig 27: c.Check(rest, equals, "")
1.6 rillig 28: t.CheckOutputEmpty()
1.1 rillig 29: }
30:
1.8 rillig 31: atom := func(typ ShAtomType, text string) *ShAtom {
32: return &ShAtom{typ, text, shqPlain, nil}
1.1 rillig 33: }
1.8 rillig 34:
35: operator := func(s string) *ShAtom { return atom(shtOperator, s) }
36: comment := func(s string) *ShAtom { return atom(shtComment, s) }
37: mkvar := func(varname string, modifiers ...string) *ShAtom {
1.1 rillig 38: text := "${" + varname
39: for _, modifier := range modifiers {
1.8 rillig 40: text += ":" + strings.Replace(strings.Replace(modifier, "\\", "\\\\", -1), ":", "\\:", -1)
1.1 rillig 41: }
42: text += "}"
1.10 rillig 43: varuse := NewMkVarUse(varname, modifiers...)
1.1 rillig 44: return &ShAtom{shtVaruse, text, shqPlain, varuse}
45: }
1.11 rillig 46: shvar := func(text, varname string) *ShAtom { return &ShAtom{shtShVarUse, text, shqPlain, varname} }
47: text := func(s string) *ShAtom { return atom(shtText, s) }
1.8 rillig 48: whitespace := func(s string) *ShAtom { return atom(shtSpace, s) }
49:
50: space := whitespace(" ")
1.3 rillig 51: semicolon := operator(";")
52: pipe := operator("|")
1.8 rillig 53: subshell := atom(shtSubshell, "$$(")
54:
55: q := func(q ShQuoting, atom *ShAtom) *ShAtom {
56: return &ShAtom{atom.Type, atom.MkText, q, atom.data}
57: }
58: backt := func(atom *ShAtom) *ShAtom { return q(shqBackt, atom) }
59: dquot := func(atom *ShAtom) *ShAtom { return q(shqDquot, atom) }
60: squot := func(atom *ShAtom) *ShAtom { return q(shqSquot, atom) }
61: subsh := func(atom *ShAtom) *ShAtom { return q(shqSubsh, atom) }
62: backtDquot := func(atom *ShAtom) *ShAtom { return q(shqBacktDquot, atom) }
63: backtSquot := func(atom *ShAtom) *ShAtom { return q(shqBacktSquot, atom) }
64: dquotBackt := func(atom *ShAtom) *ShAtom { return q(shqDquotBackt, atom) }
65: subshDquot := func(atom *ShAtom) *ShAtom { return q(shqSubshDquot, atom) }
66: subshSquot := func(atom *ShAtom) *ShAtom { return q(shqSubshSquot, atom) }
67: dquotBacktDquot := func(atom *ShAtom) *ShAtom { return q(shqDquotBacktDquot, atom) }
68: dquotBacktSquot := func(atom *ShAtom) *ShAtom { return q(shqDquotBacktSquot, atom) }
69:
70: // Ignore unused functions; useful for deleting some of the tests during debugging.
71: use := func(args ...interface{}) {}
1.13 ! rillig 72: use(testRest, test)
1.8 rillig 73: use(operator, comment, mkvar, text, whitespace)
74: use(space, semicolon, pipe, subshell)
75: use(backt, dquot, squot, subsh)
76: use(backtDquot, backtSquot, dquotBackt, subshDquot, subshSquot)
77: use(dquotBacktDquot, dquotBacktSquot)
1.1 rillig 78:
1.11 rillig 79: test("" /* none */)
1.1 rillig 80:
1.11 rillig 81: test("$$var",
82: shvar("$$var", "var"))
1.1 rillig 83:
1.11 rillig 84: test("$$var$$var",
85: shvar("$$var", "var"),
86: shvar("$$var", "var"))
1.1 rillig 87:
1.11 rillig 88: test("$$var;;",
89: shvar("$$var", "var"),
1.3 rillig 90: operator(";;"))
1.1 rillig 91:
1.11 rillig 92: test("'single-quoted'",
1.8 rillig 93: squot(text("'")),
94: squot(text("single-quoted")),
95: text("'"))
1.1 rillig 96:
1.13 ! rillig 97: rest := testRest("\"" /* none */)
1.1 rillig 98: c.Check(rest, equals, "\"")
99:
1.11 rillig 100: test("$${file%.c}.o",
101: shvar("$${file%.c}", "file"),
102: text(".o"))
1.1 rillig 103:
1.11 rillig 104: test("hello",
1.8 rillig 105: text("hello"))
1.1 rillig 106:
1.11 rillig 107: test("hello, world",
1.8 rillig 108: text("hello,"),
1.1 rillig 109: space,
1.8 rillig 110: text("world"))
1.1 rillig 111:
1.11 rillig 112: test("\"",
1.8 rillig 113: dquot(text("\"")))
1.1 rillig 114:
1.11 rillig 115: test("`",
1.8 rillig 116: backt(text("`")))
1.1 rillig 117:
1.11 rillig 118: test("`cat filename`",
1.8 rillig 119: backt(text("`")),
120: backt(text("cat")),
121: backt(space),
1.11 rillig 122: backt(text("filename")),
1.8 rillig 123: text("`"))
1.1 rillig 124:
1.11 rillig 125: test("hello, \"world\"",
1.8 rillig 126: text("hello,"),
1.1 rillig 127: space,
1.8 rillig 128: dquot(text("\"")),
129: dquot(text("world")),
130: text("\""))
1.1 rillig 131:
1.11 rillig 132: test("set -e;",
1.8 rillig 133: text("set"),
1.1 rillig 134: space,
1.8 rillig 135: text("-e"),
1.1 rillig 136: semicolon)
137:
1.11 rillig 138: test("cd ${WRKSRC}/doc/man/man3; PAGES=\"`ls -1 | ${SED} -e 's,3qt$$,3,'`\";",
1.8 rillig 139: text("cd"),
1.1 rillig 140: space,
1.8 rillig 141: mkvar("WRKSRC"),
142: text("/doc/man/man3"),
1.1 rillig 143: semicolon,
144: space,
1.8 rillig 145: text("PAGES="),
146: dquot(text("\"")),
147: dquotBackt(text("`")),
148: dquotBackt(text("ls")),
149: dquotBackt(space),
150: dquotBackt(text("-1")),
151: dquotBackt(space),
152: dquotBackt(operator("|")),
153: dquotBackt(space),
154: dquotBackt(mkvar("SED")),
155: dquotBackt(space),
156: dquotBackt(text("-e")),
157: dquotBackt(space),
158: dquotBacktSquot(text("'")),
159: dquotBacktSquot(text("s,3qt$$,3,")),
160: dquotBackt(text("'")),
161: dquot(text("`")),
162: text("\""),
1.1 rillig 163: semicolon)
164:
1.11 rillig 165: test("ls -1 | ${SED} -e 's,3qt$$,3,'",
1.8 rillig 166: text("ls"), space, text("-1"), space,
1.1 rillig 167: pipe, space,
1.8 rillig 168: mkvar("SED"), space, text("-e"), space,
169: squot(text("'")), squot(text("s,3qt$$,3,")), text("'"))
1.1 rillig 170:
1.11 rillig 171: test("(for PAGE in $$PAGES; do ",
1.8 rillig 172: operator("("),
173: text("for"),
1.1 rillig 174: space,
1.8 rillig 175: text("PAGE"),
1.1 rillig 176: space,
1.8 rillig 177: text("in"),
1.1 rillig 178: space,
1.11 rillig 179: shvar("$$PAGES", "PAGES"),
1.1 rillig 180: semicolon,
181: space,
1.8 rillig 182: text("do"),
1.1 rillig 183: space)
184:
1.11 rillig 185: test(" ${ECHO} installing ${DESTDIR}${QTPREFIX}/man/man3/$${PAGE}; ",
1.1 rillig 186: whitespace(" "),
1.8 rillig 187: mkvar("ECHO"),
1.1 rillig 188: space,
1.8 rillig 189: text("installing"),
1.1 rillig 190: space,
1.8 rillig 191: mkvar("DESTDIR"),
192: mkvar("QTPREFIX"),
1.11 rillig 193: text("/man/man3/"),
194: shvar("$${PAGE}", "PAGE"),
1.1 rillig 195: semicolon,
196: space)
197:
1.11 rillig 198: test(" set - X `head -1 $${PAGE}qt`; ",
1.1 rillig 199: whitespace(" "),
1.8 rillig 200: text("set"),
1.1 rillig 201: space,
1.8 rillig 202: text("-"),
1.1 rillig 203: space,
1.8 rillig 204: text("X"),
1.1 rillig 205: space,
1.8 rillig 206: backt(text("`")),
207: backt(text("head")),
208: backt(space),
209: backt(text("-1")),
210: backt(space),
1.11 rillig 211: backt(shvar("$${PAGE}", "PAGE")),
212: backt(text("qt")),
1.8 rillig 213: text("`"),
1.1 rillig 214: semicolon,
215: space)
216:
1.11 rillig 217: test("`\"one word\"`",
1.8 rillig 218: backt(text("`")),
219: backtDquot(text("\"")),
220: backtDquot(text("one word")),
221: backt(text("\"")),
222: text("`"))
1.1 rillig 223:
1.11 rillig 224: test("$$var \"$$var\" '$$var' `$$var`",
225: shvar("$$var", "var"),
1.1 rillig 226: space,
1.8 rillig 227: dquot(text("\"")),
1.11 rillig 228: dquot(shvar("$$var", "var")),
1.8 rillig 229: text("\""),
230: space,
231: squot(text("'")),
1.11 rillig 232: squot(shvar("$$var", "var")),
1.8 rillig 233: text("'"),
234: space,
235: backt(text("`")),
1.11 rillig 236: backt(shvar("$$var", "var")),
1.8 rillig 237: text("`"))
1.1 rillig 238:
1.11 rillig 239: test("\"`'echo;echo'`\"",
1.8 rillig 240: dquot(text("\"")),
241: dquotBackt(text("`")),
242: dquotBacktSquot(text("'")),
243: dquotBacktSquot(text("echo;echo")),
244: dquotBackt(text("'")),
245: dquot(text("`")),
246: text("\""))
1.1 rillig 247:
1.11 rillig 248: test("cat<file",
1.8 rillig 249: text("cat"),
1.3 rillig 250: operator("<"),
1.8 rillig 251: text("file"))
1.1 rillig 252:
1.11 rillig 253: test("\\$$escaped",
254: text("\\$$escaped"))
255:
256: test("-e \"s,\\$$sysconfdir/jabberd,\\$$sysconfdir,g\"",
1.8 rillig 257: text("-e"),
1.1 rillig 258: space,
1.8 rillig 259: dquot(text("\"")),
260: dquot(text("s,\\$$sysconfdir/jabberd,\\$$sysconfdir,g")),
261: text("\""))
1.1 rillig 262:
1.11 rillig 263: test("echo $$, $$- $$/ $$; $$| $$,$$/$$;$$-",
1.8 rillig 264: text("echo"),
1.1 rillig 265: space,
1.8 rillig 266: text("$$,"),
1.7 rillig 267: space,
1.11 rillig 268: shvar("$$-", "-"),
1.7 rillig 269: space,
1.8 rillig 270: text("$$/"),
1.7 rillig 271: space,
1.8 rillig 272: text("$$"),
1.7 rillig 273: semicolon,
274: space,
1.8 rillig 275: text("$$"),
1.7 rillig 276: pipe,
277: space,
1.8 rillig 278: text("$$,$$/$$"),
1.7 rillig 279: semicolon,
1.11 rillig 280: shvar("$$-", "-"))
1.1 rillig 281:
1.13 ! rillig 282: rest = testRest("COMMENT=\t\\Make $$$$ fast\"",
1.8 rillig 283: text("COMMENT="),
1.1 rillig 284: whitespace("\t"),
1.8 rillig 285: text("\\Make"),
1.1 rillig 286: space,
1.11 rillig 287: shvar("$$$$", "$"),
1.1 rillig 288: space,
1.8 rillig 289: text("fast"))
1.1 rillig 290: c.Check(rest, equals, "\"")
291:
1.11 rillig 292: test("var=`echo;echo|echo&echo||echo&&echo>echo`",
1.8 rillig 293: text("var="),
294: backt(text("`")),
295: backt(text("echo")),
296: backt(semicolon),
297: backt(text("echo")),
298: backt(operator("|")),
299: backt(text("echo")),
300: backt(operator("&")),
301: backt(text("echo")),
302: backt(operator("||")),
303: backt(text("echo")),
304: backt(operator("&&")),
305: backt(text("echo")),
306: backt(operator(">")),
307: backt(text("echo")),
308: text("`"))
1.1 rillig 309:
1.11 rillig 310: test("# comment",
1.8 rillig 311: comment("# comment"))
1.11 rillig 312: test("no#comment",
1.8 rillig 313: text("no#comment"))
1.11 rillig 314: test("`# comment`continue",
1.8 rillig 315: backt(text("`")),
316: backt(comment("# comment")),
317: text("`"),
318: text("continue"))
1.11 rillig 319: test("`no#comment`continue",
1.8 rillig 320: backt(text("`")),
321: backt(text("no#comment")),
322: text("`"),
323: text("continue"))
1.1 rillig 324:
1.11 rillig 325: test("var=`tr 'A-Z' 'a-z'`",
1.8 rillig 326: text("var="),
327: backt(text("`")),
328: backt(text("tr")),
329: backt(space),
330: backtSquot(text("'")),
331: backtSquot(text("A-Z")),
332: backt(text("'")),
333: backt(space),
334: backtSquot(text("'")),
335: backtSquot(text("a-z")),
336: backt(text("'")),
337: text("`"))
1.1 rillig 338:
1.11 rillig 339: test("var=\"`echo \"\\`echo foo\\`\"`\"",
1.8 rillig 340: text("var="),
341: dquot(text("\"")),
342: dquotBackt(text("`")),
343: dquotBackt(text("echo")),
344: dquotBackt(space),
345: dquotBacktDquot(text("\"")),
346: dquotBacktDquot(text("\\`echo foo\\`")), // One atom, since it doesn't influence parsing.
347: dquotBackt(text("\"")),
348: dquot(text("`")),
349: text("\""))
1.1 rillig 350:
1.11 rillig 351: test("if cond1; then action1; elif cond2; then action2; else action3; fi",
1.8 rillig 352: text("if"), space, text("cond1"), semicolon, space,
353: text("then"), space, text("action1"), semicolon, space,
354: text("elif"), space, text("cond2"), semicolon, space,
355: text("then"), space, text("action2"), semicolon, space,
356: text("else"), space, text("action3"), semicolon, space,
357: text("fi"))
358:
1.11 rillig 359: test("$$(cat)",
1.8 rillig 360: subsh(subshell),
361: subsh(text("cat")),
362: text(")"))
363:
1.11 rillig 364: test("$$(cat 'file')",
1.8 rillig 365: subsh(subshell),
366: subsh(text("cat")),
367: subsh(space),
368: subshSquot(text("'")),
369: subshSquot(text("file")),
370: subsh(text("'")),
371: text(")"))
372:
1.11 rillig 373: test("$$(# comment) arg",
1.8 rillig 374: subsh(subshell),
375: subsh(comment("# comment")),
376: text(")"),
377: space,
378: text("arg"))
379:
1.11 rillig 380: test("$$(echo \"first\" 'second')",
1.8 rillig 381: subsh(subshell),
382: subsh(text("echo")),
383: subsh(space),
384: subshDquot(text("\"")),
385: subshDquot(text("first")),
386: subsh(text("\"")),
387: subsh(space),
388: subshSquot(text("'")),
389: subshSquot(text("second")),
390: subsh(text("'")),
391: text(")"))
1.1 rillig 392: }
393:
1.8 rillig 394: func (s *Suite) Test_ShTokenizer_ShAtom__quoting(c *check.C) {
1.13 ! rillig 395: test := func(input, expectedOutput string) {
1.1 rillig 396: p := NewShTokenizer(dummyLine, input, false)
397: q := shqPlain
398: result := ""
399: for {
400: token := p.ShAtom(q)
401: if token == nil {
402: break
403: }
1.2 rillig 404: result += token.MkText
1.1 rillig 405: if token.Quoting != q {
406: q = token.Quoting
407: result += "[" + q.String() + "]"
408: }
409: }
410: c.Check(result, equals, expectedOutput)
411: c.Check(p.Rest(), equals, "")
412: }
413:
1.13 ! rillig 414: test("hello, world", "hello, world")
! 415: test("hello, \"world\"", "hello, \"[d]world\"[plain]")
! 416: test("1 \"\" 2 '' 3 `` 4", "1 \"[d]\"[plain] 2 '[s]'[plain] 3 `[b]`[plain] 4")
! 417: test("\"\"", "\"[d]\"[plain]")
! 418: test("''", "'[s]'[plain]")
! 419: test("``", "`[b]`[plain]")
! 420: test("x\"x`x`x\"x'x\"x'", "x\"[d]x`[db]x`[d]x\"[plain]x'[s]x\"x'[plain]")
! 421: test("x\"x`x'x'x`x\"", "x\"[d]x`[db]x'[dbs]x'[db]x`[d]x\"[plain]")
! 422: test("x\\\"x\\'x\\`x\\\\", "x\\\"x\\'x\\`x\\\\")
! 423: test("x\"x\\\"x\\'x\\`x\\\\", "x\"[d]x\\\"x\\'x\\`x\\\\")
! 424: test("x'x\\\"x\\'x\\`x\\\\", "x'[s]x\\\"x\\'[plain]x\\`x\\\\")
! 425: test("x`x\\\"x\\'x\\`x\\\\", "x`[b]x\\\"x\\'x\\`x\\\\")
1.1 rillig 426: }
427:
428: func (s *Suite) Test_ShTokenizer_ShToken(c *check.C) {
1.6 rillig 429: t := s.Init(c)
430:
1.8 rillig 431: // testRest ensures that the given string is parsed to the expected
432: // tokens, and returns the remaining text.
433: testRest := func(str string, expected ...string) string {
434: p := NewShTokenizer(dummyLine, str, false)
435: for _, exp := range expected {
436: c.Check(p.ShToken().MkText, equals, exp)
437: }
438: return p.Rest()
439: }
1.13 ! rillig 440:
1.8 rillig 441: test := func(str string, expected ...string) {
1.1 rillig 442: p := NewShTokenizer(dummyLine, str, false)
443: for _, exp := range expected {
1.8 rillig 444: c.Check(p.ShToken().MkText, equals, exp)
1.1 rillig 445: }
446: c.Check(p.Rest(), equals, "")
1.6 rillig 447: t.CheckOutputEmpty()
1.1 rillig 448: }
1.13 ! rillig 449:
! 450: testNil := func(str string) {
1.8 rillig 451: p := NewShTokenizer(dummyLine, str, false)
452: c.Check(p.ShToken(), check.IsNil)
453: c.Check(p.Rest(), equals, "")
454: t.CheckOutputEmpty()
455: }
456:
1.13 ! rillig 457: testNil("")
! 458: testNil(" ")
1.8 rillig 459: rest := testRest("\t\t\t\n\n\n\n\t ",
1.13 ! rillig 460: "\n\n\n\n")
! 461: c.Check(rest, equals, "\t ")
1.8 rillig 462:
463: test("echo",
464: "echo")
465:
466: test("`cat file`",
467: "`cat file`")
468:
469: test("PAGES=\"`ls -1 | ${SED} -e 's,3qt$$,3,'`\"",
470: "PAGES=\"`ls -1 | ${SED} -e 's,3qt$$,3,'`\"")
471:
472: test("echo hello, world",
473: "echo",
474: "hello,",
475: "world")
476:
477: test("if cond1; then action1; elif cond2; then action2; else action3; fi",
478: "if", "cond1", ";", "then",
479: "action1", ";",
480: "elif", "cond2", ";", "then",
481: "action2", ";",
482: "else", "action3", ";",
483: "fi")
484:
485: test("PATH=/nonexistent env PATH=${PATH:Q} true",
486: "PATH=/nonexistent",
487: "env",
488: "PATH=${PATH:Q}",
489: "true")
490:
491: test("id=$$(${AWK} '{print}' < ${WRKSRC}/idfile)",
492: "id=$$(${AWK} '{print}' < ${WRKSRC}/idfile)")
493:
494: test("id=`${AWK} '{print}' < ${WRKSRC}/idfile`",
495: "id=`${AWK} '{print}' < ${WRKSRC}/idfile`")
496: }
497:
1.11 rillig 498: func (s *Suite) Test_ShTokenizer_shVarUse(c *check.C) {
499:
500: test := func(input string, output *ShAtom, rest string) {
501: tok := NewShTokenizer(nil, input, false)
502: actual := tok.shVarUse(shqPlain)
503:
504: c.Check(actual, deepEquals, output)
505: c.Check(tok.Rest(), equals, rest)
506: }
507:
508: shvar := func(text, varname string) *ShAtom {
509: return &ShAtom{shtShVarUse, text, shqPlain, varname}
510: }
511:
512: test("$", nil, "$")
513: test("$$", nil, "$$")
514: test("${MKVAR}", nil, "${MKVAR}")
515:
516: test("$$a", shvar("$$a", "a"), "")
517: test("$$a.", shvar("$$a", "a"), ".")
518: test("$$a_b_123:", shvar("$$a_b_123", "a_b_123"), ":")
519: test("$$123", shvar("$$1", "1"), "23")
520:
521: test("$${varname}", shvar("$${varname}", "varname"), "")
522: test("$${varname}.", shvar("$${varname}", "varname"), ".")
523: test("$${0123}.", shvar("$${0123}", "0123"), ".")
524: test("$${varname", nil, "$${varname")
525:
526: test("$${var:=value}", shvar("$${var:=value}", "var"), "")
527: test("$${var#value}", shvar("$${var#value}", "var"), "")
528: test("$${var##value}", shvar("$${var##value}", "var"), "")
529: test("$${var##*}", shvar("$${var##*}", "var"), "")
530: test("$${var%\".gz\"}", shvar("$${var%\".gz\"}", "var"), "")
531:
532: // TODO: allow variables in patterns.
533: test("$${var%.${ext}}", nil, "$${var%.${ext}}")
534:
535: test("$${var##*", nil, "$${var##*")
536: test("$${var\"", nil, "$${var\"")
537:
538: // TODO: test("$${var%${EXT}}", shvar("$${var%${EXT}}", "var"), "")
539: test("$${var%${EXT}}", nil, "$${var%${EXT}}")
540:
541: // TODO: length of var
542: test("$${#var}", nil, "$${#var}")
543:
544: test("$${/}", nil, "$${/}")
545: test("$${\\}", nil, "$${\\}")
546: }
547:
1.13 ! rillig 548: // This test demonstrates that the shell tokenizer is not perfect yet.
! 549: // There are still some corner cases that trigger a parse error.
! 550: // To get 100% code coverage, they have been found using the fuzzer
! 551: // and trimmed down to minimal examples.
1.8 rillig 552: func (s *Suite) Test_ShTokenizer__examples_from_fuzzing(c *check.C) {
553: t := s.Init(c)
1.1 rillig 554:
1.8 rillig 555: mklines := t.NewMkLines("fuzzing.mk",
556: MkRcsID,
557: "",
558: "pre-configure:",
1.1 rillig 559:
1.8 rillig 560: // Covers shAtomBacktDquot: return nil.
561: // These are nested backticks with double quotes,
562: // which should be avoided since POSIX marks them as unspecified.
563: "\t"+"`\"`",
1.1 rillig 564:
1.8 rillig 565: // Covers shAtomBacktSquot: return nil
566: "\t"+"`'$`",
567:
568: // Covers shAtomDquotBacktSquot: return nil
569: "\t"+"\"`'`y",
570:
571: // Covers shAtomDquotBackt: return nil
1.9 rillig 572: // FIXME: Pkglint must parse unescaped dollar in the same way, everywhere.
1.8 rillig 573: "\t"+"\"`$|",
574:
575: // Covers shAtomDquotBacktDquot: return nil
576: // FIXME: Pkglint must support unlimited nesting.
577: "\t"+"\"`\"`",
578:
579: // Covers shAtomSubshDquot: return nil
580: "\t"+"$$(\"'",
581:
1.11 rillig 582: // Covers shAtomSubsh: case lexer.AdvanceStr("`")
1.8 rillig 583: "\t"+"$$(`",
584:
585: // Covers shAtomSubshSquot: return nil
586: "\t"+"$$('$)",
587:
1.11 rillig 588: // Covers shAtomDquotBackt: case lexer.AdvanceRegexp("^#[^`]*")
1.8 rillig 589: "\t"+"\"`# comment")
590:
591: mklines.Check()
592:
593: // Just good that these redundant error messages don't occur every day.
594: t.CheckOutputLines(
1.12 rillig 595: "WARN: fuzzing.mk:4: Internal pkglint error in ShTokenizer.ShAtom at \"`\" (quoting=bd).",
1.13 ! rillig 596: "WARN: fuzzing.mk:4: Pkglint ShellLine.CheckShellCommand: splitIntoShellTokens couldn't parse \"`\\\"`\"",
1.8 rillig 597:
1.12 rillig 598: "WARN: fuzzing.mk:5: Internal pkglint error in ShTokenizer.ShAtom at \"$`\" (quoting=bs).",
1.13 ! rillig 599: "WARN: fuzzing.mk:5: Pkglint ShellLine.CheckShellCommand: splitIntoShellTokens couldn't parse \"`'$`\"",
1.12 rillig 600: "WARN: fuzzing.mk:5: Internal pkglint error in MkLine.Tokenize at \"$`\".",
1.8 rillig 601:
1.13 ! rillig 602: "WARN: fuzzing.mk:6: Pkglint ShellLine.CheckShellCommand: splitIntoShellTokens couldn't parse \"\\\"`'`y\"",
1.8 rillig 603:
1.12 rillig 604: "WARN: fuzzing.mk:7: Internal pkglint error in ShTokenizer.ShAtom at \"$|\" (quoting=db).",
1.13 ! rillig 605: "WARN: fuzzing.mk:7: Pkglint ShellLine.CheckShellCommand: splitIntoShellTokens couldn't parse \"\\\"`$|\"",
1.12 rillig 606: "WARN: fuzzing.mk:7: Internal pkglint error in MkLine.Tokenize at \"$|\".",
1.8 rillig 607:
1.12 rillig 608: "WARN: fuzzing.mk:8: Internal pkglint error in ShTokenizer.ShAtom at \"`\" (quoting=dbd).",
1.13 ! rillig 609: "WARN: fuzzing.mk:8: Pkglint ShellLine.CheckShellCommand: splitIntoShellTokens couldn't parse \"\\\"`\\\"`\"",
1.8 rillig 610:
611: "WARN: fuzzing.mk:9: Invoking subshells via $(...) is not portable enough.",
612:
1.12 rillig 613: "WARN: fuzzing.mk:10: Internal pkglint error in ShTokenizer.ShAtom at \"`\" (quoting=S).",
1.8 rillig 614: "WARN: fuzzing.mk:10: Invoking subshells via $(...) is not portable enough.",
615:
1.12 rillig 616: "WARN: fuzzing.mk:11: Internal pkglint error in ShTokenizer.ShAtom at \"$)\" (quoting=Ss).",
1.8 rillig 617: "WARN: fuzzing.mk:11: Invoking subshells via $(...) is not portable enough.",
1.12 rillig 618: "WARN: fuzzing.mk:11: Internal pkglint error in MkLine.Tokenize at \"$)\".",
1.8 rillig 619:
1.13 ! rillig 620: "WARN: fuzzing.mk:12: Pkglint ShellLine.CheckShellCommand: splitIntoShellTokens couldn't parse \"\\\"`# comment\"")
1.8 rillig 621: }
622:
1.13 ! rillig 623: // In order to get 100% code coverage for the shell tokenizer, a panic() statement has been
! 624: // added to each uncovered basic block. After that, this fuzzer quickly found relatively
! 625: // small example programs that led to the uncovered code.
! 626: //
! 627: // This test is not useful as-is.
1.8 rillig 628: func (s *Suite) Test_ShTokenizer__fuzzing(c *check.C) {
629: t := s.Init(c)
630:
631: fuzzer := NewFuzzer()
632: fuzzer.Char("\"'`$();|_#", 10)
633: fuzzer.Range('a', 'z', 5)
634:
635: defer fuzzer.CheckOk()
636: for i := 0; i < 1000; i++ {
637: tokenizer := NewShTokenizer(dummyLine, fuzzer.Generate(50), false)
638: tokenizer.ShAtoms()
1.13 ! rillig 639: t.Output() // Discard the output, only react on panics.
1.1 rillig 640: }
1.8 rillig 641: fuzzer.Ok()
1.1 rillig 642: }
CVSweb <webmaster@jp.NetBSD.org>