]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | #!/usr/bin/python |
2 | ||
3 | # Copyright 2001 Dave Abrahams | |
4 | # Copyright 2011 Steven Watanabe | |
5 | # Distributed under the Boost Software License, Version 1.0. | |
1e59de90 TL |
6 | # (See accompanying file LICENSE.txt or copy at |
7 | # https://www.bfgroup.xyz/b2/LICENSE.txt) | |
7c673cae FG |
8 | |
9 | # Tests Windows command line construction. | |
10 | # | |
11 | # Note that the regular 'echo' is an internal shell command on Windows and | |
12 | # therefore can not be called directly as a standalone Windows process. | |
13 | ||
14 | import BoostBuild | |
15 | import os | |
16 | import re | |
17 | import sys | |
18 | ||
19 | ||
20 | executable = sys.executable.replace("\\", "/") | |
21 | if " " in executable: | |
22 | executable = '"%s"' % executable | |
23 | ||
24 | ||
25 | def string_of_length(n): | |
26 | if n <= 0: | |
27 | return "" | |
28 | n -= 1 | |
29 | y = ['', '$(1x10-1)', '$(10x10-1)', '$(100x10-1)', '$(1000x10-1)'] | |
30 | result = [] | |
92f5a8d4 | 31 | for i in reversed(range(5)): |
7c673cae FG |
32 | x, n = divmod(n, 10 ** i) |
33 | result += [y[i]] * x | |
34 | result.append('x') | |
35 | return " ".join(result) | |
36 | ||
37 | ||
38 | # Boost Jam currently does not allow preparing actions with completely empty | |
39 | # content and always requires at least a single whitespace after the opening | |
40 | # brace in order to satisfy its Boost Jam language grammar rules. | |
41 | def test_raw_empty(): | |
42 | whitespace_in = " \n\n\r\r\v\v\t\t \t \r\r \n\n" | |
43 | ||
44 | # We tell the testing system to read its child process output as raw | |
45 | # binary data but the bjam process we run will read its input file and | |
46 | # write out its output as text, i.e. convert all of our "\r\n" sequences to | |
47 | # "\n" on input and all of its "\n" characters back to "\r\n" on output. | |
48 | # This means that any lone "\n" input characters not preceded by "\r" will | |
49 | # get an extra "\r" added in front of it on output. | |
50 | whitespace_out = whitespace_in.replace("\r\n", "\n").replace("\n", "\r\n") | |
51 | ||
11fdf7f2 | 52 | t = BoostBuild.Tester(["-d2", "-d+4"], pass_toolset=0, |
7c673cae FG |
53 | use_test_config=False) |
54 | t.write("file.jam", """\ | |
55 | actions do_empty {%s} | |
56 | JAMSHELL = %% ; | |
57 | do_empty all ; | |
58 | """ % (whitespace_in)) | |
59 | t.run_build_system(["-ffile.jam"], universal_newlines=False) | |
60 | t.expect_output_lines("do_empty all") | |
61 | t.expect_output_lines("Executing raw command directly", False) | |
62 | if "\r\n%s\r\n" % whitespace_out not in t.stdout(): | |
63 | BoostBuild.annotation("failure", "Whitespace action content not found " | |
64 | "on stdout.") | |
65 | t.fail_test(1, dump_difference=False) | |
66 | t.cleanup() | |
67 | ||
68 | ||
69 | def test_raw_nt(n=None, error=False): | |
11fdf7f2 | 70 | t = BoostBuild.Tester(["-d1", "-d+4"], pass_toolset=0, |
7c673cae FG |
71 | use_test_config=False) |
72 | ||
73 | cmd_prefix = "%s -c \"print('XXX: " % executable | |
74 | cmd_suffix = "')\"" | |
75 | cmd_extra_length = len(cmd_prefix) + len(cmd_suffix) | |
76 | ||
77 | if n == None: | |
78 | n = cmd_extra_length | |
79 | ||
80 | data_length = n - cmd_extra_length | |
81 | if data_length < 0: | |
82 | BoostBuild.annotation("failure", """\ | |
83 | Can not construct Windows command of desired length. Requested command length | |
84 | too short for the current test configuration. | |
85 | Requested command length: %d | |
86 | Minimal supported command length: %d | |
87 | """ % (n, cmd_extra_length)) | |
88 | t.fail_test(1, dump_difference=False) | |
89 | ||
90 | # Each $(Xx10-1) variable contains X words of 9 characters each, which, | |
91 | # including spaces between words, brings the total number of characters in | |
92 | # its string representation to X * 10 - 1 (X * 9 characters + (X - 1) | |
93 | # spaces). | |
94 | t.write("file.jam", """\ | |
95 | ten = 0 1 2 3 4 5 6 7 8 9 ; | |
96 | ||
97 | 1x10-1 = 123456789 ; | |
98 | 10x10-1 = $(ten)12345678 ; | |
99 | 100x10-1 = $(ten)$(ten)1234567 ; | |
100 | 1000x10-1 = $(ten)$(ten)$(ten)123456 ; | |
101 | ||
102 | actions do_echo | |
103 | { | |
104 | %s%s%s | |
105 | } | |
106 | JAMSHELL = %% ; | |
107 | do_echo all ; | |
108 | """ % (cmd_prefix, string_of_length(data_length), cmd_suffix)) | |
109 | if error: | |
110 | expected_status = 1 | |
111 | else: | |
112 | expected_status = 0 | |
113 | t.run_build_system(["-ffile.jam"], status=expected_status) | |
114 | if error: | |
115 | t.expect_output_lines("Executing raw command directly", False) | |
116 | t.expect_output_lines("do_echo action is too long (%d, max 32766):" % n | |
117 | ) | |
118 | t.expect_output_lines("XXX: *", False) | |
119 | else: | |
120 | t.expect_output_lines("Executing raw command directly") | |
121 | t.expect_output_lines("do_echo action is too long*", False) | |
122 | ||
123 | m = re.search("^XXX: (.*)$", t.stdout(), re.MULTILINE) | |
124 | if not m: | |
125 | BoostBuild.annotation("failure", "Expected output line starting " | |
126 | "with 'XXX: ' not found.") | |
127 | t.fail_test(1, dump_difference=False) | |
128 | if len(m.group(1)) != data_length: | |
129 | BoostBuild.annotation("failure", """Unexpected output data length. | |
130 | Expected: %d | |
131 | Received: %d""" % (n, len(m.group(1)))) | |
132 | t.fail_test(1, dump_difference=False) | |
133 | ||
134 | t.cleanup() | |
135 | ||
136 | ||
137 | def test_raw_to_shell_fallback_nt(): | |
11fdf7f2 | 138 | t = BoostBuild.Tester(["-d1", "-d+4"], pass_toolset=0, |
7c673cae FG |
139 | use_test_config=False) |
140 | ||
141 | cmd_prefix = '%s -c print(' % executable | |
142 | cmd_suffix = ')' | |
143 | ||
144 | t.write("file_multiline.jam", """\ | |
145 | actions do_multiline | |
146 | { | |
147 | echo one | |
148 | ||
149 | ||
150 | echo two | |
151 | } | |
152 | JAMSHELL = % ; | |
153 | do_multiline all ; | |
154 | """) | |
155 | t.run_build_system(["-ffile_multiline.jam"]) | |
156 | t.expect_output_lines("do_multiline all") | |
157 | t.expect_output_lines("one") | |
158 | t.expect_output_lines("two") | |
159 | t.expect_output_lines("Executing raw command directly", False) | |
160 | t.expect_output_lines("Executing using a command file and the shell: " | |
161 | "cmd.exe /Q/C") | |
162 | ||
163 | t.write("file_redirect.jam", """\ | |
164 | actions do_redirect { echo one > two.txt } | |
165 | JAMSHELL = % ; | |
166 | do_redirect all ; | |
167 | """) | |
168 | t.run_build_system(["-ffile_redirect.jam"]) | |
169 | t.expect_output_lines("do_redirect all") | |
170 | t.expect_output_lines("one", False) | |
171 | t.expect_output_lines("Executing raw command directly", False) | |
172 | t.expect_output_lines("Executing using a command file and the shell: " | |
173 | "cmd.exe /Q/C") | |
174 | t.expect_addition("two.txt") | |
175 | ||
176 | t.write("file_pipe.jam", """\ | |
177 | actions do_pipe | |
178 | { | |
179 | echo one | echo two | |
180 | } | |
181 | JAMSHELL = % ; | |
182 | do_pipe all ; | |
183 | """) | |
184 | t.run_build_system(["-ffile_pipe.jam"]) | |
185 | t.expect_output_lines("do_pipe all") | |
186 | t.expect_output_lines("one*", False) | |
187 | t.expect_output_lines("two") | |
188 | t.expect_output_lines("Executing raw command directly", False) | |
189 | t.expect_output_lines("Executing using a command file and the shell: " | |
190 | "cmd.exe /Q/C") | |
191 | ||
192 | t.write("file_single_quoted.jam", """\ | |
193 | actions do_single_quoted { %s'5>10'%s } | |
194 | JAMSHELL = %% ; | |
195 | do_single_quoted all ; | |
196 | """ % (cmd_prefix, cmd_suffix)) | |
197 | t.run_build_system(["-ffile_single_quoted.jam"]) | |
198 | t.expect_output_lines("do_single_quoted all") | |
199 | t.expect_output_lines("5>10") | |
200 | t.expect_output_lines("Executing raw command directly") | |
201 | t.expect_output_lines("Executing using a command file and the shell: " | |
202 | "cmd.exe /Q/C", False) | |
203 | t.expect_nothing_more() | |
204 | ||
205 | t.write("file_double_quoted.jam", """\ | |
206 | actions do_double_quoted { %s"5>10"%s } | |
207 | JAMSHELL = %% ; | |
208 | do_double_quoted all ; | |
209 | """ % (cmd_prefix, cmd_suffix)) | |
210 | t.run_build_system(["-ffile_double_quoted.jam"]) | |
211 | t.expect_output_lines("do_double_quoted all") | |
212 | # The difference between this example and the similar previous one using | |
213 | # single instead of double quotes stems from how the used Python executable | |
214 | # parses the command-line string received from Windows. | |
215 | t.expect_output_lines("False") | |
216 | t.expect_output_lines("Executing raw command directly") | |
217 | t.expect_output_lines("Executing using a command file and the shell: " | |
218 | "cmd.exe /Q/C", False) | |
219 | t.expect_nothing_more() | |
220 | ||
221 | t.write("file_escaped_quote.jam", """\ | |
222 | actions do_escaped_quote { %s\\"5>10\\"%s } | |
223 | JAMSHELL = %% ; | |
224 | do_escaped_quote all ; | |
225 | """ % (cmd_prefix, cmd_suffix)) | |
226 | t.run_build_system(["-ffile_escaped_quote.jam"]) | |
227 | t.expect_output_lines("do_escaped_quote all") | |
228 | t.expect_output_lines("5>10") | |
229 | t.expect_output_lines("Executing raw command directly", False) | |
230 | t.expect_output_lines("Executing using a command file and the shell: " | |
231 | "cmd.exe /Q/C") | |
232 | t.expect_nothing_more() | |
233 | ||
234 | t.cleanup() | |
235 | ||
236 | ||
237 | ############################################################################### | |
238 | # | |
239 | # main() | |
240 | # ------ | |
241 | # | |
242 | ############################################################################### | |
243 | ||
244 | if os.name == 'nt': | |
245 | test_raw_empty() | |
246 | ||
247 | # Can not test much shorter lengths as the shortest possible command line | |
248 | # line length constructed in this depends on the runtime environment, e.g. | |
249 | # path to the Panther executable running this test. | |
250 | test_raw_nt() | |
251 | test_raw_nt(255) | |
252 | test_raw_nt(1000) | |
253 | test_raw_nt(8000) | |
254 | test_raw_nt(8191) | |
255 | test_raw_nt(8192) | |
256 | test_raw_nt(10000) | |
257 | test_raw_nt(30000) | |
258 | test_raw_nt(32766) | |
259 | # CreateProcessA() Windows API places a limit of 32768 on the allowed | |
260 | # command-line length, including a trailing Unicode (2-byte) nul-terminator | |
261 | # character. | |
262 | test_raw_nt(32767, error=True) | |
263 | test_raw_nt(40000, error=True) | |
264 | test_raw_nt(100001, error=True) | |
265 | ||
266 | test_raw_to_shell_fallback_nt() |