summaryrefslogtreecommitdiff
path: root/tools/build/test/core_nt_cmd_line.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/build/test/core_nt_cmd_line.py')
-rwxr-xr-xtools/build/test/core_nt_cmd_line.py266
1 files changed, 266 insertions, 0 deletions
diff --git a/tools/build/test/core_nt_cmd_line.py b/tools/build/test/core_nt_cmd_line.py
new file mode 100755
index 000000000..579242d24
--- /dev/null
+++ b/tools/build/test/core_nt_cmd_line.py
@@ -0,0 +1,266 @@
+#!/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() \ No newline at end of file