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:
Sasa Zivkov
2012-11-02 10:45:53 +01:00
parent d19be975e2
commit 8913ca43a4
3 changed files with 145 additions and 68 deletions

View File

@@ -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);
} }

View File

@@ -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();
} }

View File

@@ -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;
} }