Skip to content

Commit fb0ef8b

Browse files
committed
Fix progress bar for replicates.
1 parent a000e7d commit fb0ef8b

File tree

7 files changed

+133
-28
lines changed

7 files changed

+133
-28
lines changed

editor/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ <h2>Config</h2>
180180
<div class="form-group">
181181
<div><label for="config-textarea">Configuration (editor.jshc):</label></div>
182182
<div>
183-
<textarea id="config-textarea" name="config-textarea" class="config-textarea long" rows="10" cols="85" placeholder="testVariable = 5 m"></textarea>
183+
<textarea id="config-textarea" name="config-textarea" class="config-textarea long" rows="10" cols="50" placeholder="testVariable = 5 m"></textarea>
184184
</div>
185185
</div>
186186
<div class="dialog-buttons buttons-panel bold-buttons">

editor/style/style.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ dialog .form-group input.long {
5858
}
5959

6060
dialog .form-group textarea.config-textarea {
61-
width: 650px;
61+
width: 90%;
6262
font-family: monospace;
6363
}
6464

src/main/java/org/joshsim/command/RemoteResponseHandler.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,14 @@ private void handleProgressResponse(WireResponse response, AtomicInteger cumulat
169169
long stepCountToReport = response.getStepCount();
170170

171171
if (useCumulativeProgress && cumulativeStepCount != null) {
172-
// Use cumulative progress for coordinated reporting
173-
WireResponse cumulativeProgress = WireRewriteUtil.rewriteProgressToCumulative(
172+
// Use proper cumulative progress calculation
173+
long stepsPerReplicate = context.getMetadata().getTotalSteps();
174+
int completedReplicatesCount = completedReplicates.get();
175+
176+
WireResponse cumulativeProgress = WireRewriteUtil.rewriteProgressToProperCumulative(
174177
response,
175-
cumulativeStepCount
178+
completedReplicatesCount,
179+
stepsPerReplicate
176180
);
177181
stepCountToReport = cumulativeProgress.getStepCount();
178182
}

src/main/java/org/joshsim/pipeline/remote/LeaderResponseHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ private String generateOutputLine(WireResponse parsedResponse, int replicateNumb
6262
AtomicInteger cumulativeStepCount) {
6363
return switch (parsedResponse.getType()) {
6464
case PROGRESS -> {
65-
// Convert per-replicate progress to cumulative
66-
WireResponse cumulativeResponse = WireRewriteUtil.rewriteProgressToCumulative(
65+
// Convert per-replicate progress to cumulative for worker coordination
66+
WireResponse cumulativeResponse = WireRewriteUtil.rewriteProgressForWorkerCoordination(
6767
parsedResponse,
6868
cumulativeStepCount
6969
);

src/main/java/org/joshsim/wire/WireRewriteUtil.java

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,20 +89,51 @@ public static WireResponse rewriteReplicateNumber(WireResponse response, int new
8989
};
9090
}
9191

92+
93+
/**
94+
* Creates a new WireResponse with proper cumulative progress across replicates.
95+
*
96+
* <p>This method correctly calculates cumulative progress by treating the step count
97+
* as the current position within the active replicate, not as an increment to add.
98+
* The cumulative progress is calculated as:
99+
* (completedReplicates * stepsPerReplicate) + currentStep</p>
100+
*
101+
* @param response The original progress response containing current step within replicate
102+
* @param completedReplicates Number of fully completed replicates
103+
* @param stepsPerReplicate Total steps per individual replicate
104+
* @return A new WireResponse with proper cumulative progress count
105+
* @throws IllegalArgumentException if the response is not a PROGRESS type
106+
*/
107+
public static WireResponse rewriteProgressToProperCumulative(WireResponse response,
108+
int completedReplicates,
109+
long stepsPerReplicate) {
110+
if (response == null) {
111+
throw new IllegalArgumentException("Response cannot be null");
112+
}
113+
114+
if (response.getType() != WireResponse.ResponseType.PROGRESS) {
115+
throw new IllegalArgumentException("Can only rewrite PROGRESS responses to cumulative");
116+
}
117+
118+
long currentStepInReplicate = response.getStepCount();
119+
long cumulativeSteps = (completedReplicates * stepsPerReplicate) + currentStepInReplicate;
120+
return new WireResponse(cumulativeSteps);
121+
}
122+
92123
/**
93-
* Creates a new WireResponse with cumulative progress count.
124+
* Creates a new WireResponse with cumulative progress count for worker coordination.
94125
*
95126
* <p>This method creates a PROGRESS response with a cumulative step count calculated
96-
* by adding the original step count to the cumulative counter. This is useful for
97-
* combining progress from multiple workers into a single cumulative progress stream.</p>
127+
* by adding the original step count to the cumulative counter. This is specifically
128+
* designed for coordinating progress from multiple parallel workers.</p>
98129
*
99130
* @param response The original progress response
100131
* @param cumulativeCounter The cumulative counter to update and use
101132
* @return A new WireResponse with cumulative progress count
102133
* @throws IllegalArgumentException if the response is not a PROGRESS type
103134
*/
104-
public static WireResponse rewriteProgressToCumulative(WireResponse response,
105-
AtomicInteger cumulativeCounter) {
135+
public static WireResponse rewriteProgressForWorkerCoordination(WireResponse response,
136+
AtomicInteger cumulativeCounter) {
106137
if (response == null) {
107138
throw new IllegalArgumentException("Response cannot be null");
108139
}

src/test/java/org/joshsim/command/RemoteResponseHandlerTest.java

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.joshsim.util.OutputOptions;
2323
import org.joshsim.util.ProgressCalculator;
2424
import org.joshsim.util.ProgressUpdate;
25+
import org.joshsim.util.SimulationMetadata;
2526
import org.junit.jupiter.api.BeforeEach;
2627
import org.junit.jupiter.api.Test;
2728

@@ -37,6 +38,7 @@ public class RemoteResponseHandlerTest {
3738
private ExportFacade exportFacade;
3839
private ProgressCalculator progressCalculator;
3940
private OutputOptions outputOptions;
41+
private SimulationMetadata metadata;
4042
private RemoteResponseHandler handler;
4143
private RemoteResponseHandler cumulativeHandler;
4244

@@ -50,9 +52,12 @@ public void setUp() {
5052
exportFacade = mock(ExportFacade.class);
5153
progressCalculator = mock(ProgressCalculator.class);
5254
outputOptions = mock(OutputOptions.class);
55+
metadata = mock(SimulationMetadata.class);
5356

5457
when(context.getProgressCalculator()).thenReturn(progressCalculator);
5558
when(context.getOutputOptions()).thenReturn(outputOptions);
59+
when(context.getMetadata()).thenReturn(metadata);
60+
when(metadata.getTotalSteps()).thenReturn(100L); // 100 steps per replicate
5661
when(exportFactory.build(any(ExportTarget.class))).thenReturn(exportFacade);
5762

5863
handler = new RemoteResponseHandler(context, exportFactory, false);
@@ -80,20 +85,51 @@ public void testProcessProgressResponse() {
8085
@Test
8186
public void testProcessProgressResponseWithCumulative() {
8287
// Arrange
83-
String progressLine = "[progress 10]";
84-
AtomicInteger cumulativeCounter = new AtomicInteger(30);
85-
ProgressUpdate progressUpdate = new ProgressUpdate(true, 40.0, "Progress: 40%");
86-
when(progressCalculator.updateStep(40)).thenReturn(progressUpdate);
88+
String progressLine = "[progress 25]"; // Step 25 within current replicate
89+
AtomicInteger cumulativeCounter = new AtomicInteger(0); // Not used in new implementation
90+
ProgressUpdate progressUpdate = new ProgressUpdate(true, 25.0, "Progress: 25%");
91+
// 0 completed replicates * 100 + 25 = 25
92+
when(progressCalculator.updateStep(25)).thenReturn(progressUpdate);
8793

8894
// Act
8995
Optional<org.joshsim.wire.WireResponse> result =
9096
cumulativeHandler.processResponseLine(progressLine, 0, cumulativeCounter);
9197

9298
// Assert
9399
assertTrue(result.isPresent());
94-
assertEquals(40, cumulativeCounter.get()); // 30 + 10
95-
verify(progressCalculator).updateStep(40);
96-
verify(outputOptions).printInfo("Progress: 40%");
100+
verify(progressCalculator).updateStep(25); // Should be called with cumulative step count
101+
verify(outputOptions).printInfo("Progress: 25%");
102+
}
103+
104+
@Test
105+
public void testProcessProgressResponseWithCumulativeAfterCompletedReplicate() {
106+
// Arrange - Simulate having 2 completed replicates
107+
final String endLine1 = "[end 0]";
108+
final String endLine2 = "[end 1]";
109+
ProgressUpdate endUpdate1 = new ProgressUpdate(true, 100.0, "Replicate 1 completed");
110+
ProgressUpdate endUpdate2 = new ProgressUpdate(true, 100.0, "Replicate 2 completed");
111+
when(progressCalculator.updateReplicateCompleted(1)).thenReturn(endUpdate1);
112+
when(progressCalculator.updateReplicateCompleted(2)).thenReturn(endUpdate2);
113+
114+
// Complete 2 replicates first
115+
cumulativeHandler.processResponseLine(endLine1, 0, null);
116+
cumulativeHandler.processResponseLine(endLine2, 1, null);
117+
118+
// Now test progress in the 3rd replicate
119+
String progressLine = "[progress 30]"; // Step 30 within current replicate
120+
AtomicInteger cumulativeCounter = new AtomicInteger(0); // Not used in new implementation
121+
ProgressUpdate progressUpdate = new ProgressUpdate(true, 230.0, "Progress: 230 steps");
122+
// 2 completed * 100 + 30 = 230
123+
when(progressCalculator.updateStep(230)).thenReturn(progressUpdate);
124+
125+
// Act
126+
Optional<org.joshsim.wire.WireResponse> result =
127+
cumulativeHandler.processResponseLine(progressLine, 2, cumulativeCounter);
128+
129+
// Assert
130+
assertTrue(result.isPresent());
131+
verify(progressCalculator).updateStep(230); // Should be called with proper cumulative count
132+
verify(outputOptions).printInfo("Progress: 230 steps");
97133
}
98134

99135
@Test

src/test/java/org/joshsim/wire/WireRewriteUtilTest.java

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,53 +105,87 @@ public void testRewriteReplicateNumberNullResponse() {
105105
}
106106

107107
@Test
108-
public void testRewriteProgressToCumulative() {
108+
public void testRewriteProgressForWorkerCoordination() {
109109
AtomicInteger cumulativeCounter = new AtomicInteger(10);
110110
WireResponse original = new WireResponse(5);
111111

112112
WireResponse rewritten =
113-
WireRewriteUtil.rewriteProgressToCumulative(original, cumulativeCounter);
113+
WireRewriteUtil.rewriteProgressForWorkerCoordination(original, cumulativeCounter);
114114

115115
assertEquals(WireResponse.ResponseType.PROGRESS, rewritten.getType());
116116
assertEquals(15, rewritten.getStepCount()); // 10 + 5
117117
assertEquals(15, cumulativeCounter.get()); // Counter should be updated
118118
}
119119

120120
@Test
121-
public void testRewriteProgressToCumulativeMultipleTimes() {
121+
public void testRewriteProgressForWorkerCoordinationMultipleTimes() {
122122
AtomicInteger cumulativeCounter = new AtomicInteger(0);
123123

124124
WireResponse first = new WireResponse(10);
125125
WireResponse rewritten1 =
126-
WireRewriteUtil.rewriteProgressToCumulative(first, cumulativeCounter);
126+
WireRewriteUtil.rewriteProgressForWorkerCoordination(first, cumulativeCounter);
127127
assertEquals(10, rewritten1.getStepCount());
128128
assertEquals(10, cumulativeCounter.get());
129129

130130
WireResponse second = new WireResponse(5);
131131
WireResponse rewritten2 =
132-
WireRewriteUtil.rewriteProgressToCumulative(second, cumulativeCounter);
132+
WireRewriteUtil.rewriteProgressForWorkerCoordination(second, cumulativeCounter);
133133
assertEquals(15, rewritten2.getStepCount());
134134
assertEquals(15, cumulativeCounter.get());
135135
}
136136

137137
@Test
138-
public void testRewriteProgressToCumulativeNullResponse() {
138+
public void testRewriteProgressForWorkerCoordinationNullResponse() {
139139
AtomicInteger cumulativeCounter = new AtomicInteger(0);
140140
assertThrows(IllegalArgumentException.class, () -> {
141-
WireRewriteUtil.rewriteProgressToCumulative(null, cumulativeCounter);
141+
WireRewriteUtil.rewriteProgressForWorkerCoordination(null, cumulativeCounter);
142142
});
143143
}
144144

145145
@Test
146-
public void testRewriteProgressToCumulativeNonProgressResponse() {
146+
public void testRewriteProgressForWorkerCoordinationNonProgressResponse() {
147147
AtomicInteger cumulativeCounter = new AtomicInteger(0);
148148
WireResponse datumResponse = new WireResponse(5, "data");
149149

150150
assertThrows(IllegalArgumentException.class, () -> {
151-
WireRewriteUtil.rewriteProgressToCumulative(datumResponse, cumulativeCounter);
151+
WireRewriteUtil.rewriteProgressForWorkerCoordination(datumResponse, cumulativeCounter);
152152
});
153153
}
154154

155+
@Test
156+
public void testRewriteProgressToProperCumulative() {
157+
// Test with 0 completed replicates, step 50 of 100
158+
WireResponse response = new WireResponse(50);
159+
WireResponse rewritten = WireRewriteUtil.rewriteProgressToProperCumulative(response, 0, 100);
160+
161+
assertEquals(WireResponse.ResponseType.PROGRESS, rewritten.getType());
162+
assertEquals(50, rewritten.getStepCount()); // 0 * 100 + 50 = 50
163+
}
164+
165+
@Test
166+
public void testRewriteProgressToProperCumulativeMultipleReplicates() {
167+
// Test with 2 completed replicates, step 30 of 100 in current replicate
168+
WireResponse response = new WireResponse(30);
169+
WireResponse rewritten = WireRewriteUtil.rewriteProgressToProperCumulative(response, 2, 100);
170+
171+
assertEquals(WireResponse.ResponseType.PROGRESS, rewritten.getType());
172+
assertEquals(230, rewritten.getStepCount()); // 2 * 100 + 30 = 230
173+
}
174+
175+
@Test
176+
public void testRewriteProgressToProperCumulativeWithNullResponse() {
177+
assertThrows(IllegalArgumentException.class, () ->
178+
WireRewriteUtil.rewriteProgressToProperCumulative(null, 1, 100));
179+
}
180+
181+
@Test
182+
public void testRewriteProgressToProperCumulativeWithNonProgressResponse() {
183+
WireResponse datumResponse = new WireResponse(1, "test data");
184+
185+
assertThrows(IllegalArgumentException.class, () ->
186+
WireRewriteUtil.rewriteProgressToProperCumulative(datumResponse, 1, 100));
187+
}
188+
155189
@Test
156190
public void testFormatWireResponseProgress() {
157191
WireResponse response = new WireResponse(42);

0 commit comments

Comments
 (0)