Skip to content

Commit 51a03de

Browse files
committed
Defer frame buffers and metrics until after the BWAPI handshake
1 parent 0507796 commit 51a03de

2 files changed

Lines changed: 54 additions & 4 deletions

File tree

‎src/main/java/bwapi/BWClient.java‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,17 @@ public void startGame(boolean autoContinue) {
8787
*/
8888
public void startGame(BWClientConfiguration gameConfiguration) {
8989
this.configuration = gameConfiguration;
90-
this.performanceMetrics = new PerformanceMetrics(configuration);
91-
botWrapper = configuration.getAsync()
92-
? new BotWrapperAsync(configuration, eventListener)
93-
: new BotWrapper(configuration, eventListener);
9490

9591
if (client == null) {
9692
client = new Client(this);
9793
}
9894
client.reconnect();
9995

96+
this.performanceMetrics = new PerformanceMetrics(configuration);
97+
botWrapper = configuration.getAsync()
98+
? new BotWrapperAsync(configuration, eventListener)
99+
: new BotWrapper(configuration, eventListener);
100+
100101
do {
101102
ClientData.GameData liveGameData = client.liveClientData().gameData();
102103
while (!liveGameData.isInGame()) {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package bwapi;
2+
3+
import org.junit.Test;
4+
5+
import java.lang.management.BufferPoolMXBean;
6+
import java.lang.management.ManagementFactory;
7+
8+
import static org.junit.Assert.assertNull;
9+
import static org.junit.Assert.assertThrows;
10+
import static org.junit.Assert.assertTrue;
11+
import static org.mockito.Mockito.doAnswer;
12+
import static org.mockito.Mockito.mock;
13+
14+
/**
15+
* Guards the order of startup work: attempt the BWAPI connection before creating
16+
* performance metrics or allocating the large buffers used for asynchronous frames.
17+
* Measures direct-buffer allocation at a mocked connection boundary, so the test
18+
* needs neither a running server nor a machine-dependent elapsed-time threshold.
19+
*/
20+
public class BWClientStartupTest {
21+
// Stops startGame at the mocked connection boundary, before a game loop can run.
22+
private static class StopAfterConnectionAttempt extends RuntimeException {
23+
}
24+
25+
@Test
26+
public void attemptsConnectionBeforeAllocatingFrameBuffersOrMetrics() {
27+
BWClient bwClient = new BWClient(new DefaultBWListener());
28+
Client client = mock(Client.class);
29+
bwClient.setClient(client);
30+
BWClientConfiguration configuration = new BWClientConfiguration.Builder()
31+
.withAsync(true)
32+
.withAsyncFrameBufferCapacity(3)
33+
.build();
34+
BufferPoolMXBean directBuffers = ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class)
35+
.stream()
36+
.filter(pool -> "direct".equals(pool.getName()))
37+
.findFirst()
38+
.get();
39+
long bytesBeforeStart = directBuffers.getMemoryUsed();
40+
doAnswer(invocation -> {
41+
assertTrue("Connection attempts must precede allocation of even one game-state buffer",
42+
directBuffers.getMemoryUsed() - bytesBeforeStart < ClientData.GameData.SIZE);
43+
assertNull(bwClient.getPerformanceMetrics());
44+
throw new StopAfterConnectionAttempt();
45+
}).when(client).reconnect();
46+
47+
assertThrows(StopAfterConnectionAttempt.class, () -> bwClient.startGame(configuration));
48+
}
49+
}

0 commit comments

Comments
 (0)