| #!/usr/bin/python |
| |
| # Copyright 2001 Dave Abrahams |
| # Copyright 2011 Steven Watanabe |
| # Distributed under the Boost Software License, Version 1.0. |
| # (See accompanying file LICENSE_1_0.txt or copy at |
| # http://www.boost.org/LICENSE_1_0.txt) |
| |
| # Tests Windows command line construction. |
| # |
| # Note that the regular 'echo' is an internal shell command on Windows and |
| # therefore can not be called directly as a standalone Windows process. |
| |
| import BoostBuild |
| import os |
| import re |
| import sys |
| |
| |
| executable = sys.executable.replace("\\", "/") |
| if " " in executable: |
| executable = '"%s"' % executable |
| |
| |
| def string_of_length(n): |
| if n <= 0: |
| return "" |
| n -= 1 |
| y = ['', '$(1x10-1)', '$(10x10-1)', '$(100x10-1)', '$(1000x10-1)'] |
| result = [] |
| for i in reversed(xrange(5)): |
| x, n = divmod(n, 10 ** i) |
| result += [y[i]] * x |
| result.append('x') |
| return " ".join(result) |
| |
| |
| # Boost Jam currently does not allow preparing actions with completely empty |
| # content and always requires at least a single whitespace after the opening |
| # brace in order to satisfy its Boost Jam language grammar rules. |
| def test_raw_empty(): |
| whitespace_in = " \n\n\r\r\v\v\t\t \t \r\r \n\n" |
| |
| # We tell the testing system to read its child process output as raw |
| # binary data but the bjam process we run will read its input file and |
| # write out its output as text, i.e. convert all of our "\r\n" sequences to |
| # "\n" on input and all of its "\n" characters back to "\r\n" on output. |
| # This means that any lone "\n" input characters not preceded by "\r" will |
| # get an extra "\r" added in front of it on output. |
| whitespace_out = whitespace_in.replace("\r\n", "\n").replace("\n", "\r\n") |
| |
| t = BoostBuild.Tester(["-d2", "-d+4"], pass_d0=False, pass_toolset=0, |
| use_test_config=False) |
| t.write("file.jam", """\ |
| actions do_empty {%s} |
| JAMSHELL = %% ; |
| do_empty all ; |
| """ % (whitespace_in)) |
| t.run_build_system(["-ffile.jam"], universal_newlines=False) |
| t.expect_output_lines("do_empty all") |
| t.expect_output_lines("Executing raw command directly", False) |
| if "\r\n%s\r\n" % whitespace_out not in t.stdout(): |
| BoostBuild.annotation("failure", "Whitespace action content not found " |
| "on stdout.") |
| t.fail_test(1, dump_difference=False) |
| t.cleanup() |
| |
| |
| def test_raw_nt(n=None, error=False): |
| t = BoostBuild.Tester(["-d1", "-d+4"], pass_d0=False, pass_toolset=0, |
| use_test_config=False) |
| |
| cmd_prefix = "%s -c \"print('XXX: " % executable |
| cmd_suffix = "')\"" |
| cmd_extra_length = len(cmd_prefix) + len(cmd_suffix) |
| |
| if n == None: |
| n = cmd_extra_length |
| |
| data_length = n - cmd_extra_length |
| if data_length < 0: |
| BoostBuild.annotation("failure", """\ |
| Can not construct Windows command of desired length. Requested command length |
| too short for the current test configuration. |
| Requested command length: %d |
| Minimal supported command length: %d |
| """ % (n, cmd_extra_length)) |
| t.fail_test(1, dump_difference=False) |
| |
| # Each $(Xx10-1) variable contains X words of 9 characters each, which, |
| # including spaces between words, brings the total number of characters in |
| # its string representation to X * 10 - 1 (X * 9 characters + (X - 1) |
| # spaces). |
| t.write("file.jam", """\ |
| ten = 0 1 2 3 4 5 6 7 8 9 ; |
| |
| 1x10-1 = 123456789 ; |
| 10x10-1 = $(ten)12345678 ; |
| 100x10-1 = $(ten)$(ten)1234567 ; |
| 1000x10-1 = $(ten)$(ten)$(ten)123456 ; |
| |
| actions do_echo |
| { |
| %s%s%s |
| } |
| JAMSHELL = %% ; |
| do_echo all ; |
| """ % (cmd_prefix, string_of_length(data_length), cmd_suffix)) |
| if error: |
| expected_status = 1 |
| else: |
| expected_status = 0 |
| t.run_build_system(["-ffile.jam"], status=expected_status) |
| if error: |
| t.expect_output_lines("Executing raw command directly", False) |
| t.expect_output_lines("do_echo action is too long (%d, max 32766):" % n |
| ) |
| t.expect_output_lines("XXX: *", False) |
| else: |
| t.expect_output_lines("Executing raw command directly") |
| t.expect_output_lines("do_echo action is too long*", False) |
| |
| m = re.search("^XXX: (.*)$", t.stdout(), re.MULTILINE) |
| if not m: |
| BoostBuild.annotation("failure", "Expected output line starting " |
| "with 'XXX: ' not found.") |
| t.fail_test(1, dump_difference=False) |
| if len(m.group(1)) != data_length: |
| BoostBuild.annotation("failure", """Unexpected output data length. |
| Expected: %d |
| Received: %d""" % (n, len(m.group(1)))) |
| t.fail_test(1, dump_difference=False) |
| |
| t.cleanup() |
| |
| |
| def test_raw_to_shell_fallback_nt(): |
| t = BoostBuild.Tester(["-d1", "-d+4"], pass_d0=False, pass_toolset=0, |
| use_test_config=False) |
| |
| cmd_prefix = '%s -c print(' % executable |
| cmd_suffix = ')' |
| |
| t.write("file_multiline.jam", """\ |
| actions do_multiline |
| { |
| echo one |
| |
| |
| echo two |
| } |
| JAMSHELL = % ; |
| do_multiline all ; |
| """) |
| t.run_build_system(["-ffile_multiline.jam"]) |
| t.expect_output_lines("do_multiline all") |
| t.expect_output_lines("one") |
| t.expect_output_lines("two") |
| t.expect_output_lines("Executing raw command directly", False) |
| t.expect_output_lines("Executing using a command file and the shell: " |
| "cmd.exe /Q/C") |
| |
| t.write("file_redirect.jam", """\ |
| actions do_redirect { echo one > two.txt } |
| JAMSHELL = % ; |
| do_redirect all ; |
| """) |
| t.run_build_system(["-ffile_redirect.jam"]) |
| t.expect_output_lines("do_redirect all") |
| t.expect_output_lines("one", False) |
| t.expect_output_lines("Executing raw command directly", False) |
| t.expect_output_lines("Executing using a command file and the shell: " |
| "cmd.exe /Q/C") |
| t.expect_addition("two.txt") |
| |
| t.write("file_pipe.jam", """\ |
| actions do_pipe |
| { |
| echo one | echo two |
| } |
| JAMSHELL = % ; |
| do_pipe all ; |
| """) |
| t.run_build_system(["-ffile_pipe.jam"]) |
| t.expect_output_lines("do_pipe all") |
| t.expect_output_lines("one*", False) |
| t.expect_output_lines("two") |
| t.expect_output_lines("Executing raw command directly", False) |
| t.expect_output_lines("Executing using a command file and the shell: " |
| "cmd.exe /Q/C") |
| |
| t.write("file_single_quoted.jam", """\ |
| actions do_single_quoted { %s'5>10'%s } |
| JAMSHELL = %% ; |
| do_single_quoted all ; |
| """ % (cmd_prefix, cmd_suffix)) |
| t.run_build_system(["-ffile_single_quoted.jam"]) |
| t.expect_output_lines("do_single_quoted all") |
| t.expect_output_lines("5>10") |
| t.expect_output_lines("Executing raw command directly") |
| t.expect_output_lines("Executing using a command file and the shell: " |
| "cmd.exe /Q/C", False) |
| t.expect_nothing_more() |
| |
| t.write("file_double_quoted.jam", """\ |
| actions do_double_quoted { %s"5>10"%s } |
| JAMSHELL = %% ; |
| do_double_quoted all ; |
| """ % (cmd_prefix, cmd_suffix)) |
| t.run_build_system(["-ffile_double_quoted.jam"]) |
| t.expect_output_lines("do_double_quoted all") |
| # The difference between this example and the similar previous one using |
| # single instead of double quotes stems from how the used Python executable |
| # parses the command-line string received from Windows. |
| t.expect_output_lines("False") |
| t.expect_output_lines("Executing raw command directly") |
| t.expect_output_lines("Executing using a command file and the shell: " |
| "cmd.exe /Q/C", False) |
| t.expect_nothing_more() |
| |
| t.write("file_escaped_quote.jam", """\ |
| actions do_escaped_quote { %s\\"5>10\\"%s } |
| JAMSHELL = %% ; |
| do_escaped_quote all ; |
| """ % (cmd_prefix, cmd_suffix)) |
| t.run_build_system(["-ffile_escaped_quote.jam"]) |
| t.expect_output_lines("do_escaped_quote all") |
| t.expect_output_lines("5>10") |
| t.expect_output_lines("Executing raw command directly", False) |
| t.expect_output_lines("Executing using a command file and the shell: " |
| "cmd.exe /Q/C") |
| t.expect_nothing_more() |
| |
| t.cleanup() |
| |
| |
| ############################################################################### |
| # |
| # main() |
| # ------ |
| # |
| ############################################################################### |
| |
| if os.name == 'nt': |
| test_raw_empty() |
| |
| # Can not test much shorter lengths as the shortest possible command line |
| # line length constructed in this depends on the runtime environment, e.g. |
| # path to the Panther executable running this test. |
| test_raw_nt() |
| test_raw_nt(255) |
| test_raw_nt(1000) |
| test_raw_nt(8000) |
| test_raw_nt(8191) |
| test_raw_nt(8192) |
| test_raw_nt(10000) |
| test_raw_nt(30000) |
| test_raw_nt(32766) |
| # CreateProcessA() Windows API places a limit of 32768 on the allowed |
| # command-line length, including a trailing Unicode (2-byte) nul-terminator |
| # character. |
| test_raw_nt(32767, error=True) |
| test_raw_nt(40000, error=True) |
| test_raw_nt(100001, error=True) |
| |
| test_raw_to_shell_fallback_nt() |