git @ Cat's Eye Technologies Falderal / 6e738ee
Write ShellImplementation.subst() to replace vars with quoting. Chris Pressey 3 years ago
7 changed file(s) with 81 addition(s) and 57 deletion(s). Raw diff Collapse all Expand all
563563 def __eq__(self, other):
564564 return self.__class__ == other.__class__ and self.command == other.command
565565
566 def subst(self, command, var_name, value):
567 """Replace all occurrences of `var_name` in `command` with
568 `value`, but make sure `value` is properly shell-escaped first."""
569 # We could do this with shlex.quote, but that only appeared in 3.3.
570 # To support Python 2.7, we just take every character that is a
571 # shell metacharacter, and escape it. Note that we have to handle
572 # backslashes first, lest we escape backslashes we just added in.
573 value = value.replace('\\', '\\\\')
574 for c in """><*?[]'"`$()|;&#""":
575 value = value.replace(c, '\\' + c)
576 return command.replace(var_name, value)
577
566578 def run(self, body=None, input=None):
567 # expand variables in the command
579 # expand variables in the command. we always ensure the substitution text
580 # is shell-quoted using shlex.quote, so that no quotes need be used in the
581 # command template string.
568582 test_filename = None
569583 output_filename = None
570584 command = self.command
582596 file.close()
583597 os.close(fd)
584598 # replace all occurrences in command
585 command = command.replace('%(test-body-file)', test_filename)
599 command = self.subst(command, '%(test-body-file)', test_filename)
586600 command_contained_test_body_file = True
587601
588602 if '%(test-body-text)' in self.command:
589 # escape all single quotes in body
590 body = re.sub(r"'", r"\'", body)
591603 # replace all occurrences in command
592 command = command.replace('%(test-body-text)', body)
604 command = self.subst(command, '%(test-body-text)', body)
593605 command_contained_test_body_text = True
594606
595607 if '%(test-input-file)' in self.command:
601613 file.close()
602614 os.close(fd)
603615 # replace all occurrences in command
604 command = command.replace('%(test-input-file)', test_input_filename)
616 command = self.subst(command, '%(test-input-file)', test_input_filename)
605617 command_contained_test_input_file = True
606618
607619 if '%(test-input-text)' in self.command:
608 # escape all single quotes in input
609 input = re.sub(r"'", r"\'", input)
610620 # replace all occurrences in command
611 command = command.replace('%(test-input-text)', input)
621 command = self.subst(command, '%(test-input-text)', input)
612622 command_contained_test_input_text = True
613623
614624 if '%(output-file)' in self.command:
616626 fd, output_filename = mkstemp()
617627 os.close(fd)
618628 # replace all occurrences in command
619 command = command.replace('%(output-file)', output_filename)
629 command = self.subst(command, '%(output-file)', output_filename)
620630
621631 # subshell the command and return the output
622632 pipe = Popen(command, shell=True,
1111 cd tests
1212
1313 FIRST_TESTS="
14 test-body-text-input-text
1514 test-pass-fail test-no-functionality test-ill-formed test-no-test-body
1615 test-var-subst-no-eol
1716 test-utf8 test-crlf
1817 test-bad-indentation
18 test-shell-quoting
1919 test-input-sections test-shared-body
2020 test-stdout-stderr test-err-no-stderr
2121 test-freestyle-format
+0
-3
tests/test-body-text-input-text.expected less more
0 --------------------------------
1 Total test runs: 4, failures: 0
2 --------------------------------
+0
-35
tests/test-body-text-input-text.markdown less more
0 Falderal Test: test-body-text and test-input-text
1 -------------------------------------------------
2
3 The test body and the test input can be passed as
4 strings to the shell command.
5
6 -> Functionality "Echo Body" is implemented by shell command
7 -> "python echo.py %(test-body-text)"
8
9 -> Tests for functionality "Echo Body"
10
11 | foo
12 + bar
13 = foo
14
15 Single quotes in the test body text are single escaped.
16
17 | don't
18 + can't
19 = don't
20
21 -> Functionality "Echo Input" is implemented by shell command
22 -> "python echo.py %(test-input-text)"
23
24 -> Tests for functionality "Echo Input"
25
26 | foo
27 + bar
28 = bar
29
30 Single quotes in the test input text are single escaped.
31
32 | don't
33 + can't
34 = can't
0 --------------------------------
1 Total test runs: 4, failures: 0
2 --------------------------------
0 Falderal Test: shell quoting
1 ----------------------------
2
3 The test body and the test input can be passed as
4 strings to the shell command. They don't need quotes
5 around them.
6
7 -> Functionality "Echo Body" is implemented by shell command
8 -> "python echo.py %(test-body-text)"
9
10 -> Tests for functionality "Echo Body"
11
12 | foo
13 + bar
14 = foo
15
16 Single quotes in the test body text are single escaped.
17
18 | don't
19 + can't
20 = don't
21
22 -> Functionality "Echo Input" is implemented by shell command
23 -> "python echo.py %(test-input-text)"
24
25 -> Tests for functionality "Echo Input"
26
27 | foo
28 + bar
29 = bar
30
31 Single quotes in the test input text are single escaped.
32
33 | don't
34 + can't
35 = can't
33 Tests for variable substitution, and missing EOL at end
44 of output.
55
6 Note the use of single quotes around the `%(test-body-text)` variable;
7 without these, shell chaos is likely to result.
6 Note that Falderal is responsible for quoting the substitution text
7 of all `%(...)` variables occurring in a shell command template;
8 it is not necessary to put any quotes around them in the template string.
89
910 -> Functionality "Echo" is implemented by
10 -> shell command "python echo.py '%(test-body-text)'"
11 -> shell command "python echo.py %(test-body-text)"
1112
1213 -> Tests for functionality "Echo"
1314
1415 | hello
1516 = hello
1617
18 FIXME: this does not work for now, because the newline is not escaped,
19 so the command runs over two lines, and it thinks `hi` is a shell command.
20
21 > | hi
22 > | hi
23 > = hi
24 > = hi
25
1726 | hi
18 | hi
19 = hi
2027 = hi
2128
2229 -> Functionality "Echo, no newline" is implemented by
23 -> shell command "python echo.py -n '%(test-body-text)'"
30 -> shell command "python echo.py -n %(test-body-text)"
2431
2532 -> Tests for functionality "Echo, no newline"
2633
2734 | hello
2835 = hello
2936
37 FIXME: this does not work for now, because the newline is not escaped,
38 so the command runs over two lines, and it thinks `hi` is a shell command.
39
40 > | hi
41 > | hi
42 > = hi
43 > = hi
44
3045 | hi
31 | hi
32 = hi
3346 = hi
3447
3548 Note that when variables are expanded, backslash sequences in the