19
19
package de .rwth .idsg .steve .utils ;
20
20
21
21
import com .fasterxml .jackson .core .JsonParser ;
22
+ import com .fasterxml .jackson .core .JsonProcessingException ;
22
23
import com .fasterxml .jackson .core .TreeNode ;
23
24
import com .fasterxml .jackson .databind .JsonNode ;
24
25
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 ;
25
30
import de .rwth .idsg .ocpp .jaxb .RequestType ;
26
31
import de .rwth .idsg .ocpp .jaxb .ResponseType ;
27
32
import de .rwth .idsg .steve .SteveException ;
36
41
import de .rwth .idsg .steve .ocpp .ws .data .OcppJsonResponse ;
37
42
import de .rwth .idsg .steve .ocpp .ws .data .OcppJsonResult ;
38
43
import de .rwth .idsg .steve .ocpp .ws .pipeline .Serializer ;
44
+ import lombok .Getter ;
45
+ import lombok .Setter ;
46
+ import lombok .Value ;
39
47
import lombok .extern .slf4j .Slf4j ;
40
48
import org .eclipse .jetty .websocket .api .Session ;
41
49
import org .eclipse .jetty .websocket .api .StatusCode ;
50
58
import java .io .IOException ;
51
59
import java .net .URI ;
52
60
import java .util .ArrayList ;
61
+ import java .util .HashMap ;
53
62
import java .util .LinkedHashMap ;
54
63
import java .util .Map ;
64
+ import java .util .Objects ;
55
65
import java .util .UUID ;
56
66
import java .util .concurrent .CountDownLatch ;
57
67
import java .util .concurrent .Future ;
@@ -69,6 +79,7 @@ public class OcppJsonChargePoint {
69
79
private final String chargeBoxId ;
70
80
private final String connectionPath ;
71
81
private final Map <String , ResponseContext > responseContextMap ;
82
+ private final Map <String , RequestContext > requestContextMap ;
72
83
private final MessageDeserializer deserializer ;
73
84
private final WebSocketClient client ;
74
85
private final CountDownLatch closeHappenedSignal ;
@@ -88,6 +99,7 @@ public OcppJsonChargePoint(String ocppVersion, String chargeBoxId, String pathPr
88
99
this .chargeBoxId = chargeBoxId ;
89
100
this .connectionPath = pathPrefix + chargeBoxId ;
90
101
this .responseContextMap = new LinkedHashMap <>(); // because we want to keep the insertion order of test cases
102
+ this .requestContextMap = new HashMap <>();
91
103
this .deserializer = new MessageDeserializer ();
92
104
this .client = new WebSocketClient ();
93
105
this .closeHappenedSignal = new CountDownLatch (1 );
@@ -121,6 +133,8 @@ public void onMessage(Session session, String msg) {
121
133
} else if (ocppMsg instanceof OcppJsonError ) {
122
134
ResponseContext ctx = responseContextMap .remove (ocppMsg .getMessageId ());
123
135
ctx .errorHandler .accept ((OcppJsonError ) ocppMsg );
136
+ } else if (ocppMsg instanceof OcppJsonCallForTesting ) {
137
+ handleCall ((OcppJsonCallForTesting ) ocppMsg );
124
138
}
125
139
} catch (Exception e ) {
126
140
log .error ("Exception" , e );
@@ -171,9 +185,25 @@ public <T extends ResponseType> void prepare(RequestType payload, String action,
171
185
responseContextMap .put (messageId , resCtx );
172
186
}
173
187
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
+
174
203
public void process () {
204
+ int requestCount = requestContextMap .values ().size ();
175
205
int responseCount = responseContextMap .values ().size ();
176
- receivedMessagesSignal = new CountDownLatch (responseCount );
206
+ receivedMessagesSignal = new CountDownLatch (requestCount + responseCount );
177
207
178
208
// copy the values in a new list to be iterated over, because otherwise we get a ConcurrentModificationException,
179
209
// 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) {
231
261
return s ;
232
262
}
233
263
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
+
234
285
private static class ResponseContext {
235
286
private final String outgoingMessage ;
236
287
private final Class <ResponseType > responseClass ;
@@ -269,6 +320,8 @@ private OcppJsonMessage extract(String msg) throws Exception {
269
320
return handleResult (messageId , parser );
270
321
case CALL_ERROR :
271
322
return handleError (messageId , parser );
323
+ case CALL :
324
+ return handleCall (messageId , parser );
272
325
default :
273
326
throw new SteveException ("Unknown enum type" );
274
327
}
@@ -311,6 +364,54 @@ private OcppJsonResponse handleError(String messageId, JsonParser parser) throws
311
364
error .setErrorDetails (details );
312
365
return error ;
313
366
}
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 ;
314
415
}
315
416
316
417
}
0 commit comments