package org.corfudb.runtime.clients;
import org.corfudb.infrastructure.IServerRouter;
import org.corfudb.protocols.wireprotocol.CorfuMsg;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Created by mwei on 6/29/16.
*/
public class TestRule {
// conditions
private boolean always = false;
private Function<CorfuMsg, Boolean> matcher = null;
// actions
private boolean drop = false;
private boolean dropEven = false;
private boolean dropOdd = false;
private Consumer<CorfuMsg> transformer = null;
private Function<CorfuMsg, CorfuMsg> injectBefore = null;
// state
private AtomicInteger timesMatched = new AtomicInteger();
/**
* Always evaluate this rule.
*/
public TestRule always() {
this.always = true;
return this;
}
/**
* Transform the matched message
*
* @param transformation A function which transforms the supplied message.
*/
public TestRule transform(Consumer<CorfuMsg> transformation) {
transformer = transformation;
return this;
}
/**
* Drop this message.
*/
public TestRule drop() {
this.drop = true;
return this;
}
/**
* Drop this message on even matches (first match is even)
*/
public TestRule dropEven() {
this.dropEven = true;
return this;
}
/**
* Drop this message on odd matches
*/
public TestRule dropOdd() {
this.dropOdd = true;
return this;
}
/**
* Supply a message to be injected before this message is delivered.
*
* @param injectBefore A function which takes the CorfuMsg the rule is being
* applied to and returns a CorfuMsg to be injected.
*/
public TestRule injectBefore(Function<CorfuMsg, CorfuMsg> injectBefore) {
this.injectBefore = injectBefore;
return this;
}
/**
* Provide a custom matcher.
*
* @param matcher A function that takes a CorfuMsg and returns true if the
* message matches.
*/
public TestRule matches(Function<CorfuMsg, Boolean> matcher) {
this.matcher = matcher;
return this;
}
/**
* Evaluate this rule on a given message and router.
*/
public boolean evaluate(CorfuMsg message, Object router) {
if (message == null) return false;
if (match(message)) {
int matchNumber = timesMatched.getAndIncrement();
if (drop) return false;
if (dropOdd && matchNumber % 2 != 0) return false;
if (dropEven && matchNumber % 2 == 0) return false;
if (transformer != null) transformer.accept(message);
if (injectBefore != null && router instanceof IClientRouter)
((IClientRouter)router).sendMessage(null, injectBefore.apply(message));
if (injectBefore != null && router instanceof IServerRouter)
((IServerRouter)router).sendResponse(null, injectBefore.apply(message), injectBefore.apply(message));
}
return true;
}
/**
* Returns whether or not the rule matches the given message.
*/
boolean match(CorfuMsg message) {
return always || matcher.apply(message);
}
}