com.ocadotechnology.scenario.StepCache Maven / Gradle / Ivy
The newest version!
/*
* Copyright © 2017-2024 Ocado (Ocava)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ocadotechnology.scenario;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
import javax.annotation.CheckForNull;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.ocadotechnology.event.scheduling.Cancelable;
public class StepCache extends Cleanable {
private static final Predicate EXCEPTION_CHECKER_DEFAULT = t -> false;
private final LinkedList orderedSteps = new LinkedList<>();
private Executable lastStep;
private final Multimap unorderedSteps = LinkedHashMultimap.create();
private final ListMultimap sequencedSteps = MultimapBuilder.linkedHashKeys().arrayListValues().build();
private final Set allUnorderedStepNames = new LinkedHashSet<>();
private final Set allSequenceStepNames = new LinkedHashSet<>();
private int stepCounter = 0;
private final List finalSteps = new ArrayList<>();
private Predicate exceptionChecker = EXCEPTION_CHECKER_DEFAULT;
private final List failingSteps = new ArrayList<>();
public int getNextStepCounter() {
return stepCounter++;
}
public void addCheckStep(ExceptionCheckStep testStep) {
addOrdered(testStep);
exceptionChecker = testStep::checkThrowable;
}
public Predicate getExceptionChecker() {
return exceptionChecker;
}
public void addOrdered(Executable testStep) {
validateExceptionAsLastStep(testStep);
if (isMergeable(testStep)) {
lastStep.merge(testStep);
} else {
orderedSteps.add(testStep);
}
lastStep = testStep;
}
public void addOrdered(int idx, Executable testStep) {
validateExceptionAsLastStep(testStep);
if (idx == orderedSteps.size()) {
addOrdered(testStep);
} else {
orderedSteps.add(idx, testStep);
}
}
private void validateExceptionAsLastStep(Executable testStep) {
Preconditions.checkState(exceptionChecker == EXCEPTION_CHECKER_DEFAULT, "You can not add another steps after Exception step. Invalid step [%s] ", testStep);
}
public void clearOrderedSteps() {
orderedSteps.clear();
lastStep = null;
}
public void clearUnorderedSteps() {
unorderedSteps.clear();
sequencedSteps.clear();
allUnorderedStepNames.clear();
allSequenceStepNames.clear();
}
public ImmutableList getOrderedStepsView() {
return ImmutableList.copyOf(orderedSteps);
}
public void addFinalStep(Executable testStep) {
finalSteps.add(testStep);
}
public List getFinalSteps() {
return finalSteps;
}
public Executable removeLastStep() {
LinkedList list = orderedSteps;
Executable removedSteps = list.removeLast();
lastStep = list.getLast();
return removedSteps;
}
private boolean isMergeable(Executable testStep) {
return testStep.isMergeable() && lastStep != null && lastStep.isMergeable();
}
public boolean isUnorderedStepFinished(String name) {
return !unorderedSteps.containsKey(name) && !sequencedSteps.containsKey(name);
}
public String getRandomUnorderedStepName() {
String name = String.valueOf(System.nanoTime());
while (allUnorderedStepNames.contains(name) || allSequenceStepNames.contains(name)) {
name = String.valueOf(System.nanoTime());
}
return name;
}
public void addUnordered(String name, Executable testStep) {
allUnorderedStepNames.add(name);
Preconditions.checkState(!allSequenceStepNames.contains(name),
"Cannot add unordered step with name [%s] as it has already been used for a sequenced step.", name);
unorderedSteps.put(name, testStep);
}
public void addSequenced(String name, Executable testStep) {
allSequenceStepNames.add(name);
Preconditions.checkState(!allUnorderedStepNames.contains(name),
"Cannot add sequenced step with name [%s] as it has already been used for an unordered step.", name);
if (!sequencedSteps.containsKey(name)) {
//Sequenced steps should be executed as soon as they reach the head of the queue
testStep.setActive();
testStep.executeAndLog();
}
if (!testStep.isFinished()) {
sequencedSteps.put(name, testStep);
}
}
public ImmutableSet getAllUnorderedStepNames() {
return ImmutableSet.builder()
.addAll(allUnorderedStepNames)
.addAll(allSequenceStepNames)
.build();
}
public boolean hasAddedStepWithName(String name) {
return allUnorderedStepNames.contains(name) || allSequenceStepNames.contains(name);
}
public void removeAndCancel(String name) {
Preconditions.checkState(unorderedSteps.containsKey(name) || sequencedSteps.containsKey(name),
"Tried to remove unordered or sequenced steps with name '%s', but didn't find one. Known unorderedSteps: %s, sequencedSteps %s",
name,
unorderedSteps,
sequencedSteps);
removeAndCancelIfPresent(name);
}
public void removeAndCancelIfPresent(String name) {
unorderedSteps.removeAll(name).stream().filter(step -> step instanceof Cancelable).forEach(step -> ((Cancelable) step).cancel());
sequencedSteps.removeAll(name).stream().filter(step -> step instanceof Cancelable).forEach(step -> ((Cancelable) step).cancel());
}
public Iterator getUnorderedStepsIterator() {
return new UnorderedStepsIterator();
}
public boolean isFinished() {
boolean unorderedStepsFinished = Stream.concat(
unorderedSteps.values().stream(),
sequencedSteps.values().stream())
.noneMatch(step -> step.isRequired() && !step.isFinished());
return unorderedStepsFinished && orderedSteps.isEmpty();
}
@CheckForNull
public Executable getUnfinishedUnorderedStep() {
return Stream.concat(unorderedSteps.values().stream(), sequencedSteps.values().stream())
.filter(step -> step.isRequired() && !step.isFinished())
.findAny()
.orElse(null);
}
@Override
public void clean() {
orderedSteps.clear();
unorderedSteps.clear();
sequencedSteps.clear();
allUnorderedStepNames.clear();
allSequenceStepNames.clear();
finalSteps.clear();
stepCounter = 0;
exceptionChecker = EXCEPTION_CHECKER_DEFAULT;
}
public Executable getNextStep() {
return orderedSteps.poll();
}
public Executable peekNextStep() {
return orderedSteps.peek();
}
public boolean hasSteps() {
return !orderedSteps.isEmpty() || !unorderedSteps.isEmpty() || !sequencedSteps.isEmpty() || !finalSteps.isEmpty();
}
public List getFailingSteps() {
return failingSteps;
}
public void addFailingStep(Executable failingStep) {
this.failingSteps.add(failingStep);
}
private class UnorderedStepsIterator implements Iterator {
private final Iterator unorderedStepsIterator;
private Iterator sequenceNamesIterator;
private String currentSequenceName = null;
private UnorderedStepsIterator() {
this.unorderedStepsIterator = unorderedSteps.values().iterator();
this.sequenceNamesIterator = sequencedSteps.keySet().iterator();
}
@Override
public boolean hasNext() {
return unorderedStepsIterator.hasNext() || sequenceNamesIterator.hasNext();
}
@Override
public Executable next() {
if (unorderedStepsIterator.hasNext()) {
return unorderedStepsIterator.next();
}
currentSequenceName = sequenceNamesIterator.next();
return sequencedSteps.get(currentSequenceName).get(0);
}
@Override
public void remove() {
if (currentSequenceName == null) {
unorderedStepsIterator.remove();
} else {
advanceSequence(currentSequenceName);
this.sequenceNamesIterator = sequencedSteps.keySet().iterator(); // Reset this in case the head step from an earlier sequence is now complete
}
}
private void advanceSequence(String currentSequence) {
sequencedSteps.get(currentSequence).remove(0);
if (sequencedSteps.containsKey(currentSequence)) {
sequencedSteps.get(currentSequence).get(0).setActive();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy