Skip to content

Commit 4fc0f7c

Browse files
JackSullivanCraigacp
authored andcommitted
Fixed a bug where SubprocessConnection irretrievably lost newline information from subprocesses (#67)
1 parent 8aac951 commit 4fc0f7c

File tree

3 files changed

+85
-4
lines changed

3 files changed

+85
-4
lines changed

olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/util/SubprocessConnection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ public synchronized String run(String command) throws IOException, TimeoutExcept
319319

320320
//
321321
// Read until an empty line is returned
322-
collectOutputWithTimeout(results::append);
322+
collectOutputWithTimeout(line -> results.append(line).append("\n"));
323323
} finally {
324324
processLock.unlock();
325325
}

olcut-core/src/test/java/com/oracle/labs/mlrg/olcut/util/SubprocessConnectionTest.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.junit.jupiter.api.condition.OS;
3535

3636
import java.io.IOException;
37+
import java.util.ArrayList;
3738
import java.util.Arrays;
3839
import java.util.List;
3940
import java.util.concurrent.TimeUnit;
@@ -50,12 +51,14 @@ public class SubprocessConnectionTest {
5051
List<String> work;
5152
List<String> expectedResponse;
5253

54+
SubprocessConnection multiSubProc;
55+
5356
@BeforeEach
5457
public void setup() {
5558
// TODO I know this isn't the correct way to do this
5659
process = String.format("python3 %s/src/test/resources/com/oracle/labs/mlrg/olcut/util/testProcess.py", System.getProperty("user.dir"));
5760
work = Arrays.asList("part_a", "part_b", "part_c", "part_d");
58-
expectedResponse = Arrays.asList("0:10:part_a", "1:15:part_b", "2:27:part_c", "3:38:part_d");
61+
expectedResponse = Arrays.asList("0:10:part_a\n", "1:15:part_b\n", "2:27:part_c\n", "3:38:part_d\n");
5962
subproc = new SubprocessConnection(process);
6063
subproc.addSubprocessListener(new SubprocessConnectionListener() {
6164
private int started=0;
@@ -80,6 +83,8 @@ public void subprocessPostShutdown(SubprocessConnection connection) {
8083
System.err.println("Subprocess post shutdown times: " + postShutdown);
8184
}
8285
});
86+
87+
multiSubProc = new SubprocessConnection(String.format("python3 %s/src/test/resources/com/oracle/labs/mlrg/olcut/util/testMultipleProcess.py", System.getProperty("user.dir")));
8388
}
8489

8590
@Test
@@ -99,7 +104,7 @@ public void testReadTimeout() {
99104
resp.set(subproc.run("part_b"));
100105
}
101106
);
102-
assertEquals("0:5:part_b", resp.get());
107+
assertEquals("0:5:part_b\n", resp.get());
103108

104109
assertThrows(TimeoutException.class,() -> {
105110
subproc.run("part_a");
@@ -126,6 +131,25 @@ public void testMultipleReadTimeouts() {
126131
resp.set(subproc.run("part_b"));
127132
});
128133

129-
assertEquals("0:5:part_b",resp.get() );
134+
assertEquals("0:5:part_b\n",resp.get() );
135+
}
136+
137+
@Test
138+
public void testMultipleReturnedRows() {
139+
List<String> responses = new ArrayList<>();
140+
assertDoesNotThrow(() -> {
141+
for (String piece : work) {
142+
responses.addAll(Arrays.asList(multiSubProc.run(piece).split("\n")));
143+
}
144+
});
145+
List<String> expected = new ArrayList<>();
146+
int[] expectNum = {10, 15, 27, 38};
147+
String[] expectPart = {"a", "b", "c", "d"};
148+
for (int cnt = 0; cnt < 4; cnt++) {
149+
for (int i = 0; i < expectNum[cnt]; i++) {
150+
expected.add(String.format("%d:%d:part_%s", cnt, i, expectPart[cnt]));
151+
}
152+
}
153+
assertEquals(expected, responses);
130154
}
131155
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#
2+
# Copyright (c) 2004-2022, Oracle and/or its affiliates.
3+
#
4+
# Licensed under the 2-clause BSD license.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions are met:
8+
#
9+
# 1. Redistributions of source code must retain the above copyright notice,
10+
# this list of conditions and the following disclaimer.
11+
#
12+
# 2. Redistributions in binary form must reproduce the above copyright notice,
13+
# this list of conditions and the following disclaimer in the documentation
14+
# and/or other materials provided with the distribution.
15+
#
16+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26+
# POSSIBILITY OF SUCH DAMAGE.
27+
28+
import time
29+
import sys
30+
31+
def eprint(s: str):
32+
print(s, file=sys.stderr, flush=True)
33+
34+
if __name__ == '__main__':
35+
36+
print("Ready", flush=True)
37+
eprint("Sent Ready")
38+
sec = 0
39+
cnt = 0
40+
# expected results
41+
r = {'part_a':10, 'part_b':15, 'part_c':27, 'part_d':38}
42+
while True:
43+
try:
44+
line = input()
45+
except EOFError:
46+
eprint("EOFError")
47+
break
48+
if len(line.strip()) == 0:
49+
eprint("Received Empty Line")
50+
else:
51+
return_rows = r[line.strip()]
52+
eprint("returning {} rows".format(return_rows))
53+
for i in range(return_rows):
54+
print("{}:{}:{}".format(cnt, i, line))
55+
print("")
56+
cnt += 1
57+
eprint("Finished all work")

0 commit comments

Comments
 (0)