fix: correct STEP-057B weather runtime state semantics
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled

This commit is contained in:
OpenAI Codex
2026-09-07 13:03:30 +08:00
parent dad6876c72
commit 486cc80b6f
5 changed files with 103 additions and 3 deletions

View File

@@ -55,7 +55,7 @@ public final class WeatherRuntime {
public FieldSubmitResult submitWindField(@NonNull String metadataJson, @NonNull byte[] payloadBytes) {
requireEnabled();
if (metadataJson.isEmpty() || payloadBytes.length == 0) { state = State.ERROR; return FieldSubmitResult.REJECTED_ADAPTER; }
if (metadataJson.isEmpty() || payloadBytes.length == 0) return FieldSubmitResult.REJECTED_ADAPTER;
FieldSubmitResult result = FieldSubmitResult.fromCode(map.submitRasterArraySourceHostField(config.sourceId, metadataJson, payloadBytes));
state = stateAfterSubmit(state, result);
return result;
@@ -71,6 +71,7 @@ public final class WeatherRuntime {
state = stateAfterDisable(color && particle && clear);
return result(color && particle && clear);
}
if (state == State.ERROR) return CommandResult.REJECTED;
if (state != State.DISABLED) return CommandResult.ACCEPTED;
boolean color = map.setStyleLayerVisibility(config.weatherColorLayerId, weatherColorVisible);
boolean particle = map.setStyleLayerVisibility(config.particleLayerId, particleVisible);
@@ -105,13 +106,14 @@ public final class WeatherRuntime {
if (submit == FieldSubmitResult.ACCEPTED_PENDING_INITIAL_WINDOW) return State.WAITING_FOR_FIELDS;
if (submit == FieldSubmitResult.INITIAL_WINDOW_READY || submit == FieldSubmitResult.ACCEPTED_NEXT_SNAPSHOT) return State.READY;
if (submit == FieldSubmitResult.DUPLICATE) return current;
return State.ERROR;
return submit == FieldSubmitResult.REJECTED_SOURCE_MODE || submit == FieldSubmitResult.UNKNOWN ? State.ERROR : current;
}
static State stateAfterClear(State current, boolean accepted) { return accepted ? State.WAITING_FOR_FIELDS : current; }
static State stateAfterDisable(boolean accepted) { return accepted ? State.DISABLED : State.ERROR; }
static State stateAfterEnable(State current, boolean accepted) {
if (!accepted) return State.ERROR;
return current == State.DISABLED ? State.WAITING_FOR_FIELDS : current;
if (current == State.DISABLED) return State.WAITING_FOR_FIELDS;
return current;
}
private void requireEnabled() { if (state == State.DISABLED) throw new IllegalStateException("WeatherRuntime is disabled; enable and submit fields first"); }
}

View File

@@ -1,6 +1,7 @@
package org.maplibre.android.maps;
import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
@@ -15,9 +16,51 @@ public class WeatherRuntimeStateTest {
assertEquals(WeatherRuntime.State.READY, WeatherRuntime.stateAfterSubmit(WeatherRuntime.State.WAITING_FOR_FIELDS, WeatherRuntime.FieldSubmitResult.INITIAL_WINDOW_READY));
assertEquals(WeatherRuntime.State.READY, WeatherRuntime.stateAfterSubmit(WeatherRuntime.State.READY, WeatherRuntime.FieldSubmitResult.DUPLICATE));
}
@Test public void ordinarySubmitRejectionsPreserveRuntimeState() {
WeatherRuntime.FieldSubmitResult[] rejected = {
WeatherRuntime.FieldSubmitResult.REJECTED_ADAPTER,
WeatherRuntime.FieldSubmitResult.REJECTED_INCOMPATIBLE,
WeatherRuntime.FieldSubmitResult.REJECTED_NON_MONOTONIC,
WeatherRuntime.FieldSubmitResult.REJECTED_CAPACITY};
for (WeatherRuntime.FieldSubmitResult result : rejected) {
assertEquals(WeatherRuntime.State.READY, WeatherRuntime.stateAfterSubmit(WeatherRuntime.State.READY, result));
assertEquals(WeatherRuntime.State.WAITING_FOR_FIELDS, WeatherRuntime.stateAfterSubmit(WeatherRuntime.State.WAITING_FOR_FIELDS, result));
}
}
@Test public void onlyFacadeContractFailuresEnterError() {
assertEquals(WeatherRuntime.State.ERROR, WeatherRuntime.stateAfterSubmit(WeatherRuntime.State.READY, WeatherRuntime.FieldSubmitResult.REJECTED_SOURCE_MODE));
assertEquals(WeatherRuntime.State.ERROR, WeatherRuntime.stateAfterSubmit(WeatherRuntime.State.READY, WeatherRuntime.FieldSubmitResult.UNKNOWN));
assertEquals(WeatherRuntime.State.ERROR, WeatherRuntime.stateAfterEnable(WeatherRuntime.State.ERROR, false));
assertEquals(WeatherRuntime.State.ERROR, WeatherRuntime.stateAfterEnable(WeatherRuntime.State.ERROR, true));
}
@Test public void clearAndDisableUseSuccessfulCoreResult() {
assertEquals(WeatherRuntime.State.WAITING_FOR_FIELDS, WeatherRuntime.stateAfterClear(WeatherRuntime.State.READY, true));
assertEquals(WeatherRuntime.State.DISABLED, WeatherRuntime.stateAfterDisable(true));
assertEquals(WeatherRuntime.State.ERROR, WeatherRuntime.stateAfterDisable(false));
}
@Test public void publicFlowKeepsReadyOnInvalidSubmission() {
MapLibreMap map = Mockito.mock(MapLibreMap.class);
Mockito.when(map.getWeatherColorStopCapacity()).thenReturn(8);
Mockito.when(map.submitRasterArraySourceHostField(Mockito.anyString(), Mockito.anyString(), Mockito.any(byte[].class)))
.thenReturn(MapLibreMap.HOST_FIELD_INITIAL_WINDOW_READY)
.thenReturn(MapLibreMap.HOST_FIELD_REJECTED_ADAPTER);
WeatherRuntime runtime = new WeatherRuntime(map, new WeatherRuntime.Config("source", "color", "particle"));
assertEquals(WeatherRuntime.FieldSubmitResult.INITIAL_WINDOW_READY, runtime.submitWindField("{}", new byte[] {1}));
assertEquals(WeatherRuntime.FieldSubmitResult.REJECTED_ADAPTER, runtime.submitWindField("{}", new byte[] {1}));
assertEquals(WeatherRuntime.State.READY, runtime.getState());
assertEquals(WeatherRuntime.FieldSubmitResult.REJECTED_ADAPTER, runtime.submitWindField("", new byte[] {1}));
assertEquals(WeatherRuntime.State.READY, runtime.getState());
}
@Test public void publicFlowRejectsEnableFromErrorWithoutVisibilityRecovery() {
MapLibreMap map = Mockito.mock(MapLibreMap.class);
Mockito.when(map.getWeatherColorStopCapacity()).thenReturn(8);
WeatherRuntime runtime = new WeatherRuntime(map, new WeatherRuntime.Config("source", "color", "particle"));
assertEquals(WeatherRuntime.FieldSubmitResult.UNKNOWN, runtime.submitWindField("{}", new byte[] {1}));
assertEquals(WeatherRuntime.State.ERROR, runtime.getState());
assertEquals(WeatherRuntime.CommandResult.REJECTED, runtime.setWeatherEnabled(true));
assertEquals(WeatherRuntime.State.ERROR, runtime.getState());
Mockito.verify(map, Mockito.never()).setStyleLayerVisibility(Mockito.anyString(), Mockito.anyBoolean());
}
}