Skip to content

Commit de399e5

Browse files
committed
add support for incoming calls to OcppJsonChargePoint
thus far, test cases supported only request/response interactions initiated by the _station_. any request/call initiated by steve was not taken into account by station. with this change, station test cases can express requests that are expected (initiated by steve) and corresponding responses to send back to steve. see OcppJsonChargePoint#expectRequest
1 parent 747eef1 commit de399e5

File tree

1 file changed

+102
-1
lines changed

1 file changed

+102
-1
lines changed

src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java

+102-1
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@
1919
package de.rwth.idsg.steve.utils;
2020

2121
import com.fasterxml.jackson.core.JsonParser;
22+
import com.fasterxml.jackson.core.JsonProcessingException;
2223
import com.fasterxml.jackson.core.TreeNode;
2324
import com.fasterxml.jackson.databind.JsonNode;
2425
import com.fasterxml.jackson.databind.ObjectMapper;
26+
import com.fasterxml.jackson.databind.node.ArrayNode;
27+
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
28+
import com.fasterxml.jackson.databind.node.NullNode;
29+
import com.fasterxml.jackson.databind.node.ObjectNode;
2530
import de.rwth.idsg.ocpp.jaxb.RequestType;
2631
import de.rwth.idsg.ocpp.jaxb.ResponseType;
2732
import de.rwth.idsg.steve.SteveException;
@@ -36,6 +41,9 @@
3641
import de.rwth.idsg.steve.ocpp.ws.data.OcppJsonResponse;
3742
import de.rwth.idsg.steve.ocpp.ws.data.OcppJsonResult;
3843
import de.rwth.idsg.steve.ocpp.ws.pipeline.Serializer;
44+
import lombok.Getter;
45+
import lombok.Setter;
46+
import lombok.Value;
3947
import lombok.extern.slf4j.Slf4j;
4048
import org.eclipse.jetty.websocket.api.Session;
4149
import org.eclipse.jetty.websocket.api.StatusCode;
@@ -50,8 +58,10 @@
5058
import java.io.IOException;
5159
import java.net.URI;
5260
import java.util.ArrayList;
61+
import java.util.HashMap;
5362
import java.util.LinkedHashMap;
5463
import java.util.Map;
64+
import java.util.Objects;
5565
import java.util.UUID;
5666
import java.util.concurrent.CountDownLatch;
5767
import java.util.concurrent.Future;
@@ -69,6 +79,7 @@ public class OcppJsonChargePoint {
6979
private final String chargeBoxId;
7080
private final String connectionPath;
7181
private final Map<String, ResponseContext> responseContextMap;
82+
private final Map<String, RequestContext> requestContextMap;
7283
private final MessageDeserializer deserializer;
7384
private final WebSocketClient client;
7485
private final CountDownLatch closeHappenedSignal;
@@ -88,6 +99,7 @@ public OcppJsonChargePoint(String ocppVersion, String chargeBoxId, String pathPr
8899
this.chargeBoxId = chargeBoxId;
89100
this.connectionPath = pathPrefix + chargeBoxId;
90101
this.responseContextMap = new LinkedHashMap<>(); // because we want to keep the insertion order of test cases
102+
this.requestContextMap = new HashMap<>();
91103
this.deserializer = new MessageDeserializer();
92104
this.client = new WebSocketClient();
93105
this.closeHappenedSignal = new CountDownLatch(1);
@@ -121,6 +133,8 @@ public void onMessage(Session session, String msg) {
121133
} else if (ocppMsg instanceof OcppJsonError) {
122134
ResponseContext ctx = responseContextMap.remove(ocppMsg.getMessageId());
123135
ctx.errorHandler.accept((OcppJsonError) ocppMsg);
136+
} else if (ocppMsg instanceof OcppJsonCallForTesting) {
137+
handleCall((OcppJsonCallForTesting) ocppMsg);
124138
}
125139
} catch (Exception e) {
126140
log.error("Exception", e);
@@ -171,9 +185,25 @@ public <T extends ResponseType> void prepare(RequestType payload, String action,
171185
responseContextMap.put(messageId, resCtx);
172186
}
173187

188+
public void expectRequest(RequestType expectedRequest, ResponseType plannedResponse) {
189+
String requestPayload;
190+
JsonNode responsePayload;
191+
try {
192+
ObjectMapper mapper = JsonObjectMapper.INSTANCE.getMapper();
193+
requestPayload = mapper.writeValueAsString(expectedRequest);
194+
responsePayload = mapper.valueToTree(plannedResponse);
195+
} catch (JsonProcessingException e) {
196+
throw new RuntimeException(e);
197+
}
198+
199+
String action = getOperationName(expectedRequest);
200+
requestContextMap.put(action, new RequestContext(requestPayload, responsePayload));
201+
}
202+
174203
public void process() {
204+
int requestCount = requestContextMap.values().size();
175205
int responseCount = responseContextMap.values().size();
176-
receivedMessagesSignal = new CountDownLatch(responseCount);
206+
receivedMessagesSignal = new CountDownLatch(requestCount + responseCount);
177207

178208
// copy the values in a new list to be iterated over, because otherwise we get a ConcurrentModificationException,
179209
// since the onMessage(..) uses the same responseContextMap to remove an item while looping over its items here.
@@ -231,6 +261,27 @@ private static String getOperationName(RequestType requestType) {
231261
return s;
232262
}
233263

264+
private void handleCall(OcppJsonCallForTesting call) {
265+
try {
266+
ArrayNode node = JsonObjectMapper.INSTANCE.getMapper()
267+
.createArrayNode()
268+
.add(MessageType.CALL_RESULT.getTypeNr())
269+
.add(call.getMessageId())
270+
.add(call.getContext().getResponsePayload());
271+
272+
String str = JsonObjectMapper.INSTANCE.getMapper().writeValueAsString(node);
273+
session.getRemote().sendString(str);
274+
} catch (Exception e) {
275+
throw new RuntimeException(e);
276+
}
277+
}
278+
279+
@Value
280+
private static class RequestContext {
281+
String requestPayload;
282+
JsonNode responsePayload;
283+
}
284+
234285
private static class ResponseContext {
235286
private final String outgoingMessage;
236287
private final Class<ResponseType> responseClass;
@@ -269,6 +320,8 @@ private OcppJsonMessage extract(String msg) throws Exception {
269320
return handleResult(messageId, parser);
270321
case CALL_ERROR:
271322
return handleError(messageId, parser);
323+
case CALL:
324+
return handleCall(messageId, parser);
272325
default:
273326
throw new SteveException("Unknown enum type");
274327
}
@@ -311,6 +364,54 @@ private OcppJsonResponse handleError(String messageId, JsonParser parser) throws
311364
error.setErrorDetails(details);
312365
return error;
313366
}
367+
368+
private OcppJsonCall handleCall(String messageId, JsonParser parser) {
369+
// parse action
370+
String action;
371+
try {
372+
parser.nextToken();
373+
action = parser.getText();
374+
} catch (IOException e) {
375+
throw new RuntimeException();
376+
}
377+
378+
// parse request payload
379+
String req;
380+
try {
381+
parser.nextToken();
382+
JsonNode requestPayload = parser.readValueAsTree();
383+
384+
// https://github.com/steve-community/steve/issues/1109
385+
if (requestPayload instanceof NullNode) {
386+
requestPayload = new ObjectNode(JsonNodeFactory.instance);
387+
}
388+
389+
req = requestPayload.toString();
390+
} catch (IOException e) {
391+
log.error("Exception occurred", e);
392+
throw new RuntimeException();
393+
}
394+
395+
RequestContext context = requestContextMap.get(action);
396+
if (context == null) {
397+
testerThreadInterruptReason = new RuntimeException("Unexpected message arrived: " + req);
398+
testerThread.interrupt();
399+
} else if (Objects.equals(context.requestPayload, req)) {
400+
requestContextMap.remove(action);
401+
}
402+
403+
OcppJsonCallForTesting call = new OcppJsonCallForTesting();
404+
call.setAction(action);
405+
call.setMessageId(messageId);
406+
call.setContext(context);
407+
return call;
408+
}
409+
}
410+
411+
@Setter
412+
@Getter
413+
private static class OcppJsonCallForTesting extends OcppJsonCall {
414+
private RequestContext context;
314415
}
315416

316417
}

0 commit comments

Comments
 (0)