Skip to content

Commit 943c754

Browse files
committed
add ws/json stress tests
1 parent ddc56a2 commit 943c754

6 files changed

Lines changed: 426 additions & 164 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package de.rwth.idsg.steve;
2+
3+
import de.rwth.idsg.steve.utils.__DatabasePreparer__;
4+
import ocpp.cs._2015._10.MeterValue;
5+
import ocpp.cs._2015._10.SampledValue;
6+
import org.joda.time.DateTime;
7+
import org.junit.Assert;
8+
9+
import java.util.ArrayList;
10+
import java.util.Collections;
11+
import java.util.List;
12+
13+
/**
14+
* @author Sevket Goekay <goekay@dbis.rwth-aachen.de>
15+
* @since 10.05.2018
16+
*/
17+
public abstract class StressTest {
18+
19+
// higher values -> more stress
20+
//
21+
protected static final int THREAD_COUNT = 50;
22+
protected static final int REPEAT_COUNT_PER_THREAD = 50;
23+
24+
// lower values -> more stress
25+
//
26+
// reason: these only specify the size of the values "bag" from which a test picks a value randomly. if there are
27+
// less values to pick from, it is more likely that tests will use the same value at the same time. this produces
28+
// more overhead for steve (especially db) when multiple threads "fight" for inserting/updating a db row/cell.
29+
//
30+
protected static final int ID_TAG_COUNT = 50;
31+
protected static final int CHARGE_BOX_COUNT = THREAD_COUNT;
32+
protected static final int CONNECTOR_COUNT_PER_CHARGE_BOX = 25;
33+
34+
protected void attack() throws Exception {
35+
Assert.assertEquals(ApplicationProfile.TEST, SteveConfiguration.CONFIG.getProfile());
36+
Assert.assertTrue(SteveConfiguration.CONFIG.getOcpp().isAutoRegisterUnknownStations());
37+
38+
__DatabasePreparer__.prepare();
39+
40+
Application app = new Application();
41+
try {
42+
app.start();
43+
attackInternal();
44+
} finally {
45+
try {
46+
app.stop();
47+
} finally {
48+
__DatabasePreparer__.cleanUp();
49+
}
50+
}
51+
}
52+
53+
protected abstract void attackInternal() throws Exception;
54+
55+
protected static List<MeterValue> getMeterValues(int transactionStart, int transactionStop) {
56+
final int size = 4;
57+
int delta = (transactionStop - transactionStart) / size;
58+
if (delta == 0) {
59+
return Collections.emptyList();
60+
}
61+
62+
List<MeterValue> list = new ArrayList<>(size);
63+
for (int i = 0; i < size; i++) {
64+
int meterValue = transactionStart + delta * (i + 1);
65+
list.add(createMeterValue(meterValue));
66+
}
67+
return list;
68+
}
69+
70+
protected static MeterValue createMeterValue(int val) {
71+
return new MeterValue().withTimestamp(DateTime.now())
72+
.withSampledValue(new SampledValue().withValue(Integer.toString(val)));
73+
}
74+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package de.rwth.idsg.steve;
2+
3+
import de.rwth.idsg.steve.ocpp.OcppVersion;
4+
import de.rwth.idsg.steve.utils.OcppJsonChargePoint;
5+
import de.rwth.idsg.steve.utils.StressTester;
6+
import ocpp.cs._2015._10.AuthorizationStatus;
7+
import ocpp.cs._2015._10.AuthorizeRequest;
8+
import ocpp.cs._2015._10.AuthorizeResponse;
9+
import ocpp.cs._2015._10.BootNotificationRequest;
10+
import ocpp.cs._2015._10.BootNotificationResponse;
11+
import ocpp.cs._2015._10.ChargePointErrorCode;
12+
import ocpp.cs._2015._10.ChargePointStatus;
13+
import ocpp.cs._2015._10.HeartbeatRequest;
14+
import ocpp.cs._2015._10.HeartbeatResponse;
15+
import ocpp.cs._2015._10.MeterValuesRequest;
16+
import ocpp.cs._2015._10.MeterValuesResponse;
17+
import ocpp.cs._2015._10.RegistrationStatus;
18+
import ocpp.cs._2015._10.StartTransactionRequest;
19+
import ocpp.cs._2015._10.StartTransactionResponse;
20+
import ocpp.cs._2015._10.StatusNotificationRequest;
21+
import ocpp.cs._2015._10.StatusNotificationResponse;
22+
import ocpp.cs._2015._10.StopTransactionRequest;
23+
import ocpp.cs._2015._10.StopTransactionResponse;
24+
import org.joda.time.DateTime;
25+
import org.junit.Assert;
26+
27+
import java.util.List;
28+
import java.util.concurrent.ThreadLocalRandom;
29+
import java.util.concurrent.atomic.AtomicInteger;
30+
31+
import static de.rwth.idsg.steve.utils.Helpers.getJsonPath;
32+
import static de.rwth.idsg.steve.utils.Helpers.getRandomString;
33+
import static de.rwth.idsg.steve.utils.Helpers.getRandomStrings;
34+
35+
/**
36+
* @author Sevket Goekay <goekay@dbis.rwth-aachen.de>
37+
* @since 09.05.2018
38+
*/
39+
public class StressTestJsonOCPP16 extends StressTest {
40+
41+
private static final String PATH = getJsonPath();
42+
private static final OcppVersion VERSION = OcppVersion.V_16;
43+
44+
public static void main(String[] args) throws Exception {
45+
new StressTestJsonOCPP16().attack();
46+
}
47+
48+
protected void attackInternal() throws Exception {
49+
final List<String> idTags = getRandomStrings(ID_TAG_COUNT);
50+
final List<String> chargeBoxIds = getRandomStrings(CHARGE_BOX_COUNT);
51+
52+
StressTester.Runnable runnable = new StressTester.Runnable() {
53+
54+
private final ThreadLocal<OcppJsonChargePoint> threadLocalChargePoint = new ThreadLocal<>();
55+
56+
@Override
57+
public void beforeRepeat() {
58+
ThreadLocalRandom localRandom = ThreadLocalRandom.current();
59+
60+
String chargeBoxId = chargeBoxIds.get(localRandom.nextInt(chargeBoxIds.size()));
61+
threadLocalChargePoint.set(new OcppJsonChargePoint(VERSION, chargeBoxId, PATH));
62+
63+
OcppJsonChargePoint chargePoint = threadLocalChargePoint.get();
64+
chargePoint.start();
65+
66+
chargePoint.prepare(
67+
new BootNotificationRequest()
68+
.withChargePointVendor(getRandomString())
69+
.withChargePointModel(getRandomString()),
70+
BootNotificationResponse.class,
71+
bootResponse -> Assert.assertEquals(RegistrationStatus.ACCEPTED, bootResponse.getStatus()),
72+
error -> Assert.fail()
73+
);
74+
}
75+
76+
@Override
77+
public void toRepeat() {
78+
ThreadLocalRandom localRandom = ThreadLocalRandom.current();
79+
80+
OcppJsonChargePoint chargePoint = threadLocalChargePoint.get();
81+
82+
String idTag = idTags.get(localRandom.nextInt(idTags.size()));
83+
int connectorId = localRandom.nextInt(1, CONNECTOR_COUNT_PER_CHARGE_BOX + 1);
84+
int transactionStart = localRandom.nextInt(0, Integer.MAX_VALUE);
85+
int transactionStop = localRandom.nextInt(transactionStart + 1, Integer.MAX_VALUE);
86+
87+
chargePoint.prepare(
88+
new HeartbeatRequest(), HeartbeatResponse.class,
89+
Assert::assertNotNull,
90+
error -> Assert.fail()
91+
);
92+
93+
for (int i = 0; i <= CONNECTOR_COUNT_PER_CHARGE_BOX; i++) {
94+
chargePoint.prepare(
95+
new StatusNotificationRequest()
96+
.withErrorCode(ChargePointErrorCode.NO_ERROR)
97+
.withStatus(ChargePointStatus.AVAILABLE)
98+
.withConnectorId(i)
99+
.withTimestamp(DateTime.now()),
100+
StatusNotificationResponse.class,
101+
Assert::assertNotNull,
102+
error -> Assert.fail()
103+
);
104+
}
105+
106+
chargePoint.prepare(
107+
new AuthorizeRequest().withIdTag(idTag),
108+
AuthorizeResponse.class,
109+
response -> Assert.assertNotEquals(AuthorizationStatus.ACCEPTED, response.getIdTagInfo().getStatus()),
110+
error -> Assert.fail()
111+
);
112+
113+
final AtomicInteger transactionId = new AtomicInteger(-1);
114+
115+
chargePoint.prepare(
116+
new StartTransactionRequest()
117+
.withConnectorId(connectorId)
118+
.withIdTag(idTag)
119+
.withTimestamp(DateTime.now())
120+
.withMeterStart(transactionStart),
121+
StartTransactionResponse.class,
122+
response -> {
123+
Assert.assertNotNull(response);
124+
transactionId.set(response.getTransactionId());
125+
},
126+
error -> Assert.fail()
127+
);
128+
129+
// wait for StartTransactionResponse to arrive, since we need the transactionId from now on
130+
chargePoint.process();
131+
132+
chargePoint.prepare(
133+
new StatusNotificationRequest()
134+
.withErrorCode(ChargePointErrorCode.NO_ERROR)
135+
.withStatus(ChargePointStatus.CHARGING)
136+
.withConnectorId(connectorId)
137+
.withTimestamp(DateTime.now()),
138+
StatusNotificationResponse.class,
139+
Assert::assertNotNull,
140+
error -> Assert.fail()
141+
);
142+
143+
chargePoint.prepare(
144+
new MeterValuesRequest()
145+
.withConnectorId(connectorId)
146+
.withTransactionId(transactionId.get())
147+
.withMeterValue(getMeterValues(transactionStart, transactionStop)),
148+
MeterValuesResponse.class,
149+
Assert::assertNotNull,
150+
error -> Assert.fail()
151+
);
152+
153+
chargePoint.prepare(
154+
new StopTransactionRequest()
155+
.withTransactionId(transactionId.get())
156+
.withTimestamp(DateTime.now())
157+
.withIdTag(idTag)
158+
.withMeterStop(transactionStop),
159+
StopTransactionResponse.class,
160+
Assert::assertNotNull,
161+
error -> Assert.fail()
162+
);
163+
164+
chargePoint.prepare(
165+
new StatusNotificationRequest()
166+
.withErrorCode(ChargePointErrorCode.NO_ERROR)
167+
.withStatus(ChargePointStatus.AVAILABLE)
168+
.withConnectorId(connectorId)
169+
.withTimestamp(DateTime.now()),
170+
StatusNotificationResponse.class,
171+
Assert::assertNotNull,
172+
error -> Assert.fail()
173+
);
174+
175+
chargePoint.process();
176+
}
177+
178+
@Override
179+
public void afterRepeat() {
180+
threadLocalChargePoint.get().close();
181+
}
182+
};
183+
184+
StressTester tester = new StressTester(THREAD_COUNT, REPEAT_COUNT_PER_THREAD);
185+
tester.test(runnable);
186+
tester.shutDown();
187+
}
188+
}

0 commit comments

Comments
 (0)