Make SubmitRuleEvaluator usable for testing prolog rules.
For the purpose of testing submit rules the SubmitRuleEvaluator needs more flexibility like skipping the submit filters and loading prolog rules from a caller provided input stream. This change is a preparation for refactoring of the test-submit-rule command which currently duplicates most of the code for finding and executing the prolog rules. Change-Id: I54086590fa5dc5cefdd9c36ca2e8587d374bd988 Signed-off-by: Sasa Zivkov <sasa.zivkov@sap.com>
This commit is contained in:
@@ -45,7 +45,10 @@ import org.eclipse.jgit.util.RawParseUtils;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.io.PushbackReader;
|
import java.io.PushbackReader;
|
||||||
|
import java.io.Reader;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.lang.ref.Reference;
|
import java.lang.ref.Reference;
|
||||||
import java.lang.ref.ReferenceQueue;
|
import java.lang.ref.ReferenceQueue;
|
||||||
@@ -146,6 +149,15 @@ public class RulesCache {
|
|||||||
return pcm;
|
return pcm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PrologMachineCopy loadMachine(String name, InputStream in)
|
||||||
|
throws CompileException {
|
||||||
|
PrologMachineCopy pmc = consultRules(name, new InputStreamReader(in));
|
||||||
|
if (pmc == null) {
|
||||||
|
throw new CompileException("Cannot consult rules from the stream " + name);
|
||||||
|
}
|
||||||
|
return pmc;
|
||||||
|
}
|
||||||
|
|
||||||
private void gc() {
|
private void gc() {
|
||||||
Reference<?> ref;
|
Reference<?> ref;
|
||||||
while ((ref = dead.poll()) != null) {
|
while ((ref = dead.poll()) != null) {
|
||||||
@@ -172,16 +184,21 @@ public class RulesCache {
|
|||||||
// Dynamically consult the rules into the machine's internal database.
|
// Dynamically consult the rules into the machine's internal database.
|
||||||
//
|
//
|
||||||
String rules = read(project, rulesId);
|
String rules = read(project, rulesId);
|
||||||
BufferingPrologControl ctl = newEmptyMachine(systemLoader);
|
PrologMachineCopy pmc = consultRules("rules.pl", new StringReader(rules));
|
||||||
PushbackReader in = new PushbackReader(
|
if (pmc == null) {
|
||||||
new StringReader(rules),
|
throw new CompileException("Cannot consult rules of " + project);
|
||||||
Prolog.PUSHBACK_SIZE);
|
}
|
||||||
|
return pmc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PrologMachineCopy consultRules(String name, Reader rules) {
|
||||||
|
BufferingPrologControl ctl = newEmptyMachine(systemLoader);
|
||||||
|
PushbackReader in = new PushbackReader(rules, Prolog.PUSHBACK_SIZE);
|
||||||
if (!ctl.execute(
|
if (!ctl.execute(
|
||||||
Prolog.BUILTIN, "consult_stream",
|
Prolog.BUILTIN, "consult_stream",
|
||||||
SymbolTerm.intern("rules.pl"),
|
SymbolTerm.intern(name),
|
||||||
new JavaObjectTerm(in))) {
|
new JavaObjectTerm(in))) {
|
||||||
throw new CompileException("Cannot consult rules of " + project);
|
return null;
|
||||||
}
|
}
|
||||||
return save(ctl);
|
return save(ctl);
|
||||||
}
|
}
|
||||||
|
@@ -42,6 +42,7 @@ import org.eclipse.jgit.lib.Ref;
|
|||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -168,6 +169,20 @@ public class ProjectState {
|
|||||||
return envFactory.create(pmc);
|
return envFactory.create(pmc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like {@link #newPrologEnvironment()} but instead of reading the rules.pl
|
||||||
|
* read the provided input stream.
|
||||||
|
*
|
||||||
|
* @param name a name of the input stream. Could be any name.
|
||||||
|
* @param in InputStream to read prolog rules from
|
||||||
|
* @throws CompileException
|
||||||
|
*/
|
||||||
|
public PrologEnvironment newPrologEnvironment(String name, InputStream in)
|
||||||
|
throws CompileException {
|
||||||
|
PrologMachineCopy pmc = rulesCache.loadMachine(name, in);
|
||||||
|
return envFactory.create(pmc);
|
||||||
|
}
|
||||||
|
|
||||||
public Project getProject() {
|
public Project getProject() {
|
||||||
return config.getProject();
|
return config.getProject();
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,7 @@ import com.googlecode.prolog_cafe.lang.PrologException;
|
|||||||
import com.googlecode.prolog_cafe.lang.Term;
|
import com.googlecode.prolog_cafe.lang.Term;
|
||||||
import com.googlecode.prolog_cafe.lang.VariableTerm;
|
import com.googlecode.prolog_cafe.lang.VariableTerm;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -53,6 +54,8 @@ public class SubmitRuleEvaluator {
|
|||||||
private final String userRuleWrapperName;
|
private final String userRuleWrapperName;
|
||||||
private final String filterRuleLocatorName;
|
private final String filterRuleLocatorName;
|
||||||
private final String filterRuleWrapperName;
|
private final String filterRuleWrapperName;
|
||||||
|
private final boolean skipFilters;
|
||||||
|
private final InputStream rulesInputStream;
|
||||||
|
|
||||||
private Term submitRule;
|
private Term submitRule;
|
||||||
private String projectName;
|
private String projectName;
|
||||||
@@ -67,12 +70,38 @@ public class SubmitRuleEvaluator {
|
|||||||
* @param filterRuleWrapperName The name of the rule used to evaluate the
|
* @param filterRuleWrapperName The name of the rule used to evaluate the
|
||||||
* filter rule.
|
* filter rule.
|
||||||
*/
|
*/
|
||||||
SubmitRuleEvaluator(ReviewDb db, PatchSet patchSet,
|
public SubmitRuleEvaluator(ReviewDb db, PatchSet patchSet,
|
||||||
ProjectControl projectControl,
|
ProjectControl projectControl,
|
||||||
ChangeControl changeControl, Change change, @Nullable ChangeData cd,
|
ChangeControl changeControl, Change change, @Nullable ChangeData cd,
|
||||||
boolean fastEvalLabels,
|
boolean fastEvalLabels,
|
||||||
String userRuleLocatorName, String userRuleWrapperName,
|
String userRuleLocatorName, String userRuleWrapperName,
|
||||||
String filterRuleLocatorName, String filterRuleWrapperName) {
|
String filterRuleLocatorName, String filterRuleWrapperName) {
|
||||||
|
this(db, patchSet, projectControl, changeControl, change, cd,
|
||||||
|
fastEvalLabels, userRuleLocatorName, userRuleWrapperName,
|
||||||
|
filterRuleLocatorName, filterRuleWrapperName, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param userRuleLocatorName The name of the rule used to locate the
|
||||||
|
* user-supplied rule.
|
||||||
|
* @param userRuleWrapperName The name of the wrapper rule used to evaluate
|
||||||
|
* the user-supplied rule.
|
||||||
|
* @param filterRuleLocatorName The name of the rule used to locate the filter
|
||||||
|
* rule.
|
||||||
|
* @param filterRuleWrapperName The name of the rule used to evaluate the
|
||||||
|
* filter rule.
|
||||||
|
* @param skipSubmitFilters if <code>true</code> submit filter will not be
|
||||||
|
* applied
|
||||||
|
* @param rules when non-null the rules will be read from this input stream
|
||||||
|
* instead of refs/meta/config:rules.pl file
|
||||||
|
*/
|
||||||
|
public SubmitRuleEvaluator(ReviewDb db, PatchSet patchSet,
|
||||||
|
ProjectControl projectControl,
|
||||||
|
ChangeControl changeControl, Change change, @Nullable ChangeData cd,
|
||||||
|
boolean fastEvalLabels,
|
||||||
|
String userRuleLocatorName, String userRuleWrapperName,
|
||||||
|
String filterRuleLocatorName, String filterRuleWrapperName,
|
||||||
|
boolean skipSubmitFilters, InputStream rules) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.patchSet = patchSet;
|
this.patchSet = patchSet;
|
||||||
this.projectControl = projectControl;
|
this.projectControl = projectControl;
|
||||||
@@ -84,6 +113,8 @@ public class SubmitRuleEvaluator {
|
|||||||
this.userRuleWrapperName = userRuleWrapperName;
|
this.userRuleWrapperName = userRuleWrapperName;
|
||||||
this.filterRuleLocatorName = filterRuleLocatorName;
|
this.filterRuleLocatorName = filterRuleLocatorName;
|
||||||
this.filterRuleWrapperName = filterRuleWrapperName;
|
this.filterRuleWrapperName = filterRuleWrapperName;
|
||||||
|
this.skipFilters = skipSubmitFilters;
|
||||||
|
this.rulesInputStream = rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,25 +127,10 @@ public class SubmitRuleEvaluator {
|
|||||||
* @return List of {@link Term} objects returned from the evaluated rules.
|
* @return List of {@link Term} objects returned from the evaluated rules.
|
||||||
* @throws RuleEvalException
|
* @throws RuleEvalException
|
||||||
*/
|
*/
|
||||||
ListTerm evaluate() throws RuleEvalException {
|
public ListTerm evaluate() throws RuleEvalException {
|
||||||
List<Term> results = new ArrayList<Term>();
|
List<Term> results = new ArrayList<Term>();
|
||||||
ProjectState projectState = projectControl.getProjectState();
|
PrologEnvironment env = getPrologEnvironment();
|
||||||
PrologEnvironment env;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
env = projectState.newPrologEnvironment();
|
|
||||||
} catch (CompileException err) {
|
|
||||||
throw new RuleEvalException("Cannot consult rules.pl for "
|
|
||||||
+ getProjectName(), err);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
env.set(StoredValues.REVIEW_DB, db);
|
|
||||||
env.set(StoredValues.CHANGE, change);
|
|
||||||
env.set(StoredValues.CHANGE_DATA, cd);
|
|
||||||
env.set(StoredValues.PATCH_SET, patchSet);
|
|
||||||
env.set(StoredValues.CHANGE_CONTROL, changeControl);
|
|
||||||
|
|
||||||
submitRule = env.once("gerrit", userRuleLocatorName, new VariableTerm());
|
submitRule = env.once("gerrit", userRuleLocatorName, new VariableTerm());
|
||||||
if (fastEvalLabels) {
|
if (fastEvalLabels) {
|
||||||
env.once("gerrit", "assume_range_from_label");
|
env.once("gerrit", "assume_range_from_label");
|
||||||
@@ -135,12 +151,44 @@ public class SubmitRuleEvaluator {
|
|||||||
err);
|
err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Term resultsTerm = toListTerm(results);
|
||||||
|
if (!skipFilters) {
|
||||||
|
resultsTerm = runSubmitFilters(resultsTerm, env);
|
||||||
|
}
|
||||||
|
return (ListTerm) resultsTerm;
|
||||||
|
} finally {
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PrologEnvironment getPrologEnvironment() throws RuleEvalException {
|
||||||
|
ProjectState projectState = projectControl.getProjectState();
|
||||||
|
PrologEnvironment env;
|
||||||
|
try {
|
||||||
|
if (rulesInputStream == null) {
|
||||||
|
env = projectState.newPrologEnvironment();
|
||||||
|
} else {
|
||||||
|
env = projectState.newPrologEnvironment("stdin", rulesInputStream);
|
||||||
|
}
|
||||||
|
} catch (CompileException err) {
|
||||||
|
throw new RuleEvalException("Cannot consult rules.pl for "
|
||||||
|
+ getProjectName(), err);
|
||||||
|
}
|
||||||
|
env.set(StoredValues.REVIEW_DB, db);
|
||||||
|
env.set(StoredValues.CHANGE, change);
|
||||||
|
env.set(StoredValues.CHANGE_DATA, cd);
|
||||||
|
env.set(StoredValues.PATCH_SET, patchSet);
|
||||||
|
env.set(StoredValues.CHANGE_CONTROL, changeControl);
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Term runSubmitFilters(Term results, PrologEnvironment env) throws RuleEvalException {
|
||||||
|
ProjectState projectState = projectControl.getProjectState();
|
||||||
ProjectState parentState = projectState.getParentState();
|
ProjectState parentState = projectState.getParentState();
|
||||||
PrologEnvironment childEnv = env;
|
PrologEnvironment childEnv = env;
|
||||||
Set<Project.NameKey> projectsSeen = new HashSet<Project.NameKey>();
|
Set<Project.NameKey> projectsSeen = new HashSet<Project.NameKey>();
|
||||||
projectsSeen.add(projectState.getProject().getNameKey());
|
projectsSeen.add(projectState.getProject().getNameKey());
|
||||||
|
|
||||||
Term resultsTerm = toListTerm(results);
|
|
||||||
while (parentState != null) {
|
while (parentState != null) {
|
||||||
if (!projectsSeen.add(parentState.getProject().getNameKey())) {
|
if (!projectsSeen.add(parentState.getProject().getNameKey())) {
|
||||||
// parent has been seen before, stop walk up inheritance tree
|
// parent has been seen before, stop walk up inheritance tree
|
||||||
@@ -164,8 +212,8 @@ public class SubmitRuleEvaluator {
|
|||||||
|
|
||||||
Term[] template =
|
Term[] template =
|
||||||
parentEnv.once("gerrit", filterRuleWrapperName, filterRule,
|
parentEnv.once("gerrit", filterRuleWrapperName, filterRule,
|
||||||
resultsTerm, new VariableTerm());
|
results, new VariableTerm());
|
||||||
resultsTerm = template[2];
|
results = template[2];
|
||||||
} catch (PrologException err) {
|
} catch (PrologException err) {
|
||||||
throw new RuleEvalException("Exception calling " + filterRule
|
throw new RuleEvalException("Exception calling " + filterRule
|
||||||
+ " on change " + change.getId() + " of "
|
+ " on change " + change.getId() + " of "
|
||||||
@@ -180,10 +228,7 @@ public class SubmitRuleEvaluator {
|
|||||||
childEnv = parentEnv;
|
childEnv = parentEnv;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (ListTerm) resultsTerm;
|
return results;
|
||||||
} finally {
|
|
||||||
env.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Term toListTerm(List<Term> terms) {
|
private static Term toListTerm(List<Term> terms) {
|
||||||
@@ -194,7 +239,7 @@ public class SubmitRuleEvaluator {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
Term getSubmitRule() {
|
public Term getSubmitRule() {
|
||||||
return submitRule;
|
return submitRule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user