Skip to content

Commit b63d04a

Browse files
authored
Merge pull request #497 from avinxshKD/fix/read-status-wrapper
fix #491: read() returns ReadResult with ReadStatus instead of silent
2 parents 2eb7478 + f3299c0 commit b63d04a

File tree

2 files changed

+68
-20
lines changed

2 files changed

+68
-20
lines changed

TestConcoredockerApi.java

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ public static void main(String[] args) {
2424
testInitValExtractsSimtime();
2525
testInitValReturnsRemainingValues();
2626
testOutputFileMatchesPythonWireFormat();
27+
testReadFileNotFound();
28+
testReadRetriesExceeded();
29+
testReadParseError();
2730

2831
System.out.println("\n=== Results: " + passed + " passed, " + failed + " failed out of " + (passed + failed) + " tests ===");
2932
if (failed > 0) {
@@ -102,10 +105,11 @@ static void testReadParsesFileAndStripsSimtime() {
102105
concoredocker.setInPath(tmp.toString());
103106
writeFile(tmp, 1, "sensor", "[0.0, 42.0, 99.0]");
104107

105-
List<Object> result = concoredocker.read(1, "sensor", "[0.0, 0.0, 0.0]");
106-
check("read: strips simtime, size=2", 2, result.size());
107-
check("read: val1 correct", 42.0, result.get(0));
108-
check("read: val2 correct", 99.0, result.get(1));
108+
concoredocker.ReadResult result = concoredocker.read(1, "sensor", "[0.0, 0.0, 0.0]");
109+
check("read: status SUCCESS", concoredocker.ReadStatus.SUCCESS, result.status);
110+
check("read: strips simtime, size=2", 2, result.data.size());
111+
check("read: val1 correct", 42.0, result.data.get(0));
112+
check("read: val2 correct", 99.0, result.data.get(1));
109113
}
110114

111115
static void testReadWriteRoundtrip() {
@@ -119,10 +123,11 @@ static void testReadWriteRoundtrip() {
119123
outVals.add(8.0);
120124
concoredocker.write(1, "data", outVals, 1);
121125

122-
List<Object> inVals = concoredocker.read(1, "data", "[0.0, 0.0, 0.0]");
123-
check("roundtrip: size", 2, inVals.size());
124-
check("roundtrip: val1", 7.0, inVals.get(0));
125-
check("roundtrip: val2", 8.0, inVals.get(1));
126+
concoredocker.ReadResult inVals = concoredocker.read(1, "data", "[0.0, 0.0, 0.0]");
127+
check("roundtrip: status", concoredocker.ReadStatus.SUCCESS, inVals.status);
128+
check("roundtrip: size", 2, inVals.data.size());
129+
check("roundtrip: val1", 7.0, inVals.data.get(0));
130+
check("roundtrip: val2", 8.0, inVals.data.get(1));
126131
}
127132

128133
static void testSimtimeAdvancesWithDelta() {
@@ -195,4 +200,34 @@ static void testOutputFileMatchesPythonWireFormat() {
195200
Object reparsed = concoredocker.literalEval(raw);
196201
check("wire format: re-parseable as list", true, reparsed instanceof List);
197202
}
203+
204+
static void testReadFileNotFound() {
205+
Path tmp = makeTempDir();
206+
concoredocker.resetState();
207+
concoredocker.setInPath(tmp.toString());
208+
// no file written, port 1/missing does not exist
209+
concoredocker.ReadResult result = concoredocker.read(1, "missing", "[0.0, 0.0]");
210+
check("read file not found: status", concoredocker.ReadStatus.FILE_NOT_FOUND, result.status);
211+
check("read file not found: data is default", 1, result.data.size());
212+
}
213+
214+
static void testReadRetriesExceeded() {
215+
Path tmp = makeTempDir();
216+
concoredocker.resetState();
217+
concoredocker.setInPath(tmp.toString());
218+
writeFile(tmp, 1, "empty", ""); // always empty, exhausts retries
219+
concoredocker.ReadResult result = concoredocker.read(1, "empty", "[0.0, 0.0]");
220+
check("read retries exceeded: status", concoredocker.ReadStatus.RETRIES_EXCEEDED, result.status);
221+
check("read retries exceeded: data is default", 1, result.data.size());
222+
}
223+
224+
static void testReadParseError() {
225+
Path tmp = makeTempDir();
226+
concoredocker.resetState();
227+
concoredocker.setInPath(tmp.toString());
228+
writeFile(tmp, 1, "bad", "not_a_valid_list");
229+
concoredocker.ReadResult result = concoredocker.read(1, "bad", "[0.0, 0.0]");
230+
check("read parse error: status", concoredocker.ReadStatus.PARSE_ERROR, result.status);
231+
check("read parse error: data is default", 1, result.data.size());
232+
}
198233
}

concoredocker.java

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ public static Object tryParam(String n, Object i) {
160160
* Returns: list of values after simtime
161161
* Includes max retry limit to avoid infinite blocking (matches Python behavior).
162162
*/
163-
public static List<Object> read(int port, String name, String initstr) {
163+
public static ReadResult read(int port, String name, String initstr) {
164164
// Parse default value upfront for consistent return type
165165
List<Object> defaultVal = new ArrayList<>();
166166
try {
@@ -178,7 +178,7 @@ public static List<Object> read(int port, String name, String initstr) {
178178
} catch (InterruptedException e) {
179179
Thread.currentThread().interrupt();
180180
s += initstr;
181-
return defaultVal;
181+
return new ReadResult(ReadStatus.TIMEOUT, defaultVal);
182182
}
183183

184184
String ins;
@@ -187,7 +187,7 @@ public static List<Object> read(int port, String name, String initstr) {
187187
} catch (IOException e) {
188188
System.out.println("File " + filePath + " not found, using default value.");
189189
s += initstr;
190-
return defaultVal;
190+
return new ReadResult(ReadStatus.FILE_NOT_FOUND, defaultVal);
191191
}
192192

193193
int attempts = 0;
@@ -197,7 +197,7 @@ public static List<Object> read(int port, String name, String initstr) {
197197
} catch (InterruptedException e) {
198198
Thread.currentThread().interrupt();
199199
s += initstr;
200-
return defaultVal;
200+
return new ReadResult(ReadStatus.TIMEOUT, defaultVal);
201201
}
202202
try {
203203
ins = new String(Files.readAllBytes(Paths.get(filePath)));
@@ -210,7 +210,7 @@ public static List<Object> read(int port, String name, String initstr) {
210210

211211
if (ins.length() == 0) {
212212
System.out.println("Max retries reached for " + filePath + ", using default value.");
213-
return defaultVal;
213+
return new ReadResult(ReadStatus.RETRIES_EXCEEDED, defaultVal);
214214
}
215215

216216
s += ins;
@@ -219,12 +219,12 @@ public static List<Object> read(int port, String name, String initstr) {
219219
if (!inval.isEmpty()) {
220220
double firstSimtime = ((Number) inval.get(0)).doubleValue();
221221
simtime = Math.max(simtime, firstSimtime);
222-
return new ArrayList<>(inval.subList(1, inval.size()));
222+
return new ReadResult(ReadStatus.SUCCESS, new ArrayList<>(inval.subList(1, inval.size())));
223223
}
224224
} catch (Exception e) {
225225
System.out.println("Error parsing " + ins + ": " + e.getMessage());
226226
}
227-
return defaultVal;
227+
return new ReadResult(ReadStatus.PARSE_ERROR, defaultVal);
228228
}
229229

230230
/**
@@ -446,7 +446,7 @@ private static int zmqSocketTypeFromString(String s) {
446446
* Reads data from a ZMQ port. Same wire format as file-based read:
447447
* expects [simtime, val1, val2, ...], strips simtime, returns the rest.
448448
*/
449-
public static List<Object> read(String portName, String name, String initstr) {
449+
public static ReadResult read(String portName, String name, String initstr) {
450450
List<Object> defaultVal = new ArrayList<>();
451451
try {
452452
List<?> parsed = (List<?>) literalEval(initstr);
@@ -458,24 +458,24 @@ public static List<Object> read(String portName, String name, String initstr) {
458458
ZeroMQPort port = zmqPorts.get(portName);
459459
if (port == null) {
460460
System.err.println("read: ZMQ port '" + portName + "' not initialized");
461-
return defaultVal;
461+
return new ReadResult(ReadStatus.FILE_NOT_FOUND, defaultVal);
462462
}
463463
String msg = port.recvWithRetry();
464464
if (msg == null) {
465465
System.err.println("read: ZMQ recv timeout on port '" + portName + "'");
466-
return defaultVal;
466+
return new ReadResult(ReadStatus.TIMEOUT, defaultVal);
467467
}
468468
s += msg;
469469
try {
470470
List<?> inval = (List<?>) literalEval(msg);
471471
if (!inval.isEmpty()) {
472472
simtime = Math.max(simtime, ((Number) inval.get(0)).doubleValue());
473-
return new ArrayList<>(inval.subList(1, inval.size()));
473+
return new ReadResult(ReadStatus.SUCCESS, new ArrayList<>(inval.subList(1, inval.size())));
474474
}
475475
} catch (Exception e) {
476476
System.out.println("Error parsing ZMQ message '" + msg + "': " + e.getMessage());
477477
}
478-
return defaultVal;
478+
return new ReadResult(ReadStatus.PARSE_ERROR, defaultVal);
479479
}
480480

481481
/**
@@ -526,6 +526,19 @@ static Object literalEval(String s) {
526526
return result;
527527
}
528528

529+
public enum ReadStatus {
530+
SUCCESS, FILE_NOT_FOUND, TIMEOUT, PARSE_ERROR, RETRIES_EXCEEDED
531+
}
532+
533+
public static class ReadResult {
534+
public final ReadStatus status;
535+
public final List<Object> data;
536+
ReadResult(ReadStatus status, List<Object> data) {
537+
this.status = status;
538+
this.data = data;
539+
}
540+
}
541+
529542
/**
530543
* ZMQ socket wrapper with bind/connect, timeouts, and retry.
531544
*/

0 commit comments

Comments
 (0)