Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static com.launchdarkly.sdk.android.TestUtil.requireValue;
import static org.easymock.EasyMock.anyBoolean;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.checkOrder;
import static org.easymock.EasyMock.expectLastCall;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
Expand Down Expand Up @@ -1461,19 +1462,27 @@ public void fdv2_rapidStateChangesCoalesceIntoOneRebuild() throws Exception {
verifyForegroundDataSourceWasCreatedAndStarted(CONTEXT);
verifyAll();

// After startup, the rapid connectivity changes call updateEventProcessor in
// parallel threads, so ordering is nondeterministic. Use anyTimes().
// After startup, the rapid connectivity changes call updateEventProcessor, which
// invokes setOffline then setInBackground once per change. The number of changes is
// nondeterministic, so we use anyTimes(). We also disable order checking on this
// strict mock: each updateEventProcessor call repeats the (setOffline, setInBackground)
// pair, which a strict mock would otherwise reject as out-of-order once the first
// setInBackground has been consumed.
resetAll();
checkOrder(eventProcessor, false);
eventProcessor.setOffline(anyBoolean());
expectLastCall().anyTimes();
eventProcessor.setInBackground(anyBoolean());
expectLastCall().anyTimes();
replayAll();

// Fire multiple rapid connectivity changes — debounce should coalesce them
mockPlatformState.setAndNotifyConnectivityChangeListeners(false);
mockPlatformState.setAndNotifyConnectivityChangeListeners(true);
mockPlatformState.setAndNotifyConnectivityChangeListeners(false);
// Fire multiple rapid connectivity changes — debounce should coalesce them.
// Deliver the burst on a single thread so the listener observes the values in a
// deterministic order ending on `false`. Three separate setAndNotify... calls each
// spawn their own thread, which can reorder so that `true` (the baseline) is applied
// last; the debouncer's A→B→C→A dedup would then suppress the rebuild and no data
// source would ever be stopped, flaking verifyDataSourceWasStopped() below.
mockPlatformState.setAndNotifyConnectivityChangeListenersInSequence(false, true, false);

// Should result in exactly one data source rebuild (to OFFLINE)
verifyDataSourceWasStopped();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,25 @@ public void setAndNotifyConnectivityChangeListeners(boolean networkAvailable) {
}).start();
}

/**
* Notifies connectivity-change listeners with each value in {@code values}, in order, on a
* single background thread. Unlike repeated {@link #setAndNotifyConnectivityChangeListeners}
* calls — which each spawn their own thread and can therefore deliver notifications out of
* order — this delivers the whole sequence deterministically. The last value becomes the
* reported network state. Use this when a test depends on the final state after a burst of
* rapid changes (e.g. debounce coalescing).
*/
public void setAndNotifyConnectivityChangeListenersInSequence(boolean... values) {
new Thread(() -> {
for (boolean value : values) {
this.networkAvailable = value;
for (ConnectivityChangeListener listener : connectivityChangeListeners) {
listener.onConnectivityChanged(value);
}
}
}).start();
}

public void setAndNotifyForegroundChangeListeners(boolean foreground) {
this.foreground = foreground;
new Thread(() -> {
Expand Down
Loading