blob: 1ac68f6c341e159c1d33e281e367b6ffc38b1a72 [file] [log] [blame]
pcloudy8c21b0b2018-09-19 07:19:09 -07001// Copyright 2016 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.windows;
16
17import static com.google.common.truth.Truth.assertThat;
Laszlo Csomord5c1eee2019-02-13 04:52:45 -080018import static java.nio.charset.StandardCharsets.UTF_8;
larsrc082d9872021-01-13 06:40:59 -080019import static org.junit.Assert.assertThrows;
pcloudy8c21b0b2018-09-19 07:19:09 -070020
laszlocsomord2cb96a2020-01-14 02:35:04 -080021import com.google.common.collect.ImmutableList;
pcloudy8c21b0b2018-09-19 07:19:09 -070022import com.google.common.collect.ImmutableMap;
laszlocsomor1da48612019-02-15 01:41:57 -080023import com.google.devtools.build.lib.shell.ShellUtils;
pcloudy8c21b0b2018-09-19 07:19:09 -070024import com.google.devtools.build.lib.shell.Subprocess;
25import com.google.devtools.build.lib.shell.SubprocessBuilder;
26import com.google.devtools.build.lib.testutil.TestSpec;
27import com.google.devtools.build.lib.util.OS;
Laszlo Csomor4d05f382018-11-22 07:53:57 -080028import com.google.devtools.build.runfiles.Runfiles;
pcloudy8c21b0b2018-09-19 07:19:09 -070029import java.io.File;
larsrc082d9872021-01-13 06:40:59 -080030import java.io.InputStream;
pcloudy8c21b0b2018-09-19 07:19:09 -070031import org.junit.After;
32import org.junit.Before;
33import org.junit.Test;
34import org.junit.runner.RunWith;
35import org.junit.runners.JUnit4;
36
michajlof6cc8ac2021-06-07 12:04:04 -070037/** Unit tests for {@link WindowsSubprocess}. */
pcloudy8c21b0b2018-09-19 07:19:09 -070038@RunWith(JUnit4.class)
michajlof6cc8ac2021-06-07 12:04:04 -070039@TestSpec(supportedOs = OS.WINDOWS)
pcloudy8c21b0b2018-09-19 07:19:09 -070040public class WindowsSubprocessTest {
pcloudy8c21b0b2018-09-19 07:19:09 -070041 private String mockSubprocess;
42 private String mockBinary;
43 private Subprocess process;
Laszlo Csomord5c1eee2019-02-13 04:52:45 -080044 private Runfiles runfiles;
pcloudy8c21b0b2018-09-19 07:19:09 -070045
46 @Before
47 public void loadJni() throws Exception {
Laszlo Csomord5c1eee2019-02-13 04:52:45 -080048 runfiles = Runfiles.create();
Laszlo Csomor4d05f382018-11-22 07:53:57 -080049 mockSubprocess =
50 runfiles.rlocation(
pcloudyf8f43322020-04-08 07:56:38 -070051 "io_bazel/src/test/java/com/google/devtools/build/lib/windows/MockSubprocess_deploy.jar");
pcloudy8c21b0b2018-09-19 07:19:09 -070052 mockBinary = System.getProperty("java.home") + "\\bin\\java.exe";
53
54 process = null;
55 }
56
57 @After
58 public void terminateProcess() throws Exception {
59 if (process != null) {
60 process.destroy();
61 process.close();
62 process = null;
63 }
64 }
65
Laszlo Csomorecac47d2019-07-01 04:29:34 -070066 @Test
67 public void testSystemRootIsSetByDefault() throws Exception {
68 SubprocessBuilder subprocessBuilder = new SubprocessBuilder(WindowsSubprocessFactory.INSTANCE);
pcloudy8c21b0b2018-09-19 07:19:09 -070069 subprocessBuilder.setWorkingDirectory(new File("."));
laszlocsomord2cb96a2020-01-14 02:35:04 -080070 subprocessBuilder.setArgv(ImmutableList.of(mockBinary, "-jar", mockSubprocess, "O$SYSTEMROOT"));
pcloudy8c21b0b2018-09-19 07:19:09 -070071 process = subprocessBuilder.start();
72 process.waitFor();
73 assertThat(process.exitValue()).isEqualTo(0);
74
75 byte[] buf = new byte[11];
76 process.getInputStream().read(buf);
Laszlo Csomord5c1eee2019-02-13 04:52:45 -080077 assertThat(new String(buf, UTF_8).trim()).isEqualTo(System.getenv("SYSTEMROOT").trim());
pcloudy8c21b0b2018-09-19 07:19:09 -070078 }
79
80 @Test
Laszlo Csomorecac47d2019-07-01 04:29:34 -070081 public void testSystemDriveIsSetByDefault() throws Exception {
82 SubprocessBuilder subprocessBuilder = new SubprocessBuilder(WindowsSubprocessFactory.INSTANCE);
pcloudy8c21b0b2018-09-19 07:19:09 -070083 subprocessBuilder.setWorkingDirectory(new File("."));
laszlocsomord2cb96a2020-01-14 02:35:04 -080084 subprocessBuilder.setArgv(
85 ImmutableList.of(mockBinary, "-jar", mockSubprocess, "O$SYSTEMDRIVE"));
pcloudy8c21b0b2018-09-19 07:19:09 -070086 process = subprocessBuilder.start();
87 process.waitFor();
88 assertThat(process.exitValue()).isEqualTo(0);
89
90 byte[] buf = new byte[3];
91 process.getInputStream().read(buf);
Laszlo Csomord5c1eee2019-02-13 04:52:45 -080092 assertThat(new String(buf, UTF_8).trim()).isEqualTo(System.getenv("SYSTEMDRIVE").trim());
pcloudy8c21b0b2018-09-19 07:19:09 -070093 }
94
95 @Test
Laszlo Csomorecac47d2019-07-01 04:29:34 -070096 public void testSystemRootIsSet() throws Exception {
97 SubprocessBuilder subprocessBuilder = new SubprocessBuilder(WindowsSubprocessFactory.INSTANCE);
pcloudy8c21b0b2018-09-19 07:19:09 -070098 subprocessBuilder.setWorkingDirectory(new File("."));
laszlocsomord2cb96a2020-01-14 02:35:04 -080099 subprocessBuilder.setArgv(ImmutableList.of(mockBinary, "-jar", mockSubprocess, "O$SYSTEMROOT"));
pcloudy8c21b0b2018-09-19 07:19:09 -0700100 // Case shouldn't matter on Windows
101 subprocessBuilder.setEnv(ImmutableMap.of("SystemRoot", "C:\\MySystemRoot"));
102 process = subprocessBuilder.start();
103 process.waitFor();
104 assertThat(process.exitValue()).isEqualTo(0);
105
106 byte[] buf = new byte[16];
107 process.getInputStream().read(buf);
Laszlo Csomord5c1eee2019-02-13 04:52:45 -0800108 assertThat(new String(buf, UTF_8).trim()).isEqualTo("C:\\MySystemRoot");
pcloudy8c21b0b2018-09-19 07:19:09 -0700109 }
110
111 @Test
Laszlo Csomorecac47d2019-07-01 04:29:34 -0700112 public void testSystemDriveIsSet() throws Exception {
113 SubprocessBuilder subprocessBuilder = new SubprocessBuilder(WindowsSubprocessFactory.INSTANCE);
pcloudy8c21b0b2018-09-19 07:19:09 -0700114 subprocessBuilder.setWorkingDirectory(new File("."));
laszlocsomord2cb96a2020-01-14 02:35:04 -0800115 subprocessBuilder.setArgv(
116 ImmutableList.of(mockBinary, "-jar", mockSubprocess, "O$SYSTEMDRIVE"));
pcloudy8c21b0b2018-09-19 07:19:09 -0700117 // Case shouldn't matter on Windows
118 subprocessBuilder.setEnv(ImmutableMap.of("SystemDrive", "X:"));
119 process = subprocessBuilder.start();
120 process.waitFor();
121 assertThat(process.exitValue()).isEqualTo(0);
122
123 byte[] buf = new byte[3];
124 process.getInputStream().read(buf);
Laszlo Csomord5c1eee2019-02-13 04:52:45 -0800125 assertThat(new String(buf, UTF_8).trim()).isEqualTo("X:");
126 }
127
larsrc082d9872021-01-13 06:40:59 -0800128 @Test
129 public void testStreamAvailable_zeroAfterClose() throws Exception {
130 SubprocessBuilder subprocessBuilder = new SubprocessBuilder(WindowsSubprocessFactory.INSTANCE);
131 subprocessBuilder.setWorkingDirectory(new File("."));
132 subprocessBuilder.setArgv(ImmutableList.of(mockBinary, "-jar", mockSubprocess, "OHELLO"));
133 process = subprocessBuilder.start();
134 InputStream inputStream = process.getInputStream();
135 // We don't know if the process has already written to the pipe
136 assertThat(inputStream.available()).isAnyOf(0, 5);
137 process.waitFor();
138 // Windows allows streams to be read after the process has died.
139 assertThat(inputStream.available()).isAnyOf(0, 5);
140 inputStream.close();
Googler3aa4fa02021-02-05 10:17:25 -0800141 assertThat(assertThrows(IllegalStateException.class, inputStream::available))
142 .hasMessageThat()
larsrc082d9872021-01-13 06:40:59 -0800143 .contains("Stream already closed");
144 }
145
Laszlo Csomord5c1eee2019-02-13 04:52:45 -0800146 /**
147 * An argument and its command-line-escaped counterpart.
148 *
149 * <p>Such escaping ensures that Bazel correctly forwards arguments to subprocesses.
150 */
151 private static final class ArgPair {
152 public final String original;
153 public final String escaped;
154
155 public ArgPair(String original, String escaped) {
156 this.original = original;
157 this.escaped = escaped;
158 }
159 };
160
161 /** Asserts that a subprocess correctly receives command line arguments. */
Laszlo Csomorecac47d2019-07-01 04:29:34 -0700162 private void assertSubprocessReceivesArgsAsIntended(ArgPair... args) throws Exception {
Laszlo Csomord5c1eee2019-02-13 04:52:45 -0800163 // Look up the path of the printarg.exe utility.
164 String printArgExe =
pcloudyf8f43322020-04-08 07:56:38 -0700165 runfiles.rlocation(
166 "io_bazel/src/test/java/com/google/devtools/build/lib/windows/printarg.exe");
Laszlo Csomord5c1eee2019-02-13 04:52:45 -0800167 assertThat(printArgExe).isNotEmpty();
168
169 for (ArgPair arg : args) {
170 // Assert that the command-line encoding logic works as intended.
Laszlo Csomorecac47d2019-07-01 04:29:34 -0700171 assertThat(ShellUtils.windowsEscapeArg(arg.original)).isEqualTo(arg.escaped);
Laszlo Csomord5c1eee2019-02-13 04:52:45 -0800172
173 // Create a separate subprocess just for this argument.
laszlocsomor1da48612019-02-15 01:41:57 -0800174 SubprocessBuilder subprocessBuilder =
Laszlo Csomorecac47d2019-07-01 04:29:34 -0700175 new SubprocessBuilder(WindowsSubprocessFactory.INSTANCE);
Laszlo Csomord5c1eee2019-02-13 04:52:45 -0800176 subprocessBuilder.setWorkingDirectory(new File("."));
laszlocsomord2cb96a2020-01-14 02:35:04 -0800177 subprocessBuilder.setArgv(ImmutableList.of(printArgExe, arg.original));
Laszlo Csomord5c1eee2019-02-13 04:52:45 -0800178 process = subprocessBuilder.start();
179 process.waitFor();
180 assertThat(process.exitValue()).isEqualTo(0);
181
182 // The subprocess printed its argv[1] in parentheses, e.g. (foo).
183 // Assert that it printed exactly the *original* argument in parentheses.
184 byte[] buf = new byte[1000];
185 process.getInputStream().read(buf);
186 String actual = new String(buf, UTF_8).trim();
187 assertThat(actual).isEqualTo("(" + arg.original + ")");
188 }
189 }
190
191 @Test
Laszlo Csomorecac47d2019-07-01 04:29:34 -0700192 public void testSubprocessReceivesArgsAsIntended() throws Exception {
Laszlo Csomord5c1eee2019-02-13 04:52:45 -0800193 assertSubprocessReceivesArgsAsIntended(
laszlocsomor1da48612019-02-15 01:41:57 -0800194 new ArgPair("", "\"\""),
195 new ArgPair(" ", "\" \""),
196 new ArgPair("\"", "\"\\\"\""),
197 new ArgPair("\"\\", "\"\\\"\\\\\""),
198 new ArgPair("\\", "\\"),
199 new ArgPair("\\\"", "\"\\\\\\\"\""),
200 new ArgPair("with space", "\"with space\""),
201 new ArgPair("with^caret", "with^caret"),
202 new ArgPair("space ^caret", "\"space ^caret\""),
203 new ArgPair("caret^ space", "\"caret^ space\""),
204 new ArgPair("with\"quote", "\"with\\\"quote\""),
205 new ArgPair("with\\backslash", "with\\backslash"),
206 new ArgPair("one\\ backslash and \\space", "\"one\\ backslash and \\space\""),
207 new ArgPair("two\\\\backslashes", "two\\\\backslashes"),
208 new ArgPair("two\\\\ backslashes \\\\and space", "\"two\\\\ backslashes \\\\and space\""),
209 new ArgPair("one\\\"x", "\"one\\\\\\\"x\""),
210 new ArgPair("two\\\\\"x", "\"two\\\\\\\\\\\"x\""),
211 new ArgPair("a \\ b", "\"a \\ b\""),
212 new ArgPair("a \\\" b", "\"a \\\\\\\" b\""),
213 new ArgPair("A", "A"),
214 new ArgPair("\"a\"", "\"\\\"a\\\"\""),
215 new ArgPair("B C", "\"B C\""),
216 new ArgPair("\"b c\"", "\"\\\"b c\\\"\""),
217 new ArgPair("D\"E", "\"D\\\"E\""),
218 new ArgPair("\"d\"e\"", "\"\\\"d\\\"e\\\"\""),
219 new ArgPair("C:\\F G", "\"C:\\F G\""),
220 new ArgPair("\"C:\\f g\"", "\"\\\"C:\\f g\\\"\""),
221 new ArgPair("C:\\H\"I", "\"C:\\H\\\"I\""),
222 new ArgPair("\"C:\\h\"i\"", "\"\\\"C:\\h\\\"i\\\"\""),
223 new ArgPair("C:\\J\\\"K", "\"C:\\J\\\\\\\"K\""),
224 new ArgPair("\"C:\\j\\\"k\"", "\"\\\"C:\\j\\\\\\\"k\\\"\""),
225 new ArgPair("C:\\L M ", "\"C:\\L M \""),
226 new ArgPair("\"C:\\l m \"", "\"\\\"C:\\l m \\\"\""),
227 new ArgPair("C:\\N O\\", "\"C:\\N O\\\\\""),
228 new ArgPair("\"C:\\n o\\\"", "\"\\\"C:\\n o\\\\\\\"\""),
229 new ArgPair("C:\\P Q\\ ", "\"C:\\P Q\\ \""),
230 new ArgPair("\"C:\\p q\\ \"", "\"\\\"C:\\p q\\ \\\"\""),
231 new ArgPair("C:\\R\\S\\", "C:\\R\\S\\"),
232 new ArgPair("C:\\R x\\S\\", "\"C:\\R x\\S\\\\\""),
233 new ArgPair("\"C:\\r\\s\\\"", "\"\\\"C:\\r\\s\\\\\\\"\""),
234 new ArgPair("\"C:\\r x\\s\\\"", "\"\\\"C:\\r x\\s\\\\\\\"\""),
235 new ArgPair("C:\\T U\\W\\", "\"C:\\T U\\W\\\\\""),
236 new ArgPair("\"C:\\t u\\w\\\"", "\"\\\"C:\\t u\\w\\\\\\\"\""));
237 }
pcloudy8c21b0b2018-09-19 07:19:09 -0700238}