Skip to content

Commit

Permalink
Fix race in continuous testing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartwdouglas committed Sep 7, 2021
1 parent 5cc29c8 commit 3d2efda
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,25 @@ public void listenerRegistered(TestController testController) {
@Override
public void testsEnabled() {
if (lastState == null) {
ContinuousTestingSharedStateManager
.setLastState(new ContinuousTestingSharedStateManager.State(
ContinuousTestingSharedStateManager.getLastState().lastRun, true, false,
0, 0, 0, 0,
0, 0, 0, ContinuousTestingSharedStateManager.getLastState().isBrokenOnly,
ContinuousTestingSharedStateManager.getLastState().isTestOutput,
ContinuousTestingSharedStateManager.getLastState().isInstrumentationBasedReload,
ContinuousTestingSharedStateManager.getLastState().isLiveReload));
ContinuousTestingSharedStateManager.setRunning(true);
} else {
ContinuousTestingSharedStateManager
.setLastState(lastState);
.setLastState((s) -> {
if (s.getLastRun() > lastState.lastRun) {
//small chance of race, next run already compled
return s.setRunning(true).build();
} else {
return s.setRunning(true)
.setCurrentFailed(lastState.currentFailed)
.setCurrentSkipped(lastState.currentSkipped)
.setCurrentFailed(lastState.currentFailed)
.setFailed(lastState.failed)
.setSkipped(lastState.skipped)
.setPassed(lastState.passed)
.setRun(lastState.run)
.build();
}
});
}
}

Expand All @@ -42,19 +50,21 @@ public void testRunStarted(Consumer<TestRunListener> listenerConsumer) {

@Override
public void runComplete(TestRunResults testRunResults) {
lastState = new ContinuousTestingSharedStateManager.State(testRunResults.getId(), true, false,
testRunResults.getPassedCount() +
testRunResults.getFailedCount() +
testRunResults.getSkippedCount(),
testRunResults.getPassedCount(),
testRunResults.getFailedCount(), testRunResults.getSkippedCount(),
testRunResults.getCurrentPassedCount(), testRunResults.getCurrentFailedCount(),
testRunResults.getCurrentSkippedCount(),
ContinuousTestingSharedStateManager.getLastState().isBrokenOnly,
ContinuousTestingSharedStateManager.getLastState().isTestOutput,
ContinuousTestingSharedStateManager.getLastState().isInstrumentationBasedReload,
ContinuousTestingSharedStateManager.getLastState().isLiveReload);
ContinuousTestingSharedStateManager.setLastState(lastState);
ContinuousTestingSharedStateManager.setLastState(s -> {
return lastState = s.setLastRun(testRunResults.getId())
.setInProgress(false)
.setRun(testRunResults.getPassedCount() +
testRunResults.getFailedCount() +
testRunResults.getSkippedCount())
.setPassed(testRunResults.getPassedCount())
.setFailed(testRunResults.getFailedCount())
.setSkipped(testRunResults.getSkippedCount())
.setCurrentPassed(testRunResults.getCurrentPassedCount())
.setCurrentFailed(testRunResults.getCurrentFailedCount())
.setCurrentSkipped(testRunResults.getCurrentSkippedCount())
.build();

});
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer;
import java.util.function.Function;

public class ContinuousTestingSharedStateManager {

private static final CopyOnWriteArraySet<Consumer<State>> stateListeners = new CopyOnWriteArraySet<>();
public static final State INITIAL_STATE = new State(-1, false, false, 0, 0, 0, 0, 0, 0, 0, false, false, false, true);
public static final State INITIAL_STATE = new StateBuilder()
.setLastRun(-1).setIsLiveReload(true).build();
private static volatile State lastState = INITIAL_STATE;

public static void addStateListener(Consumer<State> stateListener) {
Expand All @@ -21,11 +23,15 @@ public static void removeStateListener(Consumer<State> stateListener) {
}

public static void reset() {
setLastState(INITIAL_STATE);
setLastState((s) -> INITIAL_STATE);
}

public static void setLastState(State state) {
lastState = state;
public static void setLastState(Function<StateBuilder, State> modifier) {
State state;
synchronized (ContinuousTestingSharedStateManager.class) {
StateBuilder builder = lastState.builder();
state = lastState = modifier.apply(builder);
}
for (var sl : stateListeners) {
sl.accept(state);
}
Expand All @@ -36,63 +42,27 @@ public static State getLastState() {
}

public static void setInProgress(boolean inProgress) {
State state = lastState;
if (state != null) {
setLastState(
new State(state.lastRun, state.running, inProgress, state.run, state.passed, state.failed, state.skipped,
state.currentPassed, state.currentFailed, state.currentSkipped, state.isBrokenOnly,
state.isTestOutput, state.isInstrumentationBasedReload, state.isLiveReload));
}
setLastState((s) -> s.setInProgress(inProgress).build());
}

public static void setRunning(boolean running) {
State state = lastState;
if (state != null) {
setLastState(new State(state.lastRun, running, running && state.inProgress, state.run, state.passed, state.failed,
state.skipped,
state.currentPassed, state.currentFailed, state.currentSkipped, state.isBrokenOnly, state.isTestOutput,
state.isInstrumentationBasedReload, state.isLiveReload));
}
setLastState((s) -> s.setRunning(running).build());
}

public static void setBrokenOnly(boolean brokenOnly) {
State state = lastState;
if (state != null) {
setLastState(new State(state.lastRun, state.running, state.inProgress, state.run, state.passed, state.failed,
state.skipped,
state.currentPassed, state.currentFailed, state.currentSkipped, brokenOnly, state.isTestOutput,
state.isInstrumentationBasedReload, state.isLiveReload));
}
setLastState((s) -> s.setIsBrokenOnly(brokenOnly).build());
}

public static void setTestOutput(boolean testOutput) {
State state = lastState;
if (state != null) {
setLastState(new State(state.lastRun, state.running, state.inProgress, state.run, state.passed, state.failed,
state.skipped,
state.currentPassed, state.currentFailed, state.currentSkipped, state.isBrokenOnly, testOutput,
state.isInstrumentationBasedReload, state.isLiveReload));
}
setLastState((s) -> s.setIsTestOutput(testOutput).build());
}

public static void setInstrumentationBasedReload(boolean instrumentationBasedReload) {
State state = lastState;
if (state != null) {
setLastState(new State(state.lastRun, state.running, state.inProgress, state.run, state.passed, state.failed,
state.skipped,
state.currentPassed, state.currentFailed, state.currentSkipped, state.isBrokenOnly, state.isTestOutput,
instrumentationBasedReload, state.isLiveReload));
}
setLastState((s) -> s.setIsInstrumentationBasedReload(instrumentationBasedReload).build());
}

public static void setLiveReloadEnabled(boolean liveReload) {
State state = lastState;
if (state != null) {
setLastState(new State(state.lastRun, state.running, state.inProgress, state.run, state.passed, state.failed,
state.skipped,
state.currentPassed, state.currentFailed, state.currentSkipped, state.isBrokenOnly, state.isTestOutput,
state.isInstrumentationBasedReload, liveReload));
}
setLastState((s) -> s.setIsLiveReload(liveReload).build());
}

public static class State {
Expand All @@ -111,23 +81,25 @@ public static class State {
public final boolean isInstrumentationBasedReload;
public final boolean isLiveReload;

public State(long lastRun, boolean running, boolean inProgress, long run, long passed, long failed, long skipped,
long currentPassed, long currentFailed, long currentSkipped, boolean isBrokenOnly, boolean isTestOutput,
boolean isInstrumentationBasedReload, boolean isLiveReload) {
this.lastRun = lastRun;
this.running = running;
this.inProgress = inProgress;
this.run = run;
this.passed = passed;
this.failed = failed;
this.skipped = skipped;
this.currentPassed = currentPassed;
this.currentFailed = currentFailed;
this.currentSkipped = currentSkipped;
this.isBrokenOnly = isBrokenOnly;
this.isTestOutput = isTestOutput;
this.isInstrumentationBasedReload = isInstrumentationBasedReload;
this.isLiveReload = isLiveReload;
public State(StateBuilder builder) {
this.lastRun = builder.lastRun;
this.running = builder.running;
this.inProgress = builder.inProgress;
this.run = builder.run;
this.passed = builder.passed;
this.failed = builder.failed;
this.skipped = builder.skipped;
this.currentPassed = builder.currentPassed;
this.currentFailed = builder.currentFailed;
this.currentSkipped = builder.currentSkipped;
this.isBrokenOnly = builder.isBrokenOnly;
this.isTestOutput = builder.isTestOutput;
this.isInstrumentationBasedReload = builder.isInstrumentationBasedReload;
this.isLiveReload = builder.isLiveReload;
}

StateBuilder builder() {
return new StateBuilder(this);
}

@Override
Expand All @@ -147,4 +119,172 @@ public String toString() {
'}';
}
}

public static class StateBuilder {

public StateBuilder() {
}

public StateBuilder(State existing) {
this.lastRun = existing.lastRun;
this.running = existing.running;
this.inProgress = existing.inProgress;
this.run = existing.run;
this.passed = existing.passed;
this.failed = existing.failed;
this.skipped = existing.skipped;
this.currentPassed = existing.currentPassed;
this.currentFailed = existing.currentFailed;
this.currentSkipped = existing.currentSkipped;
this.isBrokenOnly = existing.isBrokenOnly;
this.isTestOutput = existing.isTestOutput;
this.isInstrumentationBasedReload = existing.isInstrumentationBasedReload;
this.isLiveReload = existing.isLiveReload;
}

long lastRun;
boolean running;
boolean inProgress;
long run;
long passed;
long failed;
long skipped;
long currentPassed;
long currentFailed;
long currentSkipped;
boolean isBrokenOnly;
boolean isTestOutput;
boolean isInstrumentationBasedReload;
boolean isLiveReload;

public StateBuilder setLastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}

public StateBuilder setRunning(boolean running) {
this.running = running;
return this;
}

public StateBuilder setInProgress(boolean inProgress) {
this.inProgress = inProgress;
return this;
}

public StateBuilder setRun(long run) {
this.run = run;
return this;
}

public StateBuilder setPassed(long passed) {
this.passed = passed;
return this;
}

public StateBuilder setFailed(long failed) {
this.failed = failed;
return this;
}

public StateBuilder setSkipped(long skipped) {
this.skipped = skipped;
return this;
}

public StateBuilder setCurrentPassed(long currentPassed) {
this.currentPassed = currentPassed;
return this;
}

public StateBuilder setCurrentFailed(long currentFailed) {
this.currentFailed = currentFailed;
return this;
}

public StateBuilder setCurrentSkipped(long currentSkipped) {
this.currentSkipped = currentSkipped;
return this;
}

public StateBuilder setIsBrokenOnly(boolean isBrokenOnly) {
this.isBrokenOnly = isBrokenOnly;
return this;
}

public StateBuilder setIsTestOutput(boolean isTestOutput) {
this.isTestOutput = isTestOutput;
return this;
}

public StateBuilder setIsInstrumentationBasedReload(boolean isInstrumentationBasedReload) {
this.isInstrumentationBasedReload = isInstrumentationBasedReload;
return this;
}

public StateBuilder setIsLiveReload(boolean isLiveReload) {
this.isLiveReload = isLiveReload;
return this;
}

public long getLastRun() {
return lastRun;
}

public boolean isRunning() {
return running;
}

public boolean isInProgress() {
return inProgress;
}

public long getRun() {
return run;
}

public long getPassed() {
return passed;
}

public long getFailed() {
return failed;
}

public long getSkipped() {
return skipped;
}

public long getCurrentPassed() {
return currentPassed;
}

public long getCurrentFailed() {
return currentFailed;
}

public long getCurrentSkipped() {
return currentSkipped;
}

public boolean isBrokenOnly() {
return isBrokenOnly;
}

public boolean isTestOutput() {
return isTestOutput;
}

public boolean isInstrumentationBasedReload() {
return isInstrumentationBasedReload;
}

public boolean isLiveReload() {
return isLiveReload;
}

public ContinuousTestingSharedStateManager.State build() {
return new ContinuousTestingSharedStateManager.State(this);
}
}
}
Loading

0 comments on commit 3d2efda

Please sign in to comment.